xref: /plugin/combo/syntax/iterator.php (revision c3437056399326d621a01da73b649707fbb0ae69)
1<?php
2
3
4use ComboStrap\CallStack;
5use ComboStrap\PluginUtility;
6use ComboStrap\TagAttributes;
7
8require_once(__DIR__ . '/../ComboStrap/PluginUtility.php');
9
10
11/**
12 *
13 * An iterator to iterate over templates.
14 *
15 * *******************
16 * Iteration driver
17 * *******************
18 * The end tag of the template node is driving the iteration.
19 * This way, the tags just after the template
20 * sees them in the {@link CallStack} and can change their context
21 *
22 * For instance, a {@link syntax_plugin_combo_masonry}
23 * component will change the context of all card inside it.
24 *
25 * ********************
26 * Header and footer delimitation
27 * ********************
28 * The iterator delimits also the header and footer.
29 * Some component needs the header to be generate completely.
30 * This is the case of a complex markup such as a table
31 *
32 * ******************************
33 * Delete if no data
34 * ******************************
35 * It gives also the possibility to {@link syntax_plugin_combo_iterator::EMPTY_ROWS_COUNT_ATTRIBUTE
36 * delete the whole block}
37 * (header and footer also) if there is no data
38 *
39 * *****************************
40 * Always Contextual
41 * *****************************
42 * We don't capture the text markup such as in a {@link syntax_plugin_combo_code}
43 * in order to loop because you can't pass the actual handler (ie callstack)
44 * when you {@link p_get_instructions() parse again} a markup.
45 *
46 * The markup is then seen as a new single page without any context.
47 * That may lead to problems.
48 * Example: `heading` may then think that they are `outline heading` ...
49 *
50 */
51class syntax_plugin_combo_iterator extends DokuWiki_Syntax_Plugin
52{
53
54    /**
55     * Tag in Dokuwiki cannot have a `-`
56     * This is the last part of the class
57     */
58    const TAG = "iterator";
59
60    /**
61     * Page canonical and tag pattern
62     */
63    const CANONICAL = "iterator";
64
65    /**
66     * An attribute that is set back
67     * by the {@link DOKU_LEXER_EXIT} state in {@link syntax_plugin_combo_template::handle()}
68     * in order to delete the whole iterator content (ie header, footer)
69     * at the {@link DOKU_LEXER_EXIT} state of {@link syntax_plugin_combo_iterator::handle()}
70     * if there is no rows to iterate
71     */
72    const EMPTY_ROWS_COUNT_ATTRIBUTE = "emptyRowCount";
73
74
75    /**
76     * Syntax Type.
77     *
78     * Needs to return one of the mode types defined in $PARSER_MODES in parser.php
79     * @see https://www.dokuwiki.org/devel:syntax_plugins#syntax_types
80     * @see DokuWiki_Syntax_Plugin::getType()
81     */
82    function getType()
83    {
84        return 'container';
85    }
86
87    /**
88     * How Dokuwiki will add P element
89     *
90     *  * 'normal' - The plugin can be used inside paragraphs (inline or inside)
91     *  * 'block'  - Open paragraphs need to be closed before plugin output (box) - block should not be inside paragraphs
92     *  * 'stack'  - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs
93     *
94     * @see DokuWiki_Syntax_Plugin::getPType()
95     * @see https://www.dokuwiki.org/devel:syntax_plugins#ptype
96     */
97    function getPType()
98    {
99        return 'block';
100    }
101
102    /**
103     * @return array
104     * Allow which kind of plugin inside
105     *
106     * No one of array('baseonly','container', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs')
107     * because we manage self the content and we call self the parser
108     *
109     * Return an array of one or more of the mode types {@link $PARSER_MODES} in Parser.php
110     */
111    function getAllowedTypes()
112    {
113        return array('container', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs');
114    }
115
116    function getSort()
117    {
118        return 201;
119    }
120
121    public function accepts($mode)
122    {
123        return syntax_plugin_combo_preformatted::disablePreformatted($mode);
124    }
125
126
127    function connectTo($mode)
128    {
129
130
131        $pattern = PluginUtility::getContainerTagPattern(self::TAG);
132        $this->Lexer->addEntryPattern($pattern, $mode, PluginUtility::getModeFromTag($this->getPluginComponent()));
133
134
135    }
136
137
138    public function postConnect()
139    {
140
141        $this->Lexer->addExitPattern('</' . self::TAG . '>', PluginUtility::getModeFromTag($this->getPluginComponent()));
142
143
144    }
145
146
147    /**
148     *
149     * The handle function goal is to parse the matched syntax through the pattern function
150     * and to return the result for use in the renderer
151     * This result is always cached until the page is modified.
152     * @param string $match
153     * @param int $state
154     * @param int $pos - byte position in the original source file
155     * @param Doku_Handler $handler
156     * @return array|bool
157     * @throws Exception
158     * @see DokuWiki_Syntax_Plugin::handle()
159     *
160     */
161    function handle($match, $state, $pos, Doku_Handler $handler)
162    {
163
164        switch ($state) {
165
166            case DOKU_LEXER_ENTER :
167
168                $tagAttributes = TagAttributes::createFromTagMatch($match);
169                $callStackArray = $tagAttributes->toCallStackArray();
170                return array(
171                    PluginUtility::STATE => $state,
172                    PluginUtility::ATTRIBUTES => $callStackArray
173                );
174
175            case DOKU_LEXER_UNMATCHED :
176
177                // We should not ever come here but a user does not not known that
178                return PluginUtility::handleAndReturnUnmatchedData(self::TAG, $match, $handler);
179
180
181            case DOKU_LEXER_EXIT :
182
183                /**
184                 * Do we need to delete all call because the data returns no rows
185                 */
186                $callStack = CallStack::createFromHandler($handler);
187                $openingCall = $callStack->moveToPreviousCorrespondingOpeningCall();
188                if($openingCall->getAttribute(self::EMPTY_ROWS_COUNT_ATTRIBUTE,false)){
189                    $callStack->deleteAllCallsAfter($openingCall);
190                }
191                return array(PluginUtility::STATE => $state);
192
193        }
194        return array();
195
196    }
197
198    /**
199     * Render the output
200     * @param string $format
201     * @param Doku_Renderer $renderer
202     * @param array $data - what the function handle() return'ed
203     * @return boolean - rendered correctly? (however, returned value is not used at the moment)
204     * @see DokuWiki_Syntax_Plugin::render()
205     *
206     *
207     */
208    function render($format, Doku_Renderer $renderer, $data)
209    {
210        // unsupported $mode
211        return false;
212    }
213
214
215}
216
217