1<?php
2
3use dokuwiki\File\MediaResolver;
4use dokuwiki\File\PageResolver;
5
6/**
7 * DokuWiki Plugin gemini (Renderer Component)
8 *
9 * This implements rendering to gemtext
10 *
11 * @link https://gemini.circumlunar.space/docs/gemtext.gmi
12 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
13 * @author  Andreas Gohr <andi@splitbrain.org>
14 */
15class renderer_plugin_gemini extends Doku_Renderer
16{
17    const NL = "\n";
18
19    protected $linklist = [];
20    protected $schemes = null;
21
22    /** @inheritDoc */
23    public function getFormat()
24    {
25        return 'gemini';
26    }
27
28    /** @inheritdoc */
29    public function plugin($name, $data, $state = '', $match = '')
30    {
31        $this->doc .= $match; // FIXME always?
32    }
33
34    /** @inheritdoc */
35    public function header($text, $level, $pos)
36    {
37        if ($level > 3) $level = 3;
38        $this->doc .= str_pad('', $level, '#');
39        $this->doc .= $text;
40        $this->linebreak();
41    }
42
43    /**
44     * Output links at the end of the section
45     * @inheritdoc
46     */
47    public function section_close()
48    {
49        if (empty($this->linklist)) return;
50
51        $this->linebreak();
52        foreach ($this->linklist as $number => $link) {
53            $this->doc .= '=> ' . $link['url'] . " [$number] " . $link['title'];
54            $this->linebreak();
55        }
56        $this->linebreak();
57
58        $this->linklist = [];
59    }
60
61    /** @inheritdoc */
62    public function cdata($text)
63    {
64        $this->doc .= $text;
65    }
66
67    /** @inheritdoc */
68    public function p_close()
69    {
70        $this->linebreak();
71        $this->linebreak();
72    }
73
74    /**
75     * @inheritdoc
76     * @params bool $optional only output linebreak if not already one there
77     */
78    public function linebreak($optional = false)
79    {
80        if ($optional && $this->doc[-1] == self::NL) return;
81        $this->doc .= self::NL;
82    }
83
84    /** @inheritdoc */
85    public function hr()
86    {
87        $this->linebreak();
88        $this->doc .= str_pad('', 70, '━');
89        $this->linebreak();
90    }
91
92    /** @inheritdoc */
93    public function footnote_open()
94    {
95        $this->doc .= ' ❲';
96    }
97
98    /** @inheritdoc */
99    public function footnote_close()
100    {
101        $this->doc .= '❳ ';
102    }
103
104    /** @inheritdoc */
105    public function listu_close()
106    {
107        $this->linebreak();
108    }
109
110    /** @inheritdoc */
111    public function listo_close()
112    {
113        $this->linebreak();
114    }
115
116    /** @inheritdoc */
117    public function listitem_open($level, $node = false)
118    {
119        $this->doc .= '*';
120    }
121
122    public function listcontent_close()
123    {
124        $this->linebreak();
125    }
126
127    /** @inheritdoc */
128    public function php($text)
129    {
130        $this->cdata($text);
131    }
132
133    /** @inheritdoc */
134    public function phpblock($text)
135    {
136        $this->preformatted($text);
137    }
138
139    /** @inheritdoc */
140    public function html($text)
141    {
142        $this->cdata($text);
143    }
144
145    /** @inheritdoc */
146    public function htmlblock($text)
147    {
148        $this->preformatted($text);
149    }
150
151    /** @inheritdoc */
152    public function preformatted($text)
153    {
154        $this->file($text);
155    }
156
157    /** @inheritdoc */
158    public function quote_open()
159    {
160        $this->doc .= '> ';
161    }
162
163    /** @inheritdoc */
164    public function quote_close()
165    {
166        $this->linebreak(true);
167    }
168
169    /** @inheritdoc */
170    public function file($text, $lang = null, $file = null)
171    {
172        $this->linebreak(true);
173        $this->doc .= "```$file";
174        $this->linebreak();
175        $this->cdata($text);
176        $this->linebreak(true);
177        $this->doc .= '```';
178        $this->linebreak();
179    }
180
181    /** @inheritdoc */
182    public function code($text, $lang = null, $file = null)
183    {
184        $this->file($text, $lang, $file);
185    }
186
187    /** @inheritdoc */
188    public function acronym($acronym)
189    {
190        $this->cdata($acronym);
191    }
192
193    /** @inheritdoc */
194    public function smiley($smiley)
195    {
196        $this->cdata($smiley);
197    }
198
199    /** @inheritdoc */
200    public function entity($entity)
201    {
202        if (array_key_exists($entity, $this->entities)) {
203            $this->doc .= $this->entities[$entity];
204        } else {
205            $this->cdata($entity);
206        }
207    }
208
209    /** @inheritdoc */
210    public function multiplyentity($x, $y)
211    {
212        $this->cdata($x . '×' . $y);
213    }
214
215    /** @inheritdoc */
216    public function singlequoteopening()
217    {
218        global $lang;
219        $this->doc .= $lang['singlequoteopening'];
220    }
221
222    /** @inheritdoc */
223    public function singlequoteclosing()
224    {
225        global $lang;
226        $this->doc .= $lang['singlequoteclosing'];
227    }
228
229    /** @inheritdoc */
230    public function apostrophe()
231    {
232        global $lang;
233        $this->doc .= $lang['apostrophe'];
234    }
235
236    /** @inheritdoc */
237    public function doublequoteopening()
238    {
239        global $lang;
240        $this->doc .= $lang['doublequoteopening'];
241    }
242
243    /** @inheritdoc */
244    public function doublequoteclosing()
245    {
246        global $lang;
247        $this->doc .= $lang['doublequoteclosing'];
248    }
249
250    /** @inheritdoc */
251    public function camelcaselink($link)
252    {
253        $this->internallink($link);
254    }
255
256    /** @inheritdoc */
257    public function locallink($hash, $name = null)
258    {
259        if (!$name) $name = $hash;
260        $this->cdata($name);
261    }
262
263    /** @inheritdoc */
264    public function internallink($id, $title = null)
265    {
266        global $ID;
267
268        $id = explode('?', $id, 2)[0];
269        $id = explode('#', $id, 2)[0];
270        if ($id === '') $id = $ID;
271        $id = (new PageResolver($ID))->resolveId($id);
272
273        if (page_exists($id)) {
274            $url = '//' . $_SERVER['HTTP_HOST'] . '/' . $id;
275        } else {
276            $url = '';
277        }
278
279        // reuse externallink - handles media titles
280        $this->externallink($url, $title);
281    }
282
283    /**
284     * Note: $url may be empty when passed from internalmedia and pages does not exist
285     * @inheritdoc
286     */
287    public function externallink($url, $title = null)
288    {
289        // image in link title - print it first
290        if (is_array($title)) {
291            if ($title['type'] == 'internalmedia') {
292                $this->internalmedia($title['src'], $title['title']);
293            } else {
294                $this->externalmedia($title['src'], $title['title']);
295            }
296            $title = $title['title'];
297            $ismedia = true;
298        } else {
299            $ismedia = false;
300        }
301        if (!$title) $title = $url;
302
303        // add to list of links
304        if ($url) {
305            $count = count($this->linklist);
306            $count++;
307            $this->linklist[$count] = [
308                'url' => $url,
309                'title' => $title,
310            ];
311        }
312
313        // output
314        if (!$ismedia) {
315            // print the title and count
316            $this->cdata($title);
317            if ($url) $this->doc .= "[$count]";
318        } elseif ($url) {
319            // remove newline, print count, add newline
320            $this->doc = substr($this->doc, 0, -1);
321            $this->doc .= "[$count]";
322            $this->linebreak();
323        }
324    }
325
326    /** @inheritdoc */
327    public function rss($url, $params)
328    {
329        global $conf;
330        $feed = new FeedParser();
331        $feed->set_feed_url($url);
332
333        $rc = $feed->init();
334        if (!$rc) return;
335
336        if ($params['nosort']) $feed->enable_order_by_date(false);
337
338        //decide on start and end
339        if ($params['reverse']) {
340            $mod = -1;
341            $start = $feed->get_item_quantity() - 1;
342            $end = $start - ($params['max']);
343            $end = ($end < -1) ? -1 : $end;
344        } else {
345            $mod = 1;
346            $start = 0;
347            $end = $feed->get_item_quantity();
348            $end = ($end > $params['max']) ? $params['max'] : $end;
349        }
350
351        for ($x = $start; $x != $end; $x += $mod) {
352            $item = $feed->get_item($x);
353            $this->doc .= '=> ' . $item->get_permalink() . ' ' . $item->get_title();
354            $this->linebreak();
355            if ($params['author'] || $params['date']) {
356                if ($params['author']) $this->cdata($item->get_author(0)->get_name());
357                if ($params['date']) $this->cdata(' (' . $item->get_local_date($conf['dformat']) . ')');
358                $this->linebreak();
359            }
360            if ($params['details']) {
361                $this->doc .= '> ' . strip_tags($item->get_description());
362                $this->linebreak();
363            }
364        }
365    }
366
367    /** @inheritdoc */
368    public function interwikilink($link, $title, $wikiName, $wikiUri)
369    {
370        $exists = null;
371        $url = $this->_resolveInterWiki($wikiName, $wikiUri, $exists);
372        if (!$title) $title = $wikiUri;
373        if ($exists === null) {
374            $this->externallink($url, $title);
375        } elseif ($exists === true) {
376            $this->internallink($url, $title);
377        } else {
378            $this->cdata($title);
379        }
380    }
381
382    public function windowssharelink($link, $title = null)
383    {
384        parent::windowssharelink($link, $title); // TODO: Change the autogenerated stub
385    }
386
387    /** @inheritdoc */
388    public function emaillink($address, $name = null)
389    {
390        if (!$name) $name = $address;
391        $this->externallink('mailto:' . $address, $name);
392    }
393
394    /** @inheritdoc */
395    public function internalmedia(
396        $src,
397        $title = null,
398        $align = null,
399        $width = null,
400        $height = null,
401        $cache = null,
402        $linking = null
403    ) {
404        global $ID;
405
406        $src = (new MediaResolver($ID))->resolveId($src);
407        if (!media_exists($src)) return;
408        $src = '//' . $_SERVER['HTTP_HOST'] . '/_media/' . $src;
409
410        $this->externalmedia($src, $title);
411    }
412
413    /** @inheritdoc */
414    public function externalmedia(
415        $src,
416        $title = null,
417        $align = null,
418        $width = null,
419        $height = null,
420        $cache = null,
421        $linking = null
422    ) {
423        if (!$title) $title = basename($src);
424        $title = "[$title]";
425
426        $this->linebreak(true);
427        $this->doc .= '=> ' . $src . ' ' . $title;
428        $this->linebreak();
429    }
430
431    /** @inheritdoc */
432    public function internalmedialink($src, $title = null, $align = null, $width = null, $height = null, $cache = null)
433    {
434        $this->internalmedia($src, $title, $align, $width, $height, $cache);
435    }
436
437    /** @inheritdoc */
438    public function externalmedialink($src, $title = null, $align = null, $width = null, $height = null, $cache = null)
439    {
440        parent::externalmedia($src, $title, $align, $width, $height, $cache);
441    }
442
443    public function table_open($maxcols = null, $numrows = null, $pos = null)
444    {
445        $this->linebreak(true);
446        $this->doc .= '```';
447        $this->linebreak();
448
449    }
450
451    public function table_close($pos = null)
452    {
453        $this->doc .= '```';
454        $this->linebreak();
455    }
456
457    public function tablerow_close()
458    {
459        $this->linebreak();
460    }
461
462    public function tableheader_close()
463    {
464        $this->doc .= "\t";
465    }
466
467    public function tablecell_close()
468    {
469        $this->doc .= "\t";
470    }
471
472}
473
474