commiting uncommited changes on live site
[weblabels.fsf.org.git] / crm.fsf.org / 20131203 / files / sites / all / modules-old / civicrm / packages / HTML / Template / ITX.php
1 <?php
2 //
3 // +----------------------------------------------------------------------+
4 // | Copyright (c) 1997-2005 Ulf Wendel, Pierre-Alain Joye |
5 // +----------------------------------------------------------------------+
6 // | This source file is subject to the New BSD license, That is bundled |
7 // | with this package in the file LICENSE, and is available through |
8 // | the world-wide-web at |
9 // | http://www.opensource.org/licenses/bsd-license.php |
10 // | If you did not receive a copy of the new BSD license and are unable |
11 // | to obtain it through the world-wide-web, please send a note to |
12 // | pajoye@php.net so we can mail you a copy immediately. |
13 // +----------------------------------------------------------------------+
14 // | Author: Ulf Wendel <ulf.wendel@phpdoc.de> |
15 // | Pierre-Alain Joye <pajoye@php.net> |
16 // +----------------------------------------------------------------------+
17 //
18 // $Id: ITX.php,v 1.16 2006/08/17 15:47:22 dsp Exp $
19 //
20
21 require_once 'HTML/Template/IT.php';
22 require_once 'HTML/Template/IT_Error.php';
23
24 /**
25 * Integrated Template Extension - ITX
26 *
27 * With this class you get the full power of the phplib template class.
28 * You may have one file with blocks in it but you have as well one main file
29 * and multiple files one for each block. This is quite usefull when you have
30 * user configurable websites. Using blocks not in the main template allows
31 * you to modify some parts of your layout easily.
32 *
33 * Note that you can replace an existing block and add new blocks at runtime.
34 * Adding new blocks means changing a variable placeholder to a block.
35 *
36 * @author Ulf Wendel <uw@netuse.de>
37 * @access public
38 * @version $Id: ITX.php,v 1.16 2006/08/17 15:47:22 dsp Exp $
39 * @package HTML_Template_IT
40 */
41 class HTML_Template_ITX extends HTML_Template_IT
42 {
43 /**
44 * Array with all warnings.
45 * @var array
46 * @access public
47 * @see $printWarning, $haltOnWarning, warning()
48 */
49 var $warn = array();
50
51 /**
52 * Print warnings?
53 * @var array
54 * @access public
55 * @see $haltOnWarning, $warn, warning()
56 */
57 var $printWarning = false;
58
59 /**
60 * Call die() on warning?
61 * @var boolean
62 * @access public
63 * @see $warn, $printWarning, warning()
64 */
65 var $haltOnWarning = false;
66
67 /**
68 * RegExp used to test for a valid blockname.
69 * @var string
70 */
71 var $checkblocknameRegExp = '';
72
73 /**
74 * Functionnameprefix used when searching function calls in the template.
75 * @var string
76 */
77 var $functionPrefix = 'func_';
78
79 /**
80 * Functionname RegExp.
81 * @var string
82 */
83 var $functionnameRegExp = '[_a-zA-Z]+[A-Za-z_0-9]*';
84
85 /**
86 * RegExp used to grep function calls in the template.
87 *
88 * The variable gets set by the constructor.
89 *
90 * @var string
91 * @see HTML_Template_IT()
92 */
93 var $functionRegExp = '';
94
95 /**
96 * List of functions found in the template.
97 *
98 * @var array
99 */
100 var $functions = array();
101
102 /**
103 * List of callback functions specified by the user.
104 *
105 * @var array
106 */
107 var $callback = array();
108
109 /**
110 * Builds some complex regexps and calls the constructor
111 * of the parent class.
112 *
113 * Make sure that you call this constructor if you derive your own
114 * template class from this one.
115 *
116 * @see HTML_Template_IT()
117 */
118 function HTML_Template_ITX($root = '')
119 {
120
121 $this->checkblocknameRegExp = '@' . $this->blocknameRegExp . '@';
122 $this->functionRegExp = '@' . $this->functionPrefix . '(' .
123 $this->functionnameRegExp . ')\s*\(@sm';
124
125 $this->HTML_Template_IT($root);
126 } // end func constructor
127
128 function init()
129 {
130 $this->free();
131 $this->buildFunctionlist();
132 $this->findBlocks($this->template);
133 // we don't need it any more
134 $this->template = '';
135 $this->buildBlockvariablelist();
136
137 } // end func init
138
139 /**
140 * Replaces an existing block with new content.
141 *
142 * This function will replace a block of the template and all blocks
143 * contained in the replaced block and add a new block insted, means
144 * you can dynamically change your template.
145 *
146 * Note that changing the template structure violates one of the IT[X]
147 * development goals. I've tried to write a simple to use template engine
148 * supporting blocks. In contrast to other systems IT[X] analyses the way
149 * you've nested blocks and knows which block belongs into another block.
150 * The nesting information helps to make the API short and simple. Replacing
151 * blocks does not only mean that IT[X] has to update the nesting
152 * information (relatively time consumpting task) but you have to make sure
153 * that you do not get confused due to the template change itself.
154 *
155 * @param string Blockname
156 * @param string Blockcontent
157 * @param boolean true if the new block inherits the content
158 * of the old block
159 * @return boolean
160 * @throws IT_Error
161 * @see replaceBlockfile(), addBlock(), addBlockfile()
162 * @access public
163 */
164 function replaceBlock($block, $template, $keep_content = false)
165 {
166 if (!isset($this->blocklist[$block])) {
167 return new IT_Error(
168 "The block "."'$block'".
169 " does not exist in the template and thus it can't be replaced.",
170 __FILE__, __LINE__
171 );
172 }
173
174 if ($template == '') {
175 return new IT_Error('No block content given.', __FILE__, __LINE__);
176 }
177
178 if ($keep_content) {
179 $blockdata = $this->blockdata[$block];
180 }
181
182 // remove all kinds of links to the block / data of the block
183 $this->removeBlockData($block);
184
185 $template = "<!-- BEGIN $block -->" . $template . "<!-- END $block -->";
186 $parents = $this->blockparents[$block];
187 $this->findBlocks($template);
188 $this->blockparents[$block] = $parents;
189
190 // KLUDGE: rebuild the list for all block - could be done faster
191 $this->buildBlockvariablelist();
192
193 if ($keep_content) {
194 $this->blockdata[$block] = $blockdata;
195 }
196
197 // old TODO - I'm not sure if we need this
198 // update caches
199
200 return true;
201 } // end func replaceBlock
202
203 /**
204 * Replaces an existing block with new content from a file.
205 *
206 * @brother replaceBlock()
207 * @param string Blockname
208 * @param string Name of the file that contains the blockcontent
209 * @param boolean true if the new block inherits the content of the old block
210 * @access public
211 */
212 function replaceBlockfile($block, $filename, $keep_content = false)
213 {
214 return $this->replaceBlock($block, $this->getFile($filename), $keep_content);
215 } // end func replaceBlockfile
216
217 /**
218 * Adds a block to the template changing a variable placeholder
219 * to a block placeholder.
220 *
221 * Add means "replace a variable placeholder by a new block".
222 * This is different to PHPLibs templates. The function loads a
223 * block, creates a handle for it and assigns it to a certain
224 * variable placeholder. To to the same with PHPLibs templates you would
225 * call set_file() to create the handle and parse() to assign the
226 * parsed block to a variable. By this PHPLibs templates assume
227 * that you tend to assign a block to more than one one placeholder.
228 * To assign a parsed block to more than only the placeholder you specify
229 * in this function you have to use a combination of getBlock()
230 * and setVariable().
231 *
232 * As no updates to cached data is necessary addBlock() and addBlockfile()
233 * are rather "cheap" meaning quick operations.
234 *
235 * The block content must not start with <!-- BEGIN blockname -->
236 * and end with <!-- END blockname --> this would cause overhead and
237 * produce an error.
238 *
239 * @param string Name of the variable placeholder, the name must be unique
240 * within the template.
241 * @param string Name of the block to be added
242 * @param string Content of the block
243 * @return boolean
244 * @throws IT_Error
245 * @see addBlockfile()
246 * @access public
247 */
248 function addBlock($placeholder, $blockname, $template)
249 {
250 // Don't trust any user even if it's a programmer or yourself...
251 if ($placeholder == '') {
252 return new IT_Error('No variable placeholder given.',
253 __FILE__, __LINE__
254 );
255 } elseif ($blockname == '' ||
256 !preg_match($this->checkblocknameRegExp, $blockname)
257 ) {
258 return new IT_Error("No or invalid blockname '$blockname' given.",
259 __FILE__, __LINE__
260 );
261 } elseif ($template == '') {
262 return new IT_Error('No block content given.', __FILE__, __LINE__);
263 } elseif (isset($this->blocklist[$blockname])) {
264 return new IT_Error('The block already exists.',
265 __FILE__, __LINE__
266 );
267 }
268
269 // find out where to insert the new block
270 $parents = $this->findPlaceholderBlocks($placeholder);
271 if (count($parents) == 0) {
272
273 return new IT_Error(
274 "The variable placeholder".
275 " '$placeholder' was not found in the template.",
276 __FILE__, __LINE__
277 );
278
279 } elseif (count($parents) > 1) {
280
281 reset($parents);
282 while (list($k, $parent) = each($parents)) {
283 $msg .= "$parent, ";
284 }
285 $msg = substr($parent, -2);
286
287 return new IT_Error("The variable placeholder "."'$placeholder'".
288 " must be unique, found in multiple blocks '$msg'.",
289 __FILE__, __LINE__
290 );
291 }
292
293 $template = "<!-- BEGIN $blockname -->" . $template . "<!-- END $blockname -->";
294 $this->findBlocks($template);
295 if ($this->flagBlocktrouble) {
296 return false; // findBlocks() already throws an exception
297 }
298 $this->blockinner[$parents[0]][] = $blockname;
299 $this->blocklist[$parents[0]] = preg_replace(
300 '@' . $this->openingDelimiter . $placeholder .
301 $this->closingDelimiter . '@',
302
303 $this->openingDelimiter . '__' . $blockname . '__' .
304 $this->closingDelimiter,
305
306 $this->blocklist[$parents[0]]
307 );
308
309 $this->deleteFromBlockvariablelist($parents[0], $placeholder);
310 $this->updateBlockvariablelist($blockname);
311
312 return true;
313 } // end func addBlock
314
315 /**
316 * Adds a block taken from a file to the template changing a variable
317 * placeholder to a block placeholder.
318 *
319 * @param string Name of the variable placeholder to be converted
320 * @param string Name of the block to be added
321 * @param string File that contains the block
322 * @brother addBlock()
323 * @access public
324 */
325 function addBlockfile($placeholder, $blockname, $filename)
326 {
327 return $this->addBlock($placeholder, $blockname, $this->getFile($filename));
328 } // end func addBlockfile
329
330 /**
331 * Returns the name of the (first) block that contains
332 * the specified placeholder.
333 *
334 * @param string Name of the placeholder you're searching
335 * @param string Name of the block to scan. If left out (default)
336 * all blocks are scanned.
337 * @return string Name of the (first) block that contains
338 * the specified placeholder.
339 * If the placeholder was not found or an error occured
340 * an empty string is returned.
341 * @throws IT_Error
342 * @access public
343 */
344 function placeholderExists($placeholder, $block = '')
345 {
346 if ($placeholder == '') {
347 new IT_Error('No placeholder name given.', __FILE__, __LINE__);
348 return '';
349 }
350
351 if ($block != '' && !isset($this->blocklist[$block])) {
352 new IT_Error("Unknown block '$block'.", __FILE__, __LINE__);
353 return '';
354 }
355
356 // name of the block where the given placeholder was found
357 $found = '';
358
359 if ($block != '') {
360 if (is_array($variables = $this->blockvariables[$block])) {
361 // search the value in the list of blockvariables
362 reset($variables);
363 while (list($k, $variable) = each($variables)) {
364 if ($k == $placeholder) {
365 $found = $block;
366 break;
367 }
368 }
369 }
370 } else {
371
372 // search all blocks and return the name of the first block that
373 // contains the placeholder
374 reset($this->blockvariables);
375 while (list($blockname, $variables) = each($this->blockvariables)){
376 if (is_array($variables) && isset($variables[$placeholder])) {
377 $found = $blockname;
378 break;
379 }
380 }
381 }
382
383 return $found;
384 } // end func placeholderExists
385
386 /**
387 * Checks the list of function calls in the template and
388 * calls their callback function.
389 *
390 * @access public
391 */
392 function performCallback()
393 {
394 reset($this->functions);
395 while (list($func_id, $function) = each($this->functions)) {
396 if (isset($this->callback[$function['name']])) {
397 if ($this->callback[$function['name']]['expandParameters']) {
398 $callFunction = 'call_user_func_array';
399 } else {
400 $callFunction = 'call_user_func';
401 }
402
403 if ($this->callback[$function['name']]['object'] != '') {
404 $call =
405 $callFunction(
406 array(
407 &$GLOBALS[$this->callback[$function['name']]['object']],
408 $this->callback[$function['name']]['function']),
409 $function['args']
410 );
411
412 } else {
413 $call =
414 $callFunction(
415 $this->callback[$function['name']]['function'],
416 $function['args']
417 );
418 }
419 $this->variableCache['__function' . $func_id . '__'] = $call;
420 }
421 }
422
423 } // end func performCallback
424
425 /**
426 * Returns a list of all function calls in the current template.
427 *
428 * @return array
429 * @access public
430 */
431 function getFunctioncalls()
432 {
433 return $this->functions;
434 } // end func getFunctioncalls
435
436 /**
437 * Replaces a function call with the given replacement.
438 *
439 * @param int Function ID
440 * @param string Replacement
441 * @deprecated
442 */
443 function setFunctioncontent($functionID, $replacement)
444 {
445 $this->variableCache['__function' . $functionID . '__'] = $replacement;
446 } // end func setFunctioncontent
447
448 /**
449 * Sets a callback function.
450 *
451 * IT[X] templates (note the X) can contain simple function calls.
452 * "function call" means that the editor of the template can add
453 * special placeholder to the template like 'func_h1("embedded in h1")'.
454 * IT[X] will grab this function calls and allow you to define a callback
455 * function for them.
456 *
457 * This is an absolutely evil feature. If your application makes heavy
458 * use of such callbacks and you're even implementing if-then etc. on
459 * the level of a template engine you're reiventing the wheel... - that's
460 * actually how PHP came into life. Anyway, sometimes it's handy.
461 *
462 * Consider also using XML/XSLT or native PHP. And please do not push
463 * IT[X] any further into this direction of adding logics to the template
464 * engine.
465 *
466 * For those of you ready for the X in IT[X]:
467 *
468 * <?php
469 * ...
470 * function h_one($args) {
471 * return sprintf('<h1>%s</h1>', $args[0]);
472 * }
473 *
474 * ...
475 * $itx = new HTML_Template_ITX( ... );
476 * ...
477 * $itx->setCallbackFunction('h1', 'h_one');
478 * $itx->performCallback();
479 * ?>
480 *
481 * template:
482 * func_h1('H1 Headline');
483 *
484 * @param string Function name in the template
485 * @param string Name of the callback function
486 * @param string Name of the callback object
487 * @param boolean If the callback is called with a list of parameters or
488 * with an array holding the parameters
489 * @return boolean False on failure.
490 * @throws IT_Error
491 * @access public
492 * @deprecated The $callbackobject parameter is depricated since
493 * version 1.2 and might be dropped in further versions.
494 */
495 function
496 setCallbackFunction($tplfunction, $callbackfunction, $callbackobject = '', $expandCallbackParameters=false)
497 {
498 if ($tplfunction == '' || $callbackfunction == '') {
499 return new IT_Error(
500 "No template function "."('$tplfunction')".
501 " and/or no callback function ('$callback') given.",
502 __FILE__, __LINE__
503 );
504 }
505 $this->callback[$tplfunction] = array(
506 'function' => $callbackfunction,
507 'object' => $callbackobject,
508 'expandParameters' => (boolean) $expandCallbackParameters
509 );
510
511 return true;
512 } // end func setCallbackFunction
513
514 /**
515 * Sets the Callback function lookup table
516 *
517 * @param array function table
518 * array[templatefunction] =
519 * array(
520 * "function" => userfunction,
521 * "object" => userobject
522 * )
523 * @access public
524 */
525 function setCallbackFuntiontable($functions)
526 {
527 $this->callback = $functions;
528 } // end func setCallbackFunctiontable
529
530 /**
531 * Recursively removes all data assiciated with a block, including all inner blocks
532 *
533 * @param string block to be removed
534 * @access private
535 */
536 function removeBlockData($block)
537 {
538 if (isset($this->blockinner[$block])) {
539 foreach ($this->blockinner[$block] as $k => $inner) {
540 $this->removeBlockData($inner);
541 }
542
543 unset($this->blockinner[$block]);
544 }
545
546 unset($this->blocklist[$block]);
547 unset($this->blockdata[$block]);
548 unset($this->blockvariables[$block]);
549 unset($this->touchedBlocks[$block]);
550
551 } // end func removeBlockinner
552
553 /**
554 * Returns a list of blocknames in the template.
555 *
556 * @return array [blockname => blockname]
557 * @access public
558 * @see blockExists()
559 */
560 function getBlocklist()
561 {
562 $blocklist = array();
563 foreach ($this->blocklist as $block => $content) {
564 $blocklist[$block] = $block;
565 }
566
567 return $blocklist;
568 } // end func getBlocklist
569
570 /**
571 * Checks wheter a block exists.
572 *
573 * @param string
574 * @return boolean
575 * @access public
576 * @see getBlocklist()
577 */
578 function blockExists($blockname)
579 {
580 return isset($this->blocklist[$blockname]);
581 } // end func blockExists
582
583 /**
584 * Returns a list of variables of a block.
585 *
586 * @param string Blockname
587 * @return array [varname => varname]
588 * @access public
589 * @see BlockvariableExists()
590 */
591 function getBlockvariables($block)
592 {
593 if (!isset($this->blockvariables[$block])) {
594 return array();
595 }
596
597 $variables = array();
598 foreach ($this->blockvariables[$block] as $variable => $v) {
599 $variables[$variable] = $variable;
600 }
601
602 return $variables;
603 } // end func getBlockvariables
604
605 /**
606 * Checks wheter a block variable exists.
607 *
608 * @param string Blockname
609 * @param string Variablename
610 * @return boolean
611 * @access public
612 * @see getBlockvariables()
613 */
614 function BlockvariableExists($block, $variable)
615 {
616 return isset($this->blockvariables[$block][$variable]);
617 } // end func BlockvariableExists
618
619 /**
620 * Builds a functionlist from the template.
621 * @access private
622 */
623 function buildFunctionlist()
624 {
625 $this->functions = array();
626
627 $template = $this->template;
628 $num = 0;
629
630 while (preg_match($this->functionRegExp, $template, $regs)) {
631
632 $pos = strpos($template, $regs[0]);
633 $template = substr($template, $pos + strlen($regs[0]));
634
635 $head = $this->getValue($template, ')');
636 $args = array();
637
638 $search = $regs[0] . $head . ')';
639
640 $replace = $this->openingDelimiter .
641 '__function' . $num . '__' .
642 $this->closingDelimiter;
643
644 $this->template = str_replace($search, $replace, $this->template);
645 $template = str_replace($search, $replace, $template);
646
647 while ($head != '' && $args2 = $this->getValue($head, ',')) {
648 $arg2 = trim($args2);
649 $args[] = ('"' == $arg2{0} || "'" == $arg2{0}) ?
650 substr($arg2, 1, -1) : $arg2;
651 if ($arg2 == $head) {
652 break;
653 }
654 $head = substr($head, strlen($arg2) + 1);
655 }
656
657 $this->functions[$num++] = array(
658 'name' => $regs[1],
659 'args' => $args
660 );
661 }
662
663 } // end func buildFunctionlist
664
665 /**
666 * Truncates the given code from the first occurence of
667 * $delimiter but ignores $delimiter enclosed by " or '.
668 *
669 * @access private
670 * @param string The code which should be parsed
671 * @param string The delimiter char
672 * @return string
673 * @see buildFunctionList()
674 */
675 function getValue($code, $delimiter) {
676 if ($code == '') {
677 return '';
678 }
679
680 if (!is_array($delimiter)) {
681 $delimiter = array( $delimiter => true );
682 }
683
684 $len = strlen($code);
685 $enclosed = false;
686 $enclosed_by = '';
687
688 if (isset($delimiter[$code[0]])) {
689 $i = 1;
690 } else {
691 for ($i = 0; $i < $len; ++$i) {
692 $char = $code[$i];
693
694 if (
695 ($char == '"' || $char == "'") &&
696 ($char == $enclosed_by || '' == $enclosed_by) &&
697 (0 == $i || ($i > 0 && '\\' != $code[$i - 1]))
698 ) {
699
700 if (!$enclosed) {
701 $enclosed_by = $char;
702 } else {
703 $enclosed_by = "";
704 }
705 $enclosed = !$enclosed;
706
707 }
708
709 if (!$enclosed && isset($delimiter[$char])) {
710 break;
711 }
712 }
713 }
714
715 return substr($code, 0, $i);
716 } // end func getValue
717
718 /**
719 * Deletes one or many variables from the block variable list.
720 *
721 * @param string Blockname
722 * @param mixed Name of one variable or array of variables
723 * ( array ( name => true ) ) to be stripped.
724 * @access private
725 */
726 function deleteFromBlockvariablelist($block, $variables)
727 {
728 if (!is_array($variables)) {
729 $variables = array($variables => true);
730 }
731
732 reset($this->blockvariables[$block]);
733 while (list($varname, $val) = each($this->blockvariables[$block])) {
734 if (isset($variables[$varname])) {
735 unset($this->blockvariables[$block][$varname]);
736 }
737 }
738 } // end deleteFromBlockvariablelist
739
740 /**
741 * Updates the variable list of a block.
742 *
743 * @param string Blockname
744 * @access private
745 */
746 function updateBlockvariablelist($block)
747 {
748 preg_match_all( $this->variablesRegExp,
749 $this->blocklist[$block], $regs
750 );
751
752 if (count($regs[1]) != 0) {
753 foreach ($regs[1] as $k => $var) {
754 $this->blockvariables[$block][$var] = true;
755 }
756 } else {
757 $this->blockvariables[$block] = array();
758 }
759
760 // check if any inner blocks were found
761 if (isset($this->blockinner[$block]) &&
762 is_array($this->blockinner[$block]) &&
763 count($this->blockinner[$block]) > 0
764 ) {
765 /*
766 * loop through inner blocks, registering the variable
767 * placeholders in each
768 */
769 foreach ($this->blockinner[$block] as $childBlock) {
770 $this->updateBlockvariablelist($childBlock);
771 }
772 }
773 } // end func updateBlockvariablelist
774
775 /**
776 * Returns an array of blocknames where the given variable
777 * placeholder is used.
778 *
779 * @param string Variable placeholder
780 * @return array $parents parents[0..n] = blockname
781 * @access public
782 */
783 function findPlaceholderBlocks($variable)
784 {
785 $parents = array();
786 reset($this->blocklist);
787 while (list($blockname, $content) = each($this->blocklist)) {
788 reset($this->blockvariables[$blockname]);
789 while (
790 list($varname, $val) = each($this->blockvariables[$blockname]))
791 {
792 if ($variable == $varname) {
793 $parents[] = $blockname;
794 }
795 }
796 }
797
798 return $parents;
799 } // end func findPlaceholderBlocks
800
801 /**
802 * Handles warnings, saves them to $warn and prints them or
803 * calls die() depending on the flags
804 *
805 * @param string Warning
806 * @param string File where the warning occured
807 * @param int Linenumber where the warning occured
808 * @see $warn, $printWarning, $haltOnWarning
809 * @access private
810 */
811 function warning($message, $file = '', $line = 0)
812 {
813 $message = sprintf(
814 'HTML_Template_ITX Warning: %s [File: %s, Line: %d]',
815 $message,
816 $file,
817 $line
818 );
819
820 $this->warn[] = $message;
821
822 if ($this->printWarning) {
823 print $message;
824 }
825
826 if ($this->haltOnWarning) {
827 die($message);
828 }
829 } // end func warning
830
831 } // end class HTML_Template_ITX
832 ?>