PluginProbe
MarqueeX – Smooth Marquee Slider, News Ticker & Post Marquee for Block Editor & Elementor / 0.6.0
MarqueeX – Smooth Marquee Slider, News Ticker & Post Marquee for Block Editor & Elementor v0.6.0
0.6.0 0.5.4 0.5.3 0.5.2 0.5.1 0.5.0 0.4.0 0.3.3 0.3.2 trunk 0.0.1 0.0.2 0.0.3 0.0.4 0.0.5 0.0.6 0.1.0 0.1.1 0.1.2 0.2.0 0.2.1 0.2.2 0.2.3 0.3.0 0.3.1
marqueex / includes / Integrations / Elementor / ElementorIntegration.php

ElementorIntegration.php in MarqueeX – Smooth Marquee Slider, News Ticker & Post Marquee for Block Editor & Elementor 0.6.0, at includes/Integrations/Elementor/ElementorIntegration.php

369 lines 13.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Wpxero\Marqueex\Integrations\Elementor;
4
5 use Wpxero\Marqueex\Traits\Singleton;
6 use Wpxero\Marqueex\Core\Settings;
7 use Wpxero\Marqueex\Core\Upsell;
8
9 if (!defined('ABSPATH')) {
10 exit;
11 }
12
13 // Import WordPress functions
14 use function add_action;
15 use function get_option;
16 use function wp_enqueue_script;
17 use function wp_enqueue_style;
18 use function wp_register_script;
19 use function wp_register_style;
20 use function plugins_url;
21 use function admin_url;
22 use function wp_create_nonce;
23 use function __;
24
25 /**
26 * Elementor Integration Class
27 */
28 class ElementorIntegration {
29 use Singleton;
30
31 /**
32 * Oldest Elementor release these widgets support.
33 *
34 * `elementor/widgets/register` and `$widgets_manager->register()` — the APIs
35 * used below — arrived in 3.5.0. On anything older the widgets silently fail
36 * to register, which previously looked like a broken plugin.
37 */
38 const MIN_ELEMENTOR_VERSION = '3.5.0';
39
40 /**
41 * Constructor
42 */
43 private function __construct() {
44 add_action('init', [$this, 'init']);
45 }
46
47 /**
48 * Initialize Elementor integration
49 */
50 public function init() {
51 // Check if Elementor is active
52 if (!$this->is_elementor_active()) {
53 return;
54 }
55
56 if (!$this->is_elementor_supported()) {
57 add_action('admin_notices', [$this, 'outdated_elementor_notice']);
58 return;
59 }
60
61 // Check if integration is enabled
62 $elementor_enabled = Settings::get('builder_support.elementor.enabled', true);
63
64 if (!$elementor_enabled) {
65 return;
66 }
67
68 // Hook into Elementor
69 add_action('elementor/widgets/register', [$this, 'register_widgets']);
70 add_action('elementor/elements/categories_registered', [$this, 'add_widget_categories'], 1);
71 add_action('elementor/editor/before_enqueue_scripts', [$this, 'enqueue_editor_scripts']);
72 // add_action('elementor/frontend/after_enqueue_scripts', [$this, 'enqueue_elementor_assets']);
73 add_action('wp_enqueue_scripts', [$this, 'enqueue_elementor_assets']);
74
75 // Marketing: advertise the Pro scroll-interaction features (mouse wheel + drag)
76 // as a locked section inside the free marquee widgets. Hidden when premium is
77 // active — the pro add-on then injects the real working controls instead.
78 // Note: post-marquee's section id is `marquee_settings`; the others use
79 // `marquee_settings_section`.
80 add_action('elementor/element/marqueex-text-marquee/marquee_settings_section/after_section_end', [$this, 'add_interaction_upsell_section'], 15, 2);
81 add_action('elementor/element/marqueex-image-marquee/marquee_settings_section/after_section_end', [$this, 'add_interaction_upsell_section'], 15, 2);
82 add_action('elementor/element/marqueex-news-ticker/marquee_settings_section/after_section_end', [$this, 'add_interaction_upsell_section'], 15, 2);
83 add_action('elementor/element/marqueex-post-marquee/marquee_settings/after_section_end', [$this, 'add_interaction_upsell_section'], 15, 2);
84 }
85
86 /**
87 * Add a locked "Scroll Interactions" section (Mouse Wheel + Drag) to free
88 * marquee widgets. Shown only to non-premium users — marqueex-pro registers
89 * the real working section at priority 20 when licensed.
90 */
91 public function add_interaction_upsell_section($element, $args) {
92 if (!Upsell::should_show()) {
93 return;
94 }
95
96 $element->start_controls_section(
97 'marqueex_interactions_upsell',
98 [
99 'label' => __('Scroll Interactions', 'marqueex'),
100 'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
101 ]
102 );
103
104 Upsell::add_elementor_notice($element, 'pro_interactions_notice', [
105 'title' => __('Let visitors drive the marquee', 'marqueex'),
106 'description' => __('Turn a strip that scrolls past people into one they can grab. Visitors spin the wheel to speed it up or reverse it, or drag it to scrub through — and it eases back to your set speed on its own.', 'marqueex'),
107 'items' => [
108 __('Mouse wheel control with adjustable sensitivity', 'marqueex'),
109 __('Drag to scroll, with flick momentum on release', 'marqueex'),
110 __('Optional "scroll sets direction" mode', 'marqueex'),
111 ],
112 'cta' => __('Unlock scroll interactions', 'marqueex'),
113 'source' => 'elementor-scroll-interactions',
114 ]);
115
116 $element->end_controls_section();
117 }
118
119 /**
120 * Check if Elementor is active
121 */
122 private function is_elementor_active() {
123 return class_exists('\Elementor\Plugin');
124 }
125
126 /**
127 * Whether the installed Elementor is new enough to register our widgets.
128 *
129 * @return bool
130 */
131 private function is_elementor_supported() {
132 if (!defined('ELEMENTOR_VERSION')) {
133 // Version constant missing — assume supported rather than disabling the
134 // integration on an unexpected build.
135 return true;
136 }
137
138 return version_compare(ELEMENTOR_VERSION, self::MIN_ELEMENTOR_VERSION, '>=');
139 }
140
141 /**
142 * Explain why the MarqueeX widgets are missing from the Elementor panel.
143 */
144 public function outdated_elementor_notice() {
145 if (!current_user_can('update_plugins')) {
146 return;
147 }
148 ?>
149 <div class="notice notice-warning">
150 <p>
151 <?php
152 printf(
153 /* translators: 1: required Elementor version, 2: installed Elementor version */
154 esc_html__('MarqueeX needs Elementor %1$s or newer to add its widgets. You are running %2$s — the MarqueeX widgets will not appear in the Elementor panel until it is updated. The MarqueeX blocks in the block editor are unaffected.', 'marqueex'),
155 esc_html(self::MIN_ELEMENTOR_VERSION),
156 esc_html(defined('ELEMENTOR_VERSION') ? ELEMENTOR_VERSION : __('an older version', 'marqueex'))
157 );
158 ?>
159 </p>
160 </div>
161 <?php
162 }
163
164 /**
165 * Single source of truth for the registered Elementor widgets.
166 *
167 * Keyed by the settings element key. Each entry describes the widget class
168 * plus its registered style/script handles, files and dependencies, so
169 * registration, asset enqueuing and status reporting all derive from here
170 * (previously these three lists were maintained separately and drifted).
171 *
172 *
173 * @return array<string, array>
174 */
175 private function get_widget_definitions() {
176 $frontend_deps = ['jquery', 'marqueex-elementor-frontend'];
177
178 return [
179 'image_marquee' => [
180 'class' => 'ImageMarquee',
181 'style_handle' => 'marqueex-image-marquee',
182 'style_css' => 'image-marquee.css',
183 'script_handle' => 'marqueex-image-marquee',
184 'script_js' => 'image-marquee.js',
185 'script_deps' => $frontend_deps,
186 ],
187 'text_marquee' => [
188 'class' => 'TextMarquee',
189 'style_handle' => 'marqueex-text-marquee',
190 'style_css' => 'text-marquee.css',
191 'script_handle' => 'marqueex-text-marquee',
192 'script_js' => 'text-marquee.js',
193 'script_deps' => $frontend_deps,
194 ],
195 'animated_heading' => [
196 'class' => 'AnimatedHeading',
197 'style_handle' => 'marqueex-animated-heading-style',
198 'style_css' => 'animated-heading.css',
199 'script_handle' => 'marqueex-animated-heading',
200 'script_js' => 'animated-heading.js',
201 // Animated Heading bundles its own animation logic; no frontend dep.
202 'script_deps' => ['jquery'],
203 ],
204 'animated_word_roller' => [
205 'class' => 'AnimatedWordRoller',
206 'style_handle' => 'marqueex-animated-word-roller-style',
207 'style_css' => 'animated-word-roller.css',
208 'script_handle' => 'marqueex-animated-word-roller',
209 'script_js' => 'animated-word-roller.js',
210 'script_deps' => $frontend_deps,
211 ],
212 'news_ticker' => [
213 'class' => 'NewsTicker',
214 'style_handle' => 'marqueex-news-ticker',
215 'style_css' => 'news-ticker.css',
216 'script_handle' => 'marqueex-news-ticker',
217 'script_js' => 'news-ticker.js',
218 'script_deps' => $frontend_deps,
219 ],
220 'post_marquee' => [
221 'class' => 'PostMarquee',
222 'style_handle' => 'marqueex-post-marquee',
223 'style_css' => 'post-marquee.css',
224 'script_handle' => 'marqueex-post-marquee',
225 'script_js' => 'post-marquee.js',
226 'script_deps' => $frontend_deps,
227 ],
228 'team_members_marquee' => [
229 'class' => 'TeamMembersMarquee',
230 'style_handle' => 'marqueex-team-members-marquee',
231 'style_css' => 'team-members-marquee.css',
232 'script_handle' => 'marqueex-team-members-marquee',
233 'script_js' => 'team-members-marquee.js',
234 'script_deps' => $frontend_deps,
235 ],
236 'testimonial_marquee' => [
237 'class' => 'TestimonialMarquee',
238 'style_handle' => 'marqueex-testimonial-marquee',
239 'style_css' => 'testimonial-marquee.css',
240 'script_handle' => 'marqueex-testimonial-marquee',
241 'script_js' => 'testimonial-marquee.js',
242 'script_deps' => $frontend_deps,
243 ],
244 ];
245 }
246
247 /**
248 * Whether a widget is enabled in settings (defaults to true).
249 *
250 * @param string $key Settings element key.
251 * @return bool
252 */
253 private function is_widget_enabled($key) {
254 $elements = Settings::get('builder_support.elementor.elements', []);
255 return (bool) ($elements[$key] ?? true);
256 }
257
258 /**
259 * Register MarqueeX widgets
260 */
261 public function register_widgets($widgets_manager) {
262 foreach ($this->get_widget_definitions() as $key => $def) {
263 if (!$this->is_widget_enabled($key)) {
264 continue;
265 }
266 require_once __DIR__ . '/Widgets/' . $def['class'] . '.php';
267 $fqcn = __NAMESPACE__ . '\\Widgets\\' . $def['class'];
268 $widgets_manager->register(new $fqcn());
269 }
270 }
271
272 /**
273 * Add custom widget categories
274 */
275 public function add_widget_categories($elements_manager) {
276 $elements_manager->add_category(
277 'marqueex',
278 [
279 'title' => __('MarqueeX', 'marqueex'),
280 'icon' => 'fa fa-sliders',
281 ]
282 );
283 }
284
285 /**
286 * Enqueue editor scripts
287 */
288 public function enqueue_editor_scripts() {
289 wp_enqueue_style(
290 'marqueex-elementor-editor',
291 plugins_url('/assets/css/elementor-editor.css', MARQUEEX_FILE),
292 [],
293 MARQUEEX_VERSION
294 );
295 }
296
297 /**
298 * Enqueue frontend scripts
299 */
300 public function enqueue_elementor_assets() {
301 // Register (don't enqueue) the shared base styles. Each widget style
302 // below depends on this handle, so common.css loads only on pages that
303 // actually render a MarqueeX widget — not on every front-end page.
304 wp_register_style(
305 'marqueex-common',
306 plugins_url('/assets/css/common.css', MARQUEEX_FILE),
307 [],
308 MARQUEEX_VERSION
309 );
310 wp_register_script(
311 'marqueex-elementor-frontend',
312 plugins_url('/assets/js/elementor-frontend.js', MARQUEEX_FILE),
313 ['jquery'],
314 MARQUEEX_VERSION,
315 true
316 );
317
318 // Register each widget's style (chained to common.css) and script from
319 // the canonical definition map. Elementor enqueues these on demand via
320 // each widget's get_style_depends()/get_script_depends().
321 foreach ($this->get_widget_definitions() as $def) {
322 wp_register_style(
323 $def['style_handle'],
324 plugins_url('/assets/css/' . $def['style_css'], MARQUEEX_FILE),
325 ['marqueex-common'],
326 MARQUEEX_VERSION
327 );
328 wp_register_script(
329 $def['script_handle'],
330 plugins_url('/assets/js/' . $def['script_js'], MARQUEEX_FILE),
331 $def['script_deps'],
332 MARQUEEX_VERSION,
333 true
334 );
335 }
336 }
337
338 /**
339 * Get integration status
340 */
341 public function get_status() {
342 $status = [
343 'active' => false,
344 'version' => null,
345 'widgets_registered' => false,
346 'enabled_widgets' => [],
347 ];
348
349 if ($this->is_elementor_active()) {
350 $status['active'] = true;
351 $status['version'] = \Elementor\Plugin::$instance->get_version();
352
353 // Derive enabled widgets from the same canonical definition map so
354 // status can never drift from what register_widgets() actually does.
355 $enabled_widgets = [];
356 foreach (array_keys($this->get_widget_definitions()) as $key) {
357 if ($this->is_widget_enabled($key)) {
358 $enabled_widgets[] = $key;
359 }
360 }
361
362 $status['enabled_widgets'] = $enabled_widgets;
363 $status['widgets_registered'] = !empty($enabled_widgets);
364 }
365
366 return $status;
367 }
368 }
369