-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Parametric page name and other changes
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
Showing
6 changed files
with
163 additions
and
65 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
}); | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 = '<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'] . '"/>' | ||
|
@@ -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); | ||
} | ||
|
||
/** | ||
|
@@ -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 .= '<option ' . (($currentns == '') ? 'selected ' : '') . ' value="">' . $this->getLang('namespaceRoot') . '</option>'; | ||
|
@@ -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 .= '<option ' . | ||
(($currentns == $namespace) ? 'selected ' : '') . | ||
($i == $end? ('value="' . $namespace . '">') : 'disabled>') . | ||
($i == $end ? ('value="' . $namespace . '">') : 'disabled>') . | ||
$selectOptionText . | ||
'</option>'; | ||
} | ||
|
@@ -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 = '<input type="hidden" name="newpagetemplate" value="' . formText($template) . '" />'; | ||
} else { | ||
$first = true; | ||
|
@@ -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>'; | ||
} | ||
|