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     Joomla.Platform
   4  * @subpackage  Table
   5  *
   6  * @copyright   Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved.
   7  * @license     GNU General Public License version 2 or later; see LICENSE
   8  */
   9 
  10 defined('JPATH_PLATFORM') or die;
  11 
  12 use Joomla\Utilities\ArrayHelper;
  13 
  14 /**
  15  * Table class supporting modified pre-order tree traversal behavior.
  16  *
  17  * @since  11.1
  18  */
  19 class JTableNested extends JTable
  20 {
  21     /**
  22      * Object property holding the primary key of the parent node.  Provides adjacency list data for nodes.
  23      *
  24      * @var    integer
  25      * @since  11.1
  26      */
  27     public $parent_id;
  28 
  29     /**
  30      * Object property holding the depth level of the node in the tree.
  31      *
  32      * @var    integer
  33      * @since  11.1
  34      */
  35     public $level;
  36 
  37     /**
  38      * Object property holding the left value of the node for managing its placement in the nested sets tree.
  39      *
  40      * @var    integer
  41      * @since  11.1
  42      */
  43     public $lft;
  44 
  45     /**
  46      * Object property holding the right value of the node for managing its placement in the nested sets tree.
  47      *
  48      * @var    integer
  49      * @since  11.1
  50      */
  51     public $rgt;
  52 
  53     /**
  54      * Object property holding the alias of this node used to constuct the full text path, forward-slash delimited.
  55      *
  56      * @var    string
  57      * @since  11.1
  58      */
  59     public $alias;
  60 
  61     /**
  62      * Object property to hold the location type to use when storing the row.
  63      *
  64      * @var    string
  65      * @since  11.1
  66      * @see    JTableNested::$_validLocations
  67      */
  68     protected $_location;
  69 
  70     /**
  71      * Object property to hold the primary key of the location reference node to use when storing the row.
  72      *
  73      * A combination of location type and reference node describes where to store the current node in the tree.
  74      *
  75      * @var    integer
  76      * @since  11.1
  77      */
  78     protected $_location_id;
  79 
  80     /**
  81      * An array to cache values in recursive processes.
  82      *
  83      * @var    array
  84      * @since  11.1
  85      */
  86     protected $_cache = array();
  87 
  88     /**
  89      * Debug level
  90      *
  91      * @var    integer
  92      * @since  11.1
  93      */
  94     protected $_debug = 0;
  95 
  96     /**
  97      * Cache for the root ID
  98      *
  99      * @var    integer
 100      * @since  3.3
 101      */
 102     protected static $root_id = 0;
 103 
 104     /**
 105      * Array declaring the valid location values for moving a node
 106      *
 107      * @var    array
 108      * @since  3.7.0
 109      */
 110     private $_validLocations = array('before', 'after', 'first-child', 'last-child');
 111 
 112     /**
 113      * Sets the debug level on or off
 114      *
 115      * @param   integer  $level  0 = off, 1 = on
 116      *
 117      * @return  void
 118      *
 119      * @since   11.1
 120      */
 121     public function debug($level)
 122     {
 123         $this->_debug = (int) $level;
 124     }
 125 
 126     /**
 127      * Method to get an array of nodes from a given node to its root.
 128      *
 129      * @param   integer  $pk          Primary key of the node for which to get the path.
 130      * @param   boolean  $diagnostic  Only select diagnostic data for the nested sets.
 131      *
 132      * @return  mixed    An array of node objects including the start node.
 133      *
 134      * @since   11.1
 135      * @throws  RuntimeException on database error
 136      */
 137     public function getPath($pk = null, $diagnostic = false)
 138     {
 139         $k = $this->_tbl_key;
 140         $pk = (is_null($pk)) ? $this->$k : $pk;
 141 
 142         // Get the path from the node to the root.
 143         $select = ($diagnostic) ? 'p.' . $k . ', p.parent_id, p.level, p.lft, p.rgt' : 'p.*';
 144         $query = $this->_db->getQuery(true)
 145             ->select($select)
 146             ->from($this->_tbl . ' AS n, ' . $this->_tbl . ' AS p')
 147             ->where('n.lft BETWEEN p.lft AND p.rgt')
 148             ->where('n.' . $k . ' = ' . (int) $pk)
 149             ->order('p.lft');
 150 
 151         $this->_db->setQuery($query);
 152 
 153         return $this->_db->loadObjectList();
 154     }
 155 
 156     /**
 157      * Method to get a node and all its child nodes.
 158      *
 159      * @param   integer  $pk          Primary key of the node for which to get the tree.
 160      * @param   boolean  $diagnostic  Only select diagnostic data for the nested sets.
 161      *
 162      * @return  mixed    Boolean false on failure or array of node objects on success.
 163      *
 164      * @since   11.1
 165      * @throws  RuntimeException on database error.
 166      */
 167     public function getTree($pk = null, $diagnostic = false)
 168     {
 169         $k = $this->_tbl_key;
 170         $pk = (is_null($pk)) ? $this->$k : $pk;
 171 
 172         // Get the node and children as a tree.
 173         $select = ($diagnostic) ? 'n.' . $k . ', n.parent_id, n.level, n.lft, n.rgt' : 'n.*';
 174         $query = $this->_db->getQuery(true)
 175             ->select($select)
 176             ->from($this->_tbl . ' AS n, ' . $this->_tbl . ' AS p')
 177             ->where('n.lft BETWEEN p.lft AND p.rgt')
 178             ->where('p.' . $k . ' = ' . (int) $pk)
 179             ->order('n.lft');
 180 
 181         return $this->_db->setQuery($query)->loadObjectList();
 182     }
 183 
 184     /**
 185      * Method to determine if a node is a leaf node in the tree (has no children).
 186      *
 187      * @param   integer  $pk  Primary key of the node to check.
 188      *
 189      * @return  boolean  True if a leaf node, false if not or null if the node does not exist.
 190      *
 191      * @note    Since 12.1 this method returns null if the node does not exist.
 192      * @since   11.1
 193      * @throws  RuntimeException on database error.
 194      */
 195     public function isLeaf($pk = null)
 196     {
 197         $k = $this->_tbl_key;
 198         $pk = (is_null($pk)) ? $this->$k : $pk;
 199         $node = $this->_getNode($pk);
 200 
 201         // Get the node by primary key.
 202         if (empty($node))
 203         {
 204             // Error message set in getNode method.
 205             return;
 206         }
 207 
 208         // The node is a leaf node.
 209         return ($node->rgt - $node->lft) == 1;
 210     }
 211 
 212     /**
 213      * Method to set the location of a node in the tree object.  This method does not
 214      * save the new location to the database, but will set it in the object so
 215      * that when the node is stored it will be stored in the new location.
 216      *
 217      * @param   integer  $referenceId  The primary key of the node to reference new location by.
 218      * @param   string   $position     Location type string.
 219      *
 220      * @return  void
 221      *
 222      * @note    Since 12.1 this method returns void and throws an InvalidArgumentException when an invalid position is passed.
 223      * @see     JTableNested::$_validLocations
 224      * @since   11.1
 225      * @throws  InvalidArgumentException
 226      */
 227     public function setLocation($referenceId, $position = 'after')
 228     {
 229         // Make sure the location is valid.
 230         if (!in_array($position, $this->_validLocations))
 231         {
 232             throw new InvalidArgumentException(
 233                 sprintf('Invalid location "%1$s" given, valid values are %2$s', $position, implode(', ', $this->_validLocations))
 234             );
 235         }
 236 
 237         // Set the location properties.
 238         $this->_location = $position;
 239         $this->_location_id = $referenceId;
 240     }
 241 
 242     /**
 243      * Method to move a row in the ordering sequence of a group of rows defined by an SQL WHERE clause.
 244      * Negative numbers move the row up in the sequence and positive numbers move it down.
 245      *
 246      * @param   integer  $delta  The direction and magnitude to move the row in the ordering sequence.
 247      * @param   string   $where  WHERE clause to use for limiting the selection of rows to compact the
 248      *                           ordering values.
 249      *
 250      * @return  mixed    Boolean true on success.
 251      *
 252      * @since   11.1
 253      */
 254     public function move($delta, $where = '')
 255     {
 256         $k = $this->_tbl_key;
 257         $pk = $this->$k;
 258 
 259         $query = $this->_db->getQuery(true)
 260             ->select($k)
 261             ->from($this->_tbl)
 262             ->where('parent_id = ' . $this->parent_id);
 263 
 264         if ($where)
 265         {
 266             $query->where($where);
 267         }
 268 
 269         if ($delta > 0)
 270         {
 271             $query->where('rgt > ' . $this->rgt)
 272                 ->order('rgt ASC');
 273             $position = 'after';
 274         }
 275         else
 276         {
 277             $query->where('lft < ' . $this->lft)
 278                 ->order('lft DESC');
 279             $position = 'before';
 280         }
 281 
 282         $this->_db->setQuery($query);
 283         $referenceId = $this->_db->loadResult();
 284 
 285         if ($referenceId)
 286         {
 287             return $this->moveByReference($referenceId, $position, $pk);
 288         }
 289         else
 290         {
 291             return false;
 292         }
 293     }
 294 
 295     /**
 296      * Method to move a node and its children to a new location in the tree.
 297      *
 298      * @param   integer  $referenceId      The primary key of the node to reference new location by.
 299      * @param   string   $position         Location type string. ['before', 'after', 'first-child', 'last-child']
 300      * @param   integer  $pk               The primary key of the node to move.
 301      * @param   boolean  $recursiveUpdate  Flag indicate that method recursiveUpdatePublishedColumn should be call.
 302      *
 303      * @return  boolean  True on success.
 304      *
 305      * @since   11.1
 306      * @throws  RuntimeException on database error.
 307      */
 308     public function moveByReference($referenceId, $position = 'after', $pk = null, $recursiveUpdate = true)
 309     {
 310         // @codeCoverageIgnoreStart
 311         if ($this->_debug)
 312         {
 313             echo "\nMoving ReferenceId:$referenceId, Position:$position, PK:$pk";
 314         }
 315         // @codeCoverageIgnoreEnd
 316 
 317         $k = $this->_tbl_key;
 318         $pk = (is_null($pk)) ? $this->$k : $pk;
 319 
 320         // Get the node by id.
 321         if (!$node = $this->_getNode($pk))
 322         {
 323             // Error message set in getNode method.
 324             return false;
 325         }
 326 
 327         // Get the ids of child nodes.
 328         $query = $this->_db->getQuery(true)
 329             ->select($k)
 330             ->from($this->_tbl)
 331             ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt);
 332 
 333         $children = $this->_db->setQuery($query)->loadColumn();
 334 
 335         // @codeCoverageIgnoreStart
 336         if ($this->_debug)
 337         {
 338             $this->_logtable(false);
 339         }
 340         // @codeCoverageIgnoreEnd
 341 
 342         // Cannot move the node to be a child of itself.
 343         if (in_array($referenceId, $children))
 344         {
 345             $this->setError(
 346                 new UnexpectedValueException(
 347                     sprintf('%1$s::moveByReference() is trying to make record ID %2$d a child of itself.', get_class($this), $pk)
 348                 )
 349             );
 350 
 351             return false;
 352         }
 353 
 354         // Lock the table for writing.
 355         if (!$this->_lock())
 356         {
 357             return false;
 358         }
 359 
 360         /*
 361          * Move the sub-tree out of the nested sets by negating its left and right values.
 362          */
 363         $query->clear()
 364             ->update($this->_tbl)
 365             ->set('lft = lft * (-1), rgt = rgt * (-1)')
 366             ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt);
 367         $this->_db->setQuery($query);
 368 
 369         $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED');
 370 
 371         /*
 372          * Close the hole in the tree that was opened by removing the sub-tree from the nested sets.
 373          */
 374         // Compress the left values.
 375         $query->clear()
 376             ->update($this->_tbl)
 377             ->set('lft = lft - ' . (int) $node->width)
 378             ->where('lft > ' . (int) $node->rgt);
 379         $this->_db->setQuery($query);
 380 
 381         $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED');
 382 
 383         // Compress the right values.
 384         $query->clear()
 385             ->update($this->_tbl)
 386             ->set('rgt = rgt - ' . (int) $node->width)
 387             ->where('rgt > ' . (int) $node->rgt);
 388         $this->_db->setQuery($query);
 389 
 390         $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED');
 391 
 392         // We are moving the tree relative to a reference node.
 393         if ($referenceId)
 394         {
 395             // Get the reference node by primary key.
 396             if (!$reference = $this->_getNode($referenceId))
 397             {
 398                 // Error message set in getNode method.
 399                 $this->_unlock();
 400 
 401                 return false;
 402             }
 403 
 404             // Get the reposition data for shifting the tree and re-inserting the node.
 405             if (!$repositionData = $this->_getTreeRepositionData($reference, $node->width, $position))
 406             {
 407                 // Error message set in getNode method.
 408                 $this->_unlock();
 409 
 410                 return false;
 411             }
 412         }
 413         // We are moving the tree to be the last child of the root node
 414         else
 415         {
 416             // Get the last root node as the reference node.
 417             $query->clear()
 418                 ->select($this->_tbl_key . ', parent_id, level, lft, rgt')
 419                 ->from($this->_tbl)
 420                 ->where('parent_id = 0')
 421                 ->order('lft DESC');
 422             $this->_db->setQuery($query, 0, 1);
 423             $reference = $this->_db->loadObject();
 424 
 425             // @codeCoverageIgnoreStart
 426             if ($this->_debug)
 427             {
 428                 $this->_logtable(false);
 429             }
 430             // @codeCoverageIgnoreEnd
 431 
 432             // Get the reposition data for re-inserting the node after the found root.
 433             if (!$repositionData = $this->_getTreeRepositionData($reference, $node->width, 'last-child'))
 434             {
 435                 // Error message set in getNode method.
 436                 $this->_unlock();
 437 
 438                 return false;
 439             }
 440         }
 441 
 442         /*
 443          * Create space in the nested sets at the new location for the moved sub-tree.
 444          */
 445 
 446         // Shift left values.
 447         $query->clear()
 448             ->update($this->_tbl)
 449             ->set('lft = lft + ' . (int) $node->width)
 450             ->where($repositionData->left_where);
 451         $this->_db->setQuery($query);
 452 
 453         $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED');
 454 
 455         // Shift right values.
 456         $query->clear()
 457             ->update($this->_tbl)
 458             ->set('rgt = rgt + ' . (int) $node->width)
 459             ->where($repositionData->right_where);
 460         $this->_db->setQuery($query);
 461 
 462         $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED');
 463 
 464         /*
 465          * Calculate the offset between where the node used to be in the tree and
 466          * where it needs to be in the tree for left ids (also works for right ids).
 467          */
 468         $offset = $repositionData->new_lft - $node->lft;
 469         $levelOffset = $repositionData->new_level - $node->level;
 470 
 471         // Move the nodes back into position in the tree using the calculated offsets.
 472         $query->clear()
 473             ->update($this->_tbl)
 474             ->set('rgt = ' . (int) $offset . ' - rgt')
 475             ->set('lft = ' . (int) $offset . ' - lft')
 476             ->set('level = level + ' . (int) $levelOffset)
 477             ->where('lft < 0');
 478         $this->_db->setQuery($query);
 479 
 480         $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED');
 481 
 482         // Set the correct parent id for the moved node if required.
 483         if ($node->parent_id != $repositionData->new_parent_id)
 484         {
 485             $query = $this->_db->getQuery(true)
 486                 ->update($this->_tbl);
 487 
 488             // Update the title and alias fields if they exist for the table.
 489             $fields = $this->getFields();
 490 
 491             if (property_exists($this, 'title') && $this->title !== null)
 492             {
 493                 $query->set('title = ' . $this->_db->quote($this->title));
 494             }
 495 
 496             if (array_key_exists('alias', $fields)  && $this->alias !== null)
 497             {
 498                 $query->set('alias = ' . $this->_db->quote($this->alias));
 499             }
 500 
 501             $query->set('parent_id = ' . (int) $repositionData->new_parent_id)
 502                 ->where($this->_tbl_key . ' = ' . (int) $node->$k);
 503             $this->_db->setQuery($query);
 504 
 505             $this->_runQuery($query, 'JLIB_DATABASE_ERROR_MOVE_FAILED');
 506         }
 507 
 508         // Unlock the table for writing.
 509         $this->_unlock();
 510 
 511         if (property_exists($this, 'published') && $recursiveUpdate)
 512         {
 513             $this->recursiveUpdatePublishedColumn($node->$k);
 514         }
 515 
 516         // Set the object values.
 517         $this->parent_id = $repositionData->new_parent_id;
 518         $this->level = $repositionData->new_level;
 519         $this->lft = $repositionData->new_lft;
 520         $this->rgt = $repositionData->new_rgt;
 521 
 522         return true;
 523     }
 524 
 525     /**
 526      * Method to delete a node and, optionally, its child nodes from the table.
 527      *
 528      * @param   integer  $pk        The primary key of the node to delete.
 529      * @param   boolean  $children  True to delete child nodes, false to move them up a level.
 530      *
 531      * @return  boolean  True on success.
 532      *
 533      * @since   11.1
 534      */
 535     public function delete($pk = null, $children = true)
 536     {
 537         $k = $this->_tbl_key;
 538         $pk = (is_null($pk)) ? $this->$k : $pk;
 539 
 540         // Implement JObservableInterface: Pre-processing by observers
 541         $this->_observers->update('onBeforeDelete', array($pk));
 542 
 543         // Lock the table for writing.
 544         if (!$this->_lock())
 545         {
 546             // Error message set in lock method.
 547             return false;
 548         }
 549 
 550         // If tracking assets, remove the asset first.
 551         if ($this->_trackAssets)
 552         {
 553             $name = $this->_getAssetName();
 554             $asset = JTable::getInstance('Asset', 'JTable', array('dbo' => $this->getDbo()));
 555 
 556             // Lock the table for writing.
 557             if (!$asset->_lock())
 558             {
 559                 // Error message set in lock method.
 560                 return false;
 561             }
 562 
 563             if ($asset->loadByName($name))
 564             {
 565                 // Delete the node in assets table.
 566                 if (!$asset->delete(null, $children))
 567                 {
 568                     $this->setError($asset->getError());
 569                     $asset->_unlock();
 570 
 571                     return false;
 572                 }
 573 
 574                 $asset->_unlock();
 575             }
 576             else
 577             {
 578                 $this->setError($asset->getError());
 579                 $asset->_unlock();
 580 
 581                 return false;
 582             }
 583         }
 584 
 585         // Get the node by id.
 586         $node = $this->_getNode($pk);
 587 
 588         if (empty($node))
 589         {
 590             // Error message set in getNode method.
 591             $this->_unlock();
 592 
 593             return false;
 594         }
 595 
 596         $query = $this->_db->getQuery(true);
 597 
 598         // Should we delete all children along with the node?
 599         if ($children)
 600         {
 601             // Delete the node and all of its children.
 602             $query->clear()
 603                 ->delete($this->_tbl)
 604                 ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt);
 605             $this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED');
 606 
 607             // Compress the left values.
 608             $query->clear()
 609                 ->update($this->_tbl)
 610                 ->set('lft = lft - ' . (int) $node->width)
 611                 ->where('lft > ' . (int) $node->rgt);
 612             $this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED');
 613 
 614             // Compress the right values.
 615             $query->clear()
 616                 ->update($this->_tbl)
 617                 ->set('rgt = rgt - ' . (int) $node->width)
 618                 ->where('rgt > ' . (int) $node->rgt);
 619             $this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED');
 620         }
 621         // Leave the children and move them up a level.
 622         else
 623         {
 624             // Delete the node.
 625             $query->clear()
 626                 ->delete($this->_tbl)
 627                 ->where('lft = ' . (int) $node->lft);
 628             $this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED');
 629 
 630             // Shift all node's children up a level.
 631             $query->clear()
 632                 ->update($this->_tbl)
 633                 ->set('lft = lft - 1')
 634                 ->set('rgt = rgt - 1')
 635                 ->set('level = level - 1')
 636                 ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt);
 637             $this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED');
 638 
 639             // Adjust all the parent values for direct children of the deleted node.
 640             $query->clear()
 641                 ->update($this->_tbl)
 642                 ->set('parent_id = ' . (int) $node->parent_id)
 643                 ->where('parent_id = ' . (int) $node->$k);
 644             $this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED');
 645 
 646             // Shift all of the left values that are right of the node.
 647             $query->clear()
 648                 ->update($this->_tbl)
 649                 ->set('lft = lft - 2')
 650                 ->where('lft > ' . (int) $node->rgt);
 651             $this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED');
 652 
 653             // Shift all of the right values that are right of the node.
 654             $query->clear()
 655                 ->update($this->_tbl)
 656                 ->set('rgt = rgt - 2')
 657                 ->where('rgt > ' . (int) $node->rgt);
 658             $this->_runQuery($query, 'JLIB_DATABASE_ERROR_DELETE_FAILED');
 659         }
 660 
 661         // Unlock the table for writing.
 662         $this->_unlock();
 663 
 664         // Implement JObservableInterface: Post-processing by observers
 665         $this->_observers->update('onAfterDelete', array($pk));
 666 
 667         return true;
 668     }
 669 
 670     /**
 671      * Checks that the object is valid and able to be stored.
 672      *
 673      * This method checks that the parent_id is non-zero and exists in the database.
 674      * Note that the root node (parent_id = 0) cannot be manipulated with this class.
 675      *
 676      * @return  boolean  True if all checks pass.
 677      *
 678      * @since   11.1
 679      */
 680     public function check()
 681     {
 682         $this->parent_id = (int) $this->parent_id;
 683 
 684         // Set up a mini exception handler.
 685         try
 686         {
 687             // Check that the parent_id field is valid.
 688             if ($this->parent_id == 0)
 689             {
 690                 throw new UnexpectedValueException(sprintf('Invalid `parent_id` [%1$d] in %2$s::check()', $this->parent_id, get_class($this)));
 691             }
 692 
 693             $query = $this->_db->getQuery(true)
 694                 ->select('1')
 695                 ->from($this->_tbl)
 696                 ->where($this->_tbl_key . ' = ' . $this->parent_id);
 697 
 698             if (!$this->_db->setQuery($query)->loadResult())
 699             {
 700                 throw new UnexpectedValueException(sprintf('Invalid `parent_id` [%1$d] in %2$s::check()', $this->parent_id, get_class($this)));
 701             }
 702         }
 703         catch (UnexpectedValueException $e)
 704         {
 705             // Validation error - record it and return false.
 706             $this->setError($e);
 707 
 708             return false;
 709         }
 710 
 711         return true;
 712     }
 713 
 714     /**
 715      * Method to store a node in the database table.
 716      *
 717      * @param   boolean  $updateNulls  True to update null values as well.
 718      *
 719      * @return  boolean  True on success.
 720      *
 721      * @since   11.1
 722      */
 723     public function store($updateNulls = false)
 724     {
 725         $k = $this->_tbl_key;
 726 
 727         // Implement JObservableInterface: Pre-processing by observers
 728         // 2.5 upgrade issue - check if property_exists before executing
 729         if (property_exists($this, '_observers'))
 730         {
 731             $this->_observers->update('onBeforeStore', array($updateNulls, $k));
 732         }
 733 
 734         // @codeCoverageIgnoreStart
 735         if ($this->_debug)
 736         {
 737             echo "\n" . get_class($this) . "::store\n";
 738             $this->_logtable(true, false);
 739         }
 740         // @codeCoverageIgnoreEnd
 741 
 742         /*
 743          * If the primary key is empty, then we assume we are inserting a new node into the
 744          * tree.  From this point we would need to determine where in the tree to insert it.
 745          */
 746         if (empty($this->$k))
 747         {
 748             /*
 749              * We are inserting a node somewhere in the tree with a known reference
 750              * node.  We have to make room for the new node and set the left and right
 751              * values before we insert the row.
 752              */
 753             if ($this->_location_id >= 0)
 754             {
 755                 // Lock the table for writing.
 756                 if (!$this->_lock())
 757                 {
 758                     // Error message set in lock method.
 759                     return false;
 760                 }
 761 
 762                 // We are inserting a node relative to the last root node.
 763                 if ($this->_location_id == 0)
 764                 {
 765                     // Get the last root node as the reference node.
 766                     $query = $this->_db->getQuery(true)
 767                         ->select($this->_tbl_key . ', parent_id, level, lft, rgt')
 768                         ->from($this->_tbl)
 769                         ->where('parent_id = 0')
 770                         ->order('lft DESC');
 771                     $this->_db->setQuery($query, 0, 1);
 772                     $reference = $this->_db->loadObject();
 773 
 774                     // @codeCoverageIgnoreStart
 775                     if ($this->_debug)
 776                     {
 777                         $this->_logtable(false);
 778                     }
 779                     // @codeCoverageIgnoreEnd
 780                 }
 781                 // We have a real node set as a location reference.
 782                 else
 783                 {
 784                     // Get the reference node by primary key.
 785                     if (!$reference = $this->_getNode($this->_location_id))
 786                     {
 787                         // Error message set in getNode method.
 788                         $this->_unlock();
 789 
 790                         return false;
 791                     }
 792                 }
 793 
 794                 // Get the reposition data for shifting the tree and re-inserting the node.
 795                 if (!($repositionData = $this->_getTreeRepositionData($reference, 2, $this->_location)))
 796                 {
 797                     // Error message set in getNode method.
 798                     $this->_unlock();
 799 
 800                     return false;
 801                 }
 802 
 803                 // Create space in the tree at the new location for the new node in left ids.
 804                 $query = $this->_db->getQuery(true)
 805                     ->update($this->_tbl)
 806                     ->set('lft = lft + 2')
 807                     ->where($repositionData->left_where);
 808                 $this->_runQuery($query, 'JLIB_DATABASE_ERROR_STORE_FAILED');
 809 
 810                 // Create space in the tree at the new location for the new node in right ids.
 811                 $query->clear()
 812                     ->update($this->_tbl)
 813                     ->set('rgt = rgt + 2')
 814                     ->where($repositionData->right_where);
 815                 $this->_runQuery($query, 'JLIB_DATABASE_ERROR_STORE_FAILED');
 816 
 817                 // Set the object values.
 818                 $this->parent_id = $repositionData->new_parent_id;
 819                 $this->level = $repositionData->new_level;
 820                 $this->lft = $repositionData->new_lft;
 821                 $this->rgt = $repositionData->new_rgt;
 822             }
 823             else
 824             {
 825                 // Negative parent ids are invalid
 826                 $e = new UnexpectedValueException(sprintf('%s::store() used a negative _location_id', get_class($this)));
 827                 $this->setError($e);
 828 
 829                 return false;
 830             }
 831         }
 832         /*
 833          * If we have a given primary key then we assume we are simply updating this
 834          * node in the tree.  We should assess whether or not we are moving the node
 835          * or just updating its data fields.
 836          */
 837         else
 838         {
 839             // If the location has been set, move the node to its new location.
 840             if ($this->_location_id > 0)
 841             {
 842                 // Skip recursiveUpdatePublishedColumn method, it will be called later.
 843                 if (!$this->moveByReference($this->_location_id, $this->_location, $this->$k, false))
 844                 {
 845                     // Error message set in move method.
 846                     return false;
 847                 }
 848             }
 849 
 850             // Lock the table for writing.
 851             if (!$this->_lock())
 852             {
 853                 // Error message set in lock method.
 854                 return false;
 855             }
 856         }
 857 
 858         // Implement JObservableInterface: We do not want parent::store to update observers,
 859         // since tables are locked and we are updating it from this level of store():
 860 
 861         // 2.5 upgrade issue - check if property_exists before executing
 862         if (property_exists($this, '_observers'))
 863         {
 864             $oldCallObservers = $this->_observers->doCallObservers(false);
 865         }
 866 
 867         $result = parent::store($updateNulls);
 868 
 869         // Implement JObservableInterface: Restore previous callable observers state:
 870         // 2.5 upgrade issue - check if property_exists before executing
 871         if (property_exists($this, '_observers'))
 872         {
 873             $this->_observers->doCallObservers($oldCallObservers);
 874         }
 875 
 876         if ($result)
 877         {
 878             // @codeCoverageIgnoreStart
 879             if ($this->_debug)
 880             {
 881                 $this->_logtable();
 882             }
 883             // @codeCoverageIgnoreEnd
 884         }
 885 
 886         // Unlock the table for writing.
 887         $this->_unlock();
 888 
 889         if (property_exists($this, 'published'))
 890         {
 891             $this->recursiveUpdatePublishedColumn($this->$k);
 892         }
 893 
 894         // Implement JObservableInterface: Post-processing by observers
 895         // 2.5 upgrade issue - check if property_exists before executing
 896         if (property_exists($this, '_observers'))
 897         {
 898             $this->_observers->update('onAfterStore', array(&$result));
 899         }
 900 
 901         return $result;
 902     }
 903 
 904     /**
 905      * Method to set the publishing state for a node or list of nodes in the database
 906      * table.  The method respects rows checked out by other users and will attempt
 907      * to checkin rows that it can after adjustments are made. The method will not
 908      * allow you to set a publishing state higher than any ancestor node and will
 909      * not allow you to set a publishing state on a node with a checked out child.
 910      *
 911      * @param   mixed    $pks     An optional array of primary key values to update.  If not
 912      *                            set the instance property value is used.
 913      * @param   integer  $state   The publishing state. eg. [0 = unpublished, 1 = published]
 914      * @param   integer  $userId  The user id of the user performing the operation.
 915      *
 916      * @return  boolean  True on success.
 917      *
 918      * @since   11.1
 919      * @throws  UnexpectedValueException
 920      */
 921     public function publish($pks = null, $state = 1, $userId = 0)
 922     {
 923         $k = $this->_tbl_key;
 924 
 925         $query     = $this->_db->getQuery(true);
 926         $table     = $this->_db->quoteName($this->_tbl);
 927         $published = $this->_db->quoteName($this->getColumnAlias('published'));
 928         $key       = $this->_db->quoteName($k);
 929 
 930         // Sanitize input.
 931         $pks    = ArrayHelper::toInteger($pks);
 932         $userId = (int) $userId;
 933         $state  = (int) $state;
 934 
 935         // If $state > 1, then we allow state changes even if an ancestor has lower state
 936         // (for example, can change a child state to Archived (2) if an ancestor is Published (1)
 937         $compareState = ($state > 1) ? 1 : $state;
 938 
 939         // If there are no primary keys set check to see if the instance key is set.
 940         if (empty($pks))
 941         {
 942             if ($this->$k)
 943             {
 944                 $pks = explode(',', $this->$k);
 945             }
 946             // Nothing to set publishing state on, return false.
 947             else
 948             {
 949                 $e = new UnexpectedValueException(sprintf('%s::publish(%s, %d, %d) empty.', get_class($this), $pks[0], $state, $userId));
 950                 $this->setError($e);
 951 
 952                 return false;
 953             }
 954         }
 955 
 956         // Determine if there is checkout support for the table.
 957         $checkoutSupport = (property_exists($this, 'checked_out') || property_exists($this, 'checked_out_time'));
 958 
 959         // Iterate over the primary keys to execute the publish action if possible.
 960         foreach ($pks as $pk)
 961         {
 962             // Get the node by primary key.
 963             if (!$node = $this->_getNode($pk))
 964             {
 965                 // Error message set in getNode method.
 966                 return false;
 967             }
 968 
 969             // If the table has checkout support, verify no children are checked out.
 970             if ($checkoutSupport)
 971             {
 972                 // Ensure that children are not checked out.
 973                 $query->clear()
 974                     ->select('COUNT(' . $k . ')')
 975                     ->from($this->_tbl)
 976                     ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt)
 977                     ->where('(checked_out <> 0 AND checked_out <> ' . (int) $userId . ')');
 978                 $this->_db->setQuery($query);
 979 
 980                 // Check for checked out children.
 981                 if ($this->_db->loadResult())
 982                 {
 983                     // TODO Convert to a conflict exception when available.
 984                     $e = new RuntimeException(sprintf('%s::publish(%s, %d, %d) checked-out conflict.', get_class($this), $pks[0], $state, $userId));
 985 
 986                     $this->setError($e);
 987 
 988                     return false;
 989                 }
 990             }
 991 
 992             // If any parent nodes have lower published state values, we cannot continue.
 993             if ($node->parent_id)
 994             {
 995                 // Get any ancestor nodes that have a lower publishing state.
 996                 $query->clear()
 997                     ->select('1')
 998                     ->from($table)
 999                     ->where('lft < ' . (int) $node->lft)
1000                     ->where('rgt > ' . (int) $node->rgt)
1001                     ->where('parent_id > 0')
1002                     ->where($published . ' < ' . (int) $compareState);
1003 
1004                 // Just fetch one row (one is one too many).
1005                 $this->_db->setQuery($query, 0, 1);
1006 
1007                 if ($this->_db->loadResult())
1008                 {
1009                     $e = new UnexpectedValueException(
1010                         sprintf('%s::publish(%s, %d, %d) ancestors have lower state.', get_class($this), $pks[0], $state, $userId)
1011                     );
1012                     $this->setError($e);
1013 
1014                     return false;
1015                 }
1016             }
1017 
1018             $this->recursiveUpdatePublishedColumn($pk, $state);
1019 
1020             // If checkout support exists for the object, check the row in.
1021             if ($checkoutSupport)
1022             {
1023                 $this->checkin($pk);
1024             }
1025         }
1026 
1027         // If the JTable instance value is in the list of primary keys that were set, set the instance.
1028         if (in_array($this->$k, $pks))
1029         {
1030             $this->published = $state;
1031         }
1032 
1033         $this->setError('');
1034 
1035         return true;
1036     }
1037 
1038     /**
1039      * Method to move a node one position to the left in the same level.
1040      *
1041      * @param   integer  $pk  Primary key of the node to move.
1042      *
1043      * @return  boolean  True on success.
1044      *
1045      * @since   11.1
1046      * @throws  RuntimeException on database error.
1047      */
1048     public function orderUp($pk)
1049     {
1050         $k = $this->_tbl_key;
1051         $pk = (is_null($pk)) ? $this->$k : $pk;
1052 
1053         // Lock the table for writing.
1054         if (!$this->_lock())
1055         {
1056             // Error message set in lock method.
1057             return false;
1058         }
1059 
1060         // Get the node by primary key.
1061         $node = $this->_getNode($pk);
1062 
1063         if (empty($node))
1064         {
1065             // Error message set in getNode method.
1066             $this->_unlock();
1067 
1068             return false;
1069         }
1070 
1071         // Get the left sibling node.
1072         $sibling = $this->_getNode($node->lft - 1, 'right');
1073 
1074         if (empty($sibling))
1075         {
1076             // Error message set in getNode method.
1077             $this->_unlock();
1078 
1079             return false;
1080         }
1081 
1082         try
1083         {
1084             // Get the primary keys of child nodes.
1085             $query = $this->_db->getQuery(true)
1086                 ->select($this->_tbl_key)
1087                 ->from($this->_tbl)
1088                 ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt);
1089 
1090             $children = $this->_db->setQuery($query)->loadColumn();
1091 
1092             // Shift left and right values for the node and its children.
1093             $query->clear()
1094                 ->update($this->_tbl)
1095                 ->set('lft = lft - ' . (int) $sibling->width)
1096                 ->set('rgt = rgt - ' . (int) $sibling->width)
1097                 ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt);
1098             $this->_db->setQuery($query)->execute();
1099 
1100             // Shift left and right values for the sibling and its children.
1101             $query->clear()
1102                 ->update($this->_tbl)
1103                 ->set('lft = lft + ' . (int) $node->width)
1104                 ->set('rgt = rgt + ' . (int) $node->width)
1105                 ->where('lft BETWEEN ' . (int) $sibling->lft . ' AND ' . (int) $sibling->rgt)
1106                 ->where($this->_tbl_key . ' NOT IN (' . implode(',', $children) . ')');
1107             $this->_db->setQuery($query)->execute();
1108         }
1109         catch (RuntimeException $e)
1110         {
1111             $this->_unlock();
1112             throw $e;
1113         }
1114 
1115         // Unlock the table for writing.
1116         $this->_unlock();
1117 
1118         return true;
1119     }
1120 
1121     /**
1122      * Method to move a node one position to the right in the same level.
1123      *
1124      * @param   integer  $pk  Primary key of the node to move.
1125      *
1126      * @return  boolean  True on success.
1127      *
1128      * @since   11.1
1129      * @throws  RuntimeException on database error.
1130      */
1131     public function orderDown($pk)
1132     {
1133         $k = $this->_tbl_key;
1134         $pk = (is_null($pk)) ? $this->$k : $pk;
1135 
1136         // Lock the table for writing.
1137         if (!$this->_lock())
1138         {
1139             // Error message set in lock method.
1140             return false;
1141         }
1142 
1143         // Get the node by primary key.
1144         $node = $this->_getNode($pk);
1145 
1146         if (empty($node))
1147         {
1148             // Error message set in getNode method.
1149             $this->_unlock();
1150 
1151             return false;
1152         }
1153 
1154         $query = $this->_db->getQuery(true);
1155 
1156         // Get the right sibling node.
1157         $sibling = $this->_getNode($node->rgt + 1, 'left');
1158 
1159         if (empty($sibling))
1160         {
1161             // Error message set in getNode method.
1162             $query->_unlock($this->_db);
1163             $this->_locked = false;
1164 
1165             return false;
1166         }
1167 
1168         try
1169         {
1170             // Get the primary keys of child nodes.
1171             $query->clear()
1172                 ->select($this->_tbl_key)
1173                 ->from($this->_tbl)
1174                 ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt);
1175             $this->_db->setQuery($query);
1176             $children = $this->_db->loadColumn();
1177 
1178             // Shift left and right values for the node and its children.
1179             $query->clear()
1180                 ->update($this->_tbl)
1181                 ->set('lft = lft + ' . (int) $sibling->width)
1182                 ->set('rgt = rgt + ' . (int) $sibling->width)
1183                 ->where('lft BETWEEN ' . (int) $node->lft . ' AND ' . (int) $node->rgt);
1184             $this->_db->setQuery($query)->execute();
1185 
1186             // Shift left and right values for the sibling and its children.
1187             $query->clear()
1188                 ->update($this->_tbl)
1189                 ->set('lft = lft - ' . (int) $node->width)
1190                 ->set('rgt = rgt - ' . (int) $node->width)
1191                 ->where('lft BETWEEN ' . (int) $sibling->lft . ' AND ' . (int) $sibling->rgt)
1192                 ->where($this->_tbl_key . ' NOT IN (' . implode(',', $children) . ')');
1193             $this->_db->setQuery($query)->execute();
1194         }
1195         catch (RuntimeException $e)
1196         {
1197             $this->_unlock();
1198             throw $e;
1199         }
1200 
1201         // Unlock the table for writing.
1202         $this->_unlock();
1203 
1204         return true;
1205     }
1206 
1207     /**
1208      * Gets the ID of the root item in the tree
1209      *
1210      * @return  mixed  The primary id of the root row, or false if not found and the internal error is set.
1211      *
1212      * @since   11.1
1213      */
1214     public function getRootId()
1215     {
1216         if ((int) self::$root_id > 0)
1217         {
1218             return self::$root_id;
1219         }
1220 
1221         // Get the root item.
1222         $k = $this->_tbl_key;
1223 
1224         // Test for a unique record with parent_id = 0
1225         $query = $this->_db->getQuery(true)
1226             ->select($k)
1227             ->from($this->_tbl)
1228             ->where('parent_id = 0');
1229 
1230         $result = $this->_db->setQuery($query)->loadColumn();
1231 
1232         if (count($result) == 1)
1233         {
1234             self::$root_id = $result[0];
1235 
1236             return self::$root_id;
1237         }
1238 
1239         // Test for a unique record with lft = 0
1240         $query->clear()
1241             ->select($k)
1242             ->from($this->_tbl)
1243             ->where('lft = 0');
1244 
1245         $result = $this->_db->setQuery($query)->loadColumn();
1246 
1247         if (count($result) == 1)
1248         {
1249             self::$root_id = $result[0];
1250 
1251             return self::$root_id;
1252         }
1253 
1254         $fields = $this->getFields();
1255 
1256         if (array_key_exists('alias', $fields))
1257         {
1258             // Test for a unique record alias = root
1259             $query->clear()
1260                 ->select($k)
1261                 ->from($this->_tbl)
1262                 ->where('alias = ' . $this->_db->quote('root'));
1263 
1264             $result = $this->_db->setQuery($query)->loadColumn();
1265 
1266             if (count($result) == 1)
1267             {
1268                 self::$root_id = $result[0];
1269 
1270                 return self::$root_id;
1271             }
1272         }
1273 
1274         $e = new UnexpectedValueException(sprintf('%s::getRootId', get_class($this)));
1275         $this->setError($e);
1276         self::$root_id = false;
1277 
1278         return false;
1279     }
1280 
1281     /**
1282      * Method to recursively rebuild the whole nested set tree.
1283      *
1284      * @param   integer  $parentId  The root of the tree to rebuild.
1285      * @param   integer  $leftId    The left id to start with in building the tree.
1286      * @param   integer  $level     The level to assign to the current nodes.
1287      * @param   string   $path      The path to the current nodes.
1288      *
1289      * @return  integer  1 + value of root rgt on success, false on failure
1290      *
1291      * @since   11.1
1292      * @throws  RuntimeException on database error.
1293      */
1294     public function rebuild($parentId = null, $leftId = 0, $level = 0, $path = '')
1295     {
1296         // If no parent is provided, try to find it.
1297         if ($parentId === null)
1298         {
1299             // Get the root item.
1300             $parentId = $this->getRootId();
1301 
1302             if ($parentId === false)
1303             {
1304                 return false;
1305             }
1306         }
1307 
1308         $query = $this->_db->getQuery(true);
1309 
1310         // Build the structure of the recursive query.
1311         if (!isset($this->_cache['rebuild.sql']))
1312         {
1313             $query->clear()
1314                 ->select($this->_tbl_key . ', alias')
1315                 ->from($this->_tbl)
1316                 ->where('parent_id = %d');
1317 
1318             // If the table has an ordering field, use that for ordering.
1319             $orderingField = $this->getColumnAlias('ordering');
1320 
1321             if (property_exists($this, $orderingField))
1322             {
1323                 $query->order('parent_id, ' . $this->_db->quoteName($orderingField) . ', lft');
1324             }
1325             else
1326             {
1327                 $query->order('parent_id, lft');
1328             }
1329 
1330             $this->_cache['rebuild.sql'] = (string) $query;
1331         }
1332 
1333         // Make a shortcut to database object.
1334 
1335         // Assemble the query to find all children of this node.
1336         $this->_db->setQuery(sprintf($this->_cache['rebuild.sql'], (int) $parentId));
1337 
1338         $children = $this->_db->loadObjectList();
1339 
1340         // The right value of this node is the left value + 1
1341         $rightId = $leftId + 1;
1342 
1343         // Execute this function recursively over all children
1344         foreach ($children as $node)
1345         {
1346             /*
1347              * $rightId is the current right value, which is incremented on recursion return.
1348              * Increment the level for the children.
1349              * Add this item's alias to the path (but avoid a leading /)
1350              */
1351             $rightId = $this->rebuild($node->{$this->_tbl_key}, $rightId, $level + 1, $path . (empty($path) ? '' : '/') . $node->alias);
1352 
1353             // If there is an update failure, return false to break out of the recursion.
1354             if ($rightId === false)
1355             {
1356                 return false;
1357             }
1358         }
1359 
1360         // We've got the left value, and now that we've processed
1361         // the children of this node we also know the right value.
1362         $query->clear()
1363             ->update($this->_tbl)
1364             ->set('lft = ' . (int) $leftId)
1365             ->set('rgt = ' . (int) $rightId)
1366             ->set('level = ' . (int) $level)
1367             ->set('path = ' . $this->_db->quote($path))
1368             ->where($this->_tbl_key . ' = ' . (int) $parentId);
1369         $this->_db->setQuery($query)->execute();
1370 
1371         // Return the right value of this node + 1.
1372         return $rightId + 1;
1373     }
1374 
1375     /**
1376      * Method to rebuild the node's path field from the alias values of the nodes from the current node to the root node of the tree.
1377      *
1378      * @param   integer  $pk  Primary key of the node for which to get the path.
1379      *
1380      * @return  boolean  True on success.
1381      *
1382      * @since   11.1
1383      */
1384     public function rebuildPath($pk = null)
1385     {
1386         $fields = $this->getFields();
1387 
1388         // If there is no alias or path field, just return true.
1389         if (!array_key_exists('alias', $fields) || !array_key_exists('path', $fields))
1390         {
1391             return true;
1392         }
1393 
1394         $k = $this->_tbl_key;
1395         $pk = (is_null($pk)) ? $this->$k : $pk;
1396 
1397         // Get the aliases for the path from the node to the root node.
1398         $query = $this->_db->getQuery(true)
1399             ->select('p.alias')
1400             ->from($this->_tbl . ' AS n, ' . $this->_tbl . ' AS p')
1401             ->where('n.lft BETWEEN p.lft AND p.rgt')
1402             ->where('n.' . $this->_tbl_key . ' = ' . (int) $pk)
1403             ->order('p.lft');
1404         $this->_db->setQuery($query);
1405 
1406         $segments = $this->_db->loadColumn();
1407 
1408         // Make sure to remove the root path if it exists in the list.
1409         if ($segments[0] == 'root')
1410         {
1411             array_shift($segments);
1412         }
1413 
1414         // Build the path.
1415         $path = trim(implode('/', $segments), ' /\\');
1416 
1417         // Update the path field for the node.
1418         $query->clear()
1419             ->update($this->_tbl)
1420             ->set('path = ' . $this->_db->quote($path))
1421             ->where($this->_tbl_key . ' = ' . (int) $pk);
1422 
1423         $this->_db->setQuery($query)->execute();
1424 
1425         // Update the current record's path to the new one:
1426         $this->path = $path;
1427 
1428         return true;
1429     }
1430 
1431     /**
1432      * Method to reset class properties to the defaults set in the class
1433      * definition. It will ignore the primary key as well as any private class
1434      * properties (except $_errors).
1435      *
1436      * @return  void
1437      *
1438      * @since   3.2.1
1439      */
1440     public function reset()
1441     {
1442         parent::reset();
1443 
1444         // Reset the location properties.
1445         $this->setLocation(0);
1446     }
1447 
1448     /**
1449      * Method to update order of table rows
1450      *
1451      * @param   array  $idArray    id numbers of rows to be reordered.
1452      * @param   array  $lft_array  lft values of rows to be reordered.
1453      *
1454      * @return  integer  1 + value of root rgt on success, false on failure.
1455      *
1456      * @since   11.1
1457      * @throws  Exception on database error.
1458      */
1459     public function saveorder($idArray = null, $lft_array = null)
1460     {
1461         try
1462         {
1463             $query = $this->_db->getQuery(true);
1464 
1465             // Validate arguments
1466             if (is_array($idArray) && is_array($lft_array) && count($idArray) == count($lft_array))
1467             {
1468                 for ($i = 0, $count = count($idArray); $i < $count; $i++)
1469                 {
1470                     // Do an update to change the lft values in the table for each id
1471                     $query->clear()
1472                         ->update($this->_tbl)
1473                         ->where($this->_tbl_key . ' = ' . (int) $idArray[$i])
1474                         ->set('lft = ' . (int) $lft_array[$i]);
1475 
1476                     $this->_db->setQuery($query)->execute();
1477 
1478                     // @codeCoverageIgnoreStart
1479                     if ($this->_debug)
1480                     {
1481                         $this->_logtable();
1482                     }
1483                     // @codeCoverageIgnoreEnd
1484                 }
1485 
1486                 return $this->rebuild();
1487             }
1488             else
1489             {
1490                 return false;
1491             }
1492         }
1493         catch (Exception $e)
1494         {
1495             $this->_unlock();
1496             throw $e;
1497         }
1498     }
1499 
1500     /**
1501      * Method to recursive update published column for children rows.
1502      *
1503      * @param   integer  $pk        Id number of row which published column was changed.
1504      * @param   integer  $newState  An optional value for published column of row identified by $pk.
1505      *
1506      * @return  boolean  True on success.
1507      *
1508      * @since   3.7.0
1509      * @throws  RuntimeException on database error.
1510      */
1511     protected function recursiveUpdatePublishedColumn($pk, $newState = null)
1512     {
1513         $query     = $this->_db->getQuery(true);
1514         $table     = $this->_db->quoteName($this->_tbl);
1515         $key       = $this->_db->quoteName($this->_tbl_key);
1516         $published = $this->_db->quoteName($this->getColumnAlias('published'));
1517 
1518         if ($newState !== null)
1519         {
1520             // Use a new published state in changed row.
1521             $newState = "(CASE WHEN p2.$key = " . (int) $pk . " THEN " . (int) $newState . " ELSE p2.$published END)";
1522         }
1523         else
1524         {
1525             $newState = "p2.$published";
1526         }
1527 
1528         /**
1529          * We have to calculate the correct value for c2.published
1530          * based on p2.published and own c2.published column,
1531          * where (p2) is parent category is and (c2) current category
1532          *
1533          * p2.published <= c2.published AND p2.published > 0 THEN c2.published
1534          *            2 <=  2 THEN  2 (If archived in archived then archived)
1535          *            1 <=  2 THEN  2 (If archived in published then archived)
1536          *            1 <=  1 THEN  1 (If published in published then published)
1537          *
1538          * p2.published >  c2.published AND c2.published > 0 THEN p2.published
1539          *            2 >   1 THEN  2 (If published in archived then archived)
1540          *
1541          * p2.published >  c2.published THEN c2.published ELSE p2.published
1542          *            2 >  -2 THEN -2 (If trashed in archived then trashed)
1543          *            2 >   0 THEN  0 (If unpublished in archived then unpublished)
1544          *            1 >   0 THEN  0 (If unpublished in published then unpublished)
1545          *            0 >  -2 THEN -2 (If trashed in unpublished then trashed)
1546          * ELSE
1547          *            0 <=  2 THEN  0 (If archived in unpublished then unpublished)
1548          *            0 <=  1 THEN  0 (If published in unpublished then unpublished)
1549          *            0 <=  0 THEN  0 (If unpublished in unpublished then unpublished)
1550          *           -2 <= -2 THEN -2 (If trashed in trashed then trashed)
1551          *           -2 <=  0 THEN -2 (If unpublished in trashed then trashed)
1552          *           -2 <=  1 THEN -2 (If published in trashed then trashed)
1553          *           -2 <=  2 THEN -2 (If archived in trashed then trashed)
1554          */
1555 
1556         // Prepare a list of correct published states.
1557         $subquery = (string) $query->clear()
1558             ->select("c2.$key AS newId")
1559             ->select("CASE WHEN MIN($newState) > 0 THEN MAX($newState) ELSE MIN($newState) END AS newPublished")
1560             ->from("$table AS node")
1561             ->innerJoin("$table AS c2 ON node.lft <= c2.lft AND c2.rgt <= node.rgt")
1562             ->innerJoin("$table AS p2 ON p2.lft <= c2.lft AND c2.rgt <= p2.rgt")
1563             ->where("node.$key = " . (int) $pk)
1564             ->group("c2.$key");
1565 
1566         // Update and cascade the publishing state.
1567         $query->clear()
1568             ->update("$table AS c")
1569             ->innerJoin("($subquery) AS c2 ON c2.newId = c.$key")
1570             ->set("$published = c2.newPublished");
1571 
1572         $this->_runQuery($query, 'JLIB_DATABASE_ERROR_STORE_FAILED');
1573 
1574         return true;
1575     }
1576 
1577     /**
1578      * Method to get nested set properties for a node in the tree.
1579      *
1580      * @param   integer  $id   Value to look up the node by.
1581      * @param   string   $key  An optional key to look up the node by (parent | left | right).
1582      *                         If omitted, the primary key of the table is used.
1583      *
1584      * @return  mixed    Boolean false on failure or node object on success.
1585      *
1586      * @since   11.1
1587      * @throws  RuntimeException on database error.
1588      */
1589     protected function _getNode($id, $key = null)
1590     {
1591         // Determine which key to get the node base on.
1592         switch ($key)
1593         {
1594             case 'parent':
1595                 $k = 'parent_id';
1596                 break;
1597 
1598             case 'left':
1599                 $k = 'lft';
1600                 break;
1601 
1602             case 'right':
1603                 $k = 'rgt';
1604                 break;
1605 
1606             default:
1607                 $k = $this->_tbl_key;
1608                 break;
1609         }
1610 
1611         // Get the node data.
1612         $query = $this->_db->getQuery(true)
1613             ->select($this->_tbl_key . ', parent_id, level, lft, rgt')
1614             ->from($this->_tbl)
1615             ->where($k . ' = ' . (int) $id);
1616 
1617         $row = $this->_db->setQuery($query, 0, 1)->loadObject();
1618 
1619         // Check for no $row returned
1620         if (empty($row))
1621         {
1622             $e = new UnexpectedValueException(sprintf('%s::_getNode(%d, %s) failed.', get_class($this), $id, $key));
1623             $this->setError($e);
1624 
1625             return false;
1626         }
1627 
1628         // Do some simple calculations.
1629         $row->numChildren = (int) ($row->rgt - $row->lft - 1) / 2;
1630         $row->width = (int) $row->rgt - $row->lft + 1;
1631 
1632         return $row;
1633     }
1634 
1635     /**
1636      * Method to get various data necessary to make room in the tree at a location
1637      * for a node and its children.  The returned data object includes conditions
1638      * for SQL WHERE clauses for updating left and right id values to make room for
1639      * the node as well as the new left and right ids for the node.
1640      *
1641      * @param   object   $referenceNode  A node object with at least a 'lft' and 'rgt' with
1642      *                                   which to make room in the tree around for a new node.
1643      * @param   integer  $nodeWidth      The width of the node for which to make room in the tree.
1644      * @param   string   $position       The position relative to the reference node where the room
1645      *                                   should be made.
1646      *
1647      * @return  mixed    Boolean false on failure or data object on success.
1648      *
1649      * @since   11.1
1650      */
1651     protected function _getTreeRepositionData($referenceNode, $nodeWidth, $position = 'before')
1652     {
1653         // Make sure the reference an object with a left and right id.
1654         if (!is_object($referenceNode) || !(isset($referenceNode->lft) && isset($referenceNode->rgt)))
1655         {
1656             return false;
1657         }
1658 
1659         // A valid node cannot have a width less than 2.
1660         if ($nodeWidth < 2)
1661         {
1662             return false;
1663         }
1664 
1665         $k = $this->_tbl_key;
1666         $data = new stdClass;
1667 
1668         // Run the calculations and build the data object by reference position.
1669         switch ($position)
1670         {
1671             case 'first-child':
1672                 $data->left_where = 'lft > ' . $referenceNode->lft;
1673                 $data->right_where = 'rgt >= ' . $referenceNode->lft;
1674 
1675                 $data->new_lft = $referenceNode->lft + 1;
1676                 $data->new_rgt = $referenceNode->lft + $nodeWidth;
1677                 $data->new_parent_id = $referenceNode->$k;
1678                 $data->new_level = $referenceNode->level + 1;
1679                 break;
1680 
1681             case 'last-child':
1682                 $data->left_where = 'lft > ' . ($referenceNode->rgt);
1683                 $data->right_where = 'rgt >= ' . ($referenceNode->rgt);
1684 
1685                 $data->new_lft = $referenceNode->rgt;
1686                 $data->new_rgt = $referenceNode->rgt + $nodeWidth - 1;
1687                 $data->new_parent_id = $referenceNode->$k;
1688                 $data->new_level = $referenceNode->level + 1;
1689                 break;
1690 
1691             case 'before':
1692                 $data->left_where = 'lft >= ' . $referenceNode->lft;
1693                 $data->right_where = 'rgt >= ' . $referenceNode->lft;
1694 
1695                 $data->new_lft = $referenceNode->lft;
1696                 $data->new_rgt = $referenceNode->lft + $nodeWidth - 1;
1697                 $data->new_parent_id = $referenceNode->parent_id;
1698                 $data->new_level = $referenceNode->level;
1699                 break;
1700 
1701             default:
1702             case 'after':
1703                 $data->left_where = 'lft > ' . $referenceNode->rgt;
1704                 $data->right_where = 'rgt > ' . $referenceNode->rgt;
1705 
1706                 $data->new_lft = $referenceNode->rgt + 1;
1707                 $data->new_rgt = $referenceNode->rgt + $nodeWidth;
1708                 $data->new_parent_id = $referenceNode->parent_id;
1709                 $data->new_level = $referenceNode->level;
1710                 break;
1711         }
1712 
1713         // @codeCoverageIgnoreStart
1714         if ($this->_debug)
1715         {
1716             echo "\nRepositioning Data for $position" . "\n-----------------------------------" . "\nLeft Where:    $data->left_where"
1717                 . "\nRight Where:   $data->right_where" . "\nNew Lft:       $data->new_lft" . "\nNew Rgt:       $data->new_rgt"
1718                 . "\nNew Parent ID: $data->new_parent_id" . "\nNew Level:     $data->new_level" . "\n";
1719         }
1720         // @codeCoverageIgnoreEnd
1721 
1722         return $data;
1723     }
1724 
1725     /**
1726      * Method to create a log table in the buffer optionally showing the query and/or data.
1727      *
1728      * @param   boolean  $showData   True to show data
1729      * @param   boolean  $showQuery  True to show query
1730      *
1731      * @return  void
1732      *
1733      * @codeCoverageIgnore
1734      * @since   11.1
1735      */
1736     protected function _logtable($showData = true, $showQuery = true)
1737     {
1738         $sep = "\n" . str_pad('', 40, '-');
1739         $buffer = '';
1740 
1741         if ($showQuery)
1742         {
1743             $buffer .= "\n" . $this->_db->getQuery() . $sep;
1744         }
1745 
1746         if ($showData)
1747         {
1748             $query = $this->_db->getQuery(true)
1749                 ->select($this->_tbl_key . ', parent_id, lft, rgt, level')
1750                 ->from($this->_tbl)
1751                 ->order($this->_tbl_key);
1752             $this->_db->setQuery($query);
1753 
1754             $rows = $this->_db->loadRowList();
1755             $buffer .= sprintf("\n| %4s | %4s | %4s | %4s |", $this->_tbl_key, 'par', 'lft', 'rgt');
1756             $buffer .= $sep;
1757 
1758             foreach ($rows as $row)
1759             {
1760                 $buffer .= sprintf("\n| %4s | %4s | %4s | %4s |", $row[0], $row[1], $row[2], $row[3]);
1761             }
1762 
1763             $buffer .= $sep;
1764         }
1765 
1766         echo $buffer;
1767     }
1768 
1769     /**
1770      * Runs a query and unlocks the database on an error.
1771      *
1772      * @param   mixed   $query         A string or JDatabaseQuery object.
1773      * @param   string  $errorMessage  Unused.
1774      *
1775      * @return  boolean  void
1776      *
1777      * @note    Since 12.1 this method returns void and will rethrow the database exception.
1778      * @since   11.1
1779      * @throws  Exception on database error.
1780      */
1781     protected function _runQuery($query, $errorMessage)
1782     {
1783         // Prepare to catch an exception.
1784         try
1785         {
1786             $this->_db->setQuery($query)->execute();
1787 
1788             // @codeCoverageIgnoreStart
1789             if ($this->_debug)
1790             {
1791                 $this->_logtable();
1792             }
1793             // @codeCoverageIgnoreEnd
1794         }
1795         catch (Exception $e)
1796         {
1797             // Unlock the tables and rethrow.
1798             $this->_unlock();
1799 
1800             throw $e;
1801         }
1802     }
1803 }
1804 
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.