]> git.immae.eu Git - github/shaarli/Shaarli.git/blob - doc/md/dev/Plugin-system.md
c29774de05b46e47be8ed6c5b05b5aa539e68028
[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 `_BASE_PATH_`:
152 `($data['_BASE_PATH_'] ?? '') . '/' . PluginManager::$PLUGINS_PATH . '/mything/picture.png`.
153
154 Note that special placeholders for CSS and JS files (respectively `css_files` and `js_files`) are already prefixed
155 with the base path in template files.
156
157 ### It's not working!
158
159 Use `demo_plugin` as a functional example. It covers most of the plugin system features.
160
161 If it's still not working, please [open an issue](https://github.com/shaarli/Shaarli/issues/new).
162
163
164 ### Hooks
165
166 | Hooks | Description |
167 | ------------- |:-------------:|
168 | [render_header](#render_header) | Allow plugin to add content in page headers. |
169 | [render_includes](#render_includes) | Allow plugin to include their own CSS files. |
170 | [render_footer](#render_footer) | Allow plugin to add content in page footer and include their own JS files. |
171 | [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. |
172 | [render_editlink](#render_editlink) | Allow to add fields in the form, or display elements. |
173 | [render_tools](#render_tools) | Allow to add content at the end of the page. |
174 | [render_picwall](#render_picwall) | Allow to add content at the top and bottom of the page. |
175 | [render_tagcloud](#render_tagcloud) | Allow to add content at the top and bottom of the page, and after all tags. |
176 | [render_taglist](#render_taglist) | Allow to add content at the top and bottom of the page, and after all tags. |
177 | [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. |
178 | [render_feed](#render_feed) | Allow to do add tags in RSS and ATOM feeds. |
179 | [save_link](#save_link) | Allow to alter the link being saved in the datastore. |
180 | [delete_link](#delete_link) | Allow to do an action before a link is deleted from the datastore. |
181 | [save_plugin_parameters](#save_plugin_parameters) | Allow to manipulate plugin parameters before they're saved. |
182
183
184 #### render_header
185
186 Triggered on every page - allows plugins to add content in page headers.
187
188
189 ##### Data
190
191 `$data` is an array containing:
192
193 - [Special data](#special-data)
194
195 ##### Template placeholders
196
197 Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
198
199 List of placeholders:
200
201 - `buttons_toolbar`: after the list of buttons in the header.
202
203 ![buttons_toolbar_example](http://i.imgur.com/ssJUOrt.png)
204
205 - `fields_toolbar`: after search fields in the header.
206
207 > Note: This will only be called in linklist.
208
209 ![fields_toolbar_example](http://i.imgur.com/3GMifI2.png)
210
211
212 #### render_includes
213
214 Triggered on every page - allows plugins to include their own CSS files.
215
216 ##### data
217
218 `$data` is an array containing:
219
220 - [Special data](#special-data)
221
222 ##### Template placeholders
223
224 Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
225
226 List of placeholders:
227
228 - `css_files`: called after loading default CSS.
229
230 > Note: only add the path of the CSS file. E.g: `plugins/demo_plugin/custom_demo.css`.
231
232
233 #### render_footer
234
235 Triggered on every page.
236
237 Allow plugin to add content in page footer and include their own JS files.
238
239 ##### data
240
241 `$data` is an array containing:
242
243 - [Special data](#special-data)
244
245 ##### Template placeholders
246
247 Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
248
249 List of placeholders:
250
251 - `text`: called after the end of the footer text.
252 - `endofpage`: called at the end of the page.
253
254 ![text_example](http://i.imgur.com/L5S2YEH.png)
255
256 - `js_files`: called at the end of the page, to include custom JS scripts.
257
258 > Note: only add the path of the JS file. E.g: `plugins/demo_plugin/custom_demo.js`.
259
260
261 #### render_linklist
262
263 Triggered when `linklist` is displayed (list of links, permalink, search, tag filtered, etc.).
264
265 It allows to add content at the begining and end of the page, after every link displayed and to alter link data.
266
267 ##### data
268
269 `$data` is an array containing:
270
271 - All templates data, including links.
272 - [Special data](#special-data)
273
274 ##### template placeholders
275
276 Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
277
278 List of placeholders:
279
280 - `action_plugin`: next to the button "private only" at the top and bottom of the page.
281
282 ![action_plugin_example](http://i.imgur.com/Q12PWg0.png)
283
284 - `link_plugin`: for every link, between permalink and link URL.
285
286 ![link_plugin_example](http://i.imgur.com/3oDPhWx.png)
287
288 - `plugin_start_zone`: before displaying the template content.
289
290 ![plugin_start_zone_example](http://i.imgur.com/OVBkGy3.png)
291
292 - `plugin_end_zone`: after displaying the template content.
293
294 ![plugin_end_zone_example](http://i.imgur.com/6IoRuop.png)
295
296
297 #### render_editlink
298
299 Triggered when the link edition form is displayed.
300
301 Allow to add fields in the form, or display elements.
302
303 ##### data
304
305 `$data` is an array containing:
306
307 - All templates data.
308 - [Special data](#special-data)
309
310 ##### template placeholders
311
312 Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
313
314 List of placeholders:
315
316 - `edit_link_plugin`: after tags field.
317
318 ![edit_link_plugin_example](http://i.imgur.com/5u17Ens.png)
319
320
321 #### render_tools
322
323 Triggered when the "tools" page is displayed.
324
325 Allow to add content at the end of the page.
326
327 ##### data
328
329 `$data` is an array containing:
330
331 - All templates data.
332 - [Special data](#special-data)
333
334 ##### template placeholders
335
336 Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
337
338 List of placeholders:
339
340 - `tools_plugin`: at the end of the page.
341
342 ![tools_plugin_example](http://i.imgur.com/Bqhu9oQ.png)
343
344
345 #### render_picwall
346
347 Triggered when picwall is displayed.
348
349 Allow to add content at the top and bottom of the page.
350
351 ##### data
352
353 `$data` is an array containing:
354
355 - All templates data.
356 - [Special data](#special-data)
357
358 ##### template placeholders
359
360 Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
361
362 List of placeholders:
363
364 - `plugin_start_zone`: before displaying the template content.
365 - `plugin_end_zone`: after displaying the template content.
366
367 ![plugin_start_end_zone_example](http://i.imgur.com/tVTQFER.png)
368
369
370 #### render_tagcloud
371
372 Triggered when tagcloud is displayed.
373
374 Allow to add content at the top and bottom of the page.
375
376 ##### data
377
378 `$data` is an array containing:
379
380 - All templates data.
381 - [Special data](#special-data)
382
383 ##### Template placeholders
384
385 Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
386
387 List of placeholders:
388
389 - `plugin_start_zone`: before displaying the template content.
390 - `plugin_end_zone`: after displaying the template content.
391
392 For each tag, the following placeholder can be used:
393
394 - `tag_plugin`: after each tag
395
396 ![plugin_start_end_zone_example](http://i.imgur.com/vHmyT3a.png)
397
398
399 #### render_taglist
400
401 Triggered when taglist is displayed - allows to add content at the top and bottom of the page.
402
403 ##### data
404
405 `$data` is an array containing:
406
407 - All templates data.
408 - [Special data](#special-data)
409
410 ##### Template placeholders
411
412 Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
413
414 List of placeholders:
415
416 - `plugin_start_zone`: before displaying the template content.
417 - `plugin_end_zone`: after displaying the template content.
418
419 For each tag, the following placeholder can be used:
420
421 - `tag_plugin`: after each tag
422
423 #### render_daily
424
425 Triggered when tagcloud is displayed.
426
427 Allow to add content at the top and bottom of the page, the bottom of each link and to alter data.
428
429
430 ##### data
431
432 `$data` is an array containing:
433
434 - All templates data, including links.
435 - [Special data](#special-data)
436
437 ##### Template placeholders
438
439 Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
440
441 List of placeholders:
442
443 - `link_plugin`: used at bottom of each link.
444
445 ![link_plugin_example](http://i.imgur.com/hzhMfSZ.png)
446
447 - `plugin_start_zone`: before displaying the template content.
448 - `plugin_end_zone`: after displaying the template content.
449
450
451 #### render_feed
452
453 Triggered when the ATOM or RSS feed is displayed.
454
455 Allow to add tags in the feed, either in the header or for each items. Items (links) can also be altered before being rendered.
456
457 ##### data
458
459 `$data` is an array containing:
460
461 - All templates data, including links.
462 - [Special data](#special-data)
463
464 ##### Template placeholders
465
466 Tags can be added in feeds by adding an entry in `$data['<placeholder>']` array.
467
468 List of placeholders:
469
470 - `feed_plugins_header`: used as a header tag in the feed.
471
472 For each links:
473
474 - `feed_plugins`: additional tag for every link entry.
475
476
477 #### save_link
478
479 Triggered when a link is save (new link or edit).
480
481 Allow to alter the link being saved in the datastore.
482
483 ##### data
484
485 `$data` is an array containing the link being saved:
486
487 - id
488 - title
489 - url
490 - shorturl
491 - description
492 - private
493 - tags
494 - created
495 - updated
496
497 Also [special data](#special-data).
498
499
500 #### delete_link
501
502 Triggered when a link is deleted.
503
504 Allow to execute any action before the link is actually removed from the datastore
505
506 ##### data
507
508 `$data` is an array containing the link being deleted:
509
510 - id
511 - title
512 - url
513 - shorturl
514 - description
515 - private
516 - tags
517 - created
518 - updated
519
520 Also [special data](#special-data).
521
522 #### save_plugin_parameters
523
524 Triggered when the plugin parameters are saved from the plugin administration page.
525
526 Plugins can perform an action every times their settings are updated.
527 For example it is used to update the CSS file of the `default_colors` plugins.
528
529 ##### data
530
531 `$data` input contains the `$_POST` array.
532
533 So if the plugin has a parameter called `MYPLUGIN_PARAMETER`,
534 the array will contain an entry with `MYPLUGIN_PARAMETER` as a key.
535
536 Also [special data](#special-data).
537
538 ## Guide for template designers
539
540 ### Plugin administration
541
542 Your theme must include a plugin administration page: `pluginsadmin.html`.
543
544 > Note: repo's template link needs to be added when the PR is merged.
545
546 Use the default one as an example.
547
548 Aside from classic RainTPL loops, plugins order is handle by JavaScript. You can just include `plugin_admin.js`, only if:
549
550 - you're using a table.
551 - you call orderUp() and orderUp() onclick on arrows.
552 - you add data-line and data-order to your rows.
553
554 Otherwise, you can use your own JS as long as this field is send by the form:
555
556 <input type="hidden" name="order_{$key}" value="{$counter}">
557
558 ### Placeholder system
559
560 In order to make plugins work with every custom themes, you need to add variable placeholder in your templates.
561
562 It's a RainTPL loop like this:
563
564 {loop="$plugin_variable"}
565 {$value}
566 {/loop}
567
568 You should enable `demo_plugin` for testing purpose, since it uses every placeholder available.
569
570 ### List of placeholders
571
572 **page.header.html**
573
574 At the end of the menu:
575
576 {loop="$plugins_header.buttons_toolbar"}
577 {$value}
578 {/loop}
579
580 At the end of file, before clearing floating blocks:
581
582 {if="!empty($plugin_errors) && $is_logged_in"}
583 <ul class="errors">
584 {loop="plugin_errors"}
585 <li>{$value}</li>
586 {/loop}
587 </ul>
588 {/if}
589
590 **includes.html**
591
592 At the end of the file:
593
594 ```html
595 {loop="$plugins_includes.css_files"}
596 <link type="text/css" rel="stylesheet" href="{$value}#"/>
597 {/loop}
598 ```
599
600 **page.footer.html**
601
602 At the end of your footer notes:
603
604 ```html
605 {loop="$plugins_footer.text"}
606 {$value}
607 {/loop}
608 ```
609
610 At the end of file:
611
612 ```html
613 {loop="$plugins_footer.js_files"}
614 <script src="{$value}#"></script>
615 {/loop}
616 ```
617
618 **linklist.html**
619
620 After search fields:
621
622 ```html
623 {loop="$plugins_header.fields_toolbar"}
624 {$value}
625 {/loop}
626 ```
627
628 Before displaying the link list (after paging):
629
630 ```html
631 {loop="$plugin_start_zone"}
632 {$value}
633 {/loop}
634 ```
635
636 For every links (icons):
637
638 ```html
639 {loop="$value.link_plugin"}
640 <span>{$value}</span>
641 {/loop}
642 ```
643
644 Before end paging:
645
646 ```html
647 {loop="$plugin_end_zone"}
648 {$value}
649 {/loop}
650 ```
651
652 **linklist.paging.html**
653
654 After the "private only" icon:
655
656 ```html
657 {loop="$action_plugin"}
658 {$value}
659 {/loop}
660 ```
661
662 **editlink.html**
663
664 After tags field:
665
666 ```html
667 {loop="$edit_link_plugin"}
668 {$value}
669 {/loop}
670 ```
671
672 **tools.html**
673
674 After the last tool:
675
676 ```html
677 {loop="$tools_plugin"}
678 {$value}
679 {/loop}
680 ```
681
682 **picwall.html**
683
684 Top:
685
686 ```html
687 <div id="plugin_zone_start_picwall" class="plugin_zone">
688 {loop="$plugin_start_zone"}
689 {$value}
690 {/loop}
691 </div>
692 ```
693
694 Bottom:
695
696 ```html
697 <div id="plugin_zone_end_picwall" class="plugin_zone">
698 {loop="$plugin_end_zone"}
699 {$value}
700 {/loop}
701 </div>
702 ```
703
704 **tagcloud.html**
705
706 Top:
707
708 ```html
709 <div id="plugin_zone_start_tagcloud" class="plugin_zone">
710 {loop="$plugin_start_zone"}
711 {$value}
712 {/loop}
713 </div>
714 ```
715
716 Bottom:
717
718 ```html
719 <div id="plugin_zone_end_tagcloud" class="plugin_zone">
720 {loop="$plugin_end_zone"}
721 {$value}
722 {/loop}
723 </div>
724 ```
725
726 **daily.html**
727
728 Top:
729
730 ```html
731 <div id="plugin_zone_start_picwall" class="plugin_zone">
732 {loop="$plugin_start_zone"}
733 {$value}
734 {/loop}
735 </div>
736 ```
737
738 After every link:
739
740 ```html
741 <div class="dailyEntryFooter">
742 {loop="$link.link_plugin"}
743 {$value}
744 {/loop}
745 </div>
746 ```
747
748 Bottom:
749
750 ```html
751 <div id="plugin_zone_end_picwall" class="plugin_zone">
752 {loop="$plugin_end_zone"}
753 {$value}
754 {/loop}
755 </div>
756 ```
757
758 **feed.atom.xml** and **feed.rss.xml**:
759
760 In headers tags section:
761 ```xml
762 {loop="$feed_plugins_header"}
763 {$value}
764 {/loop}
765 ```
766
767 After each entry:
768 ```xml
769 {loop="$value.feed_plugins"}
770 {$value}
771 {/loop}
772 ```