PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.8.1
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.8.1
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.8.1, at app/Hooks/Handlers/FluentBlockEditorHandler.php

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