Changeset 3434593
- Timestamp:
- 01/07/2026 06:06:33 PM (3 months ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
admin-menu-editor/trunk/modules/redirector/redirector.php
r3432118 r3434593 12 12 use WP_Error; 13 13 use WP_User; 14 use YahnisElsts\AdminMenuEditor\Customizable\Schemas\SchemaFactory; 15 use YahnisElsts\AdminMenuEditor\Options\None; 14 16 use YahnisElsts\AdminMenuEditor\Options\Option; 15 17 use YahnisElsts\AdminMenuEditor\Options\Some; 16 use YahnisElsts\AdminMenuEditor\Options\None;17 18 use YahnisElsts\AdminMenuEditor\Utils\Forms\KnockoutSaveForm; 18 19 use YahnisElsts\AjaxActionWrapper\v2\Action; 20 use function YahnisElsts\AdminMenuEditor\Collections\w; 19 21 20 22 class Module extends amePersistentModule { … … 394 396 $submittedSettings = $submission->getSettings(); 395 397 396 $validationResult = $this->validateSubmittedSettings($submittedSettings); 397 if ( is_wp_error($validationResult) ) { 398 //It seems that wp_die() doesn't automatically escape special characters, so let's do that. 399 wp_die(esc_html($validationResult->get_error_message())); 400 } 401 402 $newRedirects = new RedirectCollection(); 403 foreach ($submittedSettings['redirects'] as $redirect) { 404 $newRedirects->add($redirect); 405 } 406 $this->redirects = $newRedirects; 398 $this->redirects = w($submittedSettings['redirects'])->foldLeft( 399 new RedirectCollection(), 400 fn($collection, $redirectData) => $collection->add($redirectData) 401 ); 407 402 $this->saveSettings(); 408 403 … … 418 413 419 414 /** 420 * @param $settings421 * @return bool|WP_Error422 */423 protected function validateSubmittedSettings($settings) {424 if ( !is_array($settings) ) {425 return new WP_Error(426 'ame_invalid_json',427 sprintf('Invalid JSON data. Expected an associative array, got %s.', gettype($settings))428 );429 }430 431 if ( !array_key_exists('redirects', $settings) ) {432 return new WP_Error('rui_missing_redirects_key', 'The required "redirects" field is missing.');433 }434 435 $allowedProperties = [436 //Actor IDs always follow the "prefix:value" format.437 'actorId' => /** @lang RegExp */438 '@^[a-z]{1,15}+:[^\s].{0,300}+$@i',439 //The URL can be basically anything, so we don't try to validate it.440 'urlTemplate' => null,441 //Menu template IDs are based on menu URLs, so they're pretty unpredictable. If one is given, it must be non-empty.442 'menuTemplateId' => /** @lang RegExp */443 '@^.@',444 //A trigger is always a lowercase string. We could just list the supported values once dev. is done.445 'trigger' => /** @lang RegExp */446 '@^[a-z\-]{2,20}+$@i',447 //The shortcode flag is a boolean value. No regex for that.448 'shortcodesEnabled' => null,449 ];450 $requiredProperties = [451 'actorId' => true,452 'urlTemplate' => true,453 'shortcodesEnabled' => true,454 'trigger' => true,455 ];456 457 foreach ($settings['redirects'] as $key => $redirect) {458 if ( !is_array($redirect) ) {459 return new WP_Error(460 'rui_bad_redirect_data_type',461 sprintf('Redirect %s should be an array but it is actually %s', $key, gettype($redirect))462 );463 }464 465 //Verify that it has all the required properties.466 $missingProperties = array_diff_key($requiredProperties, $redirect);467 if ( !empty($missingProperties) ) {468 $firstMissingProp = reset($missingProperties);469 return new WP_Error(470 'rui_missing_key',471 sprintf('Redirect %s is missing the required property "%s"', $key, $firstMissingProp)472 );473 }474 475 //Verify that the redirect has only allowed properties.476 $badProperties = array_diff_key($redirect, $allowedProperties);477 if ( !empty($badProperties) ) {478 $firstBadProp = reset($badProperties);479 return new WP_Error(480 'rui_bad_key',481 sprintf('Redirect %s has an unsupported property "%s"', $key, $firstBadProp)482 );483 }484 485 //String properties must match their validation regex (if any).486 foreach ($allowedProperties as $property => $regex) {487 if (488 is_string($regex) && isset($redirect[$property])489 && (!is_string($redirect[$property]) || !preg_match($regex, $redirect[$property]))490 ) {491 return new WP_Error(492 'rui_invalid_property_value',493 sprintf('Redirect %s: Property "%s" has an invalid value.', $key, $property)494 );495 }496 }497 498 //shortcodesEnabled must be a boolean.499 if ( array_key_exists('shortcodesEnabled', $redirect) && !is_bool($redirect['shortcodesEnabled']) ) {500 return new WP_Error(501 'rui_invalid_property_value',502 sprintf(503 'Redirect %s: The "shortcodesEnabled" property is invalid.'504 . ' Expected a boolean, but actual type is "%s".',505 $key,506 gettype($redirect['shortcodesEnabled'])507 )508 );509 }510 511 //URL template must be a string.512 if ( !is_string($redirect['urlTemplate']) ) {513 return new WP_Error(514 'rui_invalid_property_value',515 sprintf(516 'Redirect %s: The "urlTemplate" property is invalid.'517 . ' Expected a string, but actual type is "%s".',518 $key,519 gettype($redirect['urlTemplate'])520 )521 );522 }523 524 //URL template must be non-empty.525 if ( trim($redirect['urlTemplate']) === '' ) {526 return new WP_Error(527 'rui_empty_url',528 sprintf('Redirect %s: The "urlTemplate" property is empty.', $key)529 );530 }531 }532 533 return true;534 }535 536 /**537 415 * @var KnockoutSaveForm|null 538 416 */ 539 private $saveForm = null;417 private ?KnockoutSaveForm $saveForm = null; 540 418 541 419 private function getSaveSettingsForm(): KnockoutSaveForm { 542 420 if ( $this->saveForm === null ) { 421 $s = new SchemaFactory(); 422 $submittedSettingsSchema = $s->struct([ 423 'redirects' => $s->arr( 424 $s->struct([ 425 //Actor IDs always follow the "prefix:value" format. 426 'actorId' => $s->actorId(), 427 //The URL can be basically anything, so we don't try to validate it. 428 'urlTemplate' => $s->string()->max(2000), 429 //Menu template IDs are based on menu URLs, so they're pretty unpredictable. 430 //If one is given, it must be non-empty. 431 'menuTemplateId' => $s->string()->min(1), 432 //A trigger is always a lowercase string. 433 'trigger' => $s->string()->min(2)->max(20)->regex('@^[a-z\-]{2,20}+$@i'), 434 'shortcodesEnabled' => $s->boolean(), 435 ])->required([ 436 'actorId', 437 'urlTemplate', 438 'trigger', 439 'shortcodesEnabled', 440 ]) 441 ), 442 ])->required(); 443 543 444 $this->saveForm = KnockoutSaveForm::builderFor($this) 445 ->settingsFieldSchema($submittedSettingsSchema) 544 446 ->passThroughParams(['selectedTrigger']) 545 447 ->build(); … … 862 764 * 863 765 * @param array $redirectProperties 864 */ 865 public function add($redirectProperties) { 766 * @return $this 767 */ 768 public function add(array $redirectProperties): self { 866 769 $trigger = $redirectProperties['trigger']; 867 770 if ( !isset($this->rawItems[$trigger]) ) { … … 869 772 } 870 773 $this->rawItems[$trigger][] = $redirectProperties; 774 return $this; 871 775 } 872 776 }
Note: See TracChangeset
for help on using the changeset viewer.