From dfd34558020be649fc480b499659d0e1cc36630b Mon Sep 17 00:00:00 2001 From: Will Rossiter Date: Sun, 4 Nov 2012 12:59:40 +1300 Subject: [PATCH] BUG Only include processed requirements at the top level. (Fixes #7847) After each sub template was processed Requirements::includeInHTML() is included which appended requirements again. --- tests/templates/SSViewerTestProcess.ss | 7 +++ tests/templates/SSViewerTestProcessHead.ss | 3 ++ tests/view/SSViewerTest.php | 36 +++++++++++++ view/SSViewer.php | 60 ++++++++++++++++++---- 4 files changed, 95 insertions(+), 11 deletions(-) create mode 100644 tests/templates/SSViewerTestProcess.ss create mode 100644 tests/templates/SSViewerTestProcessHead.ss diff --git a/tests/templates/SSViewerTestProcess.ss b/tests/templates/SSViewerTestProcess.ss new file mode 100644 index 000000000..6fda306a0 --- /dev/null +++ b/tests/templates/SSViewerTestProcess.ss @@ -0,0 +1,7 @@ + + <% include SSViewerTestProcessHead %> + + + <% include SSViewerTestCommentsWithInclude %> + + \ No newline at end of file diff --git a/tests/templates/SSViewerTestProcessHead.ss b/tests/templates/SSViewerTestProcessHead.ss new file mode 100644 index 000000000..cb2d09414 --- /dev/null +++ b/tests/templates/SSViewerTestProcessHead.ss @@ -0,0 +1,3 @@ + + <% require javascript(framework/tests/forms/RequirementsTest_a.js) %> + diff --git a/tests/view/SSViewerTest.php b/tests/view/SSViewerTest.php index 62afaa788..45319cb35 100644 --- a/tests/view/SSViewerTest.php +++ b/tests/view/SSViewerTest.php @@ -1025,6 +1025,42 @@ after') $result = $viewer->process(new ArrayData(array('List' => $list))); $this->assertEquals($result, ''); } + + public function testProcessOnlyIncludesRequirementsOnce() { + $template = new SSViewer(array('SSViewerTestProcess')); + $basePath = dirname($this->getCurrentRelativePath()) . '/forms'; + + $backend = new Requirements_Backend; + $backend->set_combined_files_enabled(false); + $backend->combine_files( + 'RequirementsTest_ab.css', + array( + $basePath . '/RequirementsTest_a.css', + $basePath . '/RequirementsTest_b.css' + ) + ); + + Requirements::set_backend($backend); + + $this->assertEquals(1, substr_count($template->process(array()), "a.css")); + $this->assertEquals(1, substr_count($template->process(array()), "b.css")); + + // if we disable the requirements then we should get nothing + $template->includeRequirements(false); + $this->assertEquals(0, substr_count($template->process(array()), "a.css")); + $this->assertEquals(0, substr_count($template->process(array()), "b.css")); + } + + public function testRequireCallInTemplateInclude() { + $template = new SSViewer(array('SSViewerTestProcess')); + + Requirements::set_suffix_requirements(false); + + $this->assertEquals(1, substr_count( + $template->process(array()), + "tests/forms/RequirementsTest_a.js" + )); + } } /** diff --git a/view/SSViewer.php b/view/SSViewer.php index 235657932..d50e08947 100644 --- a/view/SSViewer.php +++ b/view/SSViewer.php @@ -563,6 +563,11 @@ class SSViewer { */ protected static $current_custom_theme = null; + /** + * @var boolean + */ + protected $includeRequirements = true; + /** * Create a template from a string instead of a .ss file * @@ -592,7 +597,7 @@ class SSViewer { /** * Returns the path to the theme folder * - * @return String + * @return string */ public static function get_theme_folder() { return self::current_theme() ? THEMES_DIR . "/" . self::current_theme() : project(); @@ -667,7 +672,11 @@ class SSViewer { } /** - * Returns true if at least one of the listed templates exists + * Returns true if at least one of the listed templates exists. + * + * @param array $templates + * + * @return boolean */ public static function hasTemplate($templates) { $manifest = SS_TemplateLoader::instance()->getManifest(); @@ -681,20 +690,25 @@ class SSViewer { /** * Set a global rendering option. + * * The following options are available: * - rewriteHashlinks: If true (the default), will be rewritten to contain the * current URL. This lets it play nicely with our tag. * - If rewriteHashlinks = 'php' then, a piece of PHP script will be inserted before the hash * links: "". This is useful if you're generating a * page that will be saved to a .php file and may be accessed from different URLs. + * + * @param string $optionName + * @param mixed $optionVal */ public static function setOption($optionName, $optionVal) { SSViewer::$options[$optionName] = $optionVal; } /** - * @param String - * @return Mixed + * @param string + * + * @return mixed */ public static function getOption($optionName) { return SSViewer::$options[$optionName]; @@ -705,6 +719,7 @@ class SSViewer { ); protected static $topLevel = array(); + public static function topLevel() { if(SSViewer::$topLevel) { return SSViewer::$topLevel[sizeof(SSViewer::$topLevel)-1]; @@ -728,6 +743,7 @@ class SSViewer { /** * @param string $identifier A template name without '.ss' extension or path * @param string $type The template type, either "main", "Includes" or "Layout" + * * @return string Full system path to a template file */ public static function getTemplateFileByType($identifier, $type) { @@ -766,6 +782,7 @@ class SSViewer { /** * Set the cache object to use when storing / retrieving partial cache blocks. + * * @param Zend_Cache_Core $cache */ public function setPartialCacheStore($cache) { @@ -773,13 +790,23 @@ class SSViewer { } /** - * Get the cache object to use when storing / retrieving partial cache blocks + * Get the cache object to use when storing / retrieving partial cache blocks. + * * @return Zend_Cache_Core */ public function getPartialCacheStore() { return $this->partialCacheStore ? $this->partialCacheStore : SS_Cache::factory('cacheblock'); } + /** + * Flag whether to include the requirements in this response. + * + * @param boolean + */ + public function includeRequirements($incl = true) { + $this->includeRequirements = $incl; + } + /** * An internal utility function to set up variables in preparation for including a compiled * template, then do the include @@ -790,6 +817,7 @@ class SSViewer { * @param Object $item - The item to use as the root scope for the template * @param array|null $overlay - Any variables to layer on top of the scope * @param array|null $underlay - Any variables to layer underneath the scope + * * @return string - The result of executing the template */ protected function includeGeneratedTemplate($cacheFile, $item, $overlay, $underlay) { @@ -814,15 +842,18 @@ class SSViewer { /** * 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 respective subtemplates. + * + * 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 SS_Cache $cache Optional cache backend + * @param SS_Cache $cache Optional cache backend. + * * @return String Parsed template output. */ public function process($item, $arguments = null) { @@ -869,14 +900,18 @@ class SSViewer { foreach(array('Content', 'Layout') as $subtemplate) { if(isset($this->chosenTemplates[$subtemplate])) { $subtemplateViewer = new SSViewer($this->chosenTemplates[$subtemplate]); + $subtemplateViewer->includeRequirements(false); $subtemplateViewer->setPartialCacheStore($this->getPartialCacheStore()); $underlay[$subtemplate] = $subtemplateViewer->process($item, $arguments); } } - $val = $this->includeGeneratedTemplate($cacheFile, $item, $arguments, $underlay); - $output = Requirements::includeInHTML($template, $val); + $output = $this->includeGeneratedTemplate($cacheFile, $item, $arguments, $underlay); + + if($this->includeRequirements) { + $output = Requirements::includeInHTML($template, $output); + } array_pop(SSViewer::$topLevel); @@ -890,6 +925,7 @@ class SSViewer { } else { $thisURLRelativeToBase = strip_tags($_SERVER['REQUEST_URI']); } + $output = preg_replace('/(]+href *= *)"#/i', '\\1"' . $thisURLRelativeToBase . '#', $output); } } @@ -903,6 +939,8 @@ class SSViewer { */ public static function execute_template($template, $data, $arguments = null) { $v = new SSViewer($template); + $v->includeRequirements(false); + return $v->process($data, $arguments); }