PluginProbe
Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits / trunk
Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits vtrunk
3.2.2 3.2.3 3.2.1 3.2.0 3.1.9 3.1.8 3.1.7 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.9 trunk 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.3 1.1.4 1.1.5 All 174 releases
master-addons / inc / classes / base / master-widget.php

master-widget.php in Master Addons for Elementor – Elementor Addons, Widgets, Mega Menu Builder, Popup Builder, Widget Builder & Template Kits trunk, at inc/classes/base/master-widget.php

418 lines 13.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace MasterAddons\Inc\Classes\Base;
4
5 use \Elementor\Widget_Base;
6
7 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
8
9 abstract class Master_Widget extends Widget_Base {
10
11 // Whether or not we are in edit mode
12 public $_is_edit_mode = false;
13
14 // Dynamic Loop
15 private $jltma_loop_dynamic_settings = [];
16
17 /**
18 * Widget base constructor.
19 *
20 * @param array $data
21 * @param mixed $args
22 */
23 public function __construct( $data = [], $args = null ) {
24 parent::__construct( $data, $args );
25
26 // Set edit mode
27 $this->_is_edit_mode = \Elementor\Plugin::instance()->editor->is_edit_mode();
28 }
29
30 /**
31 * Get widget categories.
32 *
33 * @return array Widget categories.
34 */
35 public function get_categories() {
36 return [ 'master-addons' ];
37 }
38
39 /**
40 * Whether the widget has dynamic content.
41 * Default is false for better editor performance.
42 * Override and return true in widgets that need dynamic content (e.g., blog, posts).
43 *
44 * @return bool
45 */
46 protected function is_dynamic_content(): bool {
47 return false;
48 }
49
50 /**
51 * Normalize FA4 icon values to FA5 format.
52 *
53 * Converts legacy Font Awesome 4 icon arrays (e.g. 'fa fa-plus')
54 * to Font Awesome 5 format (e.g. 'fas fa-plus') so icons render
55 * correctly even when Elementor's "Load Font Awesome 4 Support" is disabled.
56 *
57 * @param array $icon Icon array with 'value' and 'library' keys.
58 * @return array Normalized icon array.
59 */
60 protected function normalize_fa_icon( $icon ) {
61 if ( empty( $icon['value'] ) || ! is_string( $icon['value'] ) ) {
62 return $icon;
63 }
64
65 // Only convert FA4 single-prefix format: "fa fa-xxx"
66 if ( preg_match( '/^fa fa-(.+)$/', $icon['value'], $matches ) ) {
67 $icon_name = $matches[1];
68
69 // FA4 outline icons (-o suffix) map to FA5 "regular" style
70 if ( substr( $icon_name, -2 ) === '-o' ) {
71 $icon['value'] = 'far fa-' . substr( $icon_name, 0, -2 );
72 $icon['library'] = 'regular';
73 } else {
74 $icon['value'] = 'fas fa-' . $icon_name;
75 $icon['library'] = 'solid';
76 }
77 }
78
79 return $icon;
80 }
81
82 /**
83 * Render a Font Awesome icon with proper CSS enqueue.
84 *
85 * Normalizes FA4→FA5 format AND ensures the correct Font Awesome CSS
86 * is loaded on the frontend. Without this, icons break when Elementor's
87 * "Load Font Awesome 4 Support" setting is disabled because Elementor
88 * only loads FA CSS globally when that shim is active.
89 *
90 * @param array $icon Icon array with 'value' and 'library' keys.
91 * @param array $attributes HTML attributes for the icon element.
92 * @param string $tag HTML tag to use (default 'i').
93 * @return bool Whether the icon was rendered.
94 */
95 protected function render_icon( $icon, $attributes = [], $tag = 'i' ) {
96 $icon = $this->normalize_fa_icon( $icon );
97
98 if ( empty( $icon['value'] ) || empty( $icon['library'] ) ) {
99 return false;
100 }
101
102 // Ensure the Font Awesome CSS is enqueued for this icon's library.
103 // wp_enqueue_style() alone is too late during render (head already printed),
104 // so wp_print_styles() forces the <link> tag to print inline in the body.
105 // WordPress tracks printed handles and won't duplicate them.
106 $fa_css_map = [
107 'solid' => 'elementor-icons-fa-solid',
108 'fa-solid' => 'elementor-icons-fa-solid',
109 'regular' => 'elementor-icons-fa-regular',
110 'fa-regular' => 'elementor-icons-fa-regular',
111 'brands' => 'elementor-icons-fa-brands',
112 'fa-brands' => 'elementor-icons-fa-brands',
113 ];
114
115 if ( isset( $fa_css_map[ $icon['library'] ] ) && ! $this->_is_edit_mode ) {
116 $handle = $fa_css_map[ $icon['library'] ];
117 if ( ! wp_style_is( $handle, 'done' ) ) {
118 wp_enqueue_style( $handle );
119 wp_print_styles( [ $handle ] );
120 }
121 }
122
123 return \Elementor\Icons_Manager::render_icon( $icon, $attributes, $tag );
124 }
125
126 /**
127 * Check if widget has inner wrapper.
128 * Handles Elementor's optimized markup experiment.
129 *
130 * @return bool
131 */
132 public function has_widget_inner_wrapper(): bool {
133 return ! \Elementor\Plugin::$instance->experiments->is_feature_active( 'e_optimized_markup' );
134 }
135
136 /**
137 * Override from addon to add custom wrapper class.
138 *
139 * @return string
140 */
141 protected function get_custom_wrapper_class() {
142 return '';
143 }
144
145 /**
146 * Overriding default function to add custom html class.
147 *
148 * @return string
149 */
150 public function get_html_wrapper_class() {
151 $html_class = parent::get_html_wrapper_class();
152 $html_class .= ' jltma-addon';
153 $html_class .= ' ' . $this->get_name();
154 $html_class .= ' ' . $this->get_custom_wrapper_class();
155 return rtrim( $html_class );
156 }
157
158 /**
159 * Method for adding editor helper attributes
160 *
161 * @param string $key
162 * @param string $name
163 *
164 * @return void
165 */
166 public function add_helper_render_attribute( $key, $name = '' ) {
167 if ( ! $this->_is_edit_mode )
168 return;
169
170 $this->add_render_attribute( $key, [
171 'data-jltma-helper' => $name,
172 'class' => 'jltma-editor-helper',
173 ] );
174 }
175
176 /**
177 * Method for adding a placeholder for the widget in the preview area
178 *
179 * @param array $args
180 *
181 * @return string|void
182 */
183 public function render_placeholder( $args ) {
184 if ( ! $this->_is_edit_mode )
185 return '';
186
187 $defaults = [
188 'title_tag' => 'h4',
189 'title' => $this->get_title(),
190 'body' => __( 'This is a placeholder for this widget and will not shown on the page.', 'master-addons' ),
191 ];
192
193 $args = wp_parse_args( $args, $defaults );
194
195 $this->add_render_attribute([
196 'jltma-placeholder' => [
197 'class' => 'jltma-editor-placeholder',
198 ],
199 'jltma-placeholder-title' => [
200 'class' => 'jltma-editor-placeholder__title',
201 ],
202 'jltma-placeholder-body' => [
203 'class' => 'jltma-editor-placeholder__body',
204 ],
205 ]);
206
207 ob_start();
208 ?><div <?php echo $this->get_render_attribute_string( 'jltma-placeholder' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Elementor safe HTML ?>>
209 <<?php echo esc_html( $args['title_tag'] ); ?> <?php echo $this->get_render_attribute_string( 'jltma-placeholder-title' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Elementor safe HTML ?>>
210 <?php echo esc_html( $args['title'] ); ?>
211 </<?php echo esc_html( $args['title_tag'] ); ?>>
212 <div <?php echo $this->get_render_attribute_string( 'jltma-placeholder-body' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Elementor safe HTML ?>><?php echo esc_html( $args['body'] ); ?></div>
213 </div><?php
214 return ob_get_clean();
215 }
216
217 /**
218 * Add inline editing attributes.
219 *
220 * @param string $key Element key.
221 * @param string $toolbar Toolbar type. Accepted values are `advanced`, `basic` or `none`. Default is `basic`.
222 * @param string $setting_key Additional settings key in case $key != $setting_key
223 */
224 public function add_inline_editing_attributes( $key, $toolbar = 'basic', $setting_key = '' ) {
225 if ( ! \Elementor\Plugin::$instance->editor->is_edit_mode() ) {
226 return;
227 }
228
229 if ( empty( $setting_key ) ) {
230 $setting_key = $key;
231 }
232
233 $this->add_render_attribute( $key, [
234 'class' => 'elementor-inline-editing',
235 'data-elementor-setting-key' => $setting_key,
236 ] );
237
238 if ( 'basic' !== $toolbar ) {
239 $this->add_render_attribute( $key, [
240 'data-elementor-inline-editing-toolbar' => $toolbar,
241 ] );
242 }
243 }
244
245 /**
246 * Add link render attributes with backward compatibility.
247 *
248 * @param array|string $element The HTML element.
249 * @param array $url_control Array of link settings.
250 * @param bool $overwrite Whether to overwrite existing attribute.
251 *
252 * @return $this
253 */
254 public function add_link_attributes( $element, array $url_control, $overwrite = false ) {
255 // add_link_attributes is available from Elementor 2.8.0
256 if ( version_compare( ELEMENTOR_VERSION, '2.8.0', '>=' ) ) {
257 return parent::add_link_attributes( $element, $url_control, $overwrite );
258 }
259
260 $attributes = [];
261
262 if ( ! empty( $url_control['url'] ) ) {
263 $attributes['href'] = $url_control['url'];
264 }
265
266 if ( ! empty( $url_control['is_external'] ) ) {
267 $attributes['target'] = '_blank';
268 }
269
270 if ( ! empty( $url_control['nofollow'] ) ) {
271 $attributes['rel'] = 'nofollow';
272 }
273
274 if ( ! empty( $url_control['custom_attributes'] ) ) {
275 $custom_attributes = explode( ',', $url_control['custom_attributes'] );
276 $blacklist = [ 'onclick', 'onfocus', 'onblur', 'onchange', 'onresize', 'onmouseover', 'onmouseout', 'onkeydown', 'onkeyup' ];
277
278 foreach ( $custom_attributes as $attribute ) {
279 list( $attr_key, $attr_value ) = explode( '|', $attribute );
280 $attr_key = trim( $attr_key );
281 $attr_value = trim( $attr_value );
282
283 if ( ! in_array( strtolower( $attr_key ), $blacklist, true ) ) {
284 $attributes[ $attr_key ] = $attr_value;
285 }
286 }
287 }
288
289 if ( $attributes ) {
290 $this->add_render_attribute( $element, $attributes, $overwrite );
291 }
292
293 return $this;
294 }
295
296 /**
297 * Method for setting widget dependency on Elementor Pro plugin
298 * When returning true it doesn't allow the widget to be registered
299 *
300 * @return bool
301 */
302 public static function requires_elementor_pro() {
303 return false;
304 }
305
306 /**
307 * Get skin setting
308 *
309 * Retrieves the current skin setting
310 *
311 * @param string $setting_key
312 * @return mixed
313 */
314 protected function get_skin_setting( $setting_key ) {
315 if ( ! $setting_key )
316 return false;
317
318 return $this->get_current_skin()->get_instance_value( $setting_key );
319 }
320
321 /**
322 * Set Loop Dynamic Settings
323 *
324 * @param \WP_Query $query
325 *
326 * @return void
327 */
328 protected function set_settings_for_loop( $query ) {
329 global $wp_query;
330
331 // Temporarily force a query for the template and set it as the current query
332 $old_query = $wp_query;
333 $wp_query = $query;
334
335 while ( $query->have_posts() ) {
336 $query->the_post();
337 $this->set_settings_for_post( get_the_ID() );
338 }
339
340 // Revert to the initial query
341 $wp_query = $old_query;
342
343 wp_reset_postdata();
344 }
345
346 /**
347 * Set Post Dynamic Settings
348 *
349 * @param int $post_id
350 *
351 * @return void
352 */
353 protected function set_settings_for_post( $post_id ) {
354 if ( ! $post_id ) {
355 return;
356 }
357
358 $settings = $this->get_settings_for_display();
359 $all_settings = $this->get_settings();
360 $controls = $this->get_controls();
361
362 $this->jltma_loop_dynamic_settings[ $post_id ] = [];
363
364 foreach ( $controls as $control ) {
365 $control_name = $control['name'];
366 $control_obj = \Elementor\Plugin::$instance->controls_manager->get_control( $control['type'] );
367
368 if ( empty( $control['dynamic'] ) ) {
369 continue;
370 }
371
372 $dynamic_settings = array_merge( $control_obj->get_settings( 'dynamic' ), $control['dynamic'] );
373 $parsed_value = '';
374
375 if ( ! isset( $all_settings[ '__dynamic__' ][ $control_name ] ) || empty( $control['dynamic']['loop'] ) ) {
376 $parsed_value = $all_settings[ $control_name ];
377 } else {
378 $parsed_value = $control_obj->parse_tags( $settings[ '__dynamic__' ][ $control_name ], $dynamic_settings );
379 }
380
381 $this->jltma_loop_dynamic_settings[ $post_id ][ $control_name ] = $parsed_value;
382 }
383 }
384
385 /**
386 * Get Loop Dynamic Settings
387 *
388 * @param int|bool $post_id
389 *
390 * @return array
391 */
392 protected function get_settings_for_loop_display( $post_id = false ) {
393 if ( $post_id ) {
394 if ( array_key_exists( $post_id, $this->jltma_loop_dynamic_settings ) ) {
395 return $this->jltma_loop_dynamic_settings[ $post_id ];
396 }
397 }
398
399 return $this->jltma_loop_dynamic_settings;
400 }
401
402 /**
403 * Get ID for Loop
404 *
405 * @return string
406 */
407 public function get_id_for_loop() {
408 global $post;
409
410 if ( ! $post ) {
411 return $this->get_id();
412 }
413
414 return implode( '_', [ $this->get_id(), $post->ID ] );
415 }
416
417 }
418