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

1,124 lines 47.5 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 array(
254 'post' => null
255 )
256 );
257
258 add_filter('user_can_richedit', '__return_true');
259 wp_tinymce_inline_scripts();
260 wp_enqueue_editor();
261
262 /**
263 * Styles
264 */
265 wp_enqueue_style('wp-edit-post');
266
267 // Include block styles needed when user is not signed in. See: https://github.com/WordPress/wporg-gutenberg/issues/26
268 wp_enqueue_style('global-styles');
269 wp_enqueue_style('wp-block-library');
270 wp_enqueue_style('wp-block-image');
271 wp_enqueue_style('wp-block-group');
272 wp_enqueue_style('wp-block-heading');
273 wp_enqueue_style('wp-block-button');
274 wp_enqueue_style('wp-block-paragraph');
275 wp_enqueue_style('wp-block-separator');
276 wp_enqueue_style('wp-block-columns');
277 wp_enqueue_style('wp-block-cover');
278 wp_enqueue_style('global-styles-css-custom-properties');
279 wp_enqueue_style('wp-block-spacer');
280
281 wp_register_style('fluent_com_editor_styles', FLUENT_COMMUNITY_PLUGIN_URL . 'Modules/Gutenberg/editor/style.css', false, FLUENT_COMMUNITY_PLUGIN_VERSION, 'all');
282
283 add_action('fluent_enqueue_block_editor_assets', 'wp_enqueue_editor_format_library_assets');
284
285 /**
286 * Fires after block assets have been enqueued for the editing interface.
287 *
288 * Call `add_action` on any hook before 'admin_enqueue_scripts'.
289 *
290 * In the function call you supply, simply use `wp_enqueue_script` and
291 * `wp_enqueue_style` to add your functionality to the Gutenberg editor.
292 *
293 * @since 0.4.0
294 */
295 do_action('fluent_enqueue_block_editor_assets');
296
297 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 wp_localize_script('fcom_editor_custom', 'fcomEditorI18n', $this->getEditorI18nStrings());
299 }
300
301 private function getEditorI18nStrings()
302 {
303 $strings = [
304 'Enable Video Embed' => __('Enable Video Embed', 'fluent-community'),
305 'Enable Comments' => __('Enable Comments', 'fluent-community'),
306 'Free Preview Lesson' => __('Free Preview Lesson', 'fluent-community'),
307 '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'),
308 'Media Embed' => __('Media Embed', 'fluent-community'),
309 'Documents & Files' => __('Documents & Files', 'fluent-community'),
310 'Smartcodes' => __('Smartcodes', 'fluent-community'),
311 'You may use following smartcode in your lesson:' => __('You may use following smartcode in your lesson:', 'fluent-community'),
312 "User's Name:" => __("User's Name:", 'fluent-community'),
313 "User's Email:" => __("User's Email:", 'fluent-community'),
314 "User's photo HTML:" => __("User's photo HTML:", 'fluent-community'),
315 "User's Profile Link:" => __("User's Profile Link:", 'fluent-community'),
316 'Failed to fetch embed. Please check the URL.' => __('Failed to fetch embed. Please check the URL.', 'fluent-community'),
317 'Video thumbnail' => __('Video thumbnail', 'fluent-community'),
318 'Media embedded successfully' => __('Media embedded successfully', 'fluent-community'),
319 'Edit media' => __('Edit media', 'fluent-community'),
320 'Oembed' => __('Oembed', 'fluent-community'),
321 'Custom HTML' => __('Custom HTML', 'fluent-community'),
322 'Custom HTML Code' => __('Custom HTML Code', 'fluent-community'),
323 'Paste an iframe code' => __('Paste an iframe code', 'fluent-community'),
324 'Embed' => __('Embed', 'fluent-community'),
325 'Paste a URL to embed' => __('Paste a URL to embed', 'fluent-community'),
326 'Embed from Vimeo, YouTube, Wistia and more' => __('Embed from Vimeo, YouTube, Wistia and more', 'fluent-community'),
327 'Lesson Duration' => __('Lesson Duration', 'fluent-community'),
328 'Minutes' => __('Minutes', 'fluent-community'),
329 'Seconds' => __('Seconds', 'fluent-community'),
330 'View' => __('View', 'fluent-community'),
331 'No documents attached yet.' => __('No documents attached yet.', 'fluent-community'),
332 'Manage Documents & Files' => __('Manage Documents & Files', 'fluent-community'),
333 "User's Name" => __("User's Name", 'fluent-community'),
334 "User's Email" => __("User's Email", 'fluent-community'),
335 "User's photo HTML" => __("User's photo HTML", 'fluent-community'),
336 'Profile Link' => __('Profile Link', 'fluent-community'),
337 ];
338
339 return apply_filters('fluent_community/editor_i18n_strings', $strings);
340 }
341
342 private function gutenberg_get_available_image_sizes()
343 {
344 $size_names = apply_filters(
345 'fluent_community/image_size_names_choose',
346 array(
347 'thumbnail' => __('Thumbnail', 'fluent-community'),
348 'medium' => __('Medium', 'fluent-community'),
349 'large' => __('Large', 'fluent-community'),
350 'full' => __('Full Size', 'fluent-community'),
351 )
352 );
353 $all_sizes = array();
354 foreach ($size_names as $size_slug => $size_name) {
355 $all_sizes[] = array(
356 'slug' => $size_slug,
357 'name' => $size_name,
358 );
359 }
360 return $all_sizes;
361 }
362
363 protected function renderPage()
364 {
365
366 remove_action( 'wp_print_styles', 'print_emoji_styles' );
367
368 add_action('fluent_community/block_editor_footer', function () {
369 wp_underscore_playlist_templates();
370 wp_print_footer_scripts();
371 wp_print_media_templates();
372 wp_enqueue_global_styles();
373 wp_enqueue_stored_styles();
374 wp_maybe_inline_styles();
375 });
376
377 add_action('fluent_block_editor/head', 'wp_enqueue_scripts', 1);
378 add_action('fluent_block_editor/head', 'wp_resource_hints', 2);
379 add_action('fluent_block_editor/head', 'wp_preload_resources', 1);
380 add_action('fluent_block_editor/head', 'wp_print_styles', 8);
381 add_action('fluent_block_editor/head', 'wp_print_head_scripts', 9);
382 add_action('fluent_block_editor/head', 'wp_custom_css_cb', 101);
383
384 $this->unloadOtherScripts();
385 ?>
386 <!DOCTYPE html>
387 <html <?php language_attributes(); ?>>
388 <head>
389 <title>FluentCommunity Block Editor</title>
390 <meta charset='utf-8'>
391 <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0,viewport-fit=cover"/>
392 <meta name="mobile-web-app-capable" content="yes">
393 <meta name="robots" content="noindex">
394 <?php do_action('fluent_block_editor/head'); ?>
395 <?php do_action('fluent_community/block_editor_head'); ?>
396 </head>
397 <body class="fcom_custom_editor">
398 <div class="wp-site-blocks">
399 <div id="editor" class="gutenberg__editor"></div>
400 </div>
401 <?php
402 do_action('fluent_community/block_editor_footer');
403 ?>
404 </body>
405 </html>
406 <?php
407 }
408
409 private function shouldBlockAsset(string $src, string $pluginUrl, string $themesUrl, string $approvedPattern): bool
410 {
411 $isPlugin = strpos($src, $pluginUrl) !== false;
412 $isTheme = strpos($src, $themesUrl) !== false;
413
414 if (!$isPlugin && !$isTheme) {
415 return false;
416 }
417
418 return !preg_match('#' . $approvedPattern . '#', $src);
419 }
420
421 private function unloadOtherScripts()
422 {
423 $isSkip = apply_filters('fluent_com_editor/skip_no_conflict', false);
424 if ($isSkip) {
425 return;
426 }
427
428 /**
429 * Define the list of approved slugs for FluentCRM assets.
430 *
431 * This filter allows modification of the list of slugs that are approved for FluentCRM assets.
432 *
433 * @param array $approvedSlugs An array of approved slugs for FluentCRM assets.
434 */
435 $approvedSlugs = apply_filters('fluent_com_editor/asset_listed_slugs', [
436 '\/gutenberg\/'
437 ]);
438 $approvedSlugs[] = 'fluent-community';
439 $approvedSlugs = array_unique($approvedSlugs);
440 $approvedSlugs = implode('|', $approvedSlugs);
441
442 $pluginUrl = str_replace(['http:', 'https:'], '', plugins_url());
443
444 $themesUrl = str_replace(['http:', 'https:'], '', get_theme_root_uri());
445
446 add_filter('script_loader_src', function ($src, $handle) use ($approvedSlugs, $pluginUrl, $themesUrl) {
447 if (!$src) {
448 return $src;
449 }
450
451 if ($this->shouldBlockAsset($src, $pluginUrl, $themesUrl, $approvedSlugs)) {
452 return false;
453 }
454
455 return $src;
456 }, 1, 2);
457
458 add_action('wp_print_scripts', function () use ($approvedSlugs, $pluginUrl, $themesUrl) {
459 global $wp_scripts;
460 if (!$wp_scripts) {
461 return;
462 }
463
464 foreach ($wp_scripts->queue as $script) {
465 if (empty($wp_scripts->registered[$script]) || empty($wp_scripts->registered[$script]->src)) {
466 continue;
467 }
468
469 $src = $wp_scripts->registered[$script]->src;
470 if ($this->shouldBlockAsset($src, $pluginUrl, $themesUrl, $approvedSlugs)) {
471 wp_dequeue_script($wp_scripts->registered[$script]->handle);
472 }
473 }
474 }, 1);
475
476 add_action('wp_print_styles', function () {
477 $isSkip = apply_filters('fluent_community/skip_no_conflict', false, 'styles');
478
479 if ($isSkip) {
480 return;
481 }
482
483 global $wp_styles;
484 if (!$wp_styles) {
485 return;
486 }
487
488 $approvedSlugs = apply_filters('fluent_community/asset_listed_slugs', [
489 '\/gutenberg\/',
490 ]);
491
492 $approvedSlugs[] = '\/fluent-community\/';
493
494 $approvedSlugs = array_unique($approvedSlugs);
495 $approvedSlugs = implode('|', $approvedSlugs);
496
497 $pluginUrl = plugins_url();
498 $themeUrl = get_theme_root_uri();
499
500 $pluginUrl = str_replace(['http:', 'https:'], '', $pluginUrl);
501 $themeUrl = str_replace(['http:', 'https:'], '', $themeUrl);
502
503 foreach ($wp_styles->queue as $script) {
504
505 if (empty($wp_styles->registered[$script]) || empty($wp_styles->registered[$script]->src)) {
506 continue;
507 }
508
509 $src = $wp_styles->registered[$script]->src;
510 $pluginMatched = (strpos($src, $pluginUrl) !== false) && !preg_match('#' . $approvedSlugs . '#', $src);
511 $themeMatched = (strpos($src, $themeUrl) !== false) && !preg_match('#' . $approvedSlugs . '#', $src);
512
513 if (!$pluginMatched && !$themeMatched) {
514 continue;
515 }
516
517 wp_dequeue_style($wp_styles->registered[$script]->handle);
518 }
519 }, 999999);
520 }
521
522 private function getEditorSettings($post)
523 {
524 // Media settings.
525 $max_upload_size = wp_max_upload_size();
526 if (!$max_upload_size) {
527 $max_upload_size = 0;
528 }
529
530 $lock_details = array(
531 'isLocked' => false,
532 'user' => '',
533 );
534
535 $editor_settings = array(
536 'maxUploadFileSize' => $max_upload_size,
537 'allowedMimeTypes' => get_allowed_mime_types(),
538 'postLock' => $lock_details,
539 'postLockUtils' => array(
540 'nonce' => wp_create_nonce('lock-post_' . $post->ID),
541 'unlockNonce' => wp_create_nonce('update-post_' . $post->ID),
542 'ajaxUrl' => admin_url('admin-ajax.php'),
543 ),
544 '__experimentalFeatures' => $this->getExperimentalFeatures(),
545 'colors' => $this->getColorPalette(),
546 '__experimentalDiscussionSettings' => [
547 'avatarURL' => 'https://secure.gravatar.com/avatar/?s=96&f=y&r=g',
548 'commentOrder' => 'asc',
549 'commentsPerPage' => '50',
550 'defaultCommentsPage' => 'newest',
551 'defaultCommentStatus' => 'open',
552 'pageComments' => '',
553 'threadComments' => '1',
554 'threadCommentsDepth' => '5'
555 ],
556 '__unstableGalleryWithImageBlocks' => false,
557 '__unstableIsBlockBasedTheme' => false,
558 'enableCustomUnits' => [
559 'px',
560 'em',
561 'rem',
562 '%',
563 'vh',
564 'vw'
565 ],
566 'fontSizes' => [
567 [
568 'name' => 'Small',
569 'size' => 'var(--fcom-font-size-small)',
570 'slug' => 'small'
571 ],
572 [
573 'name' => 'Medium',
574 'size' => 'var(--fcom-font-size-medium)',
575 'slug' => 'medium'
576 ],
577 [
578 'name' => 'Large',
579 'size' => 'var(--fcom-font-size-large)',
580 'slug' => 'large'
581 ],
582 [
583 'name' => 'Larger',
584 'size' => 'var(--fcom-font-size-larger)',
585 'slug' => 'larger'
586 ],
587 [
588 'name' => 'XX-Large',
589 'size' => 'var(--fcom-font-size-xxlarge)',
590 'slug' => 'xxlarge'
591 ]
592 ],
593 'fullscreenMode' => 1,
594 'enableCustomSpacing' => 1,
595 'enableCustomLineHeight' => 1,
596 'enableCustomFields' => false,
597 'disablePostFormats' => true,
598 'disableLayoutStyles' => false,
599 'disableCustomSpacingSizes' => false,
600 'disableCustomGradients' => 1,
601 'alignWide' => true,
602 'disableCustomFontSizes' => false,
603 'disableCustomColors' => false,
604 'canUpdateBlockBindings' => false,
605 'bodyPlaceholder' => __('Start writing or type / to choose a block for your lesson content', 'fluent-community'),
606 'allowedBlockTypes' => apply_filters('fluent_community/allowed_block_types', [
607 'core/audio',
608 'core/block',
609 'core/buttons',
610 'core/button',
611 'core/code',
612 'core/columns',
613 'core/column',
614 'core/cover',
615 'core/embed',
616 'core/footnotes',
617 'core/freeform',
618 'core/gallery',
619 'core/group',
620 'core/heading',
621 'core/html',
622 'core/image',
623 'core/latest-posts',
624 'core/list',
625 'core/list-item',
626 'core/media-text',
627 'core/missing',
628 'core/paragraph',
629 'core/preformatted',
630 'core/pullquote',
631 'core/quote',
632 'core/rss',
633 'core/separator',
634 'core/social-link',
635 'core/social-links',
636 'core/spacer',
637 'core/table',
638 'core/text-columns',
639 'core/verse',
640 'core/freeform'
641 ]),
642 'gradients' => [],
643 'imageDefaultSize' => 'large',
644 'imageEditing' => true,
645 'isRTL' => Helper::isRtl(),
646 'autosaveInterval' => 999,
647 'localAutosaveInterval' => 999,
648 'richEditingEnabled' => true,
649 'spacingSizes' => [
650 [
651 'name' => '2X-Small',
652 'size' => '0.44rem',
653 'slug' => '20'
654 ],
655 [
656 'name' => 'X-Small',
657 'size' => '0.67rem',
658 'slug' => '30'
659 ],
660 [
661 'name' => 'Small',
662 'size' => '1rem',
663 'slug' => '40'
664 ],
665 [
666 'name' => 'Medium',
667 'size' => '1.5rem',
668 'slug' => '50'
669 ],
670 [
671 'name' => 'Large',
672 'size' => '2.25rem',
673 'slug' => '60'
674 ],
675 [
676 'name' => 'X-Large',
677 'size' => '3.38rem',
678 'slug' => '70'
679 ],
680 [
681 'name' => '2X-Large',
682 'size' => '5.06rem',
683 'slug' => '80'
684 ]
685 ],
686 'titlePlaceholder' => __('Add Lesson title', 'fluent-community')
687 );
688
689 $editor_settings['styles'] = $this->getEditorStyles();
690 $editor_settings['__unstableResolvedAssets'] = $this->getResolvedAssets();
691 $editor_settings['defaultEditorStyles'] = $this->getDefaultEditorStyles();
692 $editor_settings['imageSizes'] = $this->gutenberg_get_available_image_sizes();
693
694 $editor_settings = apply_filters('fluent_community/block_editor_settings', $editor_settings);
695 return $editor_settings;
696 }
697
698 private function getExperimentalFeatures()
699 {
700 return array(
701 'appearanceTools' => true,
702 'useRootPaddingAwareAlignments' => false,
703 'border' => [
704 'color' => 1,
705 'radius' => 1,
706 'style' => 1,
707 'width' => 1,
708 ],
709 'color' => [
710 'background' => true,
711 'button' => 1,
712 'caption' => 1,
713 'customDuotone' => 0,
714 'defaultDuotone' => 0,
715 'defaultGradients' => 0,
716 'defaultPalette' => [],
717 'duotone' => [],
718 'gradients' => [],
719 'heading' => 1,
720 'link' => 1,
721 'palette' => [
722 'default' => [],
723 'theme' => [
724 [
725 'name' => 'Accent',
726 'slug' => 'theme-palette-color-1',
727 'color' => 'var(--theme-palette-color-1)',
728 ],
729 [
730 'name' => 'Accent - alt',
731 'slug' => 'theme-palette-color-2',
732 'color' => 'var(--theme-palette-color-2)',
733 ],
734 [
735 'name' => 'Strongest text',
736 'slug' => 'theme-palette-color-3',
737 'color' => 'var(--theme-palette-color-3)',
738 ],
739 [
740 'name' => 'Strong Text',
741 'slug' => 'theme-palette-color-4',
742 'color' => 'var(--theme-palette-color-4)',
743 ],
744 [
745 'name' => 'Medium text',
746 'slug' => 'theme-palette-color-5',
747 'color' => 'var(--theme-palette-color-5)',
748 ],
749 [
750 'name' => 'Subtle Text',
751 'slug' => 'theme-palette-color-6',
752 'color' => 'var(--theme-palette-color-6)',
753 ],
754 [
755 'name' => 'Subtle Background',
756 'slug' => 'theme-palette-color-7',
757 'color' => 'var(--theme-palette-color-7)',
758 ],
759 [
760 'name' => 'Lighter Background',
761 'slug' => 'theme-palette-color-8',
762 'color' => 'var(--theme-palette-color-8)',
763 ]
764 ]
765 ],
766 'text' => true,
767 ],
768 'dimensions' => [
769 'defaultAspectRatios' => true,
770 'aspectRatios' => [
771 'default' => [
772 [
773 'name' => 'Square - 1:1',
774 'slug' => 'square',
775 'ratio' => '1',
776 ],
777 [
778 'name' => 'Standard - 4:3',
779 'slug' => '4-3',
780 'ratio' => '4/3',
781 ],
782 [
783 'name' => 'Portrait - 3:4',
784 'slug' => '3-4',
785 'ratio' => '3/4',
786 ],
787 [
788 'name' => 'Classic - 3:2',
789 'slug' => '3-2',
790 'ratio' => '3/2',
791 ],
792 [
793 'name' => 'Classic Portrait - 2:3',
794 'slug' => '2-3',
795 'ratio' => '2/3',
796 ],
797 [
798 'name' => 'Wide - 16:9',
799 'slug' => '16-9',
800 'ratio' => '16/9',
801 ],
802 [
803 'name' => 'Tall - 9:16',
804 'slug' => '9-16',
805 'ratio' => '9/16',
806 ],
807 ]
808 ],
809 'aspectRatio' => 1,
810 'minHeight' => 1,
811 ],
812 'shadow' => [
813 'defaultPresets' => true,
814 'presets' => [
815 'default' => [
816 [
817 'name' => 'Natural',
818 'slug' => 'natural',
819 'shadow' => '6px 6px 9px rgba(0, 0, 0, 0.2)',
820 ],
821 [
822 'name' => 'Deep',
823 'slug' => 'deep',
824 'shadow' => '12px 12px 50px rgba(0, 0, 0, 0.4)',
825 ],
826 [
827 'name' => 'Sharp',
828 'slug' => 'sharp',
829 'shadow' => '6px 6px 0px rgba(0, 0, 0, 0.2)',
830 ],
831 [
832 'name' => 'Outlined',
833 'slug' => 'outlined',
834 'shadow' => '6px 6px 0px -3px rgba(255, 255, 255, 1), 6px 6px rgba(0, 0, 0, 1)',
835 ],
836 [
837 'name' => 'Crisp',
838 'slug' => 'crisp',
839 'shadow' => '6px 6px 0px rgba(0, 0, 0, 1)',
840 ],
841 ],
842 ],
843 ],
844 'spacing' => [
845 'blockGap' => 1,
846 'margin' => 1,
847 'padding' => 1,
848 'defaultSpacingSizes' => true,
849 'spacingScale' => [
850 'default' => [
851 'operator' => '*',
852 'increment' => 1.5,
853 'steps' => 7,
854 'mediumStep' => 1.5,
855 'unit' => 'rem',
856 ],
857 ],
858 'spacingSizes' => [
859 'default' => [
860 [
861 'name' => '2X-Small',
862 'slug' => '20',
863 'size' => '0.44rem',
864 ],
865 [
866 'name' => 'X-Small',
867 'slug' => '30',
868 'size' => '0.67rem',
869 ],
870 [
871 'name' => 'Small',
872 'slug' => '40',
873 'size' => '1rem',
874 ],
875 [
876 'name' => 'Medium',
877 'slug' => '50',
878 'size' => '1.5rem',
879 ],
880 [
881 'name' => 'Large',
882 'slug' => '60',
883 'size' => '2.25rem',
884 ],
885 [
886 'name' => 'X-Large',
887 'slug' => '70',
888 'size' => '3.38rem',
889 ],
890 [
891 'name' => '2X-Large',
892 'slug' => '80',
893 'size' => '5.06rem',
894 ],
895 ],
896 ],
897 ],
898 'typography' => [
899 'defaultFontSizes' => NULL,
900 'dropCap' => true,
901 'fontSizes' => [
902 'default' => [
903 [
904 'name' => 'Small',
905 'slug' => 'small',
906 'size' => '13px',
907 ],
908 [
909 'name' => 'Medium',
910 'slug' => 'medium',
911 'size' => '20px',
912 ],
913 [
914 'name' => 'Large',
915 'slug' => 'large',
916 'size' => '36px',
917 ],
918 [
919 'name' => 'Extra Large',
920 'slug' => 'x-large',
921 'size' => '42px',
922 ],
923 ],
924 'theme' => [
925 [
926 'name' => 'Small',
927 'slug' => 'small',
928 'size' => 'var(--fcom-font-size-small)',
929 ],
930 [
931 'name' => 'Medium',
932 'slug' => 'medium',
933 'size' => 'var(--fcom-font-size-medium)',
934 ],
935 [
936 'name' => 'Large',
937 'slug' => 'large',
938 'size' => 'var(--fcom-font-size-large)',
939 ],
940 [
941 'name' => 'Larger',
942 'slug' => 'larger',
943 'size' => 'var(--fcom-font-size-larger)',
944 ],
945 [
946 'name' => 'XX-Large',
947 'slug' => 'xxlarge',
948 'size' => 'var(--fcom-font-size-xxlarge)',
949 ],
950 ],
951 ],
952 'fontStyle' => true,
953 'fontWeight' => true,
954 'letterSpacing' => true,
955 'textAlign' => true,
956 'textDecoration' => true,
957 'textTransform' => true,
958 'writingMode' => false,
959 'fluid' => 0,
960 ],
961 'blocks' => [
962 'core/button' => [
963 'border' => [
964 'radius' => true,
965 ]
966 ],
967 'core/image' => [
968 'lightbox' => [
969 'allowEditing' => true,
970 ]
971 ],
972 'core/pullquote' => [
973 'border' => [
974 'color' => true,
975 'radius' => true,
976 'style' => true,
977 'width' => true,
978 ]
979 ],
980 'core/paragraph' => [
981 'spacing' => [
982 'margin' => 1,
983 'padding' => 1,
984 ]
985 ]
986 ],
987 'layout' => [
988 'contentSize' => 'var(--theme-block-max-width)',
989 'wideSize' => 'var(--theme-block-wide-max-width)',
990 ],
991 'background' => [
992 'backgroundImage' => 1,
993 'backgroundSize' => 1,
994 ],
995 'position' => [
996 'sticky' => 0,
997 ]
998 );
999 }
1000
1001 private function getColorPalette()
1002 {
1003 return [
1004 [
1005 'color' => 'var(--fcom-primary-bg, #ffffff)',
1006 'name' => 'Accent',
1007 'slug' => 'theme-palette-color-1'
1008 ],
1009 [
1010 'color' => 'var(--fcom-secondary-bg, #f0f2f5)',
1011 'name' => 'Accent - alt',
1012 'slug' => 'theme-palette-color-2'
1013 ],
1014 [
1015 'color' => 'var(--fcom-secondary-text, #525866)',
1016 'name' => 'Strongest text',
1017 'slug' => 'theme-palette-color-3'
1018 ],
1019 [
1020 'color' => 'var(--fcom-secondary-content-bg, #f0f3f5)',
1021 'name' => 'Strong Text',
1022 'slug' => 'theme-palette-color-4'
1023 ],
1024 [
1025 'color' => 'var(--fcom-active-bg, #f0f3f5)',
1026 'name' => 'Medium text',
1027 'slug' => 'theme-palette-color-5'
1028 ],
1029 [
1030 'color' => 'var(--fcom-light-bg, #E1E4EA)',
1031 'name' => 'Subtle Text',
1032 'slug' => 'theme-palette-color-6'
1033 ],
1034 [
1035 'color' => 'var(--fcom-deep-bg, #E1E4EA)',
1036 'name' => 'Subtle Background',
1037 'slug' => 'theme-palette-color-7'
1038 ],
1039 [
1040 'color' => 'var(--fcom-primary-text, #19283a)',
1041 'name' => 'Lighter Background',
1042 'slug' => 'theme-palette-color-8'
1043 ]
1044 ];
1045 }
1046
1047 private function getEditorStyles()
1048 {
1049 $editorDir = FLUENT_COMMUNITY_PLUGIN_DIR . 'Modules/Gutenberg/editor/';
1050
1051 return [
1052 [
1053 '__unstableType' => 'colorSchema',
1054 'css' => $this->getColorSchemaCss(),
1055 'isGlobalStyles' => true
1056 ],
1057 [
1058 '__unstableType' => 'theme',
1059 'css' => file_get_contents($editorDir . 'editor-iframe-styles.css') ?: '',
1060 'isGlobalStyles' => true
1061 ],
1062 [
1063 'css' => file_get_contents($editorDir . 'editor.css') ?: '',
1064 '__unstableType' => 'user'
1065 ]
1066 ];
1067 }
1068
1069 private function getResolvedAssets()
1070 {
1071 $resolvedStyles = [
1072 'wp-components-css' => includes_url('/css/dist/components/style.min.css'),
1073 'wp-preferences-css' => includes_url('/css/dist/preferences/style.min.css'),
1074 'wp-block-editor-css' => includes_url('/css/dist/block-editor/style.min.css'),
1075 'wp-reusable-blocks-css' => includes_url('/css/dist/reusable-blocks/style.min.css'),
1076 'wp-patterns-css' => includes_url('/css/dist/patterns/style.min.css'),
1077 'wp-editor-css' => includes_url('/css/dist/editor/style.min.css'),
1078 'wp-block-library-css' => includes_url('/css/dist/block-library/style.min.css'),
1079 'wp-block-editor-content-css' => includes_url('/css/dist/block-editor/content.min.css'),
1080 'wp-edit-blocks-css' => includes_url('/css/dist/block-library/editor.min.css'),
1081 'fcom-content-styling' => FLUENT_COMMUNITY_PLUGIN_URL . 'Modules/Gutenberg/editor/content_styling.css'
1082 ];
1083
1084 global $wp_version;
1085 $cssFiles = '';
1086 foreach ($resolvedStyles as $name => $file) {
1087 $cssFiles .= "<link rel='stylesheet' id='{$name}' href='{$file}?ver={$wp_version}' media='all' />\n"; // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet
1088 }
1089
1090 return [
1091 '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
1092 'styles' => $cssFiles
1093 ];
1094 }
1095
1096 private function getDefaultEditorStyles()
1097 {
1098 return [
1099 [
1100 '__unstableType' => 'user',
1101 '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;}'
1102 ]
1103 ];
1104 }
1105
1106 private function getColorSchemaCss()
1107 {
1108 $colorSchema = Utility::getColorSchemaConfig();
1109
1110 $darkSchemaConfig = Arr::get($colorSchema, 'dark');
1111
1112 $colorSchemaCss = ':root {';
1113 foreach (Arr::get($darkSchemaConfig, 'body', []) as $colorKey => $value) {
1114 if ($value) {
1115 $cssVar = ' --fcom-' . str_replace('_', '-', $colorKey);
1116 $colorSchemaCss .= $cssVar . ':' . $value . '; ';
1117 }
1118 }
1119 $colorSchemaCss .= '}';
1120
1121 return $colorSchemaCss;
1122 }
1123 }
1124