From 96cf3a6e32291a937d2fb6289e29995945479bc1 Mon Sep 17 00:00:00 2001 From: Ryan Abel Date: Fri, 4 Dec 2020 00:04:04 -0600 Subject: [PATCH 01/16] Add estimate range settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds range settings for reading time estimate ranges. estimate_range to control the range percentage ± and range_str for the string to combine the low and high estimates. --- blueprints.yaml | 17 +++++++++++++++++ classes/TwigReadingTimeFilters.php | 4 +++- readingtime.yaml | 2 ++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/blueprints.yaml b/blueprints.yaml index 3f22d21..9efd81e 100644 --- a/blueprints.yaml +++ b/blueprints.yaml @@ -36,6 +36,23 @@ form: min: 1 max: 1000 + estimate_range: + type: text + size: x-small + append: ±% + help: 'Percentage to add and remove from the reading time estimate to get a reading time range. 10% on a 10-minute read would give 9–11 minutes.' + label: Reading time estimate range + validate: + type: int + min: 0 + max: 100 + + range_str: + type: text + label: Estimate range string + help: 'String to use between the low and high estimate range numbers (e.g., "—", "-", " to ", etc.).' + size: x-small + format: type: text label: Format diff --git a/classes/TwigReadingTimeFilters.php b/classes/TwigReadingTimeFilters.php index d5545e3..62cc342 100644 --- a/classes/TwigReadingTimeFilters.php +++ b/classes/TwigReadingTimeFilters.php @@ -51,6 +51,8 @@ public function getReadingTime( $content, $params = array() ) $words = count(preg_split('/\s+/', strip_tags($content)) ?: []); $wpm = $options['words_per_minute']; + $estimate_range = ($options['estimate_range'] / 100); + $range_str = $options['range_str']; $minutes_short_count = floor($words / $wpm); $seconds_short_count = floor($words % $wpm / ($wpm / 60)); @@ -91,7 +93,7 @@ public function getReadingTime( $content, $params = array() ) $minutes_long_count = number_format($minutes_short_count, 2); $seconds_long_count = number_format($seconds_short_count, 2); - + if (array_key_exists('minute_label', $options) and $minutes_short_count == 1) { $minutes_text = $options['minute_label']; } elseif (array_key_exists('minutes_label', $options) and $minutes_short_count > 1) { diff --git a/readingtime.yaml b/readingtime.yaml index 9247030..44a309b 100644 --- a/readingtime.yaml +++ b/readingtime.yaml @@ -1,5 +1,7 @@ enabled: true words_per_minute: 200 +estimate_range: 15 +range_str: "–" format: "{minutes_short_count} {minutes_text}, {seconds_short_count} {seconds_text}" round: seconds include_image_views: false From 8536bc854c17d0f41d5d1c2189d6b26766305b14 Mon Sep 17 00:00:00 2001 From: Ryan Abel Date: Fri, 4 Dec 2020 00:06:58 -0600 Subject: [PATCH 02/16] Add estimate range calculation --- classes/TwigReadingTimeFilters.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/classes/TwigReadingTimeFilters.php b/classes/TwigReadingTimeFilters.php index 62cc342..5691586 100644 --- a/classes/TwigReadingTimeFilters.php +++ b/classes/TwigReadingTimeFilters.php @@ -57,6 +57,9 @@ public function getReadingTime( $content, $params = array() ) $minutes_short_count = floor($words / $wpm); $seconds_short_count = floor($words % $wpm / ($wpm / 60)); + $minutes_low_range = floor(($words * (1 - $estimate_range)) / $wpm); + $minutes_high_range = floor(($words * (1 + $estimate_range)) / $wpm); + if ($options['include_image_views']) { $stripped = strip_tags($content, ""); $images_in_content = substr_count($stripped, "grav['log']->error("Plugin 'readingtime' - seconds_per_image failed regex vadation"); } @@ -82,10 +88,16 @@ public function getReadingTime( $content, $params = array() ) $round = $options['round']; if ($round == 'minutes') { - $minutes_short_count = round(($minutes_short_count*60 + $seconds_short_count) / 60); + $minutes_short_count = round(($minutes_short_count * 60 + $seconds_short_count) / 60); + + $minutes_low_range = round(($minutes_low_range * 60 + $seconds_low_range) / 60); + $minutes_high_range = round(($minutes_high_range * 60 + $seconds_high_range) / 60); if ( $minutes_short_count < 1 ) { $minutes_short_count = 1; + + $minutes_low_range = 0; + $minutes_high_range = 1; } $seconds_short_count = 0; From e5f9a85a96fdb870858e6b59c9787d3d4da14618 Mon Sep 17 00:00:00 2001 From: Ryan Abel Date: Fri, 4 Dec 2020 00:07:20 -0600 Subject: [PATCH 03/16] Add estimate range formatting Depending on the reading time estimate and the estimate range percentage, we may end up with identical high and low estimates or flooring out at zero minutes. Test for these cases and return a range that makes sense. --- classes/TwigReadingTimeFilters.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/classes/TwigReadingTimeFilters.php b/classes/TwigReadingTimeFilters.php index 5691586..4159bc2 100644 --- a/classes/TwigReadingTimeFilters.php +++ b/classes/TwigReadingTimeFilters.php @@ -105,6 +105,23 @@ public function getReadingTime( $content, $params = array() ) $minutes_long_count = number_format($minutes_short_count, 2); $seconds_long_count = number_format($seconds_short_count, 2); + $minutes_low_range_long = number_format($minutes_low_range, 2); + $minutes_high_range_long = number_format($minutes_high_range, 2); + + if ($minutes_low_range == $minutes_high_range or $minutes_low_range == 0) { + $minutes_range_short = $minutes_short_count; + $minutes_range_long = $minutes_long_count; + } elseif ($minutes_low_range == 0) { + $minutes_range_short = $minutes_short_count; + $minutes_range_long = $minutes_long_count; + } else { + $minutes_range_short = ( + $minutes_low_range . $range_str . $minutes_high_range + ); + $minutes_range_long = ( + $minutes_low_range_long . $range_str . $minutes_high_range_long + ); + } if (array_key_exists('minute_label', $options) and $minutes_short_count == 1) { $minutes_text = $options['minute_label']; From 638fb96b07eb573cd541c823c8323221da652893 Mon Sep 17 00:00:00 2001 From: Ryan Abel Date: Fri, 4 Dec 2020 00:08:49 -0600 Subject: [PATCH 04/16] Add estimate range to return list --- classes/TwigReadingTimeFilters.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/classes/TwigReadingTimeFilters.php b/classes/TwigReadingTimeFilters.php index 4159bc2..714ff0f 100644 --- a/classes/TwigReadingTimeFilters.php +++ b/classes/TwigReadingTimeFilters.php @@ -140,12 +140,14 @@ public function getReadingTime( $content, $params = array() ) } $replace = [ - 'minutes_short_count' => $minutes_short_count, - 'seconds_short_count' => $seconds_short_count, - 'minutes_long_count' => $minutes_long_count, - 'seconds_long_count' => $seconds_long_count, - 'minutes_text' => $minutes_text, - 'seconds_text' => $seconds_text + 'minutes_short_count' => $minutes_short_count, + 'minutes_range_short' => $minutes_range_short, + 'seconds_short_count' => $seconds_short_count, + 'minutes_long_count' => $minutes_long_count, + 'minutes_range_long' => $minutes_range_long, + 'seconds_long_count' => $seconds_long_count, + 'minutes_text' => $minutes_text, + 'seconds_text' => $seconds_text ]; $result = $options['format']; From afca3a272d3401d785cf4d04c761f401ac1d2100 Mon Sep 17 00:00:00 2001 From: Ryan Abel Date: Fri, 4 Dec 2020 00:26:57 -0600 Subject: [PATCH 05/16] Flip range and short/long order in variable names --- classes/TwigReadingTimeFilters.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/classes/TwigReadingTimeFilters.php b/classes/TwigReadingTimeFilters.php index 714ff0f..dec4ef7 100644 --- a/classes/TwigReadingTimeFilters.php +++ b/classes/TwigReadingTimeFilters.php @@ -109,16 +109,16 @@ public function getReadingTime( $content, $params = array() ) $minutes_high_range_long = number_format($minutes_high_range, 2); if ($minutes_low_range == $minutes_high_range or $minutes_low_range == 0) { - $minutes_range_short = $minutes_short_count; - $minutes_range_long = $minutes_long_count; + $minutes_short_range = $minutes_short_count; + $minutes_long_range = $minutes_long_count; } elseif ($minutes_low_range == 0) { - $minutes_range_short = $minutes_short_count; - $minutes_range_long = $minutes_long_count; + $minutes_short_range = $minutes_short_count; + $minutes_long_range = $minutes_long_count; } else { - $minutes_range_short = ( + $minutes_short_range = ( $minutes_low_range . $range_str . $minutes_high_range ); - $minutes_range_long = ( + $minutes_long_range = ( $minutes_low_range_long . $range_str . $minutes_high_range_long ); } @@ -141,10 +141,10 @@ public function getReadingTime( $content, $params = array() ) $replace = [ 'minutes_short_count' => $minutes_short_count, - 'minutes_range_short' => $minutes_range_short, + 'minutes_short_range' => $minutes_short_range, 'seconds_short_count' => $seconds_short_count, 'minutes_long_count' => $minutes_long_count, - 'minutes_range_long' => $minutes_range_long, + 'minutes_long_range' => $minutes_long_range, 'seconds_long_count' => $seconds_long_count, 'minutes_text' => $minutes_text, 'seconds_text' => $seconds_text From 2a4cc30f8fdc89aeb92cc2beac8d77afef07243a Mon Sep 17 00:00:00 2001 From: Carl Sinclair <69092633+CarlSinclair@users.noreply.github.com> Date: Sat, 13 Jul 2024 13:49:27 -0300 Subject: [PATCH 06/16] Update TwigReadingTimeFilters.php --- classes/TwigReadingTimeFilters.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/classes/TwigReadingTimeFilters.php b/classes/TwigReadingTimeFilters.php index 7ef5137..39b417c 100644 --- a/classes/TwigReadingTimeFilters.php +++ b/classes/TwigReadingTimeFilters.php @@ -91,7 +91,7 @@ public function getReadingTime( $content, $params = array() ) $minutes_long_count = number_format($minutes_short_count, 2); $seconds_long_count = number_format($seconds_short_count, 2); - + if (array_key_exists('minute_label', $options) and $minutes_short_count == 1) { $minutes_text = $options['minute_label']; } elseif (array_key_exists('minutes_label', $options) and $minutes_short_count > 1) { @@ -108,13 +108,17 @@ public function getReadingTime( $content, $params = array() ) $seconds_text = $language->translate(( $seconds_short_count == 1 ) ? 'PLUGIN_READINGTIME.SECOND' : 'PLUGIN_READINGTIME.SECONDS'); } + // Adding translation for reading_label + $reading_label = $language->translate('PLUGIN_READINGTIME.READING_LABEL'); + $replace = [ 'minutes_short_count' => $minutes_short_count, 'seconds_short_count' => $seconds_short_count, 'minutes_long_count' => $minutes_long_count, 'seconds_long_count' => $seconds_long_count, 'minutes_text' => $minutes_text, - 'seconds_text' => $seconds_text + 'seconds_text' => $seconds_text, + 'reading_label' => $reading_label ]; $result = $options['format']; From 6ed6a324cd6ca032f893bee679931c4bdb919d91 Mon Sep 17 00:00:00 2001 From: Carl Sinclair <69092633+CarlSinclair@users.noreply.github.com> Date: Sat, 13 Jul 2024 13:55:59 -0300 Subject: [PATCH 07/16] Update languages.yaml --- languages.yaml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/languages.yaml b/languages.yaml index 3387d7a..9668f93 100644 --- a/languages.yaml +++ b/languages.yaml @@ -4,6 +4,7 @@ en: SECONDS: seconds MINUTE: minute MINUTES: minutes + READING_LABEL: 'Reading time' cs: PLUGIN_READINGTIME: @@ -11,6 +12,7 @@ cs: SECONDS: sekundy MINUTE: minuta MINUTES: minuty + READING_LABEL: 'Čas na čtení' es: PLUGIN_READINGTIME: @@ -18,6 +20,7 @@ es: SECONDS: segundos MINUTE: minuto MINUTES: minutos + READING_LABEL: 'Tiempo de lectura' fr: PLUGIN_READINGTIME: @@ -25,6 +28,7 @@ fr: SECONDS: secondes MINUTE: minute MINUTES: minutes + READING_LABEL: 'Temps de lecture' hr: PLUGIN_READINGTIME: @@ -32,6 +36,7 @@ hr: SECONDS: sekundi MINUTE: minuta MINUTES: minuta + READING_LABEL: 'Olvasási idő' it: PLUGIN_READINGTIME: @@ -39,6 +44,7 @@ it: SECONDS: secondi MINUTE: minuto MINUTES: minuti + READING_LABEL: 'Momento della lettura' nl: PLUGIN_READINGTIME: @@ -46,13 +52,23 @@ nl: SECONDS: seconden MINUTE: minuut MINUTES: minuten + READING_LABEL: Leestijd +pt: + PLUGIN_READINGTIME: + SECOND: segundo + SECONDS: segundos + MINUTE: minuto + MINUTES: minutos + READING_LABEL: 'Tempo de leitura' + pt-BR: PLUGIN_READINGTIME: SECOND: segundo SECONDS: segundos MINUTE: minuto MINUTES: minutos + READING_LABEL: 'Tempo de leitura' de: PLUGIN_READINGTIME: @@ -60,3 +76,12 @@ de: SECONDS: Sekunden MINUTE: Minute MINUTES: Minuten + READING_LABEL: Lesezeit + +ar: + PLUGIN_READINGTIME: + SECOND: ثانية + SECONDS: ثواني + MINUTE: دقيقة + MINUTES: دقائق + READING_LABEL: 'وقت القراءة' From ade808c48cc91619d6e67b2864d4a0e85fccb329 Mon Sep 17 00:00:00 2001 From: Carl Sinclair <69092633+CarlSinclair@users.noreply.github.com> Date: Sat, 13 Jul 2024 13:56:43 -0300 Subject: [PATCH 08/16] Update readingtime.yaml --- readingtime.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readingtime.yaml b/readingtime.yaml index 9247030..824876b 100644 --- a/readingtime.yaml +++ b/readingtime.yaml @@ -1,6 +1,6 @@ enabled: true words_per_minute: 200 -format: "{minutes_short_count} {minutes_text}, {seconds_short_count} {seconds_text}" +format: "{reading_label}: {minutes_short_count} {minutes_text}, {seconds_short_count} {seconds_text}" round: seconds include_image_views: false seconds_per_image: '12,11,10,9,8,7,6,5,4,3' From 8ce42a3260dbc4ce6531874247935b0d1d60ee02 Mon Sep 17 00:00:00 2001 From: Ryan Abel Date: Fri, 4 Dec 2020 00:26:57 -0600 Subject: [PATCH 09/16] Flip range and short/long order in variable names --- classes/TwigReadingTimeFilters.php | 31 ++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/classes/TwigReadingTimeFilters.php b/classes/TwigReadingTimeFilters.php index 39b417c..f6b1017 100644 --- a/classes/TwigReadingTimeFilters.php +++ b/classes/TwigReadingTimeFilters.php @@ -91,6 +91,23 @@ public function getReadingTime( $content, $params = array() ) $minutes_long_count = number_format($minutes_short_count, 2); $seconds_long_count = number_format($seconds_short_count, 2); + $minutes_low_range_long = number_format($minutes_low_range, 2); + $minutes_high_range_long = number_format($minutes_high_range, 2); + + if ($minutes_low_range == $minutes_high_range or $minutes_low_range == 0) { + $minutes_short_range = $minutes_short_count; + $minutes_long_range = $minutes_long_count; + } elseif ($minutes_low_range == 0) { + $minutes_short_range = $minutes_short_count; + $minutes_long_range = $minutes_long_count; + } else { + $minutes_short_range = ( + $minutes_low_range . $range_str . $minutes_high_range + ); + $minutes_long_range = ( + $minutes_low_range_long . $range_str . $minutes_high_range_long + ); + } if (array_key_exists('minute_label', $options) and $minutes_short_count == 1) { $minutes_text = $options['minute_label']; @@ -112,12 +129,14 @@ public function getReadingTime( $content, $params = array() ) $reading_label = $language->translate('PLUGIN_READINGTIME.READING_LABEL'); $replace = [ - 'minutes_short_count' => $minutes_short_count, - 'seconds_short_count' => $seconds_short_count, - 'minutes_long_count' => $minutes_long_count, - 'seconds_long_count' => $seconds_long_count, - 'minutes_text' => $minutes_text, - 'seconds_text' => $seconds_text, + 'minutes_short_count' => $minutes_short_count, + 'minutes_short_range' => $minutes_short_range, + 'seconds_short_count' => $seconds_short_count, + 'minutes_long_count' => $minutes_long_count, + 'minutes_long_range' => $minutes_long_range, + 'seconds_long_count' => $seconds_long_count, + 'minutes_text' => $minutes_text, + 'seconds_text' => $seconds_text, 'reading_label' => $reading_label ]; From 0059454b7e7671fa934d5eb61a45089c1d530814 Mon Sep 17 00:00:00 2001 From: Carl Sinclair <69092633+CarlSinclair@users.noreply.github.com> Date: Sat, 20 Jul 2024 21:42:10 -0300 Subject: [PATCH 10/16] Now caches reading time in page headers for better performance --- readingtime.php | 70 ++++++++++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/readingtime.php b/readingtime.php index 230c301..5a3e751 100644 --- a/readingtime.php +++ b/readingtime.php @@ -1,49 +1,65 @@ [ - ['autoload', 100000], - ['onPluginsInitialized', 0] - ] + 'onPageContentProcessed' => ['onPageContentProcessed', 0] ]; } - /** - * [onPluginsInitialized:100000] Composer autoload. - * - * @return ClassLoader - */ - public function autoload() + public function onPageContentProcessed(Event $event) { - return require __DIR__ . '/vendor/autoload.php'; + $page = $event['page']; + $cacheKey = 'readingtime-' . $page->id(); + + $readingTime = $this->grav['cache']->fetch($cacheKey); + + if ($readingTime === false) { + $content = $page->content(); + $readingTime = $this->calculateReadingTime($content); + $this->grav['cache']->save($cacheKey, $readingTime); + } + + $this->modifyHeader($page, $readingTime); } - public function onPluginsInitialized() + private function modifyHeader($page, $readingTime) { - if ($this->isAdmin()) { - $this->active = false; - return; - } - - $this->enable([ - 'onTwigExtensions' => [ - ['onTwigExtensions', 0] - ] - ]); + $header = $page->header(); + if (isset($header)) { + // --- Translate Reading Time (without seconds) --- + $language = $this->grav['language']; + $options = array_merge($this->grav['config']->get('plugins.readingtime'), []); + $minutes_short_count = $readingTime; + + $minutes_text = ($minutes_short_count == 1) ? + $language->translate('PLUGIN_READINGTIME.MINUTE') : + $language->translate('PLUGIN_READINGTIME.MINUTES'); + + $readingTimeString = sprintf( + '%s: %s %s', + $language->translate('PLUGIN_READINGTIME.READING_LABEL'), + $minutes_short_count, + $minutes_text + ); + + $header->readingTime = $readingTimeString; + $page->rawRoute($page->route(), $header); + } } - public function onTwigExtensions() + private function calculateReadingTime($text) { - $this->grav['twig']->twig->addExtension(new TwigReadingTimeFilters()); + $wordCount = str_word_count(strip_tags($text)); + $wordsPerMinute = 200; + $readingTime = ceil($wordCount / $wordsPerMinute); + + return $readingTime; } } From ec6310e9f3de46f992878ee17f0b704e68a39026 Mon Sep 17 00:00:00 2001 From: Carl Sinclair <69092633+CarlSinclair@users.noreply.github.com> Date: Sat, 20 Jul 2024 21:45:27 -0300 Subject: [PATCH 11/16] Revert "Merge remote-tracking branch 'github-desktop-GeneralAntilles/range_estimate' into develop" This reverts commit 36d01e046106261ba2f5563a6b2098ac04115c8f, reversing changes made to 8ce42a3260dbc4ce6531874247935b0d1d60ee02. --- blueprints.yaml | 17 --------------- classes/TwigReadingTimeFilters.php | 34 +----------------------------- readingtime.yaml | 2 -- 3 files changed, 1 insertion(+), 52 deletions(-) diff --git a/blueprints.yaml b/blueprints.yaml index af3a434..608e738 100644 --- a/blueprints.yaml +++ b/blueprints.yaml @@ -36,23 +36,6 @@ form: min: 1 max: 1000 - estimate_range: - type: text - size: x-small - append: ±% - help: 'Percentage to add and remove from the reading time estimate to get a reading time range. 10% on a 10-minute read would give 9–11 minutes.' - label: Reading time estimate range - validate: - type: int - min: 0 - max: 100 - - range_str: - type: text - label: Estimate range string - help: 'String to use between the low and high estimate range numbers (e.g., "—", "-", " to ", etc.).' - size: x-small - format: type: text label: Format diff --git a/classes/TwigReadingTimeFilters.php b/classes/TwigReadingTimeFilters.php index b66508f..f6b1017 100644 --- a/classes/TwigReadingTimeFilters.php +++ b/classes/TwigReadingTimeFilters.php @@ -51,15 +51,10 @@ public function getReadingTime( $content, $params = array() ) $words = count(preg_split('/\s+/', strip_tags((string) $content)) ?: []); $wpm = $options['words_per_minute']; - $estimate_range = ($options['estimate_range'] / 100); - $range_str = $options['range_str']; $minutes_short_count = floor($words / $wpm); $seconds_short_count = floor($words % $wpm / ($wpm / 60)); - $minutes_low_range = floor(($words * (1 - $estimate_range)) / $wpm); - $minutes_high_range = floor(($words * (1 + $estimate_range)) / $wpm); - if ($options['include_image_views']) { $stripped = strip_tags($content, ""); $images_in_content = substr_count($stripped, "grav['log']->error("Plugin 'readingtime' - seconds_per_image failed regex vadation"); } @@ -88,16 +80,10 @@ public function getReadingTime( $content, $params = array() ) $round = $options['round']; if ($round == 'minutes') { - $minutes_short_count = round(($minutes_short_count * 60 + $seconds_short_count) / 60); - - $minutes_low_range = round(($minutes_low_range * 60 + $seconds_low_range) / 60); - $minutes_high_range = round(($minutes_high_range * 60 + $seconds_high_range) / 60); + $minutes_short_count = round(($minutes_short_count*60 + $seconds_short_count) / 60); if ( $minutes_short_count < 1 ) { $minutes_short_count = 1; - - $minutes_low_range = 0; - $minutes_high_range = 1; } $seconds_short_count = 0; @@ -123,24 +109,6 @@ public function getReadingTime( $content, $params = array() ) ); } - $minutes_low_range_long = number_format($minutes_low_range, 2); - $minutes_high_range_long = number_format($minutes_high_range, 2); - - if ($minutes_low_range == $minutes_high_range or $minutes_low_range == 0) { - $minutes_short_range = $minutes_short_count; - $minutes_long_range = $minutes_long_count; - } elseif ($minutes_low_range == 0) { - $minutes_short_range = $minutes_short_count; - $minutes_long_range = $minutes_long_count; - } else { - $minutes_short_range = ( - $minutes_low_range . $range_str . $minutes_high_range - ); - $minutes_long_range = ( - $minutes_low_range_long . $range_str . $minutes_high_range_long - ); - } - if (array_key_exists('minute_label', $options) and $minutes_short_count == 1) { $minutes_text = $options['minute_label']; } elseif (array_key_exists('minutes_label', $options) and $minutes_short_count > 1) { diff --git a/readingtime.yaml b/readingtime.yaml index 2cd111f..824876b 100644 --- a/readingtime.yaml +++ b/readingtime.yaml @@ -1,7 +1,5 @@ enabled: true words_per_minute: 200 -estimate_range: 15 -range_str: "–" format: "{reading_label}: {minutes_short_count} {minutes_text}, {seconds_short_count} {seconds_text}" round: seconds include_image_views: false From 355182c2d5ed5316be6426c337edb921164654e1 Mon Sep 17 00:00:00 2001 From: Carl Sinclair <69092633+CarlSinclair@users.noreply.github.com> Date: Sat, 20 Jul 2024 21:45:32 -0300 Subject: [PATCH 12/16] Revert "Flip range and short/long order in variable names" This reverts commit 8ce42a3260dbc4ce6531874247935b0d1d60ee02. --- classes/TwigReadingTimeFilters.php | 31 ++++++------------------------ 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/classes/TwigReadingTimeFilters.php b/classes/TwigReadingTimeFilters.php index f6b1017..39b417c 100644 --- a/classes/TwigReadingTimeFilters.php +++ b/classes/TwigReadingTimeFilters.php @@ -91,23 +91,6 @@ public function getReadingTime( $content, $params = array() ) $minutes_long_count = number_format($minutes_short_count, 2); $seconds_long_count = number_format($seconds_short_count, 2); - $minutes_low_range_long = number_format($minutes_low_range, 2); - $minutes_high_range_long = number_format($minutes_high_range, 2); - - if ($minutes_low_range == $minutes_high_range or $minutes_low_range == 0) { - $minutes_short_range = $minutes_short_count; - $minutes_long_range = $minutes_long_count; - } elseif ($minutes_low_range == 0) { - $minutes_short_range = $minutes_short_count; - $minutes_long_range = $minutes_long_count; - } else { - $minutes_short_range = ( - $minutes_low_range . $range_str . $minutes_high_range - ); - $minutes_long_range = ( - $minutes_low_range_long . $range_str . $minutes_high_range_long - ); - } if (array_key_exists('minute_label', $options) and $minutes_short_count == 1) { $minutes_text = $options['minute_label']; @@ -129,14 +112,12 @@ public function getReadingTime( $content, $params = array() ) $reading_label = $language->translate('PLUGIN_READINGTIME.READING_LABEL'); $replace = [ - 'minutes_short_count' => $minutes_short_count, - 'minutes_short_range' => $minutes_short_range, - 'seconds_short_count' => $seconds_short_count, - 'minutes_long_count' => $minutes_long_count, - 'minutes_long_range' => $minutes_long_range, - 'seconds_long_count' => $seconds_long_count, - 'minutes_text' => $minutes_text, - 'seconds_text' => $seconds_text, + 'minutes_short_count' => $minutes_short_count, + 'seconds_short_count' => $seconds_short_count, + 'minutes_long_count' => $minutes_long_count, + 'seconds_long_count' => $seconds_long_count, + 'minutes_text' => $minutes_text, + 'seconds_text' => $seconds_text, 'reading_label' => $reading_label ]; From d30bb46bf9a7b3a6a160521cd41478c1563d1a88 Mon Sep 17 00:00:00 2001 From: Carl Sinclair <69092633+CarlSinclair@users.noreply.github.com> Date: Sat, 20 Jul 2024 21:47:17 -0300 Subject: [PATCH 13/16] Now caches reading time in page headers for better performance --- classes/TwigReadingTimeFilters.php | 223 +++++++++++++++-------------- 1 file changed, 114 insertions(+), 109 deletions(-) diff --git a/classes/TwigReadingTimeFilters.php b/classes/TwigReadingTimeFilters.php index 39b417c..9dbf29e 100644 --- a/classes/TwigReadingTimeFilters.php +++ b/classes/TwigReadingTimeFilters.php @@ -8,133 +8,138 @@ class TwigReadingTimeFilters extends Twig_Extension { - private $grav; - - public function __construct() - { - $this->grav = Grav::instance(); - } - - public function getName() - { - return 'TwigReadingTimeFilters'; - } - - public function getFilters() - { - return [ - new \Twig_SimpleFilter( 'readingtime', [$this, 'getReadingTime'] ) - ]; - } - - public function validatePattern($seconds_per_image) - { - // Get regex that is used in the user interface - $pattern = '/' . $this->grav['plugins']->get('readingtime')->blueprints()->schema()->get('seconds_per_image')['validate']['pattern'] . '/'; - - if (preg_match($pattern, $seconds_per_image, $matches) === false) { - return false; - } - - // Note: "$matches[0] will contain the text that matched the full pattern" - // https://www.php.net/manual/en/function.preg-match.php - return strlen($seconds_per_image) === strlen($matches[0]); - } - - public function getReadingTime( $content, $params = array() ) - { + private $grav; - $this->mergeConfig($this->grav['page']); - $language = $this->grav['language']; + public function __construct() + { + $this->grav = Grav::instance(); + } - $options = array_merge($this->grav['config']->get('plugins.readingtime'), $params); + public function getName() + { + return 'TwigReadingTimeFilters'; + } - $words = count(preg_split('/\s+/', strip_tags((string) $content)) ?: []); - $wpm = $options['words_per_minute']; + public function getFilters() + { + return [ + new \Twig_SimpleFilter('readingtime', [$this, 'getReadingTime']) + ]; + } - $minutes_short_count = floor($words / $wpm); - $seconds_short_count = floor($words % $wpm / ($wpm / 60)); + public function validatePattern($seconds_per_image) + { + // Get regex that is used in the user interface + $pattern = '/' . $this->grav['plugins']->get('readingtime')->blueprints()->schema()->get('seconds_per_image')['validate']['pattern'] . '/'; - if ($options['include_image_views']) { - $stripped = strip_tags($content, ""); - $images_in_content = substr_count($stripped, " 0) { - if ($this->validatePattern($options['seconds_per_image'])) { + // Note: "$matches[0] will contain the text that matched the full pattern" + // https://www.php.net/manual/en/function.preg-match.php + return strlen($seconds_per_image) === strlen($matches[0]); + } - // assumes string only contains integers, commas, and whitespace - $spi = preg_split('/\D+/', trim($options['seconds_per_image'])); - $seconds_images = 0; + public function getReadingTime($input, $params = array()) + { + $this->mergeConfig($this->grav['page']); + $language = $this->grav['language']; + + $options = array_merge($this->grav['config']->get('plugins.readingtime'), $params); + + // --- FIX: Use readingTime from header --- + if (is_object($input) && $input instanceof Page && isset($input->header()->readingTime)) { + $minutes_short_count = $input->header()->readingTime; + } else { // Calculate reading time if not already available + $content = is_object($input) ? $input->content() : $input; + $words = count(preg_split('/\s+/', strip_tags((string)$content)) ?: []); + $wpm = $options['words_per_minute']; + + $minutes_short_count = floor($words / $wpm); + $seconds_short_count = floor($words % $wpm / ($wpm / 60)); + + if ($options['include_image_views']) { + $stripped = strip_tags($content, ""); + $images_in_content = substr_count($stripped, " 0) { + if ($this->validatePattern($options['seconds_per_image'])) { + + // assumes string only contains integers, commas, and whitespace + $spi = preg_split('/\D+/', trim($options['seconds_per_image'])); + $seconds_images = 0; + + for ($i = 0; $i < $images_in_content; ++$i) { + $seconds_images += $i < count($spi) ? $spi[$i] : end($spi); + } + + $minutes_short_count += floor($seconds_images / 60); + $seconds_short_count += $seconds_images % 60; + } else { + $this->grav['log']->error("Plugin 'readingtime' - seconds_per_image failed regex vadation"); + } + } + } + + $round = $options['round']; + if ($round == 'minutes') { + $minutes_short_count = round(($minutes_short_count * 60 + $seconds_short_count) / 60); + + if ($minutes_short_count < 1) { + $minutes_short_count = 1; + } + + $seconds_short_count = 0; + } + } - for ($i = 0; $i < $images_in_content; ++$i) { - $seconds_images += $i < count($spi) ? $spi[$i] : end($spi); - } + $minutes_long_count = number_format($minutes_short_count, 2); + $seconds_long_count = number_format($seconds_short_count, 2); - $minutes_short_count += floor($seconds_images / 60); - $seconds_short_count += $seconds_images % 60; + if (array_key_exists('minute_label', $options) and $minutes_short_count == 1) { + $minutes_text = $options['minute_label']; + } elseif (array_key_exists('minutes_label', $options) and $minutes_short_count > 1) { + $minutes_text = $options['minutes_label']; } else { - $this->grav['log']->error("Plugin 'readingtime' - seconds_per_image failed regex vadation"); + $minutes_text = $language->translate(($minutes_short_count == 1) ? 'PLUGIN_READINGTIME.MINUTE' : 'PLUGIN_READINGTIME.MINUTES'); } - } - } - - $round = $options['round']; - if ($round == 'minutes') { - $minutes_short_count = round(($minutes_short_count*60 + $seconds_short_count) / 60); - if ( $minutes_short_count < 1 ) { - $minutes_short_count = 1; - } - - $seconds_short_count = 0; - } - - $minutes_long_count = number_format($minutes_short_count, 2); - $seconds_long_count = number_format($seconds_short_count, 2); - - if (array_key_exists('minute_label', $options) and $minutes_short_count == 1) { - $minutes_text = $options['minute_label']; - } elseif (array_key_exists('minutes_label', $options) and $minutes_short_count > 1) { - $minutes_text = $options['minutes_label']; - } else { - $minutes_text = $language->translate(( $minutes_short_count == 1 ) ? 'PLUGIN_READINGTIME.MINUTE' : 'PLUGIN_READINGTIME.MINUTES'); - } + if (array_key_exists('second_label', $options) and $seconds_short_count == 1) { + $seconds_text = $options['second_label']; + } elseif (array_key_exists('seconds_label', $options) and $seconds_short_count > 1) { + $seconds_text = $options['seconds_label']; + } else { + $seconds_text = $language->translate(($seconds_short_count == 1) ? 'PLUGIN_READINGTIME.SECOND' : 'PLUGIN_READINGTIME.SECONDS'); + } - if (array_key_exists('second_label', $options) and $seconds_short_count == 1) { - $seconds_text = $options['second_label']; - } elseif (array_key_exists('seconds_label', $options) and $seconds_short_count > 1) { - $seconds_text = $options['seconds_label']; - } else { - $seconds_text = $language->translate(( $seconds_short_count == 1 ) ? 'PLUGIN_READINGTIME.SECOND' : 'PLUGIN_READINGTIME.SECONDS'); - } + // Adding translation for reading_label + $reading_label = $language->translate('PLUGIN_READINGTIME.READING_LABEL'); - // Adding translation for reading_label - $reading_label = $language->translate('PLUGIN_READINGTIME.READING_LABEL'); + $replace = [ + 'minutes_short_count' => $minutes_short_count, + 'seconds_short_count' => $seconds_short_count, + 'minutes_long_count' => $minutes_long_count, + 'seconds_long_count' => $seconds_long_count, + 'minutes_text' => $minutes_text, + 'seconds_text' => $seconds_text, + 'reading_label' => $reading_label, + ]; - $replace = [ - 'minutes_short_count' => $minutes_short_count, - 'seconds_short_count' => $seconds_short_count, - 'minutes_long_count' => $minutes_long_count, - 'seconds_long_count' => $seconds_long_count, - 'minutes_text' => $minutes_text, - 'seconds_text' => $seconds_text, - 'reading_label' => $reading_label - ]; + $result = $options['format']; - $result = $options['format']; + foreach ($replace as $key => $value) { + $result = str_replace('{' . $key . '}', $value, $result); + } - foreach ( $replace as $key => $value ) { - $result = str_replace('{' . $key . '}', $value, $result); + return $result; } - return $result; - } - - private function mergeConfig( Page $page ) - { - $defaults = (array) $this->grav['config']->get('plugins.readingtime'); - if ( isset($page->header()->readingtime) ) { - $this->grav['config']->set('plugins.readingtime', array_merge($defaults, $page->header()->readingtime)); + private function mergeConfig(Page $page) + { + $defaults = (array)$this->grav['config']->get('plugins.readingtime'); + if (isset($page->header()->readingtime)) { + $this->grav['config']->set('plugins.readingtime', array_merge($defaults, $page->header()->readingtime)); + } } - } } From 674270833418942feb82863477a80c0c47121c4f Mon Sep 17 00:00:00 2001 From: Carl Sinclair <69092633+CarlSinclair@users.noreply.github.com> Date: Sat, 20 Jul 2024 22:02:06 -0300 Subject: [PATCH 14/16] Update README.md --- README.md | 53 ++++++++++++----------------------------------------- 1 file changed, 12 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 3fb1c95..c883290 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,11 @@ -# Grav ReadingTime Plugin +# Grav BetterReadingTime Plugin -**ReadingTime** is a [Grav](http://github.com/getgrav/grav) plugin which allows Grav to display the reading time of a page's content. This is especially useful for blogs and other sites as it gives the reader a quick idea of how much time they will need to set aside to read the page in full. +**BetterReadingTime** is a [Grav](http://github.com/getgrav/grav) plugin which allows Grav to display the reading time of a page's content. This is especially useful for blogs and other sites as it gives the reader a quick idea of how much time they will need to set aside to read the page in full. + +***This fork differs from the original in two main ways:*** +1. ***Reading label is translated, not just "minutes" and "seconds".*** +2. ***Reading time (and label) is cached, in translated form, in the page headers of every page. This negates the need to run the calculation on every page load if reading time is present in the frontmatter.*** +3. ***Disabled seconds. Reading time is rounded to the nearest minute for a cleaner view.*** Enabling the plugin is very simple. Just install the plugin folder to `/user/plugins/` in your Grav install. By default, the plugin is enabled. @@ -21,48 +26,14 @@ The contents of the zipped folder should now be located in the `/your/site/grav/ Place the following line of code in the theme file you wish to add the ReadingTime plugin for: ``` -{{ page.content|readingtime }} -``` - -You need to pass a Twig Filter 'readingtime' to display the reading time values. You can translate the labels with this example: - -``` -{{ page.content|readingtime({'minutes_label': 'Minuti', 'minute_label': 'Minuto', 'seconds_label': 'Secondi', 'second_label': 'Secondo'}) }} -``` - -I used Italian translation for the labels, you can change with your language. - -If you need you can change the format with this avariable variables (the code is default format): - -``` -{{ page.content|readingtime({'format': '{minutes_short_count} {minutes_text}, {seconds_short_count} {seconds_text}'}) }} +{% if config.plugins.readingtime.enabled %} +{{ page.header.readingTime }}{{ page|readingtime }} +{% endif %} ``` -Available variables: - -| Variable | Description | Example | -| :---------------- | :---------------------- | :--------------------------------------------------------------------------- | -| `{minute_label}` | Minute Label (Singular) | `minute` | -| `{minutes_label}` | Minutes Label (Plural) | `minutes` | -| `{second_label}` | Second Label (Singular) | `second` | -| `{seconds_label}` | Second Label (Plural) | `seconds` | -| `{format}` | Display Format | `{minutes_text} {minutes_short_count}, {seconds_text} {seconds_short_count}` | - -Not available to edit but used in the format variable: - -| Variable | Description | Example | -| :---------------------- | :--------------------------------------- | :------ | -| `{minutes_short_count}` | Displays Minutes with Abbreviated Digits | `2` | -| `{seconds_short_count}` | Displays Seconds with Abbreviated Digits | `9` | -| `{minutes_long_count}` | Displays Minutes in Double Digits | `02` | -| `{seconds_long_count}` | Displays Seconds in Double Digits | `09` | - -Display variables for text labels: +You need both of these twig variables to make it work. I can't remember why both are required, but if I remove either one it breaks. So just use both. -| Variable | Description | Example | -| :--------------- | :------------------------------------------------------------------------------- | :-------- | -| `{minutes_text}` | Displays the Minutes Text Label (Singular or Plural, Based on Number of Minutes) | `minute` | -| `{seconds_text}` | Displays the Seconds Text Label (Singular or Plural, Based on Number of Seconds) | `seconds` | +I haven't tested image views so I can't tell you if they still work. I also dabbled with the "estimated reading time" PR on the original repo but eventually decided not to include it, so you might find vestigial code from that PR here that I forgot to clean up. >> NOTE: Any time you are making alterations to a theme's files, you will want to duplicate the theme folder in the `user/themes/` directory, rename it, and set the new name as your active theme. This will ensure that you don't lose your customizations in the event that a theme is updated. Once you have tested the change thoroughly, you can delete or back up that folder elsewhere. From e59b1efc25c849c3ef79a777fc7d594289b28494 Mon Sep 17 00:00:00 2001 From: Carl Sinclair <69092633+CarlSinclair@users.noreply.github.com> Date: Sat, 20 Jul 2024 22:02:25 -0300 Subject: [PATCH 15/16] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c883290..ffdf798 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ **BetterReadingTime** is a [Grav](http://github.com/getgrav/grav) plugin which allows Grav to display the reading time of a page's content. This is especially useful for blogs and other sites as it gives the reader a quick idea of how much time they will need to set aside to read the page in full. -***This fork differs from the original in two main ways:*** +***This fork differs from the original in three main ways:*** 1. ***Reading label is translated, not just "minutes" and "seconds".*** 2. ***Reading time (and label) is cached, in translated form, in the page headers of every page. This negates the need to run the calculation on every page load if reading time is present in the frontmatter.*** 3. ***Disabled seconds. Reading time is rounded to the nearest minute for a cleaner view.*** From c1793d310991b9b12f4769f096824a895896cc73 Mon Sep 17 00:00:00 2001 From: Carl Sinclair <69092633+CarlSinclair@users.noreply.github.com> Date: Sat, 27 Jul 2024 19:43:47 -0300 Subject: [PATCH 16/16] Fixed race condition --- readingtime.php | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/readingtime.php b/readingtime.php index 5a3e751..01c7f97 100644 --- a/readingtime.php +++ b/readingtime.php @@ -18,13 +18,13 @@ public function onPageContentProcessed(Event $event) $page = $event['page']; $cacheKey = 'readingtime-' . $page->id(); + // Ensure synchronous cache access $readingTime = $this->grav['cache']->fetch($cacheKey); - if ($readingTime === false) { $content = $page->content(); $readingTime = $this->calculateReadingTime($content); $this->grav['cache']->save($cacheKey, $readingTime); - } + } $this->modifyHeader($page, $readingTime); } @@ -32,26 +32,21 @@ public function onPageContentProcessed(Event $event) private function modifyHeader($page, $readingTime) { $header = $page->header(); - if (isset($header)) { - // --- Translate Reading Time (without seconds) --- - $language = $this->grav['language']; - $options = array_merge($this->grav['config']->get('plugins.readingtime'), []); - $minutes_short_count = $readingTime; - $minutes_text = ($minutes_short_count == 1) ? - $language->translate('PLUGIN_READINGTIME.MINUTE') : - $language->translate('PLUGIN_READINGTIME.MINUTES'); + $minutes_short_count = $readingTime; + $minutes_text = ($minutes_short_count == 1) ? + $this->grav['language']->translate('PLUGIN_READINGTIME.MINUTE') : + $this->grav['language']->translate('PLUGIN_READINGTIME.MINUTES'); - $readingTimeString = sprintf( - '%s: %s %s', - $language->translate('PLUGIN_READINGTIME.READING_LABEL'), - $minutes_short_count, - $minutes_text - ); + $readingTimeString = sprintf( + '%s: %s %s', + $this->grav['language']->translate('PLUGIN_READINGTIME.READING_LABEL'), + $minutes_short_count, + $minutes_text + ); - $header->readingTime = $readingTimeString; - $page->rawRoute($page->route(), $header); - } + $header->readingTime = $readingTimeString; + $page->header($header); } private function calculateReadingTime($text)