diff --git a/conf/default.php b/conf/default.php index 5a25000..4d30e5a 100644 --- a/conf/default.php +++ b/conf/default.php @@ -3,3 +3,5 @@ $conf['addpage_showroot'] = 1; $conf['addpage_hide'] = 1; $conf['addpage_hideACL'] = 0; +$conf['addpage_autopage'] = 0; + diff --git a/conf/metadata.php b/conf/metadata.php index 7590fbe..63a75fc 100644 --- a/conf/metadata.php +++ b/conf/metadata.php @@ -3,3 +3,4 @@ $meta['addpage_showroot'] = array('onoff'); $meta['addpage_hide'] = array('onoff'); $meta['addpage_hideACL'] = array('onoff'); +$meta['addpage_autopage'] = array('onoff'); diff --git a/lang/en/settings.php b/lang/en/settings.php index aa6b961..6cba688 100644 --- a/lang/en/settings.php +++ b/lang/en/settings.php @@ -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)"; diff --git a/lang/fr/settings.php b/lang/fr/settings.php index 640d899..0c4d359 100644 --- a/lang/fr/settings.php +++ b/lang/fr/settings.php @@ -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)"; diff --git a/script.js b/script.js index 153d4fa..952663a 100755 --- a/script.js +++ b/script.js @@ -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; }); - }); diff --git a/syntax.php b/syntax.php index a5cc4d1..e9d0537 100644 --- a/syntax.php +++ b/syntax.php @@ -18,6 +18,9 @@ class syntax_plugin_addnewpage extends SyntaxPlugin { + /** @var array the parsed options */ + protected $options; + /** * Syntax Type */ @@ -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; } /** @@ -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'); @@ -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 = '

' . '

' . $namespaceinput - . '' . $newpagetemplateinput . '' @@ -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 + */ + 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 * @author Michael Braun + * @author Andreas Gohr + * @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); } /** @@ -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 @@ -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 .= ''; @@ -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('  ', substr_count($namespace, ':')) . $nsparts[$i - 1]; $ret .= ''; } @@ -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 { @@ -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; } } @@ -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 = ''; } else { $first = true; @@ -294,8 +384,8 @@ public function _htmlTemplateInput($newpagetemplates) { $first = false; list($template, $name) = $this->_parseNSTemplatePage($template); - $p .= ' value="'.formText($template).'"'; - $input .= ""; + $p .= ' value="' . formText($template) . '"'; + $input .= ""; } $input .= ''; }