Skip to content

Commit

Permalink
Parametric page name and other changes
Browse files Browse the repository at this point in the history
Merge pull request #102 from dregad/pr72.

This is @splitbrain's original contribution (see PR #72), with merge
conflicts resolved and some additional code cleanup and fixes, including
French translation.

* a new `@INPUT@` placeholder can be used to reference the given input
  in namespace setup. For example you could use something like
  `{{NEWPAGE>foo:@input@:start}}` to let users create new namespaces
  inside the `foo` namespace. This makes #70 obsolete
* a new `?` syntax allows to overwrite config options from the syntax
  (defaults still come from the config setting). Example:
  `{{NEWPAGE>foo:bar?nohide}}` overwrites the default `hide` setting.
* support for strftime placeholders in the namespace config. Allows to
  create a daily page for example. Example:
  `{{NEWPAGE>projekt:notes:%Y-%m-%d}}`
* 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. Example: `{{NEWPAGE>journal:%Y-%m-%d?noautopage}}`
* Includes changes of #71 (always use id of main page in replacements)
  • Loading branch information
dregad committed Nov 10, 2024
2 parents 86f7e2d + 6666849 commit 4b4a3e9
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 65 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)";
1 change: 1 addition & 0 deletions lang/fr/settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
$lang['addpage_showroot'] = "Afficher le namespace racine";
$lang['addpage_hide'] = "Quand vous utlisez la syntaxe {{NEWPAGE>[ns]}} : Cache la selection de namespace (décoché: affiche uniquement les sous-namespaces)";
$lang['addpage_hideACL'] = "Si non cochée, affiche un message lorsque l'utlisateur n'a pas les droits d'ajout de page. Sinon, cache simplement {{NEWPAGE}}";
$lang['addpage_autopage'] = "Ne pas afficher le champ de saisie; le namespace préconfiguré est traité comme un identifiant de page complet (utile avec des attributs de date)";
51 changes: 27 additions & 24 deletions script.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,35 @@
jQuery(document).ready(function() {
jQuery(function () {
jQuery(".addnewpage form").each(function () {
var $form = jQuery(this);
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');

// 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");
// disable submit unless something is in input or input is disabled
if ($title.attr('type') === 'text') {
$submit.attr('disabled', 'disabled');
$title.on('input', function () {
if ($title.val().length > 0) {
$submit.removeAttr('disabled');
} else {
$submit.attr('disabled', 'disabled');
}
});
}
}).keyup();

// Change the form's page-ID field on submit
jQuery(".addnewpage form").submit(function(e) {
// Change the form's page-ID field on submit
$form.submit(function () {
// Build the new page ID and save in hidden form field
var id = $ns.val().replace('@INPUT@', $title.val());
$id.val(id);

// 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);
// Clean up the form vars, just to make the resultant URL a bit nicer
$ns.prop("disabled", true);
$title.prop("disabled", true);

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

return true;
});

});
172 changes: 131 additions & 41 deletions syntax.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@

class syntax_plugin_addnewpage extends SyntaxPlugin {

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

/**
* Syntax Type
*/
Expand Down Expand Up @@ -47,37 +50,63 @@ public function connectTo($mode) {
}

/**
* Handler to prepare matched data for the rendering process
* Handler to prepare matched data for the rendering process.
*
* Handled syntax options:
* {{NEWPAGE}}
* {{NEWPAGE>your:namespace}}
* {{NEWPAGE#newtpl1,newtpl2}}
* {{NEWPAGE#newtpl1|Title1,newtpl2|Title1}}
* {{NEWPAGE>your:namespace#newtpl1|Title1,newtpl2|Title1}}
* {{NEWPAGE>your:namespace#newtpl1|Title1,newtpl2|Title1#@HI@,Howdy}}
* - {{NEWPAGE}}
* - {{NEWPAGE>your:namespace}}
* - {{NEWPAGE>your:namespace:@INPUT@:start}}
* - {{NEWPAGE>your:namespace:[date formats]}} {@see strftime()}
* - {{NEWPAGE?config_overrides}}
* - {{NEWPAGE#newtpl1,newtpl2}}
* - {{NEWPAGE#newtpl1|Title1,newtpl2|Title1}}
* - {{NEWPAGE>your:namespace#newtpl1|Title1,newtpl2|Title1}}
* - {{NEWPAGE>your:namespace#newtpl1|Title1,newtpl2|Title1#@HI@,Howdy}}
*
* Refer to {@see https://www.dokuwiki.org/plugin:addnewpage} for details.
*
* @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, 3);

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

$data = array(
'namespace' => '',
'newpagetemplates' => array(),
'newpagevars' => '',
'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]);
}

# Extract the newpagetemplate plugin parameters
# - after the initial #: the template name
# - after optional 2nd #: custom variable names
if(preg_match('/#(.*?)(?:#(.*?))?(?:\?|$)/', $match, $m)) {
$data['newpagetemplates'] = array_map('trim', explode(',', $m[1]));
$data['newpagevars'] = trim($m[2] ?? '');
}

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

return $data;
}

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

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

if($format == 'xhtml') {
$disablecache = false;
$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 @@ -106,11 +138,14 @@ public function render($format, Doku_Renderer $renderer, $data) {

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

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

$form = '<div class="addnewpage"><p>'
. '<form name="addnewpage" method="get" action="' . DOKU_BASE . DOKU_SCRIPT
. '" accept-charset="' . $lang['encoding'] . '">'
. $namespaceinput
. '<input class="edit" type="text" name="title" size="20" maxlength="255" tabindex="2" placeholder="'
. '<input class="edit" type="' . $input . '" name="title" size="20" maxlength="255" tabindex="2" placeholder="'
. $this->getLang('name') . '"/>'
. $newpagetemplateinput
. '<input type="hidden" name="newpagevars" value="' . $data['newpagevars'] . '"/>'
Expand All @@ -127,22 +162,77 @@ public function render($format, 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', sexplode('=', $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) {
$ID=getID();
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);
global $INFO;

$selfid = $INFO['id'];
$selfns = getNS($selfid);
// 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);

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

/**
Expand All @@ -159,7 +249,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 @@ -184,7 +274,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 Down Expand Up @@ -213,14 +303,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));
$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 @@ -249,7 +339,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 @@ -261,7 +351,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 @@ -284,7 +374,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 @@ -294,8 +384,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 Down

0 comments on commit 4b4a3e9

Please sign in to comment.