mirror of
https://github.com/sbrl/Pepperminty-Wiki.git
synced 2024-11-26 05:32:59 +00:00
Add restore locally changed content button
This commit is contained in:
parent
f9ad937a21
commit
bcf562e7ca
4 changed files with 110 additions and 33 deletions
|
@ -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`
|
- The `history` action now supports `format=json` and `format=csv`
|
||||||
- Added tags next to the names of pages in the search results
|
- 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 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 `get_page_parent($pagename)` method.
|
||||||
- [module api] Added new remote file system to download additional required files. Use it with `register_remote_file`
|
- [module api] Added new remote file system to download additional required files. Use it with `register_remote_file`
|
||||||
|
|
||||||
|
|
112
build/index.php
112
build/index.php
|
@ -4020,9 +4020,14 @@ window.addEventListener("load", function(event) {
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds a collection to methods to manipulate various types of search index.
|
||||||
|
*/
|
||||||
class search
|
class search
|
||||||
{
|
{
|
||||||
// Words that we should exclude from the inverted index
|
/**
|
||||||
|
* Words that we should exclude from the inverted index
|
||||||
|
*/
|
||||||
public static $stop_words = [
|
public static $stop_words = [
|
||||||
"a", "about", "above", "above", "across", "after", "afterwards", "again",
|
"a", "about", "above", "above", "across", "after", "afterwards", "again",
|
||||||
"against", "all", "almost", "alone", "along", "already", "also",
|
"against", "all", "almost", "alone", "along", "already", "also",
|
||||||
|
@ -4068,6 +4073,12 @@ class search
|
||||||
"your", "yours", "yourself", "yourselves"
|
"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)
|
public static function index($source)
|
||||||
{
|
{
|
||||||
$source = html_entity_decode($source, ENT_QUOTES);
|
$source = html_entity_decode($source, ENT_QUOTES);
|
||||||
|
@ -4098,6 +4109,11 @@ class search
|
||||||
return $index;
|
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)
|
public static function tokenize($source)
|
||||||
{
|
{
|
||||||
$source = strtolower($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);
|
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)
|
public static function strip_markup($source)
|
||||||
{
|
{
|
||||||
return str_replace([ "[", "]", "\"", "*", "_", " - ", "`" ], "", $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)
|
public static function rebuild_invindex($output = true)
|
||||||
{
|
{
|
||||||
global $pageindex, $env, $paths, $settings;
|
global $pageindex, $env, $paths, $settings;
|
||||||
|
@ -4158,25 +4184,23 @@ class search
|
||||||
self::save_invindex($paths->searchindex, $invindex);
|
self::save_invindex($paths->searchindex, $invindex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* @summary Sorts an index alphabetically. Will also sort an inverted index.
|
* Sorts an index alphabetically. Will also sort an inverted index.
|
||||||
* This allows us to do a binary search instead of a regular
|
* This allows us to do a binary search instead of a regular
|
||||||
* sequential search.
|
* sequential search.
|
||||||
|
* @param array $index The index to sort.
|
||||||
*/
|
*/
|
||||||
public static function sort_index(&$index)
|
public static function sort_index(&$index)
|
||||||
{
|
{
|
||||||
ksort($index, SORT_NATURAL);
|
ksort($index, SORT_NATURAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* @summary Compares two *regular* indexes to find the differences between them.
|
* Compares two *regular* indexes to find the differences between them.
|
||||||
*
|
* @param array $oldindex The old index.
|
||||||
* @param {array} $indexa - The old index.
|
* @param array $newindex The new 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} $changed - An array to be filled with the nterms of all
|
* @param array $removed An array to be filled with the nterms of all the removed entries.
|
||||||
* 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)
|
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) {
|
public static function load_invindex($invindex_filename) {
|
||||||
$invindex = json_decode(file_get_contents($invindex_filename), true);
|
$invindex = json_decode(file_get_contents($invindex_filename), true);
|
||||||
return $invindex;
|
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) {
|
public static function measure_invindex_load_time($invindex_filename) {
|
||||||
global $env;
|
global $env;
|
||||||
|
|
||||||
|
@ -4210,8 +4238,12 @@ class search
|
||||||
$env->perfdata->searchindex_decode_time = round((microtime(true) - $searchindex_decode_start)*1000, 3);
|
$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 = [])
|
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)
|
public static function save_invindex($filename, &$invindex)
|
||||||
{
|
{
|
||||||
file_put_contents($filename, json_encode($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)
|
public static function query_invindex($query, &$invindex)
|
||||||
{
|
{
|
||||||
global $settings, $pageindex;
|
global $settings, $pageindex;
|
||||||
|
@ -4368,7 +4411,7 @@ class search
|
||||||
|
|
||||||
// Sort the new list of clump distances
|
// Sort the new list of clump distances
|
||||||
sort($clumpDistances);
|
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);
|
$tightClumpLimit = floor((count($clumpDistances) - 1) / 0.25);
|
||||||
$tightClumpsMeasure = $clumpDistances[$tightClumpLimit] - $clumpDistances[0];
|
$tightClumpsMeasure = $clumpDistances[$tightClumpLimit] - $clumpDistances[0];
|
||||||
$clumpsRange = $clumpDistances[count($clumpDistances) - 1] - $clumpDistances[0];
|
$clumpsRange = $clumpDistances[count($clumpDistances) - 1] - $clumpDistances[0];
|
||||||
|
@ -4395,6 +4438,13 @@ class search
|
||||||
return $matching_pages;
|
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)
|
public static function extract_context($query, $source)
|
||||||
{
|
{
|
||||||
global $settings;
|
global $settings;
|
||||||
|
@ -4479,6 +4529,12 @@ class search
|
||||||
return implode(" ... ", $contexts);
|
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)
|
public static function highlight_context($query, $context)
|
||||||
{
|
{
|
||||||
$qterms = self::tokenize($query);
|
$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'>
|
$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)) . "' />
|
<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>
|
<textarea name='content' autofocus tabindex='1'>$pagetext</textarea>
|
||||||
<pre class='fit-text-mirror'></pre>
|
<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' />
|
<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 ~~~ ///
|
/// ~~~ Smart saving ~~~ ///
|
||||||
// TODO: Add a button to press that restores the content that you were working on before.
|
// 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
|
// 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
|
// Saving
|
||||||
document.querySelector("textarea[name=content]").addEventListener("keyup", function(event) { window.localStorage.setItem(getSmartSaveKey(), event.target.value) });
|
document.querySelector("textarea[name=content]").addEventListener("keyup", function(event) { window.localStorage.setItem(getSmartSaveKey(), event.target.value) });
|
||||||
// Loading
|
// Loading
|
||||||
window.addEventListener("load", function(event) {
|
|
||||||
var editor = document.querySelector("textarea[name=content]");
|
var editor = document.querySelector("textarea[name=content]");
|
||||||
if(editor.value.length > 0) return; // Don\'t restore if there\'s data in the editor already
|
let smartsave_restore = function() {
|
||||||
editor.value = localStorage.getItem(getSmartSaveKey());
|
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();
|
||||||
});
|
});
|
||||||
});');
|
});');
|
||||||
|
|
||||||
|
|
|
@ -104,7 +104,7 @@
|
||||||
"author": "Starbeamrainbowlabs",
|
"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.",
|
"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",
|
"id": "feature-search",
|
||||||
"lastupdate": 1508071155,
|
"lastupdate": 1511208633,
|
||||||
"optional": false
|
"optional": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -167,7 +167,7 @@
|
||||||
"author": "Starbeamrainbowlabs",
|
"author": "Starbeamrainbowlabs",
|
||||||
"description": "Allows you to edit pages by adding the edit and save actions. You should probably include this one.",
|
"description": "Allows you to edit pages by adding the edit and save actions. You should probably include this one.",
|
||||||
"id": "page-edit",
|
"id": "page-edit",
|
||||||
"lastupdate": 1510613807,
|
"lastupdate": 1511294696,
|
||||||
"optional": false
|
"optional": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -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'>
|
$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)) . "' />
|
<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>
|
<textarea name='content' autofocus tabindex='1'>$pagetext</textarea>
|
||||||
<pre class='fit-text-mirror'></pre>
|
<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' />
|
<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 ~~~ ///
|
/// ~~~ Smart saving ~~~ ///
|
||||||
// TODO: Add a button to press that restores the content that you were working on before.
|
// 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
|
// 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
|
// Saving
|
||||||
document.querySelector("textarea[name=content]").addEventListener("keyup", function(event) { window.localStorage.setItem(getSmartSaveKey(), event.target.value) });
|
document.querySelector("textarea[name=content]").addEventListener("keyup", function(event) { window.localStorage.setItem(getSmartSaveKey(), event.target.value) });
|
||||||
// Loading
|
// Loading
|
||||||
window.addEventListener("load", function(event) {
|
|
||||||
var editor = document.querySelector("textarea[name=content]");
|
var editor = document.querySelector("textarea[name=content]");
|
||||||
if(editor.value.length > 0) return; // Don\'t restore if there\'s data in the editor already
|
let smartsave_restore = function() {
|
||||||
editor.value = localStorage.getItem(getSmartSaveKey());
|
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();
|
||||||
});
|
});
|
||||||
});');
|
});');
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue