source: subversion/branches/devel-api/program/include/rcube_plugin_api.php @ 2279

Last change on this file since 2279 was 2279, checked in by thomasb, 4 years ago

Add localization functions to plugin API

File size: 7.0 KB
Line 
1<?php
2
3/*
4 +-----------------------------------------------------------------------+
5 | program/include/rcube_plugin_api.php                                  |
6 |                                                                       |
7 | This file is part of the RoundCube Webmail client                     |
8 | Copyright (C) 2008-2009, RoundCube Dev. - Switzerland                 |
9 | Licensed under the GNU GPL                                            |
10 |                                                                       |
11 | PURPOSE:                                                              |
12 |   Plugins repository                                                  |
13 |                                                                       |
14 +-----------------------------------------------------------------------+
15 | Author: Thomas Bruederli <roundcube@gmail.com>                        |
16 +-----------------------------------------------------------------------+
17
18 $Id: $
19
20*/
21
22/**
23 * The plugin loader and global API
24 *
25 * @package Core
26 */
27class rcube_plugin_api
28{
29  static private $instance;
30 
31  public $dir;
32  public $url = 'plugins/';
33 
34  private $handlers = array();
35  private $plugins = array();
36  private $actions = array();
37  private $actionmap = array();
38  private $templobjects = array();
39  private $objectsmap = array();
40  private $scripts = array();
41  private $output;
42 
43
44  /**
45   * This implements the 'singleton' design pattern
46   *
47   * @return object rcube_plugin_api The one and only instance if this class
48   */
49  static function get_instance()
50  {
51    if (!self::$instance) {
52      self::$instance = new rcube_plugin_api();
53    }
54
55    return self::$instance;
56  }
57 
58 
59  /**
60   * Private constructor
61   */
62  private function __construct()
63  {
64    $rcmail = rcmail::get_instance();
65   
66    // only active in devel_mode for now
67    if (!$rcmail->config->get('devel_mode'))
68      return;
69   
70    $this->dir = realpath($rcmail->config->get('plugins_dir'));
71   
72    // load all enabled plugins
73    $plugins_dir = dir($this->dir);
74    $plugins_enabled = (array)$rcmail->config->get('plugins', array());
75   
76    foreach ($plugins_enabled as $plugin_name) {
77      $fn = $plugins_dir->path . DIRECTORY_SEPARATOR . $plugin_name . DIRECTORY_SEPARATOR . $plugin_name . '.php';
78     
79      if (file_exists($fn)) {
80        include($fn);
81       
82        // instantiate class if exists
83        if (class_exists($plugin_name, false)) {
84          $plugin = new $plugin_name($this);
85          // check inheritance and task specification
86          if (is_subclass_of($plugin, 'rcube_plugin') && (!$plugin->task || $plugin->task == $rcmail->task)) {
87            $plugin->init();
88            $this->plugins[] = $plugin;
89          }
90        }
91        else {
92          raise_error(array('code' => 520, 'type' => 'php', 'message' => "No plugin class $plugin_name found in $fn"), true, false);
93        }
94      }
95      else {
96        raise_error(array('code' => 520, 'type' => 'php', 'message' => "Failed to load plugin file $fn"), true, false);
97      }
98    }
99   
100    // maybe also register a shudown function which triggers shutdown functions of all plugin objects
101  }
102 
103 
104  /**
105   * Add GUI things once the output objects is created
106   */
107  public function init_gui($output)
108  {
109    if ($output->type == 'html') {
110      $output->add_handlers($this->objectsmap);
111     
112      foreach ($this->scripts as $script)
113        $output->add_header(html::tag('script', array('type' => "text/javascript", 'src' => $script)));
114    }
115   
116    $this->output = $output;
117  }
118 
119 
120  /**
121   * Allows a plugin object to register a callback for a certain hook
122   *
123   * @param string Hook name
124   * @param mixed String with global function name or array($obj, 'methodname')
125   */
126  public function register_hook($hook, $callback)
127  {
128    if (is_callable($callback))
129      $this->handlers[$hook][] = $callback;
130    else
131      raise_error(array('code' => 521, 'type' => 'php', 'message' => "Invalid callback function for $hook"), true, false);
132  }
133 
134 
135  /**
136   * Triggers a plugin hook.
137   * This is called from the application and executes all registered handlers
138   *
139   * @param string Hook name
140   * @param array Named arguments (key->value pairs)
141   * @return array The (probably) altered hook arguments
142   */
143  public function exec_hook($hook, $args = array())
144  {
145    $args += array('abort' => false);
146   
147    foreach ((array)$this->handlers[$hook] as $callback) {
148      $ret = call_user_func($callback, $args);
149      if ($ret && is_array($ret))
150        $args = $ret + $args;
151     
152      if ($args['abort'])
153        break;
154    }
155   
156    return $args;
157  }
158
159
160  /**
161   * Let a plugin register a handler for a specific request
162   *
163   * @param string Action name (_task=mail&_action=plugin.foo)
164   * @param string Plugin name that registers this action
165   * @param mixed Callback: string with global function name or array($obj, 'methodname')
166   */
167  public function register_action($action, $owner, $callback)
168  {
169    // check action name
170    if (strpos($action, 'plugin.') !== 0)
171      $action = 'plugin.'.$action;
172   
173    // can register action only if it's not taken or registered by myself
174    if (!isset($this->actionmap[$action]) || $this->actionmap[$action] == $owner) {
175      $this->actions[$action] = $callback;
176      $this->actionmap[$action] = $owner;
177    }
178    else {
179      raise_error(array('code' => 523, 'type' => 'php', 'message' => "Cannot register action $action; already taken by another plugin"), true, false);
180    }
181  }
182
183
184  /**
185   * This method handles requests like _task=mail&_action=plugin.foo
186   * It executes the callback function that was registered with the given action.
187   *
188   * @param string Action name
189   */
190  public function exec_action($action)
191  {
192    if (isset($this->actions[$action])) {
193      call_user_func($this->actions[$action]);
194    }
195    else {
196      raise_error(array('code' => 524, 'type' => 'php', 'message' => "No handler found for action $action"), true, true);
197    }
198  }
199
200
201  /**
202   * Register a handler function for template objects
203   *
204   * @param string Object name
205   * @param string Plugin name that registers this action
206   * @param mixed Callback: string with global function name or array($obj, 'methodname')
207   */
208  public function register_handler($name, $owner, $callback)
209  {
210    // check name
211    if (strpos($name, 'plugin.') !== 0)
212      $name = 'plugin.'.$name;
213   
214    // can register handler only if it's not taken or registered by myself
215    if (!isset($this->objectsmap[$name]) || $this->objectsmap[$name] == $owner) {
216      // output is ready
217      if ($this->output) {
218        $this->output->add_handler($name, $callback);
219      }
220      else {
221        $this->templobjects[$name] = $callback;
222        $this->objectsmap[$name] = $owner;
223      }
224    }
225    else {
226      raise_error(array('code' => 525, 'type' => 'php', 'message' => "Cannot register template handler $name; already taken by another plugin"), true, false);
227    }
228  }
229 
230  /**
231   *
232   */
233  public function include_script($fn)
234  {
235    $this->scripts[] = $this->url . $fn;
236  }
237
238
239}
240
Note: See TracBrowser for help on using the repository browser.