mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge pull request #5804 from open-sausages/feature/themestack
API Theme stacking
This commit is contained in:
commit
5c98d331a3
@ -1509,7 +1509,7 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
$dateFormatField->setValue($self->DateFormat);
|
$dateFormatField->setValue($self->DateFormat);
|
||||||
$dateFormatField->setDescriptionTemplate('MemberDatetimeOptionsetField_description_date');
|
$dateFormatField->setDescriptionTemplate('forms/MemberDatetimeOptionsetField_description_date');
|
||||||
|
|
||||||
$defaultTimeFormat = Zend_Locale_Format::getTimeFormat(new Zend_Locale($self->Locale));
|
$defaultTimeFormat = Zend_Locale_Format::getTimeFormat(new Zend_Locale($self->Locale));
|
||||||
$timeFormatMap = array(
|
$timeFormatMap = array(
|
||||||
@ -1526,7 +1526,7 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
$timeFormatField->setValue($self->TimeFormat);
|
$timeFormatField->setValue($self->TimeFormat);
|
||||||
$timeFormatField->setDescriptionTemplate('MemberDatetimeOptionsetField_description_time');
|
$timeFormatField->setDescriptionTemplate('forms/MemberDatetimeOptionsetField_description_time');
|
||||||
});
|
});
|
||||||
|
|
||||||
return parent::getCMSFields();
|
return parent::getCMSFields();
|
||||||
|
@ -783,7 +783,7 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
|||||||
return $controller->renderWith($controller->getTemplatesWithSuffix('_Content'));
|
return $controller->renderWith($controller->getTemplatesWithSuffix('_Content'));
|
||||||
},
|
},
|
||||||
'Breadcrumbs' => function() use (&$controller) {
|
'Breadcrumbs' => function() use (&$controller) {
|
||||||
return $controller->renderWith('CMSBreadcrumbs');
|
return $controller->renderWith('Includes/CMSBreadcrumbs');
|
||||||
},
|
},
|
||||||
'default' => function() use(&$controller) {
|
'default' => function() use(&$controller) {
|
||||||
return $controller->renderWith($controller->getViewer('show'));
|
return $controller->renderWith($controller->getViewer('show'));
|
||||||
|
@ -366,7 +366,7 @@ abstract class ModelAdmin extends LeftAndMain {
|
|||||||
'ModelName' => Convert::raw2att($modelName),
|
'ModelName' => Convert::raw2att($modelName),
|
||||||
'Fields' => $specFields,
|
'Fields' => $specFields,
|
||||||
'Relations' => $specRelations,
|
'Relations' => $specRelations,
|
||||||
))->renderWith('ModelAdmin_ImportSpec');
|
))->renderWith('Includes/ModelAdmin_ImportSpec');
|
||||||
|
|
||||||
$fields->push(new LiteralField("SpecFor{$modelName}", $specHTML));
|
$fields->push(new LiteralField("SpecFor{$modelName}", $specHTML));
|
||||||
$fields->push(
|
$fields->push(
|
||||||
|
@ -77,8 +77,7 @@ require_once 'core/manifest/ConfigManifest.php';
|
|||||||
require_once 'core/manifest/ConfigStaticManifest.php';
|
require_once 'core/manifest/ConfigStaticManifest.php';
|
||||||
require_once 'core/manifest/ClassManifest.php';
|
require_once 'core/manifest/ClassManifest.php';
|
||||||
require_once 'core/manifest/ManifestFileFinder.php';
|
require_once 'core/manifest/ManifestFileFinder.php';
|
||||||
require_once 'core/manifest/TemplateLoader.php';
|
require_once 'view/TemplateLoader.php';
|
||||||
require_once 'core/manifest/TemplateManifest.php';
|
|
||||||
require_once 'core/manifest/TokenisedRegularExpression.php';
|
require_once 'core/manifest/TokenisedRegularExpression.php';
|
||||||
require_once 'control/injector/Injector.php';
|
require_once 'control/injector/Injector.php';
|
||||||
|
|
||||||
@ -113,7 +112,7 @@ $configManifest = new SS_ConfigManifest(BASE_PATH, false, $flush);
|
|||||||
Config::inst()->pushConfigYamlManifest($configManifest);
|
Config::inst()->pushConfigYamlManifest($configManifest);
|
||||||
|
|
||||||
// Load template manifest
|
// Load template manifest
|
||||||
SS_TemplateLoader::instance()->pushManifest(new SS_TemplateManifest(
|
SilverStripe\View\TemplateLoader::instance()->addSet('$default', new SilverStripe\View\ThemeManifest(
|
||||||
BASE_PATH, project(), false, $flush
|
BASE_PATH, project(), false, $flush
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -1,87 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Handles finding templates from a stack of template manifest objects.
|
|
||||||
*
|
|
||||||
* @package framework
|
|
||||||
* @subpackage manifest
|
|
||||||
*/
|
|
||||||
class SS_TemplateLoader {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var SS_TemplateLoader
|
|
||||||
*/
|
|
||||||
private static $instance;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var SS_TemplateManifest[]
|
|
||||||
*/
|
|
||||||
protected $manifests = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return SS_TemplateLoader
|
|
||||||
*/
|
|
||||||
public static function instance() {
|
|
||||||
return self::$instance ? self::$instance : self::$instance = new self();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the currently active template manifest instance.
|
|
||||||
*
|
|
||||||
* @return SS_TemplateManifest
|
|
||||||
*/
|
|
||||||
public function getManifest() {
|
|
||||||
return $this->manifests[count($this->manifests) - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param SS_TemplateManifest $manifest
|
|
||||||
*/
|
|
||||||
public function pushManifest(SS_TemplateManifest $manifest) {
|
|
||||||
$this->manifests[] = $manifest;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return SS_TemplateManifest
|
|
||||||
*/
|
|
||||||
public function popManifest() {
|
|
||||||
return array_pop($this->manifests);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to find possible candidate templates from a set of template
|
|
||||||
* names from modules, current theme directory and finally the application
|
|
||||||
* folder.
|
|
||||||
*
|
|
||||||
* The template names can be passed in as plain strings, or be in the
|
|
||||||
* format "type/name", where type is the type of template to search for
|
|
||||||
* (e.g. Includes, Layout).
|
|
||||||
*
|
|
||||||
* @param string|array $templates
|
|
||||||
* @param string $theme
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function findTemplates($templates, $theme = null) {
|
|
||||||
$result = array();
|
|
||||||
|
|
||||||
foreach ((array) $templates as $template) {
|
|
||||||
if (strpos($template, '/')) {
|
|
||||||
list($type, $template) = explode('/', $template, 2);
|
|
||||||
} else {
|
|
||||||
$type = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($found = $this->getManifest()->getCandidateTemplate($template, $theme)) {
|
|
||||||
if ($type && isset($found[$type])) {
|
|
||||||
$found = array(
|
|
||||||
'main' => $found[$type]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$result = array_merge($found, $result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,271 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* A class which builds a manifest of all templates present in a directory,
|
|
||||||
* in both modules and themes.
|
|
||||||
*
|
|
||||||
* @package framework
|
|
||||||
* @subpackage manifest
|
|
||||||
*/
|
|
||||||
class SS_TemplateManifest {
|
|
||||||
|
|
||||||
const TEMPLATES_DIR = 'templates';
|
|
||||||
|
|
||||||
protected $base;
|
|
||||||
protected $tests;
|
|
||||||
protected $cache;
|
|
||||||
protected $cacheKey;
|
|
||||||
protected $project;
|
|
||||||
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.
|
|
||||||
* @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;
|
|
||||||
|
|
||||||
$cacheClass = defined('SS_MANIFESTCACHE') ? SS_MANIFESTCACHE : 'ManifestCache_File';
|
|
||||||
|
|
||||||
$this->cache = new $cacheClass('templatemanifest'.($includeTests ? '_tests' : ''));
|
|
||||||
$this->cacheKey = $this->getCacheKey($includeTests);
|
|
||||||
|
|
||||||
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.
|
|
||||||
* @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
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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(
|
|
||||||
* 'simple' => array(
|
|
||||||
* '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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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) {
|
|
||||||
$found = array();
|
|
||||||
$candidates = $this->getTemplate($name);
|
|
||||||
|
|
||||||
// theme overrides modules
|
|
||||||
if ($theme && isset($candidates['themes'][$theme])) {
|
|
||||||
$found = array_merge($candidates, $candidates['themes'][$theme]);
|
|
||||||
}
|
|
||||||
// project overrides theme
|
|
||||||
if ($this->project && isset($candidates[$this->project])) {
|
|
||||||
$found = array_merge($found, $candidates[$this->project]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$found = ($found) ? $found : $candidates;
|
|
||||||
|
|
||||||
if (isset($found['themes'])) unset($found['themes']);
|
|
||||||
if (isset($found[$this->project])) unset($found[$this->project]);
|
|
||||||
|
|
||||||
return $found;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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')
|
|
||||||
));
|
|
||||||
|
|
||||||
$finder->find($this->base);
|
|
||||||
|
|
||||||
if ($cache) {
|
|
||||||
$this->cache->save($this->templates, $this->cacheKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->inited = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handleFile($basename, $pathname, $depth)
|
|
||||||
{
|
|
||||||
$projectFile = false;
|
|
||||||
$theme = null;
|
|
||||||
|
|
||||||
// Template in theme
|
|
||||||
if (preg_match(
|
|
||||||
'#'.preg_quote($this->base.'/'.THEMES_DIR).'/([^/_]+)(_[^/]+)?/(.*)$#',
|
|
||||||
$pathname,
|
|
||||||
$matches
|
|
||||||
)) {
|
|
||||||
$theme = $matches[1];
|
|
||||||
$relPath = $matches[3];
|
|
||||||
|
|
||||||
// Template in project
|
|
||||||
} elseif (preg_match(
|
|
||||||
'#'.preg_quote($this->base.'/'.$this->project).'/(.*)$#',
|
|
||||||
$pathname,
|
|
||||||
$matches
|
|
||||||
)) {
|
|
||||||
$projectFile = true;
|
|
||||||
$relPath = $matches[1];
|
|
||||||
|
|
||||||
// Template in module
|
|
||||||
} elseif (preg_match(
|
|
||||||
'#'.preg_quote($this->base).'/([^/]+)/(.*)$#',
|
|
||||||
$pathname,
|
|
||||||
$matches
|
|
||||||
)) {
|
|
||||||
$relPath = $matches[2];
|
|
||||||
|
|
||||||
} else {
|
|
||||||
throw new \LogicException("Can't determine meaning of path: $pathname");
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a templates subfolder is used, ignore that
|
|
||||||
if (preg_match('#'.preg_quote(self::TEMPLATES_DIR).'/(.*)$#', $relPath, $matches)) {
|
|
||||||
$relPath = $matches[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Layout and Content folders have special meaning
|
|
||||||
if (preg_match('#^(.*/)?(Layout|Content|Includes)/([^/]+)$#', $relPath, $matches)) {
|
|
||||||
$type = $matches[2];
|
|
||||||
$relPath = "$matches[1]$matches[3]";
|
|
||||||
} else {
|
|
||||||
$type = "main";
|
|
||||||
}
|
|
||||||
|
|
||||||
$name = strtolower(substr($relPath, 0, -3));
|
|
||||||
$name = str_replace('/', '\\', $name);
|
|
||||||
|
|
||||||
if ($theme) {
|
|
||||||
$this->templates[$name]['themes'][$theme][$type] = $pathname;
|
|
||||||
} else if ($projectFile) {
|
|
||||||
$this->templates[$name][$this->project][$type] = $pathname;
|
|
||||||
} else {
|
|
||||||
$this->templates[$name][$type] = $pathname;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we've found a template in a subdirectory, then allow its use for a non-namespaced class
|
|
||||||
// as well. This was a common SilverStripe 3 approach, where templates were placed into
|
|
||||||
// subfolders to suit the whim of the developer.
|
|
||||||
if (strpos($name, '\\') !== false) {
|
|
||||||
$name2 = substr($name, strrpos($name, '\\') + 1);
|
|
||||||
// In of these cases, the template will only be provided if it isn't already set. This
|
|
||||||
// matches SilverStripe 3 prioritisation.
|
|
||||||
if ($theme) {
|
|
||||||
if (!isset($this->templates[$name2]['themes'][$theme][$type])) {
|
|
||||||
$this->templates[$name2]['themes'][$theme][$type] = $pathname;
|
|
||||||
}
|
|
||||||
} else if ($projectFile) {
|
|
||||||
if (!isset($this->templates[$name2][$this->project][$type])) {
|
|
||||||
$this->templates[$name2][$this->project][$type] = $pathname;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!isset($this->templates[$name2][$type])) {
|
|
||||||
$this->templates[$name2][$type] = $pathname;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function init() {
|
|
||||||
if ($data = $this->cache->load($this->cacheKey)) {
|
|
||||||
$this->templates = $data;
|
|
||||||
$this->inited = true;
|
|
||||||
} else {
|
|
||||||
$this->regenerate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,8 +13,8 @@ use SilverStripe\Security\Member;
|
|||||||
use SilverStripe\Security\Security;
|
use SilverStripe\Security\Security;
|
||||||
use SilverStripe\Security\Group;
|
use SilverStripe\Security\Group;
|
||||||
use SilverStripe\Security\Permission;
|
use SilverStripe\Security\Permission;
|
||||||
|
use SilverStripe\View\TemplateLoader;
|
||||||
|
use SilverStripe\View\ThemeManifest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test case class for the Sapphire framework.
|
* Test case class for the Sapphire framework.
|
||||||
@ -848,7 +848,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
|||||||
SS_ClassLoader::instance()->pushManifest($classManifest, false);
|
SS_ClassLoader::instance()->pushManifest($classManifest, false);
|
||||||
SapphireTest::set_test_class_manifest($classManifest);
|
SapphireTest::set_test_class_manifest($classManifest);
|
||||||
|
|
||||||
SS_TemplateLoader::instance()->pushManifest(new SS_TemplateManifest(
|
TemplateLoader::instance()->addSet('$default', new ThemeManifest(
|
||||||
BASE_PATH, project(), true, $flush
|
BASE_PATH, project(), true, $flush
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -1054,22 +1054,15 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
|
|||||||
*/
|
*/
|
||||||
protected function useTestTheme($themeBaseDir, $theme, $callback) {
|
protected function useTestTheme($themeBaseDir, $theme, $callback) {
|
||||||
Config::nest();
|
Config::nest();
|
||||||
global $project;
|
|
||||||
|
|
||||||
$manifest = new SS_TemplateManifest($themeBaseDir, $project, true, true);
|
if (strpos($themeBaseDir, BASE_PATH) === 0) $themeBaseDir = substr($themeBaseDir, strlen(BASE_PATH));
|
||||||
|
SSViewer::set_themes([$themeBaseDir.'/themes/'.$theme, '$default']);
|
||||||
SS_TemplateLoader::instance()->pushManifest($manifest);
|
|
||||||
|
|
||||||
Config::inst()->update('SSViewer', 'theme', $theme);
|
|
||||||
|
|
||||||
$e = null;
|
$e = null;
|
||||||
|
|
||||||
try { $callback(); }
|
try { $callback(); }
|
||||||
catch (Exception $e) { /* NOP for now, just save $e */ }
|
catch (Exception $e) { /* NOP for now, just save $e */ }
|
||||||
|
|
||||||
// Remove all the test themes we created
|
|
||||||
SS_TemplateLoader::instance()->popManifest();
|
|
||||||
|
|
||||||
Config::unnest();
|
Config::unnest();
|
||||||
|
|
||||||
if ($e) throw $e;
|
if ($e) throw $e;
|
||||||
|
@ -397,6 +397,14 @@ all changed project files.
|
|||||||
This will resolve the majority of upgrading work, but for specific changes that will
|
This will resolve the majority of upgrading work, but for specific changes that will
|
||||||
require manual intervention, please see the below upgrading notes.
|
require manual intervention, please see the below upgrading notes.
|
||||||
|
|
||||||
|
### Make sure templates are in correct locations
|
||||||
|
|
||||||
|
Templates are now much more strict about their locations. You can no longer put a template in an arbitrary
|
||||||
|
folder and have it be found. Case is now also checked on case-sensitive filesystems.
|
||||||
|
|
||||||
|
Either include the folder in the template name (`renderWith('Field.ss')` => `renderWith('forms/Field.ss')`), move the
|
||||||
|
template into the correct directory, or both.
|
||||||
|
|
||||||
### Update code that uses SQLQuery
|
### Update code that uses SQLQuery
|
||||||
|
|
||||||
Where your code once used SQLQuery you should now use SQLSelect in all cases, as this has been removed.
|
Where your code once used SQLQuery you should now use SQLSelect in all cases, as this has been removed.
|
||||||
|
@ -373,7 +373,7 @@ class Email extends ViewableData {
|
|||||||
// Requery data so that updated versions of To, From, Subject, etc are included
|
// Requery data so that updated versions of To, From, Subject, etc are included
|
||||||
$data = $this->templateData();
|
$data = $this->templateData();
|
||||||
|
|
||||||
$template = new SSViewer($this->ss_template);
|
$template = new SSViewer('email/'.$this->ss_template);
|
||||||
|
|
||||||
if($template->exists()) {
|
if($template->exists()) {
|
||||||
$fullBody = $template->process($data);
|
$fullBody = $template->process($data);
|
||||||
|
@ -16,10 +16,10 @@ class ProtectedAssetAdapter extends AssetAdapter implements ProtectedAdapter {
|
|||||||
|
|
||||||
private static $server_configuration = array(
|
private static $server_configuration = array(
|
||||||
'apache' => array(
|
'apache' => array(
|
||||||
'.htaccess' => "Protected_HTAccess"
|
'.htaccess' => "filesystem/Protected_HTAccess"
|
||||||
),
|
),
|
||||||
'microsoft-iis' => array(
|
'microsoft-iis' => array(
|
||||||
'web.config' => "Protected_WebConfig"
|
'web.config' => "filesystem/Protected_WebConfig"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -21,10 +21,10 @@ class PublicAssetAdapter extends AssetAdapter implements PublicAdapter {
|
|||||||
*/
|
*/
|
||||||
private static $server_configuration = array(
|
private static $server_configuration = array(
|
||||||
'apache' => array(
|
'apache' => array(
|
||||||
'.htaccess' => "Assets_HTAccess"
|
'.htaccess' => "filesystem/Assets_HTAccess"
|
||||||
),
|
),
|
||||||
'microsoft-iis' => array(
|
'microsoft-iis' => array(
|
||||||
'web.config' => "Assets_WebConfig"
|
'web.config' => "filesystem/Assets_WebConfig"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ class AssetField extends FileField {
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $templateFileButtons = 'AssetField_FileButtons';
|
protected $templateFileButtons = 'Includes/AssetField_FileButtons';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parent data record. Will be infered from parent form or controller if blank. The destination
|
* Parent data record. Will be infered from parent form or controller if blank. The destination
|
||||||
|
@ -1632,7 +1632,7 @@ class Form extends RequestHandler {
|
|||||||
public function forTemplate() {
|
public function forTemplate() {
|
||||||
$return = $this->renderWith(array_merge(
|
$return = $this->renderWith(array_merge(
|
||||||
(array)$this->getTemplate(),
|
(array)$this->getTemplate(),
|
||||||
array('Form')
|
array('Includes/Form')
|
||||||
));
|
));
|
||||||
|
|
||||||
// Now that we're rendered, clear message
|
// Now that we're rendered, clear message
|
||||||
|
@ -1052,7 +1052,7 @@ class FormField extends RequestHandler {
|
|||||||
$matches = array();
|
$matches = array();
|
||||||
|
|
||||||
foreach(array_reverse(ClassInfo::ancestry($this)) as $className) {
|
foreach(array_reverse(ClassInfo::ancestry($this)) as $className) {
|
||||||
$matches[] = $className . $customTemplateSuffix;
|
$matches[] = 'forms/'. $className . $customTemplateSuffix;
|
||||||
|
|
||||||
if($className == "FormField") {
|
if($className == "FormField") {
|
||||||
break;
|
break;
|
||||||
@ -1060,7 +1060,7 @@ class FormField extends RequestHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if($customTemplate) {
|
if($customTemplate) {
|
||||||
array_unshift($matches, $customTemplate);
|
array_unshift($matches, 'forms/'.$customTemplate);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $matches;
|
return $matches;
|
||||||
|
@ -44,6 +44,7 @@ class MemberDatetimeOptionsetField extends OptionsetField {
|
|||||||
'Options' => new ArrayList($options)
|
'Options' => new ArrayList($options)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
||||||
return $this->customise($properties)->renderWith(
|
return $this->customise($properties)->renderWith(
|
||||||
$this->getTemplates()
|
$this->getTemplates()
|
||||||
);
|
);
|
||||||
|
@ -65,7 +65,7 @@ class UploadField extends FileField {
|
|||||||
*
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $templateFileButtons = 'UploadField_FileButtons';
|
protected $templateFileButtons = 'Includes/UploadField_FileButtons';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Template to use for the edit form
|
* Template to use for the edit form
|
||||||
|
@ -131,7 +131,7 @@ class GridFieldAddExistingAutocompleter
|
|||||||
}
|
}
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
$this->targetFragment => $forTemplate->renderWith($this->itemClass)
|
$this->targetFragment => $forTemplate->renderWith('Includes/'.$this->itemClass)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ class GridFieldAddNewButton implements GridField_HTMLProvider {
|
|||||||
));
|
));
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
$this->targetFragment => $data->renderWith('GridFieldAddNewbutton'),
|
$this->targetFragment => $data->renderWith('Includes/GridFieldAddNewButton'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ class GridFieldButtonRow implements GridField_HTMLProvider {
|
|||||||
));
|
));
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
$this->targetFragment => $data->renderWith('GridFieldButtonRow')
|
$this->targetFragment => $data->renderWith('Includes/GridFieldButtonRow')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -441,7 +441,7 @@ class GridFieldDetailForm_ItemRequest extends RequestHandler {
|
|||||||
// Always show with base template (full width, no other panels),
|
// Always show with base template (full width, no other panels),
|
||||||
// regardless of overloaded CMS controller templates.
|
// regardless of overloaded CMS controller templates.
|
||||||
// TODO Allow customization, e.g. to display an edit form alongside a search form from the CMS controller
|
// TODO Allow customization, e.g. to display an edit form alongside a search form from the CMS controller
|
||||||
$form->setTemplate('LeftAndMain_EditForm');
|
$form->setTemplate('Includes/LeftAndMain_EditForm');
|
||||||
$form->addExtraClass('cms-content cms-edit-form center');
|
$form->addExtraClass('cms-content cms-edit-form center');
|
||||||
$form->setAttribute('data-pjax-fragment', 'CurrentForm Content');
|
$form->setAttribute('data-pjax-fragment', 'CurrentForm Content');
|
||||||
if($form->Fields()->hasTabset()) {
|
if($form->Fields()->hasTabset()) {
|
||||||
|
@ -86,7 +86,7 @@ class GridFieldEditButton implements GridField_ColumnProvider {
|
|||||||
'Link' => Controller::join_links($gridField->Link('item'), $record->ID, 'edit')
|
'Link' => Controller::join_links($gridField->Link('item'), $record->ID, 'edit')
|
||||||
));
|
));
|
||||||
|
|
||||||
return $data->renderWith('GridFieldEditButton');
|
return $data->renderWith('Includes/GridFieldEditButton');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -162,7 +162,7 @@ class GridFieldFilterHeader implements GridField_HTMLProvider, GridField_DataMan
|
|||||||
}
|
}
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'header' => $forTemplate->renderWith('GridFieldFilterHeader_Row'),
|
'header' => $forTemplate->renderWith('Includes/GridFieldFilterHeader_Row'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ class GridFieldFooter implements GridField_HTMLProvider {
|
|||||||
|
|
||||||
return array(
|
return array(
|
||||||
'footer' => $forTemplate->renderWith(
|
'footer' => $forTemplate->renderWith(
|
||||||
'GridFieldFooter',
|
'Includes/GridFieldFooter',
|
||||||
array(
|
array(
|
||||||
'Colspan' => count($gridField->getColumns())
|
'Colspan' => count($gridField->getColumns())
|
||||||
)
|
)
|
||||||
|
@ -66,7 +66,7 @@ class GridFieldLevelup extends Object implements GridField_HTMLProvider {
|
|||||||
));
|
));
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'before' => $forTemplate->renderWith('GridFieldLevelup'),
|
'before' => $forTemplate->renderWith('Includes/GridFieldLevelup'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ class GridFieldPageCount implements GridField_HTMLProvider {
|
|||||||
$paginator = $this->getPaginator($gridField);
|
$paginator = $this->getPaginator($gridField);
|
||||||
if ($paginator && ($forTemplate = $paginator->getTemplateParameters($gridField))) {
|
if ($paginator && ($forTemplate = $paginator->getTemplateParameters($gridField))) {
|
||||||
return array(
|
return array(
|
||||||
$this->targetFragment => $forTemplate->renderWith($this->itemClass)
|
$this->targetFragment => $forTemplate->renderWith('Includes/'.$this->itemClass)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -259,7 +259,7 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu
|
|||||||
$forTemplate = $this->getTemplateParameters($gridField);
|
$forTemplate = $this->getTemplateParameters($gridField);
|
||||||
if($forTemplate) {
|
if($forTemplate) {
|
||||||
return array(
|
return array(
|
||||||
'footer' => $forTemplate->renderWith($this->itemClass,
|
'footer' => $forTemplate->renderWith('Includes/'.$this->itemClass,
|
||||||
array('Colspan'=>count($gridField->getColumns())))
|
array('Colspan'=>count($gridField->getColumns())))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -164,7 +164,7 @@ class GridFieldSortableHeader implements GridField_HTMLProvider, GridField_DataM
|
|||||||
}
|
}
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'header' => $forTemplate->renderWith('GridFieldSortableHeader_Row'),
|
'header' => $forTemplate->renderWith('Includes/GridFieldSortableHeader_Row'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ class GridFieldToolbarHeader implements GridField_HTMLProvider {
|
|||||||
|
|
||||||
public function getHTMLFragments( $gridField) {
|
public function getHTMLFragments( $gridField) {
|
||||||
return array(
|
return array(
|
||||||
'header' => $gridField->renderWith('GridFieldToolbarHeader')
|
'header' => $gridField->renderWith('Includes/GridFieldToolbarHeader')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ class GridFieldViewButton implements GridField_ColumnProvider {
|
|||||||
$data = new ArrayData(array(
|
$data = new ArrayData(array(
|
||||||
'Link' => Controller::join_links($field->Link('item'), $record->ID, 'view')
|
'Link' => Controller::join_links($field->Link('item'), $record->ID, 'view')
|
||||||
));
|
));
|
||||||
return $data->renderWith('GridFieldViewButton');
|
return $data->renderWith('Includes/GridFieldViewButton');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,7 +181,7 @@ class HTMLEditorField_Toolbar extends RequestHandler {
|
|||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $templateViewFile = 'HTMLEditorField_viewfile';
|
protected $templateViewFile = 'Includes/HTMLEditorField_viewfile';
|
||||||
|
|
||||||
protected $controller, $name;
|
protected $controller, $name;
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use SilverStripe\View\TemplateLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default configuration for HtmlEditor specific to tinymce
|
* Default configuration for HtmlEditor specific to tinymce
|
||||||
*/
|
*/
|
||||||
@ -419,13 +421,18 @@ class TinyMCEConfig extends HTMLEditorConfig {
|
|||||||
Director::absoluteBaseURL(),
|
Director::absoluteBaseURL(),
|
||||||
FRAMEWORK_ADMIN_DIR . '/client/dist/styles/editor.css'
|
FRAMEWORK_ADMIN_DIR . '/client/dist/styles/editor.css'
|
||||||
);
|
);
|
||||||
if($theme = SSViewer::get_theme_folder()) {
|
|
||||||
$editorDir = $theme . '/css/editor.css';
|
foreach(SSViewer::get_themes() as $theme) {
|
||||||
|
$path = TemplateLoader::instance()->getPath($theme);
|
||||||
|
$editorDir = $path . '/css/editor.css';;
|
||||||
|
|
||||||
if(file_exists(BASE_PATH . '/' . $editorDir)) {
|
if(file_exists(BASE_PATH . '/' . $editorDir)) {
|
||||||
$editor[] = Controller::join_links(
|
$editor[] = Controller::join_links(
|
||||||
Director::absoluteBaseURL(),
|
Director::absoluteBaseURL(),
|
||||||
$editorDir
|
$editorDir
|
||||||
);
|
);
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $editor;
|
return $editor;
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use SilverStripe\View\TemplateLoader;
|
||||||
|
use SilverStripe\View\ThemeManifest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for the {@link SS_TemplateLoader} class.
|
* Tests for the {@link TemplateLoader} class.
|
||||||
*
|
*
|
||||||
* @package framework
|
* @package framework
|
||||||
* @subpackage tests
|
* @subpackage tests
|
||||||
@ -16,162 +20,82 @@ class TemplateLoaderTest extends SapphireTest {
|
|||||||
*/
|
*/
|
||||||
public function setUp() {
|
public function setUp() {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
// Fake project root
|
||||||
$this->base = dirname(__FILE__) . '/fixtures/templatemanifest';
|
$this->base = dirname(__FILE__) . '/fixtures/templatemanifest';
|
||||||
$this->manifest = new SS_TemplateManifest($this->base, 'myproject', false, true);
|
// New ThemeManifest for that root
|
||||||
$this->loader = new SS_TemplateLoader();
|
$this->manifest = new \SilverStripe\View\ThemeManifest($this->base, 'myproject', false, true);
|
||||||
$this->refreshLoader();
|
// New Loader for that root
|
||||||
|
$this->loader = new \SilverStripe\View\TemplateLoader($this->base);
|
||||||
|
$this->loader->addSet('$default', $this->manifest);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test that 'main' and 'Layout' templates are loaded from module
|
* Test that 'main' and 'Layout' templates are loaded from module
|
||||||
*/
|
*/
|
||||||
public function testFindTemplatesInModule() {
|
public function testFindTemplatesInModule() {
|
||||||
$expect = array(
|
$this->assertEquals(
|
||||||
'main' => "$this->base/module/templates/Page.ss",
|
"$this->base/module/templates/Page.ss",
|
||||||
'Layout' => "$this->base/module/templates/Layout/Page.ss"
|
$this->loader->findTemplate('Page', ['$default'])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
"$this->base/module/templates/Layout/Page.ss",
|
||||||
|
$this->loader->findTemplate(['type' => 'Layout', 'Page'], ['$default'])
|
||||||
);
|
);
|
||||||
$this->assertEquals($expect, $this->loader->findTemplates('Page'));
|
|
||||||
$this->assertEquals($expect, $this->loader->findTemplates('PAGE'));
|
|
||||||
$this->assertEquals($expect, $this->loader->findTemplates(array('Foo', 'Page')));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test that 'main' and 'Layout' templates are loaded from set theme
|
* Test that 'main' and 'Layout' templates are loaded from set theme
|
||||||
*/
|
*/
|
||||||
public function testFindTemplatesInTheme() {
|
public function testFindTemplatesInTheme() {
|
||||||
$expect = array(
|
$this->assertEquals(
|
||||||
'main' => "$this->base/themes/theme/templates/Page.ss",
|
"$this->base/themes/theme/templates/Page.ss",
|
||||||
'Layout' => "$this->base/themes/theme/templates/Layout/Page.ss"
|
$this->loader->findTemplate('Page', ['theme'])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
"$this->base/themes/theme/templates/Layout/Page.ss",
|
||||||
|
$this->loader->findTemplate(['type' => 'Layout', 'Page'], ['theme'])
|
||||||
);
|
);
|
||||||
$this->assertEquals($expect, $this->loader->findTemplates('Page', 'theme'));
|
|
||||||
$this->assertEquals($expect, $this->loader->findTemplates('PAGE', 'theme'));
|
|
||||||
$this->assertEquals($expect, $this->loader->findTemplates(array('Foo', 'Page'), 'theme'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test that 'main' and 'Layout' templates are loaded from project without a set theme
|
* Test that 'main' and 'Layout' templates are loaded from project without a set theme
|
||||||
*/
|
*/
|
||||||
public function testFindTemplatesInApplication() {
|
public function testFindTemplatesInApplication() {
|
||||||
|
// TODO: replace with one that doesn't create temporary files (so bad)
|
||||||
$templates = array(
|
$templates = array(
|
||||||
$this->base . '/myproject/templates/Page.ss',
|
$this->base . '/myproject/templates/Page.ss',
|
||||||
$this->base . '/myproject/templates/Layout/Page.ss'
|
$this->base . '/myproject/templates/Layout/Page.ss'
|
||||||
);
|
);
|
||||||
$this->createTestTemplates($templates);
|
$this->createTestTemplates($templates);
|
||||||
$this->refreshLoader();
|
|
||||||
|
|
||||||
$expect = array(
|
$this->assertEquals(
|
||||||
'main' => "$this->base/myproject/templates/Page.ss",
|
"$this->base/myproject/templates/Page.ss",
|
||||||
'Layout' => "$this->base/myproject/templates/Layout/Page.ss"
|
$this->loader->findTemplate('Page', ['$default'])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
"$this->base/myproject/templates/Layout/Page.ss",
|
||||||
|
$this->loader->findTemplate(['type' => 'Layout', 'Page'], ['$default'])
|
||||||
);
|
);
|
||||||
$this->assertEquals($expect, $this->loader->findTemplates('Page'));
|
|
||||||
$this->assertEquals($expect, $this->loader->findTemplates('PAGE'));
|
|
||||||
$this->assertEquals($expect, $this->loader->findTemplates(array('Foo', 'Page')));
|
|
||||||
|
|
||||||
$this->removeTestTemplates($templates);
|
$this->removeTestTemplates($templates);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Test that 'Layout' template is loaded from module
|
|
||||||
*/
|
|
||||||
public function testFindTemplatesInModuleLayout() {
|
|
||||||
$expect = array(
|
|
||||||
'main' => "$this->base/module/templates/Layout/Page.ss"
|
|
||||||
);
|
|
||||||
$this->assertEquals($expect, $this->loader->findTemplates('Layout/Page'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test that 'Layout' template is loaded from theme
|
|
||||||
*/
|
|
||||||
public function testFindTemplatesInThemeLayout() {
|
|
||||||
$expect = array(
|
|
||||||
'main' => "$this->base/themes/theme/templates/Layout/Page.ss"
|
|
||||||
);
|
|
||||||
$this->assertEquals($expect, $this->loader->findTemplates('Layout/Page', 'theme'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test that 'main' template is found in theme and 'Layout' is found in module
|
* Test that 'main' template is found in theme and 'Layout' is found in module
|
||||||
*/
|
*/
|
||||||
public function testFindTemplatesMainThemeLayoutModule() {
|
public function testFindTemplatesMainThemeLayoutModule() {
|
||||||
$expect = array(
|
$this->assertEquals(
|
||||||
'main' => "$this->base/themes/theme/templates/CustomThemePage.ss",
|
"$this->base/themes/theme/templates/CustomThemePage.ss",
|
||||||
'Layout' => "$this->base/module/templates/Layout/CustomThemePage.ss"
|
$this->loader->findTemplate('CustomThemePage', ['theme', '$default'])
|
||||||
);
|
);
|
||||||
$this->assertEquals($expect, $this->loader->findTemplates(array('CustomThemePage', 'Page'), 'theme'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
$this->assertEquals(
|
||||||
* Test that project template overrides module template of same name
|
"$this->base/module/templates/Layout/CustomThemePage.ss",
|
||||||
*/
|
$this->loader->findTemplate(['type' => 'Layout', 'CustomThemePage'], ['theme', '$default'])
|
||||||
public function testFindTemplatesApplicationOverridesModule() {
|
|
||||||
$expect = array(
|
|
||||||
'main' => "$this->base/myproject/templates/CustomTemplate.ss"
|
|
||||||
);
|
);
|
||||||
$this->assertEquals($expect, $this->loader->findTemplates('CustomTemplate'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test that project templates overrides theme templates
|
|
||||||
*/
|
|
||||||
public function testFindTemplatesApplicationOverridesTheme() {
|
|
||||||
$templates = array(
|
|
||||||
$this->base . '/myproject/templates/Page.ss',
|
|
||||||
$this->base . '/myproject/templates/Layout/Page.ss'
|
|
||||||
);
|
|
||||||
$this->createTestTemplates($templates);
|
|
||||||
$this->refreshLoader();
|
|
||||||
|
|
||||||
$expect = array(
|
|
||||||
'main' => "$this->base/myproject/templates/Page.ss",
|
|
||||||
'Layout' => "$this->base/myproject/templates/Layout/Page.ss"
|
|
||||||
);
|
|
||||||
$this->assertEquals($expect, $this->loader->findTemplates('Page'), 'theme');
|
|
||||||
|
|
||||||
$this->removeTestTemplates($templates);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test that project 'Layout' template overrides theme 'Layout' template
|
|
||||||
*/
|
|
||||||
public function testFindTemplatesApplicationLayoutOverridesThemeLayout() {
|
|
||||||
$templates = array(
|
|
||||||
$this->base . '/myproject/templates/Layout/Page.ss'
|
|
||||||
);
|
|
||||||
$this->createTestTemplates($templates);
|
|
||||||
$this->refreshLoader();
|
|
||||||
|
|
||||||
$expect = array(
|
|
||||||
'main' => "$this->base/themes/theme/templates/Page.ss",
|
|
||||||
'Layout' => "$this->base/myproject/templates/Layout/Page.ss"
|
|
||||||
);
|
|
||||||
$this->assertEquals($expect, $this->loader->findTemplates('Page', 'theme'));
|
|
||||||
|
|
||||||
$this->removeTestTemplates($templates);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test that project 'main' template overrides theme 'main' template
|
|
||||||
*/
|
|
||||||
public function testFindTemplatesApplicationMainOverridesThemeMain() {
|
|
||||||
$templates = array(
|
|
||||||
$this->base . '/myproject/templates/Page.ss'
|
|
||||||
);
|
|
||||||
$this->createTestTemplates($templates);
|
|
||||||
$this->refreshLoader();
|
|
||||||
|
|
||||||
$expect = array(
|
|
||||||
'main' => "$this->base/myproject/templates/Page.ss",
|
|
||||||
'Layout' => "$this->base/themes/theme/templates/Layout/Page.ss"
|
|
||||||
);
|
|
||||||
$this->assertEquals($expect, $this->loader->findTemplates('Page', 'theme'));
|
|
||||||
|
|
||||||
$this->removeTestTemplates($templates);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function refreshLoader() {
|
|
||||||
$this->manifest->regenerate(false);
|
|
||||||
$this->loader->pushManifest($this->manifest);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function createTestTemplates($templates) {
|
protected function createTestTemplates($templates) {
|
||||||
|
@ -1,141 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Tests for the template manifest.
|
|
||||||
*
|
|
||||||
* @package framework
|
|
||||||
* @subpackage tests
|
|
||||||
*/
|
|
||||||
class TemplateManifestTest extends SapphireTest {
|
|
||||||
|
|
||||||
protected $base;
|
|
||||||
protected $manifest;
|
|
||||||
protected $manifestTests;
|
|
||||||
|
|
||||||
public function setUp() {
|
|
||||||
parent::setUp();
|
|
||||||
|
|
||||||
$this->base = dirname(__FILE__) . '/fixtures/templatemanifest';
|
|
||||||
$this->manifest = new SS_TemplateManifest($this->base, 'myproject');
|
|
||||||
$this->manifestTests = new SS_TemplateManifest($this->base, 'myproject', true);
|
|
||||||
|
|
||||||
$this->manifest->regenerate(false);
|
|
||||||
$this->manifestTests->regenerate(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetTemplates() {
|
|
||||||
$expect = array(
|
|
||||||
'root' => array(
|
|
||||||
'main' => "{$this->base}/module/Root.ss"
|
|
||||||
),
|
|
||||||
'page' => array(
|
|
||||||
'main' => "{$this->base}/module/templates/Page.ss",
|
|
||||||
'Layout' => "{$this->base}/module/templates/Layout/Page.ss",
|
|
||||||
'themes' => array('theme' => array(
|
|
||||||
'main' => "{$this->base}/themes/theme/templates/Page.ss",
|
|
||||||
'Layout' => "{$this->base}/themes/theme/templates/Layout/Page.ss"
|
|
||||||
))
|
|
||||||
),
|
|
||||||
'custompage' => array(
|
|
||||||
'Layout' => "{$this->base}/module/templates/Layout/CustomPage.ss"
|
|
||||||
),
|
|
||||||
'customtemplate' => array(
|
|
||||||
'main' => "{$this->base}/module/templates/CustomTemplate.ss",
|
|
||||||
'myproject' => array(
|
|
||||||
'main' => "{$this->base}/myproject/templates/CustomTemplate.ss"
|
|
||||||
)
|
|
||||||
),
|
|
||||||
'subfolder' => array(
|
|
||||||
'main' => "{$this->base}/module/subfolder/templates/Subfolder.ss"
|
|
||||||
),
|
|
||||||
'customthemepage' => array (
|
|
||||||
'Layout' => "{$this->base}/module/templates/Layout/CustomThemePage.ss",
|
|
||||||
'themes' =>
|
|
||||||
array(
|
|
||||||
'theme' => array('main' => "{$this->base}/themes/theme/templates/CustomThemePage.ss",)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
'mynamespace\myclass' => array(
|
|
||||||
'main' => "{$this->base}/module/templates/MyNamespace/MyClass.ss",
|
|
||||||
'Layout' => "{$this->base}/module/templates/MyNamespace/Layout/MyClass.ss",
|
|
||||||
'themes' => array(
|
|
||||||
'theme' => array(
|
|
||||||
'main' => "{$this->base}/themes/theme/templates/MyNamespace/MyClass.ss",
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
'mynamespace\mysubnamespace\mysubclass' => array(
|
|
||||||
'main' => "{$this->base}/module/templates/MyNamespace/MySubnamespace/MySubclass.ss",
|
|
||||||
),
|
|
||||||
'myclass' => array(
|
|
||||||
'main' => "{$this->base}/module/templates/MyNamespace/MyClass.ss",
|
|
||||||
'Layout' => "{$this->base}/module/templates/MyNamespace/Layout/MyClass.ss",
|
|
||||||
'themes' => array(
|
|
||||||
'theme' => array(
|
|
||||||
'main' => "{$this->base}/themes/theme/templates/MyNamespace/MyClass.ss",
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
'mysubclass' => array(
|
|
||||||
'main' => "{$this->base}/module/templates/MyNamespace/MySubnamespace/MySubclass.ss",
|
|
||||||
),
|
|
||||||
'include' => array('themes' => array(
|
|
||||||
'theme' => array(
|
|
||||||
'Includes' => "{$this->base}/themes/theme/templates/Includes/Include.ss"
|
|
||||||
)
|
|
||||||
))
|
|
||||||
);
|
|
||||||
|
|
||||||
$expectTests = $expect;
|
|
||||||
$expectTests['test'] = array(
|
|
||||||
'main' => "{$this->base}/module/tests/templates/Test.ss"
|
|
||||||
);
|
|
||||||
|
|
||||||
$manifest = $this->manifest->getTemplates();
|
|
||||||
$manifestTests = $this->manifestTests->getTemplates();
|
|
||||||
|
|
||||||
ksort($expect);
|
|
||||||
ksort($expectTests);
|
|
||||||
ksort($manifest);
|
|
||||||
ksort($manifestTests);
|
|
||||||
|
|
||||||
$this->assertEquals(
|
|
||||||
$expect, $manifest,
|
|
||||||
'All templates are correctly loaded in the manifest.'
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->assertEquals(
|
|
||||||
$expectTests, $manifestTests,
|
|
||||||
'The test manifest is the same, but includes test templates.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetTemplate() {
|
|
||||||
$expectPage = array(
|
|
||||||
'main' => "{$this->base}/module/templates/Page.ss",
|
|
||||||
'Layout' => "{$this->base}/module/templates/Layout/Page.ss",
|
|
||||||
'themes' => array('theme' => array(
|
|
||||||
'main' => "{$this->base}/themes/theme/templates/Page.ss",
|
|
||||||
'Layout' => "{$this->base}/themes/theme/templates/Layout/Page.ss"
|
|
||||||
))
|
|
||||||
);
|
|
||||||
|
|
||||||
$expectTests = array(
|
|
||||||
'main' => "{$this->base}/module/tests/templates/Test.ss"
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->assertEquals($expectPage, $this->manifest->getTemplate('Page'));
|
|
||||||
$this->assertEquals($expectPage, $this->manifest->getTemplate('PAGE'));
|
|
||||||
$this->assertEquals($expectPage, $this->manifestTests->getTemplate('Page'));
|
|
||||||
$this->assertEquals($expectPage, $this->manifestTests->getTemplate('PAGE'));
|
|
||||||
|
|
||||||
$this->assertEquals(array(), $this->manifest->getTemplate('Test'));
|
|
||||||
$this->assertEquals($expectTests, $this->manifestTests->getTemplate('Test'));
|
|
||||||
|
|
||||||
$this->assertEquals(array(
|
|
||||||
'main' => "{$this->base}/module/templates/CustomTemplate.ss",
|
|
||||||
'myproject' => array(
|
|
||||||
'main' => "{$this->base}/myproject/templates/CustomTemplate.ss"
|
|
||||||
)), $this->manifestTests->getTemplate('CustomTemplate'));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -110,7 +110,7 @@ class MemberDatetimeOptionsetFieldTest extends SapphireTest {
|
|||||||
$field->setDescription('Test description');
|
$field->setDescription('Test description');
|
||||||
$this->assertEquals('Test description', $field->getDescription());
|
$this->assertEquals('Test description', $field->getDescription());
|
||||||
|
|
||||||
$field->setDescriptionTemplate('MemberDatetimeOptionsetField_description_time');
|
$field->setDescriptionTemplate('forms/MemberDatetimeOptionsetField_description_time');
|
||||||
$this->assertNotEmpty($field->getDescription());
|
$this->assertNotEmpty($field->getDescription());
|
||||||
$this->assertNotEquals('Test description', $field->getDescription());
|
$this->assertNotEquals('Test description', $field->getDescription());
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use SilverStripe\View\TemplateLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @package framework
|
* @package framework
|
||||||
* @subpackage i18n
|
* @subpackage i18n
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class i18nSSLegacyAdapterTest extends SapphireTest {
|
class i18nSSLegacyAdapterTest extends SapphireTest {
|
||||||
|
|
||||||
public function setUp() {
|
public function setUp() {
|
||||||
@ -14,10 +16,10 @@ class i18nSSLegacyAdapterTest extends SapphireTest {
|
|||||||
Filesystem::makeFolder($this->alternateBaseSavePath);
|
Filesystem::makeFolder($this->alternateBaseSavePath);
|
||||||
Config::inst()->update('Director', 'alternate_base_folder', $this->alternateBasePath);
|
Config::inst()->update('Director', 'alternate_base_folder', $this->alternateBasePath);
|
||||||
|
|
||||||
// Push a template loader running from the fake webroot onto the stack.
|
// Replace old template loader with new one with alternate base path
|
||||||
$templateManifest = new SS_TemplateManifest($this->alternateBasePath, null, false, true);
|
$this->_oldLoader = TemplateLoader::instance();
|
||||||
$templateManifest->regenerate(false);
|
TemplateLoader::set_instance(new TemplateLoader($this->alternateBasePath));
|
||||||
SS_TemplateLoader::instance()->pushManifest($templateManifest);
|
|
||||||
$this->_oldTheme = Config::inst()->get('SSViewer', 'theme');
|
$this->_oldTheme = Config::inst()->get('SSViewer', 'theme');
|
||||||
Config::inst()->update('SSViewer', 'theme', 'testtheme1');
|
Config::inst()->update('SSViewer', 'theme', 'testtheme1');
|
||||||
|
|
||||||
@ -40,7 +42,7 @@ class i18nSSLegacyAdapterTest extends SapphireTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function tearDown() {
|
public function tearDown() {
|
||||||
SS_TemplateLoader::instance()->popManifest();
|
TemplateLoader::set_instance($this->_oldLoader);
|
||||||
SS_ClassLoader::instance()->popManifest();
|
SS_ClassLoader::instance()->popManifest();
|
||||||
i18n::set_locale($this->originalLocale);
|
i18n::set_locale($this->originalLocale);
|
||||||
Config::inst()->update('Director', 'alternate_base_folder', null);
|
Config::inst()->update('Director', 'alternate_base_folder', null);
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
|
use SilverStripe\View\TemplateLoader;
|
||||||
|
use SilverStripe\View\ThemeManifest;
|
||||||
|
|
||||||
require_once 'Zend/Translate.php';
|
require_once 'Zend/Translate.php';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -35,10 +38,13 @@ class i18nTest extends SapphireTest {
|
|||||||
FileSystem::makeFolder($this->alternateBaseSavePath);
|
FileSystem::makeFolder($this->alternateBaseSavePath);
|
||||||
Config::inst()->update('Director', 'alternate_base_folder', $this->alternateBasePath);
|
Config::inst()->update('Director', 'alternate_base_folder', $this->alternateBasePath);
|
||||||
|
|
||||||
// Push a template loader running from the fake webroot onto the stack.
|
// Replace old template loader with new one with alternate base path
|
||||||
$templateManifest = new SS_TemplateManifest($this->alternateBasePath, null, false, true);
|
$this->_oldLoader = TemplateLoader::instance();
|
||||||
$templateManifest->regenerate(false);
|
TemplateLoader::set_instance($loader = new TemplateLoader($this->alternateBasePath));
|
||||||
SS_TemplateLoader::instance()->pushManifest($templateManifest);
|
$loader->addSet('$default', new ThemeManifest(
|
||||||
|
$this->alternateBasePath, project(), false, true
|
||||||
|
));
|
||||||
|
|
||||||
$this->_oldTheme = Config::inst()->get('SSViewer', 'theme');
|
$this->_oldTheme = Config::inst()->get('SSViewer', 'theme');
|
||||||
Config::inst()->update('SSViewer', 'theme', 'testtheme1');
|
Config::inst()->update('SSViewer', 'theme', 'testtheme1');
|
||||||
|
|
||||||
@ -58,7 +64,7 @@ class i18nTest extends SapphireTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function tearDown() {
|
public function tearDown() {
|
||||||
SS_TemplateLoader::instance()->popManifest();
|
TemplateLoader::set_instance($this->_oldLoader);
|
||||||
i18n::set_locale($this->originalLocale);
|
i18n::set_locale($this->originalLocale);
|
||||||
Config::inst()->update('Director', 'alternate_base_folder', null);
|
Config::inst()->update('Director', 'alternate_base_folder', null);
|
||||||
Config::inst()->update('SSViewer', 'theme', $this->_oldTheme);
|
Config::inst()->update('SSViewer', 'theme', $this->_oldTheme);
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use SilverStripe\View\TemplateLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @package framework
|
* @package framework
|
||||||
* @subpackage tests
|
* @subpackage tests
|
||||||
@ -34,13 +37,13 @@ class i18nTextCollectorTest extends SapphireTest {
|
|||||||
$this->alternateBasePath, false, true, false
|
$this->alternateBasePath, false, true, false
|
||||||
);
|
);
|
||||||
|
|
||||||
$manifest = new SS_TemplateManifest($this->alternateBasePath, null, false, true);
|
// Replace old template loader with new one with alternate base path
|
||||||
$manifest->regenerate(false);
|
$this->_oldLoader = TemplateLoader::instance();
|
||||||
SS_TemplateLoader::instance()->pushManifest($manifest);
|
TemplateLoader::set_instance(new TemplateLoader($this->alternateBasePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tearDown() {
|
public function tearDown() {
|
||||||
SS_TemplateLoader::instance()->popManifest();
|
TemplateLoader::set_instance($this->_oldLoader);
|
||||||
// Pop if added during testing
|
// Pop if added during testing
|
||||||
if(SS_ClassLoader::instance()->getManifest() === $this->manifest) {
|
if(SS_ClassLoader::instance()->getManifest() === $this->manifest) {
|
||||||
SS_ClassLoader::instance()->popManifest();
|
SS_ClassLoader::instance()->popManifest();
|
||||||
|
@ -2,6 +2,5 @@
|
|||||||
<% include SSViewerTestProcessHead %>
|
<% include SSViewerTestProcessHead %>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<% include SSViewerTestCommentsWithInclude %>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -759,17 +759,11 @@ after')
|
|||||||
$this->render('tests:( <% include Namespace/NamespaceInclude %> )', $data),
|
$this->render('tests:( <% include Namespace/NamespaceInclude %> )', $data),
|
||||||
'Forward slashes work for namespace references in includes'
|
'Forward slashes work for namespace references in includes'
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertEquals(
|
|
||||||
"tests:( NamespaceInclude\n )",
|
|
||||||
$this->render('tests:( <% include NamespaceInclude %> )', $data),
|
|
||||||
'Namespace can be missed for a namespaed include'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function testRecursiveInclude() {
|
public function testRecursiveInclude() {
|
||||||
$view = new SSViewer(array('SSViewerTestRecursiveInclude'));
|
$view = new SSViewer(array('Includes/SSViewerTestRecursiveInclude'));
|
||||||
|
|
||||||
$data = new ArrayData(array(
|
$data = new ArrayData(array(
|
||||||
'Title' => 'A',
|
'Title' => 'A',
|
||||||
@ -1160,21 +1154,21 @@ after')
|
|||||||
$this->useTestTheme(dirname(__FILE__), 'layouttest', function() use ($self) {
|
$this->useTestTheme(dirname(__FILE__), 'layouttest', function() use ($self) {
|
||||||
// Test passing a string
|
// Test passing a string
|
||||||
$templates = SSViewer::get_templates_by_class(
|
$templates = SSViewer::get_templates_by_class(
|
||||||
'TestNamespace\SSViewerTest_Controller',
|
'TestNamespace\SSViewerTestModel_Controller',
|
||||||
'',
|
'',
|
||||||
'Controller'
|
'Controller'
|
||||||
);
|
);
|
||||||
$self->assertEquals([
|
$self->assertEquals([
|
||||||
'TestNamespace\SSViewerTest_Controller',
|
'TestNamespace\SSViewerTestModel_Controller',
|
||||||
'Controller',
|
'Controller',
|
||||||
], $templates);
|
], $templates);
|
||||||
|
|
||||||
// Test to ensure we're stopping at the base class.
|
// Test to ensure we're stopping at the base class.
|
||||||
$templates = SSViewer::get_templates_by_class('TestNamespace\SSViewerTest_Controller', '', 'TestNamespace\SSViewerTest_Controller');
|
$templates = SSViewer::get_templates_by_class('TestNamespace\SSViewerTestModel_Controller', '', 'TestNamespace\SSViewerTestModel_Controller');
|
||||||
$self->assertCount(1, $templates);
|
$self->assertCount(1, $templates);
|
||||||
|
|
||||||
// Make sure we can filter our templates by suffix.
|
// Make sure we can filter our templates by suffix.
|
||||||
$templates = SSViewer::get_templates_by_class('SSViewerTest', '_Controller');
|
$templates = SSViewer::get_templates_by_class('TestNamespace\SSViewerTestModel', '_Controller');
|
||||||
$self->assertCount(1, $templates);
|
$self->assertCount(1, $templates);
|
||||||
|
|
||||||
// Let's throw something random in there.
|
// Let's throw something random in there.
|
||||||
@ -1183,38 +1177,6 @@ after')
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @covers SSViewer::get_themes()
|
|
||||||
*/
|
|
||||||
public function testThemeRetrieval() {
|
|
||||||
$ds = DIRECTORY_SEPARATOR;
|
|
||||||
$testThemeBaseDir = TEMP_FOLDER . $ds . 'test-themes';
|
|
||||||
|
|
||||||
if(file_exists($testThemeBaseDir)) Filesystem::removeFolder($testThemeBaseDir);
|
|
||||||
|
|
||||||
mkdir($testThemeBaseDir);
|
|
||||||
mkdir($testThemeBaseDir . $ds . 'blackcandy');
|
|
||||||
mkdir($testThemeBaseDir . $ds . 'blackcandy_blog');
|
|
||||||
mkdir($testThemeBaseDir . $ds . 'darkshades');
|
|
||||||
mkdir($testThemeBaseDir . $ds . 'darkshades_blog');
|
|
||||||
|
|
||||||
$this->assertEquals(array(
|
|
||||||
'blackcandy' => 'blackcandy',
|
|
||||||
'darkshades' => 'darkshades'
|
|
||||||
), SSViewer::get_themes($testThemeBaseDir), 'Our test theme directory contains 2 themes');
|
|
||||||
|
|
||||||
$this->assertEquals(array(
|
|
||||||
'blackcandy' => 'blackcandy',
|
|
||||||
'blackcandy_blog' => 'blackcandy_blog',
|
|
||||||
'darkshades' => 'darkshades',
|
|
||||||
'darkshades_blog' => 'darkshades_blog'
|
|
||||||
), SSViewer::get_themes($testThemeBaseDir, true),
|
|
||||||
'Our test theme directory contains 2 themes and 2 sub-themes');
|
|
||||||
|
|
||||||
// Remove all the test themes we created
|
|
||||||
Filesystem::removeFolder($testThemeBaseDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testRewriteHashlinks() {
|
public function testRewriteHashlinks() {
|
||||||
$orig = Config::inst()->get('SSViewer', 'rewrite_hash_links');
|
$orig = Config::inst()->get('SSViewer', 'rewrite_hash_links');
|
||||||
Config::inst()->update('SSViewer', 'rewrite_hash_links', true);
|
Config::inst()->update('SSViewer', 'rewrite_hash_links', true);
|
||||||
@ -1327,6 +1289,7 @@ EOC;
|
|||||||
$origEnv = Config::inst()->get('Director', 'environment_type');
|
$origEnv = Config::inst()->get('Director', 'environment_type');
|
||||||
Config::inst()->update('Director', 'environment_type', 'dev');
|
Config::inst()->update('Director', 'environment_type', 'dev');
|
||||||
Config::inst()->update('SSViewer', 'source_file_comments', true);
|
Config::inst()->update('SSViewer', 'source_file_comments', true);
|
||||||
|
$i = FRAMEWORK_PATH . '/tests/templates/Includes';
|
||||||
$f = FRAMEWORK_PATH . '/tests/templates/SSViewerTestComments';
|
$f = FRAMEWORK_PATH . '/tests/templates/SSViewerTestComments';
|
||||||
$templates = array(
|
$templates = array(
|
||||||
array(
|
array(
|
||||||
@ -1398,16 +1361,16 @@ EOC;
|
|||||||
"<!-- template $f/SSViewerTestCommentsWithInclude.ss -->"
|
"<!-- template $f/SSViewerTestCommentsWithInclude.ss -->"
|
||||||
. "<div class='typography'>"
|
. "<div class='typography'>"
|
||||||
. "<!-- include 'SSViewerTestCommentsInclude' -->"
|
. "<!-- include 'SSViewerTestCommentsInclude' -->"
|
||||||
. "<!-- template $f/SSViewerTestCommentsInclude.ss -->"
|
. "<!-- template $i/SSViewerTestCommentsInclude.ss -->"
|
||||||
. "Included"
|
. "Included"
|
||||||
. "<!-- end template $f/SSViewerTestCommentsInclude.ss -->"
|
. "<!-- end template $i/SSViewerTestCommentsInclude.ss -->"
|
||||||
. "<!-- end include 'SSViewerTestCommentsInclude' -->"
|
. "<!-- end include 'SSViewerTestCommentsInclude' -->"
|
||||||
. "</div>"
|
. "</div>"
|
||||||
. "<!-- end template $f/SSViewerTestCommentsWithInclude.ss -->",
|
. "<!-- end template $f/SSViewerTestCommentsWithInclude.ss -->",
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
foreach ($templates as $template) {
|
foreach ($templates as $template) {
|
||||||
$this->_renderWithSourceFileComments($template['name'], $template['expected']);
|
$this->_renderWithSourceFileComments('SSViewerTestComments/'.$template['name'], $template['expected']);
|
||||||
}
|
}
|
||||||
Config::inst()->update('SSViewer', 'source_file_comments', false);
|
Config::inst()->update('SSViewer', 'source_file_comments', false);
|
||||||
Config::inst()->update('Director', 'environment_type', $origEnv);
|
Config::inst()->update('Director', 'environment_type', $origEnv);
|
||||||
|
@ -2,7 +2,12 @@
|
|||||||
|
|
||||||
namespace TestNamespace;
|
namespace TestNamespace;
|
||||||
|
|
||||||
class SSViewerTest_Controller extends \Controller
|
use SilverStripe\ORM\DataObject;
|
||||||
{
|
|
||||||
|
class SSViewerTestModel extends DataObject {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class SSViewerTestModel_Controller extends \Controller {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use SilverStripe\Filesystem\Storage\GeneratedAssetHandler;
|
use SilverStripe\Filesystem\Storage\GeneratedAssetHandler;
|
||||||
|
use SilverStripe\View\TemplateLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requirements tracker for JavaScript and CSS.
|
* Requirements tracker for JavaScript and CSS.
|
||||||
@ -818,7 +819,7 @@ class Requirements_Backend
|
|||||||
public function javascript($file, $options = array()) {
|
public function javascript($file, $options = array()) {
|
||||||
// make sure that async/defer is set if it is set once even if file is included multiple times
|
// make sure that async/defer is set if it is set once even if file is included multiple times
|
||||||
$async = (
|
$async = (
|
||||||
isset($options['async']) && isset($options['async']) == true
|
isset($options['async']) && isset($options['async']) == true
|
||||||
|| (
|
|| (
|
||||||
isset($this->javascript[$file])
|
isset($this->javascript[$file])
|
||||||
&& isset($this->javascript[$file]['async'])
|
&& isset($this->javascript[$file]['async'])
|
||||||
@ -842,7 +843,7 @@ class Requirements_Backend
|
|||||||
if(isset($options['provides'])) {
|
if(isset($options['provides'])) {
|
||||||
$this->providedJavascript[$file] = array_values($options['provides']);
|
$this->providedJavascript[$file] = array_values($options['provides']);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1798,21 +1799,27 @@ class Requirements_Backend
|
|||||||
* (e.g. 'screen,projector')
|
* (e.g. 'screen,projector')
|
||||||
*/
|
*/
|
||||||
public function themedCSS($name, $module = null, $media = null) {
|
public function themedCSS($name, $module = null, $media = null) {
|
||||||
$theme = SSViewer::get_theme_folder();
|
|
||||||
$project = project();
|
|
||||||
$absbase = BASE_PATH . DIRECTORY_SEPARATOR;
|
|
||||||
$abstheme = $absbase . $theme;
|
|
||||||
$absproject = $absbase . $project;
|
|
||||||
$css = "/css/$name.css";
|
$css = "/css/$name.css";
|
||||||
|
|
||||||
|
$project = project();
|
||||||
|
$absbase = BASE_PATH . DIRECTORY_SEPARATOR;
|
||||||
|
$absproject = $absbase . $project;
|
||||||
|
|
||||||
if(file_exists($absproject . $css)) {
|
if(file_exists($absproject . $css)) {
|
||||||
$this->css($project . $css, $media);
|
return $this->css($project . $css, $media);
|
||||||
} elseif($module && file_exists($abstheme . '_' . $module.$css)) {
|
}
|
||||||
$this->css($theme . '_' . $module . $css, $media);
|
|
||||||
} elseif(file_exists($abstheme . $css)) {
|
foreach(SSViewer::get_themes() as $theme) {
|
||||||
$this->css($theme . $css, $media);
|
$path = TemplateLoader::instance()->getPath($theme);
|
||||||
} elseif($module) {
|
$abspath = BASE_PATH . '/' . $path;
|
||||||
$this->css($module . $css, $media);
|
|
||||||
|
if(file_exists($abspath . $css)) {
|
||||||
|
return $this->css($path . $css, $media);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($module) {
|
||||||
|
return $this->css($module . $css, $media);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3382,7 +3382,7 @@ class SSTemplateParser extends Parser implements TemplateParser {
|
|||||||
$template = $res['template'];
|
$template = $res['template'];
|
||||||
$arguments = $res['arguments'];
|
$arguments = $res['arguments'];
|
||||||
|
|
||||||
$res['php'] = '$val .= SSViewer::execute_template('.$template.', $scope->getItem(), array(' .
|
$res['php'] = '$val .= SSViewer::execute_template(["type" => "Includes", '.$template.'], $scope->getItem(), array(' .
|
||||||
implode(',', $arguments)."), \$scope);\n";
|
implode(',', $arguments)."), \$scope);\n";
|
||||||
|
|
||||||
if($this->includeDebuggingComments) { // Add include filename comments on dev sites
|
if($this->includeDebuggingComments) { // Add include filename comments on dev sites
|
||||||
|
@ -830,7 +830,7 @@ class SSTemplateParser extends Parser implements TemplateParser {
|
|||||||
$template = $res['template'];
|
$template = $res['template'];
|
||||||
$arguments = $res['arguments'];
|
$arguments = $res['arguments'];
|
||||||
|
|
||||||
$res['php'] = '$val .= SSViewer::execute_template('.$template.', $scope->getItem(), array(' .
|
$res['php'] = '$val .= SSViewer::execute_template(["type" => "Includes", '.$template.'], $scope->getItem(), array(' .
|
||||||
implode(',', $arguments)."), \$scope);\n";
|
implode(',', $arguments)."), \$scope);\n";
|
||||||
|
|
||||||
if($this->includeDebuggingComments) { // Add include filename comments on dev sites
|
if($this->includeDebuggingComments) { // Add include filename comments on dev sites
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
use SilverStripe\ORM\FieldType\DBField;
|
use SilverStripe\ORM\FieldType\DBField;
|
||||||
use SilverStripe\ORM\FieldType\DBHTMLText;
|
use SilverStripe\ORM\FieldType\DBHTMLText;
|
||||||
use SilverStripe\Security\Permission;
|
use SilverStripe\Security\Permission;
|
||||||
|
use SilverStripe\View\TemplateLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This tracks the current scope for an SSViewer instance. It has three goals:
|
* This tracks the current scope for an SSViewer instance. It has three goals:
|
||||||
@ -723,10 +724,19 @@ class SSViewer implements Flushable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array $chosenTemplates Associative array for the different
|
* @var array $templates List of templates to select from
|
||||||
* template containers: "main" and "Layout". Values are absolute file paths to *.ss files.
|
|
||||||
*/
|
*/
|
||||||
private $chosenTemplates = array();
|
private $templates = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string $chosen Absolute path to chosen template file
|
||||||
|
*/
|
||||||
|
private $chosen = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array Templates to use when looking up 'Layout' or 'Content'
|
||||||
|
*/
|
||||||
|
private $subTemplates = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var boolean
|
* @var boolean
|
||||||
@ -735,9 +745,17 @@ class SSViewer implements Flushable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @config
|
* @config
|
||||||
* @var string The used "theme", which usually consists of templates, images and stylesheets.
|
* @var string A list (highest priority first) of themes to use
|
||||||
* Only used when {@link $theme_enabled} is set to TRUE.
|
* Only used when {@link $theme_enabled} is set to TRUE.
|
||||||
*/
|
*/
|
||||||
|
private static $themes = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated 4.0..5.0
|
||||||
|
* @config
|
||||||
|
* @var string The used "theme", which usually consists of templates, images and stylesheets.
|
||||||
|
* Only used when {@link $theme_enabled} is set to TRUE, and $themes is empty
|
||||||
|
*/
|
||||||
private static $theme = null;
|
private static $theme = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -791,65 +809,33 @@ class SSViewer implements Flushable {
|
|||||||
return $viewer;
|
return $viewer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function set_themes($themes = []) {
|
||||||
|
Config::inst()->remove('SSViewer', 'themes');
|
||||||
|
Config::inst()->update('SSViewer', 'themes', $themes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function add_themes($themes = []) {
|
||||||
|
Config::inst()->update('SSViewer', 'themes', $themes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get_themes() {
|
||||||
|
$res = ['$default'];
|
||||||
|
|
||||||
|
if (Config::inst()->get('SSViewer', 'theme_enabled')) {
|
||||||
|
if ($list = Config::inst()->get('SSViewer', 'themes')) $res = $list;
|
||||||
|
elseif ($theme = Config::inst()->get('SSViewer', 'theme')) $res = [$theme, '$default'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated 4.0 Use the "SSViewer.theme" config setting instead
|
* @deprecated 4.0 Use the "SSViewer.theme" config setting instead
|
||||||
* @param string $theme The "base theme" name (without underscores).
|
* @param string $theme The "base theme" name (without underscores).
|
||||||
*/
|
*/
|
||||||
public static function set_theme($theme) {
|
public static function set_theme($theme) {
|
||||||
Deprecation::notice('4.0', 'Use the "SSViewer.theme" config setting instead');
|
Deprecation::notice('4.0', 'Use the "SSViewer#set_themes" instead');
|
||||||
Config::inst()->update('SSViewer', 'theme', $theme);
|
self::set_themes([$theme]);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated 4.0 Use the "SSViewer.theme" config setting instead
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function current_theme() {
|
|
||||||
Deprecation::notice('4.0', 'Use the "SSViewer.theme" config setting instead');
|
|
||||||
return Config::inst()->get('SSViewer', 'theme');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the path to the theme folder
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function get_theme_folder() {
|
|
||||||
$theme = Config::inst()->get('SSViewer', 'theme');
|
|
||||||
return $theme ? THEMES_DIR . "/" . $theme : project();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array of theme names present in a directory.
|
|
||||||
*
|
|
||||||
* @param string $path
|
|
||||||
* @param bool $subthemes Include subthemes (default false).
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public static function get_themes($path = null, $subthemes = false) {
|
|
||||||
$path = rtrim($path ? $path : THEMES_PATH, '/');
|
|
||||||
$themes = array();
|
|
||||||
|
|
||||||
if (!is_dir($path)) return $themes;
|
|
||||||
|
|
||||||
foreach (scandir($path) as $item) {
|
|
||||||
if ($item[0] != '.' && is_dir("$path/$item")) {
|
|
||||||
if ($subthemes || strpos($item, '_') === false) {
|
|
||||||
$themes[$item] = $item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $themes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated since version 4.0
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function current_custom_theme(){
|
|
||||||
Deprecation::notice('4.0', 'Use the "SSViewer.theme" and "SSViewer.theme_enabled" config settings instead');
|
|
||||||
return Config::inst()->get('SSViewer', 'theme_enabled') ? Config::inst()->get('SSViewer', 'theme') : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -873,6 +859,7 @@ class SSViewer implements Flushable {
|
|||||||
foreach($classes as $class) {
|
foreach($classes as $class) {
|
||||||
$template = $class . $suffix;
|
$template = $class . $suffix;
|
||||||
if(SSViewer::hasTemplate($template)) $templates[] = $template;
|
if(SSViewer::hasTemplate($template)) $templates[] = $template;
|
||||||
|
elseif(SSViewer::hasTemplate('Includes/'.$template)) $templates[] = 'Includes/'.$template;
|
||||||
|
|
||||||
// If the class is "Page_Controller", look for Page.ss
|
// If the class is "Page_Controller", look for Page.ss
|
||||||
if(stripos($class,'_controller') !== false) {
|
if(stripos($class,'_controller') !== false) {
|
||||||
@ -893,38 +880,34 @@ class SSViewer implements Flushable {
|
|||||||
* array('MySpecificPage', 'MyPage', 'Page')
|
* array('MySpecificPage', 'MyPage', 'Page')
|
||||||
* </code>
|
* </code>
|
||||||
*/
|
*/
|
||||||
public function __construct($templateList, TemplateParser $parser = null) {
|
public function __construct($templates, TemplateParser $parser = null) {
|
||||||
if ($parser) {
|
if ($parser) {
|
||||||
$this->setParser($parser);
|
$this->setParser($parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!is_array($templateList) && substr((string) $templateList,-3) == '.ss') {
|
$this->setTemplate($templates);
|
||||||
$this->chosenTemplates['main'] = $templateList;
|
|
||||||
} else {
|
|
||||||
if(Config::inst()->get('SSViewer', 'theme_enabled')) {
|
|
||||||
$theme = Config::inst()->get('SSViewer', 'theme');
|
|
||||||
} else {
|
|
||||||
$theme = null;
|
|
||||||
}
|
|
||||||
$this->chosenTemplates = SS_TemplateLoader::instance()->findTemplates(
|
|
||||||
$templateList, $theme
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!$this->chosenTemplates) {
|
if(!$this->chosen) {
|
||||||
$templateList = (is_array($templateList)) ? $templateList : array($templateList);
|
$message = 'None of the following templates could be found: ';
|
||||||
|
$message .= print_r($templates, true);
|
||||||
|
|
||||||
$message = 'None of the following templates could be found';
|
$themes = self::get_themes();
|
||||||
if(!$theme) {
|
if(!$themes) {
|
||||||
$message .= ' (no theme in use)';
|
$message .= ' (no theme in use)';
|
||||||
} else {
|
} else {
|
||||||
$message .= ' in theme "' . $theme . '"';
|
$message .= ' in themes "' . print_r($themes, true) . '"';
|
||||||
}
|
}
|
||||||
|
|
||||||
user_error($message . ': ' . implode(".ss, ", $templateList) . ".ss", E_USER_WARNING);
|
user_error($message, E_USER_WARNING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setTemplate($templates) {
|
||||||
|
$this->templates = $templates;
|
||||||
|
$this->chosen = TemplateLoader::instance()->findTemplate($templates, self::get_themes());
|
||||||
|
$this->subTemplates = [];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the template parser that will be used in template generation
|
* Set the template parser that will be used in template generation
|
||||||
* @param \TemplateParser $parser
|
* @param \TemplateParser $parser
|
||||||
@ -954,19 +937,7 @@ class SSViewer implements Flushable {
|
|||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public static function hasTemplate($templates) {
|
public static function hasTemplate($templates) {
|
||||||
$manifest = SS_TemplateLoader::instance()->getManifest();
|
return (bool)TemplateLoader::instance()->findTemplate($templates, self::get_themes());
|
||||||
|
|
||||||
if(Config::inst()->get('SSViewer', 'theme_enabled')) {
|
|
||||||
$theme = Config::inst()->get('SSViewer', 'theme');
|
|
||||||
} else {
|
|
||||||
$theme = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ((array) $templates as $template) {
|
|
||||||
if ($manifest->getCandidateTemplate($template, $theme)) return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1033,7 +1004,7 @@ class SSViewer implements Flushable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function exists() {
|
public function exists() {
|
||||||
return $this->chosenTemplates;
|
return $this->chosen;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1043,21 +1014,7 @@ class SSViewer implements Flushable {
|
|||||||
* @return string Full system path to a template file
|
* @return string Full system path to a template file
|
||||||
*/
|
*/
|
||||||
public static function getTemplateFileByType($identifier, $type) {
|
public static function getTemplateFileByType($identifier, $type) {
|
||||||
$loader = SS_TemplateLoader::instance();
|
return TemplateLoader::instance()->findTemplate(['type' => $type, $identifier], self::get_themes());
|
||||||
if(Config::inst()->get('SSViewer', 'theme_enabled')) {
|
|
||||||
$theme = Config::inst()->get('SSViewer', 'theme');
|
|
||||||
} else {
|
|
||||||
$theme = null;
|
|
||||||
}
|
|
||||||
$found = $loader->findTemplates("$type/$identifier", $theme);
|
|
||||||
|
|
||||||
if (isset($found['main'])) {
|
|
||||||
return $found['main'];
|
|
||||||
}
|
|
||||||
else if (!empty($found)) {
|
|
||||||
$founds = array_values($found);
|
|
||||||
return $founds[0];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1192,13 +1149,7 @@ class SSViewer implements Flushable {
|
|||||||
public function process($item, $arguments = null, $inheritedScope = null) {
|
public function process($item, $arguments = null, $inheritedScope = null) {
|
||||||
SSViewer::$topLevel[] = $item;
|
SSViewer::$topLevel[] = $item;
|
||||||
|
|
||||||
if(isset($this->chosenTemplates['main'])) {
|
$template = $this->chosen;
|
||||||
$template = $this->chosenTemplates['main'];
|
|
||||||
} else {
|
|
||||||
$keys = array_keys($this->chosenTemplates);
|
|
||||||
$key = reset($keys);
|
|
||||||
$template = $this->chosenTemplates[$key];
|
|
||||||
}
|
|
||||||
|
|
||||||
$cacheFile = TEMP_FOLDER . "/.cache"
|
$cacheFile = TEMP_FOLDER . "/.cache"
|
||||||
. str_replace(array('\\','/',':'), '.', Director::makeRelative(realpath($template)));
|
. str_replace(array('\\','/',':'), '.', Director::makeRelative(realpath($template)));
|
||||||
@ -1218,14 +1169,27 @@ class SSViewer implements Flushable {
|
|||||||
// Makes the rendered sub-templates available on the parent item,
|
// Makes the rendered sub-templates available on the parent item,
|
||||||
// through $Content and $Layout placeholders.
|
// through $Content and $Layout placeholders.
|
||||||
foreach(array('Content', 'Layout') as $subtemplate) {
|
foreach(array('Content', 'Layout') as $subtemplate) {
|
||||||
if(isset($this->chosenTemplates[$subtemplate])) {
|
$sub = null;
|
||||||
|
if(isset($this->subTemplates[$subtemplate])) {
|
||||||
|
$sub = $this->subTemplates[$subtemplate];
|
||||||
|
}
|
||||||
|
elseif(!is_array($this->templates)) {
|
||||||
|
$sub = ['type' => $subtemplate, $this->templates];
|
||||||
|
}
|
||||||
|
elseif(!array_key_exists('type', $this->templates) || !$this->templates['type']) {
|
||||||
|
$sub = array_merge($this->templates, ['type' => $subtemplate]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($sub) {
|
||||||
$subtemplateViewer = clone $this;
|
$subtemplateViewer = clone $this;
|
||||||
// Disable requirements - this will be handled by the parent template
|
// Disable requirements - this will be handled by the parent template
|
||||||
$subtemplateViewer->includeRequirements(false);
|
$subtemplateViewer->includeRequirements(false);
|
||||||
// The subtemplate is the only file we want to process, so set it as the "main" template file
|
// Select the right template
|
||||||
$subtemplateViewer->chosenTemplates = array('main' => $this->chosenTemplates[$subtemplate]);
|
$subtemplateViewer->setTemplate($sub);
|
||||||
|
|
||||||
$underlay[$subtemplate] = $subtemplateViewer->process($item, $arguments);
|
if ($subtemplateViewer->exists()) {
|
||||||
|
$underlay[$subtemplate] = $subtemplateViewer->process($item, $arguments);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1301,7 +1265,7 @@ class SSViewer implements Flushable {
|
|||||||
* 'Content' & 'Layout', and will have to contain 'main'
|
* 'Content' & 'Layout', and will have to contain 'main'
|
||||||
*/
|
*/
|
||||||
public function templates() {
|
public function templates() {
|
||||||
return $this->chosenTemplates;
|
return array_merge(['main' => $this->chosen], $this->subTemplates);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1309,7 +1273,8 @@ class SSViewer implements Flushable {
|
|||||||
* @param string $file Full system path to the template file
|
* @param string $file Full system path to the template file
|
||||||
*/
|
*/
|
||||||
public function setTemplateFile($type, $file) {
|
public function setTemplateFile($type, $file) {
|
||||||
$this->chosenTemplates[$type] = $file;
|
if (!$type || $type == 'main') $this->chosen = $file;
|
||||||
|
else $this->subTemplates[$type] = $file;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
118
view/TemplateLoader.php
Normal file
118
view/TemplateLoader.php
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles finding templates from a stack of template manifest objects.
|
||||||
|
*
|
||||||
|
* @package framework
|
||||||
|
* @subpackage view
|
||||||
|
*/
|
||||||
|
class TemplateLoader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var TemplateLoader
|
||||||
|
*/
|
||||||
|
private static $instance;
|
||||||
|
|
||||||
|
protected $base;
|
||||||
|
|
||||||
|
protected $sets = [];
|
||||||
|
|
||||||
|
public static function instance() {
|
||||||
|
return self::$instance ? self::$instance : self::$instance = new self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function set_instance(TemplateLoader $instance) {
|
||||||
|
self::$instance = $instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct($base = null) {
|
||||||
|
$this->base = $base ? $base : BASE_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addSet($set, $manifest) {
|
||||||
|
$this->sets[$set] = $manifest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPath($identifier) {
|
||||||
|
$slashPos = strpos($identifier, '/');
|
||||||
|
|
||||||
|
// If identifier starts with "/", it's a path from root
|
||||||
|
if ($slashPos === 0) {
|
||||||
|
return substr($identifier, 1);
|
||||||
|
}
|
||||||
|
// Otherwise if there is a "/", identifier is a vendor'ed module
|
||||||
|
elseif ($slashPos !== false) {
|
||||||
|
$parts = explode(':', $identifier, 2);
|
||||||
|
|
||||||
|
list($vendor, $module) = explode('/', $parts[0], 2);
|
||||||
|
$theme = count($parts) > 1 ? $parts[1] : '';
|
||||||
|
|
||||||
|
$path = $module . ($theme ? '/themes/'.$theme : '');
|
||||||
|
|
||||||
|
// Right now we require $module to be a silverstripe module (in root) or theme (in themes dir)
|
||||||
|
// If both exist, we prefer theme
|
||||||
|
if (is_dir(THEMES_PATH . '/' .$path)) {
|
||||||
|
return THEMES_DIR . '/' . $path;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Otherwise it's a (deprecated) old-style "theme" identifier
|
||||||
|
else {
|
||||||
|
return THEMES_DIR.'/'.$identifier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to find possible candidate templates from a set of template
|
||||||
|
* names from modules, current theme directory and finally the application
|
||||||
|
* folder.
|
||||||
|
*
|
||||||
|
* The template names can be passed in as plain strings, or be in the
|
||||||
|
* format "type/name", where type is the type of template to search for
|
||||||
|
* (e.g. Includes, Layout).
|
||||||
|
*
|
||||||
|
* @param string|array $templates
|
||||||
|
* @param string $theme
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function findTemplate($template, $themes = []) {
|
||||||
|
|
||||||
|
if(is_array($template)) {
|
||||||
|
$type = array_key_exists('type', $template) ? $template['type'] : '';
|
||||||
|
$templateList = array_key_exists('templates', $template) ? $template['templates'] : $template;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$type = '';
|
||||||
|
$templateList = array($template);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(count($templateList) == 1 && substr($templateList[0], -3) == '.ss') {
|
||||||
|
return $templateList[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($templateList as $i => $template) {
|
||||||
|
$template = str_replace('\\', '/', $template);
|
||||||
|
$parts = explode('/', $template);
|
||||||
|
|
||||||
|
$tail = array_pop($parts);
|
||||||
|
$head = implode('/', $parts);
|
||||||
|
|
||||||
|
foreach($themes as $themename) {
|
||||||
|
$subthemes = isset($this->sets[$themename]) ? $this->sets[$themename]->getThemes() : [$themename];
|
||||||
|
|
||||||
|
foreach($subthemes as $theme) {
|
||||||
|
$themePath = $this->base . '/' . $this->getPath($theme);
|
||||||
|
|
||||||
|
$path = $themePath . '/templates/' . implode('/', array_filter([$head, $type, $tail])) . '.ss';
|
||||||
|
if (file_exists($path)) return $path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
140
view/ThemeManifest.php
Normal file
140
view/ThemeManifest.php
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\View;
|
||||||
|
|
||||||
|
use \ManifestFileFinder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class which builds a manifest of all themes (which is really just a directory called "templates")
|
||||||
|
*
|
||||||
|
* @package framework
|
||||||
|
* @subpackage manifest
|
||||||
|
*/
|
||||||
|
class ThemeManifest {
|
||||||
|
|
||||||
|
const TEMPLATES_DIR = 'templates';
|
||||||
|
|
||||||
|
protected $base;
|
||||||
|
protected $tests;
|
||||||
|
protected $project;
|
||||||
|
|
||||||
|
protected $cache;
|
||||||
|
protected $cacheKey;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
$cacheClass = defined('SS_MANIFESTCACHE') ? SS_MANIFESTCACHE : 'ManifestCache_File';
|
||||||
|
|
||||||
|
$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
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a map of all themes information. The map is in the following format:
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
* [
|
||||||
|
* 'mysite',
|
||||||
|
* 'framework',
|
||||||
|
* 'framework/admin'
|
||||||
|
* ]
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getThemes() {
|
||||||
|
if ($this->themes === null) $this->init();
|
||||||
|
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,
|
||||||
|
'ignore_tests' => !$this->tests,
|
||||||
|
'dir_callback' => array($this, 'handleDirectory')
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->themes = [];
|
||||||
|
$finder->find($this->base);
|
||||||
|
|
||||||
|
if ($cache) {
|
||||||
|
$this->cache->save($this->themes, $this->cacheKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleDirectory($basename, $pathname, $depth)
|
||||||
|
{
|
||||||
|
if ($basename == self::TEMPLATES_DIR) {
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function init() {
|
||||||
|
if ($data = $this->cache->load($this->cacheKey)) {
|
||||||
|
$this->themes = $data;
|
||||||
|
} else {
|
||||||
|
$this->regenerate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user