]> git.immae.eu Git - github/shaarli/Shaarli.git/blob - doc/md/dev/Plugin-system.md
f09fadc2925db027873cd2d788ac6a239a6ffa68
[github/shaarli/Shaarli.git] / doc / md / dev / Plugin-system.md
1 # Plugin system
2
3 ## Developer API
4
5 ### What can I do with plugins?
6
7 The plugin system lets you:
8
9 - insert content into specific places across templates.
10 - alter data before templates rendering.
11 - alter data before saving new links.
12
13
14 ### How can I create a plugin for Shaarli?
15
16 First, chose a plugin name, such as `demo_plugin`.
17
18 Under `plugin` folder, create a folder named with your plugin name. Then create a <plugin_name>.meta file and a <plugin_name>.php file in that folder.
19
20 You should have the following tree view:
21
22 ```
23 | index.php
24 | plugins/
25 |---| demo_plugin/
26 | |---| demo_plugin.meta
27 | |---| demo_plugin.php
28 ```
29
30
31 ### Plugin initialization
32
33 At the beginning of Shaarli execution, all enabled plugins are loaded. At this point, the plugin system looks for an `init()` function in the <plugin_name>.php to execute and run it if it exists. This function must be named this way, and takes the `ConfigManager` as parameter.
34
35 <plugin_name>_init($conf)
36
37 This function can be used to create initial data, load default settings, etc. But also to set *plugin errors*. If the initialization function returns an array of strings, they will be understand as errors, and displayed in the header to logged in users.
38
39 The plugin system also looks for a `description` variable in the <plugin_name>.meta file, to be displayed in the plugin administration page.
40
41 description="The plugin does this and that."
42
43 ### Understanding hooks
44
45 A plugin is a set of functions. Each function will be triggered by the plugin system at certain point in Shaarli execution.
46
47 These functions need to be named with this pattern:
48
49 ```
50 hook_<plugin_name>_<hook_name>($data, $conf)
51 ```
52
53 Parameters:
54
55 - data: see [$data section](https://shaarli.readthedocs.io/en/master/Plugin-System/#plugins-data)
56 - conf: the `ConfigManager` instance.
57
58 For example, if my plugin want to add data to the header, this function is needed:
59
60 hook_demo_plugin_render_header
61
62 If this function is declared, and the plugin enabled, it will be called every time Shaarli is rendering the header.
63
64
65 ### Plugin's data
66
67 #### Parameters
68
69 Every hook function has a `$data` parameter. Its content differs for each hooks.
70
71 **This parameter needs to be returned every time**, otherwise data is lost.
72
73 return $data;
74
75 #### Special data
76
77 Special additional data are passed to every hook through the
78 `$data` parameter to give you access to additional context, and services.
79
80 Complete list:
81
82 * `_PAGE_` (string): if the current hook is used to render a template, its name is passed through this additional parameter.
83 * `_LOGGEDIN_` (bool): whether the user is logged in or not.
84 * `_BASE_PATH_` (string): if Shaarli instance is hosted under a subfolder, contains the subfolder path to `index.php` (e.g. `https://domain.tld/shaarli/` -> `/shaarli/`).
85 * `_BOOKMARK_SERVICE_` (`BookmarkServiceInterface`): bookmark service instance, for advanced usage.
86
87 Example:
88
89 ```php
90 if ($data['_PAGE_'] === TemplatePage::LINKLIST && $data['LOGGEDIN'] === true) {
91 // Do something for logged in users when the link list is rendered
92 }
93 ```
94
95 #### Filling templates placeholder
96
97 Template placeholders are displayed in template in specific places.
98
99 RainTPL displays every element contained in the placeholder's array. These element can be added by plugins.
100
101 For example, let's add a value in the placeholder `top_placeholder` which is displayed at the top of my page:
102
103 ```php
104 $data['top_placeholder'][] = 'My content';
105 # OR
106 array_push($data['top_placeholder'], 'My', 'content');
107
108 return $data;
109 ```
110
111
112 #### Data manipulation
113
114 When a page is displayed, every variable send to the template engine is passed to plugins before that in `$data`.
115
116 The data contained by this array can be altered before template rendering.
117
118 For example, in linklist, it is possible to alter every title:
119
120 ```php
121 // mind the reference if you want $data to be altered
122 foreach ($data['links'] as &$value) {
123 // String reverse every title.
124 $value['title'] = strrev($value['title']);
125 }
126
127 return $data;
128 ```
129
130 ### Metadata
131
132 Every plugin needs a `<plugin_name>.meta` file, which is in fact an `.ini` file (`KEY="VALUE"`), to be listed in plugin administration.
133
134 Each file contain two keys:
135
136 - `description`: plugin description
137 - `parameters`: user parameter names, separated by a `;`.
138 - `parameter.<PARAMETER_NAME>`: add a text description the specified parameter.
139
140 > Note: In PHP, `parse_ini_file()` seems to want strings to be between by quotes `"` in the ini file.
141
142 ### Understanding relative paths
143
144 Because Shaarli is a self-hosted tool, an instance can either be installed at the root directory, or under a subfolder.
145 This means that you can *never* use absolute paths (eg `/plugins/mything/file.png`).
146
147 If a file needs to be included in server end, use simple relative path:
148 `PluginManager::$PLUGINS_PATH . '/mything/template.html'`.
149
150 If it needs to be included in front end side (e.g. an image),
151 the relative path must be prefixed with special data:
152
153 * if it's a link that will need to be processed by Shaarli, use `_BASE_PATH_`:
154 for e.g. `$data['_BASE_PATH_'] . '/admin/tools`.
155 * if you want to include an asset, you need to add the root URL (base path without `/index.php`, for people using Shaarli without URL rewriting), then use `_ROOT_PATH_`:
156 for e.g
157 `$['_ROOT_PATH_'] . '/' . PluginManager::$PLUGINS_PATH . '/mything/picture.png`.
158
159 Note that special placeholders for CSS and JS files (respectively `css_files` and `js_files`) are already prefixed
160 with the root path in template files.
161
162 ### It's not working!
163
164 Use `demo_plugin` as a functional example. It covers most of the plugin system features.
165
166 If it's still not working, please [open an issue](https://github.com/shaarli/Shaarli/issues/new).
167
168
169 ### Hooks
170
171 | Hooks | Description |
172 | ------------- |:-------------:|
173 | [render_header](#render_header) | Allow plugin to add content in page headers. |
174 | [render_includes](#render_includes) | Allow plugin to include their own CSS files. |
175 | [render_footer](#render_footer) | Allow plugin to add content in page footer and include their own JS files. |
176 | [render_linklist](#render_linklist) | It allows to add content at the begining and end of the page, after every link displayed and to alter link data. |
177 | [render_editlink](#render_editlink) | Allow to add fields in the form, or display elements. |
178 | [render_tools](#render_tools) | Allow to add content at the end of the page. |
179 | [render_picwall](#render_picwall) | Allow to add content at the top and bottom of the page. |
180 | [render_tagcloud](#render_tagcloud) | Allow to add content at the top and bottom of the page, and after all tags. |
181 | [render_taglist](#render_taglist) | Allow to add content at the top and bottom of the page, and after all tags. |
182 | [render_daily](#render_daily) | Allow to add content at the top and bottom of the page, the bottom of each link and to alter data. |
183 | [render_feed](#render_feed) | Allow to do add tags in RSS and ATOM feeds. |
184 | [save_link](#save_link) | Allow to alter the link being saved in the datastore. |
185 | [delete_link](#delete_link) | Allow to do an action before a link is deleted from the datastore. |
186 | [save_plugin_parameters](#save_plugin_parameters) | Allow to manipulate plugin parameters before they're saved. |
187
188
189 #### render_header
190
191 Triggered on every page - allows plugins to add content in page headers.
192
193
194 ##### Data
195
196 `$data` is an array containing:
197
198 - [Special data](#special-data)
199
200 ##### Template placeholders
201
202 Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
203
204 List of placeholders:
205
206 - `buttons_toolbar`: after the list of buttons in the header.
207
208 ![buttons_toolbar_example](http://i.imgur.com/ssJUOrt.png)
209
210 - `fields_toolbar`: after search fields in the header.
211
212 > Note: This will only be called in linklist.
213
214 ![fields_toolbar_example](http://i.imgur.com/3GMifI2.png)
215
216
217 #### render_includes
218
219 Triggered on every page - allows plugins to include their own CSS files.
220
221 ##### data
222
223 `$data` is an array containing:
224
225 - [Special data](#special-data)
226
227 ##### Template placeholders
228
229 Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
230
231 List of placeholders:
232
233 - `css_files`: called after loading default CSS.
234
235 > Note: only add the path of the CSS file. E.g: `plugins/demo_plugin/custom_demo.css`.
236
237
238 #### render_footer
239
240 Triggered on every page.
241
242 Allow plugin to add content in page footer and include their own JS files.
243
244 ##### data
245
246 `$data` is an array containing:
247
248 - [Special data](#special-data)
249
250 ##### Template placeholders
251
252 Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
253
254 List of placeholders:
255
256 - `text`: called after the end of the footer text.
257 - `endofpage`: called at the end of the page.
258
259 ![text_example](http://i.imgur.com/L5S2YEH.png)
260
261 - `js_files`: called at the end of the page, to include custom JS scripts.
262
263 > Note: only add the path of the JS file. E.g: `plugins/demo_plugin/custom_demo.js`.
264
265
266 #### render_linklist
267
268 Triggered when `linklist` is displayed (list of links, permalink, search, tag filtered, etc.).
269
270 It allows to add content at the begining and end of the page, after every link displayed and to alter link data.
271
272 ##### data
273
274 `$data` is an array containing:
275
276 - All templates data, including links.
277 - [Special data](#special-data)
278
279 ##### template placeholders
280
281 Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
282
283 List of placeholders:
284
285 - `action_plugin`: next to the button "private only" at the top and bottom of the page.
286
287 ![action_plugin_example](http://i.imgur.com/Q12PWg0.png)
288
289 - `link_plugin`: for every link, between permalink and link URL.
290
291 ![link_plugin_example](http://i.imgur.com/3oDPhWx.png)
292
293 - `plugin_start_zone`: before displaying the template content.
294
295 ![plugin_start_zone_example](http://i.imgur.com/OVBkGy3.png)
296
297 - `plugin_end_zone`: after displaying the template content.
298
299 ![plugin_end_zone_example](http://i.imgur.com/6IoRuop.png)
300
301
302 #### render_editlink
303
304 Triggered when the link edition form is displayed.
305
306 Allow to add fields in the form, or display elements.
307
308 ##### data
309
310 `$data` is an array containing:
311
312 - All templates data.
313 - [Special data](#special-data)
314
315 ##### template placeholders
316
317 Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
318
319 List of placeholders:
320
321 - `edit_link_plugin`: after tags field.
322
323 ![edit_link_plugin_example](http://i.imgur.com/5u17Ens.png)
324
325
326 #### render_tools
327
328 Triggered when the "tools" page is displayed.
329
330 Allow to add content at the end of the page.
331
332 ##### data
333
334 `$data` is an array containing:
335
336 - All templates data.
337 - [Special data](#special-data)
338
339 ##### template placeholders
340
341 Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
342
343 List of placeholders:
344
345 - `tools_plugin`: at the end of the page.
346
347 ![tools_plugin_example](http://i.imgur.com/Bqhu9oQ.png)
348
349
350 #### render_picwall
351
352 Triggered when picwall is displayed.
353
354 Allow to add content at the top and bottom of the page.
355
356 ##### data
357
358 `$data` is an array containing:
359
360 - All templates data.
361 - [Special data](#special-data)
362
363 ##### template placeholders
364
365 Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
366
367 List of placeholders:
368
369 - `plugin_start_zone`: before displaying the template content.
370 - `plugin_end_zone`: after displaying the template content.
371
372 ![plugin_start_end_zone_example](http://i.imgur.com/tVTQFER.png)
373
374
375 #### render_tagcloud
376
377 Triggered when tagcloud is displayed.
378
379 Allow to add content at the top and bottom of the page.
380
381 ##### data
382
383 `$data` is an array containing:
384
385 - All templates data.
386 - [Special data](#special-data)
387
388 ##### Template placeholders
389
390 Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
391
392 List of placeholders:
393
394 - `plugin_start_zone`: before displaying the template content.
395 - `plugin_end_zone`: after displaying the template content.
396
397 For each tag, the following placeholder can be used:
398
399 - `tag_plugin`: after each tag
400
401 ![plugin_start_end_zone_example](http://i.imgur.com/vHmyT3a.png)
402
403
404 #### render_taglist
405
406 Triggered when taglist is displayed - allows to add content at the top and bottom of the page.
407
408 ##### data
409
410 `$data` is an array containing:
411
412 - All templates data.
413 - [Special data](#special-data)
414
415 ##### Template placeholders
416
417 Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
418
419 List of placeholders:
420
421 - `plugin_start_zone`: before displaying the template content.
422 - `plugin_end_zone`: after displaying the template content.
423
424 For each tag, the following placeholder can be used:
425
426 - `tag_plugin`: after each tag
427
428 #### render_daily
429
430 Triggered when tagcloud is displayed.
431
432 Allow to add content at the top and bottom of the page, the bottom of each link and to alter data.
433
434
435 ##### data
436
437 `$data` is an array containing:
438
439 - All templates data, including links.
440 - [Special data](#special-data)
441
442 ##### Template placeholders
443
444 Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
445
446 List of placeholders:
447
448 - `link_plugin`: used at bottom of each link.
449
450 ![link_plugin_example](http://i.imgur.com/hzhMfSZ.png)
451
452 - `plugin_start_zone`: before displaying the template content.
453 - `plugin_end_zone`: after displaying the template content.
454
455
456 #### render_feed
457
458 Triggered when the ATOM or RSS feed is displayed.
459
460 Allow to add tags in the feed, either in the header or for each items. Items (links) can also be altered before being rendered.
461
462 ##### data
463
464 `$data` is an array containing:
465
466 - All templates data, including links.
467 - [Special data](#special-data)
468
469 ##### Template placeholders
470
471 Tags can be added in feeds by adding an entry in `$data['<placeholder>']` array.
472
473 List of placeholders:
474
475 - `feed_plugins_header`: used as a header tag in the feed.
476
477 For each links:
478
479 - `feed_plugins`: additional tag for every link entry.
480
481
482 #### save_link
483
484 Triggered when a link is save (new link or edit).
485
486 Allow to alter the link being saved in the datastore.
487
488 ##### data
489
490 `$data` is an array containing the link being saved:
491
492 - id
493 - title
494 - url
495 - shorturl
496 - description
497 - private
498 - tags
499 - created
500 - updated
501
502 Also [special data](#special-data).
503
504
505 #### delete_link
506
507 Triggered when a link is deleted.
508
509 Allow to execute any action before the link is actually removed from the datastore
510
511 ##### data
512
513 `$data` is an array containing the link being deleted:
514
515 - id
516 - title
517 - url
518 - shorturl
519 - description
520 - private
521 - tags
522 - created
523 - updated
524
525 Also [special data](#special-data).
526
527 #### save_plugin_parameters
528
529 Triggered when the plugin parameters are saved from the plugin administration page.
530
531 Plugins can perform an action every times their settings are updated.
532 For example it is used to update the CSS file of the `default_colors` plugins.
533
534 ##### data
535
536 `$data` input contains the `$_POST` array.
537
538 So if the plugin has a parameter called `MYPLUGIN_PARAMETER`,
539 the array will contain an entry with `MYPLUGIN_PARAMETER` as a key.
540
541 Also [special data](#special-data).
542
543 ## Guide for template designers
544
545 ### Plugin administration
546
547 Your theme must include a plugin administration page: `pluginsadmin.html`.
548
549 > Note: repo's template link needs to be added when the PR is merged.
550
551 Use the default one as an example.
552
553 Aside from classic RainTPL loops, plugins order is handle by JavaScript. You can just include `plugin_admin.js`, only if:
554
555 - you're using a table.
556 - you call orderUp() and orderUp() onclick on arrows.
557 - you add data-line and data-order to your rows.
558
559 Otherwise, you can use your own JS as long as this field is send by the form:
560
561 <input type="hidden" name="order_{$key}" value="{$counter}">
562
563 ### Placeholder system
564
565 In order to make plugins work with every custom themes, you need to add variable placeholder in your templates.
566
567 It's a RainTPL loop like this:
568
569 {loop="$plugin_variable"}
570 {$value}
571 {/loop}
572
573 You should enable `demo_plugin` for testing purpose, since it uses every placeholder available.
574
575 ### List of placeholders
576
577 **page.header.html**
578
579 At the end of the menu:
580
581 {loop="$plugins_header.buttons_toolbar"}
582 {$value}
583 {/loop}
584
585 At the end of file, before clearing floating blocks:
586
587 {if="!empty($plugin_errors) && $is_logged_in"}
588 <ul class="errors">
589 {loop="plugin_errors"}
590 <li>{$value}</li>
591 {/loop}
592 </ul>
593 {/if}
594
595 **includes.html**
596
597 At the end of the file:
598
599 ```html
600 {loop="$plugins_includes.css_files"}
601 <link type="text/css" rel="stylesheet" href="{$value}#"/>
602 {/loop}
603 ```
604
605 **page.footer.html**
606
607 At the end of your footer notes:
608
609 ```html
610 {loop="$plugins_footer.text"}
611 {$value}
612 {/loop}
613 ```
614
615 At the end of file:
616
617 ```html
618 {loop="$plugins_footer.js_files"}
619 <script src="{$value}#"></script>
620 {/loop}
621 ```
622
623 **linklist.html**
624
625 After search fields:
626
627 ```html
628 {loop="$plugins_header.fields_toolbar"}
629 {$value}
630 {/loop}
631 ```
632
633 Before displaying the link list (after paging):
634
635 ```html
636 {loop="$plugin_start_zone"}
637 {$value}
638 {/loop}
639 ```
640
641 For every links (icons):
642
643 ```html
644 {loop="$value.link_plugin"}
645 <span>{$value}</span>
646 {/loop}
647 ```
648
649 Before end paging:
650
651 ```html
652 {loop="$plugin_end_zone"}
653 {$value}
654 {/loop}
655 ```
656
657 **linklist.paging.html**
658
659 After the "private only" icon:
660
661 ```html
662 {loop="$action_plugin"}
663 {$value}
664 {/loop}
665 ```
666
667 **editlink.html**
668
669 After tags field:
670
671 ```html
672 {loop="$edit_link_plugin"}
673 {$value}
674 {/loop}
675 ```
676
677 **tools.html**
678
679 After the last tool:
680
681 ```html
682 {loop="$tools_plugin"}
683 {$value}
684 {/loop}
685 ```
686
687 **picwall.html**
688
689 Top:
690
691 ```html
692 <div id="plugin_zone_start_picwall" class="plugin_zone">
693 {loop="$plugin_start_zone"}
694 {$value}
695 {/loop}
696 </div>
697 ```
698
699 Bottom:
700
701 ```html
702 <div id="plugin_zone_end_picwall" class="plugin_zone">
703 {loop="$plugin_end_zone"}
704 {$value}
705 {/loop}
706 </div>
707 ```
708
709 **tagcloud.html**
710
711 Top:
712
713 ```html
714 <div id="plugin_zone_start_tagcloud" class="plugin_zone">
715 {loop="$plugin_start_zone"}
716 {$value}
717 {/loop}
718 </div>
719 ```
720
721 Bottom:
722
723 ```html
724 <div id="plugin_zone_end_tagcloud" class="plugin_zone">
725 {loop="$plugin_end_zone"}
726 {$value}
727 {/loop}
728 </div>
729 ```
730
731 **daily.html**
732
733 Top:
734
735 ```html
736 <div id="plugin_zone_start_picwall" class="plugin_zone">
737 {loop="$plugin_start_zone"}
738 {$value}
739 {/loop}
740 </div>
741 ```
742
743 After every link:
744
745 ```html
746 <div class="dailyEntryFooter">
747 {loop="$link.link_plugin"}
748 {$value}
749 {/loop}
750 </div>
751 ```
752
753 Bottom:
754
755 ```html
756 <div id="plugin_zone_end_picwall" class="plugin_zone">
757 {loop="$plugin_end_zone"}
758 {$value}
759 {/loop}
760 </div>
761 ```
762
763 **feed.atom.xml** and **feed.rss.xml**:
764
765 In headers tags section:
766 ```xml
767 {loop="$feed_plugins_header"}
768 {$value}
769 {/loop}
770 ```
771
772 After each entry:
773 ```xml
774 {loop="$value.feed_plugins"}
775 {$value}
776 {/loop}
777 ```