]> git.wh0rd.org Git - tt-rss.git/blob - classes/pluginhost.php
remove googlereaderimport: really who cares anymore
[tt-rss.git] / classes / pluginhost.php
1 <?php
2 class PluginHost {
3         private $dbh;
4         private $hooks = array();
5         private $plugins = array();
6         private $handlers = array();
7         private $commands = array();
8         private $storage = array();
9         private $feeds = array();
10         private $api_methods = array();
11         private $plugin_actions = array();
12         private $owner_uid;
13         private $debug;
14         private $last_registered;
15         private static $instance;
16
17         const API_VERSION = 2;
18
19         // Hooks marked with *1 are run in global context and available
20         // to plugins loaded in config.php only
21
22         const HOOK_ARTICLE_BUTTON = 1;
23         const HOOK_ARTICLE_FILTER = 2;
24         const HOOK_PREFS_TAB = 3;
25         const HOOK_PREFS_TAB_SECTION = 4;
26         const HOOK_PREFS_TABS = 5;
27         const HOOK_FEED_PARSED = 6;
28         const HOOK_UPDATE_TASK = 7; // *1
29         const HOOK_AUTH_USER = 8;
30         const HOOK_HOTKEY_MAP = 9;
31         const HOOK_RENDER_ARTICLE = 10;
32         const HOOK_RENDER_ARTICLE_CDM = 11;
33         const HOOK_FEED_FETCHED = 12;
34         const HOOK_SANITIZE = 13;
35         const HOOK_RENDER_ARTICLE_API = 14;
36         const HOOK_TOOLBAR_BUTTON = 15;
37         const HOOK_ACTION_ITEM = 16;
38         const HOOK_HEADLINE_TOOLBAR_BUTTON = 17;
39         const HOOK_HOTKEY_INFO = 18;
40         const HOOK_ARTICLE_LEFT_BUTTON = 19;
41         const HOOK_PREFS_EDIT_FEED = 20;
42         const HOOK_PREFS_SAVE_FEED = 21;
43         const HOOK_FETCH_FEED = 22;
44         const HOOK_QUERY_HEADLINES = 23;
45         const HOOK_HOUSE_KEEPING = 24; // *1
46         const HOOK_SEARCH = 25;
47         const HOOK_FORMAT_ENCLOSURES = 26;
48         const HOOK_SUBSCRIBE_FEED = 27;
49         const HOOK_HEADLINES_BEFORE = 28;
50         const HOOK_RENDER_ENCLOSURE = 29;
51         const HOOK_ARTICLE_FILTER_ACTION = 30;
52
53         const KIND_ALL = 1;
54         const KIND_SYSTEM = 2;
55         const KIND_USER = 3;
56
57         function __construct() {
58                 $this->dbh = Db::get();
59
60                 $this->storage = array();
61         }
62
63         private function __clone() {
64                 //
65         }
66
67         public static function getInstance() {
68                 if (self::$instance == null)
69                         self::$instance = new self();
70
71                 return self::$instance;
72         }
73
74         private function register_plugin($name, $plugin) {
75                 //array_push($this->plugins, $plugin);
76                 $this->plugins[$name] = $plugin;
77         }
78
79         // needed for compatibility with API 1
80         function get_link() {
81                 return false;
82         }
83
84         function get_dbh() {
85                 return $this->dbh;
86         }
87
88         function get_plugin_names() {
89                 $names = array();
90
91                 foreach ($this->plugins as $p) {
92                         array_push($names, get_class($p));
93                 }
94
95                 return $names;
96         }
97
98         function get_plugins() {
99                 return $this->plugins;
100         }
101
102         function get_plugin($name) {
103                 return $this->plugins[strtolower($name)];
104         }
105
106         function run_hooks($type, $method, $args) {
107                 foreach ($this->get_hooks($type) as $hook) {
108                         $hook->$method($args);
109                 }
110         }
111
112         function add_hook($type, $sender) {
113                 if (!is_array($this->hooks[$type])) {
114                         $this->hooks[$type] = array();
115                 }
116
117                 array_push($this->hooks[$type], $sender);
118         }
119
120         function del_hook($type, $sender) {
121                 if (is_array($this->hooks[$type])) {
122                         $key = array_Search($sender, $this->hooks[$type]);
123                         if ($key !== FALSE) {
124                                 unset($this->hooks[$type][$key]);
125                         }
126                 }
127         }
128
129         function get_hooks($type) {
130                 if (isset($this->hooks[$type])) {
131                         return $this->hooks[$type];
132                 } else {
133                         return array();
134                 }
135         }
136         function load_all($kind, $owner_uid = false, $skip_init = false) {
137
138                 $plugins = array_merge(glob("plugins/*"), glob("plugins.local/*"));
139                 $plugins = array_filter($plugins, "is_dir");
140                 $plugins = array_map("basename", $plugins);
141
142                 asort($plugins);
143
144                 $this->load(join(",", $plugins), $kind, $owner_uid, $skip_init);
145         }
146
147         function load($classlist, $kind, $owner_uid = false, $skip_init = false) {
148                 $plugins = explode(",", $classlist);
149
150                 $this->owner_uid = (int) $owner_uid;
151
152                 foreach ($plugins as $class) {
153                         $class = trim($class);
154                         $class_file = strtolower(basename($class));
155
156                         if (!is_dir(__DIR__."/../plugins/$class_file") &&
157                                         !is_dir(__DIR__."/../plugins.local/$class_file")) continue;
158
159                         // try system plugin directory first
160                         $file = __DIR__ . "/../plugins/$class_file/init.php";
161
162                         if (!file_exists($file)) {
163                                 $file = __DIR__ . "/../plugins.local/$class_file/init.php";
164                         }
165
166                         if (!isset($this->plugins[$class])) {
167                                 if (file_exists($file)) require_once $file;
168
169                                 if (class_exists($class) && is_subclass_of($class, "Plugin")) {
170                                         $plugin = new $class($this);
171
172                                         $plugin_api = $plugin->api_version();
173
174                                         if ($plugin_api < PluginHost::API_VERSION) {
175                                                 user_error("Plugin $class is not compatible with current API version (need: " . PluginHost::API_VERSION . ", got: $plugin_api)", E_USER_WARNING);
176                                                 continue;
177                                         }
178
179                                         $this->last_registered = $class;
180
181                                         switch ($kind) {
182                                         case $this::KIND_SYSTEM:
183                                                 if ($this->is_system($plugin)) {
184                                                         if (!$skip_init) $plugin->init($this);
185                                                         $this->register_plugin($class, $plugin);
186                                                 }
187                                                 break;
188                                         case $this::KIND_USER:
189                                                 if (!$this->is_system($plugin)) {
190                                                         if (!$skip_init) $plugin->init($this);
191                                                         $this->register_plugin($class, $plugin);
192                                                 }
193                                                 break;
194                                         case $this::KIND_ALL:
195                                                 if (!$skip_init) $plugin->init($this);
196                                                 $this->register_plugin($class, $plugin);
197                                                 break;
198                                         }
199                                 }
200                         }
201                 }
202         }
203
204         function is_system($plugin) {
205                 $about = $plugin->about();
206
207                 return @$about[3];
208         }
209
210         // only system plugins are allowed to modify routing
211         function add_handler($handler, $method, $sender) {
212                 $handler = str_replace("-", "_", strtolower($handler));
213                 $method = strtolower($method);
214
215                 if ($this->is_system($sender)) {
216                         if (!is_array($this->handlers[$handler])) {
217                                 $this->handlers[$handler] = array();
218                         }
219
220                         $this->handlers[$handler][$method] = $sender;
221                 }
222         }
223
224         function del_handler($handler, $method, $sender) {
225                 $handler = str_replace("-", "_", strtolower($handler));
226                 $method = strtolower($method);
227
228                 if ($this->is_system($sender)) {
229                         unset($this->handlers[$handler][$method]);
230                 }
231         }
232
233         function lookup_handler($handler, $method) {
234                 $handler = str_replace("-", "_", strtolower($handler));
235                 $method = strtolower($method);
236
237                 if (is_array($this->handlers[$handler])) {
238                         if (isset($this->handlers[$handler]["*"])) {
239                                 return $this->handlers[$handler]["*"];
240                         } else {
241                                 return $this->handlers[$handler][$method];
242                         }
243                 }
244
245                 return false;
246         }
247
248         function add_command($command, $description, $sender, $suffix = "", $arghelp = "") {
249                 $command = str_replace("-", "_", strtolower($command));
250
251                 $this->commands[$command] = array("description" => $description,
252                         "suffix" => $suffix,
253                         "arghelp" => $arghelp,
254                         "class" => $sender);
255         }
256
257         function del_command($command) {
258                 $command = "-" . strtolower($command);
259
260                 unset($this->commands[$command]);
261         }
262
263         function lookup_command($command) {
264                 $command = "-" . strtolower($command);
265
266                 if (is_array($this->commands[$command])) {
267                         return $this->commands[$command]["class"];
268                 } else {
269                         return false;
270                 }
271
272                 return false;
273         }
274
275         function get_commands() {
276                 return $this->commands;
277         }
278
279         function run_commands($args) {
280                 foreach ($this->get_commands() as $command => $data) {
281                         if (isset($args[$command])) {
282                                 $command = str_replace("-", "", $command);
283                                 $data["class"]->$command($args);
284                         }
285                 }
286         }
287
288         function load_data($force = false) {
289                 if ($this->owner_uid)  {
290                         $result = $this->dbh->query("SELECT name, content FROM ttrss_plugin_storage
291                                 WHERE owner_uid = '".$this->owner_uid."'");
292
293                         while ($line = $this->dbh->fetch_assoc($result)) {
294                                 $this->storage[$line["name"]] = unserialize($line["content"]);
295                         }
296                 }
297         }
298
299         private function save_data($plugin) {
300                 if ($this->owner_uid) {
301                         $plugin = $this->dbh->escape_string($plugin);
302
303                         $this->dbh->query("BEGIN");
304
305                         $result = $this->dbh->query("SELECT id FROM ttrss_plugin_storage WHERE
306                                 owner_uid= '".$this->owner_uid."' AND name = '$plugin'");
307
308                         if (!isset($this->storage[$plugin]))
309                                 $this->storage[$plugin] = array();
310
311                         $content = $this->dbh->escape_string(serialize($this->storage[$plugin]),
312                                 false);
313
314                         if ($this->dbh->num_rows($result) != 0) {
315                                 $this->dbh->query("UPDATE ttrss_plugin_storage SET content = '$content'
316                                         WHERE owner_uid= '".$this->owner_uid."' AND name = '$plugin'");
317
318                         } else {
319                                 $this->dbh->query("INSERT INTO ttrss_plugin_storage
320                                         (name,owner_uid,content) VALUES
321                                         ('$plugin','".$this->owner_uid."','$content')");
322                         }
323
324                         $this->dbh->query("COMMIT");
325                 }
326         }
327
328         function set($sender, $name, $value, $sync = true) {
329                 $idx = get_class($sender);
330
331                 if (!isset($this->storage[$idx]))
332                         $this->storage[$idx] = array();
333
334                 $this->storage[$idx][$name] = $value;
335
336                 if ($sync) $this->save_data(get_class($sender));
337         }
338
339         function get($sender, $name, $default_value = false) {
340                 $idx = get_class($sender);
341
342                 if (isset($this->storage[$idx][$name])) {
343                         return $this->storage[$idx][$name];
344                 } else {
345                         return $default_value;
346                 }
347         }
348
349         function get_all($sender) {
350                 $idx = get_class($sender);
351
352                 return $this->storage[$idx];
353         }
354
355         function clear_data($sender) {
356                 if ($this->owner_uid) {
357                         $idx = get_class($sender);
358
359                         unset($this->storage[$idx]);
360
361                         $this->dbh->query("DELETE FROM ttrss_plugin_storage WHERE name = '$idx'
362                                 AND owner_uid = " . $this->owner_uid);
363                 }
364         }
365
366         function set_debug($debug) {
367                 $this->debug = $debug;
368         }
369
370         function get_debug() {
371                 return $this->debug;
372         }
373
374         // Plugin feed functions are *EXPERIMENTAL*!
375
376         // cat_id: only -1 is supported (Special)
377         function add_feed($cat_id, $title, $icon, $sender) {
378                 if (!$this->feeds[$cat_id]) $this->feeds[$cat_id] = array();
379
380                 $id = count($this->feeds[$cat_id]);
381
382                 array_push($this->feeds[$cat_id],
383                         array('id' => $id, 'title' => $title, 'sender' => $sender, 'icon' => $icon));
384
385                 return $id;
386         }
387
388         function get_feeds($cat_id) {
389                 return $this->feeds[$cat_id];
390         }
391
392         // convert feed_id (e.g. -129) to pfeed_id first
393         function get_feed_handler($pfeed_id) {
394                 foreach ($this->feeds as $cat) {
395                         foreach ($cat as $feed) {
396                                 if ($feed['id'] == $pfeed_id) {
397                                         return $feed['sender'];
398                                 }
399                         }
400                 }
401         }
402
403         static function pfeed_to_feed_id($label) {
404                 return PLUGIN_FEED_BASE_INDEX - 1 - abs($label);
405         }
406
407         static function feed_to_pfeed_id($feed) {
408                 return PLUGIN_FEED_BASE_INDEX - 1 + abs($feed);
409         }
410
411         function add_api_method($name, $sender) {
412                 if ($this->is_system($sender)) {
413                         $this->api_methods[strtolower($name)] = $sender;
414                 }
415         }
416
417         function get_api_method($name) {
418                 return $this->api_methods[$name];
419         }
420
421         function add_filter_action($sender, $action_name, $action_desc) {
422                 $sender_class = get_class($sender);
423
424                 if (!isset($this->plugin_actions[$sender_class]))
425                         $this->plugin_actions[$sender_class] = array();
426
427                 array_push($this->plugin_actions[$sender_class],
428                         array("action" => $action_name, "description" => $action_desc, "sender" => $sender));
429         }
430
431         function get_filter_actions() {
432                 return $this->plugin_actions;
433         }
434 }
435 ?>