1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
|
<?php
/**
* Demo Plugin.
*
* This plugin tries to completely cover Shaarli's plugin API.
* Can be used by plugin developers to make their own plugin.
*/
require_once __DIR__ . '/DemoPluginController.php';
/*
* RENDER HEADER, INCLUDES, FOOTER
*
* Those hooks are called at every page rendering.
* You can filter its execution by checking _PAGE_ value
* and check user status with _LOGGEDIN_.
*/
use Shaarli\Bookmark\Bookmark;
use Shaarli\Config\ConfigManager;
use Shaarli\Plugin\PluginManager;
use Shaarli\Render\TemplatePage;
/**
* In the footer hook, there is a working example of a translation extension for Shaarli.
*
* The extension must be attached to a new translation domain (i.e. NOT 'shaarli').
* Use case: any custom theme or non official plugin can use the translation system.
*
* See the documentation for more information.
*/
const EXT_TRANSLATION_DOMAIN = 'demo';
/*
* This is not necessary, but it's easier if you don't want Poedit to mix up your translations.
*/
function demo_plugin_t($text, $nText = '', $nb = 1)
{
return t($text, $nText, $nb, EXT_TRANSLATION_DOMAIN);
}
/**
* Initialization function.
* It will be called when the plugin is loaded.
* This function can be used to return a list of initialization errors.
*
* @param $conf ConfigManager instance.
*
* @return array List of errors (optional).
*/
function demo_plugin_init($conf)
{
$conf->get('toto', 'nope');
if (! $conf->exists('translation.extensions.demo')) {
// Custom translation with the domain 'demo'
$conf->set('translation.extensions.demo', 'plugins/demo_plugin/languages/');
$conf->write(true);
}
$errors[] = 'This a demo init error.';
return $errors;
}
function demo_plugin_register_routes(): array
{
return [
[
'method' => 'GET',
'route' => '/custom',
'callable' => 'Shaarli\DemoPlugin\DemoPluginController:index',
],
];
}
/**
* Hook render_header.
* Executed on every page render.
*
* Template placeholders:
* - buttons_toolbar
* - fields_toolbar
*
* @param array $data data passed to plugin
*
* @return array altered $data.
*/
function hook_demo_plugin_render_header($data)
{
// Only execute when linklist is rendered.
if ($data['_PAGE_'] == TemplatePage::LINKLIST) {
// If loggedin
if ($data['_LOGGEDIN_'] === true) {
/*
* Links in toolbar:
* A link is an array of its attributes (key="value"),
* and a mandatory `html` key, which contains its value.
*/
$button = [
'attr' => [
'href' => '#',
'class' => 'mybutton',
'title' => 'hover me',
],
'html' => 'DEMO buttons toolbar',
];
$data['buttons_toolbar'][] = $button;
}
/*
* Add additional input fields in the tools.
* A field is an array containing:
* [
* 'form-attribute-1' => 'form attribute 1 value',
* 'form-attribute-2' => 'form attribute 2 value',
* 'inputs' => [
* [
* 'input-1-attribute-1 => 'input 1 attribute 1 value',
* 'input-1-attribute-2 => 'input 1 attribute 2 value',
* ],
* [
* 'input-2-attribute-1 => 'input 2 attribute 1 value',
* ],
* ],
* ]
* This example renders as:
* <form form-attribute-1="form attribute 1 value" form-attribute-2="form attribute 2 value">
* <input input-1-attribute-1="input 1 attribute 1 value" input-1-attribute-2="input 1 attribute 2 value">
* <input input-2-attribute-1="input 2 attribute 1 value">
* </form>
*/
$form = [
'attr' => [
'method' => 'GET',
'action' => $data['_BASE_PATH_'] . '/',
'class' => 'addform',
],
'inputs' => [
[
'type' => 'text',
'name' => 'demo',
'placeholder' => 'demo',
]
]
];
$data['fields_toolbar'][] = $form;
}
// Another button always displayed
$button = [
'attr' => [
'href' => '#',
],
'html' => 'Demo',
];
$data['buttons_toolbar'][] = $button;
return $data;
}
/**
* Hook render_includes.
* Executed on every page render.
*
* Template placeholders:
* - css_files
*
* Data:
* - _PAGE_: current page
* - _LOGGEDIN_: true/false
*
* @param array $data data passed to plugin
*
* @return array altered $data.
*/
function hook_demo_plugin_render_includes($data)
{
// List of plugin's CSS files.
// Note that you just need to specify CSS path.
$data['css_files'][] = PluginManager::$PLUGINS_PATH . '/demo_plugin/custom_demo.css';
return $data;
}
/**
* Hook render_footer.
* Executed on every page render.
*
* Template placeholders:
* - text
* - endofpage
* - js_files
*
* Data:
* - _PAGE_: current page
* - _LOGGEDIN_: true/false
*
* @param array $data data passed to plugin
*
* @return array altered $data.
*/
function hook_demo_plugin_render_footer($data)
{
// Footer text
$data['text'][] = '<br>' . demo_plugin_t('Shaarli is now enhanced by the awesome demo_plugin.');
// Free elements at the end of the page.
$data['endofpage'][] = '<marquee id="demo_marquee">' .
'DEMO: it\'s 1999 all over again!' .
'</marquee>';
// List of plugin's JS files.
// Note that you just need to specify CSS path.
$data['js_files'][] = PluginManager::$PLUGINS_PATH . '/demo_plugin/demo_plugin.js';
return $data;
}
/*
* SPECIFIC PAGES
*/
/**
* Hook render_linklist.
*
* Template placeholders:
* - action_plugin: next to 'private only' button.
* - plugin_start_zone: page start
* - plugin_end_zone: page end
* - link_plugin: icons below each links.
*
* Data:
* - _LOGGEDIN_: true/false
*
* @param array $data data passed to plugin
*
* @return array altered $data.
*/
function hook_demo_plugin_render_linklist($data)
{
/*
* Action links (action_plugin):
* A link is an array of its attributes (key="value"),
* and a mandatory `html` key, which contains its value.
* It's also recommended to add key 'on' or 'off' for theme rendering.
*/
$action = [
'attr' => [
'href' => '?up',
'title' => 'Uppercase!',
],
'html' => '←',
];
if (isset($_GET['up'])) {
// Manipulate link data
foreach ($data['links'] as &$value) {
$value['description'] = strtoupper($value['description']);
$value['title'] = strtoupper($value['title']);
}
$action['on'] = true;
} else {
$action['off'] = true;
}
$data['action_plugin'][] = $action;
// Action to trigger custom filter hiding bookmarks not containing 'e' letter in their description
$action = [
'attr' => [
'href' => '?e',
'title' => 'Hide bookmarks without "e" in their description.',
],
'html' => 'e',
'on' => isset($_GET['e'])
];
$data['action_plugin'][] = $action;
// link_plugin (for each link)
foreach ($data['links'] as &$value) {
$value['link_plugin'][] = ' DEMO \o/';
}
// plugin_start_zone
$data['plugin_start_zone'][] = '<center>BEFORE</center>';
// plugin_start_zone
$data['plugin_end_zone'][] = '<center>AFTER</center>';
return $data;
}
/**
* Hook render_editlink.
*
* Template placeholders:
* - field_plugin: add link fields after tags.
*
* @param array $data data passed to plugin
*
* @return array altered $data.
*/
function hook_demo_plugin_render_editlink($data)
{
// Load HTML into a string
$html = file_get_contents(PluginManager::$PLUGINS_PATH . '/demo_plugin/field.html');
// Replace value in HTML if it exists in $data
if (!empty($data['link']['stuff'])) {
$html = sprintf($html, $data['link']['stuff']);
} else {
$html = sprintf($html, '');
}
// field_plugin
$data['edit_link_plugin'][] = $html;
return $data;
}
/**
* Hook render_tools.
*
* Template placeholders:
* - tools_plugin: after other tools.
*
* @param array $data data passed to plugin
*
* @return array altered $data.
*/
function hook_demo_plugin_render_tools($data)
{
// field_plugin
$data['tools_plugin'][] = '<div class="tools-item">
<a href="' . $data['_BASE_PATH_'] . '/plugin/demo_plugin/custom">
<span class="pure-button pure-u-lg-2-3 pure-u-3-4">Demo Plugin Custom Route</span>
</a>
</div>';
return $data;
}
/**
* Hook render_picwall.
*
* Template placeholders:
* - plugin_start_zone: page start.
* - plugin_end_zone: page end.
*
* Data:
* - _LOGGEDIN_: true/false
*
* @param array $data data passed to plugin
*
* @return array altered $data.
*/
function hook_demo_plugin_render_picwall($data)
{
$data['plugin_start_zone'][] = '<center>BEFORE</center>';
$data['plugin_end_zone'][] = '<center>AFTER</center>';
return $data;
}
/**
* Hook render_tagcloud.
*
* Template placeholders:
* - plugin_start_zone: page start.
* - plugin_end_zone: page end.
*
* Data:
* - _LOGGEDIN_: true/false
*
* @param array $data data passed to plugin
*
* @return array altered $data.
*/
function hook_demo_plugin_render_tagcloud($data)
{
$data['plugin_start_zone'][] = '<center>BEFORE</center>';
$data['plugin_end_zone'][] = '<center>AFTER</center>';
return $data;
}
/**
* Hook render_daily.
*
* Template placeholders:
* - plugin_start_zone: page start.
* - plugin_end_zone: page end.
*
* Data:
* - _LOGGEDIN_: true/false
*
* @param array $data data passed to plugin
*
* @return array altered $data.
*/
function hook_demo_plugin_render_daily($data)
{
$data['plugin_start_zone'][] = '<center>BEFORE</center>';
$data['plugin_end_zone'][] = '<center>AFTER</center>';
// Manipulate columns data
foreach ($data['linksToDisplay'] as &$value) {
$value['formatedDescription'] .= ' ಠ_ಠ';
}
// Add plugin content at the end of each link
foreach ($data['linksToDisplay'] as &$value) {
$value['link_plugin'][] = 'DEMO';
}
return $data;
}
/*
* DATA SAVING HOOK.
*/
/**
* Hook savelink.
*
* Triggered when a link is save (new or edit).
* All new links now contain a 'stuff' value.
*
* @param array $data contains the new link data.
*
* @return array altered $data.
*/
function hook_demo_plugin_save_link($data)
{
// Save stuff added in editlink field
if (!empty($_POST['lf_stuff'])) {
$data['stuff'] = escape($_POST['lf_stuff']);
}
return $data;
}
/**
* Hook delete_link.
*
* Triggered when a link is deleted.
*
* @param array $data contains the link to be deleted.
*
* @return array altered data.
*/
function hook_demo_plugin_delete_link($data)
{
if (strpos($data['url'], 'youtube.com') !== false) {
exit('You can not delete a YouTube link. Don\'t ask.');
}
}
/**
* Execute render_feed hook.
* Called with ATOM and RSS feed.
*
* Special data keys:
* - _PAGE_: current page
* - _LOGGEDIN_: true/false
*
* @param array $data data passed to plugin
*
* @return array altered $data.
*/
function hook_demo_plugin_render_feed($data)
{
foreach ($data['links'] as &$link) {
if ($data['_PAGE_'] == TemplatePage::FEED_ATOM) {
$link['description'] .= ' - ATOM Feed' ;
} elseif ($data['_PAGE_'] == TemplatePage::FEED_RSS) {
$link['description'] .= ' - RSS Feed';
}
}
return $data;
}
/**
* When plugin parameters are saved.
*
* @param array $data $_POST array
*
* @return array Updated $_POST array
*/
function hook_demo_plugin_save_plugin_parameters($data)
{
// Here we edit the provided value.
// This hook can also be used to generate config files, etc.
if (! empty($data['DEMO_PLUGIN_PARAMETER']) && ! endsWith($data['DEMO_PLUGIN_PARAMETER'], '_SUFFIX')) {
$data['DEMO_PLUGIN_PARAMETER'] .= '_SUFFIX';
}
return $data;
}
/**
* This hook is called when a search is performed, on every search entry.
* It allows to add custom filters, and filter out additional link.
*
* For exemple here, we hide all bookmarks not containing the letter 'e' in their description.
*
* @param Bookmark $bookmark Search entry. Note that this is a Bookmark object, and not a link array.
* It should NOT be altered.
* @param array $context Additional info on the search performed.
*
* @return bool True if the bookmark should be kept in the search result, false to discard it.
*/
function hook_demo_plugin_filter_search_entry(Bookmark $bookmark, array $context): bool
{
if (isset($_GET['e'])) {
return strpos($bookmark->getDescription(), 'e') !== false;
}
return true;
}
/**
* This function is never called, but contains translation calls for GNU gettext extraction.
*/
function demo_dummy_translation()
{
// meta
t('A demo plugin covering all use cases for template designers and plugin developers.');
t('This is a parameter dedicated to the demo plugin. It\'ll be suffixed.');
t('Other demo parameter');
}
|