mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
BUG Fix issue with Requirements mangling custom scripts (#5337)
API Remove deprecated $template parameter from Requirements::includeInHTML
This commit is contained in:
parent
6ccfbb7c52
commit
0bd62735bc
@ -223,6 +223,10 @@ New methods on `Requirements` are added to access these:
|
||||
* `set_minify_combined_js_files`
|
||||
* `get_force_js_to_bottom`
|
||||
* `get_write_js_to_body`
|
||||
|
||||
Some methods on `Requirements` have had their method signatures changed:
|
||||
|
||||
* `includeInHTML` has had the first parameter $template removed as it was previously deprecated.
|
||||
|
||||
And some methods on `Requirements` and `Requirements_Backend` have been removed as they are obsolete.
|
||||
|
||||
|
@ -33,7 +33,7 @@ class RequirementsTest extends SapphireTest {
|
||||
$backend->css('https://www.mysecuredomain.com/test.css');
|
||||
$backend->css('//scheme-relative.example.com/test.css');
|
||||
|
||||
$html = $backend->includeInHTML(false, self::$html_template);
|
||||
$html = $backend->includeInHTML(self::$html_template);
|
||||
|
||||
$this->assertTrue(
|
||||
(strpos($html, 'http://www.mydomain.com/test.js') !== false),
|
||||
@ -126,7 +126,7 @@ class RequirementsTest extends SapphireTest {
|
||||
$combinedFileName = '/_combinedfiles/RequirementsTest_bc-2a55d56.js';
|
||||
$combinedFilePath = AssetStoreTest_SpyStore::base_path() . $combinedFileName;
|
||||
|
||||
$html = $backend->includeInHTML(false, self::$html_template);
|
||||
$html = $backend->includeInHTML(self::$html_template);
|
||||
|
||||
/* COMBINED JAVASCRIPT FILE IS INCLUDED IN HTML HEADER */
|
||||
$this->assertRegExp(
|
||||
@ -171,7 +171,7 @@ class RequirementsTest extends SapphireTest {
|
||||
/** @var Requirements_Backend $backend */
|
||||
$backend = Injector::inst()->create('Requirements_Backend');
|
||||
$this->setupCombinedNonrequiredRequirements($backend);
|
||||
$html = $backend->includeInHTML(false, self::$html_template);
|
||||
$html = $backend->includeInHTML(self::$html_template);
|
||||
|
||||
/* COMBINED JAVASCRIPT FILE IS INCLUDED IN HTML HEADER */
|
||||
$this->assertRegExp(
|
||||
@ -220,7 +220,7 @@ class RequirementsTest extends SapphireTest {
|
||||
'print'
|
||||
);
|
||||
|
||||
$html = $backend->includeInHTML(false, self::$html_template);
|
||||
$html = $backend->includeInHTML(self::$html_template);
|
||||
|
||||
$this->assertRegExp(
|
||||
'/href=".*\/print\-94e723d\.css/',
|
||||
@ -252,7 +252,7 @@ class RequirementsTest extends SapphireTest {
|
||||
)
|
||||
);
|
||||
|
||||
$html = $backend->includeInHTML(false, self::$html_template);
|
||||
$html = $backend->includeInHTML(self::$html_template);
|
||||
$this->assertRegExp(
|
||||
'/href=".*\/style\-bcd90f5\.css/',
|
||||
$html,
|
||||
@ -272,7 +272,7 @@ class RequirementsTest extends SapphireTest {
|
||||
$backend->block('RequirementsTest_bc.js');
|
||||
|
||||
clearstatcache(); // needed to get accurate file_exists() results
|
||||
$html = $backend->includeInHTML(false, self::$html_template);
|
||||
$html = $backend->includeInHTML(self::$html_template);
|
||||
$this->assertFileNotExists($combinedFilePath);
|
||||
$this->assertNotRegExp(
|
||||
'/src=".*\/RequirementsTest_bc\.js/',
|
||||
@ -287,7 +287,7 @@ class RequirementsTest extends SapphireTest {
|
||||
$combinedFileName2 = '/_combinedfiles/RequirementsTest_bc-3748f67.js'; // SHA1 without file c included
|
||||
$combinedFilePath2 = AssetStoreTest_SpyStore::base_path() . $combinedFileName2;
|
||||
clearstatcache(); // needed to get accurate file_exists() results
|
||||
$html = $backend->includeInHTML(false, self::$html_template);
|
||||
$html = $backend->includeInHTML(self::$html_template);
|
||||
$this->assertFileExists($combinedFilePath2);
|
||||
$this->assertTrue(
|
||||
strpos(file_get_contents($combinedFilePath2), "alert('b')") === false,
|
||||
@ -326,7 +326,7 @@ class RequirementsTest extends SapphireTest {
|
||||
|
||||
$backend->javascript($basePath . '/RequirementsTest_a.js?test=1&test=2&test=3');
|
||||
$backend->css($basePath . '/RequirementsTest_a.css?test=1&test=2&test=3');
|
||||
$html = $backend->includeInHTML(false, self::$html_template);
|
||||
$html = $backend->includeInHTML(self::$html_template);
|
||||
|
||||
/* Javascript has correct path */
|
||||
$this->assertRegExp(
|
||||
@ -424,11 +424,11 @@ class RequirementsTest extends SapphireTest {
|
||||
$template = '<html><head></head><body><header>My header</header><p>Body</p></body></html>';
|
||||
|
||||
$backend->setWriteJavascriptToBody(false);
|
||||
$html = $backend->includeInHTML(false, $template);
|
||||
$html = $backend->includeInHTML($template);
|
||||
$this->assertContains('<head><script', $html);
|
||||
|
||||
$backend->setWriteJavascriptToBody(true);
|
||||
$html = $backend->includeInHTML(false, $template);
|
||||
$html = $backend->includeInHTML($template);
|
||||
$this->assertNotContains('<head><script', $html);
|
||||
$this->assertContains('</script></body>', $html);
|
||||
}
|
||||
@ -439,7 +439,7 @@ class RequirementsTest extends SapphireTest {
|
||||
$backend = Injector::inst()->create('Requirements_Backend');
|
||||
$this->setupRequirements($backend);
|
||||
$backend->javascript($this->getCurrentRelativePath() . '/RequirementsTest_a.js');
|
||||
$html = $backend->includeInHTML(false, $template);
|
||||
$html = $backend->includeInHTML($template);
|
||||
//wiping out commented-out html
|
||||
$html = preg_replace('/<!--(.*)-->/Uis', '', $html);
|
||||
$this->assertContains("RequirementsTest_a.js", $html);
|
||||
@ -455,7 +455,7 @@ class RequirementsTest extends SapphireTest {
|
||||
$src = $this->getCurrentRelativePath() . '/RequirementsTest_a.js';
|
||||
$urlSrc = ControllerTest_ContainerController::join_links(Director::baseURL(), $src);
|
||||
$backend->javascript($src);
|
||||
$html = $backend->includeInHTML(false, $template);
|
||||
$html = $backend->includeInHTML($template);
|
||||
$this->assertEquals('<html><head></head><body><!--<script>alert("commented out");</script>-->'
|
||||
. '<h1>more content</h1><script type="application/javascript" src="' . $urlSrc . '"></script></body></html>', $html);
|
||||
}
|
||||
@ -465,24 +465,32 @@ class RequirementsTest extends SapphireTest {
|
||||
$backend = Injector::inst()->create('Requirements_Backend');
|
||||
$this->setupRequirements($backend);
|
||||
$backend->javascript('http://www.mydomain.com/test.js');
|
||||
$backend->customScript(
|
||||
<<<'EOS'
|
||||
var globalvar = {
|
||||
pattern: '\\$custom\\1'
|
||||
};
|
||||
EOS
|
||||
);
|
||||
|
||||
// Test matching with HTML5 <header> tags as well
|
||||
$template = '<html><head></head><body><header>My header</header><p>Body<script></script></p></body></html>';
|
||||
|
||||
// The expected outputs
|
||||
$JsInHead = "<html><head><script type=\"application/javascript\" src=\"http://www.mydomain.com/test.js\">"
|
||||
. "</script>\n</head><body><header>My header</header><p>Body<script></script></p></body></html>";
|
||||
$JsInBody = "<html><head></head><body><header>My header</header><p>Body<script type=\"application/javascript\""
|
||||
. " src=\"http://www.mydomain.com/test.js\"></script><script></script></p></body></html>";
|
||||
$JsAtEnd = "<html><head></head><body><header>My header</header><p>Body<script></script></p><script "
|
||||
. "type=\"application/javascript\" src=\"http://www.mydomain.com/test.js\"></script></body></html>";
|
||||
$expectedScripts = "<script type=\"application/javascript\" src=\"http://www.mydomain.com/test.js\">"
|
||||
. "</script><script type=\"application/javascript\">//<![CDATA[\n"
|
||||
. "var globalvar = {\n\tpattern: '\\\\\$custom\\\\1'\n};\n"
|
||||
. "//]]></script>";
|
||||
$JsInHead = "<html><head>$expectedScripts</head><body><header>My header</header><p>Body<script></script></p></body></html>";
|
||||
$JsInBody = "<html><head></head><body><header>My header</header><p>Body$expectedScripts<script></script></p></body></html>";
|
||||
$JsAtEnd = "<html><head></head><body><header>My header</header><p>Body<script></script></p>$expectedScripts</body></html>";
|
||||
|
||||
|
||||
// Test if the script is before the head tag, not before the body.
|
||||
// Expected: $JsInHead
|
||||
$backend->setWriteJavascriptToBody(false);
|
||||
$backend->setForceJSToBottom(false);
|
||||
$html = $backend->includeInHTML(false, $template);
|
||||
$html = $backend->includeInHTML($template);
|
||||
$this->assertNotEquals($JsInBody, $html);
|
||||
$this->assertNotEquals($JsAtEnd, $html);
|
||||
$this->assertEquals($JsInHead, $html);
|
||||
@ -491,7 +499,7 @@ class RequirementsTest extends SapphireTest {
|
||||
// Expected: $JsInBody
|
||||
$backend->setWriteJavascriptToBody(true);
|
||||
$backend->setForceJSToBottom(false);
|
||||
$html = $backend->includeInHTML(false, $template);
|
||||
$html = $backend->includeInHTML($template);
|
||||
$this->assertNotEquals($JsAtEnd, $html);
|
||||
$this->assertEquals($JsInBody, $html);
|
||||
|
||||
@ -499,7 +507,7 @@ class RequirementsTest extends SapphireTest {
|
||||
// Expected: $JsAtEnd
|
||||
$backend->setWriteJavascriptToBody(false);
|
||||
$backend->setForceJSToBottom(true);
|
||||
$html = $backend->includeInHTML(false, $template);
|
||||
$html = $backend->includeInHTML($template);
|
||||
$this->assertNotEquals($JsInHead, $html);
|
||||
$this->assertNotEquals($JsInBody, $html);
|
||||
$this->assertEquals($JsAtEnd, $html);
|
||||
@ -508,7 +516,7 @@ class RequirementsTest extends SapphireTest {
|
||||
// Expected: $JsAtEnd
|
||||
$backend->setWriteJavascriptToBody(true);
|
||||
$backend->setForceJSToBottom(true);
|
||||
$html = $backend->includeInHTML(false, $template);
|
||||
$html = $backend->includeInHTML($template);
|
||||
$this->assertNotEquals($JsInHead, $html);
|
||||
$this->assertNotEquals($JsInBody, $html);
|
||||
$this->assertEquals($JsAtEnd, $html);
|
||||
@ -528,14 +536,14 @@ class RequirementsTest extends SapphireTest {
|
||||
$backend->css($basePath .'/RequirementsTest_b.css?foo=bar&bla=blubb');
|
||||
|
||||
$backend->setSuffixRequirements(true);
|
||||
$html = $backend->includeInHTML(false, $template);
|
||||
$html = $backend->includeInHTML($template);
|
||||
$this->assertRegexp('/RequirementsTest_a\.js\?m=[\d]*"/', $html);
|
||||
$this->assertRegexp('/RequirementsTest_b\.js\?m=[\d]*&foo=bar&bla=blubb"/', $html);
|
||||
$this->assertRegexp('/RequirementsTest_a\.css\?m=[\d]*"/', $html);
|
||||
$this->assertRegexp('/RequirementsTest_b\.css\?m=[\d]*&foo=bar&bla=blubb"/', $html);
|
||||
|
||||
$backend->setSuffixRequirements(false);
|
||||
$html = $backend->includeInHTML(false, $template);
|
||||
$html = $backend->includeInHTML($template);
|
||||
$this->assertNotContains('RequirementsTest_a.js=', $html);
|
||||
$this->assertNotRegexp('/RequirementsTest_a\.js\?m=[\d]*"/', $html);
|
||||
$this->assertNotRegexp('/RequirementsTest_b\.js\?m=[\d]*&foo=bar&bla=blubb"/', $html);
|
||||
@ -567,7 +575,7 @@ class RequirementsTest extends SapphireTest {
|
||||
$this->assertEquals([
|
||||
$basePath . '/RequirementsTest_c.js' => $basePath . '/RequirementsTest_c.js'
|
||||
], $backend->getProvidedScripts());
|
||||
$html = $backend->includeInHTML(false, $template);
|
||||
$html = $backend->includeInHTML($template);
|
||||
$this->assertRegExp('/src=".*\/RequirementsTest_a\.js/', $html);
|
||||
$this->assertRegExp('/src=".*\/RequirementsTest_b\.js/', $html);
|
||||
$this->assertNotRegExp('/src=".*\/RequirementsTest_c\.js/', $html);
|
||||
@ -586,7 +594,7 @@ class RequirementsTest extends SapphireTest {
|
||||
$this->assertEquals([
|
||||
$basePath . '/RequirementsTest_c.js' => $basePath . '/RequirementsTest_c.js'
|
||||
], $backend->getProvidedScripts());
|
||||
$html = $backend->includeInHTML(false, $template);
|
||||
$html = $backend->includeInHTML($template);
|
||||
$this->assertRegExp('/src=".*\/combined_a/', $html);
|
||||
$this->assertRegExp('/src=".*\/RequirementsTest_b\.js/', $html);
|
||||
$this->assertNotRegExp('/src=".*\/combined_c/', $html);
|
||||
|
@ -33,7 +33,7 @@ class Requirements implements Flushable {
|
||||
|
||||
/**
|
||||
* Enable combining of css/javascript files.
|
||||
*
|
||||
*
|
||||
* @param bool $enable
|
||||
*/
|
||||
public static function set_combined_files_enabled($enable) {
|
||||
@ -42,7 +42,7 @@ class Requirements implements Flushable {
|
||||
|
||||
/**
|
||||
* Checks whether combining of css/javascript files is enabled.
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function get_combined_files_enabled() {
|
||||
@ -51,7 +51,7 @@ class Requirements implements Flushable {
|
||||
|
||||
/**
|
||||
* Set the relative folder e.g. 'assets' for where to store combined files
|
||||
*
|
||||
*
|
||||
* @param string $folder Path to folder
|
||||
*/
|
||||
public static function set_combined_files_folder($folder) {
|
||||
@ -59,7 +59,7 @@ class Requirements implements Flushable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to add caching query params to the requests for file-based requirements.
|
||||
* Set whether to add caching query params to the requests for file-based requirements.
|
||||
* Eg: themes/myTheme/js/main.js?m=123456789. The parameter is a timestamp generated by
|
||||
* filemtime. This has the benefit of allowing the browser to cache the URL infinitely,
|
||||
* while automatically busting this cache every time the file is changed.
|
||||
@ -80,8 +80,8 @@ class Requirements implements Flushable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Instance of the requirements for storage. You can create your own backend to change the
|
||||
* default JS and CSS inclusion behaviour.
|
||||
* Instance of the requirements for storage. You can create your own backend to change the
|
||||
* default JS and CSS inclusion behaviour.
|
||||
*
|
||||
* @var Requirements_Backend
|
||||
*/
|
||||
@ -108,7 +108,7 @@ class Requirements implements Flushable {
|
||||
|
||||
/**
|
||||
* Register the given JavaScript file as required.
|
||||
*
|
||||
*
|
||||
* @param string $file Relative to docroot
|
||||
* @param array $options List of options. Available options include:
|
||||
* - 'provides' : List of scripts files included in this file
|
||||
@ -137,7 +137,7 @@ class Requirements implements Flushable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given CSS styles into the list of requirements
|
||||
* Register the given CSS styles into the list of requirements
|
||||
*
|
||||
* @param string $script CSS selectors as a string (without enclosing <style> tag)
|
||||
* @param string|int $uniquenessID A unique ID that ensures a piece of code is only added once
|
||||
@ -172,7 +172,7 @@ class Requirements implements Flushable {
|
||||
* Register the given stylesheet into the list of requirements.
|
||||
*
|
||||
* @param string $file The CSS file to load, relative to site root
|
||||
* @param string $media Comma-separated list of media types to use in the link tag
|
||||
* @param string $media Comma-separated list of media types to use in the link tag
|
||||
* (e.g. 'screen,projector')
|
||||
*/
|
||||
public static function css($file, $media = null) {
|
||||
@ -183,23 +183,23 @@ class Requirements implements Flushable {
|
||||
* Registers the given themeable stylesheet as required.
|
||||
*
|
||||
* A CSS file in the current theme path name 'themename/css/$name.css' is first searched for,
|
||||
* and it that doesn't exist and the module parameter is set then a CSS file with that name in
|
||||
* and it that doesn't exist and the module parameter is set then a CSS file with that name in
|
||||
* the module is used.
|
||||
*
|
||||
* @param string $name The name of the file - eg '/css/File.css' would have the name 'File'
|
||||
* @param string $module The module to fall back to if the css file does not exist in the
|
||||
* @param string $module The module to fall back to if the css file does not exist in the
|
||||
* current theme.
|
||||
* @param string $media Comma-separated list of media types to use in the link tag
|
||||
* @param string $media Comma-separated list of media types to use in the link tag
|
||||
* (e.g. 'screen,projector')
|
||||
*/
|
||||
public static function themedCSS($name, $module = null, $media = null) {
|
||||
return self::backend()->themedCSS($name, $module, $media);
|
||||
self::backend()->themedCSS($name, $module, $media);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear either a single or all requirements
|
||||
*
|
||||
* Caution: Clearing single rules added via customCSS and customScript only works if you
|
||||
*
|
||||
* Caution: Clearing single rules added via customCSS and customScript only works if you
|
||||
* originally specified a $uniquenessID.
|
||||
*
|
||||
* @param string|int $fileOrID
|
||||
@ -216,11 +216,11 @@ class Requirements implements Flushable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Block inclusion of a specific file
|
||||
*
|
||||
* The difference between this and {@link clear} is that the calling order does not matter;
|
||||
* Block inclusion of a specific file
|
||||
*
|
||||
* The difference between this and {@link clear} is that the calling order does not matter;
|
||||
* {@link clear} must be called after the initial registration, whereas {@link block} can be
|
||||
* used in advance. This is useful, for example, to block scripts included by a superclass
|
||||
* used in advance. This is useful, for example, to block scripts included by a superclass
|
||||
* without having to override entire functions and duplicate a lot of code.
|
||||
*
|
||||
* Note that blocking should be used sparingly because it's hard to trace where an file is
|
||||
@ -231,7 +231,7 @@ class Requirements implements Flushable {
|
||||
public static function block($fileOrID) {
|
||||
self::backend()->block($fileOrID);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove an item from the block list
|
||||
*
|
||||
@ -253,36 +253,43 @@ class Requirements implements Flushable {
|
||||
* requirements. Needs to receive a valid HTML/XHTML template in the $content parameter,
|
||||
* including a head and body tag.
|
||||
*
|
||||
* @param string $templateFile No longer used, only retained for compatibility
|
||||
* @param string $content HTML content that has already been parsed from the $templateFile
|
||||
* through {@link SSViewer}
|
||||
* @return string HTML content augmented with the requirements tags
|
||||
*/
|
||||
public static function includeInHTML($templateFile, $content) {
|
||||
return self::backend()->includeInHTML($templateFile, $content);
|
||||
public static function includeInHTML($content) {
|
||||
if(func_num_args() > 1) {
|
||||
Deprecation::notice(
|
||||
'5.0',
|
||||
'$templateFile argument is deprecated. includeInHTML takes a sole $content parameter now.'
|
||||
);
|
||||
$content = func_get_arg(1);
|
||||
}
|
||||
|
||||
return self::backend()->includeInHTML($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach requirements inclusion to X-Include-JS and X-Include-CSS headers on the given
|
||||
* Attach requirements inclusion to X-Include-JS and X-Include-CSS headers on the given
|
||||
* HTTP Response
|
||||
*
|
||||
*
|
||||
* @param SS_HTTPResponse $response
|
||||
*/
|
||||
public static function include_in_response(SS_HTTPResponse $response) {
|
||||
return self::backend()->includeInResponse($response);
|
||||
self::backend()->includeInResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add i18n files from the given javascript directory. SilverStripe expects that the given
|
||||
* directory will contain a number of JavaScript files named by language: en_US.js, de_DE.js,
|
||||
* Add i18n files from the given javascript directory. SilverStripe expects that the given
|
||||
* directory will contain a number of JavaScript files named by language: en_US.js, de_DE.js,
|
||||
* etc.
|
||||
*
|
||||
* @param string $langDir The JavaScript lang directory, relative to the site root, e.g.,
|
||||
* @param string $langDir The JavaScript lang directory, relative to the site root, e.g.,
|
||||
* 'framework/javascript/lang'
|
||||
* @param bool $return Return all relative file paths rather than including them in
|
||||
* @param bool $return Return all relative file paths rather than including them in
|
||||
* requirements
|
||||
* @param bool $langOnly Only include language files, not the base libraries
|
||||
*
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function add_i18n_javascript($langDir, $return = false, $langOnly = false) {
|
||||
@ -339,7 +346,7 @@ class Requirements implements Flushable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all combined files; keys are the combined file names, values are lists of
|
||||
* Return all combined files; keys are the combined file names, values are lists of
|
||||
* associative arrays with 'files', 'type', and 'media' keys for details about this
|
||||
* combined file.
|
||||
*
|
||||
@ -354,7 +361,7 @@ class Requirements implements Flushable {
|
||||
* but doesn't delete the directory itself
|
||||
*/
|
||||
public static function delete_all_combined_files() {
|
||||
return self::backend()->deleteAllCombinedFiles();
|
||||
self::backend()->deleteAllCombinedFiles();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -368,7 +375,7 @@ class Requirements implements Flushable {
|
||||
* Do the heavy lifting involved in combining the combined files.
|
||||
*/
|
||||
public static function process_combined_files() {
|
||||
return self::backend()->processCombinedFiles();
|
||||
self::backend()->processCombinedFiles();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -452,7 +459,7 @@ class Requirements implements Flushable {
|
||||
* Output debugging information
|
||||
*/
|
||||
public static function debug() {
|
||||
return self::backend()->debug();
|
||||
self::backend()->debug();
|
||||
}
|
||||
|
||||
}
|
||||
@ -465,11 +472,11 @@ class Requirements_Backend
|
||||
{
|
||||
|
||||
/**
|
||||
* Whether to add caching query params to the requests for file-based requirements.
|
||||
* Whether to add caching query params to the requests for file-based requirements.
|
||||
* Eg: themes/myTheme/js/main.js?m=123456789. The parameter is a timestamp generated by
|
||||
* filemtime. This has the benefit of allowing the browser to cache the URL infinitely,
|
||||
* while automatically busting this cache every time the file is changed.
|
||||
*
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $suffixRequirements = true;
|
||||
@ -529,7 +536,7 @@ class Requirements_Backend
|
||||
protected $customCSS = array();
|
||||
|
||||
/**
|
||||
* All custom HTML markup which is added before the closing <head> tag, e.g. additional
|
||||
* All custom HTML markup which is added before the closing <head> tag, e.g. additional
|
||||
* metatags.
|
||||
*
|
||||
* @var array
|
||||
@ -537,7 +544,7 @@ class Requirements_Backend
|
||||
protected $customHeadTags = array();
|
||||
|
||||
/**
|
||||
* Remembers the file paths or uniquenessIDs of all Requirements cleared through
|
||||
* Remembers the file paths or uniquenessIDs of all Requirements cleared through
|
||||
* {@link clear()}, so that they can be restored later.
|
||||
*
|
||||
* @var array
|
||||
@ -546,10 +553,10 @@ class Requirements_Backend
|
||||
|
||||
/**
|
||||
* The file paths (relative to docroot) or uniquenessIDs of any included requirements which
|
||||
* should be blocked when executing {@link inlcudeInHTML()}. This is useful, for example,
|
||||
* should be blocked when executing {@link inlcudeInHTML()}. This is useful, for example,
|
||||
* to block scripts included by a superclass without having to override entire functions and
|
||||
* duplicate a lot of code.
|
||||
*
|
||||
*
|
||||
* Use {@link unblock()} or {@link unblock_all()} to revert changes.
|
||||
*
|
||||
* @var array
|
||||
@ -577,21 +584,21 @@ class Requirements_Backend
|
||||
* @var boolean
|
||||
*/
|
||||
protected $writeHeaderComment = true;
|
||||
|
||||
|
||||
/**
|
||||
* Where to save combined files. By default they're placed in assets/_combinedfiles, however
|
||||
* this may be an issue depending on your setup, especially for CSS files which often contain
|
||||
* relative paths.
|
||||
*
|
||||
* relative paths.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $combinedFilesFolder = null;
|
||||
|
||||
/**
|
||||
* Put all JavaScript includes at the bottom of the template before the closing <body> tag,
|
||||
* rather than the default behaviour of placing them at the end of the <head> tag. This means
|
||||
* rather than the default behaviour of placing them at the end of the <head> tag. This means
|
||||
* script downloads won't block other HTTP requests, which can be a performance improvement.
|
||||
*
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $writeJavascriptToBody = true;
|
||||
@ -655,7 +662,7 @@ class Requirements_Backend
|
||||
|
||||
/**
|
||||
* Enable or disable the combination of CSS and JavaScript files
|
||||
*
|
||||
*
|
||||
* @param bool $enable
|
||||
*/
|
||||
public function setCombinedFilesEnabled($enable) {
|
||||
@ -664,7 +671,7 @@ class Requirements_Backend
|
||||
|
||||
/**
|
||||
* Check if header comments are written
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getWriteHeaderComment() {
|
||||
@ -684,9 +691,9 @@ class Requirements_Backend
|
||||
|
||||
/**
|
||||
* Set the folder to save combined files in. By default they're placed in _combinedfiles,
|
||||
* however this may be an issue depending on your setup, especially for CSS files which often
|
||||
* however this may be an issue depending on your setup, especially for CSS files which often
|
||||
* contain relative paths.
|
||||
*
|
||||
*
|
||||
* This must not include any 'assets' prefix
|
||||
*
|
||||
* @param string $folder
|
||||
@ -769,7 +776,7 @@ class Requirements_Backend
|
||||
public function getForceJSToBottom() {
|
||||
return $this->forceJSToBottom;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if minify js files should be combined
|
||||
*
|
||||
@ -890,8 +897,6 @@ class Requirements_Backend
|
||||
} else {
|
||||
$this->customScript[] = $script;
|
||||
}
|
||||
|
||||
$script .= "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -901,7 +906,7 @@ class Requirements_Backend
|
||||
*/
|
||||
public function getCustomScripts() {
|
||||
return array_diff_key($this->customScript, $this->blocked);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given CSS styles into the list of requirements
|
||||
@ -995,7 +1000,7 @@ class Requirements_Backend
|
||||
|
||||
/**
|
||||
* Get the list of registered CSS file requirements, excluding blocked files
|
||||
*
|
||||
*
|
||||
* @return array Associative array of file to spec
|
||||
*/
|
||||
public function getCSS() {
|
||||
@ -1100,99 +1105,157 @@ class Requirements_Backend
|
||||
* requirements. Needs to receive a valid HTML/XHTML template in the $content parameter,
|
||||
* including a head and body tag.
|
||||
*
|
||||
* @param string $templateFile No longer used, only retained for compatibility
|
||||
* @param string $content HTML content that has already been parsed from the $templateFile
|
||||
* through {@link SSViewer}
|
||||
* @return string HTML content augmented with the requirements tags
|
||||
*/
|
||||
public function includeInHTML($templateFile, $content) {
|
||||
public function includeInHTML($content) {
|
||||
if(func_num_args() > 1) {
|
||||
Deprecation::notice(
|
||||
'5.0',
|
||||
'$templateFile argument is deprecated. includeInHTML takes a sole $content parameter now.'
|
||||
);
|
||||
$content = func_get_arg(1);
|
||||
}
|
||||
|
||||
// Skip if content isn't injectable, or there is nothing to inject
|
||||
$tagsAvailable = preg_match('#</head\b#', $content);
|
||||
$hasFiles = $this->css || $this->javascript || $this->customCSS || $this->customScript || $this->customHeadTags;
|
||||
if(!$tagsAvailable || !$hasFiles) {
|
||||
return $content;
|
||||
}
|
||||
$requirements = '';
|
||||
$jsRequirements = '';
|
||||
$requirements = '';
|
||||
$jsRequirements = '';
|
||||
|
||||
// Combine files - updates $this->javascript and $this->css
|
||||
// Combine files - updates $this->javascript and $this->css
|
||||
$this->processCombinedFiles();
|
||||
|
||||
foreach($this->getJavascript() as $file) {
|
||||
$path = Convert::raw2xml($this->pathForFile($file));
|
||||
if($path) {
|
||||
$jsRequirements .= "<script type=\"application/javascript\" src=\"$path\"></script>\n";
|
||||
}
|
||||
$path = Convert::raw2att($this->pathForFile($file));
|
||||
if($path) {
|
||||
$jsRequirements .= "<script type=\"application/javascript\" src=\"$path\"></script>";
|
||||
}
|
||||
}
|
||||
|
||||
// Add all inline JavaScript *after* including external files they might rely on
|
||||
// Add all inline JavaScript *after* including external files they might rely on
|
||||
foreach($this->getCustomScripts() as $script) {
|
||||
$jsRequirements .= "<script type=\"application/javascript\">\n//<![CDATA[\n";
|
||||
$jsRequirements .= "$script\n";
|
||||
$jsRequirements .= "\n//]]>\n</script>\n";
|
||||
}
|
||||
$jsRequirements .= "<script type=\"application/javascript\">//<![CDATA[\n";
|
||||
$jsRequirements .= "$script\n";
|
||||
$jsRequirements .= "//]]></script>";
|
||||
}
|
||||
|
||||
foreach($this->getCSS() as $file => $params) {
|
||||
$path = Convert::raw2xml($this->pathForFile($file));
|
||||
if($path) {
|
||||
$media = (isset($params['media']) && !empty($params['media']))
|
||||
? " media=\"{$params['media']}\"" : "";
|
||||
$requirements .= "<link rel=\"stylesheet\" type=\"text/css\"{$media} href=\"$path\" />\n";
|
||||
}
|
||||
$path = Convert::raw2att($this->pathForFile($file));
|
||||
if($path) {
|
||||
$media = (isset($params['media']) && !empty($params['media']))
|
||||
? " media=\"{$params['media']}\"" : "";
|
||||
$requirements .= "<link rel=\"stylesheet\" type=\"text/css\" {$media} href=\"$path\" />\n";
|
||||
}
|
||||
}
|
||||
|
||||
foreach($this->getCustomCSS() as $css) {
|
||||
$requirements .= "<style type=\"text/css\">\n$css\n</style>\n";
|
||||
}
|
||||
$requirements .= "<style type=\"text/css\">\n$css\n</style>\n";
|
||||
}
|
||||
|
||||
foreach($this->getCustomHeadTags() as $customHeadTag) {
|
||||
$requirements .= "$customHeadTag\n";
|
||||
}
|
||||
$requirements .= "$customHeadTag\n";
|
||||
}
|
||||
|
||||
// Inject CSS into body
|
||||
$content = $this->insertTagsIntoHead($requirements, $content);
|
||||
|
||||
// Inject scripts
|
||||
if ($this->getForceJSToBottom()) {
|
||||
// Remove all newlines from code to preserve layout
|
||||
$jsRequirements = preg_replace('/>\n*/', '>', $jsRequirements);
|
||||
|
||||
// Forcefully put the scripts at the bottom of the body instead of before the first
|
||||
// script tag.
|
||||
$content = preg_replace("/(<\/body[^>]*>)/i", $jsRequirements . "\\1", $content);
|
||||
|
||||
// Put CSS at the bottom of the head
|
||||
$content = preg_replace("/(<\/head>)/i", $requirements . "\\1", $content);
|
||||
$content = $this->insertScriptsAtBottom($jsRequirements, $content);
|
||||
} elseif($this->getWriteJavascriptToBody()) {
|
||||
// Remove all newlines from code to preserve layout
|
||||
$jsRequirements = preg_replace('/>\n*/', '>', $jsRequirements);
|
||||
|
||||
// If your template already has script tags in the body, then we try to put our script
|
||||
// tags just before those. Otherwise, we put it at the bottom.
|
||||
$p2 = stripos($content, '<body');
|
||||
$p1 = stripos($content, '<script', $p2);
|
||||
|
||||
$commentTags = array();
|
||||
$canWriteToBody = ($p1 !== false)
|
||||
&&
|
||||
// Check that the script tag is not inside a html comment tag
|
||||
!(
|
||||
preg_match('/.*(?|(<!--)|(-->))/U', $content, $commentTags, 0, $p1)
|
||||
&&
|
||||
$commentTags[1] == '-->'
|
||||
);
|
||||
|
||||
if($canWriteToBody) {
|
||||
$content = substr($content,0,$p1) . $jsRequirements . substr($content,$p1);
|
||||
} else {
|
||||
$content = preg_replace("/(<\/body[^>]*>)/i", $jsRequirements . "\\1", $content);
|
||||
}
|
||||
|
||||
// Put CSS at the bottom of the head
|
||||
$content = preg_replace("/(<\/head>)/i", $requirements . "\\1", $content);
|
||||
} else {
|
||||
$content = preg_replace("/(<\/head>)/i", $requirements . "\\1", $content);
|
||||
$content = preg_replace("/(<\/head>)/i", $jsRequirements . "\\1", $content);
|
||||
}
|
||||
$content = $this->insertScriptsIntoBody($jsRequirements, $content);
|
||||
} else {
|
||||
$content = $this->insertTagsIntoHead($jsRequirements, $content);
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a block of HTML, insert the given scripts at the bottom before
|
||||
* the closing </body> tag
|
||||
*
|
||||
* @param string $jsRequirements String containing one or more javascript <script /> tags
|
||||
* @param string $content HTML body
|
||||
* @return string Merged HTML
|
||||
*/
|
||||
protected function insertScriptsAtBottom($jsRequirements, $content) {
|
||||
// Forcefully put the scripts at the bottom of the body instead of before the first
|
||||
// script tag.
|
||||
$content = preg_replace(
|
||||
'/(<\/body[^>]*>)/i',
|
||||
$this->escapeReplacement($jsRequirements) . '\\1',
|
||||
$content
|
||||
);
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a block of HTML, insert the given scripts inside the <body></body>
|
||||
*
|
||||
* @param string $jsRequirements String containing one or more javascript <script /> tags
|
||||
* @param string $content HTML body
|
||||
* @return string Merged HTML
|
||||
*/
|
||||
protected function insertScriptsIntoBody($jsRequirements, $content) {
|
||||
// If your template already has script tags in the body, then we try to put our script
|
||||
// tags just before those. Otherwise, we put it at the bottom.
|
||||
$bodyTagPosition = stripos($content, '<body');
|
||||
$scriptTagPosition = stripos($content, '<script', $bodyTagPosition);
|
||||
|
||||
$commentTags = array();
|
||||
$canWriteToBody = ($scriptTagPosition !== false)
|
||||
&&
|
||||
// Check that the script tag is not inside a html comment tag
|
||||
!(
|
||||
preg_match('/.*(?|(<!--)|(-->))/U', $content, $commentTags, 0, $scriptTagPosition)
|
||||
&&
|
||||
$commentTags[1] == '-->'
|
||||
);
|
||||
|
||||
if($canWriteToBody) {
|
||||
// Insert content before existing script tags
|
||||
$content = substr($content, 0, $scriptTagPosition)
|
||||
. $jsRequirements
|
||||
. substr($content, $scriptTagPosition);
|
||||
} else {
|
||||
// Insert content at bottom of page otherwise
|
||||
$content = $this->insertScriptsAtBottom($jsRequirements, $content);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a block of HTML, insert the given code inside the <head></head> block
|
||||
*
|
||||
* @param string $jsRequirements String containing one or more html tags
|
||||
* @param string $content HTML body
|
||||
* @return string Merged HTML
|
||||
*/
|
||||
protected function insertTagsIntoHead($jsRequirements, $content) {
|
||||
$content = preg_replace(
|
||||
'/(<\/head>)/i',
|
||||
$this->escapeReplacement($jsRequirements) . '\\1',
|
||||
$content
|
||||
);
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely escape a literal string for use in preg_replace replacement
|
||||
*
|
||||
* @param string $replacement
|
||||
* @return string
|
||||
*/
|
||||
protected function escapeReplacement($replacement) {
|
||||
return addcslashes($replacement, '\\$');
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach requirements inclusion to X-Include-JS and X-Include-CSS headers on the given
|
||||
* HTTP Response
|
||||
@ -1212,7 +1275,7 @@ class Requirements_Backend
|
||||
}
|
||||
|
||||
if(count($jsRequirements)) {
|
||||
$response->addHeader('X-Include-JS', implode(',', $jsRequirements));
|
||||
$response->addHeader('X-Include-JS', implode(',', $jsRequirements));
|
||||
}
|
||||
|
||||
foreach($this->getCSS() as $file => $params) {
|
||||
@ -1224,8 +1287,8 @@ class Requirements_Backend
|
||||
}
|
||||
|
||||
if(count($cssRequirements)) {
|
||||
$response->addHeader('X-Include-CSS', implode(',', $cssRequirements));
|
||||
}
|
||||
$response->addHeader('X-Include-CSS', implode(',', $cssRequirements));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1239,7 +1302,7 @@ class Requirements_Backend
|
||||
* requirements
|
||||
* @param bool $langOnly Only include language files, not the base libraries
|
||||
*
|
||||
* @return array
|
||||
* @return array|null All relative files if $return is true, or null otherwise
|
||||
*/
|
||||
public function add_i18n_javascript($langDir, $return = false, $langOnly = false) {
|
||||
$files = array();
|
||||
@ -1255,8 +1318,8 @@ class Requirements_Backend
|
||||
$candidates = array(
|
||||
'en.js',
|
||||
'en_US.js',
|
||||
i18n::get_lang_from_locale(i18n::default_locale()) . '.js',
|
||||
i18n::default_locale() . '.js',
|
||||
i18n::get_lang_from_locale(i18n::config()->default_locale) . '.js',
|
||||
i18n::config()->default_locale . '.js',
|
||||
i18n::get_lang_from_locale(i18n::get_locale()) . '.js',
|
||||
i18n::get_locale() . '.js',
|
||||
);
|
||||
@ -1278,6 +1341,7 @@ class Requirements_Backend
|
||||
foreach($files as $file) {
|
||||
$this->javascript($file);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1316,17 +1380,17 @@ class Requirements_Backend
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate several css or javascript files into a single dynamically generated file. This
|
||||
* Concatenate several css or javascript files into a single dynamically generated file. This
|
||||
* increases performance by fewer HTTP requests.
|
||||
*
|
||||
* The combined file is regenerated based on every file modification time. Optionally a
|
||||
* The combined file is regenerated based on every file modification time. Optionally a
|
||||
* rebuild can be triggered by appending ?flush=1 to the URL.
|
||||
*
|
||||
* All combined files will have a comment on the start of each concatenated file denoting their
|
||||
* All combined files will have a comment on the start of each concatenated file denoting their
|
||||
* original position.
|
||||
*
|
||||
* CAUTION: You're responsible for ensuring that the load order for combined files is
|
||||
* retained - otherwise combining JavaScript files can lead to functional errors in the
|
||||
* CAUTION: You're responsible for ensuring that the load order for combined files is
|
||||
* retained - otherwise combining JavaScript files can lead to functional errors in the
|
||||
* JavaScript logic, and combining CSS can lead to incorrect inheritance. You can also
|
||||
* only include each file once across all includes and combinations in a single page load.
|
||||
*
|
||||
@ -1456,11 +1520,11 @@ class Requirements_Backend
|
||||
/**
|
||||
* Includes all combined files, including blocked ones
|
||||
*
|
||||
* @return type
|
||||
* @return array
|
||||
*/
|
||||
protected function getAllCombinedFiles() {
|
||||
return $this->combinedFiles;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all combined files
|
||||
@ -1507,7 +1571,7 @@ class Requirements_Backend
|
||||
$providedScripts
|
||||
);
|
||||
$combinedURL = $this->getCombinedFileURL($combinedFile, $filteredFileList, $type);
|
||||
}
|
||||
}
|
||||
|
||||
// Replace all existing files, injecting the combined file at the position of the first item
|
||||
// in order to preserve inclusion order.
|
||||
@ -1523,12 +1587,12 @@ class Requirements_Backend
|
||||
} elseif(!$included && $combinedURL) {
|
||||
$newCSS[$combinedURL] = array('media' => $media);
|
||||
$included = true;
|
||||
}
|
||||
}
|
||||
// If already included, or otherwise blocked, then don't add into CSS
|
||||
}
|
||||
}
|
||||
$this->css = $newCSS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
case 'js': {
|
||||
// Assoc array of file => true
|
||||
$newJS = array();
|
||||
@ -1538,12 +1602,12 @@ class Requirements_Backend
|
||||
} elseif(!$included && $combinedURL) {
|
||||
$newJS[$combinedURL] = true;
|
||||
$included = true;
|
||||
}
|
||||
}
|
||||
// If already included, or otherwise blocked, then don't add into scripts
|
||||
}
|
||||
}
|
||||
$this->javascript = $newJS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1606,13 +1670,13 @@ class Requirements_Backend
|
||||
}
|
||||
|
||||
return $combinedURL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a filename and list of files, generate a new filename unique to these files
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $files
|
||||
* @param string $combinedFile
|
||||
* @param array $fileList
|
||||
* @return string
|
||||
*/
|
||||
protected function hashedCombinedFilename($combinedFile, $fileList) {
|
||||
@ -1620,7 +1684,7 @@ class Requirements_Backend
|
||||
$hash = $this->hashOfFiles($fileList);
|
||||
$extension = File::get_file_extension($combinedFile);
|
||||
return $name . '-' . substr($hash, 0, 7) . '.' . $extension;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if combined files are enabled
|
||||
@ -1630,17 +1694,17 @@ class Requirements_Backend
|
||||
public function getCombinedFilesEnabled() {
|
||||
if(!$this->combinedFilesEnabled) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Tests should be combined
|
||||
if(class_exists('SapphireTest', false) && SapphireTest::is_running_test()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Check if specified via querystring
|
||||
if(isset($_REQUEST['combine'])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Non-dev sites are always combined
|
||||
if(!Director::isDev()) {
|
||||
@ -1654,7 +1718,7 @@ class Requirements_Backend
|
||||
/**
|
||||
* For a given filelist, determine some discriminating value to determine if
|
||||
* any of these files have changed.
|
||||
*
|
||||
*
|
||||
* @param array $fileList List of files
|
||||
* @return string SHA1 bashed file hash
|
||||
*/
|
||||
@ -1667,7 +1731,7 @@ class Requirements_Backend
|
||||
$hash .= sha1_file($base . $file);
|
||||
} else {
|
||||
throw new InvalidArgumentException("Combined file {$file} does not exist");
|
||||
}
|
||||
}
|
||||
}
|
||||
return sha1($hash);
|
||||
}
|
||||
@ -1692,7 +1756,7 @@ class Requirements_Backend
|
||||
$abstheme = $absbase . $theme;
|
||||
$absproject = $absbase . $project;
|
||||
$css = "/css/$name.css";
|
||||
|
||||
|
||||
if(file_exists($absproject . $css)) {
|
||||
$this->css($project . $css, $media);
|
||||
} elseif($module && file_exists($abstheme . '_' . $module.$css)) {
|
||||
|
@ -7,16 +7,16 @@ use SilverStripe\Model\FieldType\DBField;
|
||||
* - Handle entering & leaving sub-scopes in loops and withs
|
||||
* - Track Up and Top
|
||||
* - (As a side effect) Inject data that needs to be available globally (used to live in ViewableData)
|
||||
*
|
||||
*
|
||||
* In order to handle up, rather than tracking it using a tree, which would involve constructing new objects
|
||||
* for each step, we use indexes into the itemStack (which already has to exist).
|
||||
*
|
||||
*
|
||||
* Each item has three indexes associated with it
|
||||
*
|
||||
*
|
||||
* - Pop. Which item should become the scope once the current scope is popped out of
|
||||
* - Up. Which item is up from this item
|
||||
* - Current. Which item is the first time this object has appeared in the stack
|
||||
*
|
||||
*
|
||||
* We also keep the index of the current starting point for lookups. A lookup is a sequence of obj calls -
|
||||
* when in a loop or with tag the end result becomes the new scope, but for injections, we throw away the lookup
|
||||
* and revert back to the original scope once we've got the value we're after
|
||||
@ -25,20 +25,20 @@ use SilverStripe\Model\FieldType\DBField;
|
||||
* @subpackage view
|
||||
*/
|
||||
class SSViewer_Scope {
|
||||
|
||||
|
||||
// The stack of previous "global" items
|
||||
// And array of item, itemIterator, itemIteratorTotal, pop_index, up_index, current_index
|
||||
private $itemStack = array();
|
||||
|
||||
private $itemStack = array();
|
||||
|
||||
// The current "global" item (the one any lookup starts from)
|
||||
protected $item;
|
||||
protected $item;
|
||||
|
||||
// If we're looping over the current "global" item, here's the iterator that tracks with item we're up to
|
||||
protected $itemIterator;
|
||||
protected $itemIterator;
|
||||
|
||||
//Total number of items in the iterator
|
||||
protected $itemIteratorTotal;
|
||||
|
||||
|
||||
// A pointer into the item stack for which item should be scope on the next pop call
|
||||
private $popIndex;
|
||||
|
||||
@ -47,7 +47,7 @@ class SSViewer_Scope {
|
||||
|
||||
// A pointer into the item stack for which item is this one (or null if not in stack yet)
|
||||
private $currentIndex = null;
|
||||
|
||||
|
||||
private $localIndex;
|
||||
|
||||
public function __construct($item, $inheritedScope = null) {
|
||||
@ -62,7 +62,7 @@ class SSViewer_Scope {
|
||||
$this->itemStack[] = array($this->item, null, 0, null, null, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function getItem(){
|
||||
return $this->itemIterator ? $this->itemIterator->current() : $this->item;
|
||||
}
|
||||
@ -103,12 +103,12 @@ class SSViewer_Scope {
|
||||
list($this->item, $this->itemIterator, $this->itemIteratorTotal, $unused2, $this->upIndex,
|
||||
$this->currentIndex) = $this->itemStack[$this->upIndex];
|
||||
break;
|
||||
|
||||
|
||||
case 'Top':
|
||||
list($this->item, $this->itemIterator, $this->itemIteratorTotal, $unused2, $this->upIndex,
|
||||
$this->currentIndex) = $this->itemStack[0];
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
$this->item = $this->getObj($name, $arguments, $forceReturnedObject, $cache, $cacheName);
|
||||
$this->itemIterator = null;
|
||||
@ -136,31 +136,31 @@ class SSViewer_Scope {
|
||||
|
||||
public function pushScope(){
|
||||
$newLocalIndex = count($this->itemStack)-1;
|
||||
|
||||
|
||||
$this->popIndex = $this->itemStack[$newLocalIndex][3] = $this->localIndex;
|
||||
$this->localIndex = $newLocalIndex;
|
||||
|
||||
|
||||
// We normally keep any previous itemIterator around, so local $Up calls reference the right element. But
|
||||
// once we enter a new global scope, we need to make sure we use a new one
|
||||
$this->itemIterator = $this->itemStack[$newLocalIndex][1] = null;
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function popScope(){
|
||||
$this->localIndex = $this->popIndex;
|
||||
$this->resetLocalScope();
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function next(){
|
||||
if (!$this->item) return false;
|
||||
|
||||
|
||||
if (!$this->itemIterator) {
|
||||
if (is_array($this->item)) $this->itemIterator = new ArrayIterator($this->item);
|
||||
else $this->itemIterator = $this->item->getIterator();
|
||||
|
||||
|
||||
$this->itemStack[$this->localIndex][1] = $this->itemIterator;
|
||||
$this->itemIteratorTotal = iterator_count($this->itemIterator); //count the total number of items
|
||||
$this->itemStack[$this->localIndex][2] = $this->itemIteratorTotal;
|
||||
@ -169,17 +169,17 @@ class SSViewer_Scope {
|
||||
else {
|
||||
$this->itemIterator->next();
|
||||
}
|
||||
|
||||
|
||||
$this->resetLocalScope();
|
||||
|
||||
if (!$this->itemIterator->valid()) return false;
|
||||
return $this->itemIterator->key();
|
||||
}
|
||||
|
||||
|
||||
public function __call($name, $arguments) {
|
||||
$on = $this->itemIterator ? $this->itemIterator->current() : $this->item;
|
||||
$retval = $on ? call_user_func_array(array($on, $name), $arguments) : null;
|
||||
|
||||
|
||||
$this->resetLocalScope();
|
||||
return $retval;
|
||||
}
|
||||
@ -348,7 +348,7 @@ class SSViewer_BasicIteratorSupport implements TemplateIteratorProvider {
|
||||
|
||||
/**
|
||||
* Returns true or false depending on if the pos of the iterator is a multiple of a specific number.
|
||||
* So, <% if MultipleOf(3) %> would return true on indexes: 3,6,9,12,15, etc.
|
||||
* So, <% if MultipleOf(3) %> would return true on indexes: 3,6,9,12,15, etc.
|
||||
* The count starts from $offset, which defaults to 1.
|
||||
* @param int $factor The multiple of which to return
|
||||
* @param int $offset Number to start count from.
|
||||
@ -365,14 +365,14 @@ class SSViewer_BasicIteratorSupport implements TemplateIteratorProvider {
|
||||
* This extends SSViewer_Scope to mix in data on top of what the item provides. This can be "global"
|
||||
* data that is scope-independant (like BaseURL), or type-specific data that is layered on top cross-cut like
|
||||
* (like $FirstLast etc).
|
||||
*
|
||||
*
|
||||
* It's separate from SSViewer_Scope to keep that fairly complex code as clean as possible.
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage view
|
||||
*/
|
||||
class SSViewer_DataPresenter extends SSViewer_Scope {
|
||||
|
||||
|
||||
private static $globalProperties = null;
|
||||
private static $iteratorProperties = null;
|
||||
|
||||
@ -404,7 +404,7 @@ class SSViewer_DataPresenter extends SSViewer_Scope {
|
||||
self::$iteratorProperties = array();
|
||||
// Get all the exposed variables from all classes that implement the TemplateIteratorProvider interface
|
||||
// //call non-statically
|
||||
$this->createCallableArray(self::$iteratorProperties, "TemplateIteratorProvider",
|
||||
$this->createCallableArray(self::$iteratorProperties, "TemplateIteratorProvider",
|
||||
"get_template_iterator_variables", true);
|
||||
}
|
||||
|
||||
@ -423,7 +423,7 @@ class SSViewer_DataPresenter extends SSViewer_Scope {
|
||||
$exposedVariables = call_user_func(array($implementer, $variableMethod));
|
||||
|
||||
foreach($exposedVariables as $varName => $details) {
|
||||
if (!is_array($details)) $details = array('method' => $details,
|
||||
if (!is_array($details)) $details = array('method' => $details,
|
||||
'casting' => Config::inst()->get('ViewableData', 'default_cast', Config::FIRST_SET));
|
||||
|
||||
// If just a value (and not a key => value pair), use it for both key and value
|
||||
@ -445,7 +445,7 @@ class SSViewer_DataPresenter extends SSViewer_Scope {
|
||||
|
||||
/**
|
||||
* Get the injected value
|
||||
*
|
||||
*
|
||||
* @param string $property Name of property
|
||||
* @param array $params
|
||||
* @param bool $cast If true, an object is always returned even if not an object.
|
||||
@ -532,7 +532,7 @@ class SSViewer_DataPresenter extends SSViewer_Scope {
|
||||
$property = $arguments[0]; //the name of the public function being called
|
||||
|
||||
//the public function parameters in an array
|
||||
if (isset($arguments[1]) && $arguments[1] != null) $params = $arguments[1];
|
||||
if (isset($arguments[1]) && $arguments[1] != null) $params = $arguments[1];
|
||||
else $params = array();
|
||||
|
||||
$val = $this->getInjectedValue($property, $params);
|
||||
@ -556,31 +556,31 @@ class SSViewer_DataPresenter extends SSViewer_Scope {
|
||||
|
||||
/**
|
||||
* Parses a template file with an *.ss file extension.
|
||||
*
|
||||
* In addition to a full template in the templates/ folder, a template in
|
||||
*
|
||||
* In addition to a full template in the templates/ folder, a template in
|
||||
* templates/Content or templates/Layout will be rendered into $Content and
|
||||
* $Layout, respectively.
|
||||
*
|
||||
*
|
||||
* A single template can be parsed by multiple nested {@link SSViewer} instances
|
||||
* through $Layout/$Content placeholders, as well as <% include MyTemplateFile %> template commands.
|
||||
*
|
||||
*
|
||||
* <b>Themes</b>
|
||||
*
|
||||
*
|
||||
* See http://doc.silverstripe.org/themes and http://doc.silverstripe.org/themes:developing
|
||||
*
|
||||
*
|
||||
* <b>Caching</b>
|
||||
*
|
||||
* Compiled templates are cached via {@link SS_Cache}, usually on the filesystem.
|
||||
* Compiled templates are cached via {@link SS_Cache}, usually on the filesystem.
|
||||
* If you put ?flush=1 on your URL, it will force the template to be recompiled.
|
||||
*
|
||||
* @see http://doc.silverstripe.org/themes
|
||||
* @see http://doc.silverstripe.org/themes:developing
|
||||
*
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage view
|
||||
*/
|
||||
class SSViewer implements Flushable {
|
||||
|
||||
|
||||
/**
|
||||
* @config
|
||||
* @var boolean $source_file_comments
|
||||
@ -608,7 +608,7 @@ class SSViewer implements Flushable {
|
||||
Deprecation::notice('4.0', 'Use the "SSViewer.source_file_comments" config setting instead');
|
||||
Config::inst()->update('SSViewer', 'source_file_comments', $val);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated 4.0 Use the "SSViewer.source_file_comments" config setting instead
|
||||
* @return boolean
|
||||
@ -617,18 +617,18 @@ class SSViewer implements Flushable {
|
||||
Deprecation::notice('4.0', 'Use the "SSViewer.source_file_comments" config setting instead');
|
||||
return Config::inst()->get('SSViewer', 'source_file_comments');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @var array $chosenTemplates Associative array for the different
|
||||
* template containers: "main" and "Layout". Values are absolute file paths to *.ss files.
|
||||
*/
|
||||
private $chosenTemplates = array();
|
||||
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
protected $rewriteHashlinks = true;
|
||||
|
||||
|
||||
/**
|
||||
* @config
|
||||
* @var string The used "theme", which usually consists of templates, images and stylesheets.
|
||||
@ -640,7 +640,7 @@ class SSViewer implements Flushable {
|
||||
* @config
|
||||
* @var boolean Use the theme. Set to FALSE in order to disable themes,
|
||||
* which can be useful for scenarios where theme overrides are temporarily undesired,
|
||||
* such as an administrative interface separate from the website theme.
|
||||
* such as an administrative interface separate from the website theme.
|
||||
* It retains the theme settings to be re-enabled, for example when a website content
|
||||
* needs to be rendered from within this administrative interface.
|
||||
*/
|
||||
@ -658,7 +658,7 @@ class SSViewer implements Flushable {
|
||||
|
||||
/*
|
||||
* Default prepended cache key for partial caching
|
||||
*
|
||||
*
|
||||
* @var string
|
||||
* @config
|
||||
*/
|
||||
@ -686,25 +686,25 @@ class SSViewer implements Flushable {
|
||||
}
|
||||
return $viewer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
Deprecation::notice('4.0', 'Use the "SSViewer.theme" config setting instead');
|
||||
Config::inst()->update('SSViewer', 'theme', $theme);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated 4.0 Use the "SSViewer.theme" config setting instead
|
||||
* @return string
|
||||
* @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
|
||||
*
|
||||
@ -760,7 +760,7 @@ class SSViewer implements Flushable {
|
||||
public static function get_templates_by_class($className, $suffix = '', $baseClass = null) {
|
||||
// Figure out the class name from the supplied context.
|
||||
if(!is_string($className) || !class_exists($className)) {
|
||||
throw new InvalidArgumentException('SSViewer::get_templates_by_class() expects a valid class name as ' .
|
||||
throw new InvalidArgumentException('SSViewer::get_templates_by_class() expects a valid class name as ' .
|
||||
'its first parameter.');
|
||||
return array();
|
||||
}
|
||||
@ -780,7 +780,7 @@ class SSViewer implements Flushable {
|
||||
}
|
||||
return $templates;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string|array $templateList If passed as a string with .ss extension, used as the "main" template.
|
||||
* If passed as an array, it can be used for template inheritance (first found template "wins").
|
||||
@ -864,15 +864,15 @@ class SSViewer implements Flushable {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set a global rendering option.
|
||||
*
|
||||
* The following options are available:
|
||||
* - rewriteHashlinks: If true (the default), <a href="#..."> will be rewritten to contain the
|
||||
* - rewriteHashlinks: If true (the default), <a href="#..."> will be rewritten to contain the
|
||||
* current URL. This lets it play nicely with our <base> tag.
|
||||
* - If rewriteHashlinks = 'php' then, a piece of PHP script will be inserted before the hash
|
||||
* links: "<?php echo $_SERVER['REQUEST_URI']; ?>". This is useful if you're generating a
|
||||
* - If rewriteHashlinks = 'php' then, a piece of PHP script will be inserted before the hash
|
||||
* links: "<?php echo $_SERVER['REQUEST_URI']; ?>". This is useful if you're generating a
|
||||
* page that will be saved to a .php file and may be accessed from different URLs.
|
||||
*
|
||||
* @deprecated 4.0 Use the "SSViewer.rewrite_hash_links" config setting instead
|
||||
@ -888,7 +888,7 @@ class SSViewer implements Flushable {
|
||||
Config::inst()->update('SSViewer', $optionName, $optionVal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated 4.0 Use the "SSViewer.rewrite_hash_links" config setting instead
|
||||
* @param string
|
||||
@ -917,7 +917,7 @@ class SSViewer implements Flushable {
|
||||
return SSViewer::$topLevel[sizeof(SSViewer::$topLevel)-1];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Call this to disable rewriting of <a href="#xxx"> links. This is useful in Ajax applications.
|
||||
* It returns the SSViewer objects, so that you can call new SSViewer("X")->dontRewriteHashlinks()->process();
|
||||
@ -927,7 +927,7 @@ class SSViewer implements Flushable {
|
||||
Config::inst()->update('SSViewer', 'rewrite_hash_links', false);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function exists() {
|
||||
return $this->chosenTemplates;
|
||||
}
|
||||
@ -955,7 +955,7 @@ class SSViewer implements Flushable {
|
||||
return $founds[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clears all parsed template files in the cache folder.
|
||||
*
|
||||
@ -986,7 +986,7 @@ class SSViewer implements Flushable {
|
||||
if (!self::$cacheblock_cache_flushed || $force) {
|
||||
$cache = SS_Cache::factory('cacheblock');
|
||||
$backend = $cache->getBackend();
|
||||
|
||||
|
||||
if(
|
||||
$backend instanceof Zend_Cache_Backend_ExtendedInterface
|
||||
&& ($capabilities = $backend->getCapabilities())
|
||||
@ -997,7 +997,7 @@ class SSViewer implements Flushable {
|
||||
$cache->clean(Zend_Cache::CLEANING_MODE_ALL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
self::$cacheblock_cache_flushed = true;
|
||||
}
|
||||
}
|
||||
@ -1071,14 +1071,14 @@ class SSViewer implements Flushable {
|
||||
/**
|
||||
* The process() method handles the "meat" of the template processing.
|
||||
*
|
||||
* It takes care of caching the output (via {@link SS_Cache}), as well as
|
||||
* replacing the special "$Content" and "$Layout" placeholders with their
|
||||
* It takes care of caching the output (via {@link SS_Cache}), as well as
|
||||
* replacing the special "$Content" and "$Layout" placeholders with their
|
||||
* respective subtemplates.
|
||||
*
|
||||
* The method injects extra HTML in the header via {@link Requirements::includeInHTML()}.
|
||||
*
|
||||
*
|
||||
* Note: You can call this method indirectly by {@link ViewableData->renderWith()}.
|
||||
*
|
||||
*
|
||||
* @param ViewableData $item
|
||||
* @param array|null $arguments - arguments to an included template
|
||||
* @param Object $inheritedScope - the current scope of a parent template including a sub-template
|
||||
@ -1095,15 +1095,15 @@ class SSViewer implements Flushable {
|
||||
$key = reset($keys);
|
||||
$template = $this->chosenTemplates[$key];
|
||||
}
|
||||
|
||||
$cacheFile = TEMP_FOLDER . "/.cache"
|
||||
|
||||
$cacheFile = TEMP_FOLDER . "/.cache"
|
||||
. str_replace(array('\\','/',':'), '.', Director::makeRelative(realpath($template)));
|
||||
$lastEdited = filemtime($template);
|
||||
|
||||
if(!file_exists($cacheFile) || filemtime($cacheFile) < $lastEdited) {
|
||||
$content = file_get_contents($template);
|
||||
$content = $this->parseTemplateContent($content, $template);
|
||||
|
||||
|
||||
$fh = fopen($cacheFile,'w');
|
||||
fwrite($fh, $content);
|
||||
fclose($fh);
|
||||
@ -1126,21 +1126,21 @@ class SSViewer implements Flushable {
|
||||
}
|
||||
|
||||
$output = $this->includeGeneratedTemplate($cacheFile, $item, $arguments, $underlay, $inheritedScope);
|
||||
|
||||
|
||||
if($this->includeRequirements) {
|
||||
$output = Requirements::includeInHTML($template, $output);
|
||||
$output = Requirements::includeInHTML($output);
|
||||
}
|
||||
|
||||
|
||||
array_pop(SSViewer::$topLevel);
|
||||
|
||||
// If we have our crazy base tag, then fix # links referencing the current page.
|
||||
|
||||
|
||||
$rewrite = Config::inst()->get('SSViewer', 'rewrite_hash_links');
|
||||
if($this->rewriteHashlinks && $rewrite) {
|
||||
if(strpos($output, '<base') !== false) {
|
||||
if($rewrite === 'php') {
|
||||
if($rewrite === 'php') {
|
||||
$thisURLRelativeToBase = "<?php echo Convert::raw2att(preg_replace(\"/^(\\\\/)+/\", \"/\", \$_SERVER['REQUEST_URI'])); ?>";
|
||||
} else {
|
||||
} else {
|
||||
$thisURLRelativeToBase = Convert::raw2att(preg_replace("/^(\\/)+/", "/", $_SERVER['REQUEST_URI']));
|
||||
}
|
||||
|
||||
@ -1154,7 +1154,7 @@ class SSViewer implements Flushable {
|
||||
/**
|
||||
* Execute the given template, passing it the given data.
|
||||
* Used by the <% include %> template tag to process templates.
|
||||
*
|
||||
*
|
||||
* @param string $template Template name
|
||||
* @param mixed $data Data context
|
||||
* @param array $arguments Additional arguments
|
||||
@ -1166,12 +1166,12 @@ class SSViewer implements Flushable {
|
||||
|
||||
return $v->process($data, $arguments, $scope);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute the evaluated string, passing it the given data.
|
||||
* Used by partial caching to evaluate custom cache keys expressed using
|
||||
* template expressions
|
||||
*
|
||||
*
|
||||
* @param string $content Input string
|
||||
* @param mixed $data Data context
|
||||
* @param array $arguments Additional arguments
|
||||
@ -1180,7 +1180,7 @@ class SSViewer implements Flushable {
|
||||
public static function execute_string($content, $data, $arguments = null) {
|
||||
$v = SSViewer::fromString($content);
|
||||
$v->includeRequirements(false);
|
||||
|
||||
|
||||
return $v->process($data, $arguments);
|
||||
}
|
||||
|
||||
@ -1199,7 +1199,7 @@ class SSViewer implements Flushable {
|
||||
public function templates() {
|
||||
return $this->chosenTemplates;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $type "Layout" or "main"
|
||||
* @param string $file Full system path to the template file
|
||||
@ -1207,17 +1207,17 @@ class SSViewer implements Flushable {
|
||||
public function setTemplateFile($type, $file) {
|
||||
$this->chosenTemplates[$type] = $file;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return an appropriate base tag for the given template.
|
||||
* It will be closed on an XHTML document, and unclosed on an HTML document.
|
||||
*
|
||||
*
|
||||
* @param $contentGeneratedSoFar The content of the template generated so far; it should contain
|
||||
* the DOCTYPE declaration.
|
||||
*/
|
||||
public static function get_base_tag($contentGeneratedSoFar) {
|
||||
$base = Director::absoluteBaseURL();
|
||||
|
||||
|
||||
// Is the document XHTML?
|
||||
if(preg_match('/<!DOCTYPE[^>]+xhtml/i', $contentGeneratedSoFar)) {
|
||||
return "<base href=\"$base\" />";
|
||||
@ -1240,19 +1240,19 @@ class SSViewer_FromString extends SSViewer {
|
||||
* @var bool
|
||||
*/
|
||||
private static $cache_template = true;
|
||||
|
||||
|
||||
/**
|
||||
* The template to use
|
||||
* @var string
|
||||
*/
|
||||
protected $content;
|
||||
|
||||
|
||||
/**
|
||||
* Indicates whether templates should be cached
|
||||
* @var bool
|
||||
*/
|
||||
protected $cacheTemplate;
|
||||
|
||||
|
||||
public function __construct($content, TemplateParser $parser = null) {
|
||||
if ($parser) {
|
||||
$this->setParser($parser);
|
||||
@ -1279,21 +1279,21 @@ class SSViewer_FromString extends SSViewer {
|
||||
} else {
|
||||
$cacheTemplate = Config::inst()->get('SSViewer_FromString', 'cache_template');
|
||||
}
|
||||
|
||||
|
||||
if (!$cacheTemplate) {
|
||||
unlink($cacheFile);
|
||||
}
|
||||
|
||||
return $val;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param boolean $cacheTemplate
|
||||
*/
|
||||
public function setCacheTemplate($cacheTemplate) {
|
||||
$this->cacheTemplate = (bool) $cacheTemplate;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user