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; } }