| @@ -9,25 +9,40 @@ | ||
| 9 | 9 | use FluentCommunity\Modules\Course\Model\CourseLesson; |
| 10 | 10 | |
| 11 | 11 | class FluentBlockEditorHandler |
| 12 | 12 | { |
| 13 | + /** | |
| 14 | + * Extra fcomEditorVars entries contributed by whoever answered the | |
| 15 | + * fluent_community/block_editor_context filter. Stashed in renderCustomEditor() | |
| 16 | + * and merged in gutenberg_editor_scripts_and_styles(), which runs later in the | |
| 17 | + * same request off the wp_enqueue_scripts callback registered below. | |
| 18 | + * | |
| 19 | + * @var array | |
| 20 | + */ | |
| 21 | + private $contextEditorVars = []; | |
| 22 | + | |
| 13 | 23 | public function register() |
| 14 | 24 | { |
| 15 | 25 | add_action('init', function () { |
| 16 | 26 | |
| 17 | - register_post_type('fcom-dummy', [ | |
| 18 | - 'label' => 'Lesson', | |
| 19 | - 'public' => false, | |
| 20 | - 'show_in_rest' => true, | |
| 21 | - 'supports' => ['title', 'editor', 'thumbnail'], | |
| 27 | + $editorPostTypes = apply_filters('fluent_community/block_editor_post_types', [ | |
| 28 | + 'fcom-dummy' => [ | |
| 29 | + 'label' => __('Lesson', 'fluent-community'), | |
| 30 | + 'public' => false, | |
| 31 | + 'show_in_rest' => true, | |
| 32 | + 'supports' => ['title', 'editor', 'thumbnail'], | |
| 33 | + ], | |
| 34 | + 'fcom-lockscreen' => [ | |
| 35 | + 'label' => __('Lock Screen', 'fluent-community'), | |
| 36 | + 'public' => false, | |
| 37 | + 'show_in_rest' => true, | |
| 38 | + 'supports' => ['editor'], | |
| 39 | + ], | |
| 22 | 40 | ]); |
| 23 | 41 | |
| 24 | - register_post_type('fcom-lockscreen', [ | |
| 25 | - 'label' => 'Lockscreen', | |
| 26 | - 'public' => false, | |
| 27 | - 'show_in_rest' => true, | |
| 28 | - 'supports' => ['editor'], | |
| 29 | - ]); | |
| 42 | + foreach ($editorPostTypes as $editorPostType => $editorPostTypeArgs) { | |
| 43 | + register_post_type($editorPostType, $editorPostTypeArgs); | |
| 44 | + } | |
| 30 | 45 | |
| 31 | 46 | if (!isset($_REQUEST['fluent_community_block_editor'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 32 | 47 | return; |
| 33 | 48 | } |
| @@ -101,8 +116,30 @@ | ||
| 101 | 116 | $hasAccess = $space && $space->isAdmin(get_current_user_id(), true); |
| 102 | 117 | } |
| 103 | 118 | } |
| 104 | 119 | |
| 120 | + /* | |
| 121 | + * Extension point for editor contexts registered outside this plugin (the pro | |
| 122 | + * Space Pages module uses it). Handlers matching $context return their own | |
| 123 | + * access decision, post type and the content to seed the simulated post with. | |
| 124 | + */ | |
| 125 | + $contextConfig = apply_filters('fluent_community/block_editor_context', [ | |
| 126 | + 'has_access' => $hasAccess, | |
| 127 | + 'post_type' => $postType, | |
| 128 | + 'has_content' => false, | |
| 129 | + 'title' => '', | |
| 130 | + 'content' => '', | |
| 131 | + ], $context, $data); | |
| 132 | + | |
| 133 | + $hasAccess = !empty($contextConfig['has_access']); | |
| 134 | + | |
| 135 | + $this->contextEditorVars = (array)Arr::get($contextConfig, 'editor_vars', []); | |
| 136 | + | |
| 137 | + $contextPostType = Arr::get($contextConfig, 'post_type'); | |
| 138 | + if ($contextPostType) { | |
| 139 | + $postType = $contextPostType; | |
| 140 | + } | |
| 141 | + | |
| 105 | 142 | if (!$hasAccess) { |
| 106 | 143 | echo '<h3 style="padding: 100px; text-align: center;">' . esc_html__('Sorry, you do not have access to this page.', 'fluent-community') . '</h3>'; |
| 107 | 144 | exit(200); |
| 108 | 145 | } |
| @@ -133,8 +170,12 @@ | ||
| 133 | 170 | |
| 134 | 171 | if ($lesson) { |
| 135 | 172 | $post->post_title = $lesson->title; |
| 136 | 173 | $post->post_content = $lesson->message ?: '<!-- wp:paragraph --><p> </p><!-- /wp:paragraph -->'; |
| 174 | + } elseif (!empty($contextConfig['has_content'])) { | |
| 175 | + $contextContent = Arr::get($contextConfig, 'content'); | |
| 176 | + $post->post_title = Arr::get($contextConfig, 'title', ''); | |
| 177 | + $post->post_content = $contextContent ? $contextContent : '<!-- wp:paragraph --><p> </p><!-- /wp:paragraph -->'; | |
| 137 | 178 | } |
| 138 | 179 | |
| 139 | 180 | // renderPage() exits during admin_init and manually fires wp_enqueue_scripts in |
| 140 | 181 | // both the admin and frontend branches. admin_enqueue_scripts never runs here, so |
| @@ -299,12 +340,12 @@ | ||
| 299 | 340 | do_action('fluent_enqueue_block_editor_assets'); |
| 300 | 341 | |
| 301 | 342 | 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); |
| 302 | 343 | wp_localize_script('fcom_editor_custom', 'fcomEditorI18n', $this->getEditorI18nStrings()); |
| 303 | - wp_localize_script('fcom_editor_custom', 'fcomEditorVars', [ | |
| 344 | + wp_localize_script('fcom_editor_custom', 'fcomEditorVars', array_merge([ | |
| 304 | 345 | 'video_gate_default_threshold' => \FluentCommunity\Modules\Course\Services\LessonVideoGateService::getDefaultThreshold(), |
| 305 | 346 | 'can_unfiltered_html' => current_user_can('unfiltered_html') ? 'yes' : 'no', |
| 306 | - ]); | |
| 347 | + ], $this->contextEditorVars)); | |
| 307 | 348 | } |
| 308 | 349 | |
| 309 | 350 | private function getEditorI18nStrings() |
| 310 | 351 | { |
| @@ -323,9 +364,17 @@ | ||
| 323 | 364 | "User's Profile Link:" => __("User's Profile Link:", 'fluent-community'), |
| 324 | 365 | 'Failed to fetch embed. Please check the URL.' => __('Failed to fetch embed. Please check the URL.', 'fluent-community'), |
| 325 | 366 | 'Video thumbnail' => __('Video thumbnail', 'fluent-community'), |
| 326 | 367 | 'Media embedded successfully' => __('Media embedded successfully', 'fluent-community'), |
| 327 | - 'Edit media' => __('Edit media', 'fluent-community'), | |
| 368 | + '+ Add Media' => __('+ Add Media', 'fluent-community'), | |
| 369 | + 'Add Media' => __('Add Media', 'fluent-community'), | |
| 370 | + 'Edit Media' => __('Edit Media', 'fluent-community'), | |
| 371 | + 'Oembed URL' => __('Oembed URL', 'fluent-community'), | |
| 372 | + 'Video URL' => __('Video URL', 'fluent-community'), | |
| 373 | + 'Supports Vimeo, YouTube, Wistia and more' => __('Supports Vimeo, YouTube, Wistia and more', 'fluent-community'), | |
| 374 | + 'Embedding…' => __('Embedding…', 'fluent-community'), | |
| 375 | + 'Cancel' => __('Cancel', 'fluent-community'), | |
| 376 | + 'Remove' => __('Remove', 'fluent-community'), | |
| 328 | 377 | 'Oembed' => __('Oembed', 'fluent-community'), |
| 329 | 378 | 'Custom HTML' => __('Custom HTML', 'fluent-community'), |
| 330 | 379 | 'Custom HTML Code' => __('Custom HTML Code', 'fluent-community'), |
| 331 | 380 | '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'), |
| @@ -354,8 +403,32 @@ | ||
| 354 | 403 | "User's Name" => __("User's Name", 'fluent-community'), |
| 355 | 404 | "User's Email" => __("User's Email", 'fluent-community'), |
| 356 | 405 | "User's photo HTML" => __("User's photo HTML", 'fluent-community'), |
| 357 | 406 | 'Profile Link' => __('Profile Link', 'fluent-community'), |
| 407 | + // Space page settings panel | |
| 408 | + 'Who can view this page' => __('Who can view this page', 'fluent-community'), | |
| 409 | + 'Everyone who can view the space' => __('Everyone who can view the space', 'fluent-community'), | |
| 410 | + 'Space members only' => __('Space members only', 'fluent-community'), | |
| 411 | + 'URL Slug' => __('URL Slug', 'fluent-community'), | |
| 412 | + 'Changing the slug will break existing links to this page.' => __('Changing the slug will break existing links to this page.', 'fluent-community'), | |
| 413 | + 'Show in space menu' => __('Show in space menu', 'fluent-community'), | |
| 414 | + 'Menu Label' => __('Menu Label', 'fluent-community'), | |
| 415 | + 'Leave empty to use the page title.' => __('Leave empty to use the page title.', 'fluent-community'), | |
| 416 | + 'SEO' => __('SEO', 'fluent-community'), | |
| 417 | + 'SEO Description' => __('SEO Description', 'fluent-community'), | |
| 418 | + 'Used for search engines and link previews' => __('Used for search engines and link previews', 'fluent-community'), | |
| 419 | + // Space page layout picker. The template labels and descriptions are | |
| 420 | + // localized by whoever registers them (SpacePageHelper::getLayoutTemplates | |
| 421 | + // in pro); only the fallback names used when pro is absent live here. | |
| 422 | + 'Page Layout' => __('Page Layout', 'fluent-community'), | |
| 423 | + 'Show the page title' => __('Show the page title', 'fluent-community'), | |
| 424 | + 'Show the featured image' => __('Show the featured image', 'fluent-community'), | |
| 425 | + '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'), | |
| 426 | + 'The image still appears in search results and link previews.' => __('The image still appears in search results and link previews.', 'fluent-community'), | |
| 427 | + 'Standard' => __('Standard', 'fluent-community'), | |
| 428 | + 'Classic' => __('Classic', 'fluent-community'), | |
| 429 | + 'Full Width' => __('Full Width', 'fluent-community'), | |
| 430 | + 'Unified' => __('Unified', 'fluent-community'), | |
| 358 | 431 | ]; |
| 359 | 432 | |
| 360 | 433 | return apply_filters('fluent_community/editor_i18n_strings', $strings); |
| 361 | 434 | } |
| @@ -465,24 +538,19 @@ | ||
| 465 | 538 | } |
| 466 | 539 | |
| 467 | 540 | private function unloadOtherScripts() |
| 468 | 541 | { |
| 542 | + // skips scripts and styles both; fluent_community/skip_no_conflict is styles only | |
| 469 | 543 | $isSkip = apply_filters('fluent_com_editor/skip_no_conflict', false); |
| 470 | 544 | if ($isSkip) { |
| 471 | 545 | return; |
| 472 | 546 | } |
| 473 | 547 | |
| 474 | - /** | |
| 475 | - * Define the list of approved slugs for FluentCRM assets. | |
| 476 | - * | |
| 477 | - * This filter allows modification of the list of slugs that are approved for FluentCRM assets. | |
| 478 | - * | |
| 479 | - * @param array $approvedSlugs An array of approved slugs for FluentCRM assets. | |
| 480 | - */ | |
| 548 | + // scripts only; styles use fluent_community/asset_listed_slugs | |
| 481 | 549 | $approvedSlugs = apply_filters('fluent_com_editor/asset_listed_slugs', [ |
| 482 | 550 | '\/gutenberg\/' |
| 483 | 551 | ]); |
| 484 | - $approvedSlugs[] = 'fluent-community'; | |
| 552 | + $approvedSlugs[] = '\/fluent-community(-pro)?\/'; | |
| 485 | 553 | $approvedSlugs = array_unique($approvedSlugs); |
| 486 | 554 | $approvedSlugs = implode('|', $approvedSlugs); |
| 487 | 555 | |
| 488 | 556 | $pluginUrl = str_replace(['http:', 'https:'], '', plugins_url()); |
| @@ -530,13 +598,14 @@ | ||
| 530 | 598 | if (!$wp_styles) { |
| 531 | 599 | return; |
| 532 | 600 | } |
| 533 | 601 | |
| 602 | + // styles only; scripts use fluent_com_editor/asset_listed_slugs | |
| 534 | 603 | $approvedSlugs = apply_filters('fluent_community/asset_listed_slugs', [ |
| 535 | 604 | '\/gutenberg\/', |
| 536 | 605 | ]); |
| 537 | 606 | |
| 538 | - $approvedSlugs[] = '\/fluent-community\/'; | |
| 607 | + $approvedSlugs[] = '\/fluent-community(-pro)?\/'; | |
| 539 | 608 | |
| 540 | 609 | $approvedSlugs = array_unique($approvedSlugs); |
| 541 | 610 | $approvedSlugs = implode('|', $approvedSlugs); |
| 542 | 611 | |