This commit is contained in:
Sean Feeney 2020-08-14 18:51:18 -07:00
commit cb1a6d6493
18 changed files with 147 additions and 69 deletions

View File

@ -2,11 +2,17 @@
This file holds the changelog for Pepperminty Wiki. This is the master list of things that have changed (second only to the commit history!) - though the information for any particular release can also be found in the description of it's page for every release made on GitHub too. This file holds the changelog for Pepperminty Wiki. This is the master list of things that have changed (second only to the commit history!) - though the information for any particular release can also be found in the description of it's page for every release made on GitHub too.
## v0.22-dev ## v0.22-beta2
### Fixed
- Hide the admin email address at the bottom of every page - we missed it in v0.22-beta1 (but got every other one though :P)
## v0.22-beta1
Make sure you have PHP 7.3+ when you update past this point! It isn't the end of the world if you don't, but it will make you more secure if you do. Make sure you have PHP 7.3+ when you update past this point! It isn't the end of the world if you don't, but it will make you more secure if you do.
### Added ### Added
- [Module Api] Add new `search::invindex_term_getpageids`, and `search::invindex_term_getoffsets`, and `search::index_sort_freq` methods - [Module Api] Add new `search::invindex_term_getpageids`, and `search::invindex_term_getoffsets`, and `search::index_sort_freq` methods
- [Module Api] Add new `ends_with` and `filepath_to_pagename` core functions
- Added new syntax features to PeppermintParsedown, inspired by ParsedownExtreme (which we couldn't get to work, and it wasn't working before as far as I can tell) - Added new syntax features to PeppermintParsedown, inspired by ParsedownExtreme (which we couldn't get to work, and it wasn't working before as far as I can tell)
- Checkboxes: `[ ]` and `[x]` after a bullet point or at the start of a line - Checkboxes: `[ ]` and `[x]` after a bullet point or at the start of a line
- Marked / highlighted text: `Some text ==marked text== more text` - Marked / highlighted text: `Some text ==marked text== more text`
@ -42,12 +48,15 @@ Make sure you have PHP 7.3+ when you update past this point! It isn't the end of
- A warning is generated in PHP 7.2 and below = [please upgrade](https://www.php.net/supported-versions.php) to PHP 7.3+! (#200) - A warning is generated in PHP 7.2 and below = [please upgrade](https://www.php.net/supported-versions.php) to PHP 7.3+! (#200)
- [security] The `Secure` cookie flag is now automatically added when clients use HTTPS to prevent downgrade-based session stealing attacks (control this with the new `cookie_secure` setting) - [security] The `Secure` cookie flag is now automatically added when clients use HTTPS to prevent downgrade-based session stealing attacks (control this with the new `cookie_secure` setting)
- Standardised prefixes to (most) `error_log()` calls to aid clarity in multi-wiki environments - Standardised prefixes to (most) `error_log()` calls to aid clarity in multi-wiki environments
- Improved pageindex rebuilder algorithm to search for and import history revisions - this helps when converting data from another wiki format
- Improved spam protection when hiding email addresses. Javascript is now required to decode email addresses - please [get in touch](https://github.com/sbrl/Pepperminty-Wiki/issues/new) if this is a problem for whatever reason. I take accessibility _very_ seriously.
- Bump weighting of title and tag matches in search results (delete the `search_title_matches_weighting` and `search_tags_matches_weighting` settings to get the new weightings)
### Fixed ### Fixed
- Squashed a warning when using the fenced code block syntax - Squashed a warning when using the fenced code block syntax
- If a redirect page sends you to create a page that doesn't exist, a link back to the redirect page itself is now displayed - If a redirect page sends you to create a page that doesn't exist, a link back to the redirect page itself is now displayed
- Really fix bots getting into infinite loops on the login page this time by marking all login pages as `noindex, nofollow` with a robots `<meta />` tag - Really fix bots getting into infinite loops on the login page this time by marking all login pages as `noindex, nofollow` with a robots `<meta />` tag
- Navigating to a redirect page from a page list will no longer cause you to automatically follow the redirect - Navigating to a redirect page from a page list or the recent changes list will no longer cause you to automatically follow the redirect
- Limited sidebar size to 20% of the screen width at most - Limited sidebar size to 20% of the screen width at most
- Fix the [large blank space problem](https://github.com/sbrl/Pepperminty-Wiki/blob/master/Changelog.md#fixed-3) in all themes - Fix the [large blank space problem](https://github.com/sbrl/Pepperminty-Wiki/blob/master/Changelog.md#fixed-3) in all themes
- Squashed the text `\A` appearing before tags at the bottom of pages for some users ([ref](https://gitter.im/Pepperminty-Wiki/Lobby?at=5f0632068342f4627401f145)) - Squashed the text `\A` appearing before tags at the bottom of pages for some users ([ref](https://gitter.im/Pepperminty-Wiki/Lobby?at=5f0632068342f4627401f145))
@ -58,6 +67,7 @@ Make sure you have PHP 7.3+ when you update past this point! It isn't the end of
- Fixed an obscure warning when previewing PDFs (#202) - Fixed an obscure warning when previewing PDFs (#202)
- Ensure that the parent page exists when moving a page to be a child of a non-existent parent (#201) - Ensure that the parent page exists when moving a page to be a child of a non-existent parent (#201)
- Fixed templating (#203) - Fixed templating (#203)
- Fixed warning from statistics engine during firstrun wizard
## v0.21.1-hotfix1 ## v0.21.1-hotfix1

View File

@ -1,6 +1,6 @@
{ {
"name": "Pepperminty Wiki", "name": "Pepperminty Wiki",
"version": "0.21.0", "version": "0.22.0",
"description": "A wiki in a box. This is the API documentation.", "description": "A wiki in a box. This is the API documentation.",
"title": "Pepperminty Wiki (0.20)" "title": "Pepperminty Wiki (0.22)"
} }

View File

@ -159,6 +159,28 @@ function path_resolve(string $path, string $basePath = null) {
return implode(DIRECTORY_SEPARATOR, $components); return implode(DIRECTORY_SEPARATOR, $components);
} }
/**
* Converts a filepath to a page name.
* @param string $filepath The filepath to convert.
* @return string The extracted pagename.
*/
function filepath_to_pagename(string $filepath) : string {
global $env;
// Strip the storage prefix, but only if it isn't a dot
if(starts_with($filepath, $env->storage_prefix) && $env->storage_prefix !== ".")
$filepath = mb_substr($filepath, mb_strlen($env->storage_prefix));
// If a revision number is detected, strip it
if(preg_match("/\.r[0-9]+$/", $filepath) > 0)
$filepath = mb_substr($filepath, 0, mb_strrpos($filepath, ".r"));
// Strip the .md file extension
if(ends_with($filepath, ".md"))
$filepath = mb_substr($filepath, 0, -3);
return $filepath;
}
/** /**
* Gets the name of the parent page to the specified page. * Gets the name of the parent page to the specified page.
* @apiVersion 0.15.0 * @apiVersion 0.15.0
@ -267,34 +289,36 @@ function makepathsafe($string)
$string = preg_replace("/\.+/", ".", $string); $string = preg_replace("/\.+/", ".", $string);
// Don't allow slashes at the beginning // Don't allow slashes at the beginning
$string = ltrim($string, "\\/"); $string = ltrim($string, "\\/");
// Don't allow dots on their own
$string = preg_replace(["/^\.\\/|\\/\.$/", "/\\/\.\\//"], ["", "/"], $string);
return $string; return $string;
} }
/** /**
* Hides an email address from bots by adding random html entities. * Hides an email address from bots. Returns a fragment of HTML that contains the mangled email address.
* @todo Make this more clevererer :D
* @package core * @package core
* @param string $str The original email address * @param string $str The original email address
* @return string The mangled email address. * @param string $display_text The display text for the resulting HTML - if null then the original email address is used.
* @return string The mangled email address.
*/ */
function hide_email($str) function hide_email(string $email, string $display_text = null) : string
{ {
$hidden_email = ""; $enc = json_encode([ $email, $display_text ]);
for($i = 0; $i < strlen($str); $i++) $len = strlen($enc);
{ $pool = []; for($i = 0; $i < $len; $i++) $pool[] = $i;
if($str[$i] == "@") $a = []; $b = [];
{ for($i = 0; $i < $len; $i++) {
$hidden_email .= "&#" . ord("@") . ";"; $n = random_int(0, $len - $i - 1);
continue; $j = array_splice($pool, $n, 1)[0]; $b[] = $j;
} // echo("chose ".$enc[$j].", index $j, n $n\n");
if(rand(0, 1) == 0) $a[] = $enc[$j];
$hidden_email .= $str[$i];
else
$hidden_email .= "&#" . ord($str[$i]) . ";";
} }
$a = base64_encode(implode("|", $a));
return $hidden_email; $b = base64_encode(implode("|", $b));
$span_id = "he-".crypto_id(16);
return "<a href='#protected-with-javascript' id='$span_id'>[protected with javascript]</span><script>(() => {let c=\"$a|$b\".split('|').map(atob).map(s=>s.split('|'));let d=[],e=document.getElementById('$span_id');c[1].map((n,i)=>d[parseInt(n)]=c[0][i]);d=JSON.parse(d.join(''));e.textContent=d[1]==null?d[0]:d[1];e.setAttribute('href', 'mailto:'+d[0])})();</script>";
} }
/** /**
* Checks to see if $haystack starts with $needle. * Checks to see if $haystack starts with $needle.
* @package core * @package core
@ -303,10 +327,22 @@ function hide_email($str)
* of $haystack. * of $haystack.
* @return bool Whether $needle can be found at the beginning of $haystack. * @return bool Whether $needle can be found at the beginning of $haystack.
*/ */
function starts_with($haystack, $needle) { function starts_with(string $haystack, string $needle) : bool {
$length = strlen($needle); $length = strlen($needle);
return (substr($haystack, 0, $length) === $needle); return (substr($haystack, 0, $length) === $needle);
} }
/**
* Checks to see if $hackstack ends with $needle.
* The matching bookend to starts_with.
* @package core
* @param string $haystack The haystack to search..
* @param string $needle The needle to look for.
* @return bool
*/
function ends_with(string $haystack, string $needle) : bool {
$length = strlen($needle);
return (substr($haystack, -$length) === $needle);
}
/** /**
* Case-insensitively finds all occurrences of $needle in $haystack. Handles * Case-insensitively finds all occurrences of $needle in $haystack. Handles

View File

@ -22,25 +22,21 @@ if(!file_exists($paths->pageindex))
// Create a new entry // Create a new entry
$newentry = new stdClass(); $newentry = new stdClass();
$newentry->filename = substr( // Store the filename, whilst trimming the storage prefix $newentry->filename = mb_substr( // Store the filename, whilst trimming the storage prefix
$pagefilename, $pagefilename,
mb_strlen(preg_replace("/^\.\//iu", "", $env->storage_prefix)) // glob_recursive trim the ./ from returned filenames , so we need to as well mb_strlen(preg_replace("/^\.\//iu", "", $env->storage_prefix)) // glob_recursive trim the ./ from returned filenames , so we need to as well
); );
// Remove the `./` from the beginning if it's still hanging around // Remove the `./` from the beginning if it's still hanging around
if(substr($newentry->filename, 0, 2) == "./") if(mb_substr($newentry->filename, 0, 2) == "./")
$newentry->filename = substr($newentry->filename, 2); $newentry->filename = mb_substr($newentry->filename, 2);
$newentry->size = filesize($pagefilename); // Store the page size $newentry->size = filesize($pagefilename); // Store the page size
$newentry->lastmodified = filemtime($pagefilename); // Store the date last modified $newentry->lastmodified = filemtime($pagefilename); // Store the date last modified
// Todo find a way to keep the last editor independent of the page index // Todo find a way to keep the last editor independent of the page index
$newentry->lasteditor = "unknown"; // Set the editor to "unknown" $newentry->lasteditor = "unknown"; // Set the editor to "unknown"
// POTENTIAL BUG: If $env->storage_prefix is not ., then this we need to be more intelligent here
// Extract the name of the (sub)page without the ".md" // Extract the name of the (sub)page without the ".md"
$pagekey = mb_substr($newentry->filename, 0, -3); $pagekey = filepath_to_pagename($newentry->filename);
error_log("pagename '$newentry->filename' → filepath '$pagekey'");
if(file_exists($env->storage_prefix . $pagekey) && // If it exists... if(file_exists($env->storage_prefix . $pagekey) && // If it exists...
!is_dir($env->storage_prefix . $pagekey)) // ...and isn't a directory !is_dir($env->storage_prefix . $pagekey)) // ...and isn't a directory
@ -84,6 +80,7 @@ if(!file_exists($paths->pageindex))
} }
} }
// If the initial revision doesn't exist on disk, create it (if it does, then we handle that later)
if(function_exists("history_add_revision") && !file_exists("{$pagefilename}.r0")) { // Can't use module_exists - too early if(function_exists("history_add_revision") && !file_exists("{$pagefilename}.r0")) { // Can't use module_exists - too early
copy($pagefilename, "{$pagefilename}.r0"); copy($pagefilename, "{$pagefilename}.r0");
$newentry->history = [ (object) [ $newentry->history = [ (object) [
@ -103,16 +100,46 @@ if(!file_exists($paths->pageindex))
if(function_exists("history_add_revision")) { if(function_exists("history_add_revision")) {
$history_revs = glob_recursive($env->storage_prefix . "*.r*"); $history_revs = glob_recursive($env->storage_prefix . "*.r*");
// It's very important that we read the history revisions in the right order and that we don't skip any
usort($history_revs, function($a, $b) {
preg_match("/[0-9]+$/", $a, $revid_a);
$revid_a = intval($revid_a[0]);
preg_match("/[0-9]+$/", $b, $revid_b);
$revid_b = intval($revid_b[0]);
return $revid_a - $revid_b;
});
// We can guarantee that the direcotry separator is present on the end - it's added explicitly earlier
$strlen_storageprefix = strlen($env->storage_prefix);
foreach($history_revs as $filename) { foreach($history_revs as $filename) {
preg_match("/[0-9]+$/", "Main Page.md.r0", $revid); preg_match("/[0-9]+$/", $filename, $revid);
error_log("raw revid | ".var_export($revid, true));
if(count($revid) === 0) continue; if(count($revid) === 0) continue;
$revid = intval($revid[0]); $revid = intval($revid[0]);
// TODO: Extract the pagename here (maybe a function is worth implementing if we haven't already?) $pagename = filepath_to_pagename($filename);
$filepath_stripped = substr($filename, $strlen_storageprefix);
if($revid == 0 && ) { if(!isset($pageindex->$pagename->history))
$pageindex->$pagename->history = [];
if(isset($pageindex->$pagename->history[$revid]))
continue;
error_log("pagename: $pagename, revid: $revid, pageindex entry: ".var_export($pageindex->$pagename, true));
$newsize = filesize($filename);
$prevsize = 0;
if($revid > 0 && isset($pageindex->$pagename->history[$revid - 1])) {
$prevsize = filesize(end($pageindex->$pagename->history)->filename);
} }
$pageindex->$pagename->history[$revid] = (object) [
"type" => "edit",
"rid" => $revid,
"timestamp" => filemtime($filename),
"filename" => $filepath_stripped,
"newsize" => $newsize,
"sizediff" => $newsize - $prevsize,
"editor" => "unknown"
];
} }
} }

View File

@ -44,7 +44,7 @@ class page_renderer
<p>{footer-message}</p> <p>{footer-message}</p>
<p>Powered by Pepperminty Wiki {version}, which was built by <a href='//starbeamrainbowlabs.com/'>Starbeamrainbowlabs</a>. Send bugs to 'bugs at starbeamrainbowlabs dot com' or <a href='//github.com/sbrl/Pepperminty-Wiki' title='Github Issue Tracker'>open an issue</a>.</p> <p>Powered by Pepperminty Wiki {version}, which was built by <a href='//starbeamrainbowlabs.com/'>Starbeamrainbowlabs</a>. Send bugs to 'bugs at starbeamrainbowlabs dot com' or <a href='//github.com/sbrl/Pepperminty-Wiki' title='Github Issue Tracker'>open an issue</a>.</p>
<p>Your local friendly moderators are {admins-name-list}.</p> <p>Your local friendly moderators are {admins-name-list}.</p>
<p>This wiki is managed by <a href='mailto:{admin-details-email}'>{admin-details-name}</a>.</p> <p>This wiki is managed by {admin-details}.</p>
</footer> </footer>
{navigation-bar-bottom} {navigation-bar-bottom}
{all-pages-datalist}"; {all-pages-datalist}";
@ -128,8 +128,7 @@ class page_renderer
if(!is_callable($function)) if(!is_callable($function))
{ {
http_response_code(500); http_response_code(500);
$admin_email = hide_email($settings->admindetails_email); exit(page_renderer::render("$settings->sitename - Module Error", "<p>$settings->sitename has got a misbehaving module installed that tried to register an invalid HTML handler with the page renderer. Please contact $settings->sitename's administrator {$settings->admindetails_name} at ".hide_email($settings->admindetails_email)."."));
exit(page_renderer::render("$settings->sitename - Module Error", "<p>$settings->sitename has got a misbehaving module installed that tried to register an invalid HTML handler with the page renderer. Please contact $settings->sitename's administrator {$settings->admindetails_name} at <a href='mailto:$admin_email'>$admin_email</a>."));
} }
self::$part_processors[] = $function; self::$part_processors[] = $function;
@ -187,8 +186,8 @@ class page_renderer
"{navigation-bar}" => self::render_navigation_bar($settings->nav_links, $settings->nav_links_extra, "top"), "{navigation-bar}" => self::render_navigation_bar($settings->nav_links, $settings->nav_links_extra, "top"),
"{navigation-bar-bottom}" => self::render_navigation_bar($settings->nav_links_bottom, [], "bottom"), "{navigation-bar-bottom}" => self::render_navigation_bar($settings->nav_links_bottom, [], "bottom"),
"{admin-details}" => hide_email($settings->admindetails_email, $settings->admindetails_name),
"{admin-details-name}" => $settings->admindetails_name, "{admin-details-name}" => $settings->admindetails_name,
"{admin-details-email}" => $settings->admindetails_email,
"{admins-name-list}" => implode(", ", array_map(function($username) { return page_renderer::render_username($username); }, $settings->admins)), "{admins-name-list}" => implode(", ", array_map(function($username) { return page_renderer::render_username($username); }, $settings->admins)),

View File

@ -45,7 +45,7 @@ function parse_page_source($source, $untrusted = false, $use_cache = true) {
if(!$settings->parser_cache || strlen($source) < $settings->parser_cache_min_size) $use_cache = false; if(!$settings->parser_cache || strlen($source) < $settings->parser_cache_min_size) $use_cache = false;
if(!isset($parsers[$settings->parser])) if(!isset($parsers[$settings->parser]))
exit(page_renderer::render_main("Parsing error - $settings->sitename", "<p>Parsing some page source data failed. This is most likely because $settings->sitename has the parser setting set incorrectly. Please contact <a href='mailto:" . hide_email($settings->admindetails_email) . "'>" . $settings->admindetails_name . "</a>, your $settings->sitename Administrator.")); exit(page_renderer::render_main("Parsing error - $settings->sitename", "<p>Parsing some page source data failed. This is most likely because $settings->sitename has the parser setting set incorrectly. Please contact " . hide_email($settings->admindetails_email, $settings->admindetails_name) . ", $settings->sitename's Administrator."));
/* Not needed atm because escaping happens when saving, not when rendering * /* Not needed atm because escaping happens when saving, not when rendering *
if($settings->clean_raw_html) if($settings->clean_raw_html)

View File

@ -1,7 +1,7 @@
<?php <?php
register_module([ register_module([
"name" => "Page Comments", "name" => "Page Comments",
"version" => "0.3.2", "version" => "0.3.3",
"author" => "Starbeamrainbowlabs", "author" => "Starbeamrainbowlabs",
"description" => "Adds threaded comments to the bottom of every page.", "description" => "Adds threaded comments to the bottom of every page.",
"id" => "feature-comments", "id" => "feature-comments",
@ -67,7 +67,7 @@ register_module([
if(!file_exists($comment_filename)) { if(!file_exists($comment_filename)) {
if(file_put_contents($comment_filename, "[]\n") === false) { if(file_put_contents($comment_filename, "[]\n") === false) {
http_response_code(503); http_response_code(503);
exit(page_renderer::renderer_main("Error posting comment - $settings->sitename", "<p>$settings->sitename ran into a problem whilst creating a file to save your comment to! Please contact <a href='mailto:" . hide_email($settings->admindetails_email) . "'>$settings->admindetails_name</a>, $settings->sitename's administrator and tell them about this problem.</p>")); exit(page_renderer::renderer_main("Error posting comment - $settings->sitename", "<p>$settings->sitename ran into a problem whilst creating a file to save your comment to! Please contact " . hide_email($settings->admindetails_email, $settings->admindetails_name) . ", $settings->sitename's administrator and tell them about this problem.</p>"));
} }
} }
@ -120,7 +120,7 @@ register_module([
// Save the comments back to disk // Save the comments back to disk
if(file_put_contents($comment_filename, json_encode($comment_data, JSON_PRETTY_PRINT)) === false) { if(file_put_contents($comment_filename, json_encode($comment_data, JSON_PRETTY_PRINT)) === false) {
http_response_code(503); http_response_code(503);
exit(page_renderer::renderer_main("Error posting comment - $settings->sitename", "<p>$settings->sitename ran into a problem whilst saving your comment to disk! Please contact <a href='mailto:" . hide_email($settings->admindetails_email) . "'>$settings->admindetails_name</a>, $settings->sitename's administrator and tell them about this problem.</p>")); exit(page_renderer::renderer_main("Error posting comment - $settings->sitename", "<p>$settings->sitename ran into a problem whilst saving your comment to disk! Please contact " . hide_email($settings->admindetails_email, $settings->admindetails_name) . ", $settings->sitename's administrator and tell them about this problem.</p>"));
} }
// Add a recent change if the recent changes module is installed // Add a recent change if the recent changes module is installed
@ -198,7 +198,7 @@ register_module([
if(!file_put_contents($comment_filename, json_encode($comments))) { if(!file_put_contents($comment_filename, json_encode($comments))) {
http_response_code(503); http_response_code(503);
exit(page_renderer::render_main("Server Error - Deleting Comment - $settings->sitename", "<p>While $settings->sitename was able to delete the comment with the id <code>" . htmlentities($target_id) . "</code> on the page <em>$env->page</em>, it couldn't save the changes back to disk. Please contact <a href='mailto:" . hide_email($settings->admindetails_email) . "'>$settings->admindetails_name</a>, $settings->sitename's local friendly administrator about this issue.</p>")); exit(page_renderer::render_main("Server Error - Deleting Comment - $settings->sitename", "<p>While $settings->sitename was able to delete the comment with the id <code>" . htmlentities($target_id) . "</code> on the page <em>$env->page</em>, it couldn't save the changes back to disk. Please contact " . hide_email($settings->admindetails_email, $settings->admindetails_name) . ", $settings->sitename's local friendly administrator about this issue.</p>"));
} }
exit(page_renderer::render_main("Comment Deleted - $settings->sitename", "<p>The comment with the id <code>" . htmlentities($target_id) . "</code> on the page <em>$env->page</em> has been deleted successfully. <a href='?page=" . rawurlencode($env->page) . "&redirect=no'>Go back</a> to " . htmlentities($env->page) . ".</p>")); exit(page_renderer::render_main("Comment Deleted - $settings->sitename", "<p>The comment with the id <code>" . htmlentities($target_id) . "</code> on the page <em>$env->page</em> has been deleted successfully. <a href='?page=" . rawurlencode($env->page) . "&redirect=no'>Go back</a> to " . htmlentities($env->page) . ".</p>"));

View File

@ -1,7 +1,7 @@
<?php <?php
register_module([ register_module([
"name" => "Reading time estimator", "name" => "Reading time estimator",
"version" => "0.1", "version" => "0.2",
"author" => "Starbeamrainbowlabs", "author" => "Starbeamrainbowlabs",
"description" => "Displays the approximate reading time for a page beneath it's title.", "description" => "Displays the approximate reading time for a page beneath it's title.",
"id" => "feature-readingtime", "id" => "feature-readingtime",
@ -44,7 +44,10 @@ register_module([
* @return array An array in the form [ low_time, high_time ] in minutes * @return array An array in the form [ low_time, high_time ] in minutes
*/ */
function estimate_reading_time(string $text, string $lang = "en") : array { function estimate_reading_time(string $text, string $lang = "en") : array {
$chars_count = mb_strlen($text); $chars_count = mb_strlen(preg_replace("/\s+?/", "", strtr($text, [
"[" => "", "]" => "", "(" => "", ")" => "",
"|" => "", "#" => "", "*" => ""
])));
$langs = [ $langs = [
"en" => (object) [ "cpm" => 987, "variance" => 118 ], "en" => (object) [ "cpm" => 987, "variance" => 118 ],
"ar" => (object) [ "cpm" => 612, "variance" => 88 ], "ar" => (object) [ "cpm" => 612, "variance" => 88 ],

View File

@ -305,7 +305,7 @@ function render_recent_change($rchange)
if($rchange_type === "revert") if($rchange_type === "revert")
$resultClasses[] = "reversion"; $resultClasses[] = "reversion";
$result .= "<a href='?page=" . rawurlencode($rchange->page) . (!empty($revisionId) ? "&revision=$revisionId" : "") . "'>$pageDisplayHtml</a> $editorDisplayHtml $timeDisplayHtml <span class='$size_display_class' title='$size_title_display'>($size_display)</span>"; $result .= "<a href='?page=" . rawurlencode($rchange->page) . (!empty($revisionId) ? "&amp;revision=$revisionId" : "") . (!empty($pageindex->{$rchange->page}->redirect) ? "&amp;redirect=no" : "" ) . "'>$pageDisplayHtml</a> $editorDisplayHtml $timeDisplayHtml <span class='$size_display_class' title='$size_title_display'>($size_display)</span>";
break; break;
case "deletion": case "deletion":

View File

@ -74,7 +74,7 @@ register_module([
if($env->action !== "view") if($env->action !== "view")
return; return;
$html = "<aside class='similar-page-suggestions'><h2>Other pages to explore</h2>\n\t\t<ul class='similar-page-suggestions-list'>\n"; $html = "<aside class='similar-page-suggestions'><h2>Similar Pages</h2>\n\t\t<ul class='similar-page-suggestions-list'>\n";
$start_time = microtime(true); $start_time = microtime(true);
$suggestions = similar_suggest( $suggestions = similar_suggest(
$env->page, $env->page,

View File

@ -1,7 +1,7 @@
<?php <?php
register_module([ register_module([
"name" => "Statistics", "name" => "Statistics",
"version" => "0.4.1", "version" => "0.4.3",
"author" => "Starbeamrainbowlabs", "author" => "Starbeamrainbowlabs",
"description" => "An extensible statistics calculation system. Comes with a range of built-in statistics, but can be extended by other modules too.", "description" => "An extensible statistics calculation system. Comes with a range of built-in statistics, but can be extended by other modules too.",
"id" => "feature-stats", "id" => "feature-stats",
@ -58,7 +58,7 @@ register_module([
switch($stat_calculator["type"]) { switch($stat_calculator["type"]) {
case "page-list": case "page-list":
if(!module_exists("page-list")) { if(!module_exists("page-list")) {
$content .= "<p>$settings->sitename doesn't current have the page listing module installed, so HTML rendering of this statistic is currently unavailable. Try <a href='mailto:" . hide_email($settings->admindetails_email) . "'>contacting $settings->admindetails_name</a>, $settings->sitename's administrator and asking then to install the <code>page-list</code> module.</p>"; $content .= "<p>$settings->sitename doesn't current have the page listing module installed, so HTML rendering of this statistic is currently unavailable. Try " . hide_email($settings->admindetails_email, "contacting $settings->admindetails_name") . ", $settings->sitename's administrator and asking then to install the <code>page-list</code> module.</p>";
break; break;
} }
$content .= "<p><strong>Count:</strong> " . count($stats->{$_GET["stat"]}->value) . "</p>\n"; $content .= "<p><strong>Count:</strong> " . count($stats->{$_GET["stat"]}->value) . "</p>\n";
@ -302,6 +302,10 @@ function update_statistics($update_all = false, $force = false)
{ {
global $settings, $env, $paths, $statistic_calculators; global $settings, $env, $paths, $statistic_calculators;
// If the firstrun wizard isn't complete, then there's no point in updating the statistics index
if(isset($settings->firstrun_complete) && $settings->firstrun_complete == false)
return;
$stats_mtime = filemtime($paths->statsindex); $stats_mtime = filemtime($paths->statsindex);
// Clear the existing statistics if we are asked to recalculate them all // Clear the existing statistics if we are asked to recalculate them all

View File

@ -1,7 +1,7 @@
<?php <?php
register_module([ register_module([
"name" => "User Preferences", "name" => "User Preferences",
"version" => "0.4", "version" => "0.4.1",
"author" => "Starbeamrainbowlabs", "author" => "Starbeamrainbowlabs",
"description" => "Adds a user preferences page, letting people do things like change their email address and password.", "description" => "Adds a user preferences page, letting people do things like change their email address and password.",
"id" => "feature-user-preferences", "id" => "feature-user-preferences",
@ -135,7 +135,7 @@ register_module([
// Save the user's preferences // Save the user's preferences
if(!save_userdata()) { if(!save_userdata()) {
http_response_code(503); http_response_code(503);
exit(page_renderer::render_main("Error Saving Preferences - $settings->sitename", "<p>$settings->sitename had some trouble saving your preferences! Please contact $settings->admindetails_name, $settings->sitename's administrator and tell them about this error if it still occurs in 5 minutes. They can be contacted by email at this address: <a href='mailto:" . hide_email($settings->admindetails_email) . "'>" . hide_email($settings->admindetails_email) . "</a>.</p>")); exit(page_renderer::render_main("Error Saving Preferences - $settings->sitename", "<p>$settings->sitename had some trouble saving your preferences! Please contact $settings->admindetails_name, $settings->sitename's administrator and tell them about this error if it still occurs in 5 minutes. They can be contacted by email at this address: ".hide_email($settings->admindetails_email).".</p>"));
} }
exit(page_renderer::render_main("Preferences Saved Successfully - $settings->sitename", "<p>Your preferences have been saved successfully! You could go back your <a href='?action=user-preferences'>preferences page</a>, or on to the <a href='?page=" . rawurlencode($settings->defaultpage) . "'>$settings->defaultpage</a>.</p> exit(page_renderer::render_main("Preferences Saved Successfully - $settings->sitename", "<p>Your preferences have been saved successfully! You could go back your <a href='?action=user-preferences'>preferences page</a>, or on to the <a href='?page=" . rawurlencode($settings->defaultpage) . "'>$settings->defaultpage</a>.</p>

View File

@ -1,7 +1,7 @@
<?php <?php
register_module([ register_module([
"name" => "Export", "name" => "Export",
"version" => "0.5", "version" => "0.5.1",
"author" => "Starbeamrainbowlabs", "author" => "Starbeamrainbowlabs",
"description" => "Adds a page that you can use to export your wiki as a .zip file. Uses \$settings->export_only_allow_admins, which controls whether only admins are allowed to export the wiki.", "description" => "Adds a page that you can use to export your wiki as a .zip file. Uses \$settings->export_only_allow_admins, which controls whether only admins are allowed to export the wiki.",
"id" => "page-export", "id" => "page-export",
@ -40,8 +40,7 @@ register_module([
$zip = new ZipArchive(); $zip = new ZipArchive();
if($zip->open($tmpfilename, ZipArchive::CREATE) !== true) if($zip->open($tmpfilename, ZipArchive::CREATE) !== true) {
{
http_response_code(507); http_response_code(507);
exit(page_renderer::render("Export error - $settings->sitename", "Pepperminty Wiki was unable to open a temporary file to store the exported data in. Please contact $settings->sitename's administrator (" . $settings->admindetails_name . " at " . hide_email($settings->admindetails_email) . ") for assistance.")); exit(page_renderer::render("Export error - $settings->sitename", "Pepperminty Wiki was unable to open a temporary file to store the exported data in. Please contact $settings->sitename's administrator (" . $settings->admindetails_name . " at " . hide_email($settings->admindetails_email) . ") for assistance."));
} }
@ -52,10 +51,9 @@ register_module([
$zip->addFile($entry->uploadedfilepath); $zip->addFile($entry->uploadedfilepath);
} }
if($zip->close() !== true) if($zip->close() !== true) {
{
http_response_code(500); http_response_code(500);
exit(page_renderer::render("Export error - $settings->sitename", "Pepperminty wiki was unable to close the temporary zip file after creating it. Please contact $settings->sitename's administrator (" . $settings->admindetails_name . " at " . hide_email($settings->admindetails_email) . ") for assistance.")); exit(page_renderer::render("Export error - $settings->sitename", "Pepperminty wiki was unable to close the temporary zip file after creating it. Please contact $settings->sitename's administrator (" . $settings->admindetails_name . " at " . hide_email($settings->admindetails_email) . ") for assistance (this might be a bug)."));
} }
header("content-type: application/zip"); header("content-type: application/zip");

View File

@ -1,7 +1,7 @@
<?php <?php
register_module([ register_module([
"name" => "Login", "name" => "Login",
"version" => "0.9.5", "version" => "0.9.6",
"author" => "Starbeamrainbowlabs", "author" => "Starbeamrainbowlabs",
"description" => "Adds a pair of actions (login and checklogin) that allow users to login. You need this one if you want your users to be able to login.", "description" => "Adds a pair of actions (login and checklogin) that allow users to login. You need this one if you want your users to be able to login.",
"id" => "page-login", "id" => "page-login",
@ -182,7 +182,7 @@ register_module([
// Register a section on logging in on the help page. // Register a section on logging in on the help page.
add_help_section("30-login", "Logging in", "<p>In order to edit $settings->sitename and have your edit attributed to you, you need to be logged in. Depending on the settings, logging in may be a required step if you want to edit at all. Thankfully, loggging in is not hard. Simply click the &quot;Login&quot; link in the top left, type your username and password, and then click login.</p> add_help_section("30-login", "Logging in", "<p>In order to edit $settings->sitename and have your edit attributed to you, you need to be logged in. Depending on the settings, logging in may be a required step if you want to edit at all. Thankfully, loggging in is not hard. Simply click the &quot;Login&quot; link in the top left, type your username and password, and then click login.</p>
<p>If you do not have an account yet and would like one, try contacting <a href='mailto:" . hide_email($settings->admindetails_email) . "'>$settings->admindetails_name</a>, $settings->sitename's administrator and ask them nicely to see if they can create you an account.</p>"); <p>If you do not have an account yet and would like one, try contacting " . hide_email($settings->admindetails_email, $settings->admindetails_name) . ", $settings->sitename's administrator and ask them nicely to see if they can create you an account.</p>");
// Re-check the password hashing cost, if necessary // Re-check the password hashing cost, if necessary
do_password_hash_code_update(); do_password_hash_code_update();

View File

@ -1,7 +1,7 @@
<?php <?php
register_module([ register_module([
"name" => "Parsedown", "name" => "Parsedown",
"version" => "0.11.1", "version" => "0.11.2",
"author" => "Emanuil Rusev & Starbeamrainbowlabs", "author" => "Emanuil Rusev & Starbeamrainbowlabs",
"description" => "An upgraded (now default!) parser based on Emanuil Rusev's Parsedown Extra PHP library (https://github.com/erusev/parsedown-extra), which is licensed MIT. Please be careful, as this module adds some weight to your installation.", "description" => "An upgraded (now default!) parser based on Emanuil Rusev's Parsedown Extra PHP library (https://github.com/erusev/parsedown-extra), which is licensed MIT. Please be careful, as this module adds some weight to your installation.",
"extra_data" => [ "extra_data" => [
@ -375,7 +375,7 @@ register_module([
<h3>Tips</h3> <h3>Tips</h3>
<ul> <ul>
<li>Put 2 spaces at the end of a line to add a soft line break. Leave a blank line to add a head line break (i.e. a new paragraph).</li> <li>Put 2 spaces at the end of a line to add a soft line break. Leave a blank line to add a head line break (i.e. a new paragraph).</li>
<li>You can add an id to a header that you can link to. Put it in curly braces after the heading name like this: <code># Heading Name {#HeadingId}</code>. Then you can link to like like this: <code>[[Page name#HeadingId}]]</code>. You can also link to a heading id on the current page by omitting the page name: <code>[[#HeadingId]]</code>.</li> <li>If you don't like the default id given to a header, you can add a custom one instead. Put it in curly braces after the heading name like this: <code># Heading Name {#HeadingId}</code>. Then you can link to like like this: <code>[[Page name#HeadingId}]]</code>. You can also link to a heading id on the current page by omitting the page name: <code>[[#HeadingId]]</code>. Finally, a heading id is automatically generated for every heading by default. Take the heading name, make it lowercase, and replace the spaces with dashes <code>.</code>, and that's the heading ID that you can link to (although sometimes some special characters are removed).</li>
</ul> </ul>
<h3>Extra Syntax</h3> <h3>Extra Syntax</h3>
<p>$settings->sitename's editor also supports some extra custom syntax, some of which is inspired by <a href='https://mediawiki.org/'>Mediawiki</a>. <p>$settings->sitename's editor also supports some extra custom syntax, some of which is inspired by <a href='https://mediawiki.org/'>Mediawiki</a>.
@ -408,7 +408,8 @@ register_module([
<tr><td><code>{{{~}}}</code></td><td>Outputs the requested page's name.</td></tr> <tr><td><code>{{{~}}}</code></td><td>Outputs the requested page's name.</td></tr>
<tr><td><code>{{{*}}}</code></td><td>Outputs a comma separated list of all the subpages of the current page.</td></tr> <tr><td><code>{{{*}}}</code></td><td>Outputs a comma separated list of all the subpages of the current page.</td></tr>
<tr><td><code>{{{+}}}</code></td><td>Shows a gallery containing all the files that are sub pages of the current page.</td></tr> <tr><td><code>{{{+}}}</code></td><td>Shows a gallery containing all the files that are sub pages of the current page.</td></tr>
</table>"); </table>
<p>Note that a page <em>doesn't not</em> need to be included as a template to use these variables.");
if($settings->parser_ext_renderers_enabled) { if($settings->parser_ext_renderers_enabled) {
$doc_help = "<p>$settings->sitename supports external renderers. External renderers take the content of a code fence block, like this:</p> $doc_help = "<p>$settings->sitename supports external renderers. External renderers take the content of a code fence block, like this:</p>
<pre><code>```language_code <pre><code>```language_code

View File

@ -1,6 +1,6 @@
{ {
"name": "pepperminty-wiki", "name": "pepperminty-wiki",
"version": "0.21.0", "version": "0.22.0-beta1",
"description": "A wiki in a box", "description": "A wiki in a box",
"main": "index.js", "main": "index.js",
"directories": { "directories": {

View File

@ -235,8 +235,8 @@
"avatars_size": { "type": "number", "description": "The image size to render avatars at. Does not affect the size they're stored at - only the inline rendered size (e.g. on the recent changes page etc.)", "default": 32}, "avatars_size": { "type": "number", "description": "The image size to render avatars at. Does not affect the size they're stored at - only the inline rendered size (e.g. on the recent changes page etc.)", "default": 32},
"search_characters_context": { "type": "number", "description": "The number of characters that should be displayed either side of a matching term in the context below each search result.", "default": 75}, "search_characters_context": { "type": "number", "description": "The number of characters that should be displayed either side of a matching term in the context below each search result.", "default": 75},
"search_characters_context_total": { "type": "number", "description": "The total number of characters that a search result context should display at most.", "default": 250 }, "search_characters_context_total": { "type": "number", "description": "The total number of characters that a search result context should display at most.", "default": 250 },
"search_title_matches_weighting": { "type": "number", "description": "The weighting to give to search term matches found in a page's title.", "default": 10 }, "search_title_matches_weighting": { "type": "number", "description": "The weighting to give to search term matches found in a page's title.", "default": 50 },
"search_tags_matches_weighting": { "type": "number", "description": "The weighting to give to search term matches found in a page's tags.", "default": 3 }, "search_tags_matches_weighting": { "type": "number", "description": "The weighting to give to search term matches found in a page's tags.", "default": 15 },
"search_didyoumean_enabled": { "type": "checkbox", "description": "Whether to enable the 'did you mean?' search query typo correction engine.", "default": false }, "search_didyoumean_enabled": { "type": "checkbox", "description": "Whether to enable the 'did you mean?' search query typo correction engine.", "default": false },
"search_didyoumean_editdistance": { "type": "number", "description": "The maximmum edit distance to search when checking for typos. Increasing this number causes an exponential increase in the amount of computing power required to correct all spellings.", "default": 2 }, "search_didyoumean_editdistance": { "type": "number", "description": "The maximmum edit distance to search when checking for typos. Increasing this number causes an exponential increase in the amount of computing power required to correct all spellings.", "default": 2 },
"search_didyoumean_cost_insert": { "type": "number", "description": "The insert cost to use when calculating levenshtein distances. If this value is changed then the did you mean index must be rebuilt.", "default": 1 }, "search_didyoumean_cost_insert": { "type": "number", "description": "The insert cost to use when calculating levenshtein distances. If this value is changed then the did you mean index must be rebuilt.", "default": 1 },

View File

@ -1 +1 @@
v0.22-dev v0.22-beta1