5 Modification information for LGPL compliance
6 Stas 2010-12-20 Added 'VERSION_MARK' to templates
8 r56990 - 2010-06-16 13:05:36 -0700 (Wed, 16 Jun 2010) - kjing - snapshot "Mango" svn branch to a new one for GitHub sync
10 r56989 - 2010-06-16 13:01:33 -0700 (Wed, 16 Jun 2010) - kjing - defunt "Mango" svn dev branch before github cutover
12 r55980 - 2010-04-19 13:31:28 -0700 (Mon, 19 Apr 2010) - kjing - create Mango (6.1) based on windex
14 r51719 - 2009-10-22 10:18:00 -0700 (Thu, 22 Oct 2009) - mitani - Converted to Build 3 tags and updated the build system
16 r51634 - 2009-10-19 13:32:22 -0700 (Mon, 19 Oct 2009) - mitani - Windex is the branch for Sugar Sales 1.0 development
18 r50375 - 2009-08-24 18:07:43 -0700 (Mon, 24 Aug 2009) - dwong - branch kobe2 from tokyo r50372
20 r42807 - 2008-12-29 11:16:59 -0800 (Mon, 29 Dec 2008) - dwong - Branch from trunk/sugarcrm r42806 to branches/tokyo/sugarcrm
22 r36874 - 2008-06-19 12:09:05 -0700 (Thu, 19 Jun 2008) - roger - bug 22568: use md5 of unique key and version for js key.
24 r32524 - 2008-03-06 15:51:02 -0800 (Thu, 06 Mar 2008) - dwong - Fix incorrect encoding on source code caused by IDE, e.g.
26 InboundEmail.php r17199
29 r30876 - 2008-01-09 19:01:57 -0800 (Wed, 09 Jan 2008) - majed - initial check in for instances
31 r29571 - 2007-11-13 10:49:09 -0800 (Tue, 13 Nov 2007) - eddy - Bug 17113
32 Added check for array element prior to accessing it.
35 r26822 - 2007-09-18 10:20:27 -0700 (Tue, 18 Sep 2007) - tswicegood - Refactor a bunch of the loops and such.
36 This code doesn't have a future in Sugar, but there are some legacy
37 areas that still rely on it. Refactoring these few areas cuts its impact
38 on Sugar's main page by 40% (6.14% to 3.72%).
40 r26819 - 2007-09-18 10:07:01 -0700 (Tue, 18 Sep 2007) - tswicegood - Reduces this execution time relatively by 25%
42 r25238 - 2007-08-07 15:40:32 -0700 (Tue, 07 Aug 2007) - dwheeler - Bug 14129. Removed field from vardefs as it is no longer used, and should not be visible from mass update.
44 r18355 - 2006-12-05 17:00:55 -0800 (Tue, 05 Dec 2006) - jenny - Bug 10292 - checking to see if we actually have an array before setting the array values.
46 r13627 - 2006-05-31 11:01:53 -0700 (Wed, 31 May 2006) - majed - name change
48 r12024 - 2006-03-09 23:42:27 -0800 (Thu, 09 Mar 2006) - majed - fixes bugs 4449 5050 4063 4976 4770
50 r11291 - 2006-01-22 10:41:45 -0800 (Sun, 22 Jan 2006) - andrew - Removed the 'Log' CVS keyword.
52 r10797 - 2005-12-21 18:10:38 -0800 (Wed, 21 Dec 2005) - wayne - sugar_version and js_custom_version xtpl assignment now in xtpl.php
54 r9351 - 2005-11-15 15:39:37 -0800 (Tue, 15 Nov 2005) - andrew - Added another check for the $focus that needs to be in for PHP 5.0.3.
56 r9270 - 2005-11-11 15:08:19 -0800 (Fri, 11 Nov 2005) - majed - Adds support for emails email marketing and email templates
58 r8555 - 2005-10-19 12:26:13 -0700 (Wed, 19 Oct 2005) - majed - adds initial acl support
60 r8508 - 2005-10-17 17:23:04 -0700 (Mon, 17 Oct 2005) - majed - adds initial acl support
62 r5820 - 2005-06-21 14:22:24 -0700 (Tue, 21 Jun 2005) - majed - fixes issues with nusoap and with custom fields
64 r4920 - 2005-04-29 00:38:19 -0700 (Fri, 29 Apr 2005) - jacob - Preventing conversion of array to string.
66 r4743 - 2005-04-27 00:57:27 -0700 (Wed, 27 Apr 2005) - jacob - Adding support for "parsing" sections that do not exist in HTML. This provides backwards compatibility for old HTML files with new PHP files.
68 r2016 - 2004-12-28 15:19:29 -0800 (Tue, 28 Dec 2004) - majed - added a function to scan through a block checking for a given variable
70 r1228 - 2004-10-20 02:09:09 -0700 (Wed, 20 Oct 2004) - lam - update
72 r1211 - 2004-10-19 21:55:03 -0700 (Tue, 19 Oct 2004) - lam - update
74 r730 - 2004-09-09 20:14:02 -0700 (Thu, 09 Sep 2004) - sugarjacob - Cleaning up blanks
76 r462 - 2004-08-25 17:43:37 -0700 (Wed, 25 Aug 2004) - sugarmsi - added an exists method to check if a block exists in a template
78 r397 - 2004-08-08 02:28:36 -0700 (Sun, 08 Aug 2004) - sugarjacob - Fix: XTemplate changed to use <?php script declarations
80 r297 - 2004-07-31 15:13:23 -0700 (Sat, 31 Jul 2004) - sugarjacob - Removing default setting of template language arrays.
82 r295 - 2004-07-31 14:37:38 -0700 (Sat, 31 Jul 2004) - sugarjacob - Adding code to automatically assign the language strings to every template created.
84 r268 - 2004-07-16 01:21:57 -0700 (Fri, 16 Jul 2004) - sugarjacob - Changing the XTemplate replacement mechanism to allow for '$' in the text being substituted.
86 r80 - 2004-06-11 16:39:47 -0700 (Fri, 11 Jun 2004) - sugarjacob - Fixing issue with a variable not being an array in some cases.
88 r78 - 2004-06-11 16:34:17 -0700 (Fri, 11 Jun 2004) - sugarjacob - Removing errors or notices about invalid indexs.
90 r3 - 2004-05-26 22:30:56 -0700 (Wed, 26 May 2004) - sugarjacob - Moving project to SourceForge.
100 xtemplate class 0.2.4-3
101 html generation with templates - fast & easy
102 copyright (c) 2000 barnabás debreceni [cranx@users.sourceforge.net]
103 code optimization by Ivar Smolin <okul@linux.ee> 14-march-2001
104 latest stable & CVS version always available @ http://sourceforge.net/projects/xtpl
106 tested with php 3.0.11 and 4.0.4pl1
108 This program is free software; you can redistribute it and/or
109 modify it under the terms of the GNU Lesser General Public License
110 version 2.1 as published by the Free Software Foundation.
112 This library is distributed in the hope that it will be useful,
113 but WITHOUT ANY WARRANTY; without even the implied warranty of
114 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
115 GNU Lesser General Public License for more details at
116 http://www.gnu.org/copyleft/lgpl.html
118 You should have received a copy of the GNU General Public License
119 along with this program; if not, write to the Free Software
120 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
125 /***[ variables ]***********************************************************/
127 var $filecontents=""; /* raw contents of template file */
128 var $blocks=array(); /* unparsed blocks */
129 var $parsed_blocks=array(); /* parsed blocks */
130 var $block_parse_order=array(); /* block parsing order for recursive parsing (sometimes reverse:) */
131 var $sub_blocks=array(); /* store sub-block names for fast resetting */
132 var $VARS=array(); /* variables array */
133 var $alternate_include_directory = "";
135 var $file_delim="/\{FILE\s*\"([^\"]+)\"\s*\}/m"; /* regexp for file includes */
136 var $block_start_delim="<!-- "; /* block start delimiter */
137 var $block_end_delim="-->"; /* block end delimiter */
138 var $block_start_word="BEGIN:"; /* block start word */
139 var $block_end_word="END:"; /* block end word */
141 /* this makes the delimiters look like: <!-- BEGIN: block_name --> if you use my syntax. */
143 var $NULL_STRING=array(""=>""); /* null string for unassigned vars */
144 var $NULL_BLOCK=array(""=>""); /* null string for unassigned blocks */
147 var $AUTORESET=1; /* auto-reset sub blocks */
149 /***[ constructor ]*********************************************************/
151 function XTemplate ($file, $alt_include = "", $mainblock="main") {
152 $this->alternate_include_directory = $alt_include;
153 $this->mainblock=$mainblock;
154 $this->filecontents=$this->r_getfile($file); /* read in template file */
155 //if(substr_count($file, 'backup') == 1)_ppd($this->filecontents);
156 $this->blocks=$this->maketree($this->filecontents,$mainblock); /* preprocess some stuff */
157 //$this->scan_globals();
161 /***************************************************************************/
162 /***[ public stuff ]********************************************************/
163 /***************************************************************************/
166 /***[ assign ]**************************************************************/
171 function assign ($name,$val="") {
172 if (is_array($name)) {
173 foreach ($name as $k => $v) {
174 $this->VARS[$k] = $v;
177 $this->VARS[$name]=$val;
181 function append ($varname, $name,$val="") {
182 if(!isset($this->VARS[$varname])){
183 $this->VARS[$varname] = array();
185 if(is_array($this->VARS[$varname])){
186 $this->VARS[$varname][$name] = $val;
190 /***[ parse ]***************************************************************/
195 function parse ($bname) {
196 global $sugar_version, $sugar_config;
198 $this->assign('SUGAR_VERSION', $GLOBALS['js_version_key']);
199 $this->assign('JS_CUSTOM_VERSION', $sugar_config['js_custom_version']);
200 $this->assign('VERSION_MARK', getVersionedPath(''));
202 if(empty($this->blocks[$bname]))
205 $copy=$this->blocks[$bname];
206 if (!isset($this->blocks[$bname]))
207 $this->set_error ("parse: blockname [$bname] does not exist");
208 preg_match_all("/\{([A-Za-z0-9\._]+?)}/",$this->blocks[$bname],$var_array);
209 $var_array=$var_array[1];
210 foreach ($var_array as $k => $v) {
211 $sub=explode(".",$v);
212 if ($sub[0]=="_BLOCK_") {
214 $bname2=implode(".",$sub);
216 if(isset($this->parsed_blocks[$bname2]))
218 $var=$this->parsed_blocks[$bname2];
225 $nul=(!isset($this->NULL_BLOCK[$bname2])) ? $this->NULL_BLOCK[""] : $this->NULL_BLOCK[$bname2];
226 $var=(empty($var))?$nul:trim($var);
227 // Commented out due to regular expression issue with '$' in replacement string.
228 //$copy=preg_replace("/\{".$v."\}/","$var",$copy);
229 // This should be faster and work better for '$'
230 $copy=str_replace("{".$v."}",$var,$copy);
234 foreach ($sub as $k1 => $v1)
236 if(is_array($var) && isset($var[$v1]))
246 $nul=(!isset($this->NULL_STRING[$v])) ? ($this->NULL_STRING[""]) : ($this->NULL_STRING[$v]);
247 $var=(!isset($var))?$nul:$var;
248 // Commented out due to regular expression issue with '$' in replacement string.
249 //$copy=preg_replace("/\{$v\}/","$var",$copy);
250 // This should be faster and work better for '$'
252 // this was periodically returning an array to string conversion error....
255 $copy=str_replace("{".$v."}",$var,$copy);
260 if(isset($this->parsed_blocks[$bname]))
262 $this->parsed_blocks[$bname].=$copy;
266 $this->parsed_blocks[$bname]=$copy;
270 if ($this->AUTORESET && (!empty($this->sub_blocks[$bname]))) {
271 reset($this->sub_blocks[$bname]);
272 foreach ($this->sub_blocks[$bname] as $v)
277 /***[ exists ]**************************************************************/
279 returns true if a block exists otherwise returns false.
281 function exists($bname){
282 return (!empty($this->parsed_blocks[$bname])) || (!empty($this->blocks[$bname]));
286 /***[ var_exists ]**************************************************************/
288 returns true if a block exists otherwise returns false.
290 function var_exists($bname,$vname){
291 if(!empty($this->blocks[$bname])){
292 return substr_count($this->blocks[$bname], '{'. $vname . '}') >0;
298 /***[ rparse ]**************************************************************/
300 returns the parsed text for a block, including all sub-blocks.
303 function rparse($bname) {
304 if (!empty($this->sub_blocks[$bname])) {
305 reset($this->sub_blocks[$bname]);
306 while (list($k,$v)=each($this->sub_blocks[$bname]))
308 $this->rparse($v,$indent."\t");
310 $this->parse($bname);
313 /***[ insert_loop ]*********************************************************/
315 inserts a loop ( call assign & parse )
318 function insert_loop($bname,$var,$value="") {
319 $this->assign($var,$value);
320 $this->parse($bname);
323 /***[ text ]****************************************************************/
325 returns the parsed text for a block
328 function text($bname) {
330 if(!empty($this->parsed_blocks)){
331 return $this->parsed_blocks[isset($bname) ? $bname :$this->mainblock];
337 /***[ out ]*****************************************************************/
339 prints the parsed text
342 function out ($bname) {
348 if($focus && is_subclass_of($focus, 'SugarBean') && !$focus->ACLAccess($action)){
350 ACLController::displayNoAccess(true);
356 echo $this->text($bname);
359 /***[ reset ]***************************************************************/
361 resets the parsed text
364 function reset ($bname) {
365 $this->parsed_blocks[$bname]="";
368 /***[ parsed ]**************************************************************/
370 returns true if block was parsed, false if not
373 function parsed ($bname) {
374 return (!empty($this->parsed_blocks[$bname]));
377 /***[ SetNullString ]*******************************************************/
379 sets the string to replace in case the var was not assigned
382 function SetNullString($str,$varname="") {
383 $this->NULL_STRING[$varname]=$str;
386 /***[ SetNullBlock ]********************************************************/
388 sets the string to replace in case the block was not parsed
391 function SetNullBlock($str,$bname="") {
392 $this->NULL_BLOCK[$bname]=$str;
395 /***[ set_autoreset ]*******************************************************/
397 sets AUTORESET to 1. (default is 1)
398 if set to 1, parse() automatically resets the parsed blocks' sub blocks
399 (for multiple level blocks)
402 function set_autoreset() {
406 /***[ clear_autoreset ]*****************************************************/
408 sets AUTORESET to 0. (default is 1)
409 if set to 1, parse() automatically resets the parsed blocks' sub blocks
410 (for multiple level blocks)
413 function clear_autoreset() {
417 /***[ scan_globals ]********************************************************/
419 scans global variables
422 function scan_globals() {
424 while (list($k,$v)=each($GLOBALS))
426 $this->assign("PHP",$GLOB); /* access global variables as {PHP.HTTP_HOST} in your template! */
432 PUBLIC FUNCTIONS BELOW THIS LINE DIDN'T GET TESTED
437 /***************************************************************************/
438 /***[ private stuff ]*******************************************************/
439 /***************************************************************************/
441 /***[ maketree ]************************************************************/
443 generates the array containing to-be-parsed stuff:
444 $blocks["main"],$blocks["main.table"],$blocks["main.table.row"], etc.
445 also builds the reverse parse order.
449 function maketree($con,$block) {
450 $con2=explode($this->block_start_delim,$con);
452 $block_names=array();
455 while(list($k,$v)=each($con2)) {
456 $patt="($this->block_start_word|$this->block_end_word)\s*(\w+)\s*$this->block_end_delim(.*)";
457 if (preg_match_all("/$patt/ims",$v,$res, PREG_SET_ORDER)) {
458 // $res[0][1] = BEGIN or END
459 // $res[0][2] = block name
460 // $res[0][3] = kinda content
461 if ($res[0][1]==$this->block_start_word) {
462 $parent_name=implode(".",$block_names);
463 $block_names[++$level]=$res[0][2]; /* add one level - array("main","table","row")*/
464 $cur_block_name=implode(".",$block_names); /* make block name (main.table.row) */
465 $this->block_parse_order[]=$cur_block_name; /* build block parsing order (reverse) */
467 if(array_key_exists($cur_block_name, $blocks))
469 $blocks[$cur_block_name].=$res[0][3]; /* add contents */
473 $blocks[$cur_block_name]=$res[0][3]; /* add contents */
476 /* add {_BLOCK_.blockname} string to parent block */
477 if(array_key_exists($parent_name, $blocks))
479 $blocks[$parent_name].="{_BLOCK_.$cur_block_name}";
483 $blocks[$parent_name]="{_BLOCK_.$cur_block_name}";
486 $this->sub_blocks[$parent_name][]=$cur_block_name; /* store sub block names for autoresetting and recursive parsing */
487 $this->sub_blocks[$cur_block_name][]=""; /* store sub block names for autoresetting */
488 } else if ($res[0][1]==$this->block_end_word) {
489 unset($block_names[$level--]);
490 $parent_name=implode(".",$block_names);
491 $blocks[$parent_name].=$res[0][3]; /* add rest of block to parent block */
493 } else { /* no block delimiters found */
494 $index = implode(".",$block_names);
495 if(array_key_exists($index, $blocks))
497 $blocks[].=$this->block_start_delim.$v;
501 $blocks[]=$this->block_start_delim.$v;
510 /***[ error stuff ]*********************************************************/
515 function get_error() {
516 return ($this->ERROR=="")?0:$this->ERROR;
520 function set_error($str) {
524 /***[ getfile ]*************************************************************/
526 returns the contents of a file
529 function getfile($file) {
531 $this->set_error("!isset file name!");
535 // Pick which folder we should include from
536 // Prefer the local directory, then try the theme directory.
538 $file = $this->alternate_include_directory.$file;
542 $file_text=file_get_contents($file);
545 $this->set_error("[$file] does not exist");
546 $file_text="<b>__XTemplate fatal error: file [$file] does not exist__</b>";
552 /***[ r_getfile ]***********************************************************/
554 recursively gets the content of a file with {FILE "filename.tpl"} directives
558 function r_getfile($file) {
559 $text=$this->getfile($file);
560 while (preg_match($this->file_delim,$text,$res)) {
561 $text2=$this->getfile($res[1]);
562 $text=preg_replace("'".preg_quote($res[0])."'",$text2,$text);
567 } /* end of XTemplate class. */