links. This is useful in Ajax applications.
* It returns the SSViewer objects, so that you can call new SSViewer("X")->dontRewriteHashlinks()->process();
*/
public function dontRewriteHashlinks() {
$this->rewriteHashlinks = false;
self::$options['rewriteHashlinks'] = false;
return $this;
}
public function exists() {
return $this->chosenTemplates;
}
/**
* Searches for a template name in the current theme:
* - themes/mytheme/templates
* - themes/mytheme/templates/Includes
* Falls back to unthemed template files.
*
* Caution: Doesn't search in any /Layout folders.
*
* @param string $identifier A template name without '.ss' extension or path.
* @return string Full system path to a template file
*/
public static function getTemplateFile($identifier) {
global $_TEMPLATE_MANIFEST;
$includeTemplateFile = self::getTemplateFileByType($identifier, 'Includes');
if($includeTemplateFile) return $includeTemplateFile;
$mainTemplateFile = self::getTemplateFileByType($identifier, 'main');
if($mainTemplateFile) return $mainTemplateFile;
return false;
}
/**
* @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) {
global $_TEMPLATE_MANIFEST;
if(self::current_theme() && isset($_TEMPLATE_MANIFEST[$identifier]['themes'][self::current_theme()][$type])) {
return $_TEMPLATE_MANIFEST[$identifier]['themes'][self::current_theme()][$type];
} else if(isset($_TEMPLATE_MANIFEST[$identifier][$type])){
return $_TEMPLATE_MANIFEST[$identifier][$type];
} else {
return false;
}
}
/**
* Used by <% include Identifier %> statements to get the full
* unparsed content of a template file.
*
* @uses getTemplateFile()
* @param string $identifier A template name without '.ss' extension or path.
* @return string content of template
*/
public static function getTemplateContent($identifier) {
if(!SSViewer::getTemplateFile($identifier)) {
return null;
}
$content = file_get_contents(SSViewer::getTemplateFile($identifier));
// $content = "". $content;
// Adds an i18n namespace to all _t(...) calls without an existing one
// to avoid confusion when using the include in different contexts.
// Entities without a namespace are deprecated, but widely used.
$content = ereg_replace('<' . '% +_t\((\'([^\.\']*)\'|"([^\."]*)")(([^)]|\)[^ ]|\) +[^% ])*)\) +%' . '>', '= _t(\''. $identifier . '.ss' . '.\\2\\3\'\\4) ?>', $content);
// Remove UTF-8 byte order mark
// This is only necessary if you don't have zend-multibyte enabled.
if(substr($content, 0,3) == pack("CCC", 0xef, 0xbb, 0xbf)) {
$content = substr($content, 3);
}
return $content;
}
/**
* @ignore
*/
static private $flushed = false;
/**
* Clears all parsed template files in the cache folder.
*
* Can only be called once per request (there may be multiple SSViewer instances).
*/
static function flush_template_cache() {
if (!self::$flushed) {
$dir = dir(TEMP_FOLDER);
while (false !== ($file = $dir->read())) {
if (strstr($file, '.cache')) { unlink(TEMP_FOLDER.'/'.$file); }
}
self::$flushed = true;
}
}
/**
* 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.
* 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
* @return String Parsed template output.
*/
public function process($item, $cache = null) {
SSViewer::$topLevel[] = $item;
if (!$cache) $cache = SS_Cache::factory('cacheblock');
if(isset($this->chosenTemplates['main'])) {
$template = $this->chosenTemplates['main'];
} else {
$template = $this->chosenTemplates[ reset($dummy = array_keys($this->chosenTemplates)) ];
}
if(isset($_GET['debug_profile'])) Profiler::mark("SSViewer::process", " for $template");
$cacheFile = TEMP_FOLDER . "/.cache" . str_replace(array('\\','/',':'), '.', Director::makeRelative(realpath($template)));
$lastEdited = filemtime($template);
if(!file_exists($cacheFile) || filemtime($cacheFile) < $lastEdited || isset($_GET['flush'])) {
if(isset($_GET['debug_profile'])) Profiler::mark("SSViewer::process - compile", " for $template");
$content = file_get_contents($template);
$content = SSViewer::parseTemplateContent($content, $template);
$fh = fopen($cacheFile,'w');
fwrite($fh, $content);
fclose($fh);
if(isset($_GET['debug_profile'])) Profiler::unmark("SSViewer::process - compile", " for $template");
}
if(isset($_GET['showtemplate']) && !Director::isLive()) {
$lines = file($cacheFile);
echo "Template: $cacheFile
";
echo "";
foreach($lines as $num => $line) {
echo str_pad($num+1,5) . htmlentities($line);
}
echo "
";
}
// Makes the rendered sub-templates available on the parent item,
// through $Content and $Layout placeholders.
foreach(array('Content', 'Layout') as $subtemplate) {
if(isset($this->chosenTemplates[$subtemplate])) {
$subtemplateViewer = new SSViewer($this->chosenTemplates[$subtemplate]);
$item = $item->customise(array(
$subtemplate => $subtemplateViewer->process($item, $cache)
));
}
}
$itemStack = array();
$val = "";
$valStack = array();
include($cacheFile);
$output = $val;
$output = Requirements::includeInHTML($template, $output);
array_pop(SSViewer::$topLevel);
if(isset($_GET['debug_profile'])) Profiler::unmark("SSViewer::process", " for $template");
// If we have our crazy base tag, then fix # links referencing the current page.
if($this->rewriteHashlinks && self::$options['rewriteHashlinks']) {
if(strpos($output, ''), array('&','"',''','<','>'), \$_SERVER['REQUEST_URI']); ?>";
} else {
$thisURLRelativeToBase = Convert::raw2att($_SERVER['REQUEST_URI']);
}
$output = preg_replace('/(