messaging_template.inc in Messaging 6.3
Same filename and directory in other branches
Base classes for messaging templates
Though the API is language enabled, this base class will only work for a single language that must be defined when creating the class.
Templates can be nested and have multiple parts
File
messaging_template/messaging_template.incView source
<?php
/**
* @file
* Base classes for messaging templates
*
* Though the API is language enabled, this base class will only work for a single
* language that must be defined when creating the class.
*
* Templates can be nested and have multiple parts
*
*/
/**
* Methods a template element must implement so it can be nested and rendered
*
* @see Messaging_Template
* @see Messaging_Template_Part
*/
interface Messaging_Template_Element {
// Get text content as array or string
public function get_content($method, $language);
// Whether this template needs object replacement
public function needs_replace();
// Reset all rendered text elements
public function reset($recurse = FALSE);
}
/**
* Static template functions that interact with the module API
*/
class Messaging_Template_Engine {
// Template information
protected static $info;
// Built templates
protected static $templates;
// Part keys indexed by group/type
protected static $keys;
// Default templates from modules
protected static $defaults;
// Global class debug
protected static $_debug = FALSE;
/**
* Get template type info
*/
static function get_info($type) {
if (!isset(self::$info)) {
self::$info = messaging_template_info(NULL, NULL);
}
return isset(self::$info[$type]) ? self::$info[$type] : NULL;
}
/**
* Create template, just allowed types
*/
static function create_template($type = 'messaging-template', $parent = NULL, $method = NULL, $language = NULL) {
// Check this is a known type, if not return NULL
if (self::get_info($type)) {
return new Message_Template($type, $parent, $method, $language);
}
else {
return NULL;
}
}
/**
* Get template part, FALSE if not found
*
* @param $key
* Part key, like 'subject', 'header', 'footer', ...
* @param $type
* Template type, like 'messaging-template', 'notifications-event'...
*/
static function get_template_part($key, $type, $method, $language) {
$lang = $language->language;
self::_debug('Getting template part', array(
'type' => $type,
'key' => $key,
'method' => $method,
'language' => $lang,
));
self::build_template($type, $method, $language);
if (!isset(self::$templates[$lang][$type][$method][$key])) {
// Try method default for the same type
if ($method_default = self::method_default($method)) {
$part = self::get_template_part($key, $type, $method_default, $language);
}
elseif (!($part = self::get_default($type, $key, $language))) {
$part = FALSE;
}
self::$templates[$lang][$type][$method][$key] = $part;
}
elseif (self::$templates[$lang][$type][$method][$key]->options == MESSAGING_TEMPLATE_FALLBACK) {
// Resolve fallbacks first time the part is retrieved
if ($fallback = self::type_fallback($type)) {
self::$templates[$lang][$type][$method][$key] = self::get_template_part($key, $fallback, $method, $language);
}
else {
self::$templates[$lang][$type][$method][$key] = FALSE;
}
}
return self::$templates[$lang][$type][$method][$key];
}
/**
* Build a given template
*/
static function build_template($type, $method, $language) {
$lang = $language->language;
if (!isset(self::$templates[$lang][$type][$method])) {
self::_debug('Building template', array(
'type' => $type,
'method' => $method,
));
self::$templates[$lang][$type][$method] = messaging_template_get_parts($type, $method, $language);
}
}
/**
* Get default provided by modules
*/
static function get_default($type, $key, $language) {
$lang = $language->language;
self::_debug('Getting template default', array(
'type' => $type,
'key' => $key,
'language' => $lang,
));
if (!isset(self::$defaults[$lang][$type])) {
self::$defaults[$lang][$type] = messaging_template_get_defaults($type, $language);
}
if (!isset(self::$defaults[$lang][$type][$key])) {
if ($type_fallback = self::type_fallback($type)) {
self::$defaults[$lang][$type][$key] = self::get_default($type_fallback, $key, $language);
}
else {
self::$defaults[$lang][$type][$key] = FALSE;
}
}
return self::$defaults[$lang][$type][$key];
}
/**
* Get part keys for a given template
*/
protected function get_keys($type) {
if (!isset(self::$keys[$type])) {
self::$keys[$type] = messaging_template_get_keys($type);
}
return array_keys(self::$keys[$type]);
}
/**
* Get method fallback
*/
static function method_default($method) {
return messaging_template_method_default($method);
}
/**
* Get type fallback
*/
static function type_fallback($type) {
return messaging_template_fallback($type);
}
/**
* Render recursively an array of elements
*/
static function render_elements(&$elements, $method, $language, $objects = array()) {
$replace = $texts = array();
foreach ($elements as $key => $element) {
if (is_object($element)) {
$needs_replace = $element
->needs_replace();
$value = $element
->get_content($method, $language);
}
elseif (is_array($element)) {
$needs_replace = FALSE;
$value = self::render_elements($element, $method, $language, $objects);
}
else {
$needs_replace = TRUE;
$value = $element;
}
// If needs replace add $texts placeholder to preserve the order
if ($needs_replace) {
$replace[$key] = $value;
$texts[$key] = NULL;
}
else {
$texts[$key] = $value;
}
}
if ($replace) {
$texts = array_merge($texts, self::text_replace($replace, $objects, $method, $language));
}
return $texts;
}
/**
* Replace text with object tokens
*/
static function text_replace($text, $objects, $language) {
return messaging_template_text_replace($text, $objects, $language);
}
/**
* Debug, static version
*/
static function _debug($txt, $variables = array()) {
if (self::$_debug) {
messaging_debug($txt, $variables);
}
}
}
/**
* Messaging Template class
*/
class Messaging_Template extends Messaging_Template_Engine implements Messaging_Message_Template, Messaging_Template_Element {
// Basic template parameters
public $type = NULL;
public $language = NULL;
public $method = NULL;
// Parent template and template engine
protected $parent;
// Template raw elements. Each of them may be:
// - A text part string, ready for replacement
// - An array like (text_part_key, default)
// - A Template object
protected $elements = array();
// Objects for replacement
protected $objects = array();
// Text templates for each part indexed by method and language code
public $texts = array();
// Predefined texts to use instead of text parts
public $presets = array();
// Debug option on/off
public $debug = TRUE;
/**
* Class constructor, create a template of given type
*/
function __construct($type = 'messaging-template', $parent = NULL, $method = NULL, $language = NULL) {
$this->type = $type;
if ($parent) {
$this->parent = $parent;
$this->method = $method;
$this->language = $language;
}
else {
$this->language = $language ? $language : language_default();
$this->method = $method ? $method : 'default';
$this->objects = array(
'global' => NULL,
);
}
}
/**
* Get child template derived from this one
*/
function get_template($type = 'messaging-template', $method = NULL, $language = NULL) {
// Check this is a known type, if not return NULL,
return self::create_template($type, $this, $method, $language);
}
/**
* Get template part, FALSE if not found
*/
function get_part($key, $type = NULL, $method = NULL, $language = NULL) {
return self::get_template_part($key, $type ? $type : $this->type, $method ? $method : $this
->get_method(), $language ? $language : $this
->get_language());
}
/**
* Set object for token replacement
*/
function set_object($type, $object = NULL) {
$this->objects[$type] = $object;
}
/**
* @param $key
* Template part key
* @param $default
* Default value if key not found
* @param $duplicate
* Add duplicates (if this part has been already added)
*/
function add_part($key, $default = NULL, $duplicate = FALSE) {
if (isset($this->elements[$key]) && !$duplicate) {
return;
}
if (isset($this->presets[$key])) {
$this
->append($this->presets[$key], $key);
}
else {
// Add as Template_Text_Part
$this
->append(new Messaging_Template_Part($this, $key, $default), $key);
}
}
// Add child template
function add_child($template, $key = NULL) {
$template->parent = $this;
$this
->append($template, $key);
}
// Get text part, possibly from parent
function xget_part($key) {
return $this->engine
->get_part($key, $this->type, $this
->get_method(), $this
->get_language());
}
/**
* Append element: may be a text or another template
*/
function append($value, $key = NULL) {
if (!isset($key)) {
$this->elements[] = $value;
}
elseif (isset($this->elements[$key])) {
// If not an array yet, make it into an array
if (!is_array($this->elements[$key])) {
$this->elements[$key] = array(
$this->elements[$key],
);
}
$this->elements[$key][] = $value;
}
else {
$this->elements[$key] = $value;
}
}
/**
* Render all template elements
*
* @param $method
* Sending method to render for
* @param $language
* Language object
*/
function render($method, $language) {
if (!isset($this->texts[$method][$language->language])) {
$this->texts[$method][$language->language] = $this
->render_elements($this->elements, $method, $language, $this
->get_objects());
}
return $this->texts;
}
/**
* Reset all template elements for new rendering
*
* @param $recurse
* Reset child templates too
*/
function reset($recurse = FALSE) {
$this->rendered = FALSE;
$this->texts = array();
if ($recurse && $this->elements) {
$this
->reset_elements($this->elements);
}
}
/**
* Reset recursively an array of elements
*/
function reset_elements(&$elements) {
foreach ($elements as $element) {
if (is_object($element)) {
$element
->reset(TRUE);
}
elseif (is_array($element)) {
reset_elements($element);
}
}
}
/**
* Get template text part, FALSE if not found
*
* Here we translate MESSAGING_EMPTY into an empty string
*/
function text_part($type, $key, $method = NULL, $language = NULL) {
$method = $method ? $method : $this
->get_method();
$language = $language ? $language : $this
->get_language();
$this
->build_template($type, $method, $language);
$part = $this
->get_part($key, $type, $method);
$text = $part ? $part->template : FALSE;
$this
->debug('Getting text part', array(
'type' => $type,
'key' => $key,
'method' => $method,
'text' => $text,
));
return $text;
}
/**
* Get text elements as array
*
* @param $key
* Text part key to return, like 'subject', 'body'...
*/
function get_text($key, $method = NULL, $language = NULL) {
$method = $method ? $method : $this
->get_method();
$language = $language ? $language : $this
->get_language();
$content = $this
->get_content($method, $language);
return isset($content[$key]) ? $content[$key] : NULL;
}
/**
* Get all text parts as array
*/
function get_content($method, $language) {
$this
->render($method, $language);
return isset($this->texts[$method][$language->language]) ? $this->texts[$method][$language->language] : NULL;
}
/**
* Get text element as string
*/
function get_string($key, $glue = "\n", $default = '') {
if ($text = $this
->get_text($key)) {
return $this
->compose($text, $glue);
}
else {
return $default;
}
}
/**
* Compose string
*/
function compose($elements, $glue = "\n") {
$compose = array();
foreach ($elements as $part) {
$compose[] = is_string($part) ? $part : $this
->compose($part, $glue);
}
return implode($glue, $compose);
}
/**
* Check whether this template needs aditional replacement
*/
function needs_replace() {
return FALSE;
}
// Implementation of Messaging_Template_Parent
/**
* Get language, default to parent's
*/
function get_language($property = NULL) {
// Either the template has a language or has a parent who's got it
if (isset($this->language)) {
return $property ? $this->language->{$property} : $this->language;
}
elseif (isset($this->parent)) {
return $this->parent
->get_language($property);
}
else {
return language_default($property);
}
}
/**
* Get sending method, default to parent's
*/
function get_method() {
return $this->method ? $this->method : $this->parent
->get_method();
}
/**
* Get template objects, included parent's objects
*/
function get_objects($get_parent = FALSE) {
if ($get_parent && isset($this->parent)) {
return $this->objects + $this->parent
->get_objects();
}
else {
return $this->objects;
}
}
/**
* Debug, adding instance information
*/
function debug($txt, $variables = array()) {
if ($this->debug) {
$variables += array(
'type' => $this->type,
'method' => $this
->get_method(),
);
messaging_debug($txt, $variables);
}
}
// Magic function, format as string
public function __toString() {
return "Messaging_Template: type={$this->type}, subject=" . $this
->get_string('subject');
}
/**
* Implementation of Messaging_Message_Template
*/
/**
* Build a new message for method, destination
*
* @param $method
* Sending method
* @param $language
* Language object
* @return Messaging_Message
* Message built for method, language
*/
public function build($method = NULL, $language = NULL) {
$build = array(
'template' => $this,
'method' => $method,
'language' => $language ? $language : $this
->get_language('language'),
);
return new Messaging_Message($build);
}
/**
* Get subject message parts
*
* @param $method
* Sending method
* @param $language
* Language code
* @return string or array()
* Subject text or text parts for rendering
*/
function get_subject($method = NULL, $language = NULL) {
return $this
->get_text('subject', $method, $language);
}
/**
* Get body parts
*
* @param $method
* Sending method
* @param $language
* Language code
* @return string or array()
* Body text or text parts for renderin
*/
function get_body($method = NULL, $language = NULL) {
return $this
->get_text('body', $method, $language);
}
}
/**
* Each text part to be rendered independently
*/
class Messaging_Template_Part implements Messaging_Template_Element {
// Part key and default value
public $key;
public $default;
public $text;
// Parent template object
protected $template;
// Constructor
function __construct($template, $key, $default = NULL) {
$this->template = $template;
$this->key = $key;
$this->default = $default;
}
// Get content as string
function get_content($method, $language) {
if (!isset($this->text[$method][$language->language])) {
if ($part = $this->template
->get_part($this->key, NULL, $method, $language)) {
$text = $part->template;
}
else {
$text = isset($this->default) ? $this->default : '';
}
$this->text[$method][$language->language] = $text;
}
return $this->text[$method][$language->language];
}
// This element needs token replacement
function needs_replace() {
return TRUE;
}
// Reset text
function reset($recurse = FALSE) {
$this->text = NULL;
}
}
Classes
Name | Description |
---|---|
Messaging_Template | Messaging Template class |
Messaging_Template_Engine | Static template functions that interact with the module API |
Messaging_Template_Part | Each text part to be rendered independently |
Interfaces
Name | Description |
---|---|
Messaging_Template_Element | Methods a template element must implement so it can be nested and rendered |