d715f46dc4a5f4e7d2947cd0bec43c7cdcdc555b
[atutor.git] / mods / photo_album / HTML / Template / IT.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 BSDlicense 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: IT.php,v 1.14 2005/11/01 10:14:19 pajoye Exp $
19 //
20
21 require_once 'PEAR.php';
22
23 define('IT_OK',                         1);
24 define('IT_ERROR',                     -1);
25 define('IT_TPL_NOT_FOUND',             -2);
26 define('IT_BLOCK_NOT_FOUND',           -3);
27 define('IT_BLOCK_DUPLICATE',           -4);
28 define('IT_UNKNOWN_OPTION',            -6);
29 /**
30  * Integrated Template - IT
31  *
32  * Well there's not much to say about it. I needed a template class that
33  * supports a single template file with multiple (nested) blocks inside and
34  * a simple block API.
35  *
36  * The Isotemplate API is somewhat tricky for a beginner although it is the best
37  * one you can build. template::parse() [phplib template = Isotemplate] requests
38  * you to name a source and a target where the current block gets parsed into.
39  * Source and target can be block names or even handler names. This API gives you
40  * a maximum of fexibility but you always have to know what you do which is
41  * quite unusual for php skripter like me.
42  *
43  * I noticed that I do not any control on which block gets parsed into which one.
44  * If all blocks are within one file, the script knows how they are nested and in
45  * which way you have to parse them. IT knows that inner1 is a child of block2, there's
46  * no need to tell him about this.
47  *
48  * <table border>
49  *   <tr>
50  *     <td colspan=2>
51  *       __global__
52  *       <p>
53  *       (hidden and automatically added)
54  *     </td>
55  *   </tr>
56  *   <tr>
57  *     <td>block1</td>
58  *     <td>
59  *       <table border>
60  *         <tr>
61  *           <td colspan=2>block2</td>
62  *         </tr>
63  *         <tr>
64  *           <td>inner1</td>
65  *           <td>inner2</td>
66  *         </tr>
67  *       </table>
68  *     </td>
69  *   </tr>
70  * </table>
71  *
72  * To add content to block1 you simply type:
73  * <code>$tpl->setCurrentBlock("block1");</code>
74  * and repeat this as often as needed:
75  * <code>
76  *   $tpl->setVariable(...);
77  *   $tpl->parseCurrentBlock();
78  * </code>
79  *
80  * To add content to block2 you would type something like:
81  * <code>
82  * $tpl->setCurrentBlock("inner1");
83  * $tpl->setVariable(...);
84  * $tpl->parseCurrentBlock();
85  *
86  * $tpl->setVariable(...);
87  * $tpl->parseCurrentBlock();
88  *
89  * $tpl->parse("block1");
90  * </code>
91  *
92  * This will result in one repition of block1 which contains two repitions
93  * of inner1. inner2 will be removed if $removeEmptyBlock is set to true which is the default.
94  *
95  * Usage:
96  * <code>
97  * $tpl = new HTML_Template_IT( [string filerootdir] );
98  *
99  * // load a template or set it with setTemplate()
100  * $tpl->loadTemplatefile( string filename [, boolean removeUnknownVariables, boolean removeEmptyBlocks] )
101  *
102  * // set "global" Variables meaning variables not beeing within a (inner) block
103  * $tpl->setVariable( string variablename, mixed value );
104  *
105  * // like with the Isotemplates there's a second way to use setVariable()
106  * $tpl->setVariable( array ( string varname => mixed value ) );
107  *
108  * // Let's use any block, even a deeply nested one
109  * $tpl->setCurrentBlock( string blockname );
110  *
111  * // repeat this as often as you need it.
112  * $tpl->setVariable( array ( string varname => mixed value ) );
113  * $tpl->parseCurrentBlock();
114  *
115  * // get the parsed template or print it: $tpl->show()
116  * $tpl->get();
117  * </code>
118  *
119  * @author   Ulf Wendel <uw@netuse.de>
120  * @version  $Id: IT.php,v 1.14 2005/11/01 10:14:19 pajoye Exp $
121  * @access   public
122  * @package  HTML_Template_IT
123  */
124 class HTML_Template_IT
125 {
126     /**
127      * Contains the error objects
128      * @var      array
129      * @access   public
130      * @see      halt(), $printError, $haltOnError
131      */
132     var $err = array();
133
134     /**
135      * Clear cache on get()?
136      * @var      boolean
137      */
138     var $clearCache = false;
139
140     /**
141      * First character of a variable placeholder ( _{_VARIABLE} ).
142      * @var      string
143      * @access   public
144      * @see      $closingDelimiter, $blocknameRegExp, $variablenameRegExp
145      */
146     var $openingDelimiter = '{';
147
148     /**
149      * Last character of a variable placeholder ( {VARIABLE_}_ ).
150      * @var      string
151      * @access   public
152      * @see      $openingDelimiter, $blocknameRegExp, $variablenameRegExp
153      */
154     var $closingDelimiter     = '}';
155
156     /**
157      * RegExp matching a block in the template.
158      * Per default "sm" is used as the regexp modifier, "i" is missing.
159      * That means a case sensitive search is done.
160      * @var      string
161      * @access   public
162      * @see      $variablenameRegExp, $openingDelimiter, $closingDelimiter
163      */
164     var $blocknameRegExp    = '[0-9A-Za-z_-]+';
165
166     /**
167      * RegExp matching a variable placeholder in the template.
168      * Per default "sm" is used as the regexp modifier, "i" is missing.
169      * That means a case sensitive search is done.
170      * @var      string
171      * @access   public
172      * @see      $blocknameRegExp, $openingDelimiter, $closingDelimiter
173      */
174     var $variablenameRegExp    = '[0-9A-Za-z_-]+';
175
176     /**
177      * RegExp used to find variable placeholder, filled by the constructor.
178      * @var      string    Looks somewhat like @(delimiter varname delimiter)@
179      * @access   public
180      * @see      IntegratedTemplate()
181      */
182     var $variablesRegExp = '';
183
184     /**
185      * RegExp used to strip unused variable placeholder.
186      * @brother  $variablesRegExp
187      */
188     var $removeVariablesRegExp = '';
189
190     /**
191      * Controls the handling of unknown variables, default is remove.
192      * @var      boolean
193      * @access   public
194      */
195     var $removeUnknownVariables = true;
196
197     /**
198      * Controls the handling of empty blocks, default is remove.
199      * @var      boolean
200      * @access   public
201      */
202     var $removeEmptyBlocks = true;
203
204     /**
205      * RegExp used to find blocks an their content, filled by the constructor.
206      * @var      string
207      * @see      IntegratedTemplate()
208      */
209     var $blockRegExp = '';
210
211     /**
212      * Name of the current block.
213      * @var      string
214      */
215     var $currentBlock = '__global__';
216
217     /**
218      * Content of the template.
219      * @var      string
220      */
221     var $template = '';
222
223     /**
224      * Array of all blocks and their content.
225      *
226      * @var      array
227      * @see      findBlocks()
228      */
229     var $blocklist = array();
230
231     /**
232      * Array with the parsed content of a block.
233      *
234      * @var      array
235      */
236     var $blockdata = array();
237
238     /**
239      * Array of variables in a block.
240      * @var      array
241      */
242     var $blockvariables = array();
243
244     /**
245      * Array of inner blocks of a block.
246      * @var      array
247      */
248     var $blockinner = array();
249
250     /**
251      * List of blocks to preverse even if they are "empty".
252      *
253      * This is something special. Sometimes you have blocks that
254      * should be preserved although they are empty (no placeholder replaced).
255      * Think of a shopping basket. If it's empty you have to drop a message to
256      * the user. If it's filled you have to show the contents of
257      * the shopping baseket. Now where do you place the message that the basket
258      * is empty? It's no good idea to place it in you applications as customers
259      * tend to like unecessary minor text changes. Having another template file
260      * for an empty basket means that it's very likely that one fine day
261      * the filled and empty basket templates have different layout. I decided
262      * to introduce blocks that to not contain any placeholder but only
263      * text such as the message "Your shopping basked is empty".
264      *
265      * Now if there is no replacement done in such a block the block will
266      * be recognized as "empty" and by default ($removeEmptyBlocks = true) be
267      * stripped off. To avoid thisyou can now call touchBlock() to avoid this.
268      *
269      * The array $touchedBlocks stores a list of touched block which must not
270      * be removed even if they are empty.
271      *
272      * @var  array    $touchedBlocks
273      * @see  touchBlock(), $removeEmptyBlocks
274      */
275      var $touchedBlocks = array();
276
277     /**
278      * List of blocks which should not be shown even if not "empty"
279      * @var  array    $_hiddenBlocks
280      * @see  hideBlock(), $removeEmptyBlocks
281      */
282     var $_hiddenBlocks = array();
283
284     /**
285      * Variable cache.
286      *
287      * Variables get cached before any replacement is done.
288      * Advantage: empty blocks can be removed automatically.
289      * Disadvantage: might take some more memory
290      *
291      * @var    array
292      * @see    setVariable(), $clearCacheOnParse
293      */
294     var $variableCache = array();
295
296     /**
297      * Clear the variable cache on parse?
298      *
299      * If you're not an expert just leave the default false.
300      * True reduces memory consumption somewhat if you tend to
301      * add lots of values for unknown placeholder.
302      *
303      * @var    boolean
304      */
305     var $clearCacheOnParse = false;
306
307     /**
308      * Root directory for all file operations.
309      * The string gets prefixed to all filenames given.
310      * @var    string
311      * @see    HTML_Template_IT(), setRoot()
312      */
313     var $fileRoot = '';
314
315     /**
316      * Internal flag indicating that a blockname was used multiple times.
317      * @var    boolean
318      */
319     var $flagBlocktrouble = false;
320
321     /**
322      * Flag indicating that the global block was parsed.
323      * @var    boolean
324      */
325     var $flagGlobalParsed = false;
326
327     /**
328      * EXPERIMENTAL! FIXME!
329      * Flag indication that a template gets cached.
330      *
331      * Complex templates require some times to be preparsed
332      * before the replacement can take place. Often I use
333      * one template file over and over again but I don't know
334      * before that I will use the same template file again.
335      * Now IT could notice this and skip the preparse.
336      *
337      * @var    boolean
338      */
339     var $flagCacheTemplatefile = true;
340
341     /**
342      * EXPERIMENTAL! FIXME!
343      */
344     var $lastTemplatefile = '';
345
346     /**
347      * $_options['preserve_data'] Whether to substitute variables and remove
348      * empty placeholders in data passed through setVariable
349      * (see also bugs #20199, #21951).
350      * $_options['use_preg'] Whether to use preg_replace instead of
351      * str_replace in parse()
352      * (this is a backwards compatibility feature, see also bugs #21951, #20392)
353      */
354     var $_options = array(
355         'preserve_data' => false,
356         'use_preg'      => true
357     );
358
359     /**
360      * Builds some complex regular expressions and optinally sets the
361      * file root directory.
362      *
363      * Make sure that you call this constructor if you derive your template
364      * class from this one.
365      *
366      * @param    string    File root directory, prefix for all filenames
367      *                     given to the object.
368      * @see      setRoot()
369      */
370     function HTML_Template_IT($root = '', $options = null)
371     {
372         if (!is_null($options)) {
373             $this->setOptions($options);
374         }
375         $this->variablesRegExp = '@' . $this->openingDelimiter .
376                                  '(' . $this->variablenameRegExp . ')' .
377                                  $this->closingDelimiter . '@sm';
378         $this->removeVariablesRegExp = '@' . $this->openingDelimiter .
379                                        "\s*(" . $this->variablenameRegExp .
380                                        ")\s*" . $this->closingDelimiter .'@sm';
381
382         $this->blockRegExp = '@<!--\s+BEGIN\s+(' . $this->blocknameRegExp .
383                              ')\s+-->(.*)<!--\s+END\s+\1\s+-->@sm';
384
385         $this->setRoot($root);
386     } // end constructor
387
388
389     /**
390      * Sets the option for the template class
391      *
392      * @access public
393      * @param  string  option name
394      * @param  mixed   option value
395      * @return mixed   IT_OK on success, error object on failure
396      */
397     function setOption($option, $value)
398     {
399         if (array_key_exists($option, $this->_options)) {
400             $this->_options[$option] = $value;
401             return IT_OK;
402         }
403
404         return PEAR::raiseError(
405                 $this->errorMessage(IT_UNKNOWN_OPTION) . ": '{$option}'",
406                 IT_UNKNOWN_OPTION
407             );
408     }
409
410     /**
411      * Sets the options for the template class
412      *
413      * @access public
414      * @param  string  options array of options
415      *                 default value:
416      *                   'preserve_data' => false,
417      *                   'use_preg'      => true
418      * @param  mixed   option value
419      * @return mixed   IT_OK on success, error object on failure
420      * @see $options
421      */
422     function setOptions($options)
423     {
424         if (is_array($options)) {
425             foreach ($options as $option => $value) {
426                 $error = $this->setOption($option, $value);
427                 if (PEAR::isError($error)) {
428                     return $error;
429                 }
430             }
431         }
432
433         return IT_OK;
434     }
435
436     /**
437      * Print a certain block with all replacements done.
438      * @brother get()
439      */
440     function show($block = '__global__')
441     {
442         print $this->get($block);
443     } // end func show
444
445     /**
446      * Returns a block with all replacements done.
447      *
448      * @param    string     name of the block
449      * @return   string
450      * @throws   PEAR_Error
451      * @access   public
452      * @see      show()
453      */
454     function get($block = '__global__')
455     {
456         if ($block == '__global__'  && !$this->flagGlobalParsed) {
457             $this->parse('__global__');
458         }
459
460         if (!isset($this->blocklist[$block])) {
461             $this->err[] = PEAR::raiseError(
462                             $this->errorMessage(IT_BLOCK_NOT_FOUND) .
463                             '"' . $block . "'",
464                             IT_BLOCK_NOT_FOUND
465                         );
466             return '';
467         }
468
469         if (isset($this->blockdata[$block])) {
470             $ret = $this->blockdata[$block];
471             if ($this->clearCache) {
472                 unset($this->blockdata[$block]);
473             }
474             if ($this->_options['preserve_data']) {
475                 $ret = str_replace(
476                         $this->openingDelimiter .
477                         '%preserved%' . $this->closingDelimiter,
478                         $this->openingDelimiter,
479                         $ret
480                     );
481             }
482             return $ret;
483         }
484
485         return '';
486     } // end func get()
487
488     /**
489      * Parses the given block.
490      *
491      * @param    string    name of the block to be parsed
492      * @access   public
493      * @see      parseCurrentBlock()
494      * @throws   PEAR_Error
495      */
496     function parse($block = '__global__', $flag_recursion = false)
497     {
498         static $regs, $values;
499
500         if (!isset($this->blocklist[$block])) {
501             return PEAR::raiseError(
502                 $this->errorMessage( IT_BLOCK_NOT_FOUND ) . '"' . $block . "'",
503                         IT_BLOCK_NOT_FOUND
504                 );
505         }
506
507         if ($block == '__global__') {
508             $this->flagGlobalParsed = true;
509         }
510
511         if (!$flag_recursion) {
512             $regs   = array();
513             $values = array();
514         }
515         $outer = $this->blocklist[$block];
516         $empty = true;
517
518         if ($this->clearCacheOnParse) {
519             foreach ($this->variableCache as $name => $value) {
520                 $regs[] = $this->openingDelimiter .
521                           $name . $this->closingDelimiter;
522                 $values[] = $value;
523                 $empty = false;
524             }
525             $this->variableCache = array();
526         } else {
527             foreach ($this->blockvariables[$block] as $allowedvar => $v) {
528
529                 if (isset($this->variableCache[$allowedvar])) {
530                    $regs[]   = $this->openingDelimiter .
531                                $allowedvar . $this->closingDelimiter;
532                    $values[] = $this->variableCache[$allowedvar];
533                    unset($this->variableCache[$allowedvar]);
534                    $empty = false;
535                 }
536             }
537         }
538
539         if (isset($this->blockinner[$block])) {
540             foreach ($this->blockinner[$block] as $k => $innerblock) {
541
542                 $this->parse($innerblock, true);
543                 if ($this->blockdata[$innerblock] != '') {
544                     $empty = false;
545                 }
546
547                 $placeholder = $this->openingDelimiter . "__" .
548                                 $innerblock . "__" . $this->closingDelimiter;
549                 $outer = str_replace(
550                                     $placeholder,
551                                     $this->blockdata[$innerblock], $outer
552                         );
553                 $this->blockdata[$innerblock] = "";
554             }
555
556         }
557
558         if (!$flag_recursion && 0 != count($values)) {
559             if ($this->_options['use_preg']) {
560                 $regs        = array_map(array(
561                                     &$this, '_addPregDelimiters'),
562                                     $regs
563                                 );
564                 $funcReplace = 'preg_replace';
565             } else {
566                 $funcReplace = 'str_replace';
567             }
568
569             if ($this->_options['preserve_data']) {
570                 $values = array_map(
571                             array(&$this, '_preserveOpeningDelimiter'), $values
572                         );
573             }
574
575             $outer = $funcReplace($regs, $values, $outer);
576
577             if ($this->removeUnknownVariables) {
578                 $outer = preg_replace($this->removeVariablesRegExp, "", $outer);
579             }
580         }
581
582         if ($empty) {
583             if (!$this->removeEmptyBlocks) {
584                 $this->blockdata[$block ].= $outer;
585             } else {
586                 if (isset($this->touchedBlocks[$block])) {
587                     $this->blockdata[$block] .= $outer;
588                     unset($this->touchedBlocks[$block]);
589                 }
590             }
591         } else {
592             $this->blockdata[$block] .= $outer;
593         }
594
595         return $empty;
596     } // end func parse
597
598     /**
599      * Parses the current block
600      * @see      parse(), setCurrentBlock(), $currentBlock
601      * @access   public
602      */
603     function parseCurrentBlock()
604     {
605         return $this->parse($this->currentBlock);
606     } // end func parseCurrentBlock
607
608     /**
609      * Sets a variable value.
610      *
611      * The function can be used eighter like setVariable( "varname", "value")
612      * or with one array $variables["varname"] = "value"
613      * given setVariable($variables) quite like phplib templates set_var().
614      *
615      * @param    mixed     string with the variable name or an array
616      *                     %variables["varname"] = "value"
617      * @param    string    value of the variable or empty if $variable
618      *                     is an array.
619      * @param    string    prefix for variable names
620      * @access   public
621      */
622     function setVariable($variable, $value = '')
623     {
624         if (is_array($variable)) {
625             $this->variableCache = array_merge(
626                                             $this->variableCache, $variable
627                                     );
628         } else {
629             $this->variableCache[$variable] = $value;
630         }
631     } // end func setVariable
632
633     /**
634      * Sets the name of the current block that is the block where variables
635      * are added.
636      *
637      * @param    string      name of the block
638      * @return   boolean     false on failure, otherwise true
639      * @throws   PEAR_Error
640      * @access   public
641      */
642     function setCurrentBlock($block = '__global__')
643     {
644
645         if (!isset($this->blocklist[$block])) {
646             return PEAR::raiseError(
647                 $this->errorMessage( IT_BLOCK_NOT_FOUND ) .
648                 '"' . $block . "'", IT_BLOCK_NOT_FOUND
649             );
650         }
651
652         $this->currentBlock = $block;
653
654         return true;
655     } // end func setCurrentBlock
656
657     /**
658      * Preserves an empty block even if removeEmptyBlocks is true.
659      *
660      * @param    string      name of the block
661      * @return   boolean     false on false, otherwise true
662      * @throws   PEAR_Error
663      * @access   public
664      * @see      $removeEmptyBlocks
665      */
666     function touchBlock($block)
667     {
668         if (!isset($this->blocklist[$block])) {
669             return PEAR::raiseError(
670                 $this->errorMessage(IT_BLOCK_NOT_FOUND) .
671                 '"' . $block . "'", IT_BLOCK_NOT_FOUND);
672         }
673
674         $this->touchedBlocks[$block] = true;
675
676         return true;
677     } // end func touchBlock
678
679     /**
680      * Clears all datafields of the object and rebuild the internal blocklist
681      *
682      * LoadTemplatefile() and setTemplate() automatically call this function
683      * when a new template is given. Don't use this function
684      * unless you know what you're doing.
685      *
686      * @access   public
687      * @see      free()
688      */
689     function init()
690     {
691         $this->free();
692         $this->findBlocks($this->template);
693         // we don't need it any more
694         $this->template = '';
695         $this->buildBlockvariablelist();
696     } // end func init
697
698     /**
699      * Clears all datafields of the object.
700      *
701      * Don't use this function unless you know what you're doing.
702      *
703      * @access   public
704      * @see      init()
705      */
706     function free()
707     {
708         $this->err = array();
709
710         $this->currentBlock = '__global__';
711
712         $this->variableCache    = array();
713         $this->blocklookup      = array();
714         $this->touchedBlocks    = array();
715
716         $this->flagBlocktrouble = false;
717         $this->flagGlobalParsed = false;
718     } // end func free
719
720     /**
721      * Sets the template.
722      *
723      * You can eighter load a template file from disk with
724      * LoadTemplatefile() or set the template manually using this function.
725      *
726      * @param        string      template content
727      * @param        boolean     remove unknown/unused variables?
728      * @param        boolean     remove empty blocks?
729      * @see          LoadTemplatefile(), $template
730      * @access       public
731      */
732     function setTemplate( $template, $removeUnknownVariables = true,
733                           $removeEmptyBlocks = true)
734     {
735         $this->removeUnknownVariables = $removeUnknownVariables;
736         $this->removeEmptyBlocks = $removeEmptyBlocks;
737
738         if ($template == '' && $this->flagCacheTemplatefile) {
739             $this->variableCache = array();
740             $this->blockdata = array();
741             $this->touchedBlocks = array();
742             $this->currentBlock = '__global__';
743         } else {
744             $this->template = '<!-- BEGIN __global__ -->' . $template .
745                               '<!-- END __global__ -->';
746             $this->init();
747         }
748
749         if ($this->flagBlocktrouble) {
750             return false;
751         }
752
753         return true;
754     } // end func setTemplate
755
756     /**
757      * Reads a template file from the disk.
758      *
759      * @param    string      name of the template file
760      * @param    bool        how to handle unknown variables.
761      * @param    bool        how to handle empty blocks.
762      * @access   public
763      * @return   boolean    false on failure, otherwise true
764      * @see      $template, setTemplate(), $removeUnknownVariables,
765      *           $removeEmptyBlocks
766      */
767     function loadTemplatefile( $filename,
768                                $removeUnknownVariables = true,
769                                $removeEmptyBlocks = true )
770     {
771         $template = '';
772         if (!$this->flagCacheTemplatefile ||
773             $this->lastTemplatefile != $filename
774         ) {
775             $template = $this->getFile($filename);
776         }
777         $this->lastTemplatefile = $filename;
778
779         return $template != '' ?
780                 $this->setTemplate(
781                         $template,$removeUnknownVariables, $removeEmptyBlocks
782                     ) : false;
783     } // end func LoadTemplatefile
784
785     /**
786      * Sets the file root. The file root gets prefixed to all filenames passed
787      * to the object.
788      *
789      * Make sure that you override this function when using the class
790      * on windows.
791      *
792      * @param    string
793      * @see      IntegratedTemplate()
794      * @access   public
795      */
796     function setRoot($root)
797     {
798         if ($root != '' && substr($root, -1) != '/') {
799             $root .= '/';
800         }
801
802         $this->fileRoot = $root;
803     } // end func setRoot
804
805     /**
806      * Build a list of all variables within of a block
807      */
808     function buildBlockvariablelist()
809     {
810         foreach ($this->blocklist as $name => $content) {
811             preg_match_all($this->variablesRegExp, $content, $regs);
812
813             if (count($regs[1]) != 0) {
814                 foreach ($regs[1] as $k => $var) {
815                     $this->blockvariables[$name][$var] = true;
816                 }
817             } else {
818                 $this->blockvariables[$name] = array();
819             }
820         }
821     } // end func buildBlockvariablelist
822
823     /**
824      * Returns a list of all global variables
825      */
826     function getGlobalvariables()
827     {
828         $regs   = array();
829         $values = array();
830
831         foreach ($this->blockvariables['__global__'] as $allowedvar => $v) {
832             if (isset($this->variableCache[$allowedvar])) {
833                 $regs[]   = '@' . $this->openingDelimiter .
834                             $allowedvar . $this->closingDelimiter . '@';
835                 $values[] = $this->variableCache[$allowedvar];
836                 unset($this->variableCache[$allowedvar]);
837             }
838         }
839
840         return array($regs, $values);
841     } // end func getGlobalvariables
842
843     /**
844      * Recusively builds a list of all blocks within the template.
845      *
846      * @param    string    string that gets scanned
847      * @see      $blocklist
848      */
849     function findBlocks($string)
850     {
851         $blocklist = array();
852
853         if (preg_match_all($this->blockRegExp, $string, $regs, PREG_SET_ORDER)) {
854             foreach ($regs as $k => $match) {
855                 $blockname         = $match[1];
856                 $blockcontent = $match[2];
857
858                 if (isset($this->blocklist[$blockname])) {
859                     $this->err[] = PEAR::raiseError(
860                                             $this->errorMessage(
861                                             IT_BLOCK_DUPLICATE, $blockname),
862                                             IT_BLOCK_DUPLICATE
863                                     );
864                     $this->flagBlocktrouble = true;
865                 }
866
867                 $this->blocklist[$blockname] = $blockcontent;
868                 $this->blockdata[$blockname] = "";
869
870                 $blocklist[] = $blockname;
871
872                 $inner = $this->findBlocks($blockcontent);
873                 foreach ($inner as $k => $name) {
874                     $pattern = sprintf(
875                         '@<!--\s+BEGIN\s+%s\s+-->(.*)<!--\s+END\s+%s\s+-->@sm',
876                         $name,
877                         $name
878                     );
879
880                     $this->blocklist[$blockname] = preg_replace(
881                                         $pattern,
882                                         $this->openingDelimiter .
883                                         '__' . $name . '__' .
884                                         $this->closingDelimiter,
885                                         $this->blocklist[$blockname]
886                                );
887                     $this->blockinner[$blockname][] = $name;
888                     $this->blockparents[$name] = $blockname;
889                 }
890             }
891         }
892
893         return $blocklist;
894     } // end func findBlocks
895
896     /**
897      * Reads a file from disk and returns its content.
898      * @param    string    Filename
899      * @return   string    Filecontent
900      */
901     function getFile($filename)
902     {
903         if ($filename{0} == '/' && substr($this->fileRoot, -1) == '/') {
904             $filename = substr($filename, 1);
905         }
906
907         $filename = $this->fileRoot . $filename;
908
909         if (!($fh = @fopen($filename, 'r'))) {
910             $this->err[] = PEAR::raiseError(
911                         $this->errorMessage(IT_TPL_NOT_FOUND) .
912                         ': "' .$filename .'"',
913                         IT_TPL_NOT_FOUND
914                     );
915             return "";
916         }
917
918         $content = fread($fh, filesize($filename));
919         fclose($fh);
920
921         return preg_replace(
922             "#<!-- INCLUDE (.*) -->#ime", "\$this->getFile('\\1')", $content
923         );
924     } // end func getFile
925
926     /**
927      * Adds delimiters to a string, so it can be used as a pattern
928      * in preg_* functions
929      *
930      * @param string
931      * @return string
932      */
933     function _addPregDelimiters($str)
934     {
935         return '@' . $str . '@';
936     }
937
938    /**
939     * Replaces an opening delimiter by a special string
940     *
941     * @param string
942     * @return string
943     */
944     function _preserveOpeningDelimiter($str)
945     {
946         return (false === strpos($str, $this->openingDelimiter))?
947                 $str:
948                 str_replace(
949                     $this->openingDelimiter,
950                     $this->openingDelimiter .
951                     '%preserved%' . $this->closingDelimiter,
952                     $str
953                 );
954     }
955
956     /**
957      * Return a textual error message for a IT error code
958      *
959      * @param integer $value error code
960      *
961      * @return string error message, or false if the error code was
962      * not recognized
963      */
964     function errorMessage($value, $blockname = '')
965     {
966         static $errorMessages;
967         if (!isset($errorMessages)) {
968             $errorMessages = array(
969                 IT_OK                       => '',
970                 IT_ERROR                    => 'unknown error',
971                 IT_TPL_NOT_FOUND            => 'Cannot read the template file',
972                 IT_BLOCK_NOT_FOUND          => 'Cannot find this block',
973                 IT_BLOCK_DUPLICATE          => 'The name of a block must be'.
974                                                ' uniquewithin a template.'.
975                                                ' Found "' . $blockname . '" twice.'.
976                                                'Unpredictable results '.
977                                                'may appear.',
978                 IT_UNKNOWN_OPTION           => 'Unknown option'
979             );
980         }
981
982         if (PEAR::isError($value)) {
983             $value = $value->getCode();
984         }
985
986         return isset($errorMessages[$value]) ?
987                 $errorMessages[$value] : $errorMessages[IT_ERROR];
988     }
989 } // end class IntegratedTemplate
990 ?>