* @license GPLv2 (http://www.gnu.org/licenses/gpl-2.0.html) */ namespace dokuwiki\template\mikio; use Doku_Event; use dokuwiki\Menu\PageMenu; use dokuwiki\Menu\SiteMenu; use dokuwiki\Menu\UserMenu; use ParensParser; use simple_html_dom; use DOMDocument; use DOMNode; if (defined('DOKU_INC') === false) { die(); } require_once('icons/icons.php'); require_once('inc/simple_html_dom.php'); require_once('inc/parens-parser.php'); class mikio { /** * @var mikio|null Instance of the class. */ private static $instance = null; /** * @var string Template directory path from local FS. */ public $tplDir = ''; /** * @var string Template directory path from web. */ public $baseDir = ''; /** * @var array Array of Javascript files to include in footer. */ public $footerScript = []; /** * @var string Notifications from included pages. */ private $includedPageNotifications = ''; /** * @var array Array of formatted template configuration values. */ static private $formattedConfigValues = []; /** * Class constructor */ public function __construct() { $this->tplDir = tpl_incdir(); $this->baseDir = tpl_basedir(); $this->registerHooks(); } /** * Returns the instance of the class * * @return self class instance */ public static function getInstance(): self { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } /** * Register the themes hooks into Dokuwiki * * @return void */ private function registerHooks(): void { global $EVENT_HANDLER; $events_dispatcher = [ 'TPL_METAHEADER_OUTPUT' => 'metaheadersHandler' ]; foreach ($events_dispatcher as $event => $method) { $EVENT_HANDLER->register_hook($event, 'BEFORE', $this, $method); } } /** * Meta handler hook for DokuWiki * * @param Doku_Event $event DokuWiki Event. * @return void */ public function metaHeadersHandler(Doku_Event $event): void { global $MIKIO_ICONS; global $conf; global $MIKIO_TEMPLATE; $MIKIO_TEMPLATE = '123'; // TODO - is this set correctly? $this->includePage('theme', false); $stylesheets = []; $scripts = []; if (empty($this->getConf('customTheme')) === false) { if (file_exists($this->tplDir . 'themes/' . $this->getConf('customTheme') . '/style.less') === true) { $stylesheets[] = $this->baseDir . 'themes/' . $this->getConf('customTheme') . '/style.less'; } else { if (file_exists($this->tplDir . 'themes/' . $this->getConf('customTheme') . '/style.css') === true) { $stylesheets[] = $this->baseDir . 'themes/' . $this->getConf('customTheme') . '/style.css'; } } if (file_exists($this->tplDir . 'themes/' . $this->getConf('customTheme') . '/script.js') === true) { $scripts[] = $this->baseDir . 'themes/' . $this->getConf('customTheme') . '/script.js'; } } if (is_array($MIKIO_ICONS) === true && empty($this->getConf('iconTag', 'icon')) === false) { $icons = []; foreach ($MIKIO_ICONS as $icon) { if (isset($icon['name']) === true && isset($icon['css']) === true && isset($icon['insert']) === true) { $icons[] = $icon; if (empty($icon['css']) === false) { if (strpos($icon['css'], '//') === false) { $stylesheets[] = $this->baseDir . 'icons/' . $icon['css']; } else { $stylesheets[] = $icon['css']; } } } } $MIKIO_ICONS = $icons; } else { $MIKIO_ICONS = []; } $scripts[] = $this->baseDir . 'assets/mikio-typeahead.js'; $scripts[] = $this->baseDir . 'assets/mikio.js'; if ($this->getConf('useLESS') === true) { $stylesheets[] = $this->baseDir . 'assets/mikio.less'; } else { $stylesheets[] = $this->baseDir . 'assets/mikio.css'; } /* MikioPlugin Support */ if (plugin_load('action', 'mikioplugin') !== null) { if ($this->getConf('useLESS') === true) { $stylesheets[] = $this->baseDir . 'assets/mikioplugin.less'; } else { $stylesheets[] = $this->baseDir . 'assets/mikioplugin.css'; } } $set = []; foreach ($stylesheets as $style) { if (in_array($style, $set, true) === false) { if ($this->getConf('useLESS') === true && strcasecmp(substr($style, -5), '.less') === 0) { $style = $this->baseDir . 'css.php?css=' . str_replace($this->baseDir, '', $style); } array_unshift($event->data['link'], [ 'type' => 'text/css', 'rel' => 'stylesheet', 'href' => $style ]); } $set[] = $style; } $set = []; foreach ($scripts as $script) { if (in_array($script, $set, true) === false) { $script_params = [ 'type' => 'text/javascript', '_data' => '', 'src' => $script ]; // equal to or greator than hogfather if ($this->getDokuWikiVersion() >= 20200729 || $this->getDokuWikiVersion() === 0) { // greator than hogfather - defer always on if ($this->getDokuWikiVersion() >= 20200729 || $this->getDokuWikiVersion() === 0) { $script_params += ['defer' => 'defer']; } else { // hogfather - defer always on unless $conf['defer_js'] is false if (array_key_exists('defer_js', $conf) === false || $conf['defer_js'] === true) { $script_params += ['defer' => 'defer']; } } } $event->data['script'][] = $script_params; }//end if $set[] = $script; }//end foreach } /** * Print or return the footer metadata * * @param boolean $print Print the data to buffer. * @return string HTML footer meta data */ public function includeFooterMeta(bool $print = true): string { $html = ''; if (count($this->footerScript) > 0) { $html .= ''; } if ($print === true) { echo $html; } return $html; } /** * Retreive and parse theme configuration options * * @param string $key The configuration key to retreive. * @param mixed $default If key doesn't exist, return this value. * @return mixed parsed value of configuration */ public function getConf(string $key, $default = false) { if(array_key_exists($key, self::$formattedConfigValues) === true) { return self::$formattedConfigValues[$key]; } $value = tpl_getConf($key, $default); $data = [ ['keys' => ['navbarDWMenuType'], 'type' => 'choice', 'values' => ['both', 'icons', 'text'] ], ['keys' => ['navbarDWMenuCombine'], 'type' => 'choice', 'values' => ['combine', 'separate', 'dropdown'] ], ['keys' => ['navbarPosLeft', 'navbarPosMiddle', 'navbarPosRight'], 'type' => 'choice', 'values' => ['none', 'custom', 'search', 'dokuwiki'], 'default' => [ 'navbarPosLeft' => 'none', 'navbarPosMiddle' => 'search', 'navbarPosRight' => 'dokuwiki' ] ], ['keys' => ['navbarItemShowCreate', 'navbarItemShowShow', 'navbarItemShowRevs', 'navbarItemShowBacklink', 'navbarItemShowRecent', 'navbarItemShowMedia', 'navbarItemShowIndex', 'navbarItemShowProfile', 'navbarItemShowAdmin' ], 'type' => 'choice', 'values' => ['always', 'logged in', 'logged out', 'never'] ], ['keys' => ['navbarItemShowLogin', 'navbarItemShowLogout'], 'type' => 'choice', 'values' => ['always', 'never'] ], ['keys' => ['searchButton'], 'type' => 'choice', 'values' => ['icon', 'text'] ], ['keys' => ['breadcrumbPosition', 'youareherePosition'], 'type' => 'choice', 'values' => ['top', 'hero', 'page', 'none'] ], ['keys' => ['youarehereHome'], 'type' => 'choice', 'values' => ['page title', 'home', 'icon', 'none'] ], ['keys' => ['sidebarLeftRow1', 'sidebarLeftRow2', 'sidebarLeftRow3', 'sidebarLeftRow4'], 'type' => 'choice', 'values' => ['none', 'logged in user', 'search', 'content', 'tags'], 'default' => [ 'sidebarLeftRow1' => 'logged in user', 'sidebarLeftRow2' => 'search', 'sidebarLeftRow3' => 'content' ] ], ['keys' => ['pageToolsFloating', 'pageToolsFooter'], 'type' => 'choice', 'values' => ['always', 'none', 'page editors'] ], ['keys' => ['pageToolsShowCreate', 'pageToolsShowEdit', 'pageToolsShowRevs', 'pageToolsShowBacklink', 'pageToolsShowTop' ], 'type' => 'choice', 'values' => ['always', 'logged in', 'logged out', 'never'] ], ['keys' => ['showNotifications'], 'type' => 'choice', 'values' => ['admin', 'always', 'none', '', 'never'] ], ['keys' => ['licenseType'], 'type' => 'choice', 'values' => ['badge', 'button', 'none'] ], ['keys' => ['navbarUseTitleIcon'], 'type' => 'bool'], ['keys' => ['navbarUseTitleText'], 'type' => 'bool'], ['keys' => ['navbarUseTaglineText'], 'type' => 'bool'], ['keys' => ['navbarShowSub'], 'type' => 'bool'], ['keys' => ['heroTitle'], 'type' => 'bool'], ['keys' => ['heroImagePropagation'], 'type' => 'bool'], ['keys' => ['breadcrumbPrefix'], 'type' => 'bool'], ['keys' => ['breadcrumbSep'], 'type' => 'bool'], ['keys' => ['youareherePrefix'], 'type' => 'bool'], ['keys' => ['youarehereSep'], 'type' => 'bool'], ['keys' => ['sidebarShowLeft'], 'type' => 'bool'], ['keys' => ['sidebarShowRight'], 'type' => 'bool'], ['keys' => ['tocFull'], 'type' => 'bool'], ['keys' => ['footerSearch'], 'type' => 'bool'], ['keys' => ['licenseImageOnly'], 'type' => 'bool'], ['keys' => ['includePageUseACL'], 'type' => 'bool'], ['keys' => ['includePagePropagate'], 'type' => 'bool'], ['keys' => ['youarehereHideHome'], 'type' => 'bool'], ['keys' => ['tagsConsolidate'], 'type' => 'bool'], ['keys' => ['tagsShowHero'], 'type' => 'bool'], ['keys' => ['footerInPage'], 'type' => 'bool'], ['keys' => ['sidebarMobileDefaultCollapse'], 'type' => 'bool'], ['keys' => ['sidebarAlwaysShowLeft'], 'type' => 'bool'], ['keys' => ['sidebarAlwaysShowRight'], 'type' => 'bool'], ['keys' => ['searchUseTypeahead'], 'type' => 'bool'], ['keys' => ['showLightDark'], 'type' => 'bool'], ['keys' => ['autoLightDark'], 'type' => 'bool'], ['keys' => ['defaultDark'], 'type' => 'bool'], ['keys' => ['youarehereShowLast'], 'type' => 'int'], ['keys' => ['iconTag'], 'type' => 'string'], ['keys' => ['customTheme'], 'type' => 'string'], ['keys' => ['navbarCustomMenuText'], 'type' => 'string'], ['keys' => ['breadcrumbPrefixText'], 'type' => 'string'], ['keys' => ['breadcrumbSepText'], 'type' => 'string'], ['keys' => ['youareherePrefixText'], 'type' => 'string'], ['keys' => ['youarehereSepText'], 'type' => 'string'], ['keys' => ['footerPageInfoText'], 'type' => 'string'], ['keys' => ['footerCustomMenuText'], 'type' => 'string'], ['keys' => ['brandURLGuest'], 'type' => 'string'], ['keys' => ['brandURLUser'], 'type' => 'string'], ['keys' => ['useLESS'], 'type' => 'bool'], ['keys' => ['stickyTopHeader'], 'type' => 'bool'], ['keys' => ['stickyNavbar'], 'type' => 'bool'], ['keys' => ['stickyHeader'], 'type' => 'bool'], ['keys' => ['stickyLeftSidebar'], 'type' => 'bool'], ]; foreach ($data as $row) { // does not check case.... if (in_array($key, $row['keys'], true) === true) { if (array_key_exists('type', $row) === true) { switch ($row['type']) { case 'bool': return (bool) $value; case 'int': return (int) $value; case 'string': return $value; }//end switch }//end if if (in_array($value, $row['values'], true) === true) { return $value; } if (array_key_exists('default', $row) === true) { if (is_array($row['default']) === true) { if (array_key_exists($key, $row['default']) === true) { return $row['default'][$key]; } } else { return $row['default']; } } return reset($row['values']); }//end if }//end foreach self::$formattedConfigValues[$key] = $value; return $value; } /** * Check if a page exist in directory or namespace * * @param string $page Page/namespace to search. * @return boolean if page exists */ public function pageExists(string $page): bool { ob_start(); tpl_includeFile($page . '.html'); $html = ob_get_clean(); if (empty($html) === false) { return true; } $useACL = $this->getConf('includePageUseACL'); $propagate = $this->getConf('includePagePropagate'); if ($propagate === true) { if (page_findnearest($page, $useACL) !== false) { return true; } } elseif ($useACL === true && auth_quickaclcheck($page) !== AUTH_NONE) { return true; } return false; } /** * Print or return page from directory or namespace * * @param string $page Page/namespace to include. * @param boolean $print Print content. * @param boolean $parse Parse content before printing/returning. * @param string $classWrapper Wrap page in a div with class. * @return string contents of page found */ public function includePage(string $page, bool $print = true, bool $parse = true, string $classWrapper = ''): string { ob_start(); tpl_includeFile($page . '.html'); $html = ob_get_clean(); if (empty($html) === true) { $useACL = $this->getConf('includePageUseACL'); $propagate = $this->getConf('includePagePropagate'); ob_start(); $html = tpl_include_page($page, false, $propagate, $useACL); $this->includedPageNotifications .= ob_get_clean(); } if (empty($html) === false && $parse === true) { $html = $this->parseContent($html); // TODO - move to end of main.php } if (empty($classWrapper) === false && empty($html) === false) { $html = '