2016-07-13 14:36:52 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace SilverStripe\View;
|
|
|
|
|
2016-08-19 00:51:35 +02:00
|
|
|
use SilverStripe\Core\Manifest\ManifestCache;
|
|
|
|
use SilverStripe\Core\Manifest\ManifestFileFinder;
|
2016-07-13 14:36:52 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* A class which builds a manifest of all themes (which is really just a directory called "templates")
|
|
|
|
*/
|
2016-07-19 04:09:15 +02:00
|
|
|
class ThemeManifest implements ThemeList {
|
2016-07-13 14:36:52 +02:00
|
|
|
|
|
|
|
const TEMPLATES_DIR = 'templates';
|
|
|
|
|
2016-07-19 04:09:15 +02:00
|
|
|
/**
|
|
|
|
* Base path
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
2016-07-13 14:36:52 +02:00
|
|
|
protected $base;
|
2016-07-19 04:09:15 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Include tests
|
|
|
|
*
|
|
|
|
* @var bool
|
|
|
|
*/
|
2016-07-13 14:36:52 +02:00
|
|
|
protected $tests;
|
2016-07-19 04:09:15 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Path to application code
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
2016-07-13 14:36:52 +02:00
|
|
|
protected $project;
|
|
|
|
|
2016-07-19 04:09:15 +02:00
|
|
|
/**
|
|
|
|
* Cache
|
|
|
|
*
|
|
|
|
* @var ManifestCache
|
|
|
|
*/
|
2016-07-13 14:36:52 +02:00
|
|
|
protected $cache;
|
2016-07-19 04:09:15 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Cache key
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
2016-07-13 14:36:52 +02:00
|
|
|
protected $cacheKey;
|
|
|
|
|
2016-07-19 04:09:15 +02:00
|
|
|
/**
|
|
|
|
* List of theme root directories
|
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
2016-07-13 14:36:52 +02:00
|
|
|
protected $themes = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructs a new template manifest. The manifest is not actually built
|
|
|
|
* or loaded from cache until needed.
|
|
|
|
*
|
|
|
|
* @param string $base The base path.
|
|
|
|
* @param string $project Path to application code
|
|
|
|
*
|
|
|
|
* @param bool $includeTests Include tests in the manifest.
|
|
|
|
* @param bool $forceRegen Force the manifest to be regenerated.
|
|
|
|
*/
|
|
|
|
public function __construct($base, $project, $includeTests = false, $forceRegen = false) {
|
|
|
|
$this->base = $base;
|
|
|
|
$this->tests = $includeTests;
|
|
|
|
|
|
|
|
$this->project = $project;
|
|
|
|
|
2016-08-19 00:51:35 +02:00
|
|
|
$cacheClass = defined('SS_MANIFESTCACHE')
|
|
|
|
? SS_MANIFESTCACHE
|
|
|
|
: 'SilverStripe\\Core\\Manifest\\ManifestCache_File';
|
2016-07-13 14:36:52 +02:00
|
|
|
|
|
|
|
$this->cache = new $cacheClass('thememanifest'.($includeTests ? '_tests' : ''));
|
|
|
|
$this->cacheKey = $this->getCacheKey();
|
|
|
|
|
|
|
|
if ($forceRegen) {
|
|
|
|
$this->regenerate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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.
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getCacheKey() {
|
|
|
|
return sha1(sprintf(
|
|
|
|
"manifest-%s-%s-%u",
|
|
|
|
$this->base,
|
|
|
|
$this->project,
|
|
|
|
$this->tests
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getThemes() {
|
2016-07-19 04:09:15 +02:00
|
|
|
if ($this->themes === null) {
|
|
|
|
$this->init();
|
|
|
|
}
|
2016-07-13 14:36:52 +02:00
|
|
|
return $this->themes;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Regenerates the manifest by scanning the base path.
|
|
|
|
*
|
|
|
|
* @param bool $cache
|
|
|
|
*/
|
|
|
|
public function regenerate($cache = true) {
|
|
|
|
$finder = new ManifestFileFinder();
|
|
|
|
$finder->setOptions(array(
|
|
|
|
'include_themes' => false,
|
2016-07-19 03:17:07 +02:00
|
|
|
'ignore_dirs' => array('node_modules', THEMES_DIR),
|
2016-07-13 14:36:52 +02:00
|
|
|
'ignore_tests' => !$this->tests,
|
|
|
|
'dir_callback' => array($this, 'handleDirectory')
|
|
|
|
));
|
|
|
|
|
|
|
|
$this->themes = [];
|
|
|
|
$finder->find($this->base);
|
|
|
|
|
|
|
|
if ($cache) {
|
|
|
|
$this->cache->save($this->themes, $this->cacheKey);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-19 04:09:15 +02:00
|
|
|
/**
|
|
|
|
* Add a directory to the manifest
|
|
|
|
*
|
|
|
|
* @param string $basename
|
|
|
|
* @param string $pathname
|
|
|
|
* @param int $depth
|
|
|
|
*/
|
2016-07-13 14:36:52 +02:00
|
|
|
public function handleDirectory($basename, $pathname, $depth)
|
|
|
|
{
|
2016-07-19 04:09:15 +02:00
|
|
|
if ($basename !== self::TEMPLATES_DIR) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We only want part of the full path, so split into directories
|
|
|
|
$parts = explode('/', $pathname);
|
|
|
|
// Take the end (the part relative to base), except the very last directory
|
|
|
|
$themeParts = array_slice($parts, -$depth, $depth-1);
|
|
|
|
// Then join again
|
|
|
|
$path = '/'.implode('/', $themeParts);
|
|
|
|
|
|
|
|
// If this is in the project, add to beginning of list. Else add to end.
|
|
|
|
if ($themeParts[0] == $this->project) {
|
|
|
|
array_unshift($this->themes, $path);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
array_push($this->themes, $path);
|
2016-07-13 14:36:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-19 04:09:15 +02:00
|
|
|
/**
|
|
|
|
* Initialise the manifest
|
|
|
|
*/
|
2016-07-13 14:36:52 +02:00
|
|
|
protected function init() {
|
|
|
|
if ($data = $this->cache->load($this->cacheKey)) {
|
|
|
|
$this->themes = $data;
|
|
|
|
} else {
|
|
|
|
$this->regenerate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|