1: <?php
2: /**
3: * This file contains the former template class.
4: *
5: * @package Core
6: * @subpackage GUI
7: * @author Jan Lengowski
8: * @author Stefan Jelner
9: * @copyright four for business AG <www.4fb.de>
10: * @license http://www.contenido.org/license/LIZENZ.txt
11: * @link http://www.4fb.de
12: * @link http://www.contenido.org
13: */
14:
15: defined('CON_FRAMEWORK') || die('Illegal call: Missing framework initialization - request aborted.');
16:
17: /**
18: * class Template
19: * Light template mechanism
20: *
21: * @package Core
22: * @subpackage GUI
23: */
24: class cTemplate {
25:
26: /**
27: * Needles (static)
28: *
29: * @var array
30: */
31: public $needles = array();
32:
33: /**
34: * Replacements (static)
35: *
36: * @var array
37: */
38: public $replacements = array();
39:
40: /**
41: * Dyn_Needles (dynamic)
42: *
43: * @var array
44: */
45: public $Dyn_needles = array();
46:
47: /**
48: * Dyn_Replacements (dynamic)
49: *
50: * @var array
51: */
52: public $Dyn_replacements = array();
53:
54:
55: /**
56: * Dynamic counter
57: *
58: * @var int
59: */
60: public $dyn_cnt = 0;
61:
62: /**
63: * Tags array (for dynamic blocks);
64: *
65: * @var array
66: */
67: public $tags = array(
68: 'static' => '{%s}',
69: 'start' => '<!-- BEGIN:BLOCK -->',
70: 'end' => '<!-- END:BLOCK -->'
71: );
72:
73: /**
74: * gettext domain (default: contenido)
75: *
76: * @var string
77: */
78: protected $_sDomain = 'contenido';
79:
80: /**
81: * Constructor to create an instance of this class.
82: *
83: * @param array|bool $tags [optional]
84: */
85: public function __construct($tags = false) {
86: if (is_array($tags)) {
87: $this->tags = $tags;
88: }
89:
90: $this->setEncoding("");
91: }
92:
93: /**
94: * Sets the gettext domain to use for translations in a template
95: *
96: * @param string $sDomain
97: * Sets the domain to use for template translations
98: */
99: public function setDomain($sDomain) {
100: $this->_sDomain = $sDomain;
101: }
102:
103: /**
104: * Set Templates placeholders and values
105: *
106: * With this method you can replace the placeholders
107: * in the static templates with dynamic data.
108: *
109: * @param string $which
110: * 's' for Static or else dynamic
111: * @param string $needle
112: * Placeholder
113: * @param string $replacement
114: * Replacement String
115: */
116: public function set($which, $needle, $replacement) {
117: if ($which == 's') {
118: // static
119: $this->needles[] = sprintf($this->tags['static'], $needle);
120: $this->replacements[] = $replacement;
121: } else {
122: // dynamic
123: $this->Dyn_needles[$this->dyn_cnt][] = sprintf($this->tags['static'], $needle);
124: $this->Dyn_replacements[$this->dyn_cnt][] = $replacement;
125: }
126: }
127:
128: /**
129: * Sets an encoding for the template's head block.
130: *
131: * @param string $encoding
132: * Encoding to set
133: */
134: public function setEncoding($encoding) {
135: $this->_encoding = $encoding;
136: }
137:
138: /**
139: * Iterate internal counter by one
140: */
141: public function next() {
142: $this->dyn_cnt++;
143: }
144:
145: /**
146: * Reset template data
147: */
148: public function reset() {
149: $this->dyn_cnt = 0;
150: $this->needles = array();
151: $this->replacements = array();
152: $this->Dyn_needles = array();
153: $this->Dyn_replacements = array();
154: }
155:
156: /**
157: * Generate the template and print/return it.
158: * (do translations sequentially to save memory!!!)
159: *
160: * @param string $template
161: * Either template string or template file path
162: * @param bool $return [optional]
163: * Return or print template
164: * @param bool $note [optional]
165: * Echo "Generated by ... " Comment
166: * @return string|void
167: * Complete template string or nothing
168: */
169: public function generate($template, $return = false, $note = false) {
170: global $cCurrentModule, $cfg, $frontend_debug;
171:
172: $moduleHandler = NULL;
173: if (!is_null($cCurrentModule)) {
174: $moduleHandler = new cModuleHandler($cCurrentModule);
175: }
176:
177: // Check if the template is a file or a string
178: if (!@is_file($template)) {
179: if (is_object($moduleHandler) && is_file($moduleHandler->getTemplatePath($template))) {
180: // Module directory has higher priority
181: $content = $moduleHandler->getFilesContent('template', '', $template);
182: if ($frontend_debug['template_display']) {
183: echo('<!-- CTEMPLATE ' . $template . ' -->');
184: }
185: } else {
186: // Template is a string (it is a reference to save memory!!!)
187: $content = &$template;
188: }
189: } else {
190: if (is_object($moduleHandler) && is_file($moduleHandler->getTemplatePath($template))) {
191: // Module directory has higher priority
192: $content = $moduleHandler->getFilesContent('template', '', $template);
193: } else {
194: // Template is a file in template directory
195: $content = implode('', file($template));
196: }
197: }
198:
199: // prepend note
200: if ($note) {
201: $content = "<!-- Generated by CONTENIDO " . CON_VERSION . "-->\n" . $content;
202: }
203:
204: // CEC for template pre processing
205: $content = cApiCecHook::executeAndReturn('Contenido.Template.BeforeParse', $content, $this);
206:
207: $pieces = array();
208:
209: // Replace i18n strings before replacing other placeholders
210: $this->replacei18n($content, 'i18n');
211: $this->replacei18n($content, 'trans');
212:
213: // If content has dynamic blocks
214: $startQ = preg_quote($this->tags['start'], '/');
215: $endQ = preg_quote($this->tags['end'], '/');
216: if (preg_match('/^.*' . $startQ . '.*?' . $endQ . '.*$/s', $content)) {
217: // Split everything into an array
218: preg_match_all('/^(.*)' . $startQ . '(.*?)' . $endQ . '(.*)$/s', $content, $pieces);
219: // Safe memory
220: array_shift($pieces);
221: $content = '';
222: // Now combine pieces together
223: // Start block
224: $content .= str_replace($this->needles, $this->replacements, $pieces[0][0]);
225: unset($pieces[0][0]);
226:
227: // Generate dynamic blocks
228: for ($a = 0; $a < $this->dyn_cnt; $a++) {
229: $content .= str_replace($this->Dyn_needles[$a], $this->Dyn_replacements[$a], $pieces[1][0]);
230: }
231: unset($pieces[1][0]);
232:
233: // End block
234: $content .= str_replace($this->needles, $this->replacements, $pieces[2][0]);
235: unset($pieces[2][0]);
236: } else {
237: $content = str_replace($this->needles, $this->replacements, $content);
238: }
239:
240: if ($this->_encoding != '') {
241: // $content = str_replace("</head>", '<meta http-equiv="Content-Type" content="text/html; charset=' . $this->_encoding . '">' . "\n" . '</head>', $content);
242: }
243:
244: if ($return) {
245: return $content;
246: } else {
247: echo $content;
248: }
249: }
250:
251: /**
252: * Replaces a named function with the translated variant
253: *
254: * @param string $template
255: * Contents of the template to translate.
256: * It is reference to save memory!!!
257: * @param string $functionName
258: * Name of the translation function (e.g. i18n)
259: */
260: public function replacei18n(&$template, $functionName) {
261: $container = array();
262:
263: // Be sure that php code stays unchanged
264: $php_matches = array();
265: /*
266: * if (preg_match_all('/<\?(php)?((.)|(\s))*?\?>/i', $template,
267: * $php_matches)) { $x = 0; foreach ($php_matches[0] as $php_match) {
268: * $x++; $template = str_replace($php_match , '{PHP#' . $x . '#PHP}',
269: * $template); $container[$x] = $php_match; } }
270: */
271:
272: $functionNameQ = preg_quote($functionName, '/');
273:
274: // If template contains functionName + parameter store all matches
275: $matches = array();
276: preg_match_all('/' . $functionNameQ . "\\(([\\\"\\'])(.*?)\\1\\)/s", $template, $matches);
277:
278: $matches = array_values(array_unique($matches[2]));
279: for ($a = 0; $a < count($matches); $a++) {
280: $template = preg_replace('/' . $functionNameQ . "\\([\\\"\\']" . preg_quote($matches[$a], '/') . "[\\\"\\']\\)/s", i18n($matches[$a], $this->_sDomain), $template);
281: }
282:
283: // Change back php placeholder
284: if (is_array($container)) {
285: foreach ($container as $x => $php_match) {
286: $template = str_replace('{PHP#' . $x . '#PHP}', $php_match, $template);
287: }
288: }
289: }
290:
291: }
292: