1<?php
2/**
3 * DokuWiki Syntax Plugin Web Component.
4 *
5 */
6if (!defined('DOKU_INC')) {
7    die();
8}
9
10if (!defined('DOKU_PLUGIN')) {
11    define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/');
12}
13
14
15require_once(DOKU_PLUGIN . 'syntax.php');
16require_once(DOKU_INC . 'inc/parserutils.php');
17
18/**
19 * All DokuWiki plugins to extend the parser/rendering mechanism
20 * need to inherit from this class
21 *
22 * The name of the class must follow a pattern (don't change it)
23 * ie:
24 *    syntax_plugin_PluginName_ComponentName
25 *
26 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
27 * !!!!!!!!!!! The component name must be the name of the php file !!!
28 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
29 */
30class syntax_plugin_webcomponent_button extends DokuWiki_Syntax_Plugin
31{
32
33
34    const INTERNAL_LINK_PATTERN = "\[\[.*?\]\](?!\])";
35
36
37    /**
38     * Syntax Type.
39     *
40     * Needs to return one of the mode types defined in $PARSER_MODES in parser.php
41     * @see DokuWiki_Syntax_Plugin::getType()
42     */
43    function getType()
44    {
45        return 'protected';
46    }
47
48    /**
49     * @return array
50     * Allow which kind of plugin inside
51     *
52     * No one of array('container', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs')
53     * because we manage self the content and we call self the parser
54     */
55    public function getAllowedTypes()
56    {
57        return array();
58    }
59
60    /**
61     * How Dokuwiki will add P element
62     *
63     * * 'normal' - The plugin can be used inside paragraphs
64     *  * 'block'  - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs
65     *  * 'stack'  - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs
66     *
67     * @see DokuWiki_Syntax_Plugin::getPType()
68     */
69    function getPType()
70    {
71        return 'block';
72    }
73
74    /**
75     * @see Doku_Parser_Mode::getSort()
76     * the mode with the lowest sort number will win out
77     * the lowest in the tree must have the lowest sort number
78     * No idea why it must be low but inside a teaser, it will work
79     * https://www.dokuwiki.org/devel:parser#order_of_adding_modes_important
80     */
81    function getSort()
82    {
83        return 10;
84    }
85
86    /**
87     * Create a pattern that will called this plugin
88     *
89     * @see Doku_Parser_Mode::connectTo()
90     * @param string $mode
91     */
92    function connectTo($mode)
93    {
94
95        foreach (self::getTags() as $tag) {
96
97            $pattern = webcomponent::getLookAheadPattern($tag);
98            $this->Lexer->addEntryPattern($pattern, $mode, 'plugin_' . webcomponent::PLUGIN_NAME . '_' . $this->getPluginComponent());
99
100        }
101
102    }
103
104    public function postConnect()
105    {
106
107        foreach (self::getTags() as $tag) {
108            $this->Lexer->addExitPattern('</' . $tag . '>', 'plugin_' . webcomponent::PLUGIN_NAME . '_' . $this->getPluginComponent());
109        }
110
111        // Link
112        $this->Lexer->addPattern(self::INTERNAL_LINK_PATTERN, 'plugin_' . webcomponent::PLUGIN_NAME . '_' . $this->getPluginComponent());
113
114
115    }
116
117    /**
118     *
119     * The handle function goal is to parse the matched syntax through the pattern function
120     * and to return the result for use in the renderer
121     * This result is always cached until the page is modified.
122     * @see DokuWiki_Syntax_Plugin::handle()
123     *
124     * @param string $match
125     * @param int $state
126     * @param int $pos
127     * @param Doku_Handler $handler
128     * @return array|bool
129     */
130    function handle($match, $state, $pos, Doku_Handler $handler)
131    {
132
133        switch ($state) {
134
135            case DOKU_LEXER_ENTER:
136
137                // Suppress the tag name
138                $match = utf8_substr($match, strlen(self::getTag()) + 1, -1);
139                $parameters = webcomponent::parseMatch($match);
140                return array($state, $parameters);
141
142            case DOKU_LEXER_UNMATCHED :
143
144                return array($state, $match);
145
146            case DOKU_LEXER_MATCHED :
147
148                $parameters = array();
149
150                if (preg_match('/' . self::INTERNAL_LINK_PATTERN . '/msSi', $match . DOKU_LF)) {
151                    // We have a internal link, we parse it (code form the function internallink in handler.php)
152                    //
153                    // Strip the opening and closing markup
154                    $link = preg_replace(array('/^\[\[/', '/\]\]$/u'), '', $match);
155
156                    // Split title from URL
157                    $link = explode('|', $link, 2);
158                    if (!isset($link[1])) {
159                        $link[1] = null;
160                    }
161                    $link[0] = trim($link[0]);
162                    // we expect only a local link
163                    $parameters['locallink']['pageid'] = $link[0];
164                    $parameters['locallink']['content'] = $link[1];
165
166                }
167
168                return array($state, $parameters);
169
170            case DOKU_LEXER_EXIT :
171
172                return array($state, '');
173
174
175        }
176
177        return array();
178
179    }
180
181    /**
182     * Render the output
183     * @see DokuWiki_Syntax_Plugin::render()
184     *
185     * @param string $mode
186     * @param Doku_Renderer $renderer
187     * @param array $data - what the function handle() return'ed
188     * @return bool
189     */
190    function render($mode, Doku_Renderer $renderer, $data)
191    {
192
193        switch ($mode) {
194
195            case 'xhtml': {
196
197                /** @var Doku_Renderer_xhtml $renderer */
198
199                list($state, $parameters) = $data;
200                switch ($state) {
201
202                    case DOKU_LEXER_ENTER :
203                        break;
204
205                    case DOKU_LEXER_UNMATCHED :
206
207                        $renderer->doc .= $renderer->_xmlEntities($parameters);
208                        break;
209
210                    case DOKU_LEXER_MATCHED:
211
212                        if (array_key_exists('locallink', $parameters)) {
213
214                            $pageid = $parameters['locallink']['pageid'];
215                            $content = $parameters['locallink']['content'];
216                            $renderer->doc .= '<a href="' . wl($pageid) . '" class="btn btn-primary">' . $renderer->_xmlEntities($content) . '</a>';
217
218                        }
219                        break;
220                    case DOKU_LEXER_EXIT :
221
222                        break;
223                }
224                return true;
225            }
226
227            case 'metadata':
228
229                /** @var Doku_Renderer_metadata $renderer */
230
231                list($state, $parameters) = $data;
232                switch ($state) {
233
234                    case DOKU_LEXER_MATCHED:
235
236                        if (array_key_exists('locallink', $parameters)) {
237
238                            // To add the link in the backlinks
239                            // See: https://www.dokuwiki.org/devel:syntax_plugins#metadata_renderer
240                            $pageIdToLinkTo = $parameters['locallink']['pageid'];
241                            $renderer->internallink($pageIdToLinkTo);
242
243                        }
244                }
245        }
246        return false;
247    }
248
249
250    public static function getTag()
251    {
252        return webcomponent::getTagName(get_called_class());
253    }
254
255    public static function getTags()
256    {
257        $elements[] = self::getTag();
258        $elements[] = 'btn';
259        return $elements;
260    }
261
262
263}
264