PluginProbe
The Innovative Form Builder – IvyForms / 1.5.1
The Innovative Form Builder – IvyForms v1.5.1
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
ivyforms / backend / src / Services / Integrations / Elementor / IvyFormsElementorLoader.php

IvyFormsElementorLoader.php in The Innovative Form Builder – IvyForms 1.5.1, at backend/src/Services/Integrations/Elementor/IvyFormsElementorLoader.php

350 lines 9.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * IvyForms Elementor Integration Loader
5 *
6 * @copyright © Melograno Venture Studio. All rights reserved.
7 * @licence See LICENCE.md for license details.
8 */
9
10 namespace IvyForms\Services\Integrations\Elementor;
11
12 // phpcs:disable PSR1.Files.SideEffects
13 if (!defined('ABSPATH')) {
14 exit; // Exit if accessed directly
15 }
16
17 use Elementor\Plugin;
18 use Elementor\Widget_Base;
19 use Elementor\Controls_Manager;
20 use IvyForms\Services\Integrations\Elementor\Widgets\IvyFormsWidget;
21 use IvyForms\Services\Shortcode\ShortcodeService;
22
23 /**
24 * Class IvyFormsElementorLoader
25 *
26 * Main loader class for Elementor integration (Singleton pattern)
27 * Following wpDataTables implementation pattern
28 *
29 * @package IvyForms\Services\Integrations\Elementor
30 */
31 final class IvyFormsElementorLoader
32 {
33 /**
34 * Singleton instance
35 *
36 * @var IvyFormsElementorLoader|null
37 */
38 private static ?IvyFormsElementorLoader $instance = null;
39
40 /**
41 * Get singleton instance
42 *
43 * @return IvyFormsElementorLoader
44 */
45 public static function instance(): IvyFormsElementorLoader
46 {
47 if (is_null(self::$instance)) {
48 self::$instance = new self();
49 }
50 return self::$instance;
51 }
52
53 /**
54 * Constructor
55 */
56 private function __construct()
57 {
58 if ($this->isCompatible()) {
59 add_action('elementor/init', [$this, 'init']);
60 }
61 }
62
63 /**
64 * Check if Elementor is installed
65 *
66 * @return bool
67 */
68 public function isCompatible(): bool
69 {
70 return defined('ELEMENTOR_VERSION');
71 }
72
73 /**
74 * Check Elementor version for backwards compatibility
75 * Returns true if using old registration method (< 3.5.0)
76 *
77 * @return bool
78 */
79 public function useLegacyRegistration(): bool
80 {
81 if (!defined('ELEMENTOR_VERSION')) {
82 return false;
83 }
84
85 $elementorVersion = (string) constant('ELEMENTOR_VERSION');
86 return version_compare($elementorVersion, '3.5.0', '<');
87 }
88
89 /**
90 * Initialize the Elementor integration
91 *
92 * @return void
93 */
94 public function init(): void
95 {
96 // Register widgets based on Elementor version
97 $action = $this->useLegacyRegistration()
98 ? 'elementor/widgets/widgets_registered'
99 : 'elementor/widgets/register';
100 add_action($action, [$this, 'registerWidgets']);
101
102 // Register widget category
103 add_action('elementor/elements/categories_registered', [$this, 'registerWidgetCategories']);
104
105 // Enqueue styles for editor and frontend
106 add_action('elementor/editor/before_enqueue_scripts', [$this, 'enqueueWidgetStyles']);
107 add_action('elementor/frontend/after_enqueue_styles', [$this, 'enqueueWidgetStyles']);
108
109 // Enqueue frontend scripts for live preview in editor
110 add_action('elementor/editor/before_enqueue_scripts', [$this, 'enqueueFrontendScriptsForEditor']);
111
112 // Add live preview support via AJAX
113 add_action('elementor/preview/enqueue_scripts', [$this, 'enqueuePreviewScripts']);
114 }
115
116 /**
117 * Include widget files
118 *
119 * @return void
120 */
121 public function includeWidgets(): void
122 {
123 if (!class_exists(Widget_Base::class)) {
124 return;
125 }
126
127 require_once IVYFORMS_PATH . '/backend/src/Services/Integrations/Elementor/Widgets/IvyFormsWidget.php';
128 }
129
130 /**
131 * Register widgets with Elementor
132 *
133 * @param mixed $widgetsManager
134 * @return void
135 */
136 public function registerWidgets($widgetsManager = null): void
137 {
138 if (!$this->canRegisterWidgets()) {
139 return;
140 }
141
142 $this->includeWidgets();
143
144 if ($this->useLegacyRegistration()) {
145 $this->registerWidgetLegacy();
146 return;
147 }
148
149 $this->registerWidgetModern($widgetsManager);
150 }
151
152 /**
153 * Check if all required classes are available for widget registration
154 *
155 * @return bool
156 */
157 private function canRegisterWidgets(): bool
158 {
159 if (!$this->isCompatible()) {
160 return false;
161 }
162
163 // @phpstan-ignore-next-line
164 $requiredClasses = [Plugin::class, Widget_Base::class, Controls_Manager::class];
165 foreach ($requiredClasses as $className) {
166 if (!class_exists($className)) {
167 return false;
168 }
169 }
170
171 return true;
172 }
173
174 /**
175 * Register widget using legacy Elementor method (< 3.5.0)
176 *
177 * @return void
178 */
179 private function registerWidgetLegacy(): void
180 {
181 // @phpstan-ignore-next-line
182 $plugin = Plugin::instance();
183 if (is_object($plugin) && isset($plugin->widgets_manager)) {
184 $plugin->widgets_manager->register_widget_type(new IvyFormsWidget());
185 }
186 }
187
188 /**
189 * Register widget using modern Elementor method (>= 3.5.0)
190 *
191 * @param mixed $widgetsManager
192 * @return void
193 */
194 private function registerWidgetModern($widgetsManager): void
195 {
196 if (is_object($widgetsManager) && method_exists($widgetsManager, 'register')) {
197 $widgetsManager->register(new IvyFormsWidget());
198 return;
199 }
200
201 // @phpstan-ignore-next-line
202 $plugin = Plugin::instance();
203 if (is_object($plugin) && isset($plugin->widgets_manager)) {
204 $plugin->widgets_manager->register(new IvyFormsWidget());
205 }
206 }
207
208 /**
209 * Register custom widget category
210 *
211 * @param mixed $elementsManager
212 * @return void
213 */
214 public function registerWidgetCategories($elementsManager): void
215 {
216 if (!is_object($elementsManager) || !method_exists($elementsManager, 'add_category')) {
217 return;
218 }
219
220 $elementsManager->add_category(
221 'ivyforms-elementor',
222 [
223 'title' => __('IvyForms', 'ivyforms'),
224 'icon' => 'ivyforms-elementor-icon',
225 ],
226 1
227 );
228 }
229
230 /**
231 * Enqueue widget styles
232 *
233 * @return void
234 */
235 public function enqueueWidgetStyles(): void
236 {
237 wp_register_style(
238 'ivyforms-elementor-widget',
239 IVYFORMS_URL . 'backend/src/Services/Integrations/Elementor/assets/css/ivyforms-elementor.css',
240 [],
241 IVYFORMS_VERSION
242 );
243 wp_enqueue_style('ivyforms-elementor-widget');
244
245 wp_enqueue_script(
246 'ivyforms-elementor-icon',
247 IVYFORMS_URL . 'backend/src/Services/Integrations/Elementor/assets/js/ivyforms-elementor-icon.js',
248 [],
249 IVYFORMS_VERSION,
250 false
251 );
252 }
253
254 /**
255 * Enqueue frontend scripts for editor/preview with editor flag set
256 *
257 * @return void
258 */
259 private function enqueueFrontendScripts(): void
260 {
261 // Enqueue IvyForms frontend scripts first
262 ShortcodeService::enqueueScripts();
263
264 // Get the correct script handle
265 // @phpstan-ignore-next-line
266 $frontendScriptHandle = (defined('IVYFORMS_DEV') && IVYFORMS_DEV)
267 ? 'ivyforms_scripts_dev_vite'
268 : 'ivyforms_script_index';
269
270 // Attach inline scripts to IvyForms handle (guaranteed to be enqueued)
271 if (wp_script_is($frontendScriptHandle, 'registered') || wp_script_is($frontendScriptHandle, 'enqueued')) {
272 // Initialize global form data list before IvyForms scripts
273 wp_add_inline_script(
274 $frontendScriptHandle,
275 'window.wpIvyFormDataList = window.wpIvyFormDataList || {};',
276 'before'
277 );
278
279 // Mark that we're in Elementor editor
280 wp_add_inline_script(
281 $frontendScriptHandle,
282 "window.IvyForms = window.IvyForms || {}; window.IvyForms.isElementorEditor = true;",
283 'before'
284 );
285 }
286 }
287
288 /**
289 * Enqueue admin preview script for data synchronization
290 *
291 * @param string $handle
292 * @return void
293 */
294 private function enqueueAdminPreviewScript(string $handle): void
295 {
296 $adminPreviewSrc = IVYFORMS_URL . 'backend/src/Services/Integrations/Gutenberg/assets/admin-preview.js';
297 wp_enqueue_script(
298 $handle,
299 esc_url($adminPreviewSrc),
300 ['jquery'],
301 IVYFORMS_VERSION,
302 true
303 );
304 }
305
306 /**
307 * Enqueue frontend scripts for editor live preview
308 *
309 * @return void
310 */
311 public function enqueueFrontendScriptsForEditor(): void
312 {
313 if (!$this->isCompatible()) {
314 return;
315 }
316
317 if (!class_exists(Plugin::class)) {
318 return;
319 }
320
321 // Only in editor mode
322 $plugin = Plugin::instance();
323 if (!is_object($plugin) || !isset($plugin->editor) || !$plugin->editor->is_edit_mode()) {
324 return;
325 }
326
327 $this->enqueueFrontendScripts();
328 $this->enqueueAdminPreviewScript('ivyforms-elementor-preview');
329 }
330
331 /**
332 * Enqueue scripts for Elementor preview iframe
333 *
334 * @return void
335 */
336 public function enqueuePreviewScripts(): void
337 {
338 if (!$this->isCompatible()) {
339 return;
340 }
341
342 if (!class_exists(Plugin::class)) {
343 return;
344 }
345
346 $this->enqueueFrontendScripts();
347 $this->enqueueAdminPreviewScript('ivyforms-elementor-preview-iframe');
348 }
349 }
350