]> git.immae.eu Git - github/shaarli/Shaarli.git/blame - doc/md/dev/Plugin-system.md
Fix plugin base path in core plugins
[github/shaarli/Shaarli.git] / doc / md / dev / Plugin-system.md
CommitLineData
91a21c27 1# Plugin system
992af0b9 2
5409ade2 3## Developer API
992af0b9
V
4
5### What can I do with plugins?
6
91a21c27 7The plugin system lets you:
992af0b9 8
43ad7c8e
V
9- insert content into specific places across templates.
10- alter data before templates rendering.
11- alter data before saving new links.
992af0b9 12
91a21c27 13
992af0b9
V
14### How can I create a plugin for Shaarli?
15
16First, chose a plugin name, such as `demo_plugin`.
17
8f6202de 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.
992af0b9
V
19
20You should have the following tree view:
21
22```
23| index.php
24| plugins/
25|---| demo_plugin/
8f6202de 26| |---| demo_plugin.meta
992af0b9
V
27| |---| demo_plugin.php
28```
29
91a21c27 30
b230bf20
A
31### Plugin initialization
32
8f6202de 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.
b230bf20
A
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
8f6202de 39The plugin system also looks for a `description` variable in the <plugin_name>.meta file, to be displayed in the plugin administration page.
40
50c9543f 41 description="The plugin does this and that."
42
992af0b9
V
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```
b230bf20 50hook_<plugin_name>_<hook_name>($data, $conf)
992af0b9
V
51```
52
b230bf20
A
53Parameters:
54
cc8f572b 55- data: see [$data section](https://shaarli.readthedocs.io/en/master/Plugin-System/#plugins-data)
43ad7c8e 56- conf: the `ConfigManager` instance.
b230bf20 57
cc8f572b 58For example, if my plugin want to add data to the header, this function is needed:
992af0b9 59
b230bf20 60 hook_demo_plugin_render_header
992af0b9
V
61
62If this function is declared, and the plugin enabled, it will be called every time Shaarli is rendering the header.
63
91a21c27 64
992af0b9
V
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
80b708a8
A
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
992af0b9
V
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
53ed6d7d 104$data['top_placeholder'][] = 'My content';
992af0b9 105# OR
53ed6d7d 106array_push($data['top_placeholder'], 'My', 'content');
992af0b9
V
107
108return $data;
109```
110
91a21c27 111
992af0b9
V
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
80b708a8 118For example, in linklist, it is possible to alter every title:
992af0b9
V
119
120```php
121// mind the reference if you want $data to be altered
53ed6d7d 122foreach ($data['links'] as &$value) {
992af0b9 123 // String reverse every title.
53ed6d7d 124 $value['title'] = strrev($value['title']);
992af0b9
V
125}
126
127return $data;
128```
129
5409ade2
A
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
43ad7c8e
V
136- `description`: plugin description
137- `parameters`: user parameter names, separated by a `;`.
138- `parameter.<PARAMETER_NAME>`: add a text description the specified parameter.
5409ade2
A
139
140> Note: In PHP, `parse_ini_file()` seems to want strings to be between by quotes `"` in the ini file.
141
76fe68d9
A
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 `_BASE_PATH_`:
152`($data['_BASE_PATH_'] ?? '') . '/' . PluginManager::$PLUGINS_PATH . '/mything/picture.png`.
153
154Note that special placeholders for CSS and JS files (respectively `css_files` and `js_files`) are already prefixed
155with the base path in template files.
91a21c27 156
992af0b9
V
157### It's not working!
158
159Use `demo_plugin` as a functional example. It covers most of the plugin system features.
160
53ed6d7d 161If it's still not working, please [open an issue](https://github.com/shaarli/Shaarli/issues/new).
992af0b9 162
91a21c27 163
992af0b9
V
164### Hooks
165
166| Hooks | Description |
167| ------------- |:-------------:|
53ed6d7d 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. |
1a8ac737 170| [render_footer](#render_footer) | Allow plugin to add content in page footer and include their own JS files. |
53ed6d7d 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. |
a8fb97a0 181| [save_plugin_parameters](#save_plugin_parameters) | Allow to manipulate plugin parameters before they're saved. |
b230bf20 182
992af0b9 183
992af0b9
V
184#### render_header
185
91a21c27 186Triggered on every page - allows plugins to add content in page headers.
992af0b9 187
992af0b9
V
188
189##### Data
190
191`$data` is an array containing:
192
80b708a8 193 - [Special data](#special-data)
992af0b9
V
194
195##### Template placeholders
196
53ed6d7d 197Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
992af0b9
V
198
199List of placeholders:
200
43ad7c8e 201- `buttons_toolbar`: after the list of buttons in the header.
992af0b9 202
53ed6d7d 203![buttons_toolbar_example](http://i.imgur.com/ssJUOrt.png)
992af0b9 204
43ad7c8e 205- `fields_toolbar`: after search fields in the header.
992af0b9
V
206
207> Note: This will only be called in linklist.
208
53ed6d7d 209![fields_toolbar_example](http://i.imgur.com/3GMifI2.png)
992af0b9 210
992af0b9 211
91a21c27 212#### render_includes
992af0b9 213
91a21c27 214Triggered on every page - allows plugins to include their own CSS files.
992af0b9 215
91a21c27 216##### data
992af0b9
V
217
218`$data` is an array containing:
219
80b708a8 220 - [Special data](#special-data)
992af0b9
V
221
222##### Template placeholders
223
53ed6d7d 224Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
992af0b9
V
225
226List of placeholders:
227
43ad7c8e 228- `css_files`: called after loading default CSS.
992af0b9
V
229
230> Note: only add the path of the CSS file. E.g: `plugins/demo_plugin/custom_demo.css`.
231
91a21c27 232
992af0b9
V
233#### render_footer
234
235Triggered on every page.
236
237Allow plugin to add content in page footer and include their own JS files.
238
91a21c27 239##### data
992af0b9
V
240
241`$data` is an array containing:
242
80b708a8 243 - [Special data](#special-data)
992af0b9
V
244
245##### Template placeholders
246
53ed6d7d 247Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
992af0b9
V
248
249List of placeholders:
250
43ad7c8e
V
251- `text`: called after the end of the footer text.
252- `endofpage`: called at the end of the page.
992af0b9 253
53ed6d7d 254![text_example](http://i.imgur.com/L5S2YEH.png)
992af0b9 255
43ad7c8e 256- `js_files`: called at the end of the page, to include custom JS scripts.
992af0b9
V
257
258> Note: only add the path of the JS file. E.g: `plugins/demo_plugin/custom_demo.js`.
259
91a21c27 260
992af0b9
V
261#### render_linklist
262
263Triggered when `linklist` is displayed (list of links, permalink, search, tag filtered, etc.).
264
265It allows to add content at the begining and end of the page, after every link displayed and to alter link data.
266
91a21c27 267##### data
992af0b9
V
268
269`$data` is an array containing:
270
80b708a8
A
271 - All templates data, including links.
272 - [Special data](#special-data)
992af0b9 273
91a21c27 274##### template placeholders
992af0b9 275
53ed6d7d 276Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
992af0b9
V
277
278List of placeholders:
279
43ad7c8e 280- `action_plugin`: next to the button "private only" at the top and bottom of the page.
992af0b9 281
53ed6d7d 282![action_plugin_example](http://i.imgur.com/Q12PWg0.png)
992af0b9 283
43ad7c8e 284- `link_plugin`: for every link, between permalink and link URL.
992af0b9 285
53ed6d7d 286![link_plugin_example](http://i.imgur.com/3oDPhWx.png)
992af0b9 287
43ad7c8e 288- `plugin_start_zone`: before displaying the template content.
992af0b9 289
53ed6d7d 290![plugin_start_zone_example](http://i.imgur.com/OVBkGy3.png)
992af0b9 291
43ad7c8e 292- `plugin_end_zone`: after displaying the template content.
992af0b9 293
53ed6d7d 294![plugin_end_zone_example](http://i.imgur.com/6IoRuop.png)
992af0b9 295
91a21c27 296
992af0b9
V
297#### render_editlink
298
299Triggered when the link edition form is displayed.
300
301Allow to add fields in the form, or display elements.
302
91a21c27 303##### data
992af0b9
V
304
305`$data` is an array containing:
306
80b708a8
A
307 - All templates data.
308 - [Special data](#special-data)
992af0b9 309
91a21c27 310##### template placeholders
992af0b9 311
53ed6d7d 312Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
992af0b9
V
313
314List of placeholders:
315
43ad7c8e 316- `edit_link_plugin`: after tags field.
992af0b9 317
53ed6d7d 318![edit_link_plugin_example](http://i.imgur.com/5u17Ens.png)
992af0b9 319
91a21c27 320
992af0b9
V
321#### render_tools
322
323Triggered when the "tools" page is displayed.
324
325Allow to add content at the end of the page.
326
91a21c27 327##### data
992af0b9
V
328
329`$data` is an array containing:
330
80b708a8
A
331 - All templates data.
332 - [Special data](#special-data)
992af0b9 333
91a21c27 334##### template placeholders
992af0b9 335
53ed6d7d 336Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
992af0b9
V
337
338List of placeholders:
339
43ad7c8e 340- `tools_plugin`: at the end of the page.
992af0b9 341
53ed6d7d 342![tools_plugin_example](http://i.imgur.com/Bqhu9oQ.png)
992af0b9 343
91a21c27 344
992af0b9
V
345#### render_picwall
346
347Triggered when picwall is displayed.
348
349Allow to add content at the top and bottom of the page.
350
91a21c27 351##### data
992af0b9
V
352
353`$data` is an array containing:
354
80b708a8
A
355 - All templates data.
356 - [Special data](#special-data)
992af0b9 357
91a21c27 358##### template placeholders
992af0b9 359
53ed6d7d 360Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
992af0b9
V
361
362List of placeholders:
363
43ad7c8e
V
364- `plugin_start_zone`: before displaying the template content.
365- `plugin_end_zone`: after displaying the template content.
992af0b9 366
53ed6d7d 367![plugin_start_end_zone_example](http://i.imgur.com/tVTQFER.png)
992af0b9 368
91a21c27 369
992af0b9
V
370#### render_tagcloud
371
372Triggered when tagcloud is displayed.
373
374Allow to add content at the top and bottom of the page.
375
91a21c27 376##### data
992af0b9
V
377
378`$data` is an array containing:
379
80b708a8
A
380 - All templates data.
381 - [Special data](#special-data)
992af0b9
V
382
383##### Template placeholders
384
53ed6d7d 385Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
992af0b9
V
386
387List of placeholders:
388
43ad7c8e
V
389- `plugin_start_zone`: before displaying the template content.
390- `plugin_end_zone`: after displaying the template content.
992af0b9 391
b230bf20
A
392For each tag, the following placeholder can be used:
393
43ad7c8e 394- `tag_plugin`: after each tag
b230bf20 395
53ed6d7d 396![plugin_start_end_zone_example](http://i.imgur.com/vHmyT3a.png)
992af0b9 397
b230bf20
A
398
399#### render_taglist
400
91a21c27 401Triggered when taglist is displayed - allows to add content at the top and bottom of the page.
b230bf20 402
91a21c27 403##### data
b230bf20
A
404
405`$data` is an array containing:
406
80b708a8
A
407 - All templates data.
408 - [Special data](#special-data)
b230bf20
A
409
410##### Template placeholders
411
53ed6d7d 412Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
b230bf20
A
413
414List of placeholders:
415
43ad7c8e
V
416- `plugin_start_zone`: before displaying the template content.
417- `plugin_end_zone`: after displaying the template content.
b230bf20
A
418
419For each tag, the following placeholder can be used:
420
43ad7c8e 421- `tag_plugin`: after each tag
b230bf20 422
992af0b9
V
423#### render_daily
424
425Triggered when tagcloud is displayed.
426
427Allow to add content at the top and bottom of the page, the bottom of each link and to alter data.
428
91a21c27 429
430##### data
992af0b9
V
431
432`$data` is an array containing:
433
80b708a8
A
434 - All templates data, including links.
435 - [Special data](#special-data)
992af0b9
V
436
437##### Template placeholders
438
53ed6d7d 439Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
992af0b9
V
440
441List of placeholders:
442
43ad7c8e 443- `link_plugin`: used at bottom of each link.
992af0b9 444
53ed6d7d 445![link_plugin_example](http://i.imgur.com/hzhMfSZ.png)
992af0b9 446
43ad7c8e
V
447- `plugin_start_zone`: before displaying the template content.
448- `plugin_end_zone`: after displaying the template content.
992af0b9 449
91a21c27 450
b230bf20
A
451#### render_feed
452
453Triggered when the ATOM or RSS feed is displayed.
454
455Allow to add tags in the feed, either in the header or for each items. Items (links) can also be altered before being rendered.
456
91a21c27 457##### data
b230bf20
A
458
459`$data` is an array containing:
460
80b708a8
A
461 - All templates data, including links.
462 - [Special data](#special-data)
b230bf20
A
463
464##### Template placeholders
465
53ed6d7d 466Tags can be added in feeds by adding an entry in `$data['<placeholder>']` array.
b230bf20
A
467
468List of placeholders:
469
43ad7c8e 470- `feed_plugins_header`: used as a header tag in the feed.
b230bf20
A
471
472For each links:
473
43ad7c8e 474- `feed_plugins`: additional tag for every link entry.
b230bf20 475
91a21c27 476
b230bf20 477#### save_link
992af0b9
V
478
479Triggered when a link is save (new link or edit).
480
481Allow to alter the link being saved in the datastore.
482
91a21c27 483##### data
992af0b9
V
484
485`$data` is an array containing the link being saved:
486
43ad7c8e
V
487- id
488- title
489- url
490- shorturl
491- description
492- private
493- tags
494- created
495- updated
b230bf20 496
80b708a8
A
497Also [special data](#special-data).
498
b230bf20
A
499
500#### delete_link
501
502Triggered when a link is deleted.
503
504Allow to execute any action before the link is actually removed from the datastore
505
91a21c27 506##### data
b230bf20 507
80b708a8 508`$data` is an array containing the link being deleted:
b230bf20 509
43ad7c8e
V
510- id
511- title
512- url
513- shorturl
514- description
515- private
516- tags
517- created
518- updated
992af0b9 519
80b708a8 520Also [special data](#special-data).
a8fb97a0
A
521
522#### save_plugin_parameters
523
524Triggered when the plugin parameters are saved from the plugin administration page.
525
526Plugins can perform an action every times their settings are updated.
527For example it is used to update the CSS file of the `default_colors` plugins.
528
91a21c27 529##### data
a8fb97a0
A
530
531`$data` input contains the `$_POST` array.
532
533So if the plugin has a parameter called `MYPLUGIN_PARAMETER`,
534the array will contain an entry with `MYPLUGIN_PARAMETER` as a key.
535
80b708a8 536Also [special data](#special-data).
a8fb97a0 537
91a21c27 538## Guide for template designers
992af0b9 539
5409ade2
A
540### Plugin administration
541
542Your 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
546Use the default one as an example.
547
548Aside from classic RainTPL loops, plugins order is handle by JavaScript. You can just include `plugin_admin.js`, only if:
549
43ad7c8e
V
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.
5409ade2
A
553
554Otherwise, 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
992af0b9
V
558### Placeholder system
559
1a8ac737 560In order to make plugins work with every custom themes, you need to add variable placeholder in your templates.
992af0b9
V
561
562It's a RainTPL loop like this:
563
564 {loop="$plugin_variable"}
565 {$value}
566 {/loop}
567
568You should enable `demo_plugin` for testing purpose, since it uses every placeholder available.
569
570### List of placeholders
571
572**page.header.html**
573
574At the end of the menu:
575
576 {loop="$plugins_header.buttons_toolbar"}
577 {$value}
578 {/loop}
579
5409ade2
A
580At the end of file, before clearing floating blocks:
581
1a8ac737 582 {if="!empty($plugin_errors) && $is_logged_in"}
5409ade2
A
583 <ul class="errors">
584 {loop="plugin_errors"}
585 <li>{$value}</li>
586 {/loop}
587 </ul>
588 {/if}
589
992af0b9
V
590**includes.html**
591
592At 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
602At the end of your footer notes:
603
604```html
605{loop="$plugins_footer.text"}
606 {$value}
607{/loop}
608```
609
610At 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
620After search fields:
621
622```html
623{loop="$plugins_header.fields_toolbar"}
624 {$value}
625{/loop}
626```
627
628Before displaying the link list (after paging):
629
630```html
631{loop="$plugin_start_zone"}
632 {$value}
633{/loop}
634```
635
636For every links (icons):
637
638```html
639{loop="$value.link_plugin"}
640 <span>{$value}</span>
641{/loop}
642```
643
644Before end paging:
645
646```html
647{loop="$plugin_end_zone"}
648 {$value}
649{/loop}
650```
651
652**linklist.paging.html**
653
654After the "private only" icon:
655
656```html
657{loop="$action_plugin"}
658 {$value}
659{/loop}
660```
661
662**editlink.html**
663
664After tags field:
665
666```html
667{loop="$edit_link_plugin"}
668 {$value}
669{/loop}
670```
671
672**tools.html**
673
674After the last tool:
675
676```html
677{loop="$tools_plugin"}
678 {$value}
679{/loop}
680```
681
682**picwall.html**
683
684Top:
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
694Bottom:
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
706Top:
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
716Bottom:
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
728Top:
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
738After every link:
739
740```html
741<div class="dailyEntryFooter">
742 {loop="$link.link_plugin"}
743 {$value}
744 {/loop}
745</div>
746```
747
748Bottom:
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```
b230bf20
A
757
758**feed.atom.xml** and **feed.rss.xml**:
759
760In headers tags section:
761```xml
762{loop="$feed_plugins_header"}
763 {$value}
764{/loop}
765```
766
767After each entry:
768```xml
769{loop="$value.feed_plugins"}
770 {$value}
771{/loop}
772```