[ Index ]
 

Code source de Serendipity 1.2

Accédez au Source d'autres logiciels libres

title

Body

[fermer]

/include/ -> plugin_api.inc.php (source)

   1  <?php # $Id: plugin_api.inc.php 1711 2007-06-05 11:44:34Z garvinhicking $
   2  # Copyright (c) 2003-2005, Jannis Hermanns (on behalf the Serendipity Developer Team)
   3  # All rights reserved.  See LICENSE file for licensing details
   4  
   5  if (IN_serendipity !== true) {
   6      die ('Don\'t hack!');
   7  }
   8  
   9  if (defined('S9Y_FRAMEWORK_PLUGIN_API')) {
  10      return;
  11  }
  12  @define('S9Y_FRAMEWORK_PLUGIN_API', true);
  13  
  14  if (!defined('S9Y_FRAMEWORK_FUNCTIONS')) {
  15      include  S9Y_INCLUDE_PATH . 'include/functions.inc.php';
  16  }
  17  
  18  /* This file defines the plugin API for serendipity.
  19   * By extending these classes, you can add your own code
  20   * to appear in the sidebar(s) of serendipity.
  21   *
  22   *
  23   * The system defines a number of built-in plugins; these are
  24   * identified by @class_name.
  25   *
  26   * Third-party plugins are identified by the name of the folder into
  27   * which they were uploaded (so there is no @ sign at the start of
  28   * their class name.
  29   *
  30   * The user creates instances of plugins; an instance is assigned
  31   * an identifier like this:
  32   *   classname:uniqid()
  33   *
  34   * The user can configure instances of plugins.
  35   */
  36  
  37  class serendipity_plugin_api {
  38  
  39  /**
  40   * Register the default list of plugins for installation.
  41   *
  42   * @access public
  43   * @return null
  44   */
  45      function register_default_plugins()
  46      {
  47          /* Register default sidebar plugins, order matters */
  48          serendipity_plugin_api::create_plugin_instance('@serendipity_calendar_plugin');
  49          serendipity_plugin_api::create_plugin_instance('@serendipity_quicksearch_plugin');
  50          serendipity_plugin_api::create_plugin_instance('@serendipity_archives_plugin');
  51          serendipity_plugin_api::create_plugin_instance('@serendipity_categories_plugin');
  52          serendipity_plugin_api::create_plugin_instance('@serendipity_syndication_plugin');
  53          serendipity_plugin_api::create_plugin_instance('@serendipity_superuser_plugin');
  54          serendipity_plugin_api::create_plugin_instance('@serendipity_plug_plugin');
  55  
  56          /* Register default event plugins */
  57          serendipity_plugin_api::create_plugin_instance('serendipity_event_s9ymarkup', null, 'event');
  58          serendipity_plugin_api::create_plugin_instance('serendipity_event_emoticate', null, 'event');
  59          serendipity_plugin_api::create_plugin_instance('serendipity_event_nl2br', null, 'event');
  60          serendipity_plugin_api::create_plugin_instance('serendipity_event_browsercompatibility', null, 'event');
  61          serendipity_plugin_api::create_plugin_instance('serendipity_event_spamblock', null, 'event');
  62  
  63          /* Register additional plugins? */
  64          if (file_exists(S9Y_INCLUDE_PATH . 'plugins/preload.txt')) {
  65              // Expects this format, one plugin per line:
  66              // serendipity_event_xxx:event
  67              // serendipity_plugin_xxx:left
  68              $plugins = file(S9Y_INCLUDE_PATH . 'plugins/preload.txt');
  69              foreach($plugins AS $plugin) {
  70                  $plugin = trim($plugin);
  71                  if (empty($plugin)) {
  72                      continue;
  73                  }
  74  
  75                  $plugin_info = explode(':', $plugin);
  76                  serendipity_plugin_api::create_plugin_instance($plugin_info[0], null, $plugin_info[1]);
  77              }
  78          }
  79      }
  80  
  81  /**
  82   * Create an instance of a plugin.
  83   *
  84   * $plugin_class_id is of the form:
  85   *    @class_name        for a built-in plugin
  86   * or
  87   *    plugin_dir_name    for a third-party plugin
  88   * returns the instance identifier for the newly created plugin.
  89   *
  90   * TO BE IMPLEMENTED:
  91   * If $copy_from_instance is not null, and identifies another plugin
  92   * of the same class, then the persistent state will be copied.
  93   * This allows the user to clone a plugin.
  94   *
  95   * @access  public
  96   * @param   string  classname of the plugin to insert (see description above for details)
  97   * @param   boolean (reserved) variable to indicate a copy of an existing instance
  98   * @param   string  The type of the plugin to insert (event/left/right/hide/eventh)
  99   * @param   int     The authorid of the plugin owner
 100   * @param   string  The source path of the plugin file
 101   * @return  string  ID of the new plugin
 102   */
 103      function create_plugin_instance($plugin_class_id, $copy_from_instance = null, $default_placement = 'right', $authorid = '0', $pluginPath = '')
 104      {
 105          global $serendipity;
 106  
 107          $id = md5(uniqid(''));
 108  
 109          $key = $plugin_class_id . ':' . $id;
 110  
 111          // Secure Plugin path. No leading slashes, no backslashes, no "up" directories
 112          $pluginPath = preg_replace('@^(/)@', '', $pluginPath);
 113          $pluginPath = str_replace(array('..', "\\"), array('', '/'), serendipity_db_escape_string($pluginPath));
 114          
 115          if ($pluginPath == 'online_repository') {
 116              $pluginPath = $key;
 117          }
 118  
 119          $rs = serendipity_db_query("SELECT MAX(sort_order) as sort_order_max FROM {$serendipity['dbPrefix']}plugins WHERE placement = '$default_placement'", true, 'num');
 120  
 121          if (is_array($rs)) {
 122              $nextidx = intval($rs[0]+1);
 123          } else {
 124              $nextidx = 0;
 125          }
 126  
 127          $serendipity['debug']['pluginload'][] = "Installing plugin: " . print_r(func_get_args(), true);
 128  
 129          $iq = "INSERT INTO {$serendipity['dbPrefix']}plugins (name, sort_order, placement, authorid, path) values ('$key', $nextidx, '$default_placement', '$authorid', '$pluginPath')";
 130          $serendipity['debug']['pluginload'][] = $iq;
 131          serendipity_db_query($iq);
 132          serendipity_plugin_api::hook_event('backend_plugins_new_instance', $key, array('default_placement' => $default_placement));
 133  
 134          /* Check for multiple dependencies */
 135          $plugin =& serendipity_plugin_api::load_plugin($key, $authorid, $pluginPath);
 136          if (is_object($plugin)) {
 137              $bag    = new serendipity_property_bag;
 138              $plugin->introspect($bag);
 139              serendipity_plugin_api::get_event_plugins(false, true); // Refresh static list of plugins to allow execution of added plugin
 140              $plugin->register_dependencies(false, $authorid);
 141              $plugin->install();
 142          } else {
 143              $serendipity['debug']['pluginload'][] = "Loading plugin failed painfully. File not found?";
 144              echo ERROR . ': ' . $key . ' (' . $pluginPath . ')<br />';
 145          }
 146  
 147          return $key;
 148      }
 149  
 150  /**
 151   * Removes a plugin by it's instance name
 152   *
 153   * @access public
 154   * @param   string  The name of the plugin id ("serendipity_plugin_xxx:1232132fsdf")
 155   * @return null
 156   */
 157      function remove_plugin_instance($plugin_instance_id)
 158      {
 159          global $serendipity;
 160  
 161          $plugin =& serendipity_plugin_api::load_plugin($plugin_instance_id);
 162          if (is_object($plugin)) {
 163              $bag    = new serendipity_property_bag;
 164              $plugin->introspect($bag);
 165              $plugin->uninstall($bag);
 166          }
 167  
 168          serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}plugins where name='$plugin_instance_id'");
 169  
 170          if (is_object($plugin)) {
 171              $plugin->register_dependencies(true);
 172          }
 173  
 174          serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}config  where name LIKE '$plugin_instance_id/%'");
 175      }
 176  
 177  /**
 178   * Removes an empty plugin configuration value
 179   *
 180   * @access public
 181   * @param   string  The name of the plugin id ("serendipity_plugin_xxx:1232132fsdf")
 182   * @param   array   An array of configuration item names
 183   * @return null
 184   */
 185      function remove_plugin_value($plugin_instance_id, $where)
 186      {
 187          global $serendipity;
 188          $where_sql = array();
 189          foreach($where AS $key) {
 190              $where_sql[] = "(name LIKE '{$plugin_instance_id}/{$key}_%' AND value = '')";
 191          }
 192  
 193          $query = "DELETE FROM  {$serendipity['dbPrefix']}config
 194                                      WHERE  " . implode(' OR ', $where_sql);
 195  
 196          serendipity_db_query($query);
 197      }
 198  
 199  /**
 200   * Retrieve a list of available plugin classes
 201   *
 202   * This function searches through all directories and loaded internal files and tries
 203   * to detect the serendipity plugins.
 204   *
 205   * @access public
 206   * @param   boolean     If true, only event plugins will be searched. If false, sidebar plugins will be searched.
 207   * @return
 208   */
 209      function &enum_plugin_classes($event_only = false)
 210      {
 211          global $serendipity;
 212  
 213          $classes = array();
 214  
 215          /* built-in classes first */
 216          $cls = get_declared_classes();
 217          foreach ($cls as $class_name) {
 218              if (strncmp($class_name, 'serendipity_', 6)) {
 219                  continue;
 220              }
 221  
 222              $p = get_parent_class($class_name);
 223              while ($p != 'serendipity_plugin' && $p != 'serendipity_event' && $p !== false) {
 224                  $p = get_parent_class($p);
 225              }
 226  
 227              if ($p == 'serendipity_plugin' && $class_name != 'serendipity_event' && (!$event_only || is_null($event_only))) {
 228                  $classes[$class_name] = array('name'       => '@' . $class_name,
 229                                                'type'       => 'internal_event',
 230                                                'true_name'  => $class_name,
 231                                                'pluginPath' => '');
 232              } elseif ($p == 'serendipity_event' && $class_name != 'serendipity_event' && ($event_only || is_null($event_only))) {
 233                  $classes[$class_name] = array('name'       => '@' . $class_name,
 234                                                'type'       => 'internal_plugin',
 235                                                'true_name'  => $class_name,
 236                                                'pluginPath' => '');
 237              }
 238          }
 239  
 240          /* GLOBAL third-party classes next */
 241          $ppath = serendipity_getRealDir(__FILE__) . 'plugins';
 242          serendipity_plugin_api::traverse_plugin_dir($ppath, $classes, $event_only);
 243  
 244          /* LOCAL third-party classes next */
 245          $local_ppath = $serendipity['serendipityPath'] . 'plugins';
 246          if ($ppath != $local_ppath) {
 247              serendipity_plugin_api::traverse_plugin_dir($local_ppath, $classes, $event_only);
 248          }
 249  
 250          return $classes;
 251      }
 252  
 253  /**
 254   * Traverse a specific directory and search if a serendipity plugin exists there.
 255   *
 256   * @access public
 257   * @param   string      The path to start from (usually '.')
 258   * @param   array       A referenced array of currently found classes
 259   * @param   boolean     If true, only event plugins will be searched. If false, only sidebar plugins will be searched.
 260   * @param   string      The maindir where we started searching from [for recursive use]
 261   * @return
 262   */
 263      function traverse_plugin_dir($ppath, &$classes, $event_only, $maindir = '') {
 264          $d = @opendir($ppath);
 265          if ($d) {
 266              while (($f = readdir($d)) !== false) {
 267                  if ($f{0} == '.' || $f == 'CVS' || !is_dir($ppath . '/' . $f) || !is_readable($ppath . '/' .$f)) {
 268                      continue;
 269                  }
 270  
 271                  $subd = opendir($ppath . '/' . $f);
 272                  if (!$subd) {
 273                      continue;
 274                  }
 275  
 276                  // Instead of only looking for directories, search for files within subdirectories
 277                  $final_loop = false;
 278                  while (($subf = readdir($subd)) !== false) {
 279  
 280                      if ($subf{0} == '.' || $subf == 'CVS') {
 281                          continue;
 282                      }
 283  
 284                      if (!$final_loop && is_dir($ppath . '/' . $f . '/' . $subf) && $maindir != $ppath . '/' . $f) {
 285                          // Search for another level of subdirectories
 286                          serendipity_plugin_api::traverse_plugin_dir($ppath . '/' . $f, $classes, $event_only, $f . '/');
 287                          // We can break after that operation because the current directory has been fully checked already.
 288                          $final_loop = true;
 289                      }
 290  
 291                      if (!preg_match('@^[^_]+_(event|plugin)_.+\.php$@i', $subf)) {
 292                          continue;
 293                      }
 294  
 295                      $class_name = str_replace('.php', '', $subf);
 296                      // If an external plugin/event already exists as internal, remove the internal reference because its redundant
 297                      if (isset($classes['@' . $class_name])) {
 298                          unset($classes['@' . $class_name]);
 299                      }
 300  
 301                      // A local plugin will be preferred over general plugins [used when calling this function the second time]
 302                      if (isset($classes[$class_name])) {
 303                          unset($classes[$class_name]);
 304                      }
 305  
 306                      if (!is_null($event_only) && $event_only && !serendipity_plugin_api::is_event_plugin($subf)) {
 307                          continue;
 308                      }
 309  
 310                      if (!is_null($event_only) && !$event_only && serendipity_plugin_api::is_event_plugin($subf)) {
 311                          continue;
 312                      }
 313  
 314                      $classes[$class_name] = array('name'       => $class_name,
 315                                                    'true_name'  => $class_name,
 316                                                    'type'       => 'additional_plugin',
 317                                                    'pluginPath' => $maindir . $f);
 318                  }
 319                  closedir($subd);
 320              }
 321              closedir($d);
 322          }
 323      }
 324  
 325  /**
 326   * Returns a list of currently installed plugins
 327   *
 328   * @access public
 329   * @param   string  The filter for plugins (left|right|hide|event|eventh)
 330   * @return  array   The list of plugins
 331   */
 332      function get_installed_plugins($filter = '*') {
 333          $plugins = serendipity_plugin_api::enum_plugins($filter);
 334          $res = array();
 335          foreach ( (array)$plugins as $plugin ) {
 336              list($class_name) = explode(':', $plugin['name']);
 337              if ($class_name{0} == '@') {
 338                  $class_name = substr($class_name, 1);
 339              }
 340              $res[] = $class_name;
 341          }
 342          return $res;
 343      }
 344  
 345  /**
 346   * Searches for installed plugins based on specific conditions
 347   *
 348   * @access public
 349   * @param   string  The filter for plugins (left|right|hide|event|eventh)
 350   * @param   boolean If true, the filtering logic will be reversed an all plugins that are NOT part of the filter will be returned
 351   * @param   string  Filter by a specific classname (like 'serendipity_plugin_archives'). Can take SQL wildcards.
 352   * @param   string  Filter by a specific plugin instance id
 353   * @return  array   Returns the associative array of found plugins in the database
 354   */
 355      function enum_plugins($filter = '*', $negate = false, $classname = null, $id = null)
 356      {
 357          global $serendipity;
 358  
 359          $sql   = "SELECT * from {$serendipity['dbPrefix']}plugins ";
 360          $where = array();
 361  
 362          if ($filter !== '*') {
 363              if ($negate) {
 364                  $where[] = " placement != '" . serendipity_db_escape_string($filter) . "' ";
 365              } else {
 366                  $where[] = " placement =  '" . serendipity_db_escape_string($filter) . "' ";
 367              }
 368          }
 369  
 370          if (!empty($classname)) {
 371              $where[] = " (name LIKE '@" . serendipity_db_escape_string($classname) . "%' OR name LIKE '" . serendipity_db_escape_string($classname) . "%') ";
 372          }
 373  
 374          if (!empty($id)) {
 375              $where[] = " name = '" . serendipity_db_escape_string($id) . "' ";
 376          }
 377  
 378          if (count($where) > 0) {
 379              $sql .= ' WHERE ' . implode(' AND ', $where);
 380          }
 381  
 382          $sql .= ' ORDER BY placement, sort_order';
 383  
 384          return serendipity_db_query($sql);
 385      }
 386  
 387  /**
 388   * Count the number of plugins to which the filter criteria matches
 389   *
 390   * @access public
 391   * @param   string  The filter for plugins (left|right|hide|event|eventh)
 392   * @param   boolean If true, the filtering logic will be reversed an all plugins that are NOT part of the filter will be evaluated
 393   * @return  int     Number of plugins that were found.
 394   */
 395      function count_plugins($filter = '*', $negate = false)
 396      {
 397          global $serendipity;
 398  
 399          // Can be shortcircuited via a $serendipity['prevent_sidebar_plugins_(left|right|event)'] variable!
 400          if (!$negate && $serendipity['prevent_sidebar_plugins_' . $filter] == true) {
 401              return 0;
 402          }
 403  
 404  
 405          $sql = "SELECT COUNT(placement) AS count from {$serendipity['dbPrefix']}plugins ";
 406  
 407          if ($filter !== '*') {
 408              if ($negate) {
 409                  $sql .= "WHERE placement != '$filter' ";
 410              } else {
 411                  $sql .= "WHERE placement='$filter' ";
 412              }
 413          }
 414  
 415          $count = serendipity_db_query($sql, true);
 416          if (is_array($count) && isset($count[0])) {
 417              return (int)$count[0];
 418          }
 419  
 420          return 0;
 421      }
 422  
 423  /**
 424   * Detect the filename to use for a specific plugin
 425   *
 426   * @access public
 427   * @param   string  The name of the plugin ('serendipity_event_archive')
 428   * @param   string  The path to the plugin file (if empty, the current path structure will be used.)
 429   * @param   string  If an instance ID is passed this means, the plugin to be loaded is internally available
 430   * @return  string  Returns the filename to include for a specific plugin
 431   */
 432      function includePlugin($name, $pluginPath = '', $instance_id = '') {
 433          global $serendipity;
 434  
 435          if (empty($pluginPath)) {
 436              $pluginPath = $name;
 437          }
 438  
 439          $file = false;
 440  
 441          // Security constraint
 442          $pluginFile = 'plugins/' . $pluginPath . '/' . $name . '.php';
 443          $pluginFile = preg_replace('@([\r\n\t\0\\\]+|\.\.+)@', '', $pluginFile);
 444  
 445          // First try the local path, and then (if existing) a shared library repository ...
 446          // Internal plugins ignored.
 447          if (!empty($instance_id) && $instance_id{0} == '@') {
 448              $file = S9Y_INCLUDE_PATH . 'include/plugin_internal.inc.php';
 449          } elseif (file_exists($serendipity['serendipityPath'] . $pluginFile)) {
 450              $file = $serendipity['serendipityPath'] . $pluginFile;
 451          } elseif (file_exists(S9Y_INCLUDE_PATH . $pluginFile)) {
 452              $file = S9Y_INCLUDE_PATH . $pluginFile;
 453          }
 454  
 455          return $file;
 456      }
 457  
 458  /**
 459   * Returns the plugin class name by a plugin instance ID
 460   *
 461   * @access public
 462   * @param   string      The ID of a plugin
 463   * @param   boolean     If true, the plugin is a internal plugin (prefixed with '@')
 464   * @return  string      The classname of the plugin
 465   */
 466      function getClassByInstanceID($instance_id, &$is_internal) {
 467          $instance = explode(':', $instance_id);
 468          $name     = $instance[0];
 469  
 470          if ($name{0} == '@') {
 471              $class_name = substr($name, 1);
 472          } else {
 473              $class_name =& $name;
 474          }
 475  
 476          return $class_name;
 477      }
 478  
 479  /**
 480   * Auto-detect a plugin and see if the file information is given, and if not, detect it.
 481   *
 482   * @access public
 483   * @param   string      The ID of a plugin to load
 484   * @param   string      A reference variable that will hold the class name of the plugin (do not pass manually)
 485   * @param   string      A reference variable that will hold the path to the plugin (do not pass manually)
 486   * @return  string      Returns the filename of a plugin to load
 487   */
 488      /* Probes for the plugin filename */
 489      function probePlugin($instance_id, &$class_name, &$pluginPath) {
 490          global $serendipity;
 491  
 492          $filename    = false;
 493          $is_internal = false;
 494  
 495          $class_name  = serendipity_plugin_api::getClassByInstanceID($instance_id, $is_internal);
 496  
 497          if (!$is_internal) {
 498              /* plugin from the plugins/ dir */
 499              // $serendipity['debug']['pluginload'][] = "Including plugin $class_name, $pluginPath";
 500              $filename = serendipity_plugin_api::includePlugin($class_name, $pluginPath, $instance_id);
 501              if (empty($filename) && !empty($instance_id)) {
 502                  // $serendipity['debug']['pluginload'][] = "No valid path/filename found.";
 503                  $sql = "SELECT path from {$serendipity['dbPrefix']}plugins WHERE name = '" . $instance_id . "'";
 504                  $plugdata = serendipity_db_query($sql, true, 'both', false, false, false, true);
 505                  if (is_array($plugdata) && isset($plugdata[0])) {
 506                      $pluginPath = $plugdata[0];
 507                  }
 508  
 509                  if (empty($pluginPath)) {
 510                      $pluginPath = $class_name;
 511                  }
 512  
 513                  // $serendipity['debug']['pluginload'][] = "Including plugin(2) $class_name, $pluginPath";
 514                  $filename = serendipity_plugin_api::includePlugin($class_name, $pluginPath);
 515              }
 516  
 517              if (empty($filename)) {
 518                  $serendipity['debug']['pluginload'][] = "No valid path/filename found. Aborting.";
 519                  return false;
 520              }
 521          }
 522  
 523          // $serendipity['debug']['pluginload'][] = "Found plugin file $filename";
 524          return $filename;
 525      }
 526  
 527  /**
 528   * Instantiates a plugin class
 529   *
 530   * @access public
 531   * @param   string      The ID of the plugin to load
 532   * @param   int         The owner of the plugin (can be autodetected)
 533   * @param   string      The path to a plugin (can be autodetected)
 534   * @param   string      The filename of a plugin (can be autodetected)
 535   * @return
 536   */
 537      function &load_plugin($instance_id, $authorid = null, $pluginPath = '', $pluginFile = null) {
 538          global $serendipity;
 539  
 540          if ($pluginFile === null) {
 541              $class_name = '';
 542              // $serendipity['debug']['pluginload'][] = "Init probe for plugin $instance_id, $class_name, $pluginPath";
 543              $pluginFile = serendipity_plugin_api::probePlugin($instance_id, $class_name, $pluginPath);
 544          } else {
 545              $is_internal = false;
 546              // $serendipity['debug']['pluginload'][] = "getClassByInstanceID $instance_id, $is_internal";
 547              $class_name  = serendipity_plugin_api::getClassByInstanceID($instance_id, $is_internal);
 548          }
 549  
 550          if (!class_exists($class_name) && !empty($pluginFile)) {
 551              // $serendipity['debug']['pluginload'][] = "Classname does not exist. Including $pluginFile.";
 552              include($pluginFile);
 553          }
 554  
 555          if (!class_exists($class_name)) {
 556              $serendipity['debug']['pluginload'][] = "Classname $class_name still does not exist. Aborting.";
 557              $retval = false;
 558              return $retval;
 559          }
 560  
 561          // $serendipity['debug']['pluginload'][] = "Returning new $class_name($instance_id)";
 562          $p =& new $class_name($instance_id);
 563          if (!is_null($authorid)) {
 564              $p->serendipity_owner = $authorid;
 565          } else {
 566              $sql = "SELECT authorid from {$serendipity['dbPrefix']}plugins WHERE name = '" . $instance_id . "'";
 567              $owner = serendipity_db_query($sql, true);
 568              if (is_array($owner) && isset($owner[0])) {
 569                  $p->serendipity_owner = $owner[0];
 570              }
 571          }
 572  
 573          return $p;
 574      }
 575  
 576  /**
 577   * Gets cached properties/information about a specific plugin, auto-loads a cache of all plugins
 578   *
 579   * @access public
 580   * @param   string      The filename of the plugin to get information about
 581   * @param   array       A referenced array that holds information about the plugin instance (self::load_plugin() response)
 582   * @param   type        The type of the plugin (local|spartacus|...)
 583   * @return  array       Information about the plugin
 584   */
 585      function &getPluginInfo(&$pluginFile, &$class_data, $type) {
 586          global $serendipity;
 587  
 588          static $pluginlist = null;
 589  
 590          if ($pluginlist === null) {
 591              $data = serendipity_db_query("SELECT p.*,
 592                                                   pc.category
 593                                              FROM {$serendipity['dbPrefix']}pluginlist AS p
 594                                   LEFT OUTER JOIN {$serendipity['dbPrefix']}plugincategories AS pc
 595                                                ON pc.class_name = p.class_name
 596                                             WHERE p.pluginlocation = 'local' AND
 597                                                   p.plugintype     = '" . serendipity_db_escape_string($type) . "'");
 598              if (is_array($data)) {
 599                  foreach($data AS $p) {
 600                      if (isset($p['pluginpath'])) {
 601                          $p['pluginPath'] = $p['pluginpath'];
 602                      }
 603                      if (!isset($pluginlist[$p['plugin_file']])) {
 604                          $pluginlist[$p['plugin_file']] = $p;
 605                      }
 606  
 607                      $pluginlist[$p['plugin_file']]['groups'][] = $p['category'];
 608                  }
 609              }
 610          }
 611  
 612          if (is_array($pluginlist[$pluginFile]) && !preg_match('@plugin_internal\.inc\.php@', $pluginFile)) {
 613              $data = $pluginlist[$pluginFile];
 614              if ((int)filemtime($pluginFile) == (int)$data['last_modified']) {
 615                  $data['stackable']    = serendipity_db_bool($data['stackable']);
 616  
 617                  $plugin    = $data;
 618                  return $plugin;
 619              }
 620          }
 621  
 622          $plugin =& serendipity_plugin_api::load_plugin($class_data['name'], null, $class_data['pluginPath'], $pluginFile);
 623  
 624          return $plugin;
 625      }
 626  
 627  /**
 628   * Set cache information about a plugin
 629   *
 630   * @access public
 631   * @param   mixed       Either an plugin object or a plugin information array that holds the information about the plugin
 632   * @param   string      The filename of the plugin
 633   * @param   object      The property bag object bundled with the plugin
 634   * @param   array       Previous/additional information about the plugin
 635   * @param   string      The location/type of a plugin (local|spartacus)
 636   * @return
 637   */
 638      function &setPluginInfo(&$plugin, &$pluginFile, &$bag, &$class_data, $pluginlocation = 'local') {
 639          global $serendipity;
 640  
 641          static $dbfields = array(
 642              'plugin_file',
 643              'class_name',
 644              'plugin_class',
 645              'pluginPath',
 646              'name',
 647              'description',
 648              'version',
 649              'upgrade_version',
 650              'plugintype',
 651              'pluginlocation',
 652              'stackable',
 653              'author',
 654              'requirements',
 655              'website',
 656              'last_modified'
 657          );
 658  
 659          serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}pluginlist WHERE plugin_file = '" . serendipity_db_escape_string($pluginFile) . "' AND pluginlocation = '" . serendipity_db_escape_string($pluginlocation) . "'");
 660  
 661          if (!empty($pluginFile) && file_exists($pluginFile)) {
 662              $lastModified = filemtime($pluginFile);
 663          } else {
 664              $lastModified = 0;
 665          }
 666  
 667          if (is_object($plugin)) {
 668              $data = array(
 669                  'class_name'      => get_class($plugin),
 670                  'stackable'       => $bag->get('stackable'),
 671                  'name'            => $bag->get('name'),
 672                  'description'     => $bag->get('description'),
 673                  'author'          => $bag->get('author'),
 674                  'version'         => $bag->get('version'),
 675                  'upgrade_version' => isset($class_data['upgrade_version']) ? $class_data['upgrade_version'] : $bag->get('version'),
 676                  'requirements'    => serialize($bag->get('requirements')),
 677                  'website'         => $bag->get('website'),
 678                  'plugin_class'    => $class_data['name'],
 679                  'pluginPath'      => $class_data['pluginPath'],
 680                  'plugin_file'     => $pluginFile,
 681                  'pluginlocation'  => $pluginlocation,
 682                  'plugintype'      => $serendipity['GET']['type'],
 683                  'last_modified'   => $lastModified
 684              );
 685              $groups = $bag->get('groups');
 686          } elseif (is_array($plugin)) {
 687              $data = $plugin;
 688              $groups = $data['groups'];
 689              unset($data['installable']);
 690              unset($data['true_name']);
 691              unset($data['customURI']);
 692              unset($data['groups']);
 693              if (isset($data['pluginpath'])) {
 694                  $data['pluginPath'] = $data['pluginpath'];
 695              }
 696              $data['requirements'] = serialize($data['requirements']);
 697          }
 698  
 699          if (!isset($data['stackable']) || empty($data['stackable'])) {
 700              $data['stackable'] = '0';
 701          }
 702  
 703          if (!isset($data['last_modified'])) {
 704              $data['last_modified'] = $lastModified;
 705          }
 706  
 707          // Only insert data keys that exist in the DB.
 708          $insertdata = array();
 709          foreach($dbfields AS $field) {
 710              $insertdata[$field] = $data[$field];
 711          }
 712  
 713          if ($data['upgradable']) {
 714              serendipity_db_query("UPDATE {$serendipity['dbPrefix']}pluginlist
 715                                       SET upgrade_version = '" . serendipity_db_escape_string($data['upgrade_version']) . "'
 716                                     WHERE plugin_class    = '" . serendipity_db_escape_string($data['plugin_class']) . "'");
 717          }
 718          serendipity_db_insert('pluginlist', $insertdata);
 719  
 720          serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}plugincategories WHERE class_name = '" . serendipity_db_escape_string($data['class_name']) . "'");
 721          foreach((array)$groups AS $group) {
 722              if (empty($group)) {
 723                  continue;
 724              }
 725  
 726              $cat = array(
 727                  'class_name'  => $data['class_name'],
 728                  'category'    => $group
 729              );
 730              serendipity_db_insert('plugincategories', $cat);
 731          }
 732  
 733          $data['groups'] = $groups;
 734  
 735          return $data;
 736      }
 737  
 738  /**
 739   * Moves a sidebar plugin to a different side or up/down
 740   *
 741   * @access public
 742   * @param   string  The instance ID of a plugin
 743   * @param   string  The new placement of a plugin (left|right|hide|event|eventh)
 744   * @param   string  A new sort order for the plugin
 745   * @return
 746   */
 747      function update_plugin_placement($name, $placement, $order=null)
 748      {
 749          global $serendipity;
 750  
 751          $admin = '';
 752          if (!serendipity_checkPermission('adminPlugins') && $placement == 'hide') {
 753              // Only administrators can set plugins to 'hide' if they are not the owners.
 754              $admin = " AND (authorid = 0 OR authorid = {$serendipity['authorid']})";
 755          }
 756  
 757          $sql = "UPDATE {$serendipity['dbPrefix']}plugins set placement='$placement' ";
 758  
 759          if ($order !== null) {
 760              $sql .= ", sort_order=$order ";
 761          }
 762  
 763          $sql .= "WHERE name='$name' $admin";
 764  
 765          return serendipity_db_query($sql);
 766      }
 767  
 768  /**
 769   * Updates the ownership information about a plugin
 770   *
 771   * @access public
 772   * @param   string  The instance ID of the plugin
 773   * @param   int     The ID of the new author owner of the plugin
 774   * @return
 775   */
 776      function update_plugin_owner($name, $authorid)
 777      {
 778          global $serendipity;
 779  
 780          if (empty($authorid) && $authorid != '0') {
 781              return;
 782          }
 783  
 784          $admin = '';
 785          if (!serendipity_checkPermission('adminPlugins')) {
 786              $admin = " AND (authorid = 0 OR authorid = {$serendipity['authorid']})";
 787          }
 788  
 789          $sql = "UPDATE {$serendipity['dbPrefix']}plugins SET authorid='$authorid' WHERE name='$name' $admin";
 790  
 791          return serendipity_db_query($sql);
 792      }
 793  
 794  /**
 795   * Get a list of Sidebar plugins and pass them to Smarty
 796   *
 797   * @access public
 798   * @param   string      The side of plugins to show (left/right/hide/event/eventh)
 799   * @param   string      deprecated: Indicated which wrapping HTML element to use for plugins
 800   * @param   boolean     Indicates whether only all plugins should be shown that are not in the $side list
 801   * @param   string      Only show plugins of this plugin class
 802   * @param   string      Only show a plugin with this instance ID
 803   * @return  string      Smarty HTML output
 804   */
 805      function generate_plugins($side, $tag = '', $negate = false, $class = null, $id = null)
 806      {
 807          global $serendipity;
 808  
 809          /* $tag parameter is deprecated and used in Smarty templates instead. Only use it in function
 810           * header for layout.php BC.
 811           */
 812  
 813          $plugins = serendipity_plugin_api::enum_plugins($side, $negate, $class, $id);
 814  
 815          if (!is_array($plugins)) {
 816              return;
 817          }
 818  
 819          if (!isset($serendipity['smarty'])) {
 820              $serendipity['smarty_raw_mode'] = true;
 821              serendipity_smarty_init();
 822          }
 823  
 824          $pluginData = array();
 825          $addData    = func_get_args();
 826          serendipity_plugin_api::hook_event('frontend_generate_plugins', $plugins, $addData);
 827  
 828          if (count($plugins) == 0) {
 829              $serendipity['prevent_sidebar_plugins_' . $side] = true;
 830          }
 831  
 832          foreach ($plugins as $plugin_data) {
 833              $plugin =& serendipity_plugin_api::load_plugin($plugin_data['name'], $plugin_data['authorid'], $plugin_data['path']);
 834              if (is_object($plugin)) {
 835                  $class  = get_class($plugin);
 836                  $title  = '';
 837  
 838                  /* TODO: make generate_content NOT echo its output */
 839                  ob_start();
 840                  $show_plugin = $plugin->generate_content($title);
 841                  $content = ob_get_contents();
 842                  ob_end_clean();
 843  
 844                  if ($show_plugin !== FALSE) {
 845                      $pluginData[] = array('side'    => $side,
 846                                            'class'   => $class,
 847                                            'title'   => $title,
 848                                            'content' => $content,
 849                                            'id'      => $plugin->instance);
 850                  }
 851              } else {
 852                      $pluginData[] = array('side'          => $side,
 853                                            'title'         => ERROR,
 854                                            'class'         => $class,
 855                                            'content'       => sprintf(INCLUDE_ERROR, $plugin_data['name']));
 856              }
 857          }
 858  
 859          $serendipity['smarty']->assign_by_ref('plugindata', $pluginData);
 860          $serendipity['smarty']->assign('pluginside', ucfirst($side));
 861  
 862          return serendipity_smarty_fetch('sidebar_'. $side, 'sidebar.tpl', true);
 863      }
 864  
 865  /**
 866   * Gets the title of a plugin to be shown in plugin overview
 867   *
 868   * @access public
 869   * @param   object      The plugin object
 870   * @param   string      The default title, if none was configured
 871   * @return  string      The title of the plugin
 872   */
 873      function get_plugin_title(&$plugin, $default_title = '')
 874      {
 875          global $serendipity;
 876  
 877          // Generate plugin output. Make sure that by probing the plugin, no events are actually called. After that,
 878          // restore setting of 'no_events'.
 879  
 880          if (!is_null($plugin->title)) {
 881              // Preferred way of fetching a plugins title
 882              $title = &$plugin->title;
 883          } else {
 884              $ne = (isset($serendipity['no_events']) && $serendipity['no_events'] ? TRUE : FALSE);
 885              $serendipity['no_events'] = TRUE;
 886              ob_start();
 887              $plugin->generate_content($title);
 888              ob_end_clean();
 889              $serendipity['no_events'] = $ne;
 890          }
 891  
 892          if (strlen(trim($title)) == 0) {
 893              if (!empty($default_title)) {
 894                  $title = $default_title;
 895              } else {
 896                  $title = $plugin->instance;
 897              }
 898          }
 899  
 900          return $title;
 901      }
 902  
 903  /**
 904   * Check if a plugin is an event plugin
 905   *
 906   * Refactoring: decompose conditional
 907   *
 908   * @access public
 909   * @param   string  Name of a plugin
 910   * @return  boolean
 911   */
 912      function is_event_plugin($name) {
 913          return (strstr($name, '_event_'));
 914      }
 915  
 916  /**
 917   * Prepares a cache of all event plugins and load them in queue so that they can be fetched
 918   *
 919   * @access public
 920   * @param  mixed    If set to a string, a certain event plugin cache object will be returned by this function
 921   * @param  boolean  If set to true, the list of cached event plugins will be refreshed
 922   * @return mixed    Either returns the whole list of event plugins, or only a specific instance
 923   */
 924      function &get_event_plugins($getInstance = false, $refresh = false) {
 925          static $event_plugins;
 926          static $false = false;
 927  
 928          if (!$refresh && isset($event_plugins) && is_array($event_plugins)) {
 929              if ($getInstance) {
 930                  if (isset($event_plugins[$getInstance]['p'])) {
 931                      return $event_plugins[$getInstance]['p'];
 932                  }
 933                  return $false;
 934              }
 935              return $event_plugins;
 936          }
 937  
 938          $plugins = serendipity_plugin_api::enum_plugins('event');
 939          if (!is_array($plugins)) {
 940              return $false;
 941          }
 942  
 943          $event_plugins = array();
 944          foreach($plugins AS $plugin_data) {
 945              if ($event_plugins[$plugin_data['name']]['p'] = &serendipity_plugin_api::load_plugin($plugin_data['name'], $plugin_data['authorid'], $plugin_data['path'])) {
 946                  /* query for its name, description and configuration data */
 947                  $event_plugins[$plugin_data['name']]['b'] = new serendipity_property_bag;
 948                  $event_plugins[$plugin_data['name']]['p']->introspect($event_plugins[$plugin_data['name']]['b']);
 949                  $event_plugins[$plugin_data['name']]['t'] = serendipity_plugin_api::get_plugin_title($event_plugins[$plugin_data['name']]['p']);
 950              } else {
 951                  unset($event_plugins[$plugin_data['name']]); // Unset failed plugins
 952              }
 953          }
 954  
 955          if ($getInstance) {
 956              if (isset($event_plugins[$getInstance]['p'])) {
 957                  return $event_plugins[$getInstance]['p'];
 958              }
 959              return $false;
 960          }
 961  
 962          return $event_plugins;
 963      }
 964  
 965  /**
 966   * Executes a specific Eventhook
 967   *
 968   * If you want to temporarily block any event plugins, you can set $serendipity['no_events'] before
 969   * this method call.
 970   *
 971   * @access public
 972   * @param   string      The name of the event to hook on to
 973   * @param   mixed       May contain any type of variables that are passed by reference to an event plugin
 974   * @param   mixed       May contain any type of variables that are passed to an event plugin
 975   * @return true
 976   */
 977      function hook_event($event_name, &$eventData, $addData = null) {
 978          global $serendipity;
 979  
 980          // Can be bypassed globally by setting $serendipity['no_events'] = TRUE;
 981          if (isset($serendipity['no_events']) && $serendipity['no_events'] == true) {
 982              return false;
 983          }
 984  
 985          if ($serendipity['enablePluginACL'] && !serendipity_hasPluginPermissions($event_name)) {
 986              return false;
 987          }
 988  
 989          // We can NOT use a "return by reference" here, because then when
 990          // a plugin executes another event_hook, the referenced variable within
 991          // that call will overwrite the previous original plugin listing and
 992          // skip the execution of any follow-up plugins.
 993          $plugins = serendipity_plugin_api::get_event_plugins();
 994  
 995          if (is_array($plugins)) {
 996              // foreach() operates on copies of values, but we want to operate on references, so we use while()
 997              @reset($plugins);
 998              while(list($plugin, $plugin_data) = each($plugins)) {
 999                  $bag    = &$plugin_data['b'];
1000                  $phooks = &$bag->get('event_hooks');
1001                  if (isset($phooks[$event_name])) {
1002  
1003                      // Check for cachable events.
1004                      if (isset($eventData['is_cached']) && $eventData['is_cached']) {
1005                          $chooks = &$bag->get('cachable_events');
1006                          if (is_array($chooks) && isset($chooks[$event_name])) {
1007                              continue;
1008                          }
1009                      }
1010  
1011                      if ($serendipity['enablePluginACL'] && !serendipity_hasPluginPermissions($plugin)) {
1012                          continue;
1013                      }
1014                      $plugin_data['p']->event_hook($event_name, $bag, $eventData, $addData);
1015                  }
1016              }
1017          }
1018  
1019          return true;
1020      }
1021  
1022  /**
1023   * Checks if a specific plugin instance is already installed
1024   *
1025   * @access public
1026   * @param   string      A name (may contain wildcards) of a plugin class to check
1027   * @return  boolean     True if a plugin was found
1028   */
1029      function exists($instance_id) {
1030          global $serendipity;
1031  
1032          if (!strstr($instance_id, ':')) {
1033              $instance_id .= ':';
1034          }
1035  
1036          $existing = serendipity_db_query("SELECT name FROM {$serendipity['dbPrefix']}plugins WHERE name LIKE '%$instance_id%'");
1037  
1038          if (is_array($existing) && !empty($existing[0][0])) {
1039              return $existing[0][0];
1040          }
1041  
1042          return false;
1043      }
1044  
1045  /**
1046   * Install a new plugin by ensuring that it does not already exist
1047   *
1048   * @access public
1049   * @param   string      The classname of the plugin
1050   * @param   int         The new owner author
1051   * @param   boolean     Indicates if the plugin is an event plugin
1052   * @return  object      Returns the plugin object or false, if failure
1053   */
1054      function &autodetect_instance($plugin_name, $authorid, $is_event_plugin = false) {
1055          if ($is_event_plugin) {
1056              $side = 'event';
1057          } else {
1058              $side = 'right';
1059          }
1060  
1061          $classes = serendipity_plugin_api::enum_plugin_classes(null);
1062          if (isset($classes[$plugin_name])) {
1063              $instance = serendipity_plugin_api::create_plugin_instance($plugin_name, null, $side, $authorid, $classes[$plugin_name]['pluginPath']);
1064          } else {
1065              $instance = false;
1066          }
1067  
1068          return $instance;
1069      }
1070  }
1071  
1072  /* holds a bunch of properties; since serendipity 0.8 only one value per key is allowed [was never really useful] */
1073  class serendipity_property_bag {
1074  /**
1075   * @access  private
1076   * @var array   property storage container.
1077   */
1078      var $properties = array();
1079  
1080  /**
1081   * @access private
1082   * @var    string   Name of the property bag
1083   */
1084      var $name       = null;
1085  
1086  /**
1087   * Adds a property value to the bag
1088   *
1089   * @access public
1090   * @param   string  The name of the property
1091   * @param   mixed   The value of a property
1092   * @return null
1093   */
1094      function add($name, $value)
1095      {
1096          $this->properties[$name] = $value;
1097      }
1098  
1099  /**
1100   * Returns a property value of a bag
1101   *
1102   * @access public
1103   * @param   string  Name of property to fetch
1104   * @return  mixed   The value of the property
1105   */
1106      function &get($name)
1107      {
1108          return $this->properties[$name];
1109      }
1110  
1111  /**
1112   * Check if a specific property name is already set
1113   *
1114   * @access public
1115   * @param   string  Name of the property to check
1116   * @return  boolean True, if already set.
1117   */
1118      function is_set($name)
1119      {
1120          if (isset($this->properties[$name])) {
1121              return true;
1122          }
1123  
1124          return false;
1125      }
1126  }
1127  
1128  /* A core plugin, with methods that both event and sidebar plugins share */
1129  class serendipity_plugin {
1130      var $instance      = null;
1131      var $protected     = false;
1132      var $wrap_class    = 'serendipitySideBarItem';
1133      var $title_class   = 'serendipitySideBarTitle';
1134      var $content_class = 'serendipitySideBarContent';
1135      var $title         = null;
1136  
1137  /**
1138   * The constructor of a plugin
1139   *
1140   * Needs to be implemented by your own class.
1141   * Be sure to call this method from your derived classes constructors,
1142   * otherwise your config data will not be stored or retrieved correctly
1143   *
1144   * @access public
1145   * @return true
1146   */
1147      function serendipity_plugin($instance)
1148      {
1149          $this->instance = $instance;
1150      }
1151  
1152  /**
1153   * Perform configuration routines
1154   *
1155   * Called by Serendipity when the plugin is being configured.
1156   * Can be used to query the database for configuration values that
1157   * only need to be available for the global configuration and not
1158   * on each page request.
1159   *
1160   * @access public
1161   * @return true
1162   */
1163      function performConfig(&$bag)
1164      {
1165          return true;
1166      }
1167  
1168  /**
1169   * Perform install routines
1170   *
1171   * Called by Serendipity when the plugin is first installed.
1172   * Can be used to install database tables etc.
1173   *
1174   * @access public
1175   * @return true
1176   */
1177      function install()
1178      {
1179          return true;
1180      }
1181  
1182  /**
1183   * Perform uninstall routines
1184   *
1185   * Called by Serendipity when the plugin is removed/uninstalled.
1186   * Can be used to drop installed database tables etc.
1187   *
1188   * @access public
1189   * @param  object   A property bag object
1190   * @return true
1191   */
1192      function uninstall(&$propbag)
1193      {
1194          return true;
1195      }
1196  
1197  /**
1198   * The introspection function of a plugin, to setup properties
1199   *
1200   * Called by serendipity when it wants to display information
1201   * about your plugin.
1202   * You need to override this method in your child class.
1203   *
1204   * @access public
1205   * @param   object  A property bag object you can manipulate
1206   * @return true
1207   */
1208      function introspect(&$propbag)
1209      {
1210          $propbag->add('copyright', 'MIT License');
1211          $propbag->add('name'     , get_class($this));
1212  
1213          // $propbag->add(
1214          //   'configuration',
1215          //   array(
1216          //     'text field',
1217          //     'foo bar'
1218          //   )
1219          // );
1220  
1221          $this->protected = FALSE; // If set to TRUE, only allows the owner of the plugin to modify its configuration
1222  
1223          return true;
1224      }
1225  
1226  /**
1227   * Introspection of a plugin configuration item
1228   *
1229   * Called by serendipity when it wants to display the configuration
1230   * editor for your plugin.
1231   * $name is the name of a configuration item you added in
1232   * your instrospect method.
1233   * You need to fill the property bag with appropriate items
1234   * that describe the type and value(s) for that particular
1235   * configuration option.
1236   * You need to override this method in your child class if
1237   * you have configuration options.
1238   *
1239   * @access public
1240   * @param   string      Name of the config item
1241   * @param   object      A property bag object you can store the configuration in
1242   * @return
1243   */
1244      function introspect_config_item($name, &$propbag)
1245      {
1246          return false;
1247      }
1248  
1249  /**
1250   * Validate plugin configuration options.
1251   *
1252   * Called from Plugin Configuration manager. Can be extended by your own plugin, if you need.
1253   *
1254   * @access public
1255   * @param   string      Name of the config item to validate
1256   * @param   object      Property bag of the config item
1257   * @param   value       The value of a config item
1258   * @return
1259   */
1260      function validate($config_item, &$cbag, &$value) {
1261          static $pattern_mail  = '([\.\-\+~@_0-9a-z]+?)';
1262          static $pattern_url   = '([@!=~\?:&;0-9a-z#\.\-_\/]+?)';
1263  
1264          $validate = $cbag->get('validate');
1265          $valid    = true;
1266  
1267          if (!empty($validate)) {
1268              switch ($validate) {
1269                  case 'string':
1270                      if (!preg_match('@^\w*$@i', $value)) {
1271                          $valid = false;
1272                      }
1273                      break;
1274  
1275                  case 'words':
1276                      if (!preg_match('@^[\w\s\r\n,\.\-!\?:;&_/=%\$]*$@i', $value)) {
1277                          $valid = false;
1278                      }
1279                      break;
1280  
1281                  case 'number':
1282                      if (!preg_match('@^[\d]*$@', $value)) {
1283                          $valid = false;
1284                      }
1285                      break;
1286  
1287                  case 'url':
1288                      if (!preg_match('§^' . $pattern_url . '$§', $value)) {
1289                          $valid = false;
1290                      }
1291                      break;
1292  
1293                  case 'mail':
1294                      if (!preg_match('§^' . $pattern_mail . '$§', $value)) {
1295                          $valid = false;
1296                      }
1297                      break;
1298  
1299                  case 'path':
1300                      if (!preg_match('@^[\w/_.\-~]$@', $value)) {
1301                          $valid = false;
1302                      }
1303                      break;
1304  
1305                  default:
1306                      if (!preg_match($validate, $value)) {
1307                          $valid = false;
1308                      }
1309                      break;
1310              }
1311  
1312              $error = $cbag->get('validate_error');
1313              if ($valid) {
1314                  return true;
1315              } elseif (!empty($error)) {
1316                  return $error;
1317              } else {
1318                  return sprintf(PLUGIN_API_VALIDATE_ERROR, $config_item, $validate);
1319              }
1320          }
1321  
1322          return true;
1323      }
1324  
1325  /**
1326   * Output plugin's contents (Sidebar plugins)
1327   *
1328   * Called by serendipity when it wants your plugin to display itself.
1329   * You need to set $title to be whatever text you want want to
1330   * appear in the item caption space.
1331   * Simply echo/print your content to the output; serendipity will
1332   * capture it and make things work.
1333   * You need to override this method in your child class.
1334   *
1335   * @access public
1336   * @param   string       The referenced varaiable that holds the sidebar title of your plugin.
1337   * @return null
1338   */
1339      function generate_content(&$title)
1340      {
1341          $title = 'Sample!';
1342          echo     'This is a sample!';
1343      }
1344  
1345  /**
1346   * Get a config value of the plugin
1347   *
1348   * @access public
1349   * @param   string  Name of the config value to fetch
1350   * @param   mixed   The default value of a configuration item, if not set
1351   * @param   boolean If true, the default value will only be set if the plugin config item was not set.
1352   * @return  mixed   The value of the config item
1353   */
1354      function get_config($name, $defaultvalue = null, $empty = true)
1355      {
1356          $_res = serendipity_get_config_var($this->instance . '/' . $name, $defaultvalue, $empty);
1357  
1358          if (is_null($_res)) {
1359              // A protected plugin by a specific owner may not have its values stored in $serendipity
1360              // because of the special authorid. To display such contents, we need to fetch it
1361              // seperately from the DB.
1362              $_res = serendipity_get_user_config_var($this->instance . '/' . $name, null, $defaultvalue);
1363          }
1364  
1365          if (is_null($_res)) {
1366              $cbag = new serendipity_property_bag;
1367              $this->introspect_config_item($name, $cbag);
1368              $_res = $cbag->get('default');
1369              unset($cbag);
1370              // Set the fetched value, so the default will not be fetched the next config call time
1371              $this->set_config($name, $_res);
1372          }
1373  
1374          return $_res;
1375      }
1376  
1377  /**
1378   * Sets a configuration value for a plugin
1379   *
1380   * @access public
1381   * @param   string  Name of the plugin configuration item
1382   * @param   string  Value of the plugin configuration item
1383   * @param   string  A concatenation key for imploding arrays
1384   * @return
1385   */
1386      function set_config($name, $value, $implodekey = '^')
1387      {
1388          $name = $this->instance . '/' . $name;
1389  
1390          if (is_array($value)) {
1391              $dbvalue = implode($implodekey, $value);
1392              $_POST['serendipity']['plugin'][$name] = $dbvalue;
1393          } else {
1394              $dbvalue = $value;
1395          }
1396  
1397          return serendipity_set_config_var($name, $dbvalue);
1398      }
1399  
1400  /**
1401   * Garbage Collection
1402   *
1403   * Called by serendipity after insertion of a config item. If you want to kick out certain
1404   * elements based on contents, create the corresponding function here.
1405   *
1406   * @access public
1407   * @return true
1408   */
1409      function cleanup()
1410      {
1411          // Cleanup. Remove all empty configs on SAVECONF-Submit.
1412          // serendipity_plugin_api::remove_plugin_value($this->instance, array('title', 'description'));
1413          return true;
1414      }
1415  
1416  /**
1417   * Auto-Register dependencies of a plugin
1418   *
1419   * This method evaluates the "dependencies" member variable to check which plugins need to be installed.
1420   *
1421   * @access public
1422   * @param   boolean     If true, a depending plugin will be removed when this plugin is uninstalled
1423   * @param   int         The owner id of the current plugin
1424   * @return true
1425   */
1426      function register_dependencies($remove = false, $authorid = '0')
1427      {
1428          global $serendipity;
1429  
1430          if (isset($this->dependencies) && is_array($this->dependencies)) {
1431  
1432              if ($remove) {
1433                  $dependencies = @explode(';', $this->get_config('dependencies'));
1434                  $modes        = @explode(';', $this->get_config('dependency_modes'));
1435  
1436                  if (!empty($dependencies) && is_array($dependencies)) {
1437                      foreach($dependencies AS $idx => $dependency) {
1438                          if ($modes[$idx] == 'remove' && serendipity_plugin_api::exists($dependency)) {
1439                              serendipity_plugin_api::remove_plugin_instance($dependency);
1440                          }
1441                      }
1442                  }
1443              } else {
1444                  $keys  = array();
1445                  $modes = array();
1446                  foreach($this->dependencies AS $dependency => $mode) {
1447                      $exists = serendipity_plugin_api::exists($dependency);
1448                      if (!$exists) {
1449                          if (serendipity_plugin_api::is_event_plugin($dependency)) {
1450                              $keys[] = serendipity_plugin_api::autodetect_instance($dependency, $authorid, true);
1451                          } else {
1452                              $keys[] = serendipity_plugin_api::autodetect_instance($dependency, $authorid, false);
1453                          }
1454                      } else {
1455                          $keys[] = $exists;
1456                      }
1457  
1458                      $modes[] = $mode;
1459                  }
1460  
1461                  $this->set_config('dependencies',     implode(';', $keys));
1462                  $this->set_config('dependency_modes', implode(';', $modes));
1463              }
1464          }
1465  
1466          return true;
1467      }
1468  }
1469  
1470  /* Events can be called on several occasions when s9y performs an action.
1471   * One or multiple plugin can be registered for each of those hooks.
1472   */
1473  class serendipity_event extends serendipity_plugin {
1474  
1475  /**
1476   * The class constructor
1477   *
1478   * Be sure to call this method from your derived classes constructors,
1479   * otherwise your config data will not be stored or retrieved correctly
1480   *
1481   * @access public
1482   * @param   string      The instance name
1483   * @return
1484   */
1485      function serendipity_event($instance)
1486      {
1487          $this->instance = $instance;
1488      }
1489  
1490  /**
1491   * Gets a reference to an $entry / $eventData array pointer, interacting with Cache-Options
1492   *
1493   * This function is used by specific event plugins that require to properly get a reference
1494   * to the 'extended' or 'body' field of an entry superarray. If they would immediately operate
1495   * on the 'body' field, it might get overwritten by other plugins later on.
1496   *
1497   * @access public
1498   * @param   string      The fieldname to get a reference for
1499   * @param   array       The entry superarray to get the reference from
1500   * @return  array       The value of the array for the fieldname (reference)
1501   */
1502      function &getFieldReference($fieldname = 'body', &$eventData) {
1503          // Get a reference to a content field (body/extended) of
1504          // $entries input data. This is a unifying function because
1505          // several plugins are using similar fields.
1506  
1507          if (is_array($eventData) && isset($eventData[0]) && is_array($eventData[0]['properties'])) {
1508              if (!empty($eventData[0]['properties']['ep_cache_' . $fieldname])) {
1509  
1510                  // It may happen that there is no extended entry to concatenate to. In that case,
1511                  // create a dummy extended entry.
1512                  if (!isset($eventData[0]['properties']['ep_cache_' . $fieldname])) {
1513                      $eventData[0]['properties']['ep_cache_' . $fieldname] = '';
1514                  }
1515  
1516                  $key = &$eventData[0]['properties']['ep_cache_' . $fieldname];
1517              } else {
1518                  $key = &$eventData[0][$fieldname];
1519              }
1520          } elseif (is_array($eventData) && is_array($eventData['properties'])) {
1521              if (!empty($eventData['properties']['ep_cache_' . $fieldname])) {
1522                  $key = &$eventData['properties']['ep_cache_' . $fieldname];
1523              } else {
1524                  $key = &$eventData[$fieldname];
1525              }
1526          } elseif (is_array($eventData[0]) && isset($eventData[0][$fieldname])) {
1527              $key = &$eventData[0][$fieldname];
1528          } elseif (isset($eventData[$fieldname])) {
1529              $key = &$eventData[$fieldname];
1530          } else {
1531              $key = '';
1532          }
1533  
1534          return $key;
1535      }
1536  
1537  /**
1538   * Main logic for making a plugin "listen" to an event
1539   *
1540   * This method is called by the main plugin API for every event, that is executed.
1541   * You need to implement each actions that shall be performed by your plugin here.
1542   *
1543   * @access public
1544   * @param   string      The name of the executed event
1545   * @param   object      A property bag for the current plugin
1546   * @param   mixed       Any referenced event data from the serendipity_plugin_api::hook_event() function
1547   * @param   mixed       Any additional data from the hook_event call
1548   * @return true
1549   */
1550      function event_hook($event, &$bag, &$eventData, $addData = null) {
1551          // Define event hooks here, if you want you plugin to execute those instead of being a sidebar item.
1552          // Look at external plugins 'serendipity_event_mailer' or 'serendipity_event_weblogping' for usage.
1553          // Currently available events:
1554          //   backend_publish [after insertion of a new article in your s9y-backend]
1555          //   backend_display [after displaying an article in your s9y-backend]
1556          //   frontend_display [before displaying an article in your s9y-frontend]
1557          //   frontend_comment [after displaying the "enter comment" dialog]
1558          return true;
1559      }
1560  }
1561  
1562  if (!defined('S9Y_FRAMEWORK_PLUGIN_INTERNAL')) {
1563      include  S9Y_INCLUDE_PATH . 'include/plugin_internal.inc.php';
1564  }
1565  
1566  /* vim: set sts=4 ts=4 expandtab : */


Généré le : Sat Nov 24 09:00:37 2007 par Balluche grâce à PHPXref 0.7
  Clicky Web Analytics