*/
// must be run within Dokuwiki
if (!defined('DOKU_INC')) {
die();
}
require_once(__DIR__ . '/lib/grammar.php');
class syntax_plugin_ifauthex extends DokuWiki_Syntax_Plugin
{
private $_nestedRenderPermission = [true];
/**
* Returns a boolean representing whether at this depth level rendering is allowed.
*/
private function innermostShouldRender()
{
return end($this->_nestedRenderPermission);
}
/**
* Pushes the permission to render of the current tag into the stack.
* It might not change the value of @ref innermostShouldRender, if rendering was
* already previously disabled by an outer tag.
*/
private function pushInnerPermission($perm) {
array_push($this->_nestedRenderPermission, $perm && $this->innermostShouldRender());
}
/**
* Returns a boolean representing whether the content at the current nesting level is being rendered.
* It then pops this boolean from the stack.
*/
private function popShouldRender() {
return array_pop($this->_nestedRenderPermission);
}
/** @inheritDoc */
public function getType()
{
return 'formatting';
}
/** @inheritDoc */
function getAllowedTypes()
{
return array('container', 'formatting', 'substition', 'protected', 'disabled', 'paragraphs', 'baseonly');
}
/** @inheritDoc */
function accepts($mode) {
// Allow nesting the mode
if ($mode === 'plugin_ifauthex') {
return true;
}
return parent::accepts($mode);
}
/** @inheritDoc */
function getPType()
{
return 'stack';
}
/** @inheritDoc */
public function getSort()
{
return 195;
}
/** @inheritDoc */
public function connectTo($mode)
{
$this->Lexer->addEntryPattern('(?=.*?)', $mode, 'plugin_ifauthex');
}
/** @inheritDoc */
public function postConnect()
{
$this->Lexer->addExitPattern('', 'plugin_ifauthex');
}
/** @inheritDoc */
public function handle($match, $state, $pos, Doku_Handler $handler)
{
global $conf;
switch ($state) {
case DOKU_LEXER_ENTER:
$matches = null;
preg_match('/^$/', $match, $matches);
if (is_array($matches) && count($matches) > 0) {
// The last group contains the expression.
// Can't already pre-parse because DokuWiki serializes the
// objects that are returned, but it doesn't know about our
// custom classes at this point.
return array($state, $matches[count($matches) - 1]);
}
return array($state, null);
case DOKU_LEXER_MATCHED:
break;
case DOKU_LEXER_UNMATCHED:
return array($state, $match);
case DOKU_LEXER_EXIT:
return array($state, null);
}
return false;
}
/** @inheritDoc */
public function render($mode, Doku_Renderer $renderer, $data)
{
list($state, $exprOrMatch) = $data;
// never cache
$renderer->nocache();
switch ($state) {
case DOKU_LEXER_ENTER:
if ($exprOrMatch === null) {
// something went wrong
return false;
}
try {
// check if current user should see the content
$exprOrMatch = auth_expr_parse($exprOrMatch);
$exprPermission = (bool) $exprOrMatch->evaluate();
$shouldRender = $this->innermostShouldRender() && $exprPermission;
// Save the state only at the first occurrence
if ($this->innermostShouldRender() && !$exprPermission) {
if ($renderer->getFormat() === 'xhtml') {
// save the level so we can detect if we have opened a new section via a header or not
$renderer->meta['ifauthex.originalLevel'] = $renderer->getLastlevel();
}
// point the renderer's doc to something else, remembering the old one
$renderer->meta['ifauthex.originalDoc'] = &$renderer->doc;
$ignoredDoc = is_array($renderer->doc) ? [] : '';
$renderer->doc = &$ignoredDoc;
// do the same for the toc list
$ignoredToc = [];
$renderer->meta['ifauthex.originalToc'] = &$renderer->toc;
$renderer->toc = &$ignoredToc;
// patch the global TOC, if defined
global $TOC;
$renderer->meta['ifauthex.originalGlobalToc'] = &$TOC;
$TOC = is_array($TOC) ? [] : null;
}
// Push the new rendering permission
$this->pushInnerPermission($exprPermission);
} catch (Exception $e) {
// something went wrong parsing the expression
msg(hsc($e->getMessage()), -1);
return false;
}
break;
case DOKU_LEXER_UNMATCHED:
$renderer->cdata($exprOrMatch);
break;
case DOKU_LEXER_EXIT:
// point the renderer's doc and toc back to the original
$shouldRenderOld = $this->popShouldRender();
// Become active if innermostShouldRender changes from false to true
if (!$shouldRenderOld && $this->innermostShouldRender()) {
$renderer->doc = &$renderer->meta['ifauthex.originalDoc'];
$renderer->toc = &$renderer->meta['ifauthex.originalToc'];
global $TOC;
$TOC = &$renderer->meta['ifauthex.originalGlobalToc'];
if ($renderer->getFormat() === 'xhtml') {
/*
Detect whether a section was opened in the meanwhile. This happens due to a header being issued inside
the hidden section. However, if we started at lev 0, and gotten at lev > 0, the lexer/parser combo will
have included a section_close command somewhere in the remaining part of the document.
We thus have to match that section_close with a corresponding section_open; the opening has occurred
within the hidden body, so we manually add a patch section_open entry.
See https://github.com/mittelab/ifauthex-dokuwiki-plugin/issues/8.
*/
if ($renderer->meta['ifauthex.originalLevel'] === 0 && $renderer->getLastlevel() > 0) {
$renderer->section_open($renderer->getLastlevel());
}
}
}
break;
}
return true;
}
}