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