system/lib-plugins.php
author Sami Barakat
Sun, 18 Oct 2009 02:05:53 +0100
branchHEAD
changeset 7387 39932e68c099
parent 7292 cd653ce9aafb
child 7571 dc6b98fef467
permissions -rw-r--r--
Looks like the Search API function setComment() is not required after all (cf. bug #0000902)
     1 <?php
     2 
     3 /* Reminder: always indent with 4 spaces (no tabs). */
     4 // +---------------------------------------------------------------------------+
     5 // | Geeklog 1.6                                                               |
     6 // +---------------------------------------------------------------------------+
     7 // | lib-plugins.php                                                           |
     8 // |                                                                           |
     9 // | This file implements plugin support in Geeklog.                           |
    10 // +---------------------------------------------------------------------------+
    11 // | Copyright (C) 2000-2009 by the following authors:                         |
    12 // |                                                                           |
    13 // | Authors: Tony Bibbs       - tony AT tonybibbs DOT com                     |
    14 // |          Blaine Lang      - blaine AT portalparts DOT com                 |
    15 // |          Dirk Haun        - dirk AT haun-online DOT de                    |
    16 // +---------------------------------------------------------------------------+
    17 // |                                                                           |
    18 // | This program is free software; you can redistribute it and/or             |
    19 // | modify it under the terms of the GNU General Public License               |
    20 // | as published by the Free Software Foundation; either version 2            |
    21 // | of the License, or (at your option) any later version.                    |
    22 // |                                                                           |
    23 // | This program is distributed in the hope that it will be useful,           |
    24 // | but WITHOUT ANY WARRANTY; without even the implied warranty of            |
    25 // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             |
    26 // | GNU General Public License for more details.                              |
    27 // |                                                                           |
    28 // | You should have received a copy of the GNU General Public License         |
    29 // | along with this program; if not, write to the Free Software Foundation,   |
    30 // | Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.           |
    31 // |                                                                           |
    32 // +---------------------------------------------------------------------------+
    33 
    34 /**
    35 * This is the plugin library for Geeklog.  This is the API that plugins can
    36 * implement to get tight integration with Geeklog.
    37 * See each function for more details.
    38 * @link http://wiki.geeklog.net/index.php/Plugin_API
    39 *
    40 */
    41 
    42 if (strpos(strtolower($_SERVER['PHP_SELF']), 'lib-plugins.php') !== false) {
    43     die('This file can not be used on its own!');
    44 }
    45 
    46 /**
    47 * Include plugin class
    48 */
    49 require_once $_CONF['path_system'] . 'classes/plugin.class.php';
    50 
    51 /**
    52 * Response codes for the service invocation PLG_invokeService(). Note that
    53 * these are intentionally vague so as not to give away too much information.
    54 */
    55 define('PLG_RET_OK',                   0);  // success
    56 define('PLG_RET_ERROR',               -1);  // generic error
    57 define('PLG_RET_PERMISSION_DENIED',   -2);  // access to item or object denied
    58 define('PLG_RET_AUTH_FAILED',         -3);  // authentication failed
    59 define('PLG_RET_PRECONDITION_FAILED', -4);  // a precondition was not met
    60 
    61 // buffer for function names for the center block API
    62 $PLG_bufferCenterAPI = array();
    63 $PLG_buffered = false;
    64 
    65 // buffer enabled plugins
    66 $result = DB_query("SELECT pi_name FROM {$_TABLES['plugins']} WHERE pi_enabled = 1");
    67 /**
    68 * @global array List of all active plugins
    69 */
    70 $_PLUGINS = array();
    71 while ($A = DB_fetchArray($result)) {
    72     $_PLUGINS[] = $A['pi_name'];
    73 }
    74 
    75 /**
    76 * Calls a function for all enabled plugins
    77 *
    78 * @param    string  $function_name  holds name of function to call
    79 * @return   void
    80 * @access   private
    81 * @internal not to be used by plugins
    82 * @todo     only supports functions without any parameters
    83 *
    84 */
    85 function PLG_callFunctionForAllPlugins($function_name)
    86 {
    87     global $_PLUGINS;
    88 
    89     foreach ($_PLUGINS as $pi_name) {
    90         $function = 'plugin_' . $function_name . '_' . $pi_name;
    91         if (function_exists($function)) {
    92             $function();
    93         }
    94     }
    95     $function = 'CUSTOM_' . $function_name;
    96     if (function_exists($function)) {
    97         $function();
    98     }
    99 }
   100 
   101 /**
   102 * Calls a function for a single plugin
   103 *
   104 * This is a generic function used by some of the other API functions to
   105 * call a function for a specific plugin and, optionally pass parameters.
   106 * This function can handle up to 5 arguments and if more exist it will
   107 * try to pass the entire args array to the function.
   108 *
   109 * @param        string      $function       holds name of function to call
   110 * @param        array       $args           arguments to send to function
   111 * @return       mixed       returns result of function call, otherwise false
   112 * @access   private
   113 * @internal not to be used by plugins
   114 *
   115 */
   116 function PLG_callFunctionForOnePlugin($function, $args='')
   117 {
   118     if (function_exists($function)) {
   119         if (empty($args)) {
   120             $args = array();
   121         }
   122 
   123         // great, function exists, run it
   124         switch (count($args)) {
   125         case 0:
   126             return $function();
   127             break;
   128         case 1:
   129             return $function($args[1]);
   130             break;
   131         case 2:
   132             return $function($args[1], $args[2]);
   133             break;
   134         case 3:
   135             return $function($args[1], $args[2], $args[3]);
   136             break;
   137         case 4:
   138             return $function($args[1], $args[2], $args[3], $args[4]);
   139             break;
   140         case 5:
   141             return $function($args[1], $args[2], $args[3], $args[4], $args[5]);
   142             break;
   143         case 6:
   144             return $function($args[1], $args[2], $args[3], $args[4], $args[5], $args[6]);
   145             break;
   146         case 7:
   147             return $function($args[1], $args[2], $args[3], $args[4], $args[5], $args[6], $args[7]);
   148             break;
   149         default:
   150             return $function($args);
   151             break;
   152         }
   153     } else {
   154         return false;
   155     }
   156 }
   157 
   158 /**
   159 * Tells a plugin to install itself. NOTE: not currently used any more
   160 *
   161 * @param    string      $type   Plugin name
   162 * @return   boolean             Returns true on success otherwise false
   163 * @deprecated deprecated since Geeklog 1.6.0
   164 * @link     http://wiki.geeklog.net/index.php/Plugin_Autoinstall
   165 *
   166 */
   167 function PLG_install($type)
   168 {
   169     return PLG_callFunctionForOnePlugin('plugin_install_' . $type);
   170 }
   171 
   172 /**
   173 * Upgrades a plugin. Tells a plugin to upgrade itself.
   174 *
   175 * @param    string  $type   Plugin name
   176 * @return   mixed           true on success, false or error number on failure
   177 *
   178 */
   179 function PLG_upgrade($type)
   180 {
   181     return PLG_callFunctionForOnePlugin('plugin_upgrade_' . $type);
   182 }
   183 
   184 /**
   185 * Called during site migration - let plugin handle changed URLs or paths
   186 *
   187 * @param    string  $type       Plugin name
   188 * @param    array   $old_conf   contents of $_CONF before the migration
   189 * @return   boolean             true on success, otherwise false
   190 * @link     http://wiki.geeklog.net/index.php/PLG_migrate
   191 * @since    Geeklog 1.6.0
   192 *
   193 */
   194 function PLG_migrate($type, $old_conf)
   195 {
   196     if (! function_exists('plugin_migrate_' . $type)) {
   197         // since PLG_callFunctionForOnePlugin would return false ...
   198         return true;
   199     }
   200 
   201     $args[1] = $old_conf;
   202 
   203     return PLG_callFunctionForOnePlugin('plugin_migrate_' . $type, $args);
   204 }
   205 
   206 /**
   207 * Calls the plugin function to return the current version of code.
   208 * Used to indicate to admin if an update or upgrade is required.
   209 *
   210 * @param        string      $type       Plugin name
   211 * @return       boolean     Returns true on success otherwise false
   212 *
   213 */
   214 function PLG_chkVersion($type)
   215 {
   216     return PLG_callFunctionForOnePlugin('plugin_chkVersion_' . $type);
   217 }
   218 
   219 /**
   220 * Tells a plugin to uninstall itself.
   221 *
   222 * @param    string      $type   Plugin to uninstall
   223 * @return   boolean             Returns true on success otherwise false
   224 * @link     http://wiki.geeklog.net/index.php/Plugin_Auto-Uninstall
   225 *
   226 */
   227 function PLG_uninstall($type)
   228 {
   229     global $_PLUGINS, $_TABLES;
   230 
   231     if (empty($type)) {
   232         return false;
   233     }
   234 
   235     if (function_exists('plugin_autouninstall_' . $type)) {
   236         COM_errorLog ("Auto-uninstalling plugin $type:", 1);
   237         $function = 'plugin_autouninstall_' . $type;
   238         $remvars = $function();
   239 
   240         if (empty($remvars) || $remvars == false) {
   241             return false;
   242         }
   243 
   244         // removing tables
   245         $num_tables = count($remvars['tables']);
   246         for ($i = 0; $i < $num_tables; $i++) {
   247             if (isset($_TABLES[$remvars['tables'][$i]])) {
   248                 COM_errorLog("Dropping table {$_TABLES[$remvars['tables'][$i]]}", 1);
   249                 DB_query("DROP TABLE {$_TABLES[$remvars['tables'][$i]]}", 1);
   250                 COM_errorLog('...success', 1);
   251             }
   252         }
   253 
   254         // removing variables
   255         $num_vars = count($remvars['vars']);
   256         for ($i = 0; $i < $num_vars; $i++) {
   257             COM_errorLog ("Removing variable {$remvars['vars'][$i]}", 1);
   258             DB_delete($_TABLES['vars'], 'name', $remvars['vars'][$i]);
   259             COM_errorLog ('...success', 1);
   260         }
   261 
   262         // removing groups
   263         $num_groups = count($remvars['groups']);
   264         for ($i = 0; $i < $num_groups; $i++) {
   265             $grp_id = DB_getItem ($_TABLES['groups'], 'grp_id',
   266                                   "grp_name = '{$remvars['groups'][$i]}'");
   267             if (!empty($grp_id)) {
   268                 COM_errorLog ("Attempting to remove the {$remvars['groups'][$i]} group", 1);
   269                 DB_delete($_TABLES['groups'], 'grp_id', $grp_id);
   270                 COM_errorLog ('...success', 1);
   271                 COM_errorLog ("Attempting to remove the {$remvars['groups'][$i]} group from all groups.", 1);
   272                 DB_delete($_TABLES['group_assignments'], 'ug_main_grp_id', $grp_id);
   273                 COM_errorLog ('...success', 1);
   274             }
   275         }
   276 
   277         // removing features
   278         $num_features = count($remvars['features']);
   279         for ($i = 0; $i < $num_features; $i++) {
   280             SEC_removeFeatureFromDB($remvars['features'][$i]);
   281         }
   282 
   283         // uninstall feeds
   284         $sql = "SELECT filename FROM {$_TABLES['syndication']} WHERE type = '$type';";
   285         $result = DB_query( $sql );
   286         $nrows = DB_numRows( $result );
   287         if ( $nrows > 0 ) {
   288             COM_errorLog ('removing feed files', 1);
   289             COM_errorLog ($nrows. ' files stored in table.', 1);
   290             for ( $i = 0; $i < $nrows; $i++ ) {
   291                 $fcount = $i + 1;
   292                 $A = DB_fetchArray( $result );
   293                 $fullpath = SYND_getFeedPath( $A[0] );
   294                 if ( file_exists( $fullpath ) ) {
   295                     unlink ($fullpath);
   296                     COM_errorLog ("removed file $fcount of $nrows: $fullpath", 1);
   297                 } else {
   298                     COM_errorLog ("cannot remove file $fcount of $nrows, it does not exist! ($fullpath)", 1);
   299                 }
   300             }
   301             COM_errorLog ('...success', 1);
   302             // Remove Links Feeds from syndiaction table
   303             COM_errorLog ('removing links feeds from table', 1);
   304             DB_delete($_TABLES['syndication'], 'type', $type);
   305             COM_errorLog ('...success', 1);
   306         }
   307 
   308         // remove comments for this plugin
   309         COM_errorLog ("Attempting to remove comments for $type", 1);
   310         DB_delete($_TABLES['comments'], 'type', $type);
   311         COM_errorLog ('...success', 1);
   312 
   313         // uninstall php-blocks
   314         $num_blocks = count($remvars['php_blocks']);
   315         for ($i = 0; $i < $num_blocks; $i++) {
   316             DB_delete($_TABLES['blocks'], array('type',     'phpblockfn'),
   317                                           array('phpblock', $remvars['php_blocks'][$i]));
   318         }
   319 
   320         // remove config table data for this plugin
   321         COM_errorLog ("Attempting to remove config table records for group_name: $type", 1);
   322         DB_delete($_TABLES['conf_values'], 'group_name', $type);
   323         COM_errorLog ('...success', 1);
   324 
   325         // uninstall the plugin
   326         COM_errorLog ("Attempting to unregister the $type plugin from Geeklog", 1);
   327         DB_delete($_TABLES['plugins'], 'pi_name', $type);
   328         COM_errorLog ('...success',1);
   329 
   330         COM_errorLog ("Finished uninstalling the $type plugin.", 1);
   331 
   332         return true;
   333     } else {
   334 
   335         $retval = PLG_callFunctionForOnePlugin ('plugin_uninstall_' . $type);
   336 
   337         if ($retval === true) {
   338             $plg = array_search ($type, $_PLUGINS);
   339             if ($plg !== false) {
   340                 unset ($_PLUGINS[$plg]);
   341             }
   342 
   343             return true;
   344 
   345         }
   346     }
   347 
   348     return false;
   349 }
   350 
   351 /**
   352 * Inform plugin that it is either being enabled or disabled.
   353 *
   354 * @param    string      $type       Plugin name
   355 * @param    boolean     $enable     true if enabling, false if disabling
   356 * @return   boolean     Returns true on success otherwise false
   357 * @see      PLG_pluginStateChange
   358 *
   359 */
   360 function PLG_enableStateChange($type, $enable)
   361 {
   362    global $_CONF, $_TABLES, $_DB_table_prefix;
   363 
   364     $args[1] = $enable;
   365 
   366     // IF we are enabling the plugin
   367     // THEN we must include its functions.inc so we have access to the function
   368     if ($enable) {
   369         require_once ($_CONF['path'] . 'plugins/' . $type . '/functions.inc');
   370     }
   371 
   372     return PLG_callFunctionForOnePlugin ('plugin_enablestatechange_' . $type,
   373                                          $args);
   374 }
   375 
   376 /**
   377 * Checks to see if user is a plugin moderator
   378 *
   379 * Geeklog is asking if the user is a moderator for any installed plugins.
   380 *
   381 * @return   boolean     True if current user is moderator of plugin otherwise false
   382 *
   383 */
   384 function PLG_isModerator()
   385 {
   386     return PLG_callFunctionForAllPlugins('ismoderator');
   387 }
   388 
   389 /**
   390 * Gives plugins a chance to print their menu items in header
   391 *
   392 * Note that this is fairly unflexible.  This simply loops through the plugins
   393 * in the database in the order they were installed and get their menu items.
   394 * If you want more flexibility in your menu then you should hard code the menu
   395 * items in header.thtml for the theme(s) you are using.
   396 *
   397 * @return   array   Returns menu options for plugin
   398 *
   399 */
   400 function PLG_getMenuItems()
   401 {
   402     global $_PLUGINS;
   403 
   404     $menu = array();
   405     foreach ($_PLUGINS as $pi_name) {
   406         $function = 'plugin_getmenuitems_' . $pi_name;
   407         if (function_exists($function)) {
   408             $menuitems = $function();
   409             if (is_array($menuitems)) {
   410                 $menu = array_merge ($menu, $menuitems);
   411             }
   412         }
   413     }
   414 
   415     return $menu;
   416 }
   417 
   418 /**
   419  * Get view URL and name of unique identifier
   420  *
   421  * @author Vincent Furia, vinny01 AT users DOT sourceforge DOT net
   422  * @param   string  $type   Plugin to delete comment
   423  * @return  array   string of URL of view page, name of unique identifier
   424  */
   425 function PLG_getCommentUrlId($type)
   426 {
   427     global $_CONF;
   428 
   429     $ret = PLG_callFunctionForOnePlugin('plugin_getcommenturlid_' . $type);
   430     if (empty($ret[0])) {
   431         $ret[0] = $_CONF['site_url'] . "/$type/index.php";
   432     }
   433     if (empty($ret[1])) {
   434         $ret[1] = 'id';
   435     }
   436 
   437     return $ret;
   438 }
   439 
   440 /**
   441  * Plugin should delete a comment
   442  *
   443  * @author Vincent Furia, vinny01 AT users DOT sourceforge DOT net
   444  * @param   string  $type   Plugin to delete comment
   445  * @param   int     $cid    Comment to be deleted
   446  * @param   string  $id     Item id to which $cid belongs
   447  * @return  mixed   false for failure, HTML string (redirect?) for success
   448  */
   449 function PLG_commentDelete($type, $cid, $id)
   450 {
   451     $args[1] = $cid;
   452     $args[2] = $id;
   453 
   454     return PLG_callFunctionForOnePlugin('plugin_deletecomment_' . $type, $args);
   455 }
   456 
   457 /**
   458  * Plugin should save a comment
   459  *
   460  * @author Vincent Furia, vinny01 AT users DOT sourceforge DOT net
   461  * @param   string  $type   Plugin to delete comment
   462  * @param   string  $title  comment title
   463  * @param   string  $comment comment text
   464  * @param   string  $id     Item id to which $cid belongs
   465  * @param   int     $pid    comment parent
   466  * @param   string  $postmode 'html' or 'text'
   467  * @return  mixed   false for failure, HTML string (redirect?) for success
   468  */
   469 function PLG_commentSave($type, $title, $comment, $id, $pid, $postmode)
   470 {
   471     $args[1] = $title;
   472     $args[2] = $comment;
   473     $args[3] = $id;
   474     $args[4] = $pid;
   475     $args[5] = $postmode;
   476 
   477     return PLG_callFunctionForOnePlugin('plugin_savecomment_' . $type, $args);
   478 }
   479 
   480 /**
   481  * Plugin should display [a] comment[s]
   482  *
   483  * @author Vincent Furia, vinny01 AT users DOT sourceforge DOT net
   484  * @param   string  $type   Plugin to display comment
   485  * @param   string  $id     Unique idenifier for item comment belongs to
   486  * @param   int     $cid    Comment id to display (possibly including sub-comments)
   487  * @param   string  $title  Page/comment title
   488  * @param   string  $order  'ASC' or 'DSC' or blank
   489  * @param   string  $format 'threaded', 'nested', or 'flat'
   490  * @param   int     $page   Page number of comments to display
   491  * @param   boolean $view   True to view comment (by cid), false to display (by $pid)
   492  * @return  mixed   results of calling the plugin_displaycomment_ function
   493  */
   494 function PLG_displayComment($type, $id, $cid, $title, $order, $format, $page, $view)
   495 {
   496     $args[1] = $id;
   497     $args[2] = $cid;
   498     $args[3] = $title;
   499     $args[4] = $order;
   500     $args[5] = $format;
   501     $args[6] = $page;
   502     $args[7] = $view;
   503 
   504     return PLG_callFunctionForOnePlugin('plugin_displaycomment_' . $type, $args);
   505 }
   506 
   507 /**
   508 * Allows plugins a chance to handle a comment before Geeklog does.
   509 
   510 * This is a first-come-first-serve affair so if a plugin returns an error, other
   511 * plugins wishing to handle comment preprocessing won't get called
   512 *
   513 * @author Tony Bibbs, tony AT tonybibbs DOT com
   514 * @access public
   515 * @param  int       $uid User ID
   516 * @param  string   &$title Comment title
   517 * @param  string   &$comment Comment text
   518 * @param  string    $sid Story ID (not always a story, remember!)
   519 * @param  int       $pid Parent comment ID
   520 * @param  string    $type Type of comment
   521 * @param  string   &$postmode HTML or text
   522 * @return mixed     an error otherwise false if no errors were encountered
   523 * @see    PLG_itemPreSave
   524 *
   525 */
   526 function PLG_commentPreSave($uid, &$title, &$comment, $sid, $pid, $type, &$postmode)
   527 {
   528 	global $_PLUGINS;
   529 
   530     foreach ($_PLUGINS as $pi_name) {
   531         $function = 'plugin_commentPreSave_' . $pi_name;
   532         if (function_exists($function)) {
   533             $someError = $function($uid, $title, $comment, $sid, $pid, $type, $postmode);
   534             if ($someError) {
   535             	// Plugin doesn't want to save the comment
   536             	return $someError;
   537             }
   538         }
   539     }
   540 
   541     $function = 'CUSTOM_commentPreSave';
   542     if (function_exists($function)) {
   543         $someError = $function($uid, $title, $comment, $sid, $pid, $type, $postmode);
   544         if ($someError) {
   545             // Custom function refused save:
   546             return $someError;
   547         }
   548     }
   549 
   550     return false;
   551 }
   552 
   553 /**
   554 * Allows plugins a chance to handle an item before Geeklog does. Modeled
   555 * after the PLG_commentPreSave() function.
   556 *
   557 * This is a first-come-first-serve affair so if a plugin returns an error, other
   558 * plugins wishing to handle comment preprocessing won't get called
   559 *
   560 * @author Mark Evans, mevans AT ecsnet DOT com
   561 * @access public
   562 * @param string $type Type of item, i.e.; registration, contact ...
   563 * @param string $content item specific content
   564 * @return string empty is no error, error message if error was encountered
   565 * @see PLG_commentPreSave
   566 *
   567 */
   568 function PLG_itemPreSave($type, $content)
   569 {
   570     global $_PLUGINS;
   571 
   572     foreach ($_PLUGINS as $pi_name) {
   573         $function = 'plugin_itemPreSave_' . $pi_name;
   574         if (function_exists ($function)) {
   575             $msgError = $function ($type, $content);
   576             if (!empty($msgError)) {
   577                 // Plugin doesn't want to save the item
   578                 return $msgError;
   579             }
   580         }
   581     }
   582 
   583     $function = 'CUSTOM_itemPreSave';
   584     if (function_exists ($function)) {
   585         $msgError = $function ($type, $content);
   586         if (!empty($msgError)) {
   587             // Custom doesn't want to save the item
   588             return $msgError;
   589         }
   590     }
   591 
   592     return '';
   593 }
   594 
   595 /**
   596 * The way this function works is very specific to how Geeklog shows its
   597 * statistics.  On stats.php, there is the top box which gives overall
   598 * statistics for Geeklog and then there are blocks below it that give
   599 * more specific statistics for various components of Geeklog.
   600 *
   601 * This plugin API function suffers from a variety of bugs and bad design
   602 * decisions for which we have to provide backward compatibility, so please
   603 * bear with us ...
   604 *
   605 * The only parameter to this function, $showsitestats, was documented as being
   606 * being 1 for the site stats and 0 for the plugin-specific stats. However, the
   607 * latter was always called with a value of 2, so plugins only did a check for 1
   608 * and "else", which makes extensions somewhat tricky.
   609 * Furthermore, due to the original templates for the site stats, it has
   610 * become standard practice to hard-code a <table> in the plugins as the return
   611 * value for $showsitestats == 1. This table, however, didn't align properly
   612 * with the built-in site stats entries.
   613 *
   614 * Because of all this, the new mode, 3, works differently:
   615 * - for $showsitestats == 3, we call a new plugin API function,
   616 *   plugin_statssummary_<plugin-name>, which is supposed to return the plugin's
   617 *   entry for the site stats in an array which stats.php will then properly
   618 *   format, alongside the entries for the built-in items.
   619 * - for $showsitestats == 1, we only call those plugins that do NOT have a
   620 *   plugin_statssummary_<plugin-name> function, thus providing backward
   621 *   compatibility
   622 * - for $showsitestats == 2, nothing has changed
   623 *
   624 * @param    int     $showsitestats      value indicating type of stats to return
   625 * @return   mixed                       array (for mode 3) or string
   626 *
   627 */
   628 function PLG_getPluginStats($showsitestats)
   629 {
   630     global $_PLUGINS;
   631 
   632     if ($showsitestats == 3) {
   633         $retval = array();
   634     } else {
   635         $retval = '';
   636     }
   637 
   638     foreach ($_PLUGINS as $pi_name) {
   639         if ($showsitestats == 3) {
   640             $function = 'plugin_statssummary_' . $pi_name;
   641             if (function_exists ($function)) {
   642                 $summary = $function ();
   643                 if (is_array($summary)) {
   644                     $retval[$pi_name] = $summary;
   645                 }
   646             }
   647         } elseif ($showsitestats == 1) {
   648             $function1 = 'plugin_showstats_' . $pi_name;
   649             $function2 = 'plugin_statssummary_' . $pi_name;
   650             if (!function_exists ($function2)) {
   651                 if (function_exists ($function1)) {
   652                     $retval .= $function1 ($showsitestats);
   653                 }
   654             }
   655         } elseif ($showsitestats == 2) {
   656             $function = 'plugin_showstats_' . $pi_name;
   657             if (function_exists ($function)) {
   658                 $retval .= $function ($showsitestats);
   659             }
   660         }
   661     }
   662 
   663     if ($showsitestats == 3) {
   664         $function = 'CUSTOM_statssummary';
   665         if (function_exists ($function)) {
   666             $summary = $function ();
   667             if (is_array($summary)) {
   668                 $retval['Custom'] = $summary;
   669             }
   670         }
   671     } elseif ($showsitestats == 1) {
   672         $function1 = 'CUSTOM_showstats';
   673         $function2 = 'CUSTOM_statssummary';
   674         if (!function_exists ($function2)) {
   675             if (function_exists ($function1)) {
   676                 $retval .= $function1 ($showsitestats);
   677             }
   678         }
   679     } elseif ($showsitestats == 2) {
   680         $function = 'CUSTOM_showstats';
   681         if (function_exists ($function)) {
   682             $retval .= $function ($showsitestats);
   683         }
   684     }
   685 
   686     return $retval;
   687 }
   688 
   689 /**
   690 * This function gives each plugin the opportunity to put a value(s) in
   691 * the 'Type' drop down box on the search.php page so that their plugin
   692 * can be incorporated into searches.
   693 *
   694 * @return   array   String array of search types for plugin(s)
   695 *
   696 */
   697 function PLG_getSearchTypes()
   698 {
   699     global $_PLUGINS;
   700 
   701     $types = array();
   702     $cur_types = array();
   703 
   704     foreach ($_PLUGINS as $pi_name) {
   705         $function = 'plugin_searchtypes_' . $pi_name;
   706         if (function_exists ($function)) {
   707             $cur_types = $function ();
   708             if (is_array($cur_types) && (count($cur_types) > 0)) {
   709                 $types = array_merge ($types, $cur_types);
   710             }
   711         } // no else because this is not a required API function
   712     }
   713 
   714     $function = 'CUSTOM_searchtypes';
   715     if (function_exists ($function)) {
   716         $cur_types = $function ();
   717         if (is_array($cur_types) && (count($cur_types) > 0)) {
   718             $types = array_merge ($types, $cur_types);
   719         }
   720     }
   721 
   722     asort($types);
   723     return $types;
   724 }
   725 
   726 /**
   727 * Determines if a specific plugin supports Geeklog's
   728 * expanded search results feature
   729 *
   730 * NOTE: This function is not currently used
   731 *
   732 * @author   Tony Bibbs, tony AT tonybibbs DOT com
   733 * @access   public
   734 * @param    string  $type   Plugin name
   735 * @return   boolean         True if it is supported, otherwise false
   736 * @deprecated no longer used
   737 *
   738 */
   739 function PLG_supportsExpandedSearch($type)
   740 {
   741     $retval = '';
   742     $function = 'plugin_supportsexpandedsearch_' . $type;
   743     if (function_exists($function)) {
   744         $retval = $function();
   745     }
   746     if (empty($retval) OR !is_bool($retval)) {
   747         $retval = false;
   748     }
   749 
   750     return $retval;
   751 }
   752 
   753 /**
   754 * This function gives each plugin the opportunity to do their search
   755 * and return their results.  Results come back in an array of HTML
   756 * formatted table rows that can be quickly printed by search.php
   757 *
   758 * @param    string  $query      What the user searched for
   759 * @param    date    $datestart  beginning of date range to search for
   760 * @param    date    $dateend    ending date range to search for
   761 * @param    string  $topic      the topic the user searched within
   762 * @param    string  $type       Type of items they are searching, or 'all'
   763 * @param    int     $author     UID...only return results for this person
   764 * @param    string  $keyType    search key type: 'all', 'phrase', 'any'
   765 * @param    int     $page       page number of current search (deprecated)
   766 * @param    int     $perpage    number of results per page (deprecated)
   767 * @return   array               Returns search results
   768 *
   769 */
   770 function PLG_doSearch($query, $datestart, $dateend, $topic, $type, $author, $keyType = 'all', $page = 1, $perpage = 10)
   771 {
   772     global $_PLUGINS;
   773 
   774     /**
   775      * The API, as of 1.6.0, does not use $page, $perpage
   776      * $type is now only used in the core and should not be passed to the plugin
   777      */
   778 
   779     $search_results = array();
   780 
   781     foreach ($_PLUGINS as $pi_name) {
   782         $function = 'plugin_dopluginsearch_' . $pi_name;
   783         if (function_exists($function)) {
   784             $result = $function($query, $datestart, $dateend, $topic, $type, $author, $keyType, $page, $perpage);
   785             if (is_array($result)) {
   786                 $search_results = array_merge($search_results, $result);
   787             } else {
   788                 $search_results[] = $result;
   789             }
   790         }
   791         // no else because implementation of this API function not required
   792     }
   793 
   794     $function = 'CUSTOM_dopluginsearch';
   795     if (function_exists($function)) {
   796         $search_results[] = $function($query, $datestart, $dateend, $topic, $type, $author, $keyType, $page, $perpage);
   797     }
   798 
   799     return $search_results;
   800 }
   801 
   802 /**
   803 * Asks each plugin to report any submissions they may have in their
   804 * submission queue
   805 *
   806 * @return   int     Number of submissions in queue for plugins
   807 *
   808 */
   809 function PLG_getSubmissionCount()
   810 {
   811     global $_PLUGINS;
   812 
   813     $num = 0;
   814     foreach ($_PLUGINS as $pi_name) {
   815         $function = 'plugin_submissioncount_' . $pi_name;
   816         if (function_exists($function)) {
   817             $num = $num + $function();
   818         }
   819     }
   820 
   821     return $num;
   822 }
   823 
   824 /**
   825 * This function will get & check user or admin options from plugins and check
   826 * required ones for availability. This function is called by several other
   827 * functions and is not to be called from the plugin directly. The function which
   828 * call this here follow below.
   829 *
   830 * NOTE for plugin developers:
   831 * The plugin is responsible for its own security.
   832 * This supports a plugin having either a single menuitem or multiple menuitems.
   833 * The plugin has to provide an array for the menuitem of the format:
   834 * <code>
   835 * array(menuitem_title, item_url, submission_count)
   836 * </code>
   837 * or an array of arrays in case there are several entries:
   838 * <code>
   839 * array(
   840 *   array(menuitem1_title, item1_url, submission1_count),
   841 *   array(menuitem2_title, item2_url, submission2_count),
   842 *   array(menuitem3_title, item3_url, submission3_count))
   843 * </code>
   844 * Plugin function can return a single record array or multiple records
   845 *
   846 *
   847 * @param    array $var_names    An array of the variables that are retrieved.
   848 *                               This has to match the named array that is used
   849 *                               in the function returning the values
   850 * @param    array $required_names An array of true/false-values, describing
   851 *                                 which of the above listed values is required
   852 *                                 to give a valid set of data.
   853 * @param    string $function_name A string that gives the name of the function
   854 *                                 at the plugin that will return the values.
   855 * @return   array Returns options to add to the given menu that is calling this
   856 * @access   private
   857 * @internal not to be used by plugins
   858 *
   859 */
   860 function PLGINT_getOptionsforMenus($var_names, $required_names, $function_name)
   861 {
   862     global $_PLUGINS;
   863 
   864     $plgresults = array();
   865 
   866     $num_var_names = count($var_names);
   867     foreach ($_PLUGINS as $pi_name) {
   868         $function = $function_name . $pi_name;
   869         if (function_exists($function)) {
   870             $plg_array = $function();
   871             if (($plg_array !== false) && (count($plg_array) > 0)) {
   872                 // Check if plugin is returning a single record array or multiple records
   873                 $sets_array = array();
   874                 $entries = count($plg_array[0]);
   875                 if ($entries == 1) {
   876                     // Single record - so we need to prepare the sets_array;
   877                     $sets_array[0] = $plg_array;
   878                 } else {
   879                     // Multiple menuitem records - in required format
   880                     $sets_array = $plg_array;
   881                 }
   882                 foreach ($sets_array as $val) {
   883                     $plugin = new Plugin();
   884                     $good_array = true;
   885                     for ($n = 0; $n < $num_var_names; $n++) {
   886                         if (isset($val[$n])) {
   887                             $plugin->$var_names[$n] = $val[$n];
   888                         } else {
   889                             $plugin->$var_names[$n] = '';
   890                         }
   891                         if (empty($plugin->$var_names[$n]) && $required_names[$n]) {
   892                             $good_array = false;
   893                         }
   894                     }
   895 
   896                     if ($good_array) {
   897                         $plgresults[] = $plugin;
   898                     }
   899                 }
   900             }
   901         }
   902     }
   903 
   904     return $plgresults;
   905 }
   906 
   907 /**
   908 * This function shows the option for all plugins at the top of the
   909 * command and control center.
   910 *
   911 * This supports that a plugin can have several lines in the CC menu.
   912 * The plugin has to provide simply a set arrays with 3 variables in order to
   913 * get n lines in the menu such as
   914 * <code>
   915 * array(
   916 *   array("first line", "url1", "1"),
   917 *   array("second line", "url2", "44"),
   918 *            etc, etc)
   919 * </code>
   920 * If there is only one item, a single array is enough:
   921 * <code>
   922 * array("first line", "url1", "1")
   923 * </code>
   924 *
   925 * @return   array   Returns Command and Control options for moderation.php
   926 *
   927 */
   928 function PLG_getCCOptions()
   929 {
   930     $var_names = array('adminlabel', 'adminurl', 'plugin_image');
   931     $required_names = array(true, true, true);
   932     $function_name = 'plugin_cclabel_';
   933     $plgresults = PLGINT_getOptionsforMenus($var_names, $required_names, $function_name);
   934 
   935     return $plgresults;
   936 }
   937 
   938 /**
   939 * This function will show any plugin adminstrative options in the
   940 * admin functions block on every page (assuming the user is an admin
   941 * and is logged in).
   942 *
   943 * NOTE: the plugin is responsible for its own security.
   944 * This supports that a plugin can have several lines in the Admin menu.
   945 * The plugin has to provide simply a set arrays with 3 variables in order to
   946 * get n lines in the menu such as
   947 * <code>
   948 * array(
   949 *   array("first line", "url1", "1"),
   950 *   array("second line", "url2", "44"),,
   951 *            etc, etc)
   952 * </code>
   953 * If there is only one item, a single array is enough:
   954 * <code>
   955 * array("first line", "url1", "1")
   956 * </code>
   957 *
   958 * @return   array   Returns options to put in admin menu
   959 *
   960 */
   961 function PLG_getAdminOptions()
   962 {
   963     $var_names = array('adminlabel', 'adminurl', 'numsubmissions');
   964     $required_names = array(true, true, false);
   965     $function_name = 'plugin_getadminoption_';
   966     $plgresults = PLGINT_getOptionsforMenus($var_names, $required_names, $function_name);
   967 
   968     return $plgresults;
   969 }
   970 
   971 /**
   972 * This function will show any plugin user options in the
   973 * user block on every page
   974 *
   975 * This supports that a plugin can have several lines in the User menu.
   976 * The plugin has to provide simply a set of arrays with 3 variables in order to
   977 * get n lines in the menu such as
   978 * <code>
   979 * array(
   980 *   array("first line", "url1", "1"),
   981 *   array("second line", "url2", "44"),
   982 *            etc, etc)
   983 * </code>
   984 * If there is only one item, a single array is enough:
   985 * <code>
   986 * array("first line", "url1", "1")
   987 * </code>
   988 *
   989 * NOTE: the plugin is responsible for its own security.
   990 *
   991 * @return   array   Returns options to add to user menu
   992 *
   993 */
   994 function PLG_getUserOptions()
   995 {
   996     // I know this uses the adminlabel, adminurl but who cares?
   997     $var_names = array('adminlabel', 'adminurl', 'numsubmissions');
   998     $required_names = array(true, true, false);
   999     $function_name = 'plugin_getuseroption_';
  1000     $plgresults = PLGINT_getOptionsforMenus($var_names, $required_names, $function_name);
  1001 
  1002     return $plgresults;
  1003 }
  1004 
  1005 /**
  1006 * This function is responsible for calling
  1007 * plugin_moderationapproves_<pluginname> which approves an item from the
  1008 * submission queue for a plugin.
  1009 *
  1010 * @param        string      $type       Plugin name to do submission approval for
  1011 * @param        string      $id         used to identify the record to approve
  1012 * @return       boolean     Returns true on success otherwise false
  1013 *
  1014 */
  1015 function PLG_approveSubmission($type, $id)
  1016 {
  1017     $args[1] = $id;
  1018 
  1019     return PLG_callFunctionForOnePlugin('plugin_moderationapprove_' . $type, $args);
  1020 }
  1021 
  1022 /**
  1023 * This function is responsible for calling
  1024 * plugin_moderationdelete_<pluginname> which deletes an item from the
  1025 * submission queue for a plugin.
  1026 *
  1027 * @param        string      $type       Plugin to do submission deletion for
  1028 * @param        string      $id         used to identify the record for which to delete
  1029 * @return       boolean     Returns true on success otherwise false
  1030 *
  1031 */
  1032 function PLG_deleteSubmission($type, $id)
  1033 {
  1034     $args[1] = $id;
  1035 
  1036     return PLG_callFunctionForOnePlugin('plugin_moderationdelete_' . $type, $args);
  1037 }
  1038 
  1039 /**
  1040 * This function calls the plugin_savesubmission_<pluginname> to save
  1041 * a user submission
  1042 *
  1043 * @param        string      $type       Plugin to save submission for
  1044 * @param        array       $A          holds plugin specific data to save
  1045 * @return       boolean     Returns true on success otherwise false
  1046 *
  1047 */
  1048 function PLG_saveSubmission($type, $A)
  1049 {
  1050     $args[1] = $A;
  1051 
  1052     return PLG_callFunctionForOnePlugin('plugin_savesubmission_' . $type, $args);
  1053 }
  1054 
  1055 /**
  1056 * This function starts the chain of calls needed to show any submissions
  1057 * needing moderation for the plugins.
  1058 *
  1059 * @param    string  $token  security token
  1060 * @return   string          returns list of items needing moderation for plugins
  1061 *
  1062 */
  1063 function PLG_showModerationList($token)
  1064 {
  1065     global $_PLUGINS;
  1066 
  1067     $retval = '';
  1068 
  1069     foreach ($_PLUGINS as $pi_name) {
  1070         $retval .= itemlist($pi_name, $token);
  1071     }
  1072 
  1073     return $retval;
  1074 }
  1075 
  1076 /**
  1077 * This function is responsible for setting the plugin-specific values
  1078 * needed by moderation.php to approve stuff.
  1079 *
  1080 * @param        string      $type       Plugin to call function for
  1081 * @return       string
  1082 *
  1083 */
  1084 function PLG_getModerationValues($type)
  1085 {
  1086     return PLG_callFunctionForOnePlugin('plugin_moderationvalues_' . $type);
  1087 }
  1088 
  1089 /**
  1090 * This function is resonsible for calling plugin_submit_<pluginname> so
  1091 * that the submission form for the plugin is displayed.
  1092 *
  1093 * @param        string      $type       Plugin to show submission form for
  1094 * @return       string      HTML for submit form for plugin
  1095 *
  1096 */
  1097 function PLG_showSubmitForm($type)
  1098 {
  1099     return PLG_callFunctionForOnePlugin('plugin_submit_' . $type);
  1100 }
  1101 
  1102 /**
  1103 * This function will show the centerblock for any plugin.
  1104 *
  1105 * Plugin can display some of their own content in a block on the index or any
  1106 * topic index page. The block can be at the top or bottom of the page, after
  1107 * the featured story or the plugin can take over the entire page.
  1108 * The plugin is responsible to format the output correctly.
  1109 *
  1110 * @param    int     $where  where 1 = top, 2 = after feat. story, 3 = bottom of page, 0 = entire page
  1111 * @param    int     $page   page number (1, ...)
  1112 * @param    string  $topic  topic ID or empty string == front page
  1113 * @return   string          Formatted center block content
  1114 * @since    Geeklog 1.3.8
  1115 *
  1116 */
  1117 function PLG_showCenterblock($where = 1, $page = 1, $topic = '')
  1118 {
  1119     global $PLG_bufferCenterAPI, $PLG_buffered, $_PLUGINS;
  1120 
  1121     $retval = '';
  1122 
  1123     // buffer function names since we're coming back for them two more times
  1124     if (!$PLG_buffered) {
  1125         $PLG_bufferCenterAPI = array();
  1126         foreach ($_PLUGINS as $pi_name) {
  1127             $function = 'plugin_centerblock_' . $pi_name;
  1128             if (function_exists($function)) {
  1129                 $PLG_bufferCenterAPI[$pi_name] = $function;
  1130             }
  1131         }
  1132         $PLG_buffered = true;
  1133     }
  1134 
  1135     foreach ($PLG_bufferCenterAPI as $function) {
  1136         $retval .= $function($where, $page, $topic);
  1137 
  1138         if (($where == 0) && !empty($retval)) {
  1139             break;
  1140         }
  1141     }
  1142     $function = 'CUSTOM_centerblock';
  1143     if (function_exists($function)) {
  1144         $retval .= $function($where, $page, $topic);
  1145     }
  1146 
  1147     return $retval;
  1148 }
  1149 
  1150 /**
  1151 * This function will inform all plugins when a new user account is created.
  1152 *
  1153 * @param    int     $uid    user id of the new user account
  1154 * @return   void
  1155 *
  1156 */
  1157 function PLG_createUser($uid)
  1158 {
  1159     global $_PLUGINS;
  1160 
  1161     foreach ($_PLUGINS as $pi_name) {
  1162         $function = 'plugin_user_create_' . $pi_name;
  1163         if (function_exists($function)) {
  1164             $function ($uid);
  1165         }
  1166     }
  1167 
  1168     $function = 'CUSTOM_user_create';
  1169     if (function_exists($function)) {
  1170         $function($uid);
  1171     }
  1172 }
  1173 
  1174 /**
  1175 * This function will inform all plugins when a user account is deleted.
  1176 *
  1177 * @param    int     $uid    user id of the deleted user account
  1178 * @return   void
  1179 *
  1180 */
  1181 function PLG_deleteUser($uid)
  1182 {
  1183     global $_PLUGINS;
  1184 
  1185     foreach ($_PLUGINS as $pi_name) {
  1186         $function = 'plugin_user_delete_' . $pi_name;
  1187         if (function_exists ($function)) {
  1188             $function($uid);
  1189         }
  1190     }
  1191 
  1192     $function = 'CUSTOM_user_delete';
  1193     if (function_exists($function)) {
  1194         $function($uid);
  1195     }
  1196 }
  1197 
  1198 /**
  1199 * This function will inform all plugins when a user logs in
  1200 *
  1201 * Note: This function is NOT called when users are re-authenticated by their
  1202 * long-term cookie. The global variable $_USER['auto_login'] will be set to
  1203 * 'true' in that case, however.
  1204 *
  1205 * @param    int     $uid    user id
  1206 * @return   void
  1207 *
  1208 */
  1209 function PLG_loginUser($uid)
  1210 {
  1211     global $_PLUGINS;
  1212 
  1213     foreach ($_PLUGINS as $pi_name) {
  1214         $function = 'plugin_user_login_' . $pi_name;
  1215         if (function_exists($function)) {
  1216             $function($uid);
  1217         }
  1218     }
  1219 
  1220     $function = 'CUSTOM_user_login';
  1221     if (function_exists($function)) {
  1222         $function($uid);
  1223     }
  1224 }
  1225 
  1226 /**
  1227 * This function will inform all plugins when a user logs out.
  1228 * Plugins should not rely on this ever being called, as the user may simply
  1229 * close the browser instead of logging out.
  1230 *
  1231 * @param    int     $uid    user id
  1232 * @return   void
  1233 *
  1234 */
  1235 function PLG_logoutUser($uid)
  1236 {
  1237     global $_PLUGINS;
  1238 
  1239     foreach ($_PLUGINS as $pi_name) {
  1240         $function = 'plugin_user_logout_' . $pi_name;
  1241         if (function_exists($function)) {
  1242             $function($uid);
  1243         }
  1244     }
  1245 
  1246     $function = 'CUSTOM_user_logout';
  1247     if (function_exists($function)) {
  1248         $function($uid);
  1249     }
  1250 }
  1251 
  1252 /**
  1253 * This function is called to inform plugins when a user's information
  1254 * (profile or preferences) has changed.
  1255 *
  1256 * @param    int     $uid    user id
  1257 * @return   void
  1258 *
  1259 */
  1260 function PLG_userInfoChanged($uid)
  1261 {
  1262     global $_PLUGINS;
  1263 
  1264     foreach ($_PLUGINS as $pi_name) {
  1265         $function = 'plugin_user_changed_' . $pi_name;
  1266         if (function_exists($function)) {
  1267             $function($uid);
  1268         }
  1269     }
  1270 
  1271     $function = 'CUSTOM_user_changed';
  1272     if (function_exists($function)) {
  1273         $function($uid);
  1274     }
  1275 }
  1276 
  1277 /**
  1278 * This function is called to inform plugins when a group's information has
  1279 * changed or a new group has been created.
  1280 *
  1281 * @param    int     $grp_id     Group ID
  1282 * @param    string  $mode       type of change: 'new', 'edit', or 'delete'
  1283 * @return   void
  1284 *
  1285 */
  1286 function PLG_groupChanged($grp_id, $mode)
  1287 {
  1288     global $_PLUGINS;
  1289 
  1290     foreach ($_PLUGINS as $pi_name) {
  1291         $function = 'plugin_group_changed_' . $pi_name;
  1292         if (function_exists($function)) {
  1293             $function($grp_id, $mode);
  1294         }
  1295     }
  1296 
  1297     $function = 'CUSTOM_group_changed';
  1298     if (function_exists($function)) {
  1299         $function($uid);
  1300     }
  1301 }
  1302 
  1303 /**
  1304 * Geeklog is about to display the edit form for the user's profile. Plugins
  1305 * now get a chance to add their own variables and input fields to the form.
  1306 *
  1307 * @param    int  $uid        user id of the user profile to be edited
  1308 * @param    ref &$template   reference of the Template for the profile edit form
  1309 * @return   void
  1310 *
  1311 */
  1312 function PLG_profileVariablesEdit($uid, &$template)
  1313 {
  1314     global $_PLUGINS;
  1315 
  1316     foreach ($_PLUGINS as $pi_name) {
  1317         $function = 'plugin_profilevariablesedit_' . $pi_name;
  1318         if (function_exists($function)) {
  1319             $function ($uid, $template);
  1320         }
  1321     }
  1322 
  1323     $function = 'CUSTOM_profilevariablesedit';
  1324     if (function_exists($function)) {
  1325         $function($uid, $template);
  1326     }
  1327 }
  1328 
  1329 /**
  1330 * Geeklog is about to display the edit form for the user's profile. Plugins
  1331 * now get a chance to add their own blocks below the standard form.
  1332 *
  1333 * @param    int      $uid   user id of the user profile to be edited
  1334 * @return   string          HTML for additional block(s)
  1335 *
  1336 */
  1337 function PLG_profileBlocksEdit($uid)
  1338 {
  1339     global $_PLUGINS;
  1340 
  1341     $retval = '';
  1342 
  1343     foreach ($_PLUGINS as $pi_name) {
  1344         $function = 'plugin_profileblocksedit_' . $pi_name;
  1345         if (function_exists($function)) {
  1346             $retval .= $function ($uid);
  1347         }
  1348     }
  1349 
  1350     $function = 'CUSTOM_profileblocksedit';
  1351     if (function_exists($function)) {
  1352         $retval .= $function($uid);
  1353     }
  1354 
  1355     return $retval;
  1356 }
  1357 
  1358 /**
  1359 * Geeklog is about to display the user's profile. Plugins now get a chance to
  1360 * add their own variables to the profile.
  1361 *
  1362 * @param   int   $uid        user id of the user profile to be edited
  1363 * @param   ref  &$template   reference of the Template for the profile edit form
  1364 * @return  void
  1365 *
  1366 */
  1367 function PLG_profileVariablesDisplay($uid, &$template)
  1368 {
  1369     global $_PLUGINS;
  1370 
  1371     foreach ($_PLUGINS as $pi_name) {
  1372         $function = 'plugin_profilevariablesdisplay_' . $pi_name;
  1373         if (function_exists($function)) {
  1374             $function ($uid, $template);
  1375         }
  1376     }
  1377 
  1378     $function = 'CUSTOM_profilevariablesdisplay';
  1379     if (function_exists($function)) {
  1380         $function($uid, $template);
  1381     }
  1382 }
  1383 
  1384 /**
  1385 * Geeklog is about to display the user's profile. Plugins now get a chance to
  1386 * add their own blocks below the standard profile form.
  1387 *
  1388 * @param    int      $uid        user id of the user profile to be edited
  1389 * @return   string               HTML for additional block(s)
  1390 *
  1391 */
  1392 function PLG_profileBlocksDisplay($uid)
  1393 {
  1394     global $_PLUGINS;
  1395 
  1396     $retval = '';
  1397 
  1398     foreach ($_PLUGINS as $pi_name) {
  1399         $function = 'plugin_profileblocksdisplay_' . $pi_name;
  1400         if (function_exists($function)) {
  1401             $retval .= $function ($uid);
  1402         }
  1403     }
  1404 
  1405     $function = 'CUSTOM_profileblocksdisplay';
  1406     if (function_exists($function)) {
  1407         $retval .= $function($uid);
  1408     }
  1409 
  1410     return $retval;
  1411 }
  1412 
  1413 /**
  1414 * The user wants to save changes to his/her profile. Any plugin that added its
  1415 * own variables or blocks to the profile input form will now have to extract
  1416 * its data and save it.
  1417 * Plugins will have to refer to the global $_POST array to get the
  1418 * actual data.
  1419 *
  1420 * @param    string  $plugin     name of a specific plugin or empty(all plugins)
  1421 * @return   void
  1422 *
  1423 */
  1424 function PLG_profileExtrasSave($plugin = '')
  1425 {
  1426     if (empty($plugin)) {
  1427         PLG_callFunctionForAllPlugins ('profileextrassave');
  1428     } else {
  1429         PLG_callFunctionForOnePlugin ('plugin_profileextrassave_' . $plugin);
  1430     }
  1431 }
  1432 
  1433 /**
  1434 * This function can be called to check if an plugin wants to set a template
  1435 * variable
  1436 *
  1437 * Example in COM_siteHeader, the API call is now added
  1438 * A plugin can check for $templatename == 'header' and then set additional
  1439 * template variables
  1440 *
  1441 * @param    string   $templatename  Name of calling template
  1442 * @param    ref     &$template      reference for the Template
  1443 * @return   void
  1444 * @see      CUSTOM_templateSetVars
  1445 *
  1446 */
  1447 function PLG_templateSetVars($templatename, &$template)
  1448 {
  1449     global $_PLUGINS;
  1450 
  1451     foreach ($_PLUGINS as $pi_name) {
  1452         $function = 'plugin_templatesetvars_' . $pi_name;
  1453         if (function_exists($function)) {
  1454             $function ($templatename, $template);
  1455         }
  1456     }
  1457 
  1458     if (function_exists('CUSTOM_templateSetVars')) {
  1459         CUSTOM_templatesetvars($templatename, $template);
  1460     }
  1461 }
  1462 
  1463 /**
  1464 * This function is called from COM_siteHeader and will return additional header
  1465 * information. This can be used for JavaScript functions required for the plugin
  1466 * or extra Metatags
  1467 *
  1468 * @return   string      returns a concatenated string of all plugins extra header code
  1469 * @since    Geeklog 1.3.8
  1470 *
  1471 */
  1472 function PLG_getHeaderCode()
  1473 {
  1474     global $_PLUGINS;
  1475 
  1476     $headercode = '';
  1477 
  1478     foreach ($_PLUGINS as $pi_name) {
  1479         $function = 'plugin_getheadercode_' . $pi_name;
  1480         if (function_exists($function)) {
  1481             $headercode .= $function();
  1482         }
  1483     }
  1484 
  1485     $function = 'CUSTOM_getheadercode';
  1486     if (function_exists($function)) {
  1487         $headercode .= $function();
  1488     }
  1489 
  1490     return $headercode;
  1491 }
  1492 
  1493 /**
  1494 * Get a list of all currently supported autolink tags.
  1495 *
  1496 * Returns an associative array where $A['tag-name'] = 'plugin-name'
  1497 *
  1498 * @return   array   All currently supported autolink tags
  1499 * @access   private
  1500 * @internal not to be used by plugins
  1501 *
  1502 */
  1503 function PLG_collectTags()
  1504 {
  1505     global $_CONF, $_PLUGINS;
  1506 
  1507     if (isset($_CONF['disable_autolinks']) && ($_CONF['disable_autolinks'] == 1)) {
  1508         // autolinks are disabled - return an empty array
  1509         return array();
  1510     }
  1511 
  1512     // Determine which Core Modules and Plugins support AutoLinks
  1513     //                        'tag'   => 'module'
  1514     $autolinkModules = array('story' => 'geeklog');
  1515 
  1516     foreach ($_PLUGINS as $pi_name) {
  1517         $function = 'plugin_autotags_' . $pi_name;
  1518         if (function_exists($function)) {
  1519             $autotag = $function ('tagname');
  1520             if (is_array($autotag)) {
  1521                 foreach ($autotag as $tag) {
  1522                     $autolinkModules[$tag] = $pi_name;
  1523                 }
  1524             } else {
  1525                 $autolinkModules[$autotag] = $pi_name;
  1526             }
  1527         }
  1528     }
  1529 
  1530     return $autolinkModules;
  1531 }
  1532 
  1533 /**
  1534 * This function will allow plugins to support the use of custom autolinks
  1535 * in other site content. Plugins can now use this API when saving content
  1536 * and have the content checked for any autolinks before saving.
  1537 * The autolink would be like:  [story:20040101093000103 here]
  1538 *
  1539 * @param   string   $content   Content that should be parsed for autolinks
  1540 * @param   string   $plugin    Optional if you only want to parse using a specific plugin
  1541 *
  1542 */
  1543 function PLG_replaceTags($content, $plugin = '')
  1544 {
  1545     global $_CONF, $_TABLES, $LANG32;
  1546 
  1547     if (isset($_CONF['disable_autolinks']) && ($_CONF['disable_autolinks'] == 1)) {
  1548         // autolinks are disabled - return $content unchanged
  1549         return $content;
  1550     }
  1551 
  1552     $autolinkModules = PLG_collectTags();
  1553 
  1554     // For each supported module, scan the content looking for any AutoLink tags
  1555     $tags = array();
  1556     $contentlen = MBYTE_strlen($content);
  1557     $content_lower = MBYTE_strtolower($content);
  1558     foreach ($autolinkModules as $moduletag => $module) {
  1559         $autotag_prefix = '['. $moduletag . ':';
  1560         $offset = 0;
  1561         $prev_offset = 0;
  1562         while ($offset < $contentlen) {
  1563             $start_pos = MBYTE_strpos($content_lower, $autotag_prefix,
  1564                                       $offset);
  1565             if ($start_pos === false) {
  1566                 break;
  1567             } else {
  1568                 $end_pos  = MBYTE_strpos($content_lower, ']', $start_pos);
  1569                 $next_tag = MBYTE_strpos($content_lower, '[', $start_pos + 1);
  1570                 if (($end_pos > $start_pos) AND
  1571                         (($next_tag === false) OR ($end_pos < $next_tag))) {
  1572                     $taglength = $end_pos - $start_pos + 1;
  1573                     $tag = MBYTE_substr($content, $start_pos, $taglength);
  1574                     $parms = explode(' ', $tag);
  1575 
  1576                     // Extra test to see if autotag was entered with a space
  1577                     // after the module name
  1578                     if (MBYTE_substr($parms[0], -1) == ':') {
  1579                         $startpos = MBYTE_strlen($parms[0]) + MBYTE_strlen($parms[1]) + 2;
  1580                         $label = str_replace(']', '', MBYTE_substr($tag, $startpos));
  1581                         $tagid = $parms[1];
  1582                     } else {
  1583                         $label = str_replace(']', '', MBYTE_substr($tag,
  1584                                                 MBYTE_strlen($parms[0]) + 1));
  1585                         $parms = explode(':', $parms[0]);
  1586                         if (count($parms) > 2) {
  1587                             // whoops, there was a ':' in the tag id ...
  1588                             array_shift($parms);
  1589                             $tagid = implode(':', $parms);
  1590                         } else {
  1591                             $tagid = $parms[1];
  1592                         }
  1593                     }
  1594 
  1595                     $newtag = array(
  1596                         'module'    => $module,
  1597                         'tag'       => $moduletag,
  1598                         'tagstr'    => $tag,
  1599                         'startpos'  => $start_pos,
  1600                         'length'    => $taglength,
  1601                         'parm1'     => str_replace(']', '', $tagid),
  1602                         'parm2'     => $label
  1603                     );
  1604                     $tags[] = $newtag;
  1605                 } else {
  1606                     // Error: tags do not match - return with no changes
  1607                     return $content . $LANG32[32];
  1608                 }
  1609                 $prev_offset = $offset;
  1610                 $offset = $end_pos;
  1611             }
  1612         }
  1613     }
  1614 
  1615     // If we have found 1 or more AutoLink tag
  1616     if (count($tags) > 0) {       // Found the [tag] - Now process them all
  1617         foreach ($tags as $autotag) {
  1618             $function = 'plugin_autotags_' . $autotag['module'];
  1619             if (($autotag['module'] == 'geeklog') AND
  1620                     (empty($plugin) OR ($plugin == 'geeklog'))) {
  1621                 $url = '';
  1622                 $linktext = $autotag['parm2'];
  1623                 if ($autotag['tag'] == 'story') {
  1624                     $autotag['parm1'] = COM_applyFilter($autotag['parm1']);
  1625                     if (! empty($autotag['parm1'])) {
  1626                         $url = COM_buildUrl($_CONF['site_url']
  1627                              . '/article.php?story=' . $autotag['parm1']);
  1628                         if (empty($linktext)) {
  1629                             $linktext = stripslashes(DB_getItem($_TABLES['stories'], 'title', "sid = '{$autotag['parm1']}'"));
  1630                         }
  1631                     }
  1632                 }
  1633 
  1634                 if (!empty($url)) {
  1635                     $filelink = COM_createLink($linktext, $url);
  1636                     $content = str_replace($autotag['tagstr'], $filelink,
  1637                                            $content);
  1638                 }
  1639             } elseif (function_exists($function) AND
  1640                     (empty($plugin) OR ($plugin == $autotag['module']))) {
  1641                 $content = $function('parse', $content, $autotag);
  1642             }
  1643         }
  1644     }
  1645 
  1646     return $content;
  1647 }
  1648 
  1649 
  1650 /**
  1651 * Prepare a list of all plugins that support feeds. To do this, we re-use
  1652 * plugin_getfeednames_<plugin name> and only keep the names of those plugins
  1653 * which support that function
  1654 *
  1655 * @return   array   array of plugin names (can be empty)
  1656 *
  1657 */
  1658 function PLG_supportingFeeds()
  1659 {
  1660     global $_PLUGINS;
  1661 
  1662     $plugins = array();
  1663 
  1664     foreach ($_PLUGINS as $pi_name) {
  1665         $function = 'plugin_getfeednames_' . $pi_name;
  1666         if (function_exists($function)) {
  1667             $feeds = $function();
  1668             if (is_array($feeds) && (count($feeds) > 0)) {
  1669                 $plugins[] = $pi_name;
  1670             }
  1671         }
  1672     }
  1673 
  1674     $function = 'CUSTOM_getfeednames';
  1675     if (function_exists($function)) {
  1676         $feeds = $function();
  1677         if (is_array($feeds) && (count($feeds) > 0)) {
  1678             $plugins[] = 'custom';
  1679         }
  1680     }
  1681 
  1682     return $plugins;
  1683 }
  1684 
  1685 /**
  1686 * Ask the plugin for a list of feeds it supports. The plugin is expected to
  1687 * return an array of id/name pairs where 'id' is the plugin's internal id
  1688 * for the feed and 'name' is what will be presented to the user.
  1689 *
  1690 * @param    string   $plugin  plugin name
  1691 * @return   array             array of id/name pairs
  1692 *
  1693 */
  1694 function PLG_getFeedNames($plugin)
  1695 {
  1696     global $_PLUGINS;
  1697 
  1698     $feeds = array();
  1699 
  1700     if ($plugin == 'custom')
  1701     {
  1702         $function = 'CUSTOM_getfeednames';
  1703         if (function_exists($function)) {
  1704             $feeds = $function();
  1705         }
  1706     } else {
  1707         if (in_array($plugin, $_PLUGINS)) {
  1708             $function = 'plugin_getfeednames_' . $plugin;
  1709             if (function_exists($function)) {
  1710                 $feeds = $function();
  1711             }
  1712         }
  1713     }
  1714 
  1715 
  1716 
  1717     return $feeds;
  1718 }
  1719 
  1720 /**
  1721 * Get the content of a feed from the plugin.
  1722 *
  1723 * The plugin is expected to return an array holding the content of the feed
  1724 * and to fill in 'link' (some link that represents the same content on the
  1725 * site as that in the feed) and 'update_data' (to be stored for later up-to-date
  1726 * checks.
  1727 *
  1728 * @param    string   $plugin       plugin name
  1729 * @param    int      $feed         feed id
  1730 * @param    string  &$link         link to content on the site
  1731 * @param    string  &$update_data  information for later up-to-date checks
  1732 * @param    string   $feedType     The type of feed (RSS/Atom etc)
  1733 * @param    string   $feedVersion  The version info of the feed.
  1734 * @return   array                  content of feed
  1735 *
  1736 */
  1737 function PLG_getFeedContent($plugin, $feed, &$link, &$update_data, $feedType, $feedVersion)
  1738 {
  1739     global $_PLUGINS;
  1740 
  1741     $content = array();
  1742 
  1743     if ($plugin == 'custom') {
  1744         $function = 'CUSTOM_getfeedcontent';
  1745         if (function_exists($function)) {
  1746             $content = $function($feed, $link, $update_data, $feedType, $feedVersion);
  1747         }
  1748     } else {
  1749         if (in_array($plugin, $_PLUGINS)) {
  1750             $function = 'plugin_getfeedcontent_' . $plugin;
  1751             if (function_exists ($function)) {
  1752                 $content = $function ($feed, $link, $update_data, $feedType, $feedVersion);
  1753             }
  1754         }
  1755     }
  1756 
  1757     return $content;
  1758 }
  1759 
  1760 /**
  1761   * Get extension tags for a feed. For example, some plugins may extened the
  1762   * available elements for an RSS 2.0 feed for articles. For some reason. This
  1763   * function allows that.
  1764   *
  1765   * @param  string  $contentType    Type of feed content, article or a plugin specific type
  1766   * @param  string  $contentID      Unique identifier of content item to extend
  1767   * @param  string  $feedType       Type of feed format (RSS/Atom/etc)
  1768   * @param  string  $feedVersion    Type of feed version (RSS 1.0 etc)
  1769   * @param  string  $topic          The topic for the feed.
  1770   * @param  string  $fid            The ID of the feed being fetched.
  1771   * @return array                   list of extension tags
  1772   *
  1773   */
  1774 function PLG_getFeedElementExtensions($contentType, $contentID, $feedType, $feedVersion, $topic, $fid)
  1775 {
  1776     global $_PLUGINS;
  1777 
  1778     $extensions = array();
  1779     foreach( $_PLUGINS as $plugin )
  1780     {
  1781         $function = 'plugin_feedElementExtensions_'.$plugin;
  1782         if (function_exists($function))
  1783         {
  1784             $extensions = array_merge($extensions, $function($contentType, $contentID, $feedType, $feedVersion, $topic, $fid));
  1785         }
  1786     }
  1787 
  1788     $function = 'CUSTOM_feedElementExtensions';
  1789     if (function_exists($function))
  1790     {
  1791         $extensions = array_merge($extensions, $function($contentType, $contentID, $feedType, $feedVersion, $topic, $fid));
  1792     }
  1793 
  1794     return $extensions;
  1795 }
  1796 
  1797 /**
  1798   * Get namespaces extensions for a feed. If a plugin has added extended tags
  1799   * to a feed, then it may also need to insert some extensions to the name
  1800   * spaces.
  1801   *
  1802   * @param  string  $contentType    Type of feed content, article or a plugin specific type
  1803   * @param  string  $feedType       Type of feed format (RSS/Atom/etc)
  1804   * @param  string  $feedVersion    Type of feed version (RSS 1.0 etc)
  1805   * @param  string  $topic          The topic for the feed.
  1806   * @param  string  $fid            The ID of the feed being fetched.
  1807   * @return array                   list of extension namespaces
  1808   *
  1809   */
  1810 function PLG_getFeedNSExtensions($contentType, $feedType, $feedVersion, $topic, $fid)
  1811 {
  1812     global $_PLUGINS;
  1813 
  1814     $namespaces = array();
  1815     foreach( $_PLUGINS as $plugin )
  1816     {
  1817         $function = 'plugin_feedNSExtensions_'.$plugin;
  1818         if (function_exists($function))
  1819         {
  1820             $namespaces = array_merge($namespaces, $function($contentType, $feedType, $feedVersion, $topic, $fid));
  1821         }
  1822     }
  1823 
  1824     $function = 'CUSTOM_feedNSExtensions';
  1825     if (function_exists($function))
  1826     {
  1827         $namespaces = array_merge($namespaces, $function($contentType, $feedType, $feedVersion, $topic, $fid));
  1828     }
  1829 
  1830     return $namespaces;
  1831 }
  1832 
  1833 /**
  1834   * Get meta tag extensions for a feed. Add extended tags to the meta
  1835   * area of a feed.
  1836   *
  1837   * @param  string  $contentType    Type of feed content, article or a plugin specific type
  1838   * @param  string  $feedType       Type of feed format (RSS/Atom/etc)
  1839   * @param  string  $feedVersion    Type of feed version (RSS 1.0 etc)
  1840   * @param  string  $topic          The topic for the feed.
  1841   * @param  string  $fid            The ID of the feed being fetched.
  1842   * @return array                   list of meta tag extensions
  1843   *
  1844   */
  1845 function PLG_getFeedExtensionTags($contentType, $feedType, $feedVersion, $topic, $fid)
  1846 {
  1847     global $_PLUGINS;
  1848 
  1849     $tags = array();
  1850     foreach( $_PLUGINS as $plugin )
  1851     {
  1852         $function = 'plugin_feedExtensionTags_'.$plugin;
  1853         if (function_exists($function))
  1854         {
  1855             $tags = array_merge($tags, $function($contentType, $feedType, $feedVersion, $topic, $fid));
  1856         }
  1857     }
  1858 
  1859     $function = 'CUSTOM_feedExtensionTags';
  1860     if (function_exists($function))
  1861     {
  1862         $tags = array_merge($tags, $function($contentType, $feedType, $feedVersion, $topic, $fid));
  1863     }
  1864 
  1865     return $tags;
  1866 }
  1867 
  1868 /**
  1869 * The plugin is expected to check if the feed content needs to be updated.
  1870 *
  1871 * This is called from COM_rdfUpToDateCheck() every time Geeklog's index.php
  1872 * is displayed - it should try to be as efficient as possible ...
  1873 *
  1874 * NOTE: The presence of non-empty $updated_XXX parameters indicates that an
  1875 *       existing entry has been changed. The plugin may therefore apply a
  1876 *       different method to check if its feed has to be updated.
  1877 *
  1878 * @param    string  $plugin         plugin name
  1879 * @param    int     $feed           feed id
  1880 * @param    string  $topic          "topic" of the feed - plugin specific
  1881 * @param    string  $update_data    comma-sep. list of updated ids
  1882 * @param    string  $limit          number of entries or number of hours
  1883 * @param    string  $updated_type   (optional) type of feed to update
  1884 * @param    string  $updated_topic  (optional) topic to update
  1885 * @param    string  $updated_id     (optional) entry id to update
  1886 * @return   boolean                 false = feed has to be updated, true = ok
  1887 *
  1888 */
  1889 function PLG_feedUpdateCheck($plugin, $feed, $topic, $update_data, $limit, $updated_type = '', $updated_topic = '', $updated_id = '')
  1890 {
  1891     global $_PLUGINS;
  1892 
  1893     $is_current = true;
  1894 
  1895     if ($plugin == 'custom') {
  1896         $function = 'CUSTOM_feedupdatecheck';
  1897         if (function_exists($function)) {
  1898             $is_current = $function ($feed, $topic, $update_data, $limit,
  1899                             $updated_type, $updated_topic, $updated_id);
  1900         }
  1901     } else {
  1902         if (in_array($plugin, $_PLUGINS)) {
  1903             $function = 'plugin_feedupdatecheck_' . $plugin;
  1904             if (function_exists($function)) {
  1905                 $is_current = $function($feed, $topic, $update_data, $limit,
  1906                                 $updated_type, $updated_topic, $updated_id);
  1907             }
  1908         }
  1909     }
  1910 
  1911     return $is_current;
  1912 }
  1913 
  1914 /**
  1915 * Ask plugins if they want to add something to Geeklog's What's New block.
  1916 *
  1917 * @return   array   array($headlines[], $bylines[], $content[$entries[]])
  1918 *
  1919 */
  1920 function PLG_getWhatsNew()
  1921 {
  1922     global $_PLUGINS;
  1923 
  1924     $newheadlines = array();
  1925     $newbylines   = array();
  1926     $newcontent   = array();
  1927 
  1928     foreach ($_PLUGINS as $pi_name) {
  1929         $fn_head = 'plugin_whatsnewsupported_' . $pi_name;
  1930         if (function_exists($fn_head)) {
  1931             $supported = $fn_head();
  1932             if (is_array($supported)) {
  1933                 list($headline, $byline) = $supported;
  1934 
  1935                 $fn_new = 'plugin_getwhatsnew_' . $pi_name;
  1936                 if (function_exists($fn_new)) {
  1937                     $whatsnew = $fn_new ();
  1938                     $newcontent[] = $whatsnew;
  1939                     $newheadlines[] = $headline;
  1940                     $newbylines[] = $byline;
  1941                 }
  1942             }
  1943         }
  1944     }
  1945 
  1946     $fn_head = 'CUSTOM_whatsnewsupported';
  1947     if (function_exists($fn_head)) {
  1948         $supported = $fn_head();
  1949         if (is_array($supported)) {
  1950             list($headline, $byline) = $supported;
  1951 
  1952             $fn_new = 'CUSTOM_getwhatsnew';
  1953             if (function_exists($fn_new)) {
  1954                 $whatsnew = $fn_new ();
  1955                 $newcontent[] = $whatsnew;
  1956                 $newheadlines[] = $headline;
  1957                 $newbylines[] = $byline;
  1958             }
  1959         }
  1960     }
  1961 
  1962     return array($newheadlines, $newbylines, $newcontent);
  1963 }
  1964 
  1965 /**
  1966 * Allows plugins and Core Geeklog Components to filter out spam.
  1967 *
  1968 * The Spam-X Plugin is now part of the Geeklog Distribution
  1969 * This plugin API will call the main function in the Spam-X plugin
  1970 * but can also be used to call other plugins or custom functions
  1971 * if available for filtering spam or content.
  1972 *
  1973 * The caller should check for return values > 0 in which case spam has been
  1974 * detected and the poster should be told, either via
  1975 * <code>
  1976 *   echo COM_refresh($_CONF['site_url'] . '/index.php?msg=' . $result
  1977 *                    . '&amp;plugin=spamx');
  1978 * </code>
  1979 * or by
  1980 * <code>
  1981 *   COM_displayMessageAndAbort($result, 'spamx', 403, 'Forbidden');
  1982 * </code>
  1983 * Where the former will only display a "spam detected" message while the latter
  1984 * will also send an HTTP status code 403 with the message.
  1985 *
  1986 * @param    string  $content    Text to be filtered or checked for spam
  1987 * @param    int     $action     what to do if spam found
  1988 * @return   int                 > 0: spam detected, == 0: no spam detected
  1989 * @link     http://wiki.geeklog.net/index.php/Filtering_Spam_with_Spam-X
  1990 *
  1991 */
  1992 function PLG_checkforSpam($content, $action = -1)
  1993 {
  1994     global $_PLUGINS;
  1995 
  1996     foreach ($_PLUGINS as $pi_name) {
  1997         $function = 'plugin_checkforSpam_' . $pi_name;
  1998         if (function_exists($function)) {
  1999             $result = $function($content, $action);
  2000             if ($result > 0) { // Plugin found a match for spam
  2001 
  2002                 $result = PLG_spamAction($content, $action);
  2003 
  2004                 return $result;
  2005             }
  2006         }
  2007     }
  2008 
  2009     $function = 'CUSTOM_checkforSpam';
  2010     if (function_exists($function)) {
  2011         $result = $function($content, $action);
  2012         if ($result > 0) { // Plugin found a match for spam
  2013 
  2014             $result = PLG_spamAction($content, $action);
  2015 
  2016             return $result;
  2017         }
  2018     }
  2019 
  2020     return 0;
  2021 }
  2022 
  2023 /**
  2024 * Act on spam
  2025 *
  2026 * This is normally called from PLG_checkforSpam (see above) automatically when
  2027 * spam has been detected. There may however be situations where spam has been
  2028 * detected by some other means, in which case you may want to trigger the
  2029 * spam action explicitly.
  2030 *
  2031 * @param    string  $content    Text to be filtered or checked for spam
  2032 * @param    int     $action     what to do if spam found
  2033 * @return   int                 > 0: spam detected, == 0: no spam detected
  2034 * @see      PLG_checkforSpam
  2035 * @since    Geeklog 1.4.1
  2036 *
  2037 */
  2038 function PLG_spamAction($content, $action = -1)
  2039 {
  2040     global $_PLUGINS;
  2041 
  2042     $result = 0;
  2043 
  2044     foreach ($_PLUGINS as $pi_name) {
  2045         $function = 'plugin_spamaction_' . $pi_name;
  2046         if (function_exists($function)) {
  2047             $res = $function($content, $action);
  2048             $result = max($result, $res);
  2049         }
  2050     }
  2051 
  2052     $function = 'CUSTOM_spamaction';
  2053     if (function_exists($function)) {
  2054         $res = $function($content, $action);
  2055         $result = max($result, $res);
  2056     }
  2057 
  2058     return $result;
  2059 }
  2060 
  2061 /**
  2062 * Ask plugin for information about one of its items
  2063 *
  2064 * Item properties that can be requested:
  2065 * - 'date-created'  - creation date, if available
  2066 * - 'date-modified' - date of last modification, if available
  2067 * - 'description'   - full description of the item
  2068 * - 'excerpt'       - short description of the item
  2069 * - 'id'            - ID of the item, e.g. sid for articles
  2070 * - 'title'         - title of the item
  2071 * - 'url'           - URL of the item
  2072 *
  2073 * 'excerpt' and 'description' may return the same value. Properties should be
  2074 * returned in the order they are listed in $what. Properties that are not
  2075 * available should return an empty string.
  2076 * Return false for errors (e.g. access denied, item does not exist, etc.).
  2077 *
  2078 * @param    string  $type       plugin type (incl. 'article' for stories)
  2079 * @param    string  $id         ID of an item under the plugin's control or '*'
  2080 * @param    string  $what       comma-separated list of item properties
  2081 * @param    int     $uid        user ID or 0 = current user
  2082 * @param    array   $options    (reserved for future extensions)
  2083 * @return   mixed               string or array of strings with the information
  2084 * @link     http://wiki.geeklog.net/index.php/PLG_getItemInfo
  2085 *
  2086 */
  2087 function PLG_getItemInfo($type, $id, $what, $uid = 0, $options = array())
  2088 {
  2089     if ($type == 'article') {
  2090 
  2091         global $_CONF;
  2092 
  2093         require_once $_CONF['path_system'] . 'lib-story.php';
  2094 
  2095         return STORY_getItemInfo($id, $what, $uid, $options);
  2096 
  2097     } else {
  2098 
  2099         $args[1] = $id;
  2100         $args[2] = $what;
  2101         $args[3] = $uid;
  2102         $args[4] = $options;
  2103 
  2104         $function = 'plugin_getiteminfo_' . $type;
  2105 
  2106         return PLG_callFunctionForOnePlugin($function, $args);
  2107     }
  2108 }
  2109 
  2110 /**
  2111 * Geeklog is about to perform an operation on a trackback or pingback comment
  2112 * to one of the items under the plugin's control and asks for the plugin's
  2113 * permission to continue.
  2114 *
  2115 * Geeklog handles receiving and deleting trackback comments and pingbacks
  2116 * for the plugin but since it doesn't know about the plugin's access control,
  2117 * it has to ask the plugin to approve / reject such an operation.
  2118 *
  2119 * $operation can be one of the following:
  2120 * - 'acceptByID':  accept a trackback comment on item with ID $id
  2121 *                  returns: true for accept, false for reject
  2122 * - 'acceptByURI': accept a pingback comment on item at URL $id
  2123 *                  returns: the item's ID for accept, false for reject
  2124 * - 'delete':      is the current user allowed to delete item with ID $id?
  2125 *                  returns: true for accept, false for reject
  2126 *
  2127 * @param    string  $type       plugin type
  2128 * @param    string  $id         an ID or URL, depending on the operation
  2129 * @param    string  $operation  operation to perform
  2130 * @return   mixed               depends on $operation
  2131 *
  2132 */
  2133 function PLG_handlePingComment($type, $id, $operation)
  2134 {
  2135     $args[1] = $id;
  2136     $args[2] = $operation;
  2137 
  2138     $function = 'plugin_handlepingoperation_' . $type;
  2139 
  2140     return PLG_callFunctionForOnePlugin ($function, $args);
  2141 }
  2142 
  2143 
  2144 /**
  2145 * Check if plugins have a scheduled task they want to run
  2146 * The interval between runs is determined by $_CONF['cron_schedule_interval']
  2147 *
  2148 * @return void
  2149 *
  2150 */
  2151 function PLG_runScheduledTask()
  2152 {
  2153     global $_PLUGINS;
  2154 
  2155     foreach ($_PLUGINS as $pi_name) {
  2156         $function = 'plugin_runScheduledTask_' . $pi_name;
  2157         if (function_exists ($function)) {
  2158             $function ();
  2159         }
  2160     }
  2161 
  2162     if (function_exists('CUSTOM_runScheduledTask')) {
  2163         CUSTOM_runScheduledTask();
  2164     }
  2165 }
  2166 
  2167 /**
  2168 * "Generic" plugin API: Save item
  2169 *
  2170 * To be called (eventually) whenever Geeklog saves an item into the database.
  2171 * Plugins can define their own 'itemsaved' function to be notified whenever
  2172 * an item is saved or modified.
  2173 *
  2174 * NOTE:     The behaviour of this API function changed in Geeklog 1.6.0
  2175 *
  2176 * @param    string  $id     unique ID of the item
  2177 * @param    string  $type   type of the item, e.g. 'article'
  2178 * @param    string  $old_id (optional) old ID when the ID was changed
  2179 * @return   void            (actually: false, for backward compatibility)
  2180 * @link     http://wiki.geeklog.net/index.php/PLG_itemSaved
  2181 *
  2182 */
  2183 function PLG_itemSaved($id, $type, $old_id = '')
  2184 {
  2185     global $_PLUGINS;
  2186 
  2187     $t = explode('.', $type);
  2188     $plg_type = $t[0];
  2189 
  2190     $plugins = count($_PLUGINS);
  2191     for ($save = 0; $save < $plugins; $save++) {
  2192         if ($_PLUGINS[$save] != $plg_type) {
  2193             $function = 'plugin_itemsaved_' . $_PLUGINS[$save];
  2194             if (function_exists($function)) {
  2195                 $function($id, $type, $old_id);
  2196             }
  2197         }
  2198     }
  2199 
  2200     if (function_exists('CUSTOM_itemsaved')) {
  2201         CUSTOM_itemsaved($id, $type, $old_id);
  2202     }
  2203 
  2204     return false; // for backward compatibility
  2205 }
  2206 
  2207 /**
  2208 * "Generic" plugin API: Delete item
  2209 *
  2210 * To be called (eventually) whenever Geeklog removes an item from the database.
  2211 * Plugins can define their own 'itemdeleted' function to be notified whenever
  2212 * an item is deleted.
  2213 *
  2214 * @param    string  $id     ID of the item
  2215 * @param    string  $type   type of the item, e.g. 'article'
  2216 * @return   void
  2217 * @since    Geeklog 1.6.0
  2218 *
  2219 */
  2220 function PLG_itemDeleted($id, $type)
  2221 {
  2222     global $_PLUGINS;
  2223 
  2224     $t = explode('.', $type);
  2225     $plg_type = $t[0];
  2226 
  2227     $plugins = count($_PLUGINS);
  2228     for ($del = 0; $del < $plugins; $del++) {
  2229         if ($_PLUGINS[$del] != $plg_type) {
  2230             $function = 'plugin_itemdeleted_' . $_PLUGINS[$del];
  2231             if (function_exists($function)) {
  2232                 $function($id, $type);
  2233             }
  2234         }
  2235     }
  2236 
  2237     if (function_exists('CUSTOM_itemdeleted')) {
  2238         CUSTOM_itemdeleted($id, $type);
  2239     }
  2240 }
  2241 
  2242 /**
  2243 * "Generic" plugin API: Display item
  2244 *
  2245 * To be called (eventually) whenever Geeklog displays an item.
  2246 * Plugins can hook into this and add content to the displayed item, in the form
  2247 * of an array (true, string1, string2...).
  2248 *
  2249 * The object that called can then display one or several items with a
  2250 * object-defined layout.
  2251 *
  2252 * Plugins can signal an error by returning an array (false, 'Error Message')
  2253 * In case of an error, the error message will be written to the error.log and
  2254 * nothing will be displayed on the output.
  2255 *
  2256 * @param    string  $id     unique ID of the item
  2257 * @param    string  $type   type of the item, e.g. 'article'
  2258 * @return   array           array with a status and one or several strings
  2259 *
  2260 */
  2261 function PLG_itemDisplay($id, $type)
  2262 {
  2263     global $_PLUGINS;
  2264     $result_arr = array();
  2265 
  2266     $plugins = count($_PLUGINS);
  2267     for ($display = 0; $display < $plugins; $display++) {
  2268         $function = 'plugin_itemdisplay_' . $_PLUGINS[$display];
  2269         if (function_exists($function)) {
  2270             $result = $function($id, $type);
  2271             if ($result[0] == false) {
  2272                 // plugin reported a problem - do not add and continue
  2273                 COM_errorLog($result[1], 1);
  2274             } else {
  2275                 array_shift($result);
  2276                 $result_arr = array_merge($result_arr,$result);
  2277             }
  2278         }
  2279     }
  2280 
  2281     $function = 'CUSTOM_itemdisplay';
  2282     if (function_exists ($function)) {
  2283         $result = $function ($id, $type);
  2284         if ($result[0] == false) {
  2285             // plugin reported a problem - do not add and continue
  2286             COM_errorLog( $result[1], 1);
  2287         } else {
  2288             array_shift($result);
  2289             $result_arr = array_merge($result_arr,$result);
  2290         }
  2291     }
  2292 
  2293     return $result_arr;
  2294 }
  2295 
  2296 
  2297 /**
  2298 * Gets Geeklog blocks from plugins
  2299 *
  2300 * Returns data for blocks on a given side and, potentially, for
  2301 * a given topic.
  2302 *
  2303 * @param    string  $side   Side to get blocks for (right or left for now)
  2304 * @param    string  $topic  Only get blocks for this topic
  2305 * @return   array           array of block data
  2306 * @link     http://wiki.geeklog.net/index.php/Dynamic_Blocks
  2307 *
  2308 */
  2309 function PLG_getBlocks($side, $topic='')
  2310 {
  2311     global $_PLUGINS;
  2312 
  2313     $ret = array();
  2314     foreach ($_PLUGINS as $pi_name) {
  2315         $function = 'plugin_getBlocks_' . $pi_name;
  2316         if (function_exists($function)) {
  2317             $items = $function($side, $topic='');
  2318             if (is_array($items)) {
  2319                 $ret = array_merge($ret, $items);
  2320             }
  2321         }
  2322     }
  2323 
  2324     if (function_exists('CUSTOM_getBlocks')) {
  2325        $cust_items .= CUSTOM_getBlocks($side, $topic='');
  2326        if (is_array($cust_items)) {
  2327           $ret = array_merge($ret, $cust_items);
  2328        }
  2329     }
  2330 
  2331     return $ret;
  2332 }
  2333 
  2334 /**
  2335 * Get the URL of a plugin's icon
  2336 *
  2337 * @param    string  $type   plugin name
  2338 * @return   string          URL of the icon
  2339 *
  2340 */
  2341 function PLG_getIcon($type)
  2342 {
  2343     global $_CONF;
  2344 
  2345     $retval = '';
  2346 
  2347     // try the "geticon" function first
  2348     $function = 'plugin_geticon_' . $type;
  2349     if (function_exists($function)) {
  2350         $retval = $function ();
  2351     }
  2352 
  2353     // if that didn't work, try the "cclabel" function
  2354     if (empty($retval)) {
  2355         $function = 'plugin_cclabel_' . $type;
  2356         if (function_exists($function)) {
  2357             $cclabel = $function ();
  2358             if (is_array($cclabel)) {
  2359                 if (!empty($cclabel[2])) {
  2360                     $retval = $cclabel[2];
  2361                 }
  2362             }
  2363         }
  2364     }
  2365 
  2366     // lastly, search for the icon (assuming it's a GIF)
  2367     if (empty($retval)) {
  2368         $icon = $_CONF['site_url'] . '/' . $type . '/images/' . $type . '.gif';
  2369         $fh = @fopen ($icon, 'r');
  2370         if ($fh === false) {
  2371             $icon = $_CONF['site_admin_url'] . '/plugins/' . $type . '/images/'
  2372                   . $type . '.gif';
  2373             $fh = @fopen ($icon, 'r');
  2374             if ($fh === false) {
  2375                 // give up and use a generic icon
  2376                 $retval = $_CONF['site_url'] . '/images/icons/plugins.gif';
  2377             } else {
  2378                 $retval = $icon;
  2379                 fclose ($fh);
  2380             }
  2381         } else {
  2382             $retval = $icon;
  2383             fclose ($fh);
  2384         }
  2385     }
  2386 
  2387     return $retval;
  2388 }
  2389 
  2390 /**
  2391  * Invoke a service
  2392  *
  2393  * @param   string  $type    The plugin type whose service is to be called
  2394  * @param   string  $action  The service action to be performed
  2395  * @param   array   $args    The arguments to be passed to the service invoked
  2396  * @param   array  &$output  The output variable that will contain the output after invocation
  2397  * @param   array  &$svc_msg The output variable that will contain the service messages
  2398  * @return  int              The result of the invocation
  2399  * @link    http://wiki.geeklog.net/index.php/Webservices_API
  2400  *
  2401  */
  2402 function PLG_invokeService($type, $action, $args, &$output, &$svc_msg)
  2403 {
  2404     global $_CONF;
  2405 
  2406     $retval = PLG_RET_ERROR;
  2407 
  2408     if ($type == 'story') {
  2409         // ensure we can see the service_XXX_story functions
  2410         require_once $_CONF['path_system'] . 'lib-story.php';
  2411     }
  2412 
  2413     $output  = '';
  2414     $svc_msg = '';
  2415 
  2416     // Check if the plugin type and action are valid
  2417     $function = 'service_' . $action . '_' . $type;
  2418 
  2419     if (function_exists($function) && PLG_wsEnabled($type)) {
  2420         if (!isset($args['gl_svc'])) {
  2421             $args['gl_svc'] = false;
  2422         }
  2423         $retval = $function($args, $output, $svc_msg);
  2424     }
  2425 
  2426     return $retval;
  2427 }
  2428 
  2429 /**
  2430  * Returns true if the plugin supports webservices
  2431  *
  2432  * @param   string  $type   The plugin type that is to be checked
  2433  * @return  boolean         true: enabled, false: disabled
  2434  * @link    http://wiki.geeklog.net/index.php/Webservices_API
  2435  *
  2436  */
  2437 function PLG_wsEnabled($type)
  2438 {
  2439     global $_CONF;
  2440 
  2441     if ($type == 'story') {
  2442         // ensure we can see the service_XXX_story functions
  2443         require_once $_CONF['path_system'] . 'lib-story.php';
  2444     }
  2445 
  2446     $function = 'plugin_wsEnabled_' . $type;
  2447     if (function_exists($function)) {
  2448         return $function();
  2449     } else {
  2450         return false;
  2451     }
  2452 }
  2453 
  2454 /**
  2455 * Forward the user depending on config setting after saving something
  2456 *
  2457 * @param  string  $target     where to redirect to
  2458 * @param  string  $item_url   the url of the item saved
  2459 * @param  string  $plugin     the name of the plugin that saved the item
  2460 * @param  string  $message    (optional) message number to attach to url
  2461 * @return string              the url where the user will be forwarded to
  2462 *
  2463 */
  2464 function PLG_afterSaveSwitch($target, $item_url, $plugin, $message = '')
  2465 {
  2466     global $_CONF;
  2467 
  2468     if (isset($message) && (!empty($message) || is_numeric($message))) {
  2469         $msg = "msg=$message";
  2470     } else {
  2471         $msg = '';
  2472     }
  2473 
  2474     switch ($target) {
  2475     case 'item':
  2476         $url = $item_url;
  2477         if (!empty($msg) && ($plugin != 'story')) {
  2478             if (strpos($url, '?') === false) {
  2479                 $url .= '?' . $msg;
  2480             } else {
  2481                 $url .= '&amp;' . $msg;
  2482             }
  2483         }
  2484         break;
  2485 
  2486     case 'home':
  2487         $url = $_CONF['site_url'] . '/index.php';
  2488         if (!empty($msg)) {
  2489             $url .= '?' . $msg;
  2490             if (($plugin != 'story') && ($plugin != 'user')) {
  2491                 $url .= '&amp;plugin=' . $plugin;
  2492             }
  2493         }
  2494         break;
  2495 
  2496     case 'admin':
  2497         $url = $_CONF['site_admin_url'] . '/moderation.php';
  2498         if (!empty($msg)) {
  2499             $url .= '?' . $msg;
  2500             if (($plugin != 'story') && ($plugin != 'user')) {
  2501                 $url .= '&amp;plugin=' . $plugin;
  2502             }
  2503         }
  2504         break;
  2505 
  2506     case 'plugin':
  2507         $url = $_CONF['site_url'] . "/$plugin/index.php";
  2508         if (!empty($msg)) {
  2509             $url .= '?' . $msg;
  2510         }
  2511         break;
  2512 
  2513     case 'list':
  2514     default:
  2515         if ($plugin == 'story') {
  2516             $url = $_CONF['site_admin_url'] . "/$plugin.php";
  2517         } elseif ($plugin == 'user') {
  2518             $url = $_CONF['site_admin_url'] . "/user.php";
  2519         } else {
  2520             $url = $_CONF['site_admin_url'] . "/plugins/$plugin/index.php";
  2521         }
  2522         if (!empty($msg)) {
  2523             $url .= '?' . $msg;
  2524         }
  2525         break;
  2526     }
  2527 
  2528     return COM_refresh($url);
  2529 }
  2530 
  2531 /**
  2532 * Inform plugins of configuration changes
  2533 *
  2534 * NOTE: Plugins will only be notified of details of changes in 'Core' and in
  2535 *       their own configuration. For other plugins, they will only be notified
  2536 *       of the fact that something in the other plugin's config changed.
  2537 *
  2538 * @param    string  $group      plugin name or 'Core' for $_CONF changes
  2539 * @param    array   $changes    names of config values that changed
  2540 * @return   void
  2541 * @link     http://wiki.geeklog.net/index.php/PLG_configChange
  2542 * @since    Geeklog 1.6.0
  2543 *
  2544 */
  2545 function PLG_configChange($group, $changes)
  2546 {
  2547     global $_PLUGINS;
  2548 
  2549     foreach ($_PLUGINS as $pi_name) {
  2550         $args = array();
  2551         $args[1] = $group;
  2552 
  2553         if (($group == 'Core') || ($group == $pi_name)) {
  2554             $args[2] = $changes;
  2555         }
  2556 
  2557         PLG_callFunctionForOnePlugin('plugin_configchange_' . $pi_name, $args);
  2558     }
  2559 
  2560     $function = 'CUSTOM_configchange';
  2561     if (function_exists($function)) {
  2562         if ($group == 'Core') {
  2563             $function($group, $changes);
  2564         } else {
  2565             $function($group);
  2566         }
  2567     }
  2568 }
  2569 
  2570 /**
  2571 * Ask plugin for the URL to its documentation
  2572 *
  2573 * @param    string  $type   plugin name
  2574 * @param    string  $file   documentation file being requested, e.g. 'config'
  2575 * @return   mixed           URL or false / empty string when not available
  2576 * @link     http://wiki.geeklog.net/index.php/PLG_getDocumentationUrl
  2577 * @since    Geeklog 1.6.0
  2578 *
  2579 */
  2580 function PLG_getDocumentationUrl($type, $file)
  2581 {
  2582     $args[1] = $file;
  2583     $function = 'plugin_getdocumentationurl_' . $type;
  2584 
  2585     return PLG_callFunctionForOnePlugin($function, $args);
  2586 }
  2587 
  2588 /**
  2589 * Inform plugins when another plugin's state changed
  2590 *
  2591 * Unlike PLG_enableStateChange, this function is called after the state
  2592 * change.
  2593 *
  2594 * NOTE: You can not rely on being informed of state changes for 'installed',
  2595 * 'uninstalled', and 'upgraded', as these may happen in the plugin's install
  2596 * script, outside of Geeklog's control.
  2597 *
  2598 * @param    string  $type   plugin name
  2599 * @param    string  $status new status: 'enabled', 'disabled', 'installed', 'uninstalled', 'upgraded'
  2600 * @return   void
  2601 * @see      PLG_enableStateChange
  2602 * @since    Geeklog 1.6.0
  2603 *
  2604 */
  2605 function PLG_pluginStateChange($type, $status)
  2606 {
  2607     global $_PLUGINS;
  2608 
  2609     $args[1] = $type;
  2610     $args[2] = $status;
  2611     foreach ($_PLUGINS as $pi_name) {
  2612         if ($pi_name != $type) {
  2613             $function = 'plugin_pluginstatechange_' . $pi_name;
  2614             PLG_callFunctionForOnePlugin($function, $args);
  2615         }
  2616     }
  2617 
  2618     $function = 'CUSTOM_pluginstatechange';
  2619     if (function_exists($function)) {
  2620         $function($type, $status);
  2621     }
  2622 }
  2623 
  2624 ?>