[ Index ]
 

Code source de b2evolution 2.1.0-beta

Accédez au Source d'autres logiciels libres

Classes | Fonctions | Variables | Constantes | Tables

title

Body

[fermer]

/blogs/inc/plugins/model/ -> _plugins.class.php (source)

   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-2007 by Francois PLANQUE - {@link http://fplanque.net/}

  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 evocore

  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,v 1.2 2007/09/22 22:11:18 fplanque Exp $

  35   */
  36  if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
  37  
  38  load_class('plugins/_plugin.class.php');
  39  
  40  
  41  /**

  42   * Plugins Class

  43   *

  44   * This is where you can plug in some {@link Plugin plugins} :D

  45   *

  46   * @todo dh> Currently when a plugin goes into "broken" status (e.g. file not readable), it is "disabled" afterwards.

  47   *       This should rather remember the old status (e.g. "enabled") and make it enabled again.

  48   *

  49   * @package evocore

  50   */
  51  class Plugins
  52  {
  53      /**#@+

  54       * @access private

  55       */
  56  
  57      /**

  58       * @var array of plugin_code => Plugin

  59       */
  60      var $index_code_Plugins = array();
  61  
  62      /**

  63       * @var array of plugin_ID => Plugin

  64       */
  65      var $index_ID_Plugins = array();
  66  
  67      /**

  68       * @see Plugins::load_events()

  69       * @var array of event => plug_ID. IDs are sorted by priority.

  70       */
  71      var $index_event_IDs = array();
  72  
  73      /**

  74       * fp> does it cost that much to instantiate plugins right away, now that init is no longer in the constructor?

  75       * @var array of plug_ID => DB row from T_plugins. Used to lazy-instantiate Plugins.

  76       */
  77      var $index_ID_rows = array();
  78  
  79      /**

  80       * fp> does it cost that much to instantiate plugins right away, now that init is no longer in the constructor?

  81       * @var array of plug_code => plug_ID. Usedp to lazy-instantiate by code.

  82       */
  83      var $index_code_ID = array();
  84  
  85      /**

  86       * Cache Plugin codes by apply_rendering setting.

  87       * @var array of apply_rendering => plug_code

  88       */
  89      var $index_apply_rendering_codes = array();
  90  
  91      /**

  92       * Path to plugins.

  93       *

  94       * The preferred method is to have a sub-directory for each plugin (named

  95       * after the plugin's classname), but they can be supplied just in this

  96       * directory.

  97       */
  98      var $plugins_path;
  99  
 100      /**

 101       * Have we loaded the plugins table (T_plugins)?

 102       * @var boolean

 103       */
 104      var $loaded_plugins_table = false;
 105  
 106      /**

 107       * Current object index in {@link $sorted_IDs} array.

 108       * @var integer

 109       */
 110      var $current_idx = -1;
 111  
 112      /**

 113       * List of IDs, sorted. This gets used to lazy-instantiate a Plugin.

 114       *

 115       * @var array

 116       */
 117      var $sorted_IDs = array();
 118  
 119      /**

 120       * The smallest internal/auto-generated Plugin ID.

 121       * @var integer

 122       */
 123      var $smallest_internal_ID = 0;
 124  
 125      /**#@-*/

 126  
 127  
 128      /**#@+

 129       * @access protected

 130       */
 131  
 132      /**

 133       * SQL to use in {@link load_plugins_table()}. Gets overwritten for {@link Plugins_admin}.

 134       * @var string

 135       * @static

 136       */
 137      var $sql_load_plugins_table = '
 138              SELECT plug_ID, plug_priority, plug_classname, plug_code, plug_name, plug_shortdesc, plug_apply_rendering, plug_status, plug_version, plug_spam_weight
 139                FROM T_plugins
 140               WHERE plug_status = \'enabled\'
 141               ORDER BY plug_priority, plug_classname';
 142  
 143      /**#@-*/

 144  
 145  
 146      /**

 147       * Errors associated to plugins (during loading), indexed by plugin_ID and

 148       * error class ("register").

 149       *

 150       * @var array

 151       */
 152      var $plugin_errors = array();
 153  
 154  
 155      /**

 156       * Constructor. Sets {@link $plugins_path} and load events.

 157       */
 158  	function Plugins()
 159      {
 160          global $basepath, $plugins_subdir, $Timer;
 161  
 162          // Set plugin path:

 163          $this->plugins_path = $basepath.$plugins_subdir;
 164  
 165          $Timer->resume( 'plugin_init' );
 166  
 167          // Load events for enabled plugins:

 168          $this->load_events();
 169  
 170          $Timer->pause( 'plugin_init' );
 171      }
 172  
 173  
 174      /**

 175       * Get a list of available Plugin groups.

 176       *

 177       * @return array

 178       */
 179  	function get_plugin_groups()
 180      {
 181          $result = array();
 182  
 183          foreach( $this->sorted_IDs as $plugin_ID )
 184          {
 185              $Plugin = & $this->get_by_ID( $plugin_ID );
 186  
 187              if( empty($Plugin->group) || in_array( $Plugin->group, $result ) )
 188              {
 189                  continue;
 190              }
 191  
 192              $result[] = $Plugin->group;
 193          }
 194  
 195          return $result;
 196      }
 197  
 198  
 199      /**

 200       * Will return an array that contents are references to plugins that have the same group, regardless of the sub_group.

 201       *

 202       * @return array

 203       */
 204  	function get_Plugins_in_group( $group )
 205      {
 206          $result = array();
 207  
 208          foreach( $this->sorted_IDs as $plugin_ID )
 209          {
 210              $Plugin = & $this->get_by_ID( $plugin_ID );
 211              if( $Plugin->group == $group )
 212              {
 213                  $result[] = & $Plugin;
 214              }
 215          }
 216  
 217          return $result;
 218      }
 219  
 220  
 221      /**

 222       * Will return an array that contents are references to plugins that have the same group and sub_group.

 223       *

 224       * @return array

 225       */
 226  	function get_Plugins_in_sub_group( $group, $sub_group = '' )
 227      {
 228          $result = array();
 229  
 230          foreach( $this->sorted_IDs as $plugin_ID )
 231          {
 232              $Plugin = & $this->get_by_ID( $plugin_ID );
 233              if( $Plugin->group == $group && $Plugin->sub_group == $sub_group )
 234              {
 235                  $result[] = & $Plugin;
 236              }
 237          }
 238  
 239          return $result;
 240      }
 241  
 242  
 243      /**

 244       * Sets the status of a Plugin in DB and registers it into the internal indices when "enabled".

 245       * Otherwise it gets unregistered, but only when we're not in {@link Plugins_admin}, because we

 246       * want to keep it in then in our indices.

 247       *

 248       * {@internal

 249       * Note: this should probably always get called on the {@link $Plugins} object,

 250       *       not {@link $admin_Plugins}.

 251       * }}

 252       *

 253       * @param Plugin

 254       * @param string New status ("enabled", "disabled", "needs_config", "broken")

 255       */
 256  	function set_Plugin_status( & $Plugin, $status )
 257      {
 258          global $DB, $Debuglog;
 259  
 260          $DB->query( "UPDATE T_plugins SET plug_status = '".$status."' WHERE plug_ID = '".$Plugin->ID."'" );
 261  
 262          if( $status == 'enabled' )
 263          { // Reload plugins tables, which includes the plugin in further requests
 264              $this->loaded_plugins_table = false;
 265              $this->load_plugins_table();
 266              $this->load_events();
 267          }
 268          else
 269          {
 270              // Notify the plugin that it has been disabled:

 271              $Plugin->BeforeDisable();
 272  
 273              $this->unregister( $Plugin );
 274          }
 275  
 276          $Plugin->status = $status;
 277  
 278          $Debuglog->add( 'Set status for plugin #'.$Plugin->ID.' to "'.$status.'"!', 'plugins' );
 279      }
 280  
 281  
 282      /**

 283       * Register a plugin.

 284       *

 285       * This handles the indexes, dynamically unregisters a Plugin that does not exist (anymore)

 286       * and instantiates the Plugin's (User)Settings.

 287       *

 288       * @access protected

 289       * @param string name of plugin class to instantiate and register

 290       * @param int ID in database (0 if not installed)

 291       * @param int Priority in database (-1 to keep default)

 292       * @param array When should rendering apply? (NULL to keep default)

 293       * @param string Path of the .php class file of the plugin.

 294       * @param boolean Must the plugin exist (classfile_path and classname)?

 295       *                This is used internally to be able to unregister a non-existing plugin.

 296       * @return Plugin Plugin ref to newly created plugin; string in case of error

 297       */
 298      function & register( $classname, $ID = 0, $priority = -1, $apply_rendering = NULL, $classfile_path = NULL, $must_exists = true )
 299      {
 300          global $Debuglog, $Messages, $Timer;
 301  
 302          if( $ID && isset($this->index_ID_Plugins[$ID]) )
 303          {
 304              debug_die( 'Tried to register already registered Plugin (ID '.$ID.')' ); // should never happen!

 305          }
 306  
 307          $Timer->resume( 'plugins_register' );
 308  
 309          if( empty($classfile_path) )
 310          {
 311              $plugin_filename = '_'.str_replace( '_plugin', '.plugin', $classname ).'.php';
 312              // Try <plug_classname>/<plug_classname>.php (subfolder) first

 313              $classfile_path = $this->plugins_path.$classname.'/'.$plugin_filename;
 314  
 315              if( ! is_readable( $classfile_path ) )
 316              { // Look directly in $plugins_path
 317                  $classfile_path = $this->plugins_path.$plugin_filename;
 318              }
 319          }
 320  
 321          $Debuglog->add( 'register(): '.$classname.', ID: '.$ID.', priority: '.$priority.', classfile_path: ['.$classfile_path.']', 'plugins' );
 322  
 323          if( ! is_readable( $classfile_path ) )
 324          { // Plugin file not found!
 325              if( $must_exists )
 326              {
 327                  $r = 'Plugin class file ['.rel_path_to_base($classfile_path).'] is not readable!';
 328                  $Debuglog->add( $r, array( 'plugins', 'error' ) );
 329  
 330                  // Get the Plugin object (must not exist)

 331                  $Plugin = & $this->register( $classname, $ID, $priority, $apply_rendering, $classfile_path, false );
 332                  $this->plugin_errors[$ID]['register'] = $r;
 333                  $this->set_Plugin_status( $Plugin, 'broken' );
 334  
 335                  // unregister:

 336                  if( $this->unregister( $Plugin ) )
 337                  {
 338                      $Debuglog->add( 'Unregistered plugin ['.$classname.']!', array( 'plugins', 'error' ) );
 339                  }
 340                  else
 341                  {
 342                      $Plugin->name = $Plugin->classname; // use the classname instead of "unnamed plugin"

 343                      $Timer->pause( 'plugins_register' );
 344                      return $Plugin;
 345                  }
 346  
 347                  $Timer->pause( 'plugins_register' );
 348                  return $r;
 349              }
 350          }
 351          elseif( ! class_exists($classname) ) // If there are several copies of one plugin for example..
 352          {
 353              $Debuglog->add( 'Loading plugin class file: '.$classname, 'plugins' );
 354              require_once $classfile_path;
 355          }
 356  
 357          if( ! class_exists( $classname ) )
 358          { // the given class does not exist
 359              if( $must_exists )
 360              {
 361                  $r = sprintf( 'Plugin class for &laquo;%s&raquo; in file &laquo;%s&raquo; not defined.', $classname, rel_path_to_base($classfile_path) );
 362                  $Debuglog->add( $r, array( 'plugins', 'error' ) );
 363  
 364                  // Get the Plugin object (must not exist)    fp> why is this recursive?

 365                  $Plugin = & $this->register( $classname, $ID, $priority, $apply_rendering, $classfile_path, false );
 366                  $this->plugin_errors[$ID]['register'] = $r;
 367                  $this->set_Plugin_status( $Plugin, 'broken' );
 368  
 369                  // unregister:

 370                  if( $this->unregister( $Plugin ) )
 371                  {
 372                      $Debuglog->add( 'Unregistered plugin ['.$classname.']!', array( 'plugins', 'error' ) );
 373                  }
 374                  else
 375                  {
 376                      $Plugin->name = $Plugin->classname; // use the classname instead of "unnamed plugin"

 377                      $Timer->pause( 'plugins_register' );
 378                      return $Plugin;
 379                  }
 380  
 381                  $Timer->pause( 'plugins_register' );
 382                  return $r;
 383              }
 384              else
 385              {
 386                  $Plugin = new Plugin;    // COPY !

 387                  $Plugin->code = NULL;
 388                  $Plugin->apply_rendering = 'never';
 389              }
 390          }
 391          else
 392          {
 393              $Plugin = new $classname;    // COPY !

 394          }
 395  
 396          $Plugin->classfile_path = $classfile_path;
 397  
 398          // Tell him his ID :)

 399          if( $ID == 0 )
 400          {
 401              $Plugin->ID = --$this->smallest_internal_ID;
 402          }
 403          else
 404          {
 405              $Plugin->ID = $ID;
 406  
 407              if( $ID > 0 )
 408              { // Properties from T_plugins
 409                  // Code

 410                  $Plugin->code = $this->index_ID_rows[$Plugin->ID]['plug_code'];
 411                  // Status

 412                  $Plugin->status = $this->index_ID_rows[$Plugin->ID]['plug_status'];
 413              }
 414          }
 415          // Tell him his name :)

 416          $Plugin->classname = $classname;
 417          // Tell him his priority:

 418          if( $priority > -1 ) { $Plugin->priority = $priority; }
 419  
 420          if( isset($apply_rendering) )
 421          {
 422              $Plugin->apply_rendering = $apply_rendering;
 423          }
 424  
 425          if( empty($Plugin->name) )
 426          {
 427              $Plugin->name = $Plugin->classname;
 428          }
 429  
 430          // Memorizes Plugin in code hash array:

 431          if( ! empty($this->index_code_ID[ $Plugin->code ]) && $this->index_code_ID[ $Plugin->code ] != $Plugin->ID )
 432          { // The plugin's default code is already in use!
 433              $Plugin->code = NULL;
 434          }
 435          else
 436          {
 437              $this->index_code_Plugins[ $Plugin->code ] = & $Plugin;
 438              $this->index_code_ID[ $Plugin->code ] = & $Plugin->ID;
 439          }
 440          $this->index_ID_Plugins[ $Plugin->ID ] = & $Plugin;
 441  
 442          if( ! in_array( $Plugin->ID, $this->sorted_IDs ) ) // TODO: check if this extra check is required..
 443          { // not in our sort index yet
 444              $this->sorted_IDs[] = & $Plugin->ID;
 445          }
 446  
 447          // Stuff only for real/existing Plugins (which exist in DB):

 448          if( $Plugin->ID > 0 )
 449          {
 450              // Instantiate the Plugins (User)Settings members:

 451              $this->init_settings( $Plugin );
 452  
 453              $tmp_params = array( 'db_row' => $this->index_ID_rows[$Plugin->ID], 'is_installed' => true );
 454              if( $Plugin->PluginInit( $tmp_params ) === false && $this->unregister( $Plugin ) )
 455              {
 456                  $Debuglog->add( 'Unregistered plugin, because PluginInit returned false.', 'plugins' );
 457                  $Plugin = '';
 458              }
 459              // Version check:

 460              elseif( $Plugin->version != $this->index_ID_rows[$Plugin->ID]['plug_version'] && $must_exists )
 461              { // Version has changed since installation or last update
 462                  $db_deltas = array();
 463  
 464                  // Tell the Plugin that we've detected a version change:

 465                  $tmp_params = array( 'old_version'=>$this->index_ID_rows[$Plugin->ID]['plug_version'], 'db_row'=>$this->index_ID_rows[$Plugin->ID] );
 466  
 467                  if( $this->call_method( $Plugin->ID, 'PluginVersionChanged', $tmp_params ) === false )
 468                  {
 469                      $Debuglog->add( 'Set plugin status to "needs_config", because PluginVersionChanged returned false.', 'plugins' );
 470                      $this->set_Plugin_status( $Plugin, 'needs_config' );
 471                      if( $this->unregister( $Plugin ) )
 472                      { // only unregister the Plugin, if it's not the admin list's class:
 473                          $Plugin = '';
 474                      }
 475                  }
 476                  else
 477                  {
 478                      // Check if there are DB deltas required (also when downgrading!), without excluding any query type:

 479                      load_class('_core/model/db/_upgrade.funcs.php');
 480                      $db_deltas = db_delta( $Plugin->GetDbLayout() );
 481  
 482                      if( empty($db_deltas) )
 483                      { // No DB changes needed, update (bump or decrease) the version
 484                          global $DB;
 485                          $Plugins_admin = & get_Cache('Plugins_admin');
 486  
 487                          // Update version in DB:

 488                          $DB->query( '
 489                                  UPDATE T_plugins
 490                                     SET plug_version = '.$DB->quote($Plugin->version).'
 491                                   WHERE plug_ID = '.$Plugin->ID );
 492  
 493                          // Update "plug_version" in indexes:

 494                          $this->index_ID_rows[$Plugin->ID]['plug_version'] = $Plugin->version;
 495                          if( isset($Plugins_admin->index_ID_rows[$Plugin->ID]) )
 496                          {
 497                              $Plugins_admin->index_ID_rows[$Plugin->ID]['plug_version'] = $Plugin->version;
 498                          }
 499  
 500                          // Remove any prerenderered content for the Plugins renderer code:

 501                          if( ! empty($Plugin->code) )
 502                          {
 503                              $DB->query( '
 504                                      DELETE FROM T_items__prerendering
 505                                       WHERE itpr_renderers REGEXP "^(.*\.)?'.$DB->escape($Plugin->code).'(\..*)?$"' );
 506                          }
 507  
 508                          // Detect new events (and delete obsolete ones - in case of downgrade):

 509                          if( $Plugins_admin->save_events( $Plugin, array() ) )
 510                          {
 511                              $this->load_events(); // re-load for the current request

 512                          }
 513  
 514                          $Debuglog->add( 'Version for '.$Plugin->classname.' changed from '.$this->index_ID_rows[$Plugin->ID]['plug_version'].' to '.$Plugin->version, 'plugins' );
 515                      }
 516                      else
 517                      { // If there are DB schema changes needed, set the Plugin status to "needs_config"
 518  
 519                          // TODO: automatic upgrade in some cases (e.g. according to query types)?

 520  
 521                          $this->set_Plugin_status( $Plugin, 'needs_config' );
 522                          $Debuglog->add( 'Set plugin status to "needs_config", because version DB schema needs upgrade.', 'plugins' );
 523  
 524                          if( $this->unregister( $Plugin ) )
 525                          { // only unregister the Plugin, if it's not the admin list's class:
 526                              $Plugin = '';
 527                          }
 528                      }
 529                  }
 530              }
 531  
 532              if( $Plugin && isset($this->index_ID_rows[$Plugin->ID]) ) // may have been unregistered above
 533              {
 534                  if( $this->index_ID_rows[$Plugin->ID]['plug_name'] !== NULL )
 535                  {
 536                      $Plugin->name = $this->index_ID_rows[$Plugin->ID]['plug_name'];
 537                  }
 538                  if( $this->index_ID_rows[$Plugin->ID]['plug_shortdesc'] !== NULL )
 539                  {
 540                      $Plugin->short_desc = $this->index_ID_rows[$Plugin->ID]['plug_shortdesc'];
 541                  }
 542              }
 543          }
 544          else
 545          { // This gets called for non-installed Plugins:
 546              // Instantiate the Plugins (User)Settings members:

 547              $this->init_settings( $Plugin );
 548  
 549              $tmp_params = array( 'db_row' => array(), 'is_installed' => false );
 550              if( $Plugin->PluginInit( $tmp_params ) === false && $this->unregister( $Plugin ) )
 551              {
 552                  $Debuglog->add( 'Unregistered plugin, because PluginInit returned false.', 'plugins' );
 553                  $Plugin = '';
 554              }
 555          }
 556  
 557          $Timer->pause( 'plugins_register' );
 558  
 559          return $Plugin;
 560      }
 561  
 562  
 563      /**

 564       * Un-register a plugin.

 565       *

 566       * This does not un-install it from DB, just from the internal indexes.

 567       *

 568       * @param Plugin

 569       * @param boolean Force unregistering (ignored here, but used in Plugins_admin)

 570       * @return boolean True, if unregistered

 571       */
 572  	function unregister( & $Plugin, $force = false )
 573      {
 574          global $Debuglog;
 575  
 576          $this->forget_events( $Plugin->ID );
 577  
 578          // Unset apply-rendering index:

 579          if( isset( $this->index_apply_rendering_codes[ $Plugin->apply_rendering ] ) )
 580          {
 581              while( ( $key = array_search( $Plugin->code, $this->index_apply_rendering_codes[$Plugin->apply_rendering] ) ) !== false )
 582              {
 583                  unset( $this->index_apply_rendering_codes[$Plugin->apply_rendering][$key] );
 584              }
 585          }
 586  
 587          unset( $this->index_code_Plugins[ $Plugin->code ] );
 588          unset( $this->index_ID_Plugins[ $Plugin->ID ] );
 589  
 590          if( isset($this->index_ID_rows[ $Plugin->ID ]) )
 591          { // It has an associated DB row (load_plugins_table() was called)
 592              unset($this->index_ID_rows[ $Plugin->ID ]);
 593          }
 594  
 595          $sort_key = array_search( $Plugin->ID, $this->sorted_IDs );
 596          if( $sort_key === false )
 597          { // this may happen if a Plugin has unregistered itself
 598              $Debuglog->add( 'Tried to unregister not-installed plugin (not in $sorted_IDs)!', 'plugins' );
 599              return false;
 600          }
 601          unset( $this->sorted_IDs[$sort_key] );
 602          $this->sorted_IDs = array_values( $this->sorted_IDs );
 603  
 604          if( $this->current_idx >= $sort_key )
 605          { // We have removed a file before or at the $sort_key'th position
 606              $this->current_idx--;
 607          }
 608  
 609          return true;
 610      }
 611  
 612  
 613      /**

 614       * Forget the events a Plugin has registered.

 615       *

 616       * This gets used when {@link unregister() unregistering} a Plugin or if

 617       * {@link Plugin::PluginInit()} returned false, which means

 618       * "do not use it for subsequent events in the request".

 619       *

 620       * @param integer Plugin ID

 621       */
 622  	function forget_events( $plugin_ID )
 623      {
 624          // Forget events:

 625          foreach( array_keys($this->index_event_IDs) as $l_event )
 626          {
 627              while( ($key = array_search( $plugin_ID, $this->index_event_IDs[$l_event] )) !== false )
 628              {
 629                  unset( $this->index_event_IDs[$l_event][$key] );
 630              }
 631          }
 632      }
 633  
 634  
 635      /**

 636       * Init {@link Plugin::$Settings} and {@link Plugin::$UserSettings}, either by

 637       * unsetting them for PHP5's overloading or instantiating them for PHP4.

 638       *

 639       * @param Plugin

 640       */
 641  	function init_settings( & $Plugin )
 642      {
 643          if( version_compare( PHP_VERSION, '5.1', '>=' ) )
 644          { // we use overloading for PHP5, therefor the member has to be unset:
 645              // Note: this is somehow buggy at least in PHP 5.0.5, therefor we use it from 5.1 on.

 646              //       see http://forums.b2evolution.net/viewtopic.php?p=49031#49031

 647              unset( $Plugin->Settings );
 648              unset( $Plugin->UserSettings );
 649  
 650              // Nothing to do here, will get called through Plugin::__get() when accessed

 651              return;
 652          }
 653  
 654          // PHP < 5.1: instantiate now:

 655          $this->instantiate_Settings( $Plugin, 'Settings' );
 656          $this->instantiate_Settings( $Plugin, 'UserSettings' );
 657      }
 658  
 659  
 660      /**

 661       * Instantiate Settings object (class {@link PluginSettings}) for the given plugin.

 662       *

 663       * The plugin must provide setting definitions (through {@link Plugin::GetDefaultSettings()}

 664       * OR {@link Plugin::GetDefaultUserSettings()}).

 665       *

 666       * @param Plugin

 667       * @param string settings type: "Settings" or "UserSettings"

 668       * @return boolean NULL, if no Settings

 669       */
 670  	function instantiate_Settings( & $Plugin, $set_type )
 671      {
 672          global $Debuglog, $Timer;
 673  
 674          $Timer->resume( 'plugins_inst_'.$set_type );
 675  
 676          // call Plugin::GetDefaultSettings() or Plugin::GetDefaultUserSettings():

 677          $defaults = $this->call_method( $Plugin->ID, 'GetDefault'.$set_type, $params = array('for_editing'=>false) );
 678  
 679          if( empty($defaults) )
 680          {    // No settings, no need to instantiate.
 681              $Timer->pause( 'plugins_inst_'.$set_type );
 682              return NULL;
 683          }
 684  
 685          if( ! is_array($defaults) )
 686          {    // invalid data
 687              $Debuglog->add( $Plugin->classname.'::GetDefault'.$set_type.'() did not return array!', array('plugins', 'error') );
 688              return NULL; // fp> correct me if I'm wrong.

 689          }
 690  
 691          if( $set_type == 'UserSettings' )
 692          {    // User specific settings:
 693              load_class('plugins/model/_pluginusersettings.class.php');
 694  
 695              $Plugin->UserSettings = new PluginUserSettings( $Plugin->ID );
 696  
 697              $set_Obj = & $Plugin->UserSettings;
 698          }
 699          else
 700          {    // Global settings:
 701              load_class('plugins/model/_pluginsettings.class.php');
 702  
 703              $Plugin->Settings = new PluginSettings( $Plugin->ID );
 704  
 705              $set_Obj = & $Plugin->Settings;
 706          }
 707  
 708          // Register default values:

 709          foreach( $defaults as $l_name => $l_meta )
 710          {
 711              if( isset($l_meta['layout']) )
 712              { // Skip non-value entries
 713                  continue;
 714              }
 715  
 716              // Register settings as _defaults into Settings:

 717              if( isset($l_meta['defaultvalue']) )
 718              {
 719                  $set_Obj->_defaults[$l_name] = $l_meta['defaultvalue'];
 720              }
 721              elseif( isset( $l_meta['type'] ) && $l_meta['type'] == 'array' )
 722              {
 723                  $set_Obj->_defaults[$l_name] = array();
 724              }
 725              else
 726              {
 727                  $set_Obj->_defaults[$l_name] = '';
 728              }
 729          }
 730  
 731          $Timer->pause( 'plugins_inst_'.$set_type );
 732  
 733          return true;
 734      }
 735  
 736  
 737      /**

 738       * Load plugins table and rewind iterator used by {@link get_next()}.

 739       */
 740  	function restart()
 741      {
 742          $this->load_plugins_table();
 743  
 744          $this->current_idx = -1;
 745      }
 746  
 747  
 748      /**

 749       * Get next plugin in the list.

 750       *

 751       * NOTE: You'll have to call {@link restart()} or {@link load_plugins_table()}

 752       * before using it.

 753       *

 754       * @return Plugin (false if no more plugin).

 755       */
 756      function & get_next()
 757      {
 758          global $Debuglog;
 759  
 760          ++$this->current_idx;
 761  
 762          $Debuglog->add( 'get_next() ('.$this->current_idx.')..', 'plugins' );
 763  
 764          if( isset($this->sorted_IDs[$this->current_idx]) )
 765          {
 766              $Plugin = & $this->get_by_ID( $this->sorted_IDs[$this->current_idx] );
 767  
 768              if( ! $Plugin )
 769              { // recurse until we've been through whole $sorted_IDs!
 770                  return $this->get_next();
 771              }
 772  
 773              $Debuglog->add( 'return: '.$Plugin->classname.' ('.$Plugin->ID.')', 'plugins' );
 774              return $Plugin;
 775          }
 776          else
 777          {
 778              $Debuglog->add( 'return: false', 'plugins' );
 779              --$this->current_idx;
 780              $r = false;
 781              return $r;
 782          }
 783      }
 784  
 785  
 786      /**

 787       * Stop propagation of events to next plugins in {@link trigger_event()}.

 788       */
 789  	function stop_propagation()
 790      {
 791          $this->_stop_propagation = true;
 792      }
 793  
 794  
 795      /**

 796       * Call all plugins for a given event.

 797       *

 798       * @param string event name, see {@link Plugins_admin::get_supported_events()}

 799       * @param array Associative array of parameters for the Plugin

 800       * @return boolean True, if at least one plugin has been called.

 801       */
 802  	function trigger_event( $event, $params = array() )
 803      {
 804          global $Debuglog;
 805  
 806          $Debuglog->add( 'Trigger event '.$event, 'plugins' );
 807  
 808          if( empty($this->index_event_IDs[$event]) )
 809          { // No events registered
 810              $Debuglog->add( 'No registered plugins.', 'plugins' );
 811              return false;
 812          }
 813  
 814          $Debuglog->add( 'Registered plugin IDs: '.implode( ', ', $this->index_event_IDs[$event]), 'plugins' );
 815  
 816          foreach( $this->index_event_IDs[$event] as $l_plugin_ID )
 817          {
 818              $this->call_method( $l_plugin_ID, $event, $params );
 819  
 820              if( ! empty($this->_stop_propagation) )
 821              {    // A plugin has requested to stop propagation.
 822                  $this->_stop_propagation = false;
 823                  break;
 824              }
 825          }
 826          return true;
 827      }
 828  
 829  
 830      /**

 831       * Call all plugins for a given event, until the first one returns true.

 832       *

 833       * @param string event name, see {@link Plugins_admin::get_supported_events()}

 834       * @param array Associative array of parameters for the Plugin

 835       * @return array The (modified) params array with key "plugin_ID" set to the last called plugin;

 836       *               Empty array if no Plugin returned true or no Plugin has this event registered.

 837       */
 838  	function trigger_event_first_true( $event, $params = NULL )
 839      {
 840          global $Debuglog;
 841  
 842          $Debuglog->add( 'Trigger event '.$event.' (first true)', 'plugins' );
 843  
 844          if( empty($this->index_event_IDs[$event]) )
 845          { // No events registered
 846              $Debuglog->add( 'No registered plugins.', 'plugins' );
 847              return array();
 848          }
 849  
 850          $Debuglog->add( 'Registered plugin IDs: '.implode( ', ', $this->index_event_IDs[$event]), 'plugins' );
 851          foreach( $this->index_event_IDs[$event] as $l_plugin_ID )
 852          {
 853              $r = $this->call_method( $l_plugin_ID, $event, $params );
 854              if( $r === true )
 855              {
 856                  $Debuglog->add( 'Plugin ID '.$l_plugin_ID.' returned true!', 'plugins' );
 857                  $params['plugin_ID'] = & $l_plugin_ID;
 858                  return $params;
 859              }
 860          }
 861          return array();
 862      }
 863  
 864  
 865      /**

 866       * Call all plugins for a given event, until the first one returns false.

 867       *

 868       * @param string event name, see {@link Plugins_admin::get_supported_events()}

 869       * @param array Associative array of parameters for the Plugin

 870       * @return array The (modified) params array with key "plugin_ID" set to the last called plugin;

 871       *               Empty array if no Plugin returned true or no Plugin has this event registered.

 872       */
 873  	function trigger_event_first_false( $event, $params = NULL )
 874      {
 875          global $Debuglog;
 876  
 877          $Debuglog->add( 'Trigger event '.$event.' (first false)', 'plugins' );
 878  
 879          if( empty($this->index_event_IDs[$event]) )
 880          { // No events registered
 881              $Debuglog->add( 'No registered plugins.', 'plugins' );
 882              return array();
 883          }
 884  
 885          $Debuglog->add( 'Registered plugin IDs: '.implode( ', ', $this->index_event_IDs[$event]), 'plugins' );
 886          foreach( $this->index_event_IDs[$event] as $l_plugin_ID )
 887          {
 888              $r = $this->call_method( $l_plugin_ID, $event, $params );
 889              if( $r === false )
 890              {
 891                  $Debuglog->add( 'Plugin ID '.$l_plugin_ID.' returned false!', 'plugins' );
 892                  $params['plugin_ID'] = & $l_plugin_ID;
 893                  return $params;
 894              }
 895          }
 896          return array();
 897      }
 898  
 899  
 900      /**

 901       * Call all plugins for a given event, until the first one returns a value

 902       * (not NULL) (and $search is fulfilled, if given).

 903       *

 904       * @param string event name, see {@link Plugins_admin::get_supported_events()}

 905       * @param array|NULL Associative array of parameters for the Plugin

 906       * @param array|NULL If provided, the return value gets checks against this criteria.

 907       *        Can be:

 908       *         - ( 'in_array' => 'needle' )

 909       * @return array The (modified) params array with key "plugin_ID" set to the last called plugin

 910       *               and 'plugin_return' set to the return value;

 911       *               Empty array if no Plugin returned true or no Plugin has this event registered.

 912       */
 913  	function trigger_event_first_return( $event, $params = NULL, $search = NULL )
 914      {
 915          global $Debuglog;
 916  
 917          $Debuglog->add( 'Trigger event '.$event.' (first return)', 'plugins' );
 918  
 919          if( empty($this->index_event_IDs[$event]) )
 920          { // No events registered
 921              $Debuglog->add( 'No registered plugins.', 'plugins' );
 922              return array();
 923          }
 924  
 925          $Debuglog->add( 'Registered plugin IDs: '.implode( ', ', $this->index_event_IDs[$event]), 'plugins' );
 926          foreach( $this->index_event_IDs[$event] as $l_plugin_ID )
 927          {
 928              $r = $this->call_method( $l_plugin_ID, $event, $params );
 929              if( isset($r) )
 930              {
 931                  if( isset($search) )
 932                  { // Apply $search:
 933                      foreach( $search as $k => $v )
 934                      { // Check search criterias and continue if it does not match:
 935                          switch( $k )
 936                          {
 937                              case 'in_array':
 938                                  if( ! in_array( $v, $r ) )
 939                                  {
 940                                      continue 3; // continue in main foreach loop

 941                                  }
 942                                  break;
 943                              default:
 944                                  debug_die('Invalid search criteria in Plugins::trigger_event_first_return / '.$k);
 945                          }
 946                      }
 947                  }
 948                  $Debuglog->add( 'Plugin ID '.$l_plugin_ID.' returned '.( $r ? 'true' : 'false' ).'!', 'plugins' );
 949                  $params['plugin_return'] = $r;
 950                  $params['plugin_ID'] = & $l_plugin_ID;
 951                  return $params;
 952              }
 953          }
 954          return array();
 955      }
 956  
 957  
 958      /**

 959       * Trigger an event and return an index of params.

 960       *

 961       * This is handy to collect return values from all plugins hooking an event.

 962       *

 963       * @param string Event name, see {@link Plugins_admin::get_supported_events()}

 964       * @param array Associative array of parameters for the Plugin

 965       * @param string Index of $params that should get returned

 966       * @return mixed The requested index of $params

 967       */
 968  	function get_trigger_event( $event, $params = NULL, $get = 'data' )
 969      {
 970          $params[$get] = & $params[$get]; // make it a reference, so it can get changed

 971  
 972          $this->trigger_event( $event, $params );
 973  
 974          return $params[$get];
 975      }
 976  
 977  
 978      /**

 979       * The same as {@link get_trigger_event()}, but stop when the first Plugin returns true.

 980       *

 981       * @param string Event name, see {@link Plugins_admin::get_supported_events()}

 982       * @param array Associative array of parameters for the Plugin

 983       * @param string Index of $params that should get returned

 984       * @return mixed The requested index of $params

 985       */
 986  	function get_trigger_event_first_true( $event, $params = NULL, $get = 'data' )
 987      {
 988          $params[$get] = & $params[$get]; // make it a reference, so it can get changed

 989  
 990          $this->trigger_event_first_true( $event, $params );
 991  
 992          return $params[$get];
 993      }
 994  
 995  
 996      /**

 997       * Trigger an event and return the first return value of a plugin.

 998       *

 999       * @param string Event name, see {@link Plugins_admin::get_supported_events()}

1000       * @param array Associative array of parameters for the Plugin

1001       * @return mixed NULL if no Plugin returned something or the return value of the first Plugin

1002       */
1003  	function get_trigger_event_first_return( $event, $params = NULL )
1004      {
1005          $r = $this->trigger_event_first_return( $event, $params );
1006  
1007          if( ! isset($r['plugin_return']) )
1008          {
1009              return NULL;
1010          }
1011  
1012          return $r['plugin_return'];
1013      }
1014  
1015  
1016      /**

1017       * Trigger an event and return an array of all return values of the

1018       * relevant plugins.

1019       *

1020       * @param string Event name, see {@link Plugins_admin::get_supported_events()}

1021       * @param array Associative array of parameters for the Plugin

1022       * @param boolean Ignore {@link empty() empty} return values?

1023       * @return array List of return values, indexed by Plugin ID

1024       */
1025  	function trigger_collect( $event, $params = NULL, $ignore_empty = true )
1026      {
1027          if( empty($this->index_event_IDs[$event]) )
1028          {
1029              return array();
1030          }
1031  
1032          $r = array();
1033          foreach( $this->index_event_IDs[$event] as $p_ID )
1034          {
1035              $sub_r = $this->call_method_if_active( $p_ID, $event, $params );
1036  
1037              if( $ignore_empty && empty($sub_r) )
1038              {
1039                  continue;
1040              }
1041  
1042              $r[$p_ID] = $sub_r;
1043          }
1044  
1045          return $r;
1046      }
1047  
1048  
1049      /**

1050       * Trigger a karma collecting event in order to get Karma percentage.

1051       *

1052       * @param string Event

1053       * @param array Params to the event

1054       * @return integer|NULL Spam Karma (-100 - 100); "100" means "absolutely spam"; NULL if no plugin gave us a karma value

1055       */
1056  	function trigger_karma_collect( $event, $params )
1057      {
1058          global $Debuglog;
1059  
1060          $karma_abs = NULL;
1061          $karma_divider = 0; // total of the "spam detection relevance weight"

1062  
1063          $Debuglog->add( 'Trigger karma collect event '.$event, 'plugins' );
1064  
1065          if( empty($this->index_event_IDs[$event]) )
1066          { // No events registered
1067              $Debuglog->add( 'No registered plugins.', 'plugins' );
1068              return NULL;
1069          }
1070  
1071          $this->load_plugins_table(); // We need index_ID_rows below

1072  
1073          $Debuglog->add( 'Registered plugin IDs: '.implode( ', ', $this->index_event_IDs[$event]), 'plugins' );
1074  
1075          $count_plugins = 0;
1076          foreach( $this->index_event_IDs[$event] as $l_plugin_ID )
1077          {
1078              $plugin_weight = $this->index_ID_rows[$l_plugin_ID]['plug_spam_weight'];
1079  
1080              if( $plugin_weight < 1 )
1081              {
1082                  $Debuglog->add( 'Skipping plugin #'.$l_plugin_ID.', because is has weight '.$plugin_weight.'.', 'plugins' );
1083                  continue;
1084              }
1085  
1086              $params['cur_karma'] = ( $karma_divider ? round($karma_abs / $karma_divider) : NULL );
1087              $params['cur_karma_abs'] = $karma_abs;
1088              $params['cur_karma_divider'] = $karma_divider;
1089              $params['cur_count_plugins'] = $count_plugins;
1090  
1091              // Call the plugin:

1092              $plugin_karma = $this->call_method( $l_plugin_ID, $event, $params );
1093  
1094              if( ! is_numeric( $plugin_karma ) )
1095              {
1096                  continue;
1097              }
1098  
1099              $count_plugins++;
1100  
1101              if( $plugin_karma > 100 )
1102              {
1103                  $plugin_karma = 100;
1104              }
1105              elseif( $plugin_karma < -100 )
1106              {
1107                  $plugin_karma = -100;
1108              }
1109  
1110              $karma_abs += ( $plugin_karma * $plugin_weight );
1111              $karma_divider += $plugin_weight;
1112  
1113              if( ! empty($this->_stop_propagation) )
1114              {
1115                  $this->_stop_propagation = false;
1116                  break;
1117              }
1118          }
1119  
1120          if( ! $karma_divider )
1121          {
1122              return NULL;
1123          }
1124  
1125          $karma = round($karma_abs / $karma_divider);
1126  
1127          if( $karma > 100 )
1128          {
1129              $karma = 100;
1130          }
1131          elseif( $karma < -100 )
1132          {
1133              $karma = -100;
1134          }
1135  
1136          return $karma;
1137      }
1138  
1139  
1140      /**

1141       * Call a method on a Plugin.

1142       *

1143       * This makes sure that the Timer for the Plugin gets resumed.

1144       *

1145       * @param integer Plugin ID

1146       * @param string Method name.

1147       * @param array Params (by reference).

1148       * @return NULL|mixed Return value of the plugin's method call or NULL if no such method.

1149       */
1150  	function call_method( $plugin_ID, $method, & $params )
1151      {
1152          global $Timer, $debug, $Debuglog;
1153  
1154          $Plugin = & $this->get_by_ID( $plugin_ID );
1155  
1156          if( ! method_exists( $Plugin, $method ) )
1157          {
1158              return NULL;
1159          }
1160  
1161          if( $debug )
1162          {
1163              /*

1164              // Note: this is commented out, because $debug_params gets not dumped anymore (last line of this block)

1165              // Hide passwords from Debuglog!

1166              // Clone/copy (references!):

1167              $debug_params = array();

1168              foreach( $params as $k => $v )

1169              {

1170                  $debug_params[$k] = $v;

1171              }

1172              if( isset($debug_params['pass']) )

1173              {

1174                  $debug_params['pass'] = '-hidden-';

1175              }

1176              if( isset($debug_params['pass_md5']) )

1177              {

1178                  $debug_params['pass_md5'] = '-hidden-';

1179              }

1180              $Debuglog->add( 'Calling '.$Plugin->classname.'(#'.$Plugin->ID.')->'.$method.'( '.htmlspecialchars(var_export( $debug_params, true )).' )', 'plugins' );

1181              */
1182              $Debuglog->add( 'Calling '.$Plugin->classname.'(#'.$Plugin->ID.')->'.$method.'( )', 'plugins' );
1183          }
1184  
1185          $Timer->resume( $Plugin->classname.'_(#'.$Plugin->ID.')' );
1186          $r = $Plugin->$method( $params );
1187          $Timer->pause( $Plugin->classname.'_(#'.$Plugin->ID.')' );
1188  
1189          return $r;
1190      }
1191  
1192  
1193      /**

1194       * Call a method on a Plugin if it is not deactivated.

1195       *

1196       * This is a wrapper around {@link call_method()}.

1197       *

1198       * fp> why doesn't call_method always check if it's deactivated?

1199       *

1200       * @param integer Plugin ID

1201       * @param string Method name.

1202       * @param array Params (by reference).

1203       * @return NULL|mixed Return value of the plugin's method call or NULL if no such method (or inactive).

1204       */
1205  	function call_method_if_active( $plugin_ID, $method, & $params )
1206      {
1207          if( ! $this->has_event($plugin_ID, $method) )
1208          {
1209              return NULL;
1210          }
1211  
1212          return $this->call_method( $plugin_ID, $method, $params );
1213      }
1214  
1215  
1216      /**

1217       * Call a specific plugin by its code.

1218       *

1219       * This will call the SkinTag event handler.

1220       *

1221       * @param string plugin code

1222       * @param array Associative array of parameters (gets passed to the plugin)

1223       * @return boolean

1224       */
1225  	function call_by_code( $code, $params = array() )
1226      {
1227          $Plugin = & $this->get_by_code( $code );
1228  
1229          if( ! $Plugin )
1230          {
1231              global $Debuglog;
1232              $Debuglog->add( 'No plugin available for code ['.$code.']!', array('plugins', 'error') );
1233              return false;
1234          }
1235  
1236          $this->call_method_if_active( $Plugin->ID, 'SkinTag', $params );
1237  
1238          return true;
1239      }
1240  
1241  
1242      /**

1243       * Render the content of an item by calling the relevant renderer plugins.

1244       *

1245       * @param string content to render (by reference)

1246       * @param array renderer codes to use for opt-out, opt-in and lazy

1247       * @param string Output format, see {@link format_to_output()}. Only 'htmlbody',

1248       *        'entityencoded', 'xml' and 'text' are supported.

1249       * @param array Additional params to the Render* methods (e.g. "Item" for items).

1250       *              Do not use "data" or "format" here, because it gets used internally.

1251       * @return string rendered content

1252       */
1253  	function render( & $content, $renderers, $format, $params, $event_prefix = 'Render' )
1254      {
1255          // echo implode(',',$renderers);

1256  
1257          $params['data'] = & $content;
1258          $params['format'] = $format;
1259  
1260          if( $format == 'htmlbody' || $format == 'entityencoded' )
1261          {
1262              $event = $event_prefix.'ItemAsHtml'; // 'RenderItemAsHtml'/'DisplayItemAsHtml'

1263          }
1264          elseif( $format == 'xml' )
1265          {
1266              $event = $event_prefix.'ItemAsXml'; // 'RenderItemAsXml'/'DisplayItemAsXml'

1267          }
1268          elseif( $format == 'text' )
1269          {
1270              $event = $event_prefix.'ItemAsText'; // 'RenderItemAsText'/'DisplayItemAsText'

1271          }
1272          else debug_die( 'Unexpected format in Plugins::render(): '.var_export($format, true) );
1273  
1274          $renderer_Plugins = $this->get_list_by_event( $event );
1275  
1276          foreach( $renderer_Plugins as $loop_RendererPlugin )
1277          { // Go through whole list of renders
1278              // echo ' ',$loop_RendererPlugin->code, ':';

1279  
1280              switch( $loop_RendererPlugin->apply_rendering )
1281              {
1282                  case 'stealth':
1283                  case 'always':
1284                      // echo 'FORCED ';

1285                      $this->call_method( $loop_RendererPlugin->ID, $event, $params );
1286                      break;
1287  
1288                  case 'opt-out':
1289                  case 'opt-in':
1290                  case 'lazy':
1291                      if( in_array( $loop_RendererPlugin->code, $renderers ) )
1292                      { // Option is activated
1293                          // echo 'OPT ';

1294                          $this->call_method( $loop_RendererPlugin->ID, $event, $params );
1295                      }
1296                      // else echo 'NOOPT ';

1297                      break;
1298  
1299                  case 'never':
1300                      // echo 'NEVER ';

1301                      break;    // STOP, don't render, go to next renderer

1302              }
1303          }
1304  
1305          return $content;
1306      }
1307  
1308  
1309      /**

1310       * Quick-render a string with a single plugin and format it for output.

1311       *

1312       * @todo rename

1313       *

1314       * @param string Plugin code (must have render() method)

1315       * @param array

1316       *   'data': Data to render

1317       *   'format: format to output, see {@link format_to_output()}

1318       * @return string Rendered string

1319       */
1320  	function quick( $plugin_code, $params )
1321      {
1322          global $Debuglog;
1323  
1324          if( !is_array($params) )
1325          {
1326              $params = array( 'format' => 'htmlbody', 'data' => $params );
1327          }
1328          else
1329          {
1330              $params = $params; // copy

1331          }
1332  
1333          $Plugin = & $this->get_by_code( $plugin_code );
1334          if( $Plugin )
1335          {
1336              // Get the most appropriate handler:

1337              $events = $this->get_enabled_events( $Plugin->ID );
1338              $event = false;
1339              if( $params['format'] == 'htmlbody' || $params['format'] == 'htmlentityencoded' )
1340              {
1341                  if( in_array( 'RenderItemAsHtml', $events ) )
1342                  {
1343                      $event = 'RenderItemAsHtml';
1344                  }
1345              }
1346              elseif( $params['format'] == 'xml' )
1347              {
1348                  if( in_array( 'RenderItemAsXml', $events ) )
1349                  {
1350                      $event = 'RenderItemAsXml';
1351                  }
1352              }
1353  
1354              if( $event )
1355              {
1356                  $this->call_method( $Plugin->ID, $event, $params );
1357              }
1358              else
1359              {
1360                  $Debuglog->add( $Plugin->classname.'(ID '.$Plugin->ID.'): failed to quick-render (tried method '.$event.')!', array( 'plugins', 'error' ) );
1361              }
1362              return format_to_output( $params['data'], $params['format'] );
1363          }
1364          else
1365          {
1366              $Debuglog->add( 'Plugins::quick() - failed to instantiate Plugin by code ['.$plugin_code.']!', array( 'plugins', 'error' ) );
1367              return format_to_output( $params['data'], $params['format'] );
1368          }
1369      }
1370  
1371  
1372      /**

1373       * Load Plugins data from T_plugins (only once), ordered by priority.

1374       *

1375       * This fills the needed indexes to lazy-instantiate a Plugin when requested.

1376       */
1377  	function load_plugins_table()
1378      {
1379          if( $this->loaded_plugins_table )
1380          {
1381              return;
1382          }
1383          global $Debuglog, $DB;
1384  
1385          $Debuglog->add( 'Loading plugins table data.', 'plugins' );
1386  
1387          $this->index_ID_rows = array();
1388          $this->index_code_ID = array();
1389          $this->index_apply_rendering_codes = array();
1390          $this->sorted_IDs = array();
1391  
1392          foreach( $DB->get_results( $this->sql_load_plugins_table, ARRAY_A ) as $row )
1393          { // Loop through installed plugins:
1394              $this->index_ID_rows[$row['plug_ID']] = $row; // remember the rows to instantiate the Plugin on request

1395              if( ! empty( $row['plug_code'] ) )
1396              {
1397                  $this->index_code_ID[$row['plug_code']] = $row['plug_ID'];
1398              }
1399              $this->index_apply_rendering_codes[$row['plug_apply_rendering']][] = $row['plug_code'];
1400  
1401              $this->sorted_IDs[] = $row['plug_ID'];
1402          }
1403  
1404          $this->loaded_plugins_table = true;
1405      }
1406  
1407  
1408      /**

1409       * Get a specific plugin by its ID.

1410       *

1411       * This is the workhorse when it comes to lazy-instantiating a Plugin.

1412       *

1413       * @param integer plugin ID

1414       * @return Plugin (false in case of error)

1415       */
1416      function & get_by_ID( $plugin_ID )
1417      {
1418          global $Debuglog;
1419  
1420          if( ! isset($this->index_ID_Plugins[ $plugin_ID ]) )
1421          { // Plugin is not instantiated yet
1422              $Debuglog->add( 'get_by_ID(): Instantiate Plugin (ID '.$plugin_ID.').', 'plugins' );
1423  
1424              $this->load_plugins_table();
1425  
1426              #pre_dump( 'get_by_ID(), index_ID_rows', $this->index_ID_rows );

1427  
1428              if( ! isset( $this->index_ID_rows[$plugin_ID] ) || ! $this->index_ID_rows[$plugin_ID] )
1429              { // no plugin rows cached
1430                  #debug_die( 'Cannot instantiate Plugin (ID '.$plugin_ID.') without DB information.' );

1431                  $Debuglog->add( 'get_by_ID(): Plugin (ID '.$plugin_ID.') not registered/enabled in DB!', array( 'plugins', 'error' ) );
1432                  $r = false;
1433                  return $r;
1434              }
1435  
1436              $row = & $this->index_ID_rows[$plugin_ID];
1437  
1438              // Register the plugin:

1439              $Plugin = & $this->register( $row['plug_classname'], $row['plug_ID'], $row['plug_priority'], $row['plug_apply_rendering'] );
1440  
1441              if( is_string( $Plugin ) )
1442              {
1443                  $Debuglog->add( 'Requested plugin [#'.$plugin_ID.'] not found!', 'plugins' );
1444                  $r = false;
1445                  return $r;
1446              }
1447  
1448              $this->index_ID_Plugins[ $plugin_ID ] = & $Plugin;
1449          }
1450  
1451          return $this->index_ID_Plugins[ $plugin_ID ];
1452      }
1453  
1454  
1455      /**

1456       * Get a plugin by its classname.

1457       *

1458       * @param string

1459       * @return Plugin (false in case of error)

1460       */
1461      function & get_by_classname( $classname )
1462      {
1463          $this->load_plugins_table(); // We use index_ID_rows (no own index yet)

1464  
1465          foreach( $this->index_ID_rows as $plug_ID => $row )
1466          {
1467              if( $row['plug_classname'] == $classname )
1468              {
1469                  return $this->get_by_ID($plug_ID);
1470              }
1471          }
1472  
1473          $r = false;
1474          return $r;
1475      }
1476  
1477  
1478      /**

1479       * Get a specific Plugin by its code.

1480       *

1481       * @param string plugin code

1482       * @return Plugin (false in case of error)

1483       */
1484      function & get_by_code( $plugin_code )
1485      {
1486          global $Debuglog;
1487  
1488          $r = false;
1489  
1490          if( ! isset($this->index_code_Plugins[ $plugin_code ]) )
1491          { // Plugin is not registered yet
1492              $this->load_plugins_table();
1493  
1494              if( ! isset($this->index_code_ID[ $plugin_code ]) )
1495              {
1496                  $Debuglog->add( 'Requested plugin ['.$plugin_code.'] is not registered/enabled!', 'plugins' );
1497                  return $r;
1498              }
1499  
1500              if( ! $this->get_by_ID( $this->index_code_ID[$plugin_code] ) )
1501              {
1502                  $Debuglog->add( 'Requested plugin ['.$plugin_code.'] could not get instantiated!', 'plugins' );
1503                  return $r;
1504              }
1505          }
1506  
1507          return $this->index_code_Plugins[ $plugin_code ];
1508      }
1509  
1510  
1511      /**

1512       * Get a list of Plugins for a given event.

1513       *

1514       * @param string Event name

1515       * @return array plugin_ID => & Plugin

1516       */
1517  	function get_list_by_event( $event )
1518      {
1519          $r = array();
1520  
1521          if( isset($this->index_event_IDs[$event]) )
1522          {
1523              foreach( $this->index_event_IDs[$event] as $l_plugin_ID )
1524              {
1525                  if( $Plugin = & $this->get_by_ID( $l_plugin_ID ) )
1526                  {
1527                      $r[ $l_plugin_ID ] = & $Plugin;
1528                      unset($Plugin); // so that we do not overwrite the reference in the next loop

1529                  }
1530              }
1531          }
1532  
1533          return $r;
1534      }
1535  
1536  
1537      /**

1538       * Get a list of Plugins for a list of events. Every Plugin is only once in this list.

1539       *

1540       * @param array Array of events

1541       * @return array plugin_ID => & Plugin

1542       */
1543  	function get_list_by_events( $events )
1544      {
1545          $r = array();
1546  
1547          foreach( $events as $l_event )
1548          {
1549              foreach( array_keys($this->get_list_by_event( $l_event )) as $l_plugin_ID )
1550              {
1551                  if( $Plugin = & $this->get_by_ID( $l_plugin_ID ) )
1552                  {
1553                      $r[ $l_plugin_ID ] = & $Plugin;
1554                      unset($Plugin); // so that we do not overwrite the reference in the next loop

1555                  }
1556              }
1557          }
1558  
1559          return $r;
1560      }
1561  
1562  
1563      /**

1564       * Get a list of plugins that provide all given events.

1565       *

1566       * @return array plugin_ID => & Plugin

1567       */
1568  	function get_list_by_all_events( $events )
1569      {
1570          $candidates = array();
1571  
1572          foreach( $events as $l_event )
1573          {
1574              if( empty($this->index_event_IDs[$l_event]) )
1575              {
1576                  return array();
1577              }
1578  
1579              if( empty($candidates) )
1580              {
1581                  $candidates = $this->index_event_IDs[$l_event];
1582              }
1583              else
1584              {
1585                  $candidates = array_intersect( $candidates, $this->index_event_IDs[$l_event] );
1586                  if( empty($candidates) )
1587                  {
1588                      return array();
1589                  }
1590              }
1591          }
1592  
1593          $r = array();
1594          foreach( $candidates as $plugin_ID )
1595          {
1596              $Plugin = & $this->get_by_ID( $plugin_ID );
1597              if( $Plugin )
1598              {
1599                  $r[ $plugin_ID ] = & $Plugin;
1600                  unset($Plugin); // so that we do not overwrite the reference in the next loop

1601              }
1602          }
1603  
1604          return $r;
1605      }
1606  
1607  
1608      /**

1609       * Get a list of (enabled) events for a given Plugin ID.

1610       *

1611       * @param integer Plugin ID

1612       * @return array

1613       */
1614  	function get_enabled_events( $plugin_ID )
1615      {
1616          $r = array();
1617          foreach( $this->index_event_IDs as $l_event => $l_plugin_IDs )
1618          {
1619              if( in_array( $plugin_ID, $l_plugin_IDs ) )
1620              {
1621                  $r[] = $l_event;
1622              }
1623          }
1624          return $r;
1625      }
1626  
1627  
1628      /**

1629       * Has a plugin a specific event registered/enabled?

1630       *

1631       * @todo fp> The plugin should discover its events itself / This question should be asked to the Plugin itself.

1632       *

1633       * @param integer

1634       * @param string

1635       * @return boolean

1636       */
1637  	function has_event( $plugin_ID, $event )
1638      {
1639          return isset($this->index_event_IDs[$event])
1640              && in_array( $plugin_ID, $this->index_event_IDs[$event] );
1641      }
1642  
1643  
1644      /**

1645       * Check if the requested list of events is provided by any or one plugin.

1646       *

1647       * @param array|string A single event or a list thereof

1648       * @param boolean Make sure there's at least one plugin that provides them all?

1649       *                This is useful for event pairs like "CaptchaPayload" and "CaptchaValidated", which

1650       *                should be served by the same plugin.

1651       * @return boolean

1652       */
1653  	function are_events_available( $events, $require_all_in_same_plugin = false )
1654      {
1655          if( ! is_array($events) )
1656          {
1657              $events = array($events);
1658          }
1659  
1660          if( $require_all_in_same_plugin )
1661          {
1662              return (bool)$this->get_list_by_all_events( $events );
1663          }
1664  
1665          return (bool)$this->get_list_by_events( $events );
1666      }
1667  
1668  
1669      /**

1670       * (Re)load Plugin Events for enabled (normal use) or all (admin use) plugins.

1671       */
1672  	function load_events()
1673      {
1674          global $Debuglog, $DB;
1675  
1676          $this->index_event_IDs = array();
1677  
1678          $Debuglog->add( 'Loading plugin events.', 'plugins' );
1679          foreach( $DB->get_results( '
1680                  SELECT pevt_plug_ID, pevt_event
1681                      FROM T_pluginevents INNER JOIN T_plugins ON pevt_plug_ID = plug_ID
1682                   WHERE pevt_enabled > 0
1683                     AND plug_status = \'enabled\'
1684                   ORDER BY plug_priority, plug_classname', OBJECT, 'Loading plugin events' ) as $l_row )
1685          {
1686              $this->index_event_IDs[$l_row->pevt_event][] = $l_row->pevt_plug_ID;
1687          }
1688      }
1689  
1690  
1691      /**

1692       * Load an object from a Cache plugin or create a new one if we have a

1693       * cache miss or no caching plugins.

1694       *

1695       * It registers a shutdown function, that refreshes the data to the cache plugin

1696       * which is not optimal, but we have no hook to see if data retrieved from

1697       * a {@link DataObjectCache} derived class has changed.

1698       * @param string object name

1699       * @param string eval this to create the object. Default is to create an object

1700       *               of class $objectName.

1701       * @return boolean True, if retrieved from cache; false if not

1702       */
1703  	function get_object_from_cacheplugin_or_create( $objectName, $eval_create_object = NULL )
1704      {
1705          $get_return = $this->trigger_event_first_true( 'CacheObjects',
1706              array( 'action' => 'get', 'key' => 'object_'.$objectName ) );
1707  
1708          if( isset( $get_return['plugin_ID'] ) )
1709          {
1710              if( is_object($get_return['data']) )
1711              {
1712                  $GLOBALS[$objectName] = $get_return['data']; // COPY! (get_Cache() uses $$objectName instead of $GLOBALS - no deal for PHP5 anyway)

1713  
1714                  $Plugin = & $this->get_by_ID( $get_return['plugin_ID'] );
1715                  register_shutdown_function( array(&$Plugin, 'CacheObjects'),
1716                      array( 'action' => 'set', 'key' => 'object_'.$objectName, 'data' => & $GLOBALS[$objectName] ) );
1717  
1718                  return true;
1719              }
1720          }
1721  
1722          // Cache miss, create it:

1723          if( empty($eval_create_object) )
1724          {
1725              $GLOBALS[$objectName] = new $objectName(); // COPY (FUNC)

1726          }
1727          else
1728          {
1729              eval( '$GLOBALS[\''.$objectName.'\'] = '.$eval_create_object.';' );
1730          }
1731  
1732          // Try to set in cache:

1733          $set_return = $this->trigger_event_first_true( 'CacheObjects',
1734              array( 'action' => 'set', 'key' => 'object_'.$objectName, 'data' => & $GLOBALS[$objectName] ) );
1735  
1736          if( isset( $set_return['plugin_ID'] ) )
1737          { // success, register a shutdown function to save this data on shutdown
1738              $Plugin = & $this->get_by_ID( $set_return['plugin_ID'] );
1739              register_shutdown_function( array(&$Plugin, 'CacheObjects'),
1740                  array( 'action' => 'set', 'key' => 'object_'.$objectName, 'data' => & $GLOBALS[$objectName] ) );
1741          }
1742  
1743          return false;
1744      }
1745  
1746  
1747      /**

1748       * Callback, which gets used for {@link Results}.

1749       *

1750       * @return Plugin (false in case of error)

1751       */
1752      function & instantiate( $row )
1753      {
1754          return $this->get_by_ID( $row->plug_ID );
1755      }
1756  
1757  
1758      // Deprecated stubs: {{{

1759  
1760      /**

1761       * @deprecated since EVO_NEXT_VERSION by Plugins_admin::count_regs()

1762       */
1763  	function count_regs( $classname )
1764      {
1765          global $Debuglog;
1766          $Debuglog->add('Call to deprecated method Plugins::count_regs()', 'deprecated');
1767          $Plugins_admin = & get_Cache('Plugins_admin');
1768          return $Plugins_admin->count_regs($classname);
1769      }
1770  
1771  
1772      /**

1773       * Set the apply_rendering value for a given Plugin ID.

1774       *

1775       * It makes sure that the index is handled and writes it to DB.

1776       *

1777       * @deprecated since EVO_NEXT_VERSION by Plugins_admin::set_apply_rendering()

1778       * @return boolean true if set to new value, false in case of error or if already set to same value

1779       */
1780  	function set_apply_rendering( $plugin_ID, $apply_rendering )
1781      {
1782          $Plugins_admin = & get_Cache('Plugins_admin');
1783          return $Plugins_admin->set_apply_rendering($plugin_ID, $apply_rendering);
1784      }
1785  
1786  
1787      /**

1788       * Validate renderer list.

1789       *

1790       * @deprecated since EVO_NEXT_VERSION by Plugins_admin::validate_renderer_list()

1791       * @param array renderer codes ('default' will include all "opt-out"-ones)

1792       * @return array validated array of renderer codes

1793       */
1794  	function validate_list( $renderers = array('default') )
1795      {
1796          global $Debuglog;
1797          $Debuglog->add('Call to deprecated method Plugins::validate_list()', 'deprecated');
1798          $Plugins_admin = & get_Cache('Plugins_admin');
1799          return $Plugins_admin->validate_renderer_list($renderers);
1800      }
1801  
1802  
1803      // }}}

1804  }
1805  
1806  
1807  /*

1808   * $Log: _plugins.class.php,v $

1809   * Revision 1.2  2007/09/22 22:11:18  fplanque

1810   * minor

1811   *

1812   * Revision 1.1  2007/06/25 11:00:45  fplanque

1813   * MODULES (refactored MVC)

1814   *

1815   * Revision 1.155  2007/06/19 23:15:08  blueyed

1816   * doc fixes

1817   *

1818   * Revision 1.154  2007/06/19 00:03:26  fplanque

1819   * doc / trying to make sense of automatic settings forms generation.

1820   *

1821   * Revision 1.153  2007/05/26 19:01:29  blueyed

1822   * Use has_event() in call_method_if_active()

1823   *

1824   * Revision 1.152  2007/05/14 02:43:05  fplanque

1825   * Started renaming tables. There probably won't be a better time than 2.0.

1826   *

1827   * Revision 1.151  2007/04/26 00:11:08  fplanque

1828   * (c) 2007

1829   *

1830   * Revision 1.150  2007/03/26 21:34:59  blueyed

1831   * Removed $Plugin->Plugins reference

1832   *

1833   * Revision 1.149  2007/02/23 00:21:23  blueyed

1834   * Fixed Plugins::get_next() if the last Plugin got unregistered; Added AdminBeforeItemEditDelete hook

1835   *

1836   * Revision 1.148  2007/02/18 20:50:42  blueyed

1837   * Fixed possible E_NOTICE when a plugin got unregistered

1838   *

1839   * Revision 1.147  2007/02/16 13:30:38  waltercruz

1840   * Changing double quotes to single quotes

1841   *

1842   * Revision 1.146  2007/02/12 15:42:59  fplanque

1843   * no message

1844   *

1845   * Revision 1.145  2007/02/10 18:39:54  blueyed

1846   * Fix: update "plug_version" in indices, so PluginVersionChanged does not get called twice (in Plugins_admin too)

1847   *

1848   * Revision 1.144  2007/02/06 14:33:21  waltercruz

1849   * Changing double quotes to single quotes

1850   *

1851   * Revision 1.143  2007/02/06 14:26:20  blueyed

1852   * MFB: do not pass $renderers by reference to Plugins::render()

1853   *

1854   * Revision 1.142  2007/02/06 00:08:56  waltercruz

1855   * Changing double quotes to single quotes

1856   *

1857   * Revision 1.141  2007/02/05 22:37:06  blueyed

1858   * doc

1859   *

1860   * Revision 1.140  2007/02/03 19:00:31  fplanque

1861   * doc

1862   *

1863   * Revision 1.139  2007/01/30 19:52:48  blueyed

1864   * Only deactivate a Plugin if PluginVersionChanged returns === false; fixes the basic_antispam_plugin, which returned NULL

1865   *

1866   * Revision 1.138  2007/01/29 21:33:52  blueyed

1867   * Fixed login with JS-hashing disabled and debugging turned on (PHP5)

1868   *

1869   * Revision 1.137  2007/01/25 23:48:18  blueyed

1870   * Fixed notice if Plugin got unregistered, e.g. because of DB schema change during loading; always pass array() if calling a Plugin method as $params

1871   *

1872   * Revision 1.136  2007/01/18 00:23:57  blueyed

1873   * doc

1874   *

1875   * Revision 1.135  2007/01/14 19:37:24  blueyed

1876   * Fix for overloading problems of (User)Settings in PHP 5.0.5

1877   *

1878   * Revision 1.134  2007/01/14 18:15:51  blueyed

1879   * Nuked hackish $is_admin_class as per todo

1880   *

1881   * Revision 1.133  2007/01/14 08:05:03  blueyed

1882   * Started to remove $is_admin_class in Plugins::register()

1883   *

1884   * Revision 1.132  2007/01/13 14:57:28  blueyed

1885   * Removed $is_admin_class hack from load_events() by re-implementing (copying most of it) to Plugins_admin as per todo

1886   *

1887   * Revision 1.131  2007/01/13 04:09:40  fplanque

1888   * doc

1889   *

1890   * Revision 1.130  2007/01/12 22:05:28  blueyed

1891   * Real fix for Plugins::get_list_by_* (keeping and returning reference instead of copy)

1892   *

1893   * Revision 1.129  2007/01/12 21:53:12  blueyed

1894   * Probably fixed Plugins::get_list_by_* methods: the returned references were always the one to the last Plugin

1895   *

1896   * Revision 1.128  2007/01/12 21:11:52  blueyed

1897   * doc fixed: it is important to know what gets returned in case of error

1898   *

1899   * Revision 1.127  2007/01/12 05:14:42  fplanque

1900   * doc

1901   *

1902   * Revision 1.126  2007/01/07 05:26:01  fplanque

1903   * doc

1904   *

1905   * Revision 1.125  2006/12/07 23:13:13  fplanque

1906   * @var needs to have only one argument: the variable type

1907   * Otherwise, I can't code!

1908   *

1909   * Revision 1.124  2006/12/05 00:23:55  blueyed

1910   * Return value for get_object_from_cacheplugin_or_create()

1911   *

1912   * Revision 1.123  2006/12/04 22:27:19  blueyed

1913   * Also call init_settings() on not installed Plugins, because it allows using $Settings/$UserSettings in Plugin::PluginInit()

1914   *

1915   * Revision 1.122  2006/12/01 20:51:27  blueyed

1916   * doc

1917   *

1918   * Revision 1.121  2006/12/01 20:46:25  blueyed

1919   * Moved Plugins::set_priority() to Plugins_admin class

1920   *

1921   * Revision 1.120  2006/12/01 20:44:01  blueyed

1922   * Moved Plugins::set_code() to Plugins_admin class

1923   *

1924   * Revision 1.119  2006/12/01 20:41:38  blueyed

1925   * Moved Plugins::uninstall() to Plugins_admin class

1926   *

1927   * Revision 1.118  2006/12/01 20:34:03  blueyed

1928   * Moved Plugins::get_apply_rendering_values() and Plugins::set_apply_rendering() to Plugins_admin class

1929   *

1930   * Revision 1.117  2006/12/01 20:19:15  blueyed

1931   * Moved Plugins::get_supported_events() to Plugins_admin class

1932   *

1933   * Revision 1.116  2006/12/01 20:13:23  blueyed

1934   * Moved Plugins::count_regs() to Plugins_admin class

1935   *

1936   * Revision 1.115  2006/12/01 20:04:31  blueyed

1937   * Renamed Plugins_admin::validate_list() to validate_renderer_list()

1938   *

1939   * Revision 1.114  2006/12/01 20:01:38  blueyed

1940   * Moved Plugins::validate_dependencies() to Plugins_admin class

1941   *

1942   * Revision 1.113  2006/12/01 19:46:42  blueyed

1943   * Moved Plugins::validate_list() to Plugins_admin class; added stub in Plugins, because at least the starrating_plugin uses it

1944   *

1945   * Revision 1.112  2006/12/01 19:16:00  blueyed

1946   * Moved Plugins::get_registered_events() to Plugins_admin class

1947   *

1948   * Revision 1.111  2006/12/01 18:18:21  blueyed

1949   * Moved Plugins::save_events() to Plugins_admin class

1950   *

1951   * Revision 1.110  2006/12/01 16:26:34  blueyed

1952   * Added AdminDisplayCommentFormFieldset hook

1953   *

1954   * Revision 1.109  2006/12/01 02:03:04  blueyed

1955   * Moved Plugins::set_event_status() to Plugins_admin

1956   *

1957   * Revision 1.108  2006/11/30 05:57:54  blueyed

1958   * Moved Plugins::install() and sort() galore to Plugins_admin

1959   *

1960   * Revision 1.107  2006/11/30 05:43:40  blueyed

1961   * Moved Plugins::discover() to Plugins_admin::discover(); Renamed Plugins_no_DB to Plugins_admin_no_DB (and deriving from Plugins_admin)

1962   *

1963   * Revision 1.106  2006/11/30 05:10:16  blueyed

1964   * Marked a bunch of methods to be moved to PLugins_admin

1965   *

1966   * Revision 1.105  2006/11/30 04:32:23  blueyed

1967   * Minor change in Plugins::discover(), before moving it

1968   *

1969   * Revision 1.104  2006/11/30 00:30:33  blueyed

1970   * Some minor memory optimizations regarding "Plugins" screen

1971   *

1972   * Revision 1.103  2006/11/24 18:27:27  blueyed

1973   * Fixed link to b2evo CVS browsing interface in file docblocks

1974   *

1975   * Revision 1.102  2006/11/14 00:47:32  fplanque

1976   * doc

1977   *

1978   * Revision 1.101  2006/11/14 00:21:05  blueyed

1979   * removed todo

1980   *

1981   * Revision 1.100  2006/11/01 14:59:27  blueyed

1982   * Handle obsoleting pre-rendered item content, if a renderer plugin version changes

1983   *

1984   * Revision 1.99  2006/11/01 14:22:33  blueyed

1985   * Fixed E_NOTICE/doc

1986   *

1987   * Revision 1.98  2006/10/30 19:00:36  blueyed

1988   * Lazy-loading of Plugin (User)Settings for PHP5 through overloading

1989   *

1990   * Revision 1.97  2006/10/29 20:07:34  blueyed

1991   * Added "app_min" plugin dependency; Deprecated "api_min"

1992   *

1993   * Revision 1.96  2006/10/16 08:39:10  blueyed

1994   * Merged fixes from v-1-9 branch

1995   *

1996   * Revision 1.95  2006/10/14 20:50:29  blueyed

1997   * Define EVO_IS_INSTALLING for /install/ and use it in Plugins to skip "dangerous" but unnecessary instantiating of other Plugins

1998   *

1999   * Revision 1.94  2006/10/14 16:27:06  blueyed

2000   * Client-side password hashing in the login form.

2001   *

2002   * Revision 1.93  2006/10/08 22:59:31  blueyed

2003   * Added GetProvidedSkins and DisplaySkin hooks. Allow for optimization in Plugins::trigger_event_first_return()

2004   *

2005   * Revision 1.92  2006/10/05 02:10:26  blueyed

2006   * Do not add empty codes in Plugins::validate_list()

2007   *

2008   * Revision 1.91  2006/10/05 01:06:37  blueyed

2009   * Removed dirty "hack"; added ItemApplyAsRenderer hook instead.

2010   *

2011   * Revision 1.90  2006/10/01 22:11:42  blueyed

2012   * Ping services as plugins.

2013   *

2014   * Revision 1.89  2006/10/01 19:56:36  blueyed

2015   * TODO

2016   *

2017   * Revision 1.88  2006/10/01 15:11:08  blueyed

2018   * Added DisplayItemAs* equivs to RenderItemAs*; removed DisplayItemAllFormats; clearing of pre-rendered cache, according to plugin event changes

2019   *

2020   * Revision 1.87  2006/10/01 00:14:58  blueyed

2021   * plug_classpath should not have get merged already

2022   */
2023  ?>


Généré le : Thu Nov 29 23:58:50 2007 par Balluche grâce à PHPXref 0.7
  Clicky Web Analytics