diff --git a/classic-editor.php b/classic-editor.php index ffb6305057944e30ce4e528611c2ccc07dc7a7e3..d1182a149235762ac242f71b7c34fa801726eaf0 100644 --- a/classic-editor.php +++ b/classic-editor.php @@ -1,1006 +1,1033 @@ -<?php -/** - * Classic Editor - * - * Plugin Name: Classic Editor - * Plugin URI: https://wordpress.org/plugins/classic-editor/ - * Description: Enables the WordPress classic editor and the old-style Edit Post screen with TinyMCE, Meta Boxes, etc. Supports the older plugins that extend this screen. - * Version: 1.6.5 - * Author: WordPress Contributors - * Author URI: https://github.com/WordPress/classic-editor/ - * License: GPLv2 or later - * License URI: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * Text Domain: classic-editor - * Domain Path: /languages - * Requires at least: 4.9 - * Requires PHP: 5.2.4 - * - * This program is free software; you can redistribute it and/or modify it under the terms of the GNU - * General Public License version 2, as published by the Free Software Foundation. You may NOT assume - * that you can use any other version of the GPL. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without - * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - */ - -if ( ! defined( 'ABSPATH' ) ) { - die( 'Invalid request.' ); -} - -if ( ! class_exists( 'Classic_Editor' ) ) : -class Classic_Editor { - private static $settings; - private static $supported_post_types = array(); - - private function __construct() {} - - public static function init_actions() { - $block_editor = has_action( 'enqueue_block_assets' ); - $gutenberg = function_exists( 'gutenberg_register_scripts_and_styles' ); - - register_activation_hook( __FILE__, array( __CLASS__, 'activate' ) ); - - $settings = self::get_settings(); - - if ( is_multisite() ) { - add_action( 'wpmu_options', array( __CLASS__, 'network_settings' ) ); - add_action( 'update_wpmu_options', array( __CLASS__, 'save_network_settings' ) ); - } - - if ( ! $settings['hide-settings-ui'] ) { - // Add a link to the plugin's settings and/or network admin settings in the plugins list table. - add_filter( 'plugin_action_links', array( __CLASS__, 'add_settings_link' ), 10, 2 ); - add_filter( 'network_admin_plugin_action_links', array( __CLASS__, 'add_settings_link' ), 10, 2 ); - - add_action( 'admin_init', array( __CLASS__, 'register_settings' ) ); - - if ( $settings['allow-users'] ) { - // User settings. - add_action( 'personal_options_update', array( __CLASS__, 'save_user_settings' ) ); - add_action( 'edit_user_profile_update', array( __CLASS__, 'save_user_settings' ) ); - add_action( 'profile_personal_options', array( __CLASS__, 'user_settings' ) ); - add_action( 'edit_user_profile', array( __CLASS__, 'user_settings') ); - } - } - - // Always remove the "Try Gutenberg" dashboard widget. See https://core.trac.wordpress.org/ticket/44635. - remove_action( 'try_gutenberg_panel', 'wp_try_gutenberg_panel' ); - - // Fix for Safari 18 negative horizontal margin on floats. - add_action( 'admin_print_styles', array( __CLASS__, 'safari_18_temp_fix' ) ); - - if ( ! $block_editor && ! $gutenberg ) { - return; - } - - if ( $settings['allow-users'] ) { - // Also used in Gutenberg. - add_filter( 'use_block_editor_for_post', array( __CLASS__, 'choose_editor' ), 100, 2 ); - - if ( $gutenberg ) { - // Support older Gutenberg versions. - add_filter( 'gutenberg_can_edit_post', array( __CLASS__, 'choose_editor' ), 100, 2 ); - - if ( $settings['editor'] === 'classic' ) { - self::remove_gutenberg_hooks( 'some' ); - } - } - - add_filter( 'get_edit_post_link', array( __CLASS__, 'get_edit_post_link' ) ); - add_filter( 'redirect_post_location', array( __CLASS__, 'redirect_location' ) ); - add_action( 'edit_form_top', array( __CLASS__, 'add_redirect_helper' ) ); - add_action( 'admin_head-edit.php', array( __CLASS__, 'add_edit_php_inline_style' ) ); - - add_action( 'edit_form_top', array( __CLASS__, 'remember_classic_editor' ) ); - - if ( version_compare( $GLOBALS['wp_version'], '5.8', '>=' ) ) { - add_filter( 'block_editor_settings_all', array( __CLASS__, 'remember_block_editor' ), 10, 2 ); - } else { - add_filter( 'block_editor_settings', array( __CLASS__, 'remember_block_editor' ), 10, 2 ); - } - - // Post state (edit.php) - add_filter( 'display_post_states', array( __CLASS__, 'add_post_state' ), 10, 2 ); - // Row actions (edit.php) - add_filter( 'page_row_actions', array( __CLASS__, 'add_edit_links' ), 15, 2 ); - add_filter( 'post_row_actions', array( __CLASS__, 'add_edit_links' ), 15, 2 ); - - // Switch editors while editing a post - add_action( 'add_meta_boxes', array( __CLASS__, 'add_meta_box' ), 10, 2 ); - add_action( 'enqueue_block_editor_assets', array( __CLASS__, 'enqueue_block_editor_scripts' ) ); - } else { - if ( $settings['editor'] === 'classic' ) { - // Also used in Gutenberg. - // Consider disabling other Block Editor functionality. - add_filter( 'use_block_editor_for_post_type', '__return_false', 100 ); - - if ( $gutenberg ) { - // Support older Gutenberg versions. - add_filter( 'gutenberg_can_edit_post_type', '__return_false', 100 ); - self::remove_gutenberg_hooks(); - } - } else { - // $settings['editor'] === 'block', nothing to do :) - return; - } - } - - if ( $block_editor ) { - // Move the Privacy Page notice back under the title. - add_action( 'admin_init', array( __CLASS__, 'on_admin_init' ) ); - } - if ( $gutenberg ) { - // These are handled by this plugin. All are older, not used in 5.3+. - remove_action( 'admin_init', 'gutenberg_add_edit_link_filters' ); - remove_action( 'admin_print_scripts-edit.php', 'gutenberg_replace_default_add_new_button' ); - remove_filter( 'redirect_post_location', 'gutenberg_redirect_to_classic_editor_when_saving_posts' ); - remove_filter( 'display_post_states', 'gutenberg_add_gutenberg_post_state' ); - remove_action( 'edit_form_top', 'gutenberg_remember_classic_editor_when_saving_posts' ); - } - } - - public static function remove_gutenberg_hooks( $remove = 'all' ) { - remove_action( 'admin_menu', 'gutenberg_menu' ); - remove_action( 'admin_init', 'gutenberg_redirect_demo' ); - - if ( $remove !== 'all' ) { - return; - } - - // Gutenberg 5.3+ - remove_action( 'wp_enqueue_scripts', 'gutenberg_register_scripts_and_styles' ); - remove_action( 'admin_enqueue_scripts', 'gutenberg_register_scripts_and_styles' ); - remove_action( 'admin_notices', 'gutenberg_wordpress_version_notice' ); - remove_action( 'rest_api_init', 'gutenberg_register_rest_widget_updater_routes' ); - remove_action( 'admin_print_styles', 'gutenberg_block_editor_admin_print_styles' ); - remove_action( 'admin_print_scripts', 'gutenberg_block_editor_admin_print_scripts' ); - remove_action( 'admin_print_footer_scripts', 'gutenberg_block_editor_admin_print_footer_scripts' ); - remove_action( 'admin_footer', 'gutenberg_block_editor_admin_footer' ); - remove_action( 'admin_enqueue_scripts', 'gutenberg_widgets_init' ); - remove_action( 'admin_notices', 'gutenberg_build_files_notice' ); - - remove_filter( 'load_script_translation_file', 'gutenberg_override_translation_file' ); - remove_filter( 'block_editor_settings', 'gutenberg_extend_block_editor_styles' ); - remove_filter( 'default_content', 'gutenberg_default_demo_content' ); - remove_filter( 'default_title', 'gutenberg_default_demo_title' ); - remove_filter( 'block_editor_settings', 'gutenberg_legacy_widget_settings' ); - remove_filter( 'rest_request_after_callbacks', 'gutenberg_filter_oembed_result' ); - - // Previously used, compat for older Gutenberg versions. - remove_filter( 'wp_refresh_nonces', 'gutenberg_add_rest_nonce_to_heartbeat_response_headers' ); - remove_filter( 'get_edit_post_link', 'gutenberg_revisions_link_to_editor' ); - remove_filter( 'wp_prepare_revision_for_js', 'gutenberg_revisions_restore' ); - - remove_action( 'rest_api_init', 'gutenberg_register_rest_routes' ); - remove_action( 'rest_api_init', 'gutenberg_add_taxonomy_visibility_field' ); - remove_filter( 'registered_post_type', 'gutenberg_register_post_prepare_functions' ); - - remove_action( 'do_meta_boxes', 'gutenberg_meta_box_save' ); - remove_action( 'submitpost_box', 'gutenberg_intercept_meta_box_render' ); - remove_action( 'submitpage_box', 'gutenberg_intercept_meta_box_render' ); - remove_action( 'edit_page_form', 'gutenberg_intercept_meta_box_render' ); - remove_action( 'edit_form_advanced', 'gutenberg_intercept_meta_box_render' ); - remove_filter( 'redirect_post_location', 'gutenberg_meta_box_save_redirect' ); - remove_filter( 'filter_gutenberg_meta_boxes', 'gutenberg_filter_meta_boxes' ); - - remove_filter( 'body_class', 'gutenberg_add_responsive_body_class' ); - remove_filter( 'admin_url', 'gutenberg_modify_add_new_button_url' ); // old - remove_action( 'admin_enqueue_scripts', 'gutenberg_check_if_classic_needs_warning_about_blocks' ); - remove_filter( 'register_post_type_args', 'gutenberg_filter_post_type_labels' ); - - // phpcs:disable Squiz.PHP.CommentedOutCode.Found - // Keep - // remove_filter( 'wp_kses_allowed_html', 'gutenberg_kses_allowedtags', 10, 2 ); // not needed in 5.0 - // remove_filter( 'bulk_actions-edit-wp_block', 'gutenberg_block_bulk_actions' ); - // remove_filter( 'wp_insert_post_data', 'gutenberg_remove_wpcom_markdown_support' ); - // remove_filter( 'the_content', 'do_blocks', 9 ); - // remove_action( 'init', 'gutenberg_register_post_types' ); - - // Continue to manage wpautop for posts that were edited in Gutenberg. - // remove_filter( 'wp_editor_settings', 'gutenberg_disable_editor_settings_wpautop' ); - // remove_filter( 'the_content', 'gutenberg_wpautop', 8 ); - // phpcs:enable Squiz.PHP.CommentedOutCode.Found - - } - - private static function get_settings( $refresh = 'no', $user_id = 0 ) { - /** - * Can be used to override the plugin's settings. Always hides the settings UI when used (as users cannot change the settings). - * - * Has to return an associative array with two keys. - * The defaults are: - * 'editor' => 'classic', // Accepted values: 'classic', 'block'. - * 'allow-users' => false, - * - * @param boolean To override the settings return an array with the above keys. Default false. - */ - $settings = apply_filters( 'classic_editor_plugin_settings', false ); - - if ( is_array( $settings ) ) { - return array( - 'editor' => ( isset( $settings['editor'] ) && $settings['editor'] === 'block' ) ? 'block' : 'classic', - 'allow-users' => ! empty( $settings['allow-users'] ), - 'hide-settings-ui' => true, - ); - } - - if ( ! empty( self::$settings ) && $refresh === 'no' ) { - return self::$settings; - } - - if ( is_multisite() ) { - $defaults = array( - 'editor' => get_network_option( null, 'classic-editor-replace' ) === 'block' ? 'block' : 'classic', - 'allow-users' => false, - ); - - /** - * Filters the default network options. - * - * @param array $defaults The default options array. See `classic_editor_plugin_settings` for supported keys and values. - */ - $defaults = apply_filters( 'classic_editor_network_default_settings', $defaults ); - - if ( get_network_option( null, 'classic-editor-allow-sites' ) !== 'allow' ) { - // Per-site settings are disabled. Return default network options nad hide the settings UI. - $defaults['hide-settings-ui'] = true; - return $defaults; - } - - // Override with the site options. - $editor_option = get_option( 'classic-editor-replace' ); - $allow_users_option = get_option( 'classic-editor-allow-users' ); - - if ( $editor_option ) { - $defaults['editor'] = $editor_option; - } - if ( $allow_users_option ) { - $defaults['allow-users'] = ( $allow_users_option === 'allow' ); - } - - $editor = ( isset( $defaults['editor'] ) && $defaults['editor'] === 'block' ) ? 'block' : 'classic'; - $allow_users = ! empty( $defaults['allow-users'] ); - } else { - $allow_users = ( get_option( 'classic-editor-allow-users' ) === 'allow' ); - $option = get_option( 'classic-editor-replace' ); - - // Normalize old options. - if ( $option === 'block' || $option === 'no-replace' ) { - $editor = 'block'; - } else { - // empty( $option ) || $option === 'classic' || $option === 'replace' - $editor = 'classic'; - } - } - - // Override the defaults with the user options. - if ( ( ! isset( $GLOBALS['pagenow'] ) || $GLOBALS['pagenow'] !== 'options-writing.php' ) && $allow_users ) { - - $user_options = get_user_option( 'classic-editor-settings', $user_id ); - - if ( $user_options === 'block' || $user_options === 'classic' ) { - $editor = $user_options; - } - } - - self::$settings = array( - 'editor' => $editor, - 'hide-settings-ui' => false, - 'allow-users' => $allow_users, - ); - - return self::$settings; - } - - private static function is_classic( $post_id = 0 ) { - if ( ! $post_id ) { - $post_id = self::get_edited_post_id(); - } - - if ( $post_id ) { - $settings = self::get_settings(); - - if ( $settings['allow-users'] && ! isset( $_GET['classic-editor__forget'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended - $which = get_post_meta( $post_id, 'classic-editor-remember', true ); - - if ( $which ) { - // The editor choice will be "remembered" when the post is opened in either the classic or the block editor. - if ( 'classic-editor' === $which ) { - return true; - } elseif ( 'block-editor' === $which ) { - return false; - } - } - - return ( ! self::has_blocks( $post_id ) ); - } - } - - if ( isset( $_GET['classic-editor'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended - return true; - } - - return false; - } - - /** - * Get the edited post ID (early) when loading the Edit Post screen. - */ - private static function get_edited_post_id() { - // phpcs:disable WordPress.Security.NonceVerification.Recommended - if ( - ! empty( $_GET['post'] ) && - ! empty( $_GET['action'] ) && - $_GET['action'] === 'edit' && - ! empty( $GLOBALS['pagenow'] ) && - $GLOBALS['pagenow'] === 'post.php' - ) { - return (int) $_GET['post']; // post_ID - } - // phpcs:enable WordPress.Security.NonceVerification.Recommended - - return 0; - } - - public static function register_settings() { - // Add an option to Settings -> Writing - register_setting( 'writing', 'classic-editor-replace', array( - 'sanitize_callback' => array( __CLASS__, 'validate_option_editor' ), - ) ); - - register_setting( 'writing', 'classic-editor-allow-users', array( - 'sanitize_callback' => array( __CLASS__, 'validate_option_allow_users' ), - ) ); - - $allowed_options = array( - 'writing' => array( - 'classic-editor-replace', - 'classic-editor-allow-users' - ), - ); - - if ( function_exists( 'add_allowed_options' ) ) { - add_allowed_options( $allowed_options ); - } else { - add_option_whitelist( $allowed_options ); - } - - $heading_1 = __( 'Default editor for all users', 'classic-editor' ); - $heading_2 = __( 'Allow users to switch editors', 'classic-editor' ); - - add_settings_field( 'classic-editor-1', $heading_1, array( __CLASS__, 'settings_1' ), 'writing' ); - add_settings_field( 'classic-editor-2', $heading_2, array( __CLASS__, 'settings_2' ), 'writing' ); - } - - public static function save_user_settings( $user_id ) { - if ( - isset( $_POST['classic-editor-user-settings'] ) && - isset( $_POST['classic-editor-replace'] ) && - wp_verify_nonce( $_POST['classic-editor-user-settings'], 'allow-user-settings' ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized - ) { - $user_id = (int) $user_id; - - if ( $user_id !== get_current_user_id() && ! current_user_can( 'edit_user', $user_id ) ) { - return; - } - - $editor = self::validate_option_editor( $_POST['classic-editor-replace'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized - update_user_option( $user_id, 'classic-editor-settings', $editor ); - } - } - - /** - * Validate - */ - public static function validate_option_editor( $value ) { - if ( $value === 'block' ) { - return 'block'; - } - - return 'classic'; - } - - public static function validate_option_allow_users( $value ) { - if ( $value === 'allow' ) { - return 'allow'; - } - - return 'disallow'; - } - - public static function settings_1( $user_id = 0 ) { - $settings = self::get_settings( 'refresh', $user_id ); - - ?> - <div class="classic-editor-options"> - <p> - <input type="radio" name="classic-editor-replace" id="classic-editor-classic" value="classic"<?php if ( $settings['editor'] === 'classic' ) echo ' checked'; ?> /> - <label for="classic-editor-classic"><?php _ex( 'Classic editor', 'Editor Name', 'classic-editor' ); ?></label> - </p> - <p> - <input type="radio" name="classic-editor-replace" id="classic-editor-block" value="block"<?php if ( $settings['editor'] !== 'classic' ) echo ' checked'; ?> /> - <label for="classic-editor-block"><?php _ex( 'Block editor', 'Editor Name', 'classic-editor' ); ?></label> - </p> - </div> - <script> - jQuery( 'document' ).ready( function( $ ) { - if ( window.location.hash === '#classic-editor-options' ) { - $( '.classic-editor-options' ).closest( 'td' ).addClass( 'highlight' ); - } - } ); - </script> - <?php - } - - public static function settings_2() { - $settings = self::get_settings( 'refresh' ); - - ?> - <div class="classic-editor-options"> - <p> - <input type="radio" name="classic-editor-allow-users" id="classic-editor-allow" value="allow"<?php if ( $settings['allow-users'] ) echo ' checked'; ?> /> - <label for="classic-editor-allow"><?php _e( 'Yes', 'classic-editor' ); ?></label> - </p> - <p> - <input type="radio" name="classic-editor-allow-users" id="classic-editor-disallow" value="disallow"<?php if ( ! $settings['allow-users'] ) echo ' checked'; ?> /> - <label for="classic-editor-disallow"><?php _e( 'No', 'classic-editor' ); ?></label> - </p> - </div> - <?php - } - - /** - * Shown on the Profile page when allowed by admin. - */ - public static function user_settings( $user = null ) { - global $user_can_edit; - $settings = self::get_settings( 'update' ); - - if ( ! $user_can_edit || ! $settings['allow-users'] ) { - return; - } - - if ( $user instanceof WP_User ) { - $user_id = (int) $user->ID; - } else { - $user_id = 0; - } - - ?> - <table class="form-table"> - <tr class="classic-editor-user-options"> - <th scope="row"><?php _e( 'Default Editor', 'classic-editor' ); ?></th> - <td> - <?php wp_nonce_field( 'allow-user-settings', 'classic-editor-user-settings' ); ?> - <?php self::settings_1( $user_id ); ?> - </td> - </tr> - </table> - <script>jQuery( 'tr.user-rich-editing-wrap' ).before( jQuery( 'tr.classic-editor-user-options' ) );</script> - <?php - } - - public static function network_settings() { - $editor = get_network_option( null, 'classic-editor-replace' ); - $is_checked = ( get_network_option( null, 'classic-editor-allow-sites' ) === 'allow' ); - - ?> - <h2 id="classic-editor-options"><?php _e( 'Editor Settings', 'classic-editor' ); ?></h2> - <table class="form-table"> - <?php wp_nonce_field( 'allow-site-admin-settings', 'classic-editor-network-settings' ); ?> - <tr> - <th scope="row"><?php _e( 'Default editor for all sites', 'classic-editor' ); ?></th> - <td> - <p> - <input type="radio" name="classic-editor-replace" id="classic-editor-classic" value="classic"<?php if ( $editor !== 'block' ) echo ' checked'; ?> /> - <label for="classic-editor-classic"><?php _ex( 'Classic Editor', 'Editor Name', 'classic-editor' ); ?></label> - </p> - <p> - <input type="radio" name="classic-editor-replace" id="classic-editor-block" value="block"<?php if ( $editor === 'block' ) echo ' checked'; ?> /> - <label for="classic-editor-block"><?php _ex( 'Block editor', 'Editor Name', 'classic-editor' ); ?></label> - </p> - </td> - </tr> - <tr> - <th scope="row"><?php _e( 'Change settings', 'classic-editor' ); ?></th> - <td> - <input type="checkbox" name="classic-editor-allow-sites" id="classic-editor-allow-sites" value="allow"<?php if ( $is_checked ) echo ' checked'; ?>> - <label for="classic-editor-allow-sites"><?php _e( 'Allow site admins to change settings', 'classic-editor' ); ?></label> - <p class="description"><?php _e( 'By default the block editor is replaced with the classic editor and users cannot switch editors.', 'classic-editor' ); ?></p> - </td> - </tr> - </table> - <?php - } - - public static function save_network_settings() { - if ( - isset( $_POST['classic-editor-network-settings'] ) && - current_user_can( 'manage_network_options' ) && - wp_verify_nonce( $_POST['classic-editor-network-settings'], 'allow-site-admin-settings' ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized - ) { - if ( isset( $_POST['classic-editor-replace'] ) && $_POST['classic-editor-replace'] === 'block' ) { - update_network_option( null, 'classic-editor-replace', 'block' ); - } else { - update_network_option( null, 'classic-editor-replace', 'classic' ); - } - if ( isset( $_POST['classic-editor-allow-sites'] ) && $_POST['classic-editor-allow-sites'] === 'allow' ) { - update_network_option( null, 'classic-editor-allow-sites', 'allow' ); - } else { - update_network_option( null, 'classic-editor-allow-sites', 'disallow' ); - } - } - } - - /** - * Add a hidden field in edit-form-advanced.php - * to help redirect back to the classic editor on saving. - */ - public static function add_redirect_helper() { - ?> - <input type="hidden" name="classic-editor" value="" /> - <?php - } - - /** - * Remember when the classic editor was used to edit a post. - */ - public static function remember_classic_editor( $post ) { - $post_type = get_post_type( $post ); - - if ( $post_type && post_type_supports( $post_type, 'editor' ) ) { - self::remember( $post->ID, 'classic-editor' ); - } - } - - /** - * Remember when the block editor was used to edit a post. - */ - public static function remember_block_editor( $editor_settings, $context ) { - if ( is_a( $context, 'WP_Post' ) ) { - $post = $context; - } elseif ( ! empty( $context->post ) ) { - $post = $context->post; - } else { - return $editor_settings; - } - - $post_type = get_post_type( $post ); - - if ( $post_type && self::can_edit_post_type( $post_type ) ) { - self::remember( $post->ID, 'block-editor' ); - } - - return $editor_settings; - } - - private static function remember( $post_id, $editor ) { - if ( get_post_meta( $post_id, 'classic-editor-remember', true ) !== $editor ) { - update_post_meta( $post_id, 'classic-editor-remember', $editor ); - } - } - - /** - * Choose which editor to use for a post. - * - * Passes through `$which_editor` for block editor (it's sets to `true` but may be changed by another plugin). - * - * @uses `use_block_editor_for_post` filter. - * - * @param boolean $use_block_editor True for block editor, false for classic editor. - * @param WP_Post $post The post being edited. - * @return boolean True for block editor, false for classic editor. - */ - public static function choose_editor( $use_block_editor, $post ) { - $settings = self::get_settings(); - $editors = self::get_enabled_editors_for_post( $post ); - - // If no editor is supported, pass through `$use_block_editor`. - if ( ! $editors['block_editor'] && ! $editors['classic_editor'] ) { - return $use_block_editor; - } - - // Open the default editor when no $post and for "Add New" links, - // or the alternate editor when the user is switching editors. - // phpcs:disable WordPress.Security.NonceVerification.Recommended - if ( empty( $post->ID ) || $post->post_status === 'auto-draft' ) { - if ( - ( $settings['editor'] === 'classic' && ! isset( $_GET['classic-editor__forget'] ) ) || // Add New - ( isset( $_GET['classic-editor'] ) && isset( $_GET['classic-editor__forget'] ) ) // Switch to classic editor when no draft post. - ) { - $use_block_editor = false; - } - } elseif ( self::is_classic( $post->ID ) ) { - $use_block_editor = false; - } - // phpcs:enable WordPress.Security.NonceVerification.Recommended - - // Enforce the editor if set by plugins. - if ( $use_block_editor && ! $editors['block_editor'] ) { - $use_block_editor = false; - } elseif ( ! $use_block_editor && ! $editors['classic_editor'] && $editors['block_editor'] ) { - $use_block_editor = true; - } - - return $use_block_editor; - } - - /** - * Keep the `classic-editor` query arg through redirects when saving posts. - */ - public static function redirect_location( $location ) { - if ( - isset( $_REQUEST['classic-editor'] ) || // phpcs:ignore WordPress.Security.NonceVerification.Recommended - ( isset( $_POST['_wp_http_referer'] ) && strpos( $_POST['_wp_http_referer'], '&classic-editor' ) !== false ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing - ) { - $location = add_query_arg( 'classic-editor', '', $location ); - } - - return $location; - } - - /** - * Keep the `classic-editor` query arg when looking at revisions. - */ - public static function get_edit_post_link( $url ) { - $settings = self::get_settings(); - - if ( isset( $_REQUEST['classic-editor'] ) || $settings['editor'] === 'classic' ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended - $url = add_query_arg( 'classic-editor', '', $url ); - } - - return $url; - } - - public static function add_meta_box( $post_type, $post ) { - $editors = self::get_enabled_editors_for_post( $post ); - - if ( ! $editors['block_editor'] || ! $editors['classic_editor'] ) { - // Editors cannot be switched. - return; - } - - $id = 'classic-editor-switch-editor'; - $title = __( 'Editor', 'classic-editor' ); - $callback = array( __CLASS__, 'do_meta_box' ); - $args = array( - '__back_compat_meta_box' => true, - ); - - add_meta_box( $id, $title, $callback, null, 'side', 'default', $args ); - } - - public static function do_meta_box( $post ) { - $edit_url = get_edit_post_link( $post->ID, 'raw' ); - - // Switching to block editor. - $edit_url = remove_query_arg( 'classic-editor', $edit_url ); - // Forget the previous value when going to a specific editor. - $edit_url = add_query_arg( 'classic-editor__forget', '', $edit_url ); - - ?> - <p style="margin: 1em 0;"> - <a href="<?php echo esc_url( $edit_url ); ?>"><?php _e( 'Switch to block editor', 'classic-editor' ); ?></a> - </p> - <?php - } - - public static function enqueue_block_editor_scripts() { - // get_enabled_editors_for_post() needs a WP_Post or post_ID. - if ( empty( $GLOBALS['post'] ) ) { - return; - } - - $editors = self::get_enabled_editors_for_post( $GLOBALS['post'] ); - - if ( ! $editors['classic_editor'] ) { - // Editor cannot be switched. - return; - } - - wp_enqueue_script( - 'classic-editor-plugin', - plugins_url( 'js/block-editor-plugin.js', __FILE__ ), - array( 'wp-element', 'wp-components', 'lodash' ), - '1.4', - true - ); - - wp_localize_script( - 'classic-editor-plugin', - 'classicEditorPluginL10n', - array( 'linkText' => __( 'Switch to classic editor', 'classic-editor' ) ) - ); - } - - /** - * Add a link to the settings on the Plugins screen. - */ - public static function add_settings_link( $links, $file ) { - $settings = self::get_settings(); - - if ( $file === 'classic-editor/classic-editor.php' && ! $settings['hide-settings-ui'] && current_user_can( 'manage_options' ) ) { - if ( current_filter() === 'plugin_action_links' ) { - $url = admin_url( 'options-writing.php#classic-editor-options' ); - } else { - $url = admin_url( '/network/settings.php#classic-editor-options' ); - } - - // Prevent warnings in PHP 7.0+ when a plugin uses this filter incorrectly. - $links = (array) $links; - $links[] = sprintf( '<a href="%s">%s</a>', $url, __( 'Settings', 'classic-editor' ) ); - } - - return $links; - } - - private static function can_edit_post_type( $post_type ) { - $can_edit = false; - - if ( function_exists( 'gutenberg_can_edit_post_type' ) ) { - $can_edit = gutenberg_can_edit_post_type( $post_type ); - } elseif ( function_exists( 'use_block_editor_for_post_type' ) ) { - $can_edit = use_block_editor_for_post_type( $post_type ); - } - - return $can_edit; - } - - /** - * Checks which editors are enabled for the post type. - * - * @param string $post_type The post type. - * @return array Associative array of the editors and whether they are enabled for the post type. - */ - private static function get_enabled_editors_for_post_type( $post_type ) { - if ( isset( self::$supported_post_types[ $post_type ] ) ) { - return self::$supported_post_types[ $post_type ]; - } - - $classic_editor = post_type_supports( $post_type, 'editor' ); - $block_editor = self::can_edit_post_type( $post_type ); - - $editors = array( - 'classic_editor' => $classic_editor, - 'block_editor' => $block_editor, - ); - - /** - * Filters the editors that are enabled for the post type. - * - * @param array $editors Associative array of the editors and whether they are enabled for the post type. - * @param string $post_type The post type. - */ - $editors = apply_filters( 'classic_editor_enabled_editors_for_post_type', $editors, $post_type ); - self::$supported_post_types[ $post_type ] = $editors; - - return $editors; - } - - /** - * Checks which editors are enabled for the post. - * - * @param WP_Post $post The post object. - * @return array Associative array of the editors and whether they are enabled for the post. - */ - private static function get_enabled_editors_for_post( $post ) { - $post_type = get_post_type( $post ); - - if ( ! $post_type ) { - return array( - 'classic_editor' => false, - 'block_editor' => false, - ); - } - - $editors = self::get_enabled_editors_for_post_type( $post_type ); - - /** - * Filters the editors that are enabled for the post. - * - * @param array $editors Associative array of the editors and whether they are enabled for the post. - * @param WP_Post $post The post object. - */ - return apply_filters( 'classic_editor_enabled_editors_for_post', $editors, $post ); - } - - /** - * Adds links to the post/page screens to edit any post or page in - * the classic editor or block editor. - * - * @param array $actions Post actions. - * @param WP_Post $post Edited post. - * @return array Updated post actions. - */ - public static function add_edit_links( $actions, $post ) { - // This is in Gutenberg, don't duplicate it. - if ( array_key_exists( 'classic', $actions ) ) { - unset( $actions['classic'] ); - } - - if ( ! array_key_exists( 'edit', $actions ) ) { - return $actions; - } - - $edit_url = get_edit_post_link( $post->ID, 'raw' ); - - if ( ! $edit_url ) { - return $actions; - } - - $editors = self::get_enabled_editors_for_post( $post ); - - // Do not show the links if only one editor is available. - if ( ! $editors['classic_editor'] || ! $editors['block_editor'] ) { - return $actions; - } - - // Forget the previous value when going to a specific editor. - $edit_url = add_query_arg( 'classic-editor__forget', '', $edit_url ); - - // Build the edit actions. See also: WP_Posts_List_Table::handle_row_actions(). - $title = _draft_or_post_title( $post->ID ); - - // Link to the block editor. - $url = remove_query_arg( 'classic-editor', $edit_url ); - $text = _x( 'Edit (block editor)', 'Editor Name', 'classic-editor' ); - /* translators: %s: post title */ - $label = sprintf( __( 'Edit “%s” in the block editor', 'classic-editor' ), $title ); - $edit_block = sprintf( '<a href="%s" aria-label="%s">%s</a>', esc_url( $url ), esc_attr( $label ), $text ); - - // Link to the classic editor. - $url = add_query_arg( 'classic-editor', '', $edit_url ); - $text = _x( 'Edit (classic editor)', 'Editor Name', 'classic-editor' ); - /* translators: %s: post title */ - $label = sprintf( __( 'Edit “%s” in the classic editor', 'classic-editor' ), $title ); - $edit_classic = sprintf( '<a href="%s" aria-label="%s">%s</a>', esc_url( $url ), esc_attr( $label ), $text ); - - $edit_actions = array( - 'classic-editor-block' => $edit_block, - 'classic-editor-classic' => $edit_classic, - ); - - // Insert the new Edit actions instead of the Edit action. - $edit_offset = array_search( 'edit', array_keys( $actions ), true ); - array_splice( $actions, $edit_offset, 1, $edit_actions ); - - return $actions; - } - - /** - * Show the editor that will be used in a "post state" in the Posts list table. - */ - public static function add_post_state( $post_states, $post ) { - if ( get_post_status( $post ) === 'trash' ) { - return $post_states; - } - - $editors = self::get_enabled_editors_for_post( $post ); - - if ( ! $editors['classic_editor'] && ! $editors['block_editor'] ) { - return $post_states; - } elseif ( $editors['classic_editor'] && ! $editors['block_editor'] ) { - // Forced to classic editor. - $state = '<span class="classic-editor-forced-state">' . _x( 'classic editor', 'Editor Name', 'classic-editor' ) . '</span>'; - } elseif ( ! $editors['classic_editor'] && $editors['block_editor'] ) { - // Forced to block editor. - $state = '<span class="classic-editor-forced-state">' . _x( 'block editor', 'Editor Name', 'classic-editor' ) . '</span>'; - } else { - $last_editor = get_post_meta( $post->ID, 'classic-editor-remember', true ); - - if ( $last_editor ) { - $is_classic = ( $last_editor === 'classic-editor' ); - } elseif ( ! empty( $post->post_content ) ) { - $is_classic = ! self::has_blocks( $post->post_content ); - } else { - $settings = self::get_settings(); - $is_classic = ( $settings['editor'] === 'classic' ); - } - - $state = $is_classic ? _x( 'Classic editor', 'Editor Name', 'classic-editor' ) : _x( 'Block editor', 'Editor Name', 'classic-editor' ); - } - - // Fix PHP 7+ warnings if another plugin returns unexpected type. - $post_states = (array) $post_states; - $post_states['classic-editor-plugin'] = $state; - - return $post_states; - } - - public static function add_edit_php_inline_style() { - ?> - <style> - .classic-editor-forced-state { - font-style: italic; - font-weight: 400; - color: #72777c; - font-size: small; - } - </style> - <?php - } - - public static function on_admin_init() { - global $pagenow; - - if ( $pagenow !== 'post.php' ) { - return; - } - - $settings = self::get_settings(); - $post_id = self::get_edited_post_id(); - - if ( $post_id && ( $settings['editor'] === 'classic' || self::is_classic( $post_id ) ) ) { - // Move the Privacy Policy help notice back under the title field. - remove_action( 'admin_notices', array( 'WP_Privacy_Policy_Content', 'notice' ) ); - add_action( 'edit_form_after_title', array( 'WP_Privacy_Policy_Content', 'notice' ) ); - } - } - - // Need to support WP < 5.0 - private static function has_blocks( $post = null ) { - if ( ! is_string( $post ) ) { - $wp_post = get_post( $post ); - - if ( $wp_post instanceof WP_Post ) { - $post = $wp_post->post_content; - } - } - - return false !== strpos( (string) $post, '<!-- wp:' ); - } - - /** - * Set defaults on activation. - */ - public static function activate() { - register_uninstall_hook( __FILE__, array( __CLASS__, 'uninstall' ) ); - - if ( is_multisite() ) { - add_network_option( null, 'classic-editor-replace', 'classic' ); - add_network_option( null, 'classic-editor-allow-sites', 'disallow' ); - } - - add_option( 'classic-editor-replace', 'classic' ); - add_option( 'classic-editor-allow-users', 'disallow' ); - } - - /** - * Delete the options on uninstall. - */ - public static function uninstall() { - if ( is_multisite() ) { - delete_network_option( null, 'classic-editor-replace' ); - delete_network_option( null, 'classic-editor-allow-sites' ); - } - - delete_option( 'classic-editor-replace' ); - delete_option( 'classic-editor-allow-users' ); - } - - /** - * Temporary fix for Safari 18 negative horizontal margin on floats. - * See: https://core.trac.wordpress.org/ticket/62082 and - * https://bugs.webkit.org/show_bug.cgi?id=280063. - * TODO: Remove when Safari is fixed. - */ - public static function safari_18_temp_fix() { - global $current_screen; - - if ( isset( $current_screen->base ) && 'post' === $current_screen->base ) { - $clear = is_rtl() ? 'right' : 'left'; - - ?> - <style id="classic-editor-safari-18-temp-fix"> - _::-webkit-full-page-media, _:future, :root #post-body #postbox-container-2 { - clear: <?php echo $clear; ?>; - } - </style> - <?php - } - } -} - -add_action( 'plugins_loaded', array( 'Classic_Editor', 'init_actions' ) ); - -endif; +<?php +/** + * Classic Editor + * + * Plugin Name: Classic Editor + * Plugin URI: https://wordpress.org/plugins/classic-editor/ + * Description: Enables the WordPress classic editor and the old-style Edit Post screen with TinyMCE, Meta Boxes, etc. Supports the older plugins that extend this screen. + * Version: 1.6.7 + * Author: WordPress Contributors + * Author URI: https://github.com/WordPress/classic-editor/ + * License: GPLv2 or later + * License URI: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * Text Domain: classic-editor + * Domain Path: /languages + * Requires at least: 4.9 + * Requires PHP: 5.2.4 + * + * This program is free software; you can redistribute it and/or modify it under the terms of the GNU + * General Public License version 2, as published by the Free Software Foundation. You may NOT assume + * that you can use any other version of the GPL. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +if ( ! defined( 'ABSPATH' ) ) { + die( 'Invalid request.' ); +} + +if ( ! class_exists( 'Classic_Editor' ) ) : +class Classic_Editor { + private static $settings; + private static $supported_post_types = array(); + + private function __construct() {} + + public static function init_actions() { + $block_editor = has_action( 'enqueue_block_assets' ); + $gutenberg = function_exists( 'gutenberg_register_scripts_and_styles' ); + + register_activation_hook( __FILE__, array( __CLASS__, 'activate' ) ); + + $settings = self::get_settings(); + + if ( is_multisite() ) { + add_action( 'wpmu_options', array( __CLASS__, 'network_settings' ) ); + add_action( 'update_wpmu_options', array( __CLASS__, 'save_network_settings' ) ); + } + + if ( ! $settings['hide-settings-ui'] ) { + // Add a link to the plugin's settings and/or network admin settings in the plugins list table. + add_filter( 'plugin_action_links', array( __CLASS__, 'add_settings_link' ), 10, 2 ); + add_filter( 'network_admin_plugin_action_links', array( __CLASS__, 'add_settings_link' ), 10, 2 ); + + add_action( 'admin_init', array( __CLASS__, 'register_settings' ) ); + + if ( $settings['allow-users'] ) { + // User settings. + add_action( 'personal_options_update', array( __CLASS__, 'save_user_settings' ) ); + add_action( 'edit_user_profile_update', array( __CLASS__, 'save_user_settings' ) ); + add_action( 'profile_personal_options', array( __CLASS__, 'user_settings' ) ); + add_action( 'edit_user_profile', array( __CLASS__, 'user_settings') ); + } + } + + // Always remove the "Try Gutenberg" dashboard widget. See https://core.trac.wordpress.org/ticket/44635. + remove_action( 'try_gutenberg_panel', 'wp_try_gutenberg_panel' ); + + // Fix for Safari 18 negative horizontal margin on floats. + add_action( 'admin_print_styles', array( __CLASS__, 'safari_18_temp_fix' ) ); + + // Fix for the Categories postbox on the classic Edit Post screen for WP 6.7.1. + global $wp_version; + + if ( '6.7.1' === $wp_version && is_admin() ) { + add_filter( 'script_loader_src', array( __CLASS__, 'replace_post_js_2' ), 11, 2 ); + } + + if ( ! $block_editor && ! $gutenberg ) { + return; + } + + if ( $settings['allow-users'] ) { + // Also used in Gutenberg. + add_filter( 'use_block_editor_for_post', array( __CLASS__, 'choose_editor' ), 100, 2 ); + + if ( $gutenberg ) { + // Support older Gutenberg versions. + add_filter( 'gutenberg_can_edit_post', array( __CLASS__, 'choose_editor' ), 100, 2 ); + + if ( $settings['editor'] === 'classic' ) { + self::remove_gutenberg_hooks( 'some' ); + } + } + + add_filter( 'get_edit_post_link', array( __CLASS__, 'get_edit_post_link' ) ); + add_filter( 'redirect_post_location', array( __CLASS__, 'redirect_location' ) ); + add_action( 'edit_form_top', array( __CLASS__, 'add_redirect_helper' ) ); + add_action( 'admin_head-edit.php', array( __CLASS__, 'add_edit_php_inline_style' ) ); + + add_action( 'edit_form_top', array( __CLASS__, 'remember_classic_editor' ) ); + + if ( version_compare( $GLOBALS['wp_version'], '5.8', '>=' ) ) { + add_filter( 'block_editor_settings_all', array( __CLASS__, 'remember_block_editor' ), 10, 2 ); + } else { + add_filter( 'block_editor_settings', array( __CLASS__, 'remember_block_editor' ), 10, 2 ); + } + + // Post state (edit.php) + add_filter( 'display_post_states', array( __CLASS__, 'add_post_state' ), 10, 2 ); + // Row actions (edit.php) + add_filter( 'page_row_actions', array( __CLASS__, 'add_edit_links' ), 15, 2 ); + add_filter( 'post_row_actions', array( __CLASS__, 'add_edit_links' ), 15, 2 ); + + // Switch editors while editing a post + add_action( 'add_meta_boxes', array( __CLASS__, 'add_meta_box' ), 10, 2 ); + add_action( 'enqueue_block_editor_assets', array( __CLASS__, 'enqueue_block_editor_scripts' ) ); + } else { + if ( $settings['editor'] === 'classic' ) { + // Also used in Gutenberg. + // Consider disabling other Block Editor functionality. + add_filter( 'use_block_editor_for_post_type', '__return_false', 100 ); + + if ( $gutenberg ) { + // Support older Gutenberg versions. + add_filter( 'gutenberg_can_edit_post_type', '__return_false', 100 ); + self::remove_gutenberg_hooks(); + } + } else { + // $settings['editor'] === 'block', nothing to do :) + return; + } + } + + if ( $block_editor ) { + // Move the Privacy Page notice back under the title. + add_action( 'admin_init', array( __CLASS__, 'on_admin_init' ) ); + } + if ( $gutenberg ) { + // These are handled by this plugin. All are older, not used in 5.3+. + remove_action( 'admin_init', 'gutenberg_add_edit_link_filters' ); + remove_action( 'admin_print_scripts-edit.php', 'gutenberg_replace_default_add_new_button' ); + remove_filter( 'redirect_post_location', 'gutenberg_redirect_to_classic_editor_when_saving_posts' ); + remove_filter( 'display_post_states', 'gutenberg_add_gutenberg_post_state' ); + remove_action( 'edit_form_top', 'gutenberg_remember_classic_editor_when_saving_posts' ); + } + } + + public static function remove_gutenberg_hooks( $remove = 'all' ) { + remove_action( 'admin_menu', 'gutenberg_menu' ); + remove_action( 'admin_init', 'gutenberg_redirect_demo' ); + + if ( $remove !== 'all' ) { + return; + } + + // Gutenberg 5.3+ + remove_action( 'wp_enqueue_scripts', 'gutenberg_register_scripts_and_styles' ); + remove_action( 'admin_enqueue_scripts', 'gutenberg_register_scripts_and_styles' ); + remove_action( 'admin_notices', 'gutenberg_wordpress_version_notice' ); + remove_action( 'rest_api_init', 'gutenberg_register_rest_widget_updater_routes' ); + remove_action( 'admin_print_styles', 'gutenberg_block_editor_admin_print_styles' ); + remove_action( 'admin_print_scripts', 'gutenberg_block_editor_admin_print_scripts' ); + remove_action( 'admin_print_footer_scripts', 'gutenberg_block_editor_admin_print_footer_scripts' ); + remove_action( 'admin_footer', 'gutenberg_block_editor_admin_footer' ); + remove_action( 'admin_enqueue_scripts', 'gutenberg_widgets_init' ); + remove_action( 'admin_notices', 'gutenberg_build_files_notice' ); + + remove_filter( 'load_script_translation_file', 'gutenberg_override_translation_file' ); + remove_filter( 'block_editor_settings', 'gutenberg_extend_block_editor_styles' ); + remove_filter( 'default_content', 'gutenberg_default_demo_content' ); + remove_filter( 'default_title', 'gutenberg_default_demo_title' ); + remove_filter( 'block_editor_settings', 'gutenberg_legacy_widget_settings' ); + remove_filter( 'rest_request_after_callbacks', 'gutenberg_filter_oembed_result' ); + + // Previously used, compat for older Gutenberg versions. + remove_filter( 'wp_refresh_nonces', 'gutenberg_add_rest_nonce_to_heartbeat_response_headers' ); + remove_filter( 'get_edit_post_link', 'gutenberg_revisions_link_to_editor' ); + remove_filter( 'wp_prepare_revision_for_js', 'gutenberg_revisions_restore' ); + + remove_action( 'rest_api_init', 'gutenberg_register_rest_routes' ); + remove_action( 'rest_api_init', 'gutenberg_add_taxonomy_visibility_field' ); + remove_filter( 'registered_post_type', 'gutenberg_register_post_prepare_functions' ); + + remove_action( 'do_meta_boxes', 'gutenberg_meta_box_save' ); + remove_action( 'submitpost_box', 'gutenberg_intercept_meta_box_render' ); + remove_action( 'submitpage_box', 'gutenberg_intercept_meta_box_render' ); + remove_action( 'edit_page_form', 'gutenberg_intercept_meta_box_render' ); + remove_action( 'edit_form_advanced', 'gutenberg_intercept_meta_box_render' ); + remove_filter( 'redirect_post_location', 'gutenberg_meta_box_save_redirect' ); + remove_filter( 'filter_gutenberg_meta_boxes', 'gutenberg_filter_meta_boxes' ); + + remove_filter( 'body_class', 'gutenberg_add_responsive_body_class' ); + remove_filter( 'admin_url', 'gutenberg_modify_add_new_button_url' ); // old + remove_action( 'admin_enqueue_scripts', 'gutenberg_check_if_classic_needs_warning_about_blocks' ); + remove_filter( 'register_post_type_args', 'gutenberg_filter_post_type_labels' ); + + // phpcs:disable Squiz.PHP.CommentedOutCode.Found + // Keep + // remove_filter( 'wp_kses_allowed_html', 'gutenberg_kses_allowedtags', 10, 2 ); // not needed in 5.0 + // remove_filter( 'bulk_actions-edit-wp_block', 'gutenberg_block_bulk_actions' ); + // remove_filter( 'wp_insert_post_data', 'gutenberg_remove_wpcom_markdown_support' ); + // remove_filter( 'the_content', 'do_blocks', 9 ); + // remove_action( 'init', 'gutenberg_register_post_types' ); + + // Continue to manage wpautop for posts that were edited in Gutenberg. + // remove_filter( 'wp_editor_settings', 'gutenberg_disable_editor_settings_wpautop' ); + // remove_filter( 'the_content', 'gutenberg_wpautop', 8 ); + // phpcs:enable Squiz.PHP.CommentedOutCode.Found + + } + + private static function get_settings( $refresh = 'no', $user_id = 0 ) { + /** + * Can be used to override the plugin's settings. Always hides the settings UI when used (as users cannot change the settings). + * + * Has to return an associative array with two keys. + * The defaults are: + * 'editor' => 'classic', // Accepted values: 'classic', 'block'. + * 'allow-users' => false, + * + * @param boolean To override the settings return an array with the above keys. Default false. + */ + $settings = apply_filters( 'classic_editor_plugin_settings', false ); + + if ( is_array( $settings ) ) { + return array( + 'editor' => ( isset( $settings['editor'] ) && $settings['editor'] === 'block' ) ? 'block' : 'classic', + 'allow-users' => ! empty( $settings['allow-users'] ), + 'hide-settings-ui' => true, + ); + } + + if ( ! empty( self::$settings ) && $refresh === 'no' ) { + return self::$settings; + } + + if ( is_multisite() ) { + $defaults = array( + 'editor' => get_network_option( null, 'classic-editor-replace' ) === 'block' ? 'block' : 'classic', + 'allow-users' => false, + ); + + /** + * Filters the default network options. + * + * @param array $defaults The default options array. See `classic_editor_plugin_settings` for supported keys and values. + */ + $defaults = apply_filters( 'classic_editor_network_default_settings', $defaults ); + + if ( get_network_option( null, 'classic-editor-allow-sites' ) !== 'allow' ) { + // Per-site settings are disabled. Return default network options nad hide the settings UI. + $defaults['hide-settings-ui'] = true; + return $defaults; + } + + // Override with the site options. + $editor_option = get_option( 'classic-editor-replace' ); + $allow_users_option = get_option( 'classic-editor-allow-users' ); + + if ( $editor_option ) { + $defaults['editor'] = $editor_option; + } + if ( $allow_users_option ) { + $defaults['allow-users'] = ( $allow_users_option === 'allow' ); + } + + $editor = ( isset( $defaults['editor'] ) && $defaults['editor'] === 'block' ) ? 'block' : 'classic'; + $allow_users = ! empty( $defaults['allow-users'] ); + } else { + $allow_users = ( get_option( 'classic-editor-allow-users' ) === 'allow' ); + $option = get_option( 'classic-editor-replace' ); + + // Normalize old options. + if ( $option === 'block' || $option === 'no-replace' ) { + $editor = 'block'; + } else { + // empty( $option ) || $option === 'classic' || $option === 'replace' + $editor = 'classic'; + } + } + + // Override the defaults with the user options. + if ( ( ! isset( $GLOBALS['pagenow'] ) || $GLOBALS['pagenow'] !== 'options-writing.php' ) && $allow_users ) { + + $user_options = get_user_option( 'classic-editor-settings', $user_id ); + + if ( $user_options === 'block' || $user_options === 'classic' ) { + $editor = $user_options; + } + } + + self::$settings = array( + 'editor' => $editor, + 'hide-settings-ui' => false, + 'allow-users' => $allow_users, + ); + + return self::$settings; + } + + private static function is_classic( $post_id = 0 ) { + if ( ! $post_id ) { + $post_id = self::get_edited_post_id(); + } + + if ( $post_id ) { + $settings = self::get_settings(); + + if ( $settings['allow-users'] && ! isset( $_GET['classic-editor__forget'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $which = get_post_meta( $post_id, 'classic-editor-remember', true ); + + if ( $which ) { + // The editor choice will be "remembered" when the post is opened in either the classic or the block editor. + if ( 'classic-editor' === $which ) { + return true; + } elseif ( 'block-editor' === $which ) { + return false; + } + } + + return ( ! self::has_blocks( $post_id ) ); + } + } + + if ( isset( $_GET['classic-editor'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended + return true; + } + + return false; + } + + /** + * Get the edited post ID (early) when loading the Edit Post screen. + */ + private static function get_edited_post_id() { + // phpcs:disable WordPress.Security.NonceVerification.Recommended + if ( + ! empty( $_GET['post'] ) && + ! empty( $_GET['action'] ) && + $_GET['action'] === 'edit' && + ! empty( $GLOBALS['pagenow'] ) && + $GLOBALS['pagenow'] === 'post.php' + ) { + return (int) $_GET['post']; // post_ID + } + // phpcs:enable WordPress.Security.NonceVerification.Recommended + + return 0; + } + + public static function register_settings() { + // Add an option to Settings -> Writing + register_setting( 'writing', 'classic-editor-replace', array( + 'sanitize_callback' => array( __CLASS__, 'validate_option_editor' ), + ) ); + + register_setting( 'writing', 'classic-editor-allow-users', array( + 'sanitize_callback' => array( __CLASS__, 'validate_option_allow_users' ), + ) ); + + $allowed_options = array( + 'writing' => array( + 'classic-editor-replace', + 'classic-editor-allow-users' + ), + ); + + if ( function_exists( 'add_allowed_options' ) ) { + add_allowed_options( $allowed_options ); + } else { + add_option_whitelist( $allowed_options ); + } + + $heading_1 = __( 'Default editor for all users', 'classic-editor' ); + $heading_2 = __( 'Allow users to switch editors', 'classic-editor' ); + + add_settings_field( 'classic-editor-1', $heading_1, array( __CLASS__, 'settings_1' ), 'writing' ); + add_settings_field( 'classic-editor-2', $heading_2, array( __CLASS__, 'settings_2' ), 'writing' ); + } + + public static function save_user_settings( $user_id ) { + if ( + isset( $_POST['classic-editor-user-settings'] ) && + isset( $_POST['classic-editor-replace'] ) && + wp_verify_nonce( $_POST['classic-editor-user-settings'], 'allow-user-settings' ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + ) { + $user_id = (int) $user_id; + + if ( $user_id !== get_current_user_id() && ! current_user_can( 'edit_user', $user_id ) ) { + return; + } + + $editor = self::validate_option_editor( $_POST['classic-editor-replace'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + update_user_option( $user_id, 'classic-editor-settings', $editor ); + } + } + + /** + * Validate + */ + public static function validate_option_editor( $value ) { + if ( $value === 'block' ) { + return 'block'; + } + + return 'classic'; + } + + public static function validate_option_allow_users( $value ) { + if ( $value === 'allow' ) { + return 'allow'; + } + + return 'disallow'; + } + + public static function settings_1( $user_id = 0 ) { + $settings = self::get_settings( 'refresh', $user_id ); + + ?> + <div class="classic-editor-options"> + <p> + <input type="radio" name="classic-editor-replace" id="classic-editor-classic" value="classic"<?php if ( $settings['editor'] === 'classic' ) echo ' checked'; ?> /> + <label for="classic-editor-classic"><?php _ex( 'Classic editor', 'Editor Name', 'classic-editor' ); ?></label> + </p> + <p> + <input type="radio" name="classic-editor-replace" id="classic-editor-block" value="block"<?php if ( $settings['editor'] !== 'classic' ) echo ' checked'; ?> /> + <label for="classic-editor-block"><?php _ex( 'Block editor', 'Editor Name', 'classic-editor' ); ?></label> + </p> + </div> + <script> + jQuery( 'document' ).ready( function( $ ) { + if ( window.location.hash === '#classic-editor-options' ) { + $( '.classic-editor-options' ).closest( 'td' ).addClass( 'highlight' ); + } + } ); + </script> + <?php + } + + public static function settings_2() { + $settings = self::get_settings( 'refresh' ); + + ?> + <div class="classic-editor-options"> + <p> + <input type="radio" name="classic-editor-allow-users" id="classic-editor-allow" value="allow"<?php if ( $settings['allow-users'] ) echo ' checked'; ?> /> + <label for="classic-editor-allow"><?php _e( 'Yes', 'classic-editor' ); ?></label> + </p> + <p> + <input type="radio" name="classic-editor-allow-users" id="classic-editor-disallow" value="disallow"<?php if ( ! $settings['allow-users'] ) echo ' checked'; ?> /> + <label for="classic-editor-disallow"><?php _e( 'No', 'classic-editor' ); ?></label> + </p> + </div> + <?php + } + + /** + * Shown on the Profile page when allowed by admin. + */ + public static function user_settings( $user = null ) { + global $user_can_edit; + $settings = self::get_settings( 'update' ); + + if ( ! $user_can_edit || ! $settings['allow-users'] ) { + return; + } + + if ( $user instanceof WP_User ) { + $user_id = (int) $user->ID; + } else { + $user_id = 0; + } + + ?> + <table class="form-table"> + <tr class="classic-editor-user-options"> + <th scope="row"><?php _e( 'Default Editor', 'classic-editor' ); ?></th> + <td> + <?php wp_nonce_field( 'allow-user-settings', 'classic-editor-user-settings' ); ?> + <?php self::settings_1( $user_id ); ?> + </td> + </tr> + </table> + <script>jQuery( 'tr.user-rich-editing-wrap' ).before( jQuery( 'tr.classic-editor-user-options' ) );</script> + <?php + } + + public static function network_settings() { + $editor = get_network_option( null, 'classic-editor-replace' ); + $is_checked = ( get_network_option( null, 'classic-editor-allow-sites' ) === 'allow' ); + + ?> + <h2 id="classic-editor-options"><?php _e( 'Editor Settings', 'classic-editor' ); ?></h2> + <table class="form-table"> + <?php wp_nonce_field( 'allow-site-admin-settings', 'classic-editor-network-settings' ); ?> + <tr> + <th scope="row"><?php _e( 'Default editor for all sites', 'classic-editor' ); ?></th> + <td> + <p> + <input type="radio" name="classic-editor-replace" id="classic-editor-classic" value="classic"<?php if ( $editor !== 'block' ) echo ' checked'; ?> /> + <label for="classic-editor-classic"><?php _ex( 'Classic Editor', 'Editor Name', 'classic-editor' ); ?></label> + </p> + <p> + <input type="radio" name="classic-editor-replace" id="classic-editor-block" value="block"<?php if ( $editor === 'block' ) echo ' checked'; ?> /> + <label for="classic-editor-block"><?php _ex( 'Block editor', 'Editor Name', 'classic-editor' ); ?></label> + </p> + </td> + </tr> + <tr> + <th scope="row"><?php _e( 'Change settings', 'classic-editor' ); ?></th> + <td> + <input type="checkbox" name="classic-editor-allow-sites" id="classic-editor-allow-sites" value="allow"<?php if ( $is_checked ) echo ' checked'; ?>> + <label for="classic-editor-allow-sites"><?php _e( 'Allow site admins to change settings', 'classic-editor' ); ?></label> + <p class="description"><?php _e( 'By default the block editor is replaced with the classic editor and users cannot switch editors.', 'classic-editor' ); ?></p> + </td> + </tr> + </table> + <?php + } + + public static function save_network_settings() { + if ( + isset( $_POST['classic-editor-network-settings'] ) && + current_user_can( 'manage_network_options' ) && + wp_verify_nonce( $_POST['classic-editor-network-settings'], 'allow-site-admin-settings' ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + ) { + if ( isset( $_POST['classic-editor-replace'] ) && $_POST['classic-editor-replace'] === 'block' ) { + update_network_option( null, 'classic-editor-replace', 'block' ); + } else { + update_network_option( null, 'classic-editor-replace', 'classic' ); + } + if ( isset( $_POST['classic-editor-allow-sites'] ) && $_POST['classic-editor-allow-sites'] === 'allow' ) { + update_network_option( null, 'classic-editor-allow-sites', 'allow' ); + } else { + update_network_option( null, 'classic-editor-allow-sites', 'disallow' ); + } + } + } + + /** + * Add a hidden field in edit-form-advanced.php + * to help redirect back to the classic editor on saving. + */ + public static function add_redirect_helper() { + ?> + <input type="hidden" name="classic-editor" value="" /> + <?php + } + + /** + * Remember when the classic editor was used to edit a post. + */ + public static function remember_classic_editor( $post ) { + $post_type = get_post_type( $post ); + + if ( $post_type && post_type_supports( $post_type, 'editor' ) ) { + self::remember( $post->ID, 'classic-editor' ); + } + } + + /** + * Remember when the block editor was used to edit a post. + */ + public static function remember_block_editor( $editor_settings, $context ) { + if ( is_a( $context, 'WP_Post' ) ) { + $post = $context; + } elseif ( ! empty( $context->post ) ) { + $post = $context->post; + } else { + return $editor_settings; + } + + $post_type = get_post_type( $post ); + + if ( $post_type && self::can_edit_post_type( $post_type ) ) { + self::remember( $post->ID, 'block-editor' ); + } + + return $editor_settings; + } + + private static function remember( $post_id, $editor ) { + if ( get_post_meta( $post_id, 'classic-editor-remember', true ) !== $editor ) { + update_post_meta( $post_id, 'classic-editor-remember', $editor ); + } + } + + /** + * Choose which editor to use for a post. + * + * Passes through `$which_editor` for block editor (it's sets to `true` but may be changed by another plugin). + * + * @uses `use_block_editor_for_post` filter. + * + * @param boolean $use_block_editor True for block editor, false for classic editor. + * @param WP_Post $post The post being edited. + * @return boolean True for block editor, false for classic editor. + */ + public static function choose_editor( $use_block_editor, $post ) { + $settings = self::get_settings(); + $editors = self::get_enabled_editors_for_post( $post ); + + // If no editor is supported, pass through `$use_block_editor`. + if ( ! $editors['block_editor'] && ! $editors['classic_editor'] ) { + return $use_block_editor; + } + + // Open the default editor when no $post and for "Add New" links, + // or the alternate editor when the user is switching editors. + // phpcs:disable WordPress.Security.NonceVerification.Recommended + if ( empty( $post->ID ) || $post->post_status === 'auto-draft' ) { + if ( + ( $settings['editor'] === 'classic' && ! isset( $_GET['classic-editor__forget'] ) ) || // Add New + ( isset( $_GET['classic-editor'] ) && isset( $_GET['classic-editor__forget'] ) ) // Switch to classic editor when no draft post. + ) { + $use_block_editor = false; + } + } elseif ( self::is_classic( $post->ID ) ) { + $use_block_editor = false; + } + // phpcs:enable WordPress.Security.NonceVerification.Recommended + + // Enforce the editor if set by plugins. + if ( $use_block_editor && ! $editors['block_editor'] ) { + $use_block_editor = false; + } elseif ( ! $use_block_editor && ! $editors['classic_editor'] && $editors['block_editor'] ) { + $use_block_editor = true; + } + + return $use_block_editor; + } + + /** + * Keep the `classic-editor` query arg through redirects when saving posts. + */ + public static function redirect_location( $location ) { + if ( + isset( $_REQUEST['classic-editor'] ) || // phpcs:ignore WordPress.Security.NonceVerification.Recommended + ( isset( $_POST['_wp_http_referer'] ) && strpos( $_POST['_wp_http_referer'], '&classic-editor' ) !== false ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing + ) { + $location = add_query_arg( 'classic-editor', '', $location ); + } + + return $location; + } + + /** + * Keep the `classic-editor` query arg when looking at revisions. + */ + public static function get_edit_post_link( $url ) { + $settings = self::get_settings(); + + if ( isset( $_REQUEST['classic-editor'] ) || $settings['editor'] === 'classic' ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended + $url = add_query_arg( 'classic-editor', '', $url ); + } + + return $url; + } + + public static function add_meta_box( $post_type, $post ) { + $editors = self::get_enabled_editors_for_post( $post ); + + if ( ! $editors['block_editor'] || ! $editors['classic_editor'] ) { + // Editors cannot be switched. + return; + } + + $id = 'classic-editor-switch-editor'; + $title = __( 'Editor', 'classic-editor' ); + $callback = array( __CLASS__, 'do_meta_box' ); + $args = array( + '__back_compat_meta_box' => true, + ); + + add_meta_box( $id, $title, $callback, null, 'side', 'default', $args ); + } + + public static function do_meta_box( $post ) { + $edit_url = get_edit_post_link( $post->ID, 'raw' ); + + // Switching to block editor. + $edit_url = remove_query_arg( 'classic-editor', $edit_url ); + // Forget the previous value when going to a specific editor. + $edit_url = add_query_arg( 'classic-editor__forget', '', $edit_url ); + + ?> + <p style="margin: 1em 0;"> + <a href="<?php echo esc_url( $edit_url ); ?>"><?php _e( 'Switch to block editor', 'classic-editor' ); ?></a> + </p> + <?php + } + + public static function enqueue_block_editor_scripts() { + // get_enabled_editors_for_post() needs a WP_Post or post_ID. + if ( empty( $GLOBALS['post'] ) ) { + return; + } + + $editors = self::get_enabled_editors_for_post( $GLOBALS['post'] ); + + if ( ! $editors['classic_editor'] ) { + // Editor cannot be switched. + return; + } + + wp_enqueue_script( + 'classic-editor-plugin', + plugins_url( 'js/block-editor-plugin.js', __FILE__ ), + array( 'wp-element', 'wp-components', 'lodash' ), + '1.4', + true + ); + + wp_localize_script( + 'classic-editor-plugin', + 'classicEditorPluginL10n', + array( 'linkText' => __( 'Switch to classic editor', 'classic-editor' ) ) + ); + } + + /** + * Add a link to the settings on the Plugins screen. + */ + public static function add_settings_link( $links, $file ) { + $settings = self::get_settings(); + + if ( $file === 'classic-editor/classic-editor.php' && ! $settings['hide-settings-ui'] && current_user_can( 'manage_options' ) ) { + if ( current_filter() === 'plugin_action_links' ) { + $url = admin_url( 'options-writing.php#classic-editor-options' ); + } else { + $url = admin_url( '/network/settings.php#classic-editor-options' ); + } + + // Prevent warnings in PHP 7.0+ when a plugin uses this filter incorrectly. + $links = (array) $links; + $links[] = sprintf( '<a href="%s">%s</a>', $url, __( 'Settings', 'classic-editor' ) ); + } + + return $links; + } + + private static function can_edit_post_type( $post_type ) { + $can_edit = false; + + if ( function_exists( 'gutenberg_can_edit_post_type' ) ) { + $can_edit = gutenberg_can_edit_post_type( $post_type ); + } elseif ( function_exists( 'use_block_editor_for_post_type' ) ) { + $can_edit = use_block_editor_for_post_type( $post_type ); + } + + return $can_edit; + } + + /** + * Checks which editors are enabled for the post type. + * + * @param string $post_type The post type. + * @return array Associative array of the editors and whether they are enabled for the post type. + */ + private static function get_enabled_editors_for_post_type( $post_type ) { + if ( isset( self::$supported_post_types[ $post_type ] ) ) { + return self::$supported_post_types[ $post_type ]; + } + + $classic_editor = post_type_supports( $post_type, 'editor' ); + $block_editor = self::can_edit_post_type( $post_type ); + + $editors = array( + 'classic_editor' => $classic_editor, + 'block_editor' => $block_editor, + ); + + /** + * Filters the editors that are enabled for the post type. + * + * @param array $editors Associative array of the editors and whether they are enabled for the post type. + * @param string $post_type The post type. + */ + $editors = apply_filters( 'classic_editor_enabled_editors_for_post_type', $editors, $post_type ); + self::$supported_post_types[ $post_type ] = $editors; + + return $editors; + } + + /** + * Checks which editors are enabled for the post. + * + * @param WP_Post $post The post object. + * @return array Associative array of the editors and whether they are enabled for the post. + */ + private static function get_enabled_editors_for_post( $post ) { + $post_type = get_post_type( $post ); + + if ( ! $post_type ) { + return array( + 'classic_editor' => false, + 'block_editor' => false, + ); + } + + $editors = self::get_enabled_editors_for_post_type( $post_type ); + + /** + * Filters the editors that are enabled for the post. + * + * @param array $editors Associative array of the editors and whether they are enabled for the post. + * @param WP_Post $post The post object. + */ + return apply_filters( 'classic_editor_enabled_editors_for_post', $editors, $post ); + } + + /** + * Adds links to the post/page screens to edit any post or page in + * the classic editor or block editor. + * + * @param array $actions Post actions. + * @param WP_Post $post Edited post. + * @return array Updated post actions. + */ + public static function add_edit_links( $actions, $post ) { + // This is in Gutenberg, don't duplicate it. + if ( array_key_exists( 'classic', $actions ) ) { + unset( $actions['classic'] ); + } + + if ( ! array_key_exists( 'edit', $actions ) ) { + return $actions; + } + + $edit_url = get_edit_post_link( $post->ID, 'raw' ); + + if ( ! $edit_url ) { + return $actions; + } + + $editors = self::get_enabled_editors_for_post( $post ); + + // Do not show the links if only one editor is available. + if ( ! $editors['classic_editor'] || ! $editors['block_editor'] ) { + return $actions; + } + + // Forget the previous value when going to a specific editor. + $edit_url = add_query_arg( 'classic-editor__forget', '', $edit_url ); + + // Build the edit actions. See also: WP_Posts_List_Table::handle_row_actions(). + $title = _draft_or_post_title( $post->ID ); + + // Link to the block editor. + $url = remove_query_arg( 'classic-editor', $edit_url ); + $text = _x( 'Edit (block editor)', 'Editor Name', 'classic-editor' ); + /* translators: %s: post title */ + $label = sprintf( __( 'Edit “%s” in the block editor', 'classic-editor' ), $title ); + $edit_block = sprintf( '<a href="%s" aria-label="%s">%s</a>', esc_url( $url ), esc_attr( $label ), $text ); + + // Link to the classic editor. + $url = add_query_arg( 'classic-editor', '', $edit_url ); + $text = _x( 'Edit (classic editor)', 'Editor Name', 'classic-editor' ); + /* translators: %s: post title */ + $label = sprintf( __( 'Edit “%s” in the classic editor', 'classic-editor' ), $title ); + $edit_classic = sprintf( '<a href="%s" aria-label="%s">%s</a>', esc_url( $url ), esc_attr( $label ), $text ); + + $edit_actions = array( + 'classic-editor-block' => $edit_block, + 'classic-editor-classic' => $edit_classic, + ); + + // Insert the new Edit actions instead of the Edit action. + $edit_offset = array_search( 'edit', array_keys( $actions ), true ); + array_splice( $actions, $edit_offset, 1, $edit_actions ); + + return $actions; + } + + /** + * Show the editor that will be used in a "post state" in the Posts list table. + */ + public static function add_post_state( $post_states, $post ) { + if ( get_post_status( $post ) === 'trash' ) { + return $post_states; + } + + $editors = self::get_enabled_editors_for_post( $post ); + + if ( ! $editors['classic_editor'] && ! $editors['block_editor'] ) { + return $post_states; + } elseif ( $editors['classic_editor'] && ! $editors['block_editor'] ) { + // Forced to classic editor. + $state = '<span class="classic-editor-forced-state">' . _x( 'classic editor', 'Editor Name', 'classic-editor' ) . '</span>'; + } elseif ( ! $editors['classic_editor'] && $editors['block_editor'] ) { + // Forced to block editor. + $state = '<span class="classic-editor-forced-state">' . _x( 'block editor', 'Editor Name', 'classic-editor' ) . '</span>'; + } else { + $last_editor = get_post_meta( $post->ID, 'classic-editor-remember', true ); + + if ( $last_editor ) { + $is_classic = ( $last_editor === 'classic-editor' ); + } elseif ( ! empty( $post->post_content ) ) { + $is_classic = ! self::has_blocks( $post->post_content ); + } else { + $settings = self::get_settings(); + $is_classic = ( $settings['editor'] === 'classic' ); + } + + $state = $is_classic ? _x( 'Classic editor', 'Editor Name', 'classic-editor' ) : _x( 'Block editor', 'Editor Name', 'classic-editor' ); + } + + // Fix PHP 7+ warnings if another plugin returns unexpected type. + $post_states = (array) $post_states; + $post_states['classic-editor-plugin'] = $state; + + return $post_states; + } + + public static function add_edit_php_inline_style() { + ?> + <style> + .classic-editor-forced-state { + font-style: italic; + font-weight: 400; + color: #72777c; + font-size: small; + } + </style> + <?php + } + + public static function on_admin_init() { + global $pagenow; + + if ( $pagenow !== 'post.php' ) { + return; + } + + $settings = self::get_settings(); + $post_id = self::get_edited_post_id(); + + if ( $post_id && ( $settings['editor'] === 'classic' || self::is_classic( $post_id ) ) ) { + // Move the Privacy Policy help notice back under the title field. + remove_action( 'admin_notices', array( 'WP_Privacy_Policy_Content', 'notice' ) ); + add_action( 'edit_form_after_title', array( 'WP_Privacy_Policy_Content', 'notice' ) ); + } + } + + // Need to support WP < 5.0 + private static function has_blocks( $post = null ) { + if ( ! is_string( $post ) ) { + $wp_post = get_post( $post ); + + if ( $wp_post instanceof WP_Post ) { + $post = $wp_post->post_content; + } + } + + return false !== strpos( (string) $post, '<!-- wp:' ); + } + + /** + * Set defaults on activation. + */ + public static function activate() { + register_uninstall_hook( __FILE__, array( __CLASS__, 'uninstall' ) ); + + if ( is_multisite() ) { + add_network_option( null, 'classic-editor-replace', 'classic' ); + add_network_option( null, 'classic-editor-allow-sites', 'disallow' ); + } + + add_option( 'classic-editor-replace', 'classic' ); + add_option( 'classic-editor-allow-users', 'disallow' ); + } + + /** + * Delete the options on uninstall. + */ + public static function uninstall() { + if ( is_multisite() ) { + delete_network_option( null, 'classic-editor-replace' ); + delete_network_option( null, 'classic-editor-allow-sites' ); + } + + delete_option( 'classic-editor-replace' ); + delete_option( 'classic-editor-allow-users' ); + } + + /** + * Temporary fix for Safari 18 negative horizontal margin on floats. + * See: https://core.trac.wordpress.org/ticket/62082 and + * https://bugs.webkit.org/show_bug.cgi?id=280063. + * TODO: Remove when Safari is fixed. + */ + public static function safari_18_temp_fix() { + global $current_screen; + + if ( isset( $current_screen->base ) && 'post' === $current_screen->base ) { + $clear = is_rtl() ? 'right' : 'left'; + + ?> + <style id="classic-editor-safari-18-temp-fix"> + _::-webkit-full-page-media, _:future, :root #post-body #postbox-container-2 { + clear: <?php echo $clear; ?>; + } + </style> + <?php + } + } + + // Back-compat with 1.6.6. + public static function replace_post_js( $scripts ) { + _deprecated_function( __METHOD__, '1.6.7' ); + } + + /** + * Fix for the Categories postbox on the classic Edit Post screen for WP 6.7.1. + * See: https://core.trac.wordpress.org/ticket/62504 and + * https://github.com/WordPress/classic-editor/issues/222. + */ + public static function replace_post_js_2( $src, $handle ) { + if ( 'post' === $handle && is_string( $src ) && false === strpos( $src, 'ver=62504-20241121' ) ) { + $suffix = wp_scripts_get_suffix(); + $src = plugins_url( 'scripts/', __FILE__ ) . "post{$suffix}.js"; + $src = add_query_arg( 'ver', '62504-20241121', $src ); + } + + return $src; + } +} + +add_action( 'plugins_loaded', array( 'Classic_Editor', 'init_actions' ) ); + +endif; diff --git a/readme.txt b/readme.txt index 379468623b8bb8bf1de538106ddad7beec319595..d7a446ddcf3adbacd4f3013d4b3d22275f18aa5e 100644 --- a/readme.txt +++ b/readme.txt @@ -1,9 +1,9 @@ === Classic Editor === Contributors: wordpressdotorg, azaozz, melchoyce, chanthaboune, alexislloyd, pento, youknowriad, desrosj, luciano-croce, ironprogrammer -Tags: gutenberg, disable, disable gutenberg, editor, classic editor, block editor +Tags: classic editor, block editor, editor, gutenberg Requires at least: 4.9 -Tested up to: 6.6 -Stable tag: 1.6.5 +Tested up to: 6.7 +Stable tag: 1.6.7 Requires PHP: 5.2.4 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -29,6 +29,12 @@ By default, this plugin hides all functionality available in the new block edito == Changelog == += 1.6.7 = +* Fixed loading of script translations when post.js is replaced in WordPress 6.7.1. + += 1.6.6 = +* Added fix for selecting/deselecting multiple unwanted categories in WordPress 6.7.1 when clicking on a category checkbox on the old Edit Post screen. + = 1.6.5 = * Added fix for Safari 18 negative horizontal margin on floats. diff --git a/scripts/post.js b/scripts/post.js new file mode 100644 index 0000000000000000000000000000000000000000..c5e15a4f3928c02bdd8d74a86b8f0ff9314021b2 --- /dev/null +++ b/scripts/post.js @@ -0,0 +1,1357 @@ +/** + * @file Contains all dynamic functionality needed on post and term pages. + * + * @output wp-admin/js/post.js + */ + + /* global ajaxurl, wpAjax, postboxes, pagenow, tinymce, alert, deleteUserSetting, ClipboardJS */ + /* global theList:true, theExtraList:true, getUserSetting, setUserSetting, commentReply, commentsBox */ + /* global WPSetThumbnailHTML, wptitlehint */ + +// Backward compatibility: prevent fatal errors. +window.makeSlugeditClickable = window.editPermalink = function(){}; + +// Make sure the wp object exists. +window.wp = window.wp || {}; + +( function( $ ) { + var titleHasFocus = false, + __ = wp.i18n.__; + + /** + * Control loading of comments on the post and term edit pages. + * + * @type {{st: number, get: commentsBox.get, load: commentsBox.load}} + * + * @namespace commentsBox + */ + window.commentsBox = { + // Comment offset to use when fetching new comments. + st : 0, + + /** + * Fetch comments using Ajax and display them in the box. + * + * @memberof commentsBox + * + * @param {number} total Total number of comments for this post. + * @param {number} num Optional. Number of comments to fetch, defaults to 20. + * @return {boolean} Always returns false. + */ + get : function(total, num) { + var st = this.st, data; + if ( ! num ) + num = 20; + + this.st += num; + this.total = total; + $( '#commentsdiv .spinner' ).addClass( 'is-active' ); + + data = { + 'action' : 'get-comments', + 'mode' : 'single', + '_ajax_nonce' : $('#add_comment_nonce').val(), + 'p' : $('#post_ID').val(), + 'start' : st, + 'number' : num + }; + + $.post( + ajaxurl, + data, + function(r) { + r = wpAjax.parseAjaxResponse(r); + $('#commentsdiv .widefat').show(); + $( '#commentsdiv .spinner' ).removeClass( 'is-active' ); + + if ( 'object' == typeof r && r.responses[0] ) { + $('#the-comment-list').append( r.responses[0].data ); + + theList = theExtraList = null; + $( 'a[className*=\':\']' ).off(); + + // If the offset is over the total number of comments we cannot fetch any more, so hide the button. + if ( commentsBox.st > commentsBox.total ) + $('#show-comments').hide(); + else + $('#show-comments').show().children('a').text( __( 'Show more comments' ) ); + + return; + } else if ( 1 == r ) { + $('#show-comments').text( __( 'No more comments found.' ) ); + return; + } + + $('#the-comment-list').append('<tr><td colspan="2">'+wpAjax.broken+'</td></tr>'); + } + ); + + return false; + }, + + /** + * Load the next batch of comments. + * + * @memberof commentsBox + * + * @param {number} total Total number of comments to load. + */ + load: function(total){ + this.st = jQuery('#the-comment-list tr.comment:visible').length; + this.get(total); + } + }; + + /** + * Overwrite the content of the Featured Image postbox + * + * @param {string} html New HTML to be displayed in the content area of the postbox. + * + * @global + */ + window.WPSetThumbnailHTML = function(html){ + $('.inside', '#postimagediv').html(html); + }; + + /** + * Set the Image ID of the Featured Image + * + * @param {number} id The post_id of the image to use as Featured Image. + * + * @global + */ + window.WPSetThumbnailID = function(id){ + var field = $('input[value="_thumbnail_id"]', '#list-table'); + if ( field.length > 0 ) { + $('#meta\\[' + field.attr('id').match(/[0-9]+/) + '\\]\\[value\\]').text(id); + } + }; + + /** + * Remove the Featured Image + * + * @param {string} nonce Nonce to use in the request. + * + * @global + */ + window.WPRemoveThumbnail = function(nonce){ + $.post( + ajaxurl, { + action: 'set-post-thumbnail', + post_id: $( '#post_ID' ).val(), + thumbnail_id: -1, + _ajax_nonce: nonce, + cookie: encodeURIComponent( document.cookie ) + }, + /** + * Handle server response + * + * @param {string} str Response, will be '0' when an error occurred otherwise contains link to add Featured Image. + */ + function(str){ + if ( str == '0' ) { + alert( __( 'Could not set that as the thumbnail image. Try a different attachment.' ) ); + } else { + WPSetThumbnailHTML(str); + } + } + ); + }; + + /** + * Heartbeat locks. + * + * Used to lock editing of an object by only one user at a time. + * + * When the user does not send a heartbeat in a heartbeat-time + * the user is no longer editing and another user can start editing. + */ + $(document).on( 'heartbeat-send.refresh-lock', function( e, data ) { + var lock = $('#active_post_lock').val(), + post_id = $('#post_ID').val(), + send = {}; + + if ( ! post_id || ! $('#post-lock-dialog').length ) + return; + + send.post_id = post_id; + + if ( lock ) + send.lock = lock; + + data['wp-refresh-post-lock'] = send; + + }).on( 'heartbeat-tick.refresh-lock', function( e, data ) { + // Post locks: update the lock string or show the dialog if somebody has taken over editing. + var received, wrap, avatar; + + if ( data['wp-refresh-post-lock'] ) { + received = data['wp-refresh-post-lock']; + + if ( received.lock_error ) { + // Show "editing taken over" message. + wrap = $('#post-lock-dialog'); + + if ( wrap.length && ! wrap.is(':visible') ) { + if ( wp.autosave ) { + // Save the latest changes and disable. + $(document).one( 'heartbeat-tick', function() { + wp.autosave.server.suspend(); + wrap.removeClass('saving').addClass('saved'); + $(window).off( 'beforeunload.edit-post' ); + }); + + wrap.addClass('saving'); + wp.autosave.server.triggerSave(); + } + + if ( received.lock_error.avatar_src ) { + avatar = $( '<img />', { + 'class': 'avatar avatar-64 photo', + width: 64, + height: 64, + alt: '', + src: received.lock_error.avatar_src, + srcset: received.lock_error.avatar_src_2x ? + received.lock_error.avatar_src_2x + ' 2x' : + undefined + } ); + wrap.find('div.post-locked-avatar').empty().append( avatar ); + } + + wrap.show().find('.currently-editing').text( received.lock_error.text ); + wrap.find('.wp-tab-first').trigger( 'focus' ); + } + } else if ( received.new_lock ) { + $('#active_post_lock').val( received.new_lock ); + } + } + }).on( 'before-autosave.update-post-slug', function() { + titleHasFocus = document.activeElement && document.activeElement.id === 'title'; + }).on( 'after-autosave.update-post-slug', function() { + + /* + * Create slug area only if not already there + * and the title field was not focused (user was not typing a title) when autosave ran. + */ + if ( ! $('#edit-slug-box > *').length && ! titleHasFocus ) { + $.post( ajaxurl, { + action: 'sample-permalink', + post_id: $('#post_ID').val(), + new_title: $('#title').val(), + samplepermalinknonce: $('#samplepermalinknonce').val() + }, + function( data ) { + if ( data != '-1' ) { + $('#edit-slug-box').html(data); + } + } + ); + } + }); + +}(jQuery)); + +/** + * Heartbeat refresh nonces. + */ +(function($) { + var check, timeout; + + /** + * Only allow to check for nonce refresh every 30 seconds. + */ + function schedule() { + check = false; + window.clearTimeout( timeout ); + timeout = window.setTimeout( function(){ check = true; }, 300000 ); + } + + $( function() { + schedule(); + }).on( 'heartbeat-send.wp-refresh-nonces', function( e, data ) { + var post_id, + $authCheck = $('#wp-auth-check-wrap'); + + if ( check || ( $authCheck.length && ! $authCheck.hasClass( 'hidden' ) ) ) { + if ( ( post_id = $('#post_ID').val() ) && $('#_wpnonce').val() ) { + data['wp-refresh-post-nonces'] = { + post_id: post_id + }; + } + } + }).on( 'heartbeat-tick.wp-refresh-nonces', function( e, data ) { + var nonces = data['wp-refresh-post-nonces']; + + if ( nonces ) { + schedule(); + + if ( nonces.replace ) { + $.each( nonces.replace, function( selector, value ) { + $( '#' + selector ).val( value ); + }); + } + + if ( nonces.heartbeatNonce ) + window.heartbeatSettings.nonce = nonces.heartbeatNonce; + } + }); +}(jQuery)); + +/** + * All post and postbox controls and functionality. + */ +jQuery( function($) { + var stamp, visibility, $submitButtons, updateVisibility, updateText, + $textarea = $('#content'), + $document = $(document), + postId = $('#post_ID').val() || 0, + $submitpost = $('#submitpost'), + releaseLock = true, + $postVisibilitySelect = $('#post-visibility-select'), + $timestampdiv = $('#timestampdiv'), + $postStatusSelect = $('#post-status-select'), + isMac = window.navigator.platform ? window.navigator.platform.indexOf( 'Mac' ) !== -1 : false, + copyAttachmentURLClipboard = new ClipboardJS( '.copy-attachment-url.edit-media' ), + copyAttachmentURLSuccessTimeout, + __ = wp.i18n.__, _x = wp.i18n._x; + + postboxes.add_postbox_toggles(pagenow); + + /* + * Clear the window name. Otherwise if this is a former preview window where the user navigated to edit another post, + * and the first post is still being edited, clicking Preview there will use this window to show the preview. + */ + window.name = ''; + + // Post locks: contain focus inside the dialog. If the dialog is shown, focus the first item. + $('#post-lock-dialog .notification-dialog').on( 'keydown', function(e) { + // Don't do anything when [Tab] is pressed. + if ( e.which != 9 ) + return; + + var target = $(e.target); + + // [Shift] + [Tab] on first tab cycles back to last tab. + if ( target.hasClass('wp-tab-first') && e.shiftKey ) { + $(this).find('.wp-tab-last').trigger( 'focus' ); + e.preventDefault(); + // [Tab] on last tab cycles back to first tab. + } else if ( target.hasClass('wp-tab-last') && ! e.shiftKey ) { + $(this).find('.wp-tab-first').trigger( 'focus' ); + e.preventDefault(); + } + }).filter(':visible').find('.wp-tab-first').trigger( 'focus' ); + + // Set the heartbeat interval to 10 seconds if post lock dialogs are enabled. + if ( wp.heartbeat && $('#post-lock-dialog').length ) { + wp.heartbeat.interval( 10 ); + } + + // The form is being submitted by the user. + $submitButtons = $submitpost.find( ':submit, a.submitdelete, #post-preview' ).on( 'click.edit-post', function( event ) { + var $button = $(this); + + if ( $button.hasClass('disabled') ) { + event.preventDefault(); + return; + } + + if ( $button.hasClass('submitdelete') || $button.is( '#post-preview' ) ) { + return; + } + + // The form submission can be blocked from JS or by using HTML 5.0 validation on some fields. + // Run this only on an actual 'submit'. + $('form#post').off( 'submit.edit-post' ).on( 'submit.edit-post', function( event ) { + if ( event.isDefaultPrevented() ) { + return; + } + + // Stop auto save. + if ( wp.autosave ) { + wp.autosave.server.suspend(); + } + + if ( typeof commentReply !== 'undefined' ) { + /* + * Warn the user they have an unsaved comment before submitting + * the post data for update. + */ + if ( ! commentReply.discardCommentChanges() ) { + return false; + } + + /* + * Close the comment edit/reply form if open to stop the form + * action from interfering with the post's form action. + */ + commentReply.close(); + } + + releaseLock = false; + $(window).off( 'beforeunload.edit-post' ); + + $submitButtons.addClass( 'disabled' ); + + if ( $button.attr('id') === 'publish' ) { + $submitpost.find( '#major-publishing-actions .spinner' ).addClass( 'is-active' ); + } else { + $submitpost.find( '#minor-publishing .spinner' ).addClass( 'is-active' ); + } + }); + }); + + // Submit the form saving a draft or an autosave, and show a preview in a new tab. + $('#post-preview').on( 'click.post-preview', function( event ) { + var $this = $(this), + $form = $('form#post'), + $previewField = $('input#wp-preview'), + target = $this.attr('target') || 'wp-preview', + ua = navigator.userAgent.toLowerCase(); + + event.preventDefault(); + + if ( $this.hasClass('disabled') ) { + return; + } + + if ( wp.autosave ) { + wp.autosave.server.tempBlockSave(); + } + + $previewField.val('dopreview'); + $form.attr( 'target', target ).trigger( 'submit' ).attr( 'target', '' ); + + // Workaround for WebKit bug preventing a form submitting twice to the same action. + // https://bugs.webkit.org/show_bug.cgi?id=28633 + if ( ua.indexOf('safari') !== -1 && ua.indexOf('chrome') === -1 ) { + $form.attr( 'action', function( index, value ) { + return value + '?t=' + ( new Date() ).getTime(); + }); + } + + $previewField.val(''); + }); + + // Auto save new posts after a title is typed. + if ( $( '#auto_draft' ).val() ) { + $( '#title' ).on( 'blur', function() { + var cancel; + + if ( ! this.value || $('#edit-slug-box > *').length ) { + return; + } + + // Cancel the auto save when the blur was triggered by the user submitting the form. + $('form#post').one( 'submit', function() { + cancel = true; + }); + + window.setTimeout( function() { + if ( ! cancel && wp.autosave ) { + wp.autosave.server.triggerSave(); + } + }, 200 ); + }); + } + + $document.on( 'autosave-disable-buttons.edit-post', function() { + $submitButtons.addClass( 'disabled' ); + }).on( 'autosave-enable-buttons.edit-post', function() { + if ( ! wp.heartbeat || ! wp.heartbeat.hasConnectionError() ) { + $submitButtons.removeClass( 'disabled' ); + } + }).on( 'before-autosave.edit-post', function() { + $( '.autosave-message' ).text( __( 'Saving Draft…' ) ); + }).on( 'after-autosave.edit-post', function( event, data ) { + $( '.autosave-message' ).text( data.message ); + + if ( $( document.body ).hasClass( 'post-new-php' ) ) { + $( '.submitbox .submitdelete' ).show(); + } + }); + + /* + * When the user is trying to load another page, or reloads current page + * show a confirmation dialog when there are unsaved changes. + */ + $( window ).on( 'beforeunload.edit-post', function( event ) { + var editor = window.tinymce && window.tinymce.get( 'content' ); + var changed = false; + + if ( wp.autosave ) { + changed = wp.autosave.server.postChanged(); + } else if ( editor ) { + changed = ( ! editor.isHidden() && editor.isDirty() ); + } + + if ( changed ) { + event.preventDefault(); + // The return string is needed for browser compat. + // See https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event. + return __( 'The changes you made will be lost if you navigate away from this page.' ); + } + }).on( 'pagehide.edit-post', function( event ) { + if ( ! releaseLock ) { + return; + } + + /* + * Unload is triggered (by hand) on removing the Thickbox iframe. + * Make sure we process only the main document unload. + */ + if ( event.target && event.target.nodeName != '#document' ) { + return; + } + + var postID = $('#post_ID').val(); + var postLock = $('#active_post_lock').val(); + + if ( ! postID || ! postLock ) { + return; + } + + var data = { + action: 'wp-remove-post-lock', + _wpnonce: $('#_wpnonce').val(), + post_ID: postID, + active_post_lock: postLock + }; + + if ( window.FormData && window.navigator.sendBeacon ) { + var formData = new window.FormData(); + + $.each( data, function( key, value ) { + formData.append( key, value ); + }); + + if ( window.navigator.sendBeacon( ajaxurl, formData ) ) { + return; + } + } + + // Fall back to a synchronous POST request. + // See https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon + $.post({ + async: false, + data: data, + url: ajaxurl + }); + }); + + // Multiple taxonomies. + if ( $('#tagsdiv-post_tag').length ) { + window.tagBox && window.tagBox.init(); + } else { + $('.meta-box-sortables').children('div.postbox').each(function(){ + if ( this.id.indexOf('tagsdiv-') === 0 ) { + window.tagBox && window.tagBox.init(); + return false; + } + }); + } + + // Handle categories. + $('.categorydiv').each( function(){ + var this_id = $(this).attr('id'), catAddBefore, catAddAfter, taxonomyParts, taxonomy, settingName; + + taxonomyParts = this_id.split('-'); + taxonomyParts.shift(); + taxonomy = taxonomyParts.join('-'); + settingName = taxonomy + '_tab'; + + if ( taxonomy == 'category' ) { + settingName = 'cats'; + } + + // @todo Move to jQuery 1.3+, support for multiple hierarchical taxonomies, see wp-lists.js. + $('a', '#' + taxonomy + '-tabs').on( 'click', function( e ) { + e.preventDefault(); + var t = $(this).attr('href'); + $(this).parent().addClass('tabs').siblings('li').removeClass('tabs'); + $('#' + taxonomy + '-tabs').siblings('.tabs-panel').hide(); + $(t).show(); + if ( '#' + taxonomy + '-all' == t ) { + deleteUserSetting( settingName ); + } else { + setUserSetting( settingName, 'pop' ); + } + }); + + if ( getUserSetting( settingName ) ) + $('a[href="#' + taxonomy + '-pop"]', '#' + taxonomy + '-tabs').trigger( 'click' ); + + // Add category button controls. + $('#new' + taxonomy).one( 'focus', function() { + $( this ).val( '' ).removeClass( 'form-input-tip' ); + }); + + // On [Enter] submit the taxonomy. + $('#new' + taxonomy).on( 'keypress', function(event){ + if( 13 === event.keyCode ) { + event.preventDefault(); + $('#' + taxonomy + '-add-submit').trigger( 'click' ); + } + }); + + // After submitting a new taxonomy, re-focus the input field. + $('#' + taxonomy + '-add-submit').on( 'click', function() { + $('#new' + taxonomy).trigger( 'focus' ); + }); + + /** + * Before adding a new taxonomy, disable submit button. + * + * @param {Object} s Taxonomy object which will be added. + * + * @return {Object} + */ + catAddBefore = function( s ) { + if ( !$('#new'+taxonomy).val() ) { + return false; + } + + s.data += '&' + $( ':checked', '#'+taxonomy+'checklist' ).serialize(); + $( '#' + taxonomy + '-add-submit' ).prop( 'disabled', true ); + return s; + }; + + /** + * Re-enable submit button after a taxonomy has been added. + * + * Re-enable submit button. + * If the taxonomy has a parent place the taxonomy underneath the parent. + * + * @param {Object} r Response. + * @param {Object} s Taxonomy data. + * + * @return {void} + */ + catAddAfter = function( r, s ) { + var sup, drop = $('#new'+taxonomy+'_parent'); + + $( '#' + taxonomy + '-add-submit' ).prop( 'disabled', false ); + if ( 'undefined' != s.parsed.responses[0] && (sup = s.parsed.responses[0].supplemental.newcat_parent) ) { + drop.before(sup); + drop.remove(); + } + }; + + $('#' + taxonomy + 'checklist').wpList({ + alt: '', + response: taxonomy + '-ajax-response', + addBefore: catAddBefore, + addAfter: catAddAfter + }); + + // Add new taxonomy button toggles input form visibility. + $('#' + taxonomy + '-add-toggle').on( 'click', function( e ) { + e.preventDefault(); + $('#' + taxonomy + '-adder').toggleClass( 'wp-hidden-children' ); + $('a[href="#' + taxonomy + '-all"]', '#' + taxonomy + '-tabs').trigger( 'click' ); + $('#new'+taxonomy).trigger( 'focus' ); + }); + + // Sync checked items between "All {taxonomy}" and "Most used" lists. + $('#' + taxonomy + 'checklist, #' + taxonomy + 'checklist-pop').on( + 'click', + 'li.popular-category > label input[type="checkbox"]', + function() { + var t = $(this), c = t.is(':checked'), id = t.val(); + if ( id && t.parents('#taxonomy-'+taxonomy).length ) { + // Fixed for ticket #62504. See https://core.trac.wordpress.org/ticket/62504. + $('input#in-' + taxonomy + '-' + id + ', input[id^="in-' + taxonomy + '-' + id + '-"]').prop('checked', c); + $('input#in-popular-' + taxonomy + '-' + id).prop('checked', c); + } + } + ); + + }); // End cats. + + // Custom Fields postbox. + if ( $('#postcustom').length ) { + $( '#the-list' ).wpList( { + /** + * Add current post_ID to request to fetch custom fields + * + * @ignore + * + * @param {Object} s Request object. + * + * @return {Object} Data modified with post_ID attached. + */ + addBefore: function( s ) { + s.data += '&post_id=' + $('#post_ID').val(); + return s; + }, + /** + * Show the listing of custom fields after fetching. + * + * @ignore + */ + addAfter: function() { + $('table#list-table').show(); + } + }); + } + + /* + * Publish Post box (#submitdiv) + */ + if ( $('#submitdiv').length ) { + stamp = $('#timestamp').html(); + visibility = $('#post-visibility-display').html(); + + /** + * When the visibility of a post changes sub-options should be shown or hidden. + * + * @ignore + * + * @return {void} + */ + updateVisibility = function() { + // Show sticky for public posts. + if ( $postVisibilitySelect.find('input:radio:checked').val() != 'public' ) { + $('#sticky').prop('checked', false); + $('#sticky-span').hide(); + } else { + $('#sticky-span').show(); + } + + // Show password input field for password protected post. + if ( $postVisibilitySelect.find('input:radio:checked').val() != 'password' ) { + $('#password-span').hide(); + } else { + $('#password-span').show(); + } + }; + + /** + * Make sure all labels represent the current settings. + * + * @ignore + * + * @return {boolean} False when an invalid timestamp has been selected, otherwise True. + */ + updateText = function() { + + if ( ! $timestampdiv.length ) + return true; + + var attemptedDate, originalDate, currentDate, publishOn, postStatus = $('#post_status'), + optPublish = $('option[value="publish"]', postStatus), aa = $('#aa').val(), + mm = $('#mm').val(), jj = $('#jj').val(), hh = $('#hh').val(), mn = $('#mn').val(); + + attemptedDate = new Date( aa, mm - 1, jj, hh, mn ); + originalDate = new Date( + $('#hidden_aa').val(), + $('#hidden_mm').val() -1, + $('#hidden_jj').val(), + $('#hidden_hh').val(), + $('#hidden_mn').val() + ); + currentDate = new Date( + $('#cur_aa').val(), + $('#cur_mm').val() -1, + $('#cur_jj').val(), + $('#cur_hh').val(), + $('#cur_mn').val() + ); + + // Catch unexpected date problems. + if ( + attemptedDate.getFullYear() != aa || + (1 + attemptedDate.getMonth()) != mm || + attemptedDate.getDate() != jj || + attemptedDate.getMinutes() != mn + ) { + $timestampdiv.find('.timestamp-wrap').addClass('form-invalid'); + return false; + } else { + $timestampdiv.find('.timestamp-wrap').removeClass('form-invalid'); + } + + // Determine what the publish should be depending on the date and post status. + if ( attemptedDate > currentDate ) { + publishOn = __( 'Schedule for:' ); + $('#publish').val( _x( 'Schedule', 'post action/button label' ) ); + } else if ( attemptedDate <= currentDate && $('#original_post_status').val() != 'publish' ) { + publishOn = __( 'Publish on:' ); + $('#publish').val( __( 'Publish' ) ); + } else { + publishOn = __( 'Published on:' ); + $('#publish').val( __( 'Update' ) ); + } + + // If the date is the same, set it to trigger update events. + if ( originalDate.toUTCString() == attemptedDate.toUTCString() ) { + // Re-set to the current value. + $('#timestamp').html(stamp); + } else { + $('#timestamp').html( + '\n' + publishOn + ' <b>' + + // translators: 1: Month, 2: Day, 3: Year, 4: Hour, 5: Minute. + __( '%1$s %2$s, %3$s at %4$s:%5$s' ) + .replace( '%1$s', $( 'option[value="' + mm + '"]', '#mm' ).attr( 'data-text' ) ) + .replace( '%2$s', parseInt( jj, 10 ) ) + .replace( '%3$s', aa ) + .replace( '%4$s', ( '00' + hh ).slice( -2 ) ) + .replace( '%5$s', ( '00' + mn ).slice( -2 ) ) + + '</b> ' + ); + } + + // Add "privately published" to post status when applies. + if ( $postVisibilitySelect.find('input:radio:checked').val() == 'private' ) { + $('#publish').val( __( 'Update' ) ); + if ( 0 === optPublish.length ) { + postStatus.append('<option value="publish">' + __( 'Privately Published' ) + '</option>'); + } else { + optPublish.html( __( 'Privately Published' ) ); + } + $('option[value="publish"]', postStatus).prop('selected', true); + $('#misc-publishing-actions .edit-post-status').hide(); + } else { + if ( $('#original_post_status').val() == 'future' || $('#original_post_status').val() == 'draft' ) { + if ( optPublish.length ) { + optPublish.remove(); + postStatus.val($('#hidden_post_status').val()); + } + } else { + optPublish.html( __( 'Published' ) ); + } + if ( postStatus.is(':hidden') ) + $('#misc-publishing-actions .edit-post-status').show(); + } + + // Update "Status:" to currently selected status. + $('#post-status-display').text( + // Remove any potential tags from post status text. + wp.sanitize.stripTagsAndEncodeText( $('option:selected', postStatus).text() ) + ); + + // Show or hide the "Save Draft" button. + if ( + $('option:selected', postStatus).val() == 'private' || + $('option:selected', postStatus).val() == 'publish' + ) { + $('#save-post').hide(); + } else { + $('#save-post').show(); + if ( $('option:selected', postStatus).val() == 'pending' ) { + $('#save-post').show().val( __( 'Save as Pending' ) ); + } else { + $('#save-post').show().val( __( 'Save Draft' ) ); + } + } + return true; + }; + + // Show the visibility options and hide the toggle button when opened. + $( '#visibility .edit-visibility').on( 'click', function( e ) { + e.preventDefault(); + if ( $postVisibilitySelect.is(':hidden') ) { + updateVisibility(); + $postVisibilitySelect.slideDown( 'fast', function() { + $postVisibilitySelect.find( 'input[type="radio"]' ).first().trigger( 'focus' ); + } ); + $(this).hide(); + } + }); + + // Cancel visibility selection area and hide it from view. + $postVisibilitySelect.find('.cancel-post-visibility').on( 'click', function( event ) { + $postVisibilitySelect.slideUp('fast'); + $('#visibility-radio-' + $('#hidden-post-visibility').val()).prop('checked', true); + $('#post_password').val($('#hidden-post-password').val()); + $('#sticky').prop('checked', $('#hidden-post-sticky').prop('checked')); + $('#post-visibility-display').html(visibility); + $('#visibility .edit-visibility').show().trigger( 'focus' ); + updateText(); + event.preventDefault(); + }); + + // Set the selected visibility as current. + $postVisibilitySelect.find('.save-post-visibility').on( 'click', function( event ) { // Crazyhorse branch - multiple OK cancels. + var visibilityLabel = '', selectedVisibility = $postVisibilitySelect.find('input:radio:checked').val(); + + $postVisibilitySelect.slideUp('fast'); + $('#visibility .edit-visibility').show().trigger( 'focus' ); + updateText(); + + if ( 'public' !== selectedVisibility ) { + $('#sticky').prop('checked', false); + } + + switch ( selectedVisibility ) { + case 'public': + visibilityLabel = $( '#sticky' ).prop( 'checked' ) ? __( 'Public, Sticky' ) : __( 'Public' ); + break; + case 'private': + visibilityLabel = __( 'Private' ); + break; + case 'password': + visibilityLabel = __( 'Password Protected' ); + break; + } + + $('#post-visibility-display').text( visibilityLabel ); + event.preventDefault(); + }); + + // When the selection changes, update labels. + $postVisibilitySelect.find('input:radio').on( 'change', function() { + updateVisibility(); + }); + + // Edit publish time click. + $timestampdiv.siblings('a.edit-timestamp').on( 'click', function( event ) { + if ( $timestampdiv.is( ':hidden' ) ) { + $timestampdiv.slideDown( 'fast', function() { + $( 'input, select', $timestampdiv.find( '.timestamp-wrap' ) ).first().trigger( 'focus' ); + } ); + $(this).hide(); + } + event.preventDefault(); + }); + + // Cancel editing the publish time and hide the settings. + $timestampdiv.find('.cancel-timestamp').on( 'click', function( event ) { + $timestampdiv.slideUp('fast').siblings('a.edit-timestamp').show().trigger( 'focus' ); + $('#mm').val($('#hidden_mm').val()); + $('#jj').val($('#hidden_jj').val()); + $('#aa').val($('#hidden_aa').val()); + $('#hh').val($('#hidden_hh').val()); + $('#mn').val($('#hidden_mn').val()); + updateText(); + event.preventDefault(); + }); + + // Save the changed timestamp. + $timestampdiv.find('.save-timestamp').on( 'click', function( event ) { // Crazyhorse branch - multiple OK cancels. + if ( updateText() ) { + $timestampdiv.slideUp('fast'); + $timestampdiv.siblings('a.edit-timestamp').show().trigger( 'focus' ); + } + event.preventDefault(); + }); + + // Cancel submit when an invalid timestamp has been selected. + $('#post').on( 'submit', function( event ) { + if ( ! updateText() ) { + event.preventDefault(); + $timestampdiv.show(); + + if ( wp.autosave ) { + wp.autosave.enableButtons(); + } + + $( '#publishing-action .spinner' ).removeClass( 'is-active' ); + } + }); + + // Post Status edit click. + $postStatusSelect.siblings('a.edit-post-status').on( 'click', function( event ) { + if ( $postStatusSelect.is( ':hidden' ) ) { + $postStatusSelect.slideDown( 'fast', function() { + $postStatusSelect.find('select').trigger( 'focus' ); + } ); + $(this).hide(); + } + event.preventDefault(); + }); + + // Save the Post Status changes and hide the options. + $postStatusSelect.find('.save-post-status').on( 'click', function( event ) { + $postStatusSelect.slideUp( 'fast' ).siblings( 'a.edit-post-status' ).show().trigger( 'focus' ); + updateText(); + event.preventDefault(); + }); + + // Cancel Post Status editing and hide the options. + $postStatusSelect.find('.cancel-post-status').on( 'click', function( event ) { + $postStatusSelect.slideUp( 'fast' ).siblings( 'a.edit-post-status' ).show().trigger( 'focus' ); + $('#post_status').val( $('#hidden_post_status').val() ); + updateText(); + event.preventDefault(); + }); + } + + /** + * Handle the editing of the post_name. Create the required HTML elements and + * update the changes via Ajax. + * + * @global + * + * @return {void} + */ + function editPermalink() { + var i, slug_value, slug_label, + $el, revert_e, + c = 0, + real_slug = $('#post_name'), + revert_slug = real_slug.val(), + permalink = $( '#sample-permalink' ), + permalinkOrig = permalink.html(), + permalinkInner = $( '#sample-permalink a' ).html(), + buttons = $('#edit-slug-buttons'), + buttonsOrig = buttons.html(), + full = $('#editable-post-name-full'); + + // Deal with Twemoji in the post-name. + full.find( 'img' ).replaceWith( function() { return this.alt; } ); + full = full.html(); + + permalink.html( permalinkInner ); + + // Save current content to revert to when cancelling. + $el = $( '#editable-post-name' ); + revert_e = $el.html(); + + buttons.html( + '<button type="button" class="save button button-small">' + __( 'OK' ) + '</button> ' + + '<button type="button" class="cancel button-link">' + __( 'Cancel' ) + '</button>' + ); + + // Save permalink changes. + buttons.children( '.save' ).on( 'click', function() { + var new_slug = $el.children( 'input' ).val(); + + if ( new_slug == $('#editable-post-name-full').text() ) { + buttons.children('.cancel').trigger( 'click' ); + return; + } + + $.post( + ajaxurl, + { + action: 'sample-permalink', + post_id: postId, + new_slug: new_slug, + new_title: $('#title').val(), + samplepermalinknonce: $('#samplepermalinknonce').val() + }, + function(data) { + var box = $('#edit-slug-box'); + box.html(data); + if (box.hasClass('hidden')) { + box.fadeIn('fast', function () { + box.removeClass('hidden'); + }); + } + + buttons.html(buttonsOrig); + permalink.html(permalinkOrig); + real_slug.val(new_slug); + $( '.edit-slug' ).trigger( 'focus' ); + wp.a11y.speak( __( 'Permalink saved' ) ); + } + ); + }); + + // Cancel editing of permalink. + buttons.children( '.cancel' ).on( 'click', function() { + $('#view-post-btn').show(); + $el.html(revert_e); + buttons.html(buttonsOrig); + permalink.html(permalinkOrig); + real_slug.val(revert_slug); + $( '.edit-slug' ).trigger( 'focus' ); + }); + + // If more than 1/4th of 'full' is '%', make it empty. + for ( i = 0; i < full.length; ++i ) { + if ( '%' == full.charAt(i) ) + c++; + } + slug_value = ( c > full.length / 4 ) ? '' : full; + slug_label = __( 'URL Slug' ); + + $el.html( + '<label for="new-post-slug" class="screen-reader-text">' + slug_label + '</label>' + + '<input type="text" id="new-post-slug" value="' + slug_value + '" autocomplete="off" spellcheck="false" />' + ).children( 'input' ).on( 'keydown', function( e ) { + var key = e.which; + // On [Enter], just save the new slug, don't save the post. + if ( 13 === key ) { + e.preventDefault(); + buttons.children( '.save' ).trigger( 'click' ); + } + // On [Esc] cancel the editing. + if ( 27 === key ) { + buttons.children( '.cancel' ).trigger( 'click' ); + } + } ).on( 'keyup', function() { + real_slug.val( this.value ); + }).trigger( 'focus' ); + } + + $( '#titlediv' ).on( 'click', '.edit-slug', function() { + editPermalink(); + }); + + /** + * Adds screen reader text to the title label when needed. + * + * Use the 'screen-reader-text' class to emulate a placeholder attribute + * and hide the label when entering a value. + * + * @param {string} id Optional. HTML ID to add the screen reader helper text to. + * + * @global + * + * @return {void} + */ + window.wptitlehint = function( id ) { + id = id || 'title'; + + var title = $( '#' + id ), titleprompt = $( '#' + id + '-prompt-text' ); + + if ( '' === title.val() ) { + titleprompt.removeClass( 'screen-reader-text' ); + } + + title.on( 'input', function() { + if ( '' === this.value ) { + titleprompt.removeClass( 'screen-reader-text' ); + return; + } + + titleprompt.addClass( 'screen-reader-text' ); + } ); + }; + + wptitlehint(); + + // Resize the WYSIWYG and plain text editors. + ( function() { + var editor, offset, mce, + $handle = $('#post-status-info'), + $postdivrich = $('#postdivrich'); + + // If there are no textareas or we are on a touch device, we can't do anything. + if ( ! $textarea.length || 'ontouchstart' in window ) { + // Hide the resize handle. + $('#content-resize-handle').hide(); + return; + } + + /** + * Handle drag event. + * + * @param {Object} event Event containing details about the drag. + */ + function dragging( event ) { + if ( $postdivrich.hasClass( 'wp-editor-expand' ) ) { + return; + } + + if ( mce ) { + editor.theme.resizeTo( null, offset + event.pageY ); + } else { + $textarea.height( Math.max( 50, offset + event.pageY ) ); + } + + event.preventDefault(); + } + + /** + * When the dragging stopped make sure we return focus and do a confidence check on the height. + */ + function endDrag() { + var height, toolbarHeight; + + if ( $postdivrich.hasClass( 'wp-editor-expand' ) ) { + return; + } + + if ( mce ) { + editor.focus(); + toolbarHeight = parseInt( $( '#wp-content-editor-container .mce-toolbar-grp' ).height(), 10 ); + + if ( toolbarHeight < 10 || toolbarHeight > 200 ) { + toolbarHeight = 30; + } + + height = parseInt( $('#content_ifr').css('height'), 10 ) + toolbarHeight - 28; + } else { + $textarea.trigger( 'focus' ); + height = parseInt( $textarea.css('height'), 10 ); + } + + $document.off( '.wp-editor-resize' ); + + // Confidence check: normalize height to stay within acceptable ranges. + if ( height && height > 50 && height < 5000 ) { + setUserSetting( 'ed_size', height ); + } + } + + $handle.on( 'mousedown.wp-editor-resize', function( event ) { + if ( typeof tinymce !== 'undefined' ) { + editor = tinymce.get('content'); + } + + if ( editor && ! editor.isHidden() ) { + mce = true; + offset = $('#content_ifr').height() - event.pageY; + } else { + mce = false; + offset = $textarea.height() - event.pageY; + $textarea.trigger( 'blur' ); + } + + $document.on( 'mousemove.wp-editor-resize', dragging ) + .on( 'mouseup.wp-editor-resize mouseleave.wp-editor-resize', endDrag ); + + event.preventDefault(); + }).on( 'mouseup.wp-editor-resize', endDrag ); + })(); + + // TinyMCE specific handling of Post Format changes to reflect in the editor. + if ( typeof tinymce !== 'undefined' ) { + // When changing post formats, change the editor body class. + $( '#post-formats-select input.post-format' ).on( 'change.set-editor-class', function() { + var editor, body, format = this.id; + + if ( format && $( this ).prop( 'checked' ) && ( editor = tinymce.get( 'content' ) ) ) { + body = editor.getBody(); + body.className = body.className.replace( /\bpost-format-[^ ]+/, '' ); + editor.dom.addClass( body, format == 'post-format-0' ? 'post-format-standard' : format ); + $( document ).trigger( 'editor-classchange' ); + } + }); + + // When changing page template, change the editor body class. + $( '#page_template' ).on( 'change.set-editor-class', function() { + var editor, body, pageTemplate = $( this ).val() || ''; + + pageTemplate = pageTemplate.substr( pageTemplate.lastIndexOf( '/' ) + 1, pageTemplate.length ) + .replace( /\.php$/, '' ) + .replace( /\./g, '-' ); + + if ( pageTemplate && ( editor = tinymce.get( 'content' ) ) ) { + body = editor.getBody(); + body.className = body.className.replace( /\bpage-template-[^ ]+/, '' ); + editor.dom.addClass( body, 'page-template-' + pageTemplate ); + $( document ).trigger( 'editor-classchange' ); + } + }); + + } + + // Save on pressing [Ctrl]/[Command] + [S] in the Text editor. + $textarea.on( 'keydown.wp-autosave', function( event ) { + // Key [S] has code 83. + if ( event.which === 83 ) { + if ( + event.shiftKey || + event.altKey || + ( isMac && ( ! event.metaKey || event.ctrlKey ) ) || + ( ! isMac && ! event.ctrlKey ) + ) { + return; + } + + wp.autosave && wp.autosave.server.triggerSave(); + event.preventDefault(); + } + }); + + // If the last status was auto-draft and the save is triggered, edit the current URL. + if ( $( '#original_post_status' ).val() === 'auto-draft' && window.history.replaceState ) { + var location; + + $( '#publish' ).on( 'click', function() { + location = window.location.href; + location += ( location.indexOf( '?' ) !== -1 ) ? '&' : '?'; + location += 'wp-post-new-reload=true'; + + window.history.replaceState( null, null, location ); + }); + } + + /** + * Copies the attachment URL in the Edit Media page to the clipboard. + * + * @since 5.5.0 + * + * @param {MouseEvent} event A click event. + * + * @return {void} + */ + copyAttachmentURLClipboard.on( 'success', function( event ) { + var triggerElement = $( event.trigger ), + successElement = $( '.success', triggerElement.closest( '.copy-to-clipboard-container' ) ); + + // Clear the selection and move focus back to the trigger. + event.clearSelection(); + + // Show success visual feedback. + clearTimeout( copyAttachmentURLSuccessTimeout ); + successElement.removeClass( 'hidden' ); + + // Hide success visual feedback after 3 seconds since last success. + copyAttachmentURLSuccessTimeout = setTimeout( function() { + successElement.addClass( 'hidden' ); + }, 3000 ); + + // Handle success audible feedback. + wp.a11y.speak( __( 'The file URL has been copied to your clipboard' ) ); + } ); +} ); + +/** + * TinyMCE word count display + */ +( function( $, counter ) { + $( function() { + var $content = $( '#content' ), + $count = $( '#wp-word-count' ).find( '.word-count' ), + prevCount = 0, + contentEditor; + + /** + * Get the word count from TinyMCE and display it + */ + function update() { + var text, count; + + if ( ! contentEditor || contentEditor.isHidden() ) { + text = $content.val(); + } else { + text = contentEditor.getContent( { format: 'raw' } ); + } + + count = counter.count( text ); + + if ( count !== prevCount ) { + $count.text( count ); + } + + prevCount = count; + } + + /** + * Bind the word count update triggers. + * + * When a node change in the main TinyMCE editor has been triggered. + * When a key has been released in the plain text content editor. + */ + $( document ).on( 'tinymce-editor-init', function( event, editor ) { + if ( editor.id !== 'content' ) { + return; + } + + contentEditor = editor; + + editor.on( 'nodechange keyup', _.debounce( update, 1000 ) ); + } ); + + $content.on( 'input keyup', _.debounce( update, 1000 ) ); + + update(); + } ); + +} )( jQuery, new wp.utils.WordCounter() ); diff --git a/scripts/post.min.js b/scripts/post.min.js new file mode 100644 index 0000000000000000000000000000000000000000..7322d5e0322ef6a8641d8b77a5b4d42dd4732935 --- /dev/null +++ b/scripts/post.min.js @@ -0,0 +1,2 @@ +/*! This file is auto-generated */ +window.makeSlugeditClickable=window.editPermalink=function(){},window.wp=window.wp||{},function(s){var t=!1,a=wp.i18n.__;window.commentsBox={st:0,get:function(t,e){var i=this.st;return this.st+=e=e||20,this.total=t,s("#commentsdiv .spinner").addClass("is-active"),t={action:"get-comments",mode:"single",_ajax_nonce:s("#add_comment_nonce").val(),p:s("#post_ID").val(),start:i,number:e},s.post(ajaxurl,t,function(t){t=wpAjax.parseAjaxResponse(t),s("#commentsdiv .widefat").show(),s("#commentsdiv .spinner").removeClass("is-active"),"object"==typeof t&&t.responses[0]?(s("#the-comment-list").append(t.responses[0].data),theList=theExtraList=null,s("a[className*=':']").off(),commentsBox.st>commentsBox.total?s("#show-comments").hide():s("#show-comments").show().children("a").text(a("Show more comments"))):1==t?s("#show-comments").text(a("No more comments found.")):s("#the-comment-list").append('<tr><td colspan="2">'+wpAjax.broken+"</td></tr>")}),!1},load:function(t){this.st=jQuery("#the-comment-list tr.comment:visible").length,this.get(t)}},window.WPSetThumbnailHTML=function(t){s(".inside","#postimagediv").html(t)},window.WPSetThumbnailID=function(t){var e=s('input[value="_thumbnail_id"]',"#list-table");0<e.length&&s("#meta\\["+e.attr("id").match(/[0-9]+/)+"\\]\\[value\\]").text(t)},window.WPRemoveThumbnail=function(t){s.post(ajaxurl,{action:"set-post-thumbnail",post_id:s("#post_ID").val(),thumbnail_id:-1,_ajax_nonce:t,cookie:encodeURIComponent(document.cookie)},function(t){"0"==t?alert(a("Could not set that as the thumbnail image. Try a different attachment.")):WPSetThumbnailHTML(t)})},s(document).on("heartbeat-send.refresh-lock",function(t,e){var i=s("#active_post_lock").val(),a=s("#post_ID").val(),n={};a&&s("#post-lock-dialog").length&&(n.post_id=a,i&&(n.lock=i),e["wp-refresh-post-lock"]=n)}).on("heartbeat-tick.refresh-lock",function(t,e){var i,a;e["wp-refresh-post-lock"]&&((e=e["wp-refresh-post-lock"]).lock_error?(i=s("#post-lock-dialog")).length&&!i.is(":visible")&&(wp.autosave&&(s(document).one("heartbeat-tick",function(){wp.autosave.server.suspend(),i.removeClass("saving").addClass("saved"),s(window).off("beforeunload.edit-post")}),i.addClass("saving"),wp.autosave.server.triggerSave()),e.lock_error.avatar_src&&(a=s("<img />",{class:"avatar avatar-64 photo",width:64,height:64,alt:"",src:e.lock_error.avatar_src,srcset:e.lock_error.avatar_src_2x?e.lock_error.avatar_src_2x+" 2x":void 0}),i.find("div.post-locked-avatar").empty().append(a)),i.show().find(".currently-editing").text(e.lock_error.text),i.find(".wp-tab-first").trigger("focus")):e.new_lock&&s("#active_post_lock").val(e.new_lock))}).on("before-autosave.update-post-slug",function(){t=document.activeElement&&"title"===document.activeElement.id}).on("after-autosave.update-post-slug",function(){s("#edit-slug-box > *").length||t||s.post(ajaxurl,{action:"sample-permalink",post_id:s("#post_ID").val(),new_title:s("#title").val(),samplepermalinknonce:s("#samplepermalinknonce").val()},function(t){"-1"!=t&&s("#edit-slug-box").html(t)})})}(jQuery),function(a){var n,t;function i(){n=!1,window.clearTimeout(t),t=window.setTimeout(function(){n=!0},3e5)}a(function(){i()}).on("heartbeat-send.wp-refresh-nonces",function(t,e){var i=a("#wp-auth-check-wrap");(n||i.length&&!i.hasClass("hidden"))&&(i=a("#post_ID").val())&&a("#_wpnonce").val()&&(e["wp-refresh-post-nonces"]={post_id:i})}).on("heartbeat-tick.wp-refresh-nonces",function(t,e){e=e["wp-refresh-post-nonces"];e&&(i(),e.replace&&a.each(e.replace,function(t,e){a("#"+t).val(e)}),e.heartbeatNonce)&&(window.heartbeatSettings.nonce=e.heartbeatNonce)})}(jQuery),jQuery(function(h){var d,e,i,a,n,s,o,l,r,t,c,p,u=h("#content"),v=h(document),f=h("#post_ID").val()||0,m=h("#submitpost"),w=!0,g=h("#post-visibility-select"),b=h("#timestampdiv"),k=h("#post-status-select"),_=!!window.navigator.platform&&-1!==window.navigator.platform.indexOf("Mac"),y=new ClipboardJS(".copy-attachment-url.edit-media"),x=wp.i18n.__,C=wp.i18n._x;function D(t){c.hasClass("wp-editor-expand")||(r?o.theme.resizeTo(null,l+t.pageY):u.height(Math.max(50,l+t.pageY)),t.preventDefault())}function j(){var t;c.hasClass("wp-editor-expand")||(t=r?(o.focus(),((t=parseInt(h("#wp-content-editor-container .mce-toolbar-grp").height(),10))<10||200<t)&&(t=30),parseInt(h("#content_ifr").css("height"),10)+t-28):(u.trigger("focus"),parseInt(u.css("height"),10)),v.off(".wp-editor-resize"),t&&50<t&&t<5e3&&setUserSetting("ed_size",t))}postboxes.add_postbox_toggles(pagenow),window.name="",h("#post-lock-dialog .notification-dialog").on("keydown",function(t){var e;9==t.which&&((e=h(t.target)).hasClass("wp-tab-first")&&t.shiftKey?(h(this).find(".wp-tab-last").trigger("focus"),t.preventDefault()):e.hasClass("wp-tab-last")&&!t.shiftKey&&(h(this).find(".wp-tab-first").trigger("focus"),t.preventDefault()))}).filter(":visible").find(".wp-tab-first").trigger("focus"),wp.heartbeat&&h("#post-lock-dialog").length&&wp.heartbeat.interval(10),i=m.find(":submit, a.submitdelete, #post-preview").on("click.edit-post",function(t){var e=h(this);e.hasClass("disabled")?t.preventDefault():e.hasClass("submitdelete")||e.is("#post-preview")||h("form#post").off("submit.edit-post").on("submit.edit-post",function(t){if(!t.isDefaultPrevented()){if(wp.autosave&&wp.autosave.server.suspend(),"undefined"!=typeof commentReply){if(!commentReply.discardCommentChanges())return!1;commentReply.close()}w=!1,h(window).off("beforeunload.edit-post"),i.addClass("disabled"),("publish"===e.attr("id")?m.find("#major-publishing-actions .spinner"):m.find("#minor-publishing .spinner")).addClass("is-active")}})}),h("#post-preview").on("click.post-preview",function(t){var e=h(this),i=h("form#post"),a=h("input#wp-preview"),n=e.attr("target")||"wp-preview",s=navigator.userAgent.toLowerCase();t.preventDefault(),e.hasClass("disabled")||(wp.autosave&&wp.autosave.server.tempBlockSave(),a.val("dopreview"),i.attr("target",n).trigger("submit").attr("target",""),-1!==s.indexOf("safari")&&-1===s.indexOf("chrome")&&i.attr("action",function(t,e){return e+"?t="+(new Date).getTime()}),a.val(""))}),h("#auto_draft").val()&&h("#title").on("blur",function(){var t;this.value&&!h("#edit-slug-box > *").length&&(h("form#post").one("submit",function(){t=!0}),window.setTimeout(function(){!t&&wp.autosave&&wp.autosave.server.triggerSave()},200))}),v.on("autosave-disable-buttons.edit-post",function(){i.addClass("disabled")}).on("autosave-enable-buttons.edit-post",function(){wp.heartbeat&&wp.heartbeat.hasConnectionError()||i.removeClass("disabled")}).on("before-autosave.edit-post",function(){h(".autosave-message").text(x("Saving Draft\u2026"))}).on("after-autosave.edit-post",function(t,e){h(".autosave-message").text(e.message),h(document.body).hasClass("post-new-php")&&h(".submitbox .submitdelete").show()}),h(window).on("beforeunload.edit-post",function(t){var e=window.tinymce&&window.tinymce.get("content"),i=!1;if(wp.autosave?i=wp.autosave.server.postChanged():e&&(i=!e.isHidden()&&e.isDirty()),i)return t.preventDefault(),x("The changes you made will be lost if you navigate away from this page.")}).on("pagehide.edit-post",function(t){if(w&&(!t.target||"#document"==t.target.nodeName)){var t=h("#post_ID").val(),e=h("#active_post_lock").val();if(t&&e){t={action:"wp-remove-post-lock",_wpnonce:h("#_wpnonce").val(),post_ID:t,active_post_lock:e};if(window.FormData&&window.navigator.sendBeacon){var i=new window.FormData;if(h.each(t,function(t,e){i.append(t,e)}),window.navigator.sendBeacon(ajaxurl,i))return}h.post({async:!1,data:t,url:ajaxurl})}}}),h("#tagsdiv-post_tag").length?window.tagBox&&window.tagBox.init():h(".meta-box-sortables").children("div.postbox").each(function(){if(0===this.id.indexOf("tagsdiv-"))return window.tagBox&&window.tagBox.init(),!1}),h(".categorydiv").each(function(){var t,a,e,i=h(this).attr("id").split("-");i.shift(),a=i.join("-"),e="category"==a?"cats":a+"_tab",h("a","#"+a+"-tabs").on("click",function(t){t.preventDefault();t=h(this).attr("href");h(this).parent().addClass("tabs").siblings("li").removeClass("tabs"),h("#"+a+"-tabs").siblings(".tabs-panel").hide(),h(t).show(),"#"+a+"-all"==t?deleteUserSetting(e):setUserSetting(e,"pop")}),getUserSetting(e)&&h('a[href="#'+a+'-pop"]',"#"+a+"-tabs").trigger("click"),h("#new"+a).one("focus",function(){h(this).val("").removeClass("form-input-tip")}),h("#new"+a).on("keypress",function(t){13===t.keyCode&&(t.preventDefault(),h("#"+a+"-add-submit").trigger("click"))}),h("#"+a+"-add-submit").on("click",function(){h("#new"+a).trigger("focus")}),i=function(t){return!!h("#new"+a).val()&&(t.data+="&"+h(":checked","#"+a+"checklist").serialize(),h("#"+a+"-add-submit").prop("disabled",!0),t)},t=function(t,e){var i=h("#new"+a+"_parent");h("#"+a+"-add-submit").prop("disabled",!1),"undefined"!=e.parsed.responses[0]&&(e=e.parsed.responses[0].supplemental.newcat_parent)&&(i.before(e),i.remove())},h("#"+a+"checklist").wpList({alt:"",response:a+"-ajax-response",addBefore:i,addAfter:t}),h("#"+a+"-add-toggle").on("click",function(t){t.preventDefault(),h("#"+a+"-adder").toggleClass("wp-hidden-children"),h('a[href="#'+a+'-all"]',"#"+a+"-tabs").trigger("click"),h("#new"+a).trigger("focus")}),h("#"+a+"checklist, #"+a+"checklist-pop").on("click",'li.popular-category > label input[type="checkbox"]',function(){var t=h(this),e=t.is(":checked"),i=t.val();i&&t.parents("#taxonomy-"+a).length&&(h("input#in-"+a+"-"+i+', input[id^="in-'+a+"-"+i+'-"]').prop("checked",e),h("input#in-popular-"+a+"-"+i).prop("checked",e))})}),h("#postcustom").length&&h("#the-list").wpList({addBefore:function(t){return t.data+="&post_id="+h("#post_ID").val(),t},addAfter:function(){h("table#list-table").show()}}),h("#submitdiv").length&&(d=h("#timestamp").html(),e=h("#post-visibility-display").html(),a=function(){"public"!=g.find("input:radio:checked").val()?(h("#sticky").prop("checked",!1),h("#sticky-span").hide()):h("#sticky-span").show(),"password"!=g.find("input:radio:checked").val()?h("#password-span").hide():h("#password-span").show()},n=function(){if(b.length){var t,e=h("#post_status"),i=h('option[value="publish"]',e),a=h("#aa").val(),n=h("#mm").val(),s=h("#jj").val(),o=h("#hh").val(),l=h("#mn").val(),r=new Date(a,n-1,s,o,l),c=new Date(h("#hidden_aa").val(),h("#hidden_mm").val()-1,h("#hidden_jj").val(),h("#hidden_hh").val(),h("#hidden_mn").val()),p=new Date(h("#cur_aa").val(),h("#cur_mm").val()-1,h("#cur_jj").val(),h("#cur_hh").val(),h("#cur_mn").val());if(r.getFullYear()!=a||1+r.getMonth()!=n||r.getDate()!=s||r.getMinutes()!=l)return b.find(".timestamp-wrap").addClass("form-invalid"),!1;b.find(".timestamp-wrap").removeClass("form-invalid"),p<r?(t=x("Schedule for:"),h("#publish").val(C("Schedule","post action/button label"))):r<=p&&"publish"!=h("#original_post_status").val()?(t=x("Publish on:"),h("#publish").val(x("Publish"))):(t=x("Published on:"),h("#publish").val(x("Update"))),c.toUTCString()==r.toUTCString()?h("#timestamp").html(d):h("#timestamp").html("\n"+t+" <b>"+x("%1$s %2$s, %3$s at %4$s:%5$s").replace("%1$s",h('option[value="'+n+'"]',"#mm").attr("data-text")).replace("%2$s",parseInt(s,10)).replace("%3$s",a).replace("%4$s",("00"+o).slice(-2)).replace("%5$s",("00"+l).slice(-2))+"</b> "),"private"==g.find("input:radio:checked").val()?(h("#publish").val(x("Update")),0===i.length?e.append('<option value="publish">'+x("Privately Published")+"</option>"):i.html(x("Privately Published")),h('option[value="publish"]',e).prop("selected",!0),h("#misc-publishing-actions .edit-post-status").hide()):("future"==h("#original_post_status").val()||"draft"==h("#original_post_status").val()?i.length&&(i.remove(),e.val(h("#hidden_post_status").val())):i.html(x("Published")),e.is(":hidden")&&h("#misc-publishing-actions .edit-post-status").show()),h("#post-status-display").text(wp.sanitize.stripTagsAndEncodeText(h("option:selected",e).text())),"private"==h("option:selected",e).val()||"publish"==h("option:selected",e).val()?h("#save-post").hide():(h("#save-post").show(),"pending"==h("option:selected",e).val()?h("#save-post").show().val(x("Save as Pending")):h("#save-post").show().val(x("Save Draft")))}return!0},h("#visibility .edit-visibility").on("click",function(t){t.preventDefault(),g.is(":hidden")&&(a(),g.slideDown("fast",function(){g.find('input[type="radio"]').first().trigger("focus")}),h(this).hide())}),g.find(".cancel-post-visibility").on("click",function(t){g.slideUp("fast"),h("#visibility-radio-"+h("#hidden-post-visibility").val()).prop("checked",!0),h("#post_password").val(h("#hidden-post-password").val()),h("#sticky").prop("checked",h("#hidden-post-sticky").prop("checked")),h("#post-visibility-display").html(e),h("#visibility .edit-visibility").show().trigger("focus"),n(),t.preventDefault()}),g.find(".save-post-visibility").on("click",function(t){var e="",i=g.find("input:radio:checked").val();switch(g.slideUp("fast"),h("#visibility .edit-visibility").show().trigger("focus"),n(),"public"!==i&&h("#sticky").prop("checked",!1),i){case"public":e=h("#sticky").prop("checked")?x("Public, Sticky"):x("Public");break;case"private":e=x("Private");break;case"password":e=x("Password Protected")}h("#post-visibility-display").text(e),t.preventDefault()}),g.find("input:radio").on("change",function(){a()}),b.siblings("a.edit-timestamp").on("click",function(t){b.is(":hidden")&&(b.slideDown("fast",function(){h("input, select",b.find(".timestamp-wrap")).first().trigger("focus")}),h(this).hide()),t.preventDefault()}),b.find(".cancel-timestamp").on("click",function(t){b.slideUp("fast").siblings("a.edit-timestamp").show().trigger("focus"),h("#mm").val(h("#hidden_mm").val()),h("#jj").val(h("#hidden_jj").val()),h("#aa").val(h("#hidden_aa").val()),h("#hh").val(h("#hidden_hh").val()),h("#mn").val(h("#hidden_mn").val()),n(),t.preventDefault()}),b.find(".save-timestamp").on("click",function(t){n()&&(b.slideUp("fast"),b.siblings("a.edit-timestamp").show().trigger("focus")),t.preventDefault()}),h("#post").on("submit",function(t){n()||(t.preventDefault(),b.show(),wp.autosave&&wp.autosave.enableButtons(),h("#publishing-action .spinner").removeClass("is-active"))}),k.siblings("a.edit-post-status").on("click",function(t){k.is(":hidden")&&(k.slideDown("fast",function(){k.find("select").trigger("focus")}),h(this).hide()),t.preventDefault()}),k.find(".save-post-status").on("click",function(t){k.slideUp("fast").siblings("a.edit-post-status").show().trigger("focus"),n(),t.preventDefault()}),k.find(".cancel-post-status").on("click",function(t){k.slideUp("fast").siblings("a.edit-post-status").show().trigger("focus"),h("#post_status").val(h("#hidden_post_status").val()),n(),t.preventDefault()})),h("#titlediv").on("click",".edit-slug",function(){var t,e,a,i,n=0,s=h("#post_name"),o=s.val(),l=h("#sample-permalink"),r=l.html(),c=h("#sample-permalink a").html(),p=h("#edit-slug-buttons"),d=p.html(),u=h("#editable-post-name-full");for(u.find("img").replaceWith(function(){return this.alt}),u=u.html(),l.html(c),a=h("#editable-post-name"),i=a.html(),p.html('<button type="button" class="save button button-small">'+x("OK")+'</button> <button type="button" class="cancel button-link">'+x("Cancel")+"</button>"),p.children(".save").on("click",function(){var i=a.children("input").val();i==h("#editable-post-name-full").text()?p.children(".cancel").trigger("click"):h.post(ajaxurl,{action:"sample-permalink",post_id:f,new_slug:i,new_title:h("#title").val(),samplepermalinknonce:h("#samplepermalinknonce").val()},function(t){var e=h("#edit-slug-box");e.html(t),e.hasClass("hidden")&&e.fadeIn("fast",function(){e.removeClass("hidden")}),p.html(d),l.html(r),s.val(i),h(".edit-slug").trigger("focus"),wp.a11y.speak(x("Permalink saved"))})}),p.children(".cancel").on("click",function(){h("#view-post-btn").show(),a.html(i),p.html(d),l.html(r),s.val(o),h(".edit-slug").trigger("focus")}),t=0;t<u.length;++t)"%"==u.charAt(t)&&n++;c=n>u.length/4?"":u,e=x("URL Slug"),a.html('<label for="new-post-slug" class="screen-reader-text">'+e+'</label><input type="text" id="new-post-slug" value="'+c+'" autocomplete="off" spellcheck="false" />').children("input").on("keydown",function(t){var e=t.which;13===e&&(t.preventDefault(),p.children(".save").trigger("click")),27===e&&p.children(".cancel").trigger("click")}).on("keyup",function(){s.val(this.value)}).trigger("focus")}),window.wptitlehint=function(t){var e=h("#"+(t=t||"title")),i=h("#"+t+"-prompt-text");""===e.val()&&i.removeClass("screen-reader-text"),e.on("input",function(){""===this.value?i.removeClass("screen-reader-text"):i.addClass("screen-reader-text")})},wptitlehint(),t=h("#post-status-info"),c=h("#postdivrich"),!u.length||"ontouchstart"in window?h("#content-resize-handle").hide():t.on("mousedown.wp-editor-resize",function(t){(o="undefined"!=typeof tinymce?tinymce.get("content"):o)&&!o.isHidden()?(r=!0,l=h("#content_ifr").height()-t.pageY):(r=!1,l=u.height()-t.pageY,u.trigger("blur")),v.on("mousemove.wp-editor-resize",D).on("mouseup.wp-editor-resize mouseleave.wp-editor-resize",j),t.preventDefault()}).on("mouseup.wp-editor-resize",j),"undefined"!=typeof tinymce&&(h("#post-formats-select input.post-format").on("change.set-editor-class",function(){var t,e,i=this.id;i&&h(this).prop("checked")&&(t=tinymce.get("content"))&&((e=t.getBody()).className=e.className.replace(/\bpost-format-[^ ]+/,""),t.dom.addClass(e,"post-format-0"==i?"post-format-standard":i),h(document).trigger("editor-classchange"))}),h("#page_template").on("change.set-editor-class",function(){var t,e,i=h(this).val()||"";(i=i.substr(i.lastIndexOf("/")+1,i.length).replace(/\.php$/,"").replace(/\./g,"-"))&&(t=tinymce.get("content"))&&((e=t.getBody()).className=e.className.replace(/\bpage-template-[^ ]+/,""),t.dom.addClass(e,"page-template-"+i),h(document).trigger("editor-classchange"))})),u.on("keydown.wp-autosave",function(t){83!==t.which||t.shiftKey||t.altKey||_&&(!t.metaKey||t.ctrlKey)||!_&&!t.ctrlKey||(wp.autosave&&wp.autosave.server.triggerSave(),t.preventDefault())}),"auto-draft"===h("#original_post_status").val()&&window.history.replaceState&&h("#publish").on("click",function(){p=(p=window.location.href)+(-1!==p.indexOf("?")?"&":"?")+"wp-post-new-reload=true",window.history.replaceState(null,null,p)}),y.on("success",function(t){var e=h(t.trigger),i=h(".success",e.closest(".copy-to-clipboard-container"));t.clearSelection(),clearTimeout(s),i.removeClass("hidden"),s=setTimeout(function(){i.addClass("hidden")},3e3),wp.a11y.speak(x("The file URL has been copied to your clipboard"))})}),function(t,o){t(function(){var i,e=t("#content"),a=t("#wp-word-count").find(".word-count"),n=0;function s(){var t=!i||i.isHidden()?e.val():i.getContent({format:"raw"}),t=o.count(t);t!==n&&a.text(t),n=t}t(document).on("tinymce-editor-init",function(t,e){"content"===e.id&&(i=e).on("nodechange keyup",_.debounce(s,1e3))}),e.on("input keyup",_.debounce(s,1e3)),s()})}(jQuery,new wp.utils.WordCounter); \ No newline at end of file