From 595429d425c5e6a64c24f61af2edc5503d792101 Mon Sep 17 00:00:00 2001 From: Ryan Cramer Date: Tue, 12 Nov 2019 12:06:08 -0500 Subject: [PATCH] Add a minimumAge config setting for PagePathHistory module per processwire/processwire-issues#1014 and add an option for throwing a 404 while still allowing PagePathHistory to perform redirects (if it has any). Also add a function: wire404(); that you can call for this, in addition to: throw new Wire404Exception(true); --- wire/core/Exceptions.php | 8 ++++ wire/core/Functions.php | 18 ++++++++ wire/modules/PagePathHistory.module | 64 ++++++++++++++++++++++++----- 3 files changed, 79 insertions(+), 11 deletions(-) diff --git a/wire/core/Exceptions.php b/wire/core/Exceptions.php index 7891caaa..b01a9055 100644 --- a/wire/core/Exceptions.php +++ b/wire/core/Exceptions.php @@ -74,6 +74,14 @@ class Wire404Exception extends WireException { * */ const codeFile = 4044; + + /** + * 404 is a result of a front-end wire404() function call + * + * #pw-internal + * + */ + const codeFunction = 4045; /** * Anonymous 404 with no code provided diff --git a/wire/core/Functions.php b/wire/core/Functions.php index d3dbcd59..6e47570f 100644 --- a/wire/core/Functions.php +++ b/wire/core/Functions.php @@ -1210,6 +1210,24 @@ function wireRegion($key, $value = null) { return $result; } +/** + * Stop execution with a 404 unless redirect URL available (for front-end use) + * + * This is an alternative to using a manual `throw new Wire404Exception()` and is recognized by + * PW as a front-end 404 where PagePathHistory (or potentially other modules) are still allowed + * to change the behavior of the request from a 404 to something else (like a 301 redirect). + * + * #pw-group-common + * + * @param string $message Optional message to send to Exception message argument (not used in output by default) + * @throws Wire404Exception + * @since 3.0.147 + * + */ +function wire404($message = '') { + throw new Wire404Exception($message, Wire404Exception::codeFunction); +} + /** * Create new WireArray, add given $items to it, and return it * diff --git a/wire/modules/PagePathHistory.module b/wire/modules/PagePathHistory.module index 66e1eb27..fdd2bdd7 100644 --- a/wire/modules/PagePathHistory.module +++ b/wire/modules/PagePathHistory.module @@ -6,21 +6,21 @@ * Keeps track of past URLs where pages have lived and automatically 301 redirects * to the new location whenever the past URL is accessed. * - * - * ProcessWire 3.x, Copyright 2018 by Ryan Cramer + * ProcessWire 3.x, Copyright 2019 by Ryan Cramer * https://processwire.com * * @method upgrade($fromVersion, $toVersion) + * @property int $minimumAge * * */ -class PagePathHistory extends WireData implements Module { +class PagePathHistory extends WireData implements Module, ConfigurableModule { public static function getModuleInfo() { return array( 'title' => 'Page Path History', - 'version' => 4, + 'version' => 5, 'summary' => "Keeps track of past URLs where pages have lived and automatically redirects (301 permament) to the new location whenever the past URL is accessed.", 'singular' => true, 'autoload' => true, @@ -55,6 +55,15 @@ class PagePathHistory extends WireData implements Module { */ protected $version = 0; + /** + * Construct + * + */ + public function __construct() { + parent::__construct(); + $this->set('minimumAge', self::minimumAge); + } + /** * Initialize the hooks * @@ -484,12 +493,12 @@ class PagePathHistory extends WireData implements Module { /** @var Page $page */ $page = $event->arguments[0]; - if($page->template == 'admin') return; - if($this->wire('pages')->cloning) return; - $age = time() - $page->created; - if($age < self::minimumAge) return; + /** @var Languages $languages */ $languages = $this->getLanguages(); + $age = time() - $page->created; + if($page->template == 'admin' || $this->wire('pages')->cloning || $age < $this->minimumAge) return; + // note that the paths we store have no trailing slash if($languages) { @@ -546,14 +555,29 @@ class PagePathHistory extends WireData implements Module { */ public function hookPageNotFound(HookEvent $event) { - - $page = $event->arguments(0); + /** @var Page $page */ + $page = $event->arguments(0); + /** @var Wire404Exception $exception */ + $exception = $event->arguments(4); // If there is a page object set, then it means the 404 was triggered // by the user not having access to it, or by the $page's template // throwing a 404 exception. In either case, we don't want to do a // redirect if there is a $page since any 404 is intentional there. - if($page && $page->id) return; + if($page && $page->id) { + // it did resolve to a Page: maybe a front-end 404 + if(!$exception) { + // pageNotFound was called without an Exception + return; + } else if($exception->getCode() == Wire404Exception::codeFunction) { + // the wire404() function was called: allow PagePathHistory + } else if($exception->getMessage() === "1") { + // also allow PagePathHistory to operate when: throw new WireException(true); + } else { + // likely user didn't have access or intentional 404 that should not redirect + return; + } + } $languages = $this->getLanguages(); if($languages) { @@ -787,4 +811,22 @@ class PagePathHistory extends WireData implements Module { } } + /** + * Module config + * + * @param InputfieldWrapper $inputfields + * + */ + public function getModuleConfigInputfields(InputfieldWrapper $inputfields) { + /** @var InputfieldInteger $f */ + $f = $this->wire('modules')->get('InputfieldInteger'); + $f->attr('name', 'minimumAge'); + $f->label = $this->_('Minimum age (seconds)'); + $f->description = $this->_('Start recording history for a page this many seconds after it has been created. At least 2 or more seconds recommended.'); + $f->notes = sprintf($this->_('Default: %s'), self::minimumAge); + $f->val((int) $this->minimumAge); + $f->required = true; + $inputfields->add($f); + } + }