Skip to content

Commit

Permalink
more flexibility
Browse files Browse the repository at this point in the history
* @input@ placeholder can be used to reference the given input (useful
  to create a new namespace) makes dregad#70 obsolete
* new ? syntax to overwrite config options from the syntax (defaults
  still come from the config setting)
* support for strftime placeholders in the namespace config. Allows to
  create a daily page for example
* New option autopage which hides the input field. Together with the new
  date placeholder this allows to create a daily page on a single button
  click
  • Loading branch information
splitbrain committed May 29, 2017
1 parent fec857f commit b8304a8
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 54 deletions.
2 changes: 2 additions & 0 deletions conf/default.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
$conf['addpage_showroot'] = 1;
$conf['addpage_hide'] = 1;
$conf['addpage_hideACL'] = 0;
$conf['addpage_autopage'] = 0;

1 change: 1 addition & 0 deletions conf/metadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
$meta['addpage_showroot'] = array('onoff');
$meta['addpage_hide'] = array('onoff');
$meta['addpage_hideACL'] = array('onoff');
$meta['addpage_autopage'] = array('onoff');
1 change: 1 addition & 0 deletions lang/en/settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
$lang['addpage_showroot'] = "Show root namespace";
$lang['addpage_hide'] = "When you use {{NEWPAGE>[ns]}} syntax: Hide namespace selection (unchecked: show only subnamespaces)";
$lang['addpage_hideACL'] = "Hide {{NEWPAGE}} if user does not have rights to add pages (show message if unchecked)";
$lang['addpage_autopage'] = "Don't show the input box, the preconfigured namespace is treated as a full page ID. (makes sense with date placeholders)";
45 changes: 24 additions & 21 deletions script.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
jQuery(document).ready(function() {
jQuery(document).ready(function () {
var $form = jQuery(".addnewpage form");
if (!$form.length) return;

// Start with disabled submit button
jQuery(".addnewpage :submit").prop("disabled", true);
// Then enable it when a title is entered
jQuery(".addnewpage input[name='title']").keyup(function(){
var $submit = jQuery(this).parent("form").find(":submit");
if (jQuery(this).val().length > 0) {
$submit.removeAttr("disabled");
} else {
// For when the user deletes the text
$submit.attr("disabled", "disabled");
}
}).keyup();
var $ns = $form.find("[name='np_cat']");
var $title = $form.find("input[name='title']");
var $id = $form.find("input[name='id']");
var $submit = $form.find(':submit');

// Change the form's page-ID field on submit
jQuery(".addnewpage form").submit(function(e) {
// disable submit unless something is in input or input is disabled
if ($title.attr('type') === 'text') {
$submit.attr('disabled', 'disabled');
$title.keyup(function () {

This comment has been minimized.

Copy link
@micgro42

micgro42 May 29, 2017

Consider using $title.on('input', function () {}); since this event catches also cut and paste actions. Browser-support is pretty complete: https://caniuse.com/#feat=input-event

This comment has been minimized.

Copy link
@micgro42

micgro42 May 29, 2017

Also, this doesn't work as expected when there are more than one {{NEWPAGE}} anywhere on the HTML-page. However $form.each(...) should fix it.

if ($title.val().length > 0) {
$submit.removeAttr('disabled');
} else {
$submit.attr('disabled', 'disabled');
}
});
}

// Change the form's page-ID field on submit
$form.submit(function () {
// Build the new page ID and save in hidden form field
var ns = jQuery(this).find("[name='np_cat']");
var title = jQuery(this).find("input[name='title']");
var id = ns.val()+":"+title.val();
jQuery(this).find("input[name='id']").val(id);
var id = $ns.val().replace('@INPUT@', $title.val());
$id.val(id);

// Clean up the form vars, just to make the resultant URL a bit nicer
ns.prop("disabled", true);
title.prop("disabled", true);
$ns.prop("disabled", true);
$title.prop("disabled", true);

return true;
});
Expand Down
150 changes: 117 additions & 33 deletions syntax.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
*/
class syntax_plugin_addnewpage extends DokuWiki_Syntax_Plugin {

/** @var array the parsed options */
protected $options;

/**
* Syntax Type
*/
Expand Down Expand Up @@ -50,25 +53,44 @@ public function connectTo($mode) {
* {{NEWPAGE#newtpl1|Title1,newtpl2|Title1}}
* {{NEWPAGE>your:namespace#newtpl1|Title1,newtpl2|Title1}}
*
* @param string $match The text matched by the patterns
* @param int $state The lexer state for the match
* @param int $pos The character position of the matched text
* @param string $match The text matched by the patterns
* @param int $state The lexer state for the match
* @param int $pos The character position of the matched text
* @param Doku_Handler $handler The Doku_Handler object
* @return array Return an array with all data you want to use in render
* @codingStandardsIgnoreStart
*/
public function handle($match, $state, $pos, Doku_Handler $handler) {
/* @codingStandardsIgnoreEnd */
$options = substr($match, 9, -2); // strip markup
$options = explode('#', $options, 2);

$namespace = trim(ltrim($options[0], '>'));
$templates = explode(',', $options[1]);
$templates = array_map('trim', $templates);
return array(
'namespace' => $namespace,
'newpagetemplates' => $templates
$match = substr($match, 9, -2); // strip markup

$data = array(
'namespace' => '',
'newpagetemplates' => array(),
'options' => array(
'exclude' => $this->getConf('addpage_exclude'),
'showroot' => $this->getConf('addpage_showroot'),
'hide' => $this->getConf('addpage_hide'),
'hideacl' => $this->getConf('addpage_hideACL'),
'autopage' => $this->getConf('addpage_autopage'),
)
);

if(preg_match('/>(.*?)(#|\?|$)/', $match, $m)) {
$data['namespace'] = trim($m[1]);
}

if(preg_match('/#(.*?)(\?|$)/', $match, $m)) {
$data['newpagetemplates'] = array_map('trim', explode(',', $m[1]));
}

if(preg_match('/\?(.*?)(#|$)/', $match, $m)) {
$this->_parseOptions($m[1], $data['options']);
// make options available in class
$this->options = $data['options'];

This comment has been minimized.

Copy link
@micgro42

micgro42 May 29, 2017

duplicate with line

$this->options = $data['options'];

}

return $data;
}

/**
Expand All @@ -82,11 +104,14 @@ public function handle($match, $state, $pos, Doku_Handler $handler) {
public function render($mode, Doku_Renderer $renderer, $data) {
global $lang;

// make options available in class
$this->options = $data['options'];

if($mode == 'xhtml') {
$disablecache = null;
$namespaceinput = $this->_htmlNamespaceInput($data['namespace'], $disablecache);
if($namespaceinput === false) {
if($this->getConf('addpage_hideACL')) {
if($this->options['hideacl']) {
$renderer->doc .= '';
} else {
$renderer->doc .= $this->getLang('nooption');
Expand All @@ -97,10 +122,13 @@ public function render($mode, Doku_Renderer $renderer, $data) {

$newpagetemplateinput = $this->_htmlTemplateInput($data['newpagetemplates']);

$input = 'text';
if($this->options['autopage']) $input = 'hidden';

$form = '<div class="addnewpage">' . DOKU_LF
. DOKU_TAB . '<form name="addnewpage" method="get" action="' . DOKU_BASE . DOKU_SCRIPT . '" accept-charset="' . $lang['encoding'] . '">' . DOKU_LF
. DOKU_TAB . DOKU_TAB . $namespaceinput . DOKU_LF
. DOKU_TAB . DOKU_TAB . '<input class="edit" type="text" name="title" size="20" maxlength="255" tabindex="2" />' . DOKU_LF
. DOKU_TAB . DOKU_TAB . '<input class="edit" type="'.$input.'" name="title" size="20" maxlength="255" tabindex="2" />' . DOKU_LF
. $newpagetemplateinput
. DOKU_TAB . DOKU_TAB . '<input type="hidden" name="do" value="edit" />' . DOKU_LF
. DOKU_TAB . DOKU_TAB . '<input type="hidden" name="id" />' . DOKU_LF
Expand All @@ -114,22 +142,78 @@ public function render($mode, Doku_Renderer $renderer, $data) {
return false;
}

/**
* Overwrites the $options with the ones parsed from $optstr
*
* @param string $optstr
* @param array $options
* @author Andreas Gohr <[email protected]>
*/
protected function _parseOptions($optstr, &$options) {
$opts = preg_split('/[,&]/', $optstr);

foreach($opts as $opt) {
$opt = strtolower(trim($opt));
$val = true;
// booleans can be negated with a no prefix
if(substr($opt, 0, 2) == 'no') {
$opt = substr($opt, 2);
$val = false;
}

// not a known option? might be a key=value pair
if(!isset($options[$opt])) {
list($opt, $val) = array_map('trim', explode('=', $opt, 2));
}

// still unknown? skip it
if(!isset($options[$opt])) continue;

// overwrite the current value
$options[$opt] = $val;
}
}

/**
* Parse namespace request
*
* This creates the final ID to be created (still having an @INPUT@ variable
* which is filled in via JavaScript)
*
* @author Samuele Tognini <[email protected]>
* @author Michael Braun <[email protected]>
* @author Andreas Gohr <[email protected]>
* @param string $ns The namespace as given in the syntax
* @return string
*/
protected function _parseNS($ns) {
global $INFO;
$id = $INFO['id'];
if(strpos($ns, '@PAGE@') !== false) {
return cleanID(str_replace('@PAGE@', $id, $ns));
}
if($ns == "@NS@") return getNS($id);
$ns = preg_replace("/^\.(:|$)/", dirname(str_replace(':', '/', $id)) . "$1", $ns);
$ns = str_replace("/", ":", $ns);

$selfid = $INFO['id'];
$selfns = getNS($INFO['id']);
// replace the input variable with something unique that survives cleanID
$keep = sha1(time());

// by default append the input to the namespace (except on autopage)
if(strpos($ns, '@INPUT@') === false && !$this->options['autopage']) $ns .= ":@INPUT@";

// date replacements
$ns = dformat(null, $ns);

// placeholders
$replacements = array(
'/\//' => ':', // forward slashes to colons
'/@PAGE@/' => $selfid,
'/@NS@/' => $selfns,
'/^\.(:|\/|$)/' => "$selfns:",
'/@INPUT@/' => $keep,
);
$ns = preg_replace(array_keys($replacements), array_values($replacements), $ns);

// clean up, then reinsert the input variable
$ns = cleanID($ns);
$ns = str_replace($keep, '@INPUT@', $ns);

return $ns;
}

Expand All @@ -147,7 +231,7 @@ protected function _htmlNamespaceInput($dest_ns, &$disablecache) {

// If a NS has been provided:
// Whether to hide the NS selection (otherwise, show only subnamespaces).
$hide = $this->getConf('addpage_hide');
$hide = $this->options['hide'];

$parsed_dest_ns = $this->_parseNS($dest_ns);
// Whether the user can create pages in the provided NS (or root, if no
Expand All @@ -172,7 +256,7 @@ protected function _htmlNamespaceInput($dest_ns, &$disablecache) {
$someopt = false;

// Show root namespace if requested and allowed
if($this->getConf('addpage_showroot') && $can_create) {
if($this->options['showroot'] && $can_create) {
if(empty($dest_ns)) {
// If no namespace has been provided, add an option for the root NS.
$ret .= '<option ' . (($currentns == '') ? 'selected ' : '') . 'value="">' . $this->getLang('namespaceRoot') . '</option>';
Expand All @@ -188,7 +272,7 @@ protected function _htmlNamespaceInput($dest_ns, &$disablecache) {

// The top of this stack will always be the last printed ancestor namespace
$ancestor_stack = array();
if (!empty($dest_ns)) {
if(!empty($dest_ns)) {
array_push($ancestor_stack, $dest_ns);
}

Expand All @@ -202,14 +286,14 @@ protected function _htmlNamespaceInput($dest_ns, &$disablecache) {
}

$nsparts = explode(':', $ns);
$first_unprinted_depth = empty($ancestor_stack)? 1 : (2 + substr_count($ancestor_stack[count($ancestor_stack) - 1], ':'));
for ($i = $first_unprinted_depth, $end = count($nsparts); $i <= $end; $i++) {
$first_unprinted_depth = empty($ancestor_stack) ? 1 : (2 + substr_count($ancestor_stack[count($ancestor_stack) - 1], ':'));
for($i = $first_unprinted_depth, $end = count($nsparts); $i <= $end; $i++) {
$namespace = implode(':', array_slice($nsparts, 0, $i));
array_push($ancestor_stack, $namespace);
$selectOptionText = str_repeat('&nbsp;&nbsp;', substr_count($namespace, ':')) . $nsparts[$i - 1];
$ret .= '<option ' .
(($currentns == $namespace) ? 'selected ' : '') .
($i == $end? ('value="' . $namespace . '">') : 'disabled>') .
($i == $end ? ('value="' . $namespace . '">') : 'disabled>') .
$selectOptionText .
'</option>';
}
Expand Down Expand Up @@ -238,7 +322,7 @@ protected function _getNamespaceList($topns = '') {

$topns = utf8_encodeFN(str_replace(':', '/', $topns));

$excludes = $this->getConf('addpage_exclude');
$excludes = $this->options['exclude'];
if($excludes == "") {
$excludes = array();
} else {
Expand All @@ -250,7 +334,7 @@ protected function _getNamespaceList($topns = '') {
$namespaces = array();
foreach($searchdata as $ns) {
foreach($excludes as $exclude) {
if( ! empty($exclude) && strpos($ns['id'], $exclude) === 0) {
if(!empty($exclude) && strpos($ns['id'], $exclude) === 0) {
continue 2;
}
}
Expand All @@ -273,7 +357,7 @@ public function _htmlTemplateInput($newpagetemplates) {

} else {
if($cnt == 1) {
list($template, ) = $this->_parseNSTemplatePage($newpagetemplates[0]);
list($template,) = $this->_parseNSTemplatePage($newpagetemplates[0]);
$input = '<input type="hidden" name="newpagetemplate" value="' . formText($template) . '" />';
} else {
$first = true;
Expand All @@ -283,8 +367,8 @@ public function _htmlTemplateInput($newpagetemplates) {
$first = false;

list($template, $name) = $this->_parseNSTemplatePage($template);
$p .= ' value="'.formText($template).'"';
$input .= "<option $p>".formText($name)."</option>";
$p .= ' value="' . formText($template) . '"';
$input .= "<option $p>" . formText($name) . "</option>";
}
$input .= '</select>';
}
Expand All @@ -307,7 +391,7 @@ protected function _parseNSTemplatePage($nstemplate) {
$exist = null;
resolve_pageid(getNS($ID), $template, $exist); //get absolute id

if (is_null($name)) $name = $template;
if(is_null($name)) $name = $template;

return array($template, $name);
}
Expand Down

0 comments on commit b8304a8

Please sign in to comment.