PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.7.5
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.7.5
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 1.1.0 All 77 releases
fluent-community / app / Hooks / Handlers / FluentBlockEditorHandler.php

FluentBlockEditorHandler.php in FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses 2.7.5, at app/Hooks/Handlers/FluentBlockEditorHandler.php

1,166 lines 51.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCommunity\App\Hooks\Handlers;
4
5 use FluentCommunity\App\Functions\Utility;
6 use FluentCommunity\App\Models\BaseSpace;
7 use FluentCommunity\App\Services\Helper;
8 use FluentCommunity\Framework\Support\Arr;
9 use FluentCommunity\Modules\Course\Model\CourseLesson;
10
11 class FluentBlockEditorHandler
12 {
13 public function register()
14 {
15 add_action('init', function () {
16
17 register_post_type('fcom-dummy', [
18 'label' => 'Lesson',
19 'public' => false,
20 'show_in_rest' => true,
21 'supports' => ['title', 'editor', 'thumbnail'],
22 ]);
23
24 register_post_type('fcom-lockscreen', [
25 'label' => 'Lockscreen',
26 'public' => false,
27 'show_in_rest' => true,
28 'supports' => ['editor'],
29 ]);
30
31 if (!isset($_REQUEST['fluent_community_block_editor'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
32 return;
33 }
34
35 if (!defined('IFRAME_REQUEST')) {
36 define('IFRAME_REQUEST', true); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound
37 }
38
39 remove_action('enqueue_block_editor_assets', 'wp_enqueue_editor_block_directory_assets');
40 add_action('fluent_community/block_editor_head', function () {
41 $url = FLUENT_COMMUNITY_PLUGIN_URL . 'Modules/Gutenberg/editor/index.css';
42 $contentStylingUrl = FLUENT_COMMUNITY_PLUGIN_URL . 'Modules/Gutenberg/editor/content_styling.css';
43 // phpcs:disable WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet -- emitted into a custom non-wp_head action hook, wp_enqueue_style not applicable
44 ?>
45 <link rel="stylesheet"
46 href="<?php echo esc_url($url); ?>?version=<?php echo esc_attr(FLUENT_COMMUNITY_PLUGIN_VERSION); ?>"
47 media="screen"/>
48 <link rel="stylesheet"
49 href="<?php echo esc_url($contentStylingUrl); ?>?version=<?php echo esc_attr(FLUENT_COMMUNITY_PLUGIN_VERSION); ?>"
50 media="screen"/>
51 <?php // phpcs:enable WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet ?>
52
53 <style>
54 <?php echo wp_strip_all_tags($this->getColorSchemaCss()); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- generated CSS, no user input ?>
55 </style>
56
57 <?php
58 });
59 add_filter('should_load_separate_core_block_assets', '__return_false', 20);
60 $this->renderCustomEditor($_REQUEST); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
61
62 $actionHook = 'template_redirect';
63 if(is_admin()) {
64 $actionHook = 'admin_init';
65 }
66
67 add_action($actionHook, function () {
68 $this->renderPage();
69 exit(200);
70 }, -1000);
71 }, 2);
72 }
73
74 public function renderCustomEditor($data = [])
75 {
76 do_action('litespeed_control_set_nocache', 'fluentcommunity api request'); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
77 // set no cache headers
78 nocache_headers();
79
80 $hasAccess = false;
81 $postType = 'fcom-dummy';
82 $context = Arr::get($data, 'context');
83
84 $lesson = null;
85
86 if ($context === 'course_lesson') {
87 $lessonId = Arr::get($data, 'lesson_id');
88 if ($lessonId) {
89 $lesson = CourseLesson::find($lessonId);
90 $hasAccess = $lesson && $lesson->course && $lesson->course->isCourseAdmin();
91 }
92 }
93
94 if ($context === 'lockscreen') {
95 $postType = 'fcom-lockscreen';
96 $spaceId = Arr::get($data, 'space_id');
97 if ($spaceId) {
98 $space = BaseSpace::query()->onlyMain()->find($spaceId);
99 $hasAccess = $space && $space->isAdmin(get_current_user_id(), true);
100 }
101 }
102
103 if (!$hasAccess) {
104 echo '<h3 style="padding: 100px; text-align: center;">' . esc_html__('Sorry, you do not have access to this page.', 'fluent-community') . '</h3>';
105 exit(200);
106 }
107
108 add_filter('should_load_separate_core_block_assets', '__return_false', 20);
109 show_admin_bar(false);
110
111 $firstPost = Utility::getApp('db')->table('posts')
112 ->where('post_type', $postType)
113 ->first();
114
115 if ($firstPost) {
116 $simulatedPost = get_post($firstPost->ID);
117 $simulatedPost->post_content = '<!-- wp:paragraph --><p> </p><!-- /wp:paragraph -->';
118 } else {
119 $newPostId = wp_insert_post(array(
120 'post_title' => $context === 'course_lesson' ? 'Demo Lesson Title' : '',
121 'post_content' => '<!-- wp:paragraph --><p> </p><!-- /wp:paragraph -->',
122 'post_type' => $postType,
123 'post_status' => 'draft',
124 ));
125
126 $simulatedPost = get_post($newPostId);
127 }
128
129 global $post;
130 $post = $simulatedPost;
131
132 if ($lesson) {
133 $post->post_title = $lesson->title;
134 $post->post_content = $lesson->message ?: '<!-- wp:paragraph --><p> </p><!-- /wp:paragraph -->';
135 }
136
137 // renderPage() exits during admin_init and manually fires wp_enqueue_scripts in
138 // both the admin and frontend branches. admin_enqueue_scripts never runs here, so
139 // the apiFetch preload must ride wp_enqueue_scripts or the editor falls back to a
140 // live GET /wp/v2/fcom-dummy/{id}?context=edit — which 403s for users without
141 // edit_others_posts (e.g. contributors) on the shared, admin-authored dummy post.
142 add_action('wp_enqueue_scripts', function () use ($post) {
143 wp_enqueue_script('postbox', admin_url('js/postbox.min.js'), array('jquery-ui-sortable'), FLUENT_COMMUNITY_PLUGIN_VERSION, true);
144 wp_enqueue_style('dashicons');
145 wp_enqueue_style('media');
146 wp_enqueue_style('admin-menu');
147 wp_enqueue_style('admin-bar');
148 wp_enqueue_style('l10n');
149
150 wp_add_inline_script(
151 'wp-api-fetch',
152 \sprintf(
153 'wp.apiFetch.use( wp.apiFetch.createPreloadingMiddleware( %s ) );',
154 wp_json_encode(
155 array(
156 '/wp/v2/' . $post->post_type . '/' . $post->ID . '?context=edit' => array(
157 'body' => array(
158 'id' => $post->ID,
159 'title' => array('raw' => $post->post_title),
160 'content' => array(
161 'block_format' => 1,
162 'raw' => $post->post_content,
163 ),
164 'excerpt' => array('raw' => ''),
165 'date' => '',
166 'date_gmt' => '',
167 'modified' => '',
168 'modified_gmt' => '',
169 'link' => home_url('/'),
170 'guid' => array(),
171 'parent' => 0,
172 'menu_order' => 0,
173 'author' => 0,
174 'featured_media' => 0,
175 'comment_status' => 'closed',
176 'ping_status' => 'closed',
177 'template' => '',
178 'meta' => array(),
179 '_links' => array(),
180 'type' => $post->post_type,
181 'status' => 'pending', // pending is the best state to remove draft saving possibilities.
182 'slug' => '',
183 'generated_slug' => '',
184 'permalink_template' => home_url('/'),
185 ),
186 ),
187 )
188 )
189 ),
190 'after'
191 );
192 }, 11);
193
194 add_action('wp_enqueue_scripts', function ($hook) use ($post) {
195 // Gutenberg requires the post-locking functions defined within:
196 // See `show_post_locked_dialog` and `get_post_metadata` filters below.
197 include_once ABSPATH . 'wp-admin/includes/post.php';
198 $this->gutenberg_editor_scripts_and_styles($hook, $post);
199 });
200
201 // Disable post locking dialogue.
202 add_filter('show_post_locked_dialog', '__return_false');
203
204 // Everyone can richedit! This avoids a case where a page can be cached where a user can't richedit.
205 $GLOBALS['wp_rich_edit'] = true;
206 add_filter('user_can_richedit', '__return_true', 1000);
207
208 // Homepage is always locked by @wordpressdotorg
209 // This prevents other logged-in users taking a lock of the post on the front-end.
210 add_filter('get_post_metadata', function ($value, $post_id, $meta_key) {
211 if ($meta_key !== '_edit_lock') {
212 return $value;
213 }
214 return time() . ':' . get_current_user_id(); // WordPressdotorg user ID
215 }, 10, 3);
216
217 // Disable Jetpack Blocks for now.
218 add_filter('jetpack_gutenberg', '__return_false');
219 }
220
221 private function gutenberg_editor_scripts_and_styles($hook, $post)
222 {
223 $initial_edits = array(
224 'title' => $post->post_title,
225 'content' => $post->post_content,
226 'excerpt' => $post->post_excerpt,
227 );
228
229 $editor_settings = $this->getEditorSettings($post);
230
231 $init_script =
232 "(function() {
233 window._wpLoadBlockEditor = new Promise(function(resolve) {
234 wp.domReady(function() {
235 resolve(wp.editPost.initializeEditor('editor', \"%s\", %d, %s, %s));
236 });
237 });
238 })();";
239
240 $script = sprintf(
241 $init_script,
242 $post->post_type,
243 $post->ID,
244 wp_json_encode($editor_settings),
245 wp_json_encode($initial_edits)
246 );
247 wp_add_inline_script('wp-edit-post', $script);
248
249 /**
250 * Scripts
251 */
252 wp_enqueue_media();
253
254 add_filter('user_can_richedit', '__return_true');
255 wp_tinymce_inline_scripts();
256 wp_enqueue_editor();
257
258 /**
259 * Styles
260 */
261 wp_enqueue_style('wp-edit-post');
262
263 // Include block styles needed when user is not signed in. See: https://github.com/WordPress/wporg-gutenberg/issues/26
264 wp_enqueue_style('global-styles');
265 wp_enqueue_style('wp-block-library');
266 wp_enqueue_style('wp-block-image');
267 wp_enqueue_style('wp-block-group');
268 wp_enqueue_style('wp-block-heading');
269 wp_enqueue_style('wp-block-button');
270 wp_enqueue_style('wp-block-paragraph');
271 wp_enqueue_style('wp-block-separator');
272 wp_enqueue_style('wp-block-columns');
273 wp_enqueue_style('wp-block-cover');
274 wp_enqueue_style('global-styles-css-custom-properties');
275 wp_enqueue_style('wp-block-spacer');
276
277 wp_register_style('fluent_com_editor_styles', FLUENT_COMMUNITY_PLUGIN_URL . 'Modules/Gutenberg/editor/style.css', [], FLUENT_COMMUNITY_PLUGIN_VERSION, 'all');
278
279 add_action('fluent_enqueue_block_editor_assets', 'wp_enqueue_editor_format_library_assets');
280
281 // WP 6.9+ loads the LaTeX-to-MathML converter as a script module; core wires this on
282 // 'enqueue_block_editor_assets', which this standalone editor page never fires.
283 if (function_exists('wp_enqueue_block_editor_script_modules')) {
284 add_action('fluent_enqueue_block_editor_assets', 'wp_enqueue_block_editor_script_modules');
285 }
286
287 /**
288 * Fires after block assets have been enqueued for the editing interface.
289 *
290 * Call `add_action` on any hook before 'admin_enqueue_scripts'.
291 *
292 * In the function call you supply, simply use `wp_enqueue_script` and
293 * `wp_enqueue_style` to add your functionality to the Gutenberg editor.
294 *
295 * @since 0.4.0
296 */
297 do_action('fluent_enqueue_block_editor_assets');
298
299 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);
300 wp_localize_script('fcom_editor_custom', 'fcomEditorI18n', $this->getEditorI18nStrings());
301 wp_localize_script('fcom_editor_custom', 'fcomEditorVars', [
302 'video_gate_default_threshold' => \FluentCommunity\Modules\Course\Services\LessonVideoGateService::getDefaultThreshold(),
303 'can_unfiltered_html' => current_user_can('unfiltered_html') ? 'yes' : 'no',
304 ]);
305 }
306
307 private function getEditorI18nStrings()
308 {
309 $strings = [
310 'Enable Video Embed' => __('Enable Video Embed', 'fluent-community'),
311 'Enable Comments' => __('Enable Comments', 'fluent-community'),
312 'Free Preview Lesson' => __('Free Preview Lesson', 'fluent-community'),
313 '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'),
314 'Media Embed' => __('Media Embed', 'fluent-community'),
315 'Documents & Files' => __('Documents & Files', 'fluent-community'),
316 'Smartcodes' => __('Smartcodes', 'fluent-community'),
317 'You may use following smartcode in your lesson:' => __('You may use following smartcode in your lesson:', 'fluent-community'),
318 "User's Name:" => __("User's Name:", 'fluent-community'),
319 "User's Email:" => __("User's Email:", 'fluent-community'),
320 "User's photo HTML:" => __("User's photo HTML:", 'fluent-community'),
321 "User's Profile Link:" => __("User's Profile Link:", 'fluent-community'),
322 'Failed to fetch embed. Please check the URL.' => __('Failed to fetch embed. Please check the URL.', 'fluent-community'),
323 'Video thumbnail' => __('Video thumbnail', 'fluent-community'),
324 'Media embedded successfully' => __('Media embedded successfully', 'fluent-community'),
325 'Edit media' => __('Edit media', 'fluent-community'),
326 'Oembed' => __('Oembed', 'fluent-community'),
327 'Custom HTML' => __('Custom HTML', 'fluent-community'),
328 'Custom HTML Code' => __('Custom HTML Code', 'fluent-community'),
329 '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'),
330 'Paste an iframe code' => __('Paste an iframe code', 'fluent-community'),
331 'Embed' => __('Embed', 'fluent-community'),
332 'Paste a URL to embed' => __('Paste a URL to embed', 'fluent-community'),
333 'Embed from Vimeo, YouTube, Wistia and more' => __('Embed from Vimeo, YouTube, Wistia and more', 'fluent-community'),
334 'FluentPlayer' => __('FluentPlayer', 'fluent-community'),
335 'Select Player Block' => __('Select Player Block', 'fluent-community'),
336 'Use FluentPlayer Block' => __('Use FluentPlayer Block', 'fluent-community'),
337 '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'),
338 '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'),
339 'Video Completion' => __('Video Completion', 'fluent-community'),
340 'Require video watch to complete' => __('Require video watch to complete', 'fluent-community'),
341 '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'),
342 'Auto-complete lesson when the video ends' => __('Auto-complete lesson when the video ends', 'fluent-community'),
343 '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'),
344 'Required watch percentage' => __('Required watch percentage', 'fluent-community'),
345 '100% means the video must be watched to the end.' => __('100% means the video must be watched to the end.', 'fluent-community'),
346 'Lesson Duration' => __('Lesson Duration', 'fluent-community'),
347 'Minutes' => __('Minutes', 'fluent-community'),
348 'Seconds' => __('Seconds', 'fluent-community'),
349 'View' => __('View', 'fluent-community'),
350 'No documents attached yet.' => __('No documents attached yet.', 'fluent-community'),
351 'Manage Documents & Files' => __('Manage Documents & Files', 'fluent-community'),
352 "User's Name" => __("User's Name", 'fluent-community'),
353 "User's Email" => __("User's Email", 'fluent-community'),
354 "User's photo HTML" => __("User's photo HTML", 'fluent-community'),
355 'Profile Link' => __('Profile Link', 'fluent-community'),
356 ];
357
358 return apply_filters('fluent_community/editor_i18n_strings', $strings);
359 }
360
361 private function gutenberg_get_available_image_sizes()
362 {
363 $size_names = apply_filters(
364 'fluent_community/image_size_names_choose',
365 array(
366 'thumbnail' => __('Thumbnail', 'fluent-community'),
367 'medium' => __('Medium', 'fluent-community'),
368 'large' => __('Large', 'fluent-community'),
369 'full' => __('Full Size', 'fluent-community'),
370 )
371 );
372 $all_sizes = array();
373 foreach ($size_names as $size_slug => $size_name) {
374 $all_sizes[] = array(
375 'slug' => $size_slug,
376 'name' => $size_name,
377 );
378 }
379 return $all_sizes;
380 }
381
382 protected function renderPage()
383 {
384
385 remove_action( 'wp_print_styles', 'print_emoji_styles' );
386
387 add_action('fluent_community/block_editor_footer', function () {
388 wp_underscore_playlist_templates();
389 wp_print_footer_scripts();
390 $this->printScriptModules();
391 wp_print_media_templates();
392 wp_enqueue_global_styles();
393 if (function_exists('wp_enqueue_stored_styles')) {
394 wp_enqueue_stored_styles();
395 }
396 wp_maybe_inline_styles();
397 });
398
399 add_action('fluent_block_editor/head', 'wp_enqueue_scripts', 1);
400 add_action('fluent_block_editor/head', 'wp_resource_hints', 2);
401 add_action('fluent_block_editor/head', 'wp_preload_resources', 1);
402 add_action('fluent_block_editor/head', 'wp_print_styles', 8);
403 add_action('fluent_block_editor/head', 'wp_print_head_scripts', 9);
404 add_action('fluent_block_editor/head', 'wp_custom_css_cb', 101);
405
406 $this->unloadOtherScripts();
407 ?>
408 <!DOCTYPE html>
409 <html <?php language_attributes(); ?>>
410 <head>
411 <title>FluentCommunity Block Editor</title>
412 <meta charset='utf-8'>
413 <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0,viewport-fit=cover"/>
414 <meta name="mobile-web-app-capable" content="yes">
415 <meta name="robots" content="noindex">
416 <?php do_action('fluent_block_editor/head'); ?>
417 <?php do_action('fluent_community/block_editor_head'); ?>
418 </head>
419 <body class="fcom_custom_editor">
420 <div class="wp-site-blocks">
421 <div id="editor" class="gutenberg__editor"></div>
422 </div>
423 <?php
424 do_action('fluent_community/block_editor_footer');
425 ?>
426 </body>
427 </html>
428 <?php
429 }
430
431 /**
432 * Core prints script modules on 'wp_footer' / 'admin_print_footer_scripts'. This editor page
433 * renders its own document and fires neither, so blocks relying on script modules (core/math
434 * and its LaTeX-to-MathML converter on WP 6.9+) would never load.
435 */
436 protected function printScriptModules()
437 {
438 if (!function_exists('wp_script_modules')) {
439 return;
440 }
441
442 $scriptModules = wp_script_modules();
443
444 $scriptModules->print_import_map();
445 $scriptModules->print_enqueued_script_modules();
446 $scriptModules->print_script_module_preloads();
447
448 if (method_exists($scriptModules, 'print_script_module_translations')) {
449 $scriptModules->print_script_module_translations();
450 }
451 }
452
453 private function shouldBlockAsset(string $src, string $pluginUrl, string $themesUrl, string $approvedPattern): bool
454 {
455 $isPlugin = strpos($src, $pluginUrl) !== false;
456 $isTheme = strpos($src, $themesUrl) !== false;
457
458 if (!$isPlugin && !$isTheme) {
459 return false;
460 }
461
462 return !preg_match('#' . $approvedPattern . '#', $src);
463 }
464
465 private function unloadOtherScripts()
466 {
467 $isSkip = apply_filters('fluent_com_editor/skip_no_conflict', false);
468 if ($isSkip) {
469 return;
470 }
471
472 /**
473 * Define the list of approved slugs for FluentCRM assets.
474 *
475 * This filter allows modification of the list of slugs that are approved for FluentCRM assets.
476 *
477 * @param array $approvedSlugs An array of approved slugs for FluentCRM assets.
478 */
479 $approvedSlugs = apply_filters('fluent_com_editor/asset_listed_slugs', [
480 '\/gutenberg\/'
481 ]);
482 $approvedSlugs[] = 'fluent-community';
483 $approvedSlugs = array_unique($approvedSlugs);
484 $approvedSlugs = implode('|', $approvedSlugs);
485
486 $pluginUrl = str_replace(['http:', 'https:'], '', plugins_url());
487
488 $themesUrl = str_replace(['http:', 'https:'], '', get_theme_root_uri());
489
490 add_filter('script_loader_src', function ($src, $handle) use ($approvedSlugs, $pluginUrl, $themesUrl) {
491 if (!$src) {
492 return $src;
493 }
494
495 if ($this->shouldBlockAsset($src, $pluginUrl, $themesUrl, $approvedSlugs)) {
496 return false;
497 }
498
499 return $src;
500 }, 1, 2);
501
502 add_action('wp_print_scripts', function () use ($approvedSlugs, $pluginUrl, $themesUrl) {
503 global $wp_scripts;
504 if (!$wp_scripts) {
505 return;
506 }
507
508 foreach ($wp_scripts->queue as $script) {
509 if (empty($wp_scripts->registered[$script]) || empty($wp_scripts->registered[$script]->src)) {
510 continue;
511 }
512
513 $src = $wp_scripts->registered[$script]->src;
514 if ($this->shouldBlockAsset($src, $pluginUrl, $themesUrl, $approvedSlugs)) {
515 wp_dequeue_script($wp_scripts->registered[$script]->handle);
516 }
517 }
518 }, 1);
519
520 add_action('wp_print_styles', function () {
521 $isSkip = apply_filters('fluent_community/skip_no_conflict', false, 'styles');
522
523 if ($isSkip) {
524 return;
525 }
526
527 global $wp_styles;
528 if (!$wp_styles) {
529 return;
530 }
531
532 $approvedSlugs = apply_filters('fluent_community/asset_listed_slugs', [
533 '\/gutenberg\/',
534 ]);
535
536 $approvedSlugs[] = '\/fluent-community\/';
537
538 $approvedSlugs = array_unique($approvedSlugs);
539 $approvedSlugs = implode('|', $approvedSlugs);
540
541 $pluginUrl = plugins_url();
542 $themeUrl = get_theme_root_uri();
543
544 $pluginUrl = str_replace(['http:', 'https:'], '', $pluginUrl);
545 $themeUrl = str_replace(['http:', 'https:'], '', $themeUrl);
546
547 foreach ($wp_styles->queue as $script) {
548
549 if (empty($wp_styles->registered[$script]) || empty($wp_styles->registered[$script]->src)) {
550 continue;
551 }
552
553 $src = $wp_styles->registered[$script]->src;
554 $pluginMatched = (strpos($src, $pluginUrl) !== false) && !preg_match('#' . $approvedSlugs . '#', $src);
555 $themeMatched = (strpos($src, $themeUrl) !== false) && !preg_match('#' . $approvedSlugs . '#', $src);
556
557 if (!$pluginMatched && !$themeMatched) {
558 continue;
559 }
560
561 wp_dequeue_style($wp_styles->registered[$script]->handle);
562 }
563 }, 999999);
564 }
565
566 private function getEditorSettings($post)
567 {
568 // Media settings.
569 $max_upload_size = wp_max_upload_size();
570 if (!$max_upload_size) {
571 $max_upload_size = 0;
572 }
573
574 $lock_details = array(
575 'isLocked' => false,
576 'user' => '',
577 );
578
579 $editor_settings = array(
580 'maxUploadFileSize' => $max_upload_size,
581 'allowedMimeTypes' => get_allowed_mime_types(),
582 'postLock' => $lock_details,
583 'postLockUtils' => array(
584 'nonce' => wp_create_nonce('lock-post_' . $post->ID),
585 'unlockNonce' => wp_create_nonce('update-post_' . $post->ID),
586 'ajaxUrl' => admin_url('admin-ajax.php'),
587 ),
588 '__experimentalFeatures' => $this->getExperimentalFeatures(),
589 'colors' => $this->getColorPalette(),
590 '__experimentalDiscussionSettings' => [
591 'avatarURL' => 'https://secure.gravatar.com/avatar/?s=96&f=y&r=g',
592 'commentOrder' => 'asc',
593 'commentsPerPage' => '50',
594 'defaultCommentsPage' => 'newest',
595 'defaultCommentStatus' => 'open',
596 'pageComments' => '',
597 'threadComments' => '1',
598 'threadCommentsDepth' => '5'
599 ],
600 '__unstableGalleryWithImageBlocks' => false,
601 '__unstableIsBlockBasedTheme' => false,
602 'enableCustomUnits' => [
603 'px',
604 'em',
605 'rem',
606 '%',
607 'vh',
608 'vw'
609 ],
610 'fontSizes' => [
611 [
612 'name' => 'Small',
613 'size' => 'var(--fcom-font-size-small)',
614 'slug' => 'small'
615 ],
616 [
617 'name' => 'Medium',
618 'size' => 'var(--fcom-font-size-medium)',
619 'slug' => 'medium'
620 ],
621 [
622 'name' => 'Large',
623 'size' => 'var(--fcom-font-size-large)',
624 'slug' => 'large'
625 ],
626 [
627 'name' => 'Larger',
628 'size' => 'var(--fcom-font-size-larger)',
629 'slug' => 'larger'
630 ],
631 [
632 'name' => 'XX-Large',
633 'size' => 'var(--fcom-font-size-xxlarge)',
634 'slug' => 'xxlarge'
635 ]
636 ],
637 'fullscreenMode' => 1,
638 'enableCustomSpacing' => 1,
639 'enableCustomLineHeight' => 1,
640 'enableCustomFields' => false,
641 'disablePostFormats' => true,
642 'disableLayoutStyles' => false,
643 'disableCustomSpacingSizes' => false,
644 'disableCustomGradients' => 1,
645 'alignWide' => true,
646 'disableCustomFontSizes' => false,
647 'disableCustomColors' => false,
648 'canUpdateBlockBindings' => false,
649 'bodyPlaceholder' => __('Start writing or type / to choose a block for your lesson content', 'fluent-community'),
650 'allowedBlockTypes' => apply_filters('fluent_community/allowed_block_types', [
651 'core/audio',
652 'core/block',
653 'core/buttons',
654 'core/button',
655 'core/code',
656 'core/columns',
657 'core/column',
658 'core/cover',
659 'core/details',
660 'core/embed',
661 'core/footnotes',
662 'core/freeform',
663 'core/gallery',
664 'core/group',
665 'core/heading',
666 'core/html',
667 'core/image',
668 'core/list',
669 'core/list-item',
670 'core/math',
671 'core/media-text',
672 'core/missing',
673 'core/paragraph',
674 'core/preformatted',
675 'core/pullquote',
676 'core/quote',
677 'core/separator',
678 'core/social-link',
679 'core/social-links',
680 'core/spacer',
681 'core/table',
682 'core/verse'
683 ]),
684 'gradients' => [],
685 'imageDefaultSize' => 'large',
686 'imageEditing' => true,
687 'isRTL' => Helper::isRtl(),
688 'autosaveInterval' => 999,
689 'localAutosaveInterval' => 999,
690 'richEditingEnabled' => true,
691 'spacingSizes' => [
692 [
693 'name' => '2X-Small',
694 'size' => '0.44rem',
695 'slug' => '20'
696 ],
697 [
698 'name' => 'X-Small',
699 'size' => '0.67rem',
700 'slug' => '30'
701 ],
702 [
703 'name' => 'Small',
704 'size' => '1rem',
705 'slug' => '40'
706 ],
707 [
708 'name' => 'Medium',
709 'size' => '1.5rem',
710 'slug' => '50'
711 ],
712 [
713 'name' => 'Large',
714 'size' => '2.25rem',
715 'slug' => '60'
716 ],
717 [
718 'name' => 'X-Large',
719 'size' => '3.38rem',
720 'slug' => '70'
721 ],
722 [
723 'name' => '2X-Large',
724 'size' => '5.06rem',
725 'slug' => '80'
726 ]
727 ],
728 'titlePlaceholder' => __('Add Lesson title', 'fluent-community')
729 );
730
731 $editor_settings['styles'] = $this->getEditorStyles();
732 $editor_settings['__unstableResolvedAssets'] = $this->getResolvedAssets();
733 $editor_settings['defaultEditorStyles'] = $this->getDefaultEditorStyles();
734 $editor_settings['imageSizes'] = $this->gutenberg_get_available_image_sizes();
735
736 $editor_settings = apply_filters('fluent_community/block_editor_settings', $editor_settings);
737 return $editor_settings;
738 }
739
740 private function getExperimentalFeatures()
741 {
742 return array(
743 'appearanceTools' => true,
744 'useRootPaddingAwareAlignments' => false,
745 'border' => [
746 'color' => 1,
747 'radius' => 1,
748 'style' => 1,
749 'width' => 1,
750 ],
751 'color' => [
752 'background' => true,
753 'button' => 1,
754 'caption' => 1,
755 'customDuotone' => 0,
756 'defaultDuotone' => 0,
757 'defaultGradients' => 0,
758 'defaultPalette' => [],
759 'duotone' => [],
760 'gradients' => [],
761 'heading' => 1,
762 'link' => 1,
763 'palette' => [
764 'default' => [],
765 'theme' => [
766 [
767 'name' => 'Accent',
768 'slug' => 'theme-palette-color-1',
769 'color' => 'var(--theme-palette-color-1)',
770 ],
771 [
772 'name' => 'Accent - alt',
773 'slug' => 'theme-palette-color-2',
774 'color' => 'var(--theme-palette-color-2)',
775 ],
776 [
777 'name' => 'Strongest text',
778 'slug' => 'theme-palette-color-3',
779 'color' => 'var(--theme-palette-color-3)',
780 ],
781 [
782 'name' => 'Strong Text',
783 'slug' => 'theme-palette-color-4',
784 'color' => 'var(--theme-palette-color-4)',
785 ],
786 [
787 'name' => 'Medium text',
788 'slug' => 'theme-palette-color-5',
789 'color' => 'var(--theme-palette-color-5)',
790 ],
791 [
792 'name' => 'Subtle Text',
793 'slug' => 'theme-palette-color-6',
794 'color' => 'var(--theme-palette-color-6)',
795 ],
796 [
797 'name' => 'Subtle Background',
798 'slug' => 'theme-palette-color-7',
799 'color' => 'var(--theme-palette-color-7)',
800 ],
801 [
802 'name' => 'Lighter Background',
803 'slug' => 'theme-palette-color-8',
804 'color' => 'var(--theme-palette-color-8)',
805 ]
806 ]
807 ],
808 'text' => true,
809 ],
810 'dimensions' => [
811 'defaultAspectRatios' => true,
812 'aspectRatios' => [
813 'default' => [
814 [
815 'name' => 'Square - 1:1',
816 'slug' => 'square',
817 'ratio' => '1',
818 ],
819 [
820 'name' => 'Standard - 4:3',
821 'slug' => '4-3',
822 'ratio' => '4/3',
823 ],
824 [
825 'name' => 'Portrait - 3:4',
826 'slug' => '3-4',
827 'ratio' => '3/4',
828 ],
829 [
830 'name' => 'Classic - 3:2',
831 'slug' => '3-2',
832 'ratio' => '3/2',
833 ],
834 [
835 'name' => 'Classic Portrait - 2:3',
836 'slug' => '2-3',
837 'ratio' => '2/3',
838 ],
839 [
840 'name' => 'Wide - 16:9',
841 'slug' => '16-9',
842 'ratio' => '16/9',
843 ],
844 [
845 'name' => 'Tall - 9:16',
846 'slug' => '9-16',
847 'ratio' => '9/16',
848 ],
849 ]
850 ],
851 'aspectRatio' => 1,
852 'minHeight' => 1,
853 ],
854 'shadow' => [
855 'defaultPresets' => true,
856 'presets' => [
857 'default' => [
858 [
859 'name' => 'Natural',
860 'slug' => 'natural',
861 'shadow' => '6px 6px 9px rgba(0, 0, 0, 0.2)',
862 ],
863 [
864 'name' => 'Deep',
865 'slug' => 'deep',
866 'shadow' => '12px 12px 50px rgba(0, 0, 0, 0.4)',
867 ],
868 [
869 'name' => 'Sharp',
870 'slug' => 'sharp',
871 'shadow' => '6px 6px 0px rgba(0, 0, 0, 0.2)',
872 ],
873 [
874 'name' => 'Outlined',
875 'slug' => 'outlined',
876 'shadow' => '6px 6px 0px -3px rgba(255, 255, 255, 1), 6px 6px rgba(0, 0, 0, 1)',
877 ],
878 [
879 'name' => 'Crisp',
880 'slug' => 'crisp',
881 'shadow' => '6px 6px 0px rgba(0, 0, 0, 1)',
882 ],
883 ],
884 ],
885 ],
886 'spacing' => [
887 'blockGap' => 1,
888 'margin' => 1,
889 'padding' => 1,
890 'defaultSpacingSizes' => true,
891 'spacingScale' => [
892 'default' => [
893 'operator' => '*',
894 'increment' => 1.5,
895 'steps' => 7,
896 'mediumStep' => 1.5,
897 'unit' => 'rem',
898 ],
899 ],
900 'spacingSizes' => [
901 'default' => [
902 [
903 'name' => '2X-Small',
904 'slug' => '20',
905 'size' => '0.44rem',
906 ],
907 [
908 'name' => 'X-Small',
909 'slug' => '30',
910 'size' => '0.67rem',
911 ],
912 [
913 'name' => 'Small',
914 'slug' => '40',
915 'size' => '1rem',
916 ],
917 [
918 'name' => 'Medium',
919 'slug' => '50',
920 'size' => '1.5rem',
921 ],
922 [
923 'name' => 'Large',
924 'slug' => '60',
925 'size' => '2.25rem',
926 ],
927 [
928 'name' => 'X-Large',
929 'slug' => '70',
930 'size' => '3.38rem',
931 ],
932 [
933 'name' => '2X-Large',
934 'slug' => '80',
935 'size' => '5.06rem',
936 ],
937 ],
938 ],
939 ],
940 'typography' => [
941 'defaultFontSizes' => NULL,
942 'dropCap' => true,
943 'fontSizes' => [
944 'default' => [
945 [
946 'name' => 'Small',
947 'slug' => 'small',
948 'size' => '13px',
949 ],
950 [
951 'name' => 'Medium',
952 'slug' => 'medium',
953 'size' => '20px',
954 ],
955 [
956 'name' => 'Large',
957 'slug' => 'large',
958 'size' => '36px',
959 ],
960 [
961 'name' => 'Extra Large',
962 'slug' => 'x-large',
963 'size' => '42px',
964 ],
965 ],
966 'theme' => [
967 [
968 'name' => 'Small',
969 'slug' => 'small',
970 'size' => 'var(--fcom-font-size-small)',
971 ],
972 [
973 'name' => 'Medium',
974 'slug' => 'medium',
975 'size' => 'var(--fcom-font-size-medium)',
976 ],
977 [
978 'name' => 'Large',
979 'slug' => 'large',
980 'size' => 'var(--fcom-font-size-large)',
981 ],
982 [
983 'name' => 'Larger',
984 'slug' => 'larger',
985 'size' => 'var(--fcom-font-size-larger)',
986 ],
987 [
988 'name' => 'XX-Large',
989 'slug' => 'xxlarge',
990 'size' => 'var(--fcom-font-size-xxlarge)',
991 ],
992 ],
993 ],
994 'fontStyle' => true,
995 'fontWeight' => true,
996 'letterSpacing' => true,
997 'textAlign' => true,
998 'textDecoration' => true,
999 'textTransform' => true,
1000 'writingMode' => false,
1001 'fluid' => 0,
1002 ],
1003 'blocks' => [
1004 'core/button' => [
1005 'border' => [
1006 'radius' => true,
1007 ]
1008 ],
1009 'core/image' => [
1010 'lightbox' => [
1011 'allowEditing' => true,
1012 ]
1013 ],
1014 'core/pullquote' => [
1015 'border' => [
1016 'color' => true,
1017 'radius' => true,
1018 'style' => true,
1019 'width' => true,
1020 ]
1021 ],
1022 'core/paragraph' => [
1023 'spacing' => [
1024 'margin' => 1,
1025 'padding' => 1,
1026 ]
1027 ]
1028 ],
1029 'layout' => [
1030 'contentSize' => 'var(--theme-block-max-width)',
1031 'wideSize' => 'var(--theme-block-wide-max-width)',
1032 ],
1033 'background' => [
1034 'backgroundImage' => 1,
1035 'backgroundSize' => 1,
1036 ],
1037 'position' => [
1038 'sticky' => 0,
1039 ]
1040 );
1041 }
1042
1043 private function getColorPalette()
1044 {
1045 return [
1046 [
1047 'color' => 'var(--fcom-primary-bg, #ffffff)',
1048 'name' => 'Accent',
1049 'slug' => 'theme-palette-color-1'
1050 ],
1051 [
1052 'color' => 'var(--fcom-secondary-bg, #f0f2f5)',
1053 'name' => 'Accent - alt',
1054 'slug' => 'theme-palette-color-2'
1055 ],
1056 [
1057 'color' => 'var(--fcom-secondary-text, #525866)',
1058 'name' => 'Strongest text',
1059 'slug' => 'theme-palette-color-3'
1060 ],
1061 [
1062 'color' => 'var(--fcom-secondary-content-bg, #f0f3f5)',
1063 'name' => 'Strong Text',
1064 'slug' => 'theme-palette-color-4'
1065 ],
1066 [
1067 'color' => 'var(--fcom-active-bg, #f0f3f5)',
1068 'name' => 'Medium text',
1069 'slug' => 'theme-palette-color-5'
1070 ],
1071 [
1072 'color' => 'var(--fcom-light-bg, #E1E4EA)',
1073 'name' => 'Subtle Text',
1074 'slug' => 'theme-palette-color-6'
1075 ],
1076 [
1077 'color' => 'var(--fcom-deep-bg, #E1E4EA)',
1078 'name' => 'Subtle Background',
1079 'slug' => 'theme-palette-color-7'
1080 ],
1081 [
1082 'color' => 'var(--fcom-primary-text, #19283a)',
1083 'name' => 'Lighter Background',
1084 'slug' => 'theme-palette-color-8'
1085 ]
1086 ];
1087 }
1088
1089 private function getEditorStyles()
1090 {
1091 $editorDir = FLUENT_COMMUNITY_PLUGIN_DIR . 'Modules/Gutenberg/editor/';
1092
1093 return [
1094 [
1095 '__unstableType' => 'colorSchema',
1096 'css' => $this->getColorSchemaCss(),
1097 'isGlobalStyles' => true
1098 ],
1099 [
1100 '__unstableType' => 'theme',
1101 'css' => file_get_contents($editorDir . 'editor-iframe-styles.css') ?: '',
1102 'isGlobalStyles' => true
1103 ],
1104 [
1105 'css' => file_get_contents($editorDir . 'editor.css') ?: '',
1106 '__unstableType' => 'user'
1107 ]
1108 ];
1109 }
1110
1111 private function getResolvedAssets()
1112 {
1113 $resolvedStyles = [
1114 'wp-components-css' => includes_url('/css/dist/components/style.min.css'),
1115 'wp-preferences-css' => includes_url('/css/dist/preferences/style.min.css'),
1116 'wp-block-editor-css' => includes_url('/css/dist/block-editor/style.min.css'),
1117 'wp-reusable-blocks-css' => includes_url('/css/dist/reusable-blocks/style.min.css'),
1118 'wp-patterns-css' => includes_url('/css/dist/patterns/style.min.css'),
1119 'wp-editor-css' => includes_url('/css/dist/editor/style.min.css'),
1120 'wp-block-library-css' => includes_url('/css/dist/block-library/style.min.css'),
1121 'wp-block-editor-content-css' => includes_url('/css/dist/block-editor/content.min.css'),
1122 'wp-edit-blocks-css' => includes_url('/css/dist/block-library/editor.min.css'),
1123 'fcom-content-styling' => FLUENT_COMMUNITY_PLUGIN_URL . 'Modules/Gutenberg/editor/content_styling.css'
1124 ];
1125
1126 global $wp_version;
1127 $cssFiles = '';
1128 foreach ($resolvedStyles as $name => $file) {
1129 $cssFiles .= "<link rel='stylesheet' id='{$name}' href='{$file}?ver={$wp_version}' media='all' />\n"; // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet
1130 }
1131
1132 return [
1133 'scripts' => '<script src="' . includes_url('/js/dist/vendor/wp-polyfill.min.js?ver=3.15.0') . '" id="wp-polyfill-js"></script>', // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
1134 'styles' => $cssFiles
1135 ];
1136 }
1137
1138 private function getDefaultEditorStyles()
1139 {
1140 return [
1141 [
1142 '__unstableType' => 'user',
1143 'css' => ':root{--wp-admin-theme-color:#007cba;--wp-admin-theme-color--rgb:0, 124, 186;--wp-admin-theme-color-darker-10:#006ba1;--wp-admin-theme-color-darker-10--rgb:0, 107, 161;--wp-admin-theme-color-darker-20:#005a87;--wp-admin-theme-color-darker-20--rgb:0, 90, 135;--wp-admin-border-width-focus:2px;--wp-block-synced-color:#7a00df;--wp-block-synced-color--rgb:122, 0, 223;--wp-bound-block-color:var(--wp-block-synced-color);}@media (min-resolution:192dpi){:root{--wp-admin-border-width-focus:1.5px;}}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:18px;line-height:1.5;--wp--style--block-gap:2em;}p{line-height:1.8;}.editor-post-title__block{font-size:2.5em;font-weight:800;margin-bottom:1em;margin-top:2em;}'
1144 ]
1145 ];
1146 }
1147
1148 private function getColorSchemaCss()
1149 {
1150 $colorSchema = Utility::getColorSchemaConfig();
1151
1152 $darkSchemaConfig = Arr::get($colorSchema, 'dark');
1153
1154 $colorSchemaCss = ':root {';
1155 foreach (Arr::get($darkSchemaConfig, 'body', []) as $colorKey => $value) {
1156 if ($value) {
1157 $cssVar = ' --fcom-' . str_replace('_', '-', $colorKey);
1158 $colorSchemaCss .= $cssVar . ':' . $value . '; ';
1159 }
1160 }
1161 $colorSchemaCss .= '}';
1162
1163 return $colorSchemaCss;
1164 }
1165 }
1166