// 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);
}
}
}
$file->free();
$this->content = $cache['data'];
}
} catch (\Exception $e) {
throw new \RuntimeException(sprintf('Failed to read %s: %s', Gantry::basename($this->filename), $e->getMessage()), 500, $e);
}
return parent::content($var);
}
}
if ($data !== null) {
$this->content($data);
}
$filename = $this->filename;
if (is_link($filename)) {
$realname = realpath($filename);
if ($realname === false) {
throw new RuntimeException('Failed to save file ' . $filename);
}
$filename = $realname;
}
$dir = dirname($filename);
if (!$dir || !$this->mkdir($dir)) {
throw new RuntimeException('Creating directory failed for ' . $filename);
}
try {
if ($this->handle) {
$tmp = true;
// As we are using non-truncating locking, make sure that the file is empty before writing.
if (@ftruncate($this->handle, 0) === false || @fwrite($this->handle, $this->raw()) === false) {
// Writing file failed, throw an error.
$tmp = false;
}
} else {
// Create file with a temporary name and rename it to make the save action atomic.
$tmp = $this->tempname($filename);
if (file_put_contents($tmp, $this->raw()) === false) {
$tmp = false;
} elseif (@rename($tmp, $filename) === false) {
@unlink($tmp);
$tmp = false;
}
}
* @return array
*/
public function content($var = null)
{
/** @var array $content */
$content = parent::content($var);
return $content;
}
/**
* Saves PHP file and invalidates opcache.
*
* @param mixed $data Optional data to be saved, usually array.
* @return void
* @throws RuntimeException
*/
public function save($data = null)
{
parent::save($data);
// Invalidate configuration file from the opcache.
if (null !== $this->filename && function_exists('opcache_invalidate')) {
@opcache_invalidate($this->filename, true);
}
}
/**
* Check contents and make sure it is in correct format.
*
* @param mixed $var
* @return array
* @throws RuntimeException
*/
protected function check($var)
{
if (!(is_array($var) || is_object($var))) {
throw new RuntimeException('Provided data is not an array');
}
) {
// 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);
}
}
}
$file->free();
$this->content = $cache['data'];
}
} catch (\Exception $e) {
throw new \RuntimeException(sprintf('Failed to read %s: %s', Gantry::basename($this->filename), $e->getMessage()), 500, $e);
}
return parent::content($var);
}
*
* @param string $theme
*/
public function __construct($theme)
{
$gantry = Gantry::instance();
/** @var UniformResourceLocator $locator */
$locator = $gantry['locator'];
$filename = $locator->findResource("gantry-themes://{$theme}/custom/gantry/theme.yaml") ?: $locator->findResource("gantry-themes://{$theme}/gantry/theme.yaml");
if (!$filename) {
throw new \RuntimeException(sprintf('Theme %s not found', $theme), 404);
}
/** @var string $cache */
$cache = $locator->findResource("gantry-cache://{$theme}/compiled/yaml", true, true);
$file = CompiledYamlFile::instance($filename);
$this->items = (array)$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());
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
*/
'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';
}
// FIXME: Do not hardcode this file.
$language->load('files_gantry5_nucleus', JPATH_SITE);
if ($application->isClient('site')) {
// Load our custom positions file as frontend requires the strings to be there.
$filename = $locator("gantry-theme://language/en-GB/en-GB.tpl_{$this->name}_positions.ini");
if ($filename) {
$language->load("tpl_{$this->name}_positions", \dirname(\dirname(\dirname($filename))), 'en-GB');
}
// Load template language files, including overrides.
$paths = $locator->findResources('gantry-theme://language');
foreach (array_reverse($paths) as $path) {
$language->load("tpl_{$this->name}", \dirname($path));
}
}
$this->language = 'en-gb';
$this->direction = 'ltr';
$this->url = Uri::root(true) . '/templates/' . $this->name;
PluginHelper::importPlugin('gantry5');
// Trigger the onGantryThemeInit event.
$application->triggerEvent('onGantry5ThemeInit', ['theme' => $this]);
}
/**
* Get list of twig paths.
*
* @return array
*/
public static function getTwigPaths()
{
/** @var UniformResourceLocator $locator */
$locator = static::gantry()['locator'];
return $locator->mergeResources(['gantry-theme://twig', 'gantry-engine://twig']);
/** @var Environment|null */
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 ?: Gantry::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 Environment $twig
* @param LoaderInterface $loader
*/
class_exists('\\Gantry\\Framework\\Gantry') or die;
// Define the template.
class GantryTheme extends \Gantry\Framework\Theme {}
// Initialize theme stream.
$gantry['platform']->set(
'streams.gantry-theme.prefixes',
['' => [
"gantry-themes://{$gantry['theme.name']}/custom",
"gantry-themes://{$gantry['theme.name']}",
"gantry-themes://{$gantry['theme.name']}/common"
]]
);
// Define Gantry services.
$gantry['theme'] = function ($c) {
return new GantryTheme($c['theme.path'], $c['theme.name']);
};
{
if (!isset($this->keys[$id])) {
throw new UnknownIdentifierException($id);
}
if (
isset($this->raw[$id])
|| !\is_object($this->values[$id])
|| isset($this->protected[$this->values[$id]])
|| !\method_exists($this->values[$id], '__invoke')
) {
return $this->values[$id];
}
if (isset($this->factories[$this->values[$id]])) {
return $this->values[$id]($this);
}
$raw = $this->values[$id];
$val = $this->values[$id] = $raw($this);
$this->raw[$id] = $raw;
$this->frozen[$id] = true;
return $val;
}
/**
* Checks if a parameter or an object is set.
*
* @param string $id The unique identifier for the parameter or object
*
* @return bool
*/
#[\ReturnTypeWillChange]
public function offsetExists($id)
{
return isset($this->keys[$id]);
}
$gantry = Gantry::instance();
// Initialize the template.
$gantry['theme.path'] = JPATH_THEMES . "/{$templateName}";
$gantry['theme.name'] = $templateName;
$classPath = $gantry['theme.path'] . '/custom/includes/theme.php';
if (!is_file($classPath)) {
$classPath = $gantry['theme.path'] . '/includes/theme.php';
}
include_once $classPath;
}
if (\GANTRY_DEBUGGER) {
Debugger::addMessage("Using Gantry 5 template {$templateName}");
}
/** @var Theme $theme */
$theme = $gantry['theme'];
$assignments = new Assignments();
if (\GANTRY_DEBUGGER) {
Debugger::addMessage('Selecting outline (rules, matches, scores):', 'debug');
Debugger::addMessage($assignments->getPage(), 'debug');
Debugger::addMessage($assignments->loadAssignments(), 'debug');
Debugger::addMessage($assignments->matches(), 'debug');
Debugger::addMessage($assignments->scores(), 'debug');
}
$theme->setLayout($assignments->select());
if ($this->params->get('asset_timestamps', 1)) {
$age = (int)($this->params->get('asset_timestamps_period', 7) * 86400);
Document::$timestamp_age = $age > 0 ? $age : PHP_INT_MAX;
} else {
Document::$timestamp_age = 0;
}
}
/**
* Return global configuration for Gantry5.
*
* @param array $global
*/
public function onGantryGlobalConfig(&$global)
{
$global = $this->params->toArray();
}
public function onAfterRoute()
{
if (version_compare(JVERSION, '4.0', '<')) {
// In Joomla 3.9 we need to make sure that user identity has been loaded.
$this->app->loadIdentity();
}
if ($this->app->isClient('site')) {
$this->onAfterRouteSite();
} elseif ($this->app->isClient('administrator')) {
$this->onAfterRouteAdmin();
}
}
/**
* Document gets set during dispatch, we need language and direction.
*/
public function onAfterDispatch()
{
if (class_exists('Gantry\Framework\Gantry')) {
$this->onAfterDispatchSiteAdmin();
}
}
public function onAfterRender()
{
if ($this->app->isClient('site') && class_exists('Gantry\Framework\Gantry')) {
$this->onAfterRenderSite();
* @param array &$args Arguments
*
* @return mixed Routine return value
*
* @since 1.5
*/
public function update(&$args)
{
// First let's get the event from the argument array. Next we will unset the
// event argument as it has no bearing on the method to handle the event.
$event = $args['event'];
unset($args['event']);
/*
* If the method to handle an event exists, call it and return its return
* value. If it does not exist, return null.
*/
if (method_exists($this, $event))
{
return call_user_func_array(array($this, $event), array_values($args));
}
}
}
if (!isset($this->_methods[$event]) || empty($this->_methods[$event]))
{
// No Plugins Associated To Event!
return $result;
}
// Loop through all plugins having a method matching our event
foreach ($this->_methods[$event] as $key)
{
// Check if the plugin is present.
if (!isset($this->_observers[$key]))
{
continue;
}
// Fire the event for an object based observer.
if (is_object($this->_observers[$key]))
{
$args['event'] = $event;
$value = $this->_observers[$key]->update($args);
}
// Fire the event for a function based observer.
elseif (is_array($this->_observers[$key]))
{
$value = call_user_func_array($this->_observers[$key]['handler'], array_values($args));
}
if (isset($value))
{
$result[] = $value;
}
}
return $result;
}
/**
* Attach an observer object
*
* @param object $observer An observer object to attach
}
return $this;
}
/**
* Calls all handlers associated with an event group.
*
* @param string $event The event name.
* @param array $args An array of arguments (optional).
*
* @return array An array of results from each function call, or null if no dispatcher is defined.
*
* @since 3.0.0
*/
public function triggerEvent($event, array $args = null)
{
if ($this->dispatcher instanceof \JEventDispatcher)
{
return $this->dispatcher->trigger($event, $args);
}
return;
}
/**
* Allows the application to load a custom or default dispatcher.
*
* The logic and options for creating this object are adequately generic for default cases
* but for many applications it will make sense to override this method and create event
* dispatchers, if required, based on more specific needs.
*
* @param \JEventDispatcher $dispatcher An optional dispatcher object. If omitted, the factory dispatcher is created.
*
* @return BaseApplication This method is chainable.
*
* @since 3.0.0
*/
public function loadDispatcher(\JEventDispatcher $dispatcher = null)
{
$this->setHeader('Expires', 'Wed, 17 Aug 2005 00:00:00 GMT', true);
$this->setHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT', true);
$this->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', false);
$this->setHeader('Pragma', 'no-cache');
$this->sendHeaders();
$this->redirect((string) $oldUri, 301);
}
}
}
foreach ($result as $key => $value)
{
$this->input->def($key, $value);
}
// Trigger the onAfterRoute event.
\JPluginHelper::importPlugin('system');
$this->triggerEvent('onAfterRoute');
}
/**
* Sets the value of a user state variable.
*
* @param string $key The path of the state.
* @param mixed $value The value of the variable.
*
* @return mixed The previous state, if one existed.
*
* @since 3.2
*/
public function setUserState($key, $value)
{
$session = \JFactory::getSession();
$registry = $session->get('registry');
if ($registry !== null)
{
return $registry->set($key, $value);
parent::render();
}
/**
* Route the application.
*
* Routing is the process of examining the request environment to determine which
* component should receive the request. The component optional parameters
* are then set in the request object to be processed when the application is being
* dispatched.
*
* @return void
*
* @since 3.2
*/
protected function route()
{
// Execute the parent method
parent::route();
$Itemid = $this->input->getInt('Itemid', null);
$this->authorise($Itemid);
}
/**
* Set the current state of the detect browser option.
*
* @param boolean $state The new state of the detect browser option
*
* @return boolean The previous state
*
* @since 3.2
*/
public function setDetectBrowser($state = false)
{
$old = $this->_detect_browser;
$this->_detect_browser = $state;
return $old;
$this->triggerEvent('onAfterDispatch');
}
/**
* Method to run the Web application routines.
*
* @return void
*
* @since 3.2
*/
protected function doExecute()
{
// Initialise the application
$this->initialiseApp();
// Mark afterInitialise in the profiler.
JDEBUG ? $this->profiler->mark('afterInitialise') : null;
// Route the application
$this->route();
// Mark afterRoute in the profiler.
JDEBUG ? $this->profiler->mark('afterRoute') : null;
/*
* Check if the user is required to reset their password
*
* Before $this->route(); "option" and "view" can't be safely read using:
* $this->input->getCmd('option'); or $this->input->getCmd('view');
* ex: due of the sef urls
*/
$this->checkUserRequireReset('com_users', 'profile', 'edit', 'com_users/profile.save,com_users/profile.apply,com_users/user.logout');
// Dispatch the application
$this->dispatch();
// Mark afterDispatch in the profiler.
JDEBUG ? $this->profiler->mark('afterDispatch') : null;
}
array('option', 'view', 'format', 'lang', 'Itemid', 'template', 'templateStyle', 'task'),
function($systemVariable) use ($input) {
return $input->exists($systemVariable) && is_array($input->getRaw($systemVariable));
}
);
// Unset invalid system variables
foreach ($invalidInputVariables as $systemVariable)
{
$input->set($systemVariable, null);
}
// Abort when there are invalid variables
if ($invalidInputVariables)
{
throw new \RuntimeException('Invalid input, aborting application.');
}
// Perform application routines.
$this->doExecute();
// If we have an application document object, render it.
if ($this->document instanceof \JDocument)
{
// Render the application output.
$this->render();
}
if ($this->get('block_floc', 1))
{
$headers = $this->getHeaders();
$notPresent = true;
foreach ($headers as $header)
{
if (strtolower($header['name']) === 'permissions-policy')
{
// Append interest-cohort if the Permissions-Policy is not set
if (strpos($header['value'], 'interest-cohort') === false)
{
include_once __DIR__ . '/defines.php';
}
if (!defined('_JDEFINES'))
{
define('JPATH_BASE', __DIR__);
require_once JPATH_BASE . '/includes/defines.php';
}
require_once JPATH_BASE . '/includes/framework.php';
// Set profiler start time and memory usage and mark afterLoad in the profiler.
JDEBUG ? JProfiler::getInstance('Application')->setStart($startTime, $startMem)->mark('afterLoad') : null;
// Instantiate the application.
$app = JFactory::getApplication('site');
// Execute the application.
$app->execute();