1<?php
2/**
3 * DokuWiki Syntax Plugin Combostrap.
4 *
5 */
6
7use ComboStrap\CallStack;
8use ComboStrap\DokuPath;
9use ComboStrap\ExceptionCombo;
10use ComboStrap\FileSystems;
11use ComboStrap\Icon;
12use ComboStrap\LogUtility;
13use ComboStrap\PluginUtility;
14use ComboStrap\SvgImageLink;
15use ComboStrap\TagAttributes;
16
17
18require_once(__DIR__ . '/../ComboStrap/PluginUtility.php');
19
20/**
21 * All DokuWiki plugins to extend the parser/rendering mechanism
22 * need to inherit from this class
23 *
24 * The name of the class must follow a pattern (don't change it)
25 * ie:
26 *    syntax_plugin_PluginName_ComponentName
27 *
28 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
29 * !!!!!!!!!!! The component name must be the name of the php file !!!
30 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
31 *
32 * https://icons.getbootstrap.com/
33 * https://remixicon.com/
34 */
35class syntax_plugin_combo_icon extends DokuWiki_Syntax_Plugin
36{
37    const TAG = "icon";
38    const CANONICAL = self::TAG;
39
40    private static function exceptionHandling(Exception $e, $tagAttribute)
41    {
42        $errorClass = syntax_plugin_combo_media::SVG_RENDERING_ERROR_CLASS;
43        $message = "Icon ({$tagAttribute->getValue("name")}). Error while rendering: {$e->getMessage()}";
44        $html = "<span class=\"text-alert $errorClass\">" . hsc(trim($message)) . "</span>";
45        if (!PluginUtility::isTest()) {
46            LogUtility::msg($message, LogUtility::LVL_MSG_WARNING, self::CANONICAL);
47        }
48        return $html;
49    }
50
51
52    /**
53     * Syntax Type.
54     *
55     * Needs to return one of the mode types defined in $PARSER_MODES in parser.php
56     * @see DokuWiki_Syntax_Plugin::getType()
57     */
58    function getType()
59    {
60        return 'substition';
61    }
62
63    /**
64     * @return array
65     * Allow which kind of plugin inside
66     *
67     * No one of array('container', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs')
68     * because we manage self the content and we call self the parser
69     */
70    public function getAllowedTypes()
71    {
72        // You can't put anything in a icon
73        return array('formatting');
74    }
75
76    /**
77     * How Dokuwiki will add P element
78     *
79     *  * 'normal' - The plugin can be used inside paragraphs
80     *  * 'block'  - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs
81     *  * 'stack'  - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs
82     *
83     * @see DokuWiki_Syntax_Plugin::getPType()
84     */
85    function getPType()
86    {
87        return 'normal';
88    }
89
90    /**
91     * @see Doku_Parser_Mode::getSort()
92     * the mode with the lowest sort number will win out
93     * the lowest in the tree must have the lowest sort number
94     * No idea why it must be low but inside a teaser, it will work
95     * https://www.dokuwiki.org/devel:parser#order_of_adding_modes_important
96     */
97    function getSort()
98    {
99        return 10;
100    }
101
102    /**
103     * Create a pattern that will called this plugin
104     *
105     * @param string $mode
106     * @see Doku_Parser_Mode::connectTo()
107     */
108    function connectTo($mode)
109    {
110
111
112        $specialPattern = PluginUtility::getEmptyTagPattern(self::TAG);
113        $this->Lexer->addSpecialPattern($specialPattern, $mode, PluginUtility::getModeFromTag($this->getPluginComponent()));
114
115        /**
116         * The content is used to add a {@link syntax_plugin_combo_tooltip}
117         */
118        $entryPattern = PluginUtility::getContainerTagPattern(self::TAG);
119        $this->Lexer->addEntryPattern($entryPattern, $mode, PluginUtility::getModeFromTag($this->getPluginComponent()));
120
121
122    }
123
124    public function postConnect()
125    {
126        $this->Lexer->addExitPattern('</' . self::TAG . '>', PluginUtility::getModeFromTag($this->getPluginComponent()));
127    }
128
129
130    /**
131     *
132     * The handle function goal is to parse the matched syntax through the pattern function
133     * and to return the result for use in the renderer
134     * This result is always cached until the page is modified.
135     * @param string $match
136     * @param int $state
137     * @param int $pos
138     * @param Doku_Handler $handler
139     * @return array|bool
140     * @throws Exception
141     * @see DokuWiki_Syntax_Plugin::handle()
142     *
143     */
144    function handle($match, $state, $pos, Doku_Handler $handler)
145    {
146
147        switch ($state) {
148
149            case DOKU_LEXER_SPECIAL:
150            case DOKU_LEXER_ENTER:
151                // Get the parameters
152                $tagAttributes = TagAttributes::createFromTagMatch($match);
153                return array(
154                    PluginUtility::STATE => $state,
155                    PluginUtility::ATTRIBUTES => $tagAttributes->toCallStackArray()
156                );
157            case DOKU_LEXER_EXIT:
158                $callStack = CallStack::createFromHandler($handler);
159                $openingCall = $callStack->moveToPreviousCorrespondingOpeningCall();
160                return array(
161                    PluginUtility::STATE => $state,
162                    PluginUtility::ATTRIBUTES => $openingCall->getAttributes(),
163                    PluginUtility::CONTEXT => $openingCall->getContext()
164                );
165
166
167        }
168
169        return array();
170
171    }
172
173    /**
174     * Render the output
175     * @param string $format
176     * @param Doku_Renderer $renderer
177     * @param array $data - what the function handle() return'ed
178     * @return boolean - rendered correctly? (however, returned value is not used at the moment)
179     * @see DokuWiki_Syntax_Plugin::render()
180     *
181     *
182     */
183    function render($format, Doku_Renderer $renderer, $data)
184    {
185
186        switch ($format) {
187
188            case 'xhtml':
189                {
190                    /** @var Doku_Renderer_xhtml $renderer */
191                    $state = $data[PluginUtility::STATE];
192                    switch ($state) {
193
194
195                        case DOKU_LEXER_SPECIAL:
196                            $tagAttribute = TagAttributes::createFromCallStackArray($data[PluginUtility::ATTRIBUTES]);
197                            try {
198                                $renderer->doc .= Icon::create($tagAttribute)
199                                    ->render();
200                            } catch (Exception $e) {
201                                $renderer->doc .= self::exceptionHandling($e, $tagAttribute);
202                            }
203                            break;
204                        case DOKU_LEXER_ENTER:
205                            /**
206                             * If there is a tooltip, we need
207                             * to start with a span to wrap the svg with it
208                             */
209                            if ($data[PluginUtility::CONTEXT] == syntax_plugin_combo_tooltip::TAG) {
210                                /**
211                                 * The inline block is to make the span take the whole space
212                                 * of the image (ie dimension)
213                                 */
214                                $renderer->doc .= "<span class=\"d-inline-block\"";
215                            }
216                            break;
217                        case DOKU_LEXER_EXIT:
218                            /**
219                             * Print the icon
220                             */
221                            $tagAttribute = TagAttributes::createFromCallStackArray($data[PluginUtility::ATTRIBUTES]);
222                            try {
223                                $renderer->doc .= Icon::create($tagAttribute)
224                                    ->render();
225                            } catch (ExceptionCombo $e) {
226                                $renderer->doc .= self::exceptionHandling($e, $tagAttribute);
227                            }
228                            /**
229                             * Close the span if we are in a tooltip context
230                             */
231                            if ($data[PluginUtility::CONTEXT] == syntax_plugin_combo_tooltip::TAG) {
232                                $renderer->doc .= "</span>";
233                            }
234
235                            break;
236                    }
237
238                }
239                break;
240            case 'metadata':
241                /**
242                 * @var Doku_Renderer_metadata $renderer
243                 */
244                $tagAttribute = TagAttributes::createFromCallStackArray($data[PluginUtility::ATTRIBUTES]);
245                try {
246                    $mediaPath = Icon::create($tagAttribute)->getPath();
247                } catch (ExceptionCombo $e) {
248                    // error is already fired in the renderer
249                    return false;
250                }
251                if ($mediaPath instanceof DokuPath) {
252                    $mediaId = $mediaPath->getDokuwikiId();
253                    syntax_plugin_combo_media::registerFirstMedia($renderer, $mediaId);
254                }
255                break;
256
257        }
258        return true;
259    }
260
261
262}
263