aboutsummaryrefslogtreecommitdiffhomepage
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/addlink_toolbar/addlink_toolbar.css4
-rw-r--r--plugins/addlink_toolbar/addlink_toolbar.html6
-rw-r--r--plugins/addlink_toolbar/addlink_toolbar.php24
-rw-r--r--plugins/archiveorg/archiveorg.html2
-rw-r--r--plugins/archiveorg/archiveorg.php3
-rw-r--r--plugins/demo_plugin/custom_demo.css4
-rw-r--r--plugins/demo_plugin/demo_plugin.php99
-rw-r--r--plugins/isso/isso.css3
-rw-r--r--plugins/isso/isso.html14
-rw-r--r--plugins/isso/isso.meta3
-rw-r--r--plugins/isso/isso.php54
-rw-r--r--plugins/markdown/Parsedown.php1528
-rw-r--r--plugins/markdown/README.md51
-rw-r--r--plugins/markdown/markdown.meta5
-rw-r--r--plugins/markdown/markdown.php105
-rw-r--r--plugins/piwik/piwik.meta4
-rw-r--r--plugins/piwik/piwik.php71
-rw-r--r--plugins/playvideos/playvideos.html1
-rw-r--r--plugins/playvideos/playvideos.php12
-rw-r--r--plugins/qrcode/qrcode.html2
-rw-r--r--plugins/readityourself/config.php.dist3
-rw-r--r--plugins/readityourself/readityourself.html2
-rw-r--r--plugins/readityourself/readityourself.php34
-rw-r--r--plugins/wallabag/README.md41
-rw-r--r--plugins/wallabag/config.php.dist4
-rw-r--r--plugins/wallabag/wallabag.html2
-rw-r--r--plugins/wallabag/wallabag.meta4
-rw-r--r--plugins/wallabag/wallabag.php38
28 files changed, 479 insertions, 1644 deletions
diff --git a/plugins/addlink_toolbar/addlink_toolbar.css b/plugins/addlink_toolbar/addlink_toolbar.css
deleted file mode 100644
index b6a612f0..00000000
--- a/plugins/addlink_toolbar/addlink_toolbar.css
+++ /dev/null
@@ -1,4 +0,0 @@
1#addlink_toolbar {
2 display: inline;
3 margin: 0 0 0 25px;
4} \ No newline at end of file
diff --git a/plugins/addlink_toolbar/addlink_toolbar.html b/plugins/addlink_toolbar/addlink_toolbar.html
deleted file mode 100644
index f38c41a0..00000000
--- a/plugins/addlink_toolbar/addlink_toolbar.html
+++ /dev/null
@@ -1,6 +0,0 @@
1<div id="addlink_toolbar">
2 <form method="GET" action="" name="addform" class="addform">
3 <input type="text" name="post" placeholder="URI">
4 <input type="submit" value="Add link" class="bigbutton">
5 </form>
6</div> \ No newline at end of file
diff --git a/plugins/addlink_toolbar/addlink_toolbar.php b/plugins/addlink_toolbar/addlink_toolbar.php
index ba3849cf..bf8a198a 100644
--- a/plugins/addlink_toolbar/addlink_toolbar.php
+++ b/plugins/addlink_toolbar/addlink_toolbar.php
@@ -15,7 +15,27 @@
15function hook_addlink_toolbar_render_header($data) 15function hook_addlink_toolbar_render_header($data)
16{ 16{
17 if ($data['_PAGE_'] == Router::$PAGE_LINKLIST && $data['_LOGGEDIN_'] === true) { 17 if ($data['_PAGE_'] == Router::$PAGE_LINKLIST && $data['_LOGGEDIN_'] === true) {
18 $data['fields_toolbar'][] = file_get_contents(PluginManager::$PLUGINS_PATH . '/addlink_toolbar/addlink_toolbar.html'); 18 $form = array(
19 'attr' => array(
20 'method' => 'GET',
21 'action' => '',
22 'name' => 'addform',
23 'class' => 'addform',
24 ),
25 'inputs' => array(
26 array(
27 'type' => 'text',
28 'name' => 'post',
29 'placeholder' => 'URI',
30 ),
31 array(
32 'type' => 'submit',
33 'value' => 'Add link',
34 'class' => 'bigbutton',
35 ),
36 ),
37 );
38 $data['fields_toolbar'][] = $form;
19 } 39 }
20 40
21 return $data; 41 return $data;
@@ -35,4 +55,4 @@ function hook_addlink_toolbar_render_includes($data)
35 } 55 }
36 56
37 return $data; 57 return $data;
38} \ No newline at end of file 58}
diff --git a/plugins/archiveorg/archiveorg.html b/plugins/archiveorg/archiveorg.html
index 576bd46e..0781fe35 100644
--- a/plugins/archiveorg/archiveorg.html
+++ b/plugins/archiveorg/archiveorg.html
@@ -1 +1 @@
<span><a href="https://web.archive.org/web/%s"><img class="linklist-plugin-icon" src="plugins/archiveorg/internetarchive.png" title="View on archive.org" /></a></span> <span><a href="https://web.archive.org/web/%s"><img class="linklist-plugin-icon" src="plugins/archiveorg/internetarchive.png" title="View on archive.org" alt="archive.org" /></a></span>
diff --git a/plugins/archiveorg/archiveorg.php b/plugins/archiveorg/archiveorg.php
index 7d172584..03d13d0e 100644
--- a/plugins/archiveorg/archiveorg.php
+++ b/plugins/archiveorg/archiveorg.php
@@ -17,6 +17,9 @@ function hook_archiveorg_render_linklist($data)
17 $archive_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/archiveorg/archiveorg.html'); 17 $archive_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/archiveorg/archiveorg.html');
18 18
19 foreach ($data['links'] as &$value) { 19 foreach ($data['links'] as &$value) {
20 if($value['private'] && preg_match('/^\?[a-zA-Z0-9-_@]{6}($|&|#)/', $value['real_url'])) {
21 continue;
22 }
20 $archive = sprintf($archive_html, $value['url']); 23 $archive = sprintf($archive_html, $value['url']);
21 $value['link_plugin'][] = $archive; 24 $value['link_plugin'][] = $archive;
22 } 25 }
diff --git a/plugins/demo_plugin/custom_demo.css b/plugins/demo_plugin/custom_demo.css
index af5e8bf9..95019e28 100644
--- a/plugins/demo_plugin/custom_demo.css
+++ b/plugins/demo_plugin/custom_demo.css
@@ -2,10 +2,6 @@
2 color: red; 2 color: red;
3} 3}
4 4
5.upper_plugin_demo {
6 float: left;
7}
8
9#demo_marquee { 5#demo_marquee {
10 background: darkmagenta; 6 background: darkmagenta;
11 color: white; 7 color: white;
diff --git a/plugins/demo_plugin/demo_plugin.php b/plugins/demo_plugin/demo_plugin.php
index 18834e53..8fdbf663 100644
--- a/plugins/demo_plugin/demo_plugin.php
+++ b/plugins/demo_plugin/demo_plugin.php
@@ -15,6 +15,23 @@
15 */ 15 */
16 16
17/** 17/**
18 * Initialization function.
19 * It will be called when the plugin is loaded.
20 * This function can be used to return a list of initialization errors.
21 *
22 * @param $conf ConfigManager instance.
23 *
24 * @return array List of errors (optional).
25 */
26function demo_plugin_init($conf)
27{
28 $conf->get('toto', 'nope');
29
30 $errors[] = 'This a demo init error.';
31 return $errors;
32}
33
34/**
18 * Hook render_header. 35 * Hook render_header.
19 * Executed on every page redering. 36 * Executed on every page redering.
20 * 37 *
@@ -33,15 +50,68 @@ function hook_demo_plugin_render_header($data)
33 50
34 // If loggedin 51 // If loggedin
35 if ($data['_LOGGEDIN_'] === true) { 52 if ($data['_LOGGEDIN_'] === true) {
36 // Buttons in toolbar 53 /*
37 $data['buttons_toolbar'][] = '<li><a href="#">DEMO_buttons_toolbar</a></li>'; 54 * Links in toolbar:
55 * A link is an array of its attributes (key="value"),
56 * and a mandatory `html` key, which contains its value.
57 */
58 $button = array(
59 'attr' => array (
60 'href' => '#',
61 'class' => 'mybutton',
62 'title' => 'hover me',
63 ),
64 'html' => 'DEMO buttons toolbar',
65 );
66 $data['buttons_toolbar'][] = $button;
38 } 67 }
39 68
40 // Fields in toolbar 69 /*
41 $data['fields_toolbar'][] = 'DEMO_fields_toolbar'; 70 * Add additional input fields in the tools.
71 * A field is an array containing:
72 * [
73 * 'form-attribute-1' => 'form attribute 1 value',
74 * 'form-attribute-2' => 'form attribute 2 value',
75 * 'inputs' => [
76 * [
77 * 'input-1-attribute-1 => 'input 1 attribute 1 value',
78 * 'input-1-attribute-2 => 'input 1 attribute 2 value',
79 * ],
80 * [
81 * 'input-2-attribute-1 => 'input 2 attribute 1 value',
82 * ],
83 * ],
84 * ]
85 * This example renders as:
86 * <form form-attribute-1="form attribute 1 value" form-attribute-2="form attribute 2 value">
87 * <input input-1-attribute-1="input 1 attribute 1 value" input-1-attribute-2="input 1 attribute 2 value">
88 * <input input-2-attribute-1="input 2 attribute 1 value">
89 * </form>
90 */
91 $form = array(
92 'attr' => array(
93 'method' => 'GET',
94 'action' => '?',
95 'class' => 'addform',
96 ),
97 'inputs' => array(
98 array(
99 'type' => 'text',
100 'name' => 'demo',
101 'placeholder' => 'demo',
102 )
103 )
104 );
105 $data['fields_toolbar'][] = $form;
42 } 106 }
43 // Another button always displayed 107 // Another button always displayed
44 $data['buttons_toolbar'][] = '<li><a href="#">DEMO</a></li>'; 108 $button = array(
109 'attr' => array(
110 'href' => '#',
111 ),
112 'html' => 'Demo',
113 );
114 $data['buttons_toolbar'][] = $button;
45 115
46 return $data; 116 return $data;
47} 117}
@@ -126,8 +196,19 @@ function hook_demo_plugin_render_footer($data)
126 */ 196 */
127function hook_demo_plugin_render_linklist($data) 197function hook_demo_plugin_render_linklist($data)
128{ 198{
129 // action_plugin 199 /*
130 $data['action_plugin'][] = '<div class="upper_plugin_demo"><a href="?up" title="Uppercase!">←</a></div>'; 200 * Action links (action_plugin):
201 * A link is an array of its attributes (key="value"),
202 * and a mandatory `html` key, which contains its value.
203 * It's also recommended to add key 'on' or 'off' for theme rendering.
204 */
205 $action = array(
206 'attr' => array(
207 'href' => '?up',
208 'title' => 'Uppercase!',
209 ),
210 'html' => '←',
211 );
131 212
132 if (isset($_GET['up'])) { 213 if (isset($_GET['up'])) {
133 // Manipulate link data 214 // Manipulate link data
@@ -135,7 +216,11 @@ function hook_demo_plugin_render_linklist($data)
135 $value['description'] = strtoupper($value['description']); 216 $value['description'] = strtoupper($value['description']);
136 $value['title'] = strtoupper($value['title']); 217 $value['title'] = strtoupper($value['title']);
137 } 218 }
219 $action['on'] = true;
220 } else {
221 $action['off'] = true;
138 } 222 }
223 $data['action_plugin'][] = $action;
139 224
140 // link_plugin (for each link) 225 // link_plugin (for each link)
141 foreach ($data['links'] as &$value) { 226 foreach ($data['links'] as &$value) {
diff --git a/plugins/isso/isso.css b/plugins/isso/isso.css
new file mode 100644
index 00000000..b89babc3
--- /dev/null
+++ b/plugins/isso/isso.css
@@ -0,0 +1,3 @@
1#isso-thread > h4 {
2 text-align: center;
3}
diff --git a/plugins/isso/isso.html b/plugins/isso/isso.html
new file mode 100644
index 00000000..babab9b8
--- /dev/null
+++ b/plugins/isso/isso.html
@@ -0,0 +1,14 @@
1<script data-isso="//%s"
2 data-isso-css="true"
3 data-isso-lang="en"
4 data-isso-reply-to-self="true"
5 data-isso-max-comments-top="20"
6 data-isso-max-comments-nested="5"
7 data-isso-reveal-on-click="5"
8 data-isso-avatar="true"
9 data-isso-avatar-bg="#f0f0f0"
10 data-isso-avatar-fg="#9abf88 #5698c4 #e279a3 #9163b6 ..."
11 data-isso-vote="true"
12 src="//%s/js/embed.min.js">
13</script>
14<section id="isso-thread" data-isso-id="%s" data-title="%s"></section>
diff --git a/plugins/isso/isso.meta b/plugins/isso/isso.meta
new file mode 100644
index 00000000..62473abc
--- /dev/null
+++ b/plugins/isso/isso.meta
@@ -0,0 +1,3 @@
1description="Let visitor comment your shaares on permalinks with Isso."
2parameters="ISSO_SERVER"
3parameter.ISSO_SERVER="Isso server URL (without 'http://')"
diff --git a/plugins/isso/isso.php b/plugins/isso/isso.php
new file mode 100644
index 00000000..ce16645f
--- /dev/null
+++ b/plugins/isso/isso.php
@@ -0,0 +1,54 @@
1<?php
2
3/**
4 * Plugin Isso.
5 */
6
7/**
8 * Display an error everywhere if the plugin is enabled without configuration.
9 *
10 * @param $data array List of links
11 * @param $conf ConfigManager instance
12 *
13 * @return mixed - linklist data with Isso plugin.
14 */
15function isso_init($conf)
16{
17 $issoUrl = $conf->get('plugins.ISSO_SERVER');
18 if (empty($issoUrl)) {
19 $error = 'Isso plugin error: '.
20 'Please define the "ISSO_SERVER" setting in the plugin administration page.';
21 return array($error);
22 }
23}
24
25/**
26 * Render linklist hook.
27 * Will only display Isso comments on permalinks.
28 *
29 * @param $data array List of links
30 * @param $conf ConfigManager instance
31 *
32 * @return mixed - linklist data with Isso plugin.
33 */
34function hook_isso_render_linklist($data, $conf)
35{
36 $issoUrl = $conf->get('plugins.ISSO_SERVER');
37 if (empty($issoUrl)) {
38 return $data;
39 }
40
41 // Only display comments for permalinks.
42 if (count($data['links']) == 1 && empty($data['search_tags']) && empty($data['search_term'])) {
43 $link = reset($data['links']);
44 $issoHtml = file_get_contents(PluginManager::$PLUGINS_PATH . '/isso/isso.html');
45
46 $isso = sprintf($issoHtml, $issoUrl, $issoUrl, $link['id'], $link['id']);
47 $data['plugin_end_zone'][] = $isso;
48
49 // Hackish way to include this CSS file only when necessary.
50 $data['plugins_includes']['css_files'][] = PluginManager::$PLUGINS_PATH . '/isso/isso.css';
51 }
52
53 return $data;
54}
diff --git a/plugins/markdown/Parsedown.php b/plugins/markdown/Parsedown.php
deleted file mode 100644
index 91e05dcc..00000000
--- a/plugins/markdown/Parsedown.php
+++ /dev/null
@@ -1,1528 +0,0 @@
1<?php
2
3#
4#
5# Parsedown
6# http://parsedown.org
7#
8# (c) Emanuil Rusev
9# http://erusev.com
10#
11# For the full license information, view the LICENSE file that was distributed
12# with this source code.
13#
14#
15
16class Parsedown
17{
18 # ~
19
20 const version = '1.6.0';
21
22 # ~
23
24 function text($text)
25 {
26 # make sure no definitions are set
27 $this->DefinitionData = array();
28
29 # standardize line breaks
30 $text = str_replace(array("\r\n", "\r"), "\n", $text);
31
32 # remove surrounding line breaks
33 $text = trim($text, "\n");
34
35 # split text into lines
36 $lines = explode("\n", $text);
37
38 # iterate through lines to identify blocks
39 $markup = $this->lines($lines);
40
41 # trim line breaks
42 $markup = trim($markup, "\n");
43
44 return $markup;
45 }
46
47 #
48 # Setters
49 #
50
51 function setBreaksEnabled($breaksEnabled)
52 {
53 $this->breaksEnabled = $breaksEnabled;
54
55 return $this;
56 }
57
58 protected $breaksEnabled;
59
60 function setMarkupEscaped($markupEscaped)
61 {
62 $this->markupEscaped = $markupEscaped;
63
64 return $this;
65 }
66
67 protected $markupEscaped;
68
69 function setUrlsLinked($urlsLinked)
70 {
71 $this->urlsLinked = $urlsLinked;
72
73 return $this;
74 }
75
76 protected $urlsLinked = true;
77
78 #
79 # Lines
80 #
81
82 protected $BlockTypes = array(
83 '#' => array('Header'),
84 '*' => array('Rule', 'List'),
85 '+' => array('List'),
86 '-' => array('SetextHeader', 'Table', 'Rule', 'List'),
87 '0' => array('List'),
88 '1' => array('List'),
89 '2' => array('List'),
90 '3' => array('List'),
91 '4' => array('List'),
92 '5' => array('List'),
93 '6' => array('List'),
94 '7' => array('List'),
95 '8' => array('List'),
96 '9' => array('List'),
97 ':' => array('Table'),
98 '<' => array('Comment', 'Markup'),
99 '=' => array('SetextHeader'),
100 '>' => array('Quote'),
101 '[' => array('Reference'),
102 '_' => array('Rule'),
103 '`' => array('FencedCode'),
104 '|' => array('Table'),
105 '~' => array('FencedCode'),
106 );
107
108 # ~
109
110 protected $unmarkedBlockTypes = array(
111 'Code',
112 );
113
114 #
115 # Blocks
116 #
117
118 private function lines(array $lines)
119 {
120 $CurrentBlock = null;
121
122 foreach ($lines as $line)
123 {
124 if (chop($line) === '')
125 {
126 if (isset($CurrentBlock))
127 {
128 $CurrentBlock['interrupted'] = true;
129 }
130
131 continue;
132 }
133
134 if (strpos($line, "\t") !== false)
135 {
136 $parts = explode("\t", $line);
137
138 $line = $parts[0];
139
140 unset($parts[0]);
141
142 foreach ($parts as $part)
143 {
144 $shortage = 4 - mb_strlen($line, 'utf-8') % 4;
145
146 $line .= str_repeat(' ', $shortage);
147 $line .= $part;
148 }
149 }
150
151 $indent = 0;
152
153 while (isset($line[$indent]) and $line[$indent] === ' ')
154 {
155 $indent ++;
156 }
157
158 $text = $indent > 0 ? substr($line, $indent) : $line;
159
160 # ~
161
162 $Line = array('body' => $line, 'indent' => $indent, 'text' => $text);
163
164 # ~
165
166 if (isset($CurrentBlock['continuable']))
167 {
168 $Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock);
169
170 if (isset($Block))
171 {
172 $CurrentBlock = $Block;
173
174 continue;
175 }
176 else
177 {
178 if (method_exists($this, 'block'.$CurrentBlock['type'].'Complete'))
179 {
180 $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
181 }
182 }
183 }
184
185 # ~
186
187 $marker = $text[0];
188
189 # ~
190
191 $blockTypes = $this->unmarkedBlockTypes;
192
193 if (isset($this->BlockTypes[$marker]))
194 {
195 foreach ($this->BlockTypes[$marker] as $blockType)
196 {
197 $blockTypes []= $blockType;
198 }
199 }
200
201 #
202 # ~
203
204 foreach ($blockTypes as $blockType)
205 {
206 $Block = $this->{'block'.$blockType}($Line, $CurrentBlock);
207
208 if (isset($Block))
209 {
210 $Block['type'] = $blockType;
211
212 if ( ! isset($Block['identified']))
213 {
214 $Blocks []= $CurrentBlock;
215
216 $Block['identified'] = true;
217 }
218
219 if (method_exists($this, 'block'.$blockType.'Continue'))
220 {
221 $Block['continuable'] = true;
222 }
223
224 $CurrentBlock = $Block;
225
226 continue 2;
227 }
228 }
229
230 # ~
231
232 if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted']))
233 {
234 $CurrentBlock['element']['text'] .= "\n".$text;
235 }
236 else
237 {
238 $Blocks []= $CurrentBlock;
239
240 $CurrentBlock = $this->paragraph($Line);
241
242 $CurrentBlock['identified'] = true;
243 }
244 }
245
246 # ~
247
248 if (isset($CurrentBlock['continuable']) and method_exists($this, 'block'.$CurrentBlock['type'].'Complete'))
249 {
250 $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
251 }
252
253 # ~
254
255 $Blocks []= $CurrentBlock;
256
257 unset($Blocks[0]);
258
259 # ~
260
261 $markup = '';
262
263 foreach ($Blocks as $Block)
264 {
265 if (isset($Block['hidden']))
266 {
267 continue;
268 }
269
270 $markup .= "\n";
271 $markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']);
272 }
273
274 $markup .= "\n";
275
276 # ~
277
278 return $markup;
279 }
280
281 #
282 # Code
283
284 protected function blockCode($Line, $Block = null)
285 {
286 if (isset($Block) and ! isset($Block['type']) and ! isset($Block['interrupted']))
287 {
288 return;
289 }
290
291 if ($Line['indent'] >= 4)
292 {
293 $text = substr($Line['body'], 4);
294
295 $Block = array(
296 'element' => array(
297 'name' => 'pre',
298 'handler' => 'element',
299 'text' => array(
300 'name' => 'code',
301 'text' => $text,
302 ),
303 ),
304 );
305
306 return $Block;
307 }
308 }
309
310 protected function blockCodeContinue($Line, $Block)
311 {
312 if ($Line['indent'] >= 4)
313 {
314 if (isset($Block['interrupted']))
315 {
316 $Block['element']['text']['text'] .= "\n";
317
318 unset($Block['interrupted']);
319 }
320
321 $Block['element']['text']['text'] .= "\n";
322
323 $text = substr($Line['body'], 4);
324
325 $Block['element']['text']['text'] .= $text;
326
327 return $Block;
328 }
329 }
330
331 protected function blockCodeComplete($Block)
332 {
333 $text = $Block['element']['text']['text'];
334
335 $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
336
337 $Block['element']['text']['text'] = $text;
338
339 return $Block;
340 }
341
342 #
343 # Comment
344
345 protected function blockComment($Line)
346 {
347 if ($this->markupEscaped)
348 {
349 return;
350 }
351
352 if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!')
353 {
354 $Block = array(
355 'markup' => $Line['body'],
356 );
357
358 if (preg_match('/-->$/', $Line['text']))
359 {
360 $Block['closed'] = true;
361 }
362
363 return $Block;
364 }
365 }
366
367 protected function blockCommentContinue($Line, array $Block)
368 {
369 if (isset($Block['closed']))
370 {
371 return;
372 }
373
374 $Block['markup'] .= "\n" . $Line['body'];
375
376 if (preg_match('/-->$/', $Line['text']))
377 {
378 $Block['closed'] = true;
379 }
380
381 return $Block;
382 }
383
384 #
385 # Fenced Code
386
387 protected function blockFencedCode($Line)
388 {
389 if (preg_match('/^['.$Line['text'][0].']{3,}[ ]*([\w-]+)?[ ]*$/', $Line['text'], $matches))
390 {
391 $Element = array(
392 'name' => 'code',
393 'text' => '',
394 );
395
396 if (isset($matches[1]))
397 {
398 $class = 'language-'.$matches[1];
399
400 $Element['attributes'] = array(
401 'class' => $class,
402 );
403 }
404
405 $Block = array(
406 'char' => $Line['text'][0],
407 'element' => array(
408 'name' => 'pre',
409 'handler' => 'element',
410 'text' => $Element,
411 ),
412 );
413
414 return $Block;
415 }
416 }
417
418 protected function blockFencedCodeContinue($Line, $Block)
419 {
420 if (isset($Block['complete']))
421 {
422 return;
423 }
424
425 if (isset($Block['interrupted']))
426 {
427 $Block['element']['text']['text'] .= "\n";
428
429 unset($Block['interrupted']);
430 }
431
432 if (preg_match('/^'.$Block['char'].'{3,}[ ]*$/', $Line['text']))
433 {
434 $Block['element']['text']['text'] = substr($Block['element']['text']['text'], 1);
435
436 $Block['complete'] = true;
437
438 return $Block;
439 }
440
441 $Block['element']['text']['text'] .= "\n".$Line['body'];;
442
443 return $Block;
444 }
445
446 protected function blockFencedCodeComplete($Block)
447 {
448 $text = $Block['element']['text']['text'];
449
450 $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
451
452 $Block['element']['text']['text'] = $text;
453
454 return $Block;
455 }
456
457 #
458 # Header
459
460 protected function blockHeader($Line)
461 {
462 if (isset($Line['text'][1]))
463 {
464 $level = 1;
465
466 while (isset($Line['text'][$level]) and $Line['text'][$level] === '#')
467 {
468 $level ++;
469 }
470
471 if ($level > 6)
472 {
473 return;
474 }
475
476 $text = trim($Line['text'], '# ');
477
478 $Block = array(
479 'element' => array(
480 'name' => 'h' . min(6, $level),
481 'text' => $text,
482 'handler' => 'line',
483 ),
484 );
485
486 return $Block;
487 }
488 }
489
490 #
491 # List
492
493 protected function blockList($Line)
494 {
495 list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]+[.]');
496
497 if (preg_match('/^('.$pattern.'[ ]+)(.*)/', $Line['text'], $matches))
498 {
499 $Block = array(
500 'indent' => $Line['indent'],
501 'pattern' => $pattern,
502 'element' => array(
503 'name' => $name,
504 'handler' => 'elements',
505 ),
506 );
507
508 $Block['li'] = array(
509 'name' => 'li',
510 'handler' => 'li',
511 'text' => array(
512 $matches[2],
513 ),
514 );
515
516 $Block['element']['text'] []= & $Block['li'];
517
518 return $Block;
519 }
520 }
521
522 protected function blockListContinue($Line, array $Block)
523 {
524 if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'(?:[ ]+(.*)|$)/', $Line['text'], $matches))
525 {
526 if (isset($Block['interrupted']))
527 {
528 $Block['li']['text'] []= '';
529
530 unset($Block['interrupted']);
531 }
532
533 unset($Block['li']);
534
535 $text = isset($matches[1]) ? $matches[1] : '';
536
537 $Block['li'] = array(
538 'name' => 'li',
539 'handler' => 'li',
540 'text' => array(
541 $text,
542 ),
543 );
544
545 $Block['element']['text'] []= & $Block['li'];
546
547 return $Block;
548 }
549
550 if ($Line['text'][0] === '[' and $this->blockReference($Line))
551 {
552 return $Block;
553 }
554
555 if ( ! isset($Block['interrupted']))
556 {
557 $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
558
559 $Block['li']['text'] []= $text;
560
561 return $Block;
562 }
563
564 if ($Line['indent'] > 0)
565 {
566 $Block['li']['text'] []= '';
567
568 $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
569
570 $Block['li']['text'] []= $text;
571
572 unset($Block['interrupted']);
573
574 return $Block;
575 }
576 }
577
578 #
579 # Quote
580
581 protected function blockQuote($Line)
582 {
583 if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
584 {
585 $Block = array(
586 'element' => array(
587 'name' => 'blockquote',
588 'handler' => 'lines',
589 'text' => (array) $matches[1],
590 ),
591 );
592
593 return $Block;
594 }
595 }
596
597 protected function blockQuoteContinue($Line, array $Block)
598 {
599 if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
600 {
601 if (isset($Block['interrupted']))
602 {
603 $Block['element']['text'] []= '';
604
605 unset($Block['interrupted']);
606 }
607
608 $Block['element']['text'] []= $matches[1];
609
610 return $Block;
611 }
612
613 if ( ! isset($Block['interrupted']))
614 {
615 $Block['element']['text'] []= $Line['text'];
616
617 return $Block;
618 }
619 }
620
621 #
622 # Rule
623
624 protected function blockRule($Line)
625 {
626 if (preg_match('/^(['.$Line['text'][0].'])([ ]*\1){2,}[ ]*$/', $Line['text']))
627 {
628 $Block = array(
629 'element' => array(
630 'name' => 'hr'
631 ),
632 );
633
634 return $Block;
635 }
636 }
637
638 #
639 # Setext
640
641 protected function blockSetextHeader($Line, array $Block = null)
642 {
643 if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
644 {
645 return;
646 }
647
648 if (chop($Line['text'], $Line['text'][0]) === '')
649 {
650 $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2';
651
652 return $Block;
653 }
654 }
655
656 #
657 # Markup
658
659 protected function blockMarkup($Line)
660 {
661 if ($this->markupEscaped)
662 {
663 return;
664 }
665
666 if (preg_match('/^<(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches))
667 {
668 $element = strtolower($matches[1]);
669
670 if (in_array($element, $this->textLevelElements))
671 {
672 return;
673 }
674
675 $Block = array(
676 'name' => $matches[1],
677 'depth' => 0,
678 'markup' => $Line['text'],
679 );
680
681 $length = strlen($matches[0]);
682
683 $remainder = substr($Line['text'], $length);
684
685 if (trim($remainder) === '')
686 {
687 if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
688 {
689 $Block['closed'] = true;
690
691 $Block['void'] = true;
692 }
693 }
694 else
695 {
696 if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
697 {
698 return;
699 }
700
701 if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder))
702 {
703 $Block['closed'] = true;
704 }
705 }
706
707 return $Block;
708 }
709 }
710
711 protected function blockMarkupContinue($Line, array $Block)
712 {
713 if (isset($Block['closed']))
714 {
715 return;
716 }
717
718 if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open
719 {
720 $Block['depth'] ++;
721 }
722
723 if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close
724 {
725 if ($Block['depth'] > 0)
726 {
727 $Block['depth'] --;
728 }
729 else
730 {
731 $Block['closed'] = true;
732 }
733 }
734
735 if (isset($Block['interrupted']))
736 {
737 $Block['markup'] .= "\n";
738
739 unset($Block['interrupted']);
740 }
741
742 $Block['markup'] .= "\n".$Line['body'];
743
744 return $Block;
745 }
746
747 #
748 # Reference
749
750 protected function blockReference($Line)
751 {
752 if (preg_match('/^\[(.+?)\]:[ ]*<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches))
753 {
754 $id = strtolower($matches[1]);
755
756 $Data = array(
757 'url' => $matches[2],
758 'title' => null,
759 );
760
761 if (isset($matches[3]))
762 {
763 $Data['title'] = $matches[3];
764 }
765
766 $this->DefinitionData['Reference'][$id] = $Data;
767
768 $Block = array(
769 'hidden' => true,
770 );
771
772 return $Block;
773 }
774 }
775
776 #
777 # Table
778
779 protected function blockTable($Line, array $Block = null)
780 {
781 if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
782 {
783 return;
784 }
785
786 if (strpos($Block['element']['text'], '|') !== false and chop($Line['text'], ' -:|') === '')
787 {
788 $alignments = array();
789
790 $divider = $Line['text'];
791
792 $divider = trim($divider);
793 $divider = trim($divider, '|');
794
795 $dividerCells = explode('|', $divider);
796
797 foreach ($dividerCells as $dividerCell)
798 {
799 $dividerCell = trim($dividerCell);
800
801 if ($dividerCell === '')
802 {
803 continue;
804 }
805
806 $alignment = null;
807
808 if ($dividerCell[0] === ':')
809 {
810 $alignment = 'left';
811 }
812
813 if (substr($dividerCell, - 1) === ':')
814 {
815 $alignment = $alignment === 'left' ? 'center' : 'right';
816 }
817
818 $alignments []= $alignment;
819 }
820
821 # ~
822
823 $HeaderElements = array();
824
825 $header = $Block['element']['text'];
826
827 $header = trim($header);
828 $header = trim($header, '|');
829
830 $headerCells = explode('|', $header);
831
832 foreach ($headerCells as $index => $headerCell)
833 {
834 $headerCell = trim($headerCell);
835
836 $HeaderElement = array(
837 'name' => 'th',
838 'text' => $headerCell,
839 'handler' => 'line',
840 );
841
842 if (isset($alignments[$index]))
843 {
844 $alignment = $alignments[$index];
845
846 $HeaderElement['attributes'] = array(
847 'style' => 'text-align: '.$alignment.';',
848 );
849 }
850
851 $HeaderElements []= $HeaderElement;
852 }
853
854 # ~
855
856 $Block = array(
857 'alignments' => $alignments,
858 'identified' => true,
859 'element' => array(
860 'name' => 'table',
861 'handler' => 'elements',
862 ),
863 );
864
865 $Block['element']['text'] []= array(
866 'name' => 'thead',
867 'handler' => 'elements',
868 );
869
870 $Block['element']['text'] []= array(
871 'name' => 'tbody',
872 'handler' => 'elements',
873 'text' => array(),
874 );
875
876 $Block['element']['text'][0]['text'] []= array(
877 'name' => 'tr',
878 'handler' => 'elements',
879 'text' => $HeaderElements,
880 );
881
882 return $Block;
883 }
884 }
885
886 protected function blockTableContinue($Line, array $Block)
887 {
888 if (isset($Block['interrupted']))
889 {
890 return;
891 }
892
893 if ($Line['text'][0] === '|' or strpos($Line['text'], '|'))
894 {
895 $Elements = array();
896
897 $row = $Line['text'];
898
899 $row = trim($row);
900 $row = trim($row, '|');
901
902 preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches);
903
904 foreach ($matches[0] as $index => $cell)
905 {
906 $cell = trim($cell);
907
908 $Element = array(
909 'name' => 'td',
910 'handler' => 'line',
911 'text' => $cell,
912 );
913
914 if (isset($Block['alignments'][$index]))
915 {
916 $Element['attributes'] = array(
917 'style' => 'text-align: '.$Block['alignments'][$index].';',
918 );
919 }
920
921 $Elements []= $Element;
922 }
923
924 $Element = array(
925 'name' => 'tr',
926 'handler' => 'elements',
927 'text' => $Elements,
928 );
929
930 $Block['element']['text'][1]['text'] []= $Element;
931
932 return $Block;
933 }
934 }
935
936 #
937 # ~
938 #
939
940 protected function paragraph($Line)
941 {
942 $Block = array(
943 'element' => array(
944 'name' => 'p',
945 'text' => $Line['text'],
946 'handler' => 'line',
947 ),
948 );
949
950 return $Block;
951 }
952
953 #
954 # Inline Elements
955 #
956
957 protected $InlineTypes = array(
958 '"' => array('SpecialCharacter'),
959 '!' => array('Image'),
960 '&' => array('SpecialCharacter'),
961 '*' => array('Emphasis'),
962 ':' => array('Url'),
963 '<' => array('UrlTag', 'EmailTag', 'Markup', 'SpecialCharacter'),
964 '>' => array('SpecialCharacter'),
965 '[' => array('Link'),
966 '_' => array('Emphasis'),
967 '`' => array('Code'),
968 '~' => array('Strikethrough'),
969 '\\' => array('EscapeSequence'),
970 );
971
972 # ~
973
974 protected $inlineMarkerList = '!"*_&[:<>`~\\';
975
976 #
977 # ~
978 #
979
980 public function line($text)
981 {
982 $markup = '';
983
984 # $excerpt is based on the first occurrence of a marker
985
986 while ($excerpt = strpbrk($text, $this->inlineMarkerList))
987 {
988 $marker = $excerpt[0];
989
990 $markerPosition = strpos($text, $marker);
991
992 $Excerpt = array('text' => $excerpt, 'context' => $text);
993
994 foreach ($this->InlineTypes[$marker] as $inlineType)
995 {
996 $Inline = $this->{'inline'.$inlineType}($Excerpt);
997
998 if ( ! isset($Inline))
999 {
1000 continue;
1001 }
1002
1003 # makes sure that the inline belongs to "our" marker
1004
1005 if (isset($Inline['position']) and $Inline['position'] > $markerPosition)
1006 {
1007 continue;
1008 }
1009
1010 # sets a default inline position
1011
1012 if ( ! isset($Inline['position']))
1013 {
1014 $Inline['position'] = $markerPosition;
1015 }
1016
1017 # the text that comes before the inline
1018 $unmarkedText = substr($text, 0, $Inline['position']);
1019
1020 # compile the unmarked text
1021 $markup .= $this->unmarkedText($unmarkedText);
1022
1023 # compile the inline
1024 $markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']);
1025
1026 # remove the examined text
1027 $text = substr($text, $Inline['position'] + $Inline['extent']);
1028
1029 continue 2;
1030 }
1031
1032 # the marker does not belong to an inline
1033
1034 $unmarkedText = substr($text, 0, $markerPosition + 1);
1035
1036 $markup .= $this->unmarkedText($unmarkedText);
1037
1038 $text = substr($text, $markerPosition + 1);
1039 }
1040
1041 $markup .= $this->unmarkedText($text);
1042
1043 return $markup;
1044 }
1045
1046 #
1047 # ~
1048 #
1049
1050 protected function inlineCode($Excerpt)
1051 {
1052 $marker = $Excerpt['text'][0];
1053
1054 if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(?<!'.$marker.')\1(?!'.$marker.')/s', $Excerpt['text'], $matches))
1055 {
1056 $text = $matches[2];
1057 $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
1058 $text = preg_replace("/[ ]*\n/", ' ', $text);
1059
1060 return array(
1061 'extent' => strlen($matches[0]),
1062 'element' => array(
1063 'name' => 'code',
1064 'text' => $text,
1065 ),
1066 );
1067 }
1068 }
1069
1070 protected function inlineEmailTag($Excerpt)
1071 {
1072 if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches))
1073 {
1074 $url = $matches[1];
1075
1076 if ( ! isset($matches[2]))
1077 {
1078 $url = 'mailto:' . $url;
1079 }
1080
1081 return array(
1082 'extent' => strlen($matches[0]),
1083 'element' => array(
1084 'name' => 'a',
1085 'text' => $matches[1],
1086 'attributes' => array(
1087 'href' => $url,
1088 ),
1089 ),
1090 );
1091 }
1092 }
1093
1094 protected function inlineEmphasis($Excerpt)
1095 {
1096 if ( ! isset($Excerpt['text'][1]))
1097 {
1098 return;
1099 }
1100
1101 $marker = $Excerpt['text'][0];
1102
1103 if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches))
1104 {
1105 $emphasis = 'strong';
1106 }
1107 elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches))
1108 {
1109 $emphasis = 'em';
1110 }
1111 else
1112 {
1113 return;
1114 }
1115
1116 return array(
1117 'extent' => strlen($matches[0]),
1118 'element' => array(
1119 'name' => $emphasis,
1120 'handler' => 'line',
1121 'text' => $matches[1],
1122 ),
1123 );
1124 }
1125
1126 protected function inlineEscapeSequence($Excerpt)
1127 {
1128 if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters))
1129 {
1130 return array(
1131 'markup' => $Excerpt['text'][1],
1132 'extent' => 2,
1133 );
1134 }
1135 }
1136
1137 protected function inlineImage($Excerpt)
1138 {
1139 if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[')
1140 {
1141 return;
1142 }
1143
1144 $Excerpt['text']= substr($Excerpt['text'], 1);
1145
1146 $Link = $this->inlineLink($Excerpt);
1147
1148 if ($Link === null)
1149 {
1150 return;
1151 }
1152
1153 $Inline = array(
1154 'extent' => $Link['extent'] + 1,
1155 'element' => array(
1156 'name' => 'img',
1157 'attributes' => array(
1158 'src' => $Link['element']['attributes']['href'],
1159 'alt' => $Link['element']['text'],
1160 ),
1161 ),
1162 );
1163
1164 $Inline['element']['attributes'] += $Link['element']['attributes'];
1165
1166 unset($Inline['element']['attributes']['href']);
1167
1168 return $Inline;
1169 }
1170
1171 protected function inlineLink($Excerpt)
1172 {
1173 $Element = array(
1174 'name' => 'a',
1175 'handler' => 'line',
1176 'text' => null,
1177 'attributes' => array(
1178 'href' => null,
1179 'title' => null,
1180 ),
1181 );
1182
1183 $extent = 0;
1184
1185 $remainder = $Excerpt['text'];
1186
1187 if (preg_match('/\[((?:[^][]|(?R))*)\]/', $remainder, $matches))
1188 {
1189 $Element['text'] = $matches[1];
1190
1191 $extent += strlen($matches[0]);
1192
1193 $remainder = substr($remainder, $extent);
1194 }
1195 else
1196 {
1197 return;
1198 }
1199
1200 if (preg_match('/^[(]((?:[^ ()]|[(][^ )]+[)])+)(?:[ ]+("[^"]*"|\'[^\']*\'))?[)]/', $remainder, $matches))
1201 {
1202 $Element['attributes']['href'] = $matches[1];
1203
1204 if (isset($matches[2]))
1205 {
1206 $Element['attributes']['title'] = substr($matches[2], 1, - 1);
1207 }
1208
1209 $extent += strlen($matches[0]);
1210 }
1211 else
1212 {
1213 if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches))
1214 {
1215 $definition = strlen($matches[1]) ? $matches[1] : $Element['text'];
1216 $definition = strtolower($definition);
1217
1218 $extent += strlen($matches[0]);
1219 }
1220 else
1221 {
1222 $definition = strtolower($Element['text']);
1223 }
1224
1225 if ( ! isset($this->DefinitionData['Reference'][$definition]))
1226 {
1227 return;
1228 }
1229
1230 $Definition = $this->DefinitionData['Reference'][$definition];
1231
1232 $Element['attributes']['href'] = $Definition['url'];
1233 $Element['attributes']['title'] = $Definition['title'];
1234 }
1235
1236 $Element['attributes']['href'] = str_replace(array('&', '<'), array('&amp;', '&lt;'), $Element['attributes']['href']);
1237
1238 return array(
1239 'extent' => $extent,
1240 'element' => $Element,
1241 );
1242 }
1243
1244 protected function inlineMarkup($Excerpt)
1245 {
1246 if ($this->markupEscaped or strpos($Excerpt['text'], '>') === false)
1247 {
1248 return;
1249 }
1250
1251 if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w*[ ]*>/s', $Excerpt['text'], $matches))
1252 {
1253 return array(
1254 'markup' => $matches[0],
1255 'extent' => strlen($matches[0]),
1256 );
1257 }
1258
1259 if ($Excerpt['text'][1] === '!' and preg_match('/^<!---?[^>-](?:-?[^-])*-->/s', $Excerpt['text'], $matches))
1260 {
1261 return array(
1262 'markup' => $matches[0],
1263 'extent' => strlen($matches[0]),
1264 );
1265 }
1266
1267 if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches))
1268 {
1269 return array(
1270 'markup' => $matches[0],
1271 'extent' => strlen($matches[0]),
1272 );
1273 }
1274 }
1275
1276 protected function inlineSpecialCharacter($Excerpt)
1277 {
1278 if ($Excerpt['text'][0] === '&' and ! preg_match('/^&#?\w+;/', $Excerpt['text']))
1279 {
1280 return array(
1281 'markup' => '&amp;',
1282 'extent' => 1,
1283 );
1284 }
1285
1286 $SpecialCharacter = array('>' => 'gt', '<' => 'lt', '"' => 'quot');
1287
1288 if (isset($SpecialCharacter[$Excerpt['text'][0]]))
1289 {
1290 return array(
1291 'markup' => '&'.$SpecialCharacter[$Excerpt['text'][0]].';',
1292 'extent' => 1,
1293 );
1294 }
1295 }
1296
1297 protected function inlineStrikethrough($Excerpt)
1298 {
1299 if ( ! isset($Excerpt['text'][1]))
1300 {
1301 return;
1302 }
1303
1304 if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches))
1305 {
1306 return array(
1307 'extent' => strlen($matches[0]),
1308 'element' => array(
1309 'name' => 'del',
1310 'text' => $matches[1],
1311 'handler' => 'line',
1312 ),
1313 );
1314 }
1315 }
1316
1317 protected function inlineUrl($Excerpt)
1318 {
1319 if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/')
1320 {
1321 return;
1322 }
1323
1324 if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE))
1325 {
1326 $Inline = array(
1327 'extent' => strlen($matches[0][0]),
1328 'position' => $matches[0][1],
1329 'element' => array(
1330 'name' => 'a',
1331 'text' => $matches[0][0],
1332 'attributes' => array(
1333 'href' => $matches[0][0],
1334 ),
1335 ),
1336 );
1337
1338 return $Inline;
1339 }
1340 }
1341
1342 protected function inlineUrlTag($Excerpt)
1343 {
1344 if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches))
1345 {
1346 $url = str_replace(array('&', '<'), array('&amp;', '&lt;'), $matches[1]);
1347
1348 return array(
1349 'extent' => strlen($matches[0]),
1350 'element' => array(
1351 'name' => 'a',
1352 'text' => $url,
1353 'attributes' => array(
1354 'href' => $url,
1355 ),
1356 ),
1357 );
1358 }
1359 }
1360
1361 # ~
1362
1363 protected function unmarkedText($text)
1364 {
1365 if ($this->breaksEnabled)
1366 {
1367 $text = preg_replace('/[ ]*\n/', "<br />\n", $text);
1368 }
1369 else
1370 {
1371 $text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', "<br />\n", $text);
1372 $text = str_replace(" \n", "\n", $text);
1373 }
1374
1375 return $text;
1376 }
1377
1378 #
1379 # Handlers
1380 #
1381
1382 protected function element(array $Element)
1383 {
1384 $markup = '<'.$Element['name'];
1385
1386 if (isset($Element['attributes']))
1387 {
1388 foreach ($Element['attributes'] as $name => $value)
1389 {
1390 if ($value === null)
1391 {
1392 continue;
1393 }
1394
1395 $markup .= ' '.$name.'="'.$value.'"';
1396 }
1397 }
1398
1399 if (isset($Element['text']))
1400 {
1401 $markup .= '>';
1402
1403 if (isset($Element['handler']))
1404 {
1405 $markup .= $this->{$Element['handler']}($Element['text']);
1406 }
1407 else
1408 {
1409 $markup .= $Element['text'];
1410 }
1411
1412 $markup .= '</'.$Element['name'].'>';
1413 }
1414 else
1415 {
1416 $markup .= ' />';
1417 }
1418
1419 return $markup;
1420 }
1421
1422 protected function elements(array $Elements)
1423 {
1424 $markup = '';
1425
1426 foreach ($Elements as $Element)
1427 {
1428 $markup .= "\n" . $this->element($Element);
1429 }
1430
1431 $markup .= "\n";
1432
1433 return $markup;
1434 }
1435
1436 # ~
1437
1438 protected function li($lines)
1439 {
1440 $markup = $this->lines($lines);
1441
1442 $trimmedMarkup = trim($markup);
1443
1444 if ( ! in_array('', $lines) and substr($trimmedMarkup, 0, 3) === '<p>')
1445 {
1446 $markup = $trimmedMarkup;
1447 $markup = substr($markup, 3);
1448
1449 $position = strpos($markup, "</p>");
1450
1451 $markup = substr_replace($markup, '', $position, 4);
1452 }
1453
1454 return $markup;
1455 }
1456
1457 #
1458 # Deprecated Methods
1459 #
1460
1461 function parse($text)
1462 {
1463 $markup = $this->text($text);
1464
1465 return $markup;
1466 }
1467
1468 #
1469 # Static Methods
1470 #
1471
1472 static function instance($name = 'default')
1473 {
1474 if (isset(self::$instances[$name]))
1475 {
1476 return self::$instances[$name];
1477 }
1478
1479 $instance = new static();
1480
1481 self::$instances[$name] = $instance;
1482
1483 return $instance;
1484 }
1485
1486 private static $instances = array();
1487
1488 #
1489 # Fields
1490 #
1491
1492 protected $DefinitionData;
1493
1494 #
1495 # Read-Only
1496
1497 protected $specialCharacters = array(
1498 '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|',
1499 );
1500
1501 protected $StrongRegex = array(
1502 '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s',
1503 '_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us',
1504 );
1505
1506 protected $EmRegex = array(
1507 '*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s',
1508 '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us',
1509 );
1510
1511 protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?';
1512
1513 protected $voidElements = array(
1514 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source',
1515 );
1516
1517 protected $textLevelElements = array(
1518 'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont',
1519 'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing',
1520 'i', 'rp', 'del', 'code', 'strike', 'marquee',
1521 'q', 'rt', 'ins', 'font', 'strong',
1522 's', 'tt', 'sub', 'mark',
1523 'u', 'xm', 'sup', 'nobr',
1524 'var', 'ruby',
1525 'wbr', 'span',
1526 'time',
1527 );
1528} \ No newline at end of file
diff --git a/plugins/markdown/README.md b/plugins/markdown/README.md
index 4f021871..bc9427e2 100644
--- a/plugins/markdown/README.md
+++ b/plugins/markdown/README.md
@@ -20,26 +20,65 @@ The directory structure should look like:
20 |--- markdown.css 20 |--- markdown.css
21 |--- markdown.meta 21 |--- markdown.meta
22 |--- markdown.php 22 |--- markdown.php
23 |--- Parsedown.php
24 |--- README.md 23 |--- README.md
25``` 24```
26 25
27To enable the plugin, just check it in the plugin administration page. 26To enable the plugin, just check it in the plugin administration page.
28 27
29You can also add `markdown` to your list of enabled plugins in `data/config.php` 28You can also add `markdown` to your list of enabled plugins in `data/config.json.php`
30(`ENABLED_PLUGINS` array). 29(`general.enabled_plugins` list).
31 30
32This should look like: 31This should look like:
33 32
34``` 33```
35$GLOBALS['config']['ENABLED_PLUGINS'] = array('qrcode', 'any_other_plugin', 'markdown') 34"general": {
35 "enabled_plugins": [
36 "markdown",
37 [...]
38 ],
39}
36``` 40```
37 41
42Parsedown parsing library is imported using Composer. If you installed Shaarli using `git`,
43or the `master` branch, run
44
45 composer update --no-dev --prefer-dist
46
38### No Markdown tag 47### No Markdown tag
39 48
40If the tag `.nomarkdown` is set for a shaare, it won't be converted to Markdown syntax. 49If the tag `nomarkdown` is set for a shaare, it won't be converted to Markdown syntax.
41 50
42> Note: it's a private tag (leading dot), so it won't be displayed to visitors. 51> Note: this is a special tag, so it won't be displayed in link list.
52
53### HTML escape
54
55By default, HTML tags are escaped. You can enable HTML tags rendering
56by setting `security.markdwon_escape` to `false` in `data/config.json.php`:
57
58```json
59{
60 "security": {
61 "markdown_escape": false
62 }
63}
64```
65
66With this setting, Markdown support HTML tags. For example:
67
68 > <strong>strong</strong><strike>strike</strike>
69
70Will render as:
71
72> <strong>strong</strong><strike>strike</strike>
73
74
75**Warning:**
76
77 * This setting might present **security risks** (XSS) on shared instances, even though tags
78 such as script, iframe, etc should be disabled.
79 * If you want to shaare HTML code, it is necessary to use inline code or code blocks.
80 * If your shaared descriptions contained HTML tags before enabling the markdown plugin,
81enabling it might break your page.
43 82
44### Known issue 83### Known issue
45 84
diff --git a/plugins/markdown/markdown.meta b/plugins/markdown/markdown.meta
index e3904ed8..8df2ed0b 100644
--- a/plugins/markdown/markdown.meta
+++ b/plugins/markdown/markdown.meta
@@ -1 +1,4 @@
1description="Render shaare description with Markdown syntax." 1description="Render shaare description with Markdown syntax.<br><strong>Warning</strong>:
2If your shaared descriptions containing HTML tags before enabling the markdown plugin,
3enabling it might break your page.
4See the <a href=\"https://github.com/shaarli/Shaarli/tree/master/plugins/markdown#html-rendering\">README</a>."
diff --git a/plugins/markdown/markdown.php b/plugins/markdown/markdown.php
index 57fcce32..de7c823d 100644
--- a/plugins/markdown/markdown.php
+++ b/plugins/markdown/markdown.php
@@ -6,30 +6,28 @@
6 * Shaare's descriptions are parsed with Markdown. 6 * Shaare's descriptions are parsed with Markdown.
7 */ 7 */
8 8
9require_once 'Parsedown.php';
10
11/* 9/*
12 * If this tag is used on a shaare, the description won't be processed by Parsedown. 10 * If this tag is used on a shaare, the description won't be processed by Parsedown.
13 * Using a private tag so it won't appear for visitors.
14 */ 11 */
15define('NO_MD_TAG', '.nomarkdown'); 12define('NO_MD_TAG', 'nomarkdown');
16 13
17/** 14/**
18 * Parse linklist descriptions. 15 * Parse linklist descriptions.
19 * 16 *
20 * @param array $data linklist data. 17 * @param array $data linklist data.
18 * @param ConfigManager $conf instance.
21 * 19 *
22 * @return mixed linklist data parsed in markdown (and converted to HTML). 20 * @return mixed linklist data parsed in markdown (and converted to HTML).
23 */ 21 */
24function hook_markdown_render_linklist($data) 22function hook_markdown_render_linklist($data, $conf)
25{ 23{
26 foreach ($data['links'] as &$value) { 24 foreach ($data['links'] as &$value) {
27 if (!empty($value['tags']) && noMarkdownTag($value['tags'])) { 25 if (!empty($value['tags']) && noMarkdownTag($value['tags'])) {
26 $value = stripNoMarkdownTag($value);
28 continue; 27 continue;
29 } 28 }
30 $value['description'] = process_markdown($value['description']); 29 $value['description'] = process_markdown($value['description'], $conf->get('security.markdown_escape', true));
31 } 30 }
32
33 return $data; 31 return $data;
34} 32}
35 33
@@ -37,16 +35,18 @@ function hook_markdown_render_linklist($data)
37 * Parse feed linklist descriptions. 35 * Parse feed linklist descriptions.
38 * 36 *
39 * @param array $data linklist data. 37 * @param array $data linklist data.
38 * @param ConfigManager $conf instance.
40 * 39 *
41 * @return mixed linklist data parsed in markdown (and converted to HTML). 40 * @return mixed linklist data parsed in markdown (and converted to HTML).
42 */ 41 */
43function hook_markdown_render_feed($data) 42function hook_markdown_render_feed($data, $conf)
44{ 43{
45 foreach ($data['links'] as &$value) { 44 foreach ($data['links'] as &$value) {
46 if (!empty($value['tags']) && noMarkdownTag($value['tags'])) { 45 if (!empty($value['tags']) && noMarkdownTag($value['tags'])) {
46 $value = stripNoMarkdownTag($value);
47 continue; 47 continue;
48 } 48 }
49 $value['description'] = process_markdown($value['description']); 49 $value['description'] = process_markdown($value['description'], $conf->get('security.markdown_escape', true));
50 } 50 }
51 51
52 return $data; 52 return $data;
@@ -55,19 +55,24 @@ function hook_markdown_render_feed($data)
55/** 55/**
56 * Parse daily descriptions. 56 * Parse daily descriptions.
57 * 57 *
58 * @param array $data daily data. 58 * @param array $data daily data.
59 * @param ConfigManager $conf instance.
59 * 60 *
60 * @return mixed daily data parsed in markdown (and converted to HTML). 61 * @return mixed daily data parsed in markdown (and converted to HTML).
61 */ 62 */
62function hook_markdown_render_daily($data) 63function hook_markdown_render_daily($data, $conf)
63{ 64{
64 // Manipulate columns data 65 // Manipulate columns data
65 foreach ($data['cols'] as &$value) { 66 foreach ($data['cols'] as &$value) {
66 foreach ($value as &$value2) { 67 foreach ($value as &$value2) {
67 if (!empty($value2['tags']) && noMarkdownTag($value2['tags'])) { 68 if (!empty($value2['tags']) && noMarkdownTag($value2['tags'])) {
69 $value2 = stripNoMarkdownTag($value2);
68 continue; 70 continue;
69 } 71 }
70 $value2['formatedDescription'] = process_markdown($value2['formatedDescription']); 72 $value2['formatedDescription'] = process_markdown(
73 $value2['formatedDescription'],
74 $conf->get('security.markdown_escape', true)
75 );
71 } 76 }
72 } 77 }
73 78
@@ -83,7 +88,30 @@ function hook_markdown_render_daily($data)
83 */ 88 */
84function noMarkdownTag($tags) 89function noMarkdownTag($tags)
85{ 90{
86 return strpos($tags, NO_MD_TAG) !== false; 91 return preg_match('/(^|\s)'. NO_MD_TAG .'(\s|$)/', $tags);
92}
93
94/**
95 * Remove the no-markdown meta tag so it won't be displayed.
96 *
97 * @param array $link Link data.
98 *
99 * @return array Updated link without no markdown tag.
100 */
101function stripNoMarkdownTag($link)
102{
103 if (! empty($link['taglist'])) {
104 $offset = array_search(NO_MD_TAG, $link['taglist']);
105 if ($offset !== false) {
106 unset($link['taglist'][$offset]);
107 }
108 }
109
110 if (!empty($link['tags'])) {
111 str_replace(NO_MD_TAG, '', $link['tags']);
112 }
113
114 return $link;
87} 115}
88 116
89/** 117/**
@@ -138,7 +166,45 @@ function hook_markdown_render_editlink($data)
138 */ 166 */
139function reverse_text2clickable($description) 167function reverse_text2clickable($description)
140{ 168{
141 return preg_replace('!<a +href="([^ ]*)">[^ ]+</a>!m', '$1', $description); 169 $descriptionLines = explode(PHP_EOL, $description);
170 $descriptionOut = '';
171 $codeBlockOn = false;
172 $lineCount = 0;
173
174 foreach ($descriptionLines as $descriptionLine) {
175 // Detect line of code: starting with 4 spaces,
176 // except lists which can start with +/*/- or `2.` after spaces.
177 $codeLineOn = preg_match('/^ +(?=[^\+\*\-])(?=(?!\d\.).)/', $descriptionLine) > 0;
178 // Detect and toggle block of code
179 if (!$codeBlockOn) {
180 $codeBlockOn = preg_match('/^```/', $descriptionLine) > 0;
181 }
182 elseif (preg_match('/^```/', $descriptionLine) > 0) {
183 $codeBlockOn = false;
184 }
185
186 $hashtagTitle = ' title="Hashtag [^"]+"';
187 // Reverse `inline code` hashtags.
188 $descriptionLine = preg_replace(
189 '!(`[^`\n]*)<a href="[^ ]*"'. $hashtagTitle .'>([^<]+)</a>([^`\n]*`)!m',
190 '$1$2$3',
191 $descriptionLine
192 );
193
194 // Reverse all links in code blocks, only non hashtag elsewhere.
195 $hashtagFilter = (!$codeBlockOn && !$codeLineOn) ? '(?!'. $hashtagTitle .')': '(?:'. $hashtagTitle .')?';
196 $descriptionLine = preg_replace(
197 '#<a href="[^ ]*"'. $hashtagFilter .'>([^<]+)</a>#m',
198 '$1',
199 $descriptionLine
200 );
201
202 $descriptionOut .= $descriptionLine;
203 if ($lineCount++ < count($descriptionLines) - 1) {
204 $descriptionOut .= PHP_EOL;
205 }
206 }
207 return $descriptionOut;
142} 208}
143 209
144/** 210/**
@@ -190,7 +256,7 @@ function sanitize_html($description)
190 $description); 256 $description);
191 } 257 }
192 $description = preg_replace( 258 $description = preg_replace(
193 '#(<[^>]+)on[a-z]*="[^"]*"#is', 259 '#(<[^>]+)on[a-z]*="?[^ "]*"?#is',
194 '$1', 260 '$1',
195 $description); 261 $description);
196 return $description; 262 return $description;
@@ -205,20 +271,21 @@ function sanitize_html($description)
205 * 5. Wrap description in 'markdown' CSS class. 271 * 5. Wrap description in 'markdown' CSS class.
206 * 272 *
207 * @param string $description input description text. 273 * @param string $description input description text.
274 * @param bool $escape escape HTML entities
208 * 275 *
209 * @return string HTML processed $description. 276 * @return string HTML processed $description.
210 */ 277 */
211function process_markdown($description) 278function process_markdown($description, $escape = true)
212{ 279{
213 $parsedown = new Parsedown(); 280 $parsedown = new Parsedown();
214 281
215 $processedDescription = $description; 282 $processedDescription = $description;
216 $processedDescription = reverse_text2clickable($processedDescription);
217 $processedDescription = reverse_nl2br($processedDescription); 283 $processedDescription = reverse_nl2br($processedDescription);
218 $processedDescription = reverse_space2nbsp($processedDescription); 284 $processedDescription = reverse_space2nbsp($processedDescription);
285 $processedDescription = reverse_text2clickable($processedDescription);
219 $processedDescription = unescape($processedDescription); 286 $processedDescription = unescape($processedDescription);
220 $processedDescription = $parsedown 287 $processedDescription = $parsedown
221 ->setMarkupEscaped(false) 288 ->setMarkupEscaped($escape)
222 ->setBreaksEnabled(true) 289 ->setBreaksEnabled(true)
223 ->text($processedDescription); 290 ->text($processedDescription);
224 $processedDescription = sanitize_html($processedDescription); 291 $processedDescription = sanitize_html($processedDescription);
diff --git a/plugins/piwik/piwik.meta b/plugins/piwik/piwik.meta
new file mode 100644
index 00000000..20b9628e
--- /dev/null
+++ b/plugins/piwik/piwik.meta
@@ -0,0 +1,4 @@
1description="A plugin that adds Piwik tracking code to Shaarli pages."
2parameters="PIWIK_URL;PIWIK_SITEID"
3parameter.PIWIK_URL="Piwik URL"
4parameter.PIWIK_SITEID="Piwik site ID"
diff --git a/plugins/piwik/piwik.php b/plugins/piwik/piwik.php
new file mode 100644
index 00000000..7c44909c
--- /dev/null
+++ b/plugins/piwik/piwik.php
@@ -0,0 +1,71 @@
1<?php
2/**
3 * Piwik plugin.
4 * Adds tracking code on each page.
5 */
6
7/**
8 * Initialization function.
9 * It will be called when the plugin is loaded.
10 * This function can be used to return a list of initialization errors.
11 *
12 * @param $conf ConfigManager instance.
13 *
14 * @return array List of errors (optional).
15 */
16function piwik_init($conf)
17{
18 $piwikUrl = $conf->get('plugins.PIWIK_URL');
19 $piwikSiteid = $conf->get('plugins.PIWIK_SITEID');
20 if (empty($piwikUrl) || empty($piwikSiteid)) {
21 $error = 'Piwik plugin error: ' .
22 'Please define PIWIK_URL and PIWIK_SITEID in the plugin administration page.';
23 return array($error);
24 }
25}
26
27/**
28 * Hook render_footer.
29 * Executed on every page redering.
30 *
31 * Template placeholders:
32 * - text
33 * - endofpage
34 * - js_files
35 *
36 * Data:
37 * - _PAGE_: current page
38 * - _LOGGEDIN_: true/false
39 *
40 * @param array $data data passed to plugin
41 *
42 * @return array altered $data.
43 */
44function hook_piwik_render_footer($data, $conf)
45{
46 $piwikUrl = $conf->get('plugins.PIWIK_URL');
47 $piwikSiteid = $conf->get('plugins.PIWIK_SITEID');
48 if (empty($piwikUrl) || empty($piwikSiteid)) {
49 return $data;
50 }
51
52 // Free elements at the end of the page.
53 $data['endofpage'][] = '<!-- Piwik -->' .
54'<script type="text/javascript">' .
55' var _paq = _paq || [];' .
56' _paq.push([\'trackPageView\']);' .
57' _paq.push([\'enableLinkTracking\']);' .
58' (function() {' .
59' var u="//' . $piwikUrl . '/";' .
60' _paq.push([\'setTrackerUrl\', u+\'piwik.php\']);' .
61' _paq.push([\'setSiteId\', \'' . $piwikSiteid . '\']);' .
62' var d=document, g=d.createElement(\'script\'), s=d.getElementsByTagName(\'script\')[0];' .
63' g.type=\'text/javascript\'; g.async=true; g.defer=true; g.src=u+\'piwik.js\'; s.parentNode.insertBefore(g,s);' .
64' })();' .
65'</script>' .
66'<noscript><p><img src="//' . $piwikUrl . '/piwik.php?idsite=' . $piwikSiteid . '" style="border:0;" alt="" /></p></noscript>' .
67'<!-- End Piwik Code -->';
68
69 return $data;
70}
71
diff --git a/plugins/playvideos/playvideos.html b/plugins/playvideos/playvideos.html
deleted file mode 100644
index fda3d54f..00000000
--- a/plugins/playvideos/playvideos.html
+++ /dev/null
@@ -1 +0,0 @@
1<a href="#" id="playvideos">► Play Videos</a> \ No newline at end of file
diff --git a/plugins/playvideos/playvideos.php b/plugins/playvideos/playvideos.php
index 0a80aa58..64484504 100644
--- a/plugins/playvideos/playvideos.php
+++ b/plugins/playvideos/playvideos.php
@@ -16,7 +16,15 @@
16function hook_playvideos_render_header($data) 16function hook_playvideos_render_header($data)
17{ 17{
18 if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) { 18 if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) {
19 $data['buttons_toolbar'][] = file_get_contents(PluginManager::$PLUGINS_PATH . '/playvideos/playvideos.html'); 19 $playvideo = array(
20 'attr' => array(
21 'href' => '#',
22 'title' => 'Video player',
23 'id' => 'playvideos',
24 ),
25 'html' => '► Play Videos'
26 );
27 $data['buttons_toolbar'][] = $playvideo;
20 } 28 }
21 29
22 return $data; 30 return $data;
@@ -37,4 +45,4 @@ function hook_playvideos_render_footer($data)
37 } 45 }
38 46
39 return $data; 47 return $data;
40} \ No newline at end of file 48}
diff --git a/plugins/qrcode/qrcode.html b/plugins/qrcode/qrcode.html
index cebc5644..dc214ed1 100644
--- a/plugins/qrcode/qrcode.html
+++ b/plugins/qrcode/qrcode.html
@@ -1,5 +1,5 @@
1<div class="linkqrcode"> 1<div class="linkqrcode">
2 <a href="http://qrfree.kaywa.com/?l=1&amp;s=8&amp;d=%s" onclick="showQrCode(this); return false;" class="qrcode" data-permalink="%s"> 2 <a href="http://qrfree.kaywa.com/?l=1&amp;s=8&amp;d=%s" onclick="showQrCode(this); return false;" class="qrcode" data-permalink="%s">
3 <img src="%s/qrcode/qrcode.png" class="linklist-plugin-icon" title="QR-Code"> 3 <img src="%s/qrcode/qrcode.png" class="linklist-plugin-icon" title="QR-Code" alt="QRCode">
4 </a> 4 </a>
5</div> 5</div>
diff --git a/plugins/readityourself/config.php.dist b/plugins/readityourself/config.php.dist
deleted file mode 100644
index d6b5cb85..00000000
--- a/plugins/readityourself/config.php.dist
+++ /dev/null
@@ -1,3 +0,0 @@
1<?php
2
3$GLOBALS['plugins']['READITYOUSELF_URL'] = 'http://someurl.com'; \ No newline at end of file
diff --git a/plugins/readityourself/readityourself.html b/plugins/readityourself/readityourself.html
index e8c5f784..5e200715 100644
--- a/plugins/readityourself/readityourself.html
+++ b/plugins/readityourself/readityourself.html
@@ -1 +1 @@
<span><a href="%s?url=%s"><img class="linklist-plugin-icon" src="%s/readityourself/book-open.png" title="Read with Readityourself" /></a></span> <span><a href="%s?url=%s"><img class="linklist-plugin-icon" src="%s/readityourself/book-open.png" title="Read with Readityourself" alt="readityourself" /></a></span>
diff --git a/plugins/readityourself/readityourself.php b/plugins/readityourself/readityourself.php
index c8df4c4f..961c5bda 100644
--- a/plugins/readityourself/readityourself.php
+++ b/plugins/readityourself/readityourself.php
@@ -8,34 +8,42 @@
8// it seems kinda dead. 8// it seems kinda dead.
9// Not tested. 9// Not tested.
10 10
11// don't raise unnecessary warnings 11/**
12if (is_file(PluginManager::$PLUGINS_PATH . '/readityourself/config.php')) { 12 * Init function, return an error if the server is not set.
13 include PluginManager::$PLUGINS_PATH . '/readityourself/config.php'; 13 *
14} 14 * @param $conf ConfigManager instance.
15 15 *
16if (empty($GLOBALS['plugins']['READITYOUSELF_URL'])) { 16 * @return array Eventual error.
17 $GLOBALS['plugin_errors'][] = 'Readityourself plugin error: '. 17 */
18 'Please define "$GLOBALS[\'plugins\'][\'READITYOUSELF_URL\']" '. 18function readityourself_init($conf)
19 'in "plugins/readityourself/config.php" or in your Shaarli config.php file.'; 19{
20 $riyUrl = $conf->get('plugins.READITYOUSELF_URL');
21 if (empty($riyUrl)) {
22 $error = 'Readityourself plugin error: '.
23 'Please define the "READITYOUSELF_URL" setting in the plugin administration page.';
24 return array($error);
25 }
20} 26}
21 27
22/** 28/**
23 * Add readityourself icon to link_plugin when rendering linklist. 29 * Add readityourself icon to link_plugin when rendering linklist.
24 * 30 *
25 * @param mixed $data - linklist data. 31 * @param mixed $data Linklist data.
32 * @param ConfigManager $conf Configuration Manager instance.
26 * 33 *
27 * @return mixed - linklist data with readityourself plugin. 34 * @return mixed - linklist data with readityourself plugin.
28 */ 35 */
29function hook_readityourself_render_linklist($data) 36function hook_readityourself_render_linklist($data, $conf)
30{ 37{
31 if (!isset($GLOBALS['plugins']['READITYOUSELF_URL'])) { 38 $riyUrl = $conf->get('plugins.READITYOUSELF_URL');
39 if (empty($riyUrl)) {
32 return $data; 40 return $data;
33 } 41 }
34 42
35 $readityourself_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/readityourself/readityourself.html'); 43 $readityourself_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/readityourself/readityourself.html');
36 44
37 foreach ($data['links'] as &$value) { 45 foreach ($data['links'] as &$value) {
38 $readityourself = sprintf($readityourself_html, $GLOBALS['plugins']['READITYOUSELF_URL'], $value['url'], PluginManager::$PLUGINS_PATH); 46 $readityourself = sprintf($readityourself_html, $riyUrl, $value['url'], PluginManager::$PLUGINS_PATH);
39 $value['link_plugin'][] = $readityourself; 47 $value['link_plugin'][] = $readityourself;
40 } 48 }
41 49
diff --git a/plugins/wallabag/README.md b/plugins/wallabag/README.md
index 5bc35be1..ea21a519 100644
--- a/plugins/wallabag/README.md
+++ b/plugins/wallabag/README.md
@@ -4,39 +4,34 @@ For each link in your Shaarli, adds a button to save the target page in your [wa
4 4
5### Installation 5### Installation
6 6
7Clone this repository inside your `tpl/plugins/` directory, or download the archive and unpack it there. 7Clone this repository inside your `tpl/plugins/` directory, or download the archive and unpack it there.
8The directory structure should look like: 8The directory structure should look like:
9 9
10``` 10```bash
11└── tpl 11└── tpl
12 └── plugins 12 └── plugins
13    └── wallabag 13 └── wallabag
14    ├── README.md 14 ├── README.md
15 ├── config.php.dist 15 ├── wallabag.html
16    ├── wallabag.html 16 ├── wallabag.meta
17    ├── wallabag.php 17 ├── wallabag.php
18    └── wallabag.png 18 ├── wallabag.php
19 └── WallabagInstance.php
19``` 20```
20 21
21To enable the plugin, add `'wallabag'` to your list of enabled plugins in `data/options.php` (`PLUGINS` array). 22To enable the plugin, you can either:
22This should look like:
23 23
24``` 24 * enable it in the plugins administration page (`?do=pluginadmin`).
25$GLOBALS['config']['PLUGINS'] = array('qrcode', 'any_other_plugin', 'wallabag') 25 * add `wallabag` to your list of enabled plugins in `data/config.json.php` (`general.enabled_plugins` section).
26```
27 26
28### Configuration 27### Configuration
29 28
30Copy `config.php.dist` into `config.php` and setup your instance. 29Go to the plugin administration page, and edit the following settings (with the plugin enabled).
31 30
32*Wallabag instance URL* 31**WALLABAG_URL**: *Wallabag instance URL*
33``` 32Example value: `http://v2.wallabag.org`
34$GLOBALS['config']['WALLABAG_URL'] = 'http://v2.wallabag.org' ;
35```
36 33
37*Wallabag version*: either `1` (for 1.x) or `2` (for 2.x) 34**WALLABAG_VERSION**: *Wallabag version*
38``` 35Value: either `1` (for 1.x) or `2` (for 2.x)
39$GLOBALS['config']['WALLABAG_VERSION'] = 2;
40```
41 36
42> Note: these settings can also be set in `data/config.php`. \ No newline at end of file 37> Note: these settings can also be set in `data/config.json.php`, in the plugins section.
diff --git a/plugins/wallabag/config.php.dist b/plugins/wallabag/config.php.dist
deleted file mode 100644
index a602708f..00000000
--- a/plugins/wallabag/config.php.dist
+++ /dev/null
@@ -1,4 +0,0 @@
1<?php
2
3$GLOBALS['plugins']['WALLABAG_URL'] = 'https://demo.wallabag.org';
4$GLOBALS['plugins']['WALLABAG_VERSION'] = 1; \ No newline at end of file
diff --git a/plugins/wallabag/wallabag.html b/plugins/wallabag/wallabag.html
index c7b1d044..e861536d 100644
--- a/plugins/wallabag/wallabag.html
+++ b/plugins/wallabag/wallabag.html
@@ -1 +1 @@
<span><a href="%s%s" target="_blank"><img class="linklist-plugin-icon" src="%s/wallabag/wallabag.png" title="Save to wallabag" /></a></span> <span><a href="%s%s" target="_blank"><img class="linklist-plugin-icon" src="%s/wallabag/wallabag.png" title="Save to wallabag" alt="wallabag" /></a></span>
diff --git a/plugins/wallabag/wallabag.meta b/plugins/wallabag/wallabag.meta
index 26e1ea63..9c93f81c 100644
--- a/plugins/wallabag/wallabag.meta
+++ b/plugins/wallabag/wallabag.meta
@@ -1,2 +1,4 @@
1description="For each link, add a Wallabag icon to save it in your instance." 1description="For each link, add a Wallabag icon to save it in your instance."
2parameters="WALLABAG_URL;WALLABAG_VERSION" \ No newline at end of file 2parameters="WALLABAG_URL;WALLABAG_VERSION"
3parameter.WALLABAG_URL="Wallabag API URL"
4parameter.WALLABAG_VERSION="Wallabag API version (1 or 2)" \ No newline at end of file
diff --git a/plugins/wallabag/wallabag.php b/plugins/wallabag/wallabag.php
index 0d6fc66d..641e4cc2 100644
--- a/plugins/wallabag/wallabag.php
+++ b/plugins/wallabag/wallabag.php
@@ -6,34 +6,40 @@
6 6
7require_once 'WallabagInstance.php'; 7require_once 'WallabagInstance.php';
8 8
9// don't raise unnecessary warnings 9/**
10if (is_file(PluginManager::$PLUGINS_PATH . '/wallabag/config.php')) { 10 * Init function, return an error if the server is not set.
11 include PluginManager::$PLUGINS_PATH . '/wallabag/config.php'; 11 *
12} 12 * @param $conf ConfigManager instance.
13 13 *
14if (empty($GLOBALS['plugins']['WALLABAG_URL'])) { 14 * @return array Eventual error.
15 $GLOBALS['plugin_errors'][] = 'Wallabag plugin error: '. 15 */
16 'Please define "$GLOBALS[\'plugins\'][\'WALLABAG_URL\']" '. 16function wallabag_init($conf)
17 'in "plugins/wallabag/config.php" or in your Shaarli config.php file.'; 17{
18 $wallabagUrl = $conf->get('plugins.WALLABAG_URL');
19 if (empty($wallabagUrl)) {
20 $error = 'Wallabag plugin error: '.
21 'Please define the "WALLABAG_URL" setting in the plugin administration page.';
22 return array($error);
23 }
18} 24}
19 25
20/** 26/**
21 * Add wallabag icon to link_plugin when rendering linklist. 27 * Add wallabag icon to link_plugin when rendering linklist.
22 * 28 *
23 * @param mixed $data - linklist data. 29 * @param mixed $data Linklist data.
30 * @param ConfigManager $conf Configuration Manager instance.
24 * 31 *
25 * @return mixed - linklist data with wallabag plugin. 32 * @return mixed - linklist data with wallabag plugin.
26 */ 33 */
27function hook_wallabag_render_linklist($data) 34function hook_wallabag_render_linklist($data, $conf)
28{ 35{
29 if (!isset($GLOBALS['plugins']['WALLABAG_URL'])) { 36 $wallabagUrl = $conf->get('plugins.WALLABAG_URL');
37 if (empty($wallabagUrl)) {
30 return $data; 38 return $data;
31 } 39 }
32 40
33 $version = isset($GLOBALS['plugins']['WALLABAG_VERSION']) 41 $version = $conf->get('plugins.WALLABAG_VERSION');
34 ? $GLOBALS['plugins']['WALLABAG_VERSION'] 42 $wallabagInstance = new WallabagInstance($wallabagUrl, $version);
35 : '';
36 $wallabagInstance = new WallabagInstance($GLOBALS['plugins']['WALLABAG_URL'], $version);
37 43
38 $wallabagHtml = file_get_contents(PluginManager::$PLUGINS_PATH . '/wallabag/wallabag.html'); 44 $wallabagHtml = file_get_contents(PluginManager::$PLUGINS_PATH . '/wallabag/wallabag.html');
39 45