[ 'label' => __('Lesson', 'fluent-community'), 'public' => false, 'show_in_rest' => true, 'supports' => ['title', 'editor', 'thumbnail'], ], 'fcom-lockscreen' => [ 'label' => __('Lock Screen', 'fluent-community'), 'public' => false, 'show_in_rest' => true, 'supports' => ['editor'], ], ]); foreach ($editorPostTypes as $editorPostType => $editorPostTypeArgs) { register_post_type($editorPostType, $editorPostTypeArgs); } if (!isset($_REQUEST['fluent_community_block_editor'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended return; } if (!defined('IFRAME_REQUEST')) { define('IFRAME_REQUEST', true); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound } remove_action('enqueue_block_editor_assets', 'wp_enqueue_editor_block_directory_assets'); add_action('fluent_community/block_editor_head', function () { $url = FLUENT_COMMUNITY_PLUGIN_URL . 'Modules/Gutenberg/editor/index.css'; $contentStylingUrl = FLUENT_COMMUNITY_PLUGIN_URL . 'Modules/Gutenberg/editor/content_styling.css'; // phpcs:disable WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet -- emitted into a custom non-wp_head action hook, wp_enqueue_style not applicable ?> renderCustomEditor($_REQUEST); // phpcs:ignore WordPress.Security.NonceVerification.Recommended $actionHook = 'template_redirect'; if(is_admin()) { $actionHook = 'admin_init'; } add_action($actionHook, function () { $this->renderPage(); exit(200); }, -1000); }, 2); } public function renderCustomEditor($data = []) { do_action('litespeed_control_set_nocache', 'fluentcommunity api request'); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound // set no cache headers nocache_headers(); $hasAccess = false; $postType = 'fcom-dummy'; $context = Arr::get($data, 'context'); $lesson = null; if ($context === 'course_lesson') { $lessonId = Arr::get($data, 'lesson_id'); if ($lessonId) { $lesson = CourseLesson::find($lessonId); $hasAccess = $lesson && $lesson->course && $lesson->course->isCourseAdmin(); } } if ($context === 'lockscreen') { $postType = 'fcom-lockscreen'; $spaceId = Arr::get($data, 'space_id'); if ($spaceId) { $space = BaseSpace::query()->onlyMain()->find($spaceId); $hasAccess = $space && $space->isAdmin(get_current_user_id(), true); } } /* * Extension point for editor contexts registered outside this plugin (the pro * Space Pages module uses it). Handlers matching $context return their own * access decision, post type and the content to seed the simulated post with. */ $contextConfig = apply_filters('fluent_community/block_editor_context', [ 'has_access' => $hasAccess, 'post_type' => $postType, 'has_content' => false, 'title' => '', 'content' => '', ], $context, $data); $hasAccess = !empty($contextConfig['has_access']); $this->contextEditorVars = (array)Arr::get($contextConfig, 'editor_vars', []); $contextPostType = Arr::get($contextConfig, 'post_type'); if ($contextPostType) { $postType = $contextPostType; } if (!$hasAccess) { echo '
'; } else { $newPostId = wp_insert_post(array( 'post_title' => $context === 'course_lesson' ? 'Demo Lesson Title' : '', 'post_content' => '
', 'post_type' => $postType, 'post_status' => 'draft', )); $simulatedPost = get_post($newPostId); } global $post; $post = $simulatedPost; if ($lesson) { $post->post_title = $lesson->title; $post->post_content = $lesson->message ?: '
'; } elseif (!empty($contextConfig['has_content'])) { $contextContent = Arr::get($contextConfig, 'content'); $post->post_title = Arr::get($contextConfig, 'title', ''); $post->post_content = $contextContent ? $contextContent : '
'; } // renderPage() exits during admin_init and manually fires wp_enqueue_scripts in // both the admin and frontend branches. admin_enqueue_scripts never runs here, so // the apiFetch preload must ride wp_enqueue_scripts or the editor falls back to a // live GET /wp/v2/fcom-dummy/{id}?context=edit — which 403s for users without // edit_others_posts (e.g. contributors) on the shared, admin-authored dummy post. add_action('wp_enqueue_scripts', function () use ($post) { wp_enqueue_script('postbox', admin_url('js/postbox.min.js'), array('jquery-ui-sortable'), FLUENT_COMMUNITY_PLUGIN_VERSION, true); wp_enqueue_style('dashicons'); wp_enqueue_style('media'); wp_enqueue_style('admin-menu'); wp_enqueue_style('admin-bar'); wp_enqueue_style('l10n'); wp_add_inline_script( 'wp-api-fetch', \sprintf( 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );', wp_json_encode( array( '/wp/v2/' . $post->post_type . '/' . $post->ID . '?context=edit' => array( 'body' => array( 'id' => $post->ID, 'title' => array('raw' => $post->post_title), 'content' => array( 'block_format' => 1, 'raw' => $post->post_content, ), 'excerpt' => array('raw' => ''), 'date' => '', 'date_gmt' => '', 'modified' => '', 'modified_gmt' => '', 'link' => home_url('/'), 'guid' => array(), 'parent' => 0, 'menu_order' => 0, 'author' => 0, 'featured_media' => 0, 'comment_status' => 'closed', 'ping_status' => 'closed', 'template' => '', 'meta' => array(), '_links' => array(), 'type' => $post->post_type, 'status' => 'pending', // pending is the best state to remove draft saving possibilities. 'slug' => '', 'generated_slug' => '', 'permalink_template' => home_url('/'), ), ), ) ) ), 'after' ); }, 11); add_action('wp_enqueue_scripts', function ($hook) use ($post) { // Gutenberg requires the post-locking functions defined within: // See `show_post_locked_dialog` and `get_post_metadata` filters below. include_once ABSPATH . 'wp-admin/includes/post.php'; $this->gutenberg_editor_scripts_and_styles($hook, $post); }); // Disable post locking dialogue. add_filter('show_post_locked_dialog', '__return_false'); // Everyone can richedit! This avoids a case where a page can be cached where a user can't richedit. $GLOBALS['wp_rich_edit'] = true; add_filter('user_can_richedit', '__return_true', 1000); // Homepage is always locked by @wordpressdotorg // This prevents other logged-in users taking a lock of the post on the front-end. add_filter('get_post_metadata', function ($value, $post_id, $meta_key) { if ($meta_key !== '_edit_lock') { return $value; } return time() . ':' . get_current_user_id(); // WordPressdotorg user ID }, 10, 3); // Disable Jetpack Blocks for now. add_filter('jetpack_gutenberg', '__return_false'); } private function gutenberg_editor_scripts_and_styles($hook, $post) { $initial_edits = array( 'title' => $post->post_title, 'content' => $post->post_content, 'excerpt' => $post->post_excerpt, ); $editor_settings = $this->getEditorSettings($post); $init_script = "(function() { window._wpLoadBlockEditor = new Promise(function(resolve) { wp.domReady(function() { resolve(wp.editPost.initializeEditor('editor', \"%s\", %d, %s, %s)); }); }); })();"; $script = sprintf( $init_script, $post->post_type, $post->ID, wp_json_encode($editor_settings), wp_json_encode($initial_edits) ); wp_add_inline_script('wp-edit-post', $script); /** * Scripts */ wp_enqueue_media(); add_filter('user_can_richedit', '__return_true'); wp_tinymce_inline_scripts(); wp_enqueue_editor(); /** * Styles */ wp_enqueue_style('wp-edit-post'); // Include block styles needed when user is not signed in. See: https://github.com/WordPress/wporg-gutenberg/issues/26 wp_enqueue_style('global-styles'); wp_enqueue_style('wp-block-library'); wp_enqueue_style('wp-block-image'); wp_enqueue_style('wp-block-group'); wp_enqueue_style('wp-block-heading'); wp_enqueue_style('wp-block-button'); wp_enqueue_style('wp-block-paragraph'); wp_enqueue_style('wp-block-separator'); wp_enqueue_style('wp-block-columns'); wp_enqueue_style('wp-block-cover'); wp_enqueue_style('global-styles-css-custom-properties'); wp_enqueue_style('wp-block-spacer'); wp_register_style('fluent_com_editor_styles', FLUENT_COMMUNITY_PLUGIN_URL . 'Modules/Gutenberg/editor/style.css', [], FLUENT_COMMUNITY_PLUGIN_VERSION, 'all'); add_action('fluent_enqueue_block_editor_assets', 'wp_enqueue_editor_format_library_assets'); // WP 6.9+ loads the LaTeX-to-MathML converter as a script module; core wires this on // 'enqueue_block_editor_assets', which this standalone editor page never fires. if (function_exists('wp_enqueue_block_editor_script_modules')) { add_action('fluent_enqueue_block_editor_assets', 'wp_enqueue_block_editor_script_modules'); } /** * Fires after block assets have been enqueued for the editing interface. * * Call `add_action` on any hook before 'admin_enqueue_scripts'. * * In the function call you supply, simply use `wp_enqueue_script` and * `wp_enqueue_style` to add your functionality to the Gutenberg editor. * * @since 0.4.0 */ do_action('fluent_enqueue_block_editor_assets'); wp_enqueue_script('fcom_editor_custom', FLUENT_COMMUNITY_PLUGIN_URL . 'Modules/Gutenberg/editor/index.js', ['react', 'wp-components', 'wp-compose', 'wp-data', 'wp-edit-post', 'wp-i18n', 'wp-plugins'], FLUENT_COMMUNITY_PLUGIN_VERSION . time(), true); wp_localize_script('fcom_editor_custom', 'fcomEditorI18n', $this->getEditorI18nStrings()); wp_localize_script('fcom_editor_custom', 'fcomEditorVars', array_merge([ 'video_gate_default_threshold' => \FluentCommunity\Modules\Course\Services\LessonVideoGateService::getDefaultThreshold(), 'can_unfiltered_html' => current_user_can('unfiltered_html') ? 'yes' : 'no', ], $this->contextEditorVars)); } private function getEditorI18nStrings() { $strings = [ 'Enable Video Embed' => __('Enable Video Embed', 'fluent-community'), 'Enable Comments' => __('Enable Comments', 'fluent-community'), 'Free Preview Lesson' => __('Free Preview Lesson', 'fluent-community'), 'If enabled, public users can view this lesson without enrolling the course.' => __('If enabled, public users can view this lesson without enrolling the course.', 'fluent-community'), 'Media Embed' => __('Media Embed', 'fluent-community'), 'Documents & Files' => __('Documents & Files', 'fluent-community'), 'Smartcodes' => __('Smartcodes', 'fluent-community'), 'You may use following smartcode in your lesson:' => __('You may use following smartcode in your lesson:', 'fluent-community'), "User's Name:" => __("User's Name:", 'fluent-community'), "User's Email:" => __("User's Email:", 'fluent-community'), "User's photo HTML:" => __("User's photo HTML:", 'fluent-community'), "User's Profile Link:" => __("User's Profile Link:", 'fluent-community'), 'Failed to fetch embed. Please check the URL.' => __('Failed to fetch embed. Please check the URL.', 'fluent-community'), 'Video thumbnail' => __('Video thumbnail', 'fluent-community'), 'Media embedded successfully' => __('Media embedded successfully', 'fluent-community'), '+ Add Media' => __('+ Add Media', 'fluent-community'), 'Add Media' => __('Add Media', 'fluent-community'), 'Edit Media' => __('Edit Media', 'fluent-community'), 'Oembed URL' => __('Oembed URL', 'fluent-community'), 'Video URL' => __('Video URL', 'fluent-community'), 'Supports Vimeo, YouTube, Wistia and more' => __('Supports Vimeo, YouTube, Wistia and more', 'fluent-community'), 'Embedding…' => __('Embedding…', 'fluent-community'), 'Cancel' => __('Cancel', 'fluent-community'), 'Remove' => __('Remove', 'fluent-community'), 'Oembed' => __('Oembed', 'fluent-community'), 'Custom HTML' => __('Custom HTML', 'fluent-community'), 'Custom HTML Code' => __('Custom HTML Code', 'fluent-community'), 'Your account cannot save custom HTML. Scripts, iframes and similar tags are removed on save.' => __('Your account cannot save custom HTML. Scripts, iframes and similar tags are removed on save.', 'fluent-community'), 'Paste an iframe code' => __('Paste an iframe code', 'fluent-community'), 'Embed' => __('Embed', 'fluent-community'), 'Paste a URL to embed' => __('Paste a URL to embed', 'fluent-community'), 'Embed from Vimeo, YouTube, Wistia and more' => __('Embed from Vimeo, YouTube, Wistia and more', 'fluent-community'), 'FluentPlayer' => __('FluentPlayer', 'fluent-community'), 'Select Player Block' => __('Select Player Block', 'fluent-community'), 'Use FluentPlayer Block' => __('Use FluentPlayer Block', 'fluent-community'), 'The feature video is managed by the FluentPlayer block in the editor.' => __('The feature video is managed by the FluentPlayer block in the editor.', 'fluent-community'), 'A FluentPlayer block will be added at the top of the lesson content. Add or edit the video directly from that block in the editor.' => __('A FluentPlayer block will be added at the top of the lesson content. Add or edit the video directly from that block in the editor.', 'fluent-community'), 'Video Completion' => __('Video Completion', 'fluent-community'), 'Require video watch to complete' => __('Require video watch to complete', 'fluent-community'), 'Students must watch the video before they can mark this lesson as completed.' => __('Students must watch the video before they can mark this lesson as completed.', 'fluent-community'), 'Auto-complete lesson when the video ends' => __('Auto-complete lesson when the video ends', 'fluent-community'), 'Marks the lesson complete a few seconds after the student watches the video to the end.' => __('Marks the lesson complete a few seconds after the student watches the video to the end.', 'fluent-community'), 'Required watch percentage' => __('Required watch percentage', 'fluent-community'), '100% means the video must be watched to the end.' => __('100% means the video must be watched to the end.', 'fluent-community'), 'Lesson Duration' => __('Lesson Duration', 'fluent-community'), 'Minutes' => __('Minutes', 'fluent-community'), 'Seconds' => __('Seconds', 'fluent-community'), 'View' => __('View', 'fluent-community'), 'No documents attached yet.' => __('No documents attached yet.', 'fluent-community'), 'Manage Documents & Files' => __('Manage Documents & Files', 'fluent-community'), "User's Name" => __("User's Name", 'fluent-community'), "User's Email" => __("User's Email", 'fluent-community'), "User's photo HTML" => __("User's photo HTML", 'fluent-community'), 'Profile Link' => __('Profile Link', 'fluent-community'), // Space page settings panel 'Who can view this page' => __('Who can view this page', 'fluent-community'), 'Everyone who can view the space' => __('Everyone who can view the space', 'fluent-community'), 'Space members only' => __('Space members only', 'fluent-community'), 'URL Slug' => __('URL Slug', 'fluent-community'), 'Changing the slug will break existing links to this page.' => __('Changing the slug will break existing links to this page.', 'fluent-community'), 'Show in space menu' => __('Show in space menu', 'fluent-community'), 'Menu Label' => __('Menu Label', 'fluent-community'), 'Leave empty to use the page title.' => __('Leave empty to use the page title.', 'fluent-community'), 'SEO' => __('SEO', 'fluent-community'), 'SEO Description' => __('SEO Description', 'fluent-community'), 'Used for search engines and link previews' => __('Used for search engines and link previews', 'fluent-community'), // Space page layout picker. The template labels and descriptions are // localized by whoever registers them (SpacePageHelper::getLayoutTemplates // in pro); only the fallback names used when pro is absent live here. 'Page Layout' => __('Page Layout', 'fluent-community'), 'Show the page title' => __('Show the page title', 'fluent-community'), 'Show the featured image' => __('Show the featured image', 'fluent-community'), 'The title stays editable here and still names the page in the menu.' => __('The title stays editable here and still names the page in the menu.', 'fluent-community'), 'The image still appears in search results and link previews.' => __('The image still appears in search results and link previews.', 'fluent-community'), 'Standard' => __('Standard', 'fluent-community'), 'Classic' => __('Classic', 'fluent-community'), 'Full Width' => __('Full Width', 'fluent-community'), 'Unified' => __('Unified', 'fluent-community'), ]; return apply_filters('fluent_community/editor_i18n_strings', $strings); } private function gutenberg_get_available_image_sizes() { $size_names = apply_filters( 'fluent_community/image_size_names_choose', array( 'thumbnail' => __('Thumbnail', 'fluent-community'), 'medium' => __('Medium', 'fluent-community'), 'large' => __('Large', 'fluent-community'), 'full' => __('Full Size', 'fluent-community'), ) ); $all_sizes = array(); foreach ($size_names as $size_slug => $size_name) { $all_sizes[] = array( 'slug' => $size_slug, 'name' => $size_name, ); } return $all_sizes; } protected function renderPage() { remove_action( 'wp_print_styles', 'print_emoji_styles' ); add_action('fluent_community/block_editor_footer', function () { wp_underscore_playlist_templates(); wp_print_footer_scripts(); $this->printScriptModules(); wp_print_media_templates(); wp_enqueue_global_styles(); if (function_exists('wp_enqueue_stored_styles')) { wp_enqueue_stored_styles(); } wp_maybe_inline_styles(); }); add_action('fluent_block_editor/head', 'wp_enqueue_scripts', 1); add_action('fluent_block_editor/head', 'wp_resource_hints', 2); add_action('fluent_block_editor/head', 'wp_preload_resources', 1); add_action('fluent_block_editor/head', 'wp_print_styles', 8); add_action('fluent_block_editor/head', 'wp_print_head_scripts', 9); add_action('fluent_block_editor/head', 'wp_custom_css_cb', 101); $this->unloadOtherScripts(); ?> >