137748cd8SNickeau<?php 237748cd8SNickeau 337748cd8SNickeau 44cadd4f8SNickeauuse ComboStrap\CacheManager; 54cadd4f8SNickeauuse ComboStrap\CacheDependencies; 64cadd4f8SNickeauuse ComboStrap\Call; 737748cd8SNickeauuse ComboStrap\CallStack; 84cadd4f8SNickeauuse ComboStrap\ExceptionCombo; 94cadd4f8SNickeauuse ComboStrap\LogUtility; 104cadd4f8SNickeauuse ComboStrap\Page; 114cadd4f8SNickeauuse ComboStrap\PageImages; 124cadd4f8SNickeauuse ComboStrap\PageSql; 134cadd4f8SNickeauuse ComboStrap\PageSqlTreeListener; 1437748cd8SNickeauuse ComboStrap\PluginUtility; 154cadd4f8SNickeauuse ComboStrap\Sqlite; 1637748cd8SNickeauuse ComboStrap\TagAttributes; 174cadd4f8SNickeauuse ComboStrap\Template; 184cadd4f8SNickeauuse 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"; 764cadd4f8SNickeau const PAGE_SQL = "page-sql"; 774cadd4f8SNickeau const VARIABLE_NAMES = "variable-names"; 784cadd4f8SNickeau const COMPLEX_MARKUP_FOUND = "complex-markup-found"; 794cadd4f8SNickeau const BEFORE_TEMPLATE_CALLSTACK = "header-callstack"; 804cadd4f8SNickeau const AFTER_TEMPLATE_CALLSTACK = "footer-callstack"; 814cadd4f8SNickeau 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 */ 914cadd4f8SNickeau 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 */ 1064cadd4f8SNickeau 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 */ 1204cadd4f8SNickeau function getAllowedTypes(): array 12137748cd8SNickeau { 12237748cd8SNickeau return array('container', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs'); 12337748cd8SNickeau } 12437748cd8SNickeau 1254cadd4f8SNickeau function getSort(): int 12637748cd8SNickeau { 12737748cd8SNickeau return 201; 12837748cd8SNickeau } 12937748cd8SNickeau 1304cadd4f8SNickeau 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 1654cadd4f8SNickeau * @return array 16637748cd8SNickeau * @see DokuWiki_Syntax_Plugin::handle() 16737748cd8SNickeau * 16837748cd8SNickeau */ 1694cadd4f8SNickeau 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); 1924cadd4f8SNickeau $openTag = $callStack->moveToPreviousCorrespondingOpeningCall(); 1934cadd4f8SNickeau /** 1944cadd4f8SNickeau * Scanning the callstack and extracting the information 1954cadd4f8SNickeau * such as sql and template instructions 1964cadd4f8SNickeau */ 1974cadd4f8SNickeau $pageSql = null; 1984cadd4f8SNickeau $beforeTemplateCallStack = []; 1994cadd4f8SNickeau $templateStack = []; 2004cadd4f8SNickeau $afterTemplateCallStack = []; 2014cadd4f8SNickeau $parsingState = "before"; 2024cadd4f8SNickeau $complexMarkupFound = false; 2034cadd4f8SNickeau $variableNames = []; 2044cadd4f8SNickeau while ($actualCall = $callStack->next()) { 2054cadd4f8SNickeau $tagName = $actualCall->getTagName(); 2064cadd4f8SNickeau switch ($tagName) { 2074cadd4f8SNickeau case syntax_plugin_combo_iteratordata::TAG: 2084cadd4f8SNickeau if ($actualCall->getState() === DOKU_LEXER_UNMATCHED) { 2094cadd4f8SNickeau $pageSql = $actualCall->getCapturedContent(); 2101fa8c418SNickeau } 2114cadd4f8SNickeau continue 2; 2124cadd4f8SNickeau case syntax_plugin_combo_template::TAG: 2134cadd4f8SNickeau $parsingState = "after"; 2144cadd4f8SNickeau if ($actualCall->getState() === DOKU_LEXER_EXIT) { 2154cadd4f8SNickeau $templateStack = $actualCall->getPluginData(syntax_plugin_combo_template::CALLSTACK); 2164cadd4f8SNickeau /** 2174cadd4f8SNickeau * Do we have markup where the instructions should be generated at once 2184cadd4f8SNickeau * and not line by line 2194cadd4f8SNickeau * 2204cadd4f8SNickeau * ie a list or a table 2214cadd4f8SNickeau */ 2224cadd4f8SNickeau foreach ($templateStack as $templateInstructions) { 2234cadd4f8SNickeau $templateCall = Call::createFromInstruction($templateInstructions); 2244cadd4f8SNickeau if (in_array($templateCall->getComponentName(), Call::BLOCK_MARKUP_DOKUWIKI_COMPONENTS)) { 2254cadd4f8SNickeau $complexMarkupFound = true; 2264cadd4f8SNickeau } 2274cadd4f8SNickeau 2284cadd4f8SNickeau /** 2294cadd4f8SNickeau * Capture variable names 2304cadd4f8SNickeau * to be able to find their value 2314cadd4f8SNickeau * in the metadata if they are not in sql 2324cadd4f8SNickeau */ 2334cadd4f8SNickeau $textWithVariables = $templateCall->getCapturedContent(); 2344cadd4f8SNickeau $attributes = $templateCall->getAttributes(); 2354cadd4f8SNickeau if ($attributes !== null) { 2364cadd4f8SNickeau $sep = " "; 2374cadd4f8SNickeau foreach ($attributes as $key => $attribute) { 2384cadd4f8SNickeau $textWithVariables .= $sep . $key . $sep . $attribute; 2394cadd4f8SNickeau } 2404cadd4f8SNickeau } 2414cadd4f8SNickeau 2424cadd4f8SNickeau if (!empty($textWithVariables)) { 2434cadd4f8SNickeau $template = Template::create($textWithVariables); 2444cadd4f8SNickeau $variablesDetected = $template->getVariablesDetected(); 2454cadd4f8SNickeau $variableNames = array_merge($variableNames, $variablesDetected); 2464cadd4f8SNickeau } 2474cadd4f8SNickeau } 2484cadd4f8SNickeau } 2494cadd4f8SNickeau continue 2; 2504cadd4f8SNickeau default: 2514cadd4f8SNickeau if ($parsingState === "before") { 2524cadd4f8SNickeau $beforeTemplateCallStack[] = $actualCall->toCallArray(); 2534cadd4f8SNickeau } else { 2544cadd4f8SNickeau $afterTemplateCallStack[] = $actualCall->toCallArray(); 2554cadd4f8SNickeau }; 2564cadd4f8SNickeau break; 2574cadd4f8SNickeau } 2584cadd4f8SNickeau } 2594cadd4f8SNickeau $variableNames = array_unique($variableNames); 2604cadd4f8SNickeau 2614cadd4f8SNickeau /** 2624cadd4f8SNickeau * Wipe the content of iterator 2634cadd4f8SNickeau */ 2644cadd4f8SNickeau $callStack->deleteAllCallsAfter($openTag); 2654cadd4f8SNickeau 2664cadd4f8SNickeau return array( 2674cadd4f8SNickeau PluginUtility::STATE => $state, 2684cadd4f8SNickeau self::PAGE_SQL => $pageSql, 2694cadd4f8SNickeau self::VARIABLE_NAMES => $variableNames, 2704cadd4f8SNickeau self::COMPLEX_MARKUP_FOUND => $complexMarkupFound, 2714cadd4f8SNickeau self::BEFORE_TEMPLATE_CALLSTACK => $beforeTemplateCallStack, 2724cadd4f8SNickeau self::AFTER_TEMPLATE_CALLSTACK => $afterTemplateCallStack, 2734cadd4f8SNickeau self::TEMPLATE_CALLSTACK => $templateStack 2744cadd4f8SNickeau ); 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 */ 2914cadd4f8SNickeau function render($format, Doku_Renderer $renderer, $data): bool 29237748cd8SNickeau { 2934cadd4f8SNickeau if ($format === "xhtml") { 2944cadd4f8SNickeau $state = $data[PluginUtility::STATE]; 2954cadd4f8SNickeau switch ($state) { 2964cadd4f8SNickeau case DOKU_LEXER_ENTER: 2974cadd4f8SNickeau return true; 2984cadd4f8SNickeau case DOKU_LEXER_UNMATCHED: 2994cadd4f8SNickeau $renderer->doc .= PluginUtility::renderUnmatched($data); 3004cadd4f8SNickeau return true; 3014cadd4f8SNickeau case DOKU_LEXER_EXIT: 3024cadd4f8SNickeau 3034cadd4f8SNickeau $pageSql = $data[self::PAGE_SQL]; 3044cadd4f8SNickeau 3054cadd4f8SNickeau /** 3064cadd4f8SNickeau * Data Processing 3074cadd4f8SNickeau */ 3084cadd4f8SNickeau if ($pageSql === null) { 309*a4e629a3Sgerardnico $renderer->doc .= "A data node could not be found as a child of the iterator."; 3104cadd4f8SNickeau return false; 3114cadd4f8SNickeau } 3124cadd4f8SNickeau if (empty($pageSql)) { 3134cadd4f8SNickeau $renderer->doc .= "The data node definition needs a logical sql content"; 3144cadd4f8SNickeau return false; 3154cadd4f8SNickeau } 3164cadd4f8SNickeau 3174cadd4f8SNickeau /** 3184cadd4f8SNickeau * Sqlite available ? 3194cadd4f8SNickeau */ 3204cadd4f8SNickeau $sqlite = Sqlite::createOrGetSqlite(); 3214cadd4f8SNickeau if ($sqlite === null) { 3224cadd4f8SNickeau $renderer->doc .= "The iterator component needs Sqlite to be able to work"; 3234cadd4f8SNickeau return false; 3244cadd4f8SNickeau } 3254cadd4f8SNickeau 3264cadd4f8SNickeau 3274cadd4f8SNickeau /** 3284cadd4f8SNickeau * Create the SQL 3294cadd4f8SNickeau */ 3304cadd4f8SNickeau try { 3314cadd4f8SNickeau $pageSql = PageSql::create($pageSql); 3324cadd4f8SNickeau } catch (Exception $e) { 3334cadd4f8SNickeau $renderer->doc .= "The page sql is not valid. Error Message: {$e->getMessage()}. Page Sql: ($pageSql)"; 3344cadd4f8SNickeau return false; 3354cadd4f8SNickeau } 3364cadd4f8SNickeau 3374cadd4f8SNickeau $table = $pageSql->getTable(); 3384cadd4f8SNickeau $cacheManager = CacheManager::getOrCreate(); 3394cadd4f8SNickeau switch ($table) { 3404cadd4f8SNickeau case PageSqlTreeListener::BACKLINKS: 3414cadd4f8SNickeau $cacheManager->addDependencyForCurrentSlot(CacheDependencies::BACKLINKS_DEPENDENCY); 3424cadd4f8SNickeau break; 3434cadd4f8SNickeau default: 3444cadd4f8SNickeau } 3454cadd4f8SNickeau 3464cadd4f8SNickeau /** 3474cadd4f8SNickeau * Execute the generated SQL 3484cadd4f8SNickeau */ 3494cadd4f8SNickeau try { 3504cadd4f8SNickeau $executableSql = $pageSql->getExecutableSql(); 3514cadd4f8SNickeau $parameters = $pageSql->getParameters(); 3524cadd4f8SNickeau $request = $sqlite 3534cadd4f8SNickeau ->createRequest() 3544cadd4f8SNickeau ->setQueryParametrized($executableSql, $parameters); 3554cadd4f8SNickeau $rowsInDb = []; 3564cadd4f8SNickeau try { 3574cadd4f8SNickeau $rowsInDb = $request 3584cadd4f8SNickeau ->execute() 3594cadd4f8SNickeau ->getRows(); 3604cadd4f8SNickeau } catch (ExceptionCombo $e) { 3614cadd4f8SNickeau $renderer->doc .= "The sql statement generated returns an error. Sql statement: $executableSql"; 3624cadd4f8SNickeau return false; 3634cadd4f8SNickeau } finally { 3644cadd4f8SNickeau $request->close(); 3654cadd4f8SNickeau } 3664cadd4f8SNickeau 3674cadd4f8SNickeau $variableNames = $data[self::VARIABLE_NAMES]; 3684cadd4f8SNickeau $rows = []; 3694cadd4f8SNickeau foreach ($rowsInDb as $sourceRow) { 3704cadd4f8SNickeau $analytics = $sourceRow["ANALYTICS"]; 3714cadd4f8SNickeau /** 3724cadd4f8SNickeau * @deprecated 3734cadd4f8SNickeau * We use id until path is full in the database 3744cadd4f8SNickeau */ 3754cadd4f8SNickeau $id = $sourceRow["ID"]; 3764cadd4f8SNickeau $page = Page::createPageFromId($id); 3774cadd4f8SNickeau if ($page->isHidden()) { 3784cadd4f8SNickeau continue; 3794cadd4f8SNickeau } 3804cadd4f8SNickeau $standardMetadata = $page->getMetadataForRendering(); 3814cadd4f8SNickeau 3824cadd4f8SNickeau $jsonArray = json_decode($analytics, true); 3834cadd4f8SNickeau $targetRow = []; 3844cadd4f8SNickeau foreach ($variableNames as $variableName) { 3854cadd4f8SNickeau 3864cadd4f8SNickeau if ($variableName === PageImages::PROPERTY_NAME) { 3874cadd4f8SNickeau 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); 3884cadd4f8SNickeau continue; 3894cadd4f8SNickeau } 3904cadd4f8SNickeau 3914cadd4f8SNickeau /** 3924cadd4f8SNickeau * Data in the pages tables 3934cadd4f8SNickeau */ 3944cadd4f8SNickeau if (isset($sourceRow[strtoupper($variableName)])) { 3954cadd4f8SNickeau $variableValue = $sourceRow[strtoupper($variableName)]; 3964cadd4f8SNickeau $targetRow[$variableName] = $variableValue; 3974cadd4f8SNickeau continue; 3984cadd4f8SNickeau } 3994cadd4f8SNickeau 4004cadd4f8SNickeau /** 4014cadd4f8SNickeau * In the analytics 4024cadd4f8SNickeau */ 4034cadd4f8SNickeau $value = $jsonArray["metadata"][$variableName]; 4044cadd4f8SNickeau if (!empty($value)) { 4054cadd4f8SNickeau $targetRow[$variableName] = $value; 4064cadd4f8SNickeau continue; 4074cadd4f8SNickeau } 4084cadd4f8SNickeau 4094cadd4f8SNickeau /** 4104cadd4f8SNickeau * Computed 4114cadd4f8SNickeau * (if the table is empty because of migration) 4124cadd4f8SNickeau */ 4134cadd4f8SNickeau $value = $standardMetadata[$variableName]; 4144cadd4f8SNickeau if (isset($value)) { 4154cadd4f8SNickeau $targetRow[$variableName] = $value; 4164cadd4f8SNickeau continue; 4174cadd4f8SNickeau } 4184cadd4f8SNickeau 4194cadd4f8SNickeau /** 4204cadd4f8SNickeau * Bad luck 4214cadd4f8SNickeau */ 4224cadd4f8SNickeau $targetRow[$variableName] = "$variableName attribute is unknown."; 4234cadd4f8SNickeau 4244cadd4f8SNickeau 4254cadd4f8SNickeau } 4264cadd4f8SNickeau $rows[] = $targetRow; 4274cadd4f8SNickeau } 4284cadd4f8SNickeau } catch (Exception $e) { 4294cadd4f8SNickeau $renderer->doc .= "Error during Sql Execution. Error: {$e->getMessage()}"; 4304cadd4f8SNickeau return false; 4314cadd4f8SNickeau } 4324cadd4f8SNickeau 4334cadd4f8SNickeau 4344cadd4f8SNickeau /** 4354cadd4f8SNickeau * Loop 4364cadd4f8SNickeau */ 4374cadd4f8SNickeau $elementCounts = sizeof($rows); 4384cadd4f8SNickeau if ($elementCounts === 0) { 4394cadd4f8SNickeau $parametersString = implode(", ", $parameters); 4404cadd4f8SNickeau LogUtility::msg("The physical query (Sql: {$pageSql->getExecutableSql()}, Parameters: $parametersString) does not return any data", LogUtility::LVL_MSG_INFO, syntax_plugin_combo_iterator::CANONICAL); 4414cadd4f8SNickeau return true; 4424cadd4f8SNickeau } 4434cadd4f8SNickeau 4444cadd4f8SNickeau 4454cadd4f8SNickeau /** 4464cadd4f8SNickeau * Template stack processing 4474cadd4f8SNickeau */ 4484cadd4f8SNickeau $iteratorTemplateInstructions = $data[self::TEMPLATE_CALLSTACK]; 4494cadd4f8SNickeau if ($iteratorTemplateInstructions === null) { 4504cadd4f8SNickeau $renderer->doc .= "No template was found in this iterator."; 4514cadd4f8SNickeau return false; 4524cadd4f8SNickeau } 4534cadd4f8SNickeau $iteratorHeaderInstructions = $data[self::BEFORE_TEMPLATE_CALLSTACK]; 4544cadd4f8SNickeau 4554cadd4f8SNickeau 4564cadd4f8SNickeau $iteratorTemplateGeneratedInstructions = []; 4574cadd4f8SNickeau 4584cadd4f8SNickeau 4594cadd4f8SNickeau /** 4604cadd4f8SNickeau * List and table syntax in template ? 4614cadd4f8SNickeau */ 4624cadd4f8SNickeau $complexMarkupFound = $data[self::COMPLEX_MARKUP_FOUND]; 4634cadd4f8SNickeau if ($complexMarkupFound) { 4644cadd4f8SNickeau 4654cadd4f8SNickeau /** 4664cadd4f8SNickeau * Splits the template into header, main and footer 4674cadd4f8SNickeau * @var Call $actualCall 4684cadd4f8SNickeau */ 4694cadd4f8SNickeau $templateCallStack = CallStack::createFromInstructions($iteratorTemplateInstructions); 4704cadd4f8SNickeau $templateHeader = array(); 4714cadd4f8SNickeau $templateMain = array(); 4724cadd4f8SNickeau $actualStack = array(); 4734cadd4f8SNickeau $templateCallStack->moveToStart(); 4744cadd4f8SNickeau while ($actualCall = $templateCallStack->next()) { 4754cadd4f8SNickeau switch ($actualCall->getComponentName()) { 4764cadd4f8SNickeau case "listitem_open": 4774cadd4f8SNickeau case "tablerow_open": 4784cadd4f8SNickeau $templateHeader = $actualStack; 4794cadd4f8SNickeau $actualStack = [$actualCall]; 4804cadd4f8SNickeau continue 2; 4814cadd4f8SNickeau case "listitem_close": 4824cadd4f8SNickeau case "tablerow_close": 4834cadd4f8SNickeau $actualStack[] = $actualCall; 4844cadd4f8SNickeau $templateMain = $actualStack; 4854cadd4f8SNickeau $actualStack = []; 4864cadd4f8SNickeau continue 2; 4874cadd4f8SNickeau default: 4884cadd4f8SNickeau $actualStack[] = $actualCall; 4894cadd4f8SNickeau } 4904cadd4f8SNickeau } 4914cadd4f8SNickeau $templateFooter = $actualStack; 4924cadd4f8SNickeau 4934cadd4f8SNickeau /** 4944cadd4f8SNickeau * Table with an header 4954cadd4f8SNickeau * If this is the case, the table_close of the header 4964cadd4f8SNickeau * and the table_open of the template should be 4974cadd4f8SNickeau * deleted to create one table 4984cadd4f8SNickeau */ 4994cadd4f8SNickeau if (!empty($templateHeader)) { 5004cadd4f8SNickeau $firstTemplateCall = $templateHeader[0]; 5014cadd4f8SNickeau if ($firstTemplateCall->getComponentName() === "table_open") { 5024cadd4f8SNickeau $lastIterationHeaderElement = sizeof($iteratorHeaderInstructions) - 1; 5034cadd4f8SNickeau $lastIterationHeaderInstruction = Call::createFromInstruction($iteratorHeaderInstructions[$lastIterationHeaderElement]); 5044cadd4f8SNickeau if ($lastIterationHeaderInstruction->getComponentName() === "table_close") { 5054cadd4f8SNickeau unset($iteratorHeaderInstructions[$lastIterationHeaderElement]); 5064cadd4f8SNickeau unset($templateHeader[0]); 5074cadd4f8SNickeau } 5084cadd4f8SNickeau } 5094cadd4f8SNickeau } 5104cadd4f8SNickeau 5114cadd4f8SNickeau /** 5124cadd4f8SNickeau * Loop and recreate the call stack in instructions form for rendering 5134cadd4f8SNickeau */ 5144cadd4f8SNickeau $iteratorTemplateGeneratedInstructions = []; 5154cadd4f8SNickeau foreach ($templateHeader as $templateHeaderCall) { 5164cadd4f8SNickeau $iteratorTemplateGeneratedInstructions[] = $templateHeaderCall->toCallArray(); 5174cadd4f8SNickeau } 5184cadd4f8SNickeau foreach ($rows as $row) { 5194cadd4f8SNickeau $templateInstructionForInstance = TemplateUtility::renderInstructionsTemplateFromDataArray($templateMain, $row); 5204cadd4f8SNickeau $iteratorTemplateGeneratedInstructions = array_merge($iteratorTemplateGeneratedInstructions, $templateInstructionForInstance); 5214cadd4f8SNickeau } 5224cadd4f8SNickeau foreach ($templateFooter as $templateFooterCall) { 5234cadd4f8SNickeau $iteratorTemplateGeneratedInstructions[] = $templateFooterCall->toCallArray(); 5244cadd4f8SNickeau } 5254cadd4f8SNickeau 5264cadd4f8SNickeau 5274cadd4f8SNickeau } else { 5284cadd4f8SNickeau 5294cadd4f8SNickeau /** 5304cadd4f8SNickeau * No Complex Markup 5314cadd4f8SNickeau * We can use the calls form 5324cadd4f8SNickeau */ 5334cadd4f8SNickeau 5344cadd4f8SNickeau 5354cadd4f8SNickeau /** 5364cadd4f8SNickeau * Append the new instructions by row 5374cadd4f8SNickeau */ 5384cadd4f8SNickeau foreach ($rows as $row) { 5394cadd4f8SNickeau $templateInstructionForInstance = TemplateUtility::renderInstructionsTemplateFromDataArray($iteratorTemplateInstructions, $row); 5404cadd4f8SNickeau $iteratorTemplateGeneratedInstructions = array_merge($iteratorTemplateGeneratedInstructions, $templateInstructionForInstance); 5414cadd4f8SNickeau } 5424cadd4f8SNickeau 5434cadd4f8SNickeau 5444cadd4f8SNickeau } 5454cadd4f8SNickeau /** 5464cadd4f8SNickeau * Rendering 5474cadd4f8SNickeau */ 5484cadd4f8SNickeau $totalInstructions = []; 5494cadd4f8SNickeau // header 5504cadd4f8SNickeau if (!empty($iteratorHeaderInstructions)) { 5514cadd4f8SNickeau $totalInstructions = $iteratorHeaderInstructions; 5524cadd4f8SNickeau } 5534cadd4f8SNickeau // content 5544cadd4f8SNickeau if (!empty($iteratorTemplateGeneratedInstructions)) { 5554cadd4f8SNickeau $totalInstructions = array_merge($totalInstructions, $iteratorTemplateGeneratedInstructions); 5564cadd4f8SNickeau } 5574cadd4f8SNickeau // footer 5584cadd4f8SNickeau $callStackFooterInstructions = $data[self::AFTER_TEMPLATE_CALLSTACK]; 5594cadd4f8SNickeau if (!empty($callStackFooterInstructions)) { 5604cadd4f8SNickeau $totalInstructions = array_merge($totalInstructions, $callStackFooterInstructions); 5614cadd4f8SNickeau } 5624cadd4f8SNickeau if (!empty($totalInstructions)) { 5634cadd4f8SNickeau 5644cadd4f8SNickeau /** 5654cadd4f8SNickeau * Advertise the total count to the 5664cadd4f8SNickeau * {@link syntax_plugin_combo_carrousel} 5674cadd4f8SNickeau * for the bullets if any 5684cadd4f8SNickeau */ 5694cadd4f8SNickeau $totalCallStack = CallStack::createFromInstructions($totalInstructions); 5704cadd4f8SNickeau $totalCallStack->moveToEnd(); 5714cadd4f8SNickeau while ($actualCall = $totalCallStack->previous()) { 5724cadd4f8SNickeau if ( 5734cadd4f8SNickeau $actualCall->getTagName() === syntax_plugin_combo_carrousel::TAG 5744cadd4f8SNickeau && in_array($actualCall->getState(), [DOKU_LEXER_ENTER, DOKU_LEXER_EXIT]) 5754cadd4f8SNickeau ) { 5764cadd4f8SNickeau $actualCall->setPluginData(syntax_plugin_combo_carrousel::ELEMENT_COUNT, $elementCounts); 5774cadd4f8SNickeau if ($actualCall->getState() === DOKU_LEXER_ENTER) { 5784cadd4f8SNickeau break; 5794cadd4f8SNickeau } 5804cadd4f8SNickeau } 5814cadd4f8SNickeau } 5824cadd4f8SNickeau 5834cadd4f8SNickeau try { 5844cadd4f8SNickeau $renderer->doc .= PluginUtility::renderInstructionsToXhtml($totalCallStack->getStack()); 5854cadd4f8SNickeau } catch (ExceptionCombo $e) { 5864cadd4f8SNickeau $renderer->doc .= "Error while rendering the iterators instructions. Error: {$e->getMessage()}"; 5874cadd4f8SNickeau } 5884cadd4f8SNickeau } 5894cadd4f8SNickeau return true; 5904cadd4f8SNickeau } 5914cadd4f8SNickeau } 59237748cd8SNickeau // unsupported $mode 59337748cd8SNickeau return false; 59437748cd8SNickeau } 59537748cd8SNickeau 59637748cd8SNickeau 59737748cd8SNickeau} 59837748cd8SNickeau 599