[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/ -> outputfactories.php (source)

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Interface and classes for creating appropriate renderers for various parts of Moodle.
  19   *
  20   * Please see http://docs.moodle.org/en/Developement:How_Moodle_outputs_HTML
  21   * for an overview.
  22   *
  23   * @copyright 2009 Tim Hunt
  24   * @license  http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25   * @package core
  26   * @category output
  27   */
  28  
  29  defined('MOODLE_INTERNAL') || die();
  30  
  31  /** General rendering target, usually normal browser page */
  32  define('RENDERER_TARGET_GENERAL', 'general');
  33  
  34  /** General rendering target, usually normal browser page, but with limited capacity to avoid API use */
  35  define('RENDERER_TARGET_MAINTENANCE', 'maintenance');
  36  
  37  /** Plain text rendering for CLI scripts and cron */
  38  define('RENDERER_TARGET_CLI', 'cli');
  39  
  40  /** Plain text rendering for Ajax scripts*/
  41  define('RENDERER_TARGET_AJAX', 'ajax');
  42  
  43  /** Plain text rendering intended for sending via email */
  44  define('RENDERER_TARGET_TEXTEMAIL', 'textemail');
  45  
  46  /** Rich text html rendering intended for sending via email */
  47  define('RENDERER_TARGET_HTMLEMAIL', 'htmlemail');
  48  
  49  // note: maybe we could define portfolio export target too
  50  
  51  
  52  /**
  53   * A renderer factory is just responsible for creating an appropriate renderer
  54   * for any given part of Moodle.
  55   *
  56   * Which renderer factory to use is chose by the current theme, and an instance
  57   * if created automatically when the theme is set up.
  58   *
  59   * A renderer factory must also have a constructor that takes a theme_config object.
  60   * (See {@link renderer_factory_base::__construct} for an example.)
  61   *
  62   * @copyright 2009 Tim Hunt
  63   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  64   * @since Moodle 2.0
  65   * @package core
  66   * @category output
  67   */
  68  interface renderer_factory {
  69  
  70      /**
  71       * Return the renderer for a particular part of Moodle.
  72       *
  73       * The renderer interfaces are defined by classes called {plugin}_renderer
  74       * where {plugin} is the name of the component. The renderers for core Moodle are
  75       * defined in lib/renderer.php. For plugins, they will be defined in a file
  76       * called renderer.php inside the plugin.
  77       *
  78       * Renderers will normally want to subclass the renderer_base class.
  79       * (However, if you really know what you are doing, you don't have to do that.)
  80       *
  81       * There is no separate interface definition for renderers. The default
  82       * {plugin}_renderer implementation also serves to define the API for
  83       * other implementations of the interface, whether or not they subclass it.
  84       *
  85       * A particular plugin can define multiple renderers if it wishes, using the
  86       * $subtype parameter. For example workshop_renderer,
  87       * workshop_allocation_manual_renderer etc.
  88       *
  89       * @param moodle_page $page the page the renderer is outputting content for.
  90       * @param string $component name such as 'core', 'mod_forum' or 'qtype_multichoice'.
  91       * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
  92       * @param string $target one of rendering target constants
  93       * @return renderer_base an object implementing the requested renderer interface.
  94       */
  95      public function get_renderer(moodle_page $page, $component, $subtype=null, $target=null);
  96  }
  97  
  98  
  99  /**
 100   * This is a base class to help you implement the renderer_factory interface.
 101   *
 102   * It keeps a cache of renderers that have been constructed, so you only need
 103   * to construct each one once in you subclass.
 104   *
 105   * It also has a method to get the name of, and include the renderer.php with
 106   * the definition of, the standard renderer class for a given module.
 107   *
 108   * @copyright 2009 Tim Hunt
 109   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 110   * @since Moodle 2.0
 111   * @package core
 112   * @category output
 113   */
 114  abstract class renderer_factory_base implements renderer_factory {
 115      /**
 116       * @var theme_config The theme we belong to.
 117       */
 118      protected $theme;
 119  
 120      /**
 121       * Constructor.
 122       *
 123       * @param theme_config $theme the theme we belong to.
 124       */
 125      public function __construct(theme_config $theme) {
 126          $this->theme = $theme;
 127      }
 128  
 129      /**
 130       * Returns suffix of renderer class expected for given target.
 131       *
 132       * @param string $target one of the renderer target constants, target is guessed if null used
 133       * @return array two element array, first element is target, second the target suffix string
 134       */
 135      protected function get_target_suffix($target) {
 136          if (empty($target) || $target === RENDERER_TARGET_MAINTENANCE) {
 137              // If the target hasn't been specified we need to guess the defaults.
 138              // We also override the target with the default if the maintenance target has been provided.
 139              // This ensures we don't use the maintenance renderer if we are processing a special target.
 140              if (CLI_SCRIPT) {
 141                  $target = RENDERER_TARGET_CLI;
 142              } else if (AJAX_SCRIPT) {
 143                  $target = RENDERER_TARGET_AJAX;
 144              }
 145          }
 146  
 147          switch ($target) {
 148              case RENDERER_TARGET_CLI: $suffix = '_cli'; break;
 149              case RENDERER_TARGET_AJAX: $suffix = '_ajax'; break;
 150              case RENDERER_TARGET_TEXTEMAIL: $suffix = '_textemail'; break;
 151              case RENDERER_TARGET_HTMLEMAIL: $suffix = '_htmlemail'; break;
 152              case RENDERER_TARGET_MAINTENANCE: $suffix = '_maintenance'; break;
 153              default: $target = RENDERER_TARGET_GENERAL; $suffix = '';
 154          }
 155  
 156          return array($target, $suffix);
 157      }
 158  
 159      /**
 160       * For a given module name, return the possible class names
 161       * that defines the renderer interface for that module.
 162       *
 163       * Newer auto-loaded class names are returned as well as the old style _renderable classnames.
 164       *
 165       * Also, if it exists, include the renderer.php file for that module, so
 166       * the class definition of the default renderer has been loaded.
 167       *
 168       * @param string $component name such as 'core', 'mod_forum' or 'qtype_multichoice'.
 169       * @param string $subtype optional subtype such as 'news' resulting to:
 170       *              '\mod_forum\output\news_renderer'
 171       *              or '\mod_forum\output\news\renderer'
 172       *              or non-autoloaded 'mod_forum_news'
 173       * @return array[] Each element of the array is an array with keys:
 174       *                 classname - The class name to search
 175       *                 autoloaded - Does this classname assume autoloading?
 176       *                 validwithprefix - Is this class name valid when a prefix is added to it?
 177       *                 validwithoutprefix - Is this class name valid when no prefix is added to it?
 178       * @throws coding_exception
 179       */
 180      protected function standard_renderer_classnames($component, $subtype = null) {
 181          global $CFG; // Needed in included files.
 182          $classnames = array();
 183  
 184          // Standardize component name ala frankenstyle.
 185          list($plugin, $type) = core_component::normalize_component($component);
 186          if ($type === null) {
 187              $component = $plugin;
 188          } else {
 189              $component = $plugin.'_'.$type;
 190          }
 191  
 192          if ($component !== 'core') {
 193              // Renderers are stored in renderer.php files.
 194              if (!$compdirectory = core_component::get_component_directory($component)) {
 195                  throw new coding_exception('Invalid component specified in renderer request', $component);
 196              }
 197              $rendererfile = $compdirectory . '/renderer.php';
 198              if (file_exists($rendererfile)) {
 199                  include_once($rendererfile);
 200              }
 201  
 202          } else if (!empty($subtype)) {
 203              $coresubsystems = core_component::get_core_subsystems();
 204              if (!array_key_exists($subtype, $coresubsystems)) { // There may be nulls.
 205                  throw new coding_exception('Invalid core subtype "' . $subtype . '" in renderer request', $subtype);
 206              }
 207              if ($coresubsystems[$subtype]) {
 208                  $rendererfile = $coresubsystems[$subtype] . '/renderer.php';
 209                  if (file_exists($rendererfile)) {
 210                      include_once($rendererfile);
 211                  }
 212              }
 213          }
 214  
 215          if (empty($subtype)) {
 216              // Theme specific auto-loaded name (only valid when prefixed with the theme name).
 217              $classnames[] = array(
 218                  'validwithprefix' => true,
 219                  'validwithoutprefix' => false,
 220                  'autoloaded' => true,
 221                  'classname' => '\\output\\' . $component . '_renderer'
 222              );
 223  
 224              // Standard autoloaded plugin name (not valid with a prefix).
 225              $classnames[] = array(
 226                  'validwithprefix' => false,
 227                  'validwithoutprefix' => true,
 228                  'autoloaded' => true,
 229                  'classname' => '\\' . $component . '\\output\\renderer'
 230              );
 231              // Legacy class name - (valid with or without a prefix).
 232              $classnames[] = array(
 233                  'validwithprefix' => true,
 234                  'validwithoutprefix' => true,
 235                  'autoloaded' => false,
 236                  'classname' => $component . '_renderer'
 237              );
 238          } else {
 239              // Theme specific auto-loaded name (only valid when prefixed with the theme name).
 240              $classnames[] = array(
 241                  'validwithprefix' => true,
 242                  'validwithoutprefix' => false,
 243                  'autoloaded' => true,
 244                  'classname' => '\\output\\' . $component . '\\' . $subtype . '_renderer'
 245              );
 246              // Version of the above with subtype being a namespace level on it's own.
 247              $classnames[] = array(
 248                  'validwithprefix' => true,
 249                  'validwithoutprefix' => false,
 250                  'autoloaded' => true,
 251                  'classname' => '\\output\\' . $component . '\\' . $subtype . '\\renderer'
 252              );
 253              // Standard autoloaded plugin name (not valid with a prefix).
 254              $classnames[] = array(
 255                  'validwithprefix' => false,
 256                  'validwithoutprefix' => true,
 257                  'autoloaded' => true,
 258                  'classname' => '\\' . $component . '\\output\\' . $subtype . '_renderer'
 259              );
 260              // Version of the above with subtype being a namespace level on it's own.
 261              $classnames[] = array(
 262                  'validwithprefix' => false,
 263                  'validwithoutprefix' => true,
 264                  'autoloaded' => true,
 265                  'classname' => '\\' . $component . '\\output\\' . $subtype . '\\renderer'
 266              );
 267              // Legacy class name - (valid with or without a prefix).
 268              $classnames[] = array(
 269                  'validwithprefix' => true,
 270                  'validwithoutprefix' => true,
 271                  'autoloaded' => false,
 272                  'classname' => $component . '_' . $subtype . '_renderer'
 273              );
 274          }
 275          return $classnames;
 276      }
 277  }
 278  
 279  /**
 280   * This is the default renderer factory for Moodle.
 281   *
 282   * It simply returns an instance of the appropriate standard renderer class.
 283   *
 284   * @copyright 2009 Tim Hunt
 285   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 286   * @since Moodle 2.0
 287   * @package core
 288   * @category output
 289   */
 290  class standard_renderer_factory extends renderer_factory_base {
 291  
 292      /**
 293       * Implement the subclass method
 294       *
 295       * @param moodle_page $page the page the renderer is outputting content for.
 296       * @param string $component name such as 'core', 'mod_forum' or 'qtype_multichoice'.
 297       * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
 298       * @param string $target one of rendering target constants
 299       * @return renderer_base an object implementing the requested renderer interface.
 300       */
 301      public function get_renderer(moodle_page $page, $component, $subtype = null, $target = null) {
 302          $classnames = $this->standard_renderer_classnames($component, $subtype);
 303          $classname = '';
 304  
 305          list($target, $suffix) = $this->get_target_suffix($target);
 306          // First look for a version with a suffix.
 307          foreach ($classnames as $classnamedetails) {
 308              if ($classnamedetails['validwithoutprefix']) {
 309                  $newclassname = $classnamedetails['classname'] . $suffix;
 310                  if (class_exists($newclassname)) {
 311                      $classname = $newclassname;
 312                      break;
 313                  } else {
 314                      $newclassname = $classnamedetails['classname'];
 315                      if (class_exists($newclassname)) {
 316                          $classname = $newclassname;
 317                          break;
 318                      }
 319                  }
 320              }
 321          }
 322          // Now look for a non-suffixed version.
 323          if (empty($classname)) {
 324              foreach ($classnames as $classnamedetails) {
 325                  if ($classnamedetails['validwithoutprefix']) {
 326                      $newclassname = $classnamedetails['classname'];
 327                      if (class_exists($newclassname)) {
 328                          $classname = $newclassname;
 329                          break;
 330                      }
 331                  }
 332              }
 333          }
 334  
 335          if (empty($classname)) {
 336              // Standard renderer must always exist.
 337              throw new coding_exception('Request for an unknown renderer class. Searched for: ' . var_export($classnames, true));
 338          }
 339  
 340          return new $classname($page, $target);
 341      }
 342  }
 343  
 344  
 345  /**
 346   * This is renderer factory allows themes to override the standard renderers using php code.
 347   *
 348   * It will load any code from theme/mytheme/renderers.php and
 349   * theme/parenttheme/renderers.php, if then exist. Then whenever you ask for
 350   * a renderer for 'component', it will create a mytheme_component_renderer or a
 351   * parenttheme_component_renderer, instead of a component_renderer,
 352   * if either of those classes exist.
 353   *
 354   * @copyright 2009 Tim Hunt
 355   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 356   * @since Moodle 2.0
 357   * @package core
 358   * @category output
 359   */
 360  class theme_overridden_renderer_factory extends renderer_factory_base {
 361  
 362      /**
 363       * @var array An array of renderer prefixes
 364       */
 365      protected $prefixes = array();
 366  
 367      /**
 368       * Constructor.
 369       * @param theme_config $theme the theme we are rendering for.
 370       */
 371      public function __construct(theme_config $theme) {
 372          parent::__construct($theme);
 373          // Initialise $this->prefixes.
 374          $this->prefixes = $theme->renderer_prefixes();
 375      }
 376  
 377      /**
 378       * Implement the subclass method
 379       *
 380       * @param moodle_page $page the page the renderer is outputting content for.
 381       * @param string $component name such as 'core', 'mod_forum' or 'qtype_multichoice'.
 382       * @param string $subtype optional subtype such as 'news' resulting to 'mod_forum_news'
 383       * @param string $target one of rendering target constants
 384       * @return renderer_base an object implementing the requested renderer interface.
 385       */
 386      public function get_renderer(moodle_page $page, $component, $subtype = null, $target = null) {
 387          $classnames = $this->standard_renderer_classnames($component, $subtype);
 388  
 389          list($target, $suffix) = $this->get_target_suffix($target);
 390  
 391          // Theme lib.php and renderers.php files are loaded automatically
 392          // when loading the theme configs.
 393  
 394          // First try the renderers with correct suffix.
 395          foreach ($this->prefixes as $prefix) {
 396              foreach ($classnames as $classnamedetails) {
 397                  if ($classnamedetails['validwithprefix']) {
 398                      if ($classnamedetails['autoloaded']) {
 399                          $newclassname = $prefix . $classnamedetails['classname'] . $suffix;
 400                      } else {
 401                          $newclassname = $prefix . '_' . $classnamedetails['classname'] . $suffix;
 402                      }
 403                      if (class_exists($newclassname)) {
 404                          return new $newclassname($page, $target);
 405                      }
 406                  }
 407              }
 408          }
 409          foreach ($classnames as $classnamedetails) {
 410              if ($classnamedetails['validwithoutprefix']) {
 411                  $newclassname = $classnamedetails['classname'] . $suffix;
 412                  if (class_exists($newclassname)) {
 413                      // Use the specialised renderer for given target, default renderer might also decide
 414                      // to implement support for more targets.
 415                      return new $newclassname($page, $target);
 416                  }
 417              }
 418          }
 419  
 420          // Then try general renderer.
 421          foreach ($this->prefixes as $prefix) {
 422              foreach ($classnames as $classnamedetails) {
 423                  if ($classnamedetails['validwithprefix']) {
 424                      if ($classnamedetails['autoloaded']) {
 425                          $newclassname = $prefix . $classnamedetails['classname'];
 426                      } else {
 427                          $newclassname = $prefix . '_' . $classnamedetails['classname'];
 428                      }
 429                      if (class_exists($newclassname)) {
 430                          return new $newclassname($page, $target);
 431                      }
 432                  }
 433              }
 434          }
 435  
 436          // Final attempt - no prefix or suffix.
 437          foreach ($classnames as $classnamedetails) {
 438              if ($classnamedetails['validwithoutprefix']) {
 439                  $newclassname = $classnamedetails['classname'];
 440                  if (class_exists($newclassname)) {
 441                      return new $newclassname($page, $target);
 442                  }
 443              }
 444          }
 445          throw new coding_exception('Request for an unknown renderer ' . $component . ', ' . $subtype . ', ' . $target);
 446      }
 447  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1