2011-03-24 11:30:20 +01:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* A class which builds a manifest of all templates present in a directory,
|
|
|
|
* in both modules and themes.
|
|
|
|
*
|
2012-04-12 08:02:46 +02:00
|
|
|
* @package framework
|
2011-03-24 11:30:20 +01:00
|
|
|
* @subpackage manifest
|
|
|
|
*/
|
|
|
|
class SS_TemplateManifest {
|
|
|
|
|
|
|
|
const TEMPLATES_DIR = 'templates';
|
|
|
|
|
|
|
|
protected $base;
|
|
|
|
protected $tests;
|
|
|
|
protected $cache;
|
|
|
|
protected $cacheKey;
|
2012-11-02 08:28:39 +01:00
|
|
|
protected $project;
|
2011-03-24 11:30:20 +01:00
|
|
|
protected $inited;
|
|
|
|
protected $templates = array();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructs a new template manifest. The manifest is not actually built
|
|
|
|
* or loaded from cache until needed.
|
|
|
|
*
|
|
|
|
* @param string $base The base path.
|
2012-11-02 08:28:39 +01:00
|
|
|
* @param string $project Path to application code
|
|
|
|
*
|
2011-03-24 11:30:20 +01:00
|
|
|
* @param bool $includeTests Include tests in the manifest.
|
|
|
|
* @param bool $forceRegen Force the manifest to be regenerated.
|
|
|
|
*/
|
2012-11-02 08:28:39 +01:00
|
|
|
public function __construct($base, $project, $includeTests = false, $forceRegen = false) {
|
2011-03-24 11:30:20 +01:00
|
|
|
$this->base = $base;
|
|
|
|
$this->tests = $includeTests;
|
|
|
|
|
2012-11-02 08:28:39 +01:00
|
|
|
$this->project = $project;
|
|
|
|
|
2013-03-13 22:33:29 +01:00
|
|
|
$cacheClass = defined('SS_MANIFESTCACHE') ? SS_MANIFESTCACHE : 'ManifestCache_File';
|
|
|
|
|
|
|
|
$this->cache = new $cacheClass('templatemanifest'.($includeTests ? '_tests' : ''));
|
2014-05-02 18:51:43 +02:00
|
|
|
$this->cacheKey = $this->getCacheKey($includeTests);
|
2016-01-06 00:34:58 +01:00
|
|
|
|
2014-11-25 00:57:42 +01:00
|
|
|
if ($forceRegen) {
|
|
|
|
$this->regenerate();
|
|
|
|
}
|
2011-03-24 11:30:20 +01:00
|
|
|
}
|
|
|
|
|
2014-05-02 18:51:43 +02:00
|
|
|
/**
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getBase() {
|
|
|
|
return $this->base;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate a unique cache key to avoid manifest cache collisions.
|
|
|
|
* We compartmentalise based on the base path, the given project, and whether
|
|
|
|
* or not we intend to include tests.
|
|
|
|
* @param boolean $includeTests
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getCacheKey($includeTests = false) {
|
|
|
|
return sha1(sprintf(
|
|
|
|
"manifest-%s-%s-%s",
|
|
|
|
$this->base,
|
|
|
|
$this->project,
|
|
|
|
(int) $includeTests // cast true to 1, false to 0
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2011-03-24 11:30:20 +01:00
|
|
|
/**
|
|
|
|
* Returns a map of all template information. The map is in the following
|
|
|
|
* format:
|
|
|
|
*
|
|
|
|
* <code>
|
|
|
|
* array(
|
|
|
|
* 'moduletemplate' => array(
|
|
|
|
* 'main' => '/path/to/module/templates/Main.ss'
|
|
|
|
* ),
|
|
|
|
* 'include' => array(
|
|
|
|
* 'include' => '/path/to/module/templates/Includes/Include.ss'
|
|
|
|
* ),
|
|
|
|
* 'page' => array(
|
|
|
|
* 'themes' => array(
|
2012-04-24 05:57:27 +02:00
|
|
|
* 'simple' => array(
|
2011-03-24 11:30:20 +01:00
|
|
|
* 'main' => '/path/to/theme/Page.ss'
|
|
|
|
* 'Layout' => '/path/to/theme/Layout/Page.ss'
|
|
|
|
* )
|
|
|
|
* )
|
|
|
|
* )
|
|
|
|
* )
|
|
|
|
* </code>
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getTemplates() {
|
|
|
|
if (!$this->inited) {
|
|
|
|
$this->init();
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->templates;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a set of possible candidate templates that match a certain
|
|
|
|
* template name.
|
|
|
|
*
|
|
|
|
* This is the same as extracting an individual array element from
|
|
|
|
* {@link SS_TemplateManifest::getTemplates()}.
|
|
|
|
*
|
|
|
|
* @param string $name
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getTemplate($name) {
|
|
|
|
if (!$this->inited) {
|
|
|
|
$this->init();
|
|
|
|
}
|
|
|
|
|
|
|
|
$name = strtolower($name);
|
|
|
|
|
|
|
|
if (array_key_exists($name, $this->templates)) {
|
|
|
|
return $this->templates[$name];
|
|
|
|
} else {
|
|
|
|
return array();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-02 08:28:39 +01:00
|
|
|
/**
|
|
|
|
* Returns the correct candidate template. In order of importance, application
|
|
|
|
* project code, current theme and finally modules.
|
|
|
|
*
|
|
|
|
* @param string $name
|
|
|
|
* @param string $theme - theme name
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getCandidateTemplate($name, $theme = null) {
|
2014-01-13 21:33:22 +01:00
|
|
|
$found = array();
|
2012-11-02 08:28:39 +01:00
|
|
|
$candidates = $this->getTemplate($name);
|
2014-08-15 08:53:05 +02:00
|
|
|
|
2014-01-13 21:33:22 +01:00
|
|
|
// theme overrides modules
|
|
|
|
if ($theme && isset($candidates['themes'][$theme])) {
|
2013-04-29 17:19:51 +02:00
|
|
|
$found = array_merge($candidates, $candidates['themes'][$theme]);
|
2012-11-02 08:28:39 +01:00
|
|
|
}
|
2014-01-13 21:33:22 +01:00
|
|
|
// project overrides theme
|
|
|
|
if ($this->project && isset($candidates[$this->project])) {
|
|
|
|
$found = array_merge($found, $candidates[$this->project]);
|
|
|
|
}
|
2014-08-15 08:53:05 +02:00
|
|
|
|
2014-01-13 21:33:22 +01:00
|
|
|
$found = ($found) ? $found : $candidates;
|
2014-08-15 08:53:05 +02:00
|
|
|
|
2014-01-13 21:33:22 +01:00
|
|
|
if (isset($found['themes'])) unset($found['themes']);
|
|
|
|
if (isset($found[$this->project])) unset($found[$this->project]);
|
2014-08-15 08:53:05 +02:00
|
|
|
|
2012-11-02 08:28:39 +01:00
|
|
|
return $found;
|
|
|
|
}
|
|
|
|
|
2011-03-24 11:30:20 +01:00
|
|
|
/**
|
|
|
|
* Regenerates the manifest by scanning the base path.
|
|
|
|
*
|
|
|
|
* @param bool $cache
|
|
|
|
*/
|
|
|
|
public function regenerate($cache = true) {
|
|
|
|
$finder = new ManifestFileFinder();
|
|
|
|
$finder->setOptions(array(
|
|
|
|
'name_regex' => '/\.ss$/',
|
|
|
|
'include_themes' => true,
|
|
|
|
'ignore_tests' => !$this->tests,
|
|
|
|
'file_callback' => array($this, 'handleFile')
|
|
|
|
));
|
2012-11-02 08:28:39 +01:00
|
|
|
|
2011-03-24 11:30:20 +01:00
|
|
|
$finder->find($this->base);
|
|
|
|
|
|
|
|
if ($cache) {
|
|
|
|
$this->cache->save($this->templates, $this->cacheKey);
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->inited = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function handleFile($basename, $pathname, $depth) {
|
2012-11-02 08:28:39 +01:00
|
|
|
$projectFile = false;
|
|
|
|
$theme = null;
|
|
|
|
|
2011-03-24 11:30:20 +01:00
|
|
|
if (strpos($pathname, $this->base . '/' . THEMES_DIR) === 0) {
|
|
|
|
$start = strlen($this->base . '/' . THEMES_DIR) + 1;
|
|
|
|
$theme = substr($pathname, $start);
|
|
|
|
$theme = substr($theme, 0, strpos($theme, '/'));
|
|
|
|
$theme = strtok($theme, '_');
|
2014-08-15 08:53:05 +02:00
|
|
|
} else if($this->project && (strpos($pathname, $this->base . '/' . $this->project .'/') === 0)) {
|
2012-11-02 08:28:39 +01:00
|
|
|
$projectFile = true;
|
2011-03-24 11:30:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$type = basename(dirname($pathname));
|
|
|
|
$name = strtolower(substr($basename, 0, -3));
|
|
|
|
|
|
|
|
if ($type == self::TEMPLATES_DIR) {
|
|
|
|
$type = 'main';
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($theme) {
|
|
|
|
$this->templates[$name]['themes'][$theme][$type] = $pathname;
|
2012-11-02 08:28:39 +01:00
|
|
|
} else if($projectFile) {
|
|
|
|
$this->templates[$name][$this->project][$type] = $pathname;
|
2011-03-24 11:30:20 +01:00
|
|
|
} else {
|
|
|
|
$this->templates[$name][$type] = $pathname;
|
|
|
|
}
|
2012-11-02 08:28:39 +01:00
|
|
|
|
2011-03-24 11:30:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function init() {
|
2014-11-25 00:57:42 +01:00
|
|
|
if ($data = $this->cache->load($this->cacheKey)) {
|
2011-03-24 11:30:20 +01:00
|
|
|
$this->templates = $data;
|
|
|
|
$this->inited = true;
|
|
|
|
} else {
|
|
|
|
$this->regenerate();
|
|
|
|
}
|
|
|
|
}
|
2012-03-24 04:04:52 +01:00
|
|
|
}
|