b2evolution PHP Cross Reference Blogging Systems

Source: /inc/plugins/model/_plugins.class.php - 2151 lines - 60576 bytes - Summary - Text - Print

Description: This file implements the PluginS class. This is where you can plug in some {@link Plugin plugins} :D

   1  <?php
   2  /**
   3   * This file implements the PluginS class.
   4   *
   5   * This is where you can plug in some {@link Plugin plugins} :D
   6   *
   7   * This file is part of the evoCore framework - {@link http://evocore.net/}
   8   * See also {@link http://sourceforge.net/projects/evocms/}.
   9   *
  10   * @copyright (c)2003-2014 by Francois Planque - {@link http://fplanque.com/}
  11   * Parts of this file are copyright (c)2004-2006 by Daniel HAHLER - {@link http://thequod.de/contact}.
  12   *
  13   * {@internal License choice
  14   * - If you have received this file as part of a package, please find the license.txt file in
  15   *   the same folder or the closest folder above for complete license terms.
  16   * - If you have received this file individually (e-g: from http://evocms.cvs.sourceforge.net/)
  17   *   then you must choose one of the following licenses before using the file:
  18   *   - GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php
  19   *   - Mozilla Public License 1.1 (MPL) - http://www.opensource.org/licenses/mozilla1.1.php
  20   * }}
  21   *
  22   * {@internal Open Source relicensing agreement:
  23   * Daniel HAHLER grants Francois PLANQUE the right to license
  24   * Daniel HAHLER's contributions to this file and the b2evolution project
  25   * under any OSI approved OSS license (http://www.opensource.org/licenses/).
  26   * }}
  27   *
  28   * @package plugins
  29   *
  30   * {@internal Below is a list of authors who have contributed to design/coding of this file: }}
  31   * @author fplanque: Francois PLANQUE - {@link http://fplanque.net/}
  32   * @author blueyed: Daniel HAHLER
  33   *
  34   * @version $Id: _plugins.class.php 6136 2014-03-08 07:59:48Z manuel $
  35   */
  36  if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
  37  
  38  
  39  // DEBUG: (Turn switch on or off to log debug info for specified category)
  40  $GLOBALS['debug_plugins'] = false;
  41  
  42  
  43  load_class( 'plugins/_plugin.class.php', 'Plugin' );
  44  
  45  
  46  /**
  47   * Plugins Class
  48   *
  49   * This is where you can plug in some {@link Plugin plugins} :D
  50   *
  51   * @todo dh> Currently when a plugin goes into "broken" status (e.g. file not readable), it is "disabled" afterwards.
  52   *       This should rather remember the old status (e.g. "enabled") and make it enabled again.
  53   *
  54   * @package plugins
  55   */
  56  class Plugins
  57  {
  58      /**#@+
  59       * @access private
  60       */
  61  
  62      /**
  63       * @var array of plugin_code => Plugin
  64       */
  65      var $index_code_Plugins = array();
  66  
  67      /**
  68       * @var array of plugin_ID => Plugin
  69       */
  70      var $index_ID_Plugins = array();
  71  
  72      /**
  73       * @see Plugins::load_events()
  74       * @var array of event => plug_ID. IDs are sorted by priority.
  75       */
  76      var $index_event_IDs = array();
  77  
  78      /**
  79       * @var array of plug_ID => DB row from T_plugins. Used to lazy-instantiate Plugins.
  80       */
  81      var $index_ID_rows = array();
  82  
  83      /**
  84       * @var array of plug_code => plug_ID. Used to lazy-instantiate by code.
  85       */
  86      var $index_code_ID = array();
  87  
  88      /**
  89       * Cache Plugin codes by apply_rendering setting.
  90       * @var array of apply_rendering => plug_code
  91       */
  92      var $index_apply_rendering_codes = array();
  93  
  94      /**
  95       * Cache all plugin codes by blogs apply_comment_rendering setting.
  96       * @var array of blog_ID => array of plugin codes
  97       */
  98      var $coll_apply_comment_rendering = array();
  99  
 100      /**
 101       * Path to plugins.
 102       *
 103       * The preferred method is to have a sub-directory for each plugin (named
 104       * after the plugin's classname), but they can be supplied just in this
 105       * directory.
 106       */
 107      var $plugins_path;
 108  
 109      /**
 110       * Have we loaded the plugins table (T_plugins)?
 111       * @var boolean
 112       */
 113      var $loaded_plugins_table = false;
 114  
 115      /**
 116       * Current object index in {@link $sorted_IDs} array.
 117       * @var integer
 118       */
 119      var $current_idx = -1;
 120  
 121      /**
 122       * List of IDs, sorted. This gets used to lazy-instantiate a Plugin.
 123       *
 124       * @var array
 125       */
 126      var $sorted_IDs = array();
 127  
 128      /**
 129       * The smallest internal/auto-generated Plugin ID.
 130       * @var integer
 131       */
 132      var $smallest_internal_ID = 0;
 133  
 134      /**#@-*/
 135  
 136  
 137      /**#@+
 138       * @access protected
 139       */
 140  
 141      /**
 142       * SQL to use in {@link load_plugins_table()}. Gets overwritten for {@link Plugins_admin}.
 143       * @var string
 144       * @static
 145       */
 146      var $sql_load_plugins_table = '
 147              SELECT plug_ID, plug_priority, plug_classname, plug_code, plug_name, plug_shortdesc, plug_status, plug_version, plug_spam_weight
 148                FROM T_plugins
 149               WHERE plug_status = \'enabled\'
 150               ORDER BY plug_priority, plug_classname';
 151  
 152      /**#@-*/
 153  
 154  
 155      /**
 156       * Errors associated to plugins (during loading), indexed by plugin_ID and
 157       * error class ("register").
 158       *
 159       * @var array
 160       */
 161      var $plugin_errors = array();
 162  
 163  
 164      /**
 165       * Constructor. Sets {@link $plugins_path} and load events.
 166       */
 167  	function Plugins()
 168      {
 169          global $basepath, $plugins_subdir, $Timer;
 170  
 171          // Set plugin path:
 172          $this->plugins_path = $basepath.$plugins_subdir;
 173  
 174          $Timer->resume( 'plugin_init' );
 175  
 176          // Load events for enabled plugins:
 177          $this->load_events();
 178  
 179          $Timer->pause( 'plugin_init' );
 180      }
 181  
 182  
 183      /**
 184       * Get a list of available Plugin groups.
 185       *
 186       * @return array
 187       */
 188  	function get_plugin_groups()
 189      {
 190          $result = array();
 191  
 192          foreach( $this->sorted_IDs as $plugin_ID )
 193          {
 194              $Plugin = & $this->get_by_ID( $plugin_ID );
 195  
 196              if( empty($Plugin->group) || in_array( $Plugin->group, $result ) )
 197              {
 198                  continue;
 199              }
 200  
 201              $result[] = $Plugin->group;
 202          }
 203  
 204          return $result;
 205      }
 206  
 207  
 208      /**
 209       * Will return an array that contents are references to plugins that have the same group, regardless of the sub_group.
 210       *
 211       * @return array
 212       */
 213  	function get_Plugins_in_group( $group )
 214      {
 215          $result = array();
 216  
 217          foreach( $this->sorted_IDs as $plugin_ID )
 218          {
 219              $Plugin = & $this->get_by_ID( $plugin_ID );
 220              if( $Plugin->group == $group )
 221              {
 222                  $result[] = & $Plugin;
 223              }
 224          }
 225  
 226          return $result;
 227      }
 228  
 229  
 230      /**
 231       * Will return an array that contents are references to plugins that have the same group and sub_group.
 232       *
 233       * @return array
 234       */
 235  	function get_Plugins_in_sub_group( $group, $sub_group = '' )
 236      {
 237          $result = array();
 238  
 239          foreach( $this->sorted_IDs as $plugin_ID )
 240          {
 241              $Plugin = & $this->get_by_ID( $plugin_ID );
 242              if( $Plugin->group == $group && $Plugin->sub_group == $sub_group )
 243              {
 244                  $result[] = & $Plugin;
 245              }
 246          }
 247  
 248          return $result;
 249      }
 250  
 251  
 252      /**
 253       * Sets the status of a Plugin in DB and registers it into the internal indices when "enabled".
 254       * Otherwise it gets unregistered, but only when we're not in {@link Plugins_admin}, because we
 255       * want to keep it in then in our indices.
 256       *
 257       * {@internal
 258       * Note: this should probably always get called on the {@link $Plugins} object,
 259       *       not {@link $admin_Plugins}.
 260       * }}
 261       *
 262       * @param Plugin
 263       * @param string New status ("enabled", "disabled", "needs_config", "broken")
 264       * @return boolean True on success, false on failure.
 265       */
 266  	function set_Plugin_status( & $Plugin, $status )
 267      {
 268          global $DB, $Debuglog;
 269  
 270          if( $Plugin->status == $status
 271              && $DB->query( "SELECT plug_status FROM T_plugins WHERE plug_ID = ".$DB->quote($Plugin->ID), 'set_Plugin_status safety net' ) == $status )
 272          {
 273              return true;
 274          }
 275  
 276          $DB->query( "UPDATE T_plugins SET plug_status = '".$status."' WHERE plug_ID = '".$Plugin->ID."'" );
 277  
 278          if( $status == 'enabled' )
 279          { // Reload plugins tables, which includes the plugin in further requests
 280              $this->loaded_plugins_table = false;
 281              $this->load_plugins_table();
 282              $this->load_events();
 283          }
 284          else
 285          {
 286              // Notify the plugin that it has been disabled:
 287              $Plugin->BeforeDisable();
 288  
 289              $this->unregister( $Plugin );
 290          }
 291  
 292          $Plugin->status = $status;
 293  
 294          $Debuglog->add( 'Set status for plugin #'.$Plugin->ID.' to "'.$status.'"!', 'plugins' );
 295  
 296          return true;
 297      }
 298  
 299  
 300      /**
 301       * Get path of a given Plugin classname.
 302       *
 303       * @param string Classname
 304       * @return string Path
 305       */
 306  	function get_classfile_path($classname)
 307      {
 308          $plugin_filename = '_'.str_replace( '_plugin', '.plugin', $classname ).'.php';
 309          // Try <plug_classname>/<plug_classname>.php (subfolder) first
 310          $classfile_path = $this->plugins_path.$classname.'/'.$plugin_filename;
 311  
 312          if( ! is_readable( $classfile_path ) )
 313          { // Look directly in $plugins_path
 314              $classfile_path = $this->plugins_path.$plugin_filename;
 315          }
 316          return $classfile_path;
 317      }
 318  
 319  
 320      /**
 321       * Register a plugin.
 322       *
 323       * This handles the indexes, dynamically unregisters a Plugin that does not exist (anymore)
 324       * and instantiates the Plugin's (User)Settings.
 325       *
 326       * @access protected
 327       * @param string name of plugin class to instantiate and register
 328       * @param int ID in database (0 if not installed)
 329       * @param int Priority in database (-1 to keep default)
 330       * @param string Path of the .php class file of the plugin.
 331       * @param boolean Must the plugin exist (classfile_path and classname)?
 332       *                This is used internally to be able to unregister a non-existing plugin.
 333       * @return Plugin Plugin ref to newly created plugin; string in case of error
 334       */
 335      function & register( $classname, $ID = 0, $priority = -1, $classfile_path = NULL, $must_exists = true )
 336      {
 337          global $Debuglog, $Messages, $Timer;
 338  
 339          if( $ID && isset($this->index_ID_Plugins[$ID]) )
 340          {
 341              debug_die( 'Tried to register already registered Plugin (ID '.$ID.')' ); // should never happen!
 342          }
 343  
 344          $Timer->resume( 'plugins_register' );
 345  
 346          if( empty($classfile_path) )
 347          {
 348              $classfile_path = $this->get_classfile_path($classname);
 349          }
 350  
 351          $Debuglog->add( 'register(): '.$classname.', ID: '.$ID.', priority: '.$priority.', classfile_path: ['.$classfile_path.']', 'plugins' );
 352  
 353          if( ! is_readable( $classfile_path ) )
 354          { // Plugin file not found!
 355              if( $must_exists )
 356              {
 357                  $r = 'Plugin class file ['.rel_path_to_base($classfile_path).'] is not readable!';
 358                  $Debuglog->add( $r, array( 'plugins', 'error' ) );
 359  
 360                  // Get the Plugin object (must not exist)
 361                  $Plugin = & $this->register( $classname, $ID, $priority, $classfile_path, false );
 362                  $this->plugin_errors[$ID]['register'] = $r;
 363                  $this->set_Plugin_status( $Plugin, 'broken' );
 364  
 365                  // unregister:
 366                  if( $this->unregister( $Plugin ) )
 367                  {
 368                      $Debuglog->add( 'Unregistered plugin ['.$classname.']!', array( 'plugins', 'error' ) );
 369                  }
 370                  else
 371                  {
 372                      $Plugin->name = $Plugin->classname; // use the classname instead of "unnamed plugin"
 373                      $Timer->pause( 'plugins_register' );
 374                      return $Plugin;
 375                  }
 376  
 377                  $Timer->pause( 'plugins_register' );
 378                  return $r;
 379              }
 380          }
 381          elseif( ! class_exists($classname) ) // If there are several copies of one plugin for example..
 382          {
 383              $Debuglog->add( 'Loading plugin class file: '.$classname, 'plugins' );
 384              require_once $classfile_path;
 385          }
 386  
 387          if( ! class_exists( $classname ) )
 388          { // the given class does not exist
 389              if( $must_exists )
 390              {
 391                  $r = sprintf( 'Plugin class for &laquo;%s&raquo; in file &laquo;%s&raquo; not defined.', $classname, rel_path_to_base($classfile_path) );
 392                  $Debuglog->add( $r, array( 'plugins', 'error' ) );
 393  
 394                  // Get the Plugin object (must not exist)    fp> why is this recursive?
 395                  $Plugin = & $this->register( $classname, $ID, $priority, $classfile_path, false );
 396                  $this->plugin_errors[$ID]['register'] = $r;
 397                  $this->set_Plugin_status( $Plugin, 'broken' );
 398  
 399                  // unregister:
 400                  if( $this->unregister( $Plugin ) )
 401                  {
 402                      $Debuglog->add( 'Unregistered plugin ['.$classname.']!', array( 'plugins', 'error' ) );
 403                  }
 404                  else
 405                  {
 406                      $Plugin->name = $Plugin->classname; // use the classname instead of "unnamed plugin"
 407                      $Timer->pause( 'plugins_register' );
 408                      return $Plugin;
 409                  }
 410  
 411                  $Timer->pause( 'plugins_register' );
 412                  return $r;
 413              }
 414              else
 415              {
 416                  $Plugin = new Plugin;    // COPY !
 417                  $Plugin->code = NULL;
 418              }
 419          }
 420          else
 421          {
 422              $Plugin = new $classname;    // COPY !
 423          }
 424  
 425          $Plugin->classfile_path = $classfile_path;
 426  
 427          // Tell him his ID :)
 428          if( $ID == 0 )
 429          {
 430              $Plugin->ID = --$this->smallest_internal_ID;
 431          }
 432          else
 433          {
 434              $Plugin->ID = $ID;
 435  
 436              if( $ID > 0 )
 437              { // Properties from T_plugins
 438                  // Code
 439                  $Plugin->code = $this->index_ID_rows[$Plugin->ID]['plug_code'];
 440                  // Status
 441                  $Plugin->status = $this->index_ID_rows[$Plugin->ID]['plug_status'];
 442              }
 443          }
 444          // Tell him his name :)
 445          $Plugin->classname = $classname;
 446          // Tell him his priority:
 447          if( $priority > -1 ) { $Plugin->priority = $priority; }
 448  
 449          if( empty($Plugin->name) )
 450          {
 451              $Plugin->name = $Plugin->classname;
 452          }
 453  
 454          // Memorizes Plugin in code hash array:
 455          if( ! empty($this->index_code_ID[ $Plugin->code ]) && $this->index_code_ID[ $Plugin->code ] != $Plugin->ID )
 456          { // The plugin's default code is already in use!
 457              $Plugin->code = NULL;
 458          }
 459          else
 460          {
 461              $this->index_code_Plugins[ $Plugin->code ] = & $Plugin;
 462              $this->index_code_ID[ $Plugin->code ] = & $Plugin->ID;
 463          }
 464          $this->index_ID_Plugins[ $Plugin->ID ] = & $Plugin;
 465  
 466          if( ! in_array( $Plugin->ID, $this->sorted_IDs ) ) // TODO: check if this extra check is required..
 467          { // not in our sort index yet
 468              $this->sorted_IDs[] = & $Plugin->ID;
 469          }
 470  
 471          // Init the Plugins (User)Settings members.
 472          $this->init_settings( $Plugin );
 473  
 474          // Stuff only for real/existing Plugins (which exist in DB):
 475          if( $Plugin->ID > 0 )
 476          {
 477              $tmp_params = array( 'db_row' => $this->index_ID_rows[$Plugin->ID], 'is_installed' => true );
 478              if( $Plugin->PluginInit( $tmp_params ) === false && $this->unregister( $Plugin ) )
 479              {
 480                  $Debuglog->add( 'Unregistered plugin, because PluginInit returned false.', 'plugins' );
 481                  $Plugin = '';
 482              }
 483              // Version check:
 484              elseif( $Plugin->version != $this->index_ID_rows[$Plugin->ID]['plug_version'] && $must_exists )
 485              { // Version has changed since installation or last update
 486                  $db_deltas = array();
 487  
 488                  // Tell the Plugin that we've detected a version change:
 489                  $tmp_params = array( 'old_version'=>$this->index_ID_rows[$Plugin->ID]['plug_version'], 'db_row'=>$this->index_ID_rows[$Plugin->ID] );
 490  
 491                  if( $this->call_method( $Plugin->ID, 'PluginVersionChanged', $tmp_params ) === false )
 492                  {
 493                      $Debuglog->add( 'Set plugin status to "needs_config", because PluginVersionChanged returned false.', 'plugins' );
 494                      $this->set_Plugin_status( $Plugin, 'needs_config' );
 495                      if( $this->unregister( $Plugin ) )
 496                      { // only unregister the Plugin, if it's not the admin list's class:
 497                          $Plugin = '';
 498                      }
 499                  }
 500                  else
 501                  {
 502                      // Check if there are DB deltas required (also when downgrading!), without excluding any query type:
 503                      load_funcs('_core/model/db/_upgrade.funcs.php');
 504                      $db_deltas = db_delta( $Plugin->GetDbLayout() );
 505  
 506                      if( empty($db_deltas) )
 507                      { // No DB changes needed, update (bump or decrease) the version
 508                          global $DB;
 509                          $Plugins_admin = & get_Plugins_admin();
 510  
 511                          // Update version in DB:
 512                          $DB->query( '
 513                                  UPDATE T_plugins
 514                                     SET plug_version = '.$DB->quote($Plugin->version).'
 515                                   WHERE plug_ID = '.$Plugin->ID );
 516  
 517                          // Update "plug_version" in indexes:
 518                          $this->index_ID_rows[$Plugin->ID]['plug_version'] = $Plugin->version;
 519                          if( isset($Plugins_admin->index_ID_rows[$Plugin->ID]) )
 520                          {
 521                              $Plugins_admin->index_ID_rows[$Plugin->ID]['plug_version'] = $Plugin->version;
 522                          }
 523  
 524                          // Remove any prerenderered content for the Plugins renderer code:
 525                          if( ! empty($Plugin->code) )
 526                          {
 527                              $DB->query( '
 528                                      DELETE FROM T_items__prerendering
 529                                       WHERE itpr_renderers REGEXP "^(.*\.)?'.$DB->escape($Plugin->code).'(\..*)?$"' );
 530                          }
 531  
 532                          // Detect new events (and delete obsolete ones - in case of downgrade):
 533                          if( $Plugins_admin->save_events( $Plugin, array() ) )
 534                          {
 535                              $this->load_events(); // re-load for the current request
 536                          }
 537  
 538                          $Debuglog->add( 'Version for '.$Plugin->classname.' changed from '.$this->index_ID_rows[$Plugin->ID]['plug_version'].' to '.$Plugin->version, 'plugins' );
 539                      }
 540                      else
 541                      { // If there are DB schema changes needed, set the Plugin status to "needs_config"
 542  
 543                          // TODO: automatic upgrade in some cases (e.g. according to query types)?
 544  
 545                          $this->set_Plugin_status( $Plugin, 'needs_config' );
 546                          $Debuglog->add( 'Set plugin status to "needs_config", because version DB schema needs upgrade.', 'plugins' );
 547  
 548                          if( $this->unregister( $Plugin ) )
 549                          { // only unregister the Plugin, if it's not the admin list's class:
 550                              $Plugin = '';
 551                          }
 552                      }
 553                  }
 554              }
 555  
 556              if( $Plugin && isset($this->index_ID_rows[$Plugin->ID]) ) // may have been unregistered above
 557              {
 558                  if( $this->index_ID_rows[$Plugin->ID]['plug_name'] !== NULL )
 559                  {
 560                      $Plugin->name = $this->index_ID_rows[$Plugin->ID]['plug_name'];
 561                  }
 562                  if( $this->index_ID_rows[$Plugin->ID]['plug_shortdesc'] !== NULL )
 563                  {
 564                      $Plugin->short_desc = $this->index_ID_rows[$Plugin->ID]['plug_shortdesc'];
 565                  }
 566              }
 567          }
 568          else
 569          { // This gets called for non-installed Plugins:
 570              // We're not instantiating UserSettings/Settings, since that needs a DB backend.
 571  
 572              $tmp_params = array( 'db_row' => array(), 'is_installed' => false );
 573              if( $Plugin->PluginInit( $tmp_params ) === false && $this->unregister( $Plugin ) )
 574              {
 575                  $Debuglog->add( 'Unregistered plugin, because PluginInit returned false.', 'plugins' );
 576                  $Plugin = '';
 577              }
 578          }
 579  
 580          $Timer->pause( 'plugins_register' );
 581  
 582          return $Plugin;
 583      }
 584  
 585  
 586      /**
 587       * Un-register a plugin.
 588       *
 589       * This does not un-install it from DB, just from the internal indexes.
 590       *
 591       * @param Plugin
 592       * @param boolean Force unregistering (ignored here, but used in Plugins_admin)
 593       * @return boolean True, if unregistered
 594       */
 595  	function unregister( & $Plugin, $force = false )
 596      {
 597          global $Debuglog;
 598  
 599          $this->forget_events( $Plugin->ID );
 600  
 601          // TODO: Check if it is necessarry to unset from the $index_apply_rendering_codes and unset if it must be removed
 602          unset( $this->index_code_Plugins[ $Plugin->code ] );
 603          unset( $this->index_ID_Plugins[ $Plugin->ID ] );
 604  
 605          if( isset($this->index_ID_rows[ $Plugin->ID ]) )
 606          { // It has an associated DB row (load_plugins_table() was called)
 607              unset($this->index_ID_rows[ $Plugin->ID ]);
 608          }
 609  
 610          $sort_key = array_search( $Plugin->ID, $this->sorted_IDs );
 611          if( $sort_key === false )
 612          { // this may happen if a Plugin has unregistered itself
 613              $Debuglog->add( 'Tried to unregister not-installed plugin (not in $sorted_IDs)!', 'plugins' );
 614              return false;
 615          }
 616          unset( $this->sorted_IDs[$sort_key] );
 617          $this->sorted_IDs = array_values( $this->sorted_IDs );
 618  
 619          if( $this->current_idx >= $sort_key )
 620          { // We have removed a file before or at the $sort_key'th position
 621              $this->current_idx--;
 622          }
 623  
 624          return true;
 625      }
 626  
 627  
 628      /**
 629       * Forget the events a Plugin has registered.
 630       *
 631       * This gets used when {@link unregister() unregistering} a Plugin or if
 632       * {@link Plugin::PluginInit()} returned false, which means
 633       * "do not use it for subsequent events in the request".
 634       *
 635       * @param integer Plugin ID
 636       */
 637  	function forget_events( $plugin_ID )
 638      {
 639          // Forget events:
 640          foreach( array_keys($this->index_event_IDs) as $l_event )
 641          {
 642              while( ($key = array_search( $plugin_ID, $this->index_event_IDs[$l_event] )) !== false )
 643              {
 644                  unset( $this->index_event_IDs[$l_event][$key] );
 645              }
 646          }
 647      }
 648  
 649  
 650      /**
 651       * Init {@link Plugin::$Settings} and {@link Plugin::$UserSettings}, either by
 652       * unsetting them for PHP5's overloading or instantiating them for PHP4.
 653       *
 654       * This only gets used for installed plugins, but we're initing it for PHP 5.1,
 655       * to trigger a helpful error, when e.g. Plugin::Settings gets accessed in PluginInit().
 656       *
 657       * @param Plugin
 658       */
 659  	function init_settings( & $Plugin )
 660      {
 661          if( version_compare( PHP_VERSION, '5.1', '>=' ) )
 662          { // we use overloading for PHP5, therefor the member has to be unset:
 663              // Note: this is somehow buggy at least in PHP 5.0.5, therefor we use it from 5.1 on.
 664              //       see http://forums.b2evolution.net/viewtopic.php?p=49031#49031
 665              unset( $Plugin->Settings );
 666              unset( $Plugin->UserSettings );
 667  
 668              // Nothing to do here, will get called through Plugin::__get() when accessed
 669              return;
 670          }
 671  
 672          // PHP < 5.1: instantiate now, but only for installed plugins (needs DB).
 673          if( $Plugin->ID > 0 )
 674          {
 675              $this->instantiate_Settings( $Plugin, 'Settings' );
 676              $this->instantiate_Settings( $Plugin, 'UserSettings' );
 677          }
 678      }
 679  
 680  
 681      /**
 682       * Instantiate Settings object (class {@link PluginSettings}) for the given plugin.
 683       *
 684       * The plugin must provide setting definitions (through {@link Plugin::GetDefaultSettings()}
 685       * OR {@link Plugin::GetDefaultUserSettings()}).
 686       *
 687       * @todo dh> there should be only one instance of Plugin(User)Settings, which would allow to
 688       *           query the DB at once.
 689       *           Defaults would need to be handled by Plugin(User)Settings::get_default() then.
 690       *
 691       * @param Plugin
 692       * @param string settings type: "Settings" or "UserSettings"
 693       * @return boolean NULL, if no Settings
 694       */
 695  	function instantiate_Settings( & $Plugin, $set_type )
 696      {
 697          global $Debuglog, $Timer;
 698  
 699          $Timer->resume( 'plugins_inst_'.$set_type );
 700  
 701          // Call Plugin::GetDefaultSettings() or Plugin::GetDefaultUserSettings():
 702          // This does not use call_method, since the plugin may not be accessible through get_by_ID().
 703          // This may happen, if Plugin(User)Settings get accessed in PluginInit (which is allowed
 704          // when is_installed=true).
 705          $method = 'GetDefault'.$set_type;
 706          $params = array('for_editing'=>false);
 707          $Timer->resume( $Plugin->classname.'_(#'.$Plugin->ID.')' );
 708          $defaults = $Plugin->$method( $params );
 709          $Timer->pause( $Plugin->classname.'_(#'.$Plugin->ID.')' );
 710  
 711          if( empty($defaults) )
 712          {    // No settings, no need to instantiate.
 713              $Timer->pause( 'plugins_inst_'.$set_type );
 714              return NULL;
 715          }
 716  
 717          if( ! is_array($defaults) )
 718          {    // invalid data
 719              $Debuglog->add( $Plugin->classname.'::GetDefault'.$set_type.'() did not return array!', array('plugins', 'error') );
 720              return NULL; // fp> correct me if I'm wrong.
 721          }
 722  
 723          if( $set_type == 'UserSettings' )
 724          {    // User specific settings:
 725              load_class( 'plugins/model/_pluginusersettings.class.php', 'PluginUserSettings' );
 726  
 727              $Plugin->UserSettings = new PluginUserSettings( $Plugin->ID );
 728  
 729              $set_Obj = & $Plugin->UserSettings;
 730          }
 731          else
 732          {    // Global settings:
 733              load_class( 'plugins/model/_pluginsettings.class.php', 'PluginSettings' );
 734  
 735              $Plugin->Settings = new PluginSettings( $Plugin->ID );
 736  
 737              $set_Obj = & $Plugin->Settings;
 738          }
 739  
 740          // Register default values:
 741          foreach( $defaults as $l_name => $l_meta )
 742          {
 743              if( isset($l_meta['layout']) )
 744              { // Skip non-value entries
 745                  continue;
 746              }
 747  
 748              // Register settings as _defaults into Settings:
 749              if( isset($l_meta['defaultvalue']) )
 750              {
 751                  $set_Obj->_defaults[$l_name] = $l_meta['defaultvalue'];
 752              }
 753              elseif( isset( $l_meta['type'] ) && $l_meta['type'] == 'array' )
 754              {
 755                  $set_Obj->_defaults[$l_name] = array();
 756              }
 757              else
 758              {
 759                  $set_Obj->_defaults[$l_name] = '';
 760              }
 761          }
 762  
 763          $Timer->pause( 'plugins_inst_'.$set_type );
 764  
 765          return true;
 766      }
 767  
 768  
 769      /**
 770       * Load plugins table and rewind iterator used by {@link get_next()}.
 771       */
 772  	function restart()
 773      {
 774          $this->load_plugins_table();
 775  
 776          $this->current_idx = -1;
 777      }
 778  
 779  
 780      /**
 781       * Get next plugin in the list.
 782       *
 783       * NOTE: You'll have to call {@link restart()} or {@link load_plugins_table()}
 784       * before using it.
 785       *
 786       * @return Plugin (false if no more plugin).
 787       */
 788      function & get_next()
 789      {
 790          global $Debuglog;
 791  
 792          ++$this->current_idx;
 793  
 794          $Debuglog->add( 'get_next() ('.$this->current_idx.')..', 'plugins' );
 795  
 796          if( isset($this->sorted_IDs[$this->current_idx]) )
 797          {
 798              $Plugin = & $this->get_by_ID( $this->sorted_IDs[$this->current_idx] );
 799  
 800              if( ! $Plugin )
 801              { // recurse until we've been through whole $sorted_IDs!
 802                  return $this->get_next();
 803              }
 804  
 805              $Debuglog->add( 'return: '.$Plugin->classname.' ('.$Plugin->ID.')', 'plugins' );
 806              return $Plugin;
 807          }
 808          else
 809          {
 810              $Debuglog->add( 'return: false', 'plugins' );
 811              --$this->current_idx;
 812              $r = false;
 813              return $r;
 814          }
 815      }
 816  
 817  
 818      /**
 819       * Stop propagation of events to next plugins in {@link trigger_event()}.
 820       */
 821  	function stop_propagation()
 822      {
 823          $this->_stop_propagation = true;
 824      }
 825  
 826  
 827      /**
 828       * Call all plugins for a given event.
 829       *
 830       * @param string event name, see {@link Plugins_admin::get_supported_events()}
 831       * @param array Associative array of parameters for the Plugin
 832       * @return boolean True, if at least one plugin has been called.
 833       */
 834  	function trigger_event( $event, $params = array() )
 835      {
 836          global $Debuglog;
 837  
 838          $Debuglog->add( 'Trigger event '.$event, 'plugins' );
 839  
 840          if( empty($this->index_event_IDs[$event]) )
 841          { // No events registered
 842              $Debuglog->add( 'No registered plugins.', 'plugins' );
 843              return false;
 844          }
 845  
 846          $Debuglog->add( 'Registered plugin IDs: '.implode( ', ', $this->index_event_IDs[$event]), 'plugins' );
 847  
 848          foreach( $this->index_event_IDs[$event] as $l_plugin_ID )
 849          {
 850              $this->call_method( $l_plugin_ID, $event, $params );
 851  
 852              if( ! empty($this->_stop_propagation) )
 853              {    // A plugin has requested to stop propagation.
 854                  $this->_stop_propagation = false;
 855                  break;
 856              }
 857          }
 858          return true;
 859      }
 860  
 861  
 862      /**
 863       * Call all plugins for a given event, until the first one returns true.
 864       *
 865       * @param string event name, see {@link Plugins_admin::get_supported_events()}
 866       * @param array Associative array of parameters for the Plugin
 867       * @return array The (modified) params array with key "plugin_ID" set to the last called plugin;
 868       *               Empty array if no Plugin returned true or no Plugin has this event registered.
 869       */
 870  	function trigger_event_first_true( $event, $params = NULL )
 871      {
 872          global $Debuglog;
 873  
 874          $Debuglog->add( 'Trigger event '.$event.' (first true)', 'plugins' );
 875  
 876          if( empty($this->index_event_IDs[$event]) )
 877          { // No events registered
 878              $Debuglog->add( 'No registered plugins.', 'plugins' );
 879              return array();
 880          }
 881  
 882          $Debuglog->add( 'Registered plugin IDs: '.implode( ', ', $this->index_event_IDs[$event]), 'plugins' );
 883          foreach( $this->index_event_IDs[$event] as $l_plugin_ID )
 884          {
 885              $r = $this->call_method( $l_plugin_ID, $event, $params );
 886              if( $r === true )
 887              {
 888                  $Debuglog->add( 'Plugin ID '.$l_plugin_ID.' returned true!', 'plugins' );
 889                  $params['plugin_ID'] = & $l_plugin_ID;
 890                  return $params;
 891              }
 892          }
 893          return array();
 894      }
 895  
 896  
 897      /**
 898       * Call all plugins for a given event, until the first one returns false.
 899       *
 900       * @param string event name, see {@link Plugins_admin::get_supported_events()}
 901       * @param array Associative array of parameters for the Plugin
 902       * @return array The (modified) params array with key "plugin_ID" set to the last called plugin;
 903       *               Empty array if no Plugin returned true or no Plugin has this event registered.
 904       */
 905  	function trigger_event_first_false( $event, $params = NULL )
 906      {
 907          global $Debuglog;
 908  
 909          $Debuglog->add( 'Trigger event '.$event.' (first false)', 'plugins' );
 910  
 911          if( empty($this->index_event_IDs[$event]) )
 912          { // No events registered
 913              $Debuglog->add( 'No registered plugins.', 'plugins' );
 914              return array();
 915          }
 916  
 917          $Debuglog->add( 'Registered plugin IDs: '.implode( ', ', $this->index_event_IDs[$event]), 'plugins' );
 918          foreach( $this->index_event_IDs[$event] as $l_plugin_ID )
 919          {
 920              $r = $this->call_method( $l_plugin_ID, $event, $params );
 921              if( $r === false )
 922              {
 923                  $Debuglog->add( 'Plugin ID '.$l_plugin_ID.' returned false!', 'plugins' );
 924                  $params['plugin_ID'] = & $l_plugin_ID;
 925                  return $params;
 926              }
 927          }
 928          return array();
 929      }
 930  
 931  
 932      /**
 933       * Call all plugins for a given event, until the first one returns a value
 934       * (not NULL) (and $search is fulfilled, if given).
 935       *
 936       * @param string event name, see {@link Plugins_admin::get_supported_events()}
 937       * @param array|NULL Associative array of parameters for the Plugin
 938       * @param array|NULL If provided, the return value gets checks against this criteria.
 939       *        Can be:
 940       *         - ( 'in_array' => 'needle' )
 941       * @return array The (modified) params array with key "plugin_ID" set to the last called plugin
 942       *               and 'plugin_return' set to the return value;
 943       *               Empty array if no Plugin returned true or no Plugin has this event registered.
 944       */
 945  	function trigger_event_first_return( $event, $params = NULL, $search = NULL )
 946      {
 947          global $Debuglog;
 948  
 949          $Debuglog->add( 'Trigger event '.$event.' (first return)', 'plugins' );
 950  
 951          if( empty($this->index_event_IDs[$event]) )
 952          { // No events registered
 953              $Debuglog->add( 'No registered plugins.', 'plugins' );
 954              return array();
 955          }
 956  
 957          $Debuglog->add( 'Registered plugin IDs: '.implode( ', ', $this->index_event_IDs[$event]), 'plugins' );
 958          foreach( $this->index_event_IDs[$event] as $l_plugin_ID )
 959          {
 960              $r = $this->call_method( $l_plugin_ID, $event, $params );
 961              if( isset($r) )
 962              {
 963                  if( isset($search) )
 964                  { // Apply $search:
 965                      foreach( $search as $k => $v )
 966                      { // Check search criterias and continue if it does not match:
 967                          switch( $k )
 968                          {
 969                              case 'in_array':
 970                                  if( ! in_array( $v, $r ) )
 971                                  {
 972                                      continue 3; // continue in main foreach loop
 973                                  }
 974                                  break;
 975                              default:
 976                                  debug_die('Invalid search criteria in Plugins::trigger_event_first_return / '.$k);
 977                          }
 978                      }
 979                  }
 980                  $Debuglog->add( 'Plugin ID '.$l_plugin_ID.' returned '.( $r ? 'true' : 'false' ).'!', 'plugins' );
 981                  $params['plugin_return'] = $r;
 982                  $params['plugin_ID'] = & $l_plugin_ID;
 983                  return $params;
 984              }
 985          }
 986          return array();
 987      }
 988  
 989  
 990      /**
 991       * Trigger an event and return an index of params.
 992       *
 993       * This is handy to collect return values from all plugins hooking an event.
 994       *
 995       * @param string Event name, see {@link Plugins_admin::get_supported_events()}
 996       * @param array Associative array of parameters for the Plugin
 997       * @param string Index of $params that should get returned
 998       * @return mixed The requested index of $params
 999       */
1000  	function get_trigger_event( $event, $params = NULL, $get = 'data' )
1001      {
1002          $params[$get] = & $params[$get]; // make it a reference, so it can get changed
1003  
1004          $this->trigger_event( $event, $params );
1005  
1006          return $params[$get];
1007      }
1008  
1009  
1010      /**
1011       * The same as {@link get_trigger_event()}, but stop when the first Plugin returns true.
1012       *
1013       * @param string Event name, see {@link Plugins_admin::get_supported_events()}
1014       * @param array Associative array of parameters for the Plugin
1015       * @param string Index of $params that should get returned
1016       * @return mixed The requested index of $params
1017       */
1018  	function get_trigger_event_first_true( $event, $params = NULL, $get = 'data' )
1019      {
1020          $params[$get] = & $params[$get]; // make it a reference, so it can get changed
1021  
1022          $this->trigger_event_first_true( $event, $params );
1023  
1024          return $params[$get];
1025      }
1026  
1027  
1028      /**
1029       * Trigger an event and return the first return value of a plugin.
1030       *
1031       * @param string Event name, see {@link Plugins_admin::get_supported_events()}
1032       * @param array Associative array of parameters for the Plugin
1033       * @return mixed NULL if no Plugin returned something or the return value of the first Plugin
1034       */
1035  	function get_trigger_event_first_return( $event, $params = NULL )
1036      {
1037          $r = $this->trigger_event_first_return( $event, $params );
1038  
1039          if( ! isset($r['plugin_return']) )
1040          {
1041              return NULL;
1042          }
1043  
1044          return $r['plugin_return'];
1045      }
1046  
1047  
1048      /**
1049       * Trigger an event and return an array of all return values of the
1050       * relevant plugins.
1051       *
1052       * @param string Event name, see {@link Plugins_admin::get_supported_events()}
1053       * @param array Associative array of parameters for the Plugin
1054       * @param boolean Ignore {@link empty() empty} return values?
1055       * @return array List of return values, indexed by Plugin ID
1056       */
1057  	function trigger_collect( $event, $params = NULL, $ignore_empty = true )
1058      {
1059          if( empty($this->index_event_IDs[$event]) )
1060          {
1061              return array();
1062          }
1063  
1064          $r = array();
1065          foreach( $this->index_event_IDs[$event] as $p_ID )
1066          {
1067              $sub_r = $this->call_method_if_active( $p_ID, $event, $params );
1068  
1069              if( $ignore_empty && empty($sub_r) )
1070              {
1071                  continue;
1072              }
1073  
1074              $r[$p_ID] = $sub_r;
1075          }
1076  
1077          return $r;
1078      }
1079  
1080  
1081      /**
1082       * Trigger a karma collecting event in order to get Karma percentage.
1083       *
1084       * @param string Event
1085       * @param array Params to the event
1086       * @return integer|NULL Spam Karma (-100 - 100); "100" means "absolutely spam"; NULL if no plugin gave us a karma value
1087       */
1088  	function trigger_karma_collect( $event, $params )
1089      {
1090          global $Debuglog;
1091  
1092          $karma_abs = NULL;
1093          $karma_divider = 0; // total of the "spam detection relevance weight"
1094  
1095          $Debuglog->add( 'Trigger karma collect event '.$event, 'plugins' );
1096  
1097          if( empty($this->index_event_IDs[$event]) )
1098          { // No events registered
1099              $Debuglog->add( 'No registered plugins.', 'plugins' );
1100              return NULL;
1101          }
1102  
1103          $this->load_plugins_table(); // We need index_ID_rows below
1104  
1105          $Debuglog->add( 'Registered plugin IDs: '.implode( ', ', $this->index_event_IDs[$event]), 'plugins' );
1106  
1107          $count_plugins = 0;
1108          foreach( $this->index_event_IDs[$event] as $l_plugin_ID )
1109          {
1110              $plugin_weight = $this->index_ID_rows[$l_plugin_ID]['plug_spam_weight'];
1111  
1112              if( $plugin_weight < 1 )
1113              {
1114                  $Debuglog->add( 'Skipping plugin #'.$l_plugin_ID.', because is has weight '.$plugin_weight.'.', 'plugins' );
1115                  continue;
1116              }
1117  
1118              $params['cur_karma'] = ( $karma_divider ? round($karma_abs / $karma_divider) : NULL );
1119              $params['cur_karma_abs'] = $karma_abs;
1120              $params['cur_karma_divider'] = $karma_divider;
1121              $params['cur_count_plugins'] = $count_plugins;
1122  
1123              // Call the plugin:
1124              $plugin_karma = $this->call_method( $l_plugin_ID, $event, $params );
1125  
1126              if( ! is_numeric( $plugin_karma ) )
1127              {
1128                  continue;
1129              }
1130  
1131              $count_plugins++;
1132  
1133              if( $plugin_karma > 100 )
1134              {
1135                  $plugin_karma = 100;
1136              }
1137              elseif( $plugin_karma < -100 )
1138              {
1139                  $plugin_karma = -100;
1140              }
1141  
1142              $karma_abs += ( $plugin_karma * $plugin_weight );
1143              $karma_divider += $plugin_weight;
1144  
1145              if( ! empty($this->_stop_propagation) )
1146              {
1147                  $this->_stop_propagation = false;
1148                  break;
1149              }
1150          }
1151  
1152          if( ! $karma_divider )
1153          {
1154              return NULL;
1155          }
1156  
1157          $karma = round($karma_abs / $karma_divider);
1158  
1159          if( $karma > 100 )
1160          {
1161              $karma = 100;
1162          }
1163          elseif( $karma < -100 )
1164          {
1165              $karma = -100;
1166          }
1167  
1168          return $karma;
1169      }
1170  
1171  
1172      /**
1173       * Call a method on a Plugin.
1174       *
1175       * This makes sure that the Timer for the Plugin gets resumed.
1176       *
1177       * @param integer Plugin ID
1178       * @param string Method name.
1179       * @param array Params (by reference).
1180       * @return NULL|mixed Return value of the plugin's method call or NULL if no such method.
1181       */
1182  	function call_method( $plugin_ID, $method, & $params )
1183      {
1184          global $Timer, $debug, $Debuglog;
1185  
1186          $Plugin = & $this->get_by_ID( $plugin_ID );
1187  
1188          if( ! method_exists( $Plugin, $method ) )
1189          {
1190              return NULL;
1191          }
1192  
1193          if( $debug )
1194          {
1195              /*
1196              // Note: this is commented out, because $debug_params gets not dumped anymore (last line of this block)
1197              // Hide passwords from Debuglog!
1198              // Clone/copy (references!):
1199              $debug_params = array();
1200              foreach( $params as $k => $v )
1201              {
1202                  $debug_params[$k] = $v;
1203              }
1204              if( isset($debug_params['pass']) )
1205              {
1206                  $debug_params['pass'] = '-hidden-';
1207              }
1208              if( isset($debug_params['pass_md5']) )
1209              {
1210                  $debug_params['pass_md5'] = '-hidden-';
1211              }
1212              $Debuglog->add( 'Calling '.$Plugin->classname.'(#'.$Plugin->ID.')->'.$method.'( '.htmlspecialchars(var_export( $debug_params, true )).' )', 'plugins' );
1213              */
1214              $Debuglog->add( 'Calling '.$Plugin->classname.'(#'.$Plugin->ID.')->'.$method.'( )', 'plugins' );
1215          }
1216  
1217          $Timer->resume( $Plugin->classname.'_(#'.$Plugin->ID.')' );
1218          $r = $Plugin->$method( $params );
1219          $Timer->pause( $Plugin->classname.'_(#'.$Plugin->ID.')' );
1220  
1221          return $r;
1222      }
1223  
1224  
1225      /**
1226       * Call a method on a Plugin if it is not deactivated.
1227       *
1228       * This is a wrapper around {@link call_method()}.
1229       *
1230       * fp> why doesn't call_method always check if it's deactivated?
1231       *
1232       * @param integer Plugin ID
1233       * @param string Method name.
1234       * @param array Params (by reference).
1235       * @return NULL|mixed Return value of the plugin's method call or NULL if no such method (or inactive).
1236       */
1237  	function call_method_if_active( $plugin_ID, $method, & $params )
1238      {
1239          if( ! $this->has_event($plugin_ID, $method) )
1240          {
1241              return NULL;
1242          }
1243  
1244          return $this->call_method( $plugin_ID, $method, $params );
1245      }
1246  
1247  
1248      /**
1249       * Call a specific plugin by its code.
1250       *
1251       * This will call the {@link Plugin::SkinTag()} event handler.
1252       *
1253       * @param string plugin code
1254       * @param array Associative array of parameters (gets passed to the plugin)
1255       * @return boolean
1256       */
1257  	function call_by_code( $code, $params = array() )
1258      {
1259          $Plugin = & $this->get_by_code( $code );
1260  
1261          if( ! $Plugin )
1262          {
1263              global $Debuglog;
1264              $Debuglog->add( 'No plugin available for code ['.$code.']!', array('plugins', 'error') );
1265              return false;
1266          }
1267  
1268          $this->call_method_if_active( $Plugin->ID, 'SkinTag', $params );
1269  
1270          return true;
1271      }
1272  
1273  
1274      /**
1275       * Render the content of an item by calling the relevant renderer plugins.
1276       *
1277       * @param string content to render (by reference)
1278       * @param array renderer codes to use for opt-out, opt-in and lazy
1279       * @param string Output format, see {@link format_to_output()}. Only 'htmlbody',
1280       *        'entityencoded', 'xml', 'htmlfeed' and 'text' are supported.
1281       * @param array Additional params to the Render* methods (e.g. "Item" for items).
1282       *              Do not use "data" or "format" here, because it gets used internally.
1283       * @return string rendered content
1284       */
1285  	function render( & $content, $renderers, $format, $params, $event_prefix = 'Render' )
1286      {
1287          // echo implode(',',$renderers);
1288  
1289          $params['data'] = & $content;
1290          $params['format'] = $format;
1291  
1292          if( in_array( $format, array('htmlbody','htmlfeed','entityencoded') ) )
1293          {
1294              $event = $event_prefix.'ItemAsHtml'; // 'RenderItemAsHtml'/'DisplayItemAsHtml'
1295          }
1296          elseif( $format == 'xml' )
1297          {
1298              $event = $event_prefix.'ItemAsXml'; // 'RenderItemAsXml'/'DisplayItemAsXml'
1299          }
1300          elseif( $format == 'text' )
1301          {
1302              $event = $event_prefix.'ItemAsText'; // 'RenderItemAsText'/'DisplayItemAsText'
1303          }
1304          else debug_die( 'Unexpected format in Plugins::render(): '.var_export($format, true) );
1305  
1306          $renderer_Plugins = $this->get_list_by_event( $event );
1307          // Set blog from where the "apply_rendering" collection setting should be get
1308          if( isset( $params['Item'] ) && !empty( $params['Item'] ) )
1309          { // the Item is set, get Item Blog
1310              $Item = & $params['Item'];
1311              $setting_Blog = & $Item->get_Blog();
1312          }
1313          else
1314          { // use global Blog if it is set
1315              global $Blog;
1316              if( !empty( $Blog ) )
1317              {
1318                  $setting_Blog = $Blog;
1319              }
1320              else
1321              {    // Try to get Blog by global blog ID
1322                  global $blog;
1323                  if( !empty( $blog ) )
1324                  {
1325                      $BlogCache = & get_BlogCache();
1326                      $setting_Blog = & $BlogCache->get_by_ID( $blog );
1327                  }
1328                  else
1329                  {
1330                      $setting_Blog = NULL;
1331                  }
1332              }
1333          }
1334  
1335          if( empty( $setting_Blog ) )
1336          { // This should be impossible, but make sure $setting_Blog is set
1337              global $Settings;
1338              $default_blog = $Settings->get('default_blog_ID');
1339              $BlogCache = & get_BlogCache();
1340              $setting_Blog = $BlogCache->get_by_ID( $default_blog );
1341          }
1342  
1343          foreach( $renderer_Plugins as $loop_RendererPlugin )
1344          { // Go through whole list of renders
1345              // echo ' ',$loop_RendererPlugin->code, ':';
1346  
1347              $apply_rendering_value = $loop_RendererPlugin->get_coll_setting( 'coll_apply_rendering', $setting_Blog );
1348              switch( $apply_rendering_value )
1349              {
1350                  case 'stealth':
1351                  case 'always':
1352                      // echo 'FORCED ';
1353                      $this->call_method( $loop_RendererPlugin->ID, $event, $params );
1354                      break;
1355  
1356                  case 'opt-out':
1357                  case 'opt-in':
1358                  case 'lazy':
1359                      if( in_array( $loop_RendererPlugin->code, $renderers ) )
1360                      { // Option is activated
1361                          // echo 'OPT ';
1362                          $this->call_method( $loop_RendererPlugin->ID, $event, $params );
1363                      }
1364                      // else echo 'NOOPT ';
1365                      break;
1366  
1367                  case 'never':
1368                      // echo 'NEVER ';
1369                      break;    // STOP, don't render, go to next renderer
1370              }
1371          }
1372  
1373          return $content;
1374      }
1375  
1376  
1377      /**
1378       * Quick-render a string with a single plugin and format it for output.
1379       *
1380       * @todo rename
1381       *
1382       * @param string Plugin code (must have render() method)
1383       * @param array
1384       *   'data': Data to render
1385       *   'format: format to output, see {@link format_to_output()}
1386       * @return string Rendered string
1387       */
1388  	function quick( $plugin_code, $params )
1389      {
1390          global $Debuglog;
1391  
1392          if( !is_array($params) )
1393          {
1394              $params = array( 'format' => 'htmlbody', 'data' => $params );
1395          }
1396          else
1397          {
1398              $params = $params; // copy
1399          }
1400  
1401          $Plugin = & $this->get_by_code( $plugin_code );
1402          if( $Plugin )
1403          {
1404              // Get the most appropriate handler:
1405              $events = $this->get_enabled_events( $Plugin->ID );
1406              $event = false;
1407              if( $params['format'] == 'htmlbody' || $params['format'] == 'htmlentityencoded' )
1408              {
1409                  if( in_array( 'RenderItemAsHtml', $events ) )
1410                  {
1411                      $event = 'RenderItemAsHtml';
1412                  }
1413              }
1414              elseif( $params['format'] == 'xml' )
1415              {
1416                  if( in_array( 'RenderItemAsXml', $events ) )
1417                  {
1418                      $event = 'RenderItemAsXml';
1419                  }
1420              }
1421  
1422              if( $event )
1423              {
1424                  $this->call_method( $Plugin->ID, $event, $params );
1425              }
1426              else
1427              {
1428                  $Debuglog->add( $Plugin->classname.'(ID '.$Plugin->ID.'): failed to quick-render (tried method '.$event.')!', array( 'plugins', 'error' ) );
1429              }
1430              return format_to_output( $params['data'], $params['format'] );
1431          }
1432          else
1433          {
1434              $Debuglog->add( 'Plugins::quick() - failed to instantiate Plugin by code ['.$plugin_code.']!', array( 'plugins', 'error' ) );
1435              return format_to_output( $params['data'], $params['format'] );
1436          }
1437      }
1438  
1439  
1440      /**
1441       * Load Plugins data from T_plugins (only once), ordered by priority.
1442       *
1443       * This fills the needed indexes to lazy-instantiate a Plugin when requested.
1444       */
1445  	function load_plugins_table()
1446      {
1447          if( $this->loaded_plugins_table )
1448          {
1449              return;
1450          }
1451          global $Debuglog, $DB;
1452  
1453          $Debuglog->add( 'Loading plugins table data.', 'plugins' );
1454  
1455          $this->index_ID_rows = array();
1456          $this->index_code_ID = array();
1457          $this->sorted_IDs = array();
1458  
1459          foreach( $DB->get_results( $this->sql_load_plugins_table, ARRAY_A ) as $row )
1460          { // Loop through installed plugins:
1461              $this->index_ID_rows[$row['plug_ID']] = $row; // remember the rows to instantiate the Plugin on request
1462              if( ! empty( $row['plug_code'] ) )
1463              {
1464                  $this->index_code_ID[$row['plug_code']] = $row['plug_ID'];
1465              }
1466  
1467              $this->sorted_IDs[] = $row['plug_ID'];
1468          }
1469  
1470          $this->loaded_plugins_table = true;
1471      }
1472  
1473  
1474      /**
1475       * Load rendering Plugins specific apply_rendering setting for the given Blog and setting_name
1476       *
1477       * @param String setting name ( 'coll_apply_rendering', 'coll_apply_comment_rendering' )
1478       * @param Object the Blog which apply rendering setting should be loaded
1479       */
1480  	function load_index_apply_rendering( $setting_name, & $Blog )
1481      {
1482          if( isset( $this->index_apply_rendering_codes[$Blog->ID][$setting_name] ) )
1483          { // This data is already loaded
1484              return;
1485          }
1486  
1487          // make sure plugins are loaded
1488          $this->load_plugins_table();
1489  
1490          $index = & $this->sorted_IDs;
1491          foreach( $index as $plugin_ID )
1492          { // iterate through each plugin
1493              $Plugin = $this->get_by_ID( $plugin_ID );
1494              if( $Plugin->group != 'rendering' )
1495              { // This plugin doesn't belong to the rendering plugins group
1496                  continue;
1497              }
1498              // get and set the specific plugin collection setting
1499              $rendering_value = $Plugin->get_coll_setting( $setting_name, $Blog );
1500              $this->index_apply_rendering_codes[$Blog->ID][$setting_name][$rendering_value][] = $Plugin->code;
1501          }
1502      }
1503  
1504  
1505      /**
1506       * Get a specific plugin by its ID.
1507       *
1508       * This is the workhorse when it comes to lazy-instantiating a Plugin.
1509       *
1510       * @param integer plugin ID
1511       * @return Plugin (false in case of error)
1512       */
1513      function & get_by_ID( $plugin_ID )
1514      {
1515          global $Debuglog;
1516  
1517          if( ! isset($this->index_ID_Plugins[ $plugin_ID ]) )
1518          { // Plugin is not instantiated yet
1519              $Debuglog->add( 'get_by_ID(): Instantiate Plugin (ID '.$plugin_ID.').', 'plugins' );
1520  
1521              $this->load_plugins_table();
1522  
1523              #pre_dump( 'get_by_ID(), index_ID_rows', $this->index_ID_rows );
1524  
1525              if( ! isset( $this->index_ID_rows[$plugin_ID] ) || ! $this->index_ID_rows[$plugin_ID] )
1526              { // no plugin rows cached
1527                  #debug_die( 'Cannot instantiate Plugin (ID '.$plugin_ID.') without DB information.' );
1528                  $Debuglog->add( 'get_by_ID(): Plugin (ID '.$plugin_ID.') not registered/enabled in DB!', array( 'plugins', 'error' ) );
1529                  $r = false;
1530                  return $r;
1531              }
1532  
1533              $row = & $this->index_ID_rows[$plugin_ID];
1534  
1535              // Register the plugin:
1536              $Plugin = & $this->register( $row['plug_classname'], $row['plug_ID'], $row['plug_priority'] );
1537  
1538              if( is_string( $Plugin ) )
1539              {
1540                  $Debuglog->add( 'Requested plugin [#'.$plugin_ID.'] not found!', 'plugins' );
1541                  $r = false;
1542                  return $r;
1543              }
1544  
1545              $this->index_ID_Plugins[ $plugin_ID ] = & $Plugin;
1546          }
1547  
1548          return $this->index_ID_Plugins[ $plugin_ID ];
1549      }
1550  
1551  
1552      /**
1553       * Get a plugin by its classname.
1554       *
1555       * @param string
1556       * @return Plugin (false in case of error)
1557       */
1558      function & get_by_classname( $classname )
1559      {
1560          $this->load_plugins_table(); // We use index_ID_rows (no own index yet)
1561  
1562          foreach( $this->index_ID_rows as $plug_ID => $row )
1563          {
1564              if( $row['plug_classname'] == $classname )
1565              {
1566                  return $this->get_by_ID($plug_ID);
1567              }
1568          }
1569  
1570          $r = false;
1571          return $r;
1572      }
1573  
1574  
1575      /**
1576       * Get a specific Plugin by its code.
1577       *
1578       * @param string plugin code
1579       * @return Plugin (false in case of error)
1580       */
1581      function & get_by_code( $plugin_code )
1582      {
1583          global $Debuglog;
1584  
1585          $r = false;
1586  
1587          if( ! isset($this->index_code_Plugins[ $plugin_code ]) )
1588          { // Plugin is not registered yet
1589              $this->load_plugins_table();
1590  
1591              if( ! isset($this->index_code_ID[ $plugin_code ]) )
1592              {
1593                  $Debuglog->add( 'Requested plugin ['.$plugin_code.'] is not registered/enabled!', 'plugins' );
1594                  return $r;
1595              }
1596  
1597              if( ! $this->get_by_ID( $this->index_code_ID[$plugin_code] ) )
1598              {
1599                  $Debuglog->add( 'Requested plugin ['.$plugin_code.'] could not get instantiated!', 'plugins' );
1600                  return $r;
1601              }
1602          }
1603  
1604          return $this->index_code_Plugins[ $plugin_code ];
1605      }
1606  
1607  
1608      /**
1609       * Get a list of Plugins for a given event.
1610       *
1611       * @param string Event name
1612       * @return array plugin_ID => & Plugin
1613       */
1614  	function get_list_by_event( $event )
1615      {
1616          $r = array();
1617  
1618          if( isset($this->index_event_IDs[$event]) )
1619          {
1620              foreach( $this->index_event_IDs[$event] as $l_plugin_ID )
1621              {
1622                  if( $Plugin = & $this->get_by_ID( $l_plugin_ID ) )
1623                  {
1624                      $r[ $l_plugin_ID ] = & $Plugin;
1625                      unset($Plugin); // so that we do not overwrite the reference in the next loop
1626                  }
1627              }
1628          }
1629  
1630          return $r;
1631      }
1632  
1633  
1634      /**
1635       * Get a list of Plugins for a list of events. Every Plugin is only once in this list.
1636       *
1637       * @param array Array of events
1638       * @return array plugin_ID => & Plugin
1639       */
1640  	function get_list_by_events( $events )
1641      {
1642          $r = array();
1643  
1644          foreach( $events as $l_event )
1645          {
1646              foreach( array_keys($this->get_list_by_event( $l_event )) as $l_plugin_ID )
1647              {
1648                  if( $Plugin = & $this->get_by_ID( $l_plugin_ID ) )
1649                  {
1650                      $r[ $l_plugin_ID ] = & $Plugin;
1651                      unset($Plugin); // so that we do not overwrite the reference in the next loop
1652                  }
1653              }
1654          }
1655  
1656          return $r;
1657      }
1658  
1659  
1660      /**
1661       * Get a list of plugins that provide all given events.
1662       *
1663       * @return array plugin_ID => & Plugin
1664       */
1665  	function get_list_by_all_events( $events )
1666      {
1667          $candidates = array();
1668  
1669          foreach( $events as $l_event )
1670          {
1671              if( empty($this->index_event_IDs[$l_event]) )
1672              {
1673                  return array();
1674              }
1675  
1676              if( empty($candidates) )
1677              {
1678                  $candidates = $this->index_event_IDs[$l_event];
1679              }
1680              else
1681              {
1682                  $candidates = array_intersect( $candidates, $this->index_event_IDs[$l_event] );
1683                  if( empty($candidates) )
1684                  {
1685                      return array();
1686                  }
1687              }
1688          }
1689  
1690          $r = array();
1691          foreach( $candidates as $plugin_ID )
1692          {
1693              $Plugin = & $this->get_by_ID( $plugin_ID );
1694              if( $Plugin )
1695              {
1696                  $r[ $plugin_ID ] = & $Plugin;
1697                  unset($Plugin); // so that we do not overwrite the reference in the next loop
1698              }
1699          }
1700  
1701          return $r;
1702      }
1703  
1704  
1705      /**
1706       * Get a list of (enabled) events for a given Plugin ID.
1707       *
1708       * @param integer Plugin ID
1709       * @return array
1710       */
1711  	function get_enabled_events( $plugin_ID )
1712      {
1713          $r = array();
1714          foreach( $this->index_event_IDs as $l_event => $l_plugin_IDs )
1715          {
1716              if( in_array( $plugin_ID, $l_plugin_IDs ) )
1717              {
1718                  $r[] = $l_event;
1719              }
1720          }
1721          return $r;
1722      }
1723  
1724  
1725      /**
1726       * Has a plugin a specific event registered and enabled?
1727       *
1728       * @todo fp> The plugin should discover its events itself / This question should be asked to the Plugin itself.
1729       *       dh> Well, "discover" means to look up if it has a given method; this does not appear to belong into the Plugin class.
1730       *           A plugin won't also really know if some event is enabled or disabled.
1731       *
1732       * @param integer
1733       * @param string
1734       * @return boolean
1735       */
1736  	function has_event( $plugin_ID, $event )
1737      {
1738          return isset($this->index_event_IDs[$event])
1739              && in_array( $plugin_ID, $this->index_event_IDs[$event] );
1740      }
1741  
1742  
1743      /**
1744       * Check if the requested list of events is provided by any or one plugin.
1745       *
1746       * @param array|string A single event or a list thereof
1747       * @param boolean Make sure there's at least one plugin that provides them all?
1748       *                This is useful for event pairs like "CaptchaPayload" and "CaptchaValidated", which
1749       *                should be served by the same plugin.
1750       * @return boolean
1751       */
1752  	function are_events_available( $events, $require_all_in_same_plugin = false )
1753      {
1754          if( ! is_array($events) )
1755          {
1756              $events = array($events);
1757          }
1758  
1759          if( $require_all_in_same_plugin )
1760          {
1761              return (bool)$this->get_list_by_all_events( $events );
1762          }
1763  
1764          return (bool)$this->get_list_by_events( $events );
1765      }
1766  
1767  
1768      /**
1769       * (Re)load Plugin Events for enabled (normal use) or all (admin use) plugins.
1770       */
1771  	function load_events()
1772      {
1773          global $Debuglog, $DB;
1774  
1775          $this->index_event_IDs = array();
1776  
1777          $Debuglog->add( 'Loading plugin events.', 'plugins' );
1778          foreach( $DB->get_results( '
1779                  SELECT pevt_plug_ID, pevt_event
1780                      FROM T_pluginevents INNER JOIN T_plugins ON pevt_plug_ID = plug_ID
1781                   WHERE pevt_enabled > 0
1782                     AND plug_status = \'enabled\'
1783                   ORDER BY plug_priority, plug_classname', OBJECT, 'Loading plugin events' ) as $l_row )
1784          {
1785              $this->index_event_IDs[$l_row->pevt_event][] = $l_row->pevt_plug_ID;
1786          }
1787      }
1788  
1789  
1790      /**
1791       * Load an object from a Cache plugin or create a new one if we have a
1792       * cache miss or no caching plugins.
1793       *
1794       * It registers a shutdown function, that refreshes the data to the cache plugin
1795       * which is not optimal, but we have no hook to see if data retrieved from
1796       * a {@link DataObjectCache} derived class has changed.
1797       * @param string object name
1798       * @param string eval this to create the object. Default is to create an object
1799       *               of class $objectName.
1800       * @return boolean True, if retrieved from cache; false if not
1801       */
1802  	function get_object_from_cacheplugin_or_create( $objectName, $eval_create_object = NULL )
1803      {
1804          $get_return = $this->trigger_event_first_true( 'CacheObjects',
1805              array( 'action' => 'get', 'key' => 'object_'.$objectName ) );
1806  
1807          if( isset( $get_return['plugin_ID'] ) )
1808          {
1809              if( is_object($get_return['data']) )
1810              {
1811                  $GLOBALS[$objectName] = $get_return['data']; // COPY! (get_Cache() uses $$objectName instead of $GLOBALS - no deal for PHP5 anyway)
1812  
1813                  $Plugin = & $this->get_by_ID( $get_return['plugin_ID'] );
1814                  register_shutdown_function( array(&$Plugin, 'CacheObjects'),
1815                      array( 'action' => 'set', 'key' => 'object_'.$objectName, 'data' => & $GLOBALS[$objectName] ) );
1816  
1817                  return true;
1818              }
1819          }
1820  
1821          // Cache miss, create it:
1822          if( empty($eval_create_object) )
1823          {
1824              $GLOBALS[$objectName] = new $objectName(); // COPY (FUNC)
1825          }
1826          else
1827          {
1828              eval( '$GLOBALS[\''.$objectName.'\'] = '.$eval_create_object.';' );
1829          }
1830  
1831          // Try to set in cache:
1832          $set_return = $this->trigger_event_first_true( 'CacheObjects',
1833              array( 'action' => 'set', 'key' => 'object_'.$objectName, 'data' => & $GLOBALS[$objectName] ) );
1834  
1835          if( isset( $set_return['plugin_ID'] ) )
1836          { // success, register a shutdown function to save this data on shutdown
1837              $Plugin = & $this->get_by_ID( $set_return['plugin_ID'] );
1838              register_shutdown_function( array(&$Plugin, 'CacheObjects'),
1839                  array( 'action' => 'set', 'key' => 'object_'.$objectName, 'data' => & $GLOBALS[$objectName] ) );
1840          }
1841  
1842          return false;
1843      }
1844  
1845  
1846      /**
1847       * Callback, which gets used for {@link Results}.
1848       *
1849       * @return Plugin (false in case of error)
1850       */
1851      function & instantiate( $row )
1852      {
1853          return $this->get_by_ID( $row->plug_ID );
1854      }
1855  
1856  
1857      /**
1858       * Validate renderer list.
1859       *
1860       * @param array renderer codes ('default' will include all "opt-out"-ones)
1861       * @params array params to set Blog and apply_rendering setting_name ( see {@link load_index_apply_rendering()} )
1862       * @return array validated array of renderer codes
1863       */
1864  	function validate_renderer_list( $renderers = array('default'), $params )
1865      {
1866          // Init Blog and $setting_name from the given params
1867          if( isset( $params['Item'] ) )
1868          { // Validate post renderers
1869              $Item = & $params['Item'];
1870              $Blog = & $Item->get_Blog();
1871              $setting_name = 'coll_apply_rendering';
1872          }
1873          elseif( isset( $params['Comment'] ) )
1874          { // Validate comment renderers
1875              $Comment = & $params['Comment'];
1876              $Item = & $Comment->get_Item();
1877              $Blog = & $Item->get_Blog();
1878              $setting_name = 'coll_apply_comment_rendering';
1879          }
1880          elseif( isset( $params['Blog'] ) && isset( $params['setting_name'] ) )
1881          { // Validate the given rendering option in the give Blog
1882              $Blog = & $params['Blog'];
1883              $setting_name = $params['setting_name'];
1884              if( !in_array( $setting_name, array( 'coll_apply_rendering', 'coll_apply_comment_rendering' ) ) )
1885              {
1886                  debug_die( 'Invalid apply rendering param name received!' );
1887              }
1888          }
1889          else
1890          { // Invalid params to validate renderers!
1891              return array();
1892          }
1893  
1894          // Make sure the requested apply_rendering settings are loaded
1895          $this->load_index_apply_rendering( $setting_name, $Blog );
1896  
1897          $validated_renderers = array();
1898  
1899          // Get requested apply_rendering setting array
1900          $index = & $this->index_apply_rendering_codes[$Blog->ID][$setting_name];
1901  
1902          if( isset( $index['stealth'] ) )
1903          {
1904              // pre_dump( 'stealth:', $index['stealth'] );
1905              $validated_renderers = array_merge( $validated_renderers, $index['stealth'] );
1906          }
1907          if( isset( $index['always'] ) )
1908          {
1909              // pre_dump( 'always:', $index['always'] );
1910              $validated_renderers = array_merge( $validated_renderers, $index['always'] );
1911          }
1912  
1913          if( isset( $index['opt-out'] ) )
1914          {
1915              foreach( $index['opt-out'] as $l_code )
1916              {
1917                  if( in_array( $l_code, $renderers ) // Option is activated
1918                      || in_array( 'default', $renderers ) ) // OR we're asking for default renderer set
1919                  {
1920                      // pre_dump( 'opt-out:', $l_code );
1921                      $validated_renderers[] = $l_code;
1922                  }
1923              }
1924          }
1925  
1926          if( isset( $index['opt-in'] ) )
1927          {
1928              foreach( $index['opt-in'] as $l_code )
1929              {
1930                  if( in_array( $l_code, $renderers ) ) // Option is activated
1931                  {
1932                      // pre_dump( 'opt-in:', $l_code );
1933                      $validated_renderers[] = $l_code;
1934                  }
1935              }
1936          }
1937          if( isset( $index['lazy'] ) )
1938          {
1939              foreach( $index['lazy'] as $l_code )
1940              {
1941                  if( in_array( $l_code, $renderers ) ) // Option is activated
1942                  {
1943                      // pre_dump( 'lazy:', $l_code );
1944                      $validated_renderers[] = $l_code;
1945                  }
1946              }
1947          }
1948  
1949          // Make sure there's no renderer code with a dot, as the list gets imploded by that when saved:
1950          foreach( $validated_renderers as $k => $l_code )
1951          {
1952              if( empty($l_code) || strpos( $l_code, '.' ) !== false )
1953              {
1954                  unset( $validated_renderers[$k] );
1955              }
1956              /*
1957              dh> not required, now that the method has been moved back to Plugins (from ~_admin)
1958              else
1959              { // remove the ones which are not enabled:
1960                  $Plugin = & $this->get_by_code($l_code);
1961                  if( ! $Plugin || $Plugin->status != 'enabled' )
1962                  {
1963                      unset( $validated_renderers[$k] );
1964                  }
1965              }
1966              */
1967          }
1968  
1969          // echo 'validated Renderers: '.count( $validated_renderers );
1970          return $validated_renderers;
1971      }
1972  
1973  
1974      /**
1975       * Get checkable list of renderers
1976       *
1977       * @param array If given, assume these renderers to be checked.
1978       * @param array params from where to get 'apply_rendering' setting
1979       */
1980  	function get_renderer_checkboxes( $current_renderers = NULL, $params )
1981      {
1982          global $inc_path, $admin_url;
1983  
1984          load_funcs('plugins/_plugin.funcs.php');
1985  
1986          $name_prefix = isset( $params['name_prefix'] ) ? $params['name_prefix'] : '';
1987  
1988          $this->restart(); // make sure iterator is at start position
1989  
1990          if( ! is_array($current_renderers) )
1991          {
1992              $current_renderers = explode( '.', $current_renderers );
1993          }
1994  
1995          $atLeastOneRenderer = false;
1996          $setting_Blog = NULL;
1997          if( isset( $params['Comment'] ) && !empty( $params['Comment'] ) )
1998          { // get Comment apply_rendering setting
1999              $Comment = & $params['Comment'];
2000              $comment_Item = & $Comment->get_Item();
2001              $setting_Blog = & $comment_Item->get_Blog();
2002              $setting_name = 'coll_apply_comment_rendering';
2003          }
2004          elseif( isset( $params['Item'] ) )
2005          { // get Post apply_rendering setting
2006              $setting_name = 'coll_apply_rendering';
2007              $Item = & $params['Item'];
2008              $setting_Blog = & $Item->get_Blog();
2009          }
2010          elseif( isset( $params['Blog'] ) && isset( $params['setting_name'] ) )
2011          { // get given "apply_rendering" collection setting from the given Blog
2012              $setting_Blog = & $params['Blog'];
2013              $setting_name = $params['setting_name'];
2014          }
2015          else
2016          { // Invalid params
2017              return '';
2018          }
2019  
2020          if( $setting_name == 'coll_apply_comment_rendering' )
2021          { // Get Comment renderer plugins
2022              $RendererPlugins = $this->get_list_by_events( array('FilterCommentContent') );
2023          }
2024          else
2025          { // Get Item renderer plugins
2026              $RendererPlugins = $this->get_list_by_events( array('RenderItemAsHtml', 'RenderItemAsXml', 'RenderItemAsText') );
2027          }
2028  
2029          $r = '<input type="hidden" name="renderers_displayed" value="1" />';
2030  
2031          foreach( $RendererPlugins as $loop_RendererPlugin )
2032          { // Go through whole list of renders
2033              // echo ' ',$loop_RendererPlugin->code;
2034              if( empty($loop_RendererPlugin->code) )
2035              { // No unique code!
2036                  continue;
2037              }
2038              if( empty( $setting_Blog ) )
2039              { // If $setting_Blog is not set we can't get apply_rendering options
2040                  continue;
2041              }
2042  
2043              // get rendering setting from plugin coll settings
2044              $apply_rendering = $loop_RendererPlugin->get_coll_setting( $setting_name, $setting_Blog );
2045  
2046              if( $apply_rendering == 'stealth'
2047                  || $apply_rendering == 'never' )
2048              { // This is not an option.
2049                  continue;
2050              }
2051              $atLeastOneRenderer = true;
2052  
2053              $r .= '<div>';
2054  
2055              $r .= '<input type="checkbox" class="checkbox" name="'.$name_prefix.'renderers[]" value="'.$loop_RendererPlugin->code.'" id="renderer_'.$loop_RendererPlugin->code.'"';
2056  
2057              switch( $apply_rendering )
2058              {
2059                  case 'always':
2060                      $r .= ' checked="checked" disabled="disabled"';
2061                      break;
2062  
2063                  case 'opt-out':
2064                      if( in_array( $loop_RendererPlugin->code, $current_renderers ) // Option is activated
2065                          || in_array( 'default', $current_renderers ) ) // OR we're asking for default renderer set
2066                      {
2067                          $r .= ' checked="checked"';
2068                      }
2069                      break;
2070  
2071                  case 'opt-in':
2072                      if( in_array( $loop_RendererPlugin->code, $current_renderers ) ) // Option is activated
2073                      {
2074                          $r .= ' checked="checked"';
2075                      }
2076                      break;
2077  
2078                  case 'lazy':
2079                      if( in_array( $loop_RendererPlugin->code, $current_renderers ) ) // Option is activated
2080                      {
2081                          $r .= ' checked="checked"';
2082                      }
2083                      $r .= ' disabled="disabled"';
2084                      break;
2085              }
2086  
2087              $r .= ' title="'.format_to_output($loop_RendererPlugin->short_desc, 'formvalue').'" /> <label for="renderer_'.$loop_RendererPlugin->code.'" title="';
2088              $r .= format_to_output($loop_RendererPlugin->short_desc, 'formvalue').'">';
2089              $r .= format_to_output($loop_RendererPlugin->name).'</label>';
2090  
2091              // fp> TODO: the first thing we want here is a TINY javascript popup with the LONG desc. The links to readme and external help should be inside of the tiny popup.
2092              // fp> a javascript DHTML onhover help would be even better than the JS popup
2093  
2094              // external help link:
2095              $r .= ' '.$loop_RendererPlugin->get_help_link('$help_url');
2096  
2097              $r .= "</div>\n";
2098          }
2099  
2100          if( !$atLeastOneRenderer )
2101          {
2102              if( is_admin_page() )
2103              {    // Display info about no renderer plugins only in backoffice
2104                  global $admin_url;
2105                  $r .= '<a title="'.T_('Configure plugins').'" href="'.$admin_url.'?ctrl=plugins"'.'>'.T_('No renderer plugins are installed.').'</a>';
2106              }
2107              else
2108              {
2109                  return '';
2110              }
2111          }
2112  
2113          return $r;
2114      }
2115  
2116  
2117      // Deprecated stubs: {{{
2118  
2119      /**
2120       * @deprecated by Plugins_admin::count_regs()
2121       */
2122  	function count_regs( $classname )
2123      {
2124          global $Debuglog;
2125          $Debuglog->add('Call to deprecated method Plugins::count_regs()', 'deprecated');
2126          $Plugins_admin = & get_Plugins_admin();
2127          return $Plugins_admin->count_regs($classname);
2128      }
2129  
2130  
2131      /**
2132       * Validate renderer list.
2133       *
2134       * @deprecated by Plugins::validate_renderer_list()
2135       * @param array renderer codes ('default' will include all "opt-out"-ones)
2136       * @return array validated array of renderer codes
2137       */
2138  	function validate_list( $renderers = array('default') )
2139      {
2140          global $Debuglog, $Plugins;
2141          $Debuglog->add('Call to deprecated method Plugins::validate_list()', 'deprecated');
2142  
2143          // Return an empty array, because this method call is not supported anymore
2144          return array();
2145      }
2146  
2147  
2148      // }}}
2149  }
2150  
2151  ?>

title

Description

title

Description

title

Description

title

title

Body