Add restore locally changed content button

This commit is contained in:
Starbeamrainbowlabs 2017-11-21 20:07:50 +00:00
parent f9ad937a21
commit bcf562e7ca
4 changed files with 110 additions and 33 deletions

View File

@ -15,6 +15,7 @@ This file holds the changelog for Pepperminty Wiki. This is the master list of t
- The `history` action now supports `format=json` and `format=csv`
- Added tags next to the names of pages in the search results
- Added new `random_page_exclude` setting that allows you to exclude pages from the random action with a (PHP) regular expression
- Added "restore locally saved content" button to page editor
- [module api] Added new `get_page_parent($pagename)` method.
- [module api] Added new remote file system to download additional required files. Use it with `register_remote_file`

View File

@ -4020,9 +4020,14 @@ window.addEventListener("load", function(event) {
}
]);
/**
* Holds a collection to methods to manipulate various types of search index.
*/
class search
{
// Words that we should exclude from the inverted index
/**
* Words that we should exclude from the inverted index
*/
public static $stop_words = [
"a", "about", "above", "above", "across", "after", "afterwards", "again",
"against", "all", "almost", "alone", "along", "already", "also",
@ -4068,6 +4073,12 @@ class search
"your", "yours", "yourself", "yourselves"
];
/**
* Converts a source string into an index of search terms that can be
* merged into an inverted index.
* @param string $source The source string to index.
* @return array An index represents the specified string.
*/
public static function index($source)
{
$source = html_entity_decode($source, ENT_QUOTES);
@ -4098,6 +4109,11 @@ class search
return $index;
}
/**
* Converts a source string into a series of raw tokens.
* @param string $source The source string to process.
* @return array An array of raw tokens extracted from the specified source string.
*/
public static function tokenize($source)
{
$source = strtolower($source);
@ -4105,11 +4121,21 @@ class search
return preg_split("/((^\p{P}+)|(\p{P}*\s+\p{P}*)|(\p{P}+$))|\|/u", $source, -1, PREG_SPLIT_NO_EMPTY);
}
/**
* Removes (most) markdown markup from the specified string.
* Stripped strings are not suitable for indexing!
* @param string $source The source string to process.
* @return string The stripped string.
*/
public static function strip_markup($source)
{
return str_replace([ "[", "]", "\"", "*", "_", " - ", "`" ], "", $source);
}
/**
* Rebuilds the master inverted index and clears the page id index.
* @param boolean $output Whether to send progress information to the user's browser.
*/
public static function rebuild_invindex($output = true)
{
global $pageindex, $env, $paths, $settings;
@ -4158,25 +4184,23 @@ class search
self::save_invindex($paths->searchindex, $invindex);
}
/*
* @summary Sorts an index alphabetically. Will also sort an inverted index.
* This allows us to do a binary search instead of a regular
* sequential search.
/**
* Sorts an index alphabetically. Will also sort an inverted index.
* This allows us to do a binary search instead of a regular
* sequential search.
* @param array $index The index to sort.
*/
public static function sort_index(&$index)
{
ksort($index, SORT_NATURAL);
}
/*
* @summary Compares two *regular* indexes to find the differences between them.
*
* @param {array} $indexa - The old index.
* @param {array} $indexb - The new index.
* @param {array} $changed - An array to be filled with the nterms of all
* the changed entries.
* @param {array} $removed - An array to be filled with the nterms of all
* the removed entries.
/**
* Compares two *regular* indexes to find the differences between them.
* @param array $oldindex The old index.
* @param array $newindex The new index.
* @param array $changed An array to be filled with the nterms of all the changed entries.
* @param array $removed An array to be filled with the nterms of all the removed entries.
*/
public static function compare_indexes($oldindex, $newindex, &$changed, &$removed)
{
@ -4193,15 +4217,19 @@ class search
}
}
/*
* @summary Reads in and parses an inverted index.
/**
* Reads in and parses an inverted index.
* @param string $invindex_filename The path tp the inverted index to parse.
* @todo Remove this function and make everything streamable
*/
// Todo remove this function and make everything streamable
public static function load_invindex($invindex_filename) {
$invindex = json_decode(file_get_contents($invindex_filename), true);
return $invindex;
}
/**
* Reads in and parses an inverted index, measuring the time it takes to do so.
* @param string $invindex_filename The path to the file inverted index to parse.
*/
public static function measure_invindex_load_time($invindex_filename) {
global $env;
@ -4210,8 +4238,12 @@ class search
$env->perfdata->searchindex_decode_time = round((microtime(true) - $searchindex_decode_start)*1000, 3);
}
/*
* @summary Merge an index into an inverted index.
/**
* Merge an index into an inverted index.
* @param array $invindex The inverted index to merge into.
* @param int $pageid The id of the page to assign to the index that's being merged.
* @param array $index The regular index to merge.
* @param array $removals An array of index entries to remove from the inverted index. Useful for applying changes to an inverted index instead of deleting and remerging an entire page's index.
*/
public static function merge_into_invindex(&$invindex, $pageid, &$index, &$removals = [])
{
@ -4262,11 +4294,22 @@ class search
}
}
/**
* Saves the given inverted index back to disk.
* @param string $filename The path to the file to save the inverted index to.
* @param array $invindex The inverted index to save.
*/
public static function save_invindex($filename, &$invindex)
{
file_put_contents($filename, json_encode($invindex));
}
/**
* Searches the given inverted index for the specified search terms.
* @param string $query The search query.
* @param array $invindex The inverted index to search.
* @return array An array of matching pages.
*/
public static function query_invindex($query, &$invindex)
{
global $settings, $pageindex;
@ -4368,7 +4411,7 @@ class search
// Sort the new list of clump distances
sort($clumpDistances);
// Calcualate a measureof how clumped the offsets are
// Calcualate a measure of how clumped the offsets are
$tightClumpLimit = floor((count($clumpDistances) - 1) / 0.25);
$tightClumpsMeasure = $clumpDistances[$tightClumpLimit] - $clumpDistances[0];
$clumpsRange = $clumpDistances[count($clumpDistances) - 1] - $clumpDistances[0];
@ -4395,6 +4438,13 @@ class search
return $matching_pages;
}
/**
* Extracts a context string (in HTML) given a search query that could be displayed
* in a list of search results.
* @param string $query The search queary to generate the context for.
* @param string $source The page source to extract the context from.
* @return string The generated context string.
*/
public static function extract_context($query, $source)
{
global $settings;
@ -4479,6 +4529,12 @@ class search
return implode(" ... ", $contexts);
}
/**
* Highlights the keywords of a context string.
* @param string $query The query to use when highlighting.
* @param string $context The context string to highlight.
* @return string The highlighted (HTML) string.
*/
public static function highlight_context($query, $context)
{
$qterms = self::tokenize($query);
@ -6165,6 +6221,7 @@ register_module([
$content .= "<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='" . ((isset($old_pagetext)) ? sha1($old_pagetext) : sha1($pagetext)) . "' />
<button class='smartsave-restore'>Restore Locally Saved Content</button>
<textarea name='content' autofocus tabindex='1'>$pagetext</textarea>
<pre class='fit-text-mirror'></pre>
<input type='text' 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' />
@ -6208,16 +6265,25 @@ window.addEventListener("load", function(event) {
/// ~~~ Smart saving ~~~ ///
// TODO: Add a button to press that restores the content that you were working on before.
page_renderer::AddJSSnippet('document.addEventListener("load", function(event) {
page_renderer::AddJSSnippet('window.addEventListener("load", function(event) {
"use strict";
// Smart saving
function getSmartSaveKey() { return document.querySelector("main h1").innerHTML.replace("Creating ", "").replace("Editing ", "").trim(); }
let getSmartSaveKey = function() { return document.querySelector("main h1").innerHTML.replace("Creating ", "").replace("Editing ", "").trim(); }
// Saving
document.querySelector("textarea[name=content]").addEventListener("keyup", function(event) { window.localStorage.setItem(getSmartSaveKey(), event.target.value) });
// Loading
window.addEventListener("load", function(event) {
var editor = document.querySelector("textarea[name=content]");
if(editor.value.length > 0) return; // Don\'t restore if there\'s data in the editor already
var editor = document.querySelector("textarea[name=content]");
let smartsave_restore = function() {
editor.value = localStorage.getItem(getSmartSaveKey());
}
if(editor.value.length === 0) // Don\'t restore if there\'s data in the editor already
smartsave_restore();
document.querySelector(".smartsave-restore").addEventListener("click", function(event) {
event.stopPropagation();
event.preventDefault();
smartsave_restore();
});
});');

View File

@ -104,7 +104,7 @@
"author": "Starbeamrainbowlabs",
"description": "Adds proper search functionality to Pepperminty Wiki using an inverted index to provide a full text search engine. If pages don't show up, then you might have hit a stop word. If not, try requesting the `invindex-rebuild` action to rebuild the inverted index from scratch.",
"id": "feature-search",
"lastupdate": 1508071155,
"lastupdate": 1511208633,
"optional": false
},
{
@ -167,7 +167,7 @@
"author": "Starbeamrainbowlabs",
"description": "Allows you to edit pages by adding the edit and save actions. You should probably include this one.",
"id": "page-edit",
"lastupdate": 1510613807,
"lastupdate": 1511294696,
"optional": false
},
{

View File

@ -138,6 +138,7 @@ register_module([
$content .= "<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='" . ((isset($old_pagetext)) ? sha1($old_pagetext) : sha1($pagetext)) . "' />
<button class='smartsave-restore'>Restore Locally Saved Content</button>
<textarea name='content' autofocus tabindex='1'>$pagetext</textarea>
<pre class='fit-text-mirror'></pre>
<input type='text' 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' />
@ -181,16 +182,25 @@ window.addEventListener("load", function(event) {
/// ~~~ Smart saving ~~~ ///
// TODO: Add a button to press that restores the content that you were working on before.
page_renderer::AddJSSnippet('document.addEventListener("load", function(event) {
page_renderer::AddJSSnippet('window.addEventListener("load", function(event) {
"use strict";
// Smart saving
function getSmartSaveKey() { return document.querySelector("main h1").innerHTML.replace("Creating ", "").replace("Editing ", "").trim(); }
let getSmartSaveKey = function() { return document.querySelector("main h1").innerHTML.replace("Creating ", "").replace("Editing ", "").trim(); }
// Saving
document.querySelector("textarea[name=content]").addEventListener("keyup", function(event) { window.localStorage.setItem(getSmartSaveKey(), event.target.value) });
// Loading
window.addEventListener("load", function(event) {
var editor = document.querySelector("textarea[name=content]");
if(editor.value.length > 0) return; // Don\'t restore if there\'s data in the editor already
var editor = document.querySelector("textarea[name=content]");
let smartsave_restore = function() {
editor.value = localStorage.getItem(getSmartSaveKey());
}
if(editor.value.length === 0) // Don\'t restore if there\'s data in the editor already
smartsave_restore();
document.querySelector(".smartsave-restore").addEventListener("click", function(event) {
event.stopPropagation();
event.preventDefault();
smartsave_restore();
});
});');