Add support for creating pages whose name is not yet known - fixes #194

This commit is contained in:
Starbeamrainbowlabs 2020-10-25 22:50:03 +00:00
parent b78aa34972
commit 7dd9bd74c4
Signed by: sbrl
GPG Key ID: 1BE5172E637709C2
4 changed files with 51 additions and 14 deletions

View File

@ -4,10 +4,18 @@ This file holds the changelog for Pepperminty Wiki. This is the master list of t
## v0.23-dev (unreleased)
## Added
- Added HTTP API support for creating pages that don't yet have a name (#194)
- This allows for having a "create new page" button in your navigation links - e.g. edit `nav_links`, `nav_links_extra`, or `nav_links_bottom` in your `peppermint.json` and add something like `[ "+", "index.php?action=edit&unknownpagename=yes" ]`.
## Changed
- Updated the [configuration guide](https://starbeamrainbowlabs.com/labs/peppermint/peppermint-config-info.php) to include count of how many settings we have
## Fixed
- [security] Fixed some potential XSS attacks in the page editor
## v0.22
_No changes were made since the last release_

View File

@ -280,7 +280,8 @@ task_start-server() {
if [[ -z "${NO_BROWSER}" ]]; then
task_begin "Opening Browser";
sensible-browser [::]:35623;
# sensible-browser isn't opening the right browser :-/
xdg-open "http://[::]:35623";
task_end $?;
fi
}

View File

@ -1,11 +1,11 @@
<?php
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* License, v2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
register_module([
"name" => "Page editor",
"version" => "0.17.9",
"version" => "0.18",
"author" => "Starbeamrainbowlabs",
"description" => "Allows you to edit pages by adding the edit and save actions. You should probably include this one.",
"id" => "page-edit",
@ -27,7 +27,8 @@ register_module([
* @apiPermission Anonymous
*
* @apiUse PageParameter
* @apiParam {string} newpage Set to 'yes' if a new page is being created. Only affects a few bits of text here and there, and the HTTP response code recieved on success from the `save` action.
* @apiParam {string} newpage Optional. Set to 'yes' if a new page is being created. Only affects a few bits of text here and there, and the HTTP response code recieved on success from the `save` action.
* @apiParam {string} unknownpagename Optional. Set to 'yes' if the name of the page to be created is currently unknown. If set, a page name box will be shown too.
*/
/*
@ -41,18 +42,23 @@ register_module([
add_action("edit", function() {
global $pageindex, $settings, $env, $paths;
$unknownpagename = isset($_GET["unknownpagename"]) && strlen(trim($_GET["unknownpagename"])) > 0;
$filename = "$env->storage_prefix$env->page.md";
$creatingpage = !isset($pageindex->{$env->page});
if((isset($_GET["newpage"]) and $_GET["newpage"] == "true") or $creatingpage)
$title = "Creating $env->page";
else if(isset($_POST['preview-edit']) && isset($_POST['content']))
$title = "Preview Edits for $env->page";
else if($unknownpagename)
$title = "Creating new page";
else
$title = "Editing $env->page";
$pagetext = "";
if(isset($pageindex->{$env->page}))
$pagetext = ""; $page_tags = "";
if(isset($pageindex->{$env->page}) && !$unknownpagename)
$pagetext = file_get_contents($filename);
if(!$unknownpagename)
$page_tags = htmlentities(implode(", ", (!empty($pageindex->{$env->page}->tags)) ? $pageindex->{$env->page}->tags : []));
$isOtherUsersPage = false;
if(
@ -90,7 +96,7 @@ register_module([
$sourceViewContent = "<p>$env->page is a special user page which acutally belongs to " . extract_user_from_userpage($env->page) . ", another user on $settings->sitename. Because of this, you are not allowed to edit it (though you can always edit your own page and any pages under it if you're logged in). You can, however, vieww it's source below.</p>";
// Append a view of the page's source
$sourceViewContent .= "<textarea name='content' readonly>$pagetext</textarea>";
$sourceViewContent .= "<textarea name='content' readonly>".htmlentities($pagetext)."</textarea>";
exit(page_renderer::render_main("Viewing source for $env->page", $sourceViewContent));
}
@ -109,7 +115,7 @@ register_module([
}
$content = "<h1>$title</h1>\n";
$page_tags = implode(", ", (!empty($pageindex->{$env->page}->tags)) ? $pageindex->{$env->page}->tags : []);
if(!$env->is_logged_in and $settings->anonedits) {
$content .= "<p><strong>Warning: You are not logged in! Your IP address <em>may</em> be recorded.</strong></p>";
}
@ -136,8 +142,12 @@ register_module([
$content .= "<button class='smartsave-restore' title=\"Only works if you haven't changed the editor's content already!\">Restore Locally Saved Content</button>
<form method='post' name='edit-form' action='index.php?action=preview-edit&page=" . rawurlencode($env->page) . "' class='editform'>
<input type='hidden' name='prev-content-hash' value='" . generate_page_hash(isset($old_pagetext) ? $old_pagetext : $pagetext) . "' />
<textarea name='content' autofocus tabindex='1'>$pagetext</textarea>
<input type='hidden' name='prev-content-hash' value='" . generate_page_hash(isset($old_pagetext) ? $old_pagetext : $pagetext) . "' />";
if($unknownpagename)
$content .= "<div><label for='page'>Page Name:</label>
<input type='text' id='page' name='page' value='' placeholder='Enter the name of the page here.' title='Enter the name of the page here.' />
<input type='hidden' name='prevent_save_if_exists' value='yes' />";
$content .= "<textarea name='content' autofocus tabindex='1'>$pagetext</textarea>
<pre class='fit-text-mirror'></pre>
<input type='text' id='tags' name='tags' value='" . htmlentities($page_tags, ENT_HTML5 | ENT_QUOTES) . "' placeholder='Enter some tags for the page here. Separate them with commas.' title='Enter some tags for the page here. Separate them with commas.' tabindex='2' />
<p class='editing-message'>$settings->editing_message</p>
@ -289,7 +299,9 @@ window.addEventListener("load", function(event) {
* @apiPermission Anonymous
*
* @apiUse PageParameter
* @apiParam {string} format The format ot return the edit key in. Possible values: text, json. Default: text.
* @apiParam {string} format The format to return the edit key in. Possible values: text, json. Default: text.
* @apiParam {string} prevent_save_if_exists Optional. If set to 'yes', then if a page exists with the specified page name the save is aborted and an error page returned instead.
* @apiParam {string} page The name of the page to save the edit to. Note that in this specific instance *only*, the page name can be specified over GET or POST (POST will override GET if both are present).
*/
/*
@ -366,7 +378,10 @@ window.addEventListener("load", function(event) {
* %save%
*/
add_action("save", function() {
global $pageindex, $settings, $env, $save_preprocessors, $paths;
global $pageindex, $settings, $env, $save_preprocessors, $paths;
// Update the page name in the main environment, since the page name may be submitted via the POST form
if(isset($_POST["page"]))
$env->page = $_POST["page"];
if(!$settings->editing)
{
@ -397,6 +412,16 @@ window.addEventListener("load", function(event) {
header("refresh: 5; url=index.php?page=" . rawurlencode($env->page));
exit("Bad request: No content specified.");
}
if(isset($_POST["prevent_save_if_exists"]) && isset($pageindex->{$env->page})) {
http_response_code(409);
exit(page_renderer::render_main("Error saving new page - ".htmlentities($env->page)." - $settings->sitename", "<p>Error: A page with that name already exists. Things you can do:</p>
<ul>
<li>View the existing page here: <a target='_blank' href='?action={$settings->defaultaction}&page=".rawurlencode($env->page)."'>".htmlentities($env->page)."</a></li>
<li><a href='javascript:history.back();'>Go back to continu editing and change the page name</a></li>
</ul>
<p>For reference, the page content you attempted to submit is shown below:</p>
<textarea name='content'>".htmlentities($_POST["content"])."</textarea>"));
}
// Make sure that the directory in which the page needs to be saved exists
if(!is_dir(dirname("$env->storage_prefix$env->page.md")))

View File

@ -123,7 +123,7 @@ nav:not(.nav-more-menu):not(.mega-menu) > span > a { font-weight: bolder; }
.nav-divider { color: transparent; }
.nav-more { position: relative; background-color: var(--accent-a3); min-width: 10em; }
.nav-more > label { font-weight: bold; }
label { font-weight: bold; }
label { cursor: pointer; }
.nav-more-menu { z-index: 10000; position: absolute; flex-direction: column; top: 2.6rem; right: 100000px; text-align: left; background-color: var(--accent-a2); border-top: 3px solid var(--accent-a3); border-bottom: 3px solid var(--accent-a3); }
input[type=checkbox]:checked ~ .nav-more-menu { right: -0.2rem; box-shadow: 0.4rem 0.4rem 1rem 0 var(--shadow); }
@ -225,11 +225,14 @@ input[type=text], input[type=password], input[type=url], input[type=email], inpu
textarea, .fit-text-mirror { min-height: 10em; line-height: 1.3em; font-size: 1.25rem; }
textarea, textarea[name=content] + pre, textarea ~ input[type=submit], #search-box { width: calc(100% - 0.3rem); box-sizing: border-box; }
textarea ~ input[type=submit] { margin: 0.5rem 0; padding: 0.5rem; font-weight: bolder; }
.editform input[type=text] { width: calc(100% - 0.3rem); box-sizing: border-box; }
.editform input[type=text] { width: calc(100% - 0.3rem); box-sizing: border-box; }
.editform label { margin-left: 1em; }
input.edit-page-button[type='submit'] { width: 49.5%; box-sizing: border-box; }
input[type=radio] { transform: scale(2); }
input[type=submit].large { width: 100%; box-sizing: border-box; padding: 0.5em; font-size: 1.25em; font-weight: bolder; }
.smartsave-restore { margin-bottom: 1em; }
.preview-message { text-align: center; }
@media (min-width: 800px) {
.jump-to-comments { position: absolute; top: 3.5em; right: 2em; display: block; text-align: right; pointer-events: none; }