diff --git a/Changelog.md b/Changelog.md index 8b39875..53d110e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -16,6 +16,8 @@ This file holds the changelog for Pepperminty Wiki. This is the master list of t - Put `[__TOC__]` on a line by itself to insert an automatic table of contents - Note that the level of heading generated can be controlled (or even removed) by the new `parser_toc_heading_level` setting - Add `` support with the new `theme_colour` setting. More information: [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta/name/theme-color), [caniuse](https://caniuse.com/#feat=meta-theme-color). Also used by some platforms to customise embed accents when generating a rich snippet (e.g. Discord). + - Added reading time estimate to the top of wiki pages - control it with the new `readingtime_enabled` setting (#172) + - The algorithm used to estimate reading times is the as the one used in Firefox's reader mode ### Changed - Fiddled with Parsedown & ParsedownExtra versions diff --git a/modules/feature-readingtime.php b/modules/feature-readingtime.php new file mode 100644 index 0000000..85deeeb --- /dev/null +++ b/modules/feature-readingtime.php @@ -0,0 +1,71 @@ + "Reading time estimator", + "version" => "0.1", + "author" => "Starbeamrainbowlabs", + "description" => "Displays the approximate reading time for a page beneath it's title.", + "id" => "feature-readingtime", + "code" => function() { + + page_renderer::register_part_preprocessor(function(&$parts) { + global $env, $settings;; + error_log("[readingtime] part_preprocessor, matching $settings->readingtime_action"); + // Only insert for the view action + if($env->action !== $settings->readingtime_action || !$settings->readingtime_enabled) + return; + error_log("[readingtime] go ahead"); + + $reading_time = estimate_reading_time( + file_get_contents($env->page_filename), + $settings->readingtime_language + ); + error_log(var_export($reading_time, true)); + + $insert = "{$reading_time[0]} - {$reading_time[1]} minute read"; + if($reading_time[0] === $reading_time[1]) + $insert = "{$reading_time[0]} minute read"; + + // TODO: Create a canonical way to insert something just below the header - this might be tough though 'cause the that isn't handled by the page_renderer though + $parts["{content}"] = str_replace("", "
$insert
", $parts["{content}"]); + }); + } +]); + +/** + * Estimates the reading time for a given lump of text. + * Ref https://github.com/sbrl/Pepperminty-Wiki/issues/172 (has snippet of + * original code from Firefox & link to study from which the numbers are + * taken). + * @param string $text The text to estimate for. + * @param string $lang The language code of the text - defaults to "en" + * @return array An array in the form [ low_time, high_time ] in minutes + */ +function estimate_reading_time(string $text, string $lang = "en") : array { + $chars_count = mb_strlen($text); + $langs = [ + "en" => (object) [ "cpm" => 987, "variance" => 118 ], + "ar" => (object) [ "cpm" => 612, "variance" => 88 ], + "de" => (object) [ "cpm" => 920, "variance" => 86 ], + "es" => (object) [ "cpm" => 1025, "variance" => 127 ], + "fi" => (object) [ "cpm" => 1078, "variance" => 121 ], + "fr" => (object) [ "cpm" => 998, "variance" => 126 ], + "he" => (object) [ "cpm" => 833, "variance" => 130 ], + "it" => (object) [ "cpm" => 950, "variance" => 140 ], + "jw" => (object) [ "cpm" => 357, "variance" => 56 ], + "nl" => (object) [ "cpm" => 978, "variance" => 143 ], + "pl" => (object) [ "cpm" => 916, "variance" => 126 ], + "pt" => (object) [ "cpm" => 913, "variance" => 145 ], + "ru" => (object) [ "cpm" => 986, "variance" => 175 ], + "sk" => (object) [ "cpm" => 885, "variance" => 145 ], + "sv" => (object) [ "cpm" => 917, "variance" => 156 ], + "tr" => (object) [ "cpm" => 1054, "variance" => 156 ], + "zh" => (object) [ "cpm" => 255, "variance" => 29 ], + ]; + if(!isset($langs[$lang])) + return null; + + return [ + ceil($chars_count / ($langs[$lang]->cpm + $langs[$lang]->variance)), + ceil($chars_count / ($langs[$lang]->cpm - $langs[$lang]->variance)) + ]; +} diff --git a/modules/page-view.php b/modules/page-view.php index 2016d35..4e91acc 100644 --- a/modules/page-view.php +++ b/modules/page-view.php @@ -92,16 +92,16 @@ register_module([ $content .= "(Revision saved by {$env->history->revision_data->editor} " . render_timestamp($env->history->revision_data->timestamp) . ". Jump to the current revision or see a list of all revisions for this page.)
\n"; + $content .= "(Revision saved by {$env->history->revision_data->editor} " . render_timestamp($env->history->revision_data->timestamp) . ". Jump to the current revision or see a list of all revisions for this page.)
\n"; } // Add a visit parent page link if we're a subpage if(get_page_parent($env->page) !== false) - $content .= "« " . htmlentities(get_page_parent($env->page)) . "
\n"; + $content .= "« " . htmlentities(get_page_parent($env->page)) . "
\n"; // Add an extra message if the requester was redirected from another page if(isset($_GET["redirected_from"])) - $content .= "Redirected from " . $_GET["redirected_from"] . ".
\n"; + $content .= "Redirected from " . $_GET["redirected_from"] . ".
\n"; $parsing_start = microtime(true); diff --git a/peppermint.guiconfig.json b/peppermint.guiconfig.json index 6db41e9..b5e5486 100644 --- a/peppermint.guiconfig.json +++ b/peppermint.guiconfig.json @@ -98,6 +98,9 @@ "password_cost_time_lastcheck": { "type": "number", "description": "Pseudo-setting used to keep track of the last recalculation of password_cost. Is updated with the current unix timestamp every time password_cost is recalculated.", "default": 0}, "new_password_length": { "type": "number", "description": "The length of newly-generated passwords. This is currently used in the user table when creating new accounts.", "default": 32}, "require_login_view": { "type": "checkbox", "description": "Whether to require that users login before they do anything else. Best used with the data_storage_dir option.", "default": false}, + "readingtime_enabled": { "type": "checkbox", "description": "Whether to display the estimated reading time beneath the header of every wiki page.", "default": true }, + "readingtime_language": { "type": "text", "description": "The language code to use when estimating the reading time. Possible values: en, ar, de, es, fi, fr, he, it, jw, nl, pl, pt, ru, sk, sv, tr, zh. Unfrotuantely adding multi-language support to the user interface is an absolutely massive undertaking that would take ages, as Peppermitny Wiki waasn't designed with that in mind :-/", "default": "en" }, + "readingtime_action": { "type": "text", "description": "The name of the action to enable the reading time estimation on. You probably shouldn't change this unless you know what you're doing.", "default": "view" }, "data_storage_dir": { "type": "text", "description": "The directory in which to store all files, except the main index.php.", "default": "." }, "watchlists_enable": { "type": "checkbox", "description": "Whether the watchlists feature should be enabled or not.", "default": true }, "delayed_indexing_time": { "type": "number", "description": "The amount of time, in seconds, that pages should be blocked from being indexed by search engines after their last edit. Aka delayed indexing.", "default": 0},