1<?php
2/**
3 * DokuWiki Syntax Plugin Web Component.
4 * Implementatiojn of https://getbootstrap.com/docs/4.1/content/typography/#blockquotes
5 *
6 */
7if (!defined('DOKU_INC')) {
8    die();
9}
10
11require_once(__DIR__ . '/../webcomponent.php');
12
13/**
14 * All DokuWiki plugins to extend the parser/rendering mechanism
15 * need to inherit from this class
16 *
17 * The name of the class must follow a pattern (don't change it)
18 * ie:
19 *    syntax_plugin_PluginName_ComponentName
20 */
21class syntax_plugin_webcomponent_blockquote extends DokuWiki_Syntax_Plugin
22{
23
24
25    const IMAGE_PATTERN = "\{\{(?:[^\}]|(?:\}[^\}]))+\}\}";
26
27
28    /**
29     * Syntax Type.
30     *
31     * Needs to return one of the mode types defined in $PARSER_MODES in parser.php
32     * @see DokuWiki_Syntax_Plugin::getType()
33     */
34    function getType()
35    {
36        return 'protected';
37    }
38
39    /**
40     * @return array
41     * Allow which kind of plugin inside
42     *
43     * No one of array('container', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs')
44     * because we manage self the content and we call self the parser
45     */
46    public function getAllowedTypes()
47    {
48        return array();
49    }
50
51    /**
52     * How Dokuwiki will add P element
53     *
54     * * 'normal' - The plugin can be used inside paragraphs
55     *  * 'block'  - Open paragraphs need to be closed before plugin output - block should not be inside paragraphs
56     *  * 'stack'  - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs
57     *
58     * @see DokuWiki_Syntax_Plugin::getPType()
59     */
60    function getPType()
61    {
62        return 'block';
63    }
64
65    /**
66     * @see Doku_Parser_Mode::getSort()
67     * Higher number than the teaser-columns
68     * because the mode with the lowest sort number will win out
69     */
70    function getSort()
71    {
72        return 200;
73    }
74
75    /**
76     * Create a pattern that will called this plugin
77     *
78     * @see Doku_Parser_Mode::connectTo()
79     * @param string $mode
80     */
81    function connectTo($mode)
82    {
83
84        $pattern = webcomponent::getLookAheadPattern($this->getPluginComponent());
85        $this->Lexer->addEntryPattern($pattern, $mode, 'plugin_' . webcomponent::PLUGIN_NAME . '_' . $this->getPluginComponent());
86
87    }
88
89    public function postConnect()
90    {
91
92        $this->Lexer->addExitPattern('</' . self::getTagName() . '>', 'plugin_' . webcomponent::PLUGIN_NAME . '_' . $this->getPluginComponent());
93        // Lookahead
94
95        // Receive the cite
96        $lookaheadPattern = webcomponent::getIncludeTagPattern(syntax_plugin_webcomponent_cite::getTag());
97        $this->Lexer->addPattern($lookaheadPattern, 'plugin_' . webcomponent::PLUGIN_NAME . '_' . $this->getPluginComponent());
98
99        // Receive the image
100        $this->Lexer->addPattern(self::IMAGE_PATTERN, 'plugin_' . webcomponent::PLUGIN_NAME . '_' . $this->getPluginComponent());
101
102    }
103
104    /**
105     *
106     * The handle function goal is to parse the matched syntax through the pattern function
107     * and to return the result for use in the renderer
108     * This result is always cached until the page is modified.
109     * @see DokuWiki_Syntax_Plugin::handle()
110     *
111     * @param string $match
112     * @param int $state
113     * @param int $pos
114     * @param Doku_Handler $handler
115     * @return array|bool
116     */
117    function handle($match, $state, $pos, Doku_Handler $handler)
118    {
119
120        switch ($state) {
121
122            case DOKU_LEXER_ENTER:
123                // Suppress the component name
124                $match = utf8_substr($match, strlen($this->getPluginComponent()) + 1, -1);
125                $parameters = webcomponent::parseMatch($match);
126                return array($state, $parameters);
127
128            case DOKU_LEXER_UNMATCHED :
129                return array($state, $match);
130
131            case DOKU_LEXER_MATCHED :
132
133                $parameters = array();
134                $citeTag = syntax_plugin_webcomponent_cite::getTag();
135                if (preg_match('/<' . $citeTag . '>(.*)<\/' . $citeTag . '>/msSi', $match, $matches)) {
136                    // We have a citation
137                    $parameters['cite']['content'] = $matches[1];
138                }
139
140                if (preg_match('/' . self::IMAGE_PATTERN . '/msSi', $match, $matches)) {
141                    // We have an image, we parse it (Doku_Handler_Parse_Media in handler.php)
142                    $parameters['image'] = Doku_Handler_Parse_Media($match);
143                }
144                return array($state, $parameters);
145
146
147            case DOKU_LEXER_EXIT :
148                // Important to get an exit in the render phase
149                return array($state, '');
150
151        }
152
153        return array();
154
155    }
156
157    /**
158     * Render the output
159     * @see DokuWiki_Syntax_Plugin::render()
160     *
161     * @param string $mode
162     * @param Doku_Renderer $renderer
163     * @param array $data - what the function handle() return'ed
164     * @return bool
165     */
166    function render($mode, Doku_Renderer $renderer, $data)
167    {
168
169        if ($mode == 'xhtml') {
170
171            /** @var Doku_Renderer_xhtml $renderer */
172            list($state, $parameters) = $data;
173            switch ($state) {
174
175                case DOKU_LEXER_ENTER :
176                    // w-75 for width 75
177                    $class = "m-3";
178                    if (array_key_exists("class", $parameters)) {
179                        $class = $parameters["class"];
180                    }
181                    if ($class != "") {
182                        $class = " " . $class;
183                    }
184                    $renderer->doc .= '<div class="card' . $class . '">' . DOKU_LF
185                        . DOKU_TAB . '<div class="card-body">' . DOKU_LF
186                        . DOKU_TAB . DOKU_TAB . '<' . self::getTagName() . ' class="' . self::getTagName() . ' m-0"';
187                    // m-0 on the blockquote element is to correct a bottom margin from bootstrap of 1em that we don't want
188                    $renderer->doc .= '>' . DOKU_LF;
189                    break;
190
191                case DOKU_LEXER_UNMATCHED :
192
193                    $renderer->doc .= DOKU_TAB . DOKU_TAB . webcomponent::render($parameters) . DOKU_LF;
194                    break;
195
196                case DOKU_LEXER_MATCHED:
197
198                    if (array_key_exists('cite', $parameters)) {
199                        $content = $parameters['cite']['content'];
200                        $renderer->doc .= DOKU_TAB . DOKU_TAB . '<footer class="blockquote-footer text-right"><cite>';
201                        $renderer->doc .= webcomponent::render($content);
202                        $renderer->doc .= "</cite></footer>" . DOKU_LF;
203                    }
204
205                    if (array_key_exists('image', $parameters)) {
206                        $src = $parameters['image']['src'];
207                        $width = $parameters['image']['width'];
208                        $height = $parameters['image']['height'];
209                        $title = $parameters['image']['title'];
210                        //Snippet taken from $renderer->doc .= $renderer->internalmedia($src, $linking = 'nolink');
211                        $renderer->doc .= '<img class="media my-3" src="' . ml($src, array('w' => $width, 'h' => $height, 'cache' => true)) . '" alt="' . $title . '" width="' . $width . '">';
212                    }
213                    break;
214
215                case DOKU_LEXER_EXIT :
216
217                    // Close the blockquote
218                    $renderer->doc .= DOKU_TAB . DOKU_TAB . '</' . $this->getPluginComponent() . '>' . DOKU_LF
219                        . DOKU_TAB . '</div>' . DOKU_LF;
220
221                    // Close the card
222                    $renderer->doc .= '</div>';
223
224                    // Reinit
225                    $this->content = "";
226                    $this->img = "";
227                    $this->cite = "";
228                    break;
229            }
230            return true;
231        }
232        return false;
233    }
234
235
236    public static function getTagName()
237    {
238        list(/* $t */, /* $p */, /* $n */, $c) = explode('_', get_called_class(), 4);
239        return (isset($c) ? $c : '');
240    }
241
242
243}
244