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