Mercurial > hg > rc2
annotate program/lib/Roundcube/rcube_plugin_api.php @ 17:dd5ed6ef69c9
Slowly cleaning up more php8 Warnings/deprecations
| author | Charlie Root |
|---|---|
| date | Mon, 06 Oct 2025 12:20:32 -0400 |
| parents | abddb304201f |
| children | b6a96bdd6b29 |
| rev | line source |
|---|---|
| 0 | 1 <?php |
| 2 | |
| 3 /** | |
| 4 +-----------------------------------------------------------------------+ | |
| 5 | This file is part of the Roundcube Webmail client | | |
| 6 | Copyright (C) 2008-2012, The Roundcube Dev Team | | |
| 7 | | | |
| 8 | Licensed under the GNU General Public License version 3 or | | |
| 9 | any later version with exceptions for skins & plugins. | | |
| 10 | See the README file for a full license statement. | | |
| 11 | | | |
| 12 | PURPOSE: | | |
| 13 | Plugins repository | | |
| 14 +-----------------------------------------------------------------------+ | |
| 15 | Author: Thomas Bruederli <roundcube@gmail.com> | | |
| 16 +-----------------------------------------------------------------------+ | |
| 17 */ | |
| 18 | |
| 19 // location where plugins are loade from | |
| 20 if (!defined('RCUBE_PLUGINS_DIR')) { | |
| 21 define('RCUBE_PLUGINS_DIR', RCUBE_INSTALL_PATH . 'plugins/'); | |
| 22 } | |
| 23 | |
| 24 /** | |
| 25 * The plugin loader and global API | |
| 26 * | |
| 27 * @package Framework | |
| 28 * @subpackage PluginAPI | |
| 29 */ | |
| 30 class rcube_plugin_api | |
| 31 { | |
| 32 static protected $instance; | |
| 33 | |
| 34 public $dir; | |
| 35 public $url = 'plugins/'; | |
| 36 public $task = ''; | |
| 37 public $initialized = false; | |
| 38 | |
| 39 public $output; | |
| 40 public $handlers = array(); | |
| 41 public $allowed_prefs = array(); | |
| 42 public $allowed_session_prefs = array(); | |
| 43 public $active_plugins = array(); | |
| 44 | |
| 45 protected $plugins = array(); | |
| 46 protected $plugins_initialized = array(); | |
| 47 protected $tasks = array(); | |
| 48 protected $actions = array(); | |
| 49 protected $actionmap = array(); | |
| 50 protected $objectsmap = array(); | |
| 51 protected $template_contents = array(); | |
| 52 protected $exec_stack = array(); | |
| 53 protected $deprecated_hooks = array(); | |
| 54 | |
| 55 | |
| 56 /** | |
| 57 * This implements the 'singleton' design pattern | |
| 58 * | |
| 59 * @return rcube_plugin_api The one and only instance if this class | |
| 60 */ | |
| 61 static function get_instance() | |
| 62 { | |
| 63 if (!self::$instance) { | |
| 64 self::$instance = new rcube_plugin_api(); | |
| 65 } | |
| 66 | |
| 67 return self::$instance; | |
| 68 } | |
| 69 | |
| 70 /** | |
| 71 * Private constructor | |
| 72 */ | |
| 73 protected function __construct() | |
| 74 { | |
| 75 $this->dir = slashify(RCUBE_PLUGINS_DIR); | |
| 76 } | |
| 77 | |
| 78 /** | |
| 79 * Initialize plugin engine | |
| 80 * | |
| 81 * This has to be done after rcmail::load_gui() or rcmail::json_init() | |
| 82 * was called because plugins need to have access to rcmail->output | |
| 83 * | |
| 84 * @param object rcube Instance of the rcube base class | |
| 85 * @param string Current application task (used for conditional plugin loading) | |
| 86 */ | |
| 87 public function init($app, $task = '') | |
| 88 { | |
| 89 $this->task = $task; | |
| 90 $this->output = $app->output; | |
| 91 // register an internal hook | |
| 92 $this->register_hook('template_container', array($this, 'template_container_hook')); | |
| 93 // maybe also register a shudown function which triggers | |
| 94 // shutdown functions of all plugin objects | |
| 95 | |
| 96 foreach ($this->plugins as $plugin) { | |
| 97 // ... task, request type and framed mode | |
|
17
dd5ed6ef69c9
Slowly cleaning up more php8 Warnings/deprecations
Charlie Root
parents:
14
diff
changeset
|
98 if (!($this->plugins_initialized[$plugin->ID]??null) && !$this->filter($plugin)) { |
| 0 | 99 $plugin->init(); |
| 100 $this->plugins_initialized[$plugin->ID] = $plugin; | |
| 101 } | |
| 102 } | |
| 103 | |
| 104 // we have finished initializing all plugins | |
| 105 $this->initialized = true; | |
| 106 } | |
| 107 | |
| 108 /** | |
| 109 * Load and init all enabled plugins | |
| 110 * | |
| 111 * This has to be done after rcmail::load_gui() or rcmail::json_init() | |
| 112 * was called because plugins need to have access to rcmail->output | |
| 113 * | |
| 114 * @param array List of configured plugins to load | |
| 115 * @param array List of plugins required by the application | |
| 116 */ | |
| 117 public function load_plugins($plugins_enabled, $required_plugins = array()) | |
| 118 { | |
| 119 foreach ($plugins_enabled as $plugin_name) { | |
| 120 $this->load_plugin($plugin_name); | |
| 121 } | |
| 122 | |
| 123 // check existance of all required core plugins | |
| 124 foreach ($required_plugins as $plugin_name) { | |
| 125 $loaded = false; | |
| 126 foreach ($this->plugins as $plugin) { | |
| 127 if ($plugin instanceof $plugin_name) { | |
| 128 $loaded = true; | |
| 129 break; | |
| 130 } | |
| 131 } | |
| 132 | |
| 133 // load required core plugin if no derivate was found | |
| 134 if (!$loaded) { | |
| 135 $loaded = $this->load_plugin($plugin_name); | |
| 136 } | |
| 137 | |
| 138 // trigger fatal error if still not loaded | |
| 139 if (!$loaded) { | |
| 140 rcube::raise_error(array( | |
| 141 'code' => 520, 'type' => 'php', | |
| 142 'file' => __FILE__, 'line' => __LINE__, | |
| 143 'message' => "Requried plugin $plugin_name was not loaded"), true, true); | |
| 144 } | |
| 145 } | |
| 146 } | |
| 147 | |
| 148 /** | |
| 149 * Load the specified plugin | |
| 150 * | |
| 151 * @param string Plugin name | |
| 152 * @param boolean Force loading of the plugin even if it doesn't match the filter | |
| 153 * @param boolean Require loading of the plugin, error if it doesn't exist | |
| 154 * | |
| 155 * @return boolean True on success, false if not loaded or failure | |
| 156 */ | |
| 157 public function load_plugin($plugin_name, $force = false, $require = true) | |
| 158 { | |
| 159 static $plugins_dir; | |
| 160 | |
| 161 if (!$plugins_dir) { | |
| 162 $dir = dir($this->dir); | |
| 163 $plugins_dir = unslashify($dir->path); | |
| 164 } | |
| 165 | |
| 166 // plugin already loaded? | |
| 14 | 167 if (!isset($this->plugins[$plugin_name])) { |
| 0 | 168 $fn = "$plugins_dir/$plugin_name/$plugin_name.php"; |
| 169 | |
| 170 if (!is_readable($fn)) { | |
| 171 if ($require) { | |
| 172 rcube::raise_error(array('code' => 520, 'type' => 'php', | |
| 173 'file' => __FILE__, 'line' => __LINE__, | |
| 174 'message' => "Failed to load plugin file $fn"), true, false); | |
| 175 } | |
| 176 | |
| 177 return false; | |
| 178 } | |
| 179 | |
| 180 if (!class_exists($plugin_name, false)) { | |
| 181 include $fn; | |
| 182 } | |
| 183 | |
| 184 // instantiate class if exists | |
| 185 if (!class_exists($plugin_name, false)) { | |
| 186 rcube::raise_error(array('code' => 520, 'type' => 'php', | |
| 187 'file' => __FILE__, 'line' => __LINE__, | |
| 188 'message' => "No plugin class $plugin_name found in $fn"), | |
| 189 true, false); | |
| 190 | |
| 191 return false; | |
| 192 } | |
| 193 | |
| 194 $plugin = new $plugin_name($this); | |
| 195 $this->active_plugins[] = $plugin_name; | |
| 196 | |
| 197 // check inheritance... | |
| 198 if (is_subclass_of($plugin, 'rcube_plugin')) { | |
| 199 // call onload method on plugin if it exists. | |
| 200 // this is useful if you want to be called early in the boot process | |
| 201 if (method_exists($plugin, 'onload')) { | |
| 202 $plugin->onload(); | |
| 203 } | |
| 204 | |
| 205 if (!empty($plugin->allowed_prefs)) { | |
| 206 $this->allowed_prefs = array_merge($this->allowed_prefs, $plugin->allowed_prefs); | |
| 207 } | |
| 208 | |
| 209 $this->plugins[$plugin_name] = $plugin; | |
| 210 } | |
| 211 } | |
| 212 | |
| 213 if ($plugin = $this->plugins[$plugin_name]) { | |
| 214 // init a plugin only if $force is set or if we're called after initialization | |
| 215 if (($force || $this->initialized) && !$this->plugins_initialized[$plugin_name] && ($force || !$this->filter($plugin))) { | |
| 216 $plugin->init(); | |
| 217 $this->plugins_initialized[$plugin_name] = $plugin; | |
| 218 } | |
| 219 } | |
| 220 | |
| 221 return true; | |
| 222 } | |
| 223 | |
| 224 /** | |
| 225 * check if we should prevent this plugin from initialising | |
| 226 * | |
| 227 * @param $plugin | |
| 228 * @return bool | |
| 229 */ | |
| 230 private function filter($plugin) | |
| 231 { | |
| 232 return ($plugin->noajax && !(is_object($this->output) && $this->output->type == 'html')) | |
| 233 || ($plugin->task && !preg_match('/^('.$plugin->task.')$/i', $this->task)) | |
| 234 || ($plugin->noframe && !empty($_REQUEST['_framed'])); | |
| 235 } | |
| 236 | |
| 237 /** | |
| 238 * Get information about a specific plugin. | |
| 239 * This is either provided my a plugin's info() method or extracted from a package.xml or a composer.json file | |
| 240 * | |
| 241 * @param string Plugin name | |
| 242 * @return array Meta information about a plugin or False if plugin was not found | |
| 243 */ | |
| 244 public function get_info($plugin_name) | |
| 245 { | |
| 246 static $composer_lock, $license_uris = array( | |
| 247 'Apache' => 'http://www.apache.org/licenses/LICENSE-2.0.html', | |
| 248 'Apache-2' => 'http://www.apache.org/licenses/LICENSE-2.0.html', | |
| 249 'Apache-1' => 'http://www.apache.org/licenses/LICENSE-1.0', | |
| 250 'Apache-1.1' => 'http://www.apache.org/licenses/LICENSE-1.1', | |
| 251 'GPL' => 'http://www.gnu.org/licenses/gpl.html', | |
| 252 'GPLv2' => 'http://www.gnu.org/licenses/gpl-2.0.html', | |
| 253 'GPL-2.0' => 'http://www.gnu.org/licenses/gpl-2.0.html', | |
| 254 'GPLv3' => 'http://www.gnu.org/licenses/gpl-3.0.html', | |
| 255 'GPLv3+' => 'http://www.gnu.org/licenses/gpl-3.0.html', | |
| 256 'GPL-3.0' => 'http://www.gnu.org/licenses/gpl-3.0.html', | |
| 257 'GPL-3.0+' => 'http://www.gnu.org/licenses/gpl.html', | |
| 258 'GPL-2.0+' => 'http://www.gnu.org/licenses/gpl.html', | |
| 259 'AGPLv3' => 'http://www.gnu.org/licenses/agpl.html', | |
| 260 'AGPLv3+' => 'http://www.gnu.org/licenses/agpl.html', | |
| 261 'AGPL-3.0' => 'http://www.gnu.org/licenses/agpl.html', | |
| 262 'LGPL' => 'http://www.gnu.org/licenses/lgpl.html', | |
| 263 'LGPLv2' => 'http://www.gnu.org/licenses/lgpl-2.0.html', | |
| 264 'LGPLv2.1' => 'http://www.gnu.org/licenses/lgpl-2.1.html', | |
| 265 'LGPLv3' => 'http://www.gnu.org/licenses/lgpl.html', | |
| 266 'LGPL-2.0' => 'http://www.gnu.org/licenses/lgpl-2.0.html', | |
| 267 'LGPL-2.1' => 'http://www.gnu.org/licenses/lgpl-2.1.html', | |
| 268 'LGPL-3.0' => 'http://www.gnu.org/licenses/lgpl.html', | |
| 269 'LGPL-3.0+' => 'http://www.gnu.org/licenses/lgpl.html', | |
| 270 'BSD' => 'http://opensource.org/licenses/bsd-license.html', | |
| 271 'BSD-2-Clause' => 'http://opensource.org/licenses/BSD-2-Clause', | |
| 272 'BSD-3-Clause' => 'http://opensource.org/licenses/BSD-3-Clause', | |
| 273 'FreeBSD' => 'http://opensource.org/licenses/BSD-2-Clause', | |
| 274 'MIT' => 'http://www.opensource.org/licenses/mit-license.php', | |
| 275 'PHP' => 'http://opensource.org/licenses/PHP-3.0', | |
| 276 'PHP-3' => 'http://www.php.net/license/3_01.txt', | |
| 277 'PHP-3.0' => 'http://www.php.net/license/3_0.txt', | |
| 278 'PHP-3.01' => 'http://www.php.net/license/3_01.txt', | |
| 279 ); | |
| 280 | |
| 281 $dir = dir($this->dir); | |
| 282 $fn = unslashify($dir->path) . "/$plugin_name/$plugin_name.php"; | |
| 283 $info = false; | |
| 284 | |
| 285 if (!class_exists($plugin_name, false)) { | |
| 286 if (is_readable($fn)) { | |
| 287 include($fn); | |
| 288 } | |
| 289 else { | |
| 290 return false; | |
| 291 } | |
| 292 } | |
| 293 | |
| 294 if (class_exists($plugin_name)) { | |
| 295 $info = $plugin_name::info(); | |
| 296 } | |
| 297 | |
| 298 // fall back to composer.json file | |
| 299 if (!$info) { | |
| 300 $composer = INSTALL_PATH . "/plugins/$plugin_name/composer.json"; | |
| 301 if (is_readable($composer) && ($json = @json_decode(file_get_contents($composer), true))) { | |
| 302 list($info['vendor'], $info['name']) = explode('/', $json['name']); | |
| 303 $info['version'] = $json['version']; | |
| 304 $info['license'] = $json['license']; | |
| 305 $info['uri'] = $json['homepage']; | |
| 306 $info['require'] = array_filter(array_keys((array)$json['require']), function($pname) { | |
| 307 if (strpos($pname, '/') == false) { | |
| 308 return false; | |
| 309 } | |
| 310 list($vendor, $name) = explode('/', $pname); | |
| 311 return !($name == 'plugin-installer' || $vendor == 'pear-pear'); | |
| 312 }); | |
| 313 } | |
| 314 | |
| 315 // read local composer.lock file (once) | |
| 316 if (!isset($composer_lock)) { | |
| 317 $composer_lock = @json_decode(@file_get_contents(INSTALL_PATH . "/composer.lock"), true); | |
| 318 if ($composer_lock['packages']) { | |
| 319 foreach ($composer_lock['packages'] as $i => $package) { | |
| 320 $composer_lock['installed'][$package['name']] = $package; | |
| 321 } | |
| 322 } | |
| 323 } | |
| 324 | |
| 325 // load additional information from local composer.lock file | |
| 326 if ($lock = $composer_lock['installed'][$json['name']]) { | |
| 327 $info['version'] = $lock['version']; | |
| 328 $info['uri'] = $lock['homepage'] ?: $lock['source']['uri']; | |
| 329 $info['src_uri'] = $lock['dist']['uri'] ?: $lock['source']['uri']; | |
| 330 } | |
| 331 } | |
| 332 | |
| 333 // fall back to package.xml file | |
| 334 if (!$info) { | |
| 335 $package = INSTALL_PATH . "/plugins/$plugin_name/package.xml"; | |
| 336 if (is_readable($package) && ($file = file_get_contents($package))) { | |
| 337 $doc = new DOMDocument(); | |
| 338 $doc->loadXML($file); | |
| 339 $xpath = new DOMXPath($doc); | |
| 340 $xpath->registerNamespace('rc', "http://pear.php.net/dtd/package-2.0"); | |
| 341 | |
| 342 // XPaths of plugin metadata elements | |
| 343 $metadata = array( | |
| 344 'name' => 'string(//rc:package/rc:name)', | |
| 345 'version' => 'string(//rc:package/rc:version/rc:release)', | |
| 346 'license' => 'string(//rc:package/rc:license)', | |
| 347 'license_uri' => 'string(//rc:package/rc:license/@uri)', | |
| 348 'src_uri' => 'string(//rc:package/rc:srcuri)', | |
| 349 'uri' => 'string(//rc:package/rc:uri)', | |
| 350 ); | |
| 351 | |
| 352 foreach ($metadata as $key => $path) { | |
| 353 $info[$key] = $xpath->evaluate($path); | |
| 354 } | |
| 355 | |
| 356 // dependent required plugins (can be used, but not included in config) | |
| 357 $deps = $xpath->evaluate('//rc:package/rc:dependencies/rc:required/rc:package/rc:name'); | |
| 358 for ($i = 0; $i < $deps->length; $i++) { | |
| 359 $dn = $deps->item($i)->nodeValue; | |
| 360 $info['require'][] = $dn; | |
| 361 } | |
| 362 } | |
| 363 } | |
| 364 | |
| 365 // At least provide the name | |
| 366 if (!$info && class_exists($plugin_name)) { | |
| 367 $info = array('name' => $plugin_name, 'version' => '--'); | |
| 368 } | |
| 369 else if ($info['license'] && empty($info['license_uri']) && ($license_uri = $license_uris[$info['license']])) { | |
| 370 $info['license_uri'] = $license_uri; | |
| 371 } | |
| 372 | |
| 373 return $info; | |
| 374 } | |
| 375 | |
| 376 /** | |
| 377 * Allows a plugin object to register a callback for a certain hook | |
| 378 * | |
| 379 * @param string $hook Hook name | |
| 380 * @param mixed $callback String with global function name or array($obj, 'methodname') | |
| 381 */ | |
| 382 public function register_hook($hook, $callback) | |
| 383 { | |
| 384 if (is_callable($callback)) { | |
| 385 if (isset($this->deprecated_hooks[$hook])) { | |
| 386 rcube::raise_error(array('code' => 522, 'type' => 'php', | |
| 387 'file' => __FILE__, 'line' => __LINE__, | |
| 388 'message' => "Deprecated hook name. " | |
| 389 . $hook . ' -> ' . $this->deprecated_hooks[$hook]), true, false); | |
| 390 $hook = $this->deprecated_hooks[$hook]; | |
| 391 } | |
| 392 $this->handlers[$hook][] = $callback; | |
| 393 } | |
| 394 else { | |
| 395 rcube::raise_error(array('code' => 521, 'type' => 'php', | |
| 396 'file' => __FILE__, 'line' => __LINE__, | |
| 397 'message' => "Invalid callback function for $hook"), true, false); | |
| 398 } | |
| 399 } | |
| 400 | |
| 401 /** | |
| 402 * Allow a plugin object to unregister a callback. | |
| 403 * | |
| 404 * @param string $hook Hook name | |
| 405 * @param mixed $callback String with global function name or array($obj, 'methodname') | |
| 406 */ | |
| 407 public function unregister_hook($hook, $callback) | |
| 408 { | |
| 409 $callback_id = array_search($callback, (array) $this->handlers[$hook]); | |
| 410 if ($callback_id !== false) { | |
| 411 // array_splice() removes the element and re-indexes keys | |
| 412 // that is required by the 'for' loop in exec_hook() below | |
| 413 array_splice($this->handlers[$hook], $callback_id, 1); | |
| 414 } | |
| 415 } | |
| 416 | |
| 417 /** | |
| 418 * Triggers a plugin hook. | |
| 419 * This is called from the application and executes all registered handlers | |
| 420 * | |
| 421 * @param string $hook Hook name | |
| 422 * @param array $args Named arguments (key->value pairs) | |
| 423 * | |
| 424 * @return array The (probably) altered hook arguments | |
| 425 */ | |
| 426 public function exec_hook($hook, $args = array()) | |
| 427 { | |
| 428 if (!is_array($args)) { | |
| 429 $args = array('arg' => $args); | |
| 430 } | |
| 431 | |
| 432 // TODO: avoid recursion by checking in_array($hook, $this->exec_stack) ? | |
| 433 | |
| 434 $args += array('abort' => false); | |
| 435 array_push($this->exec_stack, $hook); | |
| 436 | |
| 437 // Use for loop here, so handlers added in the hook will be executed too | |
| 438 if (!empty($this->handlers[$hook])) { | |
| 439 for ($i = 0; $i < count($this->handlers[$hook]); $i++) { | |
| 440 $ret = call_user_func($this->handlers[$hook][$i], $args); | |
| 441 if ($ret && is_array($ret)) { | |
| 442 $args = $ret + $args; | |
| 443 } | |
| 444 | |
|
17
dd5ed6ef69c9
Slowly cleaning up more php8 Warnings/deprecations
Charlie Root
parents:
14
diff
changeset
|
445 if ($args['break']??null) { |
| 0 | 446 break; |
| 447 } | |
| 448 } | |
| 449 } | |
| 450 | |
| 451 array_pop($this->exec_stack); | |
| 452 return $args; | |
| 453 } | |
| 454 | |
| 455 /** | |
| 456 * Let a plugin register a handler for a specific request | |
| 457 * | |
| 458 * @param string $action Action name (_task=mail&_action=plugin.foo) | |
| 459 * @param string $owner Plugin name that registers this action | |
| 460 * @param mixed $callback Callback: string with global function name or array($obj, 'methodname') | |
| 461 * @param string $task Task name registered by this plugin | |
| 462 */ | |
| 463 public function register_action($action, $owner, $callback, $task = null) | |
| 464 { | |
| 465 // check action name | |
| 466 if ($task) | |
| 467 $action = $task.'.'.$action; | |
| 468 else if (strpos($action, 'plugin.') !== 0) | |
| 469 $action = 'plugin.'.$action; | |
| 470 | |
| 471 // can register action only if it's not taken or registered by myself | |
| 472 if (!isset($this->actionmap[$action]) || $this->actionmap[$action] == $owner) { | |
| 473 $this->actions[$action] = $callback; | |
| 474 $this->actionmap[$action] = $owner; | |
| 475 } | |
| 476 else { | |
| 477 rcube::raise_error(array('code' => 523, 'type' => 'php', | |
| 478 'file' => __FILE__, 'line' => __LINE__, | |
| 479 'message' => "Cannot register action $action;" | |
| 480 ." already taken by another plugin"), true, false); | |
| 481 } | |
| 482 } | |
| 483 | |
| 484 /** | |
| 485 * This method handles requests like _task=mail&_action=plugin.foo | |
| 486 * It executes the callback function that was registered with the given action. | |
| 487 * | |
| 488 * @param string $action Action name | |
| 489 */ | |
| 490 public function exec_action($action) | |
| 491 { | |
| 492 if (isset($this->actions[$action])) { | |
| 493 call_user_func($this->actions[$action]); | |
| 494 } | |
| 495 else if (rcube::get_instance()->action != 'refresh') { | |
| 496 rcube::raise_error(array('code' => 524, 'type' => 'php', | |
| 497 'file' => __FILE__, 'line' => __LINE__, | |
| 498 'message' => "No handler found for action $action"), true, true); | |
| 499 } | |
| 500 } | |
| 501 | |
| 502 /** | |
| 503 * Register a handler function for template objects | |
| 504 * | |
| 505 * @param string $name Object name | |
| 506 * @param string $owner Plugin name that registers this action | |
| 507 * @param mixed $callback Callback: string with global function name or array($obj, 'methodname') | |
| 508 */ | |
| 509 public function register_handler($name, $owner, $callback) | |
| 510 { | |
| 511 // check name | |
| 512 if (strpos($name, 'plugin.') !== 0) { | |
| 513 $name = 'plugin.' . $name; | |
| 514 } | |
| 515 | |
| 516 // can register handler only if it's not taken or registered by myself | |
| 517 if (is_object($this->output) | |
| 518 && (!isset($this->objectsmap[$name]) || $this->objectsmap[$name] == $owner) | |
| 519 ) { | |
| 520 $this->output->add_handler($name, $callback); | |
| 521 $this->objectsmap[$name] = $owner; | |
| 522 } | |
| 523 else { | |
| 524 rcube::raise_error(array('code' => 525, 'type' => 'php', | |
| 525 'file' => __FILE__, 'line' => __LINE__, | |
| 526 'message' => "Cannot register template handler $name;" | |
| 527 ." already taken by another plugin or no output object available"), true, false); | |
| 528 } | |
| 529 } | |
| 530 | |
| 531 /** | |
| 532 * Register this plugin to be responsible for a specific task | |
| 533 * | |
| 534 * @param string $task Task name (only characters [a-z0-9_-] are allowed) | |
| 535 * @param string $owner Plugin name that registers this action | |
| 536 */ | |
| 537 public function register_task($task, $owner) | |
| 538 { | |
| 539 // tasks are irrelevant in framework mode | |
| 540 if (!class_exists('rcmail', false)) { | |
| 541 return true; | |
| 542 } | |
| 543 | |
| 544 if ($task != asciiwords($task, true)) { | |
| 545 rcube::raise_error(array('code' => 526, 'type' => 'php', | |
| 546 'file' => __FILE__, 'line' => __LINE__, | |
| 547 'message' => "Invalid task name: $task." | |
| 548 ." Only characters [a-z0-9_.-] are allowed"), true, false); | |
| 549 } | |
| 550 else if (in_array($task, rcmail::$main_tasks)) { | |
| 551 rcube::raise_error(array('code' => 526, 'type' => 'php', | |
| 552 'file' => __FILE__, 'line' => __LINE__, | |
| 553 'message' => "Cannot register taks $task;" | |
| 554 ." already taken by another plugin or the application itself"), true, false); | |
| 555 } | |
| 556 else { | |
| 557 $this->tasks[$task] = $owner; | |
| 558 rcmail::$main_tasks[] = $task; | |
| 559 return true; | |
| 560 } | |
| 561 | |
| 562 return false; | |
| 563 } | |
| 564 | |
| 565 /** | |
| 566 * Checks whether the given task is registered by a plugin | |
| 567 * | |
| 568 * @param string $task Task name | |
| 569 * | |
| 570 * @return boolean True if registered, otherwise false | |
| 571 */ | |
| 572 public function is_plugin_task($task) | |
| 573 { | |
| 574 return $this->tasks[$task] ? true : false; | |
| 575 } | |
| 576 | |
| 577 /** | |
| 578 * Check if a plugin hook is currently processing. | |
| 579 * Mainly used to prevent loops and recursion. | |
| 580 * | |
| 581 * @param string $hook Hook to check (optional) | |
| 582 * | |
| 583 * @return boolean True if any/the given hook is currently processed, otherwise false | |
| 584 */ | |
| 585 public function is_processing($hook = null) | |
| 586 { | |
| 587 return count($this->exec_stack) > 0 && (!$hook || in_array($hook, $this->exec_stack)); | |
| 588 } | |
| 589 | |
| 590 /** | |
| 591 * Include a plugin script file in the current HTML page | |
| 592 * | |
| 593 * @param string $fn Path to script | |
| 594 */ | |
| 595 public function include_script($fn) | |
| 596 { | |
| 597 if (is_object($this->output) && $this->output->type == 'html') { | |
| 598 $src = $this->resource_url($fn); | |
| 599 $this->output->add_header(html::tag('script', | |
| 600 array('type' => "text/javascript", 'src' => $src))); | |
| 601 } | |
| 602 } | |
| 603 | |
| 604 /** | |
| 605 * Include a plugin stylesheet in the current HTML page | |
| 606 * | |
| 607 * @param string $fn Path to stylesheet | |
| 608 */ | |
| 609 public function include_stylesheet($fn) | |
| 610 { | |
| 611 if (is_object($this->output) && $this->output->type == 'html') { | |
| 612 $src = $this->resource_url($fn); | |
| 613 $this->output->include_css($src); | |
| 614 } | |
| 615 } | |
| 616 | |
| 617 /** | |
| 618 * Save the given HTML content to be added to a template container | |
| 619 * | |
| 620 * @param string $html HTML content | |
| 621 * @param string $container Template container identifier | |
| 622 */ | |
| 623 public function add_content($html, $container) | |
| 624 { | |
| 625 $this->template_contents[$container] .= $html . "\n"; | |
| 626 } | |
| 627 | |
| 628 /** | |
| 629 * Returns list of loaded plugins names | |
| 630 * | |
| 631 * @return array List of plugin names | |
| 632 */ | |
| 633 public function loaded_plugins() | |
| 634 { | |
| 635 return array_keys($this->plugins); | |
| 636 } | |
| 637 | |
| 638 /** | |
| 639 * Returns loaded plugin | |
| 640 * | |
| 641 * @return rcube_plugin Plugin instance | |
| 642 */ | |
| 643 public function get_plugin($name) | |
| 644 { | |
| 645 return $this->plugins[$name]; | |
| 646 } | |
| 647 | |
| 648 /** | |
| 649 * Callback for template_container hooks | |
| 650 * | |
| 651 * @param array $attrib | |
| 652 * @return array | |
| 653 */ | |
| 654 protected function template_container_hook($attrib) | |
| 655 { | |
| 656 $container = $attrib['name']; | |
| 657 return array('content' => $attrib['content'] . $this->template_contents[$container]); | |
| 658 } | |
| 659 | |
| 660 /** | |
| 661 * Make the given file name link into the plugins directory | |
| 662 * | |
| 663 * @param string $fn Filename | |
| 664 * @return string | |
| 665 */ | |
| 666 protected function resource_url($fn) | |
| 667 { | |
| 668 if ($fn[0] != '/' && !preg_match('|^https?://|i', $fn)) | |
| 669 return $this->url . $fn; | |
| 670 else | |
| 671 return $fn; | |
| 672 } | |
| 673 } |
