]> git.immae.eu Git - github/shaarli/Shaarli.git/blame - doc/md/dev/Plugin-system.md
Inject ROOT_PATH in plugin instead of regenerating it everywhere
[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),
3adbdc2a
A
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`.
76fe68d9
A
158
159Note that special placeholders for CSS and JS files (respectively `css_files` and `js_files`) are already prefixed
3adbdc2a 160with the root path in template files.
91a21c27 161
992af0b9
V
162### It's not working!
163
164Use `demo_plugin` as a functional example. It covers most of the plugin system features.
165
53ed6d7d 166If it's still not working, please [open an issue](https://github.com/shaarli/Shaarli/issues/new).
992af0b9 167
91a21c27 168
992af0b9
V
169### Hooks
170
171| Hooks | Description |
172| ------------- |:-------------:|
53ed6d7d 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. |
1a8ac737 175| [render_footer](#render_footer) | Allow plugin to add content in page footer and include their own JS files. |
53ed6d7d 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. |
a8fb97a0 186| [save_plugin_parameters](#save_plugin_parameters) | Allow to manipulate plugin parameters before they're saved. |
b230bf20 187
992af0b9 188
992af0b9
V
189#### render_header
190
91a21c27 191Triggered on every page - allows plugins to add content in page headers.
992af0b9 192
992af0b9
V
193
194##### Data
195
196`$data` is an array containing:
197
80b708a8 198 - [Special data](#special-data)
992af0b9
V
199
200##### Template placeholders
201
53ed6d7d 202Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
992af0b9
V
203
204List of placeholders:
205
43ad7c8e 206- `buttons_toolbar`: after the list of buttons in the header.
992af0b9 207
53ed6d7d 208![buttons_toolbar_example](http://i.imgur.com/ssJUOrt.png)
992af0b9 209
43ad7c8e 210- `fields_toolbar`: after search fields in the header.
992af0b9
V
211
212> Note: This will only be called in linklist.
213
53ed6d7d 214![fields_toolbar_example](http://i.imgur.com/3GMifI2.png)
992af0b9 215
992af0b9 216
91a21c27 217#### render_includes
992af0b9 218
91a21c27 219Triggered on every page - allows plugins to include their own CSS files.
992af0b9 220
91a21c27 221##### data
992af0b9
V
222
223`$data` is an array containing:
224
80b708a8 225 - [Special data](#special-data)
992af0b9
V
226
227##### Template placeholders
228
53ed6d7d 229Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
992af0b9
V
230
231List of placeholders:
232
43ad7c8e 233- `css_files`: called after loading default CSS.
992af0b9
V
234
235> Note: only add the path of the CSS file. E.g: `plugins/demo_plugin/custom_demo.css`.
236
91a21c27 237
992af0b9
V
238#### render_footer
239
240Triggered on every page.
241
242Allow plugin to add content in page footer and include their own JS files.
243
91a21c27 244##### data
992af0b9
V
245
246`$data` is an array containing:
247
80b708a8 248 - [Special data](#special-data)
992af0b9
V
249
250##### Template placeholders
251
53ed6d7d 252Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
992af0b9
V
253
254List of placeholders:
255
43ad7c8e
V
256- `text`: called after the end of the footer text.
257- `endofpage`: called at the end of the page.
992af0b9 258
53ed6d7d 259![text_example](http://i.imgur.com/L5S2YEH.png)
992af0b9 260
43ad7c8e 261- `js_files`: called at the end of the page, to include custom JS scripts.
992af0b9
V
262
263> Note: only add the path of the JS file. E.g: `plugins/demo_plugin/custom_demo.js`.
264
91a21c27 265
992af0b9
V
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
91a21c27 272##### data
992af0b9
V
273
274`$data` is an array containing:
275
80b708a8
A
276 - All templates data, including links.
277 - [Special data](#special-data)
992af0b9 278
91a21c27 279##### template placeholders
992af0b9 280
53ed6d7d 281Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
992af0b9
V
282
283List of placeholders:
284
43ad7c8e 285- `action_plugin`: next to the button "private only" at the top and bottom of the page.
992af0b9 286
53ed6d7d 287![action_plugin_example](http://i.imgur.com/Q12PWg0.png)
992af0b9 288
43ad7c8e 289- `link_plugin`: for every link, between permalink and link URL.
992af0b9 290
53ed6d7d 291![link_plugin_example](http://i.imgur.com/3oDPhWx.png)
992af0b9 292
43ad7c8e 293- `plugin_start_zone`: before displaying the template content.
992af0b9 294
53ed6d7d 295![plugin_start_zone_example](http://i.imgur.com/OVBkGy3.png)
992af0b9 296
43ad7c8e 297- `plugin_end_zone`: after displaying the template content.
992af0b9 298
53ed6d7d 299![plugin_end_zone_example](http://i.imgur.com/6IoRuop.png)
992af0b9 300
91a21c27 301
992af0b9
V
302#### render_editlink
303
304Triggered when the link edition form is displayed.
305
306Allow to add fields in the form, or display elements.
307
91a21c27 308##### data
992af0b9
V
309
310`$data` is an array containing:
311
80b708a8
A
312 - All templates data.
313 - [Special data](#special-data)
992af0b9 314
91a21c27 315##### template placeholders
992af0b9 316
53ed6d7d 317Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
992af0b9
V
318
319List of placeholders:
320
43ad7c8e 321- `edit_link_plugin`: after tags field.
992af0b9 322
53ed6d7d 323![edit_link_plugin_example](http://i.imgur.com/5u17Ens.png)
992af0b9 324
91a21c27 325
992af0b9
V
326#### render_tools
327
328Triggered when the "tools" page is displayed.
329
330Allow to add content at the end of the page.
331
91a21c27 332##### data
992af0b9
V
333
334`$data` is an array containing:
335
80b708a8
A
336 - All templates data.
337 - [Special data](#special-data)
992af0b9 338
91a21c27 339##### template placeholders
992af0b9 340
53ed6d7d 341Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
992af0b9
V
342
343List of placeholders:
344
43ad7c8e 345- `tools_plugin`: at the end of the page.
992af0b9 346
53ed6d7d 347![tools_plugin_example](http://i.imgur.com/Bqhu9oQ.png)
992af0b9 348
91a21c27 349
992af0b9
V
350#### render_picwall
351
352Triggered when picwall is displayed.
353
354Allow to add content at the top and bottom of the page.
355
91a21c27 356##### data
992af0b9
V
357
358`$data` is an array containing:
359
80b708a8
A
360 - All templates data.
361 - [Special data](#special-data)
992af0b9 362
91a21c27 363##### template placeholders
992af0b9 364
53ed6d7d 365Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
992af0b9
V
366
367List of placeholders:
368
43ad7c8e
V
369- `plugin_start_zone`: before displaying the template content.
370- `plugin_end_zone`: after displaying the template content.
992af0b9 371
53ed6d7d 372![plugin_start_end_zone_example](http://i.imgur.com/tVTQFER.png)
992af0b9 373
91a21c27 374
992af0b9
V
375#### render_tagcloud
376
377Triggered when tagcloud is displayed.
378
379Allow to add content at the top and bottom of the page.
380
91a21c27 381##### data
992af0b9
V
382
383`$data` is an array containing:
384
80b708a8
A
385 - All templates data.
386 - [Special data](#special-data)
992af0b9
V
387
388##### Template placeholders
389
53ed6d7d 390Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
992af0b9
V
391
392List of placeholders:
393
43ad7c8e
V
394- `plugin_start_zone`: before displaying the template content.
395- `plugin_end_zone`: after displaying the template content.
992af0b9 396
b230bf20
A
397For each tag, the following placeholder can be used:
398
43ad7c8e 399- `tag_plugin`: after each tag
b230bf20 400
53ed6d7d 401![plugin_start_end_zone_example](http://i.imgur.com/vHmyT3a.png)
992af0b9 402
b230bf20
A
403
404#### render_taglist
405
91a21c27 406Triggered when taglist is displayed - allows to add content at the top and bottom of the page.
b230bf20 407
91a21c27 408##### data
b230bf20
A
409
410`$data` is an array containing:
411
80b708a8
A
412 - All templates data.
413 - [Special data](#special-data)
b230bf20
A
414
415##### Template placeholders
416
53ed6d7d 417Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
b230bf20
A
418
419List of placeholders:
420
43ad7c8e
V
421- `plugin_start_zone`: before displaying the template content.
422- `plugin_end_zone`: after displaying the template content.
b230bf20
A
423
424For each tag, the following placeholder can be used:
425
43ad7c8e 426- `tag_plugin`: after each tag
b230bf20 427
992af0b9
V
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
91a21c27 434
435##### data
992af0b9
V
436
437`$data` is an array containing:
438
80b708a8
A
439 - All templates data, including links.
440 - [Special data](#special-data)
992af0b9
V
441
442##### Template placeholders
443
53ed6d7d 444Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
992af0b9
V
445
446List of placeholders:
447
43ad7c8e 448- `link_plugin`: used at bottom of each link.
992af0b9 449
53ed6d7d 450![link_plugin_example](http://i.imgur.com/hzhMfSZ.png)
992af0b9 451
43ad7c8e
V
452- `plugin_start_zone`: before displaying the template content.
453- `plugin_end_zone`: after displaying the template content.
992af0b9 454
91a21c27 455
b230bf20
A
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
91a21c27 462##### data
b230bf20
A
463
464`$data` is an array containing:
465
80b708a8
A
466 - All templates data, including links.
467 - [Special data](#special-data)
b230bf20
A
468
469##### Template placeholders
470
53ed6d7d 471Tags can be added in feeds by adding an entry in `$data['<placeholder>']` array.
b230bf20
A
472
473List of placeholders:
474
43ad7c8e 475- `feed_plugins_header`: used as a header tag in the feed.
b230bf20
A
476
477For each links:
478
43ad7c8e 479- `feed_plugins`: additional tag for every link entry.
b230bf20 480
91a21c27 481
b230bf20 482#### save_link
992af0b9
V
483
484Triggered when a link is save (new link or edit).
485
486Allow to alter the link being saved in the datastore.
487
91a21c27 488##### data
992af0b9
V
489
490`$data` is an array containing the link being saved:
491
43ad7c8e
V
492- id
493- title
494- url
495- shorturl
496- description
497- private
498- tags
499- created
500- updated
b230bf20 501
80b708a8
A
502Also [special data](#special-data).
503
b230bf20
A
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
91a21c27 511##### data
b230bf20 512
80b708a8 513`$data` is an array containing the link being deleted:
b230bf20 514
43ad7c8e
V
515- id
516- title
517- url
518- shorturl
519- description
520- private
521- tags
522- created
523- updated
992af0b9 524
80b708a8 525Also [special data](#special-data).
a8fb97a0
A
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
91a21c27 534##### data
a8fb97a0
A
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
80b708a8 541Also [special data](#special-data).
a8fb97a0 542
91a21c27 543## Guide for template designers
992af0b9 544
5409ade2
A
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
43ad7c8e
V
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.
5409ade2
A
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
992af0b9
V
563### Placeholder system
564
1a8ac737 565In order to make plugins work with every custom themes, you need to add variable placeholder in your templates.
992af0b9
V
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
5409ade2
A
585At the end of file, before clearing floating blocks:
586
1a8ac737 587 {if="!empty($plugin_errors) && $is_logged_in"}
5409ade2
A
588 <ul class="errors">
589 {loop="plugin_errors"}
590 <li>{$value}</li>
591 {/loop}
592 </ul>
593 {/if}
594
992af0b9
V
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```
b230bf20
A
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```