Spade

Mini Shell

Directory:~$ /home/lmsyaran/public_html/joomla4/
Upload File

[Home] [System Details] [Kill Me]
Current File:~$ /home/lmsyaran/public_html/joomla4/fof.tar

autoloader/component.php000064400000047172151155117330011433
0ustar00<?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.php000064400000004655151155117330010202 0ustar00<?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.php000064400000003246151155117330012126
0ustar00<?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.php000064400000002536151155117330011741
0ustar00<?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.php000064400000016606151155117330011256
0ustar00<?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.php000064400000020231151155117330011126
0ustar00<?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.php000064400000011421151155117330010355 0ustar00<?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.php000064400000227522151155117330011637
0ustar00<?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.php000064400000013364151155117350010600 0ustar00<?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.php000064400000036406151155117350011612
0ustar00<?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.php000064400000034116151155117350011472
0ustar00<?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.php000064400000060504151155117350011643
0ustar00<?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.php000064400000035571151155117350011600
0ustar00<?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.php000064400000064717151155117350011121
0ustar00<?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.php000064400000032267151155117350012202
0ustar00<?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.php000064400000112307151155117350012527
0ustar00<?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.php000064400000001425151155117350012170
0ustar00<?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.php000064400000024400151155117350011621
0ustar00<?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.php000064400000065001151155117350011654
0ustar00<?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.php000064400000153721151155117350010331 0ustar00<?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.php000064400000007672151155117350010510 0ustar00<?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.php000064400000055700151155117350011031 0ustar00<?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.php000064400000001570151155117350010770 0ustar00<?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.php000064400000001121151155117350011777
0ustar00<?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.php000064400000002421151155117350012022
0ustar00<?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.php000064400000002426151155117350012200
0ustar00<?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.php000064400000001114151155117350012120
0ustar00<?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.php000064400000003047151155117350011444
0ustar00<?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.php000064400000001116151155117350012525
0ustar00<?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.php000064400000002430151155117350013060
0ustar00<?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.php000064400000001114151155117350012154
0ustar00<?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.php000064400000002430151155117350012207
0ustar00<?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.php000064400000012615151155117350010663 0ustar00<?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.php000064400000005135151155117350011627
0ustar00<?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.php000064400000004363151155117350012142
0ustar00<?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.php000064400000001333151155117350011337
0ustar00<?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.php000064400000006366151155117350011523
0ustar00<?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.php000064400000012720151155117350011441
0ustar00<?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.php000064400000001123151155117350010751 0ustar00<?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.php000064400000001227151155117350012044
0ustar00<?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.php000064400000033457151155117350012411
0ustar00<?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.php000064400000004477151155117350012323
0ustar00<?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.php000064400000002014151155117350012035
0ustar00<?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.php000064400000016630151155117350011501
0ustar00<?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.php000064400000021007151155117350011524
0ustar00<?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.php000064400000117652151155117350010206 0ustar00<?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.php000064400000042242151155117350011541
0ustar00<?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.php000064400000006011151155117350012311
0ustar00<?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.pem000064400001110611151155117350011744 0ustar00##
## 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.php000064400000013227151155117350011462
0ustar00<?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.php000064400000006145151155117350011625
0ustar00<?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.php000064400000027526151155117350010713 0ustar00<?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.php000064400000005073151155117350011035
0ustar00<?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.php000064400000003576151155117350011333
0ustar00<?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.php000064400000006265151155117350011466
0ustar00<?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.php000064400000006113151155117350011034
0ustar00<?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.php000064400000007002151155117350011177
0ustar00<?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.php000064400000016631151155117350007524 0ustar00<?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.php000064400000011105151155117350010022 0ustar00<?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.php000064400000010576151155117350010405 0ustar00<?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.php000064400000000746151155117350012264
0ustar00<?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.php000064400000011101151155117350007725 0ustar00<?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.php000064400000007607151155117350011612
0ustar00<?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.php000064400000013263151155117350010754 0ustar00<?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.php000064400000006166151155117350010633 0ustar00<?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.php000064400000004674151155117350011723
0ustar00<?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.php000064400000012352151155117350011063
0ustar00<?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.php000064400000003727151155117350010723 0ustar00<?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.php000064400000007235151155117370011106
0ustar00<?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.php000064400000005354151155117370011436
0ustar00<?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.php000064400000013665151155117370011511
0ustar00<?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.php000064400000004430151155117370010600 0ustar00<?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.php000064400000007145151155117370010407 0ustar00<?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.php000064400000005737151155117370012226
0ustar00<?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.php000064400000010452151155117370011654
0ustar00<?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.php000064400000003711151155117370010546 0ustar00<?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.php000064400000001042151155117400010362 0ustar00<?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.php000064400000006352151155117400011267
0ustar00<?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.php000064400000004615151155117400010746 0ustar00<?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.php000064400000005474151155117400011100
0ustar00<?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.php000064400000023506151155117400010264 0ustar00<?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.php000064400000006131151155117400010363 0ustar00<?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.php000064400000015026151155117400010407 0ustar00<?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.php000064400000013324151155117400011117
0ustar00<?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.php000064400000004620151155117400011147
0ustar00<?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.php000064400000004663151155117400010775 0ustar00<?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.php000064400000011366151155117400011271
0ustar00<?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.php000064400000004644151155117400010411 0ustar00<?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.php000064400000011564151155117400011127
0ustar00<?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.php000064400000062321151155117400010441 0ustar00<?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">|&ndash;</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>&#160; ';

                // 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">&ndash;</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>&#160; ';

                // 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.php000064400000005575151155117400011326
0ustar00<?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.php000064400000004703151155117400012330
0ustar00<?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.php000064400000003734151155117400010567 0ustar00<?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.php000064400000004700151155117400010103 0ustar00<?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.php000064400000011633151155117400010062 0ustar00<?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.php000064400000006767151155117400010107 0ustar00<?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.php000064400000012303151155117430010271 0ustar00<?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.php000064400000004240151155117430011123
0ustar00<?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.php000064400000005023151155117430011140
0ustar00<?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.php000064400000003040151155117430010424 0ustar00<?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.php000064400000006753151155117430010123 0ustar00<?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.php000064400000015557151155117430010301 0ustar00<?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.php000064400000006750151155117430011351
0ustar00<?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.php000064400000001647151155117430007316 0ustar00<?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.php000064400000037543151155117430007202 0ustar00<?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.php000064400000002027151155117430011745
0ustar00<?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.php000064400000001640151155117430010537 0ustar00<?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.php000064400000006317151155117430011403
0ustar00<?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.php000064400000005367151155117430012603
0ustar00<?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.php000064400000005366151155117430012562
0ustar00<?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.php000064400000006425151155117430012571
0ustar00<?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.php000064400000003250151155117430011256
0ustar00<?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.php000064400000001157151155117430011602
0ustar00<?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.php000064400000001172151155117430012773
0ustar00<?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.php000064400000001172151155117430012753
0ustar00<?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.php000064400000001166151155117430012770
0ustar00<?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.php000064400000001144151155117430011460
0ustar00<?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.php000064400000002022151155117430011232
0ustar00<?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.php000064400000005741151155117430010562 0ustar00<?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.php000064400000003467151155117430011276
0ustar00<?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.php000064400000002435151155117430011436
0ustar00<?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.php000064400000001323151155117430011461
0ustar00<?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.php000064400000026476151155117430007472 0ustar00<?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.php000064400000013536151155117430007512 0ustar00<?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.php000064400000012620151155117430007643 0ustar00<?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.php000064400000006153151155117430006766 0ustar00<?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.php000064400000006163151155117430007152 0ustar00<?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.php000064400000001171151155117430011243
0ustar00<?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.php000064400000006500151155117430010255 0ustar00<?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.php000064400000001402151155117430006700 0ustar00<?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.php000064400000033107151155117430011236
0ustar00<?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.php000064400000016372151155117430007567 0ustar00<?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.php000064400000007445151155117430010511 0ustar00<?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.php000064400000007634151155117430011213
0ustar00<?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.php000064400000005631151155117430011037 0ustar00<?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.php000064400000020262151155117430011071 0ustar00<?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.php000064400000002713151155117430010704 0ustar00<?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.php000064400000013412151155117430015435
0ustar00<?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.php000064400000060145151155117430012716
0ustar00<?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.php000064400000003743151155117430007523 0ustar00<?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.php000064400000002733151155117430010061 0ustar00<?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.php000064400000006754151155117430011666
0ustar00<?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.php000064400000002064151155117430012377
0ustar00<?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.php000064400000001525151155117430011515
0ustar00<?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.php000064400000001470151155117440011537
0ustar00<?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.php000064400000200516151155117440007201 0ustar00<?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.php000064400000116252151155117440011026 0ustar00<?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.txt000064400000043761151155117440006406
0ustar00================================================================================
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.php000064400000004330151155117440011421
0ustar00<?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.php000064400000001532151155117440012732
0ustar00<?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.php000064400000004401151155117440011551
0ustar00<?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.php000064400000005145151155117440011635
0ustar00<?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.php000064400000010776151155117440011756
0ustar00<?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.php000064400000005055151155117440011637
0ustar00<?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.php000064400000010503151155117440010157 0ustar00<?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.php000064400000001002151155117440012277
0ustar00<?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.php000064400000001312151155117440011060
0ustar00<?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.php000064400000011673151155117440010371 0ustar00<?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.php000064400000012011151155117440010727 0ustar00<?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.php000064400000006210151155117440010427 0ustar00<?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.php000064400000020264151155117440007450 0ustar00<?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.php000064400000221320151155117440007461 0ustar00<?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.php000064400000007730151155117440013464
0ustar00<?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.php000064400000011342151155117440013232
0ustar00<?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.php000064400000041020151155117440011042
0ustar00<?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.php000064400000043566151155117460010751 0ustar00<?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.php000064400000001761151155117460010240 0ustar00<?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.php000064400000017160151155117460010352 0ustar00<?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.php000064400000052471151155117460010034 0ustar00<?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">&nbsp;*</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.php000064400000012027151155117460010110 0ustar00<?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.php000064400000111302151155117470010401 0ustar00<?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.php000064400000004161151155117470007734 0ustar00<?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('/&szlig;/', '/&(..)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.php000064400000015217151155117470011462
0ustar00<?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.php000064400000003146151155117470013252
0ustar00<?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.php000064400000006133151155117470011113
0ustar00<?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.php000064400000014277151155117470010165 0ustar00<?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.php000064400000001032151155117470012274
0ustar00<?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.php000064400000164107151155117470007646 0ustar00<?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.php000064400000101027151155117510010347 0ustar00<?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.php000064400000261621151155117510007445 0ustar00<?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.php000064400000034465151155117510010246 0ustar00<?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)) .
'&nbsp;', $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.php000064400000070330151155117510010366 0ustar00<?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.php000064400000030761151155117510010662 0ustar00<?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.php000064400000004353151155117520011101
0ustar00<?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.php000064400000005143151155117520011147
0ustar00<?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.php000064400000015641151155117520012627
0ustar00<?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.php000064400000011061151155117520010472 0ustar00<?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.php000064400000200207151155117520014207
0ustar00<?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.php000064400000026514151155117530007451 0ustar00<?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.php000064400000012143151155117530011136
0ustar00<?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.php000064400000016452151155117530012703
0ustar00<?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.php000064400000003631151155117530011671
0ustar00<?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.php000064400000001711151155117530011527
0ustar00<?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.php000064400000003775151155117530010675 0ustar00<?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.php000064400000023630151155117530012042
0ustar00<?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.php000064400000006465151155117530011732
0ustar00<?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.php000064400000040260151155117530011166
0ustar00<?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.php000064400000101122151155117530011162
0ustar00<?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 .= '&amp;';
					}

					$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 .= '&amp;';
				}

				$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.txt000064400000000020151155117530006766 0ustar002.5.5
2016-08-19view/csv.php000064400000010770151155117530007033 0ustar00<?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.php000064400000006334151155117530007204 0ustar00<?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.php000064400000010571151155117530007203 0ustar00<?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.php000064400000017050151155117530007207 0ustar00<?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.php000064400000021062151155117530007025 0ustar00<?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.php000064400000063506151155117530007217 0ustar00<?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;
	}
}
index.html000064400000000036151165775160006556 0ustar00<!DOCTYPE
html><title></title>9ca401dc5c83ea71321893da14a56979-cache-fof-90cf80baabafd1e256f0a034c9c7586d.php000064400000040321151165775160020114
0ustar00<?php die("Access Denied");
?>#x#O:24:"Joomla\Registry\Registry":3:{s:7:"*data";O:8:"stdClass":2:{s:6:"tables";s:13210:"["x041z_action_log_config","x041z_action_logs","x041z_action_logs_extensions","x041z_action_logs_users","x041z_advancedtemplates","x041z_assets","x041z_associations","x041z_banner_clients","x041z_banner_tracks","x041z_banners","x041z_categories","x041z_componentbuilder_admin_custom_tabs","x041z_componentbuilder_admin_fields","x041z_componentbuilder_admin_fields_conditions","x041z_componentbuilder_admin_fields_relations","x041z_componentbuilder_admin_view","x041z_componentbuilder_class_extends","x041z_componentbuilder_class_method","x041z_componentbuilder_class_property","x041z_componentbuilder_component_admin_views","x041z_componentbuilder_component_config","x041z_componentbuilder_component_custom_admin_menus","x041z_componentbuilder_component_custom_admin_views","x041z_componentbuilder_component_dashboard","x041z_componentbuilder_component_files_folders","x041z_componentbuilder_component_modules","x041z_componentbuilder_component_mysql_tweaks","x041z_componentbuilder_component_placeholders","x041z_componentbuilder_component_plugins","x041z_componentbuilder_component_site_views","x041z_componentbuilder_component_updates","x041z_componentbuilder_custom_admin_view","x041z_componentbuilder_custom_code","x041z_componentbuilder_dynamic_get","x041z_componentbuilder_external_code","x041z_componentbuilder_field","x041z_componentbuilder_fieldtype","x041z_componentbuilder_help_document","x041z_componentbuilder_joomla_component","x041z_componentbuilder_joomla_module","x041z_componentbuilder_joomla_module_files_folders_urls","x041z_componentbuilder_joomla_module_updates","x041z_componentbuilder_joomla_plugin","x041z_componentbuilder_joomla_plugin_files_folders_urls","x041z_componentbuilder_joomla_plugin_group","x041z_componentbuilder_joomla_plugin_updates","x041z_componentbuilder_language","x041z_componentbuilder_language_translation","x041z_componentbuilder_layout","x041z_componentbuilder_library","x041z_componentbuilder_library_config","x041z_componentbuilder_library_files_folders_urls","x041z_componentbuilder_placeholder","x041z_componentbuilder_power","x041z_componentbuilder_server","x041z_componentbuilder_site_view","x041z_componentbuilder_snippet","x041z_componentbuilder_snippet_type","x041z_componentbuilder_template","x041z_componentbuilder_validation_rule","x041z_contact_details","x041z_content","x041z_content_frontpage","x041z_content_rating","x041z_content_types","x041z_contentitem_tag_map","x041z_core_log_searches","x041z_easyblog_acl","x041z_easyblog_acl_filters","x041z_easyblog_acl_group","x041z_easyblog_adsense","x041z_easyblog_autoarticle_map","x041z_easyblog_blogger_subscription","x041z_easyblog_captcha","x041z_easyblog_category","x041z_easyblog_category_acl","x041z_easyblog_category_acl_item","x041z_easyblog_category_subscription","x041z_easyblog_comment","x041z_easyblog_configs","x041z_easyblog_drafts","x041z_easyblog_external","x041z_easyblog_external_groups","x041z_easyblog_featured","x041z_easyblog_feedburner","x041z_easyblog_feeds","x041z_easyblog_feeds_history","x041z_easyblog_hashkeys","x041z_easyblog_likes","x041z_easyblog_mailq","x041z_easyblog_mediamanager","x041z_easyblog_meta","x041z_easyblog_migrate_content","x041z_easyblog_oauth","x041z_easyblog_oauth_posts","x041z_easyblog_post","x041z_easyblog_post_rejected","x041z_easyblog_post_subscription","x041z_easyblog_post_tag","x041z_easyblog_ratings","x041z_easyblog_reports","x041z_easyblog_site_subscription","x041z_easyblog_stream","x041z_easyblog_tag","x041z_easyblog_team","x041z_easyblog_team_groups","x041z_easyblog_team_post","x041z_easyblog_team_request","x041z_easyblog_team_subscription","x041z_easyblog_team_users","x041z_easyblog_trackback","x041z_easyblog_trackback_sent","x041z_easyblog_twitter_microblog","x041z_easyblog_users","x041z_easyblog_xml_wpdata","x041z_extensions","x041z_ezportal","x041z_ezrealty","x041z_ezrealty_catg","x041z_ezrealty_country","x041z_ezrealty_images","x041z_ezrealty_incats","x041z_ezrealty_lightbox","x041z_ezrealty_locality","x041z_ezrealty_state","x041z_fields","x041z_fields_categories","x041z_fields_groups","x041z_fields_values","x041z_finder_filters","x041z_finder_links","x041z_finder_links_terms0","x041z_finder_links_terms1","x041z_finder_links_terms2","x041z_finder_links_terms3","x041z_finder_links_terms4","x041z_finder_links_terms5","x041z_finder_links_terms6","x041z_finder_links_terms7","x041z_finder_links_terms8","x041z_finder_links_terms9","x041z_finder_links_termsa","x041z_finder_links_termsb","x041z_finder_links_termsc","x041z_finder_links_termsd","x041z_finder_links_termse","x041z_finder_links_termsf","x041z_finder_taxonomy","x041z_finder_taxonomy_map","x041z_finder_terms","x041z_finder_terms_common","x041z_finder_tokens","x041z_finder_tokens_aggregate","x041z_finder_types","x041z_helpdeskpro_articles","x041z_helpdeskpro_categories","x041z_helpdeskpro_configs","x041z_helpdeskpro_emails","x041z_helpdeskpro_field_categories","x041z_helpdeskpro_field_value","x041z_helpdeskpro_fields","x041z_helpdeskpro_labels","x041z_helpdeskpro_messages","x041z_helpdeskpro_priorities","x041z_helpdeskpro_replies","x041z_helpdeskpro_statuses","x041z_helpdeskpro_tickets","x041z_helpdeskpro_urls","x041z_hikamarket_config","x041z_hikamarket_customer_vendor","x041z_hikamarket_fee","x041z_hikamarket_order_transaction","x041z_hikamarket_vendor","x041z_hikamarket_vendor_user","x041z_hikashop_address","x041z_hikashop_badge","x041z_hikashop_banner","x041z_hikashop_cart","x041z_hikashop_cart_product","x041z_hikashop_category","x041z_hikashop_characteristic","x041z_hikashop_click","x041z_hikashop_config","x041z_hikashop_currency","x041z_hikashop_discount","x041z_hikashop_download","x041z_hikashop_email_log","x041z_hikashop_entry","x041z_hikashop_field","x041z_hikashop_file","x041z_hikashop_filter","x041z_hikashop_geolocation","x041z_hikashop_history","x041z_hikashop_limit","x041z_hikashop_massaction","x041z_hikashop_order","x041z_hikashop_order_product","x041z_hikashop_orderstatus","x041z_hikashop_payment","x041z_hikashop_plugin","x041z_hikashop_price","x041z_hikashop_product","x041z_hikashop_product_category","x041z_hikashop_product_related","x041z_hikashop_shipping","x041z_hikashop_shipping_price","x041z_hikashop_tax","x041z_hikashop_taxation","x041z_hikashop_user","x041z_hikashop_variant","x041z_hikashop_vote","x041z_hikashop_vote_user","x041z_hikashop_waitlist","x041z_hikashop_warehouse","x041z_hikashop_widget","x041z_hikashop_zone","x041z_hikashop_zone_link","x041z_hilation_relation","x041z_invoices_contacts","x041z_invoices_currencies","x041z_invoices_invoices","x041z_invoices_items","x041z_invoices_payments","x041z_invoices_tax_invoice","x041z_invoices_taxes","x041z_invoices_templateitems","x041z_invoices_templates","x041z_jamegafilter","x041z_jea_amenities","x041z_jea_areas","x041z_jea_conditions","x041z_jea_departments","x041z_jea_gateways","x041z_jea_heatingtypes","x041z_jea_hotwatertypes","x041z_jea_properties","x041z_jea_slogans","x041z_jea_tools","x041z_jea_towns","x041z_jea_types","x041z_languages","x041z_menu","x041z_menu_types","x041z_messages","x041z_messages_cfg","x041z_mobilemenuck_menus","x041z_mobilemenuck_styles","x041z_modules","x041z_modules_menu","x041z_molation_m_relation","x041z_moojla_category_map","x041z_moojla_cohort","x041z_moojla_content","x041z_moojla_course","x041z_moojla_course_field_mapper","x041z_moojla_course_group_map","x041z_moojla_enrolment","x041z_moojla_favorite","x041z_moojla_module","x041z_moojla_rate","x041z_moojla_section","x041z_moojla_tag","x041z_moojla_tag_instance","x041z_moojla_user_field_map","x041z_moojla_user_map","x041z_newsfeeds","x041z_nextend2_image_storage","x041z_nextend2_section_storage","x041z_nextend2_smartslider3_generators","x041z_nextend2_smartslider3_sliders","x041z_nextend2_smartslider3_sliders_xref","x041z_nextend2_smartslider3_slides","x041z_notifly_events","x041z_notifly_logs","x041z_notifly_templates","x041z_overrider","x041z_phocacart_attribute_values","x041z_phocacart_attributes","x041z_phocacart_bulk_prices","x041z_phocacart_cart_multiple","x041z_phocacart_categories","x041z_phocacart_countries","x041z_phocacart_coupon_categories","x041z_phocacart_coupon_count","x041z_phocacart_coupon_count_user","x041z_phocacart_coupon_products","x041z_phocacart_coupons","x041z_phocacart_currencies","x041z_phocacart_discount_categories","x041z_phocacart_discount_products","x041z_phocacart_discounts","x041z_phocacart_export","x041z_phocacart_feeds","x041z_phocacart_form_fields","x041z_phocacart_groups","x041z_phocacart_hits","x041z_phocacart_import","x041z_phocacart_item_access","x041z_phocacart_item_groups","x041z_phocacart_logs","x041z_phocacart_manufacturers","x041z_phocacart_opening_times","x041z_phocacart_order_attributes","x041z_phocacart_order_coupons","x041z_phocacart_order_discounts","x041z_phocacart_order_downloads","x041z_phocacart_order_history","x041z_phocacart_order_product_discounts","x041z_phocacart_order_products","x041z_phocacart_order_statuses","x041z_phocacart_order_tax_recapitulation","x041z_phocacart_order_total","x041z_phocacart_order_users","x041z_phocacart_orders","x041z_phocacart_parameter_values","x041z_phocacart_parameter_values_related","x041z_phocacart_parameters","x041z_phocacart_payment_method_countries","x041z_phocacart_payment_method_regions","x041z_phocacart_payment_method_shipping","x041z_phocacart_payment_method_zones","x041z_phocacart_payment_methods","x041z_phocacart_product_categories","x041z_phocacart_product_discounts","x041z_phocacart_product_featured","x041z_phocacart_product_files","x041z_phocacart_product_images","x041z_phocacart_product_point_groups","x041z_phocacart_product_price_groups","x041z_phocacart_product_price_history","x041z_phocacart_product_related","x041z_phocacart_product_stock","x041z_phocacart_products","x041z_phocacart_questions","x041z_phocacart_regions","x041z_phocacart_reviews","x041z_phocacart_reward_points","x041z_phocacart_sections","x041z_phocacart_shipping_method_countries","x041z_phocacart_shipping_method_regions","x041z_phocacart_shipping_method_zones","x041z_phocacart_shipping_methods","x041z_phocacart_specification_groups","x041z_phocacart_specifications","x041z_phocacart_stock_statuses","x041z_phocacart_submit_items","x041z_phocacart_taglabels_related","x041z_phocacart_tags","x041z_phocacart_tags_related","x041z_phocacart_tax_countries","x041z_phocacart_tax_regions","x041z_phocacart_taxes","x041z_phocacart_units","x041z_phocacart_users","x041z_phocacart_vendors","x041z_phocacart_wishlists","x041z_phocacart_zone_countries","x041z_phocacart_zone_regions","x041z_phocacart_zones","x041z_postinstall_messages","x041z_privacy_consents","x041z_privacy_requests","x041z_redirect_links","x041z_reservation_appointment","x041z_reservation_bbbclass","x041z_reservation_capital","x041z_reservation_comment","x041z_reservation_consultant","x041z_reservation_consultantt","x041z_reservation_doctor","x041z_reservation_message","x041z_reservation_order","x041z_reservation_plan","x041z_reservation_reserve","x041z_reservation_session","x041z_reservation_sick","x041z_rsmembership_categories","x041z_rsmembership_configuration","x041z_rsmembership_countries","x041z_rsmembership_coupon_items","x041z_rsmembership_coupons","x041z_rsmembership_extra_value_shared","x041z_rsmembership_extra_values","x041z_rsmembership_extras","x041z_rsmembership_fields","x041z_rsmembership_files","x041z_rsmembership_logs","x041z_rsmembership_membership_attachments","x041z_rsmembership_membership_extras","x041z_rsmembership_membership_fields","x041z_rsmembership_membership_shared","x041z_rsmembership_membership_subscribers","x041z_rsmembership_membership_upgrades","x041z_rsmembership_memberships","x041z_rsmembership_payments","x041z_rsmembership_subscribers","x041z_rsmembership_syslog","x041z_rsmembership_terms","x041z_rsmembership_tokens","x041z_rsmembership_transactions","x041z_rsticketspro_configuration","x041z_rsticketspro_custom_fields","x041z_rsticketspro_custom_fields_values","x041z_rsticketspro_departments","x041z_rsticketspro_emails","x041z_rsticketspro_groups","x041z_rsticketspro_kb_categories","x041z_rsticketspro_kb_content","x041z_rsticketspro_kb_rules","x041z_rsticketspro_priorities","x041z_rsticketspro_searches","x041z_rsticketspro_staff","x041z_rsticketspro_staff_to_department","x041z_rsticketspro_statuses","x041z_rsticketspro_ticket_files","x041z_rsticketspro_ticket_history","x041z_rsticketspro_ticket_messages","x041z_rsticketspro_ticket_notes","x041z_rsticketspro_tickets","x041z_rsticketspro_timespent","x041z_rsticketspro_tokens","x041z_schemas","x041z_session","x041z_spmedia","x041z_sppagebuilder","x041z_sppagebuilder_addonlist","x041z_sppagebuilder_addons","x041z_sppagebuilder_assets","x041z_sppagebuilder_colors","x041z_sppagebuilder_fonts","x041z_sppagebuilder_integrations","x041z_sppagebuilder_languages","x041z_sppagebuilder_sections","x041z_tags","x041z_template_styles","x041z_ucm_base","x041z_ucm_content","x041z_ucm_history","x041z_update_sites","x041z_update_sites_extensions","x041z_updates","x041z_user_keys","x041z_user_notes","x041z_user_profiles","x041z_user_usergroup_map","x041z_usergroups","x041z_users","x041z_utf8_conversion","x041z_viewlevels","x041z_zaya_content"]";s:11:"tablefields";s:3178:"{"#__postinstall_messages":{"postinstall_message_id":{"Field":"postinstall_message_id","Type":"bigint(20)
unsigned","Collation":null,"Null":"NO","Key":"PRI","Default":null,"Extra":"auto_increment","Privileges":"select,insert,update,references","Comment":""},"extension_id":{"Field":"extension_id","Type":"bigint(20)","Collation":null,"Null":"NO","Key":"","Default":"700","Extra":"","Privileges":"select,insert,update,references","Comment":"FK
to
#__extensions"},"title_key":{"Field":"title_key","Type":"varchar(255)","Collation":"utf8mb4_unicode_ci","Null":"NO","Key":"","Default":"","Extra":"","Privileges":"select,insert,update,references","Comment":"Lang
key for the
title"},"description_key":{"Field":"description_key","Type":"varchar(255)","Collation":"utf8mb4_unicode_ci","Null":"NO","Key":"","Default":"","Extra":"","Privileges":"select,insert,update,references","Comment":"Lang
key for
description"},"action_key":{"Field":"action_key","Type":"varchar(255)","Collation":"utf8mb4_unicode_ci","Null":"NO","Key":"","Default":"","Extra":"","Privileges":"select,insert,update,references","Comment":""},"language_extension":{"Field":"language_extension","Type":"varchar(255)","Collation":"utf8mb4_unicode_ci","Null":"NO","Key":"","Default":"com_postinstall","Extra":"","Privileges":"select,insert,update,references","Comment":"Extension
holding lang
keys"},"language_client_id":{"Field":"language_client_id","Type":"tinyint(3)","Collation":null,"Null":"NO","Key":"","Default":"1","Extra":"","Privileges":"select,insert,update,references","Comment":""},"type":{"Field":"type","Type":"varchar(10)","Collation":"utf8mb4_unicode_ci","Null":"NO","Key":"","Default":"link","Extra":"","Privileges":"select,insert,update,references","Comment":"Message
type - message, link,
action"},"action_file":{"Field":"action_file","Type":"varchar(255)","Collation":"utf8mb4_unicode_ci","Null":"YES","Key":"","Default":"","Extra":"","Privileges":"select,insert,update,references","Comment":"RAD
URI to the PHP file containing action
method"},"action":{"Field":"action","Type":"varchar(255)","Collation":"utf8mb4_unicode_ci","Null":"YES","Key":"","Default":"","Extra":"","Privileges":"select,insert,update,references","Comment":"Action
method name or
URL"},"condition_file":{"Field":"condition_file","Type":"varchar(255)","Collation":"utf8mb4_unicode_ci","Null":"YES","Key":"","Default":null,"Extra":"","Privileges":"select,insert,update,references","Comment":"RAD
URI to file holding display condition
method"},"condition_method":{"Field":"condition_method","Type":"varchar(255)","Collation":"utf8mb4_unicode_ci","Null":"YES","Key":"","Default":null,"Extra":"","Privileges":"select,insert,update,references","Comment":"Display
condition method, must return
boolean"},"version_introduced":{"Field":"version_introduced","Type":"varchar(50)","Collation":"utf8mb4_unicode_ci","Null":"NO","Key":"","Default":"3.2.0","Extra":"","Privileges":"select,insert,update,references","Comment":"Version
when this message was
introduced"},"enabled":{"Field":"enabled","Type":"tinyint(3)","Collation":null,"Null":"NO","Key":"","Default":"1","Extra":"","Privileges":"select,insert,update,references","Comment":""}}}";}s:14:"*initialized";b:0;s:9:"separator";s:1:".";}9ca401dc5c83ea71321893da14a56979-cache-fof-44516dac54bc32692ef1c935b07979c8.php000064400000040321151165775160017560
0ustar00<?php die("Access Denied");
?>#x#O:24:"Joomla\Registry\Registry":3:{s:7:"*data";O:8:"stdClass":2:{s:6:"tables";s:13210:"["x041z_action_log_config","x041z_action_logs","x041z_action_logs_extensions","x041z_action_logs_users","x041z_advancedtemplates","x041z_assets","x041z_associations","x041z_banner_clients","x041z_banner_tracks","x041z_banners","x041z_categories","x041z_componentbuilder_admin_custom_tabs","x041z_componentbuilder_admin_fields","x041z_componentbuilder_admin_fields_conditions","x041z_componentbuilder_admin_fields_relations","x041z_componentbuilder_admin_view","x041z_componentbuilder_class_extends","x041z_componentbuilder_class_method","x041z_componentbuilder_class_property","x041z_componentbuilder_component_admin_views","x041z_componentbuilder_component_config","x041z_componentbuilder_component_custom_admin_menus","x041z_componentbuilder_component_custom_admin_views","x041z_componentbuilder_component_dashboard","x041z_componentbuilder_component_files_folders","x041z_componentbuilder_component_modules","x041z_componentbuilder_component_mysql_tweaks","x041z_componentbuilder_component_placeholders","x041z_componentbuilder_component_plugins","x041z_componentbuilder_component_site_views","x041z_componentbuilder_component_updates","x041z_componentbuilder_custom_admin_view","x041z_componentbuilder_custom_code","x041z_componentbuilder_dynamic_get","x041z_componentbuilder_external_code","x041z_componentbuilder_field","x041z_componentbuilder_fieldtype","x041z_componentbuilder_help_document","x041z_componentbuilder_joomla_component","x041z_componentbuilder_joomla_module","x041z_componentbuilder_joomla_module_files_folders_urls","x041z_componentbuilder_joomla_module_updates","x041z_componentbuilder_joomla_plugin","x041z_componentbuilder_joomla_plugin_files_folders_urls","x041z_componentbuilder_joomla_plugin_group","x041z_componentbuilder_joomla_plugin_updates","x041z_componentbuilder_language","x041z_componentbuilder_language_translation","x041z_componentbuilder_layout","x041z_componentbuilder_library","x041z_componentbuilder_library_config","x041z_componentbuilder_library_files_folders_urls","x041z_componentbuilder_placeholder","x041z_componentbuilder_power","x041z_componentbuilder_server","x041z_componentbuilder_site_view","x041z_componentbuilder_snippet","x041z_componentbuilder_snippet_type","x041z_componentbuilder_template","x041z_componentbuilder_validation_rule","x041z_contact_details","x041z_content","x041z_content_frontpage","x041z_content_rating","x041z_content_types","x041z_contentitem_tag_map","x041z_core_log_searches","x041z_easyblog_acl","x041z_easyblog_acl_filters","x041z_easyblog_acl_group","x041z_easyblog_adsense","x041z_easyblog_autoarticle_map","x041z_easyblog_blogger_subscription","x041z_easyblog_captcha","x041z_easyblog_category","x041z_easyblog_category_acl","x041z_easyblog_category_acl_item","x041z_easyblog_category_subscription","x041z_easyblog_comment","x041z_easyblog_configs","x041z_easyblog_drafts","x041z_easyblog_external","x041z_easyblog_external_groups","x041z_easyblog_featured","x041z_easyblog_feedburner","x041z_easyblog_feeds","x041z_easyblog_feeds_history","x041z_easyblog_hashkeys","x041z_easyblog_likes","x041z_easyblog_mailq","x041z_easyblog_mediamanager","x041z_easyblog_meta","x041z_easyblog_migrate_content","x041z_easyblog_oauth","x041z_easyblog_oauth_posts","x041z_easyblog_post","x041z_easyblog_post_rejected","x041z_easyblog_post_subscription","x041z_easyblog_post_tag","x041z_easyblog_ratings","x041z_easyblog_reports","x041z_easyblog_site_subscription","x041z_easyblog_stream","x041z_easyblog_tag","x041z_easyblog_team","x041z_easyblog_team_groups","x041z_easyblog_team_post","x041z_easyblog_team_request","x041z_easyblog_team_subscription","x041z_easyblog_team_users","x041z_easyblog_trackback","x041z_easyblog_trackback_sent","x041z_easyblog_twitter_microblog","x041z_easyblog_users","x041z_easyblog_xml_wpdata","x041z_extensions","x041z_ezportal","x041z_ezrealty","x041z_ezrealty_catg","x041z_ezrealty_country","x041z_ezrealty_images","x041z_ezrealty_incats","x041z_ezrealty_lightbox","x041z_ezrealty_locality","x041z_ezrealty_state","x041z_fields","x041z_fields_categories","x041z_fields_groups","x041z_fields_values","x041z_finder_filters","x041z_finder_links","x041z_finder_links_terms0","x041z_finder_links_terms1","x041z_finder_links_terms2","x041z_finder_links_terms3","x041z_finder_links_terms4","x041z_finder_links_terms5","x041z_finder_links_terms6","x041z_finder_links_terms7","x041z_finder_links_terms8","x041z_finder_links_terms9","x041z_finder_links_termsa","x041z_finder_links_termsb","x041z_finder_links_termsc","x041z_finder_links_termsd","x041z_finder_links_termse","x041z_finder_links_termsf","x041z_finder_taxonomy","x041z_finder_taxonomy_map","x041z_finder_terms","x041z_finder_terms_common","x041z_finder_tokens","x041z_finder_tokens_aggregate","x041z_finder_types","x041z_helpdeskpro_articles","x041z_helpdeskpro_categories","x041z_helpdeskpro_configs","x041z_helpdeskpro_emails","x041z_helpdeskpro_field_categories","x041z_helpdeskpro_field_value","x041z_helpdeskpro_fields","x041z_helpdeskpro_labels","x041z_helpdeskpro_messages","x041z_helpdeskpro_priorities","x041z_helpdeskpro_replies","x041z_helpdeskpro_statuses","x041z_helpdeskpro_tickets","x041z_helpdeskpro_urls","x041z_hikamarket_config","x041z_hikamarket_customer_vendor","x041z_hikamarket_fee","x041z_hikamarket_order_transaction","x041z_hikamarket_vendor","x041z_hikamarket_vendor_user","x041z_hikashop_address","x041z_hikashop_badge","x041z_hikashop_banner","x041z_hikashop_cart","x041z_hikashop_cart_product","x041z_hikashop_category","x041z_hikashop_characteristic","x041z_hikashop_click","x041z_hikashop_config","x041z_hikashop_currency","x041z_hikashop_discount","x041z_hikashop_download","x041z_hikashop_email_log","x041z_hikashop_entry","x041z_hikashop_field","x041z_hikashop_file","x041z_hikashop_filter","x041z_hikashop_geolocation","x041z_hikashop_history","x041z_hikashop_limit","x041z_hikashop_massaction","x041z_hikashop_order","x041z_hikashop_order_product","x041z_hikashop_orderstatus","x041z_hikashop_payment","x041z_hikashop_plugin","x041z_hikashop_price","x041z_hikashop_product","x041z_hikashop_product_category","x041z_hikashop_product_related","x041z_hikashop_shipping","x041z_hikashop_shipping_price","x041z_hikashop_tax","x041z_hikashop_taxation","x041z_hikashop_user","x041z_hikashop_variant","x041z_hikashop_vote","x041z_hikashop_vote_user","x041z_hikashop_waitlist","x041z_hikashop_warehouse","x041z_hikashop_widget","x041z_hikashop_zone","x041z_hikashop_zone_link","x041z_hilation_relation","x041z_invoices_contacts","x041z_invoices_currencies","x041z_invoices_invoices","x041z_invoices_items","x041z_invoices_payments","x041z_invoices_tax_invoice","x041z_invoices_taxes","x041z_invoices_templateitems","x041z_invoices_templates","x041z_jamegafilter","x041z_jea_amenities","x041z_jea_areas","x041z_jea_conditions","x041z_jea_departments","x041z_jea_gateways","x041z_jea_heatingtypes","x041z_jea_hotwatertypes","x041z_jea_properties","x041z_jea_slogans","x041z_jea_tools","x041z_jea_towns","x041z_jea_types","x041z_languages","x041z_menu","x041z_menu_types","x041z_messages","x041z_messages_cfg","x041z_mobilemenuck_menus","x041z_mobilemenuck_styles","x041z_modules","x041z_modules_menu","x041z_molation_m_relation","x041z_moojla_category_map","x041z_moojla_cohort","x041z_moojla_content","x041z_moojla_course","x041z_moojla_course_field_mapper","x041z_moojla_course_group_map","x041z_moojla_enrolment","x041z_moojla_favorite","x041z_moojla_module","x041z_moojla_rate","x041z_moojla_section","x041z_moojla_tag","x041z_moojla_tag_instance","x041z_moojla_user_field_map","x041z_moojla_user_map","x041z_newsfeeds","x041z_nextend2_image_storage","x041z_nextend2_section_storage","x041z_nextend2_smartslider3_generators","x041z_nextend2_smartslider3_sliders","x041z_nextend2_smartslider3_sliders_xref","x041z_nextend2_smartslider3_slides","x041z_notifly_events","x041z_notifly_logs","x041z_notifly_templates","x041z_overrider","x041z_phocacart_attribute_values","x041z_phocacart_attributes","x041z_phocacart_bulk_prices","x041z_phocacart_cart_multiple","x041z_phocacart_categories","x041z_phocacart_countries","x041z_phocacart_coupon_categories","x041z_phocacart_coupon_count","x041z_phocacart_coupon_count_user","x041z_phocacart_coupon_products","x041z_phocacart_coupons","x041z_phocacart_currencies","x041z_phocacart_discount_categories","x041z_phocacart_discount_products","x041z_phocacart_discounts","x041z_phocacart_export","x041z_phocacart_feeds","x041z_phocacart_form_fields","x041z_phocacart_groups","x041z_phocacart_hits","x041z_phocacart_import","x041z_phocacart_item_access","x041z_phocacart_item_groups","x041z_phocacart_logs","x041z_phocacart_manufacturers","x041z_phocacart_opening_times","x041z_phocacart_order_attributes","x041z_phocacart_order_coupons","x041z_phocacart_order_discounts","x041z_phocacart_order_downloads","x041z_phocacart_order_history","x041z_phocacart_order_product_discounts","x041z_phocacart_order_products","x041z_phocacart_order_statuses","x041z_phocacart_order_tax_recapitulation","x041z_phocacart_order_total","x041z_phocacart_order_users","x041z_phocacart_orders","x041z_phocacart_parameter_values","x041z_phocacart_parameter_values_related","x041z_phocacart_parameters","x041z_phocacart_payment_method_countries","x041z_phocacart_payment_method_regions","x041z_phocacart_payment_method_shipping","x041z_phocacart_payment_method_zones","x041z_phocacart_payment_methods","x041z_phocacart_product_categories","x041z_phocacart_product_discounts","x041z_phocacart_product_featured","x041z_phocacart_product_files","x041z_phocacart_product_images","x041z_phocacart_product_point_groups","x041z_phocacart_product_price_groups","x041z_phocacart_product_price_history","x041z_phocacart_product_related","x041z_phocacart_product_stock","x041z_phocacart_products","x041z_phocacart_questions","x041z_phocacart_regions","x041z_phocacart_reviews","x041z_phocacart_reward_points","x041z_phocacart_sections","x041z_phocacart_shipping_method_countries","x041z_phocacart_shipping_method_regions","x041z_phocacart_shipping_method_zones","x041z_phocacart_shipping_methods","x041z_phocacart_specification_groups","x041z_phocacart_specifications","x041z_phocacart_stock_statuses","x041z_phocacart_submit_items","x041z_phocacart_taglabels_related","x041z_phocacart_tags","x041z_phocacart_tags_related","x041z_phocacart_tax_countries","x041z_phocacart_tax_regions","x041z_phocacart_taxes","x041z_phocacart_units","x041z_phocacart_users","x041z_phocacart_vendors","x041z_phocacart_wishlists","x041z_phocacart_zone_countries","x041z_phocacart_zone_regions","x041z_phocacart_zones","x041z_postinstall_messages","x041z_privacy_consents","x041z_privacy_requests","x041z_redirect_links","x041z_reservation_appointment","x041z_reservation_bbbclass","x041z_reservation_capital","x041z_reservation_comment","x041z_reservation_consultant","x041z_reservation_consultantt","x041z_reservation_doctor","x041z_reservation_message","x041z_reservation_order","x041z_reservation_plan","x041z_reservation_reserve","x041z_reservation_session","x041z_reservation_sick","x041z_rsmembership_categories","x041z_rsmembership_configuration","x041z_rsmembership_countries","x041z_rsmembership_coupon_items","x041z_rsmembership_coupons","x041z_rsmembership_extra_value_shared","x041z_rsmembership_extra_values","x041z_rsmembership_extras","x041z_rsmembership_fields","x041z_rsmembership_files","x041z_rsmembership_logs","x041z_rsmembership_membership_attachments","x041z_rsmembership_membership_extras","x041z_rsmembership_membership_fields","x041z_rsmembership_membership_shared","x041z_rsmembership_membership_subscribers","x041z_rsmembership_membership_upgrades","x041z_rsmembership_memberships","x041z_rsmembership_payments","x041z_rsmembership_subscribers","x041z_rsmembership_syslog","x041z_rsmembership_terms","x041z_rsmembership_tokens","x041z_rsmembership_transactions","x041z_rsticketspro_configuration","x041z_rsticketspro_custom_fields","x041z_rsticketspro_custom_fields_values","x041z_rsticketspro_departments","x041z_rsticketspro_emails","x041z_rsticketspro_groups","x041z_rsticketspro_kb_categories","x041z_rsticketspro_kb_content","x041z_rsticketspro_kb_rules","x041z_rsticketspro_priorities","x041z_rsticketspro_searches","x041z_rsticketspro_staff","x041z_rsticketspro_staff_to_department","x041z_rsticketspro_statuses","x041z_rsticketspro_ticket_files","x041z_rsticketspro_ticket_history","x041z_rsticketspro_ticket_messages","x041z_rsticketspro_ticket_notes","x041z_rsticketspro_tickets","x041z_rsticketspro_timespent","x041z_rsticketspro_tokens","x041z_schemas","x041z_session","x041z_spmedia","x041z_sppagebuilder","x041z_sppagebuilder_addonlist","x041z_sppagebuilder_addons","x041z_sppagebuilder_assets","x041z_sppagebuilder_colors","x041z_sppagebuilder_fonts","x041z_sppagebuilder_integrations","x041z_sppagebuilder_languages","x041z_sppagebuilder_sections","x041z_tags","x041z_template_styles","x041z_ucm_base","x041z_ucm_content","x041z_ucm_history","x041z_update_sites","x041z_update_sites_extensions","x041z_updates","x041z_user_keys","x041z_user_notes","x041z_user_profiles","x041z_user_usergroup_map","x041z_usergroups","x041z_users","x041z_utf8_conversion","x041z_viewlevels","x041z_zaya_content"]";s:11:"tablefields";s:3178:"{"#__postinstall_messages":{"postinstall_message_id":{"Field":"postinstall_message_id","Type":"bigint(20)
unsigned","Collation":null,"Null":"NO","Key":"PRI","Default":null,"Extra":"auto_increment","Privileges":"select,insert,update,references","Comment":""},"extension_id":{"Field":"extension_id","Type":"bigint(20)","Collation":null,"Null":"NO","Key":"","Default":"700","Extra":"","Privileges":"select,insert,update,references","Comment":"FK
to
#__extensions"},"title_key":{"Field":"title_key","Type":"varchar(255)","Collation":"utf8mb4_unicode_ci","Null":"NO","Key":"","Default":"","Extra":"","Privileges":"select,insert,update,references","Comment":"Lang
key for the
title"},"description_key":{"Field":"description_key","Type":"varchar(255)","Collation":"utf8mb4_unicode_ci","Null":"NO","Key":"","Default":"","Extra":"","Privileges":"select,insert,update,references","Comment":"Lang
key for
description"},"action_key":{"Field":"action_key","Type":"varchar(255)","Collation":"utf8mb4_unicode_ci","Null":"NO","Key":"","Default":"","Extra":"","Privileges":"select,insert,update,references","Comment":""},"language_extension":{"Field":"language_extension","Type":"varchar(255)","Collation":"utf8mb4_unicode_ci","Null":"NO","Key":"","Default":"com_postinstall","Extra":"","Privileges":"select,insert,update,references","Comment":"Extension
holding lang
keys"},"language_client_id":{"Field":"language_client_id","Type":"tinyint(3)","Collation":null,"Null":"NO","Key":"","Default":"1","Extra":"","Privileges":"select,insert,update,references","Comment":""},"type":{"Field":"type","Type":"varchar(10)","Collation":"utf8mb4_unicode_ci","Null":"NO","Key":"","Default":"link","Extra":"","Privileges":"select,insert,update,references","Comment":"Message
type - message, link,
action"},"action_file":{"Field":"action_file","Type":"varchar(255)","Collation":"utf8mb4_unicode_ci","Null":"YES","Key":"","Default":"","Extra":"","Privileges":"select,insert,update,references","Comment":"RAD
URI to the PHP file containing action
method"},"action":{"Field":"action","Type":"varchar(255)","Collation":"utf8mb4_unicode_ci","Null":"YES","Key":"","Default":"","Extra":"","Privileges":"select,insert,update,references","Comment":"Action
method name or
URL"},"condition_file":{"Field":"condition_file","Type":"varchar(255)","Collation":"utf8mb4_unicode_ci","Null":"YES","Key":"","Default":null,"Extra":"","Privileges":"select,insert,update,references","Comment":"RAD
URI to file holding display condition
method"},"condition_method":{"Field":"condition_method","Type":"varchar(255)","Collation":"utf8mb4_unicode_ci","Null":"YES","Key":"","Default":null,"Extra":"","Privileges":"select,insert,update,references","Comment":"Display
condition method, must return
boolean"},"version_introduced":{"Field":"version_introduced","Type":"varchar(50)","Collation":"utf8mb4_unicode_ci","Null":"NO","Key":"","Default":"3.2.0","Extra":"","Privileges":"select,insert,update,references","Comment":"Version
when this message was
introduced"},"enabled":{"Field":"enabled","Type":"tinyint(3)","Collation":null,"Null":"NO","Key":"","Default":"1","Extra":"","Privileges":"select,insert,update,references","Comment":""}}}";}s:14:"*initialized";b:0;s:9:"separator";s:1:".";}