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