experiments = $experiments; $this->meta_boxes = $meta_boxes; $this->decoder = $decoder; $this->locale = $locale; $this->google_fonts = $google_fonts; $this->assets = $assets; $this->story_post_type = $story_post_type; $this->page_template_post_type = $page_template_post_type; $this->font_post_type = $font_post_type; $this->context = $context; $this->types = $types; $this->settings = $settings; } /** * Initializes the Editor logic. * * @since 1.7.0 */ public function register(): void { add_action( 'admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] ); add_filter( 'replace_editor', [ $this, 'replace_editor' ], 10, 2 ); add_filter( 'use_block_editor_for_post_type', [ $this, 'filter_use_block_editor_for_post_type' ], 10, 2 ); } /** * Get the list of service IDs required for this service to be registered. * * Needed because the story and page template post types need to be registered first. * * @since 1.14.0 * * @return string[] List of required services. */ public static function get_requirements(): array { return [ 'page_template_post_type', 'story_post_type' ]; } /** * Replace default post editor with our own implementation. * * @codeCoverageIgnore * * @since 1.0.0 * * @param bool|mixed $replace Bool if to replace editor or not. * @param WP_Post $post Current post object. * @return bool|mixed Whether the editor has been replaced. */ public function replace_editor( $replace, WP_Post $post ) { if ( $this->story_post_type->get_slug() === get_post_type( $post ) ) { $script_dependencies = [ Tracking::SCRIPT_HANDLE, 'postbox', self::AMP_VALIDATOR_SCRIPT_HANDLE, self::LIBHEIF_SCRIPT_HANDLE, ]; // Registering here because the script handle is required for wp_add_inline_script in edit-story.php. $this->assets->register_script_asset( self::SCRIPT_HANDLE, $script_dependencies, false ); // Since the 'replace_editor' filter can be run multiple times, only load the // custom editor after the 'current_screen' action and when we can be certain the // $post_type, $post_type_object, $post globals are all set by WordPress. if ( isset( $GLOBALS['post'] ) && $post === $GLOBALS['post'] && did_action( 'current_screen' ) ) { require_once WEBSTORIES_PLUGIN_DIR_PATH . 'includes/templates/admin/edit-story.php'; } return true; } return $replace; } /** * Filters whether post type supports the block editor. * * Disables the block editor and associated logic (like enqueueing assets) * for the story post type. * * @since 1.0.0 * * @param bool|mixed $use_block_editor Whether the post type can be edited or not. Default true. * @param string $post_type The post type being checked. * @return false|mixed Whether to use the block editor. */ public function filter_use_block_editor_for_post_type( $use_block_editor, string $post_type ) { if ( $this->story_post_type->get_slug() === $post_type ) { return false; } return $use_block_editor; } /** * Enqueue scripts for the element editor. * * @since 1.0.0 * * @param string $hook The current admin page. */ public function admin_enqueue_scripts( string $hook ): void { if ( ! $this->context->is_story_editor() ) { return; } // Only output scripts and styles where in edit screens. if ( ! \in_array( $hook, [ 'post.php', 'post-new.php' ], true ) ) { return; } // Force media model to load. wp_enqueue_media(); wp_enqueue_script( self::AMP_VALIDATOR_SCRIPT_HANDLE, 'https://cdn.ampproject.org/v0/validator_wasm.js', [], WEBSTORIES_VERSION, true ); wp_enqueue_script( self::LIBHEIF_SCRIPT_HANDLE, trailingslashit( WEBSTORIES_CDN_URL ) . 'js/libheif-js@1.14.0/libheif.js', [], WEBSTORIES_VERSION, true ); wp_enqueue_script( self::SCRIPT_HANDLE ); $this->assets->enqueue_style_asset( self::SCRIPT_HANDLE, [ $this->google_fonts::SCRIPT_HANDLE ] ); wp_localize_script( self::SCRIPT_HANDLE, 'webStories', [ 'publicPath' => $this->assets->get_base_url( 'assets/js/' ), // Required before the editor script is enqueued. 'localeData' => $this->assets->get_translations( self::SCRIPT_HANDLE ), // Required for i18n setLocaleData. ] ); // Dequeue forms.css, see https://github.com/googleforcreators/web-stories-wp/issues/349 . $this->assets->remove_admin_style( [ 'forms' ] ); } /** * Get editor settings as an array. * * @SuppressWarnings(PHPMD.ExcessiveMethodLength) * * @since 1.0.0 * * @return array Editor settings. */ public function get_editor_settings(): array { $post = get_post(); $story_id = $post->ID ?? null; $general_settings_url = admin_url( 'options-general.php' ); if ( $story_id ) { $this->setup_lock( $story_id ); } // Media settings. $max_upload_size = wp_max_upload_size(); if ( ! $max_upload_size ) { $max_upload_size = 0; } $dashboard_url = add_query_arg( [ 'post_type' => $this->story_post_type->get_slug(), 'page' => 'stories-dashboard', ], admin_url( 'edit.php' ) ); $revision_url = admin_url( 'revision.php' ); $dashboard_settings_url = add_query_arg( [ 'post_type' => $this->story_post_type->get_slug(), 'page' => 'stories-dashboard#/editor-settings', ], admin_url( 'edit.php' ) ); $user = wp_get_current_user(); /** This filter is documented in wp-admin/includes/post.php */ $show_locked_dialog = apply_filters( 'show_post_locked_dialog', true, $post, $user ); $nonce = wp_create_nonce( 'wp_rest' ); $story = new Story(); $story->load_from_post( $post ); // Explicitly setting these flags which became the default in PHP 8.1. // Needed for correct single quotes in the editor & output. // See https://github.com/GoogleForCreators/web-stories-wp/issues/10809. $publisher_name = html_entity_decode( $story->get_publisher_name(), ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ); $shopping_provider = $this->settings->get_setting( $this->settings::SETTING_NAME_SHOPPING_PROVIDER ); $auto_advance = $this->settings->get_setting( $this->settings::SETTING_NAME_AUTO_ADVANCE ); $page_duration = $this->settings->get_setting( $this->settings::SETTING_NAME_DEFAULT_PAGE_DURATION ); $auto_save_link = ''; if ( isset( $story_id ) ) { $auto_save = wp_get_post_autosave( $story_id ); if ( $auto_save && $post ) { if ( mysql2date( 'U', $auto_save->post_modified_gmt, false ) > mysql2date( 'U', $post->post_modified_gmt, false ) ) { $auto_save_link = get_edit_post_link( $auto_save->ID ); } else { wp_delete_post_revision( $auto_save->ID ); } } } /** * Revision. * * @var int $revision */ $revision = isset( $_GET['revision'] ) ? absint( $_GET['revision'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended $revision_message = ! empty( $revision ) ? sprintf( /* translators: %s: Date and time of the revision. */ __( 'Story restored to revision from %s.', 'web-stories' ), wp_post_revision_title( $revision, false ) ) : false; $settings = [ 'autoSaveInterval' => \defined( 'AUTOSAVE_INTERVAL' ) ? AUTOSAVE_INTERVAL : null, 'localAutoSaveInterval' => 15, 'autoSaveLink' => $auto_save_link, 'isRTL' => is_rtl(), 'locale' => $this->locale->get_locale_settings(), 'allowedMimeTypes' => $this->types->get_allowed_mime_types(), 'postType' => $this->story_post_type->get_slug(), 'storyId' => $story_id, 'dashboardLink' => $dashboard_url, 'revisionLink' => $revision_url, 'revisionMessage' => $revision_message, 'dashboardSettingsLink' => $dashboard_settings_url, 'generalSettingsLink' => $general_settings_url, 'cdnURL' => trailingslashit( WEBSTORIES_CDN_URL ), 'maxUpload' => $max_upload_size, 'editPostsCapabilityName' => $this->story_post_type->get_cap_name( 'edit_posts' ), 'capabilities' => [ 'hasUploadMediaAction' => current_user_can( 'upload_files' ), 'canManageSettings' => current_user_can( 'manage_options' ), ], 'api' => [ 'users' => '/web-stories/v1/users/', 'currentUser' => '/web-stories/v1/users/me/', 'stories' => trailingslashit( $this->story_post_type->get_rest_url() ), 'pageTemplates' => trailingslashit( $this->page_template_post_type->get_rest_url() ), 'media' => '/web-stories/v1/media/', 'hotlink' => '/web-stories/v1/hotlink/validate/', 'publisherLogos' => '/web-stories/v1/publisher-logos/', 'products' => '/web-stories/v1/products/', 'proxy' => rest_url( '/web-stories/v1/hotlink/proxy/' ), 'link' => '/web-stories/v1/link/', 'statusCheck' => '/web-stories/v1/status-check/', 'taxonomies' => '/web-stories/v1/taxonomies/', 'fonts' => trailingslashit( $this->font_post_type->get_rest_url() ), 'metaBoxes' => $this->meta_boxes->get_meta_box_url( (int) $story_id ), 'storyLocking' => rest_url( sprintf( '%s/%s/lock/', $this->story_post_type->get_rest_url(), $story_id ) ), ], 'metadata' => [ 'publisher' => $publisher_name, ], 'postLock' => [ 'interval' => 60, 'showLockedDialog' => $show_locked_dialog, ], 'canViewDefaultTemplates' => true, 'version' => WEBSTORIES_VERSION, 'nonce' => $nonce, 'showMedia3p' => true, 'globalAutoAdvance' => (bool) $auto_advance, 'globalPageDuration' => (float) $page_duration, 'shoppingProvider' => $shopping_provider, 'encodeMarkup' => $this->decoder->supports_decoding(), 'metaBoxes' => $this->meta_boxes->get_meta_boxes_per_location(), 'ffmpegCoreUrl' => trailingslashit( WEBSTORIES_CDN_URL ) . 'js/@ffmpeg/core@0.11.0/dist/ffmpeg-core.js', 'mediainfoUrl' => trailingslashit( WEBSTORIES_CDN_URL ) . 'js/mediainfo.js@0.1.9/dist/MediaInfoModule.wasm', 'flags' => array_merge( $this->experiments->get_experiment_statuses( 'general' ), $this->experiments->get_experiment_statuses( 'editor' ), ), ]; /** * Filters settings passed to the web stories editor. * * @since 1.0.0 * * @param array $settings Array of settings passed to web stories editor. */ return apply_filters( 'web_stories_editor_settings', $settings ); } /** * Setup up post lock. * * @since 1.5.0 * * @param int $story_id Post id of story. */ protected function setup_lock( int $story_id ): void { if ( ! $this->story_post_type->has_cap( 'edit_posts' ) ) { return; } // Make sure these functions are loaded. if ( ! \function_exists( '\wp_check_post_lock' ) || ! \function_exists( '\wp_set_post_lock' ) ) { require_once ABSPATH . 'wp-admin/includes/post.php'; } // Check current lock. $lock_user_id = wp_check_post_lock( $story_id ); if ( ! $lock_user_id ) { // If no lock set, create new lock. wp_set_post_lock( $story_id ); } } }