]> git.immae.eu Git - github/shaarli/Shaarli.git/blame_incremental - doc/md/dev/Plugin-system.md
Merge pull request #1567 from ArthurHoaro/feature/async-title-retrieval
[github/shaarli/Shaarli.git] / doc / md / dev / Plugin-system.md
... / ...
CommitLineData
1# Plugin system
2
3## Developer API
4
5### What can I do with plugins?
6
7The 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
16First, chose a plugin name, such as `demo_plugin`.
17
18Under `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
20You 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
33At 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
37This 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
39The 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
45A plugin is a set of functions. Each function will be triggered by the plugin system at certain point in Shaarli execution.
46
47These functions need to be named with this pattern:
48
49```
50hook_<plugin_name>_<hook_name>($data, $conf)
51```
52
53Parameters:
54
55- data: see [$data section](https://shaarli.readthedocs.io/en/master/Plugin-System/#plugins-data)
56- conf: the `ConfigManager` instance.
57
58For example, if my plugin want to add data to the header, this function is needed:
59
60 hook_demo_plugin_render_header
61
62If 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
69Every 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
77Special additional data are passed to every hook through the
78`$data` parameter to give you access to additional context, and services.
79
80Complete 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
87Example:
88
89```php
90if ($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
97Template placeholders are displayed in template in specific places.
98
99RainTPL displays every element contained in the placeholder's array. These element can be added by plugins.
100
101For 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
106array_push($data['top_placeholder'], 'My', 'content');
107
108return $data;
109```
110
111
112#### Data manipulation
113
114When a page is displayed, every variable send to the template engine is passed to plugins before that in `$data`.
115
116The data contained by this array can be altered before template rendering.
117
118For example, in linklist, it is possible to alter every title:
119
120```php
121// mind the reference if you want $data to be altered
122foreach ($data['links'] as &$value) {
123 // String reverse every title.
124 $value['title'] = strrev($value['title']);
125}
126
127return $data;
128```
129
130### Metadata
131
132Every plugin needs a `<plugin_name>.meta` file, which is in fact an `.ini` file (`KEY="VALUE"`), to be listed in plugin administration.
133
134Each 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
144Because Shaarli is a self-hosted tool, an instance can either be installed at the root directory, or under a subfolder.
145This means that you can *never* use absolute paths (eg `/plugins/mything/file.png`).
146
147If a file needs to be included in server end, use simple relative path:
148`PluginManager::$PLUGINS_PATH . '/mything/template.html'`.
149
150If it needs to be included in front end side (e.g. an image),
151the 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
159Note that special placeholders for CSS and JS files (respectively `css_files` and `js_files`) are already prefixed
160with the root path in template files.
161
162### It's not working!
163
164Use `demo_plugin` as a functional example. It covers most of the plugin system features.
165
166If 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
191Triggered 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
202Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
203
204List 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
219Triggered 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
229Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
230
231List 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
240Triggered on every page.
241
242Allow 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
252Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
253
254List 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
268Triggered when `linklist` is displayed (list of links, permalink, search, tag filtered, etc.).
269
270It 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
281Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
282
283List 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
304Triggered when the link edition form is displayed.
305
306Allow 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
317Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
318
319List 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
328Triggered when the "tools" page is displayed.
329
330Allow 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
341Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
342
343List 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
352Triggered when picwall is displayed.
353
354Allow 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
365Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
366
367List 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
377Triggered when tagcloud is displayed.
378
379Allow 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
390Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
391
392List of placeholders:
393
394- `plugin_start_zone`: before displaying the template content.
395- `plugin_end_zone`: after displaying the template content.
396
397For 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
406Triggered 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
417Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
418
419List of placeholders:
420
421- `plugin_start_zone`: before displaying the template content.
422- `plugin_end_zone`: after displaying the template content.
423
424For each tag, the following placeholder can be used:
425
426- `tag_plugin`: after each tag
427
428#### render_daily
429
430Triggered when tagcloud is displayed.
431
432Allow 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
444Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
445
446List 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
458Triggered when the ATOM or RSS feed is displayed.
459
460Allow 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
471Tags can be added in feeds by adding an entry in `$data['<placeholder>']` array.
472
473List of placeholders:
474
475- `feed_plugins_header`: used as a header tag in the feed.
476
477For each links:
478
479- `feed_plugins`: additional tag for every link entry.
480
481
482#### save_link
483
484Triggered when a link is save (new link or edit).
485
486Allow 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
502Also [special data](#special-data).
503
504
505#### delete_link
506
507Triggered when a link is deleted.
508
509Allow 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
525Also [special data](#special-data).
526
527#### save_plugin_parameters
528
529Triggered when the plugin parameters are saved from the plugin administration page.
530
531Plugins can perform an action every times their settings are updated.
532For 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
538So if the plugin has a parameter called `MYPLUGIN_PARAMETER`,
539the array will contain an entry with `MYPLUGIN_PARAMETER` as a key.
540
541Also [special data](#special-data).
542
543## Guide for template designers
544
545### Plugin administration
546
547Your 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
551Use the default one as an example.
552
553Aside 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
559Otherwise, 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
565In order to make plugins work with every custom themes, you need to add variable placeholder in your templates.
566
567It's a RainTPL loop like this:
568
569 {loop="$plugin_variable"}
570 {$value}
571 {/loop}
572
573You should enable `demo_plugin` for testing purpose, since it uses every placeholder available.
574
575### List of placeholders
576
577**page.header.html**
578
579At the end of the menu:
580
581 {loop="$plugins_header.buttons_toolbar"}
582 {$value}
583 {/loop}
584
585At 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
597At 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
607At the end of your footer notes:
608
609```html
610{loop="$plugins_footer.text"}
611 {$value}
612{/loop}
613```
614
615At 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
625After search fields:
626
627```html
628{loop="$plugins_header.fields_toolbar"}
629 {$value}
630{/loop}
631```
632
633Before displaying the link list (after paging):
634
635```html
636{loop="$plugin_start_zone"}
637 {$value}
638{/loop}
639```
640
641For every links (icons):
642
643```html
644{loop="$value.link_plugin"}
645 <span>{$value}</span>
646{/loop}
647```
648
649Before end paging:
650
651```html
652{loop="$plugin_end_zone"}
653 {$value}
654{/loop}
655```
656
657**linklist.paging.html**
658
659After the "private only" icon:
660
661```html
662{loop="$action_plugin"}
663 {$value}
664{/loop}
665```
666
667**editlink.html**
668
669After tags field:
670
671```html
672{loop="$edit_link_plugin"}
673 {$value}
674{/loop}
675```
676
677**tools.html**
678
679After the last tool:
680
681```html
682{loop="$tools_plugin"}
683 {$value}
684{/loop}
685```
686
687**picwall.html**
688
689Top:
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
699Bottom:
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
711Top:
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
721Bottom:
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
733Top:
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
743After every link:
744
745```html
746<div class="dailyEntryFooter">
747 {loop="$link.link_plugin"}
748 {$value}
749 {/loop}
750</div>
751```
752
753Bottom:
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
765In headers tags section:
766```xml
767{loop="$feed_plugins_header"}
768 {$value}
769{/loop}
770```
771
772After each entry:
773```xml
774{loop="$value.feed_plugins"}
775 {$value}
776{/loop}
777```