*/ class syntax_plugin_qna_toc extends DokuWiki_Syntax_Plugin { private $mode; /** * Construct */ public function __construct() { $this->mode = substr(get_class($this), 7); } /** * What kind of syntax are we? */ public function getType() { return 'container'; } public function getPType() { return 'block'; } /** * Where to sort in? */ public function getSort() { return 55; } /** * */ public function connectTo($mode) { $this->Lexer->addSpecialPattern('~~(?:QNA|FAQ)(?:\s[^\n]*?)?~~', $mode, $this->mode); } /** * */ public function handle($match, $state, $pos, Doku_Handler $handler) { global $ID; if ($state == DOKU_LEXER_SPECIAL) { preg_match('/~~(?:QNA|FAQ)(.*?)~~/', $match, $match); $data = preg_split('/\s+/', $match[1], -1, PREG_SPLIT_NO_EMPTY); $resolver = new dokuwiki\File\PageResolver($ID); foreach ($data as &$pageId) { $pageId = $resolver->resolveId($pageId); } } else { $data = false; } return $data; } /** * */ public function render($mode, Doku_Renderer $renderer, $data) { global $ID; if ($mode == 'xhtml') { if (empty($data)) { /* If no page is specified, render the questions from the current one */ $data[0] = $ID; } $toc = $this->buildToc($data); if (!empty($toc)) { $this->compressToc($toc); $this->normalizeToc($toc); $this->renderToc($renderer, $toc); } return true; } elseif ($mode == 'metadata') { if (!empty($data)) { $this->addCacheDependencies($renderer, $data); } return true; } return false; } /** * Assemble questions from all pages into a single TOC */ private function buildToc($pageId) { $toc = array(); foreach ($pageId as $id) { if (auth_quickaclcheck($id) < AUTH_READ) { continue; } $pageToc = p_get_metadata($id, 'description tableofquestions'); if (!empty($pageToc)) { foreach ($pageToc as $item) { $item['link'] = $id . '#' . $item['id']; $toc[] = $item; } } } return $toc; } /** * Remove not used list levels */ private function compressToc(&$toc) { $maxLevel = 0; foreach ($toc as $item) { if ($maxLevel < $item['level']) { $maxLevel = $item['level']; } } /* Build list level usage histogram */ $level = array_fill(1, $maxLevel, 0); foreach ($toc as $item) { $level[$item['level']]++; } /* Determine how many unused list levels have to be skipped for each used one */ $skipCount = 0; for ($l = 1; $l <= $maxLevel; $l++) { if ($level[$l] == 0) { $skipCount++; } else { $level[$l] = $skipCount; } } /* Remove unused list levels */ foreach ($toc as &$item) { $item['level'] -= $level[$item['level']]; } } /** * Make sure that list starts with a first level item */ private function normalizeToc(&$toc) { $offset = 9; for ($i = 0; $toc[$i]['level'] > 1; $i++) { if (($toc[$i]['level'] - $offset) < 1) { $offset = $toc[$i]['level'] - 1; } $toc[$i]['level'] -= $offset; } } /** * */ private function renderToc($renderer, $toc) { $renderer->doc .= '
' . DOKU_LF; $this->renderList($renderer, $toc, 0); $renderer->doc .= '
' . DOKU_LF; } /** * */ private function renderList($renderer, $toc, $index) { $items = count( $toc ); $level = $toc[$index]['level']; $renderer->listu_open(); for ($i = $index; ($i < $items) && ($toc[$i]['level'] == $level); $i++) { $renderer->listitem_open($level); $renderer->listcontent_open(); $renderer->doc .= ''; $renderer->internallink($toc[$i]['link'], $toc[$i]['title']); $renderer->doc .= ''; $renderer->listcontent_close(); if ((($i + 1) < $items) && ($toc[$i + 1]['level'] > $level)) { $i = $this->renderList($renderer, $toc, $i + 1); } $renderer->listitem_close(); } $renderer->listu_close(); return $i - 1; } /** * */ private function addCacheDependencies($renderer, $pageId) { foreach ($pageId as $id) { $metafile = metaFN($id, '.meta'); $renderer->meta['relation']['depends']['rendering'][$metafile] = true; } } }