Spade

Mini Shell

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

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

PKk�[��fk�3�3ComponentHelper.phpnu�[���<?php
/**
 * Joomla! Content Management System
 *
 * @copyright  Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Component;

defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Access\Access;
use Joomla\CMS\Component\Exception\MissingComponentException;
use Joomla\Registry\Registry;

/**
 * Component helper class
 *
 * @since  1.5
 */
class ComponentHelper
{
	/**
	 * The component list cache
	 *
	 * @var    ComponentRecord[]
	 * @since  1.6
	 */
	protected static $components = array();

	/**
	 * Get the component information.
	 *
	 * @param   string   $option  The component option.
	 * @param   boolean  $strict  If set and the component does not exist, the
enabled attribute will be set to false.
	 *
	 * @return  ComponentRecord  An object with the information for the
component.
	 *
	 * @since   1.5
	 */
	public static function getComponent($option, $strict = false)
	{
		$components = static::getComponents();

		if (isset($components[$option]))
		{
			return $components[$option];
		}

		$result = new ComponentRecord;
		$result->enabled = $strict ? false : true;
		$result->setParams(new Registry);

		return $result;
	}

	/**
	 * Checks if the component is enabled
	 *
	 * @param   string  $option  The component option.
	 *
	 * @return  boolean
	 *
	 * @since   1.5
	 */
	public static function isEnabled($option)
	{
		$components = static::getComponents();

		return isset($components[$option]) &&
$components[$option]->enabled;
	}

	/**
	 * Checks if a component is installed
	 *
	 * @param   string  $option  The component option.
	 *
	 * @return  integer
	 *
	 * @since   3.4
	 */
	public static function isInstalled($option)
	{
		$components = static::getComponents();

		return isset($components[$option]) ? 1 : 0;
	}

	/**
	 * Gets the parameter object for the component
	 *
	 * @param   string   $option  The option for the component.
	 * @param   boolean  $strict  If set and the component does not exist,
false will be returned
	 *
	 * @return  Registry  A Registry object.
	 *
	 * @see     Registry
	 * @since   1.5
	 */
	public static function getParams($option, $strict = false)
	{
		return static::getComponent($option, $strict)->getParams();
	}

	/**
	 * Applies the global text filters to arbitrary text as per settings for
current user groups
	 *
	 * @param   string  $text  The string to filter
	 *
	 * @return  string  The filtered string
	 *
	 * @since   2.5
	 */
	public static function filterText($text)
	{
		// Punyencoding utf8 email addresses
		$text = \JFilterInput::getInstance()->emailToPunycode($text);

		// Filter settings
		$config     = static::getParams('com_config');
		$user       = \JFactory::getUser();
		$userGroups = Access::getGroupsByUser($user->get('id'));

		$filters = $config->get('filters');

		$blackListTags       = array();
		$blackListAttributes = array();

		$customListTags       = array();
		$customListAttributes = array();

		$whiteListTags       = array();
		$whiteListAttributes = array();

		$whiteList  = false;
		$blackList  = false;
		$customList = false;
		$unfiltered = false;

		// Cycle through each of the user groups the user is in.
		// Remember they are included in the Public group as well.
		foreach ($userGroups as $groupId)
		{
			// May have added a group by not saved the filters.
			if (!isset($filters->$groupId))
			{
				continue;
			}

			// Each group the user is in could have different filtering properties.
			$filterData = $filters->$groupId;
			$filterType = strtoupper($filterData->filter_type);

			if ($filterType === 'NH')
			{
				// Maximum HTML filtering.
			}
			elseif ($filterType === 'NONE')
			{
				// No HTML filtering.
				$unfiltered = true;
			}
			else
			{
				// Blacklist or whitelist.
				// Preprocess the tags and attributes.
				$tags           = explode(',', $filterData->filter_tags);
				$attributes     = explode(',',
$filterData->filter_attributes);
				$tempTags       = array();
				$tempAttributes = array();

				foreach ($tags as $tag)
				{
					$tag = trim($tag);

					if ($tag)
					{
						$tempTags[] = $tag;
					}
				}

				foreach ($attributes as $attribute)
				{
					$attribute = trim($attribute);

					if ($attribute)
					{
						$tempAttributes[] = $attribute;
					}
				}

				// Collect the blacklist or whitelist tags and attributes.
				// Each list is cumulative.
				if ($filterType === 'BL')
				{
					$blackList           = true;
					$blackListTags       = array_merge($blackListTags, $tempTags);
					$blackListAttributes = array_merge($blackListAttributes,
$tempAttributes);
				}
				elseif ($filterType === 'CBL')
				{
					// Only set to true if Tags or Attributes were added
					if ($tempTags || $tempAttributes)
					{
						$customList           = true;
						$customListTags       = array_merge($customListTags, $tempTags);
						$customListAttributes = array_merge($customListAttributes,
$tempAttributes);
					}
				}
				elseif ($filterType === 'WL')
				{
					$whiteList           = true;
					$whiteListTags       = array_merge($whiteListTags, $tempTags);
					$whiteListAttributes = array_merge($whiteListAttributes,
$tempAttributes);
				}
			}
		}

		// Remove duplicates before processing (because the blacklist uses both
sets of arrays).
		$blackListTags        = array_unique($blackListTags);
		$blackListAttributes  = array_unique($blackListAttributes);
		$customListTags       = array_unique($customListTags);
		$customListAttributes = array_unique($customListAttributes);
		$whiteListTags        = array_unique($whiteListTags);
		$whiteListAttributes  = array_unique($whiteListAttributes);

		if (!$unfiltered)
		{
			// Custom blacklist precedes Default blacklist
			if ($customList)
			{
				$filter = \JFilterInput::getInstance(array(), array(), 1, 1);

				// Override filter's default blacklist tags and attributes
				if ($customListTags)
				{
					$filter->tagBlacklist = $customListTags;
				}

				if ($customListAttributes)
				{
					$filter->attrBlacklist = $customListAttributes;
				}
			}
			// Blacklists take second precedence.
			elseif ($blackList)
			{
				// Remove the whitelisted tags and attributes from the black-list.
				$blackListTags       = array_diff($blackListTags, $whiteListTags);
				$blackListAttributes = array_diff($blackListAttributes,
$whiteListAttributes);

				$filter = \JFilterInput::getInstance($blackListTags,
$blackListAttributes, 1, 1);

				// Remove whitelisted tags from filter's default blacklist
				if ($whiteListTags)
				{
					$filter->tagBlacklist = array_diff($filter->tagBlacklist,
$whiteListTags);
				}

				// Remove whitelisted attributes from filter's default blacklist
				if ($whiteListAttributes)
				{
					$filter->attrBlacklist = array_diff($filter->attrBlacklist,
$whiteListAttributes);
				}
			}
			// Whitelists take third precedence.
			elseif ($whiteList)
			{
				// Turn off XSS auto clean
				$filter = \JFilterInput::getInstance($whiteListTags,
$whiteListAttributes, 0, 0, 0);
			}
			// No HTML takes last place.
			else
			{
				$filter = \JFilterInput::getInstance();
			}

			$text = $filter->clean($text, 'html');
		}

		return $text;
	}

	/**
	 * Render the component.
	 *
	 * @param   string  $option  The component option.
	 * @param   array   $params  The component parameters
	 *
	 * @return  string
	 *
	 * @since   1.5
	 * @throws  MissingComponentException
	 */
	public static function renderComponent($option, $params = array())
	{
		$app = \JFactory::getApplication();

		// Load template language files.
		$template = $app->getTemplate(true)->template;
		$lang = \JFactory::getLanguage();
		$lang->load('tpl_' . $template, JPATH_BASE, null, false,
true)
			|| $lang->load('tpl_' . $template, JPATH_THEMES .
"/$template", null, false, true);

		if (empty($option))
		{
			throw new
MissingComponentException(\JText::_('JLIB_APPLICATION_ERROR_COMPONENT_NOT_FOUND'),
404);
		}

		if (JDEBUG)
		{
			\JProfiler::getInstance('Application')->mark('beforeRenderComponent
' . $option);
		}

		// Record the scope
		$scope = $app->scope;

		// Set scope to component name
		$app->scope = $option;

		// Build the component path.
		$option = preg_replace('/[^A-Z0-9_\.-]/i', '',
$option);
		$file = substr($option, 4);

		// Define component path.
		if (!defined('JPATH_COMPONENT'))
		{
			/**
			 * Defines the path to the active component for the request
			 *
			 * Note this constant is application aware and is different for each
application (site/admin).
			 *
			 * @var    string
			 * @since  1.5
			 */
			define('JPATH_COMPONENT', JPATH_BASE .
'/components/' . $option);
		}

		if (!defined('JPATH_COMPONENT_SITE'))
		{
			/**
			 * Defines the path to the site element of the active component for the
request
			 *
			 * @var    string
			 * @since  1.5
			 */
			define('JPATH_COMPONENT_SITE', JPATH_SITE .
'/components/' . $option);
		}

		if (!defined('JPATH_COMPONENT_ADMINISTRATOR'))
		{
			/**
			 * Defines the path to the admin element of the active component for the
request
			 *
			 * @var    string
			 * @since  1.5
			 */
			define('JPATH_COMPONENT_ADMINISTRATOR', JPATH_ADMINISTRATOR .
'/components/' . $option);
		}

		$path = JPATH_COMPONENT . '/' . $file . '.php';

		// If component is disabled throw error
		if (!static::isEnabled($option) || !file_exists($path))
		{
			throw new
MissingComponentException(\JText::_('JLIB_APPLICATION_ERROR_COMPONENT_NOT_FOUND'),
404);
		}

		// Load common and local language files.
		$lang->load($option, JPATH_BASE, null, false, true) ||
$lang->load($option, JPATH_COMPONENT, null, false, true);

		// Handle template preview outlining.
		$contents = null;

		// Execute the component.
		$contents = static::executeComponent($path);

		// Revert the scope
		$app->scope = $scope;

		if (JDEBUG)
		{
			\JProfiler::getInstance('Application')->mark('afterRenderComponent
' . $option);
		}

		return $contents;
	}

	/**
	 * Execute the component.
	 *
	 * @param   string  $path  The component path.
	 *
	 * @return  string  The component output
	 *
	 * @since   1.7
	 */
	protected static function executeComponent($path)
	{
		ob_start();
		require_once $path;

		return ob_get_clean();
	}

	/**
	 * Load the installed components into the components property.
	 *
	 * @param   string  $option  The element value for the extension
	 *
	 * @return  boolean  True on success
	 *
	 * @since   1.5
	 * @deprecated  4.0  Use JComponentHelper::load() instead
	 */
	protected static function _load($option)
	{
		return static::load($option);
	}

	/**
	 * Load the installed components into the components property.
	 *
	 * @param   string  $option  The element value for the extension
	 *
	 * @return  boolean  True on success
	 *
	 * @since   3.2
	 * @note    As of 4.0 this method will be restructured to only load the
data into memory
	 */
	protected static function load($option)
	{
		$loader = function ()
		{
			$db = \JFactory::getDbo();
			$query = $db->getQuery(true)
				->select($db->quoteName(array('extension_id',
'element', 'params', 'enabled'),
array('id', 'option', null, null)))
				->from($db->quoteName('#__extensions'))
				->where($db->quoteName('type') . ' = ' .
$db->quote('component'))
				->where($db->quoteName('state') . ' = 0')
				->where($db->quoteName('enabled') . ' = 1');
			$db->setQuery($query);

			return $db->loadObjectList('option',
'\JComponentRecord');
		};

		/** @var \JCacheControllerCallback $cache */
		$cache = \JFactory::getCache('_system', 'callback');

		try
		{
			static::$components = $cache->get($loader, array(), __METHOD__);
		}
		catch (\JCacheException $e)
		{
			static::$components = $loader();
		}

		// Core CMS will use '*' as a placeholder for required
parameter in this method. In 4.0 this will not be passed at all.
		if (isset($option) && $option != '*')
		{
			// Log deprecated warning and display missing component warning only if
using deprecated format.
			try
			{
				\JLog::add(
					sprintf(
						'Passing a parameter into %s() is deprecated and will be removed
in 4.0. Read %s::$components directly after loading the data.',
						__METHOD__,
						__CLASS__
					),
					\JLog::WARNING,
					'deprecated'
				);
			}
			catch (\RuntimeException $e)
			{
				// Informational log only
			}

			if (empty(static::$components[$option]))
			{
				/*
				 * Fatal error
				 *
				 * It is possible for this error to be reached before the global
\JLanguage instance has been loaded so we check for its presence
				 * before logging the error to ensure a human friendly message is
always given
				 */

				if (\JFactory::$language)
				{
					$msg =
\JText::sprintf('JLIB_APPLICATION_ERROR_COMPONENT_NOT_LOADING',
$option,
\JText::_('JLIB_APPLICATION_ERROR_COMPONENT_NOT_FOUND'));
				}
				else
				{
					$msg = sprintf('Error loading component: %1$s, %2$s',
$option, 'Component not found.');
				}

				\JLog::add($msg, \JLog::WARNING, 'jerror');

				return false;
			}
		}

		return true;
	}

	/**
	 * Get installed components
	 *
	 * @return  ComponentRecord[]  The components property
	 *
	 * @since   3.6.3
	 */
	public static function getComponents()
	{
		if (empty(static::$components))
		{
			static::load('*');
		}

		return static::$components;
	}
}
PKk�[�3��
�
ComponentRecord.phpnu�[���<?php
/**
 * Joomla! Content Management System
 *
 * @copyright  Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Component;

defined('JPATH_PLATFORM') or die;

use Joomla\Registry\Registry;

/**
 * Object representing a component extension record
 *
 * @since  3.7.0
 * @note   As of 4.0 this class will no longer extend JObject
 */
class ComponentRecord extends \JObject
{
	/**
	 * Primary key
	 *
	 * @var    integer
	 * @since  3.7.0
	 */
	public $id;

	/**
	 * The component name
	 *
	 * @var    integer
	 * @since  3.7.0
	 */
	public $option;

	/**
	 * The component parameters
	 *
	 * @var    string|Registry
	 * @since  3.7.0
	 * @note   This field is protected to require reading this field to proxy
through the getter to convert the params to a Registry instance
	 */
	protected $params;

	/**
	 * Indicates if this component is enabled
	 *
	 * @var    integer
	 * @since  3.7.0
	 */
	public $enabled;

	/**
	 * Class constructor
	 *
	 * @param   array  $data  The component record data to load
	 *
	 * @since   3.7.0
	 */
	public function __construct($data = array())
	{
		foreach ((array) $data as $key => $value)
		{
			$this->$key = $value;
		}
	}

	/**
	 * Method to get certain otherwise inaccessible properties from the form
field object.
	 *
	 * @param   string  $name  The property name for which to get the value.
	 *
	 * @return  mixed  The property value or null.
	 *
	 * @since   3.7.0
	 * @deprecated  4.0  Access the item parameters through the `getParams()`
method
	 */
	public function __get($name)
	{
		if ($name === 'params')
		{
			return $this->getParams();
		}

		return $this->get($name);
	}

	/**
	 * Method to set certain otherwise inaccessible properties of the form
field object.
	 *
	 * @param   string  $name   The property name for which to set the value.
	 * @param   mixed   $value  The value of the property.
	 *
	 * @return  void
	 *
	 * @since   3.7.0
	 * @deprecated  4.0  Set the item parameters through the `setParams()`
method
	 */
	public function __set($name, $value)
	{
		if ($name === 'params')
		{
			$this->setParams($value);

			return;
		}

		$this->set($name, $value);
	}

	/**
	 * Returns the menu item parameters
	 *
	 * @return  Registry
	 *
	 * @since   3.7.0
	 */
	public function getParams()
	{
		if (!($this->params instanceof Registry))
		{
			$this->params = new Registry($this->params);
		}

		return $this->params;
	}

	/**
	 * Sets the menu item parameters
	 *
	 * @param   Registry|string  $params  The data to be stored as the
parameters
	 *
	 * @return  void
	 *
	 * @since   3.7.0
	 */
	public function setParams($params)
	{
		$this->params = $params;
	}
}
PKk�[��5d��'Exception/MissingComponentException.phpnu�[���<?php
/**
 * Joomla! Content Management System
 *
 * @copyright  Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Component\Exception;

defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Router\Exception\RouteNotFoundException;

/**
 * Exception class defining an error for a missing component
 *
 * @since  3.7.0
 */
class MissingComponentException extends RouteNotFoundException
{
	/**
	 * Constructor
	 *
	 * @param   string      $message   The Exception message to throw.
	 * @param   integer     $code      The Exception code.
	 * @param   \Exception  $previous  The previous exception used for the
exception chaining.
	 *
	 * @since   3.7.0
	 */
	public function __construct($message = '', $code = 404,
\Exception $previous = null)
	{
		parent::__construct($message, $code, $previous);
	}
}
PKk�[u�(zzRouter/RouterBase.phpnu�[���<?php
/**
 * Joomla! Content Management System
 *
 * @copyright  Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Component\Router;

defined('JPATH_PLATFORM') or die;

/**
 * Base component routing class
 *
 * @since  3.3
 */
abstract class RouterBase implements RouterInterface
{
	/**
	 * Application object to use in the router
	 *
	 * @var    \JApplicationCms
	 * @since  3.4
	 */
	public $app;

	/**
	 * Menu object to use in the router
	 *
	 * @var    \JMenu
	 * @since  3.4
	 */
	public $menu;

	/**
	 * Class constructor.
	 *
	 * @param   \JApplicationCms  $app   Application-object that the router
should use
	 * @param   \JMenu            $menu  Menu-object that the router should
use
	 *
	 * @since   3.4
	 */
	public function __construct($app = null, $menu = null)
	{
		if ($app)
		{
			$this->app = $app;
		}
		else
		{
			$this->app = \JFactory::getApplication('site');
		}

		if ($menu)
		{
			$this->menu = $menu;
		}
		else
		{
			$this->menu = $this->app->getMenu();
		}
	}

	/**
	 * Generic method to preprocess a URL
	 *
	 * @param   array  $query  An associative array of URL arguments
	 *
	 * @return  array  The URL arguments to use to assemble the subsequent
URL.
	 *
	 * @since   3.3
	 */
	public function preprocess($query)
	{
		return $query;
	}
}
PKk�[�M�6��Router/RouterInterface.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Router;

interface RouterInterface
{
    public function dispatch();
}
PKk�[�2L	��Router/RouterLegacy.phpnu�[���<?php
/**
 * Joomla! Content Management System
 *
 * @copyright  Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Component\Router;

defined('JPATH_PLATFORM') or die;

/**
 * Default routing class for missing or legacy component routers
 *
 * @since  3.3
 */
class RouterLegacy implements RouterInterface
{
	/**
	 * Name of the component
	 *
	 * @var    string
	 * @since  3.3
	 */
	protected $component;

	/**
	 * Constructor
	 *
	 * @param   string  $component  Component name without the com_ prefix
this router should react upon
	 *
	 * @since   3.3
	 */
	public function __construct($component)
	{
		$this->component = $component;
	}

	/**
	 * Generic preprocess function for missing or legacy component router
	 *
	 * @param   array  $query  An associative array of URL arguments
	 *
	 * @return  array  The URL arguments to use to assemble the subsequent
URL.
	 *
	 * @since   3.3
	 */
	public function preprocess($query)
	{
		return $query;
	}

	/**
	 * Generic build function for missing or legacy component router
	 *
	 * @param   array  &$query  An array of URL arguments
	 *
	 * @return  array  The URL arguments to use to assemble the subsequent
URL.
	 *
	 * @since   3.3
	 */
	public function build(&$query)
	{
		$function = $this->component . 'BuildRoute';

		if (function_exists($function))
		{
			$segments = $function($query);
			$total    = count($segments);

			for ($i = 0; $i < $total; $i++)
			{
				$segments[$i] = str_replace(':', '-',
$segments[$i]);
			}

			return $segments;
		}

		return array();
	}

	/**
	 * Generic parse function for missing or legacy component router
	 *
	 * @param   array  &$segments  The segments of the URL to parse.
	 *
	 * @return  array  The URL attributes to be used by the application.
	 *
	 * @since   3.3
	 */
	public function parse(&$segments)
	{
		$function = $this->component . 'ParseRoute';

		if (function_exists($function))
		{
			$total = count($segments);

			for ($i = 0; $i < $total; $i++)
			{
				$segments[$i] = preg_replace('/-/', ':',
$segments[$i], 1);
			}

			return $function($segments);
		}

		return array();
	}
}
PKk�[�`��Router/RouterView.phpnu�[���<?php
/**
 * Joomla! Content Management System
 *
 * @copyright  Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Component\Router;

defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Component\Router\Rules\RulesInterface;

/**
 * View-based component routing class
 *
 * @since  3.5
 */
abstract class RouterView extends RouterBase
{
	/**
	 * Name of the router of the component
	 *
	 * @var    string
	 * @since  3.5
	 */
	protected $name;

	/**
	 * Array of rules
	 *
	 * @var    RulesInterface[]
	 * @since  3.5
	 */
	protected $rules = array();

	/**
	 * Views of the component
	 *
	 * @var    RouterViewConfiguration[]
	 * @since  3.5
	 */
	protected $views = array();

	/**
	 * Register the views of a component
	 *
	 * @param   RouterViewConfiguration  $view  View configuration object
	 *
	 * @return  void
	 *
	 * @since   3.5
	 */
	public function registerView(RouterViewConfiguration $view)
	{
		$this->views[$view->name] = $view;
	}

	/**
	 * Return an array of registered view objects
	 *
	 * @return  RouterViewConfiguration[] Array of registered view objects
	 *
	 * @since   3.5
	 */
	public function getViews()
	{
		return $this->views;
	}

	/**
	 * Get the path of views from target view to root view
	 * including content items of a nestable view
	 *
	 * @param   array  $query  Array of query elements
	 *
	 * @return  array List of views including IDs of content items
	 *
	 * @since   3.5
	 */
	public function getPath($query)
	{
		$views  = $this->getViews();
		$result = array();

		// Get the right view object
		if (isset($query['view']) &&
isset($views[$query['view']]))
		{
			$viewobj = $views[$query['view']];
		}

		// Get the path from the current item to the root view with all IDs
		if (isset($viewobj))
		{
			$path     = array_reverse($viewobj->path);
			$start    = true;
			$childkey = false;

			foreach ($path as $element)
			{
				$view = $views[$element];

				if ($start)
				{
					$key   = $view->key;
					$start = false;
				}
				else
				{
					$key = $childkey;
				}

				$childkey = $view->parent_key;

				if (($key || $view->key) && is_callable(array($this,
'get' . ucfirst($view->name) . 'Segment')))
				{
					if (isset($query[$key]))
					{
						$result[$view->name] = call_user_func_array(array($this,
'get' . ucfirst($view->name) . 'Segment'),
array($query[$key], $query));
					}
					elseif (isset($query[$view->key]))
					{
						$result[$view->name] = call_user_func_array(array($this,
'get' . ucfirst($view->name) . 'Segment'),
array($query[$view->key], $query));
					}
					else
					{
						$result[$view->name] = array();
					}
				}
				else
				{
					$result[$view->name] = true;
				}
			}
		}

		return $result;
	}

	/**
	 * Get all currently attached rules
	 *
	 * @return  RulesInterface[]  All currently attached rules in an array
	 *
	 * @since   3.5
	 */
	public function getRules()
	{
		return $this->rules;
	}

	/**
	 * Add a number of router rules to the object
	 *
	 * @param   RulesInterface[]  $rules  Array of
JComponentRouterRulesInterface objects
	 *
	 * @return  void
	 *
	 * @since   3.5
	 */
	public function attachRules($rules)
	{
		foreach ($rules as $rule)
		{
			$this->attachRule($rule);
		}
	}

	/**
	 * Attach a build rule
	 *
	 * @param   RulesInterface  $rule  The function to be called.
	 *
	 * @return  void
	 *
	 * @since   3.5
	 */
	public function attachRule(RulesInterface $rule)
	{
		$this->rules[] = $rule;
	}

	/**
	 * Remove a build rule
	 *
	 * @param   RulesInterface  $rule  The rule to be removed.
	 *
	 * @return   boolean  Was a rule removed?
	 *
	 * @since   3.5
	 */
	public function detachRule(RulesInterface $rule)
	{
		foreach ($this->rules as $id => $r)
		{
			if ($r == $rule)
			{
				unset($this->rules[$id]);

				return true;
			}
		}

		return false;
	}

	/**
	 * Generic method to preprocess a URL
	 *
	 * @param   array  $query  An associative array of URL arguments
	 *
	 * @return  array  The URL arguments to use to assemble the subsequent
URL.
	 *
	 * @since   3.5
	 */
	public function preprocess($query)
	{
		// Process the parsed variables based on custom defined rules
		foreach ($this->rules as $rule)
		{
			$rule->preprocess($query);
		}

		return $query;
	}

	/**
	 * Build method for URLs
	 *
	 * @param   array  &$query  Array of query elements
	 *
	 * @return  array  Array of URL segments
	 *
	 * @since   3.5
	 */
	public function build(&$query)
	{
		$segments = array();

		// Process the parsed variables based on custom defined rules
		foreach ($this->rules as $rule)
		{
			$rule->build($query, $segments);
		}

		return $segments;
	}

	/**
	 * Parse method for URLs
	 *
	 * @param   array  &$segments  Array of URL string-segments
	 *
	 * @return  array  Associative array of query values
	 *
	 * @since   3.5
	 */
	public function parse(&$segments)
	{
		$vars = array();

		// Process the parsed variables based on custom defined rules
		foreach ($this->rules as $rule)
		{
			$rule->parse($segments, $vars);
		}

		return $vars;
	}

	/**
	 * Method to return the name of the router
	 *
	 * @return  string  Name of the router
	 *
	 * @since   3.5
	 */
	public function getName()
	{
		if (empty($this->name))
		{
			$r = null;

			if (!preg_match('/(.*)Router/i', get_class($this), $r))
			{
				throw new
\Exception('JLIB_APPLICATION_ERROR_ROUTER_GET_NAME', 500);
			}

			$this->name = strtolower($r[1]);
		}

		return $this->name;
	}
}
PKk�[���xx"Router/RouterViewConfiguration.phpnu�[���<?php
/**
 * Joomla! Content Management System
 *
 * @copyright  Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Component\Router;

defined('JPATH_PLATFORM') or die;

/**
 * View-configuration class for the view-based component router
 *
 * @since  3.5
 */
class RouterViewConfiguration
{
	/**
	 * Name of the view
	 *
	 * @var    string
	 * @since  3.5
	 */
	public $name;

	/**
	 * Key of the view
	 *
	 * @var    string
	 * @since  3.5
	 */
	public $key = false;

	/**
	 * Parentview of this one
	 *
	 * @var    RouterViewconfiguration
	 * @since  3.5
	 */
	public $parent = false;

	/**
	 * Key of the parent view
	 *
	 * @var    string
	 * @since  3.5
	 */
	public $parent_key = false;

	/**
	 * Is this view nestable?
	 *
	 * @var    bool
	 * @since  3.5
	 */
	public $nestable = false;

	/**
	 * Layouts that are supported by this view
	 *
	 * @var    array
	 * @since  3.5
	 */
	public $layouts = array('default');

	/**
	 * Child-views of this view
	 *
	 * @var    RouterViewconfiguration[]
	 * @since  3.5
	 */
	public $children = array();

	/**
	 * Keys used for this parent view by the child views
	 *
	 * @var    array
	 * @since  3.5
	 */
	public $child_keys = array();

	/**
	 * Path of views from this one to the root view
	 *
	 * @var    array
	 * @since  3.5
	 */
	public $path = array();

	/**
	 * Constructor for the View-configuration class
	 *
	 * @param   string  $name  Name of the view
	 *
	 * @since   3.5
	 */
	public function __construct($name)
	{
		$this->name   = $name;
		$this->path[] = $name;
	}

	/**
	 * Set the name of the view
	 *
	 * @param   string  $name  Name of the view
	 *
	 * @return  RouterViewconfiguration  This object for chaining
	 *
	 * @since   3.5
	 */
	public function setName($name)
	{
		$this->name = $name;

		array_pop($this->path);
		$this->path[] = $name;

		return $this;
	}

	/**
	 * Set the key-identifier for the view
	 *
	 * @param   string  $key  Key of the view
	 *
	 * @return  RouterViewconfiguration  This object for chaining
	 *
	 * @since   3.5
	 */
	public function setKey($key)
	{
		$this->key = $key;

		return $this;
	}

	/**
	 * Set the parent view of this view
	 *
	 * @param   RouterViewconfiguration  $parent     Parent view object
	 * @param   string                   $parentKey  Key of the parent view in
this context
	 *
	 * @return  RouterViewconfiguration  This object for chaining
	 *
	 * @since   3.5
	 */
	public function setParent(RouterViewconfiguration $parent, $parentKey =
false)
	{
		if ($this->parent)
		{
			$key = array_search($this, $this->parent->children);

			if ($key !== false)
			{
				unset($this->parent->children[$key]);
			}

			if ($this->parent_key)
			{
				$child_key = array_search($this->parent_key,
$this->parent->child_keys);
				unset($this->parent->child_keys[$child_key]);
			}
		}

		$this->parent       = $parent;
		$parent->children[] = $this;

		$this->path   = $parent->path;
		$this->path[] = $this->name;

		$this->parent_key = $parentKey;

		if ($parentKey)
		{
			$parent->child_keys[] = $parentKey;
		}

		return $this;
	}

	/**
	 * Set if this view is nestable or not
	 *
	 * @param   bool  $isNestable  If set to true, the view is nestable
	 *
	 * @return  RouterViewconfiguration  This object for chaining
	 *
	 * @since   3.5
	 */
	public function setNestable($isNestable = true)
	{
		$this->nestable = (bool) $isNestable;

		return $this;
	}

	/**
	 * Add a layout to this view
	 *
	 * @param   string  $layout  Layouts that this view supports
	 *
	 * @return  RouterViewconfiguration  This object for chaining
	 *
	 * @since   3.5
	 */
	public function addLayout($layout)
	{
		$this->layouts[] = $layout;
		$this->layouts   = array_unique($this->layouts);

		return $this;
	}

	/**
	 * Remove a layout from this view
	 *
	 * @param   string  $layout  Layouts that this view supports
	 *
	 * @return  RouterViewconfiguration  This object for chaining
	 *
	 * @since   3.5
	 */
	public function removeLayout($layout)
	{
		$key = array_search($layout, $this->layouts);

		if ($key !== false)
		{
			unset($this->layouts[$key]);
		}

		return $this;
	}
}
PKk�[UA*��Router/Rules/MenuRules.phpnu�[���<?php
/**
 * Joomla! Content Management System
 *
 * @copyright  Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Component\Router\Rules;

defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Component\Router\RouterView;

/**
 * Rule to identify the right Itemid for a view in a component
 *
 * @since  3.4
 */
class MenuRules implements RulesInterface
{
	/**
	 * Router this rule belongs to
	 *
	 * @var   RouterView
	 * @since 3.4
	 */
	protected $router;

	/**
	 * Lookup array of the menu items
	 *
	 * @var   array
	 * @since 3.4
	 */
	protected $lookup = array();

	/**
	 * Class constructor.
	 *
	 * @param   RouterView  $router  Router this rule belongs to
	 *
	 * @since   3.4
	 */
	public function __construct(RouterView $router)
	{
		$this->router = $router;

		$this->buildLookup();
	}

	/**
	 * Finds the right Itemid for this query
	 *
	 * @param   array  &$query  The query array to process
	 *
	 * @return  void
	 *
	 * @since   3.4
	 */
	public function preprocess(&$query)
	{
		$active = $this->router->menu->getActive();

		/**
		 * If the active item id is not the same as the supplied item id or we
have a supplied item id and no active
		 * menu item then we just use the supplied menu item and continue
		 */
		if (isset($query['Itemid']) && ($active === null ||
$query['Itemid'] != $active->id))
		{
			return;
		}

		// Get query language
		$language = isset($query['lang']) ? $query['lang'] :
'*';

		if (!isset($this->lookup[$language]))
		{
			$this->buildLookup($language);
		}

		// Check if the active menu item matches the requested query
		if ($active !== null && isset($query['Itemid']))
		{
			// Check if active->query and supplied query are the same
			$match = true;

			foreach ($active->query as $k => $v)
			{
				if (isset($query[$k]) && $v !== $query[$k])
				{
					// Compare again without alias
					if (is_string($v) && $v == current(explode(':',
$query[$k], 2)))
					{
						continue;
					}

					$match = false;
					break;
				}
			}

			if ($match)
			{
				// Just use the supplied menu item
				return;
			}
		}

		$needles = $this->router->getPath($query);

		$layout = isset($query['layout']) &&
$query['layout'] !== 'default' ? ':' .
$query['layout'] : '';

		if ($needles)
		{
			foreach ($needles as $view => $ids)
			{
				$viewLayout = $view . $layout;

				if ($layout && isset($this->lookup[$language][$viewLayout]))
				{
					if (is_bool($ids))
					{
						$query['Itemid'] =
$this->lookup[$language][$viewLayout];

						return;
					}

					foreach ($ids as $id => $segment)
					{
						if (isset($this->lookup[$language][$viewLayout][(int) $id]))
						{
							$query['Itemid'] =
$this->lookup[$language][$viewLayout][(int) $id];

							return;
						}
					}
				}

				if (isset($this->lookup[$language][$view]))
				{
					if (is_bool($ids))
					{
						$query['Itemid'] = $this->lookup[$language][$view];

						return;
					}

					foreach ($ids as $id => $segment)
					{
						if (isset($this->lookup[$language][$view][(int) $id]))
						{
							$query['Itemid'] =
$this->lookup[$language][$view][(int) $id];

							return;
						}
					}
				}
			}
		}

		// Check if the active menuitem matches the requested language
		if ($active && $active->component === 'com_' .
$this->router->getName()
			&& ($language === '*' ||
in_array($active->language, array('*', $language)) ||
!\JLanguageMultilang::isEnabled()))
		{
			$query['Itemid'] = $active->id;
			return;
		}

		// If not found, return language specific home link
		$default = $this->router->menu->getDefault($language);

		if (!empty($default->id))
		{
			$query['Itemid'] = $default->id;
		}
	}

	/**
	 * Method to build the lookup array
	 *
	 * @param   string  $language  The language that the lookup should be
built up for
	 *
	 * @return  void
	 *
	 * @since   3.4
	 */
	protected function buildLookup($language = '*')
	{
		// Prepare the reverse lookup array.
		if (!isset($this->lookup[$language]))
		{
			$this->lookup[$language] = array();

			$component  = ComponentHelper::getComponent('com_' .
$this->router->getName());
			$views = $this->router->getViews();

			$attributes = array('component_id');
			$values     = array((int) $component->id);

			$attributes[] = 'language';
			$values[]     = array($language, '*');

			$items = $this->router->menu->getItems($attributes, $values);

			foreach ($items as $item)
			{
				if (isset($item->query['view'],
$views[$item->query['view']]))
				{
					$view = $item->query['view'];

					$layout = '';

					if (isset($item->query['layout']))
					{
						$layout = ':' . $item->query['layout'];
					}

					if ($views[$view]->key)
					{
						if (!isset($this->lookup[$language][$view . $layout]))
						{
							$this->lookup[$language][$view . $layout] = array();
						}

						if (!isset($this->lookup[$language][$view]))
						{
							$this->lookup[$language][$view] = array();
						}

						// If menuitem has no key set, we assume 0.
						if (!isset($item->query[$views[$view]->key]))
						{
							$item->query[$views[$view]->key] = 0;
						}

						/**
						 * Here it will become a bit tricky
						 * language != * can override existing entries
						 * language == * cannot override existing entries
						 */
						if (!isset($this->lookup[$language][$view .
$layout][$item->query[$views[$view]->key]]) || $item->language !==
'*')
						{
							$this->lookup[$language][$view .
$layout][$item->query[$views[$view]->key]] = $item->id;
							$this->lookup[$language][$view][$item->query[$views[$view]->key]]
= $item->id;
						}
					}
					else
					{
						/**
						 * Here it will become a bit tricky
						 * language != * can override existing entries
						 * language == * cannot override existing entries
						 */
						if (!isset($this->lookup[$language][$view . $layout]) ||
$item->language !== '*')
						{
							$this->lookup[$language][$view . $layout] = $item->id;
							$this->lookup[$language][$view] = $item->id;
						}
					}
				}
			}
		}
	}

	/**
	 * Dummymethod to fullfill the interface requirements
	 *
	 * @param   array  &$segments  The URL segments to parse
	 * @param   array  &$vars      The vars that result from the segments
	 *
	 * @return  void
	 *
	 * @since   3.4
	 * @codeCoverageIgnore
	 */
	public function parse(&$segments, &$vars)
	{
	}

	/**
	 * Dummymethod to fullfill the interface requirements
	 *
	 * @param   array  &$query     The vars that should be converted
	 * @param   array  &$segments  The URL segments to create
	 *
	 * @return  void
	 *
	 * @since   3.4
	 * @codeCoverageIgnore
	 */
	public function build(&$query, &$segments)
	{
	}
}
PKk�[��`TTRouter/Rules/NomenuRules.phpnu�[���<?php
/**
 * Joomla! Content Management System
 *
 * @copyright  Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Component\Router\Rules;

defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Component\Router\RouterView;

/**
 * Rule to process URLs without a menu item
 *
 * @since  3.4
 */
class NomenuRules implements RulesInterface
{
	/**
	 * Router this rule belongs to
	 *
	 * @var RouterView
	 * @since 3.4
	 */
	protected $router;

	/**
	 * Class constructor.
	 *
	 * @param   RouterView  $router  Router this rule belongs to
	 *
	 * @since   3.4
	 */
	public function __construct(RouterView $router)
	{
		$this->router = $router;
	}

	/**
	 * Dummymethod to fullfill the interface requirements
	 *
	 * @param   array  &$query  The query array to process
	 *
	 * @return  void
	 *
	 * @since   3.4
	 * @codeCoverageIgnore
	 */
	public function preprocess(&$query)
	{
	}

	/**
	 * Parse a menu-less URL
	 *
	 * @param   array  &$segments  The URL segments to parse
	 * @param   array  &$vars      The vars that result from the segments
	 *
	 * @return  void
	 *
	 * @since   3.4
	 */
	public function parse(&$segments, &$vars)
	{
		$active = $this->router->menu->getActive();

		if (!is_object($active))
		{
			$views = $this->router->getViews();

			if (isset($views[$segments[0]]))
			{
				$vars['view'] = array_shift($segments);

				if (isset($views[$vars['view']]->key) &&
isset($segments[0]))
				{
					$vars[$views[$vars['view']]->key] =
preg_replace('/-/', ':', array_shift($segments), 1);
				}
			}
		}
	}

	/**
	 * Build a menu-less URL
	 *
	 * @param   array  &$query     The vars that should be converted
	 * @param   array  &$segments  The URL segments to create
	 *
	 * @return  void
	 *
	 * @since   3.4
	 */
	public function build(&$query, &$segments)
	{
		$menu_found = false;

		if (isset($query['Itemid']))
		{
			$item =
$this->router->menu->getItem($query['Itemid']);

			if (!isset($query['option']) || ($item &&
$item->query['option'] === $query['option']))
			{
				$menu_found = true;
			}
		}

		if (!$menu_found && isset($query['view']))
		{
			$views = $this->router->getViews();

			if (isset($views[$query['view']]))
			{
				$view = $views[$query['view']];
				$segments[] = $query['view'];

				if ($view->key && isset($query[$view->key]))
				{
					if (is_callable(array($this->router, 'get' .
ucfirst($view->name) . 'Segment')))
					{
						$result = call_user_func_array(array($this->router,
'get' . ucfirst($view->name) . 'Segment'),
array($query[$view->key], $query));
						$segments[] = str_replace(':', '-',
array_shift($result));
					}
					else
					{
						$segments[] = str_replace(':', '-',
$query[$view->key]);
					}

					unset($query[$views[$query['view']]->key]);
				}

				unset($query['view']);
			}
		}
	}
}
PKk�[fnJooRouter/Rules/RulesInterface.phpnu�[���<?php
/**
 * Joomla! Content Management System
 *
 * @copyright  Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Component\Router\Rules;

defined('JPATH_PLATFORM') or die;

/**
 * RouterRules interface for Joomla
 *
 * @since  3.4
 */
interface RulesInterface
{
	/**
	 * Prepares a query set to be handed over to the build() method.
	 * This should complete a partial query set to work as a complete
non-SEFed
	 * URL and in general make sure that all information is present and
properly
	 * formatted. For example, the Itemid should be retrieved and set here.
	 *
	 * @param   array  &$query  The query array to process
	 *
	 * @return  void
	 *
	 * @since   3.4
	 */
	public function preprocess(&$query);

	/**
	 * Parses a URI to retrieve informations for the right route through
	 * the component.
	 * This method should retrieve all its input from its method arguments.
	 *
	 * @param   array  &$segments  The URL segments to parse
	 * @param   array  &$vars      The vars that result from the segments
	 *
	 * @return  void
	 *
	 * @since   3.4
	 */
	public function parse(&$segments, &$vars);

	/**
	 * Builds URI segments from a query to encode the necessary informations
	 * for a route in a human-readable URL.
	 * This method should retrieve all its input from its method arguments.
	 *
	 * @param   array  &$query     The vars that should be converted
	 * @param   array  &$segments  The URL segments to create
	 *
	 * @return  void
	 *
	 * @since   3.4
	 */
	public function build(&$query, &$segments);
}
PKk�[_�Y��Router/Rules/StandardRules.phpnu�[���<?php
/**
 * Joomla! Content Management System
 *
 * @copyright  Copyright (C) 2005 - 2020 Open Source Matters, Inc. All
rights reserved.
 * @license    GNU General Public License version 2 or later; see
LICENSE.txt
 */

namespace Joomla\CMS\Component\Router\Rules;

defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Component\Router\RouterView;

/**
 * Rule for the standard handling of component routing
 *
 * @since  3.4
 */
class StandardRules implements RulesInterface
{
	/**
	 * Router this rule belongs to
	 *
	 * @var    RouterView
	 * @since  3.4
	 */
	protected $router;

	/**
	 * Class constructor.
	 *
	 * @param   RouterView  $router  Router this rule belongs to
	 *
	 * @since   3.4
	 */
	public function __construct(RouterView $router)
	{
		$this->router = $router;
	}

	/**
	 * Dummymethod to fullfill the interface requirements
	 *
	 * @param   array  &$query  The query array to process
	 *
	 * @return  void
	 *
	 * @since   3.4
	 */
	public function preprocess(&$query)
	{
	}

	/**
	 * Parse the URL
	 *
	 * @param   array  &$segments  The URL segments to parse
	 * @param   array  &$vars      The vars that result from the segments
	 *
	 * @return  void
	 *
	 * @since   3.4
	 */
	public function parse(&$segments, &$vars)
	{
		// Get the views and the currently active query vars
		$views  = $this->router->getViews();
		$active = $this->router->menu->getActive();

		if ($active)
		{
			$vars = array_merge($active->query, $vars);
		}

		// We don't have a view or its not a view of this component! We stop
here
		if (!isset($vars['view']) ||
!isset($views[$vars['view']]))
		{
			return;
		}

		// Copy the segments, so that we can iterate over all of them and at the
same time modify the original segments
		$tempSegments = $segments;

		// Iterate over the segments as long as a segment fits
		foreach ($tempSegments as $segment)
		{
			// Our current view is nestable. We need to check first if the segment
fits to that
			if ($views[$vars['view']]->nestable)
			{
				if (is_callable(array($this->router, 'get' .
ucfirst($views[$vars['view']]->name) . 'Id')))
				{
					$key = call_user_func_array(array($this->router, 'get' .
ucfirst($views[$vars['view']]->name) . 'Id'),
array($segment, $vars));

					// Did we get a proper key? If not, we need to look in the child-views
					if ($key)
					{
						$vars[$views[$vars['view']]->key] = $key;

						array_shift($segments);

						continue;
					}
				}
				else
				{
					// The router is not complete. The get<View>Id() method is
missing.
					return;
				}
			}

			// Lets find the right view that belongs to this segment
			$found = false;

			foreach ($views[$vars['view']]->children as $view)
			{
				if (!$view->key)
				{
					if ($view->name === $segment)
					{
						// The segment is a view name
						$parent       = $views[$vars['view']];
						$vars['view'] = $view->name;
						$found        = true;

						if ($view->parent_key && isset($vars[$parent->key]))
						{
							$parent_key              = $vars[$parent->key];
							$vars[$view->parent_key] = $parent_key;

							unset($vars[$parent->key]);
						}

						break;
					}
				}
				elseif (is_callable(array($this->router, 'get' .
ucfirst($view->name) . 'Id')))
				{
					// Hand the data over to the router specific method and see if there
is a content item that fits
					$key = call_user_func_array(array($this->router, 'get' .
ucfirst($view->name) . 'Id'), array($segment, $vars));

					if ($key)
					{
						// Found the right view and the right item
						$parent       = $views[$vars['view']];
						$vars['view'] = $view->name;
						$found        = true;

						if ($view->parent_key && isset($vars[$parent->key]))
						{
							$parent_key              = $vars[$parent->key];
							$vars[$view->parent_key] = $parent_key;

							unset($vars[$parent->key]);
						}

						$vars[$view->key] = $key;

						break;
					}
				}
			}

			if (!$found)
			{
				return;
			}

			array_shift($segments);
		}
	}

	/**
	 * Build a standard URL
	 *
	 * @param   array  &$query     The vars that should be converted
	 * @param   array  &$segments  The URL segments to create
	 *
	 * @return  void
	 *
	 * @since   3.4
	 */
	public function build(&$query, &$segments)
	{
		if (!isset($query['Itemid'], $query['view']))
		{
			return;
		}

		// Get the menu item belonging to the Itemid that has been found
		$item =
$this->router->menu->getItem($query['Itemid']);

		if ($item === null
			|| $item->component !== 'com_' .
$this->router->getName()
			|| !isset($item->query['view']))
		{
			return;
		}

		// Get menu item layout
		$mLayout = isset($item->query['layout']) ?
$item->query['layout'] : null;

		// Get all views for this component
		$views = $this->router->getViews();

		// Return directly when the URL of the Itemid is identical with the URL
to build
		if ($item->query['view'] === $query['view'])
		{
			$view = $views[$query['view']];

			if (!$view->key)
			{
				unset($query['view']);

				if (isset($query['layout']) && $mLayout ===
$query['layout'])
				{
					unset($query['layout']);
				}

				return;
			}

			if (isset($query[$view->key]) &&
$item->query[$view->key] == (int) $query[$view->key])
			{
				unset($query[$view->key]);

				while ($view)
				{
					unset($query[$view->parent_key]);

					$view = $view->parent;
				}

				unset($query['view']);

				if (isset($query['layout']) && $mLayout ===
$query['layout'])
				{
					unset($query['layout']);
				}

				return;
			}
		}

		// Get the path from the view of the current URL and parse it to the menu
item
		$path  = array_reverse($this->router->getPath($query), true);
		$found = false;

		foreach ($path as $element => $ids)
		{
			$view = $views[$element];

			if ($found === false && $item->query['view'] ===
$element)
			{
				if ($view->nestable)
				{
					$found = true;
				}
				elseif ($view->children)
				{
					$found = true;

					continue;
				}
			}

			if ($found === false)
			{
				// Jump to the next view
				continue;
			}

			if ($ids)
			{
				if ($view->nestable)
				{
					$found2 = false;

					foreach (array_reverse($ids, true) as $id => $segment)
					{
						if ($found2)
						{
							$segments[] = str_replace(':', '-', $segment);
						}
						elseif ((int) $item->query[$view->key] === (int) $id)
						{
							$found2 = true;
						}
					}
				}
				elseif ($ids === true)
				{
					$segments[] = $element;
				}
				else
				{
					$segments[] = str_replace(':', '-',
current($ids));
				}
			}

			if ($view->parent_key)
			{
				// Remove parent key from query
				unset($query[$view->parent_key]);
			}
		}

		if ($found)
		{
			unset($query[$views[$query['view']]->key],
$query['view']);

			if (isset($query['layout']) && $mLayout ===
$query['layout'])
			{
				unset($query['layout']);
			}
		}
	}
}
PKJ[�[�(r��Admin/HtmlController.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Admin;

use Gantry\Component\Controller\HtmlController as BaseController;

abstract class HtmlController extends BaseController
{
    /**
     * @param string|array $file
     * @param array $context
     * @return string
     */
    public function render($file, array $context = [])
    {
        return
$this->container['admin.theme']->render($file, $context);
    }

    /**
     * @param string $action
     * @param string $id
     * @return boolean
     */
    public function authorize($action, $id = null)
    {
        return
$this->container['platform']->authorize($action, $id);
    }
}
PKJ[�[��p���Admin/JsonController.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Admin;

use Gantry\Component\Controller\JsonController as BaseController;

abstract class JsonController extends BaseController
{
    /**
     * @param string|array $file
     * @param array $context
     * @return string
     */
    public function render($file, array $context = [])
    {
        return
$this->container['admin.theme']->render($file, $context);
    }

    /**
     * @param string $action
     * @param string $id
     * @return boolean
     */
    public function authorize($action, $id = null)
    {
        return
$this->container['platform']->authorize($action, $id);
    }
}
PKJ[�[ܵ����#Assignments/AbstractAssignments.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Assignments;

use Gantry\Component\Config\CompiledConfig;
use Gantry\Component\Config\ConfigFileFinder;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

abstract class AbstractAssignments
{
    /**
     * @var string
     */
    protected $configuration;

    /**
     * @var string
     */
    protected $className =
'\Gantry\%s\Assignments\Assignments%s';

    /**
     * @var string
     */
    protected $platform;

    /**
     * @var AssignmentFilter
     */
    protected $filter;

    /**
     * @var array
     */
    protected $candidates;

    /**
     * @var array
     */
    protected $page;

    /** @var callable */
    protected $specialFilterMethod;

    /**
     * @param string $configuration
     */
    public function __construct($configuration = null)
    {
        $this->configuration = $configuration;
    }

    /**
     * Get list of assignment items.
     */
    public function get()
    {
        return $this->getTypes();
    }

    /**
     * Set (save) assignments.
     *
     * @param array $data
     */
    public function set(array $data)
    {
        $this->save($data);
    }

    /**
     * Select assigned outline.
     *
     * @param string $default
     * @return string
     */
    public function select($default = 'default')
    {
        $scores = $this->scores();

        return key($scores) ?: $default;
    }

    /**
     * List matching outlines sorted by score.
     *
     * @param array $candidates
     * @return array
     */
    public function scores(array $candidates = null)
    {
        $this->init();
        $candidates = $candidates ?: $this->candidates;
        return $this->filter->scores($candidates, $this->page,
$this->specialFilterMethod);
    }

    /**
     * List matching outlines with matched assignments.
     *
     * @param array $candidates
     * @return array
     */
    public function matches(array $candidates = null)
    {
        $this->init();
        $candidates = $candidates ?: $this->candidates;
        return $this->filter->matches($candidates, $this->page,
$this->specialFilterMethod);
    }

    /**
     * Load all assignments.
     *
     * @return array
     */
    public function loadAssignments()
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        // Find all the assignment files.
        $paths = $locator->findResources("gantry-config://");
        $files = (new
ConfigFileFinder)->locateFileInFolder('assignments', $paths);

        // Make sure that base or system outlines aren't in the list.
        foreach ($files as $key => $array) {
            $key = (string)$key;
            if ($key === 'default' || strpos($key, '_')
=== 0) {
                unset($files[$key]);
            }
        }

        $cache =
$locator->findResource('gantry-cache://theme/compiled/config',
true, true);

        $config = new CompiledConfig($cache, [$files], GANTRY5_ROOT);

        return $config->load()->toArray();
    }

    /**
     * Get all assignments for the current page.
     *
     * @return array
     */
    public function getPage()
    {
        $list = [];

        foreach($this->types() as $class => $type) {
            $class = is_numeric($class) ? sprintf($this->className,
$this->platform, ucfirst($type)) : $class;

            if (!class_exists($class)) {
                throw new \RuntimeException("Assignment type {$type}
is missing");
            }

            /** @var AssignmentsInterface $instance */
            $instance = new $class;
            $list[$type] = $instance->getRules();
            unset($instance);
        }

        return $list;
    }

    /**
     * Filter assignments data.
     *
     * @param array $data
     * @param bool $minimize
     * @return array
     */
    public function filter(array $data, $minimize = false)
    {
        $types = [];
        foreach ($this->types() as $type) {
            $types[$type] = [];
        }

        $data = array_replace($types, $data);
        foreach ($data as $tname => &$type) {
            if (is_array($type)) {
                foreach ($type as $gname => &$group) {
                    if (is_array($group)) {
                        foreach ($group as $key => $value) {
                            if (!$value) {
                                unset($group[$key]);
                            } else {
                                $group[$key] = (bool) $value;
                            }
                        }
                        if (empty($group)) {
                            unset($type[$gname]);
                        }
                    } else {
                        $group = (bool) $group;
                    }
                }
                unset($group);
                if ($minimize && empty($type)) {
                    unset($data[$tname]);
                }
            } else {
                $type = (bool) $type;
            }
        }

        return $data;
    }

    /**
     * Save assignments for the configuration.
     *
     * @param array $data
     */
    public function save(array $data)
    {
        $data = $this->filter($data);

        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        // Save layout into custom directory for the current theme.
        $save_dir =
$locator->findResource("gantry-config://{$this->configuration}",
true, true);
        $filename = "{$save_dir}/assignments.yaml";

        $file = YamlFile::instance($filename);
        $file->save($data);
        $file->free();
    }

    /**
     * Get list of all assignment types for assignments form.
     *
     * @return array
     */
    public function getTypes()
    {
        $list = [];

        foreach ($this->types() as $class => $type) {
            $class = is_numeric($class) ? sprintf($this->className,
$this->platform, ucfirst($type)) : $class;

            if (!class_exists($class)) {
                throw new \RuntimeException("Assignment type
'{$type}' is missing");
            }

            /** @var AssignmentsInterface $instance */
            $instance = new $class;
            $list[$type] =
$instance->listRules($this->configuration);
            unset($instance);
        }

        return $list;
    }

    /**
     * Get selected assignment option.
     *
     * @return string
     */
    public function getAssignment()
    {
        return 'default';
    }

    /**
     * Set extra options for assignments.
     *
     * @param $value
     */
    public function setAssignment($value)
    {
    }

    /**
     * Get extra options for assignments.
     *
     * @return array
     */
    public function assignmentOptions()
    {
        return [];
    }

    protected function init()
    {
        if (!$this->filter) {
            $this->filter = new AssignmentFilter;
            $this->candidates = $this->loadAssignments();
            $this->page = $this->getPage();
        }
    }

    /**
     * Return list of assignment types.
     *
     * @return array
     */
    abstract public function types();
}
PKJ[�[�d�//
Assignments/AssignmentFilter.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Assignments;

/**
 * Class AssignmentFilter
 * @package Gantry\Assignments
 */
class AssignmentFilter
{
    protected $method;

    /**
     * Return all matching candidates with their score. Candidates are
ordered by their scores.
     *
     * @param array $candidates  In format of
candidates[name][section][rule].
     * @param array $page        In format of page[section][rule].
     * @return array
     */
    public function scores(array &$candidates, array &$page,
callable $function = null)
    {
        $matches = $this->matches($candidates, $page, $function);

        $scores = [];
        foreach ($matches as $type => $candidate) {
            $scores[$type] = $this->getScore($candidate) +
(isset($candidate['language']) ? 0.01 : 0);
        }

        // Always return matches by score in alphabetical order.
        ksort($scores, SORT_STRING);
        arsort($scores, SORT_NUMERIC);

        return $scores;
    }

    /**
     * Returns all matching candidates with matching rules.
     *
     * @param array $candidates  In format of
candidates[name][section][rule].
     * @param array $page        In format of page[section][rule].
     * @return array
     */
    public function matches(array $candidates, array &$page, callable
$function = null)
    {
        $matches = [];
        foreach ($candidates as $type => $candidate) {
            if (!is_array($candidate)) {
                if ($candidate === true && $page) {
                    $matches[$type] = $page;
                }
                continue;
            }
            foreach ($candidate as $section => $list) {
                if (!is_array($list)) {
                    if ($list === true && !empty($page[$section]))
{
                        $matches[$type][$section] = $page[$section];
                    }
                    continue;
                }
                foreach ($list as $name => $rules) {
                    if (!empty($page[$section][$name])) {
                        if (!is_array($rules)) {
                            $match = $rules === true ?
$page[$section][$name] : [];
                        } else {
                            $match =
\array_intersect_key($page[$section][$name], $rules);
                        }
                        if ($match) {
                            $matches[$type][$section][$name] = $match;
                        }
                    }
                }
            }
            if (isset($matches[$type]) && $function &&
call_user_func($function, $candidate, $matches[$type], $page) === false) {
                unset($matches[$type]);
            }
        }

        return $matches;
    }

    /**
     * Returns the calculated score for the assignment.
     *
     * @param array $matches
     * @param string $method
     * @return int
     */
    public function getScore(array &$matches, $method =
'max')
    {
        $this->method = 'calc' . ucfirst($method);

        if (!method_exists($this, $this->method)) {
            $this->method = 'calcMax';
        }

        return $this->calcArray(0, $matches);
    }

    /**
     * @param float $carry
     * @param float|array $item
     * @return float
     * @internal
     */
    protected function calcArray($carry, $item)
    {
        if (is_array($item)) {
            return array_reduce($item, [$this, 'calcArray'],
$carry);
        }

        $method = $this->method;
        return $this->{$method}($carry, (float) $item);
    }

    /**
     * @param float $carry
     * @param float $item
     * @return float
     * @internal
     */
    protected function calcOr($carry, $item)
    {
        return (float) ($carry || $item);
    }

    /**
     * @param float $carry
     * @param float $item
     * @return float
     * @internal
     */
    protected function calcMin($carry, $item)
    {
        return $carry ? min($carry, $item) : $item;
    }

    /**
     * @param float $carry
     * @param float $item
     * @return float
     * @internal
     */
    protected function calcMax($carry, $item)
    {
        return max($carry, $item);
    }

    /**
     * @param float $carry
     * @param float $item
     * @return float
     * @internal
     */
    protected function calcSum($carry, $item)
    {
        return $carry + $item;
    }

    /**
     * @param float $carry
     * @param float $item
     * @return float
     * @internal
     */
    protected function calcMul($carry, $item)
    {
        return $carry ? $carry * $item : $item;
    }
}
PKJ[�[3�U���$Assignments/AssignmentsInterface.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Assignments;

interface AssignmentsInterface
{
    /**
     * Returns list of rules which apply to the current page.
     *
     * @return array
     */
    public function getRules();

    /**
     * List all the rules available.
     *
     * @param string $configuration
     * @return array
     */
    public function listRules($configuration);
}
PKJ[�[�)!�99Collection/Collection.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Collection;

use RocketTheme\Toolbox\ArrayTraits\ArrayAccess;
use RocketTheme\Toolbox\ArrayTraits\Countable;
use RocketTheme\Toolbox\ArrayTraits\Export;

class Collection implements CollectionInterface
{
    use ArrayAccess, Countable, Export;

    /**
     * @var array
     */
    protected $items = [];

    public static function __set_state($variables)
    {
        $instance = new static();
        $instance->items = $variables['items'];
        return $instance;
    }

    /**
     *
     * Create a copy of this collection.
     *
     * @return static
     */
    public function copy()
    {
        return clone $this;
    }

    /**
     * @param $item
     * @return $this
     */
    public function add($item)
    {
        $this->items[] = $item;

        return $this;
    }

    /**
     * @return \ArrayIterator
     */
    public function getIterator()
    {
        return new \ArrayIterator($this->items);
    }
}
PKJ[�[�?�~~"Collection/CollectionInterface.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Collection;

interface CollectionInterface extends \IteratorAggregate, \ArrayAccess,
\Countable
{
    public function toArray();

    /**
     * @param $item
     */
    public function add($item);

    /**
     * @return \ArrayIterator
     */
    public function getIterator();

    /**
     * @param $offset
     *
     * @return bool
     */
    public function offsetExists($offset);

    /**
     * @param $offset
     * @param $value
     */
    public function offsetSet($offset, $value);

    /**
     * @param $offset
     *
     * @return mixed
     */
    public function offsetGet($offset);

    /**
     * @param $offset
     */
    public function offsetUnset($offset);

    /**
     * @return int
     */
    public function count();
}
PKK[�[�c��9$9$Config/BlueprintForm.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\Blueprints\BlueprintForm as BaseBlueprintForm;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

/**
 * The Config class contains configuration information.
 *
 * @author RocketTheme
 */
class BlueprintForm extends BaseBlueprintForm
{
    /**
     * @var string
     */
    protected $context = 'gantry-blueprints://';

    /**
     * @var BlueprintSchema
     */
    protected $schema;

    /**
     * @param string $filename
     * @param string $context
     * @return BlueprintForm
     */
    public static function instance($filename, $context = null)
    {
        /** @var BlueprintForm $instance */
        $instance = new static($filename);
        if ($context) {
            $instance->setContext($context);
        }

        return $instance->load()->init();
    }

    /**
     * Get nested structure containing default values defined in the
blueprints.
     *
     * Fields without default value are ignored in the list.
     *
     * @return array
     */
    public function getDefaults()
    {
        return $this->schema()->getDefaults();
    }

    /**
     * Merge two arrays by using blueprints.
     *
     * @param  array $data1
     * @param  array $data2
     * @param  string $name         Optional
     * @param  string $separator    Optional
     * @return array
     */
    public function mergeData(array $data1, array $data2, $name = null,
$separator = '.')
    {
        return $this->schema()->mergeData($data1, $data2, $name,
$separator);
    }

    /**
     * Return data fields that do not exist in blueprints.
     *
     * @param  array  $data
     * @param  string $prefix
     * @return array
     */
    public function extra(array $data, $prefix = '')
    {
        return $this->schema()->extra($data, $prefix);
    }

    /**
     * Validate data against blueprints.
     *
     * @param  array $data
     * @throws \RuntimeException
     */
    public function validate(array $data)
    {
        $this->schema()->validate($data);
    }

    /**
     * Filter data by using blueprints.
     *
     * @param  array $data
     * @return array
     */
    public function filter(array $data)
    {
        return $this->schema()->filter($data);
    }

    /**
     * @return BlueprintSchema
     */
    public function schema()
    {
        if (!isset($this->schema)) {
            $this->schema = new BlueprintSchema;
            $this->schema->embed('', $this->items);
            $this->schema->init();
        }

        return $this->schema;
    }

    /**
     * @param string $filename
     * @return string
     */
    protected function loadFile($filename)
    {
        $file = CompiledYamlFile::instance($filename);
        $content = $file->content();
        $file->free();

        return $content;
    }

    /**
     * @param string|array $path
     * @param string $context
     * @return array
     */
    protected function getFiles($path, $context = null)
    {
        if (is_string($path) && !strpos($path, '://')) {
            // Resolve filename.
            if (isset($this->overrides[$path])) {
                $path = $this->overrides[$path];
            } else {
                if ($context === null) {
                    $context = $this->context;
                }
                if ($context && $context[strlen($context)-1] !==
'/') {
                    $context .= '/';
                }
                $path = $context . $path;

                if (!preg_match('/\.yaml$/', $path)) {
                    $path .= '.yaml';
                }
            }
        }

        if (is_string($path) && strpos($path, '://')) {
            /** @var UniformResourceLocator $locator */
            $locator = Gantry::instance()['locator'];

            $files = $locator->findResources($path);
        } else {
            $files = (array) $path;
        }

        return $files;
    }

    /**
     * @param array $field
     * @param string $property
     * @param array $call
     */
    protected function dynamicData(array &$field, $property, array
&$call)
    {
        $params = $call['params'];

        if (is_array($params)) {
            $function = array_shift($params);
        } else {
            $function = $params;
            $params = [];
        }

        list($o, $f) = preg_split('/::/', $function, 2);
        if (!$f) {
            if (function_exists($o)) {
                $data = call_user_func_array($o, $params);
            }
        } else {
            if (method_exists($o, $f)) {
                $data = call_user_func_array(array($o, $f), $params);
            }
        }

        // If function returns a value,
        if (isset($data)) {
            if (isset($field[$property]) &&
is_array($field[$property]) && is_array($data)) {
                // Combine field and @data-field together.
                $field[$property] += $data;
            } else {
                // Or create/replace field with @data-field.
                $field[$property] = $data;
            }
        }
    }

    /**
     * @param array $field
     * @param string $property
     * @param array $call
     */
    protected function dynamicConfig(array &$field, $property, array
&$call)
    {
        $value = $call['params'];

        $default = isset($field[$property]) ? $field[$property] : null;
        $config = Gantry::instance()['config']->get($value,
$default);

        if (!is_null($config)) {
            $field[$property] = $config;
        }
    }

    /**
     * Get blueprints by using slash notation for nested arrays/objects.
     *
     * @param array  $path
     * @param string  $separator
     * @return array
     */
    public function resolve(array $path, $separator = '/')
    {
        $fields = false;
        $parts = [];
        $current = $this['form/fields'];
        $result = [null, null, null];
        $prefix = '';

        while (($field = current($path)) !== false) {
            if (!$fields && isset($current['fields'])) {
                if (!empty($current['array'])) {
                    $result = [$current, $parts, $path ?
implode($separator, $path) : null];
                    // Skip item offset.
                    $parts[] = array_shift($path);
                }

                $current = $current['fields'];
                $prefix = '';
                $fields = true;

            } elseif (isset($current[$prefix . $field])) {
                $parts[] = array_shift($path);
                $current = $current[$prefix . $field];
                $prefix = '';
                $fields = false;

            } elseif (isset($current['.' . $prefix . $field])) {
                $parts[] = array_shift($path);
                $current = $current['.' . $prefix . $field];
                $prefix = '';
                $fields = false;

            } elseif ($field && $this->fieldExists($prefix .
$field, $current)) {
                $parts[] = array_shift($path);
                $prefix = "{$prefix}{$field}.";
                $fields = false;

            } else {
                // Check if there's a container with the field.
                $current = $this->resolveContainer($current, $prefix,
$field);

                // No containers with the field found.
                if (!$current) {
                    break;
                }

                $prefix = '';
                $fields = false;
            }
        }

        if (!$fields && !empty($current['array'])) {
            $result = [$current, $parts, $path ? implode($separator, $path)
: null];
        }

        return $result;
    }

    protected function resolveContainer($current, $prefix, $fieldName)
    {
        foreach ($current as $field) {
            $type = isset($field['type']) ?
$field['type'] : 'container._implicit';
            $container = (0 === strpos($type, 'container.'));

            if (!$container || !isset($field['fields'])) {
                continue;
            }

            $current_fields = $field['fields'];
            if (isset($current_fields[$prefix . $fieldName]) ||
isset($current_fields['.' . $prefix . $fieldName])
                || $this->fieldExists($prefix . $fieldName,
$current_fields)) {
                return $current_fields;
            }

            $current_fields = $this->resolveContainer($current_fields,
$prefix, $fieldName);
            if ($current_fields !== null) {
                return $current_fields;
            }
        }

        return null;
    }

    protected function fieldExists($prefix, $list)
    {
        foreach ($list as $field => $data) {
            $pos = strpos($field, $prefix);
            if ($pos === 0 || ($pos === 1 && $field[0] ===
'.')) {
                return true;
            }
        }

        return false;
    }
}
PKK[�[ù���Config/BlueprintSchema.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\Blueprints\BlueprintSchema as BlueprintSchemaBase;

/**
 * Blueprint schema handles the internal logic of blueprints.
 *
 * @author RocketTheme
 * @license MIT
 */
class BlueprintSchema extends BlueprintSchemaBase
{
    protected $configuration;

    protected $ignoreFormKeys = [
        'title' => true,
        'help' => true,
        'placeholder' => true,
        'fields' => true
    ];

    protected $types = [
        'container.set' => [
            'input@' => false
        ],
        'container.tabs' => [
            'input@' => false
        ],
        'separator.note' => [
            'input@' => false
        ],
        'separator.separator' => [
            'input@' => false
        ],
        'key' => [
            'input@' => false
        ],
        'collection.list' => [
            'array' => true
        ]
    ];

    /**
     * Constructor.
     *
     * @param array $serialized  Serialized content if available.
     */
    public function __construct($serialized = null)
    {
        parent::__construct($serialized);

        $this->configuration = new
Config(isset($serialized['configuration']) ?
$serialized['configuration'] : []);
    }

    /**
     * Convert object into an array.
     *
     * @return array
     */
    public function getState()
    {
        return parent::getState() + ['configuration' =>
$this->configuration->toArray()];
    }

    /**
     * Get nested structure containing default values defined in the
blueprints.
     *
     * Fields without default value are ignored in the list.
     *
     * @return array
     */
    public function getDefaults()
    {
        return array_merge_recursive($this->configuration->toArray(),
$this->buildDefaults($this->nested));
    }

    /**
     * Embed an array to the blueprint.
     *
     * @param $name
     * @param array $value
     * @param string $separator
     * @param bool $merge   Merge fields instead replacing them.
     * @return $this
     */
    public function embed($name, array $value, $separator = '.',
$merge = false)
    {
        parent::embed($name, $value, $separator, $merge);

        if (isset($value['configuration'])) {
            $this->configuration->set($name,
$value['configuration'], $separator);
        }

        return $this;
    }

    /**
     * Validate data against blueprints.
     *
     * @param  array $data
     * @throws \RuntimeException
     */
    public function validate(array $data)
    {
        try {
            $messages = $this->validateArray($data, $this->nested);

        } catch (\RuntimeException $e) {
            throw (new ValidationException($e->getMessage(),
$e->getCode(), $e))->setMessages();
        }

        if (!empty($messages)) {
            throw (new ValidationException())->setMessages($messages);
        }
    }

    /**
     * Filter data by using blueprints.
     *
     * @param  array $data
     * @return array
     */
    public function filter(array $data)
    {
        return $this->filterArray($data, $this->nested);
    }

    /**
     * @param array $data
     * @param array $rules
     * @returns array
     * @throws \RuntimeException
     * @internal
     */
    protected function validateArray(array $data, array $rules)
    {
        $messages = $this->checkRequired($data, $rules);

        foreach ($data as $key => $field) {
            $val = isset($rules[$key]) ? $rules[$key] :
(isset($rules['*']) ? $rules['*'] : null);
            $rule = is_string($val) ? $this->items[$val] : null;

            if ($rule) {
                // Item has been defined in blueprints.
                $messages += Validation::validate($field, $rule);
            } elseif (is_array($field) && is_array($val)) {
                // Array has been defined in blueprints.
                $messages += $this->validateArray($field, $val);
            } elseif (isset($rules['validation']) &&
$rules['validation'] == 'strict') {
                // Undefined/extra item.
                throw new \RuntimeException(sprintf('%s is not defined
in blueprints', $key));
            }
        }

        return $messages;
    }

    /**
     * @param array $data
     * @param array $rules
     * @return array
     * @internal
     */
    protected function filterArray(array $data, array $rules)
    {
        $results = array();
        foreach ($data as $key => $field) {
            $val = isset($rules[$key]) ? $rules[$key] :
(isset($rules['*']) ? $rules['*'] : null);
            $rule = is_string($val) ? $this->items[$val] : null;

            if ($rule) {
                // Item has been defined in blueprints.
                $field = Validation::filter($field, $rule);
            } elseif (is_array($field) && is_array($val)) {
                // Array has been defined in blueprints.
                $field = $this->filterArray($field, $val);
            } elseif (isset($rules['validation']) &&
$rules['validation'] == 'strict') {
                $field = null;
            }

            if (isset($field) && (!is_array($field) ||
!empty($field))) {
                $results[$key] = $field;
            }
        }

        return $results;
    }

    /**
     * @param array $data
     * @param array $fields
     * @return array
     */
    protected function checkRequired(array $data, array $fields)
    {
        $messages = [];

        foreach ($fields as $name => $field) {
            if (!is_string($field)) {
                continue;
            }
            $field = $this->items[$field];
            if (isset($field['validate']['required'])
                &&
$field['validate']['required'] === true
                && !isset($data[$name])) {
                $value = isset($field['label']) ?
$field['label'] : $field['name'];
                // TODO: translate
                $message  = sprintf("Please fill up required field
'%s'.", $value);
                $messages[$field['name']][] = $message;
            }
        }

        return $messages;
    }

    /**
     * @param array $field
     * @param string $property
     * @param array $call
     */
    protected function dynamicConfig(array &$field, $property, array
&$call)
    {
        $value = $call['params'];

        $default = isset($field[$property]) ? $field[$property] : null;
        $config = Gantry::instance()['config']->get($value,
$default);

        if (!is_null($config)) {
            $field[$property] = $config;
        }
    }
}
PKK[�[�-��Config/CompiledBase.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\File\PhpFile;

/**
 * The Compiled base class.
 */
abstract class CompiledBase
{
    /**
     * @var int Version number for the compiled file.
     */
    public $version = 1;

    /**
     * @var string  Filename (base name) of the compiled configuration.
     */
    public $name;

    /**
     * @var string|bool  Configuration checksum.
     */
    public $checksum;

    /**
     * @var string Cache folder to be used.
     */
    protected $cacheFolder;

    /**
     * @var array  List of files to load.
     */
    protected $files;

    /**
     * @var string
     */
    protected $path;

    /**
     * @var mixed  Configuration object.
     */
    protected $object;

    /**
     * @param  string $cacheFolder  Cache folder to be used.
     * @param  array  $files  List of files as returned from
ConfigFileFinder class.
     * @param string $path  Base path for the file list.
     * @throws \BadMethodCallException
     */
    public function __construct($cacheFolder, array $files, $path =
GANTRY5_ROOT)
    {
        if (!$cacheFolder) {
            throw new \BadMethodCallException('Cache folder not
defined.');
        }

        $this->cacheFolder = $cacheFolder;
        $this->files = $files;
        $this->path = $path ? rtrim($path, '\\/') .
'/' : '';
    }

    /**
     * Get filename for the compiled PHP file.
     *
     * @param string $name
     * @return $this
     */
    public function name($name = null)
    {
        if (!$this->name) {
            $this->name = $name ?:
md5(json_encode(array_keys($this->files)));
        }

        return $this;
    }

    /**
     * Function gets called when cached configuration is saved.
     */
    public function modified() {}

    /**
     * Load the configuration.
     *
     * @return mixed
     */
    public function load()
    {
        if ($this->object) {
            return $this->object;
        }

        $filename = $this->createFilename();
        if (!$this->loadCompiledFile($filename) &&
$this->loadFiles()) {
            $this->saveCompiledFile($filename);
        }

        return $this->object;
    }

    /**
     * Returns checksum from the configuration files.
     *
     * You can set $this->checksum = false to disable this check.
     *
     * @return bool|string
     */
    public function checksum()
    {
        if (!isset($this->checksum)) {
            $this->checksum = md5(json_encode($this->files) .
$this->version);
        }

        return $this->checksum;
    }

    protected function createFilename()
    {
        return
"{$this->cacheFolder}/{$this->name()->name}.php";
    }

    /**
     * Create configuration object.
     *
     * @param  array  $data
     */
    abstract protected function createObject(array $data = []);

    /**
     * Finalize configuration object.
     */
    abstract protected function finalizeObject();

    /**
     * Load single configuration file and append it to the correct
position.
     *
     * @param  string  $name  Name of the position.
     * @param  string  $filename  File to be loaded.
     */
    abstract protected function loadFile($name, $filename);

    /**
     * Load and join all configuration files.
     *
     * @return bool
     * @internal
     */
    protected function loadFiles()
    {
        $this->createObject();

        $list = array_reverse($this->files);
        foreach ($list as $files) {
            foreach ($files as $name => $item) {
                $this->loadFile($name, $this->path .
$item['file']);
            }
        }

        $this->finalizeObject();

        return true;
    }

    /**
     * Load compiled file.
     *
     * @param  string  $filename
     * @return bool
     * @internal
     */
    protected function loadCompiledFile($filename)
    {
        $gantry = Gantry::instance();

        /** @var Config $global */
        $global = $gantry['global'];

        if (!$global->get('compile_yaml', 1)) {
            return false;
        }

        if (!file_exists($filename)) {
            return false;
        }

        $cache = include $filename;
        if (
            !is_array($cache)
            || !isset($cache['checksum'])
            || !isset($cache['data'])
            || !isset($cache['@class'])
            || $cache['@class'] != get_class($this)
        ) {
            return false;
        }

        // Load real file if cache isn't up to date (or is invalid).
        if ($cache['checksum'] !== $this->checksum()) {
            return false;
        }

        $this->createObject($cache['data']);

        $this->finalizeObject();

        return true;
    }

    /**
     * Save compiled file.
     *
     * @param  string  $filename
     * @throws \RuntimeException
     * @internal
     */
    protected function saveCompiledFile($filename)
    {
        $gantry = Gantry::instance();

        /** @var Config $global */
        $global = $gantry['global'];

        if (!$global->get('compile_yaml', 1)) {
            return;
        }

        $file = PhpFile::instance($filename);

        // Attempt to lock the file for writing.
        try {
            $file->lock(false);
        } catch (\Exception $e) {
            // Another process has locked the file; we will check this in a
bit.
        }

        if ($file->locked() === false) {
            // File was already locked by another process.
            return;
        }

        $cache = [
            '@class' => get_class($this),
            'timestamp' => time(),
            'checksum' => $this->checksum(),
            'files' => $this->files,
            'data' => $this->getState()
        ];

        $file->save($cache);
        $file->unlock();
        $file->free();

        $this->modified();
    }

    protected function getState()
    {
        return $this->object->toArray();
    }
}
PKK[�[�ԂDDConfig/CompiledBlueprints.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

/**
 * The Compiled Blueprints class.
 */
class CompiledBlueprints extends CompiledBase
{
    /**
     * @var int Version number for the compiled file.
     */
    public $version = 3;

    /**
     * @var BlueprintSchema  Blueprints object.
     */
    protected $object;

    /**
     * Create configuration object.
     *
     * @param array  $data
     */
    protected function createObject(array $data = [])
    {
        $this->object = new BlueprintSchema($data);
    }

    /**
     * Finalize configuration object.
     */
    protected function finalizeObject()
    {
    }

    /**
     * Load single configuration file and append it to the correct
position.
     *
     * @param  string  $name  Name of the position.
     * @param  array   $files  Files to be loaded.
     */
    protected function loadFile($name, $files)
    {
        // Load blueprint file.
        $blueprint = new BlueprintForm($files);

        $this->object->embed($name,
$blueprint->load()->toArray(), '/', true);
    }

    /**
     * Load and join all configuration files.
     *
     * @return bool
     * @internal
     */
    protected function loadFiles()
    {
        $this->createObject();

        // Convert file list into parent list.
        $list = [];
        foreach ($this->files as $files) {
            foreach ($files as $name => $item) {
                $list[$name][] = $this->path . $item['file'];
            }
        }

        // Load files.
        foreach ($list as $name => $files) {
            $this->loadFile($name, $files);
        }

        $this->finalizeObject();

        return true;
    }

    protected function getState()
    {
        return $this->object->getState();
    }
}
PKK[�[��ͭ

Config/CompiledConfig.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

use Gantry\Component\File\CompiledYamlFile;

/**
 * The Compiled Configuration class.
 */
class CompiledConfig extends CompiledBase
{
    /**
     * @var int Version number for the compiled file.
     */
    public $version = 2;

    /**
     * @var Config  Configuration object.
     */
    protected $object;

    /**
     * @var callable  Blueprints loader.
     */
    protected $callable;

    /**
     * @var bool
     */
    protected $withDefaults;

    /**
     * Get filename for the compiled PHP file.
     *
     * @param string $name
     * @return $this
     */
    public function name($name = null)
    {
        if (!$this->name) {
            $this->name = $name ?:
md5(json_encode(array_keys($this->files)) . (int)
$this->withDefaults);
        }

        return $this;
    }

    /**
     * Set blueprints for the configuration.
     *
     * @param callable $blueprints
     * @return $this
     */
    public function setBlueprints(callable $blueprints)
    {
        $this->callable = $blueprints;

        return $this;
    }

    /**
     * @param bool $withDefaults
     * @return mixed
     */
    public function load($withDefaults = false)
    {
        $this->withDefaults = $withDefaults;

        return parent::load();
    }

    /**
     * Create configuration object.
     *
     * @param  array  $data
     */
    protected function createObject(array $data = [])
    {
        if ($this->withDefaults && empty($data) &&
is_callable($this->callable)) {
            $blueprints = $this->callable;
            $data = $blueprints()->getDefaults();
        }

        $this->object = new Config($data, $this->callable);
    }

    /**
     * Finalize configuration object.
     */
    protected function finalizeObject()
    {
    }

    /**
     * Load single configuration file and append it to the correct
position.
     *
     * @param  string  $name  Name of the position.
     * @param  string  $filename  File to be loaded.
     */
    protected function loadFile($name, $filename)
    {
        $file = CompiledYamlFile::instance($filename);
        $this->object->join($name, $file->content(),
'/');
        $file->free();
    }
}
PKK[�[Ѱ�PddConfig/Config.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
use RocketTheme\Toolbox\ArrayTraits\Iterator;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;

/**
 * The Config class contains configuration information.
 *
 * @author RocketTheme
 */
class Config implements \ArrayAccess, \Countable, \Iterator,
ExportInterface
{
    use NestedArrayAccessWithGetters, Iterator, Export;

    /**
     * @var array
     */
    protected $items;

    /**
     * @var BlueprintSchema|BlueprintForm|callable
     */
    protected $blueprint;

    /**
     * Constructor to initialize array.
     *
     * @param  array  $items  Initial items inside the iterator.
     * @param  callable $blueprints  Function to load Blueprints for the
configuration.
     */
    public function __construct(array $items, callable $blueprint = null)
    {
        $this->items = $items;
        $this->blueprint = $blueprint;
    }

    /**
     * Join nested values together by using blueprints.
     *
     * @param string  $name       Dot separated path to the requested
value.
     * @param mixed   $value      Value to be joined.
     * @param string  $separator  Separator, defaults to '.'
     * @return $this
     * @throws \RuntimeException
     */
    public function join($name, $value, $separator = '.')
    {
        $old = $this->get($name, null, $separator);
        if ($old !== null) {
            if (!is_array($old)) {
                throw new \RuntimeException("Value is not array in
{$name}: " . print_r($old, true));
            }
            if (is_object($value)) {
                $value = (array) $value;
            } elseif (!is_array($value)) {
                throw new \RuntimeException("Value is not array in
{$name}: " . print_r($value, true));
            }
            $value = $this->blueprint()->mergeData($old, $value,
$name, $separator);
        }

        $this->set($name, $value, $separator);

        return $this;
    }

    /**
     * Get nested structure containing default values defined in the
blueprints.
     *
     * Fields without default value are ignored in the list.

     * @return array
     */
    public function getDefaults()
    {
        return $this->blueprint()->getDefaults();
    }

    /**
     * Set default values by using blueprints.
     *
     * @param string  $name       Dot separated path to the requested
value.
     * @param mixed   $value      Value to be joined.
     * @param string  $separator  Separator, defaults to '.'
     * @return $this
     */
    public function joinDefaults($name, $value, $separator = '.')
    {
        if (is_object($value)) {
            $value = (array) $value;
        }
        $old = $this->get($name, null, $separator);
        if ($old !== null) {
            $value = $this->blueprint()->mergeData($value, $old,
$name, $separator);
        }

        $this->set($name, $value, $separator);

        return $this;
    }

    /**
     * Get value from the configuration and join it with given data.
     *
     * @param string  $name       Dot separated path to the requested
value.
     * @param array   $value      Value to be joined.
     * @param string  $separator  Separator, defaults to '.'
     * @return array
     * @throws \RuntimeException
     */
    public function getJoined($name, $value, $separator = '.')
    {
        if (is_object($value)) {
            $value = (array) $value;
        } elseif (!is_array($value)) {
            throw new \RuntimeException("Value is not array in
'{$name}': " . print_r($value, true));
        }

        $old = $this->get($name, null, $separator);

        if ($old === null) {
            // No value set; no need to join data.
            return $value;
        }

        if (!is_array($old)) {
            throw new \RuntimeException("Value is not array in
'{$name}': " . print_r($value, true));
        }

        // Return joined data.
        return $this->blueprint()->mergeData($old, $value, $name,
$separator);
    }

    /**
     * Merge two configurations together.
     *
     * @param array $data
     * @return $this
     */
    public function merge(array $data)
    {
        $this->items =
$this->blueprint()->mergeData($this->items, $data);

        return $this;
    }

    /**
     * Set default values to the configuration if variables were not set.
     *
     * @param array $data
     * @return $this
     */
    public function setDefaults(array $data)
    {
        $this->items = $this->blueprint()->mergeData($data,
$this->items);

        return $this;
    }

    /**
     * Make a flat list from the configuration.
     *
     * @param string $name      Dot separated path to the requested value.
     * @param string $separator Separator, defaults to '.'
     * @param string $prefix    Name prefix.
     * @return array
     */
    public function flatten($name = null, $separator = '.',
$prefix = '')
    {
        $element = $name ? $this->offsetGet($name) : $this->items;

        if (!is_array($element)) {
            return [$name, $element];
        }

        if (strlen($separator) == 2 && in_array($separator,
['][', ')(', '}{'])) {
            $separator = [$separator[1], $separator[0]];
        }

        return $this->flattenNested('', $element, $separator,
$prefix);
    }

    /**
     * @param string $name
     * @param array  $element
     * @param string $separator
     * @param string $prefix
     * @return array
     * @internal
     */
    protected function flattenNested($name, &$element, $separator,
$prefix)
    {
        $list = [];
        foreach ($element as $key => $value) {
            $new = $name ? $name : $prefix;
            if (is_array($separator)) {
                $new .= $separator[0] . $key . $separator[1];
            } else {
                $new .= ($new ? $separator : '') . $key;
            }
            if (!is_array($value) || empty($value)) {
                $list[$new] = $value;
            } else {
                $list += $this->flattenNested($new, $value, $separator,
$prefix);
            }
        }

        return $list;
    }

    /**
     * Return blueprint.
     *
     * @return BlueprintSchema|BlueprintForm
     * @since 5.4.7
     */
    public function blueprint()
    {
        if (!$this->blueprint){
            $this->blueprint = new BlueprintSchema;
        } elseif (is_callable($this->blueprint)) {
            // Lazy load blueprints.
            $blueprint = $this->blueprint;
            $this->blueprint = $blueprint();
        }
        return $this->blueprint;
    }

    /**
     * Return blueprints.
     *
     * @return BlueprintSchema
     * @deprecated 5.4.7
     */
    public function blueprints()
    {
        return $this->blueprint();
    }

    /**
     * Count items in nested array.
     *
     * @param string $path
     * @param string $separator
     * @return int
     */
    public function count($path = null, $separator = '.')
    {
        $items = $path ? $this->get($path, null, $separator) :
$this->items;

        return is_array($items) ? count($items) : 0;
    }
}
PKK[�[ҽ`\!\!Config/ConfigFileFinder.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

use Gantry\Component\Filesystem\Folder;

/**
 * The Configuration & Blueprints Finder class.
 */
class ConfigFileFinder
{
    protected $base = '';

    /**
     * @param string $base
     * @return $this
     */
    public function setBase($base)
    {
        $this->base = $base ? "{$base}/" : '';

        return $this;
    }

    /**
     * Return all locations for all the files with a timestamp.
     *
     * @param  array  $paths    List of folders to look from.
     * @param  string $pattern  Pattern to match the file. Pattern will
also be removed from the key.
     * @param  int    $levels   Maximum number of recursive directories.
     * @return array
     */
    public function locateFiles(array $paths, $pattern =
'|\.yaml$|', $levels = -1)
    {
        $list = [];
        foreach ($paths as $folder) {
            $list += $this->detectRecursive($folder, $pattern, $levels);
        }
        return $list;
    }

    /**
     * Return all locations for all the files with a timestamp.
     *
     * @param  array  $paths    List of folders to look from.
     * @param  string $pattern  Pattern to match the file. Pattern will
also be removed from the key.
     * @param  int    $levels   Maximum number of recursive directories.
     * @return array
     */
    public function getFiles(array $paths, $pattern =
'|\.yaml$|', $levels = -1)
    {
        $list = [];
        foreach ($paths as $folder) {
            $path = trim(Folder::getRelativePath($folder), '/');

            $files = $this->detectRecursive($folder, $pattern, $levels);

            $list += $files[trim($path, '/')];
        }
        return $list;
    }

    /**
     * Return all paths for all the files with a timestamp.
     *
     * @param  array  $paths    List of folders to look from.
     * @param  string $pattern  Pattern to match the file. Pattern will
also be removed from the key.
     * @param  int    $levels   Maximum number of recursive directories.
     * @return array
     */
    public function listFiles(array $paths, $pattern =
'|\.yaml$|', $levels = -1)
    {
        $list = [];
        foreach ($paths as $folder) {
            $list = array_merge_recursive($list,
$this->detectAll($folder, $pattern, $levels));
        }
        return $list;
    }

    /**
     * Find filename from a list of folders.
     *
     * Note: Only finds the last override.
     *
     * @param string $filename
     * @param array $folders
     * @return array
     */
    public function locateFileInFolder($filename, array $folders)
    {
        $list = [];
        foreach ($folders as $folder) {
            $list += $this->detectInFolder($folder, $filename);
        }
        return $list;
    }

    /**
     * Find filename from a list of folders.
     *
     * @param array $folders
     * @param string $filename
     * @return array
     */
    public function locateInFolders(array $folders, $filename = null)
    {
        $list = [];
        foreach ($folders as $folder) {
            $path = trim(Folder::getRelativePath($folder), '/');
            $list[$path] = $this->detectInFolder($folder, $filename);
        }
        return $list;
    }

    /**
     * Return all existing locations for a single file with a timestamp.
     *
     * @param  array  $paths   Filesystem paths to look up from.
     * @param  string $name    Configuration file to be located.
     * @param  string $ext     File extension (optional, defaults to
.yaml).
     * @return array
     */
    public function locateFile(array $paths, $name, $ext =
'.yaml')
    {
        $filename = preg_replace('|[.\/]+|', '/',
$name) . $ext;

        $list = [];
        foreach ($paths as $folder) {
            $path = trim(Folder::getRelativePath($folder), '/');

            if (is_file("{$folder}/{$filename}")) {
                $modified = filemtime("{$folder}/{$filename}");
            } else {
                $modified = 0;
            }
            $basename = $this->base . $name;
            $list[$path] = [$basename => ['file' =>
"{$path}/{$filename}", 'modified' => $modified]];
        }

        return $list;
    }

    /**
     * Detects all directories with a configuration file and returns them
with last modification time.
     *
     * @param  string $folder   Location to look up from.
     * @param  string $pattern  Pattern to match the file. Pattern will
also be removed from the key.
     * @param  int    $levels   Maximum number of recursive directories.
     * @return array
     * @internal
     */
    protected function detectRecursive($folder, $pattern, $levels)
    {
        $path = trim(Folder::getRelativePath($folder), '/');

        if (is_dir($folder)) {
            // Find all system and user configuration files.
            $options = [
                'levels'  => $levels,
                'compare' => 'Filename',
                'pattern' => $pattern,
                'filters' => [
                    'pre-key' => $this->base,
                    'key' => $pattern,
                    'value' => function
(\RecursiveDirectoryIterator $file) use ($path) {
                        return ['file' =>
"{$path}/{$file->getSubPathname()}", 'modified'
=> $file->getMTime()];
                    }
                ],
                'key' => 'SubPathname'
            ];

            $list = Folder::all($folder, $options);

            ksort($list);
        } else {
            $list = [];
        }

        return [$path => $list];
    }

    /**
     * Detects all directories with the lookup file and returns them with
last modification time.
     *
     * @param  string $folder Location to look up from.
     * @param  string $lookup Filename to be located (defaults to directory
name).
     * @return array
     * @internal
     */
    protected function detectInFolder($folder, $lookup = null)
    {
        $folder = rtrim($folder, '/');
        $path = trim(Folder::getRelativePath($folder), '/');
        $base = $path === $folder ? '' : ($path ? substr($folder,
0, -strlen($path)) : $folder . '/');

        $list = [];

        if (is_dir($folder)) {
            $iterator = new \DirectoryIterator($folder);

            /** @var \DirectoryIterator $directory */
            foreach ($iterator as $directory) {
                if (!$directory->isDir() || $directory->isDot()) {
                    continue;
                }

                $name = $directory->getBasename();
                $find = ($lookup ?: $name) . '.yaml';
                $filename = "{$path}/{$name}/{$find}";

                if (file_exists($base . $filename)) {
                    $basename = $this->base . $name;
                    $list[$basename] = ['file' => $filename,
'modified' => filemtime($base . $filename)];
                }
            }
        }

        return $list;
    }

    /**
     * Detects all plugins with a configuration file and returns them with
last modification time.
     *
     * @param  string $folder   Location to look up from.
     * @param  string $pattern  Pattern to match the file. Pattern will
also be removed from the key.
     * @param  int    $levels   Maximum number of recursive directories.
     * @return array
     * @internal
     */
    protected function detectAll($folder, $pattern, $levels)
    {
        $path = trim(Folder::getRelativePath($folder), '/');

        if (is_dir($folder)) {
            // Find all system and user configuration files.
            $options = [
                'levels'  => $levels,
                'compare' => 'Filename',
                'pattern' => $pattern,
                'filters' => [
                    'pre-key' => $this->base,
                    'key' => $pattern,
                    'value' => function
(\RecursiveDirectoryIterator $file) use ($path) {
                        return
["{$path}/{$file->getSubPathname()}" =>
$file->getMTime()];
                    }
                ],
                'key' => 'SubPathname'
            ];

            $list = Folder::all($folder, $options);

            ksort($list);
        } else {
            $list = [];
        }

        return $list;
    }
}
PKK[�[��VVConfig/Validation.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Yaml;

/**
 * Data validation.
 *
 * @author RocketTheme
 * @license MIT
 */
class Validation
{
    /**
     * Validate value against a blueprint field definition.
     *
     * @param $value
     * @param array $field
     * @return array
     */
    public static function validate($value, array $field)
    {
        $messages = [];

        $validate = isset($field['validate']) ? (array)
$field['validate'] : [];

        // If value isn't required, we will stop validation if empty
value is given.
        if (empty($validate['required']) && ($value ===
null || $value === '')) {
            return $messages;
        }

        if (!isset($field['type'])) {
            $field['type'] = 'input.text';
        }

        // Special case for files, value is never empty and errors with
code 4 instead.
        if (empty($validate['required']) &&
$field['type'] === 'input.file' &&
isset($value['error'])
                && ($value['error'] ===
UPLOAD_ERR_NO_FILE || \in_array(UPLOAD_ERR_NO_FILE,
$value['error'], true))) {
            return $messages;
        }

        // Validate type with fallback type text.
        $type = (string)
isset($field['validate']['type']) ?
$field['validate']['type'] : $field['type'];
        $method = 'type_'.strtr($type, '-.',
'__');

        if (!method_exists(__CLASS__, $method)) {
            $method = 'type_Input_Text';
        }

        $name = ucfirst(isset($field['label']) ?
$field['label'] : $field['name']);
        // TODO: translate
        $message = (string)
isset($field['validate']['message'])
            ? sprintf($field['validate']['message'])
            : sprintf('Invalid input in field: ') . '
"' . $name . '"';

        $success = self::$method($value, $validate, $field);

        if (!$success) {
            $messages[$field['name']][] = $message;
        }

        // Check individual rules.
        foreach ($validate as $rule => $params) {
            $method = 'validate_' . ucfirst(strtr($rule,
'-.', '__'));

            if (method_exists(__CLASS__, $method)) {
                $success = self::$method($value, $params);

                if (!$success) {
                    $messages[$field['name']][] = $message;
                }
            }
        }

        return $messages;
    }

    /**
     * Filter value against a blueprint field definition.
     *
     * @param  mixed  $value
     * @param  array  $field
     * @return mixed  Filtered value.
     */
    public static function filter($value, array $field)
    {
        $validate = isset($field['validate']) ? (array)
$field['validate'] : [];

        // If value isn't required, we will return null if empty value
is given.
        if (($value === null || $value === '') &&
empty($validate['required'])) {
            return null;
        }

        if (!isset($field['type'])) {
            $field['type'] = 'input.text';
        }

        // Special case for files, value is never empty and errors with
code 4 instead.
        if (empty($validate['required']) &&
$field['type'] === 'input.file' &&
isset($value['error'])
            && ($value['error'] === UPLOAD_ERR_NO_FILE ||
\in_array(UPLOAD_ERR_NO_FILE, $value['error'], true))) {
            return null;
        }

        // Validate type with fallback type text.
        $type = (string)
isset($field['validate']['type']) ?
$field['validate']['type'] : $field['type'];
        $method = 'filter_' . ucfirst(str_replace('-',
'_', $type));

        if (!method_exists(__CLASS__, $method)) {
            $method = 'filter_Input_Text';
        }

        return self::$method($value, $validate, $field);
    }

    /**
     * HTML5 input: text
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Text($value, array $params, array
$field)
    {
        if (!\is_string($value) && !is_numeric($value)) {
            return false;
        }

        $value = (string)$value;

        if (isset($params['min']) && \strlen($value) <
$params['min']) {
            return false;
        }

        if (isset($params['max']) && \strlen($value) >
$params['max']) {
            return false;
        }

        $min = isset($params['min']) ? $params['min'] :
0;
        if (isset($params['step']) && (strlen($value) -
$min) % $params['step'] === 0) {
            return false;
        }

        if ((!isset($params['multiline']) ||
!$params['multiline']) && preg_match('/\R/um',
$value)) {
            return false;
        }

        return true;
    }

    protected static function filter_Input_Text($value, array $params,
array $field)
    {
        return (string) $value;
    }

    protected static function filter_Input_CommaList($value, array $params,
array $field)
    {
        return \is_array($value) ? $value :
preg_split('/\s*,\s*/', $value, -1, PREG_SPLIT_NO_EMPTY);
    }

    protected static function type_Input_CommaList($value, array $params,
array $field)
    {
        return \is_array($value) ? true : self::type_Input_Text($value,
$params, $field);
    }

    /**
     * HTML5 input: textarea
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Textarea_Textarea($value, array $params,
array $field)
    {
        if (!isset($params['multiline'])) {
            $params['multiline'] = true;
        }

        return self::type_Input_Text($value, $params, $field);
    }

    /**
     * HTML5 input: password
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Password($value, array $params, array
$field)
    {
        return self::type_Input_Text($value, $params, $field);
    }

    /**
     * HTML5 input: hidden
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Hidden($value, array $params, array
$field)
    {
        return self::type_Input_Text($value, $params, $field);
    }

    /**
     * Custom input: checkbox list
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Checkboxes_Checkboxes($value, array
$params, array $field)
    {
        // Set multiple: true so checkboxes can easily use min/max counts
to control number of options required
        $field['multiple'] = true;

        return self::typeArray((array) $value, $params, $field);
    }

    protected static function filter_Checkboxes_Checkboxes($value, array
$params, array $field)
    {
        return self::filterArray($value, $params, $field);
    }

    /**
     * HTML5 input: checkbox
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Checkbox($value, array $params, array
$field)
    {
        $value = (string) $value;

        if (!isset($field['value'])) {
            $field['value'] = 1;
        }

        if ($value && $value != $field['value']) {
            return false;
        }

        return true;
    }

    /**
     * HTML5 input: radio
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Radio($value, array $params, array
$field)
    {
        return self::typeArray((array) $value, $params, $field);
    }

    /**
     * Custom input: toggle
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Toggle_Toggle($value, array $params, array
$field)
    {
        return self::typeArray((array) $value, $params, $field);
    }

    /**
     * Custom input: file
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_File($value, array $params, array
$field)
    {
        return self::typeArray((array) $value, $params, $field);
    }

    protected static function filter_Input_File($value, array $params,
array $field)
    {
        if (isset($field['multiple']) &&
$field['multiple'] === true) {
            return (array) $value;
        }

        if (\is_array($value)) {
            return reset($value);
        }

        return $value;
    }

    /**
     * HTML5 input: select
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Select_Select($value, array $params, array
$field)
    {
        return self::typeArray((array) $value, $params, $field);
    }

    /**
     * HTML5 input: number
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Number($value, array $params, array
$field)
    {
        if (!is_numeric($value)) {
            return false;
        }

        if (isset($params['min']) && $value <
$params['min']) {
            return false;
        }

        if (isset($params['max']) && $value >
$params['max']) {
            return false;
        }

        $min = isset($params['min']) ? $params['min'] :
0;

        return !(isset($params['step']) && fmod($value -
$min, $params['step']) === 0);
    }

    protected static function filter_Input_Number($value, array $params,
array $field)
    {
        return (string)(int)$value !== (string)(float)$value ? (float)
$value : (int) $value;
    }

    protected static function filter_Input_DateTime($value, array $params,
array $field)
    {
        $converted = new \DateTime($value);
        return $converted->format('Y-m-d H:i:s');
    }


    /**
     * HTML5 input: range
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Range($value, array $params, array
$field)
    {
        return self::type_Input_Number($value, $params, $field);
    }

    protected static function filter_Input_Range($value, array $params,
array $field)
    {
        return self::filter_Input_Number($value, $params, $field);
    }

    /**
     * HTML5 input: color
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Color($value, array $params, array
$field)
    {
        return preg_match('/^\#[0-9a-fA-F]{3}[0-9a-fA-F]{3}?$/u',
$value);
    }

    /**
     * HTML5 input: email
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Email($value, array $params, array
$field)
    {
        return self::type_Input_Text($value, $params, $field) &&
filter_var($value, FILTER_VALIDATE_EMAIL);
    }

    /**
     * HTML5 input: url
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */

    public static function type_Input_Url($value, array $params, array
$field)
    {
        return self::type_Input_Text($value, $params, $field) &&
filter_var($value, FILTER_VALIDATE_URL);
    }

    /**
     * HTML5 input: datetime
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Datetime($value, array $params, array
$field)
    {
        if ($value instanceof \DateTime) {
            return true;
        }
        if (!\is_string($value)) {
            return false;
        }
        if (!isset($params['format'])) {
            return false !== strtotime($value);
        }

        $dateFromFormat =
\DateTime::createFromFormat($params['format'], $value);

        return $dateFromFormat && $value ===
date($params['format'], $dateFromFormat->getTimestamp());
    }

    /**
     * HTML5 input: datetime-local
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_DatetimeLocal($value, array $params,
array $field)
    {
        return self::type_Input_Datetime($value, $params, $field);
    }

    /**
     * HTML5 input: date
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Date($value, array $params, array
$field)
    {
        if (!isset($params['format'])) {
            $params['format'] = 'Y-m-d';
        }
        return self::type_Input_Datetime($value, $params, $field);
    }

    /**
     * HTML5 input: time
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Time($value, array $params, array
$field)
    {
        if (!isset($params['format'])) {
            $params['format'] = 'H:i';
        }
        return self::type_Input_Datetime($value, $params, $field);
    }

    /**
     * HTML5 input: month
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Month($value, array $params, array
$field)
    {
        if (!isset($params['format'])) {
            $params['format'] = 'Y-m';
        }
        return self::type_Input_Datetime($value, $params, $field);
    }

    /**
     * HTML5 input: week
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Week($value, array $params, array
$field)
    {
        if (!isset($params['format']) &&
!preg_match('/^\d{4}-W\d{2}$/u', $value)) {
            return false;
        }
        return self::type_Input_Datetime($value, $params, $field);
    }

    /**
     * Custom input: array
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function typeArray($value, array $params, array $field)
    {
        if (!\is_array($value)) {
            return false;
        }

        if (isset($field['multiple'])) {
            if (isset($params['min']) && \count($value)
< $params['min']) {
                return false;
            }

            if (isset($params['max']) && \count($value)
> $params['max']) {
                return false;
            }

            $min = isset($params['min']) ?
$params['min'] : 0;
            if (isset($params['step']) && (\count($value)
- $min) % $params['step'] === 0) {
                return false;
            }
        }

        $options = isset($field['options']) ?
array_keys($field['options']) : [];
        $values = isset($field['use']) &&
$field['use'] === 'keys' ? array_keys($value) : $value;

        return !($options && array_diff($values, $options));
    }

    protected static function filterArray($value, $params, $field)
    {
        $values = (array) $value;
        $options = isset($field['options']) ?
array_keys($field['options']) : [];
        $multi = isset($field['multiple']) ?
$field['multiple'] : false;

        if (\count($values) === 1 && isset($values[0]) &&
$values[0] === '') {
            return null;
        }

        if ($options) {
            $useKey = isset($field['use']) &&
$field['use'] === 'keys';
            foreach ($values as $key => $val) {
                $values[$key] = $useKey ? (bool) $val : $val;
            }
        }

        if ($multi) {
            foreach ($values as $key => $val) {
                if (\is_array($val)) {
                    $val = implode(',', $val);
                }

                $values[$key] =  array_map('trim',
explode(',', $val));
            }
        }

        return $values;
    }

    public static function type_Input_Yaml($value, $params)
    {
        try {
            Yaml::parse($value);
            return true;
        } catch (ParseException $e) {
            return false;
        }
    }

    public static function filter_Input_Yaml($value, $params)
    {
        try {
            return (array) Yaml::parse($value);
        } catch (ParseException $e) {
            return null;
        }
    }

    /**
     * Custom input: ignore (will not validate)
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Novalidate($value, array $params, array
$field)
    {
        return true;
    }

    public static function filter_Novalidate($value, array $params, array
$field)
    {
        return $value;
    }

    // HTML5 attributes (min, max and range are handled inside the types)

    public static function validate_Required($value, $params)
    {
        if (is_scalar($value)) {
            return (bool) $params !== true || $value !== '';
        }

        return (bool) $params !== true || !empty($value);
    }

    public static function validate_Pattern($value, $params)
    {
        return (bool) preg_match("`^{$params}$`u", $value);
    }


    // Internal types

    public static function validate_Alpha($value, $params)
    {
        return ctype_alpha($value);
    }

    public static function validate_Alnum($value, $params)
    {
        return ctype_alnum($value);
    }

    public static function type_Bool($value, $params)
    {
        return \is_bool($value) || $value == 1 || $value == 0;
    }

    public static function validate_Bool($value, $params)
    {
        return \is_bool($value) || $value == 1 || $value == 0;
    }

    protected static function filter_Bool($value, $params)
    {
        return (bool) $value;
    }

    public static function validate_Digit($value, $params)
    {
        return ctype_digit($value);
    }

    public static function validate_Float($value, $params)
    {
        return \is_float(filter_var($value, FILTER_VALIDATE_FLOAT));
    }

    protected static function filter_Float($value, $params)
    {
        return (float) $value;
    }

    public static function validate_Hex($value, $params)
    {
        return ctype_xdigit($value);
    }

    public static function validate_Int($value, $params)
    {
        return is_numeric($value) && (int) $value == $value;
    }

    protected static function filter_Int($value, $params)
    {
        return (int) $value;
    }

    public static function validate_Array($value, $params)
    {
        return \is_array($value)
            || ($value instanceof \ArrayAccess
                && $value instanceof \Traversable
                && $value instanceof \Countable);
    }

    public static function validate_Json($value, $params)
    {
        return (bool) (@json_decode($value));
    }
}
PKK[�[��"Config/ValidationException.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

class ValidationException extends \RuntimeException
{
    protected $messages = [];

    public function setMessages(array $messages = []) {
        $this->messages = $messages;

        // TODO: add translation.
        $this->message = sprintf('Form validation failed:') .
' ' . $this->message;

        foreach ($messages as $variable => &$list) {
            $list = array_unique($list);
            foreach ($list as $message) {
                $this->message .= "<br/>$message";
            }
        }

        return $this;
    }

    public function getMessages()
    {
        return $this->messages;
    }
}
PKM[�[#NJ���Content/Block/ContentBlock.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Content\Block;

/**
 * Class to create nested blocks of content.
 *
 * $innerBlock = ContentBlock::create();
 * $innerBlock->setContent('my inner content');
 * $outerBlock = ContentBlock::create();
 * $outerBlock->setContent(sprintf('Inside my outer block I have
%s.', $innerBlock->getToken()));
 * $outerBlock->addBlock($innerBlock);
 * echo $outerBlock;
 *
 * @package Gantry\Component\Content\Block
 * @since 5.4.3
 */
class ContentBlock implements ContentBlockInterface
{
    protected $version = 1;
    protected $id;
    protected $tokenTemplate = '@@BLOCK-%s@@';
    protected $content = '';
    protected $blocks = [];

    /**
     * @param string $id
     * @return static
     * @since 5.4.3
     */
    public static function create($id = null)
    {
        return new static($id);
    }

    /**
     * @param array $serialized
     * @return ContentBlockInterface
     * @since 5.4.3
     */
    public static function fromArray(array $serialized)
    {
        try {
            $type = isset($serialized['_type']) ?
$serialized['_type'] : null;
            $id = isset($serialized['id']) ?
$serialized['id'] : null;

            if (!$type || !$id || !is_a($type,
'Gantry\Component\Content\Block\ContentBlockInterface', true)) {
                throw new \RuntimeException('Bad data');
            }

            /** @var ContentBlockInterface $instance */
            $instance = new $type($id);
            $instance->build($serialized);
        } catch (\Exception $e) {
            throw new \RuntimeException(sprintf('Cannot unserialize
Block: %s', $e->getMessage()), $e->getCode(), $e);
        }

        return $instance;
    }

    /**
     * Block constructor.
     *
     * @param string $id
     * @since 5.4.3
     */
    public function __construct($id = null)
    {
        $this->id = $id ? (string) $id : $this->generateId();
    }

    /**
     * @return string
     * @since 5.4.3
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @return string
     * @since 5.4.3
     */
    public function getToken()
    {
        return sprintf($this->tokenTemplate, $this->getId());
    }

    /**
     * @return array
     * @since 5.4.3
     */
    public function toArray()
    {
        $blocks = [];
        /**
         * @var string $id
         * @var ContentBlockInterface $block
         */
        foreach ($this->blocks as $block) {
            $blocks[$block->getId()] = $block->toArray();
        }

        $array = [
            '_type' => get_class($this),
            '_version' => $this->version,
            'id' => $this->id,
        ];

        if ($this->content) {
            $array['content'] = $this->content;
        }

        if ($blocks) {
            $array['blocks'] = $blocks;
        }

        return $array;
    }

    /**
     * @return string
     * @since 5.4.3
     */
    public function toString()
    {
        if (!$this->blocks) {
            return (string) $this->content;
        }

        $tokens = [];
        $replacements = [];
        foreach ($this->blocks as $block) {
            $tokens[] = $block->getToken();
            $replacements[] = $block->toString();
        }

        return str_replace($tokens, $replacements, (string)
$this->content);
    }

    /**
     * @return string
     * @since 5.4.3
     */
    public function __toString()
    {
        try {
            return $this->toString();
        } catch (\Exception $e) {
            return sprintf('Error while rendering block: %s',
$e->getMessage());
        }
    }

    /**
     * @param array $serialized
     * @since 5.4.3
     */
    public function build(array $serialized)
    {
        $this->checkVersion($serialized);

        $this->id = isset($serialized['id']) ?
$serialized['id'] : $this->generateId();

        if (isset($serialized['content'])) {
            $this->setContent($serialized['content']);
        }

        $blocks = isset($serialized['blocks']) ? (array)
$serialized['blocks'] : [];
        foreach ($blocks as $block) {
            $this->addBlock(self::fromArray($block));
        }
    }

    /**
     * @param string $content
     * @return $this
     * @since 5.4.3
     */
    public function setContent($content)
    {
        $this->content = $content;

        return $this;
    }

    /**
     * @param ContentBlockInterface $block
     * @return $this
     * @since 5.4.3
     */
    public function addBlock(ContentBlockInterface $block)
    {
        $this->blocks[$block->getId()] = $block;

        return $this;
    }

    /**
     * @return string
     * @since 5.4.3
     */
    public function serialize()
    {
        return serialize($this->toArray());
    }

    /**
     * @param string $serialized
     * @since 5.4.3
     */
    public function unserialize($serialized)
    {
        $array = unserialize($serialized);
        $this->build($array);
    }

    /**
     * @return string
     * @since 5.4.3
     */
    protected function generateId()
    {
        return uniqid('', true);
    }

    /**
     * @param array $serialized
     * @throws \RuntimeException
     * @since 5.4.3
     */
    protected function checkVersion(array $serialized)
    {
        $version = isset($serialized['_version']) ? (string)
$serialized['_version'] : 1;
        if ($version != $this->version) {
            throw new \RuntimeException(sprintf('Unsupported version
%s', $version));
        }
    }
}PKM[�[{�?:xx'Content/Block/ContentBlockInterface.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Content\Block;

/**
 * @since 5.4.3
 */
interface ContentBlockInterface extends \Serializable
{
    public static function create($id = null);
    public static function fromArray(array $serialized);

    public function __construct($id = null);

    public function getId();
    public function getToken();

    public function toArray();
    public function build(array $serialized);

    public function setContent($content);
    public function addBlock(ContentBlockInterface $block);
}PKM[�[��ue�2�2Content/Block/HtmlBlock.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Content\Block;

use Gantry\Framework\Document;
use Gantry\Framework\Gantry;
use Gantry\Framework\Theme;

/**
 * Class HtmlBlock
 * @package Gantry\Component\Content\Block
 * @since 5.4.3
 */
class HtmlBlock extends ContentBlock implements HtmlBlockInterface
{
    protected $version = 1;
    protected $frameworks = [];
    protected $styles = [];
    protected $scripts = [];
    protected $html = [];

    /**
     * @return array
     * @since 5.4.3
     */
    public function getAssets()
    {
        $assets = $this->getAssetsFast();

        $this->sortAssets($assets['styles']);
        $this->sortAssets($assets['scripts']);
        $this->sortAssets($assets['html']);

        return $assets;
    }

    /**
     * @return array
     * @since 5.4.3
     */
    public function getFrameworks()
    {
        $assets = $this->getAssetsFast();

        return array_keys($assets['frameworks']);
    }

    /**
     * @param string $location
     * @return array
     * @since 5.4.3
     */
    public function getStyles($location = 'head')
    {
        $styles = $this->getAssetsInLocation('styles',
$location);

        if (!$styles) {
            return [];
        }

        $gantry = Gantry::instance();

        /** @var Theme $theme */
        $theme = isset($gantry['theme']) ?
$gantry['theme'] : null;

        /** @var Document $document */
        $document = $gantry['document'];

        foreach ($styles as $key => $style) {
            if (isset($style['href'])) {
                $url = $style['href'];
                if ($theme && preg_match('|\.scss$|',
$url)) {
                    // Compile SCSS files.
                    $url = $theme->css(basename($url,
'.scss'));
                }
                // Deal with streams and relative paths.
                $url = $document->url($url, false, null, false);

                $styles[$key]['href'] = $url;
            }
        }

        return $styles;
    }

    /**
     * @param string $location
     * @return array
     * @since 5.4.3
     */
    public function getScripts($location = 'head')
    {
        $scripts = $this->getAssetsInLocation('scripts',
$location);

        if (!$scripts) {
            return [];
        }

        $gantry = Gantry::instance();

        /** @var Document $document */
        $document = $gantry['document'];

        foreach ($scripts as $key => $script) {
            if (isset($script['src'])) {
                // Deal with streams and relative paths.
                $scripts[$key]['src'] =
$document->url($script['src'], false, null, false);
            }
        }

        return $scripts;
    }

    /**
     * @param string $location
     * @return array
     * @since 5.4.3
     */
    public function getHtml($location = 'bottom')
    {
        return $this->getAssetsInLocation('html', $location);
    }

    /**
     * @return array
     * @since 5.4.3
     */
    public function toArray()
    {
        $array = parent::toArray();

        if ($this->frameworks) {
            $array['frameworks'] = $this->frameworks;
        }
        if ($this->styles) {
            $array['styles'] = $this->styles;
        }
        if ($this->scripts) {
            $array['scripts'] = $this->scripts;
        }
        if ($this->html) {
            $array['html'] = $this->html;
        }

        return $array;
    }

    /**
     * @param array $serialized
     * @since 5.4.3
     */
    public function build(array $serialized)
    {
        parent::build($serialized);

        $this->frameworks = isset($serialized['frameworks']) ?
(array) $serialized['frameworks'] : [];
        $this->styles = isset($serialized['styles']) ? (array)
$serialized['styles'] : [];
        $this->scripts = isset($serialized['scripts']) ?
(array) $serialized['scripts'] : [];
        $this->html = isset($serialized['html']) ? (array)
$serialized['html'] : [];
    }

    /**
     * @param string $framework
     * @return $this
     * @since 5.4.3
     */
    public function addFramework($framework)
    {
        $this->frameworks[$framework] = 1;

        return $this;
    }

    /**
     * @param string|array $element
     * @param int $priority
     * @param string $location
     * @return bool
     *
     * @example $block->addStyle('assets/js/my.js');
     * @example $block->addStyle(['href' =>
'assets/js/my.js', 'media' => 'screen']);
     * @since 5.4.3
     */
    public function addStyle($element, $priority = 0, $location =
'head')
    {
        if (!is_array($element)) {
            $element = ['href' => (string) $element];
        }
        if (empty($element['href'])) {
            return false;
        }
        if (!isset($this->styles[$location])) {
            $this->styles[$location] = [];
        }

        $id = !empty($element['id']) ? ['id' =>
(string) $element['id']] : [];
        $href = $element['href'];
        $type = !empty($element['type']) ? (string)
$element['type'] : 'text/css';
        $media = !empty($element['media']) ? (string)
$element['media'] : null;
        unset($element['tag'], $element['id'],
$element['rel'], $element['content'],
$element['href'], $element['type'],
$element['media']);

        $this->styles[$location][md5($href) . sha1($href)] = [
                ':type' => 'file',
                ':priority' => (int) $priority,
                'href' => $href,
                'type' => $type,
                'media' => $media,
                'element' => $element
            ] + $id;

        return true;
    }

    /**
     * @param string|array $element
     * @param int $priority
     * @param string $location
     * @return bool
     * @since 5.4.3
     */
    public function addInlineStyle($element, $priority = 0, $location =
'head')
    {
        if (!is_array($element)) {
            $element = ['content' => (string) $element];
        }
        if (empty($element['content'])) {
            return false;
        }
        if (!isset($this->styles[$location])) {
            $this->styles[$location] = [];
        }

        $content = (string) $element['content'];
        $type = !empty($element['type']) ? (string)
$element['type'] : 'text/css';

        $this->styles[$location][md5($content) . sha1($content)] = [
            ':type' => 'inline',
            ':priority' => (int) $priority,
            'content' => $content,
            'type' => $type
        ];

        return true;
    }

    /**
     * @param string|array $element
     * @param int $priority
     * @param string $location
     * @return bool
     * @since 5.4.3
     */
    public function addScript($element, $priority = 0, $location =
'head')
    {
        if (!is_array($element)) {
            $element = ['src' => (string) $element];
        }
        if (empty($element['src'])) {
            return false;
        }
        if (!isset($this->scripts[$location])) {
            $this->scripts[$location] = [];
        }

        $src = $element['src'];
        $type = !empty($element['type']) ? (string)
$element['type'] : 'text/javascript';
        $defer = isset($element['defer']) ? true : false;
        $async = isset($element['async']) ? true : false;
        $handle = !empty($element['handle']) ? (string)
$element['handle'] : '';

        $this->scripts[$location][md5($src) . sha1($src)] = [
            ':type' => 'file',
            ':priority' => (int) $priority,
            'src' => $src,
            'type' => $type,
            'defer' => $defer,
            'async' => $async,
            'handle' => $handle
        ];

        return true;
    }

    /**
     * @param string|array $element
     * @param int $priority
     * @param string $location
     * @return bool
     * @since 5.4.3
     */
    public function addInlineScript($element, $priority = 0, $location =
'head')
    {
        if (!is_array($element)) {
            $element = ['content' => (string) $element];
        }
        if (empty($element['content'])) {
            return false;
        }
        if (!isset($this->scripts[$location])) {
            $this->scripts[$location] = [];
        }

        $content = (string) $element['content'];
        $type = !empty($element['type']) ? (string)
$element['type'] : 'text/javascript';

        $this->scripts[$location][md5($content) . sha1($content)] = [
            ':type' => 'inline',
            ':priority' => (int) $priority,
            'content' => $content,
            'type' => $type
        ];

        return true;
    }

    /**
     * @param string $html
     * @param int $priority
     * @param string $location
     * @return bool
     * @since 5.4.3
     */
    public function addHtml($html, $priority = 0, $location =
'bottom')
    {
        if (empty($html) || !is_string($html)) {
            return false;
        }
        if (!isset($this->html[$location])) {
            $this->html[$location] = [];
        }

        $this->html[$location][md5($html) . sha1($html)] = [
            ':priority' => (int) $priority,
            'html' => $html
        ];

        return true;
    }

    /**
     * @param string $location
     * @deprecated Temporarily needed in WP
     * @since 5.4.3
     */
    public function clearStyles($location = 'head')
    {
        foreach ($this->blocks as $block) {
            if (method_exists($block, 'clearStyles')) {
                $block->clearStyles($location);
            }
        }
        unset($this->styles[$location]);
    }

    /**
     * @param string $location
     * @deprecated Temporarily needed in WP
     * @since 5.4.3
     */
    public function clearScripts($location = 'head')
    {
        foreach ($this->blocks as $block) {
            if (method_exists($block, 'clearScripts')) {
                $block->clearScripts($location);
            }
        }
        unset($this->scripts[$location]);
    }

    /**
     * @return array
     * @since 5.4.3
     */
    protected function getAssetsFast()
    {
        $assets = [
            'frameworks' => $this->frameworks,
            'styles' => $this->styles,
            'scripts' => $this->scripts,
            'html' => $this->html
        ];

        foreach ($this->blocks as $block) {
            if ($block instanceof HtmlBlock) {
                $blockAssets = $block->getAssetsFast();
                $assets['frameworks'] +=
$blockAssets['frameworks'];

                foreach ($blockAssets['styles'] as $location
=> $styles) {
                    if (!isset($assets['styles'][$location])) {
                        $assets['styles'][$location] = $styles;
                    } elseif ($styles) {
                        $assets['styles'][$location] += $styles;
                    }
                }

                foreach ($blockAssets['scripts'] as $location
=> $scripts) {
                    if (!isset($assets['scripts'][$location])) {
                        $assets['scripts'][$location] = $scripts;
                    } elseif ($scripts) {
                        $assets['scripts'][$location] +=
$scripts;
                    }
                }

                foreach ($blockAssets['html'] as $location =>
$htmls) {
                    if (!isset($assets['html'][$location])) {
                        $assets['html'][$location] = $htmls;
                    } elseif ($htmls) {
                        $assets['html'][$location] += $htmls;
                    }
                }
            }
        }

        return $assets;
    }

    /**
     * @param string $type
     * @param string $location
     * @return array
     * @since 5.4.3
     */
    protected function getAssetsInLocation($type, $location)
    {
        $assets = $this->getAssetsFast();

        if (empty($assets[$type][$location])) {
            return [];
        }

        $styles = $assets[$type][$location];
        $this->sortAssetsInLocation($styles);

        return $styles;
    }

    /**
     * @param array $items
     * @since 5.4.3
     */
    protected function sortAssetsInLocation(array &$items)
    {
        $count = 0;
        foreach ($items as &$item) {
            $item[':order'] = ++$count;
        }
        uasort(
            $items,
            function ($a, $b) {
                return ($a[':priority'] ==
$b[':priority']) ? $a[':order'] -
$b[':order'] : $b[':priority'] -
$a[':priority'];
            }
        );
    }

    /**
     * @param array $array
     * @since 5.4.3
     */
    protected function sortAssets(array &$array)
    {
        foreach ($array as $location => &$items) {
            $this->sortAssetsInLocation($items);
        }
    }
}PKM[�[�v*B��$Content/Block/HtmlBlockInterface.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Content\Block;

/**
 * @since 5.4.3
 */
interface HtmlBlockInterface extends ContentBlockInterface
{
    public function getAssets();

    public function addFramework($framework);
    public function addStyle($element, $priority = 0, $location =
'head');
    public function addInlineStyle($element, $priority = 0, $location =
'head');
    public function addScript($element, $priority = 0, $location =
'head');
    public function addInlineScript($element, $priority = 0, $location =
'head');
    public function addHtml($html, $priority = 0, $location =
'bottom');
}
PKM[�[	�Q��i�i!Content/Document/HtmlDocument.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Content\Document;

use Gantry\Component\Content\Block\ContentBlock;
use Gantry\Component\Content\Block\HtmlBlock;
use Gantry\Component\Gantry\GantryTrait;
use Gantry\Component\Url\Url;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class HtmlDocument
{
    use GantryTrait;

    public static $timestamp_age = 604800;
    public static $urlFilterParams;

    /**
     * @var array|HtmlBlock[]
     */
    protected static $stack;
    protected static $frameworks = [];
    protected static $scripts = [];
    protected static $styles = [];
    protected static $availableFrameworks = [
        'jquery' => 'registerJquery',
        'jquery.framework' => 'registerJquery',
        'jquery.ui.core' =>
'registerJqueryUiSortable',
        'jquery.ui.sortable' =>
'registerJqueryUiSortable',
        'bootstrap.2' => 'registerBootstrap2',
        'bootstrap.3' => 'registerBootstrap3',
        'mootools' => 'registerMootools',
        'mootools.framework' => 'registerMootools',
        'mootools.core' => 'registerMootools',
        'mootools.more' => 'registerMootoolsMore',
        'lightcase' => 'registerLightcase',
        'lightcase.init' => 'registerLightcaseInit',
    ];

    public function __construct()
    {
        static::$stack = [];
        static::push();
    }

    /**
     * Create new local instance of document allowing asset caching.
     */
    public static function push()
    {
        array_unshift(static::$stack, new HtmlBlock());
    }

    /**
     * Return local instance of document allowing it to be cached.
     *
     * @return HtmlBlock
     */
    public static function pop()
    {
        return array_shift(static::$stack);
    }

    /**
     * @param ContentBlock $block
     * @return $this
     */
    public function addBlock(ContentBlock $block)
    {
        static::$stack[0]->addBlock($block);

        return $this;
    }

    /**
     * @param string $framework
     * @return bool
     */
    public static function addFramework($framework)
    {
        if (!isset(static::$availableFrameworks[$framework])) {
            return false;
        }

        static::getObject();
        static::$stack[0]->addFramework($framework);

        return true;
    }

    /**
     * @param string|array $element
     * @param int $priority
     * @param string $location
     * @return bool
     */
    public static function addStyle($element, $priority = 0, $location =
'head')
    {
        static::getObject();
        return static::$stack[0]->addStyle($element, $priority,
$location);
    }

    /**
     * @param string|array $element
     * @param int $priority
     * @param string $location
     * @return bool
     */
    public static function addInlineStyle($element, $priority = 0,
$location = 'head')
    {
        static::getObject();
        return static::$stack[0]->addInlineStyle($element, $priority,
$location);
    }

    /**
     * @param string|array $element
     * @param int $priority
     * @param string $location
     * @return bool
     */
    public static function addScript($element, $priority = 0, $location =
'head')
    {
        static::getObject();
        return static::$stack[0]->addScript($element, $priority,
$location);
    }

    /**
     * @param string|array $element
     * @param int $priority
     * @param string $location
     * @return bool
     */
    public static function addInlineScript($element, $priority = 0,
$location = 'head')
    {
        static::getObject();
        return static::$stack[0]->addInlineScript($element, $priority,
$location);
    }

    /**
     * @param string $html
     * @param int $priority
     * @param string $location
     * @return bool
     */
    public static function addHtml($html, $priority = 0, $location =
'bottom')
    {
        static::getObject();
        return static::$stack[0]->addHtml($html, $priority, $location);
    }

    /**
     * @param array $element
     * @param string $location
     * @param int $priority
     * @return bool
     */
    public static function addHeaderTag(array $element, $location =
'head', $priority = 0)
    {
        $success = false;

        switch ($element['tag']) {
            case 'link':
                if (!empty($element['rel']) &&
$element['rel'] === 'stylesheet') {
                    $success = static::addStyle($element, $priority,
$location);
                }

                break;

            case 'style':
                $success = static::addInlineStyle($element, $priority,
$location);

                break;

            case 'script':
                if (!empty($element['src'])) {
                    $success = static::addScript($element, $priority,
$location);
                } elseif (!empty($element['content'])) {
                    $success = static::addInlineScript($element, $priority,
$location);
                }

                break;
        }

        return $success;
    }

    public static function getStyles($location = 'head')
    {
        static::getObject();
        $styles = static::$stack[0]->getStyles($location);

        $output = [];

        foreach ($styles as $style) {
            switch ($style[':type']) {
                case 'file':
                    $attribs = '';
                    if ($style['media']) {
                        $attribs .= ' media="' .
static::escape($style['media']) . '"';
                    }
                    $output[] = sprintf(
                        '<link rel="stylesheet"
href="%s" type="%s"%s />',
                        static::escape($style['href']),
                        static::escape($style['type']),
                        $attribs
                    );
                    break;
                case 'inline':
                    $attribs = '';
                    if ($style['type'] !== 'text/css')
{
                        $attribs .= ' type="' .
static::escape($style['type']) . '"';
                    }
                    $output[] = sprintf(
                        '<style%s>%s</style>',
                        $attribs,
                        $style['content']
                    );
                    break;
            }
        }

        return $output;
    }

    public static function getScripts($location = 'head')
    {
        static::getObject();
        $scripts = static::$stack[0]->getScripts($location);

        $output = [];

        foreach ($scripts as $script) {
            switch ($script[':type']) {
                case 'file':
                    $attribs = '';
                    if ($script['async']) {
                        $attribs .= ' async="async"';
                    }
                    if ($script['defer']) {
                        $attribs .= ' defer="defer"';
                    }
                    $output[] = sprintf(
                        '<script type="%s"%s
src="%s"></script>',
                        static::escape($script['type']),
                        $attribs,
                        static::escape($script['src'])
                    );
                    break;
                case 'inline':
                    $output[] = sprintf(
                        '<script
type="%s">%s</script>',
                        static::escape($script['type']),
                        $script['content']
                    );
                    break;
            }
        }

        return $output;
    }

    public static function getHtml($location = 'bottom')
    {
        static::getObject();
        $htmls = static::$stack[0]->getHtml($location);
        $output = [];

        foreach ($htmls as $html) {
            $output[] = $html['html'];
        }

        return $output;
    }

    /**
     * Escape string (emulates twig filter).
     *
     * @param string|object $string
     * @param string $strategy
     * @return string
     */
    public static function escape($string, $strategy = 'html')
    {
        if (!is_string($string)) {
            if (is_object($string) && method_exists($string,
'__toString')) {
                $string = (string) $string;
            } elseif (in_array($strategy, ['html',
'js', 'css', 'html_attr', 'url']))
{
                return $string;
            }
        }

        switch ($strategy) {
            case 'html':
                return htmlspecialchars($string, ENT_QUOTES |
ENT_SUBSTITUTE, 'UTF-8');

            case 'js':
                if (0 === strlen($string) ? false : (1 ===
preg_match('/^./su', $string) ? false : true)) {
                    throw new \RuntimeException('The string to escape
is not a valid UTF-8 string.');
                }

                $string = preg_replace_callback(
                    '#[^a-zA-Z0-9,\._]#Su',
                   
'Gantry\\Component\\Content\\Document\\HtmlDocument::_escape_js_callback',
                    $string
                );

                return $string;

            case 'css':
                if (0 === strlen($string) ? false : (1 ===
preg_match('/^./su', $string) ? false : true)) {
                    throw new \RuntimeException('The string to escape
is not a valid UTF-8 string.');
                }

                $string = preg_replace_callback(
                    '#[^a-zA-Z0-9]#Su',
                   
'Gantry\\Component\\Content\\Document\\HtmlDocument::_escape_css_callback',
                    $string
                );

                return $string;

            case 'html_attr':
                if (0 === strlen($string) ? false : (1 ===
preg_match('/^./su', $string) ? false : true)) {
                    throw new \RuntimeException('The string to escape
is not a valid UTF-8 string.');
                }

                $string = preg_replace_callback(
                    '#[^a-zA-Z0-9,\.\-_]#Su',
                   
'Gantry\\Component\\Content\\Document\\HtmlDocument::_escape_html_attr_callback',
                    $string
                );

                return $string;

            case 'url':
                return rawurlencode($string);

            default:
                throw new \RuntimeException(sprintf('Invalid escaping
strategy "%s" (valid ones: html, js, css, html_attr, url).',
$strategy));
        }
    }

    /**
     * @param $framework
     * @return bool
     * @deprecated 5.3
     */
    public static function load($framework)
    {
        return static::addFramework($framework);
    }

    /**
     * Register assets.
     */
    public static function registerAssets()
    {
        static::registerFrameworks();
    }

    public static function siteUrl()
    {
        return static::rootUri();
    }

    /**
     * NOTE: In PHP this function can be called either from Gantry DI
container or statically.
     *
     * @return string
     */
    public static function rootUri()
    {
        return '';
    }

    /**
     * NOTE: In PHP this function can be called either from Gantry DI
container or statically.
     *
     * @param bool $addDomain
     * @return string
     */
    public static function domain($addDomain = false)
    {
        return '';
    }

    /**
     * Return URL to the resource.
     *
     * @example {{
url('gantry-theme://images/logo.png')|default('http://www.placehold.it/150x100/f4f4f4')
}}
     *
     * NOTE: In PHP this function can be called either from Gantry DI
container or statically.
     *
     * @param  string $url         Resource to be located.
     * @param  bool $domain        True to include domain name.
     * @param  int $timestamp_age  Append timestamp to files that are less
than x seconds old. Defaults to a week.
     *                             Use value <= 0 to disable the
feature.
     * @param  bool $allowNull     True if non-existing files should return
null.
     * @return string|null         Returns url to the resource or null if
resource was not found.
     */
    public static function url($url, $domain = false, $timestamp_age =
null, $allowNull = true)
    {
        if (!is_string($url) || $url === '') {
            // Return null on invalid input.
            return null;
        }

        if ($url[0] === '#' || $url[0] === '?') {
            // Handle urls with query string or fragment only.
            return str_replace(' ', '%20', $url);
        }

        $parts = Url::parse($url);

        if (!is_array($parts)) {
            // URL could not be parsed.
            return $allowNull ? null : str_replace(' ',
'%20', $url);
        }

        // Make sure we always have scheme, host, port and path.
        $scheme = isset($parts['scheme']) ?
$parts['scheme'] : '';
        $host = isset($parts['host']) ? $parts['host']
: '';
        $port = isset($parts['port']) ? $parts['port']
: '';
        $path = isset($parts['path']) ? $parts['path']
: '';

        if ($scheme && !$port) {
            // If URL has a scheme, we need to check if it's one of
Gantry streams.
            $gantry = static::gantry();

            /** @var UniformResourceLocator $locator */
            $locator = $gantry['locator'];

            if (!$locator->schemeExists($scheme)) {
                // If scheme does not exists as a stream, assume it's
external.
                return str_replace(' ', '%20', $url);
            }

            // Attempt to find the resource (because of parse_url() we need
to put host back to path).
            $newPath =
$locator->findResource("{$scheme}://{$host}{$path}", false);

            if ($newPath === false) {
                if ($allowNull) {
                    return null;
                }

                // Return location where the file would be if it was saved.
                $path =
$locator->findResource("{$scheme}://{$host}{$path}", false,
true);
            } else {
                $path = $newPath;
            }

        } elseif ($host || $port) {
            // If URL doesn't have scheme but has host or port, it is
external.
            return str_replace(' ', '%20', $url);
        }

        // At this point URL is either relative or absolute path; let us
find if it is relative and not . or ..
        if ($path && '/' !== $path[0] &&
'.' !== $path[0]) {
            if ($timestamp_age === null) {
                $timestamp_age = static::$timestamp_age;
            }
            if ($timestamp_age > 0) {
                // We want to add timestamp to the URI: do it only for
existing files.
                $realPath = @realpath(GANTRY5_ROOT . '/' .
$path);
                if ($realPath && is_file($realPath)) {
                    $time = filemtime($realPath);
                    // Only append timestamp for files that are less than
the maximum age.
                    if ($time > time() - $timestamp_age) {
                        $parts['query'] =
(!empty($parts['query']) ?
"{$parts['query']}&" : '') .
sprintf('%x', $time);
                    }
                }
            }

            // We need absolute URI instead of relative.
            $path = rtrim(static::rootUri(), '/') . '/'
. $path;
        }

        // Set absolute URI.
        $uri = $path;

        // Add query string back.
        if (!empty($parts['query'])) {
            if (!$uri) $uri = static::rootUri();
            $uri .= '?' . $parts['query'];
        }

        // Add fragment back.
        if (!empty($parts['fragment'])) {
            if (!$uri) $uri = static::rootUri();
            $uri .= '#' . $parts['fragment'];
        }

        return static::domain($domain) . str_replace(' ',
'%20', $uri);
    }

    /**
     * Filter stream URLs from HTML.
     *
     * @param  string $html         HTML input to be filtered.
     * @param  bool $domain         True to include domain name.
     * @param  int $timestamp_age   Append timestamp to files that are less
than x seconds old. Defaults to a week.
     *                              Use value <= 0 to disable the
feature.
     * @param  bool $streamOnly     Only touch streams.
     * @return string               Returns modified HTML.
     */
    public static function urlFilter($html, $domain = false, $timestamp_age
= null, $streamOnly = false)
    {
        static::$urlFilterParams = [$domain, $timestamp_age, $streamOnly];

        // Tokenize all PRE, CODE and SCRIPT tags to avoid modifying any
src|href|url in them
        $tokens = [];

        $html =
preg_replace_callback('#<(pre|code|script)(\s[^>]+)?>.*?</\\1>#ius',
function($matches) use (&$tokens) {
            // Unfortunately uniqid() doesn't quite work in Windows,
so we need to work it around by adding some randomness.
            $token = '@@'. uniqid(mt_rand(), true) .
'@@';
            $match = $matches[0];

            $tokens[$token] = $match;

            return $token;
        }, $html);

        if ($streamOnly) {
            $gantry = static::gantry();

            /** @var UniformResourceLocator $locator */
            $locator = $gantry['locator'];
            $schemes = $locator->getSchemes();

            $list = [];
            foreach ($schemes as $scheme) {
                if (strpos($scheme, 'gantry-') === 0) {
                    $list[] = substr($scheme, 7);
                }
            }
            if (empty($list)) {
                return $html;
            }

            $match = '(gantry-(' . implode('|', $list).
')://.*?)';
        } else {
            $match = '(.*?)';
        }

        $html = preg_replace_callback('^(\s)(src|href)="' .
$match . '"^u', 'static::linkHandler', $html);
        $html = preg_replace_callback('^(\s)url\(' . $match .
'\)^u', 'static::urlHandler', $html);
        $html = static::replaceTokens($tokens, $html);

        return $html;
    }

    /**
     * @param array $matches
     * @return string
     * @internal
     */
    public static function linkHandler(array $matches)
    {
        list($domain, $timestamp_age) = static::$urlFilterParams;
        $url = trim($matches[3]);
        $url = static::url($url, $domain, $timestamp_age, false);

        return "{$matches[1]}{$matches[2]}=\"{$url}\"";
    }

    /**
     * @param array $matches
     * @return string
     * @internal
     */
    public static function urlHandler(array $matches)
    {
        list($domain, $timestamp_age) = static::$urlFilterParams;
        $url = trim($matches[2], '"\'');
        $url = static::url($url, $domain, $timestamp_age, false);

        return "{$matches[1]}url({$url})";
    }

    /**
     * This function is adapted from code coming from Twig.
     *
     * @param array $matches
     * @return string
     * @internal
     */
    public static function _escape_js_callback($matches)
    {
        $char = $matches[0];

        /*
         * A few characters have short escape sequences in JSON and
JavaScript.
         * Escape sequences supported only by JavaScript, not JSON, are
ommitted.
         * \" is also supported but omitted, because the resulting
string is not HTML safe.
         */
        static $shortMap = [
            '\\' => '\\\\',
            '/' => '\\/',
            "\x08" => '\b',
            "\x0C" => '\f',
            "\x0A" => '\n',
            "\x0D" => '\r',
            "\x09" => '\t',
        ];

        if (isset($shortMap[$char])) {
            return $shortMap[$char];
        }

        // \uHHHH
        $char = static::convert_encoding($char, 'UTF-16BE',
'UTF-8');
        $char = strtoupper(bin2hex($char));

        if (4 >= \strlen($char)) {
            return sprintf('\u%04s', $char);
        }

        return sprintf('\u%04s\u%04s', substr($char, 0, -4),
substr($char, -4));
    }

    /**
     * This function is adapted from code coming from Twig.
     *
     * @param $matches
     * @return string
     * @internal
     */
    public static function _escape_css_callback($matches)
    {
        $char = $matches[0];

        return sprintf('\\%X ', 1 === \strlen($char) ?
\ord($char) : static::ord($char));
    }

    /**
     * This function is adapted from code coming from Twig and Zend
Framework.
     *
     * @param array $matches
     * @return string
     *
     * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc.
(https://www.zend.com)
     * @license   https://framework.zend.com/license/new-bsd New BSD
License
     * @internal
     */
    public static function _escape_html_attr_callback($matches)
    {
        $chr = $matches[0];
        $ord = \ord($chr);

        /*
         * The following replaces characters undefined in HTML with the
         * hex entity for the Unicode replacement character.
         */
        if (($ord <= 0x1f && "\t" !== $chr &&
"\n" !== $chr && "\r" !== $chr) || ($ord >=
0x7f && $ord <= 0x9f)) {
            return '&#xFFFD;';
        }

        /*
         * Check if the current character to escape has a name entity we
should
         * replace it with while grabbing the hex value of the character.
         */
        if (1 === \strlen($chr)) {
            /*
             * While HTML supports far more named entities, the lowest
common denominator
             * has become HTML5's XML Serialisation which is
restricted to the those named
             * entities that XML supports. Using HTML entities would result
in this error:
             *     XML Parsing Error: undefined entity
             */
            static $entityMap = [
                34 => '&quot;', /* quotation mark */
                38 => '&amp;',  /* ampersand */
                60 => '&lt;',   /* less-than sign */
                62 => '&gt;',   /* greater-than sign */
            ];

            if (isset($entityMap[$ord])) {
                return $entityMap[$ord];
            }

            return sprintf('&#x%02X;', $ord);
        }

        /*
         * Per OWASP recommendations, we'll use hex entities for any
other
         * characters where a named entity does not exist.
         */
        return sprintf('&#x%04X;', static::ord($chr));
    }

    /**
     * Replace tokens with strings.
     *
     * @param array $tokens
     * @param $html
     * @return string|NUll
     */
    protected static function replaceTokens(array $tokens, $html)
    {
        foreach ($tokens as $token => $replacement) {
            // We need to use callbacks to turn off backreferences ($1,
\\1) in the replacement string.
            $callback = function() use ($replacement) { return
$replacement; };

            $html = preg_replace_callback('#' .
preg_quote($token, '#') . '#u', $callback, $html);
        }

        return $html;
    }

    /**
     * Register loaded frameworks.
     */
    protected static function registerFrameworks()
    {
        foreach (static::$stack[0]->getFrameworks() as $framework) {
            if (isset(static::$availableFrameworks[$framework])) {
                call_user_func([get_called_class(),
static::$availableFrameworks[$framework]]);
            }
        }
    }

    protected static function registerJquery()
    {
        static::addScript(
            [
                'src' =>
'https://code.jquery.com/jquery-2.2.2.min.js',
                'integrity' =>
'sha256-36cp2Co+/62rEAAYHLmRCPIych47CvdM+uTBJwSzWjI=',
                'crossorigin' => 'anonymous'
            ],
            11
        );
    }

    protected static function registerJqueryUiSortable()
    {
        static::registerJquery();

        static::addScript(
            [
                'src' =>
'https://code.jquery.com/ui/1.11.4/jquery-ui.min.js',
                'integrity' =>
'sha256-xNjb53/rY+WmG+4L6tTl9m6PpqknWZvRt0rO1SRnJzw=',
                'crossorigin' => 'anonymous'
            ],
            11
        );
    }

    protected static function registerBootstrap2()
    {
        static::registerJquery();

        static::addScript(['src' =>
'https://maxcdn.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js'],
11);
    }

    protected static function registerBootstrap3()
    {
        static::registerJquery();

        static::addScript(['src' =>
'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js'],
11);
    }

    protected static function registerMootools()
    {
        static::addScript(['src' =>
'https://cdnjs.cloudflare.com/ajax/libs/mootools/1.5.2/mootools-core-compat.min.js'],
11);
    }

    protected static function registerMootoolsMore()
    {
        static::registerMootools();

        static::addScript(['src' =>
'https://cdnjs.cloudflare.com/ajax/libs/mootools-more/1.5.2/mootools-more-compat-compressed.js'],
11);
    }

    protected static function registerLightcase()
    {
        static::registerJquery();

        static::addScript(['src' =>
static::url('gantry-assets://js/lightcase.js', false, null,
false)], 11, 'footer');
        static::addStyle(['href' =>
static::url('gantry-assets://css/lightcase.css', false, null,
false)], 11);
    }

    protected static function registerLightcaseInit()
    {
        static::registerLightcase();

        static::addInlineScript(['content' =>
"jQuery(document).ready(function($) {
jQuery('[data-rel^=lightcase]').lightcase({maxWidth:
'100%', maxHeight: '100%', video: {width:
'1280', height: '720'}}); });"], 0,
'footer');
    }

    protected static function getObject()
    {
        static $object;

        if (!$object) {
            // We need to initialize document for backwards compatibility
(RokSprocket/RokGallery in WP).
            $object = Gantry::instance()['document'];
        }

        return $object;
    }

    /**
     * @param string $string
     * @param string $to
     * @param string $from
     * @return false|string|string[]|null
     * @internal
     */
    private static function convert_encoding($string, $to, $from)
    {
        if (\function_exists('mb_convert_encoding')) {
            return mb_convert_encoding($string, $to, $from);
        }
        if (\function_exists('iconv')) {
            return iconv($from, $to, $string);
        }

        throw new \RuntimeException('No suitable convert encoding
function (use UTF-8 as your encoding or install the iconv or mbstring
extension).');
    }

    /**
     * @param string $string
     * @return false|int|mixed
     * @internal
     */
    private static function ord($string)
    {
        if (\function_exists('mb_ord')) {
            return mb_ord($string, 'UTF-8');
        }

        $code = ($string = unpack('C*', substr($string, 0, 4))) ?
$string[1] : 0;
        if (0xF0 <= $code) {
            return (($code - 0xF0) << 18) + (($string[2] - 0x80)
<< 12) + (($string[3] - 0x80) << 6) + $string[4] - 0x80;
        }
        if (0xE0 <= $code) {
            return (($code - 0xE0) << 12) + (($string[2] - 0x80)
<< 6) + $string[3] - 0x80;
        }
        if (0xC0 <= $code) {
            return (($code - 0xC0) << 6) + $string[2] - 0x80;
        }

        return $code;
    }
}
PKN[�[,���Controller/BaseController.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Controller;

use Gantry\Framework\Request;
use RocketTheme\Toolbox\DI\Container;
use RuntimeException;

abstract class BaseController implements RestfulControllerInterface
{
    /**
     * @var string Default HTTP method.
     */
    protected $method = 'GET';

    /**
     * @var Request
     */
    protected $request;

    /**
     * @var array List of HTTP verbs and their actions.
     */
    protected $httpVerbs = [
        'GET' => [
            '/'         => 'index',
            '/create'   => 'create',
            '/*'        => 'display',
            '/*/edit'   => 'edit'
        ],
        'POST' => [
            '/'  => 'store'
        ],
        'PUT' => [
            '/*' => 'replace'
        ],
        'PATCH' => [
            '/*' => 'update'
        ],
        'DELETE' => [
            '/*' => 'destroy'
        ]
    ];

    /**
     * @var array Parameters from router.
     */
    protected $params = [];

    /**
     * @var Container
     */
    protected $container;

    public function __construct(Container $container)
    {
        $this->container = $container;
        $this->request = $container['request'];
    }

    /**
     * Execute controller.
     *
     * @param string $method
     * @param array $path
     * @param array $params
     * @return mixed
     * @throws \RuntimeException
     */
    public function execute($method, array $path, array $params)
    {
        $this->method = $method;
        $this->setParams($params);
        list($action, $path) = $this->resolveHttpVerb($method, $path);

        if (!method_exists($this, $action)) {
            $action = 'undefined';
        }

        return call_user_func_array([$this, $action], $path);
    }

    /**
     * Set router parameters. Replaces the old parameters.
     *
     * @param array $params
     * @return $this
     */
    public function setParams(array $params)
    {
        $this->params = $params;

        return $this;
    }

    /**
     * @example GET /resources
     *
     * @return mixed
     */
    public function index()
    {
        return $this->undefined();
    }

    /**
     * @example GET /resources/:id
     *
     * @param string $id
     * @return mixed
     */
    public function display($id)
    {
        return $this->undefined();
    }

    /**
     * Special sub-resource to create a new resource (returns a form).
     *
     * @example GET /resources/create
     *
     * @return mixed
     */
    public function create()
    {
        return $this->undefined();
    }

    /**
     * Special sub-resource to edit existing resource (returns a form).
     *
     * @example GET /resources/:id/edit
     *
     * @param string $id
     * @return mixed
     */
    public function edit($id)
    {
        return $this->undefined();
    }

    /**
     * @example POST /resources
     *
     * @return mixed
     */
    public function store()
    {
        return $this->undefined();
    }

    /**
     * @example PUT /resources/:id
     *
     * @param string $id
     * @return mixed
     */
    public function replace($id)
    {
        return $this->undefined();
    }

    /**
     * @example PATCH /resources/:id
     *
     * @param string $id
     * @return mixed
     */
    public function update($id)
    {
        return $this->undefined();
    }

    /**
     * @example DELETE /resources/:id
     *
     * @param string $id
     * @return mixed
     */
    public function destroy($id)
    {
        return $this->undefined();
    }

    /**
     * Catch all action for all undefined actions.
     *
     * @return mixed
     * @throws RuntimeException
     */
    public function undefined()
    {
        if (in_array($this->method, ['HEAD',
'GET'])) {
            throw new RuntimeException('Page Not Found', 404);
        }

        throw new RuntimeException('Invalid Action', 405);
    }

    /**
     * Catch all action for forbidden actions.
     *
     * @return mixed
     * @throws RuntimeException
     */
    public function forbidden()
    {
        throw new RuntimeException('Forbidden', 403);
    }

    /**
     * Load resource.
     *
     * Function throws an exception if resource does not exist.
     *
     * @param string|int $id
     * @throws \RuntimeException
     */
    protected function loadResource($id)
    {
        throw new RuntimeException('Resource Not Found', 404);
    }

    /**
     * Resolve HTTP verb.
     *
     * @param string $method
     * @param array $items
     * @return array [function, parameters]
     */
    protected function resolveHttpVerb($method, array $items)
    {
        // HEAD has identical behavior to GET.
        $method = ($method == 'HEAD') ? 'GET' :
$method;

        if (!isset($this->httpVerbs[$method])) {
            // HTTP method is not defined.
            return ['undefined', $items];
        }

        $path = '';
        $remaining = $items;
        $variables = [];
        $actions = $this->httpVerbs[$method];

        // Build path for the verb and fetch all the variables.
        while (($current = array_shift($remaining)) !== null) {
            $test = "{$path}/{$current}";

            if (!isset($actions[$test])) {
                // Specific path not found, check if we have a variable.
                $test = "{$path}/*";

                if (isset($actions[$test])) {
                    // Variable found, save the value and move on.
                    $variables[] = $current;

                } elseif (isset($actions[$test . '*'])) {
                    // Wildcard found, pass through rest of the variables.
                    $path = $test . '*';
                    $variables = array_merge($variables, [$current],
$remaining);
                    break;

                } else {
                    // No matches; we are done here.
                    return ['undefined', $items];
                }
            }

            // Path was found.
            $path = $test;
        }

        // No matching path; check if we have verb for the root.
        if (!$path && isset($actions['/'])) {
            $path = '/';
        }

        // Get the action.
        $action = $path ? $actions[$path] : 'undefined';

        return [$action, $variables];
    }
}
PKN[�[���00Controller/HtmlController.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Controller;

use Gantry\Component\Response\HtmlResponse;
use Gantry\Component\Response\Response;

abstract class HtmlController extends BaseController
{
    /**
     * Execute controller and returns Response object, defaulting to
HtmlResponse.
     *
     * @param string $method
     * @param array $path
     * @param array $params
     * @return mixed
     * @throws \RuntimeException
     */
    public function execute($method, array $path, array $params)
    {
        $response = parent::execute($method, $path, $params);

        if (!$response instanceof Response) {
            $response = new HtmlResponse($response);
        }

        return $response;
    }
}
PKN[�[0s���Controller/JsonController.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Controller;

use Gantry\Component\Response\JsonResponse;

abstract class JsonController extends BaseController
{
    /**
     * Execute controller and returns JsonResponse object.
     *
     * @param string $method
     * @param array $path
     * @param array $params
     * @return mixed
     * @throws \RuntimeException
     */
    public function execute($method, array $path, array $params)
    {
        $response = parent::execute($method, $path, $params);

        if (!$response instanceof JsonResponse) {
            $response = new JsonResponse($response);
        }

        return $response;
    }
}
PKN[�[�VVaa)Controller/RestfulControllerInterface.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Controller;

interface RestfulControllerInterface
{
    /**
     * @example GET /resources
     *
     * @return mixed
     */
    public function index();

    /**
     * @example GET /resources/:id
     *
     * @param string $id
     * @return mixed
     */
    public function display($id);

    /**
     * Special sub-resource to create a new resource (returns a form).
     *
     * @example GET /resources/create
     *
     * @return mixed
     */
    public function create();

    /**
     * Special sub-resource to edit existing resource (returns a form).
     *
     * @example GET /resources/:id/edit
     *
     * @param string $id
     * @return mixed
     */
    public function edit($id);

    /**
     * @example POST /resources
     *
     * @return mixed
     */
    public function store();

    /**
     * @example PUT /resources/:id
     *
     * @param string $id
     * @return mixed
     */
    public function replace($id);

    /**
     * @example PATCH /resources/:id
     *
     * @param string $id
     * @return mixed
     */
    public function update($id);

    /**
     * @example DELETE /resources/:id
     *
     * @param string $id
     * @return mixed
     */
    public function destroy($id);
}
PKN[�[��7��File/CompiledFile.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\File;

use RocketTheme\Toolbox\File\PhpFile;

/**
 * Class CompiledFile
 * @package Grav\Common\File
 *
 * @property string $filename
 * @property string $extension
 * @property string $raw
 * @property array|string $content
 */
trait CompiledFile
{
    protected $cachePath;
    protected $caching = true;

    /**
     * @param string $path
     * @return $this
     */
    public function setCachePath($path)
    {
        $this->cachePath = $path;

        return $this;
    }

    public function caching($enabled = null)
    {
        if (null !== $enabled) {
            $this->caching = (bool) $enabled;
        }

        return $this->caching;
    }

    /**
     * Get/set parsed file contents.
     *
     * @param mixed $var
     * @return string
     * @throws \BadMethodCallException
     */
    public function content($var = null)
    {
        if (!$this->cachePath) {
            throw new \BadMethodCallException("Cache path not defined
for compiled file ({$this->filename})!");
        }

        try {
            // If nothing has been loaded, attempt to get pre-compiled
version of the file first.
            if ($var === null && $this->raw === null &&
$this->content === null) {
                $modified = $this->modified();

                if (!$modified || !$this->caching) {
                    return $this->decode($this->raw());
                }

                $key = md5($this->filename);
                $file = PhpFile::instance($this->cachePath .
"/{$key}{$this->extension}.php");

                $class = get_class($this);

                $cache = $file->exists() ? $file->content() : null;

                // Load real file if cache isn't up to date (or is
invalid).
                if (!isset($cache['@class'])
                    || $cache['@class'] != $class
                    || $cache['modified'] != $modified
                    || $cache['filename'] != $this->filename
                ) {
                    // Attempt to lock the file for writing.
                    try {
                        $file->lock(false);
                    } catch (\Exception $e) {
                        // Another process has locked the file; we will
check this in a bit.
                    }

                    // Decode RAW file into compiled array.
                    $data = $this->decode($this->raw());
                    $cache = [
                        '@class' => $class,
                        'filename' => $this->filename,
                        'modified' => $modified,
                        'data' => $data
                    ];

                    // If compiled file wasn't already locked by
another process, save it.
                    if ($file->locked() !== false) {
                        $file->save($cache);
                        $file->unlock();

                        // Compile cached file into bytecode cache
                        if
(function_exists('opcache_invalidate')) {
                            // Silence error in case if
`opcache.restrict_api` directive is set.
                            @opcache_invalidate($file->filename(),
true);
                        } elseif
(function_exists('apc_compile_file')) {
                            // PHP 5.4
                            @apc_compile_file($file->filename());
                        }
                    }
                }
                $file->free();

                $this->content = $cache['data'];
            }

        } catch (\Exception $e) {
            throw new \RuntimeException(sprintf('Failed to read %s:
%s', basename($this->filename), $e->getMessage()), 500, $e);
        }

        return parent::content($var);
    }
}
PKN[�[{BBFile/CompiledYamlFile.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\File;

use RocketTheme\Toolbox\File\YamlFile;

class CompiledYamlFile extends YamlFile
{
    use CompiledFile;

    static public $defaultCachePath;
    static public $defaultCaching = true;

    protected function __construct()
    {
        parent::__construct();

        $this->caching(static::$defaultCaching);

        if (static::$defaultCachePath) {
            $this->setCachePath(static::$defaultCachePath);
        }
    }
}
PKN[�[�{�+�+Filesystem/Folder.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Filesystem;

/**
 * Folder helper class.
 *
 * @author RocketTheme
 * @license MIT
 */
abstract class Folder
{
    /**
     * Recursively find the last modified time under given path.
     *
     * @param  string $path
     * @return int
     */
    public static function lastModifiedFolder($path)
    {
        $last_modified = 0;

        $directory = new \RecursiveDirectoryIterator($path,
\RecursiveDirectoryIterator::SKIP_DOTS);
        $iterator = new \RecursiveIteratorIterator($directory,
\RecursiveIteratorIterator::SELF_FIRST);

        /** @var \RecursiveDirectoryIterator $file */
        foreach ($iterator as $file) {
            $dir_modified = $file->getMTime();
            if ($dir_modified > $last_modified) {
                $last_modified = $dir_modified;
            }
        }

        return $last_modified;
    }

    /**
     * Get relative path between target and base path. If path isn't
relative, return full path.
     *
     * @param  string  $path
     * @param  string  $base
     * @return string
     */
    public static function getRelativePath($path, $base = GANTRY5_ROOT)
    {
        $base = preg_replace('![\\\/]+!', '/', $base);
        $path = preg_replace('![\\\/]+!', '/', $path);
        if (strpos($path, $base) === 0) {
            $path = ltrim(substr($path, strlen($base)), '/');
        }

        return $path;
    }

    /**
     * Get relative path between target and base path. If path isn't
relative, return full path.
     *
     * @param  string  $path
     * @param  string  $base
     * @return string
     */
    public static function getRelativePathDotDot($path, $base)
    {
        $base = preg_replace('![\\\/]+!', '/', $base);
        $path = preg_replace('![\\\/]+!', '/', $path);

        if ($path === $base) {
            return '';
        }

        $baseParts = explode('/', isset($base[0]) &&
'/' === $base[0] ? substr($base, 1) : $base);
        $pathParts = explode('/', isset($path[0]) &&
'/' === $path[0] ? substr($path, 1) : $path);

        array_pop($baseParts);
        $lastPart = array_pop($pathParts);
        foreach ($baseParts as $i => $directory) {
            if (isset($pathParts[$i]) && $pathParts[$i] ===
$directory) {
                unset($baseParts[$i], $pathParts[$i]);
            } else {
                break;
            }
        }
        $pathParts[] = $lastPart;
        $path = str_repeat('../', count($baseParts)) .
implode('/', $pathParts);

        return '' === $path
            || '/' === $path[0]
            || false !== ($colonPos = strpos($path, ':'))
&& ($colonPos < ($slashPos = strpos($path, '/')) ||
false === $slashPos)
            ? "./$path" : $path;
    }

    /**
     * Shift first directory out of the path.
     *
     * @param string $path
     * @return string
     */
    public static function shift(&$path)
    {
        $parts = explode('/', trim($path, '/'), 2);
        $result = array_shift($parts);
        $path = array_shift($parts);

        return $result ?: null;
    }

    /**
     * Return recursive list of all files and directories under given path.
     *
     * @param  string            $path
     * @param  array             $params
     * @return array
     * @throws \RuntimeException
     */
    public static function all($path, array $params = array())
    {
        if ($path === false) {
            throw new \RuntimeException("Path to {$path} doesn't
exist.");
        }

        $compare = isset($params['compare']) ? 'get' .
$params['compare'] : null;
        $pattern = isset($params['pattern']) ?
$params['pattern'] : null;
        $filters = isset($params['filters']) ?
$params['filters'] : null;
        $recursive = isset($params['recursive']) ?
$params['recursive'] : true;
        $levels = isset($params['levels']) ?
$params['levels'] : -1;
        $key = isset($params['key']) ? 'get' .
$params['key'] : null;
        $value = isset($params['value']) ? 'get' .
$params['value'] : ($recursive ? 'getSubPathname' :
'getFilename');
        $folders = isset($params['folders']) ?
$params['folders'] : true;
        $files = isset($params['files']) ?
$params['files'] : true;

        if ($recursive) {
            $directory = new \RecursiveDirectoryIterator($path,
                \RecursiveDirectoryIterator::SKIP_DOTS +
\FilesystemIterator::UNIX_PATHS + \FilesystemIterator::CURRENT_AS_SELF);
            $iterator = new \RecursiveIteratorIterator($directory,
\RecursiveIteratorIterator::SELF_FIRST);
            $iterator->setMaxDepth(max($levels, -1));
        } else {
            $iterator = new \FilesystemIterator($path);
        }

        $results = array();

        /** @var \RecursiveDirectoryIterator $file */
        foreach ($iterator as $file) {
            // Ignore hidden files.
            if ($file->getFilename()[0] == '.') {
                continue;
            }
            if (!$folders && $file->isDir()) {
                continue;
            }
            if (!$files && $file->isFile()) {
                continue;
            }
            if ($compare && $pattern &&
!preg_match($pattern, $file->{$compare}())) {
                continue;
            }
            $fileKey = $key ? $file->{$key}() : null;
            $filePath = $file->{$value}();
            if ($filters) {
                if (isset($filters['key'])) {
                    $filter = $filters['key'];
                    $pre = !empty($filters['pre-key']) ?
$filters['pre-key'] : '';
                    if (is_callable($filter)) {
                        $fileKey = $pre . call_user_func($filter,
$fileKey);
                    } else {
                        $fileKey = $pre . preg_replace($filter,
'', $fileKey);
                    }
                }
                if (isset($filters['value'])) {
                    $filter = $filters['value'];
                    if (is_callable($filter)) {
                        $filePath = call_user_func($filter, $file);
                    } else {
                        $filePath = preg_replace($filter, '',
$filePath);
                    }
                }
            }

            if ($fileKey !== null) {
                $results[$fileKey] = $filePath;
            } else {
                $results[] = $filePath;
            }
        }

        return $results;
    }

    /**
     * Recursively copy directory in filesystem.
     *
     * @param  string $source
     * @param  string $target
     * @param  string $ignore  Ignore files matching pattern (regular
expression).
     * @throws \RuntimeException
     */
    public static function copy($source, $target, $ignore = null)
    {
        $source = rtrim($source, '\\/');
        $target = rtrim($target, '\\/');

        if (!is_dir($source)) {
            throw new \RuntimeException('Cannot copy non-existing
folder.');
        }

        // Make sure that path to the target exists before copying.
        self::create($target);

        $success = true;

        // Go through all sub-directories and copy everything.
        $files = self::all($source);
        foreach ($files as $file) {
            if ($ignore && preg_match($ignore, $file)) {
                continue;
            }
            $src = $source .'/'. $file;
            $dst = $target .'/'. $file;

            if (is_dir($src)) {
                // Create current directory (if it doesn't exist).
                if (!is_dir($dst)) {
                    $success &= @mkdir($dst, 0777, true);
                }
            } else {
                // Or copy current file.
                $success &= @copy($src, $dst);
            }
        }

        if (!$success) {
            $error = error_get_last();
            throw new \RuntimeException($error['message']);
        }

        // Make sure that the change will be detected when caching.
        @touch(dirname($target));
    }

    /**
     * Move directory in filesystem.
     *
     * @param  string $source
     * @param  string $target
     * @throws \RuntimeException
     */
    public static function move($source, $target)
    {
        if (!is_dir($source)) {
            throw new \RuntimeException('Cannot move non-existing
folder.');
        }

        // Make sure that path to the target exists before moving.
        self::create(dirname($target));

        // Just rename the directory.
        $success = @rename($source, $target);

        if (!$success) {
            $error = error_get_last();
            throw new \RuntimeException($error['message']);
        }

        // Make sure that the change will be detected when caching.
        @touch(dirname($source));
        @touch(dirname($target));
    }

    /**
     * Recursively delete directory from filesystem.
     *
     * @param  string $target
     * @param  bool   $include_target
     * @throws \RuntimeException
     */
    public static function delete($target, $include_target = true)
    {
        if (!$target) { return; }

        if (!is_dir($target)) {
            throw new \RuntimeException('Cannot delete non-existing
folder.');
        }

        $success = self::doDelete($target, $include_target);

        if (!$success) {
            $error = error_get_last();
            throw new \RuntimeException($error['message']);
        }

        // Make sure that the change will be detected when caching.
        if ($include_target) {
            @touch(dirname($target));
        } else {
            @touch($target);
        }
    }

    /**
     * @param  string  $folder
     * @throws \RuntimeException
     */
    public static function create($folder)
    {
        if (is_dir($folder)) {
            return;
        }

        $success = @mkdir($folder, 0777, true);

        if (!$success) {
            // Take yet another look, make sure that the folder
doesn't exist.
            clearstatcache(true, $folder);
            if (is_dir($folder)) {
                return;
            }

            $error = error_get_last();
            throw new \RuntimeException($error['message']);
        }
    }

    /**
     * @param  string $folder
     * @param  bool   $include_target
     * @return bool
     * @internal
     */
    protected static function doDelete($folder, $include_target = true)
    {
        // Special case for symbolic links.
        if ($include_target && is_link($folder)) {
            return @unlink($folder);
        }

        // Go through all items in filesystem and recursively remove
everything.
        $files = array_diff(scandir($folder), array('.',
'..'));
        foreach ($files as $file) {
            $path = "{$folder}/{$file}";
            (is_dir($path)) ? self::doDelete($path) : @unlink($path);
        }

        return $include_target ? @rmdir($folder) : true;
    }
}
PKO[�[ڀ}e

Filesystem/Streams.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Filesystem;

use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use RocketTheme\Toolbox\StreamWrapper\ReadOnlyStream;
use RocketTheme\Toolbox\StreamWrapper\Stream;

class Streams
{
    /**
     * @var array
     */
    protected $schemes = [];

    /**
     * @var array
     */
    protected $registered;

    /**
     * @var UniformResourceLocator
     */
    protected $locator;

    public function __construct(UniformResourceLocator $locator = null)
    {
        if ($locator) {
            $this->setLocator($locator);
        }
    }

    /**
     * @param UniformResourceLocator $locator
     */
    public function setLocator(UniformResourceLocator $locator)
    {
        $this->locator = $locator;

        // Set locator to both streams.
        Stream::setLocator($locator);
        ReadOnlyStream::setLocator($locator);
    }

    /**
     * @return UniformResourceLocator
     */
    public function getLocator()
    {
        return $this->locator;
    }

    public function add(array $schemes)
    {
        foreach ($schemes as $scheme => $config) {
            $force = !empty($config['force']);

            if (isset($config['paths'])) {
                $this->locator->addPath($scheme, '',
$config['paths'], false, $force);
            }
            if (isset($config['prefixes'])) {
                foreach ($config['prefixes'] as $prefix =>
$paths) {
                    $this->locator->addPath($scheme, $prefix, $paths,
false, $force);
                }
            }
            $type = !empty($config['type']) ?
$config['type'] : 'ReadOnlyStream';
            if ($type[0] != '\\') {
                $type = '\\Rockettheme\\Toolbox\\StreamWrapper\\'
. $type;
            }
            $this->schemes[$scheme] = $type;

            if (isset($this->registered)) {
                $this->doRegister($scheme, $type);
            }
        }
    }

    public function register()
    {
        $this->registered = stream_get_wrappers();

        foreach ($this->schemes as $scheme => $type) {
            $this->doRegister($scheme, $type);
        }
    }

    protected function doRegister($scheme, $type)
    {
        if (in_array($scheme, $this->registered)) {
            stream_wrapper_unregister($scheme);
        }

        if (!stream_wrapper_register($scheme, $type)) {
            throw new \InvalidArgumentException("Stream
'{$type}' could not be initialized.");
        }
    }
}
PKO[�[����WWGantry/GantryTrait.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Gantry;

use Gantry\Framework\Gantry;

trait GantryTrait
{
    /**
     * @var Gantry
     */
    private static $gantry;

    /**
     * Get global Gantry instance.
     *
     * @return Gantry
     */
    public static function gantry()
    {
        // We cannot set variable directly for the trait as it doesn't
work in HHVM.
        if (!self::$gantry) {
            self::$gantry = Gantry::instance();
        }

        return self::$gantry;
    }
}
PKO[�[�;o�Gettext/Gettext.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Gettext;

/**
 * Class Gettext
 * @package Gantry\Component\Gettext
 *
 * Examples on translating gettext in twig:
 *
 * {% trans string_var %}
 * http://twig.sensiolabs.org/doc/extensions/i18n.html
 *
 * {% trans %}Hello {{ author.name }}{% endtrans %}
 * http://symfony.com/doc/current/book/translation.html
 *
 * {{ 'Hello %name%'|trans({'%name%': name}) }}
 * {{ trans('Hello %name%', {'%name%': name}) }}
 */
class Gettext
{
    public $pos = 0;
    public $str;
    public $len;
    public $endian = 'V';

    public function parse($string)
    {
        $this->str = $string;
        $this->len = strlen($string);

        $magic = self::readInt() & 0xffffffff;

        if ($magic === 0x950412de) {
            // Low endian.
            $this->endian = 'V';
        } elseif ($magic === 0xde120495) {
            // Big endian.
            $this->endian = 'N';
        } else {
            throw new \Exception('Not a Gettext file (.mo)');
        }

        // Skip revision number.
        self::readInt();
        // Total count.
        $total = self::readInt();
        // Offset of original table.
        $originals = self::readInt();
        // Offset of translation table.
        $translations = self::readInt();

        $this->seek($originals);
        $table_originals = self::readIntArray($total * 2);
        $this->seek($translations);
        $table_translations = self::readIntArray($total * 2);

        $items = [];
        for ($i = 0; $i < $total; $i++) {
            $this->seek($table_originals[$i * 2 + 2]);
            $original = $this->read($table_originals[$i * 2 + 1]);

            if ($original) {
                $this->seek($table_translations[$i * 2 + 2]);
                $items[$original] = $this->read($table_translations[$i *
2 + 1]);
            }
        }

        return $items;
    }

    /**
     * @return int
     */
    protected function readInt()
    {
        $read = $this->read(4);

        if ($read === false) {
            return false;
        }

        $read = unpack($this->endian, $read);

        return array_shift($read);
    }

    /**
     * @param $count
     * @return array
     */
    protected function readIntArray($count)
    {
        return unpack($this->endian . $count, $this->read(4 *
$count));
    }

    /**
     * @param $bytes
     * @return string
     */
    private function read($bytes)
    {
        $data = substr($this->str, $this->pos, $bytes);
        $this->seek($this->pos + $bytes);
        return $data;
    }

    /**
     * @param $pos
     * @return mixed
     */
    private function seek($pos)
    {
        $this->pos = max($this->len, $pos);
        return $this->pos;
    }
}
PKO[�[�xх����Layout/Layout.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Layout;

use Gantry\Component\Config\Config;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Outlines;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ArrayTraits\ArrayAccess;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
use RocketTheme\Toolbox\ArrayTraits\Iterator;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceIterator;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

/**
 * Layout
 */
class Layout implements \ArrayAccess, \Iterator, ExportInterface
{
    use ArrayAccess, Iterator, Export;

    const VERSION = 7;

    protected static $instances = [];
    protected static $indexes = [];
    protected $layout = ['wrapper', 'container',
'section', 'grid', 'block',
'offcanvas'];

    public $name;
    public $timestamp = 0;
    public $preset = [];
    public $equalized = [3 => 33.3, 6 => 16.7, 7 => 14.3, 8 =>
12.5, 9 => 11.1, 11 => 9.1, 12 => 8.3];

    protected $exists;
    protected $items;
    protected $references;
    protected $parents;
    protected $blocks;
    protected $types;
    protected $inherit;

    /**
     * @return array
     */
    public static function presets()
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        /** @var UniformResourceIterator $iterator */
        $iterator = $locator->getIterator(
            'gantry-layouts://',
            UniformResourceIterator::CURRENT_AS_SELF |
UniformResourceIterator::UNIX_PATHS | UniformResourceIterator::SKIP_DOTS
        );

        $files = [];
        /** @var UniformResourceIterator $info */
        foreach ($iterator as $info) {
            $name = $info->getBasename('.yaml');
            if (!$info->isFile() || $info->getExtension() !==
'yaml' || $name[0] === '.') {
                continue;
            }
            $files[] = $name;
        }

        sort($files);

        $results = ['user' => [], 'system' =>
[]];
        foreach ($files as $preset) {
            $scope = $preset && $preset[0] !== '_' ?
'user' : 'system';
            $results[$scope][$preset] =
ucwords(trim(preg_replace(['|_|', '|/|'], ['
', ' / '], $preset)));
        }

        return $results;
    }

    /**
     * @param string $name
     * @return array
     * @throws \RuntimeException
     */
    public static function preset($name)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];
        $filename =
$locator->findResource("gantry-layouts://{$name}.yaml");

        if (!$filename) {
            throw new \RuntimeException(sprintf("Preset '%s'
not found", $name), 404);
        }

        $layout = LayoutReader::read($filename);
        $layout['preset']['name'] = $name;
        $layout['preset']['timestamp'] =
filemtime($filename);

        return $layout;
    }

    /**
     * @param  string $name
     * @return Layout
     */
    public static function instance($name)
    {
        if (!isset(static::$instances[$name])) {
            static::$instances[$name] = static::load($name);
        }

        return static::$instances[$name];
    }

    /**
     * @param  string $name
     * @return Layout
     */
    public static function index($name)
    {
        if (!isset(static::$indexes[$name])) {
            static::$indexes[$name] = static::loadIndex($name, true);
        }

        return static::$indexes[$name];
    }

    /**
     * @param string $name
     * @param array $items
     * @param array $preset
     */
    public function __construct($name, array $items = null, array $preset =
null)
    {
        $this->name = $name;
        $this->items = (array) $items;
        $this->exists = $items !== null;

        // Add preset data from the layout.
        if ($preset) {
            $this->preset = $preset;
        } elseif (isset($this->items['preset'])) {
            $this->preset = (array) $this->items['preset'];
        }

        unset($this->items['preset']);

        $this->preset += [
            'name' => '',
            'timestamp' => 0,
            'image' =>
'gantry-admin://images/layouts/default.png'
        ];
    }

    /**
     * @return bool
     */
    public function exists()
    {
        return $this->exists;
    }

    /**
     * Initialize layout.
     *
     * @param  bool  $force
     * @param  bool  $inherit
     * @return $this
     */
    public function init($force = false, $inherit = true)
    {
        if ($force || $this->references === null) {
            $this->initReferences();
            if ($inherit) {
                $this->initInheritance();
            }
        }

        return $this;
    }

    /**
     * Build separate meta-information from the layout.
     *
     * @return array
     */
    public function buildIndex()
    {
        return [
            'name' => $this->name,
            'timestamp' => $this->timestamp,
            'version' => static::VERSION,
            'preset' => $this->preset,
            'positions' => $this->positions(),
            'sections' => $this->sections(),
            'particles' => $this->particles(),
            'inherit' => $this->inherit()
        ];
    }

    /**
     * @return $this
     */
    public function clean()
    {
        $this->references = null;
        $this->types = null;
        $this->inherit = null;

        $this->cleanLayout($this->items);

        return $this;
    }

    /**
     * @param string $old
     * @param string $new
     * @param array  $ids
     * @return $this
     */
    public function updateInheritance($old, $new = null, $ids = null)
    {
        $this->init();

        $inherit = $this->inherit();

        if (!empty($inherit[$old])) {
            foreach ($inherit[$old] as $id => $inheritId) {
                $element = $this->find($id, false);
                if ($element) {
                    $inheritId = isset($element->inherit->particle) ?
$element->inherit->particle : $id;
                    if ($new && ($ids === null ||
isset($ids[$inheritId]))) {
                        // Add or modify inheritance.
                        if (!isset($element->inherit)) {
                            $element->inherit = new \stdClass;
                        }
                        $element->inherit->outline = $new;
                    } else {
                        // Remove inheritance.
                        $element->inherit = new \stdClass;
                        unset($this->inherit[$element->id]);
                    }
                } else {
                    // Element does not exist anymore, remove its
reference.
                    unset($this->inherit[$id]);
                }
            }
        }

        return $this;
    }


    /**
     * Save layout.
     *
     * @param bool $cascade
     * @return $this
     */
    public function save($cascade = true)
    {
        if (!$this->name) {
            throw new \LogicException('Cannot save unnamed
layout');
        }

        GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage("Saving layout for outline
{$this->name}");

        $name = strtolower(preg_replace('|[^a-z\d_-]|ui',
'_', $this->name));

        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        // If there are atoms in the layout, copy them into outline
configuration.
        $atoms = $this->atoms();
        if (is_array($atoms) && $cascade) {
                // Save layout into custom directory for the current theme.
                $filename =
$locator->findResource("gantry-config://{$name}/page/head.yaml",
true, true);

                $file = YamlFile::instance($filename);
                $config = new Config($file->content());

                $file->save($config->set('atoms',
json_decode(json_encode($atoms), true))->toArray());
                $file->free();
        }

        // Remove atoms from the layout.
        foreach ($this->items as $key => $section) {
            if ($section->type === 'atoms') {
                unset ($this->items[$key]);
            }
        }

        // Make sure that base outline never uses inheritance.
        if ($name === 'default') {
            $this->inheritNothing();
        }

        $filename =
$locator->findResource("gantry-config://{$name}/layout.yaml",
true, true);
        $file = CompiledYamlFile::instance($filename);
        $file->settings(['inline' => 20]);
        $file->save(LayoutReader::store($this->preset,
$this->items));
        $file->free();

        $this->timestamp = $file->modified();
        $this->exists = true;

        static::$instances[$this->name] = $this;

        return $this;
    }

    public function export()
    {
        return LayoutReader::store($this->preset, $this->items);
    }

    /**
     * Save index.
     *
     * @return $this
     */
    public function saveIndex($index = null)
    {
        if (!$this->name) {
            throw new \LogicException('Cannot save unnamed
layout');
        }

        GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage("Saving layout index for outline
{$this->name}");

        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];
        $filename =
$locator->findResource("gantry-config://{$this->name}/index.yaml",
true, true);
        $cache =
$locator->findResource("gantry-cache://{$this->name}/compiled/yaml",
true, true);
        $file = CompiledYamlFile::instance($filename);

        // Attempt to lock the file for writing.
        try {
            $file->lock(false);
        } catch (\Exception $e) {
            // Another process has locked the file; we will check this in a
bit.
        }

        $index = $index ? $index : $this->buildIndex();

        // If file wasn't already locked by another process, save it.
        if ($file->locked() !== false) {
            $file->setCachePath($cache)->settings(['inline'
=> 20]);
            $file->save($index);
            $file->unlock();
        }
        $file->free();

        static::$indexes[$this->name] = $index;

        return $this;
    }

    /**
     * @return array
     */
    public function getLayoutTypes()
    {
        return $this->layout;
    }

    /**
     * @param string $type
     * @return bool
     */
    public function isLayoutType($type)
    {
        return in_array($type, $this->layout, true);
    }

    /**
     * @param $id
     * @return string|null
     */
    public function getParentId($id)
    {
        return isset($this->parents[$id]) ? $this->parents[$id] :
null;
    }

    /**
     * @return array
     */
    public function references()
    {
        $this->init();

        return $this->references;
    }

    /**
     * @param string $type
     * @param string $subtype
     * @return array
     */
    public function referencesByType($type = null, $subtype = null)
    {
        $this->init();

        if (!$type) {
            return $this->types;
        }

        if (!$subtype) {
            return isset($this->types[$type]) ? $this->types[$type] :
[];
        }

        return isset($this->types[$type][$subtype]) ?
$this->types[$type][$subtype] : [];
    }

    /**
     * Return list of positions (key) with their titles (value).
     *
     * @return array Array of position => title
     */
    public function positions()
    {
        $positions = $this->referencesByType('position',
'position');

        $list = [];
        foreach($positions as $position) {
            if (!isset($position->attributes->key)) {
                continue;
            }
            $list[$position->attributes->key] = $position->title;
        }

        return $list;
    }

    /**
     * Return list of positions (key) with their titles (value).
     *
     * @return array Array of position => title
     */
    public function sections()
    {
        $list = [];
        foreach ($this->referencesByType('section') as $type
=> $sections) {
            foreach ($sections as $id => $section) {
                $list[$id] = $section->title;
            }
        }

        foreach ($this->referencesByType('offcanvas') as $type
=> $sections) {
            foreach ($sections as $id => $section) {
                $list[$id] = $section->title;
            }
        }

        return $list;
    }

    /**
     * Return list of particles with their titles.
     *
     * @param  bool  $grouped  If true, group particles by type.
     * @return array Array of position => title
     */
    public function particles($grouped = true)
    {
        $blocks = $this->referencesByType('block',
'block');

        $list = [];
        foreach ($blocks as $blockId => $block) {
            if (!empty($block->children)) {
                foreach ($block->children as $id => $particle) {
                    if (!empty($particle->layout) ||
in_array($particle->type, $this->layout, true)) {
                        continue;
                    }
                    if ($grouped) {
                        $list[$particle->subtype][$particle->id] =
$particle->title;
                    } else {
                        $list[$particle->id] = $particle->title;
                    }
                }
            }
        }

        return $list;
    }

    /**
     * @param string $outline
     * @return array
     */
    public function inherit($outline = null)
    {
        $this->init();

        $list = [];
        foreach ($this->inherit as $name => $item) {
            if (isset($item->inherit->outline)) {
                if (isset($item->inherit->particle)) {
                    $list[$item->inherit->outline][$name] =
$item->inherit->particle;
                } else {
                    $list[$item->inherit->outline][$name] = $name;
                }
            }
        }

        return $outline ? (!empty($list[$outline]) ? $list[$outline] : [])
: $list;
    }

    /**
     * Return atoms from the layout.
     *
     * @return array|null
     * @deprecated
     */
    public function atoms()
    {
        $list   = null;

        $atoms = array_filter($this->items, function ($section) {
            return $section->type === 'atoms' &&
!empty($section->children);
        });
        $atoms = array_shift($atoms);

        if (!empty($atoms->children)) {
            $list = [];
            foreach ($atoms->children as $grid) {
                if (!empty($grid->children)) {
                    foreach ($grid->children as $block) {
                        if (isset($block->children[0])) {
                            $item = $block->children[0];
                            $list[] = ['title' =>
$item->title, 'type' => $item->subtype,
'attributes' => $item->attributes];
                        }
                    }
                }
            }
        }

        return $list;
    }

    /**
     * @param string $id
     * @param bool $createIfNotExists
     * @return object
     */
    public function find($id, $createIfNotExists = true)
    {
        $this->init();

        if (!isset($this->references[$id])) {
            return $createIfNotExists ? (object)['id' => $id,
'inherit' => new \stdClass] : null;
        }

        return $this->references[$id];
    }

    /**
     * @param string $id
     * @return null
     */
    public function block($id)
    {
        $this->init();

        return isset($this->blocks[$id]) ? $this->blocks[$id] : null;
    }

    public function clearSections()
    {
        $this->items = $this->clearChildren($this->items);

        return $this;
    }

    protected function clearChildren(&$items)
    {
        foreach ($items as $key => $item) {
            if (!empty($item->children)) {
                $this->children =
$this->clearChildren($item->children);
            }

            if (empty($item->children) &&
in_array($item->type, ['grid', 'block',
'particle', 'position', 'spacer',
'system'], true)) {
                unset($items[$key]);
            }
        }

        return array_values($items);
    }

    public function copySections(array $old)
    {
        $this->init();

        /** @var Layout $old */
        $old = new static('tmp', $old);

        $leftover = [];

        // Copy normal sections.
        $data = $old->referencesByType('section');

        if (isset($this->types['section'])) {
            $sections = $this->types['section'];

            $this->copyData($data, $sections, $leftover);
        }

        // Copy offcanvas.
        $data = $old->referencesByType('offcanvas');
        if (isset($this->types['offcanvas'])) {
            $offcanvas = $this->types['offcanvas'];

            $this->copyData($data, $offcanvas, $leftover);
        }

        // Copy atoms.
        $data = $old->referencesByType('atoms');
        if (isset($this->types['atoms'])) {
            $atoms = $this->types['atoms'];

            $this->copyData($data, $atoms, $leftover);
        }

        return $leftover;
    }

    public function inheritAll()
    {
        foreach ($this->references() as $item) {
            if (!empty($item->inherit->outline)) {
                continue;
            }
            if (!$this->isLayoutType($item->type)) {
                $item->inherit = (object) ['outline' =>
$this->name, 'include' => ['attributes',
'block']];
            } elseif ($item->type === 'section' ||
$item->type === 'offcanvas') {
                $item->inherit = (object) ['outline' =>
$this->name, 'include' => ['attributes',
'block', 'children']];
            }
        }

        $this->init(true);

        return $this;
    }

    public function inheritNothing()
    {
        foreach ($this->references() as $item) {
            unset($item->inherit);
        }

        $this->init(true);

        return $this;
    }

    protected function copyData(array $data, array $sections, array
&$leftover)
    {
        foreach ($data as $type => $items) {
            foreach ($items as $item) {
                $found = false;
                if (isset($sections[$type])) {
                    foreach ($sections[$type] as $section) {
                        if ($section->id === $item->id) {
                            $found = true;
                            $section->inherit =
$this->cloneData($item->inherit);
                            $section->children =
$this->cloneData($item->children);
                            break;
                        }
                    }
                }
                if (!$found && !empty($item->children)) {
                    $leftover[$item->id] = $item->title;
                }
            }
        }
    }

    /**
     * Clone data which consists mixed set of arrays and stdClass objects.
     *
     * @param mixed $data
     * @return mixed
     */
    protected function cloneData($data)
    {
        if (!($isObject = is_object($data)) && !is_array($data)) {
            return $data;
        }

        $clone = [];

        foreach((array) $data as $key => $value) {
            if (is_object($value) || is_array($value)) {
                $clone[$key] = $this->cloneData($value);
            } else {
                $clone[$key] = $value;
            }
        }

        return $isObject ? (object) $clone : $clone;
    }

    /**
     * @param array $items
     */
    protected function cleanLayout(array $items)
    {
        foreach ($items as $item) {
            if (!empty($item->inherit->include)) {
                $include = $item->inherit->include;
                foreach ($include as $part) {
                    switch ($part) {
                        case 'attributes':
                            $item->attributes = new \stdClass();
                            break;
                        case 'block':
                            break;
                        case 'children':
                            $item->children = [];
                            break;
                    }
                }
            }
            if (!empty($item->children)) {
                $this->cleanLayout($item->children);
            }
        }
    }

    protected function initInheritance()
    {
        $index = null;
        if ($this->name) {
            $index = static::loadIndexFile($this->name);
        }

        $inheriting = $this->inherit();

        if (GANTRY_DEBUGGER && $inheriting) {
            \Gantry\Debugger::addMessage(sprintf("Layout from outline
%s inherits %s", $this->name, implode(", ",
array_keys($inheriting))));
        }

        foreach ($inheriting as $outlineId => $list) {
            try {
                $outline = $this->instance($outlineId);
            } catch (\Exception $e) {
                // Outline must have been deleted.
                GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage("Outline {$outlineId} is missing /
deleted", 'error');
                $outline = null;
            }
            foreach ($list as $id => $inheritId) {
                $item = $this->find($id);

                $inheritId = !empty($item->inherit->particle) ?
$item->inherit->particle : $id;
                $inherited = $outline ? $outline->find($inheritId) :
null;
                $include = !empty($item->inherit->include) ? (array)
$item->inherit->include : [];

                foreach ($include as $part) {
                    switch ($part) {
                        case 'attributes':
                            // Deep clone attributes.
                            $item->attributes =
isset($inherited->attributes) ?
$this->cloneData($inherited->attributes) : new \stdClass();
                            break;
                        case 'block':
                            $block = $this->block($id);
                            if (isset($block->attributes)) {
                                $inheritBlock = $outline ?
$this->cloneData($outline->block($inheritId)) : null;
                                $blockAttributes = $inheritBlock ?
                                   
array_diff_key((array)$inheritBlock->attributes, ['fixed'
=> 1, 'size' => 1]) : [];
                                $block->attributes =
(object)($blockAttributes + (array)$block->attributes);
                            }
                            break;
                        case 'children':
                            if (!empty($inherited->children)) {
                                // Deep clone children.
                                $item->children =
$this->cloneData($inherited->children);
                               
$this->initReferences($item->children, $this->getParentId($id),
null,
                                    ['outline' => $outlineId,
'include' => ['attributes', 'block']],
$index);
                            } else {
                                $item->children = [];
                            }
                            break;
                    }
                }

                if (!$outline || !isset($inherited->attributes)) {
                    // Remove inheritance information if outline
doesn't exist.
                    $item->inherit = new \stdClass;
                    unset($this->inherit[$item->id]);
                }
            }
        }

    }

    /**
     * @param array $items
     * @param object $parent
     * @param object $block
     * @param string $inherit
     * @param array $index
     */
    protected function initReferences(array $items = null, $parent = null,
$block = null, $inherit = null, array $index = null)
    {
        if ($items === null) {
            $items = $this->items;
            $this->references = [];
            $this->types = [];
            $this->inherit = [];
        }

        foreach ($items as $item) {
            if (is_object($item)) {
                $type = $item->type;
                $subtype = !empty($item->subtype) ? $item->subtype :
$type;

                if ($block) {
                    $this->parents[$item->id] = $parent;
                }
                if ($block) {
                    $this->blocks[$item->id] = $block;
                }

                if ($inherit && !$this->isLayoutType($type)) {
                    $item->inherit = (object) $inherit;
                    $item->inherit->particle = $item->id;

                    if
(isset($index['inherit'][$item->inherit->outline])
&& ($newId = array_search($item->id,
$index['inherit'][$item->inherit->outline], true))) {
                        $item->id = $newId;
                    } else {
                        $item->id = $this->id($type, $subtype);
                    }
                }

                if (isset($item->id)) {
                    if (isset($this->references[$item->id])) {
                        if ($type === 'block' || $type ===
'grid') {
                            $item->id = $this->id($type, $subtype);
                        }
//                        elseif (null === $inherit) {
//                            throw new \RuntimeException('Layout
reference conflict on #' . $item->id);
//                        }
                    }
                    $this->references[$item->id] = $item;
                    $this->types[$type][$subtype][$item->id] = $item;

                    if (!empty($item->inherit->outline)) {
                        $this->inherit[$item->id] = $item;
                    }
                } else {
                    $this->types[$type][$subtype][] = $item;
                }

                if (isset($item->children) &&
is_array($item->children)) {
                    $this->initReferences($item->children, $type ===
'section' ? $item : $parent, $type === 'block' ? $item
: null, $inherit, $index);
                }
            }
        }
    }

    /**
     * @param string $type
     * @param string $subtype
     * @param string $id
     * @return string
     */
    protected function id($type, $subtype = null, $id = null)
    {
        $result = [];
        if ($type !== 'particle') {
            $result[] = $type;
        }
        if ($subtype && ($subtype !== $type || $subtype ===
'position')) {
            $result[] = $subtype;
        }
        $key = implode('-', $result);

        $key_id = $key . '-'. $id;
        if (!$id || isset($this->references[$key_id])) {
            while ($id = rand(1000, 9999)) {
                $key_id = $key . '-'. $id;
                if (!isset($this->references[$key_id])) {
                    break;
                }
            }
        }

        return $key_id;
    }

    /**
     * Prepare block width sizes.
     *
     * @return $this
     */
    public function prepareWidths()
    {
        $this->init();

        $this->calcWidths($this->items);

        return $this;
    }

    /**
     * Recalculate block widths.
     *
     * @param array $items
     * @internal
     */
    protected function calcWidths(array &$items)
    {
        foreach ($items as $i => $item) {
            if (empty($item->children)) {
                continue;
            }

            $this->calcWidths($item->children);

            $dynamicSize = 0;
            $fixedSize = 0;
            $childrenCount = 0;
            foreach ($item->children as $child) {
                if ($child->type !== 'block') {
                    continue;
                }
                $childrenCount++;
                if (!isset($child->attributes->size)) {
                    $child->attributes->size = 100 /
count($item->children);
                }
                if (empty($child->attributes->fixed)) {
                    $dynamicSize += $child->attributes->size;
                } else {
                    $fixedSize += $child->attributes->size;
                }
            }

            if (!$childrenCount) {
                continue;
            }

            $roundSize = round($dynamicSize, 1);
            $equalized = isset($this->equalized[$childrenCount]) ?
$this->equalized[$childrenCount] : 0;

            // force-casting string for testing comparison due to weird PHP
behavior that returns wrong result
            if ($roundSize !== 100 && (string) $roundSize !==
(string) ($equalized * $childrenCount)) {
                $fraction = 0;
                $multiplier = (100 - $fixedSize) / ($dynamicSize ?: 1);
                foreach ($item->children as $child) {
                    if ($child->type !== 'block') {
                        continue;
                    }
                    if (!empty($child->attributes->fixed)) {
                        continue;
                    }

                    // Calculate size for the next item by taking account
the rounding error from the last item.
                    // This will allow us to approximate cumulating error
and fix it when rounding error grows
                    // over the rounding treshold.
                    $size = ($child->attributes->size * $multiplier)
+ $fraction;
                    $newSize = round($size);
                    $fraction = $size - $newSize;
                    $child->attributes->size = $newSize;
                }
            }
        }
    }

    /**
     * @param  string $name
     * @param  string $preset
     * @return static
     */
    public static function load($name, $preset = null)
    {
        if (!$name) {
            throw new \BadMethodCallException('Layout needs to have a
name');
        }

        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $layout = null;
        $filename =
$locator("gantry-config://{$name}/layout.yaml");

        // If layout file doesn't exists, figure out what preset was
used.
        if (!$filename) {

            // Attempt to load the index file.
            $indexFile =
$locator("gantry-config://{$name}/index.yaml");
            if ($indexFile || !$preset) {
                $index = static::loadIndex($name, true);
                $preset = $index['preset']['name'];
            }

            try {
                $layout = static::preset($preset);
            } catch (\Exception $e) {
                // Layout doesn't exist, do nothing.
            }
        } else {
            $layout = LayoutReader::read($filename);
        }

        return new static($name, $layout);
    }

    protected static function loadIndexFile($name)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        // Attempt to load the index file.
        $indexFile =
$locator("gantry-config://{$name}/index.yaml");
        if ($indexFile) {
            $file = CompiledYamlFile::instance($indexFile);
            $index = (array)$file->content();
            $file->free();
        } else {
            $index = [];
        }

        return $index;
    }

    /**
     * @param  string $name
     * @param  bool   $autoSave
     * @return array
     */
    public static function loadIndex($name, $autoSave = false)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $index = static::loadIndexFile($name);

        // Find out the currently used layout file.
        $layoutFile =
$locator("gantry-config://{$name}/layout.yaml");
        if (!$layoutFile) {
            /** @var Outlines $outlines */
            $outlines = $gantry['outlines'];

            $preset = isset($index['preset']['name']) ?
$index['preset']['name'] : $outlines->preset($name);
        }

        // Get timestamp for the layout file.
        $timestamp = $layoutFile ? filemtime($layoutFile) : 0;

        // If layout index file doesn't exist or is not up to date,
rebuild it.
        if (empty($index['timestamp']) ||
$index['timestamp'] != $timestamp ||
!isset($index['version']) || $index['version'] !=
static::VERSION) {
            $layout = isset($preset) ? new static($name,
static::preset($preset)) : static::instance($name);
            $layout->timestamp = $timestamp;

            if ($autoSave) {
                if (!$layout->timestamp) {
                    $layout->save();
                }
                $index = $layout->buildIndex();
                $layout->saveIndex($index);
            } else {
                $index = $layout->buildIndex();
            }
        }

        $index += [
            'name' => $name,
            'timestamp' => $timestamp,
            'preset' => [
                'name' => '',
                'image' =>
'gantry-admin://images/layouts/default.png'
            ],
            'positions' => [],
            'sections' => [],
            'inherit' => []
        ];

        return $index;
    }

    public function check(array $children = null)
    {
        if ($children === null) {
            $children = $this->items;
        }

        foreach ($children as $item) {
            if (!$item instanceof \stdClass) {
                throw new \RuntimeException('Invalid layout
element');
            }
            if (!isset($item->type)) {
                throw new \RuntimeException('Type missing');
            }
            if (!isset($item->subtype)) {
                throw new \RuntimeException('Subtype missing');
            }
            if (!isset($item->attributes)) {
                throw new \RuntimeException('Attributes
missing');
            }
            if (!is_object($item->attributes)) {
                throw new \RuntimeException('Attributes not
object');
            }
            if (isset($item->children)) {
                if (!is_array($item->children)) {
                    throw new \RuntimeException('Children not
array');
                }
                $this->check($item->children);
            }
        }
    }
}
PKO[�[.|�G	
	
Layout/LayoutReader.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Layout;

use Gantry\Component\File\CompiledYamlFile;

/**
 * Read layout from yaml file.
 */
class LayoutReader
{
    /**
     * Get layout version.
     *
     * @param array $data
     * @return int
     */
    public static function version(array &$data)
    {
        if (isset($data['version'])) {
            return $data['version'];
        }

        return isset($data['children']) &&
is_array($data['children']) ? 0 : 1;
    }

    /**
     * Make layout from array data.
     *
     * @param array $data
     * @return array
     */
    public static function data(array $data)
    {
        $version = static::version($data);
        $reader = static::getClass($version, $data);
        $result = $reader->load();

        // Make sure that all preset values are set by defining defaults.
        $result['preset'] += [
            'name' => '',
            'image' =>
'gantry-admin://images/layouts/default.png'
        ];

        return $result;
    }

    /**
     * Read layout from yaml file and return parsed version of it.
     *
     * @param string $file
     * @return array
     */
    public static function read($file)
    {
        if (!$file) {
            return [];
        }

        $file = CompiledYamlFile::instance($file);
        $content = (array) $file->content();
        $file->free();

        return static::data($content);
    }

    /**
     * Convert layout into file format.
     *
     * @param array $preset
     * @param array $structure
     * @param int $version
     * @return mixed
     */
    public static function store(array $preset, array $structure, $version
= 2)
    {
        $reader = static::getClass($version);

        return $reader->store($preset, $structure);
    }

    /**
     * @param int $version
     * @param array $data
     * @return object
     */
    protected static function getClass($version, array $data = [])
    {
        $class =
"Gantry\\Component\\Layout\\Version\\Format{$version}";

        if (!class_exists($class)) {
            throw new \RuntimeException('Layout file cound not be
read: unsupported version {$version}.');
        }

        return new $class($data);

    }
}
PKO[�[Q�E���Layout/Version/Format0.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Layout\Version;

/**
 * Read layout from Layout Manager yaml file.
 */
class Format0 extends Format1
{
    /**
     * @return array
     */
    public function load()
    {
        $data = &$this->data;

        $preset = isset($data['preset']) &&
is_array($data['preset']) ? $data['preset'] : [];

        $result = is_array($data['children']) ?
$this->object($data['children']) : [];

        $invisible = [
            'offcanvas' =>
$this->parse('offcanvas', [], 0),
            'atoms' => $this->parse('atoms', [],
0)
        ];
        foreach ($result as $key => &$item) {
            if (isset($invisible[$item->type])) {
                $invisible[$item->type] = $item;
                unset($result[$key]);
            }
        }

        $result += $invisible;

        $result = array_values($result);

        return ['preset' => $preset] + $result;
    }

    protected function object(array $items, $container = true)
    {
        foreach ($items as &$item) {
            $item = (object) $item;

            if (isset($item->attributes) &&
(is_array($item->attributes) || is_object($item->attributes))) {
                $item->attributes = (object) $item->attributes;
            } else {
                $item->attributes = (object) [];
            }

            if (!empty($item->children) &&
is_array($item->children)) {
                $item->children = $this->object($item->children,
false);
            }

            $this->normalize($item, $container);
        }

        return $items;
    }
}
PKQ[�[��+�#�#Layout/Version/Format1.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Layout\Version;

/**
 * Read layout from simplified yaml file.
 */
class Format1
{
    protected $scopes = [0 => 'grid', 1 =>
'block'];

    protected $data;

    protected $keys = [];

    public function __construct(array $data)
    {
        $this->data = $data;
    }

    public function load()
    {
        $data = &$this->data;

        // Check if we have preset.
        $preset = [];
        if (isset($data['preset']) &&
is_array($data['preset']) &&
isset($data['layout']) &&
is_array($data['layout'])) {
            $preset = $data['preset'];
            $data = $data['layout'];
        }

        // We have user entered file; let's build the layout.

        // Two last items are always offcanvas and atoms.
        $offcanvas = isset($data['offcanvas']) ?
$data['offcanvas'] : [];
        $atoms = isset($data['atoms']) ? $data['atoms']
: [];

        unset($data['offcanvas'], $data['atoms']);

        $data['offcanvas'] = $offcanvas;
        if ($atoms) {
            $data['atoms'] = $atoms;
        }

        $result = [];
        foreach ($data as $field => $params) {
            $child = $this->parse($field, (array) $params, 0);
            unset($child->size);

            $result[] = $child;
        }

        return ['preset' => $preset] + $result;
    }

    public function store(array $preset, array $structure)
    {
        return ['preset' => $preset, 'children'
=> $structure];
    }

    protected function normalize(&$item, $container = false)
    {
        if ($item->type === 'pagecontent') {
            // Update pagecontent to match the new standards.
            $item->type = 'system';
            if (!$item->subtype || $item->subtype ==
'pagecontent') {
                $item->subtype = 'content';
                $item->title = 'Page Content';
            } else {
                $item->subtype ='messages';
                $item->title = 'System Messages';
            }
        }

        if ($item->type === 'section') {
            // Update section to match the new standards.
            $section = strtolower($item->title);
            $item->id = $section;
            $item->subtype = (in_array($section, ['aside',
'nav', 'article', 'header',
'footer', 'main']) ? $section : 'section');
        } elseif ($item->type === 'offcanvas') {
            $item->id = $item->subtype = $item->type;
            unset ($item->attributes->name,
$item->attributes->boxed);
            return;
        } else {
            // Update all ids to match the new standards.
            $item->id = $this->id($item->type, $item->subtype);
        }

        if (!empty($item->attributes->extra)) {
            foreach ($item->attributes->extra as $i => $extra) {
                $v = reset($extra);
                $k = key($extra);
                if ($k === 'id') {
                    $item->id = preg_replace('/^g-/',
'', $v);
                    $item->attributes->id = $v;
                    unset ($item->attributes->extra[$i]);
                }
            }
            if (empty($item->attributes->extra)) {
                unset ($item->attributes->extra);
            }
        }

        $item->subtype = $item->subtype ?: $item->type;
        $item->layout = in_array($item->type, ['container',
'section', 'grid', 'block',
'offcanvas']);

        if (isset($item->attributes->boxed)) {
            // Boxed already set, just change boxed=0 to boxed=''
to use default settings.
            $item->attributes->boxed = $item->attributes->boxed
?: '';
            return;
        }

        if (!$container) {
            return;
        }

        // Update boxed model to match the new standards.
        if (isset($item->children) && count($item->children)
=== 1) {
            $child = reset($item->children);
            if ($item->type === 'container') {
                // Remove parent container only if the only child is a
section.
                if ($child->type === 'section') {
                    $child->attributes->boxed = 1;
                    $item = $child;
                }
                $item->attributes->boxed = '';
            } elseif ($child->type === 'container') {
                // Remove child container.
                $item->attributes->boxed = '';
                $item->children = $child->children;
            }
        }
    }

    /**
     * @param int|string $field
     * @param array $content
     * @param int $scope
     * @param bool|null $container
     * @return array
     */
    protected function parse($field, array $content, $scope, $container =
true)
    {
        if (is_numeric($field))  {
            // Row or block
            $type = $this->scopes[$scope];
            $result = (object) ['id' => null, 'type'
=> $type, 'subtype' => $type, 'layout' =>
true, 'attributes' => (object) []];
            $scope = ($scope + 1) % 2;
        } elseif (substr($field, 0, 9) == 'container') {
            // Container
            $type = 'container';
            $result = (object) ['id' => null, 'type'
=> $type, 'subtype' => $type, 'layout' =>
true, 'attributes' => (object) []];
            $id = substr($field, 10) ?: null;
            if ($id !== null) {
                $result->attributes->id = $id;
            }
        } else {
            // Section
            $list = explode(' ', $field, 2);
            $field = array_shift($list);
            $size = ((float) array_shift($list)) ?: null;
            $type = in_array($field, ['atoms',
'offcanvas']) ? $field : 'section';
            $subtype = in_array($field, ['aside',
'nav', 'article', 'header',
'footer', 'main']) ? $field : 'section';

            $result = (object) [
                'id' => null,
                'type' => $type,
                'subtype' => $subtype,
                'layout' => true,
                'title' => ucfirst($field),
                'attributes' => (object) ['id' =>
'g-' . $field]
            ];

            if ($size) {
                $result->size = $size;
            }
        }

        if (!empty($content)) {
            $result->children = [];
            foreach ($content as $child => $params) {
                if (is_array($params)) {
                    $child = $this->parse($child, $params, $scope,
false);
                } else {
                    $child = $this->resolve($params, $scope);
                }
                if (!empty($child->size)) {
                    $result->attributes->size = $child->size;
                }
                unset($child->size);
                $result->children[] = $child;
            }
        }

        $this->normalize($result, $container);

        return $result;
    }

    /**
     * @param string $field
     * @param int $scope
     * @return array
     */
    protected function resolve($field, $scope)
    {
        $list = explode(' ', $field, 2);
        $list2 = explode('-', array_shift($list), 2);
        $size = ((float) array_shift($list)) ?: null;
        $type = array_shift($list2);
        $subtype = array_shift($list2) ?: false;
        $title = ucfirst($subtype ?: $type);

        $attributes = new \stdClass;

        $attributes->enabled = 1;

        if ($subtype && $type === 'position') {
            $attributes->key = $subtype;
            $subtype = false;
        }

        $result = (object) ['id' => $this->id($type,
$subtype), 'title' => $title, 'type' => $type,
'subtype' => $subtype, 'attributes' =>
$attributes];
        $this->normalize($result);

        if ($scope > 1) {
            if ($size) {
                $result->attributes->size = $size;
            }
            return $result;
        }
        if ($scope <= 1) {
            $result = (object) ['id' =>
$this->id('block'), 'type' => 'block',
'subtype' => 'block', 'layout' => true,
'children' => [$result], 'attributes' => new
\stdClass];
            if ($size) {
                $result->attributes->size = $size;
            }
        }
        if ($scope == 0) {
            $result = (object) ['id' =>
$this->id('grid'), 'type' => 'grid',
'subtype' => 'grid', 'layout' => true,
'children' => [$result], 'attributes' => new
\stdClass];
        }

        return $result;
    }


    protected function id($type, $subtype = null)
    {
        if ($type === 'atoms') {
            return $type;
        }

        $result = [];
        if ($type !== 'particle' && $type !==
'atom') {
            $result[] = $type;
        }
        if ($subtype && $subtype !== $type) {
            $result[] = $subtype;
        }
        $key = implode('-', $result);

        while ($id = rand(1000, 9999)) {
            if (!isset($this->keys[$key][$id])) {
                break;
            }
        }

        $this->keys[$key][$id] = true;

        return $key . '-'. $id;
    }
}
PKQ[�[��]��E�ELayout/Version/Format2.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Layout\Version;

/**
 * Read layout from simplified yaml file.
 */
class Format2
{
    protected $scopes = [0 => 'grid', 1 =>
'block'];
    protected $sections = ['wrapper', 'container',
'section', 'grid', 'block',
'offcanvas'];
    protected $structures = ['div', 'section',
'aside', 'nav', 'article',
'header', 'footer', 'main'];

    protected $data;
    protected $structure;
    protected $content;
    protected $keys;

    /**
     * @param array $data
     */
    public function __construct(array $data = [])
    {
        $this->data = $data;
    }

    /**
     * @return array
     */
    public function load()
    {
        $data = &$this->data;

        // Parse layout.
        $result = [];
        foreach ($data['layout'] as $field => &$params) {
            if (!is_array($params)) {
                $params = [];
            }
            $child = $this->parse($field, $params);
            unset($child->size);

            $result[] = $child;
        }

        return ['preset' => $data['preset']] +
$result;
    }

    /**
     * @param array $preset
     * @param array $structure
     * @return array
     */
    public function store(array $preset, array $structure)
    {
        $this->structure = [];
        $this->content = [];

        $structure = ['children' =>
json_decode(json_encode($structure), true)];
        $structure = $this->build($structure);

        $result = [
            'version' => 2,
            'preset' => $preset,
            'layout' => $structure
        ];

        if ($this->structure) {
            $result['structure'] = $this->structure;
        }
        if ($this->content) {
            $result['content'] = $this->content;
        }

        return $result;
    }

    /**
     * @param int|string $field
     * @param array $content
     * @param int $scope
     * @param object $parent
     * @return array
     */
    protected function parse($field, array &$content, $scope = 0,
$parent = null)
    {
        if (is_numeric($field)) {
            // Row or block
            $result = (object)['id' =>
$this->id($this->scopes[$scope]), 'type' =>
$this->scopes[$scope], 'subtype' =>
$this->scopes[$scope], 'layout' => true,
'attributes' => (object)[]];
            $scope = ($scope + 1) % 2;

        } else {
            list ($type, $subtype, $id, $size, $section_id, $boxed) =
$this->parseSectionString($field);

            if ($type == 'grid') {
                $scope = 1;
            }
            if ($type == 'block') {
                $scope = 0;
            }

            // Build object.
            $result =
isset($this->data['structure'][$section_id]) ? (array)
$this->data['structure'][$section_id] : [];
            $result += [
                'id' => $section_id,
                'layout' => true,
                'type' => $type,
                'subtype' => $subtype,
                'title' => $this->getTitle($type, $subtype,
$id),
                'attributes' => []
            ];
            if (isset($boxed) &&
!isset($result['attributes']['boxed'])) {
                $result['attributes']['boxed'] =
$boxed;
            }
            if ($parent && $parent->type === 'block'
&& !empty($result['block'])) {
                $parent->attributes = (object)
($result['block'] + (array) $parent->attributes);
            }
            unset ($result['block']);

            $result = (object) $result;
            $result->attributes = (object) $result->attributes;
            if (isset($result->inherit)) {
                $result->inherit = (object) $result->inherit;
            }

            if ($size) {
                $result->size = $size;
            }
            if (($type === 'grid' || $type === 'block')
&& !isset($result->attributes->id)) {
                $result->attributes->id = $section_id;
            }
        }

        if (!empty($content)) {
            $result->children = [];
            foreach ($content as $child => &$params) {
                if (!$params && !is_array($params)) {
                    $params = [];
                }
                if (is_array($params)) {
                    $child = $this->parse($child, $params, $scope,
$result);
                } else {
                    $child = $this->resolve($params, $scope, $result);
                }
                if (!empty($child->size)) {
                    $result->attributes->size = $child->size;
                }
                unset($child->size);
                $result->children[] = $child;
            }
        }

        return $result;
    }

    /**
     * @param string $field
     * @param int $scope
     * @param object $parent
     * @return array
     */
    protected function resolve($field, $scope, $parent)
    {
        list ($type, $subtype, $id, $size, $content_id) =
$this->parseContentString($field);

        $title = $this->getTitle($type, $subtype, $id);

        $result = isset($this->data['content'][$content_id]) ?
(array) $this->data['content'][$content_id] : [];
        $result += ['id' => $this->id($type, $subtype,
$id), 'title' => $title, 'type' => $type,
'subtype' => $subtype, 'attributes' => []];

        $result['attributes'] = (object)
($result['attributes'] + ['enabled' => 1]);
        if (isset($result['inherit'])) {
            $result['inherit'] = (object)
$result['inherit'];
        }

        if (isset($result['block'])) {
            $block = $result['block'];
            unset ($result['block']);
        }

        $result = (object) $result;

        if ($type === 'position' &&
!isset($result->attributes->key) && !in_array($subtype,
['module', 'widget'])) {
            $result->attributes->key = $id;
        }
        if ($scope > 1) {
            if ($parent->type === 'block' &&
!empty($block)) {
                $parent->attributes = (object) ($block + (array)
$parent->attributes);
            }
            if ($size) {
                $result->attributes->size = $size;
            }
        }
        if ($scope <= 1) {
            $result = (object) ['id' =>
$this->id('block'), 'type' => 'block',
'subtype' => 'block', 'layout' => true,
'children' => [$result], 'attributes' => new
\stdClass];
            if (!empty($block)) {
                $result->attributes = (object) $block;
            }
            if ($size) {
                $result->attributes->size = $size;
            }
        }
        if ($scope == 0) {
            $result = (object) ['id' =>
$this->id('grid'), 'type' => 'grid',
'subtype' => 'grid', 'layout' => true,
'children' => [$result], 'attributes' => new
\stdClass];
        }

        return $result;
    }

    /**
     * @param array $content
     * @return array|null
     */
    protected function build(array &$content)
    {
        $result = [];
        $ctype = isset($content['type']) ?
$content['type'] : null;

        if (in_array($ctype, ['grid', 'block'])) {
            if (empty($content['attributes']['id']) ||
$content['attributes']['id'] ===
$content['id']) {
                unset ($content['attributes']['id']);
            }
        }
        if ($ctype === 'block') {
            if (empty($content['attributes']['extra']))
{
                unset
($content['attributes']['extra']);
            }
            if (empty($content['attributes']['fixed']))
{
                unset
($content['attributes']['fixed']);
            }
        }
        if ($ctype === 'section') {
            if (empty($content['attributes']['extra']))
{
                unset
($content['attributes']['extra']);
            }
        }

        if (!isset($content['children'])) {
            $content['children'] = [];
        }
        unset ($content['layout']);

        // Clean up all items for saving.
        foreach ($content['children'] as &$child) {
            $size = null;
            $id = $child['id'];
            $type = $child['type'];
            $subtype = $child['subtype'];
            $isSection = in_array($type, $this->sections);

            if (empty($child['inherit']['outline']) ||
empty($child['inherit']['include'])) {
                unset ($child['inherit']);
            } else {
                foreach ($child['inherit']['include']
as $include) {
                    switch ($include) {
                        case 'attributes':
                            unset($child['attributes']);
                            break;
                        case 'block':
                            if ($ctype === 'block') {
                                // Keep block size and fixed status.
                                $attributes =
!empty($content['attributes']) ? $content['attributes']
: [];
                                $content['attributes'] =
array_intersect_key($attributes, ['fixed' => 1,
'size' => 1]);
                            }
                            break;
                        case 'children':
                            $child['children'] = [];
                            break;
                    }
                }
            }

            if (!$isSection) {
                // Special handling for positions.
                if ($type === 'position') {
                    // TODO: we may want to simplify position id, but we
need to take into account multiple instances of the same position key.
/*
                    if (!$subtype || $subtype === 'position') {
                        $id = 'position-' .
(isset($child['attributes']['key']) ?
$child['attributes']['key'] : rand(1000,9999));
                        unset
($child['attributes']['key']);
                    }
*/
                    unset
($child['attributes']['title']);
                }

                $value = $id;
                if
(!empty($child['attributes']['enabled'])) {
                    unset
($child['attributes']['enabled']);
                }
            } else {
                // Recursively handle structure.
                $value = $this->build($child);
            }

            // Clean up defaults.
            if (empty($child['title']) ||
$child['title'] === 'Untitled' ||
$child['title'] === $this->getTitle($type, $subtype, $id)) {
                unset ($child['title']);
            }
            if (!$subtype || $subtype === $type) {
                unset ($child['subtype']);
            }

            // Remove id and children as we store data in flat structure
with id being the key.
            unset ($child['id'], $child['children']);

            if ($type === 'offcanvas' &&
isset($child['attributes']['name'])) {
                unset ($child['attributes']['name']);
            }

            if ($ctype === 'block') {
                // Embed size into array key/value.
                if
(isset($content['attributes']['size']) &&
$content['attributes']['size'] != 100) {
                    $size =
$content['attributes']['size'];
                }
                unset ($content['attributes']['size']);
                // Embed parent block.
                if (!empty($content['attributes'])) {
                    $child['block'] =
$content['attributes'];
                    unset ($content['attributes']);
                }
            }

            if (isset($child['attributes']['size'])) {
                if ($child['attributes']['size'] != 100
&& is_string($value)) {
                    $size =
$child['attributes']['size'];
                }
                unset ($child['attributes']['size']);
            }

            // Remove attributes if there aren't any.
            if (empty($child['attributes'])) {
                unset ($child['attributes']);
            }

            // Special handling for grid and block elements.
            if (in_array($type, ['grid', 'block'])
&& count($child) === 1 && isset($child['type']))
{
                $id = null;
            }

            // Check if type and subtype can be generated from the id.
            if ($subtype &&
(preg_match("/^{$type}-{$subtype}(-|$)/", $id))
                || (in_array($type, ['section',
'particle']) &&
preg_match("/^{$subtype}(-|$)/", $id))) {
                unset ($child['type'],
$child['subtype']);
            } elseif (preg_match("/^{$type}(-|$)/", $id)) {
                unset ($child['type']);
            }

            // Add item configuration if not empty.
            if ($id && !empty($child)) {
                if (!is_string($value)) {
                    $this->structure[$id] = $child;
                } else {
                    $this->content[$id] = $child;
                }
            }

            // Add item to the layout.
            if (!is_string($value)) {
                // Add structural item.
                if ($id) {
                    // Sections and other complex items.
                    $id =
isset($child['attributes']['boxed']) ?
"/{$id}/" : $id;
                    $result[trim("{$id} {$size}")] = $value;
                } elseif (!empty($value)) {
                    // Simple grid / block item.
                    $result[] = $value;
                }
            } else {
                // Add content item.
                $result[] = trim("{$value} {$size}");
            }
        }

        // TODO: maybe collapse grid as well?
        if ($ctype && in_array($ctype, ['block'])
&& count($result) <= 1 && key($result) === 0) {
            unset ($this->structure[$content['id']]);
            return reset($result) ?: null;
        }

        return $result;
    }

    /**
     * @param string $string
     * @return array
     */
    protected function parseSectionString($string)
    {
        // Extract: "[section-id] [size]".
        $list = explode(' ', $string, 2);
        $section_id = array_shift($list);
        $size = ((float) array_shift($list)) ?: null;

        // Extract slashes from "/[section-id]/".
        $boxedLeft = $section_id[0] === '/';
        $boxedRight = $section_id[strlen($section_id)-1] === '/';
        $boxed = ($boxedLeft && $boxedRight ? '' :
($boxedLeft ? '1' : ($boxedRight ? '0' : null)));
        $section_id = trim($section_id, '/');

        // Extract section id if it exists: "[section]-[id]".
        $list = explode('-', $section_id, 2);

        // Get section and its type.
        $section = reset($list);
        $type = (in_array($section, $this->sections)) ? $section :
'section';
        $subtype = ($type !== 'section' || in_array($section,
$this->structures)) ? $section : 'section';

        // Extract id.
        if ($type == 'section' && in_array($section,
$this->structures)) {
            $id = array_pop($list);
        } else {
            $id = $section_id;
        }

        return [$type, $subtype, $id, $size, $section_id, $boxed];
    }

    /**
     * @param string $string
     * @return array
     */
    protected function parseContentString($string)
    {
        // Extract: "[type-subtype] [size]".
        $list = explode(' ', $string, 2);
        $content_id = array_shift($list);
        $size = ((float) array_shift($list)) ?: null;

        // Extract sub-type if it exists:
"[type]-[subtype]-[id]".
        $list = explode('-', $content_id);

        // Get type, subtype and id.
        $type = reset($list);
        $test = end($list);
        $id = ((string)(int) $test === (string) $test) ? array_pop($list) :
null;
        if (in_array($type, ['system', 'position',
'particle', 'spacer'])) {
            array_shift($list);
        } else {
            $type = 'particle';
        }
        $subtype = implode('-', $list);

        if ($type === 'position' && !in_array($subtype,
['module', 'widget'])) {
            $id = ($subtype ?: $type) . ($id !== null ? "-{$id}"
: '');
            $subtype = 'position';
        }

        return [$type, $subtype ?: $type, $id, $size, $content_id];
    }

    /**
     * @param string $type
     * @param string $subtype
     * @param string $id
     * @return string
     */
    protected function getTitle($type, $subtype, $id)
    {
        if (in_array($type, $this->sections)) {
            if ($type === 'offcanvas') {
                return 'Offcanvas';
            }

            if ($type === 'grid' || $type === 'block')
{
                return null;
            }

            return ucfirst((string)(int) $id === (string) $id ? ($subtype
?: $type) . "-{$id}" : $id);
        }

        if ($type === 'position' && !in_array($subtype,
['module', 'widget'])) {
            return
ucfirst(preg_replace('/^position-(.*?[a-z])/ui', '\1',
$id));
        }

        if ($type === 'system') {
            if ($subtype === 'messages') {
                return 'System Messages';
            }
            if ($subtype === 'content') {
                return 'Page Content';
            }
        }

        return ucfirst($subtype ?: $type);
    }

    /**
     * @param string $type
     * @param string $subtype
     * @param string $id
     * @return string
     */
    protected function id($type, $subtype = null, $id = null)
    {
        $result = [];
        if ($type !== 'particle') {
            $result[] = $type;
        }
        if ($subtype && $subtype !== $type) {
            $result[] = $subtype;
        }
        $key = implode('-', $result);

        if (!$id || isset($this->keys[$key][$id])) {
            while ($id = rand(1000, 9999)) {
                if (!isset($this->keys[$key][$id])) {
                    break;
                }
            }
        }

        $this->keys[$key][$id] = true;

        return $key . '-'. $id;
    }
}
PKQ[�[j픞--Menu/AbstractMenu.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Menu;

use Gantry\Component\Config\Config;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Gantry\GantryTrait;
use RocketTheme\Toolbox\ArrayTraits\ArrayAccessWithGetters;
use RocketTheme\Toolbox\ArrayTraits\Countable;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\Iterator;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

abstract class AbstractMenu implements \ArrayAccess, \Iterator, \Countable
{
    use GantryTrait, ArrayAccessWithGetters, Iterator, Export, Countable;

    protected $default;
    protected $base;
    protected $active;
    protected $params;
    protected $override = false;
    protected $config;

    /**
     * @var array|Item[]
     */
    protected $items;

    /**
     * @var Config|null
     */
    protected $pathMap;

    protected $defaults = [
        'menu' => '',
        'base' => '/',
        'startLevel' => 1,
        'maxLevels' => 0,
        'showAllChildren' => true,
        'highlightAlias' => true,
        'highlightParentAlias' => true
    ];

    abstract public function __construct();

    /**
     * Return list of menus.
     *
     * @return array
     */
    abstract public function getMenus();


    /**
     * Return default menu.
     *
     * @return string
     */
    public function getDefaultMenuName()
    {
        return null;
    }

    /**
     * Returns true if the platform implements a Default menu.
     *
     * @return boolean
     */
    public function hasDefaultMenu()
    {
        return false;
    }

    /**
     * Return active menu.
     *
     * @return string
     */
    public function getActiveMenuName()
    {
        return null;
    }

    /**
     * Returns true if the platform implements an Active menu.
     *
     * @return boolean
     */
    public function hasActiveMenu()
    {
        return false;
    }

    /**
     * @param array $params
     * @param Config $menu
     * @return AbstractMenu
     */
    public function instance(array $params = [], Config $menu = null)
    {
        $params = $params + $this->defaults;

        $menus = $this->getMenus();

        if (!$menus) {
            throw new \RuntimeException('Site does not have
menus', 404);
        }
        if (empty($params['menu'])) {
            $params['menu'] = $this->getDefaultMenuName();
            if (!$params['menu'] &&
!empty($params['admin'])) {
                // In admin just select the first menu if there isn't
default menu to be selected.
                $params['menu'] = reset($menus);
            };
        } elseif ($params['menu'] == '-active-') {
            $params['menu'] = $this->getActiveMenuName();
        }
        if (!$params['menu']) {
            throw new \RuntimeException('No menu selected', 404);
        }
        if (!in_array($params['menu'], $menus)) {
            throw new \RuntimeException('Menu not found', 404);
        }

        $instance = clone $this;
        $instance->params = $params;

        if ($menu) {
            $instance->override = true;
            $instance->config = $menu;
        } else {
            $instance->config = null;
        }

        $config = $instance->config();
        $items = isset($config['items']) ?
$config['items'] : [];

        // Create menu structure.
        $instance->init($params);

        // Get menu items from the system (if not specified otherwise).
        if ($config->get('settings.type') !==
'custom') {
            $instance->getList($params, $items);
        }

        // Add custom menu items.
        $instance->addCustom($params, $items);

        // Sort menu items.
        $instance->sortAll();

        return $instance;
    }

    /**
     * Get menu configuration.
     *
     * @return Config
     */
    public function config()
    {
        if (!$this->config) {
            $gantry = static::gantry();

            /** @var UniformResourceLocator $locator */
            $locator = $gantry['locator'];

            $menu = $this->params['menu'];

            $file =
CompiledYamlFile::instance($locator("gantry-config://menu/{$menu}.yaml"));
            $this->config = new Config($file->content());
            $this->config->def('settings.title',
ucfirst($menu));
            $file->free();
        }

        return $this->config;
    }

    public function name()
    {
        return $this->params['menu'];
    }

    public function root()
    {
        return $this->offsetGet('');
    }

    public function ordering()
    {
        $list = [];
        foreach ($this->items as $name => $item) {
            $groups = $item->groups();
            if (count($groups) == 1 && empty($groups[0])) {
                continue;
            }

            $list[$name] = [];
            foreach ($groups as $col => $children) {
                $list[$name][$col] = [];
                foreach ($children as $child) {
                    $list[$name][$col][] = $child->path;
                }
            }
        }

        return $list;
    }

    public function items($withdefaults = true)
    {
        $list = [];
        foreach ($this->items as $key => $item) {
            if ($key !== '') {
                $list[$item->path] = $item->toArray($withdefaults);
            }
        }

        return $list;
    }

    public function settings()
    {
        return (array) $this->config()->get('settings');
    }

    /**
     * @return object
     */
    public function getBase()
    {
        return $this->offsetGet($this->base);
    }

    /**
     * @return object
     */
    public function getDefault()
    {
        return $this->offsetGet($this->default);
    }

    /**
     * @return object
     */
    public function getActive()
    {
        return $this->offsetGet($this->active);
    }

    /**
     * @return string|null
     */
    public function getCacheId()
    {
        return $this->active ?: '-inactive-';
    }

    public function isActive($item)
    {
        $active = $this->getActive();

        if ($active && $item && ($active->path ===
$item->path || strpos($active->path, $item->path . '/')
=== 0)) {
            return true;
        }

        return false;
    }

    public function isCurrent($item)
    {
        $active = $this->getActive();

        return $item && $active && $item->path ===
$active->path;
    }

    public function init(&$params)
    {
        $this->items = ['' => new Item($this, '',
['layout' => 'horizontal'])];
    }

    public function add(Item $item)
    {
        $this->items[$item->path] = $item;

        // If parent exists, assign menu item to its parent; otherwise
ignore menu item.
        if (isset($this->items[$item->parent_id])) {
            $this->items[$item->parent_id]->addChild($item);
        } elseif (!$this->items['']->count()) {
            $this->items[$item->parent_id] =
$this->items[''];
            $this->items[$item->parent_id]->addChild($item);
        }

        return $this;
    }

    /**
     * Get menu items from the platform.
     *
     * @param int $levels
     * @return array
     */
    abstract protected function getItemsFromPlatform($levels);

    /**
     * Get base menu item.
     *
     * If itemid is not specified or does not exist, return active menu
item.
     * If there is no active menu item, fall back to home page for the
current language.
     * If there is no home page, return null.
     *
     * @param   string  $path
     *
     * @return  string
     */
    abstract protected function calcBase($path);

    /**
     * Get a list of the menu items.
     *
     * @param  array  $params
     * @param  array  $items
     */
    abstract public function getList(array $params, array $items);

    /**
     * Add custom menu items.
     *
     * @param  array  $params
     * @param array $items
     */
    public function addCustom(array $params, array $items)
    {
        $start   = $params['startLevel'];
        $max     = $params['maxLevels'];
        $end     = $max ? $start + $max - 1 : 0;

        $config = $this->config();
        $type = $config->get('settings.type');

        // Add custom menu elements.
        foreach ($items as $route => $item) {
            if ($type !== 'custom' &&
(!isset($item['type']) || $item['type'] !==
'particle')) {
                continue;
            }

            $tree = explode('/', $route);
            $parentTree = $tree;
            array_pop($parentTree);

            // Enabled state should equal particle setting.
            $item['enabled'] =
!isset($item['options']['particle']['enabled'])
||
!empty($item['options']['particle']['enabled']);
            $item['level'] = $level = count($tree);
            $item['parent_id'] = implode('/',
$parentTree);
            if (($start && $start > $level)
                || ($end && $level > $end)
                // TODO: Improve. In the mean time Item::add() handles this
part.
                // || ($start > 1 && !in_array($tree[$start -
2], $tree))
            ) {
                continue;
            }
            $item = new Item($this, $route, $item);
            $this->add($item);
        }
    }

    /**
     * @param array $ordering
     * @param string $path
     * @param array $map
     */
    public function sortAll(array $ordering = null, $path = '',
$map = null)
    {
        if ($ordering === null) {
            $config = $this->config();
            $ordering = $config['ordering'] ?
$config['ordering'] : [];
        }

        if (!isset($this->items[$path]) ||
!$this->items[$path]->hasChildren()) {
            return;
        }

        if ($map === null) {
            $map = $this->pathMap ? $this->pathMap->toArray() :
[];
        }

        $order = [];
        $newMap = [];
        $item = $this->items[$path];
        if ($this->isAssoc($ordering)) {
            foreach ($ordering as $key => $value) {
                if ($map) {
                    $newMap = isset($map[$key]['children']) ?
$map[$key]['children'] : [];
                    $key = isset($map[$key]['path']) ?
basename($map[$key]['path']) : $key;
                    $order[$key] = $value;
                }

                if (is_array($value)) {
                    $this->sortAll($value, $path ? $path . '/'
. $key : $key, $newMap);
                }
            }

            $item->sortChildren($order ?: $ordering);
        } else {
            foreach ($ordering as $i => $group) {
                foreach ($group as $key => $value) {
                    if ($map) {
                        $newMap = isset($map[$key]['children']) ?
$map[$key]['children'] : [];
                        $key = isset($map[$key]['path']) ?
basename($map[$key]['path']) : $key;
                        $order[$i][$key] = $value;
                    }

                    if (is_array($value)) {
                        $this->sortAll($value, $path ? $path .
'/' . $key : $key, $newMap);
                    }
                }
            }

            $item->groupChildren($order ?: $ordering);
        }

    }

    protected function isAssoc(array $array)
    {
        return (array_values($array) !== $array);
    }
}
PKQ[�[��1D�&�&
Menu/Item.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Menu;

use RocketTheme\Toolbox\ArrayTraits\ArrayAccessWithGetters;
use RocketTheme\Toolbox\ArrayTraits\Export;

/**
 * @property string $id
 * @property string $type
 * @property string $path
 * @property string $alias
 * @property string $title
 * @property string $link
 * @property string $parent_id
 * @property string $layout
 * @property int $browserNav
 * @property bool $menu_text
 * @property bool $visible
 * @property int $group
 * @property int $level
 */
class Item implements \ArrayAccess, \Iterator, \Serializable, \Countable
{
    use ArrayAccessWithGetters, Export;

    const VERSION = 1;

    protected $items;
    protected $menu;
    protected $groups = [];
    protected $children = [];
    protected $url;

    protected static $defaults = [
        'id' => 0,
        'type' => 'link',
        'path' => null,
        'alias' => null,
        'title' => null,
        'link' => null,
        'parent_id' => null,
        'layout' => 'list',
        'target' => '_self',
        'dropdown' => '',
        'icon' => '',
        'image' => '',
        'subtitle' => '',
        'hash' => '',
        'class' => '',
        'icon_only' => false,
        'enabled' => true,
        'visible' => true,
        'group' => 0,
        'columns' => [],
        'level' => 0,
        'link_title' => '',
        'anchor_class' => ''
    ];

    public function __construct(AbstractMenu $menu, $name, array $item =
[])
    {
        $this->menu = $menu;

        $tree = explode('/', $name);
        $alias = array_pop($tree);
        $parent = implode('/', $tree);

        // As we always calculate parent (it can change), prevent old one
from being inserted.
        unset($item['parent_id']);

        $this->items = $item + [
            'id' => preg_replace('|[^a-z0-9]|i',
'-', $name) ?: 'root',
            'path' => $name,
            'alias' => $alias,
            'title' => ucfirst($alias),
            'link' => $name,
            'parent_id' => $parent != '.' ? $parent
: '',
        ] + static::$defaults;
    }

    public function getDropdown()
    {
        if (!$this->items['dropdown']) {
            return count($this->groups()) > 1 ? 'fullwidth'
: 'standard';
        }

        return $this->items['dropdown'];
    }

    public function serialize()
    {
        // FIXME: need to create collection class to gather the sibling
data.
        return serialize([
            'version' => static::VERSION,
            'items' => $this->items,
            'groups' => $this->groups,
            'children' => $this->children,
            'url' => $this->url
        ]);
    }

    public function unserialize($serialized)
    {
        // FIXME: need to create collection class to gather the sibling
data.
        $data = unserialize($serialized);

        if (!isset($data['version']) &&
$data['version'] === static::VERSION) {
            throw new \UnexpectedValueException('Serialized data is
not valid');
        }

        $this->items = $data['items'];
        $this->groups =  $data['groups'];
        $this->children = $data['children'];
        $this->url = $data['url'];
    }

    /**
     * @param  string|null|bool $url
     * @return string
     */
    public function url($url = false)
    {
        if ($url !== false) {
            $this->url = $url;
        }
        return $this->url;
    }

    /**
     * @return AbstractMenu
     * @deprecated Need to break relationship to the menu and use a
collection instead.
     */
    protected function menu()
    {
        return $this->menu;
    }

    /**
     * @return Item
     */
    public function parent()
    {
        return $this->menu()[$this->items['parent_id']];
    }

    public function columnWidth($column)
    {
        if (isset($this->items['columns'][$column])) {
            return $this->items['columns'][$column];
        }

        return 100 / count($this->groups());
    }

    public function groups()
    {
        if ($this->groups) {
            $list = [];
            foreach ($this->groups as $i => $group) {
                $list[$i] = [];
                foreach ($group as $path) {
                    $list[$i][] = $this->menu()[$path];
                }
            }
            return $list;
        }
        return [$this->children()];
    }

    public function children()
    {
        $list = [];
        foreach ($this as $child) {
            $list[] = $child;
        }
        return $list;
    }

    public function hasChildren()
    {
        return !empty($this->children);
    }

    public function getGroup($i)
    {
        $groups = $this->groups();
        $i = (int) $i;

        return isset($groups[$i]) ? $groups[$i] : [];
    }

    public function update(array $data)
    {
        $this->items = array_replace($this->items, $data);

        return $this;
    }

    public function addChild(Item $child)
    {
        $child->level = $this->level + 1;
        $child->parent_id = $this->path;
        $this->children[$child->alias] = $child->path;

        return $this;
    }

    public function removeChild(Item $child)
    {
        unset($this->children[$child->alias]);

        return $this;
    }

    public function sortChildren($ordering)
    {
        // Array with keys that point to the items.
        $children =& $this->children;

        if ($children) {
            if (is_array($ordering)) {
                // Remove extra items from ordering and reorder.
                $children = array_replace(array_intersect_key($ordering,
$children), $children);
            } else {
                switch ((string) $ordering) {
                    case 'abc':
                        // Alphabetical ordering.
                        ksort($children, SORT_NATURAL);
                        break;
                    case 'cba':
                        // Reversed alphabetical ordering.
                        krsort($children, SORT_NATURAL);
                        break;
                }
            }
        }

        return $this;
    }


    public function reverse()
    {
        array_reverse($this->children, true);
        array_reverse($this->groups, true);

        return $this;
    }

    public function groupChildren(array $groups)
    {
        // Array with keys that point to the items.
        $children =& $this->children;

        if ($children) {
            $menu = $this->menu();
            $ordered = [];

            // Create empty groups.
            $this->groups = array_fill(0, max(1,
count($this->items['columns'])), []);

            foreach ($groups as $i => $ordering) {
                if (!is_array($ordering)) {
                    continue;
                }

                // Get the items for this group with proper ordering.
                $group = array_replace(
                    array_intersect_key($ordering, $children),
array_intersect_key($children, $ordering)
                );

                // Assign each menu items to the group.
                $group = array_map(
                    function($value) use ($i, $menu) {
                        $item = $menu[$value];
                        $item->group = $i;
                        return $value;
                    },
                    $group
                );

                // Update remaining children.
                $children = array_diff_key($children, $ordering);

                // Build child ordering.
                $ordered += $group;

                // Add items to the current group.
                $this->groups[$i] = $group;
            }

            if ($children) {
                // Add leftover children to the ordered list and to the
first group.
                $ordered += $children;
                $this->groups[0] += $children;
            }

            // Reorder children by their groups.
            $children = $ordered;
        }

        return $this;
    }

    // Implements \Iterator

    /**
     * Returns the current child.
     *
     * @return Item
     */
    public function current()
    {
        return $this->menu()[current($this->children)];
    }

    /**
     * Returns the key of the current child.
     *
     * @return mixed  Returns scalar on success, or NULL on failure.
     */
    public function key()
    {
        return key($this->children);
    }

    /**
     * Moves the current position to the next child.
     *
     * @return void
     */
    public function next()
    {
        next($this->children);
    }

    /**
     * Rewinds back to the first child.
     *
     * @return void
     */
    public function rewind()
    {
        reset($this->children);
    }

    /**
     * Count number of children.
     *
     * @return int
     */
    public function count()
    {
        return count($this->children);
    }

    /**
     * This method is called after Iterator::rewind() and Iterator::next()
to check if the current position is valid.
     *
     * @return bool  Returns TRUE on success or FALSE on failure.
     */
    public function valid()
    {
        return key($this->children) !== null;
    }

    /**
     * Convert object into an array.
     *
     * @return array
     */
    public function toArray($withDefaults = true)
    {
        $items = $this->items;

        if (!$withDefaults) {
            foreach (static::$defaults as $key => $value) {
                if ($items[$key] === $value) {
                    unset($items[$key]);
                }
            }
        }

        return $items;
    }
}
PKQ[�[�{{GLGLOutline/OutlineCollection.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Outline;

use FilesystemIterator;
use Gantry\Component\Collection\Collection;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Layout\Layout;
use Gantry\Framework\Atoms;
use RocketTheme\Toolbox\DI\Container;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceIterator;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class OutlineCollection extends Collection
{
    /**
     * @var Container
     */
    protected $container;

    /**
     * @var string
     */
    protected $path;

    /**
     * @param Container $container
     * @param array $items
     */
    public function __construct(Container $container, $items = [])
    {
        $this->container = $container;
        $this->items = $items;
    }

    /**
     * @param string $id
     * @return string|null
     */
    public function name($id)
    {
        return isset($this->items[$id]) ? $this->items[$id] : null;
    }

    /**
     * @param string $id
     * @return string
     */
    public function title($id)
    {
        return isset($this->items[$id]) ? $this->items[$id] : $id;
    }


    public function all()
    {
        return $this;
    }

    public function system()
    {
        foreach ($this->items as $key => $item) {
            if (substr($key, 0, 1) !== '_') {
                unset($this->items[$key]);
            }
        }

        return $this;
    }

    public function user()
    {
        foreach ($this->items as $key => $item) {
            if (substr($key, 0, 1) === '_' || $key ==
'default') {
                unset($this->items[$key]);
            }
        }

        return $this;
    }

    public function filter(array $include = null)
    {
        if ($include !== null) {
            foreach ($this->items as $key => $item) {
                if (!in_array($key, $include)) {
                    unset($this->items[$key]);
                }
            }
        }

        return $this;
    }

    /**
     * Returns list of all positions defined in all outlines.
     *
     * @return array
     */
    public function positions()
    {
        $list = [];
        foreach ($this->items as $name => $title) {
            try {
                $index = Layout::index($name);

                $list += $index['positions'];
            } catch (\Exception $e) {
                // Layout cannot be read. We will just skip it instead of
throwing an exception.
            }
        }

        return $list;
    }

    /**
     * @param string $section
     * @param bool $includeInherited
     * @return array
     */
    public function getOutlinesWithSection($section, $includeInherited =
true)
    {
        $list = [];
        foreach ($this->items as $name => $title) {
            try {
                $index = Layout::index($name);
            } catch (\Exception $e) {
                // Layout cannot be read. We will just skip it instead of
throwing an exception.
                continue;
            }

            if (isset($index['sections'][$section])) {
                if (!$includeInherited) {
                    foreach ($index['inherit'] as $outline =>
$items) {
                        if (is_array($items) && in_array($section,
$items)) {
                            continue 2;
                        }
                    }
                }
                $list[$name] = $title;
            }
        }

        return $list;
    }

    /**
     * @param string $particle
     * @param bool $includeInherited
     * @return array
     */
    public function getOutlinesWithParticle($particle, $includeInherited =
true)
    {
        $list = [];
        foreach ($this->items as $name => $title) {
            try {
                $index = Layout::index($name);
            } catch (\Exception $e) {
                // Layout cannot be read. We will just skip it instead of
throwing an exception.
                continue;
            }

            if (isset($index['particles'][$particle])) {
                $ids = $index['particles'][$particle];
                if (!$includeInherited &&
!empty($index['inherit'])) {
                    foreach ($index['inherit'] as $items) {
                        foreach ((array) $items as $id => $inheritId) {
                            unset($ids[$id]);
                        }
                    }
                }
                if ($ids) {
                    $list[$name] = $title;
                }
            }
        }

        return $list;
    }

    /**
     * @param string $type
     * @param bool $includeInherited
     * @return array
     */
    public function getOutlinesWithAtom($type, $includeInherited = true)
    {
        $list = [];

        foreach ($this->items as $name => $title) {
            $file =
CompiledYamlFile::instance("gantry-theme://config/{$name}/page/head.yaml");
            $index = $file->content();
            $file->free();
            if (isset($index['atoms'])) {
                foreach ($index['atoms'] as $atom) {
                    if (!empty($atom['id']) &&
$atom['type'] === $type && ($includeInherited ||
empty($atom['inherit']))) {
                        $list[$name] = $title;
                    }
                }
            }
        }

        return $list;
    }

    /**
     * @param string $particle
     * @param bool $includeInherited
     * @return array
     */
    public function getAllParticleInstances($particle, $includeInherited =
true)
    {
        $list = [];
        foreach ($this->items as $name => $title) {
            $list += $this->getParticleInstances($name, $particle,
$includeInherited);
        }

        return $list;
    }

    /**
     * @param string $outline
     * @param string $particle
     * @param bool $includeInherited
     * @return array
     */
    public function getParticleInstances($outline, $particle,
$includeInherited = true)
    {
        $list = [];
        $index = Layout::index($outline);
        if (isset($index['particles'][$particle])) {
            $list = $index['particles'][$particle];
            if (!$includeInherited &&
!empty($index['inherit'])) {
                foreach ($index['inherit'] as $items) {
                    foreach ((array) $items as $id => $inheritId) {
                        unset($list[$id]);
                    }
                }
            }
        }

        $layout = Layout::instance($outline);

        foreach ($list as $id => $title) {
            $item = clone $layout->find($id);
            $block = $layout->block($id);
            $item->block = isset($block->attributes) ?
$block->attributes : new \stdClass();
            $list[$id] = $item;
        }

        return $list;
    }


    /**
     * @param string $outline
     * @param string $type
     * @param bool $includeInherited
     * @return array
     */
    public function getAtomInstances($outline, $type, $includeInherited =
true)
    {
        $list = [];

        $file =
CompiledYamlFile::instance("gantry-theme://config/{$outline}/page/head.yaml");
        $head = $file->content();
        $file->free();
        if (isset($head['atoms'])) {
            foreach ($head['atoms'] as $atom) {
                if (!empty($atom['id']) &&
$atom['type'] === $type && ($includeInherited ||
empty($atom['inherit']['outline']))) {
                    $list[$atom['id']] = (object) $atom;
                }
            }
        }

        return $list;
    }

    /**
     * Return list of outlines which are inheriting the specified atom.
     *
     * @param string $outline
     * @param string $id
     * @return array
     */
    public function getInheritingOutlinesWithAtom($outline, $id = null)
    {
        $list = [];
        foreach ($this->items as $name => $title) {
            $file =
CompiledYamlFile::instance("gantry-theme://config/{$name}/page/head.yaml");
            $head = $file->content();
            $file->free();

            if (isset($head['atoms'])) {
                foreach ($head['atoms'] as $atom) {
                    if
(!empty($atom['inherit']['outline']) &&
$atom['inherit']['outline'] == $outline &&
(!$id || $atom['inherit']['atom'] == $id)) {
                        $list[$name] = $title;
                    }
                }
            }
        }

        return $list;
    }

    /**
     * Return list of outlines which are inheriting the specified outline.
     *
     * You can additionally pass section or particle id to filter the
results for only that type.
     *
     * @param string $outline
     * @param string|array $id
     * @return array
     */
    public function getInheritingOutlines($outline, $id = null)
    {
        $list = [];
        foreach ($this->items as $name => $title) {
            try {
                $index = Layout::index($name);
            } catch (\Exception $e) {
                // Layout cannot be read. We will just skip it instead of
throwing an exception.
                continue;
            }

            if (!empty($index['inherit'][$outline]) &&
(!$id || array_intersect((array) $id,
$index['inherit'][$outline]))) {
                $list[$name] = $title;
            }
        }

        return $list;
    }

    /**
     * Return list of outlines inherited by the specified outline.
     *
     * You can additionally pass section or particle id to filter the
results for only that type.
     *
     * @param string $outline
     * @param string $id
     * @return array
     */
    public function getInheritedOutlines($outline, $id = null)
    {
        try {
            $index = Layout::index($outline);
        } catch (\Exception $e) {
            // Layout cannot be read. We will just return nothing instead
of throwing an exception.
            return [];
        }

        $list = [];
        foreach ($index['inherit'] as $name => $inherited) {
            if (!$id || array_intersect_key((array) $id, $inherited[$id]))
{
                $list[$name] = isset($this->items[$name]) ?
$this->items[$name] : $name;
            }
        }

        return $list;
    }

    /**
     * @param int|string $id
     * @return int|string
     */
    public function preset($id)
    {
        return $id;
    }

    /**
     * @param int|string $id
     * @return Layout
     */
    public function layout($id)
    {
        return Layout::load($id);
    }

    /**
     * @param int|string $id
     * @return array
     */
    public function layoutPreset($id)
    {
        $layout = Layout::load($id);
        $preset = $layout->preset;

        unset($layout);

        return $preset;
    }

    /**
     * @param string $path
     * @return $this
     * @throws \RuntimeException
     */
    public function load($path = 'gantry-config://')
    {
        $this->path = $path;

        $iterator = $this->getFilesystemIterator($path);

        $files = [];
        /** @var FilesystemIterator $info */
        foreach ($iterator as $name => $info) {
            if (!$info->isDir() || $name[0] == '.' ||
!is_file($info->getPathname() . '/index.yaml')) {
                continue;
            }
            $files[$name] = ucwords(trim(preg_replace(['|_|',
'|/|'], [' ', ' / '], $name)));
        }

        unset($files['default']);
        unset($files['menu']);

        asort($files);

        $this->items = $this->addDefaults($files);

        return $this;
    }

    /**
     * @param string|null $id
     * @param string $title
     * @param string|array $preset
     * @return string
     * @throws \RuntimeException
     */
    public function create($id, $title = null, $preset = null)
    {
        $title = $title ?: 'Untitled';
        $name = ltrim(strtolower(preg_replace('|[^a-z\d_-]|ui',
'_', $id ?: $title)), '_');

        if (!$name) {
            throw new \RuntimeException("Outline needs a name",
400);
        }

        if ($name === 'default') {
            throw new \RuntimeException("Outline cannot use reserved
name '{$name}'", 400);
        }

        $name = $this->findFreeName($name);
        if (!$id) {
            $title = ucwords(trim(preg_replace(['|_|',
'|/|'], [' ', ' / '], $name)));
        }

        if (!is_array($preset)) {
            // Load preset.
            $preset = Layout::preset($preset ?: 'default');
        }

        // Create layout and index for the new layout.
        $layout = new Layout($name, $preset);
        $layout->save()->saveIndex();

        $this->items[$name] = $title;

        return $name;
    }

    /**
     * @param string $id
     * @param string $title
     * @param bool $inherit
     * @return string
     * @throws \RuntimeException
     */
    public function duplicate($id, $title = null, $inherit = false)
    {
        if (!$this->canDuplicate($id)) {
            throw new \RuntimeException("Outline '$id'
cannot be duplicated", 400);
        }

        $layout = Layout::load($id);
        if ($inherit) {
            $layout->inheritAll()->clean();
        }

        $new = $this->create(null, $title, $layout->toArray() +
['preset' => $layout->preset]);

        if ($id === 'default') {
            // For Base Outline we're done.
            return $new;
        }

        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];

        $path =
$locator->findResource("{$this->path}/{$id}");
        if (!$path) {
            // Nothing to copy.
            return $new;
        }

        $newPath =
$locator->findResource("{$this->path}/{$new}", true, true);

        try {
            // Copy everything over except index, layout and assignments.
            Folder::copy($path, $newPath,
'/^(index|layout|assignments)\..*$/');
        } catch (\Exception $e) {
            throw new \RuntimeException(sprintf('Duplicating Outline
failed: ', $e->getMessage()), 500, $e);
        }

        return $new;
    }

    /**
     * @param string $id
     * @param string $title
     * @return string
     * @throws \RuntimeException
     */
    public function rename($id, $title)
    {
        if (!$this->canDelete($id)) {
            throw new \RuntimeException("Outline '$id'
cannot be renamed", 400);
        }

        $gantry = $this->container;

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $path =
$locator->findResource("{$this->path}/{$id}", true, true);
        if (!$path || !is_dir($path)) {
            throw new \RuntimeException('Outline not found',
404);
        }

        $folder = strtolower(preg_replace('|[^a-z\d_-]|ui',
'_', $title));

        if ($folder === 'default' || $folder[0] ===
'_') {
            throw new \RuntimeException("Outline cannot use reserved
name '{$folder}'", 400);
        }

        $newPath =
$locator->findResource("{$this->path}/{$folder}", true,
true);
        if (is_dir($newPath)) {
            throw new \RuntimeException("Outline '$id'
already exists.", 400);
        }

        try {
            foreach ($this->getInheritingOutlines($id) as $outline =>
$title) {
                $this->layout($outline)->updateInheritance($id,
$folder)->save()->saveIndex();
            }
            foreach ($this->getInheritingOutlinesWithAtom($id) as
$outline => $title) {
                Atoms::instance($outline)->updateInheritance($id,
$folder)->save();
            }

            Folder::move($path, $newPath);

        } catch (\Exception $e) {
            throw new \RuntimeException(sprintf('Renaming Outline
failed: %s', $e->getMessage()), 500, $e);
        }

        $this->items[$id] = $title;

        return $folder;
    }

    /**
     * @param string $id
     * @throws \RuntimeException
     */
    public function delete($id)
    {
        if (!$this->canDelete($id)) {
            throw new \RuntimeException("Outline '$id'
cannot be deleted", 400);
        }

        $gantry = $this->container;

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];
        $path =
$locator->findResource("{$this->path}/{$id}", true, true);
        if (!is_dir($path)) {
            throw new \RuntimeException('Outline not found',
404);
        }

        foreach ($this->getInheritingOutlines($id) as $outline =>
$title) {
           
$this->layout($outline)->updateInheritance($id)->save()->saveIndex();
        }
        foreach ($this->getInheritingOutlinesWithAtom($id) as $outline
=> $title) {
           
Atoms::instance($outline)->updateInheritance($id)->save();
        }

        if (file_exists($path)) {
            Folder::delete($path);
        }

        unset($this->items[$id]);
    }

    /**
     * @param string $id
     * @return boolean
     */
    public function canDuplicate($id)
    {
        if (!isset($this->items[$id])) {
            return false;
        }

        return true;
    }

    /**
     * @param string $id
     * @return boolean
     */
    public function canDelete($id)
    {
        if (!$id || $id[0] === '_' || $id ===
'default') {
            return false;
        }

        return true;
    }

    /**
     * @param string $id
     * @return boolean
     */
    public function isDefault($id)
    {
        return $id === 'default';
    }

    /**
     * @param array $outlines
     * @return array
     */
    protected function addDefaults(array $outlines)
    {
        return [
            'default' => 'Base Outline',
            '_body_only' => 'Body Only',
            '_error' => 'Error',
            '_offline' => 'Offline'
        ] + $outlines;
    }

    /**
     * Find unused name with number appended to it when duplicating an
outline.
     *
     * @param string $id
     * @return string
     */
    protected function findFreeName($id)
    {
        if (!isset($this->items[$id])) {
            return $id;
        }

        $name = $id;
        $count = 0;
        if (preg_match('|^(?:_)?(.*?)(?:_(\d+))?$|ui', $id,
$matches)) {
            $matches += ['', '', ''];
            list (, $name, $count) = $matches;
        }

        $count = max(1, $count);

        do {
            $count++;
        } while (isset($this->items["{$name}_{$count}"]));

        return "{$name}_{$count}";
    }

    protected function getFilesystemIterator($path)
    {
        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];

        $custom = $locator->findResource($path, true, true);
        if (is_dir($custom)) {
            /** @var FilesystemIterator $iterator */
            $iterator = new FilesystemIterator(
                $custom,
                FilesystemIterator::CURRENT_AS_SELF |
FilesystemIterator::KEY_AS_FILENAME |
                FilesystemIterator::UNIX_PATHS |
FilesystemIterator::SKIP_DOTS
            );
        } else {
            /** @var UniformResourceIterator $iterator */
            $iterator = $locator->getIterator(
                $path,
                UniformResourceIterator::CURRENT_AS_SELF |
UniformResourceIterator::KEY_AS_FILENAME |
                UniformResourceIterator::UNIX_PATHS |
UniformResourceIterator::SKIP_DOTS
            );
        }

        return $iterator;
    }
}
PKR[�[�T�ڍ�Position/Module.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Position;

use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Module implements \ArrayAccess
{
    use NestedArrayAccessWithGetters, Export;

    public $name;
    public $position;
    public $assigned;
    protected $items;

    /**
     * Module constructor.
     *
     * @param string $name
     * @param string $position
     * @param array $data
     */
    public function __construct($name, $position = null, array $data =
null)
    {
        $this->name = $name;
        $this->position = $position;

        if ($data) {
            $this->init($data);
        } else {
            $this->load();
        }
    }

    public function update(array $data)
    {
        $this->init($data);

        return $this;
    }

    /**
     * Save module.
     *
     * @param string $position
     * @param string $name
     * @return $this
     */
    public function save($name = null, $position = null)
    {
        $this->name = $name ?: $this->name;
        $this->position = $position ?: $this->position;

        $items = $this->toArray();
        unset($items['position'], $items['id']);

        $file = $this->file(true);
        $file->save($items);

        return $this;
    }

    /**
     * Delete module.
     *
     * @return $this
     */
    public function delete()
    {
        $file = $this->file(true);
        if ($file->exists()) {
            $file->delete();
        }

        return $this;
    }

    /**
     * Return true if module exists.
     *
     * @return bool
     */
    public function exists()
    {
        return $this->name ? $this->file()->exists() : false;
    }

    public function toArray()
    {
        return  ['position' => $this->position,
'id' => $this->name] + $this->items;
    }

    protected function load()
    {
        $file = $this->file();
        $this->init($file->content());
        $file->free();
    }

    protected function init($data)
    {
        unset($data['id'], $data['position']);

        $this->items = $data;

        if (isset($this->items['assignments'])) {
            $assignments = $this->items['assignments'];
            if (is_array($assignments)) {
                $this->assigned = 'some';
            } elseif ($assignments !== 'all') {
                $this->assigned = 'none';
            } else {
                $this->assigned = 'all';
            }
        } else {
            $this->assigned = 'all';
        }
    }

    protected function file($save = false)
    {
        $position = $this->position ?: '_unassigned_';

        $this->name = $this->name ?: ($save ?
$this->findFreeName() : null);
        $name = $this->name ?: '_untitled_';

        /** @var UniformResourceLocator $locator */
        $locator = Gantry::instance()['locator'];

        return
CompiledYamlFile::instance($locator->findResource("gantry-positions://{$position}/{$name}.yaml",
true, $save));
    }

    /**
     * Find unused name with number appended.
     */
    protected function findFreeName()
    {
        $position = $this->position ?: '_unassigned_';
        $name = $this->get('type');
        $name = $name == 'particle' ?
$this->get('options.type') : $name;

        /** @var UniformResourceLocator $locator */
        $locator = Gantry::instance()['locator'];

        if
(!file_exists($locator->findResource("gantry-positions://{$position}/{$name}.yaml",
true, true))) {
            return $name;
        }

        $count = 1;

        do {
            $count++;
        } while
(file_exists($locator->findResource("gantry-positions://{$position}/{$name}_{$count}.yaml",
true, true)));

        return "{$name}_{$count}";
    }
}
PKR[�[�l*�)#)#Position/Position.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Position;

use Gantry\Component\Collection\Collection;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Filesystem\Folder;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use Symfony\Component\Yaml\Yaml;

class Position extends Collection
{
    public $name;
    public $title;
    protected $modules = [];

    /**
     * Position constructor.
     *
     * @param string $name
     * @param array $items
     */
    public function __construct($name, array $items = null)
    {
        $this->name = $name;

        $this->load($items);
    }

    /**
     * Save position.
     *
     * @return $this
     */
    public function save()
    {
        $file = $this->file(true);
        $file->save($this->toArray());

        return $this;
    }

    /**
     * Clone position together with its modules. Returns new position.
     *
     * @param string $name
     * @return Position
     */
    public function duplicate($name)
    {
        $new = clone $this;
        $new->name = $name;
        $new->save();

        foreach ($this as $module) {
            $clone = clone $module;
            $clone->position = $name;
            $clone->save();
        }

        return $new;
    }

    /**
     * Raname module key
     *
     * @param string $name
     * @return static
     */
    public function rename($name)
    {
        $new = $this->duplicate($name);
        $this->delete();

        return $new;
    }

    /**
     * Delete position.
     *
     * @return $this
     */
    public function delete()
    {
        $file = $this->file(true);
        if ($file->exists()) {
            $file->delete();
        }

        $folder = $this->folder(true);
        if (is_dir($folder)) {
            Folder::delete($folder);
        }

        return $this;
    }

    /**
     * Update modules in the position.
     *
     * @param array $items
     * @return $this
     */
    public function update(array $items)
    {
        $list = [];
        foreach ($items as $item) {
            $name = ($item instanceof Module) ? $item->name : $item;

            $list[] = $name;
            if (!in_array($name, $this->items)) {
                $this->add($item);
            }
        }

        $remove = array_diff($this->items, $list);
        foreach ($remove as $item) {
            $module = $this->get($item);
            if ($module->position === $this->name) {
                $module->delete();
            }
        }

        $this->items = $list;

        return $this;
    }
    
    /**
     * @param Module|string $item
     * @param string        $name  Temporary name for the module.
     * @return $this
     */
    public function add($item, $name = null)
    {
        if ($item instanceof Module) {
            $this->modules[$name ?: $item->name] = $item;
            $item = $name ?: $item->name;
        }

        $this->items[] = $item;

        return $this;
    }

    public function remove($item)
    {
        if ($item instanceof Module) {
            $item = $item->name;
        }

        unset($this->modules[$item]);

        $this->items = array_diff($this->items, $item);

        return $this;
    }

    /**
     * @param $name
     * @return Module
     */
    public function get($name)
    {
        if (!isset($this->modules[$name])) {
            $this->modules[$name] = $this->loadModule($name);
        }

        return $this->modules[$name];
    }

    /**
     * Returns the value at specified offset.
     *
     * @param string $offset  The offset to retrieve.
     * @return Module
     */
    public function offsetGet($offset)
    {
        if (!isset($this->items[$offset])) {
            return null;
        }

        $name = $this->items[$offset];

        if (!isset($this->modules[$name])) {
            $this->modules[$name] = $this->loadModule($name);
        }

        return $this->modules[$name];
    }

    /**
     * Assigns a value to the specified offset.
     *
     * @param mixed $offset  The offset to assign the value to.
     * @param mixed $value   The value to set.
     */
    public function offsetSet($offset, $value)
    {
        if (!$value instanceof Position) {
            throw new \InvalidArgumentException('Value has to be
instance of Position');
        }
        if (is_null($offset)) {
            $this->items[] = $value->name;
            $this->modules[$value->name] = $value;
        } else {
            $this->items[$offset] = $value->name;
            $this->modules[$value->name] = $value;
        }
    }

    /**
     * Unsets an offset.
     *
     * @param mixed $offset  The offset to unset.
     */
    public function offsetUnset($offset)
    {
        parent::offsetUnset($offset);

        if (!isset($this->items[$offset])) {
            return;
        }

        $name = $this->items[$offset];
        if (isset($this->modules[$name])) {
            unset($this->modules[$name]);
        }
    }

    /**
     * @return \ArrayIterator
     */
    public function getIterator()
    {
        $items = [];
        foreach ($this->items as $key => $name) {
            $items[] = $this->offsetGet($key);
        }

        return new \ArrayIterator($items);
    }

    /**
     * @return array
     */
    public function toArray($includeModules = false)
    {
        $array = [
            'name' => $this->name,
            'title' => $this->title,
        ];

        if (!$includeModules) {
            $array['ordering'] = $this->items;

        } else {
            $list = [];
            foreach ($this->getIterator() as $key => $module) {
                $list[$key] = $module->toArray();
            }
            $array['modules'] = $list;
        }

        return $array;
    }

    /**
     * @param int $inline
     * @param int $indent
     * @param bool $includeModules
     * @return string
     */
    public function toYaml($inline = 3, $indent = 2, $includeModules =
false)
    {
        return Yaml::dump($this->toArray($includeModules), $inline,
$indent, true, false);
    }

    /**
     * @param bool $includeModules
     * @return string
     */
    public function toJson($includeModules = false)
    {
        return json_encode($this->toArray($includeModules));
    }

    /**
     * @return array
     */
    public function listModules()
    {
        $list = [];
        foreach ($this->items as $name) {
            $list[] = "{$this->name}/{$name}";
        }

        return $list;
    }

    /**
     * @param bool $save
     * @return string
     */
    public function folder($save = false)
    {
        return $this->locator()->findResource($this->path(), true,
$save);
    }

    /**
     * @param $data
     */
    protected function load($data)
    {
        if ($data === null) {
            $file = $this->file();
            $data = $file->content();
            $file->free();
        }

        $this->title = isset($data['title']) ?
$data['title'] : $this->name;

        if (isset($data['modules'])) {
            foreach ($data['modules'] as $array) {
                $this->add(new Module($array['id'],
$this->name, $array), $array['id'] ?: rand());
            }

            return;
        }

        // Sort modules by ordering, if items are not listed in ordering,
use alphabetical order.
        $ordering = isset($data['ordering']) ?
array_flip($data['ordering']) : [];
        $path = $this->locator()->findResource($this->path());
        $files = $path ? Folder::all(
            $path,
            [
                'compare' => 'Filename',
                'pattern' => '|\.yaml$|',
                'folders' => false,
                'recursive' => false,
                'key' => 'Filename',
                'filters' => ['key' =>
'|\.yaml$|']
            ]
        ) : [];
        ksort($files);
        $this->items = array_keys($ordering + $files);
    }

    /**
     * @param  string $name
     * @return $this
     */
    protected function loadModule($name)
    {
        return new Module($name, $this->name);
    }

    /**
     * @param bool $save
     * @return CompiledYamlFile
     */
    protected function file($save = false)
    {
        return
CompiledYamlFile::instance($this->locator()->findResource($this->path()
. '.yaml', true, $save));
    }

    /**
     * @return UniformResourceLocator
     */
    protected function locator()
    {
        return Gantry::instance()['locator'];
    }

    /**
     * @return string
     */
    protected function path()
    {
        return "gantry-positions://{$this->name}";
    }

}
PKR[�[���..Position/Positions.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Position;

use Gantry\Component\Collection\Collection;
use Gantry\Component\File\CompiledYamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceIterator;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use RocketTheme\Toolbox\DI\Container;

class Positions extends Collection
{
    /**
     * @var array|Position[]
     */
    protected $items;

    /**
     * @var string
     */
    protected $path;

    /**
     * @var Container
     */
    protected $container;

    public function __construct(Container $container)
    {
        $this->container = $container;
    }

    /**
     * @param string $path
     *
     * @return $this
     * @throws \RuntimeException
     */
    public function load($path = 'gantry-positions://')
    {
        $this->path = $path;
        $positions = [];

        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];
        if ($locator->findResource($path)) {
            /** @var UniformResourceIterator $iterator */
            $iterator = $locator->getIterator($path);

            /** @var UniformResourceIterator $info */
            foreach ($iterator as $info) {
                if (!$info->isFile() || $info->getExtension() !==
'yaml') {
                    continue;
                }

                $name = $info->getBasename('.yaml');
                $position =
CompiledYamlFile::instance($info->getPathname())->content();

                // Only use filesystem position if it it is properly set
up.
                if ($position) {
                    $positions[$name] = new Position($name, $position);
                }
            }
        }

        // Add empty positions from the layouts.
        foreach ($this->container['outlines']->positions()
as $name => $title) {
            if (!isset($positions[$name])) {
                $positions[$name] = new Position($name, ['title'
=> $title]);
            }
        }

        ksort($positions);

        $this->items = $positions;

        return $this;
    }

    /**
     * Updates all positions with their modules from an array and saves
them.
     *
     * @param array $data
     * @return $this
     */
    public function import(array $data)
    {
        foreach ($data as $pos) {
            $list = [];
            $position = $pos['name'];
            foreach ($pos['modules'] as $item) {
                $name = !empty($item['id']) ?
$item['id'] : '';

                if ($name && !empty($item['position'])) {
                    $module =
$this[$item['position']]->get($name);

                    if ($position !== $item['position']) {
                        $module->delete();
                    }
                } else {
                    $module = new Module($name, $position);
                }
                $module->update($item)->save($name, $position);

                $list[] = $module;
            }

            $this[$pos['name']]->update($list)->save();
        }

        return $this;
    }

    /**
     * @param Position $item
     * @return $this
     */
    public function add($item)
    {
        if ($item instanceof Position) {
            $this->items[$item->name] = $item;
        }

        return $this;
    }

    /**
     * @param string $title
     * @param string $id
     *
     * @return string
     * @throws \RuntimeException
     */
    public function create($title = 'Untitled', $id = null)
    {
        $name = strtolower(preg_replace('|[^a-z\d_-]|ui',
'_', $id ?: $title));

        if (!$name) {
            throw new \RuntimeException("Position needs a name",
400);
        }

        $name = $this->findFreeName($name);

        $position = new Position($name, ['title' => $title]);
        $position->save();

        return $name;
    }

    /**
     * @param string $id
     * @param string $new
     *
     * @return string
     * @throws \RuntimeException
     */
    public function duplicate($id, $new = null)
    {
        if (!isset($this->items[$id])) {
            throw new \RuntimeException(sprintf("Duplicating Position
failed: '%s' not found.", $id), 400);
        }

        $new = $this->findFreeName($new ?
strtolower(preg_replace('|[^a-z\d_-]|ui', '_', $new)) :
$id);

        $position = $this->items[$id];
        $new = $position->duplicate($new);

        return $new->name;
    }

    /**
     * @param string $id
     * @param string $new
     *
     * @return string
     * @throws \RuntimeException
     */
    public function rename($id, $new)
    {
        if (!isset($this->items[$id])) {
            throw new \RuntimeException(sprintf("Renaming Position
failed: '%s' not found.", $id), 400);
        }

        $newId = strtolower(preg_replace('|[^a-z\d_-]|ui',
'_', $new));

        if (isset($this->items[$newId])) {
            throw new \RuntimeException(sprintf("Renaming Position
failed: '%s' already exists.", $newId), 400);
        }

        $position = $this->items[$id];
        $position->rename($new);

        return $position->name;
    }

    /**
     * @param string $id
     *
     * @throws \RuntimeException
     */
    public function delete($id)
    {
        if (!isset($this->items[$id])) {
            throw new \RuntimeException(sprintf("Deleting Position
failed: '%s' not found.", $id), 400);
        }

        $position = $this->items[$id];
        $position->delete();
    }

    /**
     * Find unused name with number appended to it when duplicating an
position.
     *
     * @param string $id
     *
     * @return string
     */
    protected function findFreeName($id)
    {
        if (!isset($this->items[$id])) {
            return $id;
        }

        $name  = $id;
        $count = 0;
        if (preg_match('|^(?:_)?(.*?)(?:_(\d+))?$|ui', $id,
$matches)) {
            $matches += ['', '', ''];
            list (, $name, $count) = $matches;
        }

        $count = max(1, $count);

        do {
            $count++;
        } while (isset($this->items["{$name}_{$count}"]));

        return "{$name}_{$count}";
    }
}
PKR[�[���O��Remote/Response.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Remote;

class Response
{
    /**
     * The callback for the progress
     *
     * @var callable    Either a function or callback in array notation
     */
    public static $callback = null;

    /**
     * Which method to use for HTTP calls, can be 'curl',
'fopen' or 'auto'. Auto is default and fopen is the
preferred method
     *
     * @var string
     */
    private static $method = 'auto';

    /**
     * Default parameters for `curl` and `fopen`
     *
     * @var array
     */
    private static $defaults = [

        'curl'  => [
            CURLOPT_REFERER        => 'Gantry5 Response',
            CURLOPT_USERAGENT      => 'Gantry5 Response',
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_TIMEOUT        => 15,
            CURLOPT_HEADER         => false,
            /**
             * Example of callback parameters from within your own class
             */
            //CURLOPT_NOPROGRESS     => false,
            //CURLOPT_PROGRESSFUNCTION => [$this, 'progress']
        ],
        'fopen' => [
            'method'          => 'GET',
            'user_agent'      => 'Gantry5 Response',
            'max_redirects'   => 5,
            'follow_location' => 1,
            'timeout'         => 15,
            /**
             * Example of callback parameters from within your own class
             */
            //'notification' => [$this, 'progress']
        ]
    ];

    /**
     * Sets the preferred method to use for making HTTP calls.
     *
     * @param string $method Default is `auto`
     *
     * @return Response
     */
    public static function setMethod($method = 'auto')
    {
        if (!in_array($method, ['auto', 'curl',
'fopen'])) {
            $method = 'auto';
        }

        self::$method = $method;

        return new self();
    }

    /**
     * Makes a request to the URL by using the preferred method
     *
     * @param  string   $uri     URL to call
     * @param  array    $options An array of parameters for both `curl` and
`fopen`
     * @param  callable $callback
     *
     * @return string The response of the request
     */
    public static function get($uri = '', $options = [],
$callback = null)
    {
        if (!self::isCurlAvailable() && !self::isFopenAvailable())
{
            throw new \RuntimeException('Could not start an HTTP
request. `allow_url_open` is disabled and `cURL` is not available');
        }

        $options = array_replace_recursive(self::$defaults, $options);
        $method  = 'get' . ucfirst(strtolower(self::$method));

        self::$callback = $callback;
        return static::$method($uri, $options, $callback);
    }

    /**
     * Checks if cURL is available
     *
     * @return boolean
     */
    public static function isCurlAvailable()
    {
        return function_exists('curl_version');
    }

    /**
     * Checks if the remote fopen request is enabled in PHP
     *
     * @return boolean
     */
    public static function isFopenAvailable()
    {
        return preg_match('/1|yes|on|true/i',
ini_get('allow_url_fopen'));
    }

    /**
     * Progress normalized for cURL and fopen
     *
     * @return array Normalized array with useful data.
     *               Format: ['code' => int|false,
'filesize' => bytes, 'transferred' => bytes,
'percent' => int]
     */
    public static function progress()
    {
        static $filesize = null;

        $args           = func_get_args();
        $isCurlResource = is_resource($args[0]) &&
get_resource_type($args[0]) == 'curl';

        $notification_code = !$isCurlResource ? $args[0] : false;
        $bytes_transferred = $isCurlResource ? $args[2] : $args[4];

        if ($isCurlResource) {
            $filesize = $args[1];
        } elseif ($notification_code == STREAM_NOTIFY_FILE_SIZE_IS) {
            $filesize = $args[5];
        }

        if ($bytes_transferred > 0) {
            if ($notification_code == STREAM_NOTIFY_PROGRESS |
STREAM_NOTIFY_COMPLETED || $isCurlResource) {

                $progress = [
                    'code'        => $notification_code,
                    'filesize'    => $filesize,
                    'transferred' => $bytes_transferred,
                    'percent'     => $filesize <= 0 ?
'-' : round(($bytes_transferred * 100) / $filesize, 1)
                ];

                if (self::$callback !== null) {
                    call_user_func_array(self::$callback, [$progress]);
                }
            }
        }
    }

    /**
     * Automatically picks the preferred method
     *
     * @return string The response of the request
     */
    private static function getAuto()
    {
        if (self::isFopenAvailable()) {
            return self::getFopen(func_get_args());
        }

        if (self::isCurlAvailable()) {
            return self::getCurl(func_get_args());
        }

        return '';
    }

    /**
     * Starts a HTTP request via fopen
     *
     * @return string The response of the request
     */
    private static function getFopen()
    {
        if (count($args = func_get_args()) == 1) {
            $args = $args[0];
        }

        $uri      = $args[0];
        $options  = $args[1];
        $callback = $args[2];

        if ($callback) {
            $options['fopen']['notification'] =
['self', 'progress'];
        }

        $stream  = stream_context_create(['http' =>
$options['fopen']], $options['fopen']);
        $content = @file_get_contents($uri, false, $stream);

        if ($content === false) {
            throw new \RuntimeException("Error while trying to
download '$uri'");
        }

        return $content;
    }

    /**
     * Starts a HTTP request via cURL
     *
     * @return string The response of the request
     */
    private static function getCurl()
    {
        $args = func_get_args();
        $args = count($args) > 1 ? $args : array_shift($args);

        $uri      = $args[0];
        $options  = $args[1];
        $callback = $args[2];

        $ch = curl_init($uri);
        curl_setopt_array($ch, $options['curl']);

        if ($callback) {
            curl_setopt_array(
                $ch,
                [
                    CURLOPT_NOPROGRESS       => false,
                    CURLOPT_PROGRESSFUNCTION => ['self',
'progress']
                ]
            );
        }

        $response = curl_exec($ch);

        if ($errno = curl_errno($ch)) {
            $error_message = curl_strerror($errno);
            throw new \RuntimeException("cURL error ({$errno}):\n
{$error_message}");
        }

        curl_close($ch);

        return $response;
    }
}
PKR[�[��N@Request/Input.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Request;

use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
use RocketTheme\Toolbox\ArrayTraits\Iterator;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;

class Input implements \ArrayAccess, \Iterator, ExportInterface
{
    use NestedArrayAccessWithGetters, Iterator, Export;

    /**
     * @var array
     */
    protected $items;

    /**
     * Constructor to initialize array.
     *
     * @param  array  $items  Initial items inside the iterator.
     */
    public function __construct(array &$items = [])
    {
        $this->items = &$items;
    }

    /**
     * Returns input array. If there are any JSON encoded fields (key:
_json), those will be decoded as well.
     *
     * @param string  $path       Dot separated path to the requested
value.
     * @param mixed   $default    Default value (or null).
     * @param string  $separator  Separator, defaults to '.'
     * @return array
     */
    public function getArray($path = null, $default = null, $separator =
'.')
    {
        $data = $path ? $this->get($path, $default, $separator) :
$this->items;

        return (array) $this->getChildren($data);
    }

    /**
     * Returns JSON decoded input array.
     *
     * @param string  $path       Dot separated path to the requested
value.
     * @param mixed   $default    Default value (or null).
     * @param string  $separator  Separator, defaults to '.'
     * @return array
     */
    public function getJsonArray($path = null, $default = null, $separator
= '.')
    {
        return (array) $this->getJson($path, $default, $separator,
true);
    }

    /**
     * Returns JSON decoded input. Accosiative arrays become objects.
     *
     * @param string|null  $path       Dot separated path to the requested
value.
     * @param mixed        $default    Default value (or null).
     * @param string       $separator  Separator, defaults to '.'
     * @param bool         $assoc      True to return associative arrays
instead of objects.
     * @return mixed
     */
    public function getJson($path = null, $default = null, $separator =
'.', $assoc = false)
    {
        $data = $this->get($path, null, $separator);

        if (!isset($data)) {
            return $default;
        }

        if (!is_string($data)) {
            throw new \RuntimeException(sprintf('%s::%s(%s) expects
input to be JSON encoded string', __CLASS__, __FUNCTION__, $path));
        }

        $data = json_decode($data, $assoc);
        if (!isset($data)) {
            throw new \RuntimeException(sprintf('%s::%s(): %s',
__CLASS__, __FUNCTION__, json_last_error_msg()));
        }

        return $data;
    }

    /**
     * @param $current
     * @return array|mixed
     * @internal
     */
    protected function getChildren(&$current)
    {
        if (!is_array($current)) {
            return $current;
        }
        $array = [];
        foreach ($current as $key => &$value) {
            if ($key === '_json') {
                $array += json_decode($value, true);
            } else {
                $array[$key] = $this->getChildren($value);
            }
        }

        return $array;
    }
}
PKS[�[(5���Request/Request.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Request;

class Request
{
    /**
     * @var string
     */
    protected $method;

    /**
     * @var Input
     */
    public $get;

    /**
     * @var Input
     */
    public $post;

    /**
     * @var Input
     */
    public $cookie;

    /**
     * @var Input
     */
    public $server;

    /**
     * @var Input
     */
    public $request;

    public function __construct()
    {
        $this->init();
    }

    public function getMethod()
    {
        if (!$this->method) {
            $method = $this->server['REQUEST_METHOD'] ?:
'GET';
            if ('POST' === $method) {
                $method =
$this->server['X-HTTP-METHOD-OVERRIDE'] ?: $method;
                $method = $this->post['METHOD'] ?: $method;
            }
            $this->method = strtoupper($method);
        }

        return $this->method;
    }

    protected function init()
    {
        $this->get = new Input($_GET);
        $this->post = new Input($_POST);
        $this->cookie = new Input($_COOKIE);
        $this->server = new Input($_SERVER);
        $this->request = new Input($_REQUEST);
    }
}
PKT[�[]�>J��Response/HtmlResponse.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Response;

class HtmlResponse extends Response
{
}
PKT[�[(�ggResponse/JsonResponse.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Response;

class JsonResponse extends Response
{
    public $mimeType = 'application/json';

    protected $success = true;
    protected $message;
    protected $exceptions = [];
    protected $messages = [];
    protected $content = [];

    /**
     * @param string $content
     * @param bool $success
     * @return $this
     */
    public function setContent($content, $success = true)
    {
        $this->success = (bool) $success;

        if (is_array($content)) {
            foreach ($content as $key => $value) {
                $this->parseValue($key, $value);
            }
        } else {
            $this->parseValue(null, $content);
        }

        return $this;
    }

    /**
     * @return string
     */
    public function __toString()
    {
        // Empty output buffer to make sure that the response is clean and
valid.
        while (($output = ob_get_clean()) !== false) {
            // In debug mode send also output buffers (debug dumps, PHP
notices and warnings).
            if ($output && (GANTRY5_DEBUG || headers_sent())) {
                $this->messages['php'][] = $output;
            }
        }

        $json = [
            'code' => $this->code,
            'success' => $this->success
        ];
        if ($this->messages) {
            $json['messages'] = $this->messages;
        }
        if ($this->exceptions) {
            $json['exceptions'] = $this->exceptions;
        }
        if (GANTRY5_DEBUG) {
            $json['memory'] = ['peak' =>
memory_get_peak_usage(), 'current' => memory_get_usage()];
        }
        $json += $this->content;

        return (string) json_encode($json);
    }

    protected function parseValue($key, $value)
    {
        if ($value instanceof \Exception) {
            // Prepare the error response if we are dealing with an error.
            $this->success = false;
            $this->exceptions = $this->parseException($value);

        } elseif ($value instanceof HtmlResponse) {
            // Add HTML response (numeric keys are always integers).
            $key =  !$key || is_int($key) ? 'html' : $key;
            $this->content[$key] = trim((string) $value);

        } elseif (is_null($key)) {
            // If the returned value was not an array, put the contents
into data variable.
            $this->content['data'] = $value;

        } elseif (is_int($key)) {
            // If the key was an integer, also put the contents into data
variable.
            $this->content['data'][$key] = $value;

        } else {
            $this->content[$key] = $value;
        }
    }

    protected function parseException(\Exception $e)
    {
        $this->code = $e->getCode();

        // Build data from exceptions.
        $exceptions = [];

        do {
            $exception = [
                'code' => $e->getCode(),
                'message' => $e->getMessage()
            ];

            if (GANTRY5_DEBUG) {
                $exception += [
                    'type' => get_class($e),
                    'file' => $e->getFile(),
                    'line' => $e->getLine()
                ];
            }

            $exceptions[] = $exception;
            $e = $e->getPrevious();
        }
        while (GANTRY5_DEBUG && $e);

        return $exceptions;
    }
}
PKT[�[�~�77Response/RedirectResponse.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Response;

class RedirectResponse extends Response
{
    public function __construct($content = '', $status = 303)
    {
        parent::__construct('', $status);

        $this->setHeader('Location', $content);
    }

    public function getContent()
    {
        return (string) $this->getHeaders()['Location'];
    }

    public function setContent($content)
    {
        $this->setHeader('Location', $content);
    }
}
PKT[�[ʚ@%��Response/Response.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Response;

class Response
{
    public $charset = 'utf-8';
    public $mimeType = 'text/html';

    protected $code = 200;
    protected $message = 'OK';
    protected $lifetime = 0;
    protected $etag;

    /**
     * @var array Response headers.
     */
    protected $headers = [];

    /**
     * @var string Response body.
     */
    protected $content;

    protected $responseCodes = [
        200 => 'OK',
        400 => 'Bad Request',
        401 => 'Unauthorized',
        403 => 'Forbidden',
        404 => 'Not Found',
        405 => 'Method Not Allowed',
        410 => 'Gone',
        500 => 'Internal Server Error',
        501 => 'Not Implemented',
        503 => 'Service Temporarily Unavailable'
    ];

    public function __construct($content = '', $status = 200)
    {
        if ($content) {
            $this->setContent($content);
        }

        if ($status != 200) {
            $this->setStatusCode($status);
        }
    }

    /**
     * @param int $seconds
     * @return $this
     */
    public function setLifetime($seconds)
    {
        $this->lifetime = $seconds;

        return $this;
    }

    /**
     * @param mixed $key
     * @return $this
     */
    public function setKey($key)
    {
        $this->etag = md5(json_encode($key));

        return $this;
    }

    /**
     * @return int
     */
    public function getStatusCode()
    {
        return $this->code;
    }

    /**
     * @param int $code
     * @param string $message
     * @return $this
     */
    public function setStatusCode($code, $message = null)
    {
        if ($message) {
            $this->code = $code;
            $this->message = $message;
        } else {
            $this->code = isset($this->responseCodes[$code]) ? (int)
$code : 500;
            $this->message = $this->responseCodes[$this->code];
        }

        return $this;
    }

    /**
     * @return string
     */
    public function getStatus()
    {
        $code = $this->getStatusCode();

        return $code . ' ' .
(isset($this->responseCodes[$code]) ? $this->responseCodes[$code] :
'Unknown error');
    }

    /**
     * @return array
     */
    public function getHeaders()
    {
        return $this->headers;
    }

    /**
     * @param array $headers
     * @param bool $replace
     * @return $this
     */
    public function setHeaders(array $headers, $replace = false)
    {
        foreach ($headers as $key => $values) {
            $act = $replace;
            foreach ((array) $values as $value) {
                $this->setHeader($key, $value, $act);
                $act = false;
            }
        }

        return $this;
    }

    /**
     * @return $this
     */
    public function clearHeaders()
    {
        $this->headers = [];

        return $this;
    }

    /**
     * @param $name
     * @param $value
     * @param bool $replace
     * @return $this
     */
    public function setHeader($name, $value, $replace = false)
    {
        if ($replace) {
            $this->headers[$name] = [$value];
        } else {
            $this->headers[$name][] = $value;
        }

        return $this;
    }

    /**
     * @return string
     */
    public function getContent()
    {
        return (string) $this->content;
    }

    /**
     * @param string $content
     * @return Response
     * @throws \UnexpectedValueException
     */
    public function setContent($content) {
        if ($content !== null && !is_string($content) &&
!is_numeric($content) && !is_callable([$content,
'__toString'])) {
            throw new \UnexpectedValueException(
                sprintf('Content must be a string or object
implementing __toString()')
            );
        }
        $this->content = $content;

        return $this;
    }

    /**
     * @return string
     */
    public function __toString()
    {
        return (string) $this->content;
    }
}
PKU[�[V�99Router/Router.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Router;

use Gantry\Admin\EventListener;
use Gantry\Admin\Theme;
use Gantry\Component\Controller\BaseController;
use Gantry\Component\Response\HtmlResponse;
use Gantry\Component\Response\Response;
use Gantry\Component\Response\JsonResponse;
use Gantry\Framework\Services\ErrorServiceProvider;
use RocketTheme\Toolbox\DI\Container;
use RocketTheme\Toolbox\Event\EventDispatcher;
use Whoops\Exception\ErrorException;

abstract class Router implements RouterInterface
{
    /**
     * @var Container
     */
    protected $container;

    protected $format;
    protected $resource;
    protected $method;
    protected $path;
    protected $params;

    public function __construct(Container $container)
    {
        $this->container = $container;
    }

    public function dispatch()
    {
        $this->boot();

        $this->load();

        // Render the page or execute the task.
        try {
            $response = static::execute($this->resource,
$this->method, $this->path, $this->params, $this->format);

        } catch (ErrorException $e) {
            throw $e;

        } catch (\Exception $e) {
            // Handle errors.
            if ($this->container->debug()) {
                throw $e;
            }
            $response = $this->getErrorResponse($e, $this->format ==
'json');
        }

        return $this->send($response);
    }

    public function execute($resource, $method = 'GET', $path,
$params = [], $format = 'html')
    {
        $class = '\\Gantry\\Admin\\Controller\\' .
ucfirst($format) . '\\' . strtr(ucwords(strtr($resource,
'/', ' ')), ' ', '\\');

        // Protect against CSRF Attacks.
        if (!in_array($method, ['GET', 'HEAD'], true)
&& !$this->checkSecurityToken()) {
            throw new \RuntimeException('Invalid security token;
please reload the page and try again.', 403);
        }

        if (!class_exists($class)) {
            if ($format === 'json') {
                // Special case: All HTML requests can be returned also as
JSON.
                $response = $this->execute($resource, $method, $path,
$params, 'html');
                return $response instanceof JsonResponse ? $response : new
JsonResponse($response);
            }

            throw new \RuntimeException('Page Not Found', 404);
        }

        /** @var BaseController $controller */
        $controller = new $class($this->container);

        // Execute action.
        $response = $controller->execute($method, $path, $params);

        if (!$response instanceof Response) {
            $response = new HtmlResponse($response);
        }

        return $response;
    }

    /**
     * @return $this
     */
    abstract protected function boot();

    abstract protected function checkSecurityToken();

    /**
     * @return $this
     */
    public function load()
    {
        static $loaded = false;

        if ($loaded) {
            return $this;
        }

        $loaded = true;

        if (isset($this->container['theme.path']) &&
file_exists($this->container['theme.path'] .
'/includes/gantry.php')) {
            include $this->container['theme.path'] .
'/includes/gantry.php';
        }

        if (isset($this->container['theme'])) {
            // Initialize current theme if it is set.
            $this->container['theme'];
        } else {
            // Otherwise initialize streams and error handler manually.
            $this->container['streams']->register();
            $this->container->register(new ErrorServiceProvider);
        }

        $this->container['admin.theme'] = function () {
            return new Theme(GANTRYADMIN_PATH);
        };

        // Add event listener.
        if (class_exists('Gantry\\Admin\\EventListener')) {
            $listener = new EventListener;

            /** @var EventDispatcher $events */
            $events = $this->container['events'];
            $events->addSubscriber($listener);
        }

        // Boot the service.
        $this->container['admin.theme'];

        return $this;
    }

    protected function getErrorResponse(\Exception $e, $json = false)
    {
        $response = new HtmlResponse;
        $response->setStatusCode($e->getCode());

        $params = [
            'ajax' => $json,
            'title' => $response->getStatus(),
            'error' => $e,
        ];
       
$response->setContent($this->container['admin.theme']->render('@gantry-admin/error.html.twig',
$params));

        if ($json) {
            return new JsonResponse([$e, $response]);
        }

        return $response;
    }

    protected function send(Response $response) {
        // Output HTTP header.
        header("HTTP/1.1 {$response->getStatus()}", true,
$response->getStatusCode());
        header("Content-Type: {$response->mimeType};
charset={$response->charset}");
        foreach ($response->getHeaders() as $key => $values) {
            foreach ($values as $value) {
                header("{$key}: {$value}");
            }
        }

        echo $response;

        return true;
    }
}
PKU[�[X}�q&q&Stylesheet/CssCompiler.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Stylesheet;

use Gantry\Component\Config\Config;
use Gantry\Component\Gantry\GantryTrait;
use Gantry\Framework\Gantry;
use Leafo\ScssPhp\Colors;
use RocketTheme\Toolbox\File\PhpFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

abstract class CssCompiler implements CssCompilerInterface
{
    use GantryTrait;

    protected $type;

    protected $name;

    protected $debug = false;

    protected $warnings = [];

    /**
     * @var array
     */
    protected $fonts;

    /**
     * @var array
     */
    protected $variables;

    /**
     * @var string
     */
    protected $target = 'gantry-theme://css-compiled';

    /**
     * @var string
     */
    protected $configuration = 'default';

    /**
     * @var array
     */
    protected $paths;

    /**
     * @var array
     */
    protected $files;

    /**
     * @var mixed
     */
    protected $compiler;

    /**
     * @var bool
     */
    protected $production;

    public function __construct()
    {
        $gantry = static::gantry();

        /** @var Config $global */
        $global = $gantry['global'];

        // In production mode we do not need to do any other checks.
        $this->production = (bool)
$global->get('production');
    }

    public function getWarnings()
    {
        return $this->warnings;
    }

    /**
     * @return string
     */
    public function getTarget()
    {
        return $this->target;
    }

    /**
     * @param string $target
     * @return $this
     */
    public function setTarget($target = null)
    {
        if ($target !== null) {
            $this->target = (string) $target;
        }

        return $this;
    }

    /**
     * @param string $configuration
     * @return $this
     */
    public function setConfiguration($configuration = null)
    {
        if ($configuration !== null) {
            $this->configuration = $configuration;
        }

        return $this;
    }

    /**
     * @param array $fonts
     * @return $this
     */
    public function setFonts(array $fonts = null)
    {
        if ($fonts !== null) {
            // Normalize font data.
            $list = [];
            foreach ($fonts as $family => $data) {
                $family = strtolower($family);

                if (is_array($data)) {
                    // font: [400: url1, 500: url2, 700: url3]
                    $list[$family] = $data;
                } else {
                    // font: url
                    $list[$family] = [400 => (string) $data];
                }
            }
            $this->compiler->setFonts($list);
        }

        return $this;
    }

    /**
     * @param array $paths
     * @return $this
     */
    public function setPaths(array $paths = null)
    {
        if ($paths !== null) {
            $this->paths = $paths;
        }

        return $this;
    }

    /**
     * @param array $files
     * @return $this
     */
    public function setFiles(array $files = null)
    {
        if ($files !== null) {
            $this->files = $files;
        }

         return $this;
    }


    /**
     * @param string $name
     * @return string
     */
    public function getCssUrl($name)
    {
        $out = $name . ($this->configuration !== 'default' ?
'_'. $this->configuration : '');

        return "{$this->target}/{$out}.css";
    }

    /**
     * @return $this
     */
    public function compileAll()
    {
        foreach ($this->files as $file) {
            $this->compileFile($file);
        }

        return $this;
    }

    public function needsCompile($in, $variables)
    {
        $gantry = static::gantry();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $out = $this->getCssUrl($in);
        $path = $locator->findResource($out);

        // Check if CSS file exists at all.
        if (!$path) {
            $this->setVariables($variables());

            return true;
        }

        if ($this->production) {
            // Open the file to see if it contains development comment in
the beginning of the file.
            $handle = fopen($path, 'rb');
            $contents = fread($handle, 36);
            fclose($handle);

            if ($contents === '/* GANTRY5 DEVELOPMENT MODE
ENABLED.') {
                $this->setVariables($variables());
                return true;
            }

            // Compare checksum comment in the file.
            if ($contents !== $this->checksum()) {
                $this->setVariables($variables());
                return true;
            }

            // In production mode we do not need to do any other checks.
            return false;
        }

        $uri = basename($out);
        $metaFile =
PhpFile::instance($locator->findResource("gantry-cache://theme/scss/{$uri}.php",
true, true));

        // Check if meta file exists.
        if (!$metaFile->exists()) {
            $this->setVariables($variables());
            return true;
        }

        $content = $metaFile->content();
        $metaFile->free();

        // Check if filename in meta file matches.
        if (empty($content['file']) || $content['file']
!== $out) {
            $this->setVariables($variables());
            return true;
        }

        // Check if meta timestamp matches to CSS file.
        if (filemtime($path) !== $content['timestamp']) {
            $this->setVariables($variables());
            return true;
        }

        $this->setVariables($variables());

        // Check if variables have been changed.
        $oldVariables = isset($content['variables']) ?
$content['variables'] : [];
        if ($oldVariables != $this->getVariables()) {
            return true;
        }

        // Preload all CSS files to locator cache.
        foreach ($this->paths as $path) {
            $locator->fillCache($path);
        }

        // Check if any of the imported files have been changed.
        $imports = isset($content['imports']) ?
$content['imports'] : [];

        if (!$imports) {
            return $this->findImport($in) !== null;
        }

        foreach ($imports as $resource => $timestamp) {
            $import = $locator->isStream($resource) ?
$locator->findResource($resource) : realpath($resource);
            if (!$import || filemtime($import) !== $timestamp) {
                return true;
            }
        }

        return false;
    }

    public function setVariables(array $variables)
    {
        $this->variables = array_filter($variables);

        foreach($this->variables as &$value) {
            // Check variable against colors and units.
            /* Test regex against these:
             * Should only match the ones marked as +
             *      - family=Aguafina+Script
             *      - #zzzzzz
             *      - #fff
             *      + #ffaaff
             *      + 33em
             *      + 0.5px
             *      - 50 rem
             *      - rgba(323,323,2323)
             *      + rgba(125,200,100,0.3)
             *      - rgb(120,12,12)
             */
            if
(preg_match('/(^(#([a-fA-F0-9]{6})|(rgba\(\s*(0|[1-9]\d?|1\d\d?|2[0-4]\d|25[0-5])\s*,\s*(0|[1-9]\d?|1\d\d?|2[0-4]\d|25[0-5])\s*,\s*(0|[1-9]\d?|1\d\d?|2[0-4]\d|25[0-5])\s*,\s*((0.[0-9]+)|[01])\s*\)))|(\d+(\.\d+){0,1}(rem|em|ex|ch|vw|vh|vmin|vmax|%|px|cm|mm|in|pt|pc))$)/i',
$value)) {
                continue;
            }

            // Check variable against predefined color names (we use Leafo
SCSS Color class to do that).
            if (isset(Colors::$cssColors[strtolower($value)])) {
                continue;
            }

            // All the unknown values need to be quoted.
            $value = "'{$value}'";
        }

        return $this;
    }

    public function getVariables()
    {
        return $this->variables;
    }

    public function reset()
    {
        $this->compiler->reset();

        return $this;
    }

    /**
     * @param string $url
     * @return null|string
     */
    abstract public function findImport($url);

    protected function checksum($len = 36)
    {
        static $checksum;

        if (!$checksum) {
            $checksum = md5(GANTRY5_VERSION . ' ' .
Gantry::instance()['theme']->version);
        }

        return '/*' . substr($checksum, 0, $len - 4) .
'*/';
    }

    protected function createMeta($out, $md5)
    {
        $gantry = Gantry::instance();

        if ($this->production) {
            return;
        }

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $uri = basename($out);
        $metaFile =
PhpFile::instance($locator->findResource("gantry-cache://theme/scss/{$uri}.php",
true, true));
        $data = [
            'file' => $out,
            'timestamp' =>
filemtime($locator->findResource($out)),
            'md5' => $md5,
            'variables' => $this->getVariables(),
            'imports' =>
$this->compiler->getParsedFiles()
        ];

        // Attempt to lock the file for writing.
        try {
            $metaFile->lock(false);
        } catch (\Exception $e) {
            // Another process has locked the file; we will check this in a
bit.
        }
        // If meta file wasn't already locked by another process, save
it.
        if ($metaFile->locked() !== false) {
            $metaFile->save($data);
            $metaFile->unlock();
        }
        $metaFile->free();
    }
}
PKU[�[�03��#Stylesheet/CssCompilerInterface.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Stylesheet;

interface CssCompilerInterface
{
    /**
     * @return array
     */
    public function getWarnings();

    /**
     * @return string
     */
    public function getTarget();

    /**
     * @param string $target
     * @return $this
     */
    public function setTarget($target = null);

    /**
     * @param string $configuration
     * @return $this
     */
    public function setConfiguration($configuration = null);

    /**
     * @param array $paths
     * @return $this
     */
    public function setPaths(array $paths = null);

    /**
     * @param array $files
     * @return $this
     */
    public function setFiles(array $files = null);

    /**
     * @param array $fonts
     * @return $this
     */
    public function setFonts(array $fonts);

    /**
     * @param string $name
     * @return string
     */
    public function getCssUrl($name);

    public function getVariables();
    public function setVariables(array $variables);
    public function registerFunction($name, callable $callback);
    public function unregisterFunction($name);
    public function needsCompile($in, $variables);
    public function compileFile($in);

    /**
     * @return $this
     */
    public function reset();

    /**
     * @return $this
     */
    public function compileAll();

    public function resetCache();
}
PKU[�[�d瑄,�,Stylesheet/Scss/Compiler.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Stylesheet\Scss;

use Gantry\Component\Filesystem\Folder;
use Gantry\Framework\Document;
use Gantry\Framework\Gantry;
use Leafo\ScssPhp\Compiler as BaseCompiler;
use Leafo\ScssPhp\Formatter\OutputBlock;
use Leafo\ScssPhp\Parser;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Compiler extends BaseCompiler
{
    protected $basePath;
    protected $fonts;
    protected $usedFonts;
    protected $streamNames;
    protected $parsedFiles = [];

    public function __construct()
    {
        parent::__construct();

        $this->registerFunction('get-font-url', [$this,
'userGetFontUrl']);
        $this->registerFunction('get-font-family', [$this,
'userGetFontFamily']);
        $this->registerFunction('get-local-fonts', [$this,
'userGetLocalFonts']);
        $this->registerFunction('get-local-font-weights',
[$this, 'userGetLocalFontWeights']);
        $this->registerFunction('get-local-font-url', [$this,
'userGetLocalFontUrl']);
    }

    /**
     * @param $basePath
     */
    public function setBasePath($basePath)
    {
        /** @var Document $document */
        $document = Gantry::instance()['document'];
        $this->basePath = rtrim($document->rootUri(), '/')
. '/' . Folder::getRelativePath($basePath);
    }

    /**
     * @param array $fonts
     */
    public function setFonts(array $fonts)
    {
        $this->fonts = $fonts;
    }

    /**
     * @param $args
     * @return mixed
     */
    public function compileArgs($args)
    {
        foreach ($args as &$arg) {
            $arg = $this->compileValue($arg);
        }

        return $args;
    }

    /**
     * Get variable
     *
     * @api
     *
     * @param string    $name
     * @param boolean   $shouldThrow
     * @param BaseCompiler\Environment $env
     * @param bool $unreduced
     *
     * @return mixed
     */
    public function get($name, $shouldThrow = true,
BaseCompiler\Environment $env = null, $unreduced = false)
    {
        try {
            return parent::get($name, $shouldThrow, $env, $unreduced);
        } catch (\Exception $e) {
            echo $e->getMessage() . "\n";
            return ['string', '', ['']];
        }
    }

    /**
     * @param array $args
     * @return string
     * @throws \Leafo\ScssPhp\Exception\CompilerException
     */
    public function libUrl(array $args)
    {
        // Function has a single parameter.
        $parsed = reset($args);
        if (!$parsed) {
            $this->throwError('url() is missing parameter');
        }

        // Compile parsed value to string.
        $url = trim($this->compileValue($parsed),
'\'"');

        // Handle ../ inside CSS files (points to current theme).
        if (strpos($url, '../') === 0 && strpos($url,
'../', 3) === false) {
            $url = 'gantry-theme://' . substr($url, 3);
        }

        // Generate URL, failed streams will be transformed to 404 URLs.
        $url = Gantry::instance()['document']->url($url, null,
null, false);

        // Changes absolute URIs to relative to make the path to work even
if the site gets moved.
        if ($url && $url[0] === '/' &&
$this->basePath) {
            $url = Folder::getRelativePathDotDot($url, $this->basePath);
        }

        // Make sure that all the URLs inside CSS are https compatible by
replacing http:// protocol with //.
        if (strpos($url, 'http://') === 0) {
            $url = str_replace('http://', '//', $url);
        }

        // Return valid CSS.
        return "url('{$url}')";
    }

    /**
     * get-font-url($my-font-variable);
     *
     * @param array $args
     * @return string
     */
    public function userGetFontUrl($args)
    {
        $value = trim($this->compileValue(reset($args)),
'\'"');

        // It's a google font
        if (0 === strpos($value, 'family=')) {
            $fonts = $this->decodeFonts($value);
            $font = reset($fonts);

            // Only return url once per font.
            if ($font && !isset($this->usedFonts[$font])) {
                $this->usedFonts[$font] = true;
                return
"url('//fonts.googleapis.com/css?{$value}')";
            }
        }

        return false;
    }

    /**
     * font-family: get-font-family($my-font-variable);
     *
     * @param array $args
     * @return string
     */
    public function userGetFontFamily($args)
    {
        $value = trim($this->compileValue(reset($args)),
'\'"');

        return $this->encodeFonts($this->decodeFonts($value));
    }

    /**
     * get-local-fonts($my-font-variable, $my-font-variable2, ...);
     *
     * @param array $args
     * @return array
     */
    public function userGetLocalFonts($args)
    {
        $args = $this->compileArgs($args);

        $fonts = [];
        foreach ($args as $value) {
            // It's a local font, we need to load any of the mapped
fonts from the theme
            $fonts = array_merge($fonts, $this->decodeFonts($value,
true));
        }

        $fonts = $this->getLocalFonts($fonts);

        // Create a basic list of strings so that SCSS parser can parse the
list.
        $list = [];
        foreach ($fonts as $font => $data) {
            $list[] = ['string', '"', [$font]];
        }

        return ['list', ',', $list];
    }

    /**
     * get-local-font-weights(roboto);
     *
     * @param array $args
     * @return array
     */
    public function userGetLocalFontWeights($args)
    {
        $name = trim($this->compileValue(reset($args)),
'\'"');

        $weights = isset($this->fonts[$name]) ?
array_keys($this->fonts[$name]) : [];

        // Create a list of numbers so that SCSS parser can parse the list.
        $list = [];
        foreach ($weights as $weight) {
            $list[] = ['string', '', [(int) $weight]];
        }

        return ['list', ',', $list];
    }

    /**
     * get-local-font-url(roboto, 400);
     *
     * @param array $args
     * @return string
     */
    public function userGetLocalFontUrl($args)
    {
        $args = $this->compileArgs($args);

        $name = isset($args[0]) ? trim($args[0], '\'"')
: '';
        $weight = isset($args[1]) ? $args[1] : 400;

        // Only return url once per font.
        $weightName = $name . '-' . $weight;
        if (isset($this->fonts[$name][$weight]) &&
!isset($this->usedFonts[$weightName])) {
            $this->usedFonts[$weightName] = true;

            return $this->fonts[$name][$weight];
        }

        return false;
    }

    /**
     * Get local font data.
     *
     * @param array $fonts
     * @return array
     */
    protected function getLocalFonts(array $fonts)
    {
        $list = [];
        foreach ($fonts as $family) {
            $family = strtolower($family);

            if (isset($this->fonts[$family])) {
                $list[$family] = $this->fonts[$family];
            }
        }

        return $list;
    }

    /**
     * Convert array of fonts into a CSS parameter string.
     *
     * @param array $fonts
     * @return string
     */
    protected function encodeFonts(array $fonts)
    {
        array_walk($fonts, function(&$val) {
            // Check if font family is one of the 4 default ones, otherwise
add quotes.
            if (!\in_array($val, ['cursive', 'serif',
'sans-serif', 'monospace'], true)) {
                $val = '"' . $val . '"';
            }
        });

        return implode(', ', $fonts);
    }

    /**
     * Convert string into array of fonts.
     *
     * @param  string $string
     * @param  bool   $localOnly
     * @return array
     */
    protected function decodeFonts($string, $localOnly = false)
    {
        if (0 === strpos($string, 'family=')) {
            if ($localOnly) {
                // Do not return external fonts.
                return [];
            }

            // Matches google font family name
            preg_match('/^family=([^&:]+).*$/ui', $string,
$matches);
            return [urldecode($matches[1])];
        }

        // Filter list of fonts and quote them.
        $list = (array) explode(',', $string);
        array_walk($list, function(&$val) {
            $val = trim($val, "'\" \t\n\r\0\x0B");
        });
        array_filter($list);

        return $list;
    }

    public function reset()
    {
        $this->usedFonts = [];

        return $this;
    }

    /**
     * Instantiate parser
     *
     * @param string $path
     *
     * @return \Leafo\ScssPhp\Parser
     */
    protected function parserFactory($path)
    {
        $parser = new Parser($path, count($this->sourceNames),
$this->encoding);

        /** @var UniformResourceLocator $locator */
        $locator = Gantry::instance()['locator'];

        $this->sourceNames[] = $locator->isStream($path) ?
$locator->findResource($path, false) : $path;
        $this->streamNames[] = $path;
        $this->addParsedFile($path);
        return $parser;
    }

    /**
     * Adds to list of parsed files
     *
     * @api
     *
     * @param string $path
     */
    public function addParsedFile($path)
    {
        if ($path && file_exists($path)) {
            $this->parsedFiles[$path] = filemtime($path);
        }
    }

    /**
     * Returns list of parsed files
     *
     * @api
     *
     * @return array
     */
    public function getParsedFiles()
    {
        return $this->parsedFiles;
    }

    /**
     * Clean parset files.
     *
     * @api
     */
    public function cleanParsedFiles()
    {
        $this->parsedFiles = [];
    }

    /**
     * Handle import loop
     *
     * @param string $name
     *
     * @throws \Exception
     */
    protected function handleImportLoop($name)
    {
        for ($env = $this->env; $env; $env = $env->parent) {
            $file = $this->streamNames[$env->block->sourceIndex];

            if (realpath($file) === $name) {
                $this->throwError('An @import loop has been found:
%s imports %s', $file, basename($file));
                break;
            }
        }
    }

    /**
     * Override function to improve the logic.
     *
     * @param string $path
     * @param OutputBlock  $out
     *
     * @throws \Exception
     */
    protected function importFile($path, OutputBlock $out)
    {
        $this->addParsedFile($path);

        /** @var UniformResourceLocator $locator */
        $locator = Gantry::instance()['locator'];

        // see if tree is cached
        $realPath = $locator($path);

        if (isset($this->importCache[$realPath])) {
            $this->handleImportLoop($realPath);

            $tree = $this->importCache[$realPath];
        } else {
            $code   = file_get_contents($realPath);
            $parser = $this->parserFactory($path);
            $tree   = $parser->parse($code);

            $this->importCache[$realPath] = $tree;
        }

        $dirname = dirname($path);
        array_unshift($this->importPaths, $dirname);
        $this->compileChildrenNoReturn($tree->children, $out);
        array_shift($this->importPaths);
    }
}
PKU[�[�}��Stylesheet/ScssCompiler.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Stylesheet;

use Gantry\Component\Stylesheet\Scss\Compiler;
use Gantry\Framework\Document;
use Gantry\Framework\Gantry;
use Leafo\ScssPhp\Exception\CompilerException;
use RocketTheme\Toolbox\File\File;
use RocketTheme\Toolbox\File\JsonFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class ScssCompiler extends CssCompiler
{
    /**
     * @var string
     */
    public $type = 'scss';

    /**
     * @var string
     */
    public $name = 'SCSS';

    /**
     * @var Compiler
     */
    protected $compiler;

    /**
     * Constructor.
     */
    public function __construct()
    {
        parent::__construct();

        $this->compiler = new Compiler();

        if ($this->production) {
           
$this->compiler->setFormatter('Leafo\ScssPhp\Formatter\Crunched');
        } else {
           
$this->compiler->setFormatter('Leafo\ScssPhp\Formatter\Expanded');
            // Work around bugs in SCSS compiler.
            // TODO: Pass our own SourceMapGenerator instance instead.
           
$this->compiler->setSourceMap(Compiler::SOURCE_MAP_INLINE);
            $this->compiler->setSourceMapOptions([
                'sourceMapBasepath' => '/',
                'sourceRoot'        => '/',
            ]);
           
$this->compiler->setLineNumberStyle(Compiler::LINE_COMMENTS);
        }
    }

    public function compile($in)
    {
        return $this->compiler->compile($in);
    }

    public function resetCache()
    {
    }

    /**
     * @param string $in    Filename without path or extension.
     * @return bool         True if the output file was saved.
     * @throws \RuntimeException
     */
    public function compileFile($in)
    {
        // Buy some extra time as compilation may take a lot of time in
shared environments.
        @set_time_limit(30);
        @set_time_limit(60);
        @set_time_limit(90);
        @set_time_limit(120);
        ob_start();

        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $out = $this->getCssUrl($in);
        $path = $locator->findResource($out, true, true);
        $file = File::instance($path);

        // Attempt to lock the file for writing.
        try {
            $file->lock(false);
        } catch (\Exception $e) {
            // Another process has locked the file; we will check this in a
bit.
        }

        if ($file->locked() === false) {
            // File was already locked by another process, lets avoid
compiling the same file twice.
            return false;
        }

        // Set the lookup paths.
        $this->compiler->setBasePath($path);
        $this->compiler->setImportPaths([[$this,
'findImport']]);

        // Run the compiler.
        $this->compiler->setVariables($this->getVariables());
        $scss = '@import "' . $in . '.scss"';
        try {
            $css = $this->compiler->compile($scss);
        } catch (CompilerException $e) {
            throw new \RuntimeException("CSS Compilation on file
'{$in}.scss' failed on error: {$e->getMessage()}", 500,
$e);
        }
        if (strpos($css, $scss) === 0) {
            $css = '/* ' . $scss . ' */';
        }

        // Extract map from css and save it as separate file.
        if ($pos = strrpos($css, '/*# sourceMappingURL=')) {
            $map = json_decode(urldecode(substr($css, $pos + 43, -3)),
true);

            /** @var Document $document */
            $document = $gantry['document'];

            foreach ($map['sources'] as &$source) {
                $source = $document->url($source, null, -1);
            }
            unset($source);

            $mapFile = JsonFile::instance($path . '.map');
            $mapFile->save($map);
            $mapFile->free();

            $css = substr($css, 0, $pos) . '/*#
sourceMappingURL=' . basename($out) . '.map */';
        }

        $warnings = trim(ob_get_clean());
        if ($warnings) {
            $this->warnings[$in] = explode("\n", $warnings);
        }

        if (!$this->production) {
            $warning = <<<WARN
/* GANTRY5 DEVELOPMENT MODE ENABLED.

   WARNING: This file is automatically generated by Gantry5. Any
modifications to this file will be lost!

   For more information on modifying CSS, please read:

   http://docs.gantry.org/gantry5/configure/styles
   http://docs.gantry.org/gantry5/tutorials/adding-a-custom-style-sheet
 */
WARN;
            $css = $warning . "\n\n" . $css;
        } else {
            $css = "{$this->checksum()}\n{$css}";
        }

        $file->save($css);
        $file->unlock();
        $file->free();

        $this->createMeta($out, md5($css));
        $this->compiler->cleanParsedFiles();

        return true;
    }

    /**
     * @param string   $name       Name of function to register to the
compiler.
     * @param callable $callback   Function to run when called by the
compiler.
     * @return $this
     */
    public function registerFunction($name, callable $callback)
    {
        $this->compiler->registerFunction($name, $callback);

        return $this;
    }

    /**
     * @param string $name       Name of function to unregister.
     * @return $this
     */
    public function unregisterFunction($name)
    {
        $this->compiler->unregisterFunction($name);

        return $this;
    }

    /**
     * @param string $url
     * @return null|string
     * @internal
     */
    public function findImport($url)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        // Ignore vanilla css and external requests.
        if (preg_match('/\.css$|^https?:\/\//', $url)) {
            return null;
        }

        // Try both normal and the _partial filename.
        $files = array($url, preg_replace('/[^\/]+$/',
'_\0', $url));

        foreach ($this->paths as $base) {
            foreach ($files as $file) {
                if (!preg_match('|\.scss$|', $file)) {
                    $file .= '.scss';
                }
                if ($locator->findResource($base . '/' .
$file)) {
                    return $base . '/' . $file;
                }
            }
        }

        return null;
    }
}
PKU[�[�i}}System/Messages.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Component\System;

class Messages
{
    protected $messages = [];

    public function add($message, $type = 'warning')
    {
        $this->messages[] = ['type' => $type,
'message' => $message];

        return $this;
    }

    public function get()
    {
        return $this->messages;
    }

    public function clean()
    {
        $this->messages = [];

        return $this;
    }
}
PKV[�[�Y�22Theme/AbstractTheme.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Theme;

use Gantry\Component\Config\Config;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Gantry\GantryTrait;
use Gantry\Component\Twig\TwigCacheFilesystem;
use Gantry\Component\Twig\TwigExtension;
use Gantry\Framework\Platform;
use Gantry\Framework\Services\ErrorServiceProvider;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

/**
 * Class AbstractTheme
 * @package Gantry\Component
 *
 * @property string $path
 * @property string $layout
 */
abstract class AbstractTheme
{
    use GantryTrait;

    /**
     * @var string
     */
    public $name;

    /**
     * @var string
     */
    public $path;

    /**
     * @var \Twig_Environment
     */
    protected $renderer;

    /**
     * Construct theme object.
     *
     * @param string $path
     * @param string $name
     */
    public function __construct($path, $name = null)
    {
        if (!is_dir($path)) {
            throw new \LogicException('Theme not found!');
        }

        $this->name = $name ? $name : basename($path);
        $this->path = $path;

        $this->init();
    }

    /**
     * Get context for render().
     *
     * @param array $context
     * @return array
     */
    public function getContext(array $context)
    {
        $context['theme'] = $this;

        return $context;
    }

    /**
     * Define twig environment.
     *
     * @param \Twig_Environment $twig
     * @param \Twig_LoaderInterface $loader
     * @return \Twig_Environment
     */
    public function extendTwig(\Twig_Environment $twig,
\Twig_LoaderInterface $loader = null)
    {
        if
($twig->hasExtension('Gantry\Component\Twig\TwigExtension')) {
            return $twig;
        }

        if (!$loader) {
            $loader = $twig->getLoader();
        }

        $this->setTwigLoaderPaths($loader);

        $twig->addExtension(new TwigExtension);

        if (method_exists($this, 'toGrid')) {
            $filter = new \Twig_SimpleFilter('toGrid', [$this,
'toGrid']);
            $twig->addFilter($filter);
        }

        return $twig;
    }

    /**
     * Return renderer.
     *
     * @return \Twig_Environment
     */
    public function renderer()
    {
        if (!$this->renderer) {
            $gantry = static::gantry();

            /** @var Config $global */
            $global = $gantry['global'];

            $cachePath = $global->get('compile_twig', 1) ?
$this->getCachePath('twig') : null;
            $cache = $cachePath ? new TwigCacheFilesystem($cachePath,
\Twig_Cache_Filesystem::FORCE_BYTECODE_INVALIDATION) : null;
            $debug = $gantry->debug();
            $production = (bool) $global->get('production',
1);
            $loader = new \Twig_Loader_Filesystem();
            $params = [
                'cache' => $cache,
                'debug' => $debug,
                'auto_reload' => !$production,
                'autoescape' => 'html'
            ];

            $twig = new \Twig_Environment($loader, $params);

            $this->setTwigLoaderPaths($loader);

            if ($debug) {
                $twig->addExtension(new \Twig_Extension_Debug());
            }

            $this->renderer = $this->extendTwig($twig, $loader);
        }

        return $this->renderer;
    }

    /**
     * Render a template file by using given context.
     *
     * @param string $file
     * @param array $context
     * @return string
     */
    public function render($file, array $context = [])
    {
        // Include Gantry specific things to the context.
        $context = $this->getContext($context);

        return $this->renderer()->render($file, $context);
    }

    /**
     * Compile and render twig string.
     *
     * @param string $string
     * @param array $context
     * @return string
     */
    public function compile($string, array $context = [])
    {
        $renderer = $this->renderer();
        $template = $renderer->createTemplate($string);

        // Include Gantry specific things to the context.
        $context = $this->getContext($context);

        return $template->render($context);
    }

    /**
     * Initialize theme.
     */
    protected function init()
    {
        $gantry = static::gantry();
        $gantry['streams']->register();

        // Only add error service if development or debug mode has been
enabled or user is admin.
        if (!$gantry['global']->get('production', 0)
|| $gantry->debug() || $gantry->admin()) {
            $gantry->register(new ErrorServiceProvider);
        }

        // Initialize theme cache stream.
        $cachePath = $this->getCachePath();

        Folder::create($cachePath);

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];
        $locator->addPath('gantry-cache', 'theme',
[$cachePath], true, true);

        CompiledYamlFile::$defaultCachePath =
$locator->findResource('gantry-cache://theme/compiled/yaml',
true, true);
        CompiledYamlFile::$defaultCaching =
$gantry['global']->get('compile_yaml', 1);
    }

    /**
     * Set twig lookup paths to the loader.
     *
     * @param \Twig_LoaderInterface $loader
     * @return \Twig_Loader_Filesystem|null
     * @internal
     */
    protected function setTwigLoaderPaths(\Twig_LoaderInterface $loader)
    {
        if ($loader instanceof \Twig_Loader_Chain) {
            $new = new \Twig_Loader_Filesystem();
            $loader->addLoader($new);
            $loader = $new;
        } elseif (!($loader instanceof \Twig_Loader_Filesystem)) {
            return null;
        }

        $gantry = static::gantry();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

       
$loader->setPaths($locator->findResources('gantry-engine://templates'),
'nucleus');
       
$loader->setPaths($locator->findResources('gantry-particles://'),
'particles');

        return $loader;
    }

    /**
     * Get path to Twig cache.
     *
     * @param string $path
     * @return string
     */
    protected function getCachePath($path = '')
    {
        $gantry = static::gantry();

        /** @var Platform $patform */
        $patform = $gantry['platform'];

        // Initialize theme cache stream.
        return $patform->getCachePath() . '/' . $this->name
. ($path ? '/' . $path : '');
    }

    /**
     * @deprecated 5.0.2
     */
    public function debug()
    {
        return static::gantry()->debug();
    }

    /**
     * @deprecated 5.1.5
     */
    public function add_to_context(array $context)
    {
        return $this->getContext($context);
    }

    /**
     * @deprecated 5.1.5
     */
    public function add_to_twig(\Twig_Environment $twig,
\Twig_LoaderInterface $loader = null)
    {
        return $this->extendTwig($twig, $loader);
    }
}
PKV[�[�����Theme/ThemeDetails.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Theme;

use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Filesystem\Streams;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

/**
 * Class ThemeDetails
 * @package Gantry\Component\Theme
 */
class ThemeDetails implements \ArrayAccess
{
    use NestedArrayAccessWithGetters, Export;

    protected $items;
    protected $parent;

    /**
     * Create new theme details.
     *
     * @param string $theme
     */
    public function __construct($theme)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $filename =
$locator->findResource("gantry-themes://{$theme}/gantry/theme.yaml");
        if (!$filename) {
            throw new \RuntimeException(sprintf('Theme %s not
found', $theme), 404);
        }

        $cache =
$locator->findResource("gantry-cache://{$theme}/compiled/yaml",
true, true);

        $file = CompiledYamlFile::instance($filename);
        $this->items = $file->setCachePath($cache)->content();
        $file->free();

        $this->offsetSet('name', $theme);

        $parent = (string)
$this->get('configuration.theme.parent', $theme);
        $parent = $parent != $theme ? $parent : null;

        $this->offsetSet('parent', $parent);
    }

    /**
     * @return string
     */
    public function addStreams()
    {
        $gantry = Gantry::instance();

        // Initialize theme stream.
        $streamName =
$this->addStream($this->offsetGet('name'),
$this->getPaths());

        // Initialize parent theme streams.
        $loaded = [$this->offsetGet('name')];
        $details = $this;

        while ($details = $details->parent()) {
            if (in_array($details->name, $loaded)) {
                break;
            }
            $this->addStream($details->name,
$details->getPaths(false));
            $loaded[] = $details->name;
        }

        /** @var Streams $streams */
        $streams = $gantry['streams'];
        $streams->register();

        return $streamName;
    }

    /**
     * Get parent theme details if theme has a parent.
     *
     * @return ThemeDetails|null
     * @throws \RuntimeException
     */
    public function parent()
    {
        $parent = $this->offsetGet('parent');

        if (!$this->parent && $parent) {
            try {
                $this->parent = new ThemeDetails($parent);
            } catch (\RuntimeException $e) {
                throw new \RuntimeException(sprintf('Parent theme %s
not found', $parent), 404);
            }
        }

        return $this->parent;
    }

    /**
     * Get all possible paths to the theme.
     *
     * @return array
     */
    public function getPaths($overrides = true)
    {
        $paths = array_merge(
            $overrides ? (array)
$this->get('configuration.theme.overrides',
'gantry-theme://custom') : [],
            ['gantry-theme://'],
            (array) $this->get('configuration.theme.base',
'gantry-theme://common')
        );

        $parent = $this->offsetGet('parent');
        if ($parent) {
            // Stream needs to be valid URL.
            $streamName = 'gantry-themes-' .
preg_replace('|[^a-z\d+.-]|ui', '-', $parent);
            $paths[] = "{$streamName}://";
        }

        return $this->parsePaths($paths);
    }

    /**
     * Convert theme path into stream URI.
     *
     * @param string $path
     * @return string
     */
    public function getUrl($path)
    {
        $uri = (string) $this->offsetGet($path);

        if (strpos($uri, 'gantry-theme://') === 0) {
            list (, $uri) = explode('://', $uri, 2);
        }
        if (!strpos($uri, '://')) {
            $name = $this->offsetGet('name');

            // Stream needs to be valid URL.
            $streamName = 'gantry-themes-' .
preg_replace('|[^a-z\d+.-]|ui', '-', $name);
            $uri = "{$streamName}://{$uri}";
        }

        return $uri;
    }

    /**
     * Turn list of theme paths to be universal, so they can be used
outside of the theme.
     *
     * @param array $items
     * @return array
     */
    public function parsePaths(array $items)
    {
        foreach ($items as &$item) {
            $item = $this->parsePath($item);
        }

        return $items;
    }

    /**
     * Convert theme paths to be universal, so they can be used outside of
the theme.
     *
     * @param string $path
     * @return string
     */
    public function parsePath($path)
    {
        if (strpos($path, 'gantry-theme://') === 0) {
            list (, $path) = explode('://', $path, 2);
        }
        if (!strpos($path, '://')) {
            $name = $this->offsetGet('name');
            $path = "gantry-themes://{$name}/{$path}";
        }

        return $path;
    }

    /**
     * @return string|null
     * @deprecated 5.1.5
     */
    public function getParent()
    {
        return $this->offsetGet('parent');
    }

    /**
     * @param string $name
     * @param array $paths
     * @return string|null
     */
    protected function addStream($name, $paths)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        /** @var Streams $streams */
        $streams = $gantry['streams'];

        // Add theme stream.
        $streamName = 'gantry-themes-' .
preg_replace('|[^a-z\d+.-]|ui', '-', $name);
        if (!$locator->schemeExists($streamName)) {
            $streams->add([$streamName => ['paths' =>
$paths]]);
        }

        return $streamName;
    }
}
PKX[�[1���%�%Theme/ThemeInstaller.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Theme;

use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Layout\Layout;
use Gantry\Framework\Gantry;
use Gantry\Framework\Platform;
use Gantry\Framework\Services\ErrorServiceProvider;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

abstract class ThemeInstaller
{
    /**
     * Set to true if in Gantry.
     *
     * @var bool
     */
    public $initialized = false;
    public $actions = [];

    protected $name;
    protected $outlines;
    protected $script;

    public function __construct($extension = null)
    {
        if ($extension) {
            $this->name = $extension;
        }
    }

    abstract public function getPath();

    /**
     * Get list of available outlines.
     *
     * @param array $filter
     * @return array
     */
    public function getOutlines(array $filter = null)
    {
        if (!isset($this->outlines)) {
            $this->outlines = [];
            $path = $this->getPath();

            // If no outlines are given, try loading outlines.yaml file.
            $file = YamlFile::instance($path .
'/install/outlines.yaml');

            if ($file->exists()) {
                // Load the list from the yaml file.
                $this->outlines = (array) $file->content();
                $file->free();

            } elseif (is_dir($path . '/install/outlines')) {
                // Build the list from the install folder.
                // recurse = false, full=true
                $folders = Folder::all($path .
'/install/outlines', ['folders' => true,
'recursive' => false]);
                foreach ($folders as $folder) {
                    $this->outlines[basename($folder)] = [];
                }
            }

            // Always include system outlines.
            $this->outlines += ['default' => [],
'_body_only' => [], '_error' => [],
'_offline' => []];

        }

        return is_array($filter) ? array_intersect_key($this->outlines,
array_flip($filter)) : $this->outlines;
    }

    public function getOutline($name)
    {
        $list = $this->getOutlines([$name]);

        return reset($list);
    }

    public function installDefaults()
    {
        $installerScript = $this->getInstallerScript();

        if ($installerScript && method_exists($installerScript,
'installDefaults')) {
            $installerScript->installDefaults($this);
        } else {
            $this->createDefaults();
        }
    }

    public function installSampleData()
    {
        $installerScript = $this->getInstallerScript();

        if ($installerScript && method_exists($installerScript,
'installSampleData')) {
            $installerScript->installSampleData($this);
        } else {
            $this->createSampleData();
        }
    }

    public function createDefaults()
    {
        $this->createOutlines();
    }

    public function createSampleData()
    {
    }

    public function render($template, $context = [])
    {
        try {
            $loader = new \Twig_Loader_Filesystem();
            $loader->setPaths([$this->getPath() .
'/install/templates']);

            $params = [
                'cache' => null,
                'debug' => false,
                'autoescape' => 'html'
            ];

            $twig = new \Twig_Environment($loader, $params);

            $name = $this->name;
            $context += [
                'name' => $this->translate($name),
                'actions' => $this->actions
            ];

            return $twig->render($template, $context);
        } catch (\Exception $e) {
            return '';
        }
    }

    /**
     * Set available outlines.
     *
     * @param array $outlines If parameter isn't provided, outlines
list get reloaded from the disk.
     * @return $this
     */
    public function setOutlines(array $outlines = null)
    {
        $this->outlines = $outlines;

        return $this;
    }

    /**
     * @param array $filter
     */
    public function createOutlines(array $filter = null)
    {
        $outlines = $this->getOutlines($filter);

        foreach ($outlines as $folder => $params) {
            $this->createOutline($folder, $params);
        }
    }

    /**
     * @param string $folder
     * @param array $params
     * @return string|bool
     */
    public function createOutline($folder, array $params = [])
    {
        if (!$folder) {
            throw new \RuntimeException('Cannot create outline without
folder name');
        }

        $this->initialize();

        $created = false;

        $params += [
            'preset' => null,
            'title' => null
        ];

        $title = $params['title'] ?: ucwords(trim(strtr($folder,
['_' => ' '])));
        $preset = $params['preset'] ?: 'default';

        // Copy configuration for the new layout.
        if (($this->copyCustom($folder, $folder) || $created)) {
            // Update layout and save it.
            $layout = Layout::load($folder, $preset);
            $layout->save()->saveIndex();

            if ($created) {
                $this->actions[] = ['action' =>
'outline_created', 'text' =>
$this->translate('GANTRY5_INSTALLER_ACTION_OUTLINE_CREATED',
$title)];
            } else {
                $this->actions[] = ['action' =>
'outline_updated', 'text' =>
$this->translate('GANTRY5_INSTALLER_ACTION_OUTLINE_UPDATED',
$title)];
            }
        }

        return $folder;
    }

    public function initialize()
    {
        if ($this->initialized) {
            return;
        }

        $name = $this->name;
        $path = $this->getPath();

        // Remove compiled CSS files if they exist.
        $cssPath = $path . '/custom/css-compiled';
        if (is_dir($cssPath)) {
            Folder::delete($cssPath);
        } elseif (is_file($cssPath)) {
            @unlink($cssPath);
        }

        // Remove wrongly named file if it exists.
        $md5path = $path . '/MD5SUM';
        if (is_file($md5path)) {
            @unlink($md5path);
        }

        // Restart Gantry and initialize it.
        $gantry = Gantry::restart();
        $gantry['theme.name'] = $name;
        $gantry['streams']->register();

        // Only add error service if debug mode has been enabled.
        if ($gantry->debug()) {
            $gantry->register(new ErrorServiceProvider);
        }

        /** @var Platform $patform */
        $patform = $gantry['platform'];

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        // Initialize theme stream.
        $details = new ThemeDetails($name);
        $locator->addPath('gantry-theme', '',
$details->getPaths(), false, true);

        // Initialize theme cache stream and clear theme cache.
        $cachePath = $patform->getCachePath() . '/' . $name;
        if (is_dir($cachePath)) {
            Folder::delete($cachePath);
        }
        Folder::create($cachePath);
        $locator->addPath('gantry-cache', 'theme',
[$cachePath], true, true);

        CompiledYamlFile::$defaultCachePath =
$locator->findResource('gantry-cache://theme/compiled/yaml',
true, true);
        CompiledYamlFile::$defaultCaching =
$gantry['global']->get('compile_yaml', 1);

        $this->initialized = true;
    }

    public function finalize()
    {
        // Copy standard outlines if they haven't been copied already.
        $this->copyCustom('default', 'default');
        $this->copyCustom('_body_only',
'_body_only');
        $this->copyCustom('_error', '_error');
        $this->copyCustom('_offline', '_offline');

        $this->initialize();
    }

    /**
     * @param string $layout
     * @param string $id
     * @return bool True if files were copied over.
     */
    protected function copyCustom($layout, $id)
    {
        $path = $this->getPath();

        // Only copy files if the target id doesn't exist.
        $dst = $path . '/custom/config/' . $id;
        if (!$layout || !$id || is_dir($dst)) {
            return false;
        }

        // New location for G5.3.2+
        $src = $path . '/install/outlines/' . $layout;
        if (!is_dir($src)) {
            // Old and deprecated location.
            $src = $path . '/install/layouts/' . $layout;
        }

        try {
            is_dir($src) ? Folder::copy($src, $dst) : Folder::create($dst);
        } catch (\Exception $e) {
            throw new \RuntimeException("Creating configuration for
outline '{$layout}' failed: {$e->getMessage()}", 500,
$e);
        }

        return true;
    }

    protected function translate($text)
    {
        $translator = Gantry::instance()['translator'];

        $args = func_get_args();

        return call_user_func_array([$translator, 'translate'],
$args);
    }


    protected function getInstallerScript()
    {
        if (!$this->script) {
            $className = ucfirst($this->name) .
'InstallerScript';

            if (!class_exists($className)) {

                $path = "{$this->getPath()}/install.php";
                if (is_file($path)) {
                    require_once $path;
                }
            }

            if (class_exists($className)) {
                $this->script = new $className;
            }
        }

        return $this->script;
    }
}
PKX[�[��IA��Theme/ThemeInterface.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Theme;

use Gantry\Component\Config\Config;
use Gantry\Component\Layout\Layout;
use Gantry\Component\Stylesheet\CssCompilerInterface;

/**
 * Class ThemeTrait
 * @package Gantry\Framework\Base
 *
 * @property string $path
 * @property string $layout
 */
interface ThemeInterface
{
    // AbstractTheme class

    /**
     * Get context for render().
     *
     * @param array $context
     * @return array
     */
    public function getContext(array $context);

    /**
     * Define twig environment.
     *
     * @param \Twig_Environment $twig
     * @param \Twig_LoaderInterface $loader
     * @return \Twig_Environment
     */
    public function extendTwig(\Twig_Environment $twig,
\Twig_LoaderInterface $loader = null);

    /**
     * Returns renderer.
     *
     * @return \Twig_Environment
     */
    public function renderer();

    /**
     * Render a template file.
     *
     * @param string $file
     * @param array $context
     * @return string
     */
    public function render($file, array $context = array());

    // ThemeTrait class

    /**
     * Update all CSS files in the theme.
     *
     * @param array $outlines
     * @return array List of CSS warnings.
     */
    public function updateCss(array $outlines = null);

    /**
     * Set current layout.
     *
     * @param string $name
     * @param bool $force
     * @return $this
     */
    public function setLayout($name = null, $force = false);

    /**
     * Get current preset.
     *
     * @param  bool $forced     If true, return only forced preset or null.
     * @return string|null $preset
     */
    public function preset($forced = false);

    /**
     * Set preset to be used.
     *
     * @param string $name
     * @return $this
     */
    public function setPreset($name = null);

    /**
     * Return CSS compiler used in the theme.
     *
     * @return CssCompilerInterface
     * @throws \RuntimeException
     */
    public function compiler();

    /**
     * Returns URL to CSS file.
     *
     * If file does not exist, it will be created by using CSS compiler.
     *
     * @param string $name
     * @return string
     */
    public function css($name);

    /**
     * Return all CSS variables.
     *
     * @return array
     */
    public function getCssVariables();

    /**
     * Returns style presets for the theme.
     *
     * @return Config
     */
    public function presets();

    /**
     * Return name of the used layout preset.
     *
     * @return string
     * @throws \RuntimeException
     */
    public function type();

    /**
     * Load current layout and its configuration.
     *
     * @param string $name
     * @return Layout
     * @throws \LogicException
     */
    public function loadLayout($name = null);

    /**
     * Check whether layout has content bock.
     *
     * @return bool
     */
    public function hasContent();

    /**
     * Returns all non-empty segments from the layout.
     *
     * @return array
     */
    public function segments();

    /**
     * Returns details of the theme.
     *
     * @return ThemeDetails
     */
    public function details();

    /**
     * Returns configuration of the theme.
     *
     * @return array
     */
    public function configuration();

    /**
     * Function to convert block sizes into CSS classes.
     *
     * @param $text
     * @return string
     */
    public function toGrid($text);
}
PKX[�[b����Z�ZTheme/ThemeTrait.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Theme;

use Gantry\Component\Config\Config;
use Gantry\Component\Content\Block\ContentBlock;
use Gantry\Component\Content\Block\HtmlBlock;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Gantry\GantryTrait;
use Gantry\Component\Layout\Layout;
use Gantry\Component\Stylesheet\CssCompilerInterface;
use Gantry\Framework\Document;
use Gantry\Framework\Menu;
use Gantry\Framework\Services\ConfigServiceProvider;
use RocketTheme\Toolbox\File\PhpFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

/**
 * Class ThemeTrait
 * @package Gantry\Component
 *
 * @property string $path
 * @property string $layout
 */
trait ThemeTrait
{
    use GantryTrait;

    protected $layoutObject;
    protected $atoms;
    protected $segments;
    protected $preset;
    protected $cssCache;
    /**
     * @var CssCompilerInterface
     */
    protected $compiler;
    protected $equalized = [3 => 33.3, 6 => 16.7, 7 => 14.3, 8
=> 12.5, 9 => 11.1, 11 => 9.1, 12 => 8.3];

    /**
     * @var ThemeDetails
     */
    protected $details;

    /**
     * Register Theme stream.
     *
     * @param string $savePath
     */
    public function registerStream($savePath = null)
    {
        $streamName = $this->details()->addStreams();

        /** @var UniformResourceLocator $locator */
        $locator = self::gantry()['locator'];
        $locator->addPath('gantry-theme', '',
array_merge((array) $savePath, [[$streamName, '']]));
    }

    /**
     * Update all CSS files in the theme.
     *
     * @param array $outlines
     * @return array List of CSS warnings.
     */
    public function updateCss(array $outlines = null)
    {
        $gantry = static::gantry();
        $compiler = $this->compiler();

        if (is_null($outlines)) {
            /** @var UniformResourceLocator $locator */
            $locator = $gantry['locator'];
            $path = $locator->findResource($compiler->getTarget(),
true, true);

            // Make sure that all the CSS files get deleted.
            if (is_dir($path)) {
                Folder::delete($path, false);
            }

            $outlines = $gantry['outlines'];
        }

        // Make sure that PHP has the latest data of the files.
        clearstatcache();

        $warnings = [];
        foreach ($outlines as $outline => $title) {
            $config = ConfigServiceProvider::load($gantry, $outline);

           
$compiler->reset()->setConfiguration($outline)->setVariables($config->flatten('styles',
'-'));

            $results = $compiler->compileAll()->getWarnings();
            if ($results) {
                $warnings[$outline] = $results;
            }
        }

        return $warnings;
    }

    /**
     * Set layout to be used.
     *
     * @param string $name
     * @param bool $force
     * @return $this
     */
    public function setLayout($name = null, $force = false)
    {
        $gantry = static::gantry();

        // Force new layout to be set.
        if ($force) {
            unset($gantry['configuration']);
        }

        // Set default name only if configuration has not been set before.
        if ($name === null &&
!isset($gantry['configuration'])) {
            $name = 'default';
        }

        $outline = isset($gantry['configuration']) ?
$gantry['configuration'] : null;

        // Set configuration if given.
        if ($name && $name != $outline) {
            GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage("Using Gantry outline {$name}");

            $gantry['configuration'] = $name;
            unset($gantry['config']);
            $gantry['config'] =
ConfigServiceProvider::load($gantry, $name);
        }

        return $this;
    }

    /**
     * Get current preset.
     *
     * @param  bool $forced     If true, return only forced preset or null.
     * @return string|null $preset
     */
    public function preset($forced = false)
    {
        $presets = $this->presets()->toArray();

        $preset = $this->preset;

        if (!$preset && !$forced) {
            $preset =
static::gantry()['config']->get('styles.preset',
'-undefined-');
        }

        if ($preset && !isset($presets[$preset])) {
            $preset = null;
        }

        return $preset;
    }

    /**
     * Set preset to be used.
     *
     * @param string $name
     * @return $this
     */
    public function setPreset($name = null)
    {
        // Set preset if given.
        if ($name) {
            $this->preset = $name;
        }

        return $this;
    }

    /**
     * Return CSS compiler used in the theme.
     *
     * @return CssCompilerInterface
     * @throws \RuntimeException
     */
    public function compiler()
    {
        if (!$this->compiler) {
            $compilerClass = (string)
$this->details()->get('configuration.css.compiler',
'\Gantry\Component\Stylesheet\ScssCompiler');

            if (!class_exists($compilerClass)) {
                throw new \RuntimeException('CSS compiler used by the
theme not found');
            }

            $details = $this->details();

            /** @var CssCompilerInterface $compiler */
            $this->compiler = new $compilerClass();
            $this->compiler
               
->setTarget($details->get('configuration.css.target'))
               
->setPaths($details->get('configuration.css.paths'))
               
->setFiles($details->get('configuration.css.files'))
               
->setFonts($details->get('configuration.fonts'));
        }

        $preset = $this->preset(true);
        if ($preset) {
            $this->compiler->setConfiguration($preset);
        } else {
            $gantry = static::gantry();
           
$this->compiler->setConfiguration(isset($gantry['configuration'])
? $gantry['configuration'] : 'default');
        }

        return $this->compiler->reset();
    }

    /**
     * Returns URL to CSS file.
     *
     * If file does not exist, it will be created by using CSS compiler.
     *
     * @param string $name
     * @return string
     */
    public function css($name)
    {
        if (!isset($this->cssCache[$name])) {
            $compiler = $this->compiler();

            if ($compiler->needsCompile($name, [$this,
'getCssVariables'])) {
                GANTRY_DEBUGGER &&
\Gantry\Debugger::startTimer("css-{$name}", "Compiling CSS:
{$name}") && \Gantry\Debugger::addMessage("Compiling CSS:
{$name}");

                $compiler->compileFile($name);

                GANTRY_DEBUGGER &&
\Gantry\Debugger::stopTimer("css-{$name}");
            }

            $this->cssCache[$name] = $compiler->getCssUrl($name);
        }

        return $this->cssCache[$name];
    }

    public function getCssVariables()
    {
        if ($this->preset) {
            $variables = $this->presets()->flatten($this->preset .
'.styles', '-');
        } else {
            $gantry = self::gantry();
            $variables =
$gantry['config']->flatten('styles', '-');
        }

        return $variables;
    }

    /**
     * Returns style presets for the theme.
     *
     * @return Config
     */
    public function presets()
    {
        static $presets;

        if (!$presets) {
            $gantry = static::gantry();

            /** @var UniformResourceLocator $locator */
            $locator = $gantry['locator'];

            $filename =
$locator->findResource("gantry-theme://gantry/presets.yaml");
            $file = CompiledYamlFile::instance($filename);
            $presets = new Config($file->content());
            $file->free();
        }

        return $presets;
    }

    /**
     * Return name of the used layout preset.
     *
     * @return string
     * @throws \RuntimeException
     */
    public function type()
    {
        if (!$this->layoutObject) {
            throw new \RuntimeException('Function called too
early');
        }
        $name = isset($this->layoutObject->preset['name'])
? $this->layoutObject->preset['name'] :
'unknown';

        return $name;
    }

    /**
     * Load current layout and its configuration.
     *
     * @param string $name
     * @return Layout
     * @throws \LogicException
     */
    public function loadLayout($name = null)
    {
        if (!$name) {
            try {
                $name = static::gantry()['configuration'];
            } catch (\Exception $e) {
                throw new \LogicException('Gantry: Outline has not
been defined yet', 500);
            }
        }

        if (!isset($this->layoutObject) ||
$this->layoutObject->name != $name) {
            $layout = Layout::instance($name);

            if (!$layout->exists()) {
                $layout = Layout::instance('default');
            }

            // TODO: Optimize
            $this->layoutObject = $layout->init();
        }

        return $this->layoutObject;
    }

    /**
     * Check whether layout has content bock.
     *
     * @return bool
     */
    public function hasContent()
    {
        $layout = $this->loadLayout();
        $content = $layout->referencesByType('system',
'content');

        return !empty($content);
    }

    /**
     * Load atoms and assets from the page settings.
     *
     * @since 5.4.9
     */
    public function loadAtoms()
    {
        if (!isset($this->atoms)) {
            $this->atoms = true;

            GANTRY_DEBUGGER &&
\Gantry\Debugger::startTimer('atoms', "Preparing
atoms");

            $gantry = static::gantry();

            /** @var Config $config */
            $config = $gantry['config'];

            /** @var \Gantry\Framework\Document $document */
            $document = $gantry['document'];

            $atoms = (array) $config->get('page.head.atoms');

            foreach ($atoms as $data) {
                $atom = [
                    'type' => 'atom',
                    'subtype' => $data['type'],
                ] + $data;

                try {
                    $block = $this->getContent($atom);
                    $document->addBlock($block);

                } catch (\Exception $e) {
                    if ($gantry->debug()) {
                        throw new \RuntimeException("Rendering Atom
'{$atom['subtype']}' failed on error:
{$e->getMessage()}", 500, $e);
                    }
                }
            }

            $assets = (array) $config->get('page.assets');

            if ($assets) {
                $atom = [
                    'id' => 'page-assets',
                    'title' => 'Page Assets',
                    'type' => 'atom',
                    'subtype' => 'assets',
                    'attributes' => $assets +
['enabled' => 1]
                ];

                try {
                    $block = $this->getContent($atom);
                    $document->addBlock($block);

                } catch (\Exception $e) {
                    if ($gantry->debug()) {
                        throw new \RuntimeException("Rendering CSS/JS
Assets failed on error: {$e->getMessage()}", 500, $e);
                    }
                }
            }

            GANTRY_DEBUGGER &&
\Gantry\Debugger::stopTimer('atoms');
        }
    }

    /**
     * Returns all non-empty segments from the layout.
     *
     * @return array
     */
    public function segments()
    {
        if (!isset($this->segments)) {
            $this->segments = $this->loadLayout()->toArray();

            GANTRY_DEBUGGER &&
\Gantry\Debugger::startTimer('segments', "Preparing
layout");

            $this->prepareLayout($this->segments);

            GANTRY_DEBUGGER &&
\Gantry\Debugger::stopTimer('segments');
        }

        return $this->segments;
    }

    /**
     * Prepare layout for rendering. Initializes all CSS/JS in particles.
     */
    public function prepare()
    {
        $this->segments();
    }

    /**
     * Returns details of the theme.
     *
     * @return ThemeDetails
     */
    public function details()
    {
        if (!$this->details) {
            $this->details = new ThemeDetails($this->name);
        }
        return $this->details;
    }

    /**
     * Returns configuration of the theme.
     *
     * @return array
     */
    public function configuration()
    {
        return (array) $this->details()['configuration'];
    }

    /**
     * Function to convert block sizes into CSS classes.
     *
     * @param $text
     * @return string
     */
    public function toGrid($text)
    {
        if (!$text) {
            return '';
        }

        $number = round($text, 1);
        $number = max(5, $number);
        $number = (string) ($number == 100 ? 100 : min(95, $number));

        static $sizes = array(
            '33.3' => 'size-33-3',
            '16.7' => 'size-16-7',
            '14.3' => 'size-14-3',
            '12.5' => 'size-12-5',
            '11.1' => 'size-11-1',
            '9.1'  => 'size-9-1',
            '8.3'  => 'size-8-3'
        );

        return isset($sizes[$number]) ? ' ' . $sizes[$number] :
'size-' . (int) $number;
    }

    /**
     * Magic setter method
     *
     * @param mixed $offset Asset name value
     * @param mixed $value  Asset value
     */
    public function __set($offset, $value)
    {
        if ($offset == 'title') {
            $offset = 'name';
        }

        $this->details()->offsetSet('details.' . $offset,
$value);
    }

    /**
     * Magic getter method
     *
     * @param  mixed $offset Asset name value
     * @return mixed         Asset value
     */
    public function __get($offset)
    {
        if ($offset == 'title') {
            $offset = 'name';
        }

        $value = $this->details()->offsetGet('details.' .
$offset);

        if ($offset == 'version' && is_int($value)) {
            $value .= '.0';
        }

        return $value;
    }

    /**
     * Magic method to determine if the attribute is set
     *
     * @param  mixed   $offset Asset name value
     * @return boolean         True if the value is set
     */
    public function __isset($offset)
    {
        if ($offset == 'title') {
            $offset = 'name';
        }

        return $this->details()->offsetExists('details.' .
$offset);
    }

    /**
     * Magic method to unset the attribute
     *
     * @param mixed $offset The name value to unset
     */
    public function __unset($offset)
    {
        if ($offset == 'title') {
            $offset = 'name';
        }

        $this->details()->offsetUnset('details.' .
$offset);
    }

    /**
     * Prepare layout by loading all the positions and particles.
     *
     * Action is needed before displaying the layout as it recalculates
block widths based on the visible content.
     *
     * @param array $items
     * @param bool  $temporary
     * @param bool  $sticky
     * @internal
     */
    protected function prepareLayout(array &$items, $temporary = false,
$sticky = false)
    {
        foreach ($items as $i => &$item) {
            // Non-numeric items are meta-data which should be ignored.
            if (((string)(int) $i !== (string) $i) || !is_object($item)) {
                continue;
            }

            if (!empty($item->children)) {
                $fixed = true;
                foreach ($item->children as $child) {
                    $fixed &= !empty($child->attributes->fixed);
                }

                $this->prepareLayout($item->children, $fixed,
$temporary);
            }

            // TODO: remove hard coded types.
            switch ($item->type) {
                case 'system':
                    break;

                case 'atom':
                case 'particle':
                case 'position':
                case 'spacer':
                    GANTRY_DEBUGGER &&
\Gantry\Debugger::startTimer($item->id, "Rendering
{$item->id}");

                    $item->content = $this->renderContent($item,
['prepare_layout' => true]);
                    // Note that content can also be null (postpone
rendering).
                    if ($item->content === '') {
                        unset($items[$i]);
                    }

                    GANTRY_DEBUGGER &&
\Gantry\Debugger::stopTimer($item->id);

                    break;

                default:
                    if ($sticky) {
                        $item->attributes->sticky = 1;
                        break;
                    }

                    if (empty($item->children)) {
                        unset($items[$i]);
                        break;
                    }

                    $dynamicSize = 0;
                    $fixedSize = 0;
                    $childrenCount = count($item->children);
                    foreach ($item->children as $child) {
                        if (!isset($child->attributes->size)) {
                            $child->attributes->size = 100 /
count($item->children);
                        }
                        if (empty($child->attributes->fixed)) {
                            $dynamicSize += $child->attributes->size;
                        } else {
                            $fixedSize += $child->attributes->size;
                        }
                    }

                    $roundSize = round($dynamicSize, 1);
                    $equalized = isset($this->equalized[$childrenCount])
? $this->equalized[$childrenCount] : 0;

                    // force-casting string for testing comparison due to
weird PHP behavior that returns wrong result
                    if ($roundSize != 100 && (string) $roundSize !=
(string) ($equalized * $childrenCount)) {
                        $fraction = 0;
                        $multiplier = (100 - $fixedSize) / ($dynamicSize ?:
1);
                        foreach ($item->children as $child) {
                            if (!empty($child->attributes->fixed)) {
                                continue;
                            }

                            // Calculate size for the next item by taking
account the rounding error from the last item.
                            // This will allow us to approximate cumulating
error and fix it when rounding error grows
                            // over the rounding treshold.
                            $size = ($child->attributes->size *
$multiplier) + $fraction;
                            $newSize = round($size);
                            $fraction = $size - $newSize;
                            $child->attributes->size = $newSize;
                        }
                    }
            }
        }
    }

    /**
     * Renders individual content block, like particle or position.
     *
     * Function is used to pre-render content.
     *
     * @param object|array $item
     * @param array $options
     * @return string|null
     */
    public function renderContent($item, $options = [])
    {
        $gantry = static::gantry();

        $content = $this->getContent($item, $options);

        /** @var Document $document */
        $document = $gantry['document'];
        $document->addBlock($content);

        $html = $content->toString();
        return !strstr($html, '@@DEFERRED@@') ? $html : null;
    }

    /**
     * Renders individual content block, like particle or position.
     *
     * Function is used to pre-render content.
     *
     * @param object|array $item
     * @param array $options
     * @return ContentBlock
     * @since 5.4.3
     */
    public function getContent($item, $options = [])
    {
        if (is_array($item)) {
            $item = (object) $item;
        }

        $gantry = static::gantry();

        /** @var Config $global */
        $global = $gantry['global'];

        $production = (bool) $global->get('production');
        $subtype = $item->subtype;
        $enabled =
$gantry['config']->get("particles.{$subtype}.enabled",
1);

        if (!$enabled) {
            return new HtmlBlock;
        }

        $attributes = isset($item->attributes) ? $item->attributes :
[];
        $particle =
$gantry['config']->getJoined("particles.{$subtype}",
$attributes);

        $cached = false;
        $cacheKey = [];

        // Enable particle caching only in production mode.
        if ($production && isset($particle['caching'])) {
            $caching = $particle['caching'] + ['type'
=> 'dynamic'];

            switch ($caching['type']) {
                case 'static':
                    $cached = true;
                    break;
                case 'config_matches':
                    if
(isset($particle['caching']['values'])) {
                        $values = (array)
$particle['caching']['values'];
                        $compare = array_intersect_key($particle, $values);
                        $cached = ($values === $compare);
                    }
                    break;
                case 'menu':
                    /** @var Menu $menu */
                    $menu = $gantry['menu'];
                    $cacheId = $menu->getCacheId();

                    // FIXME: menu caching needs to handle dynamic modules
inside menu: turning it off for now.
                    if (false && $cacheId !== null) {
                        $cached = true;
                        $cacheKey['menu_cache_key'] = $cacheId;
                    }
                    break;
            }
        }

        if ($cached) {
            $cacheKey['language'] =
$gantry['page']->language;
            $cacheKey['attributes'] = $particle;
            $cacheKey += (array) $item;

            /** @var UniformResourceLocator $locator */
            $locator = $gantry['locator'];
            $key = md5(json_encode($cacheKey));

            $filename =
$locator->findResource("gantry-cache://theme/html/{$key}.php",
true, true);
            $file = PhpFile::instance($filename);
            if ($file->exists()) {
                try {
                    return ContentBlock::fromArray((array)
$file->content());
                } catch (\Exception $e) {
                    // Invalid cache, continue to rendering.
                    GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage(sprintf('Failed to load %s %s
cache', $item->type, $item->id), 'debug');
                }
            }
        }

        // Create new document context for assets.
        $context = $this->getContext(['segment' => $item,
'enabled' => 1, 'particle' => $particle] +
$options);

        /** @var Document $document */
        $document = $gantry['document'];
        $document->push();
        $html =
trim($this->render("@nucleus/content/{$item->type}.html.twig",
$context));
        $content = $document->pop()->setContent($html);

        if (isset($file)) {
            // Save HTML and assets into the cache.
            GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage(sprintf('Caching %s %s',
$item->type, $item->id), 'debug');
            $file->save($content->toArray());
        }

        return $content;
    }
}
PKX[�[$�E��
�
Translator/Translator.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Translator;

use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Translator implements TranslatorInterface
{
    protected $default = 'en';
    protected $active = 'en';
    protected $sections = [];
    protected $translations = [];
    protected $untranslated = [];

    public function translate($string)
    {
        if (preg_match('|^GANTRY5(_[A-Z0-9]+){2,}$|', $string)) {
            list(, $section, $code) = explode('_', $string, 3);

            $string = ($this->find($this->active, $section, $string)
?: $this->find($this->default, $section, $string)) ?: $string;
        }

        if (func_num_args() === 1) {
            return $string;
        }

        $args = func_get_args();
        $args[0] = $string;

        return call_user_func_array('sprintf', $args);
    }

    /**
     * Set new active language if given and return previous active
language.
     *
     * @param  string  $language  Language code. If not given, current
language is kept.
     * @return string  Previously active language.
     */
    public function active($language = null)
    {
        $previous = $this->active;

        if ($language) {
            $this->active = $language;
        }

        return $previous;
    }

    public function untranslated()
    {
        return $this->untranslated;
    }

    protected function find($language, $section, $string)
    {
        if (!isset($this->sections[$language][$section])) {
            $translations = $this->load($language, $section);

            if (isset($this->translations[$language])) {
                $this->translations[$language] += $translations;
            } else {
                $this->translations[$language] = $translations;
            }

            $this->sections[$language][$section] =
!empty($translations);
        }

        if (!isset($this->translations[$language][$string])) {
            $this->untranslated[$language][$section][$string] = null;

            return null;
        }

        return $this->translations[$language][$string];
    }

    protected function load($language, $section)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $section = strtolower($section);
        if ($section === 'engine') {
            // TODO: add support for other engines than nucleus.
            $section = 'nucleus';
        }

        $filename = 'gantry-admin://translations/' . $language .
'/' . $section . '.yaml';
        $file = CompiledYamlFile::instance($filename);

        if (!$file->exists() && ($pos = strpos($language,
'-')) > 0) {
            $filename = 'gantry-admin://translations/' .
substr($language, 0, $pos) . '/' . $section . '.yaml';
            $file = CompiledYamlFile::instance($filename);
        }

        $cachePath =
$locator->findResource('gantry-cache://translations', true,
true);
        $translations = (array)
$file->setCachePath($cachePath)->content();
        $file->free();

        return $translations;
    }
}
PKX[�[�f��GG"Translator/TranslatorInterface.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Translator;

interface TranslatorInterface
{
    /**
     * @param string $string
     * @return string
     */
    public function translate($string);

    /**
     * Set new active language if given and return previous active
language.
     *
     * @param  string  $language  Language code. If not given, current
language is kept.
     * @return string  Previously active language.
     */
    public function active($language = null);
}
PKX[�[�Ϸ�Twig/Node/TwigNodeAssets.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\Node;

class TwigNodeAssets extends \Twig_Node implements
\Twig_NodeCaptureInterface
{
    protected $tagName = 'assets';

    public function __construct(\Twig_Node $body = null,
\Twig_Node_Expression $location = null, \Twig_Node_Expression $variables =
null, $lineno = 0, $tag = null)
    {
        parent::__construct(['body' => $body,
'location' => $location, 'variables' =>
$variables], [], $lineno, $tag);
    }
    /**
     * Compiles the node to PHP.
     *
     * @param \Twig_Compiler $compiler A Twig_Compiler instance
     */
    public function compile(\Twig_Compiler $compiler)
    {
        $compiler->addDebugInfo($this)
            ->write("\$assetFunction =
\$this->env->getFunction('parse_assets')->getCallable();\n")
            ->write('$assetVariables = ')
            ->subcompile($this->getNode('variables'))
            ->raw(";\n")
            ->write("if (\$assetVariables &&
!is_array(\$assetVariables)) {\n")
            ->indent()
            ->write("throw new UnexpectedValueException('{%
{$this->tagName} with x %}: x is not an array');\n")
            ->outdent()
            ->write("}\n")
            ->write('$location = ')
            ->subcompile($this->getNode('location'))
            ->raw(";\n")
            ->write("if (\$location &&
!is_string(\$location)) {\n")
            ->indent()
            ->write("throw new UnexpectedValueException('{%
{$this->tagName} in x %}: x is not a string');\n")
            ->outdent()
            ->write("}\n")
            ->write("\$priority =
isset(\$assetVariables['priority']) ?
\$assetVariables['priority'] : 0;\n")
            ->write("ob_start();\n")
            ->subcompile($this->getNode('body'))
            ->write("\$content = ob_get_clean();\n")
            ->write("\$assetFunction(\$content, \$location,
\$priority);\n");
    }
}
PKX[�[���y��Twig/Node/TwigNodeMarkdown.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\Node;

class TwigNodeMarkdown extends \Twig_Node implements
\Twig_NodeOutputInterface
{
    public function __construct(\Twig_Node $body, $lineno, $tag =
'markdown')
    {
        parent::__construct(['body' => $body], [], $lineno,
$tag);
    }
    /**
     * Compiles the node to PHP.
     *
     * @param \Twig_Compiler A Twig_Compiler instance
     */
    public function compile(\Twig_Compiler $compiler)
    {
        $compiler
            ->addDebugInfo($this)
            ->write('ob_start();' . PHP_EOL)
            ->subcompile($this->getNode('body'))
            ->write('$content = ob_get_clean();' . PHP_EOL)
            ->write('preg_match("/^\s*/", $content,
$matches);' . PHP_EOL)
            ->write('$lines = explode("\n",
$content);' . PHP_EOL)
            ->write('$content = preg_replace(\'/^\' .
$matches[0]. \'/\', "", $lines);' . PHP_EOL)
            ->write('$content = join("\n",
$content);' . PHP_EOL)
            ->write('echo
$this->env->getExtension(\'Gantry\Component\Twig\TwigExtension\')->markdownFunction($content);'
. PHP_EOL);
    }
}
PKY[�[7n^c��Twig/Node/TwigNodePageblock.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\Node;

class TwigNodePageblock extends \Twig_Node implements
\Twig_NodeCaptureInterface
{
    protected $tagName = 'pageblock';

    public function __construct(\Twig_Node $body = null,
\Twig_Node_Expression $location = null, \Twig_Node_Expression $variables =
null, $lineno = 0, $tag = null)
    {
        parent::__construct(['body' => $body,
'location' => $location, 'variables' =>
$variables], [], $lineno, $tag);
    }
    /**
     * Compiles the node to PHP.
     *
     * @param \Twig_Compiler $compiler A Twig_Compiler instance
     */
    public function compile(\Twig_Compiler $compiler)
    {
        $compiler->addDebugInfo($this)
            ->write('$pageblockVariables = ')
            ->subcompile($this->getNode('variables'))
            ->raw(";\n")
            ->write("if (\$pageblockVariables &&
!is_array(\$pageblockVariables)) {\n")
            ->indent()
            ->write("throw new UnexpectedValueException('{%
{$this->tagName} with x %}: x is not an array');\n")
            ->outdent()
            ->write("}\n")
            ->write('$location = ')
            ->subcompile($this->getNode('location'))
            ->raw(";\n")
            ->write("if (\$location &&
!is_string(\$location)) {\n")
            ->indent()
            ->write("throw new UnexpectedValueException('{%
{$this->tagName} in x %}: x is not a string');\n")
            ->outdent()
            ->write("}\n")
            ->write("\$priority =
isset(\$pageblockVariables['priority']) ?
\$pageblockVariables['priority'] : 0;\n")
            ->write("ob_start();\n")
            ->subcompile($this->getNode('body'))
            ->write("\$content = ob_get_clean();\n")
           
->write("Gantry\Framework\Gantry::instance()['document']->addHtml(\$content,
\$priority, \$location);\n");
    }
}
PKY[�[�ܽ���Twig/Node/TwigNodeScripts.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\Node;

class TwigNodeScripts extends TwigNodeAssets
{
    protected $tagName = 'scripts';
}
PKY[�[JtE5��Twig/Node/TwigNodeStyles.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\Node;

class TwigNodeStyles extends TwigNodeScripts
{
    protected $tagName = 'styles';
}
PKY[�[�$��		Twig/Node/TwigNodeSwitch.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\Node;

class TwigNodeSwitch extends \Twig_Node
{
    public function __construct(\Twig_Node $value, \Twig_Node $cases,
\Twig_Node $default = null, $lineno = 0, $tag = null)
    {
        parent::__construct(array('value' => $value,
'cases' => $cases, 'default' => $default),
array(), $lineno, $tag);
    }

    /**
     * Compiles the node to PHP.
     *
     * @param \Twig_Compiler A Twig_Compiler instance
     */
    public function compile(\Twig_Compiler $compiler)
    {
        $compiler
            ->addDebugInfo($this)
            ->write('switch (')
            ->subcompile($this->getNode('value'))
            ->raw(") {\n")
            ->indent();

        foreach ($this->getNode('cases') as $case) {
            if (!$case->hasNode('body')) {
                continue;
            }

            foreach ($case->getNode('values') as $value) {
                $compiler
                    ->write('case ')
                    ->subcompile($value)
                    ->raw(":\n");
            }

            $compiler
                ->write("{\n")
                ->indent()
                ->subcompile($case->getNode('body'))
                ->write("break;\n")
                ->outdent()
                ->write("}\n");
        }

        if ($this->hasNode('default') &&
$this->getNode('default') !== null) {
            $compiler
                ->write("default:\n")
                ->write("{\n")
                ->indent()
                ->subcompile($this->getNode('default'))
                ->outdent()
                ->write("}\n");
        }

        $compiler
            ->outdent()
            ->write("}\n");
    }
}
PKY[�[�K��Twig/Node/TwigNodeThrow.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\Node;

class TwigNodeThrow extends \Twig_Node
{
    public function __construct(
        $code,
        \Twig_Node $message,
        $lineno = 0,
        $tag = null
    )
    {
        parent::__construct(['message' => $message],
['code' => $code], $lineno, $tag);
    }

    /**
     * Compiles the node to PHP.
     *
     * @param \Twig_Compiler $compiler A Twig_Compiler instance
     * @throws \LogicException
     */
    public function compile(\Twig_Compiler $compiler)
    {
        $compiler->addDebugInfo($this);

        $compiler
            ->write('throw new \RuntimeException(')
            ->subcompile($this->getNode('message'))
            ->write(', ')
            ->write($this->getAttribute('code') ?: 500)
            ->write(");\n");
    }
}
PKY[�[Bh���Twig/Node/TwigNodeTryCatch.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\Node;

class TwigNodeTryCatch extends \Twig_Node
{
    public function __construct(\Twig_Node $try, \Twig_Node $catch = null,
$lineno = 0, $tag = null)
    {
        parent::__construct(array('try' => $try,
'catch' => $catch), array(), $lineno, $tag);
    }

    /**
     * Compiles the node to PHP.
     *
     * @param \Twig_Compiler $compiler A Twig_Compiler instance
     */
    public function compile(\Twig_Compiler $compiler)
    {
        $compiler->addDebugInfo($this);

        $compiler
            ->write('try {')
        ;

        $compiler
            ->indent()
            ->subcompile($this->getNode('try'))
        ;

        if ($this->hasNode('catch') && null !==
$this->getNode('catch')) {
            $compiler
                ->outdent()
                ->write('} catch (\Exception $e) {' .
"\n")
                ->indent()
                ->write('if
($context[\'gantry\']->debug()) throw $e;' .
"\n")
                ->write('GANTRY_DEBUGGER &&
method_exists(\'Gantry\\Debugger\', \'addException\')
&& \Gantry\Debugger::addException($e);' . "\n")
                ->write('$context[\'e\'] = $e;' .
"\n")
                ->subcompile($this->getNode('catch'))
            ;
        }

        $compiler
            ->outdent()
            ->write("}\n");
    }
}
PKY[�[��5��
�
&Twig/TokenParser/TokenParserAssets.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\TokenParser;

use Gantry\Component\Twig\Node\TwigNodeScripts;

/**
 * Adds javascript / style assets to head/footer/custom location.
 *
 * {% assets in 'head' with { priority: 2 } %}
 *   <script type="text/javascript" src="{{
url('gantry-theme://js/my.js') }}"></script>
 *   <link rel="stylesheet" href="{{
url('gantry-assets://css/font-awesome.min.css') }}"
type="text/css"/>
 * {% endassets -%}
 */
class TokenParserAssets extends \Twig_TokenParser
{
    /**
     * Parses a token and returns a node.
     *
     * @param \Twig_Token $token A Twig_Token instance
     *
     * @return \Twig_Node A Twig_Node instance
     */
    public function parse(\Twig_Token $token)
    {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();

        list($location, $variables) = $this->parseArguments($token);

        $content = $this->parser->subparse([$this,
'decideBlockEnd'], true);
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        return new TwigNodeScripts($content, $location, $variables,
$lineno, $this->getTag());
    }

    /**
     * @param \Twig_Token $token
     * @return array
     */
    protected function parseArguments(\Twig_Token $token)
    {
        $stream = $this->parser->getStream();
        $location = null;
        if ($stream->nextIf(\Twig_Token::OPERATOR_TYPE, 'in'))
{
            $location =
$this->parser->getExpressionParser()->parseExpression();
        } else {
            $lineno = $token->getLine();
            $location = new
\Twig_Node_Expression_Constant('head', $lineno);
        }

        if ($stream->nextIf(\Twig_Token::NAME_TYPE, 'with')) {
            $variables =
$this->parser->getExpressionParser()->parseExpression();
        } else {
            $lineno = $token->getLine();
            $variables = new \Twig_Node_Expression_Array([], $lineno);
            $variables->setAttribute('priority', 0);
        }
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        return [$location, $variables];
    }

    public function decideBlockEnd(\Twig_Token $token)
    {
        return $token->test('endassets');
    }

    /**
     * Gets the tag name associated with this token parser.
     *
     * @return string The tag name
     */
    public function getTag()
    {
        return 'assets';
    }
}
PKY[�[B�$��(Twig/TokenParser/TokenParserMarkdown.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\TokenParser;

use Gantry\Component\Twig\Node\TwigNodeMarkdown;

/**
 * Adds ability to inline markdown between tags.
 *
 * {% markdown %}
 * This is **bold** and this _underlined_
 *
 * 1. This is a bullet list
 * 2. This is another item in that same list
 * {% endmarkdown %}
 */
class TokenParserMarkdown extends \Twig_TokenParser
{
    /**
     * {@inheritdoc}
     */
    public function parse(\Twig_Token $token)
    {
        $lineno = $token->getLine();
       
$this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);
        $body = $this->parser->subparse(array($this,
'decideMarkdownEnd'), true);
       
$this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);
        return new TwigNodeMarkdown($body, $lineno, $this->getTag());
    }

    /**
     * Decide if current token marks end of Markdown block.
     *
     * @param \Twig_Token $token
     * @return bool
     */
    public function decideMarkdownEnd(\Twig_Token $token)
    {
        return $token->test('endmarkdown');
    }

    /**
     * {@inheritdoc}
     */
    public function getTag()
    {
        return 'markdown';
    }
}
PKY[�[I��\	\	)Twig/TokenParser/TokenParserPageblock.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\TokenParser;

use Gantry\Component\Twig\Node\TwigNodePageblock;

/**
 * Adds javascript / style assets to head/footer/custom location.
 *
 * {% pageblock in 'bottom' with { priority: 0 } %}
 *   <div>Bottom HTML</div>
 * {% endpageblock -%}
 */
class TokenParserPageblock extends \Twig_TokenParser
{
    /**
     * Parses a token and returns a node.
     *
     * @param \Twig_Token $token A Twig_Token instance
     *
     * @return \Twig_Node A Twig_Node instance
     */
    public function parse(\Twig_Token $token)
    {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();

        list($location, $variables) = $this->parseArguments($token);

        $content = $this->parser->subparse([$this,
'decideBlockEnd'], true);
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        return new TwigNodePageblock($content, $location, $variables,
$lineno, $this->getTag());
    }

    /**
     * @param \Twig_Token $token
     * @return array
     */
    protected function parseArguments(\Twig_Token $token)
    {
        $stream = $this->parser->getStream();
        $lineno = $token->getLine();
        $location = new
\Twig_Node_Expression_Constant($stream->expect(\Twig_Token::NAME_TYPE)->getValue(),
$lineno);

        if ($stream->nextIf(\Twig_Token::NAME_TYPE, 'with')) {
            $variables =
$this->parser->getExpressionParser()->parseExpression();
        } else {
            $lineno = $token->getLine();
            $variables = new \Twig_Node_Expression_Array([], $lineno);
            $variables->setAttribute('priority', 0);
        }
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        return [$location, $variables];
    }

    public function decideBlockEnd(\Twig_Token $token)
    {
        return $token->test('endpageblock');
    }

    /**
     * Gets the tag name associated with this token parser.
     *
     * @return string The tag name
     */
    public function getTag()
    {
        return 'pageblock';
    }
}
PKY[�[
FX���'Twig/TokenParser/TokenParserScripts.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\TokenParser;

/**
 * Adds scripts to head/footer/custom location.
 *
 * {% scripts in 'head' with { priority: 2 } %}
 *   <script type="text/javascript" src="{{
url('gantry-theme://js/my.js') }}"></script>
 * {% endscripts -%}
 */
class TokenParserScripts extends TokenParserAssets
{
    public function decideBlockEnd(\Twig_Token $token)
    {
        return $token->test('endscripts');
    }

    /**
     * Gets the tag name associated with this token parser.
     *
     * @return string The tag name
     */
    public function getTag()
    {
        return 'scripts';
    }
}
PKY[�[�++B��&Twig/TokenParser/TokenParserStyles.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\TokenParser;

/**
 * Adds stylesheets to document.
 *
 * {% styles with { priority: 2 } %}
 *   <link rel="stylesheet" href="{{
url('gantry-assets://css/font-awesome.min.css') }}"
type="text/css"/>
 * {% endstyles -%}
 */
class TokenParserStyles extends TokenParserAssets
{
    public function decideBlockEnd(\Twig_Token $token)
    {
        return $token->test('endstyles');
    }

    /**
     * Gets the tag name associated with this token parser.
     *
     * @return string The tag name
     */
    public function getTag()
    {
        return 'styles';
    }
}
PKY[�[���#��&Twig/TokenParser/TokenParserSwitch.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\TokenParser;

use Gantry\Component\Twig\Node\TwigNodeSwitch;

/**
 * Adds ability use elegant switch instead of ungainly if statements
 *
 * {% switch type %}
 *   {% case 'foo' %}
 *      {{ my_data.foo }}
 *   {% case 'bar' %}
 *      {{ my_data.bar }}
 *   {% default %}
 *      {{ my_data.default }}
 * {% endswitch %}
 */
class TokenParserSwitch extends \Twig_TokenParser
{
    /**
     * {@inheritdoc}
     */
    public function parse(\Twig_Token $token)
    {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();

        $name =
$this->parser->getExpressionParser()->parseExpression();
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        // There can be some whitespace between the {% switch %} and first
{% case %} tag.
        while ($stream->getCurrent()->getType() ===
\Twig_Token::TEXT_TYPE &&
trim($stream->getCurrent()->getValue()) === '') {
            $stream->next();
        }

        $stream->expect(\Twig_Token::BLOCK_START_TYPE);

        $expressionParser = $this->parser->getExpressionParser();

        $default = null;
        $cases = [];
        $end = false;

        while (!$end) {
            $next = $stream->next();

            switch ($next->getValue()) {
                case 'case':
                    $values = [];

                    while (true) {
                        $values[] =
$expressionParser->parsePrimaryExpression();
                        // Multiple allowed values?
                        if ($stream->test(\Twig_Token::OPERATOR_TYPE,
'or')) {
                            $stream->next();
                        } else {
                            break;
                        }
                    }

                    $stream->expect(\Twig_Token::BLOCK_END_TYPE);
                    $body = $this->parser->subparse(array($this,
'decideIfFork'));
                    $cases[] = new \Twig_Node([
                        'values' => new \Twig_Node($values),
                        'body' => $body
                    ]);
                    break;

                case 'default':
                    $stream->expect(\Twig_Token::BLOCK_END_TYPE);
                    $default = $this->parser->subparse(array($this,
'decideIfEnd'));
                    break;

                case 'endswitch':
                    $end = true;
                    break;

                default:
                    throw new \Twig_Error_Syntax(sprintf('Unexpected
end of template. Twig was looking for the following tags "case",
"default", or "endswitch" to close the
"switch" block started at line %d)', $lineno), -1);
            }
        }

        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        return new TwigNodeSwitch($name, new \Twig_Node($cases), $default,
$lineno, $this->getTag());
    }

    /**
     * Decide if current token marks switch logic.
     *
     * @param \Twig_Token $token
     * @return bool
     */
    public function decideIfFork(\Twig_Token $token)
    {
        return $token->test(array('case', 'default',
'endswitch'));
    }

    /**
     * Decide if current token marks end of swtich block.
     *
     * @param \Twig_Token $token
     * @return bool
     */
    public function decideIfEnd(\Twig_Token $token)
    {
        return $token->test(array('endswitch'));
    }

    /**
     * {@inheritdoc}
     */
    public function getTag()
    {
        return 'switch';
    }
}
PKY[�[�ʱtt%Twig/TokenParser/TokenParserThrow.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\TokenParser;

use Gantry\Component\Twig\Node\TwigNodeThrow;

/**
 * Handles try/catch in template file.
 *
 * <pre>
 * {% throw 404 'Not Found' %}
 * </pre>
 */
class TokenParserThrow extends \Twig_TokenParser
{
    /**
     * Parses a token and returns a node.
     *
     * @param \Twig_Token $token A Twig_Token instance
     *
     * @return \Twig_Node A Twig_Node instance
     */
    public function parse(\Twig_Token $token)
    {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();

        $code =
$stream->expect(\Twig_Token::NUMBER_TYPE)->getValue();
        $message =
$this->parser->getExpressionParser()->parseExpression();
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        return new TwigNodeThrow($code, $message, $lineno,
$this->getTag());
    }

    /**
     * Gets the tag name associated with this token parser.
     *
     * @return string The tag name
     */
    public function getTag()
    {
        return 'throw';
    }
}
PKY[�[�TLL(Twig/TokenParser/TokenParserTryCatch.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\TokenParser;

use Gantry\Component\Twig\Node\TwigNodeTryCatch;

/**
 * Handles try/catch in template file.
 *
 * <pre>
 * {% try %}
 *    <li>{{ user.get('name') }}</li>
 * {% catch %}
 *    {{ e.message }}
 * {% endcatch %}
 * </pre>
 */
class TokenParserTryCatch extends \Twig_TokenParser
{
    /**
     * Parses a token and returns a node.
     *
     * @param \Twig_Token $token A Twig_Token instance
     *
     * @return \Twig_Node A Twig_Node instance
     */
    public function parse(\Twig_Token $token)
    {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();

        $stream->expect(\Twig_Token::BLOCK_END_TYPE);
        $try = $this->parser->subparse([$this,
'decideCatch']);
        $stream->next();
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);
        $catch = $this->parser->subparse([$this,
'decideEnd']);
        $stream->next();
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        return new TwigNodeTryCatch($try, $catch, $lineno,
$this->getTag());
    }

    public function decideCatch(\Twig_Token $token)
    {
        return $token->test(array('catch'));
    }

    public function decideEnd(\Twig_Token $token)
    {
        return $token->test(array('endtry')) ||
$token->test(array('endcatch'));
    }

    /**
     * Gets the tag name associated with this token parser.
     *
     * @return string The tag name
     */
    public function getTag()
    {
        return 'try';
    }
}
PKY[�[�
�Twig/TwigCacheFilesystem.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig;

/**
 * Class TwigCacheFilesystem
 * @package Gantry\Component\Twig
 *
 * Replaces \Twig_Cache_Filesystem, needed for being able to change PHP
versions on fly.
 */
class TwigCacheFilesystem implements \Twig_CacheInterface
{
    const FORCE_BYTECODE_INVALIDATION = 1;
    private $directory;
    private $options;
    /**
     * @param $directory string The root cache directory
     * @param $options   int    A set of options
     */
    public function __construct($directory, $options = 0)
    {
        $this->directory = rtrim($directory,
'\/').'/';
        $this->options = $options;
    }
    /**
     * {@inheritdoc}
     */
    public function generateKey($name, $className)
    {
        $hash = hash('sha256', $className . '-' .
PHP_VERSION);
        return
$this->directory.$hash[0].$hash[1].'/'.$hash.'.php';
    }
    /**
     * {@inheritdoc}
     */
    public function load($key)
    {
        @include_once $key;
    }
    /**
     * {@inheritdoc}
     */
    public function write($key, $content)
    {
        $dir = dirname($key);
        if (!is_dir($dir)) {
            if (false === @mkdir($dir, 0777, true) &&
!is_dir($dir)) {
                throw new \RuntimeException(sprintf('Unable to create
the cache directory (%s).', $dir));
            }
        } elseif (!is_writable($dir)) {
            throw new \RuntimeException(sprintf('Unable to write in
the cache directory (%s).', $dir));
        }
        $tmpFile = tempnam($dir, basename($key));
        if (false !== @file_put_contents($tmpFile, $content) &&
@rename($tmpFile, $key)) {
            @chmod($key, 0666 & ~umask());
            if (self::FORCE_BYTECODE_INVALIDATION == ($this->options
& self::FORCE_BYTECODE_INVALIDATION)) {
                // Compile cached file into bytecode cache
                if (function_exists('opcache_invalidate')) {
                    // Silence error in case if `opcache.restrict_api`
directive is set.
                    @opcache_invalidate($key, true);
                } elseif (function_exists('apc_compile_file')) {
                    @apc_compile_file($key);
                }
            }
            return;
        }
        throw new \RuntimeException(sprintf('Failed to write cache
file "%s".', $key));
    }
    /**
     * {@inheritdoc}
     */
    public function getTimestamp($key)
    {
        if (!file_exists($key)) {
            return 0;
        }
        return (int) @filemtime($key);
    }
}
PKY[�[K�
k�Q�QTwig/TwigExtension.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig;

use Gantry\Component\Content\Document\HtmlDocument;
use Gantry\Component\Gantry\GantryTrait;
use Gantry\Component\Translator\TranslatorInterface;
use Gantry\Component\Twig\TokenParser\TokenParserPageblock;
use Gantry\Component\Twig\TokenParser\TokenParserAssets;
use Gantry\Component\Twig\TokenParser\TokenParserScripts;
use Gantry\Component\Twig\TokenParser\TokenParserStyles;
use Gantry\Component\Twig\TokenParser\TokenParserTryCatch;
use Gantry\Component\Twig\TokenParser\TokenParserMarkdown;
use Gantry\Component\Twig\TokenParser\TokenParserSwitch;
use Gantry\Component\Twig\TokenParser\TokenParserThrow;
use Gantry\Framework\Gantry;
use Gantry\Framework\Markdown\Parsedown;
use Gantry\Framework\Markdown\ParsedownExtra;
use Gantry\Framework\Request;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccess;

class TwigExtension extends \Twig_Extension implements
\Twig_Extension_GlobalsInterface
{
    use GantryTrait;

    /**
     * Register some standard globals
     *
     * @return array
     */
    public function getGlobals()
    {
        return [
            'gantry' => static::gantry(),
        ];
    }

    /**
     * Return a list of all filters.
     *
     * @return array
     */
    public function getFilters()
    {
        $filters = [
            new \Twig_SimpleFilter('html', [$this,
'htmlFilter']),
            new \Twig_SimpleFilter('url', [$this,
'urlFunc']),
            new \Twig_SimpleFilter('trans_key', [$this,
'transKeyFilter']),
            new \Twig_SimpleFilter('trans', [$this,
'transFilter']),
            new \Twig_SimpleFilter('repeat', [$this,
'repeatFilter']),
            new \Twig_SimpleFilter('values', [$this,
'valuesFilter']),
            new \Twig_SimpleFilter('base64',
'base64_encode'),
            new \Twig_SimpleFilter('imagesize', [$this,
'imageSize']),
            new \Twig_SimpleFilter('truncate_text', [$this,
'truncateText']),
            new \Twig_SimpleFilter('attribute_array', [$this,
'attributeArrayFilter'], ['is_safe' =>
['html']]),
        ];

        if (1 || GANTRY5_PLATFORM !== 'grav') {
            $filters = array_merge($filters, [
                new \Twig_SimpleFilter('fieldName', [$this,
'fieldNameFilter']),
                new \Twig_SimpleFilter('json_decode', [$this,
'jsonDecodeFilter']),
                new \Twig_SimpleFilter('truncate_html', [$this,
'truncateHtml']),
                new \Twig_SimpleFilter('markdown', [$this,
'markdownFunction'], ['is_safe' =>
['html']]),
                new \Twig_SimpleFilter('nicetime', [$this,
'nicetimeFilter']),

                // Casting values
                new \Twig_SimpleFilter('string', [$this,
'stringFilter']),
                new \Twig_SimpleFilter('int', [$this,
'intFilter'], ['is_safe' => ['all']]),
                new \Twig_SimpleFilter('bool', [$this,
'boolFilter']),
                new \Twig_SimpleFilter('float', [$this,
'floatFilter'], ['is_safe' => ['all']]),
                new \Twig_SimpleFilter('array', [$this,
'arrayFilter']),
            ]);
        }

        return $filters;
    }

    /**
     * Return a list of all functions.
     *
     * @return array
     */
    public function getFunctions()
    {
        $functions = [
            new \Twig_SimpleFunction('nested', [$this,
'nestedFunc']),
            new \Twig_SimpleFunction('parse_assets', [$this,
'parseAssetsFunc']),
            new \Twig_SimpleFunction('colorContrast', [$this,
'colorContrastFunc']),
            new \Twig_SimpleFunction('get_cookie', [$this,
'getCookie']),
            new \Twig_SimpleFunction('preg_match', [$this,
'pregMatch']),
            new \Twig_SimpleFunction('imagesize', [$this,
'imageSize']),
            new \Twig_SimpleFunction('is_selected', [$this,
'is_selectedFunc']),
            new \Twig_SimpleFunction('url', [$this,
'urlFunc']),
        ];

        if (1 || GANTRY5_PLATFORM !== 'grav') {
            $functions = array_merge($functions, [
                new \Twig_SimpleFunction('array', [$this,
'arrayFilter']),
                new \Twig_SimpleFunction('json_decode', [$this,
'jsonDecodeFilter']),
            ]);
        }

        return $functions;
    }

    /**
     * @return array
     */
    public function getTokenParsers()
    {
        return [
            new TokenParserPageblock(),
            new TokenParserAssets(),
            new TokenParserScripts(),
            new TokenParserStyles(),
            new TokenParserThrow(),
            new TokenParserTryCatch(),
            new TokenParserMarkdown(),
            new TokenParserSwitch()
        ];
    }

    /**
     * Filters field name by changing dot notation into array notation.
     *
     * @param  string  $str
     * @return string
     */
    public function fieldNameFilter($str)
    {
        $path = explode('.', $str);

        return array_shift($path) . ($path ? '[' .
implode('][', $path) . ']' : '');
    }

    /**
     * Translate by using key, default on original string.
     *
     * @param $str
     * @return string
     */
    public function transKeyFilter($str)
    {
        $params = \func_get_args();
        array_shift($params);

        $key = preg_replace('|[^A-Z0-9]+|', '_',
strtoupper(implode('_', $params)));

        $translation = $this->transFilter($key);

        return $translation === $key ? $str : $translation;
    }

    /**
     * Translate string.
     *
     * @param  string  $str
     * @return string
     */
    public function transFilter($str)
    {
        /** @var TranslatorInterface $translator */
        static $translator;

        $params = \func_get_args();

        if (!$translator) {
            $translator = self::gantry()['translator'];
        }

        return \call_user_func_array([$translator, 'translate'],
$params);
    }

    /**
     * Repeat string x times.
     *
     * @param  string  $str
     * @param  int  $count
     * @return string
     */
    public function repeatFilter($str, $count)
    {
        return str_repeat($str, max(0, (int) $count));
    }


    /**
     * Decodes string from JSON.
     *
     * @param  string  $str
     * @param  bool  $assoc
     * @param int $depth
     * @param int $options
     * @return array
     */
    public function jsonDecodeFilter($str, $assoc = false, $depth = 512,
$options = 0)
    {
        return json_decode(html_entity_decode($str), $assoc, $depth,
$options);
    }

    public function imageSize($src, $attrib = true, $remote = false)
    {
        // TODO: need to better handle absolute and relative paths
        //$url =
Gantry::instance()['document']->url(trim((string) $src),
false, false);
        $width = $height = null;
        $sizes = ['width' => $width, 'height' =>
$height];
        $attr = '';

        if (@is_file($src) || $remote) {
            try {
                list($width, $height,, $attr) = @getimagesize($src);
            } catch (\Exception $e) {}

            $sizes['width'] = $width;
            $sizes['height'] = $height;
        }

        return $attrib ? $attr : $sizes;
    }

    /**
     * Reindexes values in array.
     *
     * @param array $array
     * @return array
     */
    public function valuesFilter(array $array)
    {
        return array_values($array);
    }

    /**
     * Casts input to string.
     *
     * @param mixed $input
     * @return string
     */
    public function stringFilter($input)
    {
        return (string) $input;
    }


    /**
     * Casts input to int.
     *
     * @param mixed $input
     * @return int
     */
    public function intFilter($input)
    {
        return (int) $input;
    }

    /**
     * Casts input to bool.
     *
     * @param mixed $input
     * @return bool
     */
    public function boolFilter($input)
    {
        return (bool) $input;
    }

    /**
     * Casts input to float.
     *
     * @param mixed $input
     * @return float
     */
    public function floatFilter($input)
    {
        return (float) $input;
    }

    /**
     * Casts input to array.
     *
     * @param mixed $input
     * @return array
     */
    public function arrayFilter($input)
    {
        return (array) $input;
    }

    /**
     * Takes array of attribute keys and values and converts it to properly
escaped HTML attributes.
     *
     * @example ['data-id' => 'id',
'data-key' => 'key'] => '
data-id="id" data-key="key"'
     * @example [['data-id' => 'id'],
['data-key' => 'key']] => '
data-id="id" data-key="key"'
     *
     * @param string|string[] $input
     * @return string
     */
    public function attributeArrayFilter($input)
    {
        if (\is_string($input)) {
            return $input;
        }

        $array = [];
        foreach ((array) $input as $key => $value) {
            if (\is_array($value)) {
                foreach ((array) $value as $key2 => $value2) {
                    $array[] = HtmlDocument::escape($key2) .
'="' . HtmlDocument::escape($value2, 'html_attr')
. '"';
                }
            } elseif ($key) {
                $array[] = HtmlDocument::escape($key) . '="'
. HtmlDocument::escape($value, 'html_attr') . '"';
            }
        }
        return $array ? ' ' . implode(' ', $array) :
'';
    }

    public function is_selectedFunc($a, $b)
    {
        $b = (array) $b;
        array_walk(
            $b,
            function (&$item) {
                if (\is_bool($item)) {
                    $item = (int) $item;
                }
                $item = (string) $item;
            }
        );

        return \in_array((string) $a, $b, true);
    }

    /**
     * Truncate text by number of characters but can cut off words. Removes
html tags.
     *
     * @param  string $string
     * @param  int    $limit       Max number of characters.
     *
     * @return string
     */
    public function truncateText($string, $limit = 150)
    {
        $platform = Gantry::instance()['platform'];

        return $platform->truncate($string, (int) $limit, false);
    }

    /**
     * Truncate text by number of characters but can cut off words.
     *
     * @param  string $string
     * @param  int    $limit       Max number of characters.
     *
     * @return string
     */
    public function truncateHtml($string, $limit = 150)
    {
        $platform = Gantry::instance()['platform'];

        return $platform->truncate($string, (int) $limit, true);
    }

    /**
     * @param string $string
     * @param bool $block  Block or Line processing
     * @param array $settings
     * @return mixed|string
     */
    public function markdownFunction($string, $block = true, array
$settings = null)
    {
        // Initialize the preferred variant of Parsedown
        if (!empty($settings['extra'])) {
            $parsedown = new ParsedownExtra($settings);
        } else {
            $parsedown = new Parsedown($settings);
        }

        if ($block) {
            $string = $parsedown->text($string);
        } else {
            $string = $parsedown->line($string);
        }

        return $string;
    }

    /**
     * Get value by using dot notation for nested arrays/objects.
     *
     * @example {{ nested(array,
'this.is.my.nested.variable')|json_encode }}
     *
     * @param array   $items      Array of items.
     * @param string  $name       Dot separated path to the requested
value.
     * @param mixed   $default    Default value (or null).
     * @param string  $separator  Separator, defaults to '.'
     * @return mixed  Value.
     */
    public function nestedFunc($items, $name, $default = null, $separator =
'.')
    {
        if ($items instanceof NestedArrayAccess) {
            return $items->get($name, $default, $separator);
        }
        $path = explode($separator, $name);
        $current = $items;
        foreach ($path as $field) {
            if (\is_object($current) &&
isset($current->{$field})) {
                $current = $current->{$field};
            } elseif (\is_array($current) &&
isset($current[$field])) {
                $current = $current[$field];
            } else {
                return $default;
            }
        }

        return $current;
    }

    /**
     * Return URL to the resource.
     *
     * @example {{
url('theme://images/logo.png')|default('http://www.placehold.it/150x100/f4f4f4')
}}
     *
     * @param  string $input       Resource to be located.
     * @param  bool $domain        True to include domain name.
     * @param  int $timestamp_age  Append timestamp to files that are less
than x seconds old. Defaults to a week.
     *                             Use value <= 0 to disable the
feature.
     * @return string|null         Returns url to the resource or null if
resource was not found.
     */
    public function urlFunc($input, $domain = false, $timestamp_age = null)
    {
        $gantry = Gantry::instance();

        return $gantry['document']->url(trim((string) $input),
$domain, $timestamp_age);
    }

    /**
     * Filter stream URLs from HTML input.
     *
     * @param  string $str          HTML input to be filtered.
     * @param  bool $domain         True to include domain name.
     * @param  int $timestamp_age   Append timestamp to files that are less
than x seconds old. Defaults to a week.
     *                              Use value <= 0 to disable the
feature.
     * @return string               Returns modified HTML.
     */
    public function htmlFilter($str, $domain = false, $timestamp_age =
null)
    {
        $gantry = Gantry::instance();

        return $gantry['document']->urlFilter($str, $domain,
$timestamp_age);
    }

    /**
     * @param \libXMLError $error
     * @param string $input
     * @throws \RuntimeException
     */
    protected function dealXmlError(\libXMLError $error, $input)
    {
        switch ($error->level) {
            case LIBXML_ERR_WARNING:
                $level = 1;
                $message = "DOM Warning {$error->code}: ";
                break;
            case LIBXML_ERR_ERROR:
                $level = 2;
                $message = "DOM Error {$error->code}: ";
                break;
            case LIBXML_ERR_FATAL:
                $level = 3;
                $message = "Fatal DOM Error {$error->code}: ";
                break;
            default:
                $level = 3;
                $message = "Unknown DOM Error {$error->code}:
";
        }
        $message .= "{$error->message} while
parsing:\n{$input}\n";

        if ($level <= 2 && !Gantry::instance()->debug()) {
            return;
        }

        throw new \RuntimeException($message, 500);
    }

    /**
     * Move supported document head elements into platform document object,
return all
     * unsupported tags in a string.
     *
     * @param string $input
     * @param string $location
     * @param int $priority
     * @return string
     */
    public function parseAssetsFunc($input, $location = 'head',
$priority = 0)
    {
        if ($location === 'head') {
            $scope = 'head';
            $html = "<!doctype
html>\n<html><head>{$input}</head><body></body></html>";
        } else {
            $scope = 'body';
            $html = "<!doctype
html>\n<html><head></head><body>{$input}</body></html>";
        }

        libxml_clear_errors();

        $internal = libxml_use_internal_errors(true);

        $doc = new \DOMDocument();
        $doc->loadHTML($html);
        foreach (libxml_get_errors() as $error) {
            $this->dealXmlError($error, $html);
        }

        libxml_clear_errors();

        libxml_use_internal_errors($internal);

        $raw = [];
        /** @var \DomElement $element */
        foreach
($doc->getElementsByTagName($scope)->item(0)->childNodes as
$element) {
            if (empty($element->tagName)) {
                continue;
            }
            $result = ['tag' => $element->tagName,
'content' => $element->textContent];
            foreach ($element->attributes as $attribute) {
                $result[$attribute->name] = $attribute->value;
            }
            $success =
Gantry::instance()['document']->addHeaderTag($result,
$location, (int) $priority);
            if (!$success) {
                $raw[] = $doc->saveHTML($element);
            }
        }

        return implode("\n", $raw);
    }

    public function colorContrastFunc($value)
    {
        $value = str_replace(' ', '', $value);
        $rgb = new \stdClass;
        $opacity = 1;

        if (0 !== strpos($value, 'rgb')) {
            $value = str_replace('#', '', $value);
            if (\strlen($value) === 3) {
                $h0 = str_repeat(substr($value, 0, 1), 2);
                $h1 = str_repeat(substr($value, 1, 1), 2);
                $h2 = str_repeat(substr($value, 2, 1), 2);
                $value = $h0 . $h1 . $h2;
            }

            $rgb->r = hexdec(substr($value, 0, 2));
            $rgb->g = hexdec(substr($value, 2, 2));
            $rgb->b = hexdec(substr($value, 4, 2));
        } else {
           
preg_match("/(\\d+),\\s*(\\d+),\\s*(\\d+)(?:,\\s*(1\\.|0?\\.?[0-9]?+))?/uim",
$value, $matches);
            $rgb->r = $matches[1];
            $rgb->g = $matches[2];
            $rgb->b = $matches[3];
            $opacity = isset($matches[4]) ? $matches[4] : 1;
            $opacity = substr($opacity, 0, 1) === '.' ?
'0' . $opacity : $opacity;
        }

        $yiq = ((($rgb->r * 299) + ($rgb->g * 587) + ($rgb->b *
114)) / 1000) >= 128;
        $contrast = $yiq || (!$opacity || (float) $opacity < 0.35);

        return $contrast;
    }

    /**
     * Displays a facebook style 'time ago' formatted date/time.
     *
     * @param string|int $date
     * @param bool $long_strings
     *
     * @return string
     */
    public function nicetimeFilter($date, $long_strings = true)
    {
        static $lengths = [60, 60, 24, 7, 4.35, 12, 10];
        static $periods_long = [
            'GANTRY5_ENGINE_NICETIME_SECOND',
            'GANTRY5_ENGINE_NICETIME_MINUTE',
            'GANTRY5_ENGINE_NICETIME_HOUR',
            'GANTRY5_ENGINE_NICETIME_DAY',
            'GANTRY5_ENGINE_NICETIME_WEEK',
            'GANTRY5_ENGINE_NICETIME_MONTH',
            'GANTRY5_ENGINE_NICETIME_YEAR',
            'GANTRY5_ENGINE_NICETIME_DECADE'
        ];
        static $periods_short = [
            'GANTRY5_ENGINE_NICETIME_SEC',
            'GANTRY5_ENGINE_NICETIME_MIN',
            'GANTRY5_ENGINE_NICETIME_HR',
            'GANTRY5_ENGINE_NICETIME_DAY',
            'GANTRY5_ENGINE_NICETIME_WK',
            'GANTRY5_ENGINE_NICETIME_MO',
            'GANTRY5_ENGINE_NICETIME_YR',
            'GANTRY5_ENGINE_NICETIME_DEC'
        ];

        if (empty($date)) {
            return
$this->transFilter('GANTRY5_ENGINE_NICETIME_NO_DATE_PROVIDED');
        }

        $periods = $long_strings ? $periods_long : $periods_short;

        $now = time();

        // check if unix timestamp
        if ((string)(int)$date === (string)$date) {
            $unix_date = (int)$date;
        } else {
            $unix_date = strtotime($date);
        }

        // check validity of date
        if (!$unix_date) {
            return
$this->transFilter('GANTRY5_ENGINE_NICETIME_BAD_DATE');
        }

        // is it future date or past date
        if ($now > $unix_date) {
            $difference = $now - $unix_date;
            $tense      =
$this->transFilter('GANTRY5_ENGINE_NICETIME_AGO');

        } else if ($now === $unix_date) {
            $difference = $now - $unix_date;
            $tense      =
$this->transFilter('GANTRY5_ENGINE_NICETIME_JUST_NOW');

        } else {
            $difference = $unix_date - $now;
            $tense      =
$this->transFilter('GANTRY5_ENGINE_NICETIME_FROM_NOW');
        }


        for ($j = 0; $difference >= $lengths[$j] && $j <
\count($lengths) - 1; $j++) {
            $difference /= $lengths[$j];
        }
        $period = $periods[$j];

        $difference = round($difference);

        if ($difference !== 1) {
            $period .= '_PLURAL';
        }

        $period = $this->transFilter($period);

        if ($now === $unix_date) {
            return $tense;
        }

        return "{$difference} {$period} {$tense}";
    }

    public function getCookie($name)
    {
        $gantry = Gantry::instance();

        /** @var Request $request */
        $request = $gantry['request'];

        return $request->cookie[$name];
    }

    public function pregMatch($pattern, $subject, &$matches = [])
    {
        preg_match($pattern, $subject, $matches);

        return $matches ?: false;
    }
}
PKZ[�[��6���Url/Url.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Url;

class Url
{
    /**
     * UTF8 aware parse_url().
     *
     * @param  string $url
     * @param  bool   $queryArray
     * @return array|bool
     */
    public static function parse($url, $queryArray = false)
    {
        $encodedUrl = preg_replace_callback(
            '%[^:/@?&=#]+%usD',
            function ($matches) { return rawurlencode($matches[0]); },
            $url
        );

        // PHP versions below 5.4.7 have troubles with URLs without scheme,
so lets help by fixing that.
        // TODO: This is not needed in PHP >= 5.4.7, but for now we need
to test if the function works.
        if ('/' === $encodedUrl[0] && false !==
strpos($encodedUrl, '://')) {
            $schemeless = true;

            // Fix the path so that parse_url() will not return false.
            $parts = parse_url('fake://fake.com' . $encodedUrl);

            // Remove the fake values.
            unset($parts['scheme'], $parts['host']);

        } else {
            $parts = parse_url($encodedUrl);
        }

        if (!$parts) {
            return false;
        }

        // PHP versions below 5.4.7 do not understand schemeless URLs
starting with // either.
        if (isset($schemeless) && !isset($parts['host'])
&& 0 === strpos($encodedUrl, '//')) {
            // Path is stored in format: //[host]/[path], so let's fix
it.
            list($parts['host'], $path) = explode('/',
substr($parts['path'], 2), 2);
            $parts['path'] = "/{$path}";
        }

        foreach($parts as $name => $value) {
            $parts[$name] = rawurldecode($value);
        }

        // Return query string also as an array if requested.
        if ($queryArray) {
            $parts['vars'] = isset($parts['query']) ?
static::parseQuery($parts['query']) : [];
        }

        return $parts;
    }

    /**
     * Parse query string and return array.
     *
     * @param $query
     * @return mixed
     */
    public static function parseQuery($query)
    {
        parse_str($query, $vars);

        return $vars;
    }

    /**
     * Build parsed URL array.
     *
     * @param array $parsed_url
     * @return string
     */
    public static function build(array $parsed_url)
    {
        // Build query string from variables if they are set.
        if (isset($parsed_url['vars'])) {
            $parsed_url['query'] =
static::buildQuery($parsed_url['vars']);
        }

        // Build individual parts of the url.
        $scheme   = isset($parsed_url['scheme']) ?
$parsed_url['scheme'] . '://' : '';
        $host     = isset($parsed_url['host']) ?
$parsed_url['host'] : '';
        $port     = isset($parsed_url['port']) ? ':' .
$parsed_url['port'] : '';
        $user     = isset($parsed_url['user']) ?
$parsed_url['user'] : '';
        $pass     = isset($parsed_url['pass']) ? ':' .
$parsed_url['pass']  : '';
        $pass     = ($user || $pass) ? "{$pass}@" : '';
        $path     = isset($parsed_url['path']) ?
$parsed_url['path'] : '';
        $query    = isset($parsed_url['query']) ? '?' .
$parsed_url['query'] : '';
        $fragment = isset($parsed_url['fragment']) ?
'#' . $parsed_url['fragment'] : '';
        $scheme   = $host && !$scheme ? '//' : $scheme;

        return
"{$scheme}{$user}{$pass}{$host}{$port}{$path}{$query}{$fragment}";
    }

    /**
     * Build query string from variables.
     *
     * @param array $vars
     * @return null|string
     */
    public static function buildQuery(array $vars)
    {
        $list = [];
        foreach ($vars as $key => $var) {
            $list[] = $key . '=' . rawurlencode($var);
        }

        return $list ? implode('&', $list) : null;
    }
}
PKZ[�[�H��AAWhoops/SystemFacade.phpnu�[���<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Whoops;

class SystemFacade extends \Whoops\Util\SystemFacade
{
    protected $registeredPatterns;
    protected $whoopsErrorHandler;
    protected $whoopsExceptionHandler;
    protected $whoopsShutdownHandler;
    protected $platformExceptionHandler;

    /**
     * @param  array|string $patterns List or a single regex pattern to
match for silencing errors in particular files.
     */
    public function __construct($patterns = [])
    {
        $this->registeredPatterns = array_map(
            function ($pattern) {
                return["pattern" => $pattern];
            },
            (array) $patterns
        );
    }

    /**
     * @param callable $handler
     * @param int|string $types
     *
     * @return callable|null
     */
    public function setErrorHandler(callable $handler, $types =
'use-php-defaults')
    {
        // Workaround for PHP 5.5
        if ($types === 'use-php-defaults') {
            $types = E_ALL | E_STRICT;
        }

        $this->whoopsErrorHandler = $handler;

        return parent::setErrorHandler([$this, 'handleError'],
$types);
    }

    /**
     * @param callable $function
     *
     * @return void
     */
    public function registerShutdownFunction(callable $function)
    {
        $this->whoopsShutdownHandler = $function;
        register_shutdown_function([$this, 'handleShutdown']);
    }

    /**
     * @param callable $handler
     *
     * @return callable|null
     */
    public function setExceptionHandler(callable $handler)
    {
        $this->whoopsExceptionHandler = $handler;
        $this->platformExceptionHandler =
parent::setExceptionHandler([$this, 'handleException']);

        return $this->platformExceptionHandler;
    }

    /**
     * Converts generic PHP errors to \ErrorException instances, before
passing them off to be handled.
     *
     * This method MUST be compatible with set_error_handler.
     *
     * @param int    $level
     * @param string $message
     * @param string $file
     * @param int    $line
     *
     * @return bool
     * @throws \ErrorException
     */
    public function handleError($level, $message, $file = null, $line =
null)
    {
        $handler = $this->whoopsErrorHandler;

        if (!$this->registeredPatterns) {
            // Just forward to parent function is there aren't no
registered patterns.
            return $handler($level, $message, $file, $line);

        }

        // If there are registered patterns, only handle errors if error
matches one of the patterns.
        if ($level & error_reporting()) {
            foreach ($this->registeredPatterns as $entry) {
                $pathMatches = $file &&
preg_match($entry["pattern"], $file);
                if ($pathMatches) {
                    return $handler($level, $message, $file, $line);
                }
            }
        }

        // Propagate error to the next handler, allows error_get_last() to
work on silenced errors.
        return false;
    }

    /**
     * Handles an exception, ultimately generating a Whoops error page.
     *
     * @param  \Throwable $exception
     * @return void
     */
    public function handleException($exception)
    {
        $handler = $this->whoopsExceptionHandler;

        // If there are registered patterns, only handle errors if error
matches one of the patterns.
        if ($this->registeredPatterns) {
            foreach ($this->registeredPatterns as $entry) {
                $file = $exception->getFile();
                $pathMatches = $file &&
preg_match($entry["pattern"], $file);
                if ($pathMatches) {
                    $handler($exception);
                    return;
                }
            }
        }

        // Propagate error to the next handler.
        if ($this->platformExceptionHandler) {
            call_user_func_array($this->platformExceptionHandler,
[&$exception]);
        }
    }

    /**
     * Special case to deal with Fatal errors and the like.
     */
    public function handleShutdown()
    {
        $handler = $this->whoopsShutdownHandler;

        $error = $this->getLastError();

        // Ignore core warnings and errors.
        if ($error && !($error['type'] &
(E_CORE_WARNING | E_CORE_ERROR))) {
            $handler();
        }
    }
}
PKk�[��fk�3�3ComponentHelper.phpnu�[���PKk�[�3��
�
:4ComponentRecord.phpnu�[���PKk�[��5d��'D?Exception/MissingComponentException.phpnu�[���PKk�[u�(zz7CRouter/RouterBase.phpnu�[���PKk�[�M�6���HRouter/RouterInterface.phpnu�[���PKk�[�2L	��KRouter/RouterLegacy.phpnu�[���PKk�[�`��
TRouter/RouterView.phpnu�[���PKk�[���xx"�iRouter/RouterViewConfiguration.phpnu�[���PKk�[UA*���zRouter/Rules/MenuRules.phpnu�[���PKk�[��`TT{�Router/Rules/NomenuRules.phpnu�[���PKk�[fnJoo�Router/Rules/RulesInterface.phpnu�[���PKk�[_�Y��٧Router/Rules/StandardRules.phpnu�[���PKJ[�[�(r����Admin/HtmlController.phpnu�[���PKJ[�[��p�����Admin/JsonController.phpnu�[���PKJ[�[ܵ����#�Assignments/AbstractAssignments.phpnu�[���PKJ[�[�d�//
W�Assignments/AssignmentFilter.phpnu�[���PKJ[�[3�U���$�Assignments/AssignmentsInterface.phpnu�[���PKJ[�[�)!�99Collection/Collection.phpnu�[���PKJ[�[�?�~~"�Collection/CollectionInterface.phpnu�[���PKK[�[�c��9$9$j
Config/BlueprintForm.phpnu�[���PKK[�[ù����.Config/BlueprintSchema.phpnu�[���PKK[�[�-��&JConfig/CompiledBase.phpnu�[���PKK[�[�ԂDD�bConfig/CompiledBlueprints.phpnu�[���PKK[�[��ͭ

�kConfig/CompiledConfig.phpnu�[���PKK[�[Ѱ�Pdd�uConfig/Config.phpnu�[���PKK[�[ҽ`\!\!��Config/ConfigFileFinder.phpnu�[���PKK[�[��VV4�Config/Validation.phpnu�[���PKK[�[��"�Config/ValidationException.phpnu�[���PKM[�[#NJ����Content/Block/ContentBlock.phpnu�[���PKM[�[{�?:xx'�&Content/Block/ContentBlockInterface.phpnu�[���PKM[�[��ue�2�2�*Content/Block/HtmlBlock.phpnu�[���PKM[�[�v*B��$�]Content/Block/HtmlBlockInterface.phpnu�[���PKM[�[	�Q��i�i!�aContent/Document/HtmlDocument.phpnu�[���PKN[�[,���"�Controller/BaseController.phpnu�[���PKN[�[���00h�Controller/HtmlController.phpnu�[���PKN[�[0s�����Controller/JsonController.phpnu�[���PKN[�[�VVaa)&�Controller/RestfulControllerInterface.phpnu�[���PKN[�[��7���File/CompiledFile.phpnu�[���PKN[�[{BB!File/CompiledYamlFile.phpnu�[���PKN[�[�{�+�+�	Filesystem/Folder.phpnu�[���PKO[�[ڀ}e

�5Filesystem/Streams.phpnu�[���PKO[�[����WW�@Gantry/GantryTrait.phpnu�[���PKO[�[�;o�rDGettext/Gettext.phpnu�[���PKO[�[�xх�����PLayout/Layout.phpnu�[���PKO[�[.|�G	
	
��Layout/LayoutReader.phpnu�[���PKO[�[Q�E���O�Layout/Version/Format0.phpnu�[���PKQ[�[��+�#�#�Layout/Version/Format1.phpnu�[���PKQ[�[��]��E�E-
Layout/Version/Format2.phpnu�[���PKQ[�[j픞--=SMenu/AbstractMenu.phpnu�[���PKQ[�[��1D�&�&
��Menu/Item.phpnu�[���PKQ[�[�{{GLGL��Outline/OutlineCollection.phpnu�[���PKR[�[�T�ڍ�(�Position/Module.phpnu�[���PKR[�[�l*�)#)#�Position/Position.phpnu�[���PKR[�[���..f(Position/Positions.phpnu�[���PKR[�[���O���ARemote/Response.phpnu�[���PKR[�[��N@�]Request/Input.phpnu�[���PKS[�[(5����kRequest/Request.phpnu�[���PKT[�[]�>J��rResponse/HtmlResponse.phpnu�[���PKT[�[(�gg&tResponse/JsonResponse.phpnu�[���PKT[�[�~�77ւResponse/RedirectResponse.phpnu�[���PKT[�[ʚ@%��Z�Response/Response.phpnu�[���PKU[�[V�99j�Router/Router.phpnu�[���PKU[�[X}�q&q&�Stylesheet/CssCompiler.phpnu�[���PKU[�[�03��#��Stylesheet/CssCompilerInterface.phpnu�[���PKU[�[�d瑄,�,��Stylesheet/Scss/Compiler.phpnu�[���PKU[�[�}���Stylesheet/ScssCompiler.phpnu�[���PKU[�[�i}}�!System/Messages.phpnu�[���PKV[�[�Y�22�$Theme/AbstractTheme.phpnu�[���PKV[�[�����ATheme/ThemeDetails.phpnu�[���PKX[�[1���%�%5YTheme/ThemeInstaller.phpnu�[���PKX[�[��IA��jTheme/ThemeInterface.phpnu�[���PKX[�[b����Z�Z��Theme/ThemeTrait.phpnu�[���PKX[�[$�E��
�
��Translator/Translator.phpnu�[���PKX[�[�f��GG"�Translator/TranslatorInterface.phpnu�[���PKX[�[�Ϸ���Twig/Node/TwigNodeAssets.phpnu�[���PKX[�[���y���Twig/Node/TwigNodeMarkdown.phpnu�[���PKY[�[7n^c���
Twig/Node/TwigNodePageblock.phpnu�[���PKY[�[�ܽ���yTwig/Node/TwigNodeScripts.phpnu�[���PKY[�[JtE5���Twig/Node/TwigNodeStyles.phpnu�[���PKY[�[�$��		�Twig/Node/TwigNodeSwitch.phpnu�[���PKY[�[�K��D
Twig/Node/TwigNodeThrow.phpnu�[���PKY[�[Bh���
%Twig/Node/TwigNodeTryCatch.phpnu�[���PKY[�[��5��
�
&	,Twig/TokenParser/TokenParserAssets.phpnu�[���PKY[�[B�$��(�6Twig/TokenParser/TokenParserMarkdown.phpnu�[���PKY[�[I��\	\	)?=Twig/TokenParser/TokenParserPageblock.phpnu�[���PKY[�[
FX���'�FTwig/TokenParser/TokenParserScripts.phpnu�[���PKY[�[�++B��&$KTwig/TokenParser/TokenParserStyles.phpnu�[���PKY[�[���#��&GOTwig/TokenParser/TokenParserSwitch.phpnu�[���PKY[�[�ʱtt%�^Twig/TokenParser/TokenParserThrow.phpnu�[���PKY[�[�TLL(RdTwig/TokenParser/TokenParserTryCatch.phpnu�[���PKY[�[�
��kTwig/TwigCacheFilesystem.phpnu�[���PKY[�[K�
k�Q�Q_wTwig/TwigExtension.phpnu�[���PKZ[�[��6���\�Url/Url.phpnu�[���PKZ[�[�H��AAp�Whoops/SystemFacade.phpnu�[���PK^^X!��