xref: /plugin/combo/syntax/iterator.php (revision 4cadd4f8c541149bdda95f080e38a6d4e3a640ca)
137748cd8SNickeau<?php
237748cd8SNickeau
337748cd8SNickeau
4*4cadd4f8SNickeauuse ComboStrap\CacheManager;
5*4cadd4f8SNickeauuse ComboStrap\CacheDependencies;
6*4cadd4f8SNickeauuse ComboStrap\Call;
737748cd8SNickeauuse ComboStrap\CallStack;
8*4cadd4f8SNickeauuse ComboStrap\ExceptionCombo;
9*4cadd4f8SNickeauuse ComboStrap\LogUtility;
10*4cadd4f8SNickeauuse ComboStrap\Page;
11*4cadd4f8SNickeauuse ComboStrap\PageImages;
12*4cadd4f8SNickeauuse ComboStrap\PageSql;
13*4cadd4f8SNickeauuse ComboStrap\PageSqlTreeListener;
1437748cd8SNickeauuse ComboStrap\PluginUtility;
15*4cadd4f8SNickeauuse ComboStrap\Sqlite;
1637748cd8SNickeauuse ComboStrap\TagAttributes;
17*4cadd4f8SNickeauuse ComboStrap\Template;
18*4cadd4f8SNickeauuse ComboStrap\TemplateUtility;
1937748cd8SNickeau
2037748cd8SNickeaurequire_once(__DIR__ . '/../ComboStrap/PluginUtility.php');
2137748cd8SNickeau
2237748cd8SNickeau
2337748cd8SNickeau/**
2437748cd8SNickeau *
2537748cd8SNickeau * An iterator to iterate over templates.
2637748cd8SNickeau *
2737748cd8SNickeau * *******************
2837748cd8SNickeau * Iteration driver
2937748cd8SNickeau * *******************
3037748cd8SNickeau * The end tag of the template node is driving the iteration.
3137748cd8SNickeau * This way, the tags just after the template
3237748cd8SNickeau * sees them in the {@link CallStack} and can change their context
3337748cd8SNickeau *
3437748cd8SNickeau * For instance, a {@link syntax_plugin_combo_masonry}
3537748cd8SNickeau * component will change the context of all card inside it.
3637748cd8SNickeau *
3737748cd8SNickeau * ********************
3837748cd8SNickeau * Header and footer delimitation
3937748cd8SNickeau * ********************
4037748cd8SNickeau * The iterator delimits also the header and footer.
4137748cd8SNickeau * Some component needs the header to be generate completely.
4237748cd8SNickeau * This is the case of a complex markup such as a table
4337748cd8SNickeau *
4437748cd8SNickeau * ******************************
4537748cd8SNickeau * Delete if no data
4637748cd8SNickeau * ******************************
4737748cd8SNickeau * It gives also the possibility to {@link syntax_plugin_combo_iterator::EMPTY_ROWS_COUNT_ATTRIBUTE
4837748cd8SNickeau * delete the whole block}
4937748cd8SNickeau * (header and footer also) if there is no data
5037748cd8SNickeau *
5137748cd8SNickeau * *****************************
5237748cd8SNickeau * Always Contextual
5337748cd8SNickeau * *****************************
5437748cd8SNickeau * We don't capture the text markup such as in a {@link syntax_plugin_combo_code}
5537748cd8SNickeau * in order to loop because you can't pass the actual handler (ie callstack)
5637748cd8SNickeau * when you {@link p_get_instructions() parse again} a markup.
5737748cd8SNickeau *
5837748cd8SNickeau * The markup is then seen as a new single page without any context.
5937748cd8SNickeau * That may lead to problems.
6037748cd8SNickeau * Example: `heading` may then think that they are `outline heading` ...
6137748cd8SNickeau *
6237748cd8SNickeau */
6337748cd8SNickeauclass syntax_plugin_combo_iterator extends DokuWiki_Syntax_Plugin
6437748cd8SNickeau{
6537748cd8SNickeau
6637748cd8SNickeau    /**
6737748cd8SNickeau     * Tag in Dokuwiki cannot have a `-`
6837748cd8SNickeau     * This is the last part of the class
6937748cd8SNickeau     */
7037748cd8SNickeau    const TAG = "iterator";
7137748cd8SNickeau
7237748cd8SNickeau    /**
7337748cd8SNickeau     * Page canonical and tag pattern
7437748cd8SNickeau     */
7537748cd8SNickeau    const CANONICAL = "iterator";
76*4cadd4f8SNickeau    const PAGE_SQL = "page-sql";
77*4cadd4f8SNickeau    const VARIABLE_NAMES = "variable-names";
78*4cadd4f8SNickeau    const COMPLEX_MARKUP_FOUND = "complex-markup-found";
79*4cadd4f8SNickeau    const BEFORE_TEMPLATE_CALLSTACK = "header-callstack";
80*4cadd4f8SNickeau    const AFTER_TEMPLATE_CALLSTACK = "footer-callstack";
81*4cadd4f8SNickeau    const TEMPLATE_CALLSTACK = "template-callstack";
8237748cd8SNickeau
8337748cd8SNickeau
8437748cd8SNickeau    /**
8537748cd8SNickeau     * Syntax Type.
8637748cd8SNickeau     *
8737748cd8SNickeau     * Needs to return one of the mode types defined in $PARSER_MODES in parser.php
8837748cd8SNickeau     * @see https://www.dokuwiki.org/devel:syntax_plugins#syntax_types
8937748cd8SNickeau     * @see DokuWiki_Syntax_Plugin::getType()
9037748cd8SNickeau     */
91*4cadd4f8SNickeau    function getType(): string
9237748cd8SNickeau    {
9337748cd8SNickeau        return 'container';
9437748cd8SNickeau    }
9537748cd8SNickeau
9637748cd8SNickeau    /**
9737748cd8SNickeau     * How Dokuwiki will add P element
9837748cd8SNickeau     *
9937748cd8SNickeau     *  * 'normal' - The plugin can be used inside paragraphs (inline or inside)
10037748cd8SNickeau     *  * 'block'  - Open paragraphs need to be closed before plugin output (box) - block should not be inside paragraphs
10137748cd8SNickeau     *  * 'stack'  - Special case. Plugin wraps other paragraphs. - Stacks can contain paragraphs
10237748cd8SNickeau     *
10337748cd8SNickeau     * @see DokuWiki_Syntax_Plugin::getPType()
10437748cd8SNickeau     * @see https://www.dokuwiki.org/devel:syntax_plugins#ptype
10537748cd8SNickeau     */
106*4cadd4f8SNickeau    function getPType(): string
10737748cd8SNickeau    {
10837748cd8SNickeau        return 'block';
10937748cd8SNickeau    }
11037748cd8SNickeau
11137748cd8SNickeau    /**
11237748cd8SNickeau     * @return array
11337748cd8SNickeau     * Allow which kind of plugin inside
11437748cd8SNickeau     *
11537748cd8SNickeau     * No one of array('baseonly','container', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs')
11637748cd8SNickeau     * because we manage self the content and we call self the parser
11737748cd8SNickeau     *
11837748cd8SNickeau     * Return an array of one or more of the mode types {@link $PARSER_MODES} in Parser.php
11937748cd8SNickeau     */
120*4cadd4f8SNickeau    function getAllowedTypes(): array
12137748cd8SNickeau    {
12237748cd8SNickeau        return array('container', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs');
12337748cd8SNickeau    }
12437748cd8SNickeau
125*4cadd4f8SNickeau    function getSort(): int
12637748cd8SNickeau    {
12737748cd8SNickeau        return 201;
12837748cd8SNickeau    }
12937748cd8SNickeau
130*4cadd4f8SNickeau    public function accepts($mode): bool
13137748cd8SNickeau    {
13237748cd8SNickeau        return syntax_plugin_combo_preformatted::disablePreformatted($mode);
13337748cd8SNickeau    }
13437748cd8SNickeau
13537748cd8SNickeau
13637748cd8SNickeau    function connectTo($mode)
13737748cd8SNickeau    {
13837748cd8SNickeau
13937748cd8SNickeau
14037748cd8SNickeau        $pattern = PluginUtility::getContainerTagPattern(self::TAG);
14137748cd8SNickeau        $this->Lexer->addEntryPattern($pattern, $mode, PluginUtility::getModeFromTag($this->getPluginComponent()));
14237748cd8SNickeau
14337748cd8SNickeau
14437748cd8SNickeau    }
14537748cd8SNickeau
14637748cd8SNickeau
14737748cd8SNickeau    public function postConnect()
14837748cd8SNickeau    {
14937748cd8SNickeau
15037748cd8SNickeau        $this->Lexer->addExitPattern('</' . self::TAG . '>', PluginUtility::getModeFromTag($this->getPluginComponent()));
15137748cd8SNickeau
15237748cd8SNickeau
15337748cd8SNickeau    }
15437748cd8SNickeau
15537748cd8SNickeau
15637748cd8SNickeau    /**
15737748cd8SNickeau     *
15837748cd8SNickeau     * The handle function goal is to parse the matched syntax through the pattern function
15937748cd8SNickeau     * and to return the result for use in the renderer
16037748cd8SNickeau     * This result is always cached until the page is modified.
16137748cd8SNickeau     * @param string $match
16237748cd8SNickeau     * @param int $state
16337748cd8SNickeau     * @param int $pos - byte position in the original source file
16437748cd8SNickeau     * @param Doku_Handler $handler
165*4cadd4f8SNickeau     * @return array
16637748cd8SNickeau     * @see DokuWiki_Syntax_Plugin::handle()
16737748cd8SNickeau     *
16837748cd8SNickeau     */
169*4cadd4f8SNickeau    function handle($match, $state, $pos, Doku_Handler $handler): array
17037748cd8SNickeau    {
17137748cd8SNickeau
17237748cd8SNickeau        switch ($state) {
17337748cd8SNickeau
17437748cd8SNickeau            case DOKU_LEXER_ENTER :
17537748cd8SNickeau
17637748cd8SNickeau                $tagAttributes = TagAttributes::createFromTagMatch($match);
17737748cd8SNickeau                $callStackArray = $tagAttributes->toCallStackArray();
17837748cd8SNickeau                return array(
17937748cd8SNickeau                    PluginUtility::STATE => $state,
18037748cd8SNickeau                    PluginUtility::ATTRIBUTES => $callStackArray
18137748cd8SNickeau                );
18237748cd8SNickeau
18337748cd8SNickeau            case DOKU_LEXER_UNMATCHED :
18437748cd8SNickeau
18537748cd8SNickeau                // We should not ever come here but a user does not not known that
18637748cd8SNickeau                return PluginUtility::handleAndReturnUnmatchedData(self::TAG, $match, $handler);
18737748cd8SNickeau
18837748cd8SNickeau
18937748cd8SNickeau            case DOKU_LEXER_EXIT :
19037748cd8SNickeau
1911fa8c418SNickeau                $callStack = CallStack::createFromHandler($handler);
192*4cadd4f8SNickeau                $openTag = $callStack->moveToPreviousCorrespondingOpeningCall();
193*4cadd4f8SNickeau                /**
194*4cadd4f8SNickeau                 * Scanning the callstack and extracting the information
195*4cadd4f8SNickeau                 * such as sql and template instructions
196*4cadd4f8SNickeau                 */
197*4cadd4f8SNickeau                $pageSql = null;
198*4cadd4f8SNickeau                $beforeTemplateCallStack = [];
199*4cadd4f8SNickeau                $templateStack = [];
200*4cadd4f8SNickeau                $afterTemplateCallStack = [];
201*4cadd4f8SNickeau                $parsingState = "before";
202*4cadd4f8SNickeau                $complexMarkupFound = false;
203*4cadd4f8SNickeau                $variableNames = [];
204*4cadd4f8SNickeau                while ($actualCall = $callStack->next()) {
205*4cadd4f8SNickeau                    $tagName = $actualCall->getTagName();
206*4cadd4f8SNickeau                    switch ($tagName) {
207*4cadd4f8SNickeau                        case syntax_plugin_combo_iteratordata::TAG:
208*4cadd4f8SNickeau                            if ($actualCall->getState() === DOKU_LEXER_UNMATCHED) {
209*4cadd4f8SNickeau                                $pageSql = $actualCall->getCapturedContent();
2101fa8c418SNickeau                            }
211*4cadd4f8SNickeau                            continue 2;
212*4cadd4f8SNickeau                        case syntax_plugin_combo_template::TAG:
213*4cadd4f8SNickeau                            $parsingState = "after";
214*4cadd4f8SNickeau                            if ($actualCall->getState() === DOKU_LEXER_EXIT) {
215*4cadd4f8SNickeau                                $templateStack = $actualCall->getPluginData(syntax_plugin_combo_template::CALLSTACK);
216*4cadd4f8SNickeau                                /**
217*4cadd4f8SNickeau                                 * Do we have markup where the instructions should be generated at once
218*4cadd4f8SNickeau                                 * and not line by line
219*4cadd4f8SNickeau                                 *
220*4cadd4f8SNickeau                                 * ie a list or a table
221*4cadd4f8SNickeau                                 */
222*4cadd4f8SNickeau                                foreach ($templateStack as $templateInstructions) {
223*4cadd4f8SNickeau                                    $templateCall = Call::createFromInstruction($templateInstructions);
224*4cadd4f8SNickeau                                    if (in_array($templateCall->getComponentName(), Call::BLOCK_MARKUP_DOKUWIKI_COMPONENTS)) {
225*4cadd4f8SNickeau                                        $complexMarkupFound = true;
226*4cadd4f8SNickeau                                    }
227*4cadd4f8SNickeau
228*4cadd4f8SNickeau                                    /**
229*4cadd4f8SNickeau                                     * Capture variable names
230*4cadd4f8SNickeau                                     * to be able to find their value
231*4cadd4f8SNickeau                                     * in the metadata if they are not in sql
232*4cadd4f8SNickeau                                     */
233*4cadd4f8SNickeau                                    $textWithVariables = $templateCall->getCapturedContent();
234*4cadd4f8SNickeau                                    $attributes = $templateCall->getAttributes();
235*4cadd4f8SNickeau                                    if ($attributes !== null) {
236*4cadd4f8SNickeau                                        $sep = " ";
237*4cadd4f8SNickeau                                        foreach ($attributes as $key => $attribute) {
238*4cadd4f8SNickeau                                            $textWithVariables .= $sep . $key . $sep . $attribute;
239*4cadd4f8SNickeau                                        }
240*4cadd4f8SNickeau                                    }
241*4cadd4f8SNickeau
242*4cadd4f8SNickeau                                    if (!empty($textWithVariables)) {
243*4cadd4f8SNickeau                                        $template = Template::create($textWithVariables);
244*4cadd4f8SNickeau                                        $variablesDetected = $template->getVariablesDetected();
245*4cadd4f8SNickeau                                        $variableNames = array_merge($variableNames, $variablesDetected);
246*4cadd4f8SNickeau                                    }
247*4cadd4f8SNickeau                                }
248*4cadd4f8SNickeau                            }
249*4cadd4f8SNickeau                            continue 2;
250*4cadd4f8SNickeau                        default:
251*4cadd4f8SNickeau                            if ($parsingState === "before") {
252*4cadd4f8SNickeau                                $beforeTemplateCallStack[] = $actualCall->toCallArray();
253*4cadd4f8SNickeau                            } else {
254*4cadd4f8SNickeau                                $afterTemplateCallStack[] = $actualCall->toCallArray();
255*4cadd4f8SNickeau                            };
256*4cadd4f8SNickeau                            break;
257*4cadd4f8SNickeau                    }
258*4cadd4f8SNickeau                }
259*4cadd4f8SNickeau                $variableNames = array_unique($variableNames);
260*4cadd4f8SNickeau
261*4cadd4f8SNickeau                /**
262*4cadd4f8SNickeau                 * Wipe the content of iterator
263*4cadd4f8SNickeau                 */
264*4cadd4f8SNickeau                $callStack->deleteAllCallsAfter($openTag);
265*4cadd4f8SNickeau
266*4cadd4f8SNickeau                return array(
267*4cadd4f8SNickeau                    PluginUtility::STATE => $state,
268*4cadd4f8SNickeau                    self::PAGE_SQL => $pageSql,
269*4cadd4f8SNickeau                    self::VARIABLE_NAMES => $variableNames,
270*4cadd4f8SNickeau                    self::COMPLEX_MARKUP_FOUND => $complexMarkupFound,
271*4cadd4f8SNickeau                    self::BEFORE_TEMPLATE_CALLSTACK => $beforeTemplateCallStack,
272*4cadd4f8SNickeau                    self::AFTER_TEMPLATE_CALLSTACK => $afterTemplateCallStack,
273*4cadd4f8SNickeau                    self::TEMPLATE_CALLSTACK => $templateStack
274*4cadd4f8SNickeau                );
27537748cd8SNickeau
27637748cd8SNickeau        }
27737748cd8SNickeau        return array();
27837748cd8SNickeau
27937748cd8SNickeau    }
28037748cd8SNickeau
28137748cd8SNickeau    /**
28237748cd8SNickeau     * Render the output
28337748cd8SNickeau     * @param string $format
28437748cd8SNickeau     * @param Doku_Renderer $renderer
28537748cd8SNickeau     * @param array $data - what the function handle() return'ed
28637748cd8SNickeau     * @return boolean - rendered correctly? (however, returned value is not used at the moment)
28737748cd8SNickeau     * @see DokuWiki_Syntax_Plugin::render()
28837748cd8SNickeau     *
28937748cd8SNickeau     *
29037748cd8SNickeau     */
291*4cadd4f8SNickeau    function render($format, Doku_Renderer $renderer, $data): bool
29237748cd8SNickeau    {
293*4cadd4f8SNickeau        if ($format === "xhtml") {
294*4cadd4f8SNickeau            $state = $data[PluginUtility::STATE];
295*4cadd4f8SNickeau            switch ($state) {
296*4cadd4f8SNickeau                case DOKU_LEXER_ENTER:
297*4cadd4f8SNickeau                    return true;
298*4cadd4f8SNickeau                case DOKU_LEXER_UNMATCHED:
299*4cadd4f8SNickeau                    $renderer->doc .= PluginUtility::renderUnmatched($data);
300*4cadd4f8SNickeau                    return true;
301*4cadd4f8SNickeau                case DOKU_LEXER_EXIT:
302*4cadd4f8SNickeau
303*4cadd4f8SNickeau                    $pageSql = $data[self::PAGE_SQL];
304*4cadd4f8SNickeau
305*4cadd4f8SNickeau                    /**
306*4cadd4f8SNickeau                     * Data Processing
307*4cadd4f8SNickeau                     */
308*4cadd4f8SNickeau                    if ($pageSql === null) {
309*4cadd4f8SNickeau                        $renderer->doc .= "A data node could not be found in the iterator";
310*4cadd4f8SNickeau                        return false;
311*4cadd4f8SNickeau                    }
312*4cadd4f8SNickeau                    if (empty($pageSql)) {
313*4cadd4f8SNickeau                        $renderer->doc .= "The data node definition needs a logical sql content";
314*4cadd4f8SNickeau                        return false;
315*4cadd4f8SNickeau                    }
316*4cadd4f8SNickeau
317*4cadd4f8SNickeau                    /**
318*4cadd4f8SNickeau                     * Sqlite available ?
319*4cadd4f8SNickeau                     */
320*4cadd4f8SNickeau                    $sqlite = Sqlite::createOrGetSqlite();
321*4cadd4f8SNickeau                    if ($sqlite === null) {
322*4cadd4f8SNickeau                        $renderer->doc .= "The iterator component needs Sqlite to be able to work";
323*4cadd4f8SNickeau                        return false;
324*4cadd4f8SNickeau                    }
325*4cadd4f8SNickeau
326*4cadd4f8SNickeau
327*4cadd4f8SNickeau                    /**
328*4cadd4f8SNickeau                     * Create the SQL
329*4cadd4f8SNickeau                     */
330*4cadd4f8SNickeau                    try {
331*4cadd4f8SNickeau                        $pageSql = PageSql::create($pageSql);
332*4cadd4f8SNickeau                    } catch (Exception $e) {
333*4cadd4f8SNickeau                        $renderer->doc .= "The page sql is not valid. Error Message: {$e->getMessage()}. Page Sql: ($pageSql)";
334*4cadd4f8SNickeau                        return false;
335*4cadd4f8SNickeau                    }
336*4cadd4f8SNickeau
337*4cadd4f8SNickeau                    $table = $pageSql->getTable();
338*4cadd4f8SNickeau                    $cacheManager = CacheManager::getOrCreate();
339*4cadd4f8SNickeau                    switch ($table) {
340*4cadd4f8SNickeau                        case PageSqlTreeListener::BACKLINKS:
341*4cadd4f8SNickeau                            $cacheManager->addDependencyForCurrentSlot(CacheDependencies::BACKLINKS_DEPENDENCY);
342*4cadd4f8SNickeau                            break;
343*4cadd4f8SNickeau                        default:
344*4cadd4f8SNickeau                    }
345*4cadd4f8SNickeau
346*4cadd4f8SNickeau                    /**
347*4cadd4f8SNickeau                     * Execute the generated SQL
348*4cadd4f8SNickeau                     */
349*4cadd4f8SNickeau                    try {
350*4cadd4f8SNickeau                        $executableSql = $pageSql->getExecutableSql();
351*4cadd4f8SNickeau                        $parameters = $pageSql->getParameters();
352*4cadd4f8SNickeau                        $request = $sqlite
353*4cadd4f8SNickeau                            ->createRequest()
354*4cadd4f8SNickeau                            ->setQueryParametrized($executableSql, $parameters);
355*4cadd4f8SNickeau                        $rowsInDb = [];
356*4cadd4f8SNickeau                        try {
357*4cadd4f8SNickeau                            $rowsInDb = $request
358*4cadd4f8SNickeau                                ->execute()
359*4cadd4f8SNickeau                                ->getRows();
360*4cadd4f8SNickeau                        } catch (ExceptionCombo $e) {
361*4cadd4f8SNickeau                            $renderer->doc .= "The sql statement generated returns an error. Sql statement: $executableSql";
362*4cadd4f8SNickeau                            return false;
363*4cadd4f8SNickeau                        } finally {
364*4cadd4f8SNickeau                            $request->close();
365*4cadd4f8SNickeau                        }
366*4cadd4f8SNickeau
367*4cadd4f8SNickeau                        $variableNames = $data[self::VARIABLE_NAMES];
368*4cadd4f8SNickeau                        $rows = [];
369*4cadd4f8SNickeau                        foreach ($rowsInDb as $sourceRow) {
370*4cadd4f8SNickeau                            $analytics = $sourceRow["ANALYTICS"];
371*4cadd4f8SNickeau                            /**
372*4cadd4f8SNickeau                             * @deprecated
373*4cadd4f8SNickeau                             * We use id until path is full in the database
374*4cadd4f8SNickeau                             */
375*4cadd4f8SNickeau                            $id = $sourceRow["ID"];
376*4cadd4f8SNickeau                            $page = Page::createPageFromId($id);
377*4cadd4f8SNickeau                            if ($page->isHidden()) {
378*4cadd4f8SNickeau                                continue;
379*4cadd4f8SNickeau                            }
380*4cadd4f8SNickeau                            $standardMetadata = $page->getMetadataForRendering();
381*4cadd4f8SNickeau
382*4cadd4f8SNickeau                            $jsonArray = json_decode($analytics, true);
383*4cadd4f8SNickeau                            $targetRow = [];
384*4cadd4f8SNickeau                            foreach ($variableNames as $variableName) {
385*4cadd4f8SNickeau
386*4cadd4f8SNickeau                                if ($variableName === PageImages::PROPERTY_NAME) {
387*4cadd4f8SNickeau                                    LogUtility::msg("To add an image, you must use the page image component, not the image metadata", LogUtility::LVL_MSG_ERROR, syntax_plugin_combo_pageimage::CANONICAL);
388*4cadd4f8SNickeau                                    continue;
389*4cadd4f8SNickeau                                }
390*4cadd4f8SNickeau
391*4cadd4f8SNickeau                                /**
392*4cadd4f8SNickeau                                 * Data in the pages tables
393*4cadd4f8SNickeau                                 */
394*4cadd4f8SNickeau                                if (isset($sourceRow[strtoupper($variableName)])) {
395*4cadd4f8SNickeau                                    $variableValue = $sourceRow[strtoupper($variableName)];
396*4cadd4f8SNickeau                                    $targetRow[$variableName] = $variableValue;
397*4cadd4f8SNickeau                                    continue;
398*4cadd4f8SNickeau                                }
399*4cadd4f8SNickeau
400*4cadd4f8SNickeau                                /**
401*4cadd4f8SNickeau                                 * In the analytics
402*4cadd4f8SNickeau                                 */
403*4cadd4f8SNickeau                                $value = $jsonArray["metadata"][$variableName];
404*4cadd4f8SNickeau                                if (!empty($value)) {
405*4cadd4f8SNickeau                                    $targetRow[$variableName] = $value;
406*4cadd4f8SNickeau                                    continue;
407*4cadd4f8SNickeau                                }
408*4cadd4f8SNickeau
409*4cadd4f8SNickeau                                /**
410*4cadd4f8SNickeau                                 * Computed
411*4cadd4f8SNickeau                                 * (if the table is empty because of migration)
412*4cadd4f8SNickeau                                 */
413*4cadd4f8SNickeau                                $value = $standardMetadata[$variableName];
414*4cadd4f8SNickeau                                if (isset($value)) {
415*4cadd4f8SNickeau                                    $targetRow[$variableName] = $value;
416*4cadd4f8SNickeau                                    continue;
417*4cadd4f8SNickeau                                }
418*4cadd4f8SNickeau
419*4cadd4f8SNickeau                                /**
420*4cadd4f8SNickeau                                 * Bad luck
421*4cadd4f8SNickeau                                 */
422*4cadd4f8SNickeau                                $targetRow[$variableName] = "$variableName attribute is unknown.";
423*4cadd4f8SNickeau
424*4cadd4f8SNickeau
425*4cadd4f8SNickeau                            }
426*4cadd4f8SNickeau                            $rows[] = $targetRow;
427*4cadd4f8SNickeau                        }
428*4cadd4f8SNickeau                    } catch (Exception $e) {
429*4cadd4f8SNickeau                        $renderer->doc .= "Error during Sql Execution. Error: {$e->getMessage()}";
430*4cadd4f8SNickeau                        return false;
431*4cadd4f8SNickeau                    }
432*4cadd4f8SNickeau
433*4cadd4f8SNickeau
434*4cadd4f8SNickeau                    /**
435*4cadd4f8SNickeau                     * Loop
436*4cadd4f8SNickeau                     */
437*4cadd4f8SNickeau                    $elementCounts = sizeof($rows);
438*4cadd4f8SNickeau                    if ($elementCounts === 0) {
439*4cadd4f8SNickeau                        $parametersString = implode(", ", $parameters);
440*4cadd4f8SNickeau                        LogUtility::msg("The physical query (Sql: {$pageSql->getExecutableSql()}, Parameters: $parametersString) does not return any data", LogUtility::LVL_MSG_INFO, syntax_plugin_combo_iterator::CANONICAL);
441*4cadd4f8SNickeau                        return true;
442*4cadd4f8SNickeau                    }
443*4cadd4f8SNickeau
444*4cadd4f8SNickeau
445*4cadd4f8SNickeau                    /**
446*4cadd4f8SNickeau                     * Template stack processing
447*4cadd4f8SNickeau                     */
448*4cadd4f8SNickeau                    $iteratorTemplateInstructions = $data[self::TEMPLATE_CALLSTACK];
449*4cadd4f8SNickeau                    if ($iteratorTemplateInstructions === null) {
450*4cadd4f8SNickeau                        $renderer->doc .= "No template was found in this iterator.";
451*4cadd4f8SNickeau                        return false;
452*4cadd4f8SNickeau                    }
453*4cadd4f8SNickeau                    $iteratorHeaderInstructions = $data[self::BEFORE_TEMPLATE_CALLSTACK];
454*4cadd4f8SNickeau
455*4cadd4f8SNickeau
456*4cadd4f8SNickeau                    $iteratorTemplateGeneratedInstructions = [];
457*4cadd4f8SNickeau
458*4cadd4f8SNickeau
459*4cadd4f8SNickeau                    /**
460*4cadd4f8SNickeau                     * List and table syntax in template ?
461*4cadd4f8SNickeau                     */
462*4cadd4f8SNickeau                    $complexMarkupFound = $data[self::COMPLEX_MARKUP_FOUND];
463*4cadd4f8SNickeau                    if ($complexMarkupFound) {
464*4cadd4f8SNickeau
465*4cadd4f8SNickeau                        /**
466*4cadd4f8SNickeau                         * Splits the template into header, main and footer
467*4cadd4f8SNickeau                         * @var Call $actualCall
468*4cadd4f8SNickeau                         */
469*4cadd4f8SNickeau                        $templateCallStack = CallStack::createFromInstructions($iteratorTemplateInstructions);
470*4cadd4f8SNickeau                        $templateHeader = array();
471*4cadd4f8SNickeau                        $templateMain = array();
472*4cadd4f8SNickeau                        $actualStack = array();
473*4cadd4f8SNickeau                        $templateCallStack->moveToStart();
474*4cadd4f8SNickeau                        while ($actualCall = $templateCallStack->next()) {
475*4cadd4f8SNickeau                            switch ($actualCall->getComponentName()) {
476*4cadd4f8SNickeau                                case "listitem_open":
477*4cadd4f8SNickeau                                case "tablerow_open":
478*4cadd4f8SNickeau                                    $templateHeader = $actualStack;
479*4cadd4f8SNickeau                                    $actualStack = [$actualCall];
480*4cadd4f8SNickeau                                    continue 2;
481*4cadd4f8SNickeau                                case "listitem_close":
482*4cadd4f8SNickeau                                case "tablerow_close":
483*4cadd4f8SNickeau                                    $actualStack[] = $actualCall;
484*4cadd4f8SNickeau                                    $templateMain = $actualStack;
485*4cadd4f8SNickeau                                    $actualStack = [];
486*4cadd4f8SNickeau                                    continue 2;
487*4cadd4f8SNickeau                                default:
488*4cadd4f8SNickeau                                    $actualStack[] = $actualCall;
489*4cadd4f8SNickeau                            }
490*4cadd4f8SNickeau                        }
491*4cadd4f8SNickeau                        $templateFooter = $actualStack;
492*4cadd4f8SNickeau
493*4cadd4f8SNickeau                        /**
494*4cadd4f8SNickeau                         * Table with an header
495*4cadd4f8SNickeau                         * If this is the case, the table_close of the header
496*4cadd4f8SNickeau                         * and the table_open of the template should be
497*4cadd4f8SNickeau                         * deleted to create one table
498*4cadd4f8SNickeau                         */
499*4cadd4f8SNickeau                        if (!empty($templateHeader)) {
500*4cadd4f8SNickeau                            $firstTemplateCall = $templateHeader[0];
501*4cadd4f8SNickeau                            if ($firstTemplateCall->getComponentName() === "table_open") {
502*4cadd4f8SNickeau                                $lastIterationHeaderElement = sizeof($iteratorHeaderInstructions) - 1;
503*4cadd4f8SNickeau                                $lastIterationHeaderInstruction = Call::createFromInstruction($iteratorHeaderInstructions[$lastIterationHeaderElement]);
504*4cadd4f8SNickeau                                if ($lastIterationHeaderInstruction->getComponentName() === "table_close") {
505*4cadd4f8SNickeau                                    unset($iteratorHeaderInstructions[$lastIterationHeaderElement]);
506*4cadd4f8SNickeau                                    unset($templateHeader[0]);
507*4cadd4f8SNickeau                                }
508*4cadd4f8SNickeau                            }
509*4cadd4f8SNickeau                        }
510*4cadd4f8SNickeau
511*4cadd4f8SNickeau                        /**
512*4cadd4f8SNickeau                         * Loop and recreate the call stack in instructions  form for rendering
513*4cadd4f8SNickeau                         */
514*4cadd4f8SNickeau                        $iteratorTemplateGeneratedInstructions = [];
515*4cadd4f8SNickeau                        foreach ($templateHeader as $templateHeaderCall) {
516*4cadd4f8SNickeau                            $iteratorTemplateGeneratedInstructions[] = $templateHeaderCall->toCallArray();
517*4cadd4f8SNickeau                        }
518*4cadd4f8SNickeau                        foreach ($rows as $row) {
519*4cadd4f8SNickeau                            $templateInstructionForInstance = TemplateUtility::renderInstructionsTemplateFromDataArray($templateMain, $row);
520*4cadd4f8SNickeau                            $iteratorTemplateGeneratedInstructions = array_merge($iteratorTemplateGeneratedInstructions, $templateInstructionForInstance);
521*4cadd4f8SNickeau                        }
522*4cadd4f8SNickeau                        foreach ($templateFooter as $templateFooterCall) {
523*4cadd4f8SNickeau                            $iteratorTemplateGeneratedInstructions[] = $templateFooterCall->toCallArray();
524*4cadd4f8SNickeau                        }
525*4cadd4f8SNickeau
526*4cadd4f8SNickeau
527*4cadd4f8SNickeau                    } else {
528*4cadd4f8SNickeau
529*4cadd4f8SNickeau                        /**
530*4cadd4f8SNickeau                         * No Complex Markup
531*4cadd4f8SNickeau                         * We can use the calls form
532*4cadd4f8SNickeau                         */
533*4cadd4f8SNickeau
534*4cadd4f8SNickeau
535*4cadd4f8SNickeau                        /**
536*4cadd4f8SNickeau                         * Append the new instructions by row
537*4cadd4f8SNickeau                         */
538*4cadd4f8SNickeau                        foreach ($rows as $row) {
539*4cadd4f8SNickeau                            $templateInstructionForInstance = TemplateUtility::renderInstructionsTemplateFromDataArray($iteratorTemplateInstructions, $row);
540*4cadd4f8SNickeau                            $iteratorTemplateGeneratedInstructions = array_merge($iteratorTemplateGeneratedInstructions, $templateInstructionForInstance);
541*4cadd4f8SNickeau                        }
542*4cadd4f8SNickeau
543*4cadd4f8SNickeau
544*4cadd4f8SNickeau                    }
545*4cadd4f8SNickeau                    /**
546*4cadd4f8SNickeau                     * Rendering
547*4cadd4f8SNickeau                     */
548*4cadd4f8SNickeau                    $totalInstructions = [];
549*4cadd4f8SNickeau                    // header
550*4cadd4f8SNickeau                    if (!empty($iteratorHeaderInstructions)) {
551*4cadd4f8SNickeau                        $totalInstructions = $iteratorHeaderInstructions;
552*4cadd4f8SNickeau                    }
553*4cadd4f8SNickeau                    // content
554*4cadd4f8SNickeau                    if (!empty($iteratorTemplateGeneratedInstructions)) {
555*4cadd4f8SNickeau                        $totalInstructions = array_merge($totalInstructions, $iteratorTemplateGeneratedInstructions);
556*4cadd4f8SNickeau                    }
557*4cadd4f8SNickeau                    // footer
558*4cadd4f8SNickeau                    $callStackFooterInstructions = $data[self::AFTER_TEMPLATE_CALLSTACK];
559*4cadd4f8SNickeau                    if (!empty($callStackFooterInstructions)) {
560*4cadd4f8SNickeau                        $totalInstructions = array_merge($totalInstructions, $callStackFooterInstructions);
561*4cadd4f8SNickeau                    }
562*4cadd4f8SNickeau                    if (!empty($totalInstructions)) {
563*4cadd4f8SNickeau
564*4cadd4f8SNickeau                        /**
565*4cadd4f8SNickeau                         * Advertise the total count to the
566*4cadd4f8SNickeau                         * {@link syntax_plugin_combo_carrousel}
567*4cadd4f8SNickeau                         * for the bullets if any
568*4cadd4f8SNickeau                         */
569*4cadd4f8SNickeau                        $totalCallStack = CallStack::createFromInstructions($totalInstructions);
570*4cadd4f8SNickeau                        $totalCallStack->moveToEnd();
571*4cadd4f8SNickeau                        while ($actualCall = $totalCallStack->previous()) {
572*4cadd4f8SNickeau                            if (
573*4cadd4f8SNickeau                                $actualCall->getTagName() === syntax_plugin_combo_carrousel::TAG
574*4cadd4f8SNickeau                                && in_array($actualCall->getState(), [DOKU_LEXER_ENTER, DOKU_LEXER_EXIT])
575*4cadd4f8SNickeau                            ) {
576*4cadd4f8SNickeau                                $actualCall->setPluginData(syntax_plugin_combo_carrousel::ELEMENT_COUNT, $elementCounts);
577*4cadd4f8SNickeau                                if ($actualCall->getState() === DOKU_LEXER_ENTER) {
578*4cadd4f8SNickeau                                    break;
579*4cadd4f8SNickeau                                }
580*4cadd4f8SNickeau                            }
581*4cadd4f8SNickeau                        }
582*4cadd4f8SNickeau
583*4cadd4f8SNickeau                        try {
584*4cadd4f8SNickeau                            $renderer->doc .= PluginUtility::renderInstructionsToXhtml($totalCallStack->getStack());
585*4cadd4f8SNickeau                        } catch (ExceptionCombo $e) {
586*4cadd4f8SNickeau                            $renderer->doc .= "Error while rendering the iterators instructions. Error: {$e->getMessage()}";
587*4cadd4f8SNickeau                        }
588*4cadd4f8SNickeau                    }
589*4cadd4f8SNickeau                    return true;
590*4cadd4f8SNickeau            }
591*4cadd4f8SNickeau        }
59237748cd8SNickeau        // unsupported $mode
59337748cd8SNickeau        return false;
59437748cd8SNickeau    }
59537748cd8SNickeau
59637748cd8SNickeau
59737748cd8SNickeau}
59837748cd8SNickeau
599