* @license GPLv2 (http://www.gnu.org/licenses/gpl-2.0.html) */ namespace dokuwiki\template\mikio; if (!defined('DOKU_INC')) die(); require_once('icons/icons.php'); require_once('inc/simple_html_dom.php'); class Template { public $tplDir = ''; public $baseDir = ''; public $footerScript = array(); /** * Class constructor */ public function __construct() { $this->tplDir = tpl_incdir(); $this->baseDir = tpl_basedir(); $this->_registerHooks(); } /** * Returns the instance of the class * * @return Template class instance */ public static function getInstance() { static $instance = null; if ($instance === null) { $instance = new Template(); } return $instance; } /** * Register the themes hooks into Dokuwiki */ private function _registerHooks() { global $EVENT_HANDLER; $events_dispatcher = array( '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 */ public function metaHeadersHandler(\Doku_Event $event) { global $MIKIO_ICONS; $this->includePage('theme', FALSE, TRUE); $stylesheets = array(); $scripts = array(); if($this->getConf('customTheme') != '') { if(file_exists($this->tplDir . 'themes/' . $this->getConf('customTheme') . '/style.less')) { $stylesheets[] = $this->baseDir . 'themes/' . $this->getConf('customTheme') . '/style.less'; } else { if(file_exists($this->tplDir . 'themes/' . $this->getConf('customTheme') . '/style.css')) { $stylesheets[] = $this->baseDir . 'themes/' . $this->getConf('customTheme') . '/style.css'; } } if(file_exists($this->tplDir . 'themes/' . $this->getConf('customTheme') . '/script.js')) { $scripts[] = $this->baseDir . 'themes/' . $this->getConf('customTheme') . '/script.js'; } } if(is_array($MIKIO_ICONS) && $this->getConf('iconTag', 'icon') != '') { $icons = Array(); foreach($MIKIO_ICONS as $icon) { if(isset($icon['name']) && isset($icon['css']) && isset($icon['insert'])) { $icons[] = $icon; if($icon['css'] != '') { 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.js'; if($this->getConf('useLESS')) { $stylesheets[] = $this->baseDir . 'assets/mikio.less'; } else { $stylesheets[] = $this->baseDir . 'assets/mikio.css'; } $set = []; foreach ($stylesheets as $style) { if(in_array($style, $set) == FALSE) { if(strtolower(substr($style, -5)) == '.less' && $this->getConf('useLESS')) { $style = $this->baseDir . 'css.php?css=' . str_replace($this->baseDir, '', $style); } array_unshift($event->data['link'], array( 'type' => 'text/css', 'rel' => 'stylesheet', 'href' => $style )); } $set[] = $style; } $set = []; foreach ($scripts as $script) { if(in_array($script, $set) == FALSE) { $event->data['script'][] = array( 'type' => 'text/javascript', '_data' => '', 'src' => $script); } $set[] = $script; } } /** * Print or return the footer meta data * * @param boolean $print print the data to buffer */ public function includeFooterMeta($print = TRUE) { $html = ''; if(count($this->footerScript) > 0) { $html .= ''; } if($print) 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($key, $default = FALSE) { $value = tpl_getConf($key, $default); switch($key) { case 'navbarDWMenuType': $value = strtolower($value); if($value != 'icons' && $value != 'text' && $value != 'both') $value = 'both'; break; case 'navbarDWMenuCombine': $value = strtolower($value); if($value != 'seperate' && $value != 'dropdown' && $value != 'combine') $value = 'combine'; break; case 'navbarPosLeft': case 'navbarPosMiddle': case 'navbarPosRight': $value = strtolower($value); if($value != 'none' && $value != 'custom' && $value != 'search' && $value != 'dokuwiki') { if($key == 'navbarPosLeft') $value = 'none'; if($key == 'navbarPosMiddle') $value = 'search'; if($key == 'navbarPosRight') $value = 'dokuwiki'; } break; case 'searchButton': $value = strtolower($value); if($value != 'icon' && $value != 'text') $value = 'icon'; break; case 'searchButton': $value = strtolower($value); if($value != 'icon' && $value != 'text') $value = 'icon'; break; case 'breadcrumbPosition': $value = strtolower($value); if($value != 'none' && $value != 'top' && $value != 'hero' && $value != 'page') $value = 'top'; break; case 'breadcrumbHome': $value = strtolower($value); if($value != 'none' && $value != 'page title' && $value != 'home' && $value != 'icon') $value = 'page title'; break; case 'sidebarLeftRow1': case 'sidebarLeftRow2': case 'sidebarLeftRow3': case 'sidebarLeftRow4': $value = strtolower($value); if($value != 'none' && $value != 'logged in user' && $value != 'search' && $value != 'content' && $value != 'tags') { if($key == 'sidebarLeftRow1') $value = 'logged in user'; if($key == 'sidebarLeftRow2') $value = 'search'; if($key == 'sidebarLeftRow3') $value = 'content'; if($key == 'sidebarLeftRow4') $value = 'none'; } break; case 'pageToolsFloating': case 'pageToolsFooter': $value = strtolower($value); if($value != 'none' && $value != 'page editors' && $value != 'always') { if($key == 'pageToolsFloating') $value = 'always'; if($key == 'pageToolsFooter') $value = 'always'; } break; case 'showNotifications': $value = strtolower($value); if($value != 'none' && $value != 'admin' && $value != 'always') $value = 'admin'; break; case 'licenseType': $value = strtolower($value); if($value != 'none' && $value != 'badge' && $value != 'buttom') $value = 'badge'; break; case 'navbarUseTitleIcon': case 'navbarUseTitleText': case 'navbarUseTaglineText': case 'navbarShowSub': case 'heroTitle': case 'heroImagePropagation': case 'breadcrumbPrefix': case 'breadcrumbSep': case 'sidebarShowLeft': case 'sidebarShowRight': case 'tocFull': case 'footerSearch': case 'licenseImageOnly': case 'includePageUseACL': case 'includePagePropagate': case 'breadcrumbHideHome': case 'tagsConsolidate': case 'footerInPage': case 'sidebarMobileDefaultCollapse': $value = (bool)$value; break; case 'breadcrumbShowLast': $value = (int)$value; break; case 'iconTag': case 'customTheme': case 'navbarCustomMenuText': case 'breadcrumbPrefixText': case 'breadcrumbSepText': case 'footerCustomMenuText': break; case 'useLESS': $value = (bool)$value; if($value && !function_exists('ctype_digit')) { $value = false; } break; } 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($page) { ob_start(); tpl_includeFile($page . '.html'); $html = ob_get_contents(); ob_end_clean(); if($html != '') return TRUE; $useACL = $this->getConf('includePageUseACL'); $propagate = $this->getConf('includePagePropagate'); if($propagate) { if(page_findnearest($page, $useACL)) return TRUE; } elseif($useACL && 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($page, $print = TRUE, $parse = TRUE, $classWrapper = '') { ob_start(); tpl_includeFile($page . '.html'); $html = ob_get_contents(); ob_end_clean(); if($html == '') { $useACL = $this->getConf('includePageUseACL'); $propagate = $this->getConf('includePagePropagate'); $html = ''; $html = tpl_include_page($page, false, $propagate, $useACL); } if($html != '' && $parse) { $html = $this->parseContent($html); } if($classWrapper != '' && $html != '') $html = '
(.*)[^<]*/';
}
$content = preg_replace_callback($page_regex, function($icons) {
$iconTag = $this->getConf('iconTag', 'icon');
return preg_replace_callback('/<' . $iconTag . ' ([\w\- #]*)>(?=[^>]*(<|$))/',
function ($matches) {
global $MIKIO_ICONS;
$s = $matches[0];
if(count($MIKIO_ICONS) > 0) {
$icon = $MIKIO_ICONS[0];
if(count($matches) > 1) {
$e = explode(' ', $matches[1]);
if(count($e) > 1) {
foreach($MIKIO_ICONS as $iconItem) {
if(strcasecmp($iconItem['name'], $e[0]) == 0) {
$icon = $iconItem;
$s = $icon['insert'];
for($i = 1; $i < 9; $i++) {
if(count($e) < $i || $e[$i] == '') {
if(isset($icon['$'.$i])) {
$s = str_replace('$' . $i, $icon['$'.$i], $s);
}
} else {
$s = str_replace('$' . $i, $e[$i], $s);
}
}
$dir = '';
if(isset($icon['dir'])) $dir = $this->baseDir . 'icons/' . $icon['dir'] . '/';
$s = str_replace('$0', $dir, $s);
break;
}
}
} else {
$s = str_replace('$1', $matches[1], $icon['insert']);
}
}
}
$s = preg_replace('/(class=")(.*)"/', '$1mikio-icon $2"', $s, -1, $count);
if($count == 0) {
$s = preg_replace('/(<\w* )/', '$1class="mikio-icon" ', $s);
}
return $s;
},
$icons[0]);
}, $content);
if($ACT == 'preview') {
if(is_array($preview) && count($preview) > 0) {
$preview[0]->innertext = $content;
}
$str = $html->save();
$html->clear();
unset($html);
} else {
$str = $content;
}
}
return $str;
}
/**
* Parse HTML for theme
*
* @param string $content HTML content to parse
* @return string Parsed content
*/
public function parseContent($content) {
global $INPUT, $ACT;
// Add Mikio Section titles
if($INPUT->str('page') == 'config') {
$admin_sections = array(
// Section Insert Before Icon
'navbar' => array('navbarUseTitleIcon', ''),
'search' => array('searchButton', ''),
'hero' => array('heroTitle', ''),
'tags' => array('tagsConsolidate', ''),
'breadcrumb' => array('breadcrumbHideHome', ''),
'sidebar' => array('sidebarShowLeft', ''),
'toc' => array('tocFull', ''),
'pagetools' => array('pageToolsFloating', ''),
'footer' => array('footerCustomMenuText', ''),
'license' => array('licenseType', ''),
'acl' => array('includePageUseACL', ''),
);
foreach ($admin_sections as $section => $items) {
$search = $items[0];
$icon = $items[1];
// $content = preg_replace('/\s+\s+(tpl»mikio»' . $search . ')<\/span>/',
// '' . $this->mikioInlineIcon($icon) . tpl_getLang('config_' . $section) . ' $4', $content);
$content = preg_replace('/ \s*\s*(tpl»mikio»' . $search . ')<\/span>/',
'' . $this->mikioInlineIcon($icon) . tpl_getLang('config_' . $section) . ' tpl»mikio»' . $search . '', $content);
}
}
if($ACT == 'admin' && !isset($_GET['page'])) {
$content = preg_replace('/(.*?)<\/ul>.*?(.*?<\/ul>)/s', '$1$2', $content);
}
// Page Revisions - Table Fix
if(strpos($content, 'id="page__revisions"') !== FALSE) {
$content = preg_replace('/(\s.*<\/span>\s.*\s.*<\/span>)/', '$1', $content);
}
$html = new \simple_html_dom;
$html->stripRNAttrValues = false;
$html->load($content, true, false);
if (!$html) return $content;
/* Buttons */
foreach($html->find('#config__manager button') as $node) {
$c = explode(' ', $node->class);
if(!in_array('mikio-button', $c)) $c[] = 'mikio-button';
$node->class = implode(' ', $c);
}
/* Buttons - Primary */
foreach($html->find('#config__manager [type=submit]') as $node) {
$c = explode(' ', $node->class);
if(!in_array('mikio-primary', $c)) $c[] = 'mikio-primary';
$node->class = implode(' ', $c);
}
/* Hide page title if hero is enabled */
if($this->getConf('heroTitle') && $ACT != 'preview') {
$pageTitle = $this->parsePageTitle();
foreach($html->find('h1,h2,h3,h4') as $elm) {
if($elm->innertext == $pageTitle) {
// $elm->innertext = '';
$elm->setAttribute('style', 'display:none');
break;
}
}
}
/* Hero subtitle */
foreach($html->find('p') as $elm) {
$i = stripos($elm->innertext, '~~hero-subtitle');
if($i !== false) {
$j = strpos($elm->innertext, '~~', $i + 2);
if($j !== false) {
if($j > $i + 16) {
$subtitle = substr($elm->innertext, $i + 16, $j - $i - 16);
$this->footerScript['hero-subtitle'] = 'mikio.setHeroSubTitle(\'' . $subtitle .'\')';
// $elm->innertext = substr($elm->innertext, 0, $i + 2) . substr($elm->innertext, $j + 2);
$elm->innertext = preg_replace('/~~hero-subtitle (.+?)~~.*/ui', '', $elm->innertext);
}
break;
}
}
}
/* Hero image */
foreach($html->find('p') as $elm) {
$image = '';
preg_match('/~~hero-image (.+?)~~(?!.?")/ui', $elm->innertext, $matches);
if(count($matches) > 0) {
preg_match('/ 0) {
$image = $imageTagMatches[1];
} else {
preg_match('/(.+?)[~<]/ui', $matches[1], $imageTagMatches);
if(count($imageTagMatches) > 0) {
$image = $imageTagMatches[1];
} else {
$image = strip_tags($matches[1]);
if(stripos($image, ':') === FALSE) {
$image = str_replace(array('{', '}'), '', $image);
$i = stripos($image, '?');
if($i !== FALSE) {
$image = substr($image, 0, $i);
}
$image = ml($image, '', true, '', false);
}
}
}
$this->footerScript['hero-image'] = 'mikio.setHeroImage(\'' . $image .'\')';
$elm->innertext = preg_replace('/~~hero-image (.+?)~~.*/ui', '', $elm->innertext);
}
}
/* Hero colors - ~~hero-colors [background-color] [hero-title-color] [hero-subtitle-color] [breadcrumb-text-color] [breadcrumb-hover-color] (use 'initial' for original color) */
foreach($html->find('p') as $elm) {
$i = stripos($elm->innertext, '~~hero-colors');
if($i !== false) {
$j = strpos($elm->innertext, '~~', $i + 2);
if($j !== false) {
if($j > $i + 14) {
$color = substr($elm->innertext, $i + 14, $j - $i - 14);
$this->footerScript['hero-colors'] = 'mikio.setHeroColor(\'' . $color .'\')';
$elm->innertext = preg_replace('/~~hero-colors (.+?)~~.*/ui', '', $elm->innertext);
}
break;
}
}
}
/* Page Tags (tag plugin) */
if($this->getConf('tagsConsolidate')) {
$tags = '';
foreach($html->find('div.tags a') as $elm) {
$tags .= $elm->outertext;
}
foreach($html->find('div.tags') as $elm) {
$elm->innertext = '';
$elm->setAttribute('style', 'display:none');
}
if($tags != '') {
$this->footerScript['tags'] = 'mikio.setTags(\'' . $tags . '\')';
}
}
// Configuration Manager
if($INPUT->str('page') == 'config') {
// Additional save buttons
foreach ($html->find('#config__manager') as $cm) {
$saveButtons = '';
foreach($cm->find('p') as $elm) {
$saveButtons = $elm->outertext;
$saveButtons = str_replace('', '
', $saveButtons);
$elm->outertext = '';
}
foreach($cm->find('fieldset') as $elm) {
$elm->innertext .= $saveButtons;
}
}
}
$content = $html->save();
$html->clear();
unset($html);
return $content;
}
/**
* Get DokuWiki namespace/page/URI as link
*
* @param string $str string to parse
* @return string parsed uri
*/
public function getLink($str) {
$i = strpos($str, '://');
if($i !== false) return $str;
return wl($str);
}
/**
* Check if the user can edit current namespace/page
*
* @return boolean user can edit
*/
public function userCanEdit() {
global $INFO;
global $ID;
$wiki_file = wikiFN($ID);
if (@!file_exists($wiki_file)) return true;
if ($INFO['isadmin'] || $INFO['ismanager']) return true;
// $meta_file = metaFN($ID, '.meta');
if (!$INFO['meta']['user']) return true;
if ($INFO['client'] == $INFO['meta']['user']) return true;
return false;
}
/**
* Search for and return the uri of a media file
*
* @param string $image image name to search for (without extension)
* @param bool $searchCurrentNS search the current namespace
* @return string uri of the found media file
*/
public function getMediaFile($image, $searchCurrentNS=TRUE, $propagate=TRUE) {
global $INFO;
$ext = array('png', 'jpg', 'gif', 'svg');
if($searchCurrentNS) $prefix[] = ':'.$INFO['namespace'].':';
if($propagate) {
$prefix[] = ':';
$prefix[] = ':wiki:';
}
$theme = $this->getConf('customTheme');
if($theme != '') $prefix[] = $this->tplDir . 'themes/' . $theme . '/images/';
$prefix[] = 'images/';
$search = array();
foreach($prefix as $pitem) {
foreach($ext as $eitem) {
$search[] = $pitem . $image . '.' . $eitem;
}
}
$img = '';
$file = '';
$url = '';
$ismedia = false;
$found = false;
foreach($search as $img) {
if(substr($img, 0, 1) == ':') {
$file = mediaFN($img);
$ismedia = true;
} else {
$file = tpl_incdir().$img;
$ismedia = false;
}
if(file_exists($file)) {
$found = true;
break;
}
}
if(!$found) return false;
if($ismedia) {
$url = ml($img, '', true, '', false);
} else {
$url = tpl_basedir().$img;
}
return $url;
}
/**
* Print or return the page title
*
* @param string $page page id or empty string for current page
* @return string generated content
*/
public function getPageTitle($page = '') {
global $ID, $conf;
$html = '';
if($page == '') $page = $ID;
$html = p_get_first_heading($page);
$html = strip_tags($html);
$html = preg_replace('/\s+/', ' ', $html);
$html .= ' [' . strip_tags($conf['title']) . ']';
$html = trim($html);
return $html;
}
/**
* Return inline theme icon
*
* @param string $type icon to retreive
* @return string html icon content
*/
public function mikioInlineIcon($type) {
switch($type) {
case 'wrench':
return '';
case 'file':
return '';
case 'gear':
return '';
case 'user':
return '';
case 'search':
return '';
case 'home':
return '';
}
return '';
}
/**
* Finalize theme
*/
public function finalize() {
}
/**
* Show Messages
*/
public function showMessages() {
global $ACT;
$show = $this->getConf('showNotifications');
if($show == 'always' || ($show == 'admin' && $ACT == 'admin')) {
global $MSG, $MSG_shown;
$MSG_shown = true;
if (!isset($MSG)) {
return;
}
$shown = array();
foreach ($MSG as $msg) {
$hash = md5($msg['msg']);
if (isset($shown[$hash])) {
continue;
}
// skip double messages
if (info_msg_allowed($msg)) {
print '
';
print $msg['msg'];
print '';
}
$shown[$hash] = 1;
}
unset($GLOBALS['MSG']);
}
}
}
global $TEMPLATE;
$TEMPLATE = \dokuwiki\template\mikio\Template::getInstance();