summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--HISTORY1
-rw-r--r--demos/quickstart/protected/pages/Advanced/MasterContent.page15
-rw-r--r--demos/quickstart/protected/pages/Configurations/Templates1.page15
-rw-r--r--framework/Exceptions/messages.txt1
-rw-r--r--framework/Web/UI/TTemplateManager.php109
5 files changed, 134 insertions, 7 deletions
diff --git a/HISTORY b/HISTORY
index 106bafcc..cce08b77 100644
--- a/HISTORY
+++ b/HISTORY
@@ -1,6 +1,7 @@
Version 3.0.5 October 8, 2006
===============================
CHG: Ticket#358 - TFileUpload::saveAs() now returns false instead of exception when error (Qiang)
+ENH: Ticket#361 - Introduced include template tag that supports including external templates (Qiang)
ENH: Ticket#366 - white spaces are now allowed around attribute names in template (Qiang)
Version 3.0.4 September 4, 2006
diff --git a/demos/quickstart/protected/pages/Advanced/MasterContent.page b/demos/quickstart/protected/pages/Advanced/MasterContent.page
index 2698f865..b7bcc9bb 100644
--- a/demos/quickstart/protected/pages/Advanced/MasterContent.page
+++ b/demos/quickstart/protected/pages/Advanced/MasterContent.page
@@ -39,4 +39,19 @@ Then, the contents are inserted into the master control according to the followi
</p>
<img src=<%~ mastercontent.gif %> alt="Master and Content" />
<img src=<%~ pcrelation.gif %> alt="Parent-child relationship between master and content" />
+
+<h2>Master vs. External Template</h2>
+<p>
+Master is very similar to external templates which are introduced since version 3.0.5. A special <a href="?page=Configurations.Templates1">include tag</a> is used to include an external template file into a base template.
+</p>
+<p>
+Both master and external template can be used to share common contents among pages. A master is a template control whose template contains the common content and whose class file contains the logic associated with the master. An external template, on the other hand, is a pure template file without any class files.
+</p>
+<p>
+Therefore, use master control if the common content has to be associated with some logic, such as a page header with search box or login box. A master control allows you to specify how the common content should interact with end users. If you use external templates, you will have to put the needed logic in the page or control class who owns the base template.
+</p>
+<p>
+Performancewise, external template is lighter than master as the latter is a self-contained control participating the page lifecycles, while the former is used only when the template is being parsed.
+</p>
+
</com:TContent> \ No newline at end of file
diff --git a/demos/quickstart/protected/pages/Configurations/Templates1.page b/demos/quickstart/protected/pages/Configurations/Templates1.page
index 3f2fcc5e..7acd92d0 100644
--- a/demos/quickstart/protected/pages/Configurations/Templates1.page
+++ b/demos/quickstart/protected/pages/Configurations/Templates1.page
@@ -85,4 +85,19 @@ Comments INVISIBLE to end-users
Note, template comments (by &lt;!-- ... --!&gt;) cannot appear in a property value.
</p>
+<h2>Include Tags</h2>
+<p>
+Since version 3.0.5, PRADO starts to support external template inclusion. This is accomplished via include tags, where external template files are specified in namespace format and their file name must be terminated as <tt>.tpl</tt>.
+</p>
+<com:TTextHighlighter Language="prado" CssClass="source">
+&lt;%include path.to.templateFile %&gt;
+</com:TTextHighlighter>
+
+<p>
+External templates will be inserted at the places where the include tags occur in the base template.
+</p>
+<p>
+Note, nested template inclusion is not supported, i.e., you cannot have include tags in an external template.
+</p>
+
</com:TContent> \ No newline at end of file
diff --git a/framework/Exceptions/messages.txt b/framework/Exceptions/messages.txt
index d0aff516..38aab10f 100644
--- a/framework/Exceptions/messages.txt
+++ b/framework/Exceptions/messages.txt
@@ -123,6 +123,7 @@ template_eventhandler_invalid = {0}.{1} can only accept a static string.
template_controlid_invalid = {0}.ID can only accept a static text string.
template_controlskinid_invalid = {0}.SkinID can only accept a static text string.
template_content_unexpected = Unexpected content is encountered when instantiating template: {0}.
+template_include_invalid = Invalid template inclusion. Make sure {0} is a valid namespace pointing to an existing template file whose extension is .tpl.
xmldocument_file_read_failed = TXmlDocument is unable to read file '{0}'.
xmldocument_file_write_failed = TXmlDocument is unable to write file '{0}'.
diff --git a/framework/Web/UI/TTemplateManager.php b/framework/Web/UI/TTemplateManager.php
index 264112fc..4e71e4c1 100644
--- a/framework/Web/UI/TTemplateManager.php
+++ b/framework/Web/UI/TTemplateManager.php
@@ -80,12 +80,28 @@ class TTemplateManager extends TModule
$array=$cache->get(self::TEMPLATE_CACHE_PREFIX.$fileName);
if(is_array($array))
{
- list($template,$timestamp)=$array;
- if(filemtime($fileName)<$timestamp)
+ list($template,$timestamps)=$array;
+ if($this->getApplication()->getMode()===TApplicationMode::Performance)
+ return $template;
+ $cacheValid=true;
+ foreach($timestamps as $tplFile=>$timestamp)
+ {
+ if(!is_file($tplFile) || filemtime($tplFile)>$timestamp)
+ {
+ $cacheValid=false;
+ break;
+ }
+ }
+ if($cacheValid)
return $template;
}
$template=new TTemplate(file_get_contents($fileName),dirname($fileName),$fileName);
- $cache->set(self::TEMPLATE_CACHE_PREFIX.$fileName,array($template,time()));
+ $includedFiles=$template->getIncludedFiles();
+ $timestamps=array();
+ $timestamps[$fileName]=filemtime($fileName);
+ foreach($includedFiles as $includedFile)
+ $timestamps[$includedFile]=filemtime($includedFile);
+ $cache->set(self::TEMPLATE_CACHE_PREFIX.$fileName,array($template,$timestamps));
return $template;
}
}
@@ -194,6 +210,9 @@ class TTemplate extends TApplicationComponent implements ITemplate
*/
private $_sourceTemplate=true;
private $_tplControl=null;
+ private $_includedFiles=array();
+ private $_includeAtLine=array();
+ private $_includeLines=array();
/**
@@ -513,6 +532,7 @@ class TTemplate extends TApplicationComponent implements ITemplate
*/
protected function parse($input)
{
+ $input=$this->preprocess($input);
$tpl=&$this->_tpl;
$n=preg_match_all(self::REGEX_RULES,$input,$matches,PREG_SET_ORDER|PREG_OFFSET_CAPTURE);
$expectPropEnd=false;
@@ -681,10 +701,7 @@ class TTemplate extends TApplicationComponent implements ITemplate
$line=$this->_startingLine+1;
else
$line=$this->_startingLine+count(explode("\n",substr($input,0,$matchEnd+1)));
- if(empty($this->_tplFile))
- throw new TConfigurationException('template_format_invalid2',$line,$e->getMessage(),$input);
- else
- throw new TConfigurationException('template_format_invalid',$this->_tplFile,$line,$e->getMessage());
+ $this->handleException($e,$line,$input);
}
if($this->_directive===null)
@@ -898,6 +915,84 @@ class TTemplate extends TApplicationComponent implements ITemplate
else
throw new TConfigurationException('template_component_required',$type);
}
+
+ /**
+ * @return array list of included external template files
+ */
+ public function getIncludedFiles()
+ {
+ return $this->_includedFiles;
+ }
+
+ /**
+ * Handles template parsing exception.
+ * This method rethrows the exception caught during template parsing.
+ * It adjusts the error location by giving out correct error line number and source file.
+ * @param Exception template exception
+ * @param int line number
+ * @param string template string if no source file is used
+ */
+ protected function handleException($e,$line,$input=null)
+ {
+ $srcFile=$this->_tplFile;
+ if(($n=count($this->_includedFiles))>0) // need to adjust error row number and file name
+ {
+ for($i=$n-1;$i>=0;--$i)
+ {
+ if($this->_includeAtLine[$i]<=$line)
+ {
+ if($line<$this->_includeAtLine[$i]+$this->_includeLines[$i])
+ {
+ $line=$line-$this->_includeAtLine[$i]+1;
+ $srcFile=$this->_includedFiles[$i];
+ break;
+ }
+ else
+ $line=$line-$this->_includeLines[$i]+1;
+ }
+ }
+ }
+ if(empty($srcFile))
+ throw new TConfigurationException('template_format_invalid2',$line,$e->getMessage(),$input);
+ else
+ throw new TConfigurationException('template_format_invalid',$srcFile,$line,$e->getMessage());
+ }
+
+ /**
+ * Preprocesses the template string by including external templates
+ * @param string template string
+ * @return string expanded template string
+ */
+ protected function preprocess($input)
+ {
+ if($n=preg_match_all('/<%include(.*?)%>/',$input,$matches,PREG_SET_ORDER|PREG_OFFSET_CAPTURE))
+ {
+ for($i=0;$i<$n;++$i)
+ {
+ $filePath=Prado::getPathOfNamespace(trim($matches[$i][1][0]),TTemplateManager::TEMPLATE_FILE_EXT);
+ if($filePath!==null && is_file($filePath))
+ $this->_includedFiles[]=$filePath;
+ else
+ {
+ $errorLine=count(explode("\n",substr($input,0,$matches[$i][0][1]+1)));
+ $this->handleException(new TConfigurationException('template_include_invalid',trim($matches[$i][1][0])),$errorLine,$input);
+ }
+ }
+ $base=0;
+ for($i=0;$i<$n;++$i)
+ {
+ $ext=file_get_contents($this->_includedFiles[$i]);
+ $length=strlen($matches[$i][0][0]);
+ $offset=$base+$matches[$i][0][1];
+ $this->_includeAtLine[$i]=count(explode("\n",substr($input,0,$offset)));
+ $this->_includeLines[$i]=count(explode("\n",$ext));
+ $input=substr_replace($input,$ext,$offset,$length);
+ $base+=strlen($ext)-$length;
+ }
+ }
+
+ return $input;
+ }
}
?> \ No newline at end of file