PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.11.0
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.11.0
2.11.0 2.10.0 2.10.01 2.9.1 2.9.0 2.8.1 2.8.0 2.7.7 2.7.5 2.7.0 2.6.01 2.6.0 2.5.0 2.4.01 trunk 1.0.90 1.0.91 1.0.92 1.0.93 1.0.94 1.0.95 1.0.96 1.0.97 1.0.98 1.0.99 All 78 releases
← All changes | app/Hooks/Handlers/FluentBlockEditorHandler.php +134 -36 2.7.02.11.0 View file →
@@ -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 }
@@ -50,8 +65,10 @@
50 65 media="screen"/>
51 66 <?php // phpcs:enable WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet ?>
52 67
53 68 <style>
69 + /* No admin bar here; WP 7.1 offsets the editor by this var, leaving a gap. */
70 + html { --wp-admin--admin-bar--height: 0; }
54 71 <?php echo wp_strip_all_tags($this->getColorSchemaCss()); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- generated CSS, no user input ?>
55 72 </style>
56 73
57 74 <?php
@@ -99,8 +116,30 @@
99 116 $hasAccess = $space && $space->isAdmin(get_current_user_id(), true);
100 117 }
101 118 }
102 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 +
103 142 if (!$hasAccess) {
104 143 echo '<h3 style="padding: 100px; text-align: center;">' . esc_html__('Sorry, you do not have access to this page.', 'fluent-community') . '</h3>';
105 144 exit(200);
106 145 }
@@ -131,8 +170,12 @@
131 170
132 171 if ($lesson) {
133 172 $post->post_title = $lesson->title;
134 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 -->';
135 178 }
136 179
137 180 // renderPage() exits during admin_init and manually fires wp_enqueue_scripts in
138 181 // both the admin and frontend branches. admin_enqueue_scripts never runs here, so
@@ -248,13 +291,9 @@
248 291
249 292 /**
250 293 * Scripts
251 294 */
252 - wp_enqueue_media(
253 - array(
254 - 'post' => null
255 - )
256 - );
295 + wp_enqueue_media();
257 296
258 297 add_filter('user_can_richedit', '__return_true');
259 298 wp_tinymce_inline_scripts();
260 299 wp_enqueue_editor();
@@ -277,12 +316,18 @@
277 316 wp_enqueue_style('wp-block-cover');
278 317 wp_enqueue_style('global-styles-css-custom-properties');
279 318 wp_enqueue_style('wp-block-spacer');
280 319
281 - wp_register_style('fluent_com_editor_styles', FLUENT_COMMUNITY_PLUGIN_URL . 'Modules/Gutenberg/editor/style.css', false, FLUENT_COMMUNITY_PLUGIN_VERSION, 'all');
320 + wp_register_style('fluent_com_editor_styles', FLUENT_COMMUNITY_PLUGIN_URL . 'Modules/Gutenberg/editor/style.css', [], FLUENT_COMMUNITY_PLUGIN_VERSION, 'all');
282 321
283 322 add_action('fluent_enqueue_block_editor_assets', 'wp_enqueue_editor_format_library_assets');
284 323
324 + // WP 6.9+ loads the LaTeX-to-MathML converter as a script module; core wires this on
325 + // 'enqueue_block_editor_assets', which this standalone editor page never fires.
326 + if (function_exists('wp_enqueue_block_editor_script_modules')) {
327 + add_action('fluent_enqueue_block_editor_assets', 'wp_enqueue_block_editor_script_modules');
328 + }
329 +
285 330 /**
286 331 * Fires after block assets have been enqueued for the editing interface.
287 332 *
288 333 * Call `add_action` on any hook before 'admin_enqueue_scripts'.
@@ -295,11 +340,12 @@
295 340 do_action('fluent_enqueue_block_editor_assets');
296 341
297 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);
298 343 wp_localize_script('fcom_editor_custom', 'fcomEditorI18n', $this->getEditorI18nStrings());
299 - wp_localize_script('fcom_editor_custom', 'fcomEditorVars', [
300 - 'video_gate_default_threshold' => \FluentCommunity\Modules\Course\Services\LessonVideoGateService::getDefaultThreshold()
301 - ]);
344 + wp_localize_script('fcom_editor_custom', 'fcomEditorVars', array_merge([
345 + 'video_gate_default_threshold' => \FluentCommunity\Modules\Course\Services\LessonVideoGateService::getDefaultThreshold(),
346 + 'can_unfiltered_html' => current_user_can('unfiltered_html') ? 'yes' : 'no',
347 + ], $this->contextEditorVars));
302 348 }
303 349
304 350 private function getEditorI18nStrings()
305 351 {
@@ -318,12 +364,21 @@
318 364 "User's Profile Link:" => __("User's Profile Link:", 'fluent-community'),
319 365 'Failed to fetch embed. Please check the URL.' => __('Failed to fetch embed. Please check the URL.', 'fluent-community'),
320 366 'Video thumbnail' => __('Video thumbnail', 'fluent-community'),
321 367 'Media embedded successfully' => __('Media embedded successfully', 'fluent-community'),
322 - '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'),
323 377 'Oembed' => __('Oembed', 'fluent-community'),
324 378 'Custom HTML' => __('Custom HTML', 'fluent-community'),
325 379 'Custom HTML Code' => __('Custom HTML Code', 'fluent-community'),
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'),
326 381 'Paste an iframe code' => __('Paste an iframe code', 'fluent-community'),
327 382 'Embed' => __('Embed', 'fluent-community'),
328 383 'Paste a URL to embed' => __('Paste a URL to embed', 'fluent-community'),
329 384 'Embed from Vimeo, YouTube, Wistia and more' => __('Embed from Vimeo, YouTube, Wistia and more', 'fluent-community'),
@@ -348,8 +403,32 @@
348 403 "User's Name" => __("User's Name", 'fluent-community'),
349 404 "User's Email" => __("User's Email", 'fluent-community'),
350 405 "User's photo HTML" => __("User's photo HTML", 'fluent-community'),
351 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'),
352 431 ];
353 432
354 433 return apply_filters('fluent_community/editor_i18n_strings', $strings);
355 434 }
@@ -382,11 +461,14 @@
382 461
383 462 add_action('fluent_community/block_editor_footer', function () {
384 463 wp_underscore_playlist_templates();
385 464 wp_print_footer_scripts();
465 + $this->printScriptModules();
386 466 wp_print_media_templates();
387 467 wp_enqueue_global_styles();
388 - wp_enqueue_stored_styles();
468 + if (function_exists('wp_enqueue_stored_styles')) {
469 + wp_enqueue_stored_styles();
470 + }
389 471 wp_maybe_inline_styles();
390 472 });
391 473
392 474 add_action('fluent_block_editor/head', 'wp_enqueue_scripts', 1);
@@ -420,8 +502,30 @@
420 502 </html>
421 503 <?php
422 504 }
423 505
506 + /**
507 + * Core prints script modules on 'wp_footer' / 'admin_print_footer_scripts'. This editor page
508 + * renders its own document and fires neither, so blocks relying on script modules (core/math
509 + * and its LaTeX-to-MathML converter on WP 6.9+) would never load.
510 + */
511 + protected function printScriptModules()
512 + {
513 + if (!function_exists('wp_script_modules')) {
514 + return;
515 + }
516 +
517 + $scriptModules = wp_script_modules();
518 +
519 + $scriptModules->print_import_map();
520 + $scriptModules->print_enqueued_script_modules();
521 + $scriptModules->print_script_module_preloads();
522 +
523 + if (method_exists($scriptModules, 'print_script_module_translations')) {
524 + $scriptModules->print_script_module_translations();
525 + }
526 + }
527 +
424 528 private function shouldBlockAsset(string $src, string $pluginUrl, string $themesUrl, string $approvedPattern): bool
425 529 {
426 530 $isPlugin = strpos($src, $pluginUrl) !== false;
427 531 $isTheme = strpos($src, $themesUrl) !== false;
@@ -434,24 +538,19 @@
434 538 }
435 539
436 540 private function unloadOtherScripts()
437 541 {
542 + // skips scripts and styles both; fluent_community/skip_no_conflict is styles only
438 543 $isSkip = apply_filters('fluent_com_editor/skip_no_conflict', false);
439 544 if ($isSkip) {
440 545 return;
441 546 }
442 547
443 - /**
444 - * Define the list of approved slugs for FluentCRM assets.
445 - *
446 - * This filter allows modification of the list of slugs that are approved for FluentCRM assets.
447 - *
448 - * @param array $approvedSlugs An array of approved slugs for FluentCRM assets.
449 - */
548 + // scripts only; styles use fluent_community/asset_listed_slugs
450 549 $approvedSlugs = apply_filters('fluent_com_editor/asset_listed_slugs', [
451 550 '\/gutenberg\/'
452 551 ]);
453 - $approvedSlugs[] = 'fluent-community';
552 + $approvedSlugs[] = '\/fluent-community(-pro)?\/';
454 553 $approvedSlugs = array_unique($approvedSlugs);
455 554 $approvedSlugs = implode('|', $approvedSlugs);
456 555
457 556 $pluginUrl = str_replace(['http:', 'https:'], '', plugins_url());
@@ -499,13 +598,14 @@
499 598 if (!$wp_styles) {
500 599 return;
501 600 }
502 601
602 + // styles only; scripts use fluent_com_editor/asset_listed_slugs
503 603 $approvedSlugs = apply_filters('fluent_community/asset_listed_slugs', [
504 604 '\/gutenberg\/',
505 605 ]);
506 606
507 - $approvedSlugs[] = '\/fluent-community\/';
607 + $approvedSlugs[] = '\/fluent-community(-pro)?\/';
508 608
509 609 $approvedSlugs = array_unique($approvedSlugs);
510 610 $approvedSlugs = implode('|', $approvedSlugs);
511 611
@@ -626,8 +726,9 @@
626 726 'core/code',
627 727 'core/columns',
628 728 'core/column',
629 729 'core/cover',
730 + 'core/details',
630 731 'core/embed',
631 732 'core/footnotes',
632 733 'core/freeform',
633 734 'core/gallery',
@@ -634,11 +735,11 @@
634 735 'core/group',
635 736 'core/heading',
636 737 'core/html',
637 738 'core/image',
638 - 'core/latest-posts',
639 739 'core/list',
640 740 'core/list-item',
741 + 'core/math',
641 742 'core/media-text',
642 743 'core/missing',
643 744 'core/paragraph',
644 745 'core/preformatted',
@@ -643,17 +744,14 @@
643 744 'core/paragraph',
644 745 'core/preformatted',
645 746 'core/pullquote',
646 747 'core/quote',
647 - 'core/rss',
648 748 'core/separator',
649 749 'core/social-link',
650 750 'core/social-links',
651 751 'core/spacer',
652 752 'core/table',
653 - 'core/text-columns',
654 - 'core/verse',
655 - 'core/freeform'
753 + 'core/verse'
656 754 ]),
657 755 'gradients' => [],
658 756 'imageDefaultSize' => 'large',
659 757 'imageEditing' => true,