| @@ -140,9 +140,9 @@ | ||
| 140 | 140 | wp_register_ability( |
| 141 | 141 | 'woody/upsert-snippet', |
| 142 | 142 | [ |
| 143 | 143 | 'label' => __( 'Create or update snippet', 'insert-php' ), |
| 144 | - 'description' => __( 'Creates a snippet (always inactive) or updates the code and execution settings of an existing one. The code is never run: new or changed PHP and universal code is only checked for syntax errors and, unless the snippet is active, for functions and classes whose names are already in use; nothing is saved when the check fails. With dry_run=true only the check runs and nothing is saved. Changing the code or scope of an active PHP or universal snippet saves it inactive, because its new code has not run yet; after other updates an active snippet stays active only when the "activate by default" setting is on. This ability never activates a snippet; use woody/set-snippet-state, which runs the code once to check for errors. CSS and JS snippet code cannot be written through this ability.', 'insert-php' ), | |
| 144 | + 'description' => __( 'Creates a snippet (always inactive) or updates the code and execution settings of an existing one. PHP and universal snippets that run everywhere or are inserted automatically are validated first by evaluating the code once, exactly as the snippet editor does; nothing is saved when validation fails. With dry_run=true only the validation runs and nothing is saved. After an update an active snippet stays active only when the "activate by default" setting is on; this ability never activates an inactive snippet, use woody/set-snippet-state for that. CSS and JS snippet code cannot be written through this ability.', 'insert-php' ), | |
| 145 | 145 | 'category' => self::CATEGORY, |
| 146 | 146 | 'input_schema' => $this->get_upsert_input_schema(), |
| 147 | 147 | 'output_schema' => [ |
| 148 | 148 | 'type' => 'object', |
| @@ -154,14 +154,10 @@ | ||
| 154 | 154 | ], |
| 155 | 155 | 'valid' => [ 'type' => 'boolean' ], |
| 156 | 156 | 'validated' => [ |
| 157 | 157 | 'type' => 'boolean', |
| 158 | - 'description' => __( 'Whether the code was checked for syntax errors; it is never run. False when the code and scope did not change, for snippet types that do not run PHP, and when the PHP tokenizer extension is missing.', 'insert-php' ), | |
| 158 | + 'description' => __( 'Whether the code was evaluated. False for snippet types or scopes the editor does not validate.', 'insert-php' ), | |
| 159 | 159 | ], |
| 160 | - 'deactivated' => [ | |
| 161 | - 'type' => 'boolean', | |
| 162 | - 'description' => __( 'Whether saving left (or, with dry_run, would leave) a previously active snippet inactive.', 'insert-php' ), | |
| 163 | - ], | |
| 164 | 160 | 'message' => [ 'type' => 'string' ], |
| 165 | 161 | 'revision_id' => [ 'type' => 'integer' ], |
| 166 | 162 | 'snippet' => $this->get_snippet_schema( true ), |
| 167 | 163 | ], |
| @@ -183,9 +179,9 @@ | ||
| 183 | 179 | wp_register_ability( |
| 184 | 180 | 'woody/set-snippet-state', |
| 185 | 181 | [ |
| 186 | 182 | 'label' => __( 'Activate or deactivate snippet', 'insert-php' ), |
| 187 | - 'description' => __( 'Activates or deactivates one snippet. Activating a PHP or universal snippet that runs everywhere or is inserted automatically first runs its code once with output discarded, and is refused when the code raises an error or a warning or returns false. Activating such an HTML snippet runs the check of the activation switch in the snippets list.', 'insert-php' ), | |
| 183 | + 'description' => __( 'Activates or deactivates one snippet. Activating a PHP, universal or HTML snippet that runs everywhere or is inserted automatically first executes it once to check for errors, exactly as the activation switch in the snippets list does, and is refused when the check fails.', 'insert-php' ), | |
| 188 | 184 | 'category' => self::CATEGORY, |
| 189 | 185 | 'input_schema' => [ |
| 190 | 186 | 'type' => 'object', |
| 191 | 187 | 'properties' => [ |
| @@ -436,40 +432,43 @@ | ||
| 436 | 432 | if ( is_wp_error( $data ) ) { |
| 437 | 433 | return $data; |
| 438 | 434 | } |
| 439 | 435 | |
| 440 | - $was_active = $post ? $this->is_active( $post->ID ) : false; | |
| 441 | - $is_executable = in_array( $data['type'], [ WINP_SNIPPET_TYPE_PHP, WINP_SNIPPET_TYPE_UNIVERSAL ], true ); | |
| 442 | - $validation = [ | |
| 436 | + $was_active = $post ? $this->is_active( $post->ID ) : false; | |
| 437 | + | |
| 438 | + // Same rule the editor uses to decide whether the code has to be evaluated before saving. | |
| 439 | + $is_executable = in_array( $data['type'], [ WINP_SNIPPET_TYPE_PHP, WINP_SNIPPET_TYPE_UNIVERSAL ], true ); | |
| 440 | + $needs_validation = $is_executable && 'shortcode' !== $data['scope'] && '' !== trim( $data['code'] ) && $data['execution_changed']; | |
| 441 | + $validation = [ | |
| 443 | 442 | 'valid' => true, |
| 444 | - 'checked' => false, | |
| 445 | 443 | 'message' => '', |
| 446 | 444 | ]; |
| 447 | 445 | |
| 448 | - if ( $is_executable && '' !== trim( $data['code'] ) && $data['execution_changed'] ) { | |
| 449 | - $validation = $this->check_code( $data, $was_active ); | |
| 450 | - } | |
| 446 | + if ( $needs_validation ) { | |
| 447 | + if ( WINP_Helper::is_safe_mode() ) { | |
| 448 | + return new WP_Error( 'woody_safe_mode', __( 'Safe mode is enabled, snippet code cannot be evaluated. Disable safe mode first.', 'insert-php' ) ); | |
| 449 | + } | |
| 451 | 450 | |
| 452 | - // New snippets are always inactive. Like the editor, an update keeps an active snippet active only when the | |
| 453 | - // "activate by default" setting is on, and changed executable code is saved inactive because it has not run yet. | |
| 454 | - $keep_active = $was_active && ! ( $is_executable && $data['execution_changed'] ) && get_option( 'wbcr_inp_activate_by_default', true ); | |
| 455 | - $deactivated = $was_active && ! $keep_active; | |
| 456 | - $operation = $post ? 'update' : 'create'; | |
| 451 | + $validation = WINP_Code_Validator::validate_code( $this->prepare_code( $data['code'], $data['type'] ), $data['type'] ); | |
| 457 | 452 | |
| 458 | - if ( $dry_run ) { | |
| 459 | - $message = $validation['message']; | |
| 453 | + if ( ! $validation['valid'] ) { | |
| 454 | + $validation['message'] = wp_strip_all_tags( str_replace( '<br>', "\n", $validation['message'] ) ); | |
| 460 | 455 | |
| 461 | - if ( $validation['valid'] && $deactivated ) { | |
| 462 | - $message = __( 'Saving will leave the snippet inactive. Activate it again with woody/set-snippet-state.', 'insert-php' ); | |
| 456 | + if ( $was_active ) { | |
| 457 | + $validation['message'] .= ' ' . __( 'The snippet is currently active and has already run in this request; deactivate it first if the error is caused by its own declarations.', 'insert-php' ); | |
| 458 | + } | |
| 463 | 459 | } |
| 460 | + } | |
| 464 | 461 | |
| 462 | + $operation = $post ? 'update' : 'create'; | |
| 463 | + | |
| 464 | + if ( $dry_run ) { | |
| 465 | 465 | return [ |
| 466 | - 'dry_run' => true, | |
| 467 | - 'operation' => $operation, | |
| 468 | - 'valid' => $validation['valid'], | |
| 469 | - 'validated' => $validation['checked'], | |
| 470 | - 'deactivated' => $validation['valid'] && $deactivated, | |
| 471 | - 'message' => $message, | |
| 466 | + 'dry_run' => true, | |
| 467 | + 'operation' => $operation, | |
| 468 | + 'valid' => $validation['valid'], | |
| 469 | + 'validated' => $needs_validation, | |
| 470 | + 'message' => $validation['message'], | |
| 472 | 471 | ]; |
| 473 | 472 | } |
| 474 | 473 | |
| 475 | 474 | if ( ! $validation['valid'] ) { |
| @@ -475,9 +474,9 @@ | ||
| 475 | 474 | if ( ! $validation['valid'] ) { |
| 476 | 475 | return new WP_Error( 'woody_invalid_code', $validation['message'] ); |
| 477 | 476 | } |
| 478 | 477 | |
| 479 | - $saved_id = $this->save_snippet( $data, $post, $keep_active ); | |
| 478 | + $saved_id = $this->save_snippet( $data, $post, $was_active ); | |
| 480 | 479 | |
| 481 | 480 | if ( is_wp_error( $saved_id ) ) { |
| 482 | 481 | return $saved_id; |
| 483 | 482 | } |
| @@ -502,11 +501,10 @@ | ||
| 502 | 501 | return [ |
| 503 | 502 | 'dry_run' => false, |
| 504 | 503 | 'operation' => $operation, |
| 505 | 504 | 'valid' => true, |
| 506 | - 'validated' => $validation['checked'], | |
| 507 | - 'deactivated' => $deactivated, | |
| 508 | - 'message' => $deactivated ? __( 'The snippet was saved inactive. Activate it again with woody/set-snippet-state.', 'insert-php' ) : '', | |
| 505 | + 'validated' => $needs_validation, | |
| 506 | + 'message' => '', | |
| 509 | 507 | 'revision_id' => $revision_id, |
| 510 | 508 | 'snippet' => $this->format_snippet( $saved, true ), |
| 511 | 509 | ]; |
| 512 | 510 | } |
| @@ -511,36 +509,8 @@ | ||
| 511 | 509 | ]; |
| 512 | 510 | } |
| 513 | 511 | |
| 514 | 512 | /** |
| 515 | - * Check new or changed executable code without running it. | |
| 516 | - * | |
| 517 | - * @param array<string, mixed> $data Resolved snippet data. | |
| 518 | - * @param bool $was_active Whether the snippet was active before the update. | |
| 519 | - * | |
| 520 | - * @return array{valid:bool,checked:bool,message:string} | |
| 521 | - */ | |
| 522 | - private function check_code( array $data, bool $was_active ): array { | |
| 523 | - $code = $this->prepare_code( $data['code'], $data['type'] ); | |
| 524 | - $validation = WINP_Code_Validator::check_syntax( $code, $data['type'] ); | |
| 525 | - | |
| 526 | - // An active snippet may already have run in this request, so its own declarations would be | |
| 527 | - // reported as conflicts; activating it again runs the same check once it is inactive. | |
| 528 | - if ( ! $validation['valid'] || $was_active ) { | |
| 529 | - return $validation; | |
| 530 | - } | |
| 531 | - | |
| 532 | - $redeclaration_error = WINP_Code_Validator::get_redeclaration_error( $code, $data['type'] ); | |
| 533 | - | |
| 534 | - if ( null !== $redeclaration_error ) { | |
| 535 | - $validation['valid'] = false; | |
| 536 | - $validation['message'] = $redeclaration_error->getMessage(); | |
| 537 | - } | |
| 538 | - | |
| 539 | - return $validation; | |
| 540 | - } | |
| 541 | - | |
| 542 | - /** | |
| 543 | 513 | * Execute callback: activate or deactivate a snippet. |
| 544 | 514 | * |
| 545 | 515 | * @param array<string, mixed>|null $input Ability input. |
| 546 | 516 | * |
| @@ -616,198 +586,36 @@ | ||
| 616 | 586 | return new WP_Error( 'woody_safe_mode', __( 'Safe mode is enabled, snippet code cannot be executed. Disable safe mode first.', 'insert-php' ) ); |
| 617 | 587 | } |
| 618 | 588 | |
| 619 | 589 | if ( in_array( $type, [ WINP_SNIPPET_TYPE_PHP, WINP_SNIPPET_TYPE_UNIVERSAL ], true ) ) { |
| 620 | - return $this->run_activation_check( $post, $type ); | |
| 590 | + // Redeclaring a function inside eval() ends the request, so detect it before executing the snippet. | |
| 591 | + $code = $this->prepare_code( (string) WINP_Helper::get_snippet_code( $post ), $type ); | |
| 592 | + $redeclaration = WINP_Code_Validator::find_function_redeclaration( $code, $type ); | |
| 593 | + | |
| 594 | + if ( null !== $redeclaration ) { | |
| 595 | + return new WP_Error( | |
| 596 | + 'woody_invalid_code', | |
| 597 | + // translators: %1$d is the line number, %2$s is the fully qualified function name. | |
| 598 | + sprintf( __( 'Line %1$d: Cannot redeclare function %2$s(). Rename the function or guard its declaration with function_exists().', 'insert-php' ), $redeclaration['line'], $redeclaration['name'] ) | |
| 599 | + ); | |
| 600 | + } | |
| 621 | 601 | } |
| 622 | 602 | |
| 623 | 603 | $error = WINP_Plugin::app()->get_execute_object()->getSnippetError( $post->ID ); |
| 624 | 604 | |
| 625 | 605 | if ( $error ) { |
| 626 | - return $this->activation_error( is_array( $error ) && isset( $error['message'] ) ? (string) $error['message'] : '' ); | |
| 627 | - } | |
| 606 | + $message = is_array( $error ) && isset( $error['message'] ) ? wp_strip_all_tags( (string) $error['message'] ) : ''; | |
| 628 | 607 | |
| 629 | - return true; | |
| 630 | - } | |
| 631 | - | |
| 632 | - /** | |
| 633 | - * Run executable snippet code once before it is activated. | |
| 634 | - * | |
| 635 | - * Saving never runs the code, so this is the first run. The executor runs it, so a | |
| 636 | - * fatal error is still attributed to the snippet and logged. Like the editor's | |
| 637 | - * validation, errors, warnings raised by the snippet's own code and a false return | |
| 638 | - * refuse the activation. | |
| 639 | - * | |
| 640 | - * @param WP_Post $post Snippet post. | |
| 641 | - * @param string $type Snippet type. | |
| 642 | - * | |
| 643 | - * @return true|WP_Error | |
| 644 | - */ | |
| 645 | - private function run_activation_check( WP_Post $post, string $type ) { | |
| 646 | - $code = $this->prepare_code( (string) WINP_Helper::get_snippet_code( $post ), $type ); | |
| 647 | - | |
| 648 | - // The executor skips empty code, '0' included, and reports it as a false return. | |
| 649 | - if ( empty( $code ) ) { | |
| 650 | - return true; | |
| 651 | - } | |
| 652 | - | |
| 653 | - $redeclaration_error = WINP_Code_Validator::get_redeclaration_error( $code, $type ); | |
| 654 | - | |
| 655 | - // Redeclaring a function or class inside eval() ends the request, so detect it before running the snippet. | |
| 656 | - if ( null !== $redeclaration_error ) { | |
| 657 | - return $this->activation_error( $redeclaration_error->getMessage() ); | |
| 658 | - } | |
| 659 | - | |
| 660 | - $warnings = []; | |
| 661 | - $result = null; | |
| 662 | - $executed = false; | |
| 663 | - $failure = null; | |
| 664 | - $buffer_level = ob_get_level(); | |
| 665 | - $previous = self::get_error_handler(); | |
| 666 | - $marker = static function (): bool { | |
| 667 | - return false; | |
| 668 | - }; | |
| 669 | - | |
| 670 | - // The marker sits below the check's handler, so cleanup knows where to stop. | |
| 671 | - set_error_handler( $marker ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler | |
| 672 | - set_error_handler( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler | |
| 673 | - static function ( int $errno, string $errstr, string $errfile, int $errline ) use ( &$warnings ): bool { | |
| 674 | - // Like the editor's save check, errors raised outside the snippet's own code go to PHP's standard handler. | |
| 675 | - if ( false === strpos( $errfile, "eval()'d code" ) ) { | |
| 676 | - return false; | |
| 677 | - } | |
| 678 | - | |
| 679 | - // translators: %1$d is the line number, %2$s is the error message. | |
| 680 | - $warnings[] = sprintf( __( 'Line %1$d: %2$s', 'insert-php' ), $errline, $errstr ); | |
| 681 | - | |
| 682 | - return true; | |
| 683 | - } | |
| 684 | - ); | |
| 685 | - | |
| 686 | - try { | |
| 687 | - $result = WINP_Plugin::app()->get_execute_object()->executeSnippet( $code, $post->ID ); | |
| 688 | - $executed = true; | |
| 689 | - | |
| 690 | - // Closing a buffer the snippet left open runs its callback, which can throw as well. | |
| 691 | - self::discard_output_buffers( $buffer_level ); | |
| 692 | - } catch ( Throwable $e ) { | |
| 693 | - // The executor leaves the snippet context open when the code throws. | |
| 694 | - if ( ! $executed ) { | |
| 695 | - WINP_Error_Handler::clear_current_snippet(); | |
| 696 | - } | |
| 697 | - | |
| 698 | - $failure = $e; | |
| 699 | - } finally { | |
| 700 | - self::restore_error_handlers( $marker, $previous ); | |
| 701 | - } | |
| 702 | - | |
| 703 | - if ( null !== $failure ) { | |
| 704 | - self::discard_output_buffers( $buffer_level ); | |
| 705 | - | |
| 706 | - return $this->activation_error( | |
| 707 | - false !== strpos( $failure->getFile(), "eval()'d code" ) | |
| 708 | - // translators: %1$d is the line number, %2$s is the error message. | |
| 709 | - ? sprintf( __( 'Error on line %1$d: %2$s', 'insert-php' ), $failure->getLine(), $failure->getMessage() ) | |
| 710 | - // translators: %s is the error message. | |
| 711 | - : sprintf( __( 'Error: %s', 'insert-php' ), $failure->getMessage() ) | |
| 608 | + return new WP_Error( | |
| 609 | + 'woody_invalid_code', | |
| 610 | + trim( __( 'The snippet was not activated because its code contains an error.', 'insert-php' ) . ' ' . $message ) | |
| 712 | 611 | ); |
| 713 | 612 | } |
| 714 | 613 | |
| 715 | - if ( ! empty( $warnings ) ) { | |
| 716 | - return $this->activation_error( implode( "\n", $warnings ) ); | |
| 717 | - } | |
| 718 | - | |
| 719 | - if ( false === $result ) { | |
| 720 | - return $this->activation_error( __( 'The code returned false.', 'insert-php' ) ); | |
| 721 | - } | |
| 722 | - | |
| 723 | 614 | return true; |
| 724 | 615 | } |
| 725 | 616 | |
| 726 | 617 | /** |
| 727 | - * Build the error returned when a snippet is not activated. | |
| 728 | - * | |
| 729 | - * @param string $message Error details. | |
| 730 | - * | |
| 731 | - * @return WP_Error | |
| 732 | - */ | |
| 733 | - private function activation_error( string $message ): WP_Error { | |
| 734 | - return new WP_Error( | |
| 735 | - 'woody_invalid_code', | |
| 736 | - trim( __( 'The snippet was not activated because its code contains an error.', 'insert-php' ) . ' ' . wp_strip_all_tags( $message ) ) | |
| 737 | - ); | |
| 738 | - } | |
| 739 | - | |
| 740 | - /** | |
| 741 | - * Remove the check's handlers and any the snippet installed above them. | |
| 742 | - * | |
| 743 | - * PHP reads an empty handler stack and a null entry the same way, so the | |
| 744 | - * check installs a marker below its own handler and cleanup stops there. | |
| 745 | - * | |
| 746 | - * @param Closure $marker Handler installed below the check's handler; it returns false. | |
| 747 | - * @param callable|null $previous Handler that was active before the check. Not typed natively: | |
| 748 | - * a private method another class registered is not callable here. | |
| 749 | - * @phpstan-param Closure(): bool $marker | |
| 750 | - * | |
| 751 | - * @return void | |
| 752 | - */ | |
| 753 | - private static function restore_error_handlers( Closure $marker, $previous ): void { | |
| 754 | - $null_reads = 0; | |
| 755 | - | |
| 756 | - // Only a long run of null reads, which an emptied stack gives forever, ends the loop before the marker. | |
| 757 | - while ( $null_reads < 100 ) { | |
| 758 | - $current = self::get_error_handler(); | |
| 759 | - | |
| 760 | - if ( $current === $marker ) { | |
| 761 | - restore_error_handler(); | |
| 762 | - return; | |
| 763 | - } | |
| 764 | - | |
| 765 | - // The snippet removed the marker as well. | |
| 766 | - if ( null !== $previous && $current === $previous ) { | |
| 767 | - return; | |
| 768 | - } | |
| 769 | - | |
| 770 | - $null_reads = null === $current ? $null_reads + 1 : 0; | |
| 771 | - restore_error_handler(); | |
| 772 | - } | |
| 773 | - } | |
| 774 | - | |
| 775 | - /** | |
| 776 | - * Read the active error handler without changing it. | |
| 777 | - * | |
| 778 | - * @return callable|null Not typed natively: a private method another class registered is not callable here. | |
| 779 | - */ | |
| 780 | - private static function get_error_handler() { | |
| 781 | - $current = set_error_handler( '__return_false' ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler | |
| 782 | - restore_error_handler(); | |
| 783 | - | |
| 784 | - return $current; | |
| 785 | - } | |
| 786 | - | |
| 787 | - /** | |
| 788 | - * Close the output buffers opened above a level, stopping at one that cannot be removed. | |
| 789 | - * | |
| 790 | - * A snippet can open a buffer without the removable flag, which ob_end_clean() cannot close. | |
| 791 | - * A callback on a buffer can throw when the buffer closes; the caller handles that. | |
| 792 | - * | |
| 793 | - * @param int $level Buffer level to return to. | |
| 794 | - * | |
| 795 | - * @return void | |
| 796 | - */ | |
| 797 | - private static function discard_output_buffers( int $level ): void { | |
| 798 | - while ( ob_get_level() > $level ) { | |
| 799 | - $buffer = ob_get_status(); | |
| 800 | - | |
| 801 | - if ( ! ( (int) ( $buffer['flags'] ?? 0 ) & PHP_OUTPUT_HANDLER_REMOVABLE ) ) { | |
| 802 | - return; | |
| 803 | - } | |
| 804 | - | |
| 805 | - ob_end_clean(); | |
| 806 | - } | |
| 807 | - } | |
| 808 | - | |
| 809 | - /** | |
| 810 | 618 | * Merge the input over the stored snippet and validate the result. |
| 811 | 619 | * |
| 812 | 620 | * @param array<string, mixed> $input Ability input. |
| 813 | 621 | * @param WP_Post|null $post Existing snippet, null when creating. |
| @@ -1145,15 +953,15 @@ | ||
| 1145 | 953 | |
| 1146 | 954 | /** |
| 1147 | 955 | * Persist the snippet. |
| 1148 | 956 | * |
| 1149 | - * @param array<string, mixed> $data Validated snippet data. | |
| 1150 | - * @param WP_Post|null $post Existing snippet, null when creating. | |
| 1151 | - * @param bool $keep_active Whether an updated snippet stays active; new snippets are always inactive. | |
| 957 | + * @param array<string, mixed> $data Validated snippet data. | |
| 958 | + * @param WP_Post|null $post Existing snippet, null when creating. | |
| 959 | + * @param bool $was_active Whether the snippet was active before the update. | |
| 1152 | 960 | * |
| 1153 | 961 | * @return int|WP_Error Snippet ID. |
| 1154 | 962 | */ |
| 1155 | - private function save_snippet( $data, $post, $keep_active ) { | |
| 963 | + private function save_snippet( $data, $post, $was_active ) { | |
| 1156 | 964 | $postarr = [ |
| 1157 | 965 | 'post_title' => $data['title'], |
| 1158 | 966 | 'post_content' => $data['code'], |
| 1159 | 967 | 'post_type' => WINP_SNIPPETS_POST_TYPE, |
| @@ -1177,13 +985,8 @@ | ||
| 1177 | 985 | if ( $had_rel ) { |
| 1178 | 986 | remove_filter( 'content_save_pre', 'wp_targeted_link_rel' ); |
| 1179 | 987 | } |
| 1180 | 988 | |
| 1181 | - // Switch the snippet off before its new code is stored, so no request in between runs that code. | |
| 1182 | - if ( $post && ! $keep_active ) { | |
| 1183 | - update_post_meta( $post->ID, 'wbcr_inp_snippet_activate', 0 ); | |
| 1184 | - } | |
| 1185 | - | |
| 1186 | 989 | $saved_id = $post ? wp_update_post( wp_slash( $postarr ), true ) : wp_insert_post( wp_slash( $postarr ), true ); |
| 1187 | 990 | |
| 1188 | 991 | if ( $had_kses ) { |
| 1189 | 992 | kses_init_filters(); |
| @@ -1217,10 +1020,14 @@ | ||
| 1217 | 1020 | if ( ! $post ) { |
| 1218 | 1021 | update_post_meta( $saved_id, 'wbcr_inp_snippet_priority', WINP_Helper::get_next_snippet_priority() ); |
| 1219 | 1022 | } |
| 1220 | 1023 | |
| 1221 | - update_post_meta( $saved_id, 'wbcr_inp_snippet_activate', $post && $keep_active ? 1 : 0 ); | |
| 1024 | + // New snippets are always inactive. Like the editor, an update keeps an active | |
| 1025 | + // snippet active only when the "activate by default" setting is on. | |
| 1026 | + $keep_active = $post && $was_active && get_option( 'wbcr_inp_activate_by_default', true ); | |
| 1222 | 1027 | |
| 1028 | + update_post_meta( $saved_id, 'wbcr_inp_snippet_activate', $keep_active ? 1 : 0 ); | |
| 1029 | + | |
| 1223 | 1030 | return $saved_id; |
| 1224 | 1031 | } |
| 1225 | 1032 | |
| 1226 | 1033 | /** |
| @@ -1439,9 +1246,9 @@ | ||
| 1439 | 1246 | 'description' => __( 'Display conditions for non-PHP snippets as a JSON string, in the format the conditions editor stores: [{"type":"showif","conditions":[{"type":"scope","conditions":[{"param":"location-some-page","operator":"equals","type":"select","value":"base_web"}]}]}]. Groups hold AND-ed scopes, scopes hold OR-ed conditions. Pass "[]" to clear. Params starting with technology- or auditory- require the premium plugin.', 'insert-php' ), |
| 1440 | 1247 | ], |
| 1441 | 1248 | 'dry_run' => [ |
| 1442 | 1249 | 'type' => 'boolean', |
| 1443 | - 'description' => __( 'Check only; nothing is saved. The code is never run.', 'insert-php' ), | |
| 1250 | + 'description' => __( 'Validate only; nothing is saved. Validation of PHP and universal snippets evaluates the code once.', 'insert-php' ), | |
| 1444 | 1251 | 'default' => false, |
| 1445 | 1252 | ], |
| 1446 | 1253 | ], |
| 1447 | 1254 | 'additionalProperties' => false, |