PluginProbe ʕ •ᴥ•ʔ
Superb Addons: Blocks, Patterns, Pre-built Pages, Sliders, Popups, Free Forms, Animations & More / 4.1.0
Superb Addons: Blocks, Patterns, Pre-built Pages, Sliders, Popups, Free Forms, Animations & More v4.1.0
4.1.0 4.0.9 4.0.8 4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 4.0.2 4.0.1 4.0.0 trunk 1.0.0 2.0.0 2.0.1 2.0.2 2.0.3 3.0 3.0.1 3.0.2 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.2 3.1.3 3.2.0 3.2.1 3.2.2 3.2.4 3.2.5 3.2.7 3.2.8 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.4.5 3.4.6 3.5.0 3.5.1 3.5.2 3.5.3 3.5.4 3.5.6 3.5.7 3.5.8 3.5.9 3.6.0 3.6.1 3.6.2 3.7.0 3.7.1
superb-blocks / src / gutenberg / class-gutenberg-controller.php
superb-blocks / src / gutenberg Last commit date
block-api 2 weeks ago form 1 week ago import 2 weeks ago popup 2 weeks ago templates 2 weeks ago class-gutenberg-block-styles.php 2 weeks ago class-gutenberg-cache-util.php 2 weeks ago class-gutenberg-conditions-controller.php 2 weeks ago class-gutenberg-controller.php 2 weeks ago class-gutenberg-dynamic-content-controller.php 2 weeks ago class-gutenberg-enhancements-controller.php 1 week ago class-gutenberg-social-icons-controller.php 2 weeks ago class-gutenberg-sticky-controller.php 2 weeks ago class-gutenberg-z-index-controller.php 2 weeks ago
class-gutenberg-controller.php
693 lines
1 <?php
2
3 namespace SuperbAddons\Gutenberg\Controllers;
4
5 defined('ABSPATH') || exit();
6
7 use SuperbAddons\Admin\Controllers\SettingsController;
8 use SuperbAddons\Admin\Controllers\Wizard\WizardController;
9 use SuperbAddons\Admin\Controllers\Wizard\WizardRestorationPointController;
10 use SuperbAddons\Admin\Utils\AdminLinkSource;
11 use SuperbAddons\Admin\Utils\AdminLinkUtil;
12 use SuperbAddons\Commerce\CommerceController;
13 use SuperbAddons\Data\Controllers\CompatibilitySettingsOptionKey;
14 use SuperbAddons\Data\Controllers\KeyController;
15 use SuperbAddons\Data\Controllers\LinkController;
16 use SuperbAddons\Data\Controllers\RestController;
17 use SuperbAddons\Data\Utils\AllowedTemplateHTMLUtil;
18 use SuperbAddons\Data\Utils\ScriptTranslations;
19 use SuperbAddons\Gutenberg\BlocksAPI\Controllers\AuthorBoxController;
20 use SuperbAddons\Gutenberg\BlocksAPI\Controllers\DynamicBlockAssets;
21 use SuperbAddons\Gutenberg\BlocksAPI\Controllers\RecentPostsController;
22 use SuperbAddons\Gutenberg\BlocksAPI\Controllers\TableOfContentsController;
23 use SuperbAddons\Library\Controllers\FavoritesController;
24 use SuperbAddons\Library\Controllers\LibraryController;
25 use SuperbAddons\Library\Controllers\LibraryRequestController;
26 use SuperbAddons\Data\Controllers\OptionController;
27 use SuperbAddons\Gutenberg\Form\FormAccessControl;
28 use SuperbAddons\Gutenberg\Form\FormController;
29 use SuperbAddons\Gutenberg\Form\FormEmailConfigCheck;
30 use SuperbAddons\Gutenberg\Form\FormEncryption;
31 use SuperbAddons\Gutenberg\Form\FormFileHandler;
32 use SuperbAddons\Gutenberg\Form\FormPermissions;
33 use SuperbAddons\Gutenberg\Form\FormRegistry;
34 use SuperbAddons\Gutenberg\Import\GutenbergBlockIdRegenerator;
35 use SuperbAddons\Gutenberg\Popup\PopupButtonRender;
36 use SuperbAddons\Gutenberg\Popup\PopupRegistry;
37
38 class GutenbergController
39 {
40 const MINIMUM_WORDPRESS_VERSION = '6.2';
41 const MINIMUM_PHP_VERSION = '5.6';
42
43 const VARIABLE_FALLBACKS_HANDLE = 'superb-addons-variable-fallbacks';
44
45 const BLOCK_PREFIX = 'superb-addons/';
46
47 // Parent blocks that users can toggle on/off
48 const TOGGLEABLE_BLOCKS = array(
49 'author-box',
50 'ratings',
51 'table-of-contents',
52 'recent-posts',
53 'cover-image',
54 'google-maps',
55 'reveal-buttons',
56 'accordion',
57 'carousel',
58 'countdown',
59 'progress-bar',
60 'popup',
61 'form',
62 'add-to-cart',
63 );
64
65 // Child blocks that should be hidden when their parent is disabled
66 const CHILD_BLOCK_MAP = array(
67 'carousel-slide' => 'carousel',
68 'reveal-button' => 'reveal-buttons',
69 'form-field' => 'form',
70 'form-step' => 'form',
71 'multistep-form' => 'form',
72 );
73
74 // Block variations shown as separate discoverable blocks in admin UI,
75 // sharing the enable/disable state of their parent toggleable block.
76 // Map: variation slug => parent slug (must be in TOGGLEABLE_BLOCKS).
77 const DISCOVERABLE_VARIATIONS = array(
78 'buy-now' => 'add-to-cart',
79 'multistep-form' => 'form',
80 );
81
82 // Blocks that should not be available in the widget editor
83 const WIDGET_EDITOR_EXCLUDED_BLOCKS = array(
84 'superb-addons/form',
85 'superb-addons/multistep-form',
86 'superb-addons/form-field',
87 'superb-addons/form-step',
88 'superb-addons/popup',
89 );
90
91 const PATTERN_BLOCK_ARG = 'is_pattern_block';
92 const BLOCKS = array(
93 ['path' => "animated-heading", "args" => ['render_callback' => array(DynamicBlockAssets::class, 'EnqueueAnimatedHeader')]],
94 ['path' => "author-box", "args" => ['render_callback' => array(AuthorBoxController::class, 'Render')]],
95 ['path' => "ratings", "args" => []],
96 ['path' => "table-of-contents", "args" => ['render_callback' => array(TableOfContentsController::class, 'DynamicRender')]],
97 ['path' => "recent-posts", "args" => ['render_callback' => array(RecentPostsController::class, 'DynamicRender')]],
98 ['path' => "cover-image", "args" => []],
99 ['path' => "google-maps", "args" => []],
100 ['path' => "reveal-buttons", "args" => []],
101 ['path' => "reveal-button", "args" => ['render_callback' => array(DynamicBlockAssets::class, 'EnqueueRevealButton')]],
102 ['path' => "accordion", "args" => ['render_callback' => array(DynamicBlockAssets::class, 'EnqueueAccordion')]],
103 ['path' => "carousel", "args" => ['render_callback' => array(DynamicBlockAssets::class, 'EnqueueCarousel')]],
104 ['path' => "carousel-slide", "args" => []],
105 ['path' => "countdown", "args" => ['render_callback' => array(DynamicBlockAssets::class, 'EnqueueCountdown')]],
106 ['path' => "progress-bar", "args" => ['render_callback' => array(DynamicBlockAssets::class, 'EnqueueProgressBar')]],
107 ['path' => "popup", "args" => ['render_callback' => array(DynamicBlockAssets::class, 'EnqueuePopup')]],
108 ['path' => "form", "args" => ['render_callback' => array(DynamicBlockAssets::class, 'EnqueueForm')]],
109 ['path' => "form-field", "args" => []],
110 ['path' => "multistep-form", "args" => ['render_callback' => array(DynamicBlockAssets::class, 'EnqueueForm')]],
111 ['path' => "form-step", "args" => []],
112 ['path' => "add-to-cart", "args" => ['render_callback' => array(CommerceController::class, 'RenderBlock')]],
113 );
114
115 public function __construct()
116 {
117 WizardRestorationPointController::Initialize();
118
119 if (!self::is_compatible()) {
120 return;
121 }
122
123 add_action('block_categories_all', array($this, 'RegisterBlockCategory'), defined('PHP_INT_MAX') ? PHP_INT_MAX : 999, 2);
124 add_action('init', array($this, 'RegisterBlocksAndStyles'), 0);
125 add_action('enqueue_block_editor_assets', array($this, 'EnqueueBlockEditorAssets'));
126 add_filter('register_block_type_args', array($this, 'MaybeHideDisabledBlock'), 10, 2);
127 add_filter('allowed_block_types_all', array($this, 'MaybeExcludeBlocksFromWidgetEditor'), 10, 2);
128
129 // enqueue_block_assets fires on the frontend AND inside the editor iframe.
130 // Patterns CSS is registered through here so it reaches the iframe correctly.
131 add_action("enqueue_block_assets", array($this, 'EnqueueEditorIframeAssets'));
132
133 add_action('enqueue_block_editor_assets', array($this, 'EnqueueVariableFallbacks'), PHP_INT_MIN);
134 add_action("wp_enqueue_scripts", array($this, 'EnqueueVariableFallbacks'), PHP_INT_MIN);
135 add_action('wp_print_styles', array($this, 'ReorderVariableFallbacks'), PHP_INT_MAX);
136
137 GutenbergEnhancementsController::Initialize();
138 TableOfContentsController::Initialize();
139 FormController::Initialize();
140 CommerceController::Initialize();
141 PopupRegistry::Initialize();
142 PopupButtonRender::Initialize();
143 WizardController::Initialize();
144 }
145
146 /**
147 * Check if WooCommerce plugin files exist in wp-content/plugins (installed but possibly inactive).
148 */
149 public static function IsWooCommerceInstalled()
150 {
151 if (!function_exists('get_plugins')) {
152 require_once ABSPATH . 'wp-admin/includes/plugin.php';
153 }
154 $plugins = get_plugins();
155 return isset($plugins['woocommerce/woocommerce.php']);
156 }
157
158 /**
159 * Activation URL for WooCommerce with the proper nonce, or '' if not installed.
160 */
161 public static function GetWooCommerceActivateUrl()
162 {
163 if (!self::IsWooCommerceInstalled()) {
164 return '';
165 }
166 $plugin = 'woocommerce/woocommerce.php';
167 return wp_nonce_url(
168 self_admin_url('plugins.php?action=activate&plugin=' . urlencode($plugin) . '&plugin_status=all'),
169 'activate-plugin_' . $plugin
170 );
171 }
172
173 /**
174 * Total number of blocks shown in the admin UI, counting each discoverable variation separately.
175 */
176 public static function GetDiscoverableBlockTotal()
177 {
178 return count(self::TOGGLEABLE_BLOCKS) + count(self::DISCOVERABLE_VARIATIONS);
179 }
180
181 /**
182 * Number of currently-enabled blocks shown in the admin UI, counting each discoverable variation
183 * separately. A variation is enabled when its parent toggleable block is not disabled.
184 */
185 public static function GetDiscoverableBlockActiveCount($disabled_blocks)
186 {
187 $active = count(self::TOGGLEABLE_BLOCKS) - count($disabled_blocks);
188 foreach (self::DISCOVERABLE_VARIATIONS as $parent_slug) {
189 if (!in_array($parent_slug, $disabled_blocks, true)) {
190 $active++;
191 }
192 }
193 return $active;
194 }
195
196 public static function is_compatible()
197 {
198 // Check for required WP version
199 if (version_compare(get_bloginfo('version'), self::MINIMUM_WORDPRESS_VERSION, '<')) {
200 return false;
201 }
202
203 // Check for required PHP version
204 if (version_compare(PHP_VERSION, self::MINIMUM_PHP_VERSION, '<')) {
205 return false;
206 }
207
208 return true;
209 }
210
211 public static function is_block_theme()
212 {
213 if (!function_exists('wp_is_block_theme')) {
214 return false;
215 }
216
217 if (!wp_is_block_theme()) {
218 return false;
219 }
220
221 return true;
222 }
223
224 public static function GetGutenbergLibraryMenuItems()
225 {
226 return array(
227 array(
228 "id" => "patterns",
229 "premium_url" => AdminLinkUtil::GetExpLink(AdminLinkSource::LIBRARY_ITEM),
230 "title" => esc_html__('Patterns', "superb-blocks"),
231 "routes" => array(
232 "list" => LibraryRequestController::GUTENBERG_V2_LIST_ROUTE,
233 "insert" => LibraryRequestController::GUTENBERG_V2_INSERT_ROUTE
234 ),
235 "type" => LibraryRequestController::GUTENBERG_TYPE_PATTERN,
236 "hidden" => false
237 ),
238 array(
239 "id" => "pages",
240 "premium_url" => AdminLinkUtil::GetExpLink(AdminLinkSource::LIBRARY_PAGE_ITEM),
241 "title" => esc_html__('Pages', "superb-blocks"),
242 "routes" => array(
243 "list" => LibraryRequestController::GUTENBERG_V2_LIST_ROUTE,
244 "insert" => LibraryRequestController::GUTENBERG_V2_INSERT_ROUTE
245 ),
246 "type" => LibraryRequestController::GUTENBERG_TYPE_PAGE,
247 "hidden" => false
248 )
249 );
250 }
251
252 public function EnqueuePatternAssets()
253 {
254 wp_enqueue_style(
255 'superb-addons-patterns'
256 );
257 }
258
259 public function EnqueueVariableFallbacks()
260 {
261 $fallbacks = ":root{--wp--preset--color--primary:#1f7cec;--wp--preset--color--primary-hover:#3993ff;--wp--preset--color--base:#fff;--wp--preset--color--featured:#0a284b;--wp--preset--color--contrast-light:#fff;--wp--preset--color--contrast-dark:#000;--wp--preset--color--mono-1:#0d3c74;--wp--preset--color--mono-2:#64748b;--wp--preset--color--mono-3:#e2e8f0;--wp--preset--color--mono-4:#f8fafc;--wp--preset--spacing--superbspacing-xxsmall:clamp(5px,1vw,10px);--wp--preset--spacing--superbspacing-xsmall:clamp(10px,2vw,20px);--wp--preset--spacing--superbspacing-small:clamp(20px,4vw,40px);--wp--preset--spacing--superbspacing-medium:clamp(30px,6vw,60px);--wp--preset--spacing--superbspacing-large:clamp(40px,8vw,80px);--wp--preset--spacing--superbspacing-xlarge:clamp(50px,10vw,100px);--wp--preset--spacing--superbspacing-xxlarge:clamp(60px,12vw,120px);--wp--preset--font-size--superbfont-tiny:clamp(10px,0.625rem + ((1vw - 3.2px) * 0.227),12px);--wp--preset--font-size--superbfont-xxsmall:clamp(12px,0.75rem + ((1vw - 3.2px) * 0.227),14px);--wp--preset--font-size--superbfont-xsmall:clamp(16px,1rem + ((1vw - 3.2px) * 1),16px);--wp--preset--font-size--superbfont-small:clamp(16px,1rem + ((1vw - 3.2px) * 0.227),18px);--wp--preset--font-size--superbfont-medium:clamp(18px,1.125rem + ((1vw - 3.2px) * 0.227),20px);--wp--preset--font-size--superbfont-large:clamp(24px,1.5rem + ((1vw - 3.2px) * 0.909),32px);--wp--preset--font-size--superbfont-xlarge:clamp(32px,2rem + ((1vw - 3.2px) * 1.818),48px);--wp--preset--font-size--superbfont-xxlarge:clamp(40px,2.5rem + ((1vw - 3.2px) * 2.727),64px)}.has-primary-color{color:var(--wp--preset--color--primary)!important}.has-primary-hover-color{color:var(--wp--preset--color--primary-hover)!important}.has-base-color{color:var(--wp--preset--color--base)!important}.has-featured-color{color:var(--wp--preset--color--featured)!important}.has-contrast-light-color{color:var(--wp--preset--color--contrast-light)!important}.has-contrast-dark-color{color:var(--wp--preset--color--contrast-dark)!important}.has-mono-1-color{color:var(--wp--preset--color--mono-1)!important}.has-mono-2-color{color:var(--wp--preset--color--mono-2)!important}.has-mono-3-color{color:var(--wp--preset--color--mono-3)!important}.has-mono-4-color{color:var(--wp--preset--color--mono-4)!important}.has-primary-background-color{background-color:var(--wp--preset--color--primary)!important}.has-primary-hover-background-color{background-color:var(--wp--preset--color--primary-hover)!important}.has-base-background-color{background-color:var(--wp--preset--color--base)!important}.has-featured-background-color{background-color:var(--wp--preset--color--featured)!important}.has-contrast-light-background-color{background-color:var(--wp--preset--color--contrast-light)!important}.has-contrast-dark-background-color{background-color:var(--wp--preset--color--contrast-dark)!important}.has-mono-1-background-color{background-color:var(--wp--preset--color--mono-1)!important}.has-mono-2-background-color{background-color:var(--wp--preset--color--mono-2)!important}.has-mono-3-background-color{background-color:var(--wp--preset--color--mono-3)!important}.has-mono-4-background-color{background-color:var(--wp--preset--color--mono-4)!important}.has-superbfont-tiny-font-size{font-size:var(--wp--preset--font-size--superbfont-tiny)!important}.has-superbfont-xxsmall-font-size{font-size:var(--wp--preset--font-size--superbfont-xxsmall)!important}.has-superbfont-xsmall-font-size{font-size:var(--wp--preset--font-size--superbfont-xsmall)!important}.has-superbfont-small-font-size{font-size:var(--wp--preset--font-size--superbfont-small)!important}.has-superbfont-medium-font-size{font-size:var(--wp--preset--font-size--superbfont-medium)!important}.has-superbfont-large-font-size{font-size:var(--wp--preset--font-size--superbfont-large)!important}.has-superbfont-xlarge-font-size{font-size:var(--wp--preset--font-size--superbfont-xlarge)!important}.has-superbfont-xxlarge-font-size{font-size:var(--wp--preset--font-size--superbfont-xxlarge)!important}";
262 wp_add_inline_style(self::VARIABLE_FALLBACKS_HANDLE, $fallbacks);
263 wp_enqueue_style(self::VARIABLE_FALLBACKS_HANDLE);
264 }
265
266 public function ReorderVariableFallbacks()
267 {
268 // Some themes/plugins may incorrectly call wp_enqueue_global_styles outside of the enqueue phase, causing our fallbacks to be enqueued in the wrong order.
269 // To fix this, we will move our fallbacks to the front of the queue
270 global $wp_styles;
271
272 if (!isset($wp_styles) || !is_object($wp_styles)) {
273 return;
274 }
275 if (!isset($wp_styles->queue) || !is_array($wp_styles->queue) || empty($wp_styles->queue)) {
276 return;
277 }
278 if (!isset($wp_styles->registered[self::VARIABLE_FALLBACKS_HANDLE])) {
279 return;
280 }
281 if (!in_array(self::VARIABLE_FALLBACKS_HANDLE, $wp_styles->queue)) {
282 return;
283 }
284 if ($wp_styles->queue[0] === self::VARIABLE_FALLBACKS_HANDLE) {
285 return;
286 }
287
288 $wp_styles->queue = array_diff($wp_styles->queue, array(self::VARIABLE_FALLBACKS_HANDLE));
289 array_unshift($wp_styles->queue, self::VARIABLE_FALLBACKS_HANDLE);
290 }
291
292 public function EnqueueEditorIframeAssets()
293 {
294 global $pagenow;
295
296 // Patterns CSS — needed on the frontend and inside the editor iframe.
297 // This hook is the only path that reaches the iframe correctly.
298 $this->EnqueuePatternAssets();
299
300 // Enhancements CSS must load inside the editor iframe so that
301 // media queries respond to the iframe width (device preview).
302 if (is_admin()) {
303 wp_enqueue_style(
304 'superb-addons-editor-enhancements',
305 SUPERBADDONS_ASSETS_PATH . '/css/editor-enhancements.min.css',
306 array(),
307 SUPERBADDONS_VERSION
308 );
309 }
310
311 // Site-editor-only assets
312 if ('site-editor.php' === $pagenow) {
313 wp_enqueue_style(
314 'superb-gutenberg-layout-library',
315 SUPERBADDONS_ASSETS_PATH . '/css/layout-library-preview.min.css',
316 array(),
317 SUPERBADDONS_VERSION
318 );
319 $this->EnqueueVariableFallbacks();
320 }
321 }
322
323 public function EnqueueBlockEditorAssets()
324 {
325 self::AddonsLibrary();
326 self::EditorEnhancements();
327 wp_enqueue_script(
328 'superb-addons-gutenberg-library',
329 SUPERBADDONS_ASSETS_PATH . '/js/gutenberg/pattern-library.js',
330 array("jquery", "wp-plugins", "wp-hooks", "wp-data", "wp-element", "wp-i18n", "wp-components", "wp-compose", "wp-blocks", "wp-editor", "wp-block-editor"),
331 SUPERBADDONS_VERSION,
332 true
333 );
334 ScriptTranslations::Set('superb-addons-gutenberg-library');
335 wp_enqueue_script(
336 'superb-addons-upsell-modal',
337 SUPERBADDONS_ASSETS_PATH . '/js/gutenberg/upsell-modal.js',
338 array("wp-plugins", "wp-data", "wp-element", "wp-i18n", "wp-components", "wp-url", "wp-escape-html"),
339 SUPERBADDONS_VERSION,
340 true
341 );
342 ScriptTranslations::Set('superb-addons-upsell-modal');
343 wp_enqueue_script(
344 'superb-addons-upsell-modal-admin',
345 SUPERBADDONS_ASSETS_PATH . '/js/admin/upsell-modal.js',
346 array('wp-i18n', 'wp-url', 'wp-escape-html'),
347 SUPERBADDONS_VERSION,
348 true
349 );
350 ScriptTranslations::Set('superb-addons-upsell-modal-admin');
351 wp_enqueue_style(
352 'superb-addons-admin-modal',
353 SUPERBADDONS_ASSETS_PATH . '/css/admin-modal.min.css',
354 array(),
355 SUPERBADDONS_VERSION
356 );
357 wp_localize_script('superb-addons-gutenberg-library', 'superblayoutlibrary_g', array(
358 "style_placeholder" => esc_html__('All themes', "superb-blocks"),
359 "category_placeholder" => esc_html__('All categories', "superb-blocks"),
360 "snacks" => array(
361 "settings_save_message" => esc_html__("Settings saved successfully.", "superb-blocks"),
362 "settings_save_error" => esc_html__("Something went wrong while attempting to save your settings. Please try again or contact support if the problem persists.", "superb-blocks"),
363 "insert_error" => esc_html__('Something went wrong while attempting to insert this element. Please try again or contact support if the problem persists.', "superb-blocks"),
364 "list_error" => esc_html__('Something went wrong while attempting to list elements. Please try again or contact support if the problem persists.', "superb-blocks")
365 ),
366 "menu_items" => self::GetGutenbergLibraryMenuItems(),
367 "chunk_route" => LibraryRequestController::GUTENBERG_V2_LIST_CHUNK_ROUTE,
368 "favorites" => FavoritesController::GetFavorites(get_current_user_id()),
369 "rest" => array(
370 "base" => \get_rest_url(),
371 "namespace" => RestController::NAMESPACE,
372 "nonce" => wp_create_nonce("wp_rest")
373 ),
374 "addons_link_id" => AdminLinkUtil::GetLinkID(),
375 "visibility" => array(
376 "roles" => GutenbergConditionsController::GetConditionsRoles(),
377 ),
378 ));
379
380 LinkController::Localize('superb-addons-gutenberg-library');
381 wp_enqueue_style(
382 'superb-addons-elements',
383 SUPERBADDONS_ASSETS_PATH . '/css/framework.min.css',
384 array(),
385 SUPERBADDONS_VERSION
386 );
387 wp_enqueue_style(
388 'superb-addons-font-manrope',
389 SUPERBADDONS_ASSETS_PATH . '/fonts/manrope/manrope.css',
390 array(),
391 SUPERBADDONS_VERSION
392 );
393 wp_enqueue_style(
394 'superb-gutenberg-editor-layout-library',
395 SUPERBADDONS_ASSETS_PATH . '/css/layout-library-editor.min.css',
396 array(),
397 SUPERBADDONS_VERSION
398 );
399 wp_enqueue_style(
400 'superb-gutenberg-layout-library',
401 SUPERBADDONS_ASSETS_PATH . '/css/layout-library-preview.min.css',
402 array(),
403 SUPERBADDONS_VERSION
404 );
405 wp_enqueue_style(
406 'superbaddons-toast',
407 SUPERBADDONS_ASSETS_PATH . '/css/toast.min.css',
408 array(),
409 SUPERBADDONS_VERSION
410 );
411 wp_enqueue_script('superb-addons-select2', SUPERBADDONS_ASSETS_PATH . '/lib/select2.min.js', array('jquery'), SUPERBADDONS_VERSION, true);
412 wp_enqueue_style(
413 'superbaddons-select2',
414 SUPERBADDONS_ASSETS_PATH . '/lib/select2.min.css',
415 array(),
416 SUPERBADDONS_VERSION
417 );
418
419 wp_enqueue_script(
420 'superbaddons-animated-heading',
421 SUPERBADDONS_ASSETS_PATH . '/js/dynamic-blocks/animated-heading.js',
422 [],
423 SUPERBADDONS_VERSION,
424 true
425 );
426
427 // Form editor config — pass settings URL, REST namespace, and registry (API key status checked dynamically)
428 $ac_enabled = FormAccessControl::IsEnabled();
429 wp_add_inline_script('superb-addons-gutenberg-library', 'window.superbFormsEditorConfig = ' . wp_json_encode(array(
430 'registry' => FormRegistry::GetAll(),
431 'settingsUrl' => admin_url('admin.php?page=superbaddons-settings#integrations'),
432 'restNamespace' => RestController::NAMESPACE,
433 'encryptionAvailable' => FormEncryption::IsAvailable(),
434 'emailConfigured' => FormEmailConfigCheck::IsConfigured(),
435 'formAccessControl' => array(
436 'enabled' => $ac_enabled,
437 'canEdit' => $ac_enabled ? FormPermissions::Can('edit') : true,
438 'canConfigure' => $ac_enabled ? FormPermissions::Can('configure') : true,
439 'canCreate' => $ac_enabled ? FormPermissions::Can('create') : true,
440 'sensitiveAttrs' => FormAccessControl::GetSensitiveAttrs(),
441 ),
442 // Picker for the file-field accept whitelist. Sourced from
443 // FormFileHandler so the editor list, server-side validation, and
444 // the superbaddons_form_allowed_file_types filter stay in sync;
445 // deny-listed extensions are stripped there so editors cannot pick
446 // an extension whose uploads will be rejected.
447 'allowedFileTypes' => FormFileHandler::GetAllowedFileTypes(),
448 'maxUploadSizeMb' => max(1, (int) floor(wp_max_upload_size() / (1024 * 1024))),
449 ), JSON_HEX_TAG) . ';', 'before');
450
451 // Popup registry — pass registered popups to the block editor
452 wp_add_inline_script('superb-addons-gutenberg-library', 'window.superbPopupsEditorConfig = ' . wp_json_encode(array(
453 'registry' => PopupRegistry::GetAll(),
454 ), JSON_HEX_TAG) . ';', 'before');
455
456 // Commerce (Add to Cart) editor config: WC active/installed state, REST namespace, recent picks.
457 wp_add_inline_script('superb-addons-gutenberg-library', 'window.superbAddToCartEditorConfig = ' . wp_json_encode(array(
458 'wcActive' => CommerceController::IsWcActive(),
459 'wcInstalled' => self::IsWooCommerceInstalled(),
460 'wcSupported' => CommerceController::IsWcActive() && CommerceController::IsWcVersionSupported(),
461 'wcActivateUrl' => self::GetWooCommerceActivateUrl(),
462 'wcInstallUrl' => admin_url('plugin-install.php?tab=plugin-information&plugin=woocommerce'),
463 'restNamespace' => RestController::NAMESPACE,
464 'recentPicks' => CommerceController::GetRecentPicks(get_current_user_id()),
465 'currencySymbol' => CommerceController::IsWcActive() ? html_entity_decode(get_woocommerce_currency_symbol(), ENT_QUOTES, 'UTF-8') : '$',
466 'currencyPosition' => CommerceController::IsWcActive() ? (string) get_option('woocommerce_currency_pos', 'left') : 'left',
467 'canEditCoupons' => current_user_can('edit_shop_coupons'),
468 ), JSON_HEX_TAG) . ';', 'before');
469
470 // Enhancements
471 wp_enqueue_style(
472 'superb-addons-editor-enhancements',
473 SUPERBADDONS_ASSETS_PATH . '/css/editor-enhancements.min.css',
474 array(),
475 SUPERBADDONS_VERSION
476 );
477
478 /// Compatibility
479
480 if (SettingsController::IsCompatibilitySettingRelevantAndEnabled(CompatibilitySettingsOptionKey::SPECTRA_BLOCK_SPACING)) {
481 wp_enqueue_script(
482 'superb-addons-block-spacing-compatibility-fix',
483 SUPERBADDONS_ASSETS_PATH . '/js/compatibility/block-spacing.js',
484 array('jquery'),
485 SUPERBADDONS_VERSION,
486 true
487 );
488 }
489 }
490
491 public function RegisterBlockCategory($block_categories, $block_editor_context)
492 {
493 return array_merge(
494 array(
495 array(
496 'slug' => 'superb-addons-blocks',
497 'title' => __('Superb Addons', "superb-blocks"),
498 ),
499 ),
500 $block_categories
501 );
502 }
503
504 /**
505 * Hide disabled blocks from the inserter while keeping them registered
506 * so existing content still renders.
507 */
508 public function MaybeHideDisabledBlock($args, $block_type)
509 {
510 if (strpos($block_type, self::BLOCK_PREFIX) !== 0) {
511 return $args;
512 }
513
514 $slug = str_replace(self::BLOCK_PREFIX, '', $block_type);
515 $disabled = OptionController::GetDisabledBlocks();
516
517 // Check if this block is directly disabled
518 $is_disabled = in_array($slug, $disabled, true);
519
520 // Check if this is a child block whose parent is disabled
521 if (!$is_disabled && isset(self::CHILD_BLOCK_MAP[$slug])) {
522 $parent = self::CHILD_BLOCK_MAP[$slug];
523 $is_disabled = in_array($parent, $disabled, true);
524 }
525
526 // Hide form blocks from inserter when access control is enabled and user lacks 'create'
527 if (!$is_disabled && FormAccessControl::IsEnabled() && !FormPermissions::Can('create')) {
528 $form_slugs = array('form', 'multistep-form');
529 if (in_array($slug, $form_slugs, true)) {
530 $is_disabled = true;
531 }
532 // Also hide child blocks (form-field, form-step) via CHILD_BLOCK_MAP
533 if (!$is_disabled && isset(self::CHILD_BLOCK_MAP[$slug])) {
534 $parent = self::CHILD_BLOCK_MAP[$slug];
535 if (in_array($parent, $form_slugs, true)) {
536 $is_disabled = true;
537 }
538 }
539 }
540
541 if ($is_disabled) {
542 if (!isset($args['supports'])) {
543 $args['supports'] = array();
544 }
545 $args['supports']['inserter'] = false;
546 }
547
548 return $args;
549 }
550
551 /**
552 * Remove blocks that are not supported in the widget editor context.
553 * Widget areas store blocks in wp_options, not post content, so blocks
554 * that rely on save_post hooks (like forms) cannot track their state.
555 */
556 public function MaybeExcludeBlocksFromWidgetEditor($allowed_block_types, $editor_context)
557 {
558 if (!isset($editor_context->name)) {
559 return $allowed_block_types;
560 }
561
562 if ($editor_context->name !== 'core/edit-widgets' && $editor_context->name !== 'core/customize-widgets') {
563 return $allowed_block_types;
564 }
565
566 // If all blocks are allowed (default), build the full list minus excluded blocks
567 if ($allowed_block_types === true) {
568 $registry = \WP_Block_Type_Registry::get_instance();
569 $allowed_block_types = array_keys($registry->get_all_registered());
570 }
571
572 return array_values(array_diff($allowed_block_types, self::WIDGET_EDITOR_EXCLUDED_BLOCKS));
573 }
574
575 public function RegisterBlocksAndStyles()
576 {
577 foreach (self::BLOCKS as $block) {
578 if (isset($block['args'][self::PATTERN_BLOCK_ARG])) {
579 $block['args']['category'] = 'superb-addons-blocks-patterns';
580 unset($block['args'][self::PATTERN_BLOCK_ARG]);
581 }
582 register_block_type(SUPERBADDONS_PLUGIN_DIR . 'blocks/' . $block['path'], $block['args']);
583 }
584
585 wp_register_style(self::VARIABLE_FALLBACKS_HANDLE, false, array(), SUPERBADDONS_VERSION);
586 wp_register_style(
587 'superb-addons-patterns',
588 SUPERBADDONS_ASSETS_PATH . '/css/patterns.min.css',
589 array(),
590 SUPERBADDONS_VERSION
591 );
592 }
593
594 private static function EditorEnhancements()
595 {
596 add_action('admin_footer', function () {
597 AllowedTemplateHTMLUtil::enable_safe_styles();
598 ob_start();
599 include(SUPERBADDONS_PLUGIN_DIR . 'src/gutenberg/templates/block-quick-options.php');
600 $template = ob_get_clean();
601 echo '<script type="text/template" id="tmpl-gutenberg-superb-block-quick-options">' . wp_kses($template, AllowedTemplateHTMLUtil::get_allowed_html()) . '</script>';
602 AllowedTemplateHTMLUtil::disable_safe_styles();
603 });
604 }
605
606 public static function AddonsLibrary()
607 {
608 add_action('admin_footer', function () {
609 AllowedTemplateHTMLUtil::enable_safe_styles();
610 ///// Buttons
611 ob_start();
612 include(SUPERBADDONS_PLUGIN_DIR . 'src/gutenberg/templates/library-button.php');
613 $template = ob_get_clean();
614 echo '<script type="text/template" id="tmpl-gutenberg-superb-library-button">' . wp_kses($template, "post") . '</script>';
615 //
616 ob_start();
617 include(SUPERBADDONS_PLUGIN_DIR . 'src/gutenberg/templates/pattern-tab-library-button.php');
618 $template = ob_get_clean();
619 echo '<script type="text/template" id="tmpl-gutenberg-superb-library-button-patternstab">' . wp_kses($template, "post") . '</script>';
620 //
621 ob_start();
622 include(SUPERBADDONS_PLUGIN_DIR . 'src/gutenberg/templates/appender-button.php');
623 $template = ob_get_clean();
624 echo '<script type="text/template" id="tmpl-gutenberg-superb-library-appender-button">' . wp_kses($template, "post") . '</script>';
625 AllowedTemplateHTMLUtil::disable_safe_styles();
626 ////// Library
627 LibraryController::InsertTemplatesWithWrapper();
628 });
629 }
630
631 public static function GutenbergDataImportAction($data)
632 {
633 if (!isset($data['content'])) {
634 return $data;
635 }
636
637 $options_controller = new OptionController();
638 $preferred_domain = $options_controller->GetPreferredDomain();
639 // Proxy is preferred if domain starts with https://superbthemes.com
640 $is_proxy_preferred = strpos($preferred_domain, 'https://superbthemes.com') === 0;
641
642 $content = $data['content'];
643 $content = preg_replace_callback("/(http)?s?:?(\/\/[^\"']*\.(?:png|jpg|jpeg|gif|png|webp))/", function ($matches) use ($preferred_domain, $is_proxy_preferred) {
644 // Get the URL
645 $url = $matches[0];
646 if ($is_proxy_preferred) {
647 $url = $preferred_domain . "image-library/theme-designer-images?path=" . $url;
648 }
649 $basename = pathinfo($url, PATHINFO_BASENAME);
650 $title = sanitize_title($basename);
651
652 // Check if attachment exists based on the file slug
653 $posts = get_posts(
654 array(
655 'post_type' => 'attachment',
656 'title' => $title,
657 'numberposts' => 1,
658 )
659 );
660 if (!empty($posts)) {
661 // Return existing attachment URL
662 $attachment = $posts[0];
663 return wp_get_attachment_image_url($attachment->ID, 'full');
664 }
665
666 if (!function_exists('media_sideload_image')) {
667 require_once(ABSPATH . 'wp-admin/includes/image.php');
668 require_once(ABSPATH . 'wp-admin/includes/file.php');
669 require_once(ABSPATH . 'wp-admin/includes/media.php');
670 }
671
672 // Create new attachment
673 $attachment_id = \media_sideload_image($url, 0, $title, 'id');
674 if (is_wp_error($attachment_id) || !is_numeric($attachment_id)) {
675 // Return original URL if any error occurred
676 return $url;
677 }
678
679 // Return new attachment URL
680 return wp_get_attachment_image_url($attachment_id, 'full');
681 }, $content);
682
683 // Regenerate owner block IDs (popupId, formId, accordionId, etc.) and
684 // rewrite matching references (e.g. core/button spbaddPopupTarget) so
685 // that every library import produces fresh, collision-free IDs.
686 $content = GutenbergBlockIdRegenerator::RegenerateIds($content);
687
688 $data['content'] = $content;
689
690 return $data;
691 }
692 }
693