Mercurial > hg > rc2
comparison program/lib/Roundcube/rcube_plugin_api.php @ 0:4681f974d28b
vanilla 1.3.3 distro, I hope
author | Charlie Root |
---|---|
date | Thu, 04 Jan 2018 15:52:31 -0500 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4681f974d28b |
---|---|
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 | |
98 if (!$this->plugins_initialized[$plugin->ID] && !$this->filter($plugin)) { | |
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? | |
167 if (!$this->plugins[$plugin_name]) { | |
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 | |
445 if ($args['break']) { | |
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 } |