MDL-49152 output: Templates for renderers (JS and PHP)

Mustache support for rendering templates from either php or js.
This commit is contained in:
Damyon Wiese
2015-03-14 22:48:40 +08:00
parent 72f8324ec5
commit 9bdcf57951
66 changed files with 6532 additions and 33 deletions
+161 -1
View File
@@ -66,6 +66,129 @@ class renderer_base {
*/
protected $target;
/**
* @var Mustache_Engine $mustache The mustache template compiler
*/
private $mustache;
/**
* @var string $component The component used when requesting this renderer.
*/
private $component;
/**
* @var string $subtype The subtype used when requesting this renderer.
*/
private $subtype;
/**
* This is not done in the constructor because that would be a
* compatibility breaking change, and we can just pass this always in the
* renderer factory, immediately after creating the renderer.
* @since 2.9
* @param string $subtype
*/
public function set_subtype($subtype) {
$this->subtype = $subtype;
}
/**
* This is not done in the constructor because that would be a
* compatibility breaking change, and we can just pass this always in the
* renderer factory, immediately after creating the renderer.
* @since 2.9
* @param string $component
*/
public function set_component($component) {
$this->component = $component;
}
/**
* Return an instance of the mustache class.
*
* @since 2.9
* @return Mustache_Engine
*/
protected function get_mustache() {
global $CFG;
if ($this->mustache === null) {
require_once($CFG->dirroot . '/lib/mustache/src/Mustache/Autoloader.php');
Mustache_Autoloader::register();
$themename = $this->page->theme->name;
$themerev = theme_get_revision();
$target = $this->target;
$cachedir = make_localcache_directory("mustache/$themerev/$themename/$target");
$loaderoptions = array();
// Where are all the places we should look for templates?
$suffix = $this->component;
if ($this->subtype !== null) {
$suffix .= '_' . $this->subtype;
}
// Start with an empty list.
$loader = new Mustache_Loader_CascadingLoader(array());
$loaderdir = $CFG->dirroot . '/theme/' . $themename . '/templates/' . $suffix;
if (is_dir($loaderdir)) {
$loader->addLoader(new \core\output\mustache_filesystem_loader($loaderdir, $loaderoptions));
}
// Search each of the parent themes second.
foreach ($this->page->theme->parents as $parent) {
$loaderdir = $CFG->dirroot . '/theme/' . $parent . '/templates/' . $suffix;
if (is_dir($loaderdir)) {
$loader->addLoader(new \core\output\mustache_filesystem_loader($loaderdir, $loaderoptions));
}
}
// Look in a components templates dir for a base implementation.
$compdirectory = core_component::get_component_directory($suffix);
if ($compdirectory) {
$loaderdir = $compdirectory . '/templates';
if (is_dir($loaderdir)) {
$loader->addLoader(new \core\output\mustache_filesystem_loader($loaderdir, $loaderoptions));
}
}
// Look in the core templates dir as a final fallback.
$compdirectory = $CFG->libdir;
if ($compdirectory) {
$loaderdir = $compdirectory . '/templates';
if (is_dir($loaderdir)) {
$loader->addLoader(new \core\output\mustache_filesystem_loader($loaderdir, $loaderoptions));
}
}
$stringhelper = new \core\output\mustache_string_helper();
$jshelper = new \core\output\mustache_javascript_helper($this->page->requires);
$pixhelper = new \core\output\mustache_pix_helper($this);
// We only expose the variables that are exposed to JS templates.
$safeconfig = $this->page->requires->get_config_for_javascript($this->page, $this);
$helpers = array('config' => $safeconfig,
'str' => array($stringhelper, 'str'),
'js' => array($jshelper, 'help'),
'pix' => array($pixhelper, 'pix'));
$this->mustache = new Mustache_Engine(array(
'cache' => $cachedir,
'escape' => 's',
'loader' => $loader,
'helpers' => $helpers));
}
return $this->mustache;
}
/**
* Constructor
*
@@ -83,6 +206,39 @@ class renderer_base {
$this->target = $target;
}
/**
* Renders a template by name with the given context.
*
* The provided data needs to be array/stdClass made up of only simple types.
* Simple types are array,stdClass,bool,int,float,string
*
* @since 2.9
* @param array|stdClass $context Context containing data for the template.
* @return string|boolean
*/
public function render_from_template($templatename, $context) {
static $templatecache = array();
$mustache = $this->get_mustache();
// Provide 1 random value that will not change within a template
// but will be different from template to template. This is useful for
// e.g. aria attributes that only work with id attributes and must be
// unique in a page.
$mustache->addHelper('uniqid', new \core\output\mustache_uniqid_helper());
if (isset($templatecache[$templatename])) {
$template = $templatecache[$templatename];
} else {
try {
$template = $mustache->loadTemplate($templatename);
$templatecache[$templatename] = $template;
} catch (Mustache_Exception_UnknownTemplateException $e) {
throw new moodle_exception('Unknown template: ' . $templatename);
}
}
return trim($template->render($context));
}
/**
* Returns rendered widget.
*
@@ -2016,7 +2172,11 @@ class core_renderer extends renderer_base {
protected function render_pix_icon(pix_icon $icon) {
$attributes = $icon->attributes;
$attributes['src'] = $this->pix_url($icon->pix, $icon->component);
return html_writer::empty_tag('img', $attributes);
$templatecontext = array();
foreach ($attributes as $name => $value) {
$templatecontext[] = array('name' => $name, 'value' => $value);
}
return $this->render_from_template('core/pix_icon', array('attributes' => $templatecontext));
}
/**