$settings Editor settings, returned unchanged. * @param mixed $context Editor context from the filter, checked here. * @return array */ public static function remember_block( $settings, $context ) { if ( $context instanceof WP_Block_Editor_Context && $context->post instanceof WP_Post ) { self::remember( $context->post, 'block' ); } return $settings; } /** * Stores the editor a post was opened in, only for users who may edit that post. * * @param mixed $post Post from an action or filter, checked here. * @param string $editor 'classic' or 'block'. */ private static function remember( $post, $editor ): void { if ( ! $post instanceof WP_Post || ! current_user_can( 'edit_post', $post->ID ) ) { return; } if ( get_post_meta( $post->ID, self::POST_META, true ) !== $editor ) { update_post_meta( $post->ID, self::POST_META, $editor ); } } /** * Editor choice on the profile screen, shown only while users may choose. * * @param WP_User $user Profile being shown. */ public static function user_field( $user ): void { if ( ! self::settings()['allow_users'] || ! current_user_can( 'edit_user', $user->ID ) ) { return; } $current = self::user_editor( $user->ID ); ?> 'array', 'sanitize_callback' => array( __CLASS__, 'sanitize_settings' ), 'show_in_rest' => false, ) ); } /** * Keeps only known keys with valid values. * * @param mixed $input Raw value. * @return array{editor: string, allow_users: bool} */ public static function sanitize_settings( $input ) { $input = is_array( $input ) ? $input : array(); return array( 'editor' => ( isset( $input['editor'] ) && 'block' === $input['editor'] ) ? 'block' : 'classic', 'allow_users' => ! empty( $input['allow_users'] ), ); } /** * Site settings with defaults. * * @return array{editor: string, allow_users: bool} */ public static function settings() { $forced = self::forced(); return null !== $forced ? $forced : self::sanitize_settings( get_option( self::OPTION, array() ) ); } /** * Settings fixed by code, or null. * * Hosts, themes and agency plugins use the filter of the plugin "Classic Editor" * (keys editor, allow-users); ours, threece_forced_settings (keys editor, allow_users), wins. * * @return array{editor: string, allow_users: bool}|null */ public static function forced() { /** * Fixes the editor settings and locks the settings page. * * @param array|false $settings Array with 'editor' ('classic'|'block') and 'allow_users' (bool), or false. */ $own = apply_filters( 'threece_forced_settings', false ); if ( is_array( $own ) ) { return self::sanitize_settings( $own ); } $classic_editor = apply_filters( 'classic_editor_plugin_settings', false ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- filter of the plugin "Classic Editor", read for compatibility. if ( is_array( $classic_editor ) ) { return self::sanitize_settings( array( 'editor' => $classic_editor['editor'] ?? '', 'allow_users' => $classic_editor['allow-users'] ?? false, ) ); } return null; } /** * Filter for use_block_editor_for_post. * * @param bool $use_block_editor Core decision. * @param mixed $post Post being edited (checked here, filters may hand on anything). * @return bool */ public static function use_block_editor( $use_block_editor, $post ) { if ( ! $post instanceof WP_Post || in_array( $post->post_type, self::BLOCK_ONLY, true ) ) { return $use_block_editor; } $editors = self::enabled_editors( $post->post_type, $post ); if ( ! $editors['classic_editor'] && $editors['block_editor'] ) { return true; } if ( ! $use_block_editor || ! $editors['block_editor'] ) { return false; } return 'block' === self::editor_for( $post ); } /** * Filter for use_block_editor_for_post_type: with "classic for everyone" the whole type counts as classic, like * the plugin Classic Editor does, so the dashboard quick draft stays plain HTML and other plugins see the same * answer on the type level. With a user choice the type level stays with core; a plugin that locks a type to one * editor (filters below) wins either way. * * @param bool $use_block_editor Core decision for the type. * @param string $post_type Post type. * @return bool */ public static function use_block_editor_for_type( $use_block_editor, $post_type ) { if ( in_array( (string) $post_type, self::BLOCK_ONLY, true ) ) { return $use_block_editor; } $editors = self::enabled_editors( (string) $post_type, null ); if ( ! $editors['classic_editor'] && $editors['block_editor'] ) { return true; } if ( ! $use_block_editor || ! $editors['block_editor'] ) { return false; } $settings = self::settings(); return $settings['allow_users'] || 'block' === $settings['editor']; } /** * Which editors a post type, and optionally one post, may use. Reads the filters of the plugin Classic Editor * (classic_editor_enabled_editors_for_post_type, …_for_post), so a site moving over keeps what its plugins set, * and offers the same under our own names (threece_enabled_editors_for_post_type, …_for_post). * * @param string $post_type Post type. * @param WP_Post|null $post Post, when the decision is about one post. * @return array{classic_editor: bool, block_editor: bool} */ public static function enabled_editors( $post_type, $post ) { $editors = array( 'classic_editor' => post_type_supports( $post_type, 'editor' ), 'block_editor' => true, ); // phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- filters of the plugin "Classic Editor", read for compatibility. $editors = self::clean_editors( apply_filters( 'classic_editor_enabled_editors_for_post_type', $editors, $post_type ), $editors ); /** * Filters which editors a post type may use (keys classic_editor, block_editor). * * @param array $editors Editors and whether they are enabled. * @param string $post_type Post type. */ $editors = self::clean_editors( apply_filters( 'threece_enabled_editors_for_post_type', $editors, $post_type ), $editors ); if ( $post instanceof WP_Post ) { $editors = self::clean_editors( apply_filters( 'classic_editor_enabled_editors_for_post', $editors, $post ), $editors ); /** * Filters which editors one post may use (keys classic_editor, block_editor). * * @param array $editors Editors and whether they are enabled. * @param WP_Post $post Post. */ $editors = self::clean_editors( apply_filters( 'threece_enabled_editors_for_post', $editors, $post ), $editors ); } // phpcs:enable return $editors; } /** * Only the two known keys as booleans; anything else falls back on the value before the filter. * * @param mixed $filtered Filter result. * @param array{classic_editor: bool, block_editor: bool} $before Value before the filter. * @return array{classic_editor: bool, block_editor: bool} */ private static function clean_editors( $filtered, $before ) { if ( ! is_array( $filtered ) ) { return $before; } return array( 'classic_editor' => isset( $filtered['classic_editor'] ) ? (bool) $filtered['classic_editor'] : $before['classic_editor'], 'block_editor' => isset( $filtered['block_editor'] ) ? (bool) $filtered['block_editor'] : $before['block_editor'], ); } /** * Editor for a post: 'classic' or 'block'. * * @param WP_Post $post Post. * @return string */ public static function editor_for( $post ) { $settings = self::settings(); if ( ! $settings['allow_users'] ) { return $settings['editor']; } $wanted = self::wanted_from_link( $post ); if ( '' !== $wanted ) { return $wanted; } $last = self::post_editor( $post->ID ); if ( '' !== $last ) { return $last; } return self::user_editor( get_current_user_id() ); } /** * Editor requested by an "Edit (classic/block editor)" link from the post list, or ''. * * The link only counts with its nonce for this post and this user, and only for users who may * edit the post. Rights first, then the nonce, then the value. * * @param WP_Post $post Post. * @return string 'classic', 'block' or ''. */ private static function wanted_from_link( $post ) { if ( ! isset( $_GET['threece-editor'], $_GET[ self::LINK_NONCE ] ) || ! current_user_can( 'edit_post', $post->ID ) ) { return ''; } $nonce = sanitize_text_field( wp_unslash( $_GET[ self::LINK_NONCE ] ) ); if ( ! wp_verify_nonce( $nonce, self::link_action( $post->ID ) ) ) { return ''; } $wanted = sanitize_key( wp_unslash( $_GET['threece-editor'] ) ); return ( 'block' === $wanted || 'classic' === $wanted ) ? $wanted : ''; } }