JOOMLA中国
  • Joomla中国首页
  • 社区
  • 教程
  • 应用市场
  • B计划
Joomla! Framework TM
  • Namespace
  • Class
  • Tree
  • Deprecated

Namespaces

  • Composer
    • Autoload
  • Joomla
    • Application
      • Cli
        • Output
          • Processor
      • Web
    • Data
    • DI
      • Exception
    • Event
    • Filter
    • Input
    • Ldap
    • Registry
      • Format
    • Session
      • Storage
    • String
    • Uri
    • Utilities
  • None
  • PasswordCompat
    • binary
  • PHP
  • Psr
    • Log
  • Symfony
    • Component
      • Yaml
        • Exception
    • Polyfill
      • Util

Classes

  • CallbackFilterIterator
  • ComposerAutoloaderInit205c915b9c7d3e718e7c95793ee67ffe
  • easyparse
  • EasyPeasyICS
  • FOFAutoloaderComponent
  • FOFAutoloaderFof
  • FOFConfigDomainDispatcher
  • FOFConfigDomainTables
  • FOFConfigDomainViews
  • FOFConfigProvider
  • FOFController
  • FOFDatabase
  • FOFDatabaseDriver
  • FOFDatabaseDriverJoomla
  • FOFDatabaseDriverMysql
  • FOFDatabaseDriverMysqli
  • FOFDatabaseDriverOracle
  • FOFDatabaseDriverPdo
  • FOFDatabaseDriverPdomysql
  • FOFDatabaseDriverPostgresql
  • FOFDatabaseDriverSqlazure
  • FOFDatabaseDriverSqlite
  • FOFDatabaseDriverSqlsrv
  • FOFDatabaseFactory
  • FOFDatabaseInstaller
  • FOFDatabaseIterator
  • FOFDatabaseIteratorAzure
  • FOFDatabaseIteratorMysql
  • FOFDatabaseIteratorMysqli
  • FOFDatabaseIteratorOracle
  • FOFDatabaseIteratorPdo
  • FOFDatabaseIteratorPdomysql
  • FOFDatabaseIteratorPostgresql
  • FOFDatabaseIteratorSqlite
  • FOFDatabaseIteratorSqlsrv
  • FOFDatabaseQuery
  • FOFDatabaseQueryElement
  • FOFDatabaseQueryMysql
  • FOFDatabaseQueryMysqli
  • FOFDatabaseQueryOracle
  • FOFDatabaseQueryPdo
  • FOFDatabaseQueryPdomysql
  • FOFDatabaseQueryPostgresql
  • FOFDatabaseQuerySqlazure
  • FOFDatabaseQuerySqlite
  • FOFDatabaseQuerySqlsrv
  • FOFDispatcher
  • FOFDownload
  • FOFDownloadAdapterAbstract
  • FOFDownloadAdapterCurl
  • FOFDownloadAdapterFopen
  • FOFEncryptAes
  • FOFEncryptAesAbstract
  • FOFEncryptAesMcrypt
  • FOFEncryptAesOpenssl
  • FOFEncryptBase32
  • FOFEncryptRandval
  • FOFEncryptTotp
  • FOFForm
  • FOFFormFieldAccesslevel
  • FOFFormFieldActions
  • FOFFormFieldButton
  • FOFFormFieldCachehandler
  • FOFFormFieldCalendar
  • FOFFormFieldCaptcha
  • FOFFormFieldCheckbox
  • FOFFormFieldCheckboxes
  • FOFFormFieldComponents
  • FOFFormFieldEditor
  • FOFFormFieldEmail
  • FOFFormFieldGroupedbutton
  • FOFFormFieldGroupedlist
  • FOFFormFieldHidden
  • FOFFormFieldImage
  • FOFFormFieldImagelist
  • FOFFormFieldInteger
  • FOFFormFieldLanguage
  • FOFFormFieldList
  • FOFFormFieldMedia
  • FOFFormFieldModel
  • FOFFormFieldOrdering
  • FOFFormFieldPassword
  • FOFFormFieldPlugins
  • FOFFormFieldPublished
  • FOFFormFieldRadio
  • FOFFormFieldRelation
  • FOFFormFieldRules
  • FOFFormFieldSelectrow
  • FOFFormFieldSessionhandler
  • FOFFormFieldSpacer
  • FOFFormFieldSql
  • FOFFormFieldTag
  • FOFFormFieldTel
  • FOFFormFieldText
  • FOFFormFieldTextarea
  • FOFFormFieldTimezone
  • FOFFormFieldTitle
  • FOFFormFieldUrl
  • FOFFormFieldUser
  • FOFFormFieldUsergroup
  • FOFFormHeader
  • FOFFormHeaderAccesslevel
  • FOFFormHeaderField
  • FOFFormHeaderFielddate
  • FOFFormHeaderFieldfilterable
  • FOFFormHeaderFieldsearchable
  • FOFFormHeaderFieldselectable
  • FOFFormHeaderFieldsql
  • FOFFormHeaderFilterdate
  • FOFFormHeaderFilterfilterable
  • FOFFormHeaderFiltersearchable
  • FOFFormHeaderFilterselectable
  • FOFFormHeaderFiltersql
  • FOFFormHeaderLanguage
  • FOFFormHeaderModel
  • FOFFormHeaderOrdering
  • FOFFormHeaderPublished
  • FOFFormHeaderRowselect
  • FOFFormHelper
  • FOFHalDocument
  • FOFHalLink
  • FOFHalLinks
  • FOFHalRenderJson
  • FOFInflector
  • FOFInput
  • FOFIntegrationJoomlaFilesystem
  • FOFIntegrationJoomlaPlatform
  • FOFLayoutFile
  • FOFLayoutHelper
  • FOFLess
  • FOFLessFormatterClassic
  • FOFLessFormatterCompressed
  • FOFLessFormatterJoomla
  • FOFLessFormatterLessjs
  • FOFLessParser
  • FOFModel
  • FOFModelBehavior
  • FOFModelBehaviorAccess
  • FOFModelBehaviorEmptynonzero
  • FOFModelBehaviorEnabled
  • FOFModelBehaviorFilters
  • FOFModelBehaviorLanguage
  • FOFModelBehaviorPrivate
  • FOFModelDispatcherBehavior
  • FOFModelField
  • FOFModelFieldBoolean
  • FOFModelFieldDate
  • FOFModelFieldNumber
  • FOFModelFieldText
  • FOFPlatform
  • FOFPlatformFilesystem
  • FOFQueryAbstract
  • FOFRenderAbstract
  • FOFRenderJoomla
  • FOFRenderJoomla3
  • FOFRenderStrapper
  • FOFStringUtils
  • FOFTable
  • FOFTableBehavior
  • FOFTableBehaviorAssets
  • FOFTableBehaviorContenthistory
  • FOFTableBehaviorTags
  • FOFTableDispatcherBehavior
  • FOFTableNested
  • FOFTableRelations
  • FOFTemplateUtils
  • FOFToolbar
  • FOFUtilsArray
  • FOFUtilsCacheCleaner
  • FOFUtilsConfigHelper
  • FOFUtilsFilescheck
  • FOFUtilsIniParser
  • FOFUtilsInstallscript
  • FOFUtilsIp
  • FOFUtilsObject
  • FOFUtilsObservableDispatcher
  • FOFUtilsObservableEvent
  • FOFUtilsPhpfunc
  • FOFUtilsTimer
  • FOFUtilsUpdate
  • FOFUtilsUpdateCollection
  • FOFUtilsUpdateExtension
  • FOFUtilsUpdateJoomla
  • FOFView
  • FOFViewCsv
  • FOFViewForm
  • FOFViewHtml
  • FOFViewJson
  • FOFViewRaw
  • idna_convert
  • JAccess
  • JAccessRule
  • JAccessRules
  • JAccessWrapperAccess
  • JAdapter
  • JAdapterInstance
  • JApplication
  • JApplicationAdministrator
  • JApplicationBase
  • JApplicationCli
  • JApplicationCms
  • JApplicationDaemon
  • JApplicationHelper
  • JApplicationSite
  • JApplicationWeb
  • JApplicationWebRouter
  • JApplicationWebRouterBase
  • JApplicationWebRouterRest
  • JArchive
  • JArchiveBzip2
  • JArchiveGzip
  • JArchiveTar
  • JArchiveWrapperArchive
  • JArchiveZip
  • JArrayHelper
  • JAssociationExtensionHelper
  • JAuthentication
  • JAuthenticationHelper
  • JAuthenticationResponse
  • JBrowser
  • JBuffer
  • JButton
  • JCache
  • JCacheController
  • JCacheControllerCallback
  • JCacheControllerOutput
  • JCacheControllerPage
  • JCacheControllerView
  • JCacheStorage
  • JCacheStorageApc
  • JCacheStorageApcu
  • JCacheStorageCachelite
  • JCacheStorageFile
  • JCacheStorageHelper
  • JCacheStorageMemcache
  • JCacheStorageMemcached
  • JCacheStorageRedis
  • JCacheStorageWincache
  • JCacheStorageXcache
  • JCaptcha
  • JCategories
  • JCategoryNode
  • JClassLoader
  • JCli
  • JClientFtp
  • JClientHelper
  • JClientLdap
  • JClientWrapperHelper
  • JComponentHelper
  • JComponentRecord
  • JComponentRouterBase
  • JComponentRouterLegacy
  • JComponentRouterRulesMenu
  • JComponentRouterRulesNomenu
  • JComponentRouterRulesStandard
  • JComponentRouterView
  • JComponentRouterViewconfiguration
  • JControllerAdmin
  • JControllerBase
  • JControllerForm
  • JControllerLegacy
  • JCrypt
  • JCryptCipher3Des
  • JCryptCipherBlowfish
  • JCryptCipherCrypto
  • JCryptCipherMcrypt
  • JCryptCipherRijndael256
  • JCryptCipherSimple
  • JCryptKey
  • JCryptPasswordSimple
  • JDaemon
  • JDatabase
  • JDatabaseDriver
  • JDatabaseDriverMysql
  • JDatabaseDriverMysqli
  • JDatabaseDriverOracle
  • JDatabaseDriverPdo
  • JDatabaseDriverPdomysql
  • JDatabaseDriverPostgresql
  • JDatabaseDriverSqlazure
  • JDatabaseDriverSqlite
  • JDatabaseDriverSqlsrv
  • JDatabaseExporter
  • JDatabaseExporterMysql
  • JDatabaseExporterMysqli
  • JDatabaseExporterPdomysql
  • JDatabaseExporterPostgresql
  • JDatabaseFactory
  • JDatabaseImporter
  • JDatabaseImporterMysql
  • JDatabaseImporterMysqli
  • JDatabaseImporterPdomysql
  • JDatabaseImporterPostgresql
  • JDatabaseInterface
  • JDatabaseIterator
  • JDatabaseIteratorMysql
  • JDatabaseIteratorMysqli
  • JDatabaseIteratorOracle
  • JDatabaseIteratorPdo
  • JDatabaseIteratorPdomysql
  • JDatabaseIteratorPostgresql
  • JDatabaseIteratorSqlazure
  • JDatabaseIteratorSqlite
  • JDatabaseIteratorSqlsrv
  • JDatabaseMysql
  • JDatabaseMysqli
  • JDatabaseQuery
  • JDatabaseQueryElement
  • JDatabaseQueryLimitable
  • JDatabaseQueryMysql
  • JDatabaseQueryMysqli
  • JDatabaseQueryOracle
  • JDatabaseQueryPdo
  • JDatabaseQueryPdomysql
  • JDatabaseQueryPostgresql
  • JDatabaseQueryPreparable
  • JDatabaseQuerySqlazure
  • JDatabaseQuerySqlite
  • JDatabaseQuerySqlsrv
  • JDatabaseSqlazure
  • JDatabaseSqlsrv
  • JDate
  • JDispatcher
  • JDocument
  • JDocumentError
  • JDocumentFeed
  • JDocumentHtml
  • JDocumentImage
  • JDocumentJson
  • JDocumentOpensearch
  • JDocumentRaw
  • JDocumentRenderer
  • JDocumentRendererAtom
  • JDocumentRendererComponent
  • JDocumentRendererFeedAtom
  • JDocumentRendererFeedRss
  • JDocumentRendererHead
  • JDocumentRendererHtmlComponent
  • JDocumentRendererHtmlHead
  • JDocumentRendererHtmlMessage
  • JDocumentRendererHtmlModule
  • JDocumentRendererHtmlModules
  • JDocumentRendererMessage
  • JDocumentRendererModule
  • JDocumentRendererModules
  • JDocumentRendererRSS
  • JDocumentXml
  • JEditor
  • JError
  • JErrorPage
  • JEvent
  • JEventDispatcher
  • JExtension
  • JFacebook
  • JFacebookAlbum
  • JFacebookCheckin
  • JFacebookComment
  • JFacebookEvent
  • JFacebookGroup
  • JFacebookLink
  • JFacebookNote
  • JFacebookOAuth
  • JFacebookObject
  • JFacebookPhoto
  • JFacebookPost
  • JFacebookStatus
  • JFacebookUser
  • JFacebookVideo
  • JFactory
  • JFeed
  • JFeedEnclosure
  • JFeedEntry
  • JFeedFactory
  • JFeedImage
  • JFeedItem
  • JFeedLink
  • JFeedParser
  • JFeedParserAtom
  • JFeedParserRss
  • JFeedParserRssItunes
  • JFeedParserRssMedia
  • JFeedPerson
  • JFile
  • JFilesystemHelper
  • JFilesystemPatcher
  • JFilesystemWrapperFile
  • JFilesystemWrapperFolder
  • JFilesystemWrapperPath
  • JFilterInput
  • JFilterOutput
  • JFilterWrapperOutput
  • JFolder
  • JForm
  • JFormField
  • JFormFieldAccessLevel
  • JFormFieldAliastag
  • JFormFieldAuthor
  • JFormFieldCacheHandler
  • JFormFieldCalendar
  • JFormFieldCaptcha
  • JFormFieldCategory
  • JFormFieldCheckbox
  • JFormFieldCheckboxes
  • JFormFieldChromeStyle
  • JFormFieldColor
  • JFormFieldCombo
  • JFormFieldComponentlayout
  • JFormFieldComponents
  • JFormFieldContenthistory
  • JFormFieldContentlanguage
  • JFormFieldContenttype
  • JFormFieldDatabaseConnection
  • JFormFieldEditor
  • JFormFieldEMail
  • JFormFieldFile
  • JFormFieldFileList
  • JFormFieldFolderList
  • JFormFieldFrontend_Language
  • JFormFieldGroupedList
  • JFormFieldHeadertag
  • JFormFieldHelpsite
  • JFormFieldHidden
  • JFormFieldImageList
  • JFormFieldInteger
  • JFormFieldLanguage
  • JFormFieldLastvisitDateRange
  • JFormFieldLimitbox
  • JFormFieldList
  • JFormFieldMedia
  • JFormFieldMenu
  • JFormFieldMenuitem
  • JFormFieldMeter
  • JFormFieldModulelayout
  • JFormFieldModuleOrder
  • JFormFieldModulePosition
  • JFormFieldModuletag
  • JFormFieldNote
  • JFormFieldNumber
  • JFormFieldOrdering
  • JFormFieldPassword
  • JFormFieldPlugin_Status
  • JFormFieldPlugins
  • JFormFieldPredefinedList
  • JFormFieldRadio
  • JFormFieldRange
  • JFormFieldRegistrationDateRange
  • JFormFieldRepeatable
  • JFormFieldRules
  • JFormFieldSessionHandler
  • JFormFieldSpacer
  • JFormFieldSQL
  • JFormFieldStatus
  • JFormFieldSubform
  • JFormFieldTag
  • JFormFieldTel
  • JFormFieldTemplatestyle
  • JFormFieldText
  • JFormFieldTextarea
  • JFormFieldTimezone
  • JFormFieldUrl
  • JFormFieldUser
  • JFormFieldUserActive
  • JFormFieldUsergroup
  • JFormFieldUserGroupList
  • JFormFieldUserState
  • JFormHelper
  • JFormRule
  • JFormRuleBoolean
  • JFormRuleCalendar
  • JFormRuleCaptcha
  • JFormRuleColor
  • JFormRuleEmail
  • JFormRuleEquals
  • JFormRuleNotequals
  • JFormRuleNumber
  • JFormRuleOptions
  • JFormRulePassword
  • JFormRuleRules
  • JFormRuleTel
  • JFormRuleUrl
  • JFormRuleUsername
  • JFormWrapperHelper
  • JFTP
  • JGithub
  • JGithubAccount
  • JGithubCommits
  • JGithubForks
  • JGithubHooks
  • JGithubHttp
  • JGithubMeta
  • JGithubMilestones
  • JGithubObject
  • JGithubPackage
  • JGithubPackageActivity
  • JGithubPackageActivityEvents
  • JGithubPackageActivityNotifications
  • JGithubPackageActivityStarring
  • JGithubPackageActivityWatching
  • JGithubPackageAuthorization
  • JGithubPackageData
  • JGithubPackageDataBlobs
  • JGithubPackageDataCommits
  • JGithubPackageDataRefs
  • JGithubPackageDataTags
  • JGithubPackageDataTrees
  • JGithubPackageGists
  • JGithubPackageGistsComments
  • JGithubPackageGitignore
  • JGithubPackageIssues
  • JGithubPackageIssuesAssignees
  • JGithubPackageIssuesComments
  • JGithubPackageIssuesEvents
  • JGithubPackageIssuesLabels
  • JGithubPackageIssuesMilestones
  • JGithubPackageMarkdown
  • JGithubPackageOrgs
  • JGithubPackageOrgsMembers
  • JGithubPackageOrgsTeams
  • JGithubPackagePulls
  • JGithubPackagePullsComments
  • JGithubPackageRepositories
  • JGithubPackageRepositoriesCollaborators
  • JGithubPackageRepositoriesComments
  • JGithubPackageRepositoriesCommits
  • JGithubPackageRepositoriesContents
  • JGithubPackageRepositoriesDownloads
  • JGithubPackageRepositoriesForks
  • JGithubPackageRepositoriesHooks
  • JGithubPackageRepositoriesKeys
  • JGithubPackageRepositoriesMerging
  • JGithubPackageRepositoriesStatistics
  • JGithubPackageRepositoriesStatuses
  • JGithubPackageSearch
  • JGithubPackageUsers
  • JGithubPackageUsersEmails
  • JGithubPackageUsersFollowers
  • JGithubPackageUsersKeys
  • JGithubRefs
  • JGithubStatuses
  • JGoogle
  • JGoogleAuth
  • JGoogleAuthOauth2
  • JGoogleData
  • JGoogleDataAdsense
  • JGoogleDataCalendar
  • JGoogleDataPicasa
  • JGoogleDataPicasaAlbum
  • JGoogleDataPicasaPhoto
  • JGoogleDataPlus
  • JGoogleDataPlusActivities
  • JGoogleDataPlusComments
  • JGoogleDataPlusPeople
  • JGoogleEmbed
  • JGoogleEmbedAnalytics
  • JGoogleEmbedMaps
  • JGrid
  • JHelp
  • JHelper
  • JHelperContent
  • JHelperContenthistory
  • JHelperMedia
  • JHelperRoute
  • JHelperTags
  • JHelperUsergroups
  • JHtml
  • JHtmlAccess
  • JHtmlActionsDropdown
  • JHtmlBatch
  • JHtmlBehavior
  • JHtmlBootstrap
  • JHtmlCategory
  • JHtmlContent
  • JHtmlContentLanguage
  • JHtmlDate
  • JHtmlDebug
  • JHtmlDropdown
  • JHtmlEmail
  • JHtmlForm
  • JHtmlFormbehavior
  • JHtmlGrid
  • JHtmlIcons
  • JHtmlJGrid
  • JHtmlJquery
  • JHtmlLinks
  • JHtmlList
  • JHtmlMenu
  • JHtmlNumber
  • JHtmlRules
  • JHtmlSearchtools
  • JHtmlSelect
  • JHtmlSidebar
  • JHtmlSliders
  • JHtmlSortablelist
  • JHtmlString
  • JHtmlTabs
  • JHtmlTag
  • JHtmlTel
  • JHtmlUser
  • JHttp
  • JHttpFactory
  • JHttpResponse
  • JHttpTransportCurl
  • JHttpTransportSocket
  • JHttpTransportStream
  • JHttpWrapperFactory
  • JImage
  • JImageFilter
  • JImageFilterBackgroundfill
  • JImageFilterBrightness
  • JImageFilterContrast
  • JImageFilterEdgedetect
  • JImageFilterEmboss
  • JImageFilterGrayscale
  • JImageFilterNegate
  • JImageFilterSketchy
  • JImageFilterSmooth
  • JInput
  • JInputCli
  • JInputCookie
  • JInputFiles
  • JInputJSON
  • JInstaller
  • JInstallerAdapter
  • JInstallerAdapterComponent
  • JInstallerAdapterFile
  • JInstallerAdapterLanguage
  • JInstallerAdapterLibrary
  • JInstallerAdapterModule
  • JInstallerAdapterPackage
  • JInstallerAdapterPlugin
  • JInstallerAdapterTemplate
  • JInstallerComponent
  • JInstallerExtension
  • JInstallerFile
  • JInstallerHelper
  • JInstallerLanguage
  • JInstallerLibrary
  • JInstallerManifest
  • JInstallerManifestLibrary
  • JInstallerManifestPackage
  • JInstallerModule
  • JInstallerPackage
  • JInstallerPlugin
  • JInstallerScript
  • JInstallerTemplate
  • JKeychain
  • JLanguage
  • JLanguageAssociations
  • JLanguageHelper
  • JLanguageMultilang
  • JLanguageStemmer
  • JLanguageStemmerPorteren
  • JLanguageTransliterate
  • JLanguageWrapperHelper
  • JLanguageWrapperText
  • JLanguageWrapperTransliterate
  • JLayoutBase
  • JLayoutFile
  • JLayoutHelper
  • JLDAP
  • JLess
  • JLessFormatterJoomla
  • JLibraryHelper
  • JLinkedin
  • JLinkedinCommunications
  • JLinkedinCompanies
  • JLinkedinGroups
  • JLinkedinJobs
  • JLinkedinOauth
  • JLinkedinObject
  • JLinkedinPeople
  • JLinkedinStream
  • JLoader
  • JLog
  • JLogEntry
  • JLogger
  • JLogLogger
  • JLogLoggerCallback
  • JLogLoggerDatabase
  • JLogLoggerEcho
  • JLogLoggerFormattedtext
  • JLogLoggerMessagequeue
  • JLogLoggerSyslog
  • JLogLoggerW3c
  • JMail
  • JMailHelper
  • JMailWrapperHelper
  • JMediawiki
  • JMediawikiCategories
  • JMediawikiHttp
  • JMediawikiImages
  • JMediawikiLinks
  • JMediawikiObject
  • JMediawikiPages
  • JMediawikiSearch
  • JMediawikiSites
  • JMediawikiUsers
  • JMenu
  • JMenuAdministrator
  • JMenuItem
  • JMenuSite
  • JMicrodata
  • JModelAdmin
  • JModelBase
  • JModelDatabase
  • JModelForm
  • JModelItem
  • JModelLegacy
  • JModelList
  • JModuleHelper
  • JNode
  • JOAuth1Client
  • JOAuth2Client
  • JObject
  • JObservable
  • JObserver
  • JObserverMapper
  • JObserverUpdater
  • JObserverWrapperMapper
  • JOpenSearchImage
  • JOpenSearchUrl
  • JOpenstreetmap
  • JOpenstreetmapChangesets
  • JOpenstreetmapElements
  • JOpenstreetmapGps
  • JOpenstreetmapInfo
  • JOpenstreetmapOauth
  • JOpenstreetmapObject
  • JOpenstreetmapUser
  • JPagination
  • JPaginationObject
  • JPath
  • JPathway
  • JPathwaySite
  • JPlatform
  • JPlugin
  • JPluginHelper
  • JProfiler
  • JRequest
  • JResponse
  • JResponseJson
  • JRoute
  • JRouter
  • JRouterAdministrator
  • JRouterSite
  • JRouteWrapperRoute
  • JRule
  • JRules
  • JSchemaChangeitem
  • JSchemaChangeitemMysql
  • JSchemaChangeitemPostgresql
  • JSchemaChangeitemSqlsrv
  • JSchemaChangeset
  • JSearchHelper
  • JSession
  • JSessionHandlerJoomla
  • JSessionHandlerNative
  • JSessionStorage
  • JSessionStorageApc
  • JSessionStorageDatabase
  • JSessionStorageMemcache
  • JSessionStorageMemcached
  • JSessionStorageNone
  • JSessionStorageWincache
  • JSessionStorageXcache
  • JSimplecrypt
  • JSimplepieFactory
  • JStream
  • JStreamString
  • JString
  • JStringController
  • JStringPunycode
  • JStringWrapperNormalise
  • JStringWrapperPunycode
  • JTable
  • JTableAsset
  • JTableCategory
  • JTableContent
  • JTableContenthistory
  • JTableContenttype
  • JTableCorecontent
  • JTableExtension
  • JTableInterface
  • JTableLanguage
  • JTableMenu
  • JTableMenuType
  • JTableModule
  • JTableNested
  • JTableObserver
  • JTableObserverContenthistory
  • JTableObserverTags
  • JTableSession
  • JTableUcm
  • JTableUpdate
  • JTableUpdatesite
  • JTableUser
  • JTableUsergroup
  • JTableViewlevel
  • JText
  • JToolbar
  • JToolbarButton
  • JToolbarButtonConfirm
  • JToolbarButtonCustom
  • JToolbarButtonHelp
  • JToolbarButtonLink
  • JToolbarButtonPopup
  • JToolbarButtonSeparator
  • JToolbarButtonSlider
  • JToolbarButtonStandard
  • JTree
  • JTwitter
  • JTwitterBlock
  • JTwitterDirectmessages
  • JTwitterFavorites
  • JTwitterFriends
  • JTwitterHelp
  • JTwitterLists
  • JTwitterOAuth
  • JTwitterObject
  • JTwitterPlaces
  • JTwitterProfile
  • JTwittersearch
  • JTwitterStatuses
  • JTwitterTrends
  • JTwitterUsers
  • JUcmBase
  • JUcmContent
  • JUcmType
  • JUpdate
  • JUpdateAdapter
  • JUpdater
  • JUpdaterCollection
  • JUpdaterExtension
  • JUri
  • JUser
  • JUserHelper
  • JUserWrapperHelper
  • JUtility
  • JVersion
  • JViewBase
  • JViewCategories
  • JViewCategory
  • JViewCategoryfeed
  • JViewHtml
  • JViewLegacy
  • JWeb
  • JWebClient
  • JXMLElement
  • lessc
  • lessc_formatter_classic
  • lessc_formatter_compressed
  • lessc_formatter_lessjs
  • lessc_parser
  • lessify
  • Net_IDNA_php4
  • nodecounter
  • ntlm_sasl_client_class
  • PHPMailer
  • PHPMailerOAuth
  • PHPMailerOAuthGoogle
  • POP3
  • SimplePie
  • SimplePie_Author
  • SimplePie_Autoloader
  • SimplePie_Cache
  • SimplePie_Cache_DB
  • SimplePie_Cache_File
  • SimplePie_Cache_Memcache
  • SimplePie_Cache_MySQL
  • SimplePie_Caption
  • SimplePie_Category
  • SimplePie_Content_Type_Sniffer
  • SimplePie_Copyright
  • SimplePie_Core
  • SimplePie_Credit
  • SimplePie_Decode_HTML_Entities
  • SimplePie_Enclosure
  • SimplePie_File
  • SimplePie_gzdecode
  • SimplePie_HTTP_Parser
  • SimplePie_IRI
  • SimplePie_Item
  • SimplePie_Locator
  • SimplePie_Misc
  • SimplePie_Net_IPv6
  • SimplePie_Parse_Date
  • SimplePie_Parser
  • SimplePie_Rating
  • SimplePie_Registry
  • SimplePie_Restriction
  • SimplePie_Sanitize
  • SimplePie_Source
  • SimplePie_XML_Declaration_Parser
  • SMTP
  • tagparse
  • TypeError

Interfaces

  • FOFConfigDomainInterface
  • FOFDatabaseInterface
  • FOFDatabaseQueryLimitable
  • FOFDatabaseQueryPreparable
  • FOFDownloadInterface
  • FOFEncryptAesInterface
  • FOFEncryptRandvalinterface
  • FOFFormField
  • FOFHalRenderInterface
  • FOFPlatformFilesystemInterface
  • FOFPlatformInterface
  • JArchiveExtractable
  • JAssociationExtensionInterface
  • JCacheException
  • JComponentRouterInterface
  • JComponentRouterRulesInterface
  • JController
  • JCryptCipher
  • JCryptPassword
  • JFeedParserNamespace
  • JHttpTransport
  • JLayout
  • JModel
  • JObservableInterface
  • JObserverInterface
  • JObserverUpdaterInterface
  • JSessionHandlerInterface
  • JsonSerializable
  • JUcm
  • JView
  • SimplePie_Cache_Base

Exceptions

  • Error
  • JAccessExceptionNotallowed
  • JCacheExceptionConnecting
  • JCacheExceptionUnsupported
  • JComponentExceptionMissing
  • JDatabaseException
  • JDatabaseExceptionConnecting
  • JDatabaseExceptionExecuting
  • JDatabaseExceptionUnsupported
  • JException
  • JSessionExceptionUnsupported
  • LogException
  • phpmailerException
  • SimplePie_Exception

Constants

  • JERROR_CALLBACK_NOT_CALLABLE
  • JERROR_ILLEGAL_MODE
  • JERROR_ILLEGAL_OPTIONS
  • JREQUEST_ALLOWHTML
  • JREQUEST_ALLOWRAW
  • JREQUEST_NOTRIM
  • JROUTER_MODE_RAW
  • JROUTER_MODE_SEF

Functions

  • __autoload
  • array_column
  • boolval
  • composerRequire205c915b9c7d3e718e7c95793ee67ffe
  • gzopen
  • gzseek
  • gztell
  • hash_equals
  • hash_pbkdf2
  • HTMLFilter
  • jexit
  • jimport
  • json_last_error_msg
  • ldap_escape
  • password_get_info
  • password_hash
  • password_needs_rehash
  • password_verify
  • PHPMailerAutoload
  • random_bytes
  • random_int
  • RandomCompat_intval
  • RandomCompat_strlen
  • RandomCompat_substr
  • tln_body2div
  • tln_casenormalize
  • tln_deent
  • tln_defang
  • tln_findnxreg
  • tln_findnxstr
  • tln_fixatts
  • tln_fixstyle
  • tln_fixurl
  • tln_getnxtag
  • tln_sanitize
  • tln_skipspace
  • tln_tagprint
  • tln_unspace
  • utf8_accents_to_ascii
  • utf8_bad_explain
  • utf8_bad_find
  • utf8_bad_findall
  • utf8_bad_identify
  • utf8_bad_replace
  • utf8_bad_strip
  • utf8_byte_position
  • utf8_compliant
  • utf8_from_unicode
  • utf8_ireplace
  • utf8_is_ascii
  • utf8_is_ascii_ctrl
  • utf8_is_valid
  • utf8_is_word_chars
  • utf8_locate_current_chr
  • utf8_locate_next_chr
  • utf8_ltrim
  • utf8_ord
  • utf8_rtrim
  • utf8_specials_pattern
  • utf8_str_pad
  • utf8_str_split
  • utf8_strcasecmp
  • utf8_strcspn
  • utf8_strip_ascii_ctrl
  • utf8_strip_non_ascii
  • utf8_strip_non_ascii_ctrl
  • utf8_strip_specials
  • utf8_stristr
  • utf8_strlen
  • utf8_strpos
  • utf8_strrev
  • utf8_strrpos
  • utf8_strspn
  • utf8_strtolower
  • utf8_strtoupper
  • utf8_substr
  • utf8_substr_replace
  • utf8_to_unicode
  • utf8_trim
  • utf8_ucfirst
  • utf8_ucwords
  • utf8_ucwords_callback
   1 <?php
   2 /**
   3  * @package     FrameworkOnFramework
   4  * @subpackage  table
   5  * @copyright   Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved.
   6  * @license     GNU General Public License version 2 or later; see LICENSE.txt
   7  */
   8 // Protect from unauthorized access
   9 defined('FOF_INCLUDED') or die;
  10 
  11 /**
  12  * Normally this shouldn't be required. Some PHP versions, however, seem to
  13  * require this. Why? No idea whatsoever. If I remove it, FOF crashes on some
  14  * hosts. Same PHP version on another host and no problem occurs. Any takers?
  15  */
  16 if (class_exists('FOFTable', false))
  17 {
  18     return;
  19 }
  20 
  21 if (!interface_exists('JTableInterface', true))
  22 {
  23     interface JTableInterface {}
  24 }
  25 
  26 /**
  27  * FrameworkOnFramework Table class. The Table is one part controller, one part
  28  * model and one part data adapter. It's supposed to handle operations for single
  29  * records.
  30  *
  31  * @package  FrameworkOnFramework
  32  * @since    1.0
  33  */
  34 class FOFTable extends FOFUtilsObject implements JTableInterface
  35 {
  36     /**
  37      * Cache array for instances
  38      *
  39      * @var    array
  40      */
  41     protected static $instances = array();
  42 
  43     /**
  44      * Include paths for searching for FOFTable classes.
  45      *
  46      * @var    array
  47      */
  48     protected static $_includePaths = array();
  49 
  50     /**
  51      * The configuration parameters array
  52      *
  53      * @var  array
  54      */
  55     protected $config = array();
  56 
  57     /**
  58      * Name of the database table to model.
  59      *
  60      * @var    string
  61      */
  62     protected $_tbl = '';
  63 
  64     /**
  65      * Name of the primary key field in the table.
  66      *
  67      * @var    string
  68      */
  69     protected $_tbl_key = '';
  70 
  71     /**
  72      * FOFDatabaseDriver object.
  73      *
  74      * @var    FOFDatabaseDriver
  75      */
  76     protected $_db;
  77 
  78     /**
  79      * Should rows be tracked as ACL assets?
  80      *
  81      * @var    boolean
  82      */
  83     protected $_trackAssets = false;
  84 
  85     /**
  86      * Does the resource support joomla tags?
  87      *
  88      * @var    boolean
  89      */
  90     protected $_has_tags = false;
  91 
  92     /**
  93      * The rules associated with this record.
  94      *
  95      * @var    JAccessRules  A JAccessRules object.
  96      */
  97     protected $_rules;
  98 
  99     /**
 100      * Indicator that the tables have been locked.
 101      *
 102      * @var    boolean
 103      */
 104     protected $_locked = false;
 105 
 106     /**
 107      * If this is set to true, it triggers automatically plugin events for
 108      * table actions
 109      *
 110      * @var    boolean
 111      */
 112     protected $_trigger_events = false;
 113 
 114     /**
 115      * Table alias used in queries
 116      *
 117      * @var    string
 118      */
 119     protected $_tableAlias = false;
 120 
 121     /**
 122      * Array with alias for "special" columns such as ordering, hits etc etc
 123      *
 124      * @var    array
 125      */
 126     protected $_columnAlias = array();
 127 
 128     /**
 129      * If set to true, it enabled automatic checks on fields based on columns properties
 130      *
 131      * @var    boolean
 132      */
 133     protected $_autoChecks = false;
 134 
 135     /**
 136      * Array with fields that should be skipped by automatic checks
 137      *
 138      * @var    array
 139      */
 140     protected $_skipChecks = array();
 141 
 142     /**
 143      * Does the table actually exist? We need that to avoid PHP notices on
 144      * table-less views.
 145      *
 146      * @var    boolean
 147      */
 148     protected $_tableExists = true;
 149 
 150     /**
 151      * The asset key for items in this table. It's usually something in the
 152      * com_example.viewname format. They asset name will be this key appended
 153      * with the item's ID, e.g. com_example.viewname.123
 154      *
 155      * @var    string
 156      */
 157     protected $_assetKey = '';
 158 
 159     /**
 160      * The input data
 161      *
 162      * @var    FOFInput
 163      */
 164     protected $input = null;
 165 
 166     /**
 167      * Extended query including joins with other tables
 168      *
 169      * @var    FOFDatabaseQuery
 170      */
 171     protected $_queryJoin = null;
 172 
 173     /**
 174      * The prefix for the table class
 175      *
 176      * @var     string
 177      */
 178     protected $_tablePrefix = '';
 179 
 180     /**
 181      * The known fields for this table
 182      *
 183      * @var     array
 184      */
 185     protected $knownFields = array();
 186 
 187     /**
 188      * A list of table fields, keyed per table
 189      *
 190      * @var array
 191      */
 192     protected static $tableFieldCache = array();
 193 
 194     /**
 195      * A list of tables in the database
 196      *
 197      * @var array
 198      */
 199     protected static $tableCache = array();
 200 
 201     /**
 202      * An instance of FOFConfigProvider to provision configuration overrides
 203      *
 204      * @var    FOFConfigProvider
 205      */
 206     protected $configProvider = null;
 207 
 208     /**
 209      * FOFTableDispatcherBehavior for dealing with extra behaviors
 210      *
 211      * @var    FOFTableDispatcherBehavior
 212      */
 213     protected $tableDispatcher = null;
 214 
 215     /**
 216      * List of default behaviors to apply to the table
 217      *
 218      * @var    array
 219      */
 220     protected $default_behaviors = array('tags', 'assets');
 221 
 222     /**
 223      * The relations object of the table. It's lazy-loaded by getRelations().
 224      *
 225      * @var   FOFTableRelations
 226      */
 227     protected $_relations = null;
 228 
 229     /**
 230      * The configuration provider's key for this table, e.g. foobar.tables.bar for the #__foobar_bars table. This is set
 231      * automatically by the constructor
 232      *
 233      * @var  string
 234      */
 235     protected $_configProviderKey = '';
 236 
 237     /**
 238      * The content type of the table. Required if using tags or content history behaviour
 239      *
 240      * @var  string
 241      */
 242     protected $contentType = null;
 243 
 244     /**
 245      * Returns a static object instance of a particular table type
 246      *
 247      * @param   string  $type    The table name
 248      * @param   string  $prefix  The prefix of the table class
 249      * @param   array   $config  Optional configuration variables
 250      *
 251      * @return FOFTable
 252      */
 253     public static function getInstance($type, $prefix = 'JTable', $config = array())
 254     {
 255         return self::getAnInstance($type, $prefix, $config);
 256     }
 257 
 258     /**
 259      * Returns a static object instance of a particular table type
 260      *
 261      * @param   string  $type    The table name
 262      * @param   string  $prefix  The prefix of the table class
 263      * @param   array   $config  Optional configuration variables
 264      *
 265      * @return FOFTable
 266      */
 267     public static function &getAnInstance($type = null, $prefix = 'JTable', $config = array())
 268     {
 269         // Make sure $config is an array
 270         if (is_object($config))
 271         {
 272             $config = (array) $config;
 273         }
 274         elseif (!is_array($config))
 275         {
 276             $config = array();
 277         }
 278 
 279         // Guess the component name
 280         if (!array_key_exists('input', $config))
 281         {
 282             $config['input'] = new FOFInput;
 283         }
 284 
 285         if ($config['input'] instanceof FOFInput)
 286         {
 287             $tmpInput = $config['input'];
 288         }
 289         else
 290         {
 291             $tmpInput = new FOFInput($config['input']);
 292         }
 293 
 294         $option = $tmpInput->getCmd('option', '');
 295         $tmpInput->set('option', $option);
 296         $config['input'] = $tmpInput;
 297 
 298         if (!in_array($prefix, array('Table', 'JTable')))
 299         {
 300             preg_match('/(.*)Table$/', $prefix, $m);
 301             $option = 'com_' . strtolower($m[1]);
 302         }
 303 
 304         if (array_key_exists('option', $config))
 305         {
 306             $option = $config['option'];
 307         }
 308 
 309         $config['option'] = $option;
 310 
 311         if (!array_key_exists('view', $config))
 312         {
 313             $config['view'] = $config['input']->getCmd('view', 'cpanel');
 314         }
 315 
 316         if (is_null($type))
 317         {
 318             if ($prefix == 'JTable')
 319             {
 320                 $prefix = 'Table';
 321             }
 322 
 323             $type = $config['view'];
 324         }
 325 
 326         $type       = preg_replace('/[^A-Z0-9_\.-]/i', '', $type);
 327         $tableClass = $prefix . ucfirst($type);
 328 
 329         $config['_table_type'] = $type;
 330         $config['_table_class'] = $tableClass;
 331 
 332         $configProvider = new FOFConfigProvider;
 333         $configProviderKey = $option . '.views.' . FOFInflector::singularize($type) . '.config.';
 334 
 335         if (!array_key_exists($tableClass, self::$instances))
 336         {
 337             if (!class_exists($tableClass))
 338             {
 339                 $componentPaths = FOFPlatform::getInstance()->getComponentBaseDirs($config['option']);
 340 
 341                 $searchPaths = array(
 342                     $componentPaths['main'] . '/tables',
 343                     $componentPaths['admin'] . '/tables'
 344                 );
 345 
 346                 if (array_key_exists('tablepath', $config))
 347                 {
 348                     array_unshift($searchPaths, $config['tablepath']);
 349                 }
 350 
 351                 $altPath = $configProvider->get($configProviderKey . 'table_path', null);
 352 
 353                 if ($altPath)
 354                 {
 355                     array_unshift($searchPaths, $componentPaths['admin'] . '/' . $altPath);
 356                 }
 357 
 358                 $filesystem = FOFPlatform::getInstance()->getIntegrationObject('filesystem');
 359 
 360                 $path = $filesystem->pathFind(
 361                     $searchPaths, strtolower($type) . '.php'
 362                 );
 363 
 364                 if ($path)
 365                 {
 366                     require_once $path;
 367                 }
 368             }
 369 
 370             if (!class_exists($tableClass))
 371             {
 372                 $tableClass = 'FOFTable';
 373             }
 374 
 375             $component = str_replace('com_', '', $config['option']);
 376             $tbl_common = $component . '_';
 377 
 378             if (!array_key_exists('tbl', $config))
 379             {
 380                 $config['tbl'] = strtolower('#__' . $tbl_common . strtolower(FOFInflector::pluralize($type)));
 381             }
 382 
 383             $altTbl = $configProvider->get($configProviderKey . 'tbl', null);
 384 
 385             if ($altTbl)
 386             {
 387                 $config['tbl'] = $altTbl;
 388             }
 389 
 390             if (!array_key_exists('tbl_key', $config))
 391             {
 392                 $keyName           = FOFInflector::singularize($type);
 393                 $config['tbl_key'] = strtolower($tbl_common . $keyName . '_id');
 394             }
 395 
 396             $altTblKey = $configProvider->get($configProviderKey . 'tbl_key', null);
 397 
 398             if ($altTblKey)
 399             {
 400                 $config['tbl_key'] = $altTblKey;
 401             }
 402 
 403             if (!array_key_exists('db', $config))
 404             {
 405                 $config['db'] = FOFPlatform::getInstance()->getDbo();
 406             }
 407 
 408             // Assign the correct table alias
 409             if (array_key_exists('table_alias', $config))
 410             {
 411                 $table_alias = $config['table_alias'];
 412             }
 413             else
 414             {
 415                 $configProviderTableAliasKey = $option . '.tables.' . FOFInflector::singularize($type) . '.tablealias';
 416                 $table_alias = $configProvider->get($configProviderTableAliasKey, false );
 417             }
 418 
 419             // Can we use the FOF cache?
 420             if (!array_key_exists('use_table_cache', $config))
 421             {
 422                 $config['use_table_cache'] = FOFPlatform::getInstance()->isGlobalFOFCacheEnabled();
 423             }
 424 
 425             $alt_use_table_cache = $configProvider->get($configProviderKey . 'use_table_cache', null);
 426 
 427             if (!is_null($alt_use_table_cache))
 428             {
 429                 $config['use_table_cache'] = $alt_use_table_cache;
 430             }
 431 
 432             // Create a new table instance
 433             $instance = new $tableClass($config['tbl'], $config['tbl_key'], $config['db'], $config);
 434             $instance->setInput($tmpInput);
 435             $instance->setTablePrefix($prefix);
 436             $instance->setTableAlias($table_alias);
 437 
 438             // Determine and set the asset key for this table
 439             $assetKey = 'com_' . $component . '.' . strtolower(FOFInflector::singularize($type));
 440             $assetKey = $configProvider->get($configProviderKey . 'asset_key', $assetKey);
 441             $instance->setAssetKey($assetKey);
 442 
 443             if (array_key_exists('trigger_events', $config))
 444             {
 445                 $instance->setTriggerEvents($config['trigger_events']);
 446             }
 447 
 448             if (version_compare(JVERSION, '3.1', 'ge'))
 449             {
 450                 if (array_key_exists('has_tags', $config))
 451                 {
 452                     $instance->setHasTags($config['has_tags']);
 453                 }
 454 
 455                 $altHasTags = $configProvider->get($configProviderKey . 'has_tags', null);
 456 
 457                 if ($altHasTags)
 458                 {
 459                     $instance->setHasTags($altHasTags);
 460                 }
 461             }
 462             else
 463             {
 464                 $instance->setHasTags(false);
 465             }
 466 
 467             $configProviderFieldmapKey = $option . '.tables.' . FOFInflector::singularize($type) . '.field';
 468             $aliases = $configProvider->get($configProviderFieldmapKey, $instance->_columnAlias);
 469             $instance->_columnAlias = array_merge($instance->_columnAlias, $aliases);
 470 
 471             self::$instances[$tableClass] = $instance;
 472         }
 473 
 474         return self::$instances[$tableClass];
 475     }
 476 
 477     /**
 478      * Force an instance inside class cache. Setting arguments to null nukes all or part of the cache
 479      *
 480      * @param    string|null       $key        TableClass to replace. Set it to null to nuke the entire cache
 481      * @param    FOFTable|null     $instance   Instance to replace. Set it to null to nuke $key instances
 482      *
 483      * @return   bool              Did I correctly switch the instance?
 484      */
 485     public static function forceInstance($key = null, $instance = null)
 486     {
 487         if(is_null($key))
 488         {
 489             self::$instances = array();
 490 
 491             return true;
 492         }
 493         elseif($key && isset(self::$instances[$key]))
 494         {
 495             // I'm forcing an instance, but it's not a FOFTable, abort! abort!
 496             if(!$instance || ($instance && $instance instanceof FOFTable))
 497             {
 498                 self::$instances[$key] = $instance;
 499 
 500                 return true;
 501             }
 502         }
 503 
 504         return false;
 505     }
 506 
 507     /**
 508      * Class Constructor.
 509      *
 510      * @param   string           $table   Name of the database table to model.
 511      * @param   string           $key     Name of the primary key field in the table.
 512      * @param   FOFDatabaseDriver  &$db     Database driver
 513      * @param   array            $config  The configuration parameters array
 514      */
 515     public function __construct($table, $key, &$db, $config = array())
 516     {
 517         $this->_tbl     = $table;
 518         $this->_tbl_key = $key;
 519         $this->_db      = $db;
 520 
 521         // Make sure the use FOF cache information is in the config
 522         if (!array_key_exists('use_table_cache', $config))
 523         {
 524             $config['use_table_cache'] = FOFPlatform::getInstance()->isGlobalFOFCacheEnabled();
 525         }
 526         $this->config   = $config;
 527 
 528         // Load the configuration provider
 529         $this->configProvider = new FOFConfigProvider;
 530 
 531         // Load the behavior dispatcher
 532         $this->tableDispatcher = new FOFTableDispatcherBehavior;
 533 
 534         // Initialise the table properties.
 535 
 536         if ($fields = $this->getTableFields())
 537         {
 538             // Do I have anything joined?
 539             $j_fields = $this->getQueryJoinFields();
 540 
 541             if ($j_fields)
 542             {
 543                 $fields = array_merge($fields, $j_fields);
 544             }
 545 
 546             $this->setKnownFields(array_keys($fields), true);
 547             $this->reset();
 548         }
 549         else
 550         {
 551             $this->_tableExists = false;
 552         }
 553 
 554         // Get the input
 555         if (array_key_exists('input', $config))
 556         {
 557             if ($config['input'] instanceof FOFInput)
 558             {
 559                 $this->input = $config['input'];
 560             }
 561             else
 562             {
 563                 $this->input = new FOFInput($config['input']);
 564             }
 565         }
 566         else
 567         {
 568             $this->input = new FOFInput;
 569         }
 570 
 571         // Set the $name/$_name variable
 572         $component = $this->input->getCmd('option', 'com_foobar');
 573 
 574         if (array_key_exists('option', $config))
 575         {
 576             $component = $config['option'];
 577         }
 578 
 579         $this->input->set('option', $component);
 580 
 581         // Apply table behaviors
 582         $type = explode("_", $this->_tbl);
 583         $type = $type[count($type) - 1];
 584 
 585         $this->_configProviderKey = $component . '.tables.' . FOFInflector::singularize($type);
 586 
 587         $configKey = $this->_configProviderKey . '.behaviors';
 588 
 589         if (isset($config['behaviors']))
 590         {
 591             $behaviors = (array) $config['behaviors'];
 592         }
 593         elseif ($behaviors = $this->configProvider->get($configKey, null))
 594         {
 595             $behaviors = explode(',', $behaviors);
 596         }
 597         else
 598         {
 599             $behaviors = $this->default_behaviors;
 600         }
 601 
 602         if (is_array($behaviors) && count($behaviors))
 603         {
 604             foreach ($behaviors as $behavior)
 605             {
 606                 $this->addBehavior($behavior);
 607             }
 608         }
 609 
 610         // If we are tracking assets, make sure an access field exists and initially set the default.
 611         $asset_id_field = $this->getColumnAlias('asset_id');
 612         $access_field   = $this->getColumnAlias('access');
 613 
 614         if (in_array($asset_id_field, $this->getKnownFields()))
 615         {
 616             JLoader::import('joomla.access.rules');
 617             $this->_trackAssets = true;
 618         }
 619 
 620         // If the access property exists, set the default.
 621         if (in_array($access_field, $this->getKnownFields()))
 622         {
 623             $this->$access_field = (int) FOFPlatform::getInstance()->getConfig()->get('access');
 624         }
 625 
 626         $this->config = $config;
 627     }
 628 
 629     /**
 630      * Replace the entire known fields array
 631      *
 632      * @param   array    $fields      A simple array of known field names
 633      * @param   boolean  $initialise  Should we initialise variables to null?
 634      *
 635      * @return  void
 636      */
 637     public function setKnownFields($fields, $initialise = false)
 638     {
 639         $this->knownFields = $fields;
 640 
 641         if ($initialise)
 642         {
 643             foreach ($this->knownFields as $field)
 644             {
 645                 $this->$field = null;
 646             }
 647         }
 648     }
 649 
 650     /**
 651      * Get the known fields array
 652      *
 653      * @return  array
 654      */
 655     public function getKnownFields()
 656     {
 657         return $this->knownFields;
 658     }
 659 
 660     /**
 661      * Does the specified field exist?
 662      *
 663      * @param   string  $fieldName  The field name to search (it's OK to use aliases)
 664      *
 665      * @return  bool
 666      */
 667     public function hasField($fieldName)
 668     {
 669         $search = $this->getColumnAlias($fieldName);
 670 
 671         return in_array($search, $this->knownFields);
 672     }
 673 
 674     /**
 675      * Add a field to the known fields array
 676      *
 677      * @param   string   $field       The name of the field to add
 678      * @param   boolean  $initialise  Should we initialise the variable to null?
 679      *
 680      * @return  void
 681      */
 682     public function addKnownField($field, $initialise = false)
 683     {
 684         if (!in_array($field, $this->knownFields))
 685         {
 686             $this->knownFields[] = $field;
 687 
 688             if ($initialise)
 689             {
 690                 $this->$field = null;
 691             }
 692         }
 693     }
 694 
 695     /**
 696      * Remove a field from the known fields array
 697      *
 698      * @param   string  $field  The name of the field to remove
 699      *
 700      * @return  void
 701      */
 702     public function removeKnownField($field)
 703     {
 704         if (in_array($field, $this->knownFields))
 705         {
 706             $pos = array_search($field, $this->knownFields);
 707             unset($this->knownFields[$pos]);
 708         }
 709     }
 710 
 711     /**
 712      * Adds a behavior to the table
 713      *
 714      * @param   string  $name    The name of the behavior
 715      * @param   array   $config  Optional Behavior configuration
 716      *
 717      * @return  boolean
 718      */
 719     public function addBehavior($name, $config = array())
 720     {
 721         // First look for ComponentnameTableViewnameBehaviorName (e.g. FoobarTableItemsBehaviorTags)
 722         if (isset($this->config['option']))
 723         {
 724             $option_name = str_replace('com_', '', $this->config['option']);
 725             $behaviorClass = $this->config['_table_class'] . 'Behavior' . ucfirst(strtolower($name));
 726 
 727             if (class_exists($behaviorClass))
 728             {
 729                 $behavior = new $behaviorClass($this->tableDispatcher, $config);
 730 
 731                 return true;
 732             }
 733 
 734             // Then look for ComponentnameTableBehaviorName (e.g. FoobarTableBehaviorTags)
 735             $option_name = str_replace('com_', '', $this->config['option']);
 736             $behaviorClass = ucfirst($option_name) . 'TableBehavior' . ucfirst(strtolower($name));
 737 
 738             if (class_exists($behaviorClass))
 739             {
 740                 $behavior = new $behaviorClass($this->tableDispatcher, $config);
 741 
 742                 return true;
 743             }
 744         }
 745 
 746         // Nothing found? Return false.
 747 
 748         $behaviorClass = 'FOFTableBehavior' . ucfirst(strtolower($name));
 749 
 750         if (class_exists($behaviorClass) && $this->tableDispatcher)
 751         {
 752             $behavior = new $behaviorClass($this->tableDispatcher, $config);
 753 
 754             return true;
 755         }
 756 
 757         return false;
 758     }
 759 
 760     /**
 761      * Sets the events trigger switch state
 762      *
 763      * @param   boolean  $newState  The new state of the switch (what else could it be?)
 764      *
 765      * @return  void
 766      */
 767     public function setTriggerEvents($newState = false)
 768     {
 769         $this->_trigger_events = $newState ? true : false;
 770     }
 771 
 772     /**
 773      * Gets the events trigger switch state
 774      *
 775      * @return  boolean
 776      */
 777     public function getTriggerEvents()
 778     {
 779         return $this->_trigger_events;
 780     }
 781 
 782     /**
 783      * Gets the has tags switch state
 784      *
 785      * @return bool
 786      */
 787     public function hasTags()
 788     {
 789         return $this->_has_tags;
 790     }
 791 
 792     /**
 793      * Sets the has tags switch state
 794      *
 795      * @param   bool  $newState
 796      */
 797     public function setHasTags($newState = false)
 798     {
 799         $this->_has_tags = false;
 800 
 801         // Tags are available only in 3.1+
 802         if (version_compare(JVERSION, '3.1', 'ge'))
 803         {
 804             $this->_has_tags = $newState ? true : false;
 805         }
 806     }
 807 
 808     /**
 809      * Set the class prefix
 810      *
 811      * @param string $prefix The prefix
 812      */
 813     public function setTablePrefix($prefix)
 814     {
 815         $this->_tablePrefix = $prefix;
 816     }
 817 
 818     /**
 819      * Sets fields to be skipped from automatic checks.
 820      *
 821      * @param   array/string  $skip  Fields to be skipped by automatic checks
 822      *
 823      * @return void
 824      */
 825     public function setSkipChecks($skip)
 826     {
 827         $this->_skipChecks = (array) $skip;
 828     }
 829 
 830     /**
 831      * Method to load a row from the database by primary key and bind the fields
 832      * to the FOFTable instance properties.
 833      *
 834      * @param   mixed    $keys   An optional primary key value to load the row by, or an array of fields to match.  If not
 835      *                           set the instance property value is used.
 836      * @param   boolean  $reset  True to reset the default values before loading the new row.
 837      *
 838      * @return  boolean  True if successful. False if row not found.
 839      *
 840      * @throws  RuntimeException
 841      * @throws  UnexpectedValueException
 842      */
 843     public function load($keys = null, $reset = true)
 844     {
 845         if (!$this->_tableExists)
 846         {
 847             $result = false;
 848 
 849             return $this->onAfterLoad($result);
 850         }
 851 
 852         if (empty($keys))
 853         {
 854             // If empty, use the value of the current key
 855             $keyName = $this->_tbl_key;
 856 
 857             if (isset($this->$keyName))
 858             {
 859                 $keyValue = $this->$keyName;
 860             }
 861             else
 862             {
 863                 $keyValue = null;
 864             }
 865 
 866             // If empty primary key there's is no need to load anything
 867 
 868             if (empty($keyValue))
 869             {
 870                 $result = true;
 871 
 872                 return $this->onAfterLoad($result);
 873             }
 874 
 875             $keys = array($keyName => $keyValue);
 876         }
 877         elseif (!is_array($keys))
 878         {
 879             // Load by primary key.
 880             $keys = array($this->_tbl_key => $keys);
 881         }
 882 
 883         if ($reset)
 884         {
 885             $this->reset();
 886         }
 887 
 888         // Initialise the query.
 889         $query = $this->_db->getQuery(true);
 890         $query->select($this->_tbl . '.*');
 891         $query->from($this->_tbl);
 892 
 893         // Joined fields are ok, since I initialized them in the constructor
 894         $fields = $this->getKnownFields();
 895 
 896         foreach ($keys as $field => $value)
 897         {
 898             // Check that $field is in the table.
 899 
 900             if (!in_array($field, $fields))
 901             {
 902                 throw new UnexpectedValueException(sprintf('Missing field in table %s : %s.', $this->_tbl, $field));
 903             }
 904 
 905             // Add the search tuple to the query.
 906             $query->where($this->_db->qn($this->_tbl . '.' . $field) . ' = ' . $this->_db->q($value));
 907         }
 908 
 909         // Do I have any joined table?
 910         $j_query = $this->getQueryJoin();
 911 
 912         if ($j_query)
 913         {
 914             if ($j_query->select && $j_query->select->getElements())
 915             {
 916                 //$query->select($this->normalizeSelectFields($j_query->select->getElements(), true));
 917                 $query->select($j_query->select->getElements());
 918             }
 919 
 920             if ($j_query->join)
 921             {
 922                 foreach ($j_query->join as $join)
 923                 {
 924                     $t = (string) $join;
 925 
 926                     // Joomla doesn't provide any access to the "name" variable, so I have to work with strings...
 927                     if (stripos($t, 'inner') !== false)
 928                     {
 929                         $query->innerJoin($join->getElements());
 930                     }
 931                     elseif (stripos($t, 'left') !== false)
 932                     {
 933                         $query->leftJoin($join->getElements());
 934                     }
 935                     elseif (stripos($t, 'right') !== false)
 936                     {
 937                         $query->rightJoin($join->getElements());
 938                     }
 939                     elseif (stripos($t, 'outer') !== false)
 940                     {
 941                         $query->outerJoin($join->getElements());
 942                     }
 943                 }
 944             }
 945         }
 946 
 947         $this->_db->setQuery($query);
 948 
 949         $row = $this->_db->loadAssoc();
 950 
 951         // Check that we have a result.
 952         if (empty($row))
 953         {
 954             $result = false;
 955 
 956             return $this->onAfterLoad($result);
 957         }
 958 
 959         // Bind the object with the row and return.
 960         $result = $this->bind($row);
 961 
 962         $this->onAfterLoad($result);
 963 
 964         return $result;
 965     }
 966 
 967     /**
 968      * Based on fields properties (nullable column), checks if the field is required or not
 969      *
 970      * @return boolean
 971      */
 972     public function check()
 973     {
 974         if (!$this->_autoChecks)
 975         {
 976             return true;
 977         }
 978 
 979         $fields = $this->getTableFields();
 980 
 981         // No fields? Why in the hell am I here?
 982         if(!$fields)
 983         {
 984             return false;
 985         }
 986 
 987         $result       = true;
 988         $known        = $this->getKnownFields();
 989         $skipFields[] = $this->_tbl_key;
 990 
 991         if(in_array($this->getColumnAlias('title'), $known)
 992             && in_array($this->getColumnAlias('slug'), $known))      $skipFields[] = $this->getColumnAlias('slug');
 993         if(in_array($this->getColumnAlias('hits'), $known))         $skipFields[] = $this->getColumnAlias('hits');
 994         if(in_array($this->getColumnAlias('created_on'), $known))   $skipFields[] = $this->getColumnAlias('created_on');
 995         if(in_array($this->getColumnAlias('created_by'), $known))   $skipFields[] = $this->getColumnAlias('created_by');
 996         if(in_array($this->getColumnAlias('modified_on'), $known))  $skipFields[] = $this->getColumnAlias('modified_on');
 997         if(in_array($this->getColumnAlias('modified_by'), $known))  $skipFields[] = $this->getColumnAlias('modified_by');
 998         if(in_array($this->getColumnAlias('locked_by'), $known))    $skipFields[] = $this->getColumnAlias('locked_by');
 999         if(in_array($this->getColumnAlias('locked_on'), $known))    $skipFields[] = $this->getColumnAlias('locked_on');
1000 
1001         // Let's merge it with custom skips
1002         $skipFields = array_merge($skipFields, $this->_skipChecks);
1003 
1004         foreach ($fields as $field)
1005         {
1006             $fieldName = $field->Field;
1007 
1008             if (empty($fieldName))
1009             {
1010                 $fieldName = $field->column_name;
1011             }
1012 
1013             // Field is not nullable but it's null, set error
1014 
1015             if ($field->Null == 'NO' && $this->$fieldName == '' && !in_array($fieldName, $skipFields))
1016             {
1017                 $text = str_replace('#__', 'COM_', $this->getTableName()) . '_ERR_' . $fieldName;
1018                 $this->setError(JText::_(strtoupper($text)));
1019                 $result = false;
1020             }
1021         }
1022 
1023         return $result;
1024     }
1025 
1026     /**
1027      * Method to reset class properties to the defaults set in the class
1028      * definition. It will ignore the primary key as well as any private class
1029      * properties.
1030      *
1031      * @return void
1032      */
1033     public function reset()
1034     {
1035         if (!$this->onBeforeReset())
1036         {
1037             return false;
1038         }
1039 
1040         // Get the default values for the class from the table.
1041         $fields   = $this->getTableFields();
1042         $j_fields = $this->getQueryJoinFields();
1043 
1044         if ($j_fields)
1045         {
1046             $fields = array_merge($fields, $j_fields);
1047         }
1048 
1049         if (is_array($fields) && !empty($fields))
1050         {
1051             foreach ($fields as $k => $v)
1052             {
1053                 // If the property is not the primary key or private, reset it.
1054                 if ($k != $this->_tbl_key && (strpos($k, '_') !== 0))
1055                 {
1056                     $this->$k = $v->Default;
1057                 }
1058             }
1059 
1060             if (!$this->onAfterReset())
1061             {
1062                 return false;
1063             }
1064         }
1065     }
1066 
1067     /**
1068      * Clones the current object, after resetting it
1069      *
1070      * @return static
1071      */
1072     public function getClone()
1073     {
1074         $clone = clone $this;
1075         $clone->reset();
1076 
1077         $key = $this->getKeyName();
1078         $clone->$key = null;
1079 
1080         return $clone;
1081     }
1082 
1083     /**
1084      * Generic check for whether dependencies exist for this object in the db schema
1085      *
1086      * @param   integer  $oid    The primary key of the record to delete
1087      * @param   array    $joins  Any joins to foreign table, used to determine if dependent records exist
1088      *
1089      * @return  boolean  True if the record can be deleted
1090      */
1091     public function canDelete($oid = null, $joins = null)
1092     {
1093         $k = $this->_tbl_key;
1094 
1095         if ($oid)
1096         {
1097             $this->$k = intval($oid);
1098         }
1099 
1100         if (is_array($joins))
1101         {
1102             $db      = $this->_db;
1103             $query   = $db->getQuery(true)
1104                 ->select($db->qn('master') . '.' . $db->qn($k))
1105                 ->from($db->qn($this->_tbl) . ' AS ' . $db->qn('master'));
1106             $tableNo = 0;
1107 
1108             foreach ($joins as $table)
1109             {
1110                 $tableNo++;
1111                 $query->select(
1112                     array(
1113                         'COUNT(DISTINCT ' . $db->qn('t' . $tableNo) . '.' . $db->qn($table['idfield']) . ') AS ' . $db->qn($table['idalias'])
1114                     )
1115                 );
1116                 $query->join('LEFT', $db->qn($table['name']) .
1117                     ' AS ' . $db->qn('t' . $tableNo) .
1118                     ' ON ' . $db->qn('t' . $tableNo) . '.' . $db->qn($table['joinfield']) .
1119                     ' = ' . $db->qn('master') . '.' . $db->qn($k)
1120                 );
1121             }
1122 
1123             $query->where($db->qn('master') . '.' . $db->qn($k) . ' = ' . $db->q($this->$k));
1124             $query->group($db->qn('master') . '.' . $db->qn($k));
1125             $this->_db->setQuery((string) $query);
1126 
1127             if (version_compare(JVERSION, '3.0', 'ge'))
1128             {
1129                 try
1130                 {
1131                     $obj = $this->_db->loadObject();
1132                 }
1133                 catch (Exception $e)
1134                 {
1135                     $this->setError($e->getMessage());
1136                 }
1137             }
1138             else
1139             {
1140                 if (!$obj = $this->_db->loadObject())
1141                 {
1142                     $this->setError($this->_db->getErrorMsg());
1143 
1144                     return false;
1145                 }
1146             }
1147 
1148             $msg = array();
1149             $i   = 0;
1150 
1151             foreach ($joins as $table)
1152             {
1153                 $k = $table['idalias'];
1154 
1155                 if ($obj->$k > 0)
1156                 {
1157                     $msg[] = JText::_($table['label']);
1158                 }
1159 
1160                 $i++;
1161             }
1162 
1163             if (count($msg))
1164             {
1165                 $option  = $this->input->getCmd('option', 'com_foobar');
1166                 $comName = str_replace('com_', '', $option);
1167                 $tview   = str_replace('#__' . $comName . '_', '', $this->_tbl);
1168                 $prefix  = $option . '_' . $tview . '_NODELETE_';
1169 
1170                 foreach ($msg as $key)
1171                 {
1172                     $this->setError(JText::_($prefix . $key));
1173                 }
1174 
1175                 return false;
1176             }
1177             else
1178             {
1179                 return true;
1180             }
1181         }
1182 
1183         return true;
1184     }
1185 
1186     /**
1187      * Method to bind an associative array or object to the FOFTable instance.This
1188      * method only binds properties that are publicly accessible and optionally
1189      * takes an array of properties to ignore when binding.
1190      *
1191      * @param   mixed  $src     An associative array or object to bind to the FOFTable instance.
1192      * @param   mixed  $ignore  An optional array or space separated list of properties to ignore while binding.
1193      *
1194      * @return  boolean  True on success.
1195      *
1196      * @throws  InvalidArgumentException
1197      */
1198     public function bind($src, $ignore = array())
1199     {
1200         if (!$this->onBeforeBind($src))
1201         {
1202             return false;
1203         }
1204 
1205         // If the source value is not an array or object return false.
1206         if (!is_object($src) && !is_array($src))
1207         {
1208             throw new InvalidArgumentException(sprintf('%s::bind(*%s*)', get_class($this), gettype($src)));
1209         }
1210 
1211         // If the source value is an object, get its accessible properties.
1212         if (is_object($src))
1213         {
1214             $src = get_object_vars($src);
1215         }
1216 
1217         // If the ignore value is a string, explode it over spaces.
1218         if (!is_array($ignore))
1219         {
1220             $ignore = explode(' ', $ignore);
1221         }
1222 
1223         // Bind the source value, excluding the ignored fields.
1224         foreach ($this->getKnownFields() as $k)
1225         {
1226             // Only process fields not in the ignore array.
1227             if (!in_array($k, $ignore))
1228             {
1229                 if (isset($src[$k]))
1230                 {
1231                     $this->$k = $src[$k];
1232                 }
1233             }
1234         }
1235 
1236         $result = $this->onAfterBind($src);
1237 
1238         return $result;
1239     }
1240 
1241     /**
1242      * Method to store a row in the database from the FOFTable instance properties.
1243      * If a primary key value is set the row with that primary key value will be
1244      * updated with the instance property values.  If no primary key value is set
1245      * a new row will be inserted into the database with the properties from the
1246      * FOFTable instance.
1247      *
1248      * @param   boolean  $updateNulls  True to update fields even if they are null.
1249      *
1250      * @return  boolean  True on success.
1251      */
1252     public function store($updateNulls = false)
1253     {
1254         if (!$this->onBeforeStore($updateNulls))
1255         {
1256             return false;
1257         }
1258 
1259         $k = $this->_tbl_key;
1260 
1261         if ($this->$k == 0)
1262         {
1263             $this->$k = null;
1264         }
1265 
1266         // Create the object used for inserting/updating data to the database
1267         $fields     = $this->getTableFields();
1268         $properties = $this->getKnownFields();
1269         $keys       = array();
1270 
1271         foreach ($properties as $property)
1272         {
1273             // 'input' property is a reserved name
1274 
1275             if (isset($fields[$property]))
1276             {
1277                 $keys[] = $property;
1278             }
1279         }
1280 
1281         $updateObject = array();
1282         foreach ($keys as $key)
1283         {
1284             $updateObject[$key] = $this->$key;
1285         }
1286         $updateObject = (object)$updateObject;
1287 
1288         /**
1289          * While the documentation for update/insertObject and execute() say they return a boolean,
1290          * not all of the implemtnations.  Depending on the version of J! and the specific driver,
1291          * they may return a database object, or boolean, or a mix, or toss an exception.  So try/catch,
1292          * and test for false.
1293          */
1294 
1295         try
1296         {
1297             // If a primary key exists update the object, otherwise insert it.
1298             if ($this->$k)
1299             {
1300                 $result = $this->_db->updateObject($this->_tbl, $updateObject, $this->_tbl_key, $updateNulls);
1301             }
1302             else
1303             {
1304                 $result = $this->_db->insertObject($this->_tbl, $updateObject, $this->_tbl_key);
1305             }
1306 
1307             if ($result === false)
1308             {
1309                 $this->setError($this->_db->getErrorMsg());
1310 
1311                 return false;
1312             }
1313         }
1314         catch (Exception $e)
1315         {
1316             $this->setError($e->getMessage());
1317         }
1318 
1319         $this->bind($updateObject);
1320 
1321         if ($this->_locked)
1322         {
1323             $this->_unlock();
1324         }
1325 
1326         $result = $this->onAfterStore();
1327 
1328         return $result;
1329     }
1330 
1331     /**
1332      * Method to move a row in the ordering sequence of a group of rows defined by an SQL WHERE clause.
1333      * Negative numbers move the row up in the sequence and positive numbers move it down.
1334      *
1335      * @param   integer  $delta  The direction and magnitude to move the row in the ordering sequence.
1336      * @param   string   $where  WHERE clause to use for limiting the selection of rows to compact the
1337      *                           ordering values.
1338      *
1339      * @return  mixed    Boolean  True on success.
1340      *
1341      * @throws  UnexpectedValueException
1342      */
1343     public function move($delta, $where = '')
1344     {
1345         if (!$this->onBeforeMove($delta, $where))
1346         {
1347             return false;
1348         }
1349 
1350         // If there is no ordering field set an error and return false.
1351         $ordering_field = $this->getColumnAlias('ordering');
1352 
1353         if (!in_array($ordering_field, $this->getKnownFields()))
1354         {
1355             throw new UnexpectedValueException(sprintf('%s does not support ordering.', $this->_tbl));
1356         }
1357 
1358         // If the change is none, do nothing.
1359         if (empty($delta))
1360         {
1361             $result = $this->onAfterMove();
1362 
1363             return $result;
1364         }
1365 
1366         $k     = $this->_tbl_key;
1367         $row   = null;
1368         $query = $this->_db->getQuery(true);
1369 
1370         // If the table is not loaded, return false
1371         if (empty($this->$k))
1372         {
1373             return false;
1374         }
1375 
1376         // Select the primary key and ordering values from the table.
1377         $query->select(array($this->_db->qn($this->_tbl_key), $this->_db->qn($ordering_field)));
1378         $query->from($this->_tbl);
1379 
1380         // If the movement delta is negative move the row up.
1381 
1382         if ($delta < 0)
1383         {
1384             $query->where($this->_db->qn($ordering_field) . ' < ' . $this->_db->q((int) $this->$ordering_field));
1385             $query->order($this->_db->qn($ordering_field) . ' DESC');
1386         }
1387 
1388         // If the movement delta is positive move the row down.
1389 
1390         elseif ($delta > 0)
1391         {
1392             $query->where($this->_db->qn($ordering_field) . ' > ' . $this->_db->q((int) $this->$ordering_field));
1393             $query->order($this->_db->qn($ordering_field) . ' ASC');
1394         }
1395 
1396         // Add the custom WHERE clause if set.
1397 
1398         if ($where)
1399         {
1400             $query->where($where);
1401         }
1402 
1403         // Select the first row with the criteria.
1404         $this->_db->setQuery($query, 0, 1);
1405         $row = $this->_db->loadObject();
1406 
1407         // If a row is found, move the item.
1408 
1409         if (!empty($row))
1410         {
1411             // Update the ordering field for this instance to the row's ordering value.
1412             $query = $this->_db->getQuery(true);
1413             $query->update($this->_tbl);
1414             $query->set($this->_db->qn($ordering_field) . ' = ' . $this->_db->q((int) $row->$ordering_field));
1415             $query->where($this->_tbl_key . ' = ' . $this->_db->q($this->$k));
1416             $this->_db->setQuery($query);
1417             $this->_db->execute();
1418 
1419             // Update the ordering field for the row to this instance's ordering value.
1420             $query = $this->_db->getQuery(true);
1421             $query->update($this->_tbl);
1422             $query->set($this->_db->qn($ordering_field) . ' = ' . $this->_db->q((int) $this->$ordering_field));
1423             $query->where($this->_tbl_key . ' = ' . $this->_db->q($row->$k));
1424             $this->_db->setQuery($query);
1425             $this->_db->execute();
1426 
1427             // Update the instance value.
1428             $this->$ordering_field = $row->$ordering_field;
1429         }
1430         else
1431         {
1432             // Update the ordering field for this instance.
1433             $query = $this->_db->getQuery(true);
1434             $query->update($this->_tbl);
1435             $query->set($this->_db->qn($ordering_field) . ' = ' . $this->_db->q((int) $this->$ordering_field));
1436             $query->where($this->_tbl_key . ' = ' . $this->_db->q($this->$k));
1437             $this->_db->setQuery($query);
1438             $this->_db->execute();
1439         }
1440 
1441         $result = $this->onAfterMove();
1442 
1443         return $result;
1444     }
1445 
1446     /**
1447      * Change the ordering of the records of the table
1448      *
1449      * @param   string   $where  The WHERE clause of the SQL used to fetch the order
1450      *
1451      * @return  boolean  True is successful
1452      *
1453      * @throws  UnexpectedValueException
1454      */
1455     public function reorder($where = '')
1456     {
1457         if (!$this->onBeforeReorder($where))
1458         {
1459             return false;
1460         }
1461 
1462         // If there is no ordering field set an error and return false.
1463 
1464         $order_field = $this->getColumnAlias('ordering');
1465 
1466         if (!in_array($order_field, $this->getKnownFields()))
1467         {
1468             throw new UnexpectedValueException(sprintf('%s does not support ordering.', $this->_tbl_key));
1469         }
1470 
1471         $k = $this->_tbl_key;
1472 
1473         // Get the primary keys and ordering values for the selection.
1474         $query = $this->_db->getQuery(true);
1475         $query->select($this->_tbl_key . ', ' . $this->_db->qn($order_field));
1476         $query->from($this->_tbl);
1477         $query->where($this->_db->qn($order_field) . ' >= ' . $this->_db->q(0));
1478         $query->order($this->_db->qn($order_field));
1479 
1480         // Setup the extra where and ordering clause data.
1481 
1482         if ($where)
1483         {
1484             $query->where($where);
1485         }
1486 
1487         $this->_db->setQuery($query);
1488         $rows = $this->_db->loadObjectList();
1489 
1490         // Compact the ordering values.
1491 
1492         foreach ($rows as $i => $row)
1493         {
1494             // Make sure the ordering is a positive integer.
1495 
1496             if ($row->$order_field >= 0)
1497             {
1498                 // Only update rows that are necessary.
1499 
1500                 if ($row->$order_field != $i + 1)
1501                 {
1502                     // Update the row ordering field.
1503                     $query = $this->_db->getQuery(true);
1504                     $query->update($this->_tbl);
1505                     $query->set($this->_db->qn($order_field) . ' = ' . $this->_db->q($i + 1));
1506                     $query->where($this->_tbl_key . ' = ' . $this->_db->q($row->$k));
1507                     $this->_db->setQuery($query);
1508                     $this->_db->execute();
1509                 }
1510             }
1511         }
1512 
1513         $result = $this->onAfterReorder();
1514 
1515         return $result;
1516     }
1517 
1518     /**
1519      * Check out (lock) a record
1520      *
1521      * @param   integer  $userId  The locking user's ID
1522      * @param   integer  $oid     The primary key value of the record to lock
1523      *
1524      * @return  boolean  True on success
1525      */
1526     public function checkout($userId, $oid = null)
1527     {
1528         $fldLockedBy = $this->getColumnAlias('locked_by');
1529         $fldLockedOn = $this->getColumnAlias('locked_on');
1530 
1531         if (!(in_array($fldLockedBy, $this->getKnownFields())
1532             || in_array($fldLockedOn, $this->getKnownFields())))
1533         {
1534             return true;
1535         }
1536 
1537         $k = $this->_tbl_key;
1538 
1539         if ($oid !== null)
1540         {
1541             $this->$k = $oid;
1542         }
1543 
1544         // No primary key defined, stop here
1545         if (!$this->$k)
1546         {
1547             return false;
1548         }
1549 
1550         $date = FOFPlatform::getInstance()->getDate();
1551 
1552         if (method_exists($date, 'toSql'))
1553         {
1554             $time = $date->toSql();
1555         }
1556         else
1557         {
1558             $time = $date->toMySQL();
1559         }
1560 
1561 
1562         $query = $this->_db->getQuery(true)
1563             ->update($this->_db->qn($this->_tbl))
1564             ->set(
1565                 array(
1566                     $this->_db->qn($fldLockedBy) . ' = ' . $this->_db->q((int) $userId),
1567                     $this->_db->qn($fldLockedOn) . ' = ' . $this->_db->q($time)
1568                 )
1569             )
1570             ->where($this->_db->qn($this->_tbl_key) . ' = ' . $this->_db->q($this->$k));
1571         $this->_db->setQuery((string) $query);
1572 
1573         $this->$fldLockedBy = $userId;
1574         $this->$fldLockedOn = $time;
1575 
1576         return $this->_db->execute();
1577     }
1578 
1579     /**
1580      * Check in (unlock) a record
1581      *
1582      * @param   integer  $oid  The primary key value of the record to unlock
1583      *
1584      * @return  boolean  True on success
1585      */
1586     public function checkin($oid = null)
1587     {
1588         $fldLockedBy = $this->getColumnAlias('locked_by');
1589         $fldLockedOn = $this->getColumnAlias('locked_on');
1590 
1591         if (!(in_array($fldLockedBy, $this->getKnownFields())
1592             || in_array($fldLockedOn, $this->getKnownFields())))
1593         {
1594             return true;
1595         }
1596 
1597         $k = $this->_tbl_key;
1598 
1599         if ($oid !== null)
1600         {
1601             $this->$k = $oid;
1602         }
1603 
1604         if ($this->$k == null)
1605         {
1606             return false;
1607         }
1608 
1609         $query = $this->_db->getQuery(true)
1610             ->update($this->_db->qn($this->_tbl))
1611             ->set(
1612                 array(
1613                     $this->_db->qn($fldLockedBy) . ' = 0',
1614                     $this->_db->qn($fldLockedOn) . ' = ' . $this->_db->q($this->_db->getNullDate())
1615                 )
1616             )
1617             ->where($this->_db->qn($this->_tbl_key) . ' = ' . $this->_db->q($this->$k));
1618         $this->_db->setQuery((string) $query);
1619 
1620         $this->$fldLockedBy = 0;
1621         $this->$fldLockedOn = '';
1622 
1623         return $this->_db->execute();
1624     }
1625 
1626     /**
1627      * Is a record locked?
1628      *
1629      * @param   integer $with            The userid to preform the match with. If an item is checked
1630      *                                   out by this user the function will return false.
1631      * @param   integer $unused_against  Junk inherited from JTable; ignore
1632      *
1633      * @throws  UnexpectedValueException
1634      *
1635      * @return  boolean  True if the record is locked by another user
1636      */
1637     public function isCheckedOut($with = 0, $unused_against = null)
1638     {
1639         $against     = null;
1640         $fldLockedBy = $this->getColumnAlias('locked_by');
1641 
1642         $k  = $this->_tbl_key;
1643 
1644         // If no primary key is given, return false.
1645 
1646         if ($this->$k === null)
1647         {
1648             throw new UnexpectedValueException('Null primary key not allowed.');
1649         }
1650 
1651         if (isset($this) && is_a($this, 'FOFTable') && !$against)
1652         {
1653             $against = $this->get($fldLockedBy);
1654         }
1655 
1656         // Item is not checked out, or being checked out by the same user
1657 
1658         if (!$against || $against == $with)
1659         {
1660             return false;
1661         }
1662 
1663         $session = JTable::getInstance('session');
1664 
1665         return $session->exists($against);
1666     }
1667 
1668     /**
1669      * Copy (duplicate) one or more records
1670      *
1671      * @param   integer|array  $cid  The primary key value (or values) or the record(s) to copy
1672      *
1673      * @return  boolean  True on success
1674      */
1675     public function copy($cid = null)
1676     {
1677         //We have to cast the id as array, or the helper function will return an empty set
1678         if($cid)
1679         {
1680             $cid = (array) $cid;
1681         }
1682 
1683         FOFUtilsArray::toInteger($cid);
1684         $k = $this->_tbl_key;
1685 
1686         if (count($cid) < 1)
1687         {
1688             if ($this->$k)
1689             {
1690                 $cid = array($this->$k);
1691             }
1692             else
1693             {
1694                 $this->setError("No items selected.");
1695 
1696                 return false;
1697             }
1698         }
1699 
1700         $created_by  = $this->getColumnAlias('created_by');
1701         $created_on  = $this->getColumnAlias('created_on');
1702         $modified_by = $this->getColumnAlias('modified_by');
1703         $modified_on = $this->getColumnAlias('modified_on');
1704 
1705         $locked_byName = $this->getColumnAlias('locked_by');
1706         $checkin       = in_array($locked_byName, $this->getKnownFields());
1707 
1708         foreach ($cid as $item)
1709         {
1710             // Prevent load with id = 0
1711 
1712             if (!$item)
1713             {
1714                 continue;
1715             }
1716 
1717             $this->load($item);
1718 
1719             if ($checkin)
1720             {
1721                 // We're using the checkin and the record is used by someone else
1722 
1723                 if ($this->isCheckedOut($item))
1724                 {
1725                     continue;
1726                 }
1727             }
1728 
1729             if (!$this->onBeforeCopy($item))
1730             {
1731                 continue;
1732             }
1733 
1734             $this->$k           = null;
1735             $this->$created_by  = null;
1736             $this->$created_on  = null;
1737             $this->$modified_on = null;
1738             $this->$modified_by = null;
1739 
1740             // Let's fire the event only if everything is ok
1741             if ($this->store())
1742             {
1743                 $this->onAfterCopy($item);
1744             }
1745 
1746             $this->reset();
1747         }
1748 
1749         return true;
1750     }
1751 
1752     /**
1753      * Publish or unpublish records
1754      *
1755      * @param   integer|array  $cid      The primary key value(s) of the item(s) to publish/unpublish
1756      * @param   integer        $publish  1 to publish an item, 0 to unpublish
1757      * @param   integer        $user_id  The user ID of the user (un)publishing the item.
1758      *
1759      * @return  boolean  True on success, false on failure (e.g. record is locked)
1760      */
1761     public function publish($cid = null, $publish = 1, $user_id = 0)
1762     {
1763         $enabledName   = $this->getColumnAlias('enabled');
1764         $locked_byName = $this->getColumnAlias('locked_by');
1765 
1766         // Mhm... you called the publish method on a table without publish support...
1767         if(!in_array($enabledName, $this->getKnownFields()))
1768         {
1769             return false;
1770         }
1771 
1772         //We have to cast the id as array, or the helper function will return an empty set
1773         if($cid)
1774         {
1775             $cid = (array) $cid;
1776         }
1777 
1778         FOFUtilsArray::toInteger($cid);
1779         $user_id = (int) $user_id;
1780         $publish = (int) $publish;
1781         $k       = $this->_tbl_key;
1782 
1783         if (count($cid) < 1)
1784         {
1785             if ($this->$k)
1786             {
1787                 $cid = array($this->$k);
1788             }
1789             else
1790             {
1791                 $this->setError("No items selected.");
1792 
1793                 return false;
1794             }
1795         }
1796 
1797         if (!$this->onBeforePublish($cid, $publish))
1798         {
1799             return false;
1800         }
1801 
1802         $query = $this->_db->getQuery(true)
1803             ->update($this->_db->qn($this->_tbl))
1804             ->set($this->_db->qn($enabledName) . ' = ' . (int) $publish);
1805 
1806         $checkin = in_array($locked_byName, $this->getKnownFields());
1807 
1808         if ($checkin)
1809         {
1810             $query->where(
1811                 ' (' . $this->_db->qn($locked_byName) .
1812                     ' = 0 OR ' . $this->_db->qn($locked_byName) . ' = ' . (int) $user_id . ')', 'AND'
1813             );
1814         }
1815 
1816         // TODO Rewrite this statment using IN. Check if it work in SQLServer and PostgreSQL
1817         $cids = $this->_db->qn($k) . ' = ' . implode(' OR ' . $this->_db->qn($k) . ' = ', $cid);
1818 
1819         $query->where('(' . $cids . ')');
1820 
1821         $this->_db->setQuery((string) $query);
1822 
1823         if (version_compare(JVERSION, '3.0', 'ge'))
1824         {
1825             try
1826             {
1827                 $this->_db->execute();
1828             }
1829             catch (Exception $e)
1830             {
1831                 $this->setError($e->getMessage());
1832             }
1833         }
1834         else
1835         {
1836             if (!$this->_db->execute())
1837             {
1838                 $this->setError($this->_db->getErrorMsg());
1839 
1840                 return false;
1841             }
1842         }
1843 
1844         if (count($cid) == 1 && $checkin)
1845         {
1846             if ($this->_db->getAffectedRows() == 1)
1847             {
1848                 $this->checkin($cid[0]);
1849 
1850                 if ($this->$k == $cid[0])
1851                 {
1852                     $this->$enabledName = $publish;
1853                 }
1854             }
1855         }
1856 
1857         $this->setError('');
1858 
1859         return true;
1860     }
1861 
1862     /**
1863      * Delete a record
1864      *
1865      * @param   integer $oid  The primary key value of the item to delete
1866      *
1867      * @throws  UnexpectedValueException
1868      *
1869      * @return  boolean  True on success
1870      */
1871     public function delete($oid = null)
1872     {
1873         if ($oid)
1874         {
1875             $this->load($oid);
1876         }
1877 
1878         $k  = $this->_tbl_key;
1879         $pk = (!$oid) ? $this->$k : $oid;
1880 
1881         // If no primary key is given, return false.
1882         if (!$pk)
1883         {
1884             throw new UnexpectedValueException('Null primary key not allowed.');
1885         }
1886 
1887         // Execute the logic only if I have a primary key, otherwise I could have weird results
1888         if (!$this->onBeforeDelete($oid))
1889         {
1890             return false;
1891         }
1892 
1893         // Delete the row by primary key.
1894         $query = $this->_db->getQuery(true);
1895         $query->delete();
1896         $query->from($this->_tbl);
1897         $query->where($this->_tbl_key . ' = ' . $this->_db->q($pk));
1898         $this->_db->setQuery($query);
1899 
1900         $this->_db->execute();
1901 
1902         $result = $this->onAfterDelete($oid);
1903 
1904         return $result;
1905     }
1906 
1907     /**
1908      * Register a hit on a record
1909      *
1910      * @param   integer  $oid  The primary key value of the record
1911      * @param   boolean  $log  Should I log the hit?
1912      *
1913      * @return  boolean  True on success
1914      */
1915     public function hit($oid = null, $log = false)
1916     {
1917         if (!$this->onBeforeHit($oid, $log))
1918         {
1919             return false;
1920         }
1921 
1922         // If there is no hits field, just return true.
1923         $hits_field = $this->getColumnAlias('hits');
1924 
1925         if (!in_array($hits_field, $this->getKnownFields()))
1926         {
1927             return true;
1928         }
1929 
1930         $k  = $this->_tbl_key;
1931         $pk = ($oid) ? $oid : $this->$k;
1932 
1933         // If no primary key is given, return false.
1934         if (!$pk)
1935         {
1936             $result = false;
1937         }
1938         else
1939         {
1940             // Check the row in by primary key.
1941             $query = $this->_db->getQuery(true)
1942                           ->update($this->_tbl)
1943                           ->set($this->_db->qn($hits_field) . ' = (' . $this->_db->qn($hits_field) . ' + 1)')
1944                           ->where($this->_tbl_key . ' = ' . $this->_db->q($pk));
1945 
1946             $this->_db->setQuery($query)->execute();
1947 
1948             // In order to update the table object, I have to load the table
1949             if(!$this->$k)
1950             {
1951                 $query = $this->_db->getQuery(true)
1952                               ->select($this->_db->qn($hits_field))
1953                               ->from($this->_db->qn($this->_tbl))
1954                               ->where($this->_db->qn($this->_tbl_key) . ' = ' . $this->_db->q($pk));
1955 
1956                 $this->$hits_field = $this->_db->setQuery($query)->loadResult();
1957             }
1958             else
1959             {
1960                 // Set table values in the object.
1961                 $this->$hits_field++;
1962             }
1963 
1964             $result = true;
1965         }
1966 
1967         if ($result)
1968         {
1969             $result = $this->onAfterHit($oid);
1970         }
1971 
1972         return $result;
1973     }
1974 
1975     /**
1976      * Export the item as a CSV line
1977      *
1978      * @param   string  $separator  CSV separator. Tip: use "\t" to get a TSV file instead.
1979      *
1980      * @return  string  The CSV line
1981      */
1982     public function toCSV($separator = ',')
1983     {
1984         $csv = array();
1985 
1986         foreach (get_object_vars($this) as $k => $v)
1987         {
1988             if (!in_array($k, $this->getKnownFields()))
1989             {
1990                 continue;
1991             }
1992 
1993             $csv[] = '"' . str_replace('"', '""', $v) . '"';
1994         }
1995 
1996         $csv = implode($separator, $csv);
1997 
1998         return $csv;
1999     }
2000 
2001     /**
2002      * Exports the table in array format
2003      *
2004      * @return  array
2005      */
2006     public function getData()
2007     {
2008         $ret = array();
2009 
2010         foreach (get_object_vars($this) as $k => $v)
2011         {
2012             if (!in_array($k, $this->getKnownFields()))
2013             {
2014                 continue;
2015             }
2016 
2017             $ret[$k] = $v;
2018         }
2019 
2020         return $ret;
2021     }
2022 
2023     /**
2024      * Get the header for exporting item list to CSV
2025      *
2026      * @param   string  $separator  CSV separator. Tip: use "\t" to get a TSV file instead.
2027      *
2028      * @return  string  The CSV file's header
2029      */
2030     public function getCSVHeader($separator = ',')
2031     {
2032         $csv = array();
2033 
2034         foreach (get_object_vars($this) as $k => $v)
2035         {
2036             if (!in_array($k, $this->getKnownFields()))
2037             {
2038                 continue;
2039             }
2040 
2041             $csv[] = '"' . str_replace('"', '\"', $k) . '"';
2042         }
2043 
2044         $csv = implode($separator, $csv);
2045 
2046         return $csv;
2047     }
2048 
2049     /**
2050      * Get the columns from a database table.
2051      *
2052      * @param   string  $tableName  Table name. If null current table is used
2053      *
2054      * @return  mixed  An array of the field names, or false if an error occurs.
2055      */
2056     public function getTableFields($tableName = null)
2057     {
2058         // Should I load the cached data?
2059         $useCache = array_key_exists('use_table_cache', $this->config) ? $this->config['use_table_cache'] : false;
2060 
2061         // Make sure we have a list of tables in this db
2062 
2063         if (empty(self::$tableCache))
2064         {
2065             if ($useCache)
2066             {
2067                 // Try to load table cache from a cache file
2068                 $cacheData = FOFPlatform::getInstance()->getCache('tables', null);
2069 
2070                 // Unserialise the cached data, or set the table cache to empty
2071                 // if the cache data wasn't loaded.
2072                 if (!is_null($cacheData))
2073                 {
2074                     self::$tableCache = json_decode($cacheData, true);
2075                 }
2076                 else
2077                 {
2078                     self::$tableCache = array();
2079                 }
2080             }
2081 
2082             // This check is true if the cache data doesn't exist / is not loaded
2083             if (empty(self::$tableCache))
2084             {
2085                 self::$tableCache = $this->_db->getTableList();
2086 
2087                 if ($useCache)
2088                 {
2089                     FOFPlatform::getInstance()->setCache('tables', json_encode(self::$tableCache));
2090                 }
2091             }
2092         }
2093 
2094         // Make sure the cached table fields cache is loaded
2095         if (empty(self::$tableFieldCache))
2096         {
2097             if ($useCache)
2098             {
2099                 // Try to load table cache from a cache file
2100                 $cacheData = FOFPlatform::getInstance()->getCache('tablefields', null);
2101 
2102                 // Unserialise the cached data, or set to empty if the cache
2103                 // data wasn't loaded.
2104                 if (!is_null($cacheData))
2105                 {
2106                     $decoded = json_decode($cacheData, true);
2107                     $tableCache = array();
2108 
2109                     if (count($decoded))
2110                     {
2111                         foreach ($decoded as $myTableName => $tableFields)
2112                         {
2113                             $temp = array();
2114 
2115                             if (is_array($tableFields))
2116                             {
2117                                 foreach($tableFields as $field => $def)
2118                                 {
2119                                     $temp[$field] = (object)$def;
2120                                 }
2121                                 $tableCache[$myTableName] = $temp;
2122                             }
2123                             elseif (is_object($tableFields) || is_bool($tableFields))
2124                             {
2125                                 $tableCache[$myTableName] = $tableFields;
2126                             }
2127                         }
2128                     }
2129 
2130                     self::$tableFieldCache = $tableCache;
2131                 }
2132                 else
2133                 {
2134                     self::$tableFieldCache = array();
2135                 }
2136             }
2137         }
2138 
2139         if (!$tableName)
2140         {
2141             $tableName = $this->_tbl;
2142         }
2143 
2144         // Try to load again column specifications if the table is not loaded OR if it's loaded and
2145         // the previous call returned an error
2146         if (!array_key_exists($tableName, self::$tableFieldCache) ||
2147             (isset(self::$tableFieldCache[$tableName]) && !self::$tableFieldCache[$tableName]))
2148         {
2149             // Lookup the fields for this table only once.
2150             $name = $tableName;
2151 
2152             $prefix = $this->_db->getPrefix();
2153 
2154             if (substr($name, 0, 3) == '#__')
2155             {
2156                 $checkName = $prefix . substr($name, 3);
2157             }
2158             else
2159             {
2160                 $checkName = $name;
2161             }
2162 
2163             if (!in_array($checkName, self::$tableCache))
2164             {
2165                 // The table doesn't exist. Return false.
2166                 self::$tableFieldCache[$tableName] = false;
2167             }
2168             elseif (version_compare(JVERSION, '3.0', 'ge'))
2169             {
2170                 $fields = $this->_db->getTableColumns($name, false);
2171 
2172                 if (empty($fields))
2173                 {
2174                     $fields = false;
2175                 }
2176 
2177                 self::$tableFieldCache[$tableName] = $fields;
2178             }
2179             else
2180             {
2181                 $fields = $this->_db->getTableFields($name, false);
2182 
2183                 if (!isset($fields[$name]))
2184                 {
2185                     $fields = false;
2186                 }
2187 
2188                 self::$tableFieldCache[$tableName] = $fields[$name];
2189             }
2190 
2191             // PostgreSQL date type compatibility
2192             if (($this->_db->name == 'postgresql') && (self::$tableFieldCache[$tableName] != false))
2193             {
2194                 foreach (self::$tableFieldCache[$tableName] as $field)
2195                 {
2196                     if (strtolower($field->type) == 'timestamp without time zone')
2197                     {
2198                         if (stristr($field->Default, '\'::timestamp without time zone'))
2199                         {
2200                             list ($date, $junk) = explode('::', $field->Default, 2);
2201                             $field->Default = trim($date, "'");
2202                         }
2203                     }
2204                 }
2205             }
2206 
2207             // Save the data for this table into the cache
2208             if ($useCache)
2209             {
2210                 $cacheData = FOFPlatform::getInstance()->setCache('tablefields', json_encode(self::$tableFieldCache));
2211             }
2212         }
2213 
2214         return self::$tableFieldCache[$tableName];
2215     }
2216 
2217     public function getTableAlias()
2218     {
2219         return $this->_tableAlias;
2220     }
2221 
2222     public function setTableAlias($string)
2223     {
2224         $string = preg_replace('#[^A-Z0-9_]#i', '', $string);
2225         $this->_tableAlias = $string;
2226     }
2227 
2228     /**
2229      * Method to return the real name of a "special" column such as ordering, hits, published
2230      * etc etc. In this way you are free to follow your db naming convention and use the
2231      * built in Joomla functions.
2232      *
2233      * @param   string  $column  Name of the "special" column (ie ordering, hits etc etc)
2234      *
2235      * @return  string  The string that identify the special
2236      */
2237     public function getColumnAlias($column)
2238     {
2239         if (isset($this->_columnAlias[$column]))
2240         {
2241             $return = $this->_columnAlias[$column];
2242         }
2243         else
2244         {
2245             $return = $column;
2246         }
2247 
2248         $return = preg_replace('#[^A-Z0-9_]#i', '', $return);
2249 
2250         return $return;
2251     }
2252 
2253     /**
2254      * Method to register a column alias for a "special" column.
2255      *
2256      * @param   string  $column       The "special" column (ie ordering)
2257      * @param   string  $columnAlias  The real column name (ie foo_ordering)
2258      *
2259      * @return  void
2260      */
2261     public function setColumnAlias($column, $columnAlias)
2262     {
2263         $column = strtolower($column);
2264 
2265         $column                      = preg_replace('#[^A-Z0-9_]#i', '', $column);
2266         $this->_columnAlias[$column] = $columnAlias;
2267     }
2268 
2269     /**
2270      * Get a JOIN query, used to join other tables
2271      *
2272      * @param   boolean  $asReference  Return an object reference instead of a copy
2273      *
2274      * @return  FOFDatabaseQuery  Query used to join other tables
2275      */
2276     public function getQueryJoin($asReference = false)
2277     {
2278         if ($asReference)
2279         {
2280             return $this->_queryJoin;
2281         }
2282         else
2283         {
2284             if ($this->_queryJoin)
2285             {
2286                 return clone $this->_queryJoin;
2287             }
2288             else
2289             {
2290                 return null;
2291             }
2292         }
2293     }
2294 
2295     /**
2296      * Sets the query with joins to other tables
2297      *
2298      * @param   FOFDatabaseQuery  $query  The JOIN query to use
2299      *
2300      * @return  void
2301      */
2302     public function setQueryJoin($query)
2303     {
2304         $this->_queryJoin = $query;
2305     }
2306 
2307     /**
2308      * Extracts the fields from the join query
2309      *
2310      * @return   array    Fields contained in the join query
2311      */
2312     protected function getQueryJoinFields()
2313     {
2314         $query = $this->getQueryJoin();
2315 
2316         if (!$query)
2317         {
2318             return array();
2319         }
2320 
2321         $tables   = array();
2322         $j_tables = array();
2323         $j_fields = array();
2324 
2325         // Get joined tables. Ignore FROM clause, since it should not be used (the starting point is the table "table")
2326         $joins    = $query->join;
2327 
2328         foreach ($joins as $join)
2329         {
2330             $tables = array_merge($tables, $join->getElements());
2331         }
2332 
2333         // Clean up table names
2334         foreach($tables as $table)
2335         {
2336             preg_match('#(.*)((\w)*(on|using))(.*)#i', $table, $matches);
2337 
2338             if($matches && isset($matches[1]))
2339             {
2340                 // I always want the first part, no matter what
2341                 $parts = explode(' ', $matches[1]);
2342                 $t_table = $parts[0];
2343 
2344                 if($this->isQuoted($t_table))
2345                 {
2346                     $t_table = substr($t_table, 1, strlen($t_table) - 2);
2347                 }
2348 
2349                 if(!in_array($t_table, $j_tables))
2350                 {
2351                     $j_tables[] =  $t_table;
2352                 }
2353             }
2354         }
2355 
2356         // Do I have the current table inside the query join? Remove it (its fields are already ok)
2357         $find = array_search($this->getTableName(), $j_tables);
2358         if($find !== false)
2359         {
2360             unset($j_tables[$find]);
2361         }
2362 
2363         // Get table fields
2364         $fields = array();
2365 
2366         foreach ($j_tables as $table)
2367         {
2368             $t_fields = $this->getTableFields($table);
2369 
2370             if ($t_fields)
2371             {
2372                 $fields = array_merge($fields, $t_fields);
2373             }
2374         }
2375 
2376         // Remove any fields that aren't in the joined select
2377         $j_select = $query->select;
2378 
2379         if ($j_select && $j_select->getElements())
2380         {
2381             $j_fields = $this->normalizeSelectFields($j_select->getElements());
2382         }
2383 
2384         // I can intesect the keys
2385         $fields   = array_intersect_key($fields, $j_fields);
2386 
2387         // Now I walk again the array to change the key of columns that have an alias
2388         foreach ($j_fields as $column => $alias)
2389         {
2390             if ($column != $alias)
2391             {
2392                 $fields[$alias] = $fields[$column];
2393                 unset($fields[$column]);
2394             }
2395         }
2396 
2397         return $fields;
2398     }
2399 
2400     /**
2401      * Normalizes the fields, returning an associative array with all the fields.
2402      * Ie array('foobar as foo, bar') becomes array('foobar' => 'foo', 'bar' => 'bar')
2403      *
2404      * @param   array $fields    Array with column fields
2405      *
2406      * @return  array  Normalized array
2407      */
2408     protected function normalizeSelectFields($fields)
2409     {
2410         $db     = FOFPlatform::getInstance()->getDbo();
2411         $return = array();
2412 
2413         foreach ($fields as $field)
2414         {
2415             $t_fields = explode(',', $field);
2416 
2417             foreach ($t_fields as $t_field)
2418             {
2419                 // Is there any alias?
2420                 $parts  = preg_split('#\sas\s#i', $t_field);
2421 
2422                 // Do I have a table.column situation? Let's get the field name
2423                 $tableField  = explode('.', $parts[0]);
2424 
2425                 if(isset($tableField[1]))
2426                 {
2427                     $column = trim($tableField[1]);
2428                 }
2429                 else
2430                 {
2431                     $column = trim($tableField[0]);
2432                 }
2433 
2434                 // Is this field quoted? If so, remove the quotes
2435                 if($this->isQuoted($column))
2436                 {
2437                     $column = substr($column, 1, strlen($column) - 2);
2438                 }
2439 
2440                 if(isset($parts[1]))
2441                 {
2442                     $alias = trim($parts[1]);
2443 
2444                     // Is this field quoted? If so, remove the quotes
2445                     if($this->isQuoted($alias))
2446                     {
2447                         $alias = substr($alias, 1, strlen($alias) - 2);
2448                     }
2449                 }
2450                 else
2451                 {
2452                     $alias = $column;
2453                 }
2454 
2455                 $return[$column] = $alias;
2456             }
2457         }
2458 
2459         return $return;
2460     }
2461 
2462     /**
2463      * Is the field quoted?
2464      *
2465      * @param   string  $column     Column field
2466      *
2467      * @return  bool    Is the field quoted?
2468      */
2469     protected function isQuoted($column)
2470     {
2471         // Empty string, un-quoted by definition
2472         if(!$column)
2473         {
2474             return false;
2475         }
2476 
2477         // I need some "magic". If the first char is not a letter, a number
2478         // an underscore or # (needed for table), then most likely the field is quoted
2479         preg_match_all('/^[a-z0-9_#]/i', $column, $matches);
2480 
2481         if(!$matches[0])
2482         {
2483             return true;
2484         }
2485 
2486         return false;
2487     }
2488 
2489     /**
2490      * The event which runs before binding data to the table
2491      *
2492      * NOTE TO 3RD PARTY DEVELOPERS:
2493      *
2494      * When you override the following methods in your child classes,
2495      * be sure to call parent::method *AFTER* your code, otherwise the
2496      * plugin events do NOT get triggered
2497      *
2498      * Example:
2499      * protected function onBeforeBind(){
2500      *       // Your code here
2501      *     return parent::onBeforeBind() && $your_result;
2502      * }
2503      *
2504      * Do not do it the other way around, e.g. return $your_result && parent::onBeforeBind()
2505      * Due to  PHP short-circuit boolean evaluation the parent::onBeforeBind()
2506      * will not be called if $your_result is false.
2507      *
2508      * @param   object|array  &$from  The data to bind
2509      *
2510      * @return  boolean  True on success
2511      */
2512     protected function onBeforeBind(&$from)
2513     {
2514         // Call the behaviors
2515         $result = $this->tableDispatcher->trigger('onBeforeBind', array(&$this, &$from));
2516 
2517         if (in_array(false, $result, true))
2518         {
2519             // Behavior failed, return false
2520             return false;
2521         }
2522 
2523         if ($this->_trigger_events)
2524         {
2525             $name = FOFInflector::pluralize($this->getKeyName());
2526 
2527             $result     = FOFPlatform::getInstance()->runPlugins('onBeforeBind' . ucfirst($name), array(&$this, &$from));
2528 
2529             if (in_array(false, $result, true))
2530             {
2531                 return false;
2532             }
2533             else
2534             {
2535                 return true;
2536             }
2537         }
2538 
2539         return true;
2540     }
2541 
2542     /**
2543      * The event which runs after loading a record from the database
2544      *
2545      * @param   boolean  &$result  Did the load succeeded?
2546      *
2547      * @return  void
2548      */
2549     protected function onAfterLoad(&$result)
2550     {
2551         // Call the behaviors
2552         $eventResult = $this->tableDispatcher->trigger('onAfterLoad', array(&$this, &$result));
2553 
2554         if (in_array(false, $eventResult, true))
2555         {
2556             // Behavior failed, return false
2557             $result = false;
2558             return false;
2559         }
2560 
2561         if ($this->_trigger_events)
2562         {
2563             $name = FOFInflector::pluralize($this->getKeyName());
2564 
2565             FOFPlatform::getInstance()->runPlugins('onAfterLoad' . ucfirst($name), array(&$this, &$result));
2566         }
2567     }
2568 
2569     /**
2570      * The event which runs before storing (saving) data to the database
2571      *
2572      * @param   boolean  $updateNulls  Should nulls be saved as nulls (true) or just skipped over (false)?
2573      *
2574      * @return  boolean  True to allow saving
2575      */
2576     protected function onBeforeStore($updateNulls)
2577     {
2578         // Do we have a "Created" set of fields?
2579         $created_on  = $this->getColumnAlias('created_on');
2580         $created_by  = $this->getColumnAlias('created_by');
2581         $modified_on = $this->getColumnAlias('modified_on');
2582         $modified_by = $this->getColumnAlias('modified_by');
2583         $locked_on   = $this->getColumnAlias('locked_on');
2584         $locked_by   = $this->getColumnAlias('locked_by');
2585         $title       = $this->getColumnAlias('title');
2586         $slug        = $this->getColumnAlias('slug');
2587 
2588         $hasCreatedOn = in_array($created_on, $this->getKnownFields());
2589         $hasCreatedBy = in_array($created_by, $this->getKnownFields());
2590 
2591         if ($hasCreatedOn && $hasCreatedBy)
2592         {
2593             $hasModifiedOn = in_array($modified_on, $this->getKnownFields());
2594             $hasModifiedBy = in_array($modified_by, $this->getKnownFields());
2595 
2596             $nullDate = $this->_db->getNullDate();
2597 
2598             if (empty($this->$created_by) || ($this->$created_on == $nullDate) || empty($this->$created_on))
2599             {
2600                 $uid = FOFPlatform::getInstance()->getUser()->id;
2601 
2602                 if ($uid)
2603                 {
2604                     $this->$created_by = FOFPlatform::getInstance()->getUser()->id;
2605                 }
2606 
2607                 $date = FOFPlatform::getInstance()->getDate('now', null, false);
2608 
2609                 $this->$created_on = method_exists($date, 'toSql') ? $date->toSql() : $date->toMySQL();
2610             }
2611             elseif ($hasModifiedOn && $hasModifiedBy)
2612             {
2613                 $uid = FOFPlatform::getInstance()->getUser()->id;
2614 
2615                 if ($uid)
2616                 {
2617                     $this->$modified_by = FOFPlatform::getInstance()->getUser()->id;
2618                 }
2619 
2620                 $date = FOFPlatform::getInstance()->getDate('now', null, false);
2621 
2622                 $this->$modified_on = method_exists($date, 'toSql') ? $date->toSql() : $date->toMySQL();
2623             }
2624         }
2625 
2626         // Do we have a set of title and slug fields?
2627         $hasTitle = in_array($title, $this->getKnownFields());
2628         $hasSlug  = in_array($slug, $this->getKnownFields());
2629 
2630         if ($hasTitle && $hasSlug)
2631         {
2632             if (empty($this->$slug))
2633             {
2634                 // Create a slug from the title
2635                 $this->$slug = FOFStringUtils::toSlug($this->$title);
2636             }
2637             else
2638             {
2639                 // Filter the slug for invalid characters
2640                 $this->$slug = FOFStringUtils::toSlug($this->$slug);
2641             }
2642 
2643             // Make sure we don't have a duplicate slug on this table
2644             $db    = $this->getDbo();
2645             $query = $db->getQuery(true)
2646                 ->select($db->qn($slug))
2647                 ->from($this->_tbl)
2648                 ->where($db->qn($slug) . ' = ' . $db->q($this->$slug))
2649                 ->where('NOT ' . $db->qn($this->_tbl_key) . ' = ' . $db->q($this->{$this->_tbl_key}));
2650             $db->setQuery($query);
2651             $existingItems = $db->loadAssocList();
2652 
2653             $count   = 0;
2654             $newSlug = $this->$slug;
2655 
2656             while (!empty($existingItems))
2657             {
2658                 $count++;
2659                 $newSlug = $this->$slug . '-' . $count;
2660                 $query   = $db->getQuery(true)
2661                     ->select($db->qn($slug))
2662                     ->from($this->_tbl)
2663                     ->where($db->qn($slug) . ' = ' . $db->q($newSlug))
2664                     ->where('NOT '. $db->qn($this->_tbl_key) . ' = ' . $db->q($this->{$this->_tbl_key}));
2665                 $db->setQuery($query);
2666                 $existingItems = $db->loadAssocList();
2667             }
2668 
2669             $this->$slug = $newSlug;
2670         }
2671 
2672         // Call the behaviors
2673         $result = $this->tableDispatcher->trigger('onBeforeStore', array(&$this, $updateNulls));
2674 
2675         if (in_array(false, $result, true))
2676         {
2677             // Behavior failed, return false
2678             return false;
2679         }
2680 
2681         // Execute onBeforeStore<tablename> events in loaded plugins
2682         if ($this->_trigger_events)
2683         {
2684             $name       = FOFInflector::pluralize($this->getKeyName());
2685             $result     = FOFPlatform::getInstance()->runPlugins('onBeforeStore' . ucfirst($name), array(&$this, $updateNulls));
2686 
2687             if (in_array(false, $result, true))
2688             {
2689                 return false;
2690             }
2691             else
2692             {
2693                 return true;
2694             }
2695         }
2696 
2697         return true;
2698     }
2699 
2700     /**
2701      * The event which runs after binding data to the class
2702      *
2703      * @param   object|array  &$src  The data to bind
2704      *
2705      * @return  boolean  True to allow binding without an error
2706      */
2707     protected function onAfterBind(&$src)
2708     {
2709         // Call the behaviors
2710         $options = array(
2711             'component'     => $this->input->get('option'),
2712             'view'          => $this->input->get('view'),
2713             'table_prefix'  => $this->_tablePrefix
2714         );
2715 
2716         $result = $this->tableDispatcher->trigger('onAfterBind', array(&$this, &$src, $options));
2717 
2718         if (in_array(false, $result, true))
2719         {
2720             // Behavior failed, return false
2721             return false;
2722         }
2723 
2724         if ($this->_trigger_events)
2725         {
2726             $name = FOFInflector::pluralize($this->getKeyName());
2727 
2728             $result     = FOFPlatform::getInstance()->runPlugins('onAfterBind' . ucfirst($name), array(&$this, &$src));
2729 
2730             if (in_array(false, $result, true))
2731             {
2732                 return false;
2733             }
2734             else
2735             {
2736                 return true;
2737             }
2738         }
2739 
2740         return true;
2741     }
2742 
2743     /**
2744      * The event which runs after storing (saving) data to the database
2745      *
2746      * @return  boolean  True to allow saving without an error
2747      */
2748     protected function onAfterStore()
2749     {
2750         // Call the behaviors
2751         $result = $this->tableDispatcher->trigger('onAfterStore', array(&$this));
2752 
2753         if (in_array(false, $result, true))
2754         {
2755             // Behavior failed, return false
2756             return false;
2757         }
2758 
2759         if ($this->_trigger_events)
2760         {
2761             $name = FOFInflector::pluralize($this->getKeyName());
2762 
2763             $result     = FOFPlatform::getInstance()->runPlugins('onAfterStore' . ucfirst($name), array(&$this));
2764 
2765             if (in_array(false, $result, true))
2766             {
2767                 return false;
2768             }
2769             else
2770             {
2771                 return true;
2772             }
2773         }
2774 
2775         return true;
2776     }
2777 
2778     /**
2779      * The event which runs before moving a record
2780      *
2781      * @param   boolean  $updateNulls  Should nulls be saved as nulls (true) or just skipped over (false)?
2782      *
2783      * @return  boolean  True to allow moving
2784      */
2785     protected function onBeforeMove($updateNulls)
2786     {
2787         // Call the behaviors
2788         $result = $this->tableDispatcher->trigger('onBeforeMove', array(&$this, $updateNulls));
2789 
2790         if (in_array(false, $result, true))
2791         {
2792             // Behavior failed, return false
2793             return false;
2794         }
2795 
2796         if ($this->_trigger_events)
2797         {
2798             $name = FOFInflector::pluralize($this->getKeyName());
2799 
2800             $result     = FOFPlatform::getInstance()->runPlugins('onBeforeMove' . ucfirst($name), array(&$this, $updateNulls));
2801 
2802             if (in_array(false, $result, true))
2803             {
2804                 return false;
2805             }
2806             else
2807             {
2808                 return true;
2809             }
2810         }
2811 
2812         return true;
2813     }
2814 
2815     /**
2816      * The event which runs after moving a record
2817      *
2818      * @return  boolean  True to allow moving without an error
2819      */
2820     protected function onAfterMove()
2821     {
2822         // Call the behaviors
2823         $result = $this->tableDispatcher->trigger('onAfterMove', array(&$this));
2824 
2825         if (in_array(false, $result, true))
2826         {
2827             // Behavior failed, return false
2828             return false;
2829         }
2830 
2831         if ($this->_trigger_events)
2832         {
2833             $name = FOFInflector::pluralize($this->getKeyName());
2834 
2835             $result     = FOFPlatform::getInstance()->runPlugins('onAfterMove' . ucfirst($name), array(&$this));
2836 
2837             if (in_array(false, $result, true))
2838             {
2839                 return false;
2840             }
2841             else
2842             {
2843                 return true;
2844             }
2845         }
2846 
2847         return true;
2848     }
2849 
2850     /**
2851      * The event which runs before reordering a table
2852      *
2853      * @param   string  $where  The WHERE clause of the SQL query to run on reordering (record filter)
2854      *
2855      * @return  boolean  True to allow reordering
2856      */
2857     protected function onBeforeReorder($where = '')
2858     {
2859         // Call the behaviors
2860         $result = $this->tableDispatcher->trigger('onBeforeReorder', array(&$this, $where));
2861 
2862         if (in_array(false, $result, true))
2863         {
2864             // Behavior failed, return false
2865             return false;
2866         }
2867 
2868         if ($this->_trigger_events)
2869         {
2870             $name = FOFInflector::pluralize($this->getKeyName());
2871 
2872             $result     = FOFPlatform::getInstance()->runPlugins('onBeforeReorder' . ucfirst($name), array(&$this, $where));
2873 
2874             if (in_array(false, $result, true))
2875             {
2876                 return false;
2877             }
2878             else
2879             {
2880                 return true;
2881             }
2882         }
2883 
2884         return true;
2885     }
2886 
2887     /**
2888      * The event which runs after reordering a table
2889      *
2890      * @return  boolean  True to allow the reordering to complete without an error
2891      */
2892     protected function onAfterReorder()
2893     {
2894         // Call the behaviors
2895         $result = $this->tableDispatcher->trigger('onAfterReorder', array(&$this));
2896 
2897         if (in_array(false, $result, true))
2898         {
2899             // Behavior failed, return false
2900             return false;
2901         }
2902 
2903         if ($this->_trigger_events)
2904         {
2905             $name = FOFInflector::pluralize($this->getKeyName());
2906 
2907             $result     = FOFPlatform::getInstance()->runPlugins('onAfterReorder' . ucfirst($name), array(&$this));
2908 
2909             if (in_array(false, $result, true))
2910             {
2911                 return false;
2912             }
2913             else
2914             {
2915                 return true;
2916             }
2917         }
2918 
2919         return true;
2920     }
2921 
2922     /**
2923      * The event which runs before deleting a record
2924      *
2925      * @param   integer  $oid  The PK value of the record to delete
2926      *
2927      * @return  boolean  True to allow the deletion
2928      */
2929     protected function onBeforeDelete($oid)
2930     {
2931         // Call the behaviors
2932         $result = $this->tableDispatcher->trigger('onBeforeDelete', array(&$this, $oid));
2933 
2934         if (in_array(false, $result, true))
2935         {
2936             // Behavior failed, return false
2937             return false;
2938         }
2939 
2940         if ($this->_trigger_events)
2941         {
2942             $name = FOFInflector::pluralize($this->getKeyName());
2943 
2944             $result     = FOFPlatform::getInstance()->runPlugins('onBeforeDelete' . ucfirst($name), array(&$this, $oid));
2945 
2946             if (in_array(false, $result, true))
2947             {
2948                 return false;
2949             }
2950             else
2951             {
2952                 return true;
2953             }
2954         }
2955 
2956         return true;
2957     }
2958 
2959     /**
2960      * The event which runs after deleting a record
2961      *
2962      * @param   integer  $oid  The PK value of the record which was deleted
2963      *
2964      * @return  boolean  True to allow the deletion without errors
2965      */
2966     protected function onAfterDelete($oid)
2967     {
2968         // Call the behaviors
2969         $result = $this->tableDispatcher->trigger('onAfterDelete', array(&$this, $oid));
2970 
2971         if (in_array(false, $result, true))
2972         {
2973             // Behavior failed, return false
2974             return false;
2975         }
2976 
2977         if ($this->_trigger_events)
2978         {
2979             $name = FOFInflector::pluralize($this->getKeyName());
2980 
2981             $result     = FOFPlatform::getInstance()->runPlugins('onAfterDelete' . ucfirst($name), array(&$this, $oid));
2982 
2983             if (in_array(false, $result, true))
2984             {
2985                 return false;
2986             }
2987             else
2988             {
2989                 return true;
2990             }
2991         }
2992 
2993         return true;
2994     }
2995 
2996     /**
2997      * The event which runs before hitting a record
2998      *
2999      * @param   integer  $oid  The PK value of the record to hit
3000      * @param   boolean  $log  Should we log the hit?
3001      *
3002      * @return  boolean  True to allow the hit
3003      */
3004     protected function onBeforeHit($oid, $log)
3005     {
3006         // Call the behaviors
3007         $result = $this->tableDispatcher->trigger('onBeforeHit', array(&$this, $oid, $log));
3008 
3009         if (in_array(false, $result, true))
3010         {
3011             // Behavior failed, return false
3012             return false;
3013         }
3014 
3015         if ($this->_trigger_events)
3016         {
3017             $name = FOFInflector::pluralize($this->getKeyName());
3018 
3019             $result     = FOFPlatform::getInstance()->runPlugins('onBeforeHit' . ucfirst($name), array(&$this, $oid, $log));
3020 
3021             if (in_array(false, $result, true))
3022             {
3023                 return false;
3024             }
3025             else
3026             {
3027                 return true;
3028             }
3029         }
3030 
3031         return true;
3032     }
3033 
3034     /**
3035      * The event which runs after hitting a record
3036      *
3037      * @param   integer  $oid  The PK value of the record which was hit
3038      *
3039      * @return  boolean  True to allow the hitting without errors
3040      */
3041     protected function onAfterHit($oid)
3042     {
3043         // Call the behaviors
3044         $result = $this->tableDispatcher->trigger('onAfterHit', array(&$this, $oid));
3045 
3046         if (in_array(false, $result, true))
3047         {
3048             // Behavior failed, return false
3049             return false;
3050         }
3051 
3052         if ($this->_trigger_events)
3053         {
3054             $name = FOFInflector::pluralize($this->getKeyName());
3055 
3056             $result     = FOFPlatform::getInstance()->runPlugins('onAfterHit' . ucfirst($name), array(&$this, $oid));
3057 
3058             if (in_array(false, $result, true))
3059             {
3060                 return false;
3061             }
3062             else
3063             {
3064                 return true;
3065             }
3066         }
3067 
3068         return true;
3069     }
3070 
3071     /**
3072      * The even which runs before copying a record
3073      *
3074      * @param   integer  $oid  The PK value of the record being copied
3075      *
3076      * @return  boolean  True to allow the copy to take place
3077      */
3078     protected function onBeforeCopy($oid)
3079     {
3080         // Call the behaviors
3081         $result = $this->tableDispatcher->trigger('onBeforeCopy', array(&$this, $oid));
3082 
3083         if (in_array(false, $result, true))
3084         {
3085             // Behavior failed, return false
3086             return false;
3087         }
3088 
3089         if ($this->_trigger_events)
3090         {
3091             $name = FOFInflector::pluralize($this->getKeyName());
3092 
3093             $result     = FOFPlatform::getInstance()->runPlugins('onBeforeCopy' . ucfirst($name), array(&$this, $oid));
3094 
3095             if (in_array(false, $result, true))
3096             {
3097                 return false;
3098             }
3099             else
3100             {
3101                 return true;
3102             }
3103         }
3104 
3105         return true;
3106     }
3107 
3108     /**
3109      * The even which runs after copying a record
3110      *
3111      * @param   integer  $oid  The PK value of the record which was copied (not the new one)
3112      *
3113      * @return  boolean  True to allow the copy without errors
3114      */
3115     protected function onAfterCopy($oid)
3116     {
3117         // Call the behaviors
3118         $result = $this->tableDispatcher->trigger('onAfterCopy', array(&$this, $oid));
3119 
3120         if (in_array(false, $result, true))
3121         {
3122             // Behavior failed, return false
3123             return false;
3124         }
3125 
3126         if ($this->_trigger_events)
3127         {
3128             $name = FOFInflector::pluralize($this->getKeyName());
3129 
3130             $result     = FOFPlatform::getInstance()->runPlugins('onAfterCopy' . ucfirst($name), array(&$this, $oid));
3131 
3132             if (in_array(false, $result, true))
3133             {
3134                 return false;
3135             }
3136             else
3137             {
3138                 return true;
3139             }
3140         }
3141 
3142         return true;
3143     }
3144 
3145     /**
3146      * The event which runs before a record is (un)published
3147      *
3148      * @param   integer|array  &$cid     The PK IDs of the records being (un)published
3149      * @param   integer        $publish  1 to publish, 0 to unpublish
3150      *
3151      * @return  boolean  True to allow the (un)publish to proceed
3152      */
3153     protected function onBeforePublish(&$cid, $publish)
3154     {
3155         // Call the behaviors
3156         $result = $this->tableDispatcher->trigger('onBeforePublish', array(&$this, &$cid, $publish));
3157 
3158         if (in_array(false, $result, true))
3159         {
3160             // Behavior failed, return false
3161             return false;
3162         }
3163 
3164         if ($this->_trigger_events)
3165         {
3166             $name = FOFInflector::pluralize($this->getKeyName());
3167 
3168             $result     = FOFPlatform::getInstance()->runPlugins('onBeforePublish' . ucfirst($name), array(&$this, &$cid, $publish));
3169 
3170             if (in_array(false, $result, true))
3171             {
3172                 return false;
3173             }
3174             else
3175             {
3176                 return true;
3177             }
3178         }
3179 
3180         return true;
3181     }
3182 
3183     /**
3184      * The event which runs after the object is reset to its default values.
3185      *
3186      * @return  boolean  True to allow the reset to complete without errors
3187      */
3188     protected function onAfterReset()
3189     {
3190         // Call the behaviors
3191         $result = $this->tableDispatcher->trigger('onAfterReset', array(&$this));
3192 
3193         if (in_array(false, $result, true))
3194         {
3195             // Behavior failed, return false
3196             return false;
3197         }
3198 
3199         if ($this->_trigger_events)
3200         {
3201             $name = FOFInflector::pluralize($this->getKeyName());
3202 
3203             $result     = FOFPlatform::getInstance()->runPlugins('onAfterReset' . ucfirst($name), array(&$this));
3204 
3205             if (in_array(false, $result, true))
3206             {
3207                 return false;
3208             }
3209             else
3210             {
3211                 return true;
3212             }
3213         }
3214 
3215         return true;
3216     }
3217 
3218     /**
3219      * The even which runs before the object is reset to its default values.
3220      *
3221      * @return  boolean  True to allow the reset to complete
3222      */
3223     protected function onBeforeReset()
3224     {
3225         // Call the behaviors
3226         $result = $this->tableDispatcher->trigger('onBeforeReset', array(&$this));
3227 
3228         if (in_array(false, $result, true))
3229         {
3230             // Behavior failed, return false
3231             return false;
3232         }
3233 
3234         if ($this->_trigger_events)
3235         {
3236             $name = FOFInflector::pluralize($this->getKeyName());
3237 
3238             $result     = FOFPlatform::getInstance()->runPlugins('onBeforeReset' . ucfirst($name), array(&$this));
3239 
3240             if (in_array(false, $result, true))
3241             {
3242                 return false;
3243             }
3244             else
3245             {
3246                 return true;
3247             }
3248         }
3249 
3250         return true;
3251     }
3252 
3253     /**
3254      * Replace the input object of this table with the provided FOFInput object
3255      *
3256      * @param   FOFInput  $input  The new input object
3257      *
3258      * @return  void
3259      */
3260     public function setInput(FOFInput $input)
3261     {
3262         $this->input = $input;
3263     }
3264 
3265     /**
3266      * Get the columns from database table.
3267      *
3268      * @return  mixed  An array of the field names, or false if an error occurs.
3269      *
3270      * @deprecated  2.1
3271      */
3272     public function getFields()
3273     {
3274         return $this->getTableFields();
3275     }
3276 
3277     /**
3278      * Add a filesystem path where FOFTable should search for table class files.
3279      * You may either pass a string or an array of paths.
3280      *
3281      * @param   mixed  $path  A filesystem path or array of filesystem paths to add.
3282      *
3283      * @return  array  An array of filesystem paths to find FOFTable classes in.
3284      */
3285     public static function addIncludePath($path = null)
3286     {
3287         // If the internal paths have not been initialised, do so with the base table path.
3288         if (empty(self::$_includePaths))
3289         {
3290             self::$_includePaths = array(__DIR__);
3291         }
3292 
3293         // Convert the passed path(s) to add to an array.
3294         settype($path, 'array');
3295 
3296         // If we have new paths to add, do so.
3297         if (!empty($path) && !in_array($path, self::$_includePaths))
3298         {
3299             // Check and add each individual new path.
3300             foreach ($path as $dir)
3301             {
3302                 // Sanitize path.
3303                 $dir = trim($dir);
3304 
3305                 // Add to the front of the list so that custom paths are searched first.
3306                 array_unshift(self::$_includePaths, $dir);
3307             }
3308         }
3309 
3310         return self::$_includePaths;
3311     }
3312 
3313     /**
3314      * Loads the asset table related to this table.
3315      * This will help tests, too, since we can mock this function.
3316      *
3317      * @return bool|JTableAsset     False on failure, otherwise JTableAsset
3318      */
3319     protected function getAsset()
3320     {
3321         $name     = $this->_getAssetName();
3322 
3323         // Do NOT touch JTable here -- we are loading the core asset table which is a JTable, not a FOFTable
3324         $asset    = JTable::getInstance('Asset');
3325 
3326         if (!$asset->loadByName($name))
3327         {
3328             return false;
3329         }
3330 
3331         return $asset;
3332     }
3333 
3334     /**
3335      * Method to compute the default name of the asset.
3336      * The default name is in the form table_name.id
3337      * where id is the value of the primary key of the table.
3338      *
3339      * @throws  UnexpectedValueException
3340      *
3341      * @return  string
3342      */
3343     public function getAssetName()
3344     {
3345         $k = $this->_tbl_key;
3346 
3347         // If there is no assetKey defined, stop here, or we'll get a wrong name
3348         if(!$this->_assetKey || !$this->$k)
3349         {
3350             throw new UnexpectedValueException('Table must have an asset key defined and a value for the table id in order to track assets');
3351         }
3352 
3353         return $this->_assetKey . '.' . (int) $this->$k;
3354     }
3355 
3356     /**
3357      * Method to compute the default name of the asset.
3358      * The default name is in the form table_name.id
3359      * where id is the value of the primary key of the table.
3360      *
3361      * @throws  UnexpectedValueException
3362      *
3363      * @return  string
3364      */
3365     public function getAssetKey()
3366     {
3367         return $this->_assetKey;
3368     }
3369 
3370     /**
3371      * Method to return the title to use for the asset table.  In
3372      * tracking the assets a title is kept for each asset so that there is some
3373      * context available in a unified access manager.  Usually this would just
3374      * return $this->title or $this->name or whatever is being used for the
3375      * primary name of the row. If this method is not overridden, the asset name is used.
3376      *
3377      * @return  string  The string to use as the title in the asset table.
3378      */
3379     public function getAssetTitle()
3380     {
3381         return $this->getAssetName();
3382     }
3383 
3384     /**
3385      * Method to get the parent asset under which to register this one.
3386      * By default, all assets are registered to the ROOT node with ID,
3387      * which will default to 1 if none exists.
3388      * The extended class can define a table and id to lookup.  If the
3389      * asset does not exist it will be created.
3390      *
3391      * @param   FOFTable  $table  A FOFTable object for the asset parent.
3392      * @param   integer   $id     Id to look up
3393      *
3394      * @return  integer
3395      */
3396     public function getAssetParentId($table = null, $id = null)
3397     {
3398         // For simple cases, parent to the asset root.
3399         $assets = JTable::getInstance('Asset', 'JTable', array('dbo' => $this->getDbo()));
3400         $rootId = $assets->getRootId();
3401 
3402         if (!empty($rootId))
3403         {
3404             return $rootId;
3405         }
3406 
3407         return 1;
3408     }
3409 
3410     /**
3411      * This method sets the asset key for the items of this table. Obviously, it
3412      * is only meant to be used when you have a table with an asset field.
3413      *
3414      * @param   string  $assetKey  The name of the asset key to use
3415      *
3416      * @return  void
3417      */
3418     public function setAssetKey($assetKey)
3419     {
3420         $this->_assetKey = $assetKey;
3421     }
3422 
3423     /**
3424      * Method to get the database table name for the class.
3425      *
3426      * @return  string  The name of the database table being modeled.
3427      */
3428     public function getTableName()
3429     {
3430         return $this->_tbl;
3431     }
3432 
3433     /**
3434      * Method to get the primary key field name for the table.
3435      *
3436      * @return  string  The name of the primary key for the table.
3437      */
3438     public function getKeyName()
3439     {
3440         return $this->_tbl_key;
3441     }
3442 
3443     /**
3444      * Returns the identity value of this record
3445      *
3446      * @return mixed
3447      */
3448     public function getId()
3449     {
3450         $key = $this->getKeyName();
3451 
3452         return $this->$key;
3453     }
3454 
3455     /**
3456      * Method to get the FOFDatabaseDriver object.
3457      *
3458      * @return  FOFDatabaseDriver  The internal database driver object.
3459      */
3460     public function getDbo()
3461     {
3462         return $this->_db;
3463     }
3464 
3465     /**
3466      * Method to set the FOFDatabaseDriver object.
3467      *
3468      * @param   FOFDatabaseDriver  $db  A FOFDatabaseDriver object to be used by the table object.
3469      *
3470      * @return  boolean  True on success.
3471      */
3472     public function setDBO($db)
3473     {
3474         $this->_db = $db;
3475 
3476         return true;
3477     }
3478 
3479     /**
3480      * Method to set rules for the record.
3481      *
3482      * @param   mixed  $input  A JAccessRules object, JSON string, or array.
3483      *
3484      * @return  void
3485      */
3486     public function setRules($input)
3487     {
3488         if ($input instanceof JAccessRules)
3489         {
3490             $this->_rules = $input;
3491         }
3492         else
3493         {
3494             $this->_rules = new JAccessRules($input);
3495         }
3496     }
3497 
3498     /**
3499      * Method to get the rules for the record.
3500      *
3501      * @return  JAccessRules object
3502      */
3503     public function getRules()
3504     {
3505         return $this->_rules;
3506     }
3507 
3508     /**
3509      * Method to check if the record is treated as an ACL asset
3510      *
3511      * @return  boolean [description]
3512      */
3513     public function isAssetsTracked()
3514     {
3515         return $this->_trackAssets;
3516     }
3517 
3518     /**
3519      * Method to manually set this record as ACL asset or not.
3520      * We have to do this since the automatic check is made in the constructor, but here we can't set any alias.
3521      * So, even if you have an alias for `asset_id`, it wouldn't be reconized and assets won't be tracked.
3522      *
3523      * @param $state
3524      */
3525     public function setAssetsTracked($state)
3526     {
3527         $state = (bool) $state;
3528 
3529         if($state)
3530         {
3531             JLoader::import('joomla.access.rules');
3532         }
3533 
3534         $this->_trackAssets = $state;
3535     }
3536 
3537     /**
3538      * Method to provide a shortcut to binding, checking and storing a FOFTable
3539      * instance to the database table.  The method will check a row in once the
3540      * data has been stored and if an ordering filter is present will attempt to
3541      * reorder the table rows based on the filter.  The ordering filter is an instance
3542      * property name.  The rows that will be reordered are those whose value matches
3543      * the FOFTable instance for the property specified.
3544      *
3545      * @param   mixed   $src             An associative array or object to bind to the FOFTable instance.
3546      * @param   string  $orderingFilter  Filter for the order updating
3547      * @param   mixed   $ignore          An optional array or space separated list of properties
3548      *                                   to ignore while binding.
3549      *
3550      * @return  boolean  True on success.
3551      */
3552     public function save($src, $orderingFilter = '', $ignore = '')
3553     {
3554         // Attempt to bind the source to the instance.
3555         if (!$this->bind($src, $ignore))
3556         {
3557             return false;
3558         }
3559 
3560         // Run any sanity checks on the instance and verify that it is ready for storage.
3561         if (!$this->check())
3562         {
3563             return false;
3564         }
3565 
3566         // Attempt to store the properties to the database table.
3567         if (!$this->store())
3568         {
3569             return false;
3570         }
3571 
3572         // Attempt to check the row in, just in case it was checked out.
3573         if (!$this->checkin())
3574         {
3575             return false;
3576         }
3577 
3578         // If an ordering filter is set, attempt reorder the rows in the table based on the filter and value.
3579         if ($orderingFilter)
3580         {
3581             $filterValue = $this->$orderingFilter;
3582             $this->reorder($orderingFilter ? $this->_db->qn($orderingFilter) . ' = ' . $this->_db->q($filterValue) : '');
3583         }
3584 
3585         // Set the error to empty and return true.
3586         $this->setError('');
3587 
3588         return true;
3589     }
3590 
3591     /**
3592      * Method to get the next ordering value for a group of rows defined by an SQL WHERE clause.
3593      * This is useful for placing a new item last in a group of items in the table.
3594      *
3595      * @param   string  $where  WHERE clause to use for selecting the MAX(ordering) for the table.
3596      *
3597      * @return  mixed  Boolean false an failure or the next ordering value as an integer.
3598      */
3599     public function getNextOrder($where = '')
3600     {
3601         // If there is no ordering field set an error and return false.
3602         $ordering = $this->getColumnAlias('ordering');
3603         if (!in_array($ordering, $this->getKnownFields()))
3604         {
3605             throw new UnexpectedValueException(sprintf('%s does not support ordering.', get_class($this)));
3606         }
3607 
3608         // Get the largest ordering value for a given where clause.
3609         $query = $this->_db->getQuery(true);
3610         $query->select('MAX('.$this->_db->qn($ordering).')');
3611         $query->from($this->_tbl);
3612 
3613         if ($where)
3614         {
3615             $query->where($where);
3616         }
3617 
3618         $this->_db->setQuery($query);
3619         $max = (int) $this->_db->loadResult();
3620 
3621         // Return the largest ordering value + 1.
3622         return ($max + 1);
3623     }
3624 
3625     /**
3626      * Method to lock the database table for writing.
3627      *
3628      * @return  boolean  True on success.
3629      *
3630      * @throws  RuntimeException
3631      */
3632     protected function _lock()
3633     {
3634         $this->_db->lockTable($this->_tbl);
3635         $this->_locked = true;
3636 
3637         return true;
3638     }
3639 
3640     /**
3641      * Method to unlock the database table for writing.
3642      *
3643      * @return  boolean  True on success.
3644      */
3645     protected function _unlock()
3646     {
3647         $this->_db->unlockTables();
3648         $this->_locked = false;
3649 
3650         return true;
3651     }
3652 
3653     public function setConfig(array $config)
3654     {
3655         $this->config = $config;
3656     }
3657 
3658     /**
3659      * Get the content type for ucm
3660      *
3661      * @return string The content type alias
3662      */
3663     public function getContentType()
3664     {
3665         if ($this->contentType)
3666         {
3667             return $this->contentType;
3668         }
3669 
3670         /**
3671          * When tags was first introduced contentType variable didn't exist - so we guess one
3672          * This will fail if content history behvaiour is enabled. This code is deprecated
3673          * and will be removed in FOF 3.0 in favour of the content type class variable
3674          */
3675         $component = $this->input->get('option');
3676 
3677         $view = FOFInflector::singularize($this->input->get('view'));
3678         $alias = $component . '.' . $view;
3679 
3680         return $alias;
3681     }
3682 
3683     /**
3684      * Returns the table relations object of the current table, lazy-loading it if necessary
3685      *
3686      * @return  FOFTableRelations
3687      */
3688     public function getRelations()
3689     {
3690         if (is_null($this->_relations))
3691         {
3692             $this->_relations = new FOFTableRelations($this);
3693         }
3694 
3695         return $this->_relations;
3696     }
3697 
3698     /**
3699      * Gets a reference to the configuration parameters provider for this table
3700      *
3701      * @return  FOFConfigProvider
3702      */
3703     public function getConfigProvider()
3704     {
3705         return $this->configProvider;
3706     }
3707 
3708     /**
3709      * Returns the configuration parameters provider's key for this table
3710      *
3711      * @return  string
3712      */
3713     public function getConfigProviderKey()
3714     {
3715         return $this->_configProviderKey;
3716     }
3717 
3718     /**
3719      * Check if a UCM content type exists for this resource, and
3720      * create it if it does not
3721      *
3722      * @param  string  $alias  The content type alias (optional)
3723      *
3724      * @return  null
3725      */
3726     public function checkContentType($alias = null)
3727     {
3728         $contentType = new JTableContenttype($this->getDbo());
3729 
3730         if (!$alias)
3731         {
3732             $alias = $this->getContentType();
3733         }
3734 
3735         $aliasParts = explode('.', $alias);
3736 
3737         // Fetch the extension name
3738         $component = $aliasParts[0];
3739         $component = JComponentHelper::getComponent($component);
3740 
3741         // Fetch the name using the menu item
3742         $query = $this->getDbo()->getQuery(true);
3743         $query->select('title')->from('#__menu')->where('component_id = ' . (int) $component->id);
3744         $this->getDbo()->setQuery($query);
3745         $component_name = JText::_($this->getDbo()->loadResult());
3746 
3747         $name = $component_name . ' ' . ucfirst($aliasParts[1]);
3748 
3749         // Create a new content type for our resource
3750         if (!$contentType->load(array('type_alias' => $alias)))
3751         {
3752             $contentType->type_title = $name;
3753             $contentType->type_alias = $alias;
3754             $contentType->table = json_encode(
3755                 array(
3756                     'special' => array(
3757                         'dbtable' => $this->getTableName(),
3758                         'key'     => $this->getKeyName(),
3759                         'type'    => $name,
3760                         'prefix'  => $this->_tablePrefix,
3761                         'class'   => 'FOFTable',
3762                         'config'  => 'array()'
3763                     ),
3764                     'common' => array(
3765                         'dbtable' => '#__ucm_content',
3766                         'key' => 'ucm_id',
3767                         'type' => 'CoreContent',
3768                         'prefix' => 'JTable',
3769                         'config' => 'array()'
3770                     )
3771                 )
3772             );
3773 
3774             $contentType->field_mappings = json_encode(
3775                 array(
3776                     'common' => array(
3777                         0 => array(
3778                             "core_content_item_id" => $this->getKeyName(),
3779                             "core_title"           => $this->getUcmCoreAlias('title'),
3780                             "core_state"           => $this->getUcmCoreAlias('enabled'),
3781                             "core_alias"           => $this->getUcmCoreAlias('alias'),
3782                             "core_created_time"    => $this->getUcmCoreAlias('created_on'),
3783                             "core_modified_time"   => $this->getUcmCoreAlias('created_by'),
3784                             "core_body"            => $this->getUcmCoreAlias('body'),
3785                             "core_hits"            => $this->getUcmCoreAlias('hits'),
3786                             "core_publish_up"      => $this->getUcmCoreAlias('publish_up'),
3787                             "core_publish_down"    => $this->getUcmCoreAlias('publish_down'),
3788                             "core_access"          => $this->getUcmCoreAlias('access'),
3789                             "core_params"          => $this->getUcmCoreAlias('params'),
3790                             "core_featured"        => $this->getUcmCoreAlias('featured'),
3791                             "core_metadata"        => $this->getUcmCoreAlias('metadata'),
3792                             "core_language"        => $this->getUcmCoreAlias('language'),
3793                             "core_images"          => $this->getUcmCoreAlias('images'),
3794                             "core_urls"            => $this->getUcmCoreAlias('urls'),
3795                             "core_version"         => $this->getUcmCoreAlias('version'),
3796                             "core_ordering"        => $this->getUcmCoreAlias('ordering'),
3797                             "core_metakey"         => $this->getUcmCoreAlias('metakey'),
3798                             "core_metadesc"        => $this->getUcmCoreAlias('metadesc'),
3799                             "core_catid"           => $this->getUcmCoreAlias('cat_id'),
3800                             "core_xreference"      => $this->getUcmCoreAlias('xreference'),
3801                             "asset_id"             => $this->getUcmCoreAlias('asset_id')
3802                         )
3803                     ),
3804                     'special' => array(
3805                         0 => array(
3806                         )
3807                     )
3808                 )
3809             );
3810 
3811             $ignoreFields = array(
3812                 $this->getUcmCoreAlias('modified_on', null),
3813                 $this->getUcmCoreAlias('modified_by', null),
3814                 $this->getUcmCoreAlias('locked_by', null),
3815                 $this->getUcmCoreAlias('locked_on', null),
3816                 $this->getUcmCoreAlias('hits', null),
3817                 $this->getUcmCoreAlias('version', null)
3818             );
3819 
3820             $contentType->content_history_options = json_encode(
3821                 array(
3822                     "ignoreChanges" => array_filter($ignoreFields, 'strlen')
3823                 )
3824             );
3825 
3826             $contentType->router = '';
3827 
3828             $contentType->store();
3829         }
3830     }
3831 
3832     /**
3833      * Utility methods that fetches the column name for the field.
3834      * If it does not exists, returns a "null" string
3835      *
3836      * @param  string  $alias  The alias for the column
3837      * @param  string  $null   What to return if no column exists
3838      *
3839      * @return string The column name
3840      */
3841     protected function getUcmCoreAlias($alias, $null = "null")
3842     {
3843         $alias = $this->getColumnAlias($alias);
3844 
3845         if (in_array($alias, $this->getKnownFields()))
3846         {
3847             return $alias;
3848         }
3849 
3850         return $null;
3851     }
3852 }
3853 
Joomla! Framework TM API documentation generated by ApiGen 2.8.0
Joomla!® and Joomla! Framework™ are trademarks of Open Source Matters, Inc. in the United States and other countries.