Файловый менеджер - Редактировать - /home/lmsyaran/public_html/pusher/fof.tar
Назад
autoloader/component.php 0000644 00000047172 15116755272 0011444 0 ustar 00 <?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 } } autoloader/fof.php 0000644 00000004655 15116755272 0010213 0 ustar 00 <?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; } } } } config/domain/dispatcher.php 0000644 00000003246 15116755272 0012137 0 ustar 00 <?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; } } } config/domain/interface.php 0000644 00000002536 15116755272 0011752 0 ustar 00 <?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); } config/domain/tables.php 0000644 00000016606 15116755272 0011267 0 ustar 00 <?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; } } config/domain/views.php 0000644 00000020231 15116755272 0011137 0 ustar 00 <?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; } } config/provider.php 0000644 00000011421 15116755272 0010366 0 ustar 00 <?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; } } controller/controller.php 0000644 00000227522 15116755272 0011650 0 ustar 00 <?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; } } } database/database.php 0000644 00000013364 15116755272 0010607 0 ustar 00 <?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(); } } database/driver/joomla.php 0000644 00000036406 15116755272 0011621 0 ustar 00 <?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); } } } database/driver/mysql.php 0000644 00000034116 15116755272 0011501 0 ustar 00 <?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; } } database/driver/mysqli.php 0000644 00000060504 15116755272 0011652 0 ustar 00 <?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; } } database/driver/oracle.php 0000644 00000035571 15116755272 0011607 0 ustar 00 <?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++; } } } database/driver/pdo.php 0000644 00000064717 15116755272 0011130 0 ustar 00 <?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; } } database/driver/pdomysql.php 0000644 00000032267 15116755272 0012211 0 ustar 00 <?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++; } } } } database/driver/postgresql.php 0000644 00000112307 15116755272 0012536 0 ustar 00 <?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; } } database/driver/sqlazure.php 0000644 00000001425 15116755272 0012177 0 ustar 00 <?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'; } database/driver/sqlite.php 0000644 00000024400 15116755272 0011630 0 ustar 00 <?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++; } } } database/driver/sqlsrv.php 0000644 00000065001 15116755272 0011663 0 ustar 00 <?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; } } database/driver.php 0000644 00000153721 15116755272 0010340 0 ustar 00 <?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(); } database/factory.php 0000644 00000007672 15116755272 0010517 0 ustar 00 <?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; } } database/installer.php 0000644 00000055700 15116755272 0011040 0 ustar 00 <?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]; } } database/interface.php 0000644 00000001570 15116755272 0010777 0 ustar 00 <?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 { } database/iterator/azure.php 0000644 00000001121 15116755272 0012006 0 ustar 00 <?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 { } database/iterator/mysql.php 0000644 00000002421 15116755272 0012031 0 ustar 00 <?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); } } database/iterator/mysqli.php 0000644 00000002426 15116755272 0012207 0 ustar 00 <?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); } } database/iterator/oracle.php 0000644 00000001114 15116755272 0012127 0 ustar 00 <?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 { } database/iterator/pdo.php 0000644 00000003047 15116755272 0011453 0 ustar 00 <?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(); } } } database/iterator/pdomysql.php 0000644 00000001116 15116755272 0012534 0 ustar 00 <?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 { } database/iterator/postgresql.php 0000644 00000002430 15116755272 0013067 0 ustar 00 <?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); } } database/iterator/sqlite.php 0000644 00000001114 15116755272 0012163 0 ustar 00 <?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 { } database/iterator/sqlsrv.php 0000644 00000002430 15116755272 0012216 0 ustar 00 <?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); } } database/iterator.php 0000644 00000012615 15116755272 0010672 0 ustar 00 <?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; } } database/query/element.php 0000644 00000005135 15116755272 0011636 0 ustar 00 <?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)); } } } } database/query/limitable.php 0000644 00000004363 15116755272 0012151 0 ustar 00 <?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 { } database/query/mysql.php 0000644 00000001333 15116755272 0011346 0 ustar 00 <?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 { } database/query/mysqli.php 0000644 00000006366 15116755272 0011532 0 ustar 00 <?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() '; } } database/query/oracle.php 0000644 00000012720 15116755272 0011450 0 ustar 00 <?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; } } database/query/pdo.php 0000644 00000001123 15116755272 0010760 0 ustar 00 <?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 { } database/query/pdomysql.php 0000644 00000001227 15116755272 0012053 0 ustar 00 <?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 { } database/query/postgresql.php 0000644 00000033457 15116755272 0012420 0 ustar 00 <?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() '; } } database/query/preparable.php 0000644 00000004477 15116755272 0012332 0 ustar 00 <?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 { } database/query/sqlazure.php 0000644 00000002014 15116755272 0012044 0 ustar 00 <?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 = ''; } database/query/sqlite.php 0000644 00000016630 15116755272 0011510 0 ustar 00 <?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'; } } database/query/sqlsrv.php 0000644 00000021007 15116755272 0011533 0 ustar 00 <?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() '; } } database/query.php 0000644 00000117652 15116755272 0010215 0 ustar 00 <?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; } } dispatcher/dispatcher.php 0000644 00000042242 15116755272 0011550 0 ustar 00 <?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); } } download/adapter/abstract.php 0000644 00000006011 15116755272 0012320 0 ustar 00 <?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; } } download/adapter/cacert.pem 0000644 00001110611 15116755272 0011753 0 ustar 00 ## ## 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----- download/adapter/curl.php 0000644 00000013227 15116755272 0011471 0 ustar 00 <?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; } } download/adapter/fopen.php 0000644 00000006145 15116755272 0011634 0 ustar 00 <?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; } } } download/download.php 0000644 00000027526 15116755272 0010722 0 ustar 00 <?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; } } download/interface.php 0000644 00000005073 15116755272 0011044 0 ustar 00 <?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); } encrypt/aes/abstract.php 0000644 00000003576 15116755272 0011342 0 ustar 00 <?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); } } encrypt/aes/interface.php 0000644 00000006265 15116755272 0011475 0 ustar 00 <?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); } encrypt/aes/mcrypt.php 0000644 00000006113 15116755272 0011043 0 ustar 00 <?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); } } encrypt/aes/openssl.php 0000644 00000007002 15116755272 0011206 0 ustar 00 <?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); } } encrypt/aes.php 0000644 00000016631 15116755272 0007533 0 ustar 00 <?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; } } encrypt/base32.php 0000644 00000011105 15116755272 0010031 0 ustar 00 <?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)); } } encrypt/randval.php 0000644 00000010576 15116755272 0010414 0 ustar 00 <?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); } } encrypt/randvalinterface.php 0000644 00000000746 15116755272 0012273 0 ustar 00 <?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(); } encrypt/totp.php 0000644 00000011101 15116755272 0007734 0 ustar 00 <?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); } } form/field/accesslevel.php 0000644 00000007607 15116755272 0011621 0 ustar 00 <?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>'; } } form/field/actions.php 0000644 00000013263 15116755272 0010763 0 ustar 00 <?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; } } form/field/button.php 0000644 00000006166 15116755272 0010642 0 ustar 00 <?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; } } form/field/cachehandler.php 0000644 00000004674 15116755272 0011732 0 ustar 00 <?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>'; } } form/field/calendar.php 0000644 00000012352 15116755272 0011072 0 ustar 00 <?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>'; } } } form/field/captcha.php 0000644 00000003727 15116755272 0010732 0 ustar 00 <?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(); } } form/field/checkbox.php 0000644 00000007235 15116755272 0011113 0 ustar 00 <?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>'; } } form/field/checkboxes.php 0000644 00000005354 15116755272 0011443 0 ustar 00 <?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(); } } form/field/components.php 0000644 00000013665 15116755272 0011516 0 ustar 00 <?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; } } form/field/editor.php 0000644 00000004430 15116755272 0010605 0 ustar 00 <?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>'; } } form/field/email.php 0000644 00000007145 15116755272 0010414 0 ustar 00 <?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; } } form/field/groupedbutton.php 0000644 00000005737 15116755272 0012233 0 ustar 00 <?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; } } form/field/groupedlist.php 0000644 00000010452 15116755272 0011661 0 ustar 00 <?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; } } form/field/hidden.php 0000644 00000003711 15116755272 0010553 0 ustar 00 <?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(); } } form/field/image.php 0000644 00000001042 15116755272 0010375 0 ustar 00 <?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 { } form/field/imagelist.php 0000644 00000006352 15116755272 0011302 0 ustar 00 <?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(); } } form/field/integer.php 0000644 00000004615 15116755272 0010761 0 ustar 00 <?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>'; } } form/field/language.php 0000644 00000005474 15116755272 0011113 0 ustar 00 <?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>'; } } form/field/list.php 0000644 00000023506 15116755272 0010277 0 ustar 00 <?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; } } form/field/media.php 0000644 00000006131 15116755272 0010376 0 ustar 00 <?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(); } } form/field/model.php 0000644 00000015026 15116755272 0010422 0 ustar 00 <?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; } } form/field/ordering.php 0000644 00000013324 15116755272 0011132 0 ustar 00 <?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; } } form/field/password.php 0000644 00000004620 15116755272 0011162 0 ustar 00 <?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>'; } } form/field/plugins.php 0000644 00000004663 15116755272 0011010 0 ustar 00 <?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>'; } } form/field/published.php 0000644 00000011366 15116755272 0011304 0 ustar 00 <?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); } } form/field/radio.php 0000644 00000004644 15116755272 0010424 0 ustar 00 <?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>'; } } form/field/relation.php 0000644 00000011564 15116755272 0011142 0 ustar 00 <?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; } } form/field/rules.php 0000644 00000062321 15116755272 0010454 0 ustar 00 <?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); } } form/field/selectrow.php 0000644 00000005575 15116755272 0011341 0 ustar 00 <?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); } } form/field/sessionhandler.php 0000644 00000004703 15116755272 0012343 0 ustar 00 <?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>'; } } form/field/spacer.php 0000644 00000003734 15116755272 0010602 0 ustar 00 <?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(); } } form/field/sql.php 0000644 00000004700 15116755272 0010116 0 ustar 00 <?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>'; } } form/field/tag.php 0000644 00000011633 15116755272 0010075 0 ustar 00 <?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>'; } } form/field/tel.php 0000644 00000006767 15116755272 0010122 0 ustar 00 <?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; } } form/field/text.php 0000644 00000012303 15116755272 0010301 0 ustar 00 <?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; } } form/field/textarea.php 0000644 00000004240 15116755272 0011133 0 ustar 00 <?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(); } } form/field/timezone.php 0000644 00000005023 15116755272 0011150 0 ustar 00 <?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(); } } form/field/title.php 0000644 00000003040 15116755272 0010434 0 ustar 00 <?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; } } form/field/url.php 0000644 00000006753 15116755272 0010133 0 ustar 00 <?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; } } form/field/user.php 0000644 00000015557 15116755272 0010311 0 ustar 00 <?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; } } form/field/usergroup.php 0000644 00000006750 15116755272 0011361 0 ustar 00 <?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>'; } } form/field.php 0000644 00000001647 15116755272 0007326 0 ustar 00 <?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(); } form/form.php 0000644 00000037543 15116755272 0007212 0 ustar 00 <?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); } } form/header/accesslevel.php 0000644 00000002027 15116755272 0011755 0 ustar 00 <?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; } } form/header/field.php 0000644 00000001640 15116755272 0010547 0 ustar 00 <?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); } } } form/header/fielddate.php 0000644 00000006317 15116755272 0011413 0 ustar 00 <?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 ''; } } form/header/fieldfilterable.php 0000644 00000005367 15116755272 0012613 0 ustar 00 <?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; } } form/header/fieldsearchable.php 0000644 00000005366 15116755272 0012572 0 ustar 00 <?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; } } form/header/fieldselectable.php 0000644 00000006425 15116755272 0012601 0 ustar 00 <?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; } } form/header/fieldsql.php 0000644 00000003250 15116755272 0011266 0 ustar 00 <?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; } } form/header/filterdate.php 0000644 00000001157 15116755272 0011612 0 ustar 00 <?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 ''; } } form/header/filterfilterable.php 0000644 00000001172 15116755272 0013003 0 ustar 00 <?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 ''; } } form/header/filtersearchable.php 0000644 00000001172 15116755272 0012763 0 ustar 00 <?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 ''; } } form/header/filterselectable.php 0000644 00000001166 15116755272 0013000 0 ustar 00 <?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 ''; } } form/header/filtersql.php 0000644 00000001144 15116755272 0011470 0 ustar 00 <?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 ''; } } form/header/language.php 0000644 00000002022 15116755272 0011242 0 ustar 00 <?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; } } form/header/model.php 0000644 00000005741 15116755272 0010572 0 ustar 00 <?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; } } form/header/ordering.php 0000644 00000003467 15116755272 0011306 0 ustar 00 <?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; } } } form/header/published.php 0000644 00000002435 15116755272 0011446 0 ustar 00 <?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; } } form/header/rowselect.php 0000644 00000001323 15116755272 0011471 0 ustar 00 <?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)" />'; } } form/header.php 0000644 00000026476 15116755272 0007502 0 ustar 00 <?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(); } } form/helper.php 0000644 00000013536 15116755272 0007522 0 ustar 00 <?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); } } hal/document.php 0000644 00000012620 15116755272 0007653 0 ustar 00 <?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 ) ); } } hal/link.php 0000644 00000006153 15116755272 0006776 0 ustar 00 <?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; } } } hal/links.php 0000644 00000006163 15116755272 0007162 0 ustar 00 <?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(); } } } hal/render/interface.php 0000644 00000001171 15116755272 0011253 0 ustar 00 <?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()); } hal/render/json.php 0000644 00000006500 15116755272 0010265 0 ustar 00 <?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; } } include.php 0000644 00000001402 15116755272 0006710 0 ustar 00 <?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'); } } inflector/inflector.php 0000644 00000033107 15116755272 0011246 0 ustar 00 <?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; } } input/input.php 0000644 00000016372 15116755272 0007577 0 ustar 00 <?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; } } input/jinput/cli.php 0000644 00000007445 15116755272 0010521 0 ustar 00 <?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; } } input/jinput/cookie.php 0000644 00000007634 15116755272 0011223 0 ustar 00 <?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; } } input/jinput/files.php 0000644 00000005631 15116755272 0011047 0 ustar 00 <?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) { } } input/jinput/input.php 0000644 00000020262 15116755272 0011101 0 ustar 00 <?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; } } } input/jinput/json.php 0000644 00000002713 15116755272 0010714 0 ustar 00 <?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; } } integration/joomla/filesystem/filesystem.php 0000644 00000013412 15116755272 0015445 0 ustar 00 <?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); } } integration/joomla/platform.php 0000644 00000060145 15116755272 0012726 0 ustar 00 <?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(); } } } layout/file.php 0000644 00000003743 15116755272 0007533 0 ustar 00 <?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; } } layout/helper.php 0000644 00000002733 15116755272 0010071 0 ustar 00 <?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; } } less/formatter/classic.php 0000644 00000006754 15116755272 0011676 0 ustar 00 <?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--; } } } less/formatter/compressed.php 0000644 00000002064 15116755272 0012407 0 ustar 00 <?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 ""; } } less/formatter/joomla.php 0000644 00000001525 15116755272 0011525 0 ustar 00 <?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"; } less/formatter/lessjs.php 0000644 00000001470 15116755272 0011546 0 ustar 00 <?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 = ","; } less/less.php 0000644 00000200516 15116755272 0007210 0 ustar 00 <?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' ); } less/parser/parser.php 0000644 00000116252 15116755274 0011037 0 ustar 00 <?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; } } LICENSE.txt 0000644 00000043761 15116755274 0006417 0 ustar 00 ================================================================================ 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. model/behavior/access.php 0000644 00000004330 15116755274 0011432 0 ustar 00 <?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; } } } } model/behavior/emptynonzero.php 0000644 00000001532 15116755274 0012743 0 ustar 00 <?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'); } } model/behavior/enabled.php 0000644 00000004401 15116755274 0011562 0 ustar 00 <?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; } } } } model/behavior/filters.php 0000644 00000005145 15116755274 0011646 0 ustar 00 <?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); } } } } model/behavior/language.php 0000644 00000010776 15116755274 0011767 0 ustar 00 <?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; } } } } model/behavior/private.php 0000644 00000005055 15116755274 0011650 0 ustar 00 <?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; } } } } model/behavior.php 0000644 00000010503 15116755274 0010170 0 ustar 00 <?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) { } } model/dispatcher/behavior.php 0000644 00000001002 15116755274 0012310 0 ustar 00 <?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 { } model/field/boolean.php 0000644 00000001312 15116755274 0011071 0 ustar 00 <?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 === ''); } } model/field/date.php 0000644 00000011673 15116755274 0010402 0 ustar 00 <?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; } } model/field/number.php 0000644 00000012011 15116755274 0010740 0 ustar 00 <?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; } } model/field/text.php 0000644 00000006210 15116755274 0010440 0 ustar 00 <?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 ''; } } model/field.php 0000644 00000020264 15116755274 0007461 0 ustar 00 <?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; } } model/model.php 0000644 00000221320 15116755274 0007472 0 ustar 00 <?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); } } platform/filesystem/filesystem.php 0000644 00000007730 15116755274 0013475 0 ustar 00 <?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); } } platform/filesystem/interface.php 0000644 00000011342 15116755274 0013243 0 ustar 00 <?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); } platform/interface.php 0000644 00000041020 15116755274 0011053 0 ustar 00 <?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(); } platform/platform.php 0000644 00000043566 15116755274 0010760 0 ustar 00 <?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; } } query/abstract.php 0000644 00000001761 15116755274 0010247 0 ustar 00 <?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; } } render/abstract.php 0000644 00000017160 15116755274 0010361 0 ustar 00 <?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; } } } render/joomla.php 0000644 00000052471 15116755274 0010043 0 ustar 00 <?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>'; } } render/joomla3.php 0000644 00000012027 15116755274 0010117 0 ustar 00 <?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; } } render/strapper.php 0000644 00000111302 15116755274 0010407 0 ustar 00 <?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; } } string/utils.php 0000644 00000004161 15116755274 0007742 0 ustar 00 <?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; } } table/behavior/assets.php 0000644 00000015217 15116755274 0011470 0 ustar 00 <?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; } } table/behavior/contenthistory.php 0000644 00000003146 15116755274 0013260 0 ustar 00 <?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; } } table/behavior/tags.php 0000644 00000006133 15116755274 0011121 0 ustar 00 <?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; } } } } table/behavior.php 0000644 00000014277 15116755274 0010173 0 ustar 00 <?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; } } table/dispatcher/behavior.php 0000644 00000001032 15116755274 0012302 0 ustar 00 <?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 { } table/nested.php 0000644 00000164107 15116755274 0007654 0 ustar 00 <?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; } } table/relations.php 0000644 00000101027 15116755274 0010362 0 ustar 00 <?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; } } table/table.php 0000644 00000261621 15116755275 0007461 0 ustar 00 <?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; } } template/utils.php 0000644 00000034465 15116755275 0010262 0 ustar 00 <?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); } } toolbar/toolbar.php 0000644 00000070330 15116755275 0010402 0 ustar 00 <?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)); } } } utils/array/array.php 0000644 00000030761 15116755275 0010676 0 ustar 00 <?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; } } utils/cache/cleaner.php 0000644 00000004353 15116755275 0011114 0 ustar 00 <?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 } } } } } utils/config/helper.php 0000644 00000005143 15116755275 0011162 0 ustar 00 <?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; } } } utils/filescheck/filescheck.php 0000644 00000015641 15116755275 0012642 0 ustar 00 <?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; } } utils/ini/parser.php 0000644 00000011061 15116755275 0010505 0 ustar 00 <?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; } } utils/installscript/installscript.php 0000644 00000200207 15116755275 0014222 0 ustar 00 <?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; } } } utils/ip/ip.php 0000644 00000026514 15116755275 0007463 0 ustar 00 <?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; } } utils/object/object.php 0000644 00000012143 15116755275 0011150 0 ustar 00 <?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); } } utils/observable/dispatcher.php 0000644 00000016452 15116755275 0012715 0 ustar 00 <?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; } } utils/observable/event.php 0000644 00000003631 15116755275 0011703 0 ustar 00 <?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; } } } utils/phpfunc/phpfunc.php 0000644 00000001711 15116755275 0011541 0 ustar 00 <?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); } } utils/timer/timer.php 0000644 00000003775 15116755275 0010707 0 ustar 00 <?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(); } } utils/update/collection.php 0000644 00000023630 15116755275 0012054 0 ustar 00 <?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; } } } utils/update/extension.php 0000644 00000006465 15116755275 0011744 0 ustar 00 <?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; } } utils/update/joomla.php 0000644 00000040260 15116755275 0011200 0 ustar 00 <?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; } } utils/update/update.php 0000644 00000101122 15116755275 0011174 0 ustar 00 <?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(); } } version.txt 0000644 00000000020 15116755275 0007000 0 ustar 00 2.5.5 2016-08-19 view/csv.php 0000644 00000010770 15116755275 0007045 0 ustar 00 <?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; } } view/form.php 0000644 00000006334 15116755275 0007216 0 ustar 00 <?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; } } view/html.php 0000644 00000010571 15116755275 0007215 0 ustar 00 <?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; } } view/json.php 0000644 00000017050 15116755275 0007221 0 ustar 00 <?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; } } view/raw.php 0000644 00000021062 15116755275 0007037 0 ustar 00 <?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; } } view/view.php 0000644 00000063506 15116755275 0007231 0 ustar 00 <?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; } }
| ver. 1.4 |
Github
|
.
| PHP 8.1.33 | Генерация страницы: 1.79 |
proxy
|
phpinfo
|
Настройка