Make Smarty search template in database first

If not, fallback to file template
Implement normalizer to convert gettingstarted/../support/default.tpl to support/default.tpl
This commit is contained in:
Sergey Kukunin 2013-11-20 15:20:24 +02:00
parent 75c7e0fc6d
commit 1aee65859f
4 changed files with 211 additions and 3 deletions

View File

@ -30,6 +30,7 @@ if ($detect->isMobile() && $setting->getValue('website_mobile_theme')) {
}
define('THEME', $theme);
//Required for Smarty
require_once(CLASS_DIR . '/template.class.php');
// Load smarty now that we have our theme defined
require_once(INCLUDE_DIR . '/smarty.inc.php');

View File

@ -6,6 +6,17 @@ if (!defined('SECURITY'))
class Template extends Base {
protected $table = 'templates';
/**
* Get filepath for template name based on current PAGE and ACTION
*/
public function getFullpath($name) {
$chunks = array(PAGE);
if( ACTION )
$chunks[] = ACTION;
$chunks[] = $name;
return join('/', $chunks);
}
/**
* Get all available themes
@ -23,6 +34,41 @@ class Template extends Base {
return $aThemes;
}
/**
* Cached getActiveTemplates method
*
* @see getActiveTemplates
*/
private static $active_templates;
public function cachedGetActiveTemplates() {
if ( is_null(self::$active_templates) ) {
self::$active_templates = $this->getActiveTemplates();
}
return self::$active_templates;
}
/**
* Return the all active templates as hash,
* where key is template and value is modified_at
*
* @return array - list of active templates
*/
public function getActiveTemplates() {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("SELECT template, modified_at FROM $this->table WHERE active = 1");
if ($stmt && $stmt->execute() && $result = $stmt->get_result()) {
$rows = $result->fetch_all(MYSQLI_ASSOC);
$hash = array();
foreach($rows as $row) {
$hash[$row['template']] = strtotime($row['modified_at']);
}
return $hash;
}
$this->setErrorMessage('Failed to get active templates');
$this->debug->append('Template::getActiveTemplates failed: ' . $this->mysqli->error);
return false;
}
/**
* Return the content of specific template file
*
@ -57,15 +103,15 @@ class Template extends Base {
}
/**
* Return specific template form database
* Return specific template from database
*
* @param $template - name (filepath) of the template
* @return array - result from database
*/
public function getEntry($template) {
public function getEntry($template, $columns = "*") {
$this->debug->append("STA " . __METHOD__, 4);
$stmt = $this->mysqli->prepare("SELECT * FROM $this->table WHERE template = ?");
$stmt = $this->mysqli->prepare("SELECT $columns FROM $this->table WHERE template = ?");
if ($stmt && $stmt->bind_param('s', $template) && $stmt->execute() && $result = $stmt->get_result())
return $result->fetch_assoc();
@ -74,6 +120,22 @@ class Template extends Base {
return false;
}
/**
* Return last modified time of specific template from database
*
* @param $template - name (filepath) of the template
* @return timestamp - last modified time of template
*/
public function getEntryMTime($template) {
$this->debug->append("STA " . __METHOD__, 4);
$entry = $this->getEntry($template, "modified_at, active");
if ( $entry && $entry['active'])
return strtotime($entry['modified_at']);
return false;
}
/**
* Update template in database
*

View File

@ -10,6 +10,143 @@ define('SMARTY_DIR', INCLUDE_DIR . '/smarty/libs/');
// Include the actual smarty class file
include(SMARTY_DIR . 'Smarty.class.php');
/**
* Custom Smarty Template Resource for Pages
* Get templates from Database
* Allow admin to manage his templates from Backoffice
*/
class Smarty_Resource_Database extends Smarty_Resource_Custom {
protected $template;
public function __construct($template) {
$this->template = $template;
}
/**
* Fetch a template and its modification time from database
*
* @param string $name template name
* @param string $source template source
* @param integer $mtime template modification timestamp (epoch)
* @return void
*/
protected function fetch($name, &$source, &$mtime) {
$oTemplate = $this->template->getEntry($this->fullTemplateName($name));
if ( $oTemplate && $oTemplate['active'] ) {
$source = $oTemplate['content'];
$mtime = strtotime($oTemplate['modified_at']);
} else {
$source = null;
$mtime = null;
}
}
/**
* Fetch a template's modification time from database
*
* @note implementing this method is optional. Only implement it if modification times can be accessed faster than loading the comple template source.
* @param string $name template name
* @return integer timestamp (epoch) the template was modified
*/
protected function fetchTimestamp($name) {
$templates = $this->template->cachedGetActiveTemplates();
$mtime = @$templates[$this->fullTemplateName($name)];
return $mtime ? $mtime : false;
}
/**
* Prepend THEME name to template name to get valid DB primary key
*
* @param string $name template name
*/
protected function fullTemplateName($name) {
return $this->normalisePath(THEME . "/" . $name);
}
/**
* Normalise a file path string so that it can be checked safely.
*
* Attempt to avoid invalid encoding bugs by transcoding the path. Then
* remove any unnecessary path components including '.', '..' and ''.
*
* @param $path string
* The path to normalise.
* @return string
* The path, normalised.
* @see https://gist.github.com/thsutton/772287
*/
protected function normalisePath($path) {
// Process the components
$parts = explode('/', $path);
$safe = array();
foreach ($parts as $idx => $part) {
if (empty($part) || ('.' == $part)) {
continue;
} elseif ('..' == $part) {
array_pop($safe);
continue;
} else {
$safe[] = $part;
}
}
// Return the "clean" path
$path = implode(DIRECTORY_SEPARATOR, $safe);
return $path;
}
}
class Smarty_Resource_Hybrid extends Smarty_Resource {
protected $databaseResource;
protected $fileResource;
public function __construct($dbResource, $fileResource) {
$this->databaseResource = $dbResource;
$this->fileResource = $fileResource;
}
/**
* populate Source Object with meta data from Resource
*
* @param Smarty_Template_Source $source source object
* @param Smarty_Internal_Template $_template template object
*/
public function populate(Smarty_Template_Source $source, Smarty_Internal_Template $_template=null) {
$this->databaseResource->populate($source, $_template);
if ( !$source->exists ) {
$source->type = 'file';
return $this->fileResource->populate($source, $_template);
}
}
/**
* Load template's source into current template object
*
* @param Smarty_Template_Source $source source object
* @return string template source
* @throws SmartyException if source cannot be loaded
*/
public function getContent(Smarty_Template_Source $source) {
try {
return $this->databaseResource->getContent($source);
} catch(SmartyException $e) {
return $this->fileResource->getContent($source);
}
}
/**
* Determine basename for compiled filename
*
* @param Smarty_Template_Source $source source object
* @return string resource's basename
*/
public function getBasename(Smarty_Template_Source $source) {
return $this->fileResource->getBasename($source);
}
}
// We initialize smarty here
$debug->append('Instantiating Smarty Object', 3);
$smarty = new Smarty;
@ -18,6 +155,11 @@ $smarty = new Smarty;
$debug->append('Define Smarty Paths', 3);
$smarty->template_dir = BASEPATH . 'templates/' . THEME . '/';
$smarty->compile_dir = BASEPATH . 'templates/compile/';
$smarty->registerResource('hybrid', new Smarty_Resource_Hybrid(
new Smarty_Resource_Database($template),
new Smarty_Internal_Resource_File()
));
$smarty->default_resource_type = "hybrid";
$smarty_cache_key = md5(serialize($_REQUEST) . serialize(@$_SESSION['USERDATA']['id']));
// Optional smarty caching, check Smarty documentation for details

View File

@ -80,6 +80,9 @@ if (!empty($action)) {
require_once(PAGES_DIR . '/' . $arrPages[$page]);
}
define('PAGE', $page);
define('ACTION', $action);
// For our content inclusion
$smarty->assign("PAGE", $page);
$smarty->assign("ACTION", $action);