1 <?php
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
50 class SSViewer {
51
52 53 54
55 protected static = false;
56
57 58 59 60 61 62
63 static function ($val) {
64 self::$source_file_comments = $val;
65 }
66
67 68 69
70 static function () {
71 return self::$source_file_comments;
72 }
73
74 75 76 77
78 private $chosenTemplates = array();
79
80 81 82
83 protected $rewriteHashlinks = true;
84
85 86 87
88 protected static $current_theme = null;
89
90 91 92
93 protected static $current_custom_theme = null;
94
95 96 97 98 99
100 static function fromString($content) {
101 return new SSViewer_FromString($content);
102 }
103
104 105 106
107 static function set_theme($theme) {
108 self::$current_theme = $theme;
109
110 if(!is_null($theme))
111 self::$current_custom_theme=$theme;
112 }
113
114 115 116
117 static function current_theme() {
118 return self::$current_theme;
119 }
120
121 122 123
124 static function current_custom_theme(){
125 return self::$current_custom_theme;
126 }
127
128 129 130 131 132 133
134 public function __construct($templateList) {
135 global $_TEMPLATE_MANIFEST;
136
137
138 if (isset($_GET['flush']) && $_GET['flush'] == 'all') {
139 if(Director::isDev() || Director::is_cli() || Permission::check('ADMIN')) {
140 self::flush_template_cache();
141 } else {
142 return Security::permissionFailure(null, 'Please log in as an administrator to flush the template cache.');
143 }
144 }
145
146 if(is_string($templateList) && substr((string) $templateList,-3) == '.ss') {
147 $this->chosenTemplates['main'] = $templateList;
148 } else {
149 if(!is_array($templateList)) $templateList = array($templateList);
150
151 if(isset($_GET['debug_request'])) Debug::message("Selecting templates from the following list: " . implode(", ", $templateList));
152
153 foreach($templateList as $template) {
154
155 if(strpos($template,'/') !== false) list($templateFolder, $template) = explode('/', $template, 2);
156 else $templateFolder = null;
157
158
159 if(self::current_theme() && isset($_TEMPLATE_MANIFEST[$template]['themes'][self::current_theme()])) {
160 $this->chosenTemplates = array_merge(
161 $_TEMPLATE_MANIFEST[$template]['themes'][self::current_theme()],
162 $this->chosenTemplates
163 );
164
165 if(isset($_GET['debug_request'])) Debug::message("Found template '$template' from main theme '" . self::current_theme() . "': " . var_export($_TEMPLATE_MANIFEST[$template]['themes'][self::current_theme()], true));
166 }
167
168
169 if(isset($_TEMPLATE_MANIFEST[$template]) && (array_keys($_TEMPLATE_MANIFEST[$template]) != array('themes'))) {
170 $this->chosenTemplates = array_merge(
171 $_TEMPLATE_MANIFEST[$template],
172 $this->chosenTemplates
173 );
174
175 if(isset($_GET['debug_request'])) Debug::message("Found template '$template' from main template archive, containing the following items: " . var_export($_TEMPLATE_MANIFEST[$template], true));
176
177 unset($this->chosenTemplates['themes']);
178 }
179
180 if($templateFolder) {
181 $this->chosenTemplates['main'] = $this->chosenTemplates[$templateFolder];
182 unset($this->chosenTemplates[$templateFolder]);
183 }
184 }
185
186 if(isset($_GET['debug_request'])) Debug::message("Final template selections made: " . var_export($this->chosenTemplates, true));
187
188 }
189
190 if(!$this->chosenTemplates) user_error("None of these templates can be found in theme '"
191 . self::current_theme() . "': ". implode(".ss, ", $templateList) . ".ss", E_USER_WARNING);
192 }
193
194 195 196
197 static function hasTemplate($templateList) {
198 if(!is_array($templateList)) $templateList = array($templateList);
199
200 global $_TEMPLATE_MANIFEST;
201 foreach($templateList as $template) {
202 if(strpos($template,'/') !== false) list($templateFolder, $template) = explode('/', $template, 2);
203 if(isset($_TEMPLATE_MANIFEST[$template])) return true;
204 }
205
206 return false;
207 }
208
209 210 211 212 213 214 215 216 217
218 public static function setOption($optionName, $optionVal) {
219 SSViewer::$options[$optionName] = $optionVal;
220 }
221 protected static $options = array(
222 'rewriteHashlinks' => true,
223 );
224
225 protected static $topLevel = array();
226 public static function topLevel() {
227 if(SSViewer::$topLevel) {
228 return SSViewer::$topLevel[sizeof(SSViewer::$topLevel)-1];
229 }
230 }
231
232 233 234 235
236 public function dontRewriteHashlinks() {
237 $this->rewriteHashlinks = false;
238 self::$options['rewriteHashlinks'] = false;
239 return $this;
240 }
241
242 public function exists() {
243 return $this->chosenTemplates;
244 }
245
246 247 248 249 250 251 252 253 254 255 256
257 public static function getTemplateFile($identifier) {
258 global $_TEMPLATE_MANIFEST;
259
260 $includeTemplateFile = self::getTemplateFileByType($identifier, 'Includes');
261 if($includeTemplateFile) return $includeTemplateFile;
262
263 $mainTemplateFile = self::getTemplateFileByType($identifier, 'main');
264 if($mainTemplateFile) return $mainTemplateFile;
265
266 return false;
267 }
268
269 270 271 272 273
274 public static function getTemplateFileByType($identifier, $type) {
275 global $_TEMPLATE_MANIFEST;
276 if(self::current_theme() && isset($_TEMPLATE_MANIFEST[$identifier]['themes'][self::current_theme()][$type])) {
277 return $_TEMPLATE_MANIFEST[$identifier]['themes'][self::current_theme()][$type];
278 } else if(isset($_TEMPLATE_MANIFEST[$identifier][$type])){
279 return $_TEMPLATE_MANIFEST[$identifier][$type];
280 } else {
281 return false;
282 }
283 }
284
285 286 287 288 289 290 291 292
293 public static function getTemplateContent($identifier) {
294 if(!SSViewer::getTemplateFile($identifier)) {
295 return null;
296 }
297
298 $content = file_get_contents(SSViewer::getTemplateFile($identifier));
299
300
301
302
303
304 $content = ereg_replace('<' . '% +_t\((\'([^\.\']*)\'|"([^\."]*)")(([^)]|\)[^ ]|\) +[^% ])*)\) +%' . '>', '<?= _t(\''. $identifier . '.ss' . '.\\2\\3\'\\4) ?>', $content);
305 $content = ereg_replace('<' . '% +sprintf\(_t\((\'([^\.\']*)\'|"([^\."]*)")(([^)]|\)[^ ]|\) +[^% ])*)\), \$([a-zA-Z][a-zA-Z0-9_]*)\) +%' . '>', '<?= sprintf(_t(\'' . $identifier . '.ss.\\2\\3\'\\4), {dlr}item->XML_val("\\6",null,true)) ?>', $content);
306
307
308
309 if(substr($content, 0,3) == pack("CCC", 0xef, 0xbb, 0xbf)) {
310 $content = substr($content, 3);
311 }
312
313 return $content;
314 }
315
316 317 318
319 static private $flushed = false;
320
321 322 323 324 325
326 static function flush_template_cache() {
327 if (!self::$flushed) {
328 $dir = dir(TEMP_FOLDER);
329 while (false !== ($file = $dir->read())) {
330 if (strstr($file, '.cache')) { unlink(TEMP_FOLDER.'/'.$file); }
331 }
332 self::$flushed = true;
333 }
334 }
335
336 337 338
339 public function process($item, $cache = null) {
340 SSViewer::$topLevel[] = $item;
341
342 if (!$cache) $cache = SS_Cache::factory('cacheblock');
343
344 if(isset($this->chosenTemplates['main'])) {
345 $template = $this->chosenTemplates['main'];
346 } else {
347 $template = $this->chosenTemplates[ reset($dummy = array_keys($this->chosenTemplates)) ];
348 }
349
350 if(isset($_GET['debug_profile'])) Profiler::mark("SSViewer::process", " for $template");
351
352 $cacheFile = TEMP_FOLDER . "/.cache" . str_replace(array("\\", '/', ':'), '.', str_replace(BASE_PATH, '', $template));
353
354 $lastEdited = filemtime($template);
355
356 if(!file_exists($cacheFile)
357 || (Director::isDev() && filemtime($cacheFile) < $lastEdited)
358 || (isset($_GET['flush']) && (Director::isDev() || Director::is_cli() || Permission::check('ADMIN')))
359 ) {
360 if(isset($_GET['debug_profile'])) Profiler::mark("SSViewer::process - compile", " for $template");
361
362 $content = file_get_contents($template);
363 $content = SSViewer::parseTemplateContent($content, $template);
364
365 $fh = fopen($cacheFile,'w');
366 fwrite($fh, $content);
367 fclose($fh);
368
369 if(isset($_GET['debug_profile'])) Profiler::unmark("SSViewer::process - compile", " for $template");
370 }
371
372
373 if(isset($_GET['showtemplate']) && !Director::isLive()) {
374 $lines = file($cacheFile);
375 echo "<h2>Template: $cacheFile</h2>";
376 echo "<pre>";
377 foreach($lines as $num => $line) {
378 echo str_pad($num+1,5) . htmlentities($line, ENT_COMPAT, 'UTF-8');
379 }
380 echo "</pre>";
381 }
382
383
384 foreach(array('Content', 'Layout') as $subtemplate) {
385 if(isset($this->chosenTemplates[$subtemplate])) {
386 $subtemplateViewer = new SSViewer($this->chosenTemplates[$subtemplate]);
387 $item = $item->customise(array(
388 $subtemplate => $subtemplateViewer->process($item, $cache)
389 ));
390 }
391 }
392
393 $itemStack = array();
394 $val = "";
395 $valStack = array();
396
397 include($cacheFile);
398
399 $output = $val;
400 $output = Requirements::includeInHTML($template, $output);
401
402 array_pop(SSViewer::$topLevel);
403
404 if(isset($_GET['debug_profile'])) Profiler::unmark("SSViewer::process", " for $template");
405
406
407 if($this->rewriteHashlinks && self::$options['rewriteHashlinks']) {
408 if(strpos($output, '<base') !== false) {
409 if(SSViewer::$options['rewriteHashlinks'] === 'php') {
410 $thisURLRelativeToBase = "<?php echo \$_SERVER['REQUEST_URI']; ?>";
411 } else {
412 $thisURLRelativeToBase = Director::makeRelative(Director::absoluteURL($_SERVER['REQUEST_URI']));
413 }
414 $output = preg_replace('/(<a[^>]+href *= *)"#/i', '\\1"' . $thisURLRelativeToBase . '#', $output);
415 }
416 }
417
418 return $output;
419 }
420
421 static function parseTemplateContent($content, $template="") {
422
423
424 if(substr($content, 0,3) == pack("CCC", 0xef, 0xbb, 0xbf)) {
425 $content = substr($content, 3);
426 }
427
428
429 if(Director::isDev() && self::$source_file_comments && $template && stripos($content, "<?xml") === false) {
430
431 if(stripos($content, "<html") !== false) {
432 $content = preg_replace('/(<html[^>]*>)/i', "\\1<!-- template $template -->", $content);
433 $content = preg_replace('/(<\/html[^>]*>)/i', "\\1<!-- end template $template -->", $content);
434 } else {
435 $content = "<!-- template $template -->\n" . $content . "\n<!-- end template $template -->";
436 }
437 }
438
439 while(true) {
440 $oldContent = $content;
441
442
443 if(Director::isDev() && self::$source_file_comments) $replacementCode = 'return "<!-- include " . SSViewer::getTemplateFile($matches[1]) . " -->\n"
444 . SSViewer::getTemplateContent($matches[1])
445 . "\n<!-- end include " . SSViewer::getTemplateFile($matches[1]) . " -->";';
446 else $replacementCode = 'return SSViewer::getTemplateContent($matches[1]);';
447
448 $content = preg_replace_callback('/<' . '% include +([A-Za-z0-9_]+) +%' . '>/', create_function(
449 '$matches', $replacementCode
450 ), $content);
451 if($oldContent == $content) break;
452 }
453
454
455 $replacements = array(
456 '/<%--.*--%>/U' => '',
457 '/\$Iteration/' => '<?= {dlr}key ?>',
458
459 '/{\\$([A-Za-z_][A-Za-z0-9_]*)\\(([^),]+), *([^),]+), *([^),]+)\\)}/' => '<?= {dlr}item->XML_val("\\1",array("\\2","\\3","\\4"),true) ?>',
460
461 '/{\\$([A-Za-z_][A-Za-z0-9_]*)\\(([^),]+), *([^),]+)\\)\\.([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+)}/' => '<?= {dlr}item->obj("\\1",array("\\2","\\3"),true)->obj("\\4",null,true)->XML_val("\\5",null,true) ?>',
462
463 '/{\\$([A-Za-z_][A-Za-z0-9_]*)\\(([^),]+), *([^),]+)\\)\\.([A-Za-z0-9_]+)}/' => '<?= {dlr}item->obj("\\1",array("\\2","\\3"),true)->XML_val("\\4",null,true) ?>',
464
465 '/{\\$([A-Za-z_][A-Za-z0-9_]*)\\(([^),]+), *([^),]+)\\)}/' => '<?= {dlr}item->XML_val("\\1",array("\\2","\\3"),true) ?>',
466
467 '/{\\$([A-Za-z_][A-Za-z0-9_]*)\\(([^),]+)\\)\\.([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+)}/' => '<?= {dlr}item->obj("\\1",array("\\2"),true)->obj("\\3",null,true)->XML_val("\\4",null,true) ?>',
468
469 '/{\\$([A-Za-z_][A-Za-z0-9_]*)\\(([^),]+)\\)\\.([A-Za-z0-9_]+)}/' => '<?= {dlr}item->obj("\\1",array("\\2"),true)->XML_val("\\3",null,true) ?>',
470
471 '/{\\$([A-Za-z_][A-Za-z0-9_]*)\\(([^),]+)\\)}/' => '<?= {dlr}item->XML_val("\\1",array("\\2"),true) ?>',
472
473 '/{\\$([A-Za-z_][A-Za-z0-9_]*)\\.([A-Za-z0-9_]+)\\(([^),]+)\\)\\.([A-Za-z0-9_]+)}/' => '<?= {dlr}item->obj("\\1",null,true)->obj("\\2",array("\\3"),true)->XML_val("\\4",null,true) ?>',
474
475 '/{\\$([A-Za-z_][A-Za-z0-9_]*)\\.([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+)}/' => '<?= {dlr}item->obj("\\1",null,true)->XML_val("\\2",array("\\3"),true) ?>',
476
477 '/{\\$([A-Za-z_][A-Za-z0-9_]*)\\.([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+)}/' => '<?= {dlr}item->obj("\\1",null,true)->obj("\\2",null,true)->XML_val("\\3",null,true) ?>',
478
479 '/{\\$([A-Za-z_][A-Za-z0-9_]*)\\.([A-Za-z0-9_]+)}/' => '<?= {dlr}item->obj("\\1",null,true)->XML_val("\\2",null,true) ?>',
480
481 '/{\\$([A-Za-z_][A-Za-z0-9_]*)}/' => '<?= {dlr}item->XML_val("\\1",null,true) ?>\\2',
482
483
484 '/\\$([A-Za-z_][A-Za-z0-9_]*)\\(([^),]+), *([^),]+), *([^),]+)\\)/' => '<?= {dlr}item->XML_val("\\1",array("\\2","\\3","\\4"),true) ?>',
485 '/\\$([A-Za-z_][A-Za-z0-9_]*)\\.([A-Za-z0-9_]+)\\(([^),]+)\\)([^A-Za-z0-9]|$)/' => '<?= {dlr}item->obj("\\1")->XML_val("\\2",array("\\3"),true) ?>\\4',
486 '/\\$([A-Za-z_][A-Za-z0-9_]*)\\.([A-Za-z0-9_]+)\\(([^),]+), *([^),]+)\\)([^A-Za-z0-9]|$)/' => '<?= {dlr}item->obj("\\1")->XML_val("\\2",array("\\3", "\\4"),true) ?>\\5',
487
488 '/\\$([A-Za-z_][A-Za-z0-9_]*)\\(([^),]+), *([^),]+)\\)\\.([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+)([^A-Za-z0-9]|$)/' => '<?= {dlr}item->obj("\\1",array("\\2","\\3"),true)->obj("\\4",null,true)->XML_val("\\5",null,true) ?>\\6',
489 '/\\$([A-Za-z_][A-Za-z0-9_]*)\\(([^),]+), *([^),]+)\\)\\.([A-Za-z0-9_]+)([^A-Za-z0-9]|$)/' => '<?= {dlr}item->obj("\\1",array("\\2","\\3"),true)->XML_val("\\4",null,true) ?>\\5',
490 '/\\$([A-Za-z_][A-Za-z0-9_]*)\\(([^),]+), *([^),]+)\\)([^A-Za-z0-9]|$)/' => '<?= {dlr}item->XML_val("\\1",array("\\2","\\3"),true) ?>\\4',
491 '/\\$([A-Za-z_][A-Za-z0-9_]*)\\(([^),]+)\\)\\.([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+)([^A-Za-z0-9]|$)/' => '<?= {dlr}item->obj("\\1",array("\\2"),true)->obj("\\3",null,true)->XML_val("\\4",null,true) ?>\\5',
492 '/\\$([A-Za-z_][A-Za-z0-9_]*)\\(([^),]+)\\)\\.([A-Za-z0-9_]+)([^A-Za-z0-9]|$)/' => '<?= {dlr}item->obj("\\1",array("\\2"),true)->XML_val("\\3",null,true) ?>\\4',
493 '/\\$([A-Za-z_][A-Za-z0-9_]*)\\(([^),]+)\\)([^A-Za-z0-9]|$)/' => '<?= {dlr}item->XML_val("\\1",array("\\2"),true) ?>\\3',
494 '/\\$([A-Za-z_][A-Za-z0-9_]*)\\.([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+)([^A-Za-z0-9]|$)/' => '<?= {dlr}item->obj("\\1",null,true)->obj("\\2",null,true)->XML_val("\\3",null,true) ?>\\4',
495 '/\\$([A-Za-z_][A-Za-z0-9_]*)\\.([A-Za-z0-9_]+)([^A-Za-z0-9]|$)/' => '<?= {dlr}item->obj("\\1",null,true)->XML_val("\\2",null,true) ?>\\3',
496 '/\\$([A-Za-z_][A-Za-z0-9_]*)([^A-Za-z0-9]|$)/' => '<?= {dlr}item->XML_val("\\1",null,true) ?>\\2',
497 );
498
499 $content = preg_replace(array_keys($replacements), array_values($replacements), $content);
500 $content = str_replace('{dlr}','$',$content);
501
502
503 $content = SSViewer_PartialParser::process($template, $content);
504
505
506 $content = ereg_replace('<!-- +pc +([A-Za-z0-9_(),]+) +-->', '<' . '% control \\1 %' . '>', $content);
507 $content = ereg_replace('<!-- +pc_end +-->', '<' . '% end_control %' . '>', $content);
508
509
510 $content = ereg_replace('<' . '% +select +([A-Za-z0-9_]+) +%' . '>', '<? array_push($itemStack, $item); if ($item = $item->obj("\\1")) { ?>', $content);
511
512 $content = ereg_replace('<' . '% +select +([A-Za-z0-9_]+)\\(([^),]+)\\) +%' . '>', '<? array_push($itemStack, $item); if ($item = $item->obj("\\1", array("\\2"))) { ?>', $content);
513
514 $content = ereg_replace('<' . '% +select +([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+) +%' . '>', '<? array_push($itemStack, $item); if ($item = $item->obj("\\1")->hasValue("\\2")) { ?>', $content);
515
516 $content = ereg_replace('<' . '% +select +([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+)\\(([^),]+)\\) +%' . '>', '<? array_push($itemStack, $item); if (($item = $item->obj("\\1")) && ($item = $item->obj("\\2", array("\\3")))) { ?>', $content);
517 $content = ereg_replace('<' . '% +end_select +%' . '>', '<? } $item = array_pop($itemStack); ?>', $content);
518
519
520 $content = ereg_replace('<' . '% +control +([A-Za-z0-9_]+) +%' . '>', '<? array_push($itemStack, $item); if($loop = $item->obj("\\1")) foreach($loop as $key => $item) { ?>', $content);
521
522 $content = ereg_replace('<' . '% +control +([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+) +%' . '>', '<? array_push($itemStack, $item); if(($loop = $item->obj("\\1")) && ($loop = $loop->obj("\\2"))) foreach($loop as $key => $item) { ?>', $content);
523
524 $content = ereg_replace('<' . '% +control +([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+)\\(([^),]+)\\) +%' . '>', '<? array_push($itemStack, $item); if(($loop = $item->obj("\\1")) && ($loop = $loop->obj("\\2", array("\\3")))) foreach($loop as $key => $item) { ?>', $content);
525
526 $content = ereg_replace('<' . '% +control +([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+)\\(([^),]+), *([^),]+)\\) +%' . '>', '<? array_push($itemStack, $item); if(($loop = $item->obj("\\1")) && ($loop = $loop->obj("\\2", array("\\3", "\\4")))) foreach($loop as $key => $item) { ?>', $content);
527
528 $content = ereg_replace('<' . '% +control +([A-Za-z0-9_]+)\\(([^),]+)\\) +%' . '>', '<? array_push($itemStack, $item); if($loop = $item->obj("\\1", array("\\2"))) foreach($loop as $key => $item) { ?>', $content);
529
530 $content = ereg_replace('<' . '% +control +([A-Za-z0-9_]+)\\(([^),]+), *([^),]+)\\) +%' . '>', '<? array_push($itemStack, $item); if($loop = $item->obj("\\1", array("\\2","\\3"))) foreach($loop as $key => $item) { ?>', $content);
531
532 $content = ereg_replace('<' . '% +control +([A-Za-z0-9_]+)\\(([^),]+), *([^),]+), *([^),]+)\\) +%' . '>', '<? array_push($itemStack, $item); if($loop = $item->obj("\\1", array("\\2", "\\3", "\\4"))) foreach($loop as $key => $item) { ?>', $content);
533 $content = ereg_replace('<' . '% +end_control +%' . '>', '<? } $item = array_pop($itemStack); ?>', $content);
534
535
536 $content = ereg_replace('<' . '% +debug +%' . '>', '<? Debug::show($item) ?>', $content);
537
538 $content = ereg_replace('<' . '% +debug +([A-Za-z0-9_]+) +%' . '>', '<? Debug::show($item->cachedCall("\\1")) ?>', $content);
539
540
541 $content = ereg_replace('<' . '% +if +([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+) +%' . '>', '<? if($item->obj("\\1",null,true)->hasValue("\\2")) { ?>', $content);
542
543
544 $content = ereg_replace('<' . '% +if +([A-Za-z0-9_]+)\\(([^),]+)\\) +%' . '>', '<? if($item->hasValue("\\1",array("\\2"))) { ?>', $content);
545
546 $content = ereg_replace('<' . '% +if +([A-Za-z0-9_]+)\\(([^),]+), *([^),]+)\\) +%' . '>', '<? if($item->hasValue("\\1",array("\\2","\\3"))) { ?>', $content);
547
548 $content = ereg_replace('<' . '% +if +([A-Za-z0-9_]+)\\(([^),]+)\\)\\.([A-Za-z0-9_]+) +%' . '>', '<? if ($item->obj("\\1",array("\\2"),true)->hasValue("\\3")) { ?>', $content);
549
550
551 $content = ereg_replace('<' . '% +if +([A-Za-z0-9_]+) +%' . '>', '<? if($item->hasValue("\\1")) { ?>', $content);
552 $content = ereg_replace('<' . '% +else_if +([A-Za-z0-9_]+) +%' . '>', '<? } else if($item->hasValue("\\1")) { ?>', $content);
553
554
555 $content = ereg_replace('<' . '% +if +([A-Za-z0-9_]+) *\\|\\|? *([A-Za-z0-9_]+) +%' . '>', '<? if($item->hasValue("\\1") || $item->hasValue("\\2")) { ?>', $content);
556 $content = ereg_replace('<' . '% +else_if +([A-Za-z0-9_]+) *\\|\\|? *([A-Za-z0-9_]+) +%' . '>', '<? } else if($item->hasValue("\\1") || $item->hasValue("\\2")) { ?>', $content);
557
558
559 $content = ereg_replace('<' . '% +if +([A-Za-z0-9_]+) *&&? *([A-Za-z0-9_]+) +%' . '>', '<? if($item->hasValue("\\1") && $item->hasValue("\\2")) { ?>', $content);
560 $content = ereg_replace('<' . '% +else_if +([A-Za-z0-9_]+) *&&? *([A-Za-z0-9_]+) +%' . '>', '<? } else if($item->hasValue("\\1") && $item->hasValue("\\2")) { ?>', $content);
561
562
563 $content = ereg_replace('<' . '% +if +([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+) *\\|\\|? *([A-Za-z0-9_]+) +%' . '>', '<? if($item->obj("\\1",null,true)->hasValue("\\2") || $item->hasValue("\\3")) { ?>', $content);
564 $content = ereg_replace('<' . '% +else_if +([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+) *\\|\\|? *([A-Za-z0-9_]+) +%' . '>', '<? else if($item->obj("\\1",null,true)->hasValue("\\2") || $item->hasValue("\\3")) { ?>', $content);
565
566
567 $content = ereg_replace('<' . '% +if +([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+) *&&? *([A-Za-z0-9_]+) +%' . '>', '<? if($item->obj("\\1",null,true)->hasValue("\\2") && $item->hasValue("\\3")) { ?>', $content);
568 $content = ereg_replace('<' . '% +else_if +([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+) *&&? *([A-Za-z0-9_]+) +%' . '>', '<? else if($item->obj("\\1",null,true)->hasValue("\\2") && $item->hasValue("\\3")) { ?>', $content);
569
570
571 $content = ereg_replace('<' . '% +if +([A-Za-z0-9_]+) *==? *"?([A-Za-z0-9_-]+)"? +%' . '>', '<? if($item->XML_val("\\1",null,true) == "\\2") { ?>', $content);
572 $content = ereg_replace('<' . '% +else_if +([A-Za-z0-9_]+) *==? *"?([A-Za-z0-9_-]+)"? +%' . '>', '<? } else if($item->XML_val("\\1",null,true) == "\\2") { ?>', $content);
573
574
575 $content = ereg_replace('<' . '% +if +([A-Za-z0-9_]+) *> *"?([A-Za-z0-9_-]+)"? +%' . '>', '<? if($item->XML_val("\\1",null,true) > "\\2") { ?>', $content);
576 $content = ereg_replace('<' . '% +else_if +([A-Za-z0-9_]+) *> *"?([A-Za-z0-9_-]+)"? +%' . '>', '<? } else if($item->XML_val("\\1",null,true) > "\\2") { ?>', $content);
577
578
579 $content = ereg_replace('<' . '% +if +([A-Za-z0-9_]+) *< *"?([A-Za-z0-9_-]+)"? +%' . '>', '<? if($item->XML_val("\\1",null,true) < "\\2") { ?>', $content);
580 $content = ereg_replace('<' . '% +else_if +([A-Za-z0-9_]+) *< *"?([A-Za-z0-9_-]+)"? +%' . '>', '<? } else if($item->XML_val("\\1",null,true) < "\\2") { ?>', $content);
581
582
583 $content = ereg_replace('<' . '% +if +([A-Za-z0-9_]+) *>= *"?([A-Za-z0-9_-]+)"? +%' . '>', '<? if($item->XML_val("\\1",null,true) >= "\\2") { ?>', $content);
584 $content = ereg_replace('<' . '% +else_if +([A-Za-z0-9_]+) *>= *"?([A-Za-z0-9_-]+)"? +%' . '>', '<? } else if($item->XML_val("\\1",null,true) > "\\2") { ?>', $content);
585
586
587 $content = ereg_replace('<' . '% +if +([A-Za-z0-9_]+) *<= *"?([A-Za-z0-9_-]+)"? +%' . '>', '<? if($item->XML_val("\\1",null,true) <= "\\2") { ?>', $content);
588 $content = ereg_replace('<' . '% +else_if +([A-Za-z0-9_]+) *<= *"?([A-Za-z0-9_-]+)"? +%' . '>', '<? } else if($item->XML_val("\\1",null,true) < "\\2") { ?>', $content);
589
590
591 $content = ereg_replace('<' . '% +if +([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+) *==? *"?([A-Za-z0-9_-]+)"? +%' . '>', '<? if($item->obj("\\1",null,true)->XML_val("\\2") == "\\3") { ?>', $content);
592 $content = ereg_replace('<' . '% +else_if +([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+) *==? *"?([A-Za-z0-9_-]+)"? +%' . '>', '<? } else if($item->obj("\\1",null,true)->XML_val("\\2") == "\\3") { ?>', $content);
593
594
595 $content = ereg_replace('<' . '% +if +([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+) *> *"?([A-Za-z0-9_-]+)"? +%' . '>', '<? if($item->obj("\\1",null,true)->XML_val("\\2") > "\\3") { ?>', $content);
596 $content = ereg_replace('<' . '% +else_if +([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+) *> *"?([A-Za-z0-9_-]+)"? +%' . '>', '<? } else if($item->obj("\\1",null,true)->XML_val("\\2") > "\\3") { ?>', $content);
597
598
599 $content = ereg_replace('<' . '% +if +([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+) *< *"?([A-Za-z0-9_-]+)"? +%' . '>', '<? if($item->obj("\\1",null,true)->XML_val("\\2") < "\\3") { ?>', $content);
600 $content = ereg_replace('<' . '% +else_if +([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+) *< *"?([A-Za-z0-9_-]+)"? +%' . '>', '<? } else if($item->obj("\\1",null,true)->XML_val("\\2") < "\\3") { ?>', $content);
601
602
603 $content = ereg_replace('<' . '% +if +([A-Za-z0-9_]+)\\(([^),]+)\\) *==? *"?([A-Za-z0-9_-]+)"? +%' . '>', '<? if($item->XML_val("\\1",array("\\2"),true) == "\\3") { ?>', $content);
604 $content = ereg_replace('<' . '% +else_if +([A-Za-z0-9_]+)\\(([^),]+)\\) *==? *"?([A-Za-z0-9_-]+)"? +%' . '>', '<? } else if($item->XML_val("\\1",array("\\2"),true) == "\\3") { ?>', $content);
605
606
607 $content = ereg_replace('<' . '% +if +([A-Za-z0-9_]+)\\(([^),]+)\\)\\.([A-Za-z0-9_]+) *==? *"?([A-Za-z0-9_-]+)"? +%' . '>', '<? if($item->obj("\\1",array("\\2"),true)->XML_val("\\3") == "\\4") { ?>', $content);
608 $content = ereg_replace('<' . '% +else_if +([A-Za-z0-9_]+)\\(([^),]+)\\)\\.([A-Za-z0-9_]+) *==? *"?([A-Za-z0-9_-]+)"? +%' . '>', '<? } else if($item->obj("\\1",array("\\2"),true)->XML_val("\\3") == "\\4") { ?>', $content);
609
610
611 $content = ereg_replace('<' . '% +if +([A-Za-z0-9_]+) *!= *"?([A-Za-z0-9_-]+)"? +%' . '>', '<? if($item->XML_val("\\1",null,true) != "\\2") { ?>', $content);
612 $content = ereg_replace('<' . '% +else_if +([A-Za-z0-9_]+) *!= *"?([A-Za-z0-9_-]+)"? +%' . '>', '<? } else if($item->XML_val("\\1",null,true) != "\\2") { ?>', $content);
613
614 $content = ereg_replace('<' . '% +else_if +([A-Za-z0-9_]+) +%' . '>', '<? } else if(($test = $item->cachedCall("\\1")) && ((!is_object($test) && $test) || ($test && $test->exists()) )) { ?>', $content);
615
616 $content = ereg_replace('<' . '% +if +([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+) +%' . '>', '<? $test = $item->obj("\\1",null,true)->cachedCall("\\2"); if((!is_object($test) && $test) || ($test && $test->exists())) { ?>', $content);
617 $content = ereg_replace('<' . '% +else_if +([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+) +%' . '>', '<? } else if(($test = $item->obj("\\1",null,true)->cachedCall("\\2")) && ((!is_object($test) && $test) || ($test && $test->exists()) )) { ?>', $content);
618
619 $content = ereg_replace('<' . '% +if +([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+) +%' . '>', '<? $test = $item->obj("\\1",null,true)->obj("\\2",null,true)->cachedCall("\\3"); if((!is_object($test) && $test) || ($test && $test->exists())) { ?>', $content);
620 $content = ereg_replace('<' . '% +else_if +([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+)\\.([A-Za-z0-9_]+) +%' . '>', '<? } else if(($test = $item->obj("\\1",null,true)->obj("\\2",null,true)->cachedCall("\\3")) && ((!is_object($test) && $test) || ($test && $test->exists()) )) { ?>', $content);
621
622 $content = ereg_replace('<' . '% +else +%' . '>', '<? } else { ?>', $content);
623 $content = ereg_replace('<' . '% +end_if +%' . '>', '<? } ?>', $content);
624
625
626
627 ereg('.*[\/](.*)',$template,$path);
628
629
630
631
632
633 $content = ereg_replace('<' . '% +_t\((\'([^\.\']*)\'|"([^\."]*)")(([^)]|\)[^ ]|\) +[^% ])*)\) +%' . '>', '<?= _t(\''. $path[1] . '.\\2\\3\'\\4) ?>', $content);
634
635 $content = ereg_replace('<' . '% +_t\((\'([^\']*)\'|"([^"]*)")(([^)]|\)[^ ]|\) +[^% ])*)\) +%' . '>', '<?= _t(\'\\2\\3\'\\4) ?>', $content);
636
637
638 $content = ereg_replace('<' . '% +sprintf\(_t\((\'([^\.\']*)\'|"([^\."]*)")(([^)]|\)[^ ]|\) +[^% ])*)\), *\<\?= +([^\?]*) +\?\>) +%' . '>', '<?= sprintf(_t(\''. $path[1] . '.\\2\\3\'\\4),\\6) ?>', $content);
639
640 $content = ereg_replace('<' . '% +sprintf\(_t\((\'([^\']*)\'|"([^"]*)")(([^)]|\)[^ ]|\) +[^% ])*)\), *\<\?= +([^\?]*) +\?\>) +%' . '>', '<?= sprintf(_t(\'\\2\\3\'\\4),\\6) ?>', $content);
641
642
643 $content = ereg_replace('<' . '% +base_tag +%' . '>', '<?= SSViewer::get_base_tag($val); ?>', $content);
644
645 $content = ereg_replace('<' . '% +current_page +%' . '>', '<?= @$_SERVER["REQUEST_URI"] ?>', $content);
646
647
648 $content = preg_replace('/<% +require +([a-zA-Z]+)(?:\(([^),]+)\))? +%>/', '<? Requirements::\\1("\\2"); ?>', $content);
649 $content = preg_replace('/<% +require +([a-zA-Z]+)\(([^),]+), *([^),]+)\) +%>/', '<? Requirements::\\1("\\2", "\\3"); ?>', $content);
650
651
652 $content = ereg_replace('<!-- +if +([A-Za-z0-9_]+) +-->', '<? if($item->cachedCall("\\1")) { ?>', $content);
653 $content = ereg_replace('<!-- +else +-->', '<? } else { ?>', $content);
654 $content = ereg_replace('<!-- +if_end +-->', '<? } ?>', $content);
655
656
657 $content = ereg_replace('href *= *"#', 'href="<?= SSViewer::$options[\'rewriteHashlinks\'] ? Convert::raw2att( $_SERVER[\'REQUEST_URI\'] ) : "" ?>#', $content);
658
659
660 $content = ereg_replace('<\?xml([^>]+)\?' . '>', '<##xml\\1##>', $content);
661
662
663 $content = str_replace('<?=',"\nSSVIEWER;\n\$val .= ", $content);
664 $content = str_replace('<?',"\nSSVIEWER;\n", $content);
665 $content = str_replace('?>',";\n \$val .= <<<SSVIEWER\n", $content);
666
667 $output = "<?php\n";
668 $output .= '$val .= <<<SSVIEWER' . "\n" . $content . "\nSSVIEWER;\n";
669
670
671 $output = ereg_replace('<##xml([^>]+)##>', '<' . '?xml\\1?' . '>', $output);
672
673 return $output;
674 }
675
676 677 678 679
680 public function templates() {
681 return $this->chosenTemplates;
682 }
683
684 685 686 687
688 public function setTemplateFile($type, $file) {
689 $this->chosenTemplates[$type] = $file;
690 }
691
692 693 694 695 696 697 698
699 static function get_base_tag($contentGeneratedSoFar) {
700 $base = Director::absoluteBaseURL();
701
702
703 if(preg_match('/<!DOCTYPE[^>]+xhtml/i', $contentGeneratedSoFar)) {
704 return "<base href=\"$base\"></base>";
705 } else {
706 return "<base href=\"$base\"><!--[if lte IE 6]></base><![endif]-->";
707 }
708 }
709 }
710
711 712 713 714 715
716 class SSViewer_FromString extends SSViewer {
717 protected $content;
718
719 public function __construct($content) {
720 $this->content = $content;
721 }
722
723 public function process($item, $cache = null) {
724 $template = SSViewer::parseTemplateContent($this->content, "string sha1=".sha1($this->content));
725
726 $tmpFile = tempnam(TEMP_FOLDER,"");
727 $fh = fopen($tmpFile, 'w');
728 fwrite($fh, $template);
729 fclose($fh);
730
731 if(isset($_GET['showtemplate']) && $_GET['showtemplate']) {
732 $lines = file($tmpFile);
733 echo "<h2>Template: $tmpFile</h2>";
734 echo "<pre>";
735 foreach($lines as $num => $line) {
736 echo str_pad($num+1,5) . htmlentities($line);
737 }
738 echo "</pre>";
739 }
740
741 $itemStack = array();
742 $val = "";
743 $valStack = array();
744
745 $cache = SS_Cache::factory('cacheblock');
746
747 include($tmpFile);
748 unlink($tmpFile);
749
750
751 return $val;
752 }
753 }
754
755 756 757 758 759 760 761 762 763 764 765 766
767 class SSViewer_PartialParser {
768
769 static $tag = '/< % [ \t]+ (cached|cacheblock|uncached|end_cached|end_cacheblock|end_uncached) [ \t]+ ([^%]+ [ \t]+)? % >/xS';
770
771 static $argument_splitter = '/^\s*
772 # The argument itself
773 (
774 (?P<conditional> if | unless ) | # The if or unless keybreak
775 (?P<property> (?P<identifier> \w+) \s* # A property lookup or a function call
776 ( \( (?P<arguments> [^\)]*) \) )?
777 ) |
778 (?P<sqstring> \' (\\\'|[^\'])+ \' ) | # A string surrounded by \'
779 (?P<dqstring> " (\\"|[^"])+ " ) # A string surrounded by "
780 )
781 # Some seperator after the argument
782 (
783 \s*(?P<comma>,)\s* | # A comma (maybe with whitespace before or after)
784 (?P<fullstop>\.) # A period (no whitespace before)
785 )?
786 /xS';
787
788 static function process($template, $content) {
789 $parser = new SSViewer_PartialParser($template, $content, 0);
790 $parser->parse();
791 return $parser->generate();
792 }
793
794 function __construct($template, $content, $offset) {
795 $this->template = $template;
796 $this->content = $content;
797 $this->offset = $offset;
798
799 $this->blocks = array();
800 }
801
802 function controlcheck($text) {
803
804 }
805
806 function parse() {
807 $current_tag_offset = 0;
808
809 while (preg_match(self::$tag, $this->content, $matches, PREG_OFFSET_CAPTURE, $this->offset)) {
810 $tag = $matches[1][0];
811
812 $startpos = $matches[0][1];
813 $endpos = $matches[0][1] + strlen($matches[0][0]);
814
815 switch($tag) {
816 case 'cached':
817 case 'uncached':
818 case 'cacheblock':
819
820 $pretext = substr($this->content, $this->offset, $startpos - $this->offset);
821 $this->controlcheck($pretext);
822 $this->blocks[] = $pretext;
823
824 if ($tag == 'cached' || $tag == 'cacheblock') {
825 list($keyparts, $conditional, $condition) = $this->parseargs(@$matches[2][0]);
826 $parser = new SSViewer_Cached_PartialParser($this->template, $this->content, $endpos, $keyparts, $conditional, $condition);
827 }
828 else {
829 $parser = new SSViewer_PartialParser($this->template, $this->content, $endpos);
830 }
831
832 $parser->parse();
833 $this->blocks[] = $parser;
834 $this->offset = $parser->offset;
835 break;
836
837 case 'end_cached':
838 case 'end_cacheblock':
839 case 'end_uncached':
840 $this->blocks[] = substr($this->content, $this->offset, $startpos - $this->offset);
841 $this->content = null;
842
843 $this->offset = $endpos;
844 return $this;
845 }
846 }
847
848 $this->blocks[] = substr($this->content, $this->offset);
849 $this->content = null;
850 }
851
852 function parseargs($string) {
853 preg_match_all(self::$argument_splitter, $string, $matches, PREG_SET_ORDER);
854
855 $parts = array();
856 $conditional = null; $condition = null;
857
858 $current = '$item->';
859
860 while (strlen($string) && preg_match(self::$argument_splitter, $string, $match)) {
861
862 $string = substr($string, strlen($match[0]));
863
864
865 if (@$match['conditional']) {
866 $conditional = $match['conditional'];
867 continue;
868 }
869
870
871 if (@$match['property']) {
872
873 $what = $match['identifier'];
874 $args = array();
875
876
877 if (@$match['arguments']) {
878 foreach (explode(',', $match['arguments']) as $arg) {
879 $args[] = is_numeric($arg) ? (string)$arg : '"'.$arg.'"';
880 }
881 }
882
883 $args = empty($args) ? 'null' : 'array('.implode(',',$args).')';
884
885
886 if (@$match['fullstop']) {
887 $current .= "obj('$what', $args, true)->";
888 }
889
890 else {
891 $accessor = $current . "XML_val('$what', $args, true)"; $current = '$item->';
892
893
894 if ($conditional) {
895 $condition = $accessor;
896 break;
897 }
898
899 else $parts[] = $accessor;
900 }
901 }
902
903
904 else if (@$match['sqstring']) $parts[] = $match['sqstring'];
905 else if (@$match['dqstring']) $parts[] = $match['dqstring'];
906 }
907
908 if ($conditional && !$condition) {
909 throw new Exception("You need to have a condition after the conditional $conditional in your cache block");
910 }
911
912 return array($parts, $conditional, $condition);
913 }
914
915 function generate() {
916 $res = array();
917
918 foreach ($this->blocks as $i => $block) {
919 if ($block instanceof SSViewer_PartialParser)
920 $res[] = $block->generate();
921 else {
922 $res[] = $block;
923 }
924 }
925
926 return implode('', $res);
927 }
928 }
929
930 931 932 933
934 class SSViewer_Cached_PartialParser extends SSViewer_PartialParser {
935
936 function __construct($template, $content, $offset, $keyparts, $conditional, $condition) {
937 $this->keyparts = $keyparts;
938 $this->conditional = $conditional;
939 $this->condition = $condition;
940
941 parent::__construct($template, $content, $offset);
942 }
943
944 function controlcheck($text) {
945 $ifs = preg_match_all('/<'.'% +if +/', $text, $matches);
946 $end_ifs = preg_match_all('/<'.'% +end_if +/', $text, $matches);
947
948 if ($ifs != $end_ifs) throw new Exception('You can\'t have cached or uncached blocks within condition structures');
949
950 $controls = preg_match_all('/<'.'% +control +/', $text, $matches);
951 $end_controls = preg_match_all('/<'.'% +end_control +/', $text, $matches);
952
953 if ($controls != $end_controls) throw new Exception('You can\'t have cached or uncached blocks within control structures');
954 }
955
956 function key() {
957 if (empty($this->keyparts)) return "''";
958 return 'sha1(' . implode(".'_'.", $this->keyparts) . ')';
959 }
960
961 function generate() {
962 $res = array();
963 $key = $this->key();
964
965 $condition = "";
966
967 switch ($this->conditional) {
968 case 'if':
969 $condition = "{$this->condition} && ";
970 break;
971 case 'unless':
972 $condition = "!({$this->condition}) && ";
973 break;
974 }
975
976
977
978 foreach ($this->blocks as $i => $block) {
979 if ($block instanceof SSViewer_PartialParser)
980 $res[] = $block->generate();
981 else {
982
983
984 $partialkey = "'".sha1($this->template . $block)."_'.$key.'_$i'";
985
986
987 $res[] = "<?\n".'if ('.$condition.' ($partial = $cache->load('.$partialkey.'))) $val .= $partial;'."\n";
988
989
990 $res[] = "else {\n";
991 $res[] = '$oldval = $val; $val = "";'."\n";
992 $res[] = "\n?>" . $block . "<?\n";
993 $res[] = $condition . ' $cache->save($val); $val = $oldval . $val ;'."\n";
994 $res[] = "}\n?>";
995 }
996 }
997
998 return implode('', $res);
999 }
1000 }
1001
1002 function supressOutput() {
1003 return "";
1004 }
1005
1006 ?>
[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the
Silverstripe Forums to ask development related questions.
-