Mercurial > hg > rc2
comparison program/lib/Roundcube/rcube_plugin.php @ 0:4681f974d28b
vanilla 1.3.3 distro, I hope
| author | Charlie Root |
|---|---|
| date | Thu, 04 Jan 2018 15:52:31 -0500 |
| parents | |
| children | 3a5f959af5ae |
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-2014, 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 | Abstract plugins interface/class | | |
| 14 | All plugins need to extend this class | | |
| 15 +-----------------------------------------------------------------------+ | |
| 16 | Author: Thomas Bruederli <roundcube@gmail.com> | | |
| 17 +-----------------------------------------------------------------------+ | |
| 18 */ | |
| 19 | |
| 20 /** | |
| 21 * Plugin interface class | |
| 22 * | |
| 23 * @package Framework | |
| 24 * @subpackage PluginAPI | |
| 25 */ | |
| 26 abstract class rcube_plugin | |
| 27 { | |
| 28 /** | |
| 29 * Class name of the plugin instance | |
| 30 * | |
| 31 * @var string | |
| 32 */ | |
| 33 public $ID; | |
| 34 | |
| 35 /** | |
| 36 * Instance of Plugin API | |
| 37 * | |
| 38 * @var rcube_plugin_api | |
| 39 */ | |
| 40 public $api; | |
| 41 | |
| 42 /** | |
| 43 * Regular expression defining task(s) to bind with | |
| 44 * | |
| 45 * @var string | |
| 46 */ | |
| 47 public $task; | |
| 48 | |
| 49 /** | |
| 50 * Disables plugin in AJAX requests | |
| 51 * | |
| 52 * @var boolean | |
| 53 */ | |
| 54 public $noajax = false; | |
| 55 | |
| 56 /** | |
| 57 * Disables plugin in framed mode | |
| 58 * | |
| 59 * @var boolean | |
| 60 */ | |
| 61 public $noframe = false; | |
| 62 | |
| 63 /** | |
| 64 * A list of config option names that can be modified | |
| 65 * by the user via user interface (with save-prefs command) | |
| 66 * | |
| 67 * @var array | |
| 68 */ | |
| 69 public $allowed_prefs; | |
| 70 | |
| 71 protected $home; | |
| 72 protected $urlbase; | |
| 73 private $mytask; | |
| 74 private $loaded_config = array(); | |
| 75 | |
| 76 | |
| 77 /** | |
| 78 * Default constructor. | |
| 79 * | |
| 80 * @param rcube_plugin_api $api Plugin API | |
| 81 */ | |
| 82 public function __construct($api) | |
| 83 { | |
| 84 $this->ID = get_class($this); | |
| 85 $this->api = $api; | |
| 86 $this->home = $api->dir . $this->ID; | |
| 87 $this->urlbase = $api->url . $this->ID . '/'; | |
| 88 } | |
| 89 | |
| 90 /** | |
| 91 * Initialization method, needs to be implemented by the plugin itself | |
| 92 */ | |
| 93 abstract function init(); | |
| 94 | |
| 95 /** | |
| 96 * Provide information about this | |
| 97 * | |
| 98 * @return array Meta information about a plugin or false if not implemented: | |
| 99 * As hash array with the following keys: | |
| 100 * name: The plugin name | |
| 101 * vendor: Name of the plugin developer | |
| 102 * version: Plugin version name | |
| 103 * license: License name (short form according to http://spdx.org/licenses/) | |
| 104 * uri: The URL to the plugin homepage or source repository | |
| 105 * src_uri: Direct download URL to the source code of this plugin | |
| 106 * require: List of plugins required for this one (as array of plugin names) | |
| 107 */ | |
| 108 public static function info() | |
| 109 { | |
| 110 return false; | |
| 111 } | |
| 112 | |
| 113 /** | |
| 114 * Attempt to load the given plugin which is required for the current plugin | |
| 115 * | |
| 116 * @param string Plugin name | |
| 117 * @return boolean True on success, false on failure | |
| 118 */ | |
| 119 public function require_plugin($plugin_name) | |
| 120 { | |
| 121 return $this->api->load_plugin($plugin_name, true); | |
| 122 } | |
| 123 | |
| 124 /** | |
| 125 * Attempt to load the given plugin which is optional for the current plugin | |
| 126 * | |
| 127 * @param string Plugin name | |
| 128 * @return boolean True on success, false on failure | |
| 129 */ | |
| 130 public function include_plugin($plugin_name) | |
| 131 { | |
| 132 return $this->api->load_plugin($plugin_name, true, false); | |
| 133 } | |
| 134 | |
| 135 /** | |
| 136 * Load local config file from plugins directory. | |
| 137 * The loaded values are patched over the global configuration. | |
| 138 * | |
| 139 * @param string $fname Config file name relative to the plugin's folder | |
| 140 * | |
| 141 * @return boolean True on success, false on failure | |
| 142 */ | |
| 143 public function load_config($fname = 'config.inc.php') | |
| 144 { | |
| 145 if (in_array($fname, $this->loaded_config)) { | |
| 146 return true; | |
| 147 } | |
| 148 | |
| 149 $this->loaded_config[] = $fname; | |
| 150 | |
| 151 $fpath = $this->home.'/'.$fname; | |
| 152 $rcube = rcube::get_instance(); | |
| 153 | |
| 154 if (($is_local = is_file($fpath)) && !$rcube->config->load_from_file($fpath)) { | |
| 155 rcube::raise_error(array( | |
| 156 'code' => 527, 'type' => 'php', | |
| 157 'file' => __FILE__, 'line' => __LINE__, | |
| 158 'message' => "Failed to load config from $fpath"), true, false); | |
| 159 return false; | |
| 160 } | |
| 161 else if (!$is_local) { | |
| 162 // Search plugin_name.inc.php file in any configured path | |
| 163 return $rcube->config->load_from_file($this->ID . '.inc.php'); | |
| 164 } | |
| 165 | |
| 166 return true; | |
| 167 } | |
| 168 | |
| 169 /** | |
| 170 * Register a callback function for a specific (server-side) hook | |
| 171 * | |
| 172 * @param string $hook Hook name | |
| 173 * @param mixed $callback Callback function as string or array | |
| 174 * with object reference and method name | |
| 175 */ | |
| 176 public function add_hook($hook, $callback) | |
| 177 { | |
| 178 $this->api->register_hook($hook, $callback); | |
| 179 } | |
| 180 | |
| 181 /** | |
| 182 * Unregister a callback function for a specific (server-side) hook. | |
| 183 * | |
| 184 * @param string $hook Hook name | |
| 185 * @param mixed $callback Callback function as string or array | |
| 186 * with object reference and method name | |
| 187 */ | |
| 188 public function remove_hook($hook, $callback) | |
| 189 { | |
| 190 $this->api->unregister_hook($hook, $callback); | |
| 191 } | |
| 192 | |
| 193 /** | |
| 194 * Load localized texts from the plugins dir | |
| 195 * | |
| 196 * @param string $dir Directory to search in | |
| 197 * @param mixed $add2client Make texts also available on the client | |
| 198 * (array with list or true for all) | |
| 199 */ | |
| 200 public function add_texts($dir, $add2client = false) | |
| 201 { | |
| 202 $domain = $this->ID; | |
| 203 $lang = $_SESSION['language']; | |
| 204 $langs = array_unique(array('en_US', $lang)); | |
| 205 $locdir = slashify(realpath(slashify($this->home) . $dir)); | |
| 206 $texts = array(); | |
| 207 | |
| 208 // Language aliases used to find localization in similar lang, see below | |
| 209 $aliases = array( | |
| 210 'de_CH' => 'de_DE', | |
| 211 'es_AR' => 'es_ES', | |
| 212 'fa_AF' => 'fa_IR', | |
| 213 'nl_BE' => 'nl_NL', | |
| 214 'pt_BR' => 'pt_PT', | |
| 215 'zh_CN' => 'zh_TW', | |
| 216 ); | |
| 217 | |
| 218 // use buffering to handle empty lines/spaces after closing PHP tag | |
| 219 ob_start(); | |
| 220 | |
| 221 foreach ($langs as $lng) { | |
| 222 $fpath = $locdir . $lng . '.inc'; | |
| 223 if (is_file($fpath) && is_readable($fpath)) { | |
| 224 include $fpath; | |
| 225 $texts = (array)$labels + (array)$messages + (array)$texts; | |
| 226 } | |
| 227 else if ($lng != 'en_US') { | |
| 228 // Find localization in similar language (#1488401) | |
| 229 $alias = null; | |
| 230 if (!empty($aliases[$lng])) { | |
| 231 $alias = $aliases[$lng]; | |
| 232 } | |
| 233 else if ($key = array_search($lng, $aliases)) { | |
| 234 $alias = $key; | |
| 235 } | |
| 236 | |
| 237 if (!empty($alias)) { | |
| 238 $fpath = $locdir . $alias . '.inc'; | |
| 239 if (is_file($fpath) && is_readable($fpath)) { | |
| 240 include $fpath; | |
| 241 $texts = (array)$labels + (array)$messages + (array)$texts; | |
| 242 } | |
| 243 } | |
| 244 } | |
| 245 } | |
| 246 | |
| 247 ob_end_clean(); | |
| 248 | |
| 249 // prepend domain to text keys and add to the application texts repository | |
| 250 if (!empty($texts)) { | |
| 251 $add = array(); | |
| 252 foreach ($texts as $key => $value) { | |
| 253 $add[$domain.'.'.$key] = $value; | |
| 254 } | |
| 255 | |
| 256 $rcube = rcube::get_instance(); | |
| 257 $rcube->load_language($lang, $add); | |
| 258 | |
| 259 // add labels to client | |
| 260 if ($add2client && method_exists($rcube->output, 'add_label')) { | |
| 261 if (is_array($add2client)) { | |
| 262 $js_labels = array_map(array($this, 'label_map_callback'), $add2client); | |
| 263 } | |
| 264 else { | |
| 265 $js_labels = array_keys($add); | |
| 266 } | |
| 267 $rcube->output->add_label($js_labels); | |
| 268 } | |
| 269 } | |
| 270 } | |
| 271 | |
| 272 /** | |
| 273 * Wrapper for add_label() adding the plugin ID as domain | |
| 274 */ | |
| 275 public function add_label() | |
| 276 { | |
| 277 $rcube = rcube::get_instance(); | |
| 278 | |
| 279 if (method_exists($rcube->output, 'add_label')) { | |
| 280 $args = func_get_args(); | |
| 281 if (count($args) == 1 && is_array($args[0])) { | |
| 282 $args = $args[0]; | |
| 283 } | |
| 284 | |
| 285 $args = array_map(array($this, 'label_map_callback'), $args); | |
| 286 $rcube->output->add_label($args); | |
| 287 } | |
| 288 } | |
| 289 | |
| 290 /** | |
| 291 * Wrapper for rcube::gettext() adding the plugin ID as domain | |
| 292 * | |
| 293 * @param string $p Message identifier | |
| 294 * | |
| 295 * @return string Localized text | |
| 296 * @see rcube::gettext() | |
| 297 */ | |
| 298 public function gettext($p) | |
| 299 { | |
| 300 return rcube::get_instance()->gettext($p, $this->ID); | |
| 301 } | |
| 302 | |
| 303 /** | |
| 304 * Register this plugin to be responsible for a specific task | |
| 305 * | |
| 306 * @param string $task Task name (only characters [a-z0-9_-] are allowed) | |
| 307 */ | |
| 308 public function register_task($task) | |
| 309 { | |
| 310 if ($this->api->register_task($task, $this->ID)) { | |
| 311 $this->mytask = $task; | |
| 312 } | |
| 313 } | |
| 314 | |
| 315 /** | |
| 316 * Register a handler for a specific client-request action | |
| 317 * | |
| 318 * The callback will be executed upon a request like /?_task=mail&_action=plugin.myaction | |
| 319 * | |
| 320 * @param string $action Action name (should be unique) | |
| 321 * @param mixed $callback Callback function as string | |
| 322 * or array with object reference and method name | |
| 323 */ | |
| 324 public function register_action($action, $callback) | |
| 325 { | |
| 326 $this->api->register_action($action, $this->ID, $callback, $this->mytask); | |
| 327 } | |
| 328 | |
| 329 /** | |
| 330 * Register a handler function for a template object | |
| 331 * | |
| 332 * When parsing a template for display, tags like <roundcube:object name="plugin.myobject" /> | |
| 333 * will be replaced by the return value if the registered callback function. | |
| 334 * | |
| 335 * @param string $name Object name (should be unique and start with 'plugin.') | |
| 336 * @param mixed $callback Callback function as string or array with object reference | |
| 337 * and method name | |
| 338 */ | |
| 339 public function register_handler($name, $callback) | |
| 340 { | |
| 341 $this->api->register_handler($name, $this->ID, $callback); | |
| 342 } | |
| 343 | |
| 344 /** | |
| 345 * Make this javascipt file available on the client | |
| 346 * | |
| 347 * @param string $fn File path; absolute or relative to the plugin directory | |
| 348 */ | |
| 349 public function include_script($fn) | |
| 350 { | |
| 351 $this->api->include_script($this->resource_url($fn)); | |
| 352 } | |
| 353 | |
| 354 /** | |
| 355 * Make this stylesheet available on the client | |
| 356 * | |
| 357 * @param string $fn File path; absolute or relative to the plugin directory | |
| 358 */ | |
| 359 public function include_stylesheet($fn) | |
| 360 { | |
| 361 $this->api->include_stylesheet($this->resource_url($fn)); | |
| 362 } | |
| 363 | |
| 364 /** | |
| 365 * Append a button to a certain container | |
| 366 * | |
| 367 * @param array $p Hash array with named parameters (as used in skin templates) | |
| 368 * @param string $container Container name where the buttons should be added to | |
| 369 * | |
| 370 * @see rcube_remplate::button() | |
| 371 */ | |
| 372 public function add_button($p, $container) | |
| 373 { | |
| 374 if ($this->api->output->type == 'html') { | |
| 375 // fix relative paths | |
| 376 foreach (array('imagepas', 'imageact', 'imagesel') as $key) { | |
| 377 if ($p[$key]) { | |
| 378 $p[$key] = $this->api->url . $this->resource_url($p[$key]); | |
| 379 } | |
| 380 } | |
| 381 | |
| 382 $this->api->add_content($this->api->output->button($p), $container); | |
| 383 } | |
| 384 } | |
| 385 | |
| 386 /** | |
| 387 * Generate an absolute URL to the given resource within the current | |
| 388 * plugin directory | |
| 389 * | |
| 390 * @param string $fn The file name | |
| 391 * | |
| 392 * @return string Absolute URL to the given resource | |
| 393 */ | |
| 394 public function url($fn) | |
| 395 { | |
| 396 return $this->api->url . $this->resource_url($fn); | |
| 397 } | |
| 398 | |
| 399 /** | |
| 400 * Make the given file name link into the plugin directory | |
| 401 * | |
| 402 * @param string $fn Filename | |
| 403 */ | |
| 404 private function resource_url($fn) | |
| 405 { | |
| 406 if ($fn[0] != '/' && !preg_match('|^https?://|i', $fn)) { | |
| 407 return $this->ID . '/' . $fn; | |
| 408 } | |
| 409 else { | |
| 410 return $fn; | |
| 411 } | |
| 412 } | |
| 413 | |
| 414 /** | |
| 415 * Provide path to the currently selected skin folder within the plugin directory | |
| 416 * with a fallback to the default skin folder. | |
| 417 * | |
| 418 * @return string Skin path relative to plugins directory | |
| 419 */ | |
| 420 public function local_skin_path() | |
| 421 { | |
| 422 $rcube = rcube::get_instance(); | |
| 423 $skins = array_keys((array)$rcube->output->skins); | |
| 424 if (empty($skins)) { | |
| 425 $skins = (array) $rcube->config->get('skin'); | |
| 426 } | |
| 427 foreach ($skins as $skin) { | |
| 428 $skin_path = 'skins/' . $skin; | |
| 429 if (is_dir(realpath(slashify($this->home) . $skin_path))) { | |
| 430 break; | |
| 431 } | |
| 432 } | |
| 433 | |
| 434 return $skin_path; | |
| 435 } | |
| 436 | |
| 437 /** | |
| 438 * Callback function for array_map | |
| 439 * | |
| 440 * @param string $key Array key. | |
| 441 * @return string | |
| 442 */ | |
| 443 private function label_map_callback($key) | |
| 444 { | |
| 445 if (strpos($key, $this->ID.'.') === 0) { | |
| 446 return $key; | |
| 447 } | |
| 448 | |
| 449 return $this->ID.'.'.$key; | |
| 450 } | |
| 451 } |
