diff --git a/wire/core/Exceptions.php b/wire/core/Exceptions.php index f1d9a25f..7891caaa 100644 --- a/wire/core/Exceptions.php +++ b/wire/core/Exceptions.php @@ -29,7 +29,61 @@ class WirePermissionException extends WireException {} * Thrown when a requested page does not exist, or can be thrown manually to show the 404 page * */ -class Wire404Exception extends WireException {} +class Wire404Exception extends WireException { + + /** + * 404 is because core determined requested resource by URL does not physically exist + * + * #pw-internal + * + */ + const codeNonexist = 404; + + /** + * 404 is a result of a resource that might exist but there is there is no access + * + * Similar to a WirePermissionException except always still a 404 externally + * + * #pw-internal + * + */ + const codePermission = 4041; + + /** + * 404 is a result of a secondary non-file asset that does not exist, even if page does + * + * For example: /foo/bar/?id=123 where /foo/bar/ exists but 123 points to non-existent asset. + * + * #pw-internal + * + */ + const codeSecondary = 4042; + + /** + * 404 is a result of content not available in requested language + * + * #pw-internal + * + */ + const codeLanguage = 4043; + + /** + * 404 is a result of a physical file that does not exist on the file system + * + * #pw-internal + * + */ + const codeFile = 4044; + + /** + * Anonymous 404 with no code provided + * + * #pw-internal + * + */ + const codeAnonymous = 0; + +} /** * Thrown when ProcessWire is unable to connect to the database at boot diff --git a/wire/core/InputfieldWrapper.php b/wire/core/InputfieldWrapper.php index 72e5fd25..5ca8277a 100644 --- a/wire/core/InputfieldWrapper.php +++ b/wire/core/InputfieldWrapper.php @@ -452,6 +452,54 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre return $p[$c]; } + /** + * Prepare Inputfield for attributes used during rendering + * + * #pw-internal + * + * @param Inputfield $inputfield + * @param array $markup + * @since 3.0.144 + * + */ + private function attributeInputfield(Inputfield $inputfield, &$markup) { + + $inputfieldClass = $inputfield->className(); + $markupTemplate = array('attr' => array(), 'wrapAttr' => array()); + $classParents = $this->classParents($inputfield); + $classParents[] = $inputfieldClass; + $classKeys = array('class', 'wrapClass', 'headerClass', 'contentClass'); + $classes = array(); + $attr = array(); + $wrapAttr = array(); + + foreach($classParents as $classParent) { + if(!isset($markup[$classParent])) continue; + $markupParent = array_merge($markupTemplate, $markup[$classParent]); + foreach($classKeys as $classKey) { + if(!empty($markupParent[$classKey])) { + $classes[$classKey] = $markupParent[$classKey]; + } + } + foreach($markupParent['attr'] as $k => $v) { + $attr[$k] = $v; + } + foreach($markupParent['wrapAttr'] as $k => $v) { + $wrapAttr[$k] = $v; + } + } + + foreach($attr as $attrName => $attrVal) { + $inputfield->attr($attrName, $attrVal); + } + foreach($wrapAttr as $attrName => $attrVal) { + $inputfield->wrapAttr($attrName, $attrVal); + } + foreach($classes as $classKey => $class) { + $inputfield->addClass($class, $classKey); + } + } + /** * Render this Inputfield and the output of its children. * @@ -512,24 +560,7 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre if($collapsed == Inputfield::collapsedHidden) continue; if($collapsed == Inputfield::collapsedNoLocked || $collapsed == Inputfield::collapsedYesLocked) $renderValueMode = true; - // allow adding custom classes and/or attributes at runtime (since 3.0.143) - $classParents = $this->classParents($inputfield); - $classParents[] = $inputfieldClass; - foreach($classParents as $classParent) { - if(!isset($_markup[$classParent])) continue; - $markupParent = $_markup[$classParent]; - foreach(array('class', 'wrapClass', 'headerClass', 'contentClass') as $classKey) { - if(!empty($markupParent[$classKey])) $inputfield->addClass($markupParent[$classKey], $classKey); - } - foreach(array('attr', 'wrapAttr') as $attrKey) { - if(!empty($markupParent[$attrKey]) && is_array($markupParent[$attrKey])) { - foreach($markupParent[$attrKey] as $k => $v) { - $inputfield->$attrKey($k, $v); - } - } - } - } - + $this->attributeInputfield($inputfield, $_markup); $ffOut = $this->renderInputfield($inputfield, $renderValueMode); if(!strlen($ffOut)) continue; $collapsed = (int) $inputfield->getSetting('collapsed'); // retrieve again after render @@ -576,7 +607,7 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre if($appendMarkup) $ffOut .= $appendMarkup; } - // The inputfield's classname is always used in it's LI wrapper + // The inputfield classname is always used in its wrapping element $ffAttrs = array( 'class' => str_replace( array('{class}', '{name}'), @@ -621,116 +652,116 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre } } } + + // if inputfield produced no output, then move to next + if(!$ffOut) continue; - // if the inputfield resulted in output, wrap it in an LI - if($ffOut) { - $attrs = ''; - $label = $inputfield->getSetting('label'); - $skipLabel = $inputfield->getSetting('skipLabel'); - $skipLabel = is_bool($skipLabel) || empty($skipLabel) ? (bool) $skipLabel : (int) $skipLabel; // force as bool or int - if(!strlen($label) && $skipLabel !== Inputfield::skipLabelBlank && $inputfield->className() != 'InputfieldWrapper') { - $label = $inputfield->attr('name'); + // wrap the inputfield output + $attrs = ''; + $label = $inputfield->getSetting('label'); + $skipLabel = $inputfield->getSetting('skipLabel'); + $skipLabel = is_bool($skipLabel) || empty($skipLabel) ? (bool) $skipLabel : (int) $skipLabel; // force as bool or int + if(!strlen($label) && $skipLabel !== Inputfield::skipLabelBlank && $inputfield->className() != 'InputfieldWrapper') { + $label = $inputfield->attr('name'); + } + if(($label || $quietMode) && $skipLabel !== Inputfield::skipLabelMarkup) { + $for = $skipLabel || $quietMode ? '' : $inputfield->attr('id'); + // if $inputfield has a property of entityEncodeLabel with a value of boolean FALSE, we don't entity encode + $entityEncodeLabel = $inputfield->getSetting('entityEncodeLabel'); + if(is_int($entityEncodeLabel) && $entityEncodeLabel >= Inputfield::textFormatBasic) { + // uses an Inputfield::textFormat constant + $label = $inputfield->entityEncode($label, $entityEncodeLabel); + } else if($entityEncodeLabel !== false) { + $label = $inputfield->entityEncode($label); } - if(($label || $quietMode) && $skipLabel !== Inputfield::skipLabelMarkup) { - $for = $skipLabel || $quietMode ? '' : $inputfield->attr('id'); - // if $inputfield has a property of entityEncodeLabel with a value of boolean FALSE, we don't entity encode - $entityEncodeLabel = $inputfield->getSetting('entityEncodeLabel'); - if(is_int($entityEncodeLabel) && $entityEncodeLabel >= Inputfield::textFormatBasic) { - // uses an Inputfield::textFormat constant - $label = $inputfield->entityEncode($label, $entityEncodeLabel); - } else if($entityEncodeLabel !== false) { - $label = $inputfield->entityEncode($label); - } - $icon = $inputfield->getSetting('icon'); - $icon = $icon ? str_replace('{name}', $this->wire('sanitizer')->name(str_replace(array('icon-', 'fa-'), '', $icon)), $markup['item_icon']) : ''; - $toggle = $collapsed == Inputfield::collapsedNever ? '' : $markup['item_toggle']; - if($toggle && strpos($toggle, 'title=') === false) { - $toggle = str_replace("class=", "title='" . $this->_('Toggle open/close') . "' class=", $toggle); - } - if($skipLabel === Inputfield::skipLabelHeader || $quietMode) { - // label only shows when field is collapsed - $label = str_replace('{out}', $icon . $label . $toggle, $markup['item_label_hidden']); - } else { - // label always visible - $label = str_replace(array('{for}', '{out}'), array($for, $icon . $label . $toggle), $markup['item_label']); - } - $headerClass = trim($inputfield->getSetting('headerClass') . " $classes[item_label]"); - if($headerClass) { - if(strpos($label, '{class}') !== false) { - $label = str_replace('{class}', ' ' . $headerClass, $label); - } else { - $label = preg_replace('/( class=[\'"][^\'"]+)/', '$1 ' . $headerClass, $label, 1); - } - } else if(strpos($label, '{class}') !== false) { - $label = str_replace('{class}', '', $label); - } - } else if($skipLabel === Inputfield::skipLabelMarkup) { - // no header and no markup for header - $label = ''; + $icon = $inputfield->getSetting('icon'); + $icon = $icon ? str_replace('{name}', $this->wire('sanitizer')->name(str_replace(array('icon-', 'fa-'), '', $icon)), $markup['item_icon']) : ''; + $toggle = $collapsed == Inputfield::collapsedNever ? '' : $markup['item_toggle']; + if($toggle && strpos($toggle, 'title=') === false) { + $toggle = str_replace("class=", "title='" . $this->_('Toggle open/close') . "' class=", $toggle); + } + if($skipLabel === Inputfield::skipLabelHeader || $quietMode) { + // label only shows when field is collapsed + $label = str_replace('{out}', $icon . $label . $toggle, $markup['item_label_hidden']); } else { - // no header - // $inputfield->addClass('InputfieldNoHeader', 'wrapClass'); + // label always visible + $label = str_replace(array('{for}', '{out}'), array($for, $icon . $label . $toggle), $markup['item_label']); } - - $columnWidth = (int) $inputfield->getSetting('columnWidth'); - $columnWidthAdjusted = $columnWidth; - if($columnWidthSpacing) { - $columnWidthAdjusted = $columnWidth + ($columnWidthTotal ? -1 * $columnWidthSpacing : 0); - } - if($columnWidth >= 9 && $columnWidth <= 100) { - $ffAttrs['class'] .= ' ' . $classes['item_column_width']; - if(!$columnWidthTotal) { - $ffAttrs['class'] .= ' ' . $classes['item_column_width_first']; - } - $columnWidthTotal += $columnWidth; - if(!$useColumnWidth || $useColumnWidth > 1) { - if($columnWidthTotal >= 95 && $columnWidthTotal < 100) { - $columnWidthAdjusted += (100 - $columnWidthTotal); - $columnWidthTotal = 100; - } - $ffAttrs['data-colwidth'] = "$columnWidthAdjusted%"; - } - if($useColumnWidth) { - $ffAttrs['style'] = "width: $columnWidthAdjusted%;"; - } - //if($columnWidthTotal >= 100 && !$requiredIf) $columnWidthTotal = 0; // requiredIf meant to be a showIf? - if($columnWidthTotal >= 100) $columnWidthTotal = 0; - } else { - $columnWidthTotal = 0; - } - if(!isset($ffAttrs['id'])) $ffAttrs['id'] = 'wrap_' . $inputfield->attr('id'); - $ffAttrs['class'] = str_replace('Inputfield_ ', '', $ffAttrs['class']); - $wrapClass = $inputfield->getSetting('wrapClass'); - if($wrapClass) $ffAttrs['class'] .= " " . $wrapClass; - foreach($inputfield->wrapAttr() as $k => $v) { - if(!empty($ffAttrs[$k])) { - $ffAttrs[$k] .= " $v"; + $headerClass = trim($inputfield->getSetting('headerClass') . " $classes[item_label]"); + if($headerClass) { + if(strpos($label, '{class}') !== false) { + $label = str_replace('{class}', ' ' . $headerClass, $label); } else { - $ffAttrs[$k] = $v; + $label = preg_replace('/( class=[\'"][^\'"]+)/', '$1 ' . $headerClass, $label, 1); } + } else if(strpos($label, '{class}') !== false) { + $label = str_replace('{class}', '', $label); } - foreach($ffAttrs as $k => $v) { - $k = $this->entityEncode($k); - $v = $this->entityEncode(trim($v)); - $attrs .= " $k='$v'"; - } - $markupItemContent = $markup['item_content']; - $contentClass = trim($inputfield->getSetting('contentClass') . " $classes[item_content]"); - if($contentClass) { - if(strpos($markupItemContent, '{class}') !== false) { - $markupItemContent = str_replace('{class}', ' ' . $contentClass, $markupItemContent); - } else { - $markupItemContent = preg_replace('/( class=[\'"][^\'"]+)/', '$1 ' . $contentClass, $markupItemContent, 1); - } - } else if(strpos($markupItemContent, '{class}') !== false) { - $markupItemContent = str_replace('{class}', '', $markupItemContent); - } - if($inputfield->className() != 'InputfieldWrapper') $ffOut = str_replace('{out}', $ffOut, $markupItemContent); - $out .= str_replace(array('{attrs}', '{out}'), array(trim($attrs), $label . $ffOut), $markup['item']); - $lastInputfield = $inputfield; - } // if($ffOut) + } else if($skipLabel === Inputfield::skipLabelMarkup) { + // no header and no markup for header + $label = ''; + } else { + // no header + // $inputfield->addClass('InputfieldNoHeader', 'wrapClass'); + } - } + $columnWidth = (int) $inputfield->getSetting('columnWidth'); + $columnWidthAdjusted = $columnWidth; + if($columnWidthSpacing) { + $columnWidthAdjusted = $columnWidth + ($columnWidthTotal ? -1 * $columnWidthSpacing : 0); + } + if($columnWidth >= 9 && $columnWidth <= 100) { + $ffAttrs['class'] .= ' ' . $classes['item_column_width']; + if(!$columnWidthTotal) { + $ffAttrs['class'] .= ' ' . $classes['item_column_width_first']; + } + $columnWidthTotal += $columnWidth; + if(!$useColumnWidth || $useColumnWidth > 1) { + if($columnWidthTotal >= 95 && $columnWidthTotal < 100) { + $columnWidthAdjusted += (100 - $columnWidthTotal); + $columnWidthTotal = 100; + } + $ffAttrs['data-colwidth'] = "$columnWidthAdjusted%"; + } + if($useColumnWidth) { + $ffAttrs['style'] = "width: $columnWidthAdjusted%;"; + } + //if($columnWidthTotal >= 100 && !$requiredIf) $columnWidthTotal = 0; // requiredIf meant to be a showIf? + if($columnWidthTotal >= 100) $columnWidthTotal = 0; + } else { + $columnWidthTotal = 0; + } + if(!isset($ffAttrs['id'])) $ffAttrs['id'] = 'wrap_' . $inputfield->attr('id'); + $ffAttrs['class'] = str_replace('Inputfield_ ', '', $ffAttrs['class']); + $wrapClass = $inputfield->getSetting('wrapClass'); + if($wrapClass) $ffAttrs['class'] .= " " . $wrapClass; + foreach($inputfield->wrapAttr() as $k => $v) { + if(!empty($ffAttrs[$k])) { + $ffAttrs[$k] .= " $v"; + } else { + $ffAttrs[$k] = $v; + } + } + foreach($ffAttrs as $k => $v) { + $k = $this->entityEncode($k); + $v = $this->entityEncode(trim($v)); + $attrs .= " $k='$v'"; + } + $markupItemContent = $markup['item_content']; + $contentClass = trim($inputfield->getSetting('contentClass') . " $classes[item_content]"); + if($contentClass) { + if(strpos($markupItemContent, '{class}') !== false) { + $markupItemContent = str_replace('{class}', ' ' . $contentClass, $markupItemContent); + } else { + $markupItemContent = preg_replace('/( class=[\'"][^\'"]+)/', '$1 ' . $contentClass, $markupItemContent, 1); + } + } else if(strpos($markupItemContent, '{class}') !== false) { + $markupItemContent = str_replace('{class}', '', $markupItemContent); + } + if($inputfield->className() != 'InputfieldWrapper') $ffOut = str_replace('{out}', $ffOut, $markupItemContent); + $out .= str_replace(array('{attrs}', '{out}'), array(trim($attrs), $label . $ffOut), $markup['item']); + $lastInputfield = $inputfield; + } // foreach($children as $inputfield) if($out) { $ulClass = $classes['list']; diff --git a/wire/core/Password.php b/wire/core/Password.php index 70fba285..5d999d0f 100644 --- a/wire/core/Password.php +++ b/wire/core/Password.php @@ -5,10 +5,13 @@ * Class to hold combined password/salt info. Uses Blowfish when possible. * Specially used by FieldtypePassword. * - * ProcessWire 3.x, Copyright 2018 by Ryan Cramer + * ProcessWire 3.x, Copyright 2019 by Ryan Cramer * https://processwire.com * - * @method setPass($value) + * @method setPass($value) Protected internal use method + * @property string $salt + * @property string $hash + * @property-write string $pass * */ diff --git a/wire/core/Process.php b/wire/core/Process.php index 074da9d7..997be55a 100644 --- a/wire/core/Process.php +++ b/wire/core/Process.php @@ -108,7 +108,11 @@ abstract class Process extends WireData implements Module { * */ private $_viewVars = array(); - + + /** + * Construct + * + */ public function __construct() { } /** @@ -324,6 +328,9 @@ abstract class Process extends WireData implements Module { */ public function ___upgrade($fromVersion, $toVersion) { // any code needed to upgrade between versions + if($fromVersion && $toVersion && false === true) { + throw new WireException('Uncallable exception for phpdoc'); + } } /** @@ -352,13 +359,18 @@ abstract class Process extends WireData implements Module { $name = $this->wire('sanitizer')->pageName($name); if(!strlen($name)) $name = strtolower(preg_replace('/([A-Z])/', '-$1', str_replace('Process', '', $this->className()))); $adminPage = $this->wire('pages')->get($this->wire('config')->adminRootPageID); - if($parent instanceof Page) $parent = $parent; // nice - else if(ctype_digit("$parent")) $parent = $this->wire('pages')->get((int) $parent); - else if(strpos($parent, '/') !== false) $parent = $this->wire('pages')->get($parent); - else if($parent) $parent = $adminPage->child("include=all, name=" . $this->wire('sanitizer')->pageName($parent)); + if($parent instanceof Page) { + // already have what we need + } else if(ctype_digit("$parent")) { + $parent = $this->wire('pages')->get((int) $parent); + } else if(strpos($parent, '/') !== false) { + $parent = $this->wire('pages')->get($parent); + } else if($parent) { + $parent = $adminPage->child("include=all, name=" . $this->wire('sanitizer')->pageName($parent)); + } if(!$parent || !$parent->id) $parent = $adminPage; // default $page = $parent->child("include=all, name=$name"); // does it already exist? - if($page->id && $page->process == $this) return $page; // return existing copy + if($page->id && "$page->process" == "$this") return $page; // return existing copy $page = $this->wire('pages')->newPage(); $page->template = $template ? $template : 'admin'; $page->name = $name; @@ -389,7 +401,7 @@ abstract class Process extends WireData implements Module { if(!$moduleID) return 0; $n = 0; foreach($this->wire('pages')->find("process=$moduleID, include=all") as $page) { - if($page->process != $this) continue; + if("$page->process" != "$this") continue; $page->process = null; $this->message(sprintf($this->_('Trashed Page: %s'), $page->path)); $this->wire('pages')->trash($page); @@ -408,7 +420,7 @@ abstract class Process extends WireData implements Module { * #pw-internal @todo work on documenting this method further * * @param array $options For descending classes to modify behavior (see $defaults in method) - * @return string rendered JSON string + * @return string|array rendered JSON string or array if `getArray` option is true. * @throws Wire404Exception if getModuleInfo() doesn't specify useNavJSON=true; * */ @@ -431,7 +443,9 @@ abstract class Process extends WireData implements Module { $options = array_merge($defaults, $options); $moduleInfo = $this->modules->getModuleInfo($this); - if(empty($moduleInfo['useNavJSON'])) throw new Wire404Exception(); + if(empty($moduleInfo['useNavJSON'])) { + throw new Wire404Exception('No JSON nav available', Wire404Exception::codeSecondary); + } $page = $this->wire('page'); $data = array( diff --git a/wire/core/Wire.php b/wire/core/Wire.php index 561a3d1b..6469a6cb 100644 --- a/wire/core/Wire.php +++ b/wire/core/Wire.php @@ -79,10 +79,10 @@ * @method WireFileTools files() Access the $files API variable as a function. #pw-group-api-helpers * @method WireCache|string|array|PageArray|null cache($name = '', $expire = null, $func = null) Access the $cache API variable as a function. #pw-group-api-helpers * @method Languages|Language|NullPage|null languages($name = '') Access the $languages API variable as a function. #pw-group-api-helpers - * @method WireInput|WireInputData|array|string|int|null input($type = '', $key = '', $sanitizer = '') Access the $input API variable as a function. #pw-group-api-helpers + * @method WireInput|WireInputData|WireInputDataCookie|array|string|int|null input($type = '', $key = '', $sanitizer = '') Access the $input API variable as a function. #pw-group-api-helpers * @method WireInputData|string|int|array|null inputGet($key = '', $sanitizer = '') Access the $input->get() API variable as a function. #pw-group-api-helpers * @method WireInputData|string|int|array|null inputPost($key = '', $sanitizer = '') Access the $input->post() API variable as a function. #pw-group-api-helpers - * @method WireInputData|string|int|array|null inputCookie($key = '', $sanitizer = '') Access the $input->cookie() API variable as a function. #pw-group-api-helpers + * @method WireInputDataCookie|string|int|array|null inputCookie($key = '', $sanitizer = '') Access the $input->cookie() API variable as a function. #pw-group-api-helpers * */ @@ -437,7 +437,15 @@ abstract class Wire implements WireTranslatable, WireFuelable, WireTrackable { * */ public function __call($method, $arguments) { - $hooks = $this->wire('hooks'); + if(empty($arguments) && Fuel::isCommon($method)) { + // faster version of _callWireAPI for when conditions allow + if($this->_wire && !method_exists($this, "___$method")) { + // get a common API var with no arguments as method call more quickly + $val = $this->_wire->fuel($method); + if($val !== null) return $val; + } + } + $hooks = $this->wire('hooks'); /** @var WireHooks $hooks */ if($hooks) { $result = $hooks->runHooks($this, $method, $arguments); if(!$result['methodExists'] && !$result['numHooksRun']) { @@ -465,15 +473,17 @@ abstract class Wire implements WireTranslatable, WireFuelable, WireTrackable { if(!$var) return false; // requested method maps to an API variable $result = array('return' => null); - $funcName = 'wire' . ucfirst($method); - if(__NAMESPACE__) $funcName = __NAMESPACE__ . "\\$funcName"; - if(count($arguments) && function_exists($funcName)) { - // a function exists with this API var name - $wire = ProcessWire::getCurrentInstance(); - // ensure function call maps to this PW instance - if($wire !== $this->_wire) ProcessWire::setCurrentInstance($this->_wire); - $result['return'] = call_user_func_array($funcName, $arguments); - if($wire !== $this->_wire) ProcessWire::setCurrentInstance($wire); + if(count($arguments)) { + $funcName = 'wire' . ucfirst($method); + if(__NAMESPACE__) $funcName = __NAMESPACE__ . "\\$funcName"; + if(function_exists($funcName)) { + // a function exists with this API var name + $wire = ProcessWire::getCurrentInstance(); + // ensure function call maps to this PW instance + if($wire !== $this->_wire) ProcessWire::setCurrentInstance($this->_wire); + $result['return'] = call_user_func_array($funcName, $arguments); + if($wire !== $this->_wire) ProcessWire::setCurrentInstance($wire); + } } else { // if no arguments provided, just return API var $result['return'] = $var; diff --git a/wire/core/WireInput.php b/wire/core/WireInput.php index c4b50ae3..06413b7e 100644 --- a/wire/core/WireInput.php +++ b/wire/core/WireInput.php @@ -867,6 +867,27 @@ class WireInput extends Wire { if($method) return strtoupper($method) === $requestMethod; return $requestMethod; } + + /** + * Is the current request of the specified type? + * + * This is a more readable/shorter alias of `$input->requestMethod('type')` for syntax convenience. + * Internally, it determines the request type without accessing any input data, so it is efficient. + * + * ~~~~~ + * // The following are equivalent: + * $isPost = $input->is('post'); + * $isPost = $input->requestMethod('post'); + * ~~~~~ + * + * @param string $method Specify one of: post, get, head, put, delete, options, patch (not case sensitive) + * @return bool + * @since 3.0.145 + * + */ + public function is($method) { + return empty($method) ? false : $this->requestMethod($method); + } /** * Provides the implementation for get/post/cookie method validation and fallback features diff --git a/wire/modules/Fieldtype/FieldtypePassword.module b/wire/modules/Fieldtype/FieldtypePassword.module index 001b6b4b..4146d315 100644 --- a/wire/modules/Fieldtype/FieldtypePassword.module +++ b/wire/modules/Fieldtype/FieldtypePassword.module @@ -102,8 +102,8 @@ class FieldtypePassword extends Fieldtype { * * @param Page $page * @param Field $field - * @param string|int|array $value - * @return string|int|array|object $value + * @param array $value + * @return Password * */ public function ___wakeupValue(Page $page, Field $field, $value) { diff --git a/wire/modules/LanguageSupport/LanguageSupportPageNames.module b/wire/modules/LanguageSupport/LanguageSupportPageNames.module index 01fca6f4..4a6003ac 100644 --- a/wire/modules/LanguageSupport/LanguageSupportPageNames.module +++ b/wire/modules/LanguageSupport/LanguageSupportPageNames.module @@ -415,7 +415,7 @@ class LanguageSupportPageNames extends WireData implements Module, ConfigurableM if($event) {} if($this->force404) { $this->force404 = false; // prevent another 404 on the 404 page - throw new Wire404Exception(); + throw new Wire404Exception('Not available in requested language', Wire404Exception::codeLanguage); // $page = $event->wire('page'); // if(!$page || ($page->id != $event->wire('config')->http404PageID)) { // throw new Wire404Exception(); diff --git a/wire/modules/Process/ProcessPageAdd/ProcessPageAdd.module b/wire/modules/Process/ProcessPageAdd/ProcessPageAdd.module index 3ded9a24..c25d25a6 100644 --- a/wire/modules/Process/ProcessPageAdd/ProcessPageAdd.module +++ b/wire/modules/Process/ProcessPageAdd/ProcessPageAdd.module @@ -487,8 +487,12 @@ class ProcessPageAdd extends Process implements ConfigurableModule, WirePageEdit if(!$this->parent_id) return $this->renderChooseTemplate(); $this->parent = $this->pages->get((int) $this->parent_id); - if(!$this->parent->id) throw new Wire404Exception("Unable to load parent page $this->parent_id"); - if(!$this->isAllowedParent($this->parent, true, $this->template)) throw new WireException($this->errors('string')); + if(!$this->parent->id) { + throw new Wire404Exception("Unable to load parent page $this->parent_id", Wire404Exception::codeSecondary); + } + if(!$this->isAllowedParent($this->parent, true, $this->template)) { + throw new WireException($this->errors('string')); + } if(count($this->parent->template->childTemplates) == 1) { // only one type of template is allowed for the parent diff --git a/wire/modules/Process/ProcessPageEdit/ProcessPageEdit.module b/wire/modules/Process/ProcessPageEdit/ProcessPageEdit.module index ad578261..8bc6b79a 100644 --- a/wire/modules/Process/ProcessPageEdit/ProcessPageEdit.module +++ b/wire/modules/Process/ProcessPageEdit/ProcessPageEdit.module @@ -390,7 +390,7 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod if(!$id) { $this->session->redirect('./bookmarks/'); - throw new Wire404Exception($this->noticeUnknown); // Init error: no page provided + throw new Wire404Exception($this->noticeUnknown, Wire404Exception::codeSecondary); // Init error: no page provided } $this->page = $this->loadPage($id); diff --git a/wire/modules/Process/ProcessPageList/ProcessPageList.module b/wire/modules/Process/ProcessPageList/ProcessPageList.module index 90af73a5..67b79e97 100644 --- a/wire/modules/Process/ProcessPageList/ProcessPageList.module +++ b/wire/modules/Process/ProcessPageList/ProcessPageList.module @@ -186,7 +186,7 @@ class ProcessPageList extends Process implements ConfigurableModule { if($this->openPage->id && $this->speed > 50) $this->speed = floor($this->speed / 2); $this->page = $pages->get("id=" . ($this->id > 0 ? $this->id : 1) . ", status<" . Page::statusMax); - if(!$this->page) throw new Wire404Exception("Unable to load page {$this->id}"); + if(!$this->page) throw new Wire404Exception("Unable to load page {$this->id}", Wire404Exception::codeSecondary); if(!$this->page->listable()) throw new WirePermissionException("You don't have access to list page {$this->page->url}"); $this->page->setOutputFormatting(false); diff --git a/wire/modules/Process/ProcessPageLister/ProcessPageLister.module b/wire/modules/Process/ProcessPageLister/ProcessPageLister.module index 87feae39..378e98e7 100644 --- a/wire/modules/Process/ProcessPageLister/ProcessPageLister.module +++ b/wire/modules/Process/ProcessPageLister/ProcessPageLister.module @@ -2216,7 +2216,9 @@ class ProcessPageLister extends Process implements ConfigurableModule { } else { $bookmarkID = ''; } - if(!$bookmarkID || !$this->checkBookmark($bookmarkID)) throw new Wire404Exception(); + if(!$bookmarkID || !$this->checkBookmark($bookmarkID)) { + throw new Wire404Exception('Unknown Lister action', Wire404Exception::codeNonexist); + } return $this->execute(); } diff --git a/wire/modules/Process/ProcessPageView.module b/wire/modules/Process/ProcessPageView.module index 10b23fc4..247ddf82 100644 --- a/wire/modules/Process/ProcessPageView.module +++ b/wire/modules/Process/ProcessPageView.module @@ -162,7 +162,9 @@ class ProcessPageView extends Process { $_page = $page; $page = $this->checkAccess($page); if(!$page || $_page->id == $config->http404PageID) { - return $this->pageNotFound($_page, $this->requestURL, true, 'access not allowed'); + $s = 'access not allowed'; + $e = new Wire404Exception($s, Wire404Exception::codePermission); + return $this->pageNotFound($_page, $this->requestURL, true, $s, $e); } if(!$this->delayRedirects) { @@ -851,10 +853,10 @@ class ProcessPageView extends Process { // use the static hasPath first to make sure this page actually has a files directory // this ensures one isn't automatically created when we call $page->filesManager->path below - if(!PagefilesManager::hasPath($page)) throw new Wire404Exception($err); + if(!PagefilesManager::hasPath($page)) throw new Wire404Exception($err, Wire404Exception::codeFile); $filename = $page->filesManager->path() . $basename; - if(!is_file($filename)) throw new Wire404Exception($err); + if(!is_file($filename)) throw new Wire404Exception($err, Wire404Exception::codeFile); if($page->isPublic()) { // deprecated, only necessary for method 2 in checkRequestFile @@ -881,7 +883,10 @@ class ProcessPageView extends Process { */ protected function ___pageNotFound($page, $url, $triggerReady = false, $reason = '', $exception = null) { - if(!$exception) $exception = new Wire404Exception($reason); // create but do not throw + if(!$exception) { + // create exception but do not throw + $exception = new Wire404Exception($reason, Wire404Exception::codeNonexist); + } $this->failed($exception, $reason, $page, $url); $this->responseType = self::responseTypeError;