PluginProbe ʕ •ᴥ•ʔ
Premium Addons for Elementor – Powerful Elementor Templates & Widgets / 4.11.84
Premium Addons for Elementor – Powerful Elementor Templates & Widgets v4.11.84
4.11.84 4.11.83 4.11.82 4.11.80 4.11.81 4.11.79 4.11.78 4.11.77 4.11.76 4.11.75 3.20.5 4.11.69 3.20.6 4.11.7 3.20.7 4.11.70 3.20.8 4.11.71 3.20.9 4.11.72 3.21.1 4.11.73 3.21.2 4.11.74 3.21.3 4.11.8 3.21.4 4.11.9 3.21.5 4.2.0 3.21.6 4.2.1 3.3.0 4.2.2 3.3.1 4.2.3 3.3.2 4.2.4 3.3.3 4.2.5 3.3.4 4.2.6 3.3.5 4.2.7 3.3.6 4.2.8 3.3.7 4.2.9 3.3.8 4.3.0 3.3.9 4.3.1 3.4.0 4.3.2 3.4.1 4.3.3 3.4.2 4.3.4 3.4.3 4.3.5 3.4.4 4.3.6 3.4.5 4.3.7 3.4.6 4.3.8 3.4.7 4.3.9 3.4.8 4.4.0 3.4.9 4.4.1 3.5.0 4.4.2 3.5.1 4.4.3 3.5.2 4.4.4 3.5.3 4.4.5 3.5.4 4.4.6 3.5.5 4.4.7 3.5.6 4.4.8 3.5.7 4.4.9 3.5.8 4.5.0 3.5.9 4.5.1 3.6.0 4.5.2 3.6.1 4.5.3 3.6.2 4.5.4 3.6.3 4.5.5 3.6.4 4.5.6 3.6.5 4.5.7 3.6.6 4.5.8 3.6.7 4.5.9 3.6.8 4.6.0 3.6.9 4.6.1 3.7.0 4.7.0 3.7.1 4.7.1 3.7.2 4.7.2 3.7.3 4.7.3 3.7.4 4.7.4 3.7.5 4.7.5 3.7.6 4.7.6 3.7.7 4.7.7 3.7.8 4.7.8 3.7.9 4.7.9 3.8.0 4.8.0 3.8.1 4.8.1 3.8.2 4.8.10 3.8.3 4.8.11 3.8.4 4.8.2 3.8.5 4.8.3 3.8.6 4.8.4 3.8.7 4.8.5 3.8.8 4.8.6 3.8.9 4.8.7 3.9.0 4.8.8 3.9.1 4.8.9 3.9.2 4.9.0 3.9.3 4.9.0-beta1 3.9.4 4.9.0-beta2 3.9.5 4.9.1 3.9.6 4.9.10 3.9.7 4.9.11 3.9.8 4.9.12 3.9.9 4.9.13 4.0.1 4.9.14 4.0.3 4.9.15 4.0.4 4.9.16 4.0.5 4.9.17 4.0.6 4.9.18 4.0.7 4.9.19 4.0.8 4.9.2 4.0.9 4.9.20 4.1.0 4.9.21 4.1.1 4.9.22 4.1.2 4.9.23 4.1.3 4.9.24 trunk 4.1.4 4.9.25 1.0 4.1.5 4.9.26 1.01 4.1.6 4.9.27 1.02 4.1.7 4.9.28 1.03 4.1.8 4.9.29 1.04 4.1.9 4.9.3 1.05 4.10.0 4.9.30 1.06 4.10.1 4.9.31 1.07 4.10.10 4.9.32 1.08 4.10.11 4.9.33 1.09 4.10.12 4.9.34 2.0 4.10.13 4.9.35 2.0.1 4.10.14 4.9.36 2.0.2 4.10.15 4.9.37 2.0.3 4.10.16 4.9.38 2.0.4 4.10.17 4.9.39 2.0.5 4.10.18 4.9.4 2.0.6 4.10.19 4.9.40 2.0.7 4.10.2 4.9.41 2.0.8 4.10.20 4.9.42 2.0.9 4.10.21 4.9.43 2.1.0 4.10.22 4.9.45 2.1.1 4.10.23 4.9.46 2.1.2 4.10.24 4.9.47 2.1.3 4.10.25 4.9.48 2.1.4 4.10.26 4.9.49 2.1.5 4.10.27 4.9.5 2.1.5-beta1 4.10.28 4.9.50 2.1.6 4.10.29 4.9.51 2.1.7 4.10.3 4.9.52 2.1.8 4.10.30 4.9.53 2.1.9 4.10.31 4.9.54 2.2.0 4.10.32 4.9.55 2.2.1 4.10.33 4.9.56 2.2.2 4.10.34 4.9.57 2.2.3 4.10.35 4.9.6 2.2.4 4.10.36 4.9.7 2.2.5 4.10.37 4.9.8 2.2.6 4.10.38 4.9.9 2.2.7 4.10.39 2.2.8 4.10.4 2.2.9 4.10.40 2.3.0 4.10.41 2.3.1 4.10.42 2.3.2 4.10.43 2.3.3 4.10.44 2.3.4 4.10.45 2.3.5 4.10.46 2.3.6 4.10.47 2.3.7 4.10.48 2.3.8 4.10.49 2.3.9 4.10.5 2.4.0 4.10.50 2.4.1 4.10.51 2.5.0 4.10.52 2.5.1 4.10.53 2.5.2 4.10.54 2.5.3 4.10.55 2.5.4 4.10.56 2.5.5 4.10.57 2.5.6 4.10.58 2.5.7 4.10.59 2.5.8 4.10.6 2.5.9 4.10.60 2.6.0 4.10.61 2.6.1 4.10.62 2.6.2 4.10.63 2.6.3 4.10.64 2.6.4 4.10.65 2.6.5 4.10.66 2.6.6 4.10.67 2.6.7 4.10.68 2.6.8 4.10.69 2.6.9 4.10.7 2.7.0 4.10.70 2.7.1 4.10.71 2.7.2 4.10.72 2.7.3 4.10.73 2.7.4 4.10.74 2.7.5 4.10.75 2.7.6 4.10.76 2.7.7 4.10.77 2.7.8 4.10.78 2.7.9 4.10.79 2.8.0 4.10.8 2.8.1 4.10.80 2.8.2 4.10.81 2.8.3 4.10.82 2.8.4 4.10.83 2.8.5 4.10.84 2.8.6 4.10.85 2.8.7 4.10.86 2.8.8 4.10.87 2.8.9 4.10.88 2.9.0 4.10.89 2.9.1 4.10.9 2.9.2 4.10.90 2.9.3 4.11.0 2.9.4 4.11.1 2.9.5 4.11.10 2.9.6 4.11.11 2.9.7 4.11.12 2.9.8 4.11.13 2.9.9 4.11.14 3.0.0 4.11.15 3.0.1 4.11.16 3.0.2 4.11.17 3.0.3 4.11.18 3.0.4 4.11.19 3.0.5 4.11.2 3.0.6 4.11.20 3.0.7 4.11.21 3.0.8 4.11.22 3.0.9 4.11.23 3.1.0 4.11.24 3.1.1 4.11.25 3.1.2 4.11.26 3.1.3 4.11.27 3.1.4 4.11.28 3.1.5 4.11.29 3.1.6 4.11.3 3.1.7 4.11.30 3.1.8 4.11.31 3.1.9 4.11.32 3.10.0 4.11.33 3.10.1 4.11.34 3.10.2 4.11.35 3.10.3 4.11.36 3.10.4 4.11.37 3.10.5 4.11.38 3.10.6 4.11.39 3.10.7 4.11.4 3.10.8 4.11.40 3.10.9 4.11.41 3.11.0 4.11.42 3.11.1 4.11.43 3.11.2 4.11.44 3.11.3 4.11.45 3.11.4 4.11.46 3.11.5 4.11.47 3.11.6 4.11.48 3.11.7 4.11.49 3.11.8 4.11.5 3.11.9 4.11.50 3.12.0 4.11.51 3.12.1 4.11.52 3.12.2 4.11.53 3.12.3 4.11.54 3.2.0 4.11.55 3.2.1 4.11.56 3.2.2 4.11.57 3.2.3 4.11.58 3.2.4 4.11.59 3.2.5 4.11.6 3.2.6 4.11.60 3.2.7 4.11.61 3.2.8 4.11.62 3.2.9 4.11.63 3.20.0 4.11.64 3.20.1 4.11.65 3.20.2 4.11.66 3.20.3 4.11.67 3.20.4 4.11.68
premium-addons-for-elementor / includes / helper-functions.php
premium-addons-for-elementor / includes Last commit date
controls 2 months ago extras 2 months ago helpers 1 day ago pa-display-conditions 2 months ago templates 1 day ago addons-integration.php 1 month ago assets-manager.php 2 weeks ago class-pa-core.php 2 months ago helper-functions.php 1 day ago module-base.php 2 months ago premium-template-tags.php 2 weeks ago promotion-pointer.php 1 day ago
helper-functions.php
2369 lines
1 <?php
2 /**
3 * PA Helper Functions.
4 */
5
6 namespace PremiumAddons\Includes;
7
8 // Premium Addons Pro Classes.
9 use PremiumAddonsPro\Includes\White_Label\Helper;
10 use PremiumAddons\Admin\Includes\Admin_Helper;
11
12 // Elementor Classes.
13 use Elementor\Icons_Manager;
14 use Elementor\Core\Settings\Manager as SettingsManager;
15 use Elementor\Plugin;
16 use Elementor\Controls_Manager;
17
18 if ( ! defined( 'ABSPATH' ) ) {
19 exit;
20 }
21
22 /**
23 * Class Helper_Functions.
24 */
25 class Helper_Functions {
26
27 /**
28 * A list of safe tags for `validate_html_tag` method.
29 */
30 const ALLOWED_HTML_WRAPPER_TAGS = array(
31 'article',
32 'aside',
33 'div',
34 'footer',
35 'h1',
36 'h2',
37 'h3',
38 'h4',
39 'h5',
40 'h6',
41 'header',
42 'main',
43 'nav',
44 'p',
45 'section',
46 'span',
47 );
48
49 /**
50 * Theme
51 *
52 * @var theme
53 */
54 private static $current_theme = null;
55
56 /**
57 * Google maps prefixes
58 *
59 * @var google_localize
60 */
61 private static $google_localize = null;
62
63 /**
64 * SVG Shapes
65 *
66 * @var shapes
67 */
68 private static $shapes = null;
69
70 /**
71 * WP lang prefixes
72 *
73 * @var lang_locales
74 */
75 private static $lang_locales = null;
76
77 /**
78 * Script debug enabled
79 *
80 * @var script_debug
81 */
82 private static $script_debug = null;
83
84 /**
85 * JS scripts directory
86 *
87 * @var js_dir
88 */
89 private static $js_dir = null;
90
91 /**
92 * CSS files directory
93 *
94 * @var js_dir
95 */
96 private static $css_dir = null;
97
98 /**
99 * JS Suffix
100 *
101 * @var js_suffix
102 */
103 private static $assets_suffix = null;
104
105 /**
106 * Get Icon SVG Data
107 *
108 * @since 4.10.72
109 * @access private
110 *
111 * @return array icon data.
112 */
113 private static function get_icon_svg_data( $icon ) {
114
115 preg_match( '/fa(.*) fa-/', $icon['value'], $icon_name_matches );
116
117 if ( empty( $icon_name_matches ) ) {
118 return;
119 }
120
121 $icon_name = str_replace( $icon_name_matches[0], '', $icon['value'] );
122
123 $icon_key = str_replace( ' fa-', '-', $icon['value'] );
124
125 $icon_file_name = str_replace( 'fa-', '', $icon['library'] );
126
127 $path = ELEMENTOR_ASSETS_PATH . 'lib/font-awesome/json/' . $icon_file_name . '.json';
128
129 $data = file_get_contents( $path );
130
131 if ( ! $data ) {
132 return;
133 }
134
135 $data = json_decode( $data, true );
136
137 $svg_data = $data['icons'][ $icon_name ];
138
139 return array(
140 'width' => $svg_data[0],
141 'height' => $svg_data[1],
142 'path' => $svg_data[4],
143 );
144 }
145
146 /**
147 * Check if white labeling - Free version author field is set
148 *
149 * @since 1.0.0
150 * @access public
151 *
152 * @return string
153 */
154 public static function author() {
155
156 $author_free = 'Leap13';
157
158 if ( self::check_papro_version() ) {
159
160 $white_label = Helper::get_white_labeling_settings();
161
162 $author_free = $white_label['premium-wht-lbl-name'];
163
164 }
165
166 return '' !== $author_free ? $author_free : 'Leap13';
167 }
168
169 /**
170 * Check if white labeling - Free version name field is set
171 *
172 * @since 1.0.0
173 * @access public
174 *
175 * @return string
176 */
177 public static function name() {
178
179 $name_free = 'Premium Addons';
180
181 if ( self::check_papro_version() ) {
182
183 $white_label = Helper::get_white_labeling_settings();
184
185 $name_free = $white_label['premium-wht-lbl-plugin-name'];
186
187 }
188
189 return '' !== $name_free ? $name_free : 'Premium Addons';
190 }
191
192 /**
193 * Check if white labeling - Hide row meta option is checked
194 *
195 * @since 1.0.0
196 * @return boolean
197 */
198 public static function is_hide_row_meta() {
199
200 $hide_meta = false;
201
202 if ( self::check_papro_version() ) {
203
204 $white_label = Helper::get_white_labeling_settings();
205
206 $hide_meta = $white_label['premium-wht-lbl-row'];
207
208 }
209
210 return $hide_meta;
211 }
212
213 /**
214 * Check if white labeling - Hide plugin logo option is checked
215 *
216 * @since 1.0.0
217 * @access public
218 *
219 * @return boolean
220 */
221 public static function is_hide_logo() {
222
223 if ( self::check_papro_version() ) {
224
225 if ( isset( get_option( 'pa_wht_lbl_save_settings' )['premium-wht-lbl-logo'] ) ) {
226
227 $hide_logo = get_option( 'pa_wht_lbl_save_settings' )['premium-wht-lbl-logo'];
228
229 }
230 }
231
232 return isset( $hide_logo ) ? $hide_logo : false;
233 }
234
235 /**
236 * Get White Labeling - Widgets Category string
237 *
238 * @since 1.0.0
239 * @access public
240 *
241 * @return string
242 */
243 public static function get_category() {
244
245 $category = __( 'Premium Addons', 'premium-addons-for-elementor' );
246
247 if ( self::check_papro_version() ) {
248
249 $white_label = Helper::get_white_labeling_settings();
250
251 $category = $white_label['premium-wht-lbl-short-name'];
252
253 }
254
255 return '' !== $category ? $category : __( 'Premium Addons', 'premium-addons-for-elementor' );
256 }
257
258 /**
259 * Get White Labeling - Widgets Prefix string
260 *
261 * @since 1.0.0
262 * @access public
263 *
264 * @return string
265 */
266 public static function get_prefix() {
267
268 $prefix = __( 'Premium', 'premium-addons-for-elementor' );
269
270 if ( self::check_papro_version() ) {
271
272 $white_label = Helper::get_white_labeling_settings();
273
274 $prefix = $white_label['premium-wht-lbl-prefix'];
275
276 }
277
278 return '' !== $prefix ? $prefix : __( 'Premium', 'premium-addons-for-elementor' );
279 }
280
281 /**
282 * Check if white labeling - Future notification checked
283 *
284 * @since 1.0.0
285 * @return boolean
286 */
287 public static function check_hide_notifications() {
288
289 if ( self::check_papro_version() ) {
290
291 $white_label = Helper::get_white_labeling_settings();
292
293 $hide_notification = isset( $white_label['premium-wht-lbl-not'] ) ? $white_label['premium-wht-lbl-not'] : false;
294
295 }
296
297 return isset( $hide_notification ) ? $hide_notification : false;
298 }
299
300 /**
301 * Get White Labeling - Widgets Badge string
302 *
303 * @since 1.0.0
304 * @access public
305 *
306 * @return string
307 */
308 public static function get_badge() {
309
310 $badge = 'PA';
311
312 if ( self::check_papro_version() ) {
313
314 $white_label = Helper::get_white_labeling_settings();
315
316 $badge = $white_label['premium-wht-lbl-badge'];
317
318 }
319
320 return '' !== $badge ? $badge : 'PA';
321 }
322
323 /**
324 * Get Google Maps localization prefixes
325 *
326 * @since 1.0.0
327 * @access public
328 *
329 * @return array
330 */
331 public static function get_google_maps_prefixes() {
332
333 if ( null === self::$google_localize ) {
334
335 self::$google_localize = array(
336 'ar' => __( 'Arabic', 'premium-addons-for-elementor' ),
337 'eu' => __( 'Basque', 'premium-addons-for-elementor' ),
338 'bg' => __( 'Bulgarian', 'premium-addons-for-elementor' ),
339 'bn' => __( 'Bengali', 'premium-addons-for-elementor' ),
340 'ca' => __( 'Catalan', 'premium-addons-for-elementor' ),
341 'cs' => __( 'Czech', 'premium-addons-for-elementor' ),
342 'da' => __( 'Danish', 'premium-addons-for-elementor' ),
343 'de' => __( 'German', 'premium-addons-for-elementor' ),
344 'el' => __( 'Greek', 'premium-addons-for-elementor' ),
345 'en' => __( 'English', 'premium-addons-for-elementor' ),
346 'en-AU' => __( 'English (australian)', 'premium-addons-for-elementor' ),
347 'en-GB' => __( 'English (great britain)', 'premium-addons-for-elementor' ),
348 'es' => __( 'Spanish', 'premium-addons-for-elementor' ),
349 'fa' => __( 'Farsi', 'premium-addons-for-elementor' ),
350 'fi' => __( 'Finnish', 'premium-addons-for-elementor' ),
351 'fil' => __( 'Filipino', 'premium-addons-for-elementor' ),
352 'fr' => __( 'French', 'premium-addons-for-elementor' ),
353 'gl' => __( 'Galician', 'premium-addons-for-elementor' ),
354 'gu' => __( 'Gujarati', 'premium-addons-for-elementor' ),
355 'hi' => __( 'Hindi', 'premium-addons-for-elementor' ),
356 'hr' => __( 'Croatian', 'premium-addons-for-elementor' ),
357 'hu' => __( 'Hungarian', 'premium-addons-for-elementor' ),
358 'id' => __( 'Indonesian', 'premium-addons-for-elementor' ),
359 'it' => __( 'Italian', 'premium-addons-for-elementor' ),
360 'iw' => __( 'Hebrew', 'premium-addons-for-elementor' ),
361 'ja' => __( 'Japanese', 'premium-addons-for-elementor' ),
362 'kn' => __( 'Kannada', 'premium-addons-for-elementor' ),
363 'ko' => __( 'Korean', 'premium-addons-for-elementor' ),
364 'lt' => __( 'Lithuanian', 'premium-addons-for-elementor' ),
365 'lv' => __( 'Latvian', 'premium-addons-for-elementor' ),
366 'ml' => __( 'Malayalam', 'premium-addons-for-elementor' ),
367 'mr' => __( 'Marathi', 'premium-addons-for-elementor' ),
368 'nl' => __( 'Dutch', 'premium-addons-for-elementor' ),
369 'no' => __( 'Norwegian', 'premium-addons-for-elementor' ),
370 'pl' => __( 'Polish', 'premium-addons-for-elementor' ),
371 'pt' => __( 'Portuguese', 'premium-addons-for-elementor' ),
372 'pt-BR' => __( 'Portuguese (brazil)', 'premium-addons-for-elementor' ),
373 'pt-PT' => __( 'Portuguese (portugal)', 'premium-addons-for-elementor' ),
374 'ro' => __( 'Romanian', 'premium-addons-for-elementor' ),
375 'ru' => __( 'Russian', 'premium-addons-for-elementor' ),
376 'sk' => __( 'Slovak', 'premium-addons-for-elementor' ),
377 'sl' => __( 'Slovenian', 'premium-addons-for-elementor' ),
378 'sr' => __( 'Serbian', 'premium-addons-for-elementor' ),
379 'sv' => __( 'Swedish', 'premium-addons-for-elementor' ),
380 'tl' => __( 'Tagalog', 'premium-addons-for-elementor' ),
381 'ta' => __( 'Tamil', 'premium-addons-for-elementor' ),
382 'te' => __( 'Telugu', 'premium-addons-for-elementor' ),
383 'th' => __( 'Thai', 'premium-addons-for-elementor' ),
384 'tr' => __( 'Turkish', 'premium-addons-for-elementor' ),
385 'uk' => __( 'Ukrainian', 'premium-addons-for-elementor' ),
386 'vi' => __( 'Vietnamese', 'premium-addons-for-elementor' ),
387 'zh-CN' => __( 'Chinese (simplified)', 'premium-addons-for-elementor' ),
388 'zh-TW' => __( 'Chinese (traditional)', 'premium-addons-for-elementor' ),
389 );
390 }
391
392 return self::$google_localize;
393 }
394
395 /**
396 * Checks if a plugin is installed
397 *
398 * @since 1.0.0
399 * @access public
400 *
401 * @param string $plugin_path plugin path.
402 *
403 * @return boolean
404 */
405 public static function is_plugin_installed( $plugin_path ) {
406
407 require_once ABSPATH . 'wp-admin/includes/plugin.php';
408
409 $plugins = get_plugins();
410
411 return isset( $plugins[ $plugin_path ] );
412 }
413
414 /**
415 * Check Plugin Active
416 *
417 * @since 4.2.5
418 * @access public
419 *
420 * @param string $slug plugin slug.
421 *
422 * @return boolean $is_active plugin active.
423 */
424 public static function check_plugin_active( $slug = '' ) {
425
426 include_once ABSPATH . 'wp-admin/includes/plugin.php';
427
428 $is_active = in_array( $slug, (array) get_option( 'active_plugins', array() ), true );
429
430 return $is_active;
431 }
432
433 /**
434 * Check if script debug mode enabled.
435 *
436 * @since 3.11.1
437 * @access public
438 *
439 * @return boolean is debug mode enabled
440 */
441 public static function is_debug_enabled() {
442
443 if ( null === self::$script_debug ) {
444
445 self::$script_debug = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG;
446 }
447
448 return self::$script_debug;
449 }
450
451 /**
452 * Get scripts dir.
453 *
454 * @access public
455 *
456 * @return string JS scripts directory.
457 */
458 public static function get_scripts_dir() {
459
460 if ( null === self::$js_dir ) {
461
462 self::$js_dir = self::is_debug_enabled() ? 'js' : 'min-js';
463 }
464
465 return self::$js_dir;
466 }
467
468 /**
469 * Get styles dir.
470 *
471 * @access public
472 *
473 * @return string CSS files directory.
474 */
475 public static function get_styles_dir() {
476
477 if ( null === self::$css_dir ) {
478
479 self::$css_dir = self::is_debug_enabled() ? 'css' : 'min-css';
480 }
481
482 return self::$css_dir;
483 }
484
485 /**
486 * Get assets suffix.
487 *
488 * @access public
489 *
490 * @return string JS scripts suffix.
491 */
492 public static function get_assets_suffix() {
493
494 if ( null === self::$assets_suffix ) {
495
496 self::$assets_suffix = self::is_debug_enabled() ? '' : '.min';
497 }
498
499 return self::$assets_suffix;
500 }
501
502 /**
503 * Get Installed Theme
504 *
505 * Returns the active theme slug
506 *
507 * @access public
508 *
509 * @return string theme slug
510 */
511 public static function get_installed_theme() {
512
513 if ( null === self::$current_theme ) {
514
515 $theme = wp_get_theme();
516
517 if ( $theme->parent() ) {
518
519 $theme_name = sanitize_key( $theme->parent()->get( 'Name' ) );
520
521 } else {
522
523 $theme_name = $theme->get( 'Name' );
524
525 $theme_name = sanitize_key( $theme_name );
526
527 }
528
529 self::$current_theme = $theme_name;
530
531 }
532
533 return self::$current_theme;
534 }
535
536 /**
537 * Get Vimeo Video Data
538 *
539 * Get video data using Vimeo API
540 *
541 * @since 3.11.4
542 * @access public
543 *
544 * @param string $video_id video ID.
545 */
546 public static function get_vimeo_video_data( $video_id ) {
547
548 $vimeo_data = get_transient( 'premium_vimeo_' . $video_id );
549
550 if ( false === $vimeo_data ) {
551
552 $response = wp_remote_get(
553 'https://vimeo.com/api/oembed.json?url=' . rawurlencode( 'https://vimeo.com/' . intval( $video_id ) ) . '&width=1280',
554 array(
555 'timeout' => 5,
556 'sslverify' => true,
557 )
558 );
559
560 if ( is_wp_error( $response ) ) {
561 return false;
562 }
563
564 $status_code = wp_remote_retrieve_response_code( $response );
565
566 if ( 200 === $status_code ) {
567
568 $body = json_decode( wp_remote_retrieve_body( $response ), true );
569
570 if ( ! is_array( $body ) ) {
571 return false;
572 }
573
574 $vimeo_data = array(
575 'src' => isset( $body['thumbnail_url'] ) ? $body['thumbnail_url'] : false,
576 'url' => isset( $body['author_url'] ) ? $body['author_url'] : false,
577 'portrait' => false,
578 'title' => isset( $body['title'] ) ? $body['title'] : false,
579 'user' => isset( $body['author_name'] ) ? $body['author_name'] : false,
580 );
581
582 set_transient( 'premium_vimeo_' . $video_id, $vimeo_data, WEEK_IN_SECONDS );
583
584 return $vimeo_data;
585
586 }
587 }
588
589 return $vimeo_data;
590 }
591
592 /**
593 * Get Video Thumbnail
594 *
595 * Get thumbnail URL for embed or self hosted
596 *
597 * @since 3.7.0
598 * @access public
599 *
600 * @param string $video_id video ID.
601 * @param string $type embed type.
602 * @param string $size youtube thumbnail size.
603 */
604 public static function get_video_thumbnail( $video_id, $type, $size = '' ) {
605
606 $thumbnail_src = 'transparent';
607
608 if ( 'youtube' === $type ) {
609 if ( '' === $size ) {
610 $size = 'maxresdefault';
611 }
612 $thumbnail_src = sprintf( 'https://i.ytimg.com/vi/%s/%s.jpg', $video_id, $size );
613
614 } elseif ( 'vimeo' === $type ) {
615
616 $vimeo = self::get_vimeo_video_data( $video_id );
617
618 $thumbnail_src = is_array( $vimeo ) ? $vimeo['src'] : '';
619
620 } elseif ( 'dailymotion' === $type ) {
621
622 $cache_key = 'pa_dm_' . $video_id;
623
624 $thumbnail_src = get_transient( $cache_key );
625
626 if ( false === $thumbnail_src ) {
627
628 $video_data = wp_remote_get(
629 'https://api.dailymotion.com/video/' . $video_id . '?fields=thumbnail_url',
630 array(
631 'timeout' => 5,
632 'sslverify' => true,
633 )
634 );
635
636 if ( is_wp_error( $video_data ) || 200 !== wp_remote_retrieve_response_code( $video_data ) ) {
637 $thumbnail_src = 'transparent';
638 } else {
639 $video_data = wp_remote_retrieve_body( $video_data );
640 $video_data = json_decode( $video_data );
641
642 $thumbnail_src = isset( $video_data->thumbnail_url ) ? $video_data->thumbnail_url : 'transparent';
643 }
644
645 set_transient( $cache_key, $thumbnail_src, WEEK_IN_SECONDS );
646
647 }
648 }
649
650 return $thumbnail_src;
651 }
652
653 /**
654 * Transient Expire
655 *
656 * Gets expire time of transient.
657 *
658 * @since 3.20.8
659 * @access public
660 *
661 * @param string $period transient expiration period.
662 *
663 * @return int $expire_time expire time in seconds.
664 */
665 public static function transient_expire( $period ) {
666
667 $expire_time = 24 * HOUR_IN_SECONDS;
668
669 switch ( $period ) {
670 case 'minute':
671 $expire_time = MINUTE_IN_SECONDS;
672 break;
673 case 'minutes':
674 $expire_time = 5 * MINUTE_IN_SECONDS;
675 break;
676 case 'hour':
677 $expire_time = 60 * MINUTE_IN_SECONDS;
678 break;
679 case 'week':
680 $expire_time = 7 * DAY_IN_SECONDS;
681 break;
682 case 'month':
683 $expire_time = 30 * DAY_IN_SECONDS;
684 break;
685 case 'year':
686 $expire_time = 365 * DAY_IN_SECONDS;
687 break;
688 default:
689 $expire_time = 24 * HOUR_IN_SECONDS;
690 }
691
692 return $expire_time;
693 }
694
695 /**
696 * Get Campaign Link
697 *
698 * @since 3.20.9
699 * @access public
700 *
701 * @param string $link page link.
702 * @param string $source source.
703 * @param string $medium media.
704 * @param string $campaign campaign name.
705 *
706 * @return string $link campaign URL
707 */
708 public static function get_campaign_link( $link, $source, $medium, $campaign = '' ) {
709
710 if ( null === self::$current_theme ) {
711 self::get_installed_theme();
712 }
713
714 $args = array(
715 'utm_source' => $source,
716 'utm_medium' => $medium,
717 'utm_campaign' => $campaign,
718 'utm_term' => self::$current_theme,
719 );
720
721 $args = array_filter( $args );
722
723 $url = add_query_arg( $args, $link );
724
725 return $url;
726 }
727
728 /**
729 * Get Elementor UI Theme
730 *
731 * Detects user setting for UI theme
732 *
733 * @since 3.21.1
734 * @access public
735 *
736 * @return string $theme UI Theme
737 */
738 public static function get_elementor_ui_theme() {
739
740 $theme = SettingsManager::get_settings_managers( 'editorPreferences' )->get_model()->get_settings( 'ui_theme' );
741
742 return $theme;
743 }
744
745 /**
746 * Check PAPRO Version
747 *
748 * Check if PAPRO version is updated
749 *
750 * @since 3.21.6
751 * @access public
752 *
753 * @return boolean
754 */
755 public static function check_papro_version() {
756 return defined( 'PREMIUM_PRO_ADDONS_VERSION' );
757 }
758
759 /**
760 * Check Elementor Version
761 *
762 * Check if Elementor is installed and activated
763 *
764 * @since 4.11.54
765 * @access public
766 *
767 * @return boolean
768 */
769 public static function check_elementor_version() {
770 return defined( 'ELEMENTOR_VERSION' ) && class_exists( 'Elementor\Plugin' );
771 }
772
773 /**
774 * Validate HTML Tag
775 *
776 * Validates an HTML tag against a safe allowed list.
777 *
778 * @param string $tag HTML tag.
779 *
780 * @return string
781 */
782 public static function validate_html_tag( $tag ) {
783 $tag = strtolower( (string) $tag );
784 return in_array( $tag, self::ALLOWED_HTML_WRAPPER_TAGS, true ) ? $tag : 'div';
785 }
786
787 /**
788 * Get Image Data
789 *
790 * Returns image data based on image id.
791 *
792 * @since 0.0.1
793 * @access public
794 *
795 * @param int $image_id Image ID.
796 * @param string $image_url Image URL.
797 * @param string|array $image_size Image size name or [ width, height ] array.
798 *
799 * @return array $data image data.
800 */
801 public static function get_image_data( $image_id, $image_url, $image_size ) {
802
803 if ( ! $image_id && ! $image_url ) {
804 return false;
805 }
806
807 $data = array();
808
809 $image_url = esc_url_raw( $image_url );
810
811 if ( ! empty( $image_id ) ) { // Existing attachment.
812
813 $attachment = get_post( $image_id );
814
815 if ( is_object( $attachment ) ) {
816 $data['id'] = $image_id;
817 $data['url'] = $image_url;
818
819 $data['image'] = wp_get_attachment_image( $attachment->ID, $image_size, true );
820 $data['image_size'] = $image_size;
821 $data['caption'] = $attachment->post_excerpt;
822 $data['title'] = $attachment->post_title;
823 $data['description'] = $attachment->post_content;
824
825 }
826 } else { // Placeholder image, most likely.
827
828 if ( empty( $image_url ) ) {
829 return;
830 }
831
832 $data['id'] = false;
833 $data['url'] = $image_url;
834 $data['image'] = '<img src="' . $image_url . '" alt="" title="" />';
835 $data['image_size'] = $image_size;
836 $data['caption'] = '';
837 $data['title'] = '';
838 $data['description'] = '';
839 }
840
841 return $data;
842 }
843
844 /**
845 * Returns the current page id.
846 * get_the_ID returns the first product ID in the loop if it's the shop page.
847 * and it sometimes returns a wrong ID in the other cases.
848 *
849 * @access public
850 * @since 4.11.8
851 * @return int
852 */
853 public static function pa_get_current_page_id() {
854
855 if ( class_exists( 'woocommerce' ) ) {
856
857 switch ( true ) {
858 case is_shop():
859 $page_id = wc_get_page_id( 'shop' );
860 break;
861
862 case is_cart():
863 $page_id = wc_get_page_id( 'cart' );
864 break;
865
866 case is_checkout():
867 $page_id = wc_get_page_id( 'checkout' );
868 break;
869
870 case is_account_page():
871 $page_id = wc_get_page_id( 'myaccount' );
872 break;
873
874 default:
875 $page_id = get_the_ID();
876 break;
877 }
878 } else {
879 $page_id = get_the_ID();
880 }
881
882 return $page_id;
883 }
884
885 /**
886 * Get Final Result.
887 *
888 * @access public
889 * @since 4.4.8
890 *
891 * @param bool $condition_result result.
892 * @param string $operator operator.
893 *
894 * @return bool
895 */
896 public static function get_final_result( $condition_result, $operator ) {
897
898 if ( 'is' === $operator ) {
899 return true === $condition_result;
900 } else {
901 return true !== $condition_result;
902 }
903 }
904
905 /**
906 * Get Local Time ( WordPress TimeZone Setting ).
907 *
908 * @access public
909 * @since 4.4.8
910 *
911 * @param string $format format.
912 */
913 public static function get_local_time( $format ) {
914
915 $local_time_zone = isset( $_COOKIE['localTimeZone'] ) && ! empty( $_COOKIE['localTimeZone'] ) ?
916 str_replace( 'GMT ', 'GMT+', sanitize_text_field( wp_unslash( $_COOKIE['localTimeZone'] ) ) )
917 : self::get_location_time_zone();
918
919 // $today = new \DateTime( 'now', new \DateTimeZone( $local_time_zone ) );
920
921 try {
922 $today = new \DateTime( 'now', new \DateTimeZone( $local_time_zone ) );
923 } catch ( \Exception $e ) {
924 $today = new \DateTime( 'now', new \DateTimeZone( 'UTC' ) );
925 }
926
927 return $today->format( $format );
928 }
929
930 /**
931 * Gets the user's timezone based on his ip address.
932 *
933 * @access public
934 * @since 4.10.26
935 *
936 * @return string
937 */
938 public static function get_location_time_zone() {
939
940 static $cached_timezone = null;
941
942 if ( null !== $cached_timezone ) {
943 return $cached_timezone;
944 }
945
946 $ip_address = self::get_user_ip_address();
947
948 $cached_timezone = self::get_timezone_by_ip( $ip_address );
949
950 return $cached_timezone;
951 }
952
953 /**
954 * Get user's IP address.
955 *
956 * @access public
957 * @since 4.10.26
958 *
959 * @return string
960 */
961 public static function get_user_ip_address() {
962
963 $ip_address = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '';
964
965 if ( isset( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
966
967 $x_forward = sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) );
968
969 $ips = explode( ',', $x_forward );
970
971 if ( ! empty( $ips ) ) {
972 $ip_address = trim( $ips[0] );
973 }
974 }
975
976 return '::1' === $ip_address ? '127.0.0.1' : $ip_address;
977 }
978
979 /**
980 * Get IP location data.
981 *
982 * Uses multi-layered caching (object cache + transients) to optimize performance.
983 *
984 * @access public
985 * @since 4.11.54
986 *
987 * @param string $ip_address user's ip address.
988 *
989 * @return array|false
990 */
991 public static function get_ip_location_data( $ip_address ) {
992
993 if ( '127.0.0.1' === $ip_address || empty( $ip_address ) ) {
994 return false;
995 }
996
997 $cache_key = 'pa_ip_loc_' . md5( $ip_address );
998
999 $location_data = wp_cache_get( $cache_key, 'premium_addons' );
1000
1001 if ( false === $location_data ) {
1002
1003 $location_data = get_transient( $cache_key );
1004
1005 if ( false === $location_data ) {
1006
1007 $response = wp_remote_get(
1008 'https://api.findip.net/' . $ip_address . '/?token=e6624afe9983128700bc94e55d5227cb',
1009 array(
1010 'timeout' => 15,
1011 'sslverify' => true,
1012 )
1013 );
1014
1015 if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
1016 return false;
1017 }
1018
1019 $location_data = json_decode( wp_remote_retrieve_body( $response ), true );
1020
1021 set_transient( $cache_key, $location_data, 24 * HOUR_IN_SECONDS );
1022
1023 }
1024
1025 wp_cache_set( $cache_key, $location_data, 'premium_addons' );
1026
1027 }
1028
1029 return $location_data;
1030 }
1031
1032 /**
1033 * Get timezone by ip address.
1034 *
1035 * @access public
1036 * @since 4.10.26
1037 *
1038 * @param string $ip_address user's ip address.
1039 *
1040 * @return string
1041 */
1042 public static function get_timezone_by_ip( $ip_address ) {
1043
1044 $location_data = self::get_ip_location_data( $ip_address );
1045
1046 if ( ! $location_data || ! isset( $location_data['location']['time_zone'] ) ) {
1047 return date_default_timezone_get();
1048 }
1049
1050 return strtolower( $location_data['location']['time_zone'] );
1051 }
1052
1053 /**
1054 * Get Site Server Time ( WordPress TimeZone Setting ).
1055 *
1056 * @access public
1057 * @since 4.4.8
1058 *
1059 * @param string $format format.
1060 */
1061 public static function get_site_server_time( $format ) {
1062
1063 $today = gmdate( $format, strtotime( 'now' ) + ( get_option( 'gmt_offset' ) * HOUR_IN_SECONDS ) );
1064
1065 return $today;
1066 }
1067
1068 /**
1069 * Get All Breakpoints.
1070 *
1071 * @param string $type result return type.
1072 *
1073 * @access public
1074 * @since 4.6.1
1075 *
1076 * @return array $devices enabled breakpoints.
1077 */
1078 public static function get_all_breakpoints( $type = 'assoc' ) {
1079
1080 $devices = array(
1081 'desktop' => __( 'Desktop', 'elementor' ),
1082 'tablet' => __( 'Tablet', 'elementor' ),
1083 'mobile' => __( 'Mobile', 'elementor' ),
1084 );
1085
1086 $method_available = method_exists( Plugin::$instance->breakpoints, 'has_custom_breakpoints' );
1087
1088 if ( ( defined( 'ELEMENTOR_VERSION' ) && version_compare( ELEMENTOR_VERSION, '3.4.0', '>' ) ) && $method_available ) {
1089
1090 if ( Plugin::$instance->breakpoints->has_custom_breakpoints() ) {
1091 $devices = array_merge(
1092 $devices,
1093 array(
1094 'widescreen' => __( 'Widescreen', 'elementor' ),
1095 'laptop' => __( 'Laptop', 'elementor' ),
1096 'tablet_extra' => __( 'Tablet Extra', 'elementor' ),
1097 'mobile_extra' => __( 'Mobile Extra', 'elementor' ),
1098 )
1099 );
1100 }
1101 }
1102
1103 if ( 'keys' === $type ) {
1104 $devices = array_keys( $devices );
1105 }
1106
1107 return $devices;
1108 }
1109
1110 /**
1111 * Get WordPress language prefixes.
1112 *
1113 * @since 4.4.8
1114 * @access public
1115 *
1116 * @return array
1117 */
1118 public static function get_lang_prefixes() {
1119
1120 if ( null === self::$lang_locales ) {
1121
1122 $langs = require_once PREMIUM_ADDONS_PATH . 'includes/pa-display-conditions/lang-locale.php';
1123
1124 foreach ( $langs as $lang => $props ) {
1125 /* translators: %s: Language Name */
1126 $val = ucwords( $props['name'] );
1127 self::$lang_locales[ $lang ] = $val;
1128 }
1129 }
1130
1131 return self::$lang_locales;
1132 }
1133
1134 /**
1135 * Get Woocommerce Categories.
1136 *
1137 * @access public
1138 * @since 4.4.8
1139 *
1140 * @param string $id array key.
1141 *
1142 * @return array
1143 */
1144 public static function get_woo_categories( $id = 'slug' ) {
1145
1146 $product_cat = array();
1147
1148 $cat_args = array(
1149 'taxonomy' => 'product_cat',
1150 'orderby' => 'name',
1151 'order' => 'asc',
1152 'hide_empty' => false,
1153 );
1154
1155 $product_categories = get_terms( $cat_args );
1156
1157 if ( ! empty( $product_categories ) ) {
1158
1159 foreach ( $product_categories as $key => $category ) {
1160
1161 $cat_id = 'slug' === $id ? $category->slug : $category->term_id;
1162 $product_cat[ $cat_id ] = $category->name;
1163
1164 }
1165 }
1166
1167 return $product_cat;
1168 }
1169
1170 /**
1171 * Check Elementor Experiment
1172 *
1173 * Check if an Elementor experiment is enabled.
1174 *
1175 * @since 4.8.6
1176 * @access public
1177 *
1178 * @param string $experiment feature ID.
1179 *
1180 * @return boolean $is_enabled is feature enabled.
1181 */
1182 public static function check_elementor_experiment( $experiment ) {
1183
1184 $experiments_manager = Plugin::$instance->experiments;
1185
1186 $is_enabled = $experiments_manager->is_feature_active( $experiment );
1187
1188 return $is_enabled;
1189 }
1190
1191 /**
1192 * Is Edit Mode.
1193 *
1194 * @access public
1195 * @since 4.6.1
1196 *
1197 * @return boolean
1198 */
1199 public static function is_edit_mode() {
1200 return isset( $_REQUEST['elementor-preview'] ) && ! empty( $_REQUEST['elementor-preview'] ); // phpcs:ignore WordPress.Security.NonceVerification
1201 }
1202
1203 /**
1204 * Generate Unique ID
1205 *
1206 * Generates a unique ID for the current page.
1207 *
1208 * @since 4.6.9
1209 * @access public
1210 *
1211 * @param string $id page ID.
1212 *
1213 * @return string unique ID.
1214 */
1215 public static function generate_unique_id( $id ) {
1216 return substr( md5( $id ), 0, 9 );
1217 }
1218
1219 /**
1220 * Get Safe Path
1221 *
1222 * @since 4.6.9
1223 * @access public
1224 *
1225 * @param string $file_path unsafe file path.
1226 *
1227 * @return string safe file path.
1228 */
1229 public static function get_safe_path( $file_path ) {
1230
1231 $path = str_replace( array( '//', '\\\\' ), array( '/', '\\' ), $file_path );
1232
1233 return str_replace( array( '/', '\\' ), DIRECTORY_SEPARATOR, $path );
1234 }
1235
1236 public static function get_safe_url( $url ) {
1237 if ( is_ssl() ) {
1238 $url = wp_parse_url( $url );
1239
1240 if ( ! empty( $url['host'] ) ) {
1241 $url['scheme'] = 'https';
1242 }
1243
1244 return self::unparse_url( $url );
1245 }
1246
1247 return $url;
1248 }
1249
1250 public static function unparse_url( $parsed_url ) {
1251 $scheme = isset( $parsed_url['scheme'] ) ? $parsed_url['scheme'] . '://' : '';
1252 $host = isset( $parsed_url['host'] ) ? $parsed_url['host'] : '';
1253 $port = isset( $parsed_url['port'] ) ? ':' . $parsed_url['port'] : '';
1254 $user = isset( $parsed_url['user'] ) ? $parsed_url['user'] : '';
1255 $pass = isset( $parsed_url['pass'] ) ? ':' . $parsed_url['pass'] : '';
1256 $pass = ( $user || $pass ) ? "$pass@" : '';
1257 $path = isset( $parsed_url['path'] ) ? $parsed_url['path'] : '';
1258 $query = isset( $parsed_url['query'] ) ? '?' . $parsed_url['query'] : '';
1259 $fragment = isset( $parsed_url['fragment'] ) ? '#' . $parsed_url['fragment'] : '';
1260
1261 return "$scheme$user$pass$host$port$path$query$fragment";
1262 }
1263
1264 /**
1265 * Check if the current post type should include addons.
1266 *
1267 * @param string $id current post ID.
1268 *
1269 * @since 4.9.18
1270 * @access public
1271 */
1272 public static function check_post_type( $id ) {
1273
1274 if ( ! $id ) {
1275 return false;
1276 }
1277
1278 $template_name = get_post_meta( $id, '_elementor_template_type', true );
1279
1280 $template_list = array(
1281 'header',
1282 'footer',
1283 'single',
1284 'post',
1285 'page',
1286 'archive',
1287 'search-results',
1288 'error-404',
1289 'product',
1290 'product-archive',
1291 'section',
1292 );
1293
1294 return in_array( $template_name, $template_list, true );
1295 }
1296
1297 /**
1298 * Get Draw SVG Notice
1299 *
1300 * @since 4.9.26
1301 * @access public
1302 *
1303 * @param object $elem element object.
1304 * @param string $search search query.
1305 * @param array $conditions control conditions
1306 */
1307 public static function get_draw_svg_notice( $elem, $search, $conditions, $index = 0, $nested = 'condition' ) {
1308
1309 $url = add_query_arg(
1310 array(
1311 'page' => 'premium-addons',
1312 'search' => $search,
1313 '#tab' => 'elements',
1314 ),
1315 esc_url( admin_url( 'admin.php' ) )
1316 );
1317
1318 $control_attr = array(
1319 'type' => Controls_Manager::RAW_HTML,
1320 'raw' => __( 'You need first to enable SVG Draw option checkbox from ', 'premium-addons-for-elementor' ) . '<a href="' . esc_url( $url ) . '" target="_blank">' . __( 'here.', 'premium-addons-for-elementor' ) . '</a>',
1321 'classes' => 'editor-pa-control-notice',
1322 'content_classes' => 'elementor-panel-alert elementor-panel-alert-warning',
1323 );
1324
1325 $control_attr[ $nested ] = $conditions;
1326
1327 $elem->add_control(
1328 'draw_svg_notice_' . $index,
1329 $control_attr
1330 );
1331 }
1332
1333 /**
1334 * Checks if Elementor PRO 3.8 or higher is activated && if the Loop experiment is activated.
1335 *
1336 * @since 4.9.45
1337 * @access public
1338 *
1339 * @return bool
1340 */
1341 public static function is_loop_exp_enabled() {
1342
1343 if ( defined( 'ELEMENTOR_PRO_VERSION' ) ) {
1344
1345 if ( version_compare( ELEMENTOR_PRO_VERSION, '3.16.0', '>=' ) ) {
1346 return true;
1347 } elseif ( version_compare( ELEMENTOR_PRO_VERSION, '3.8', '>=' ) ) {
1348 $is_loop_enabled = self::check_elementor_experiment( 'loop' );
1349
1350 if ( $is_loop_enabled ) {
1351 return true;
1352 }
1353 }
1354 }
1355
1356 return false;
1357 }
1358
1359 /**
1360 * Get Element Classes.
1361 *
1362 * @access private
1363 * @since 2.8.22
1364 *
1365 * @param array $devices devices to hide on.
1366 *
1367 * @return array
1368 */
1369 public static function get_element_classes( $devices, $default = array() ) {
1370
1371 $classes = $default;
1372
1373 if ( count( $devices ) ) {
1374 foreach ( $devices as $index => $device ) {
1375 $classes[] = 'elementor-hidden-' . $device;
1376 }
1377
1378 $classes[] = 'premium-addons-element';
1379 }
1380
1381 return $classes;
1382 }
1383
1384 /**
1385 * Round Numbers In A Reading-friendly Format.
1386 *
1387 * @param integer $num followers number.
1388 */
1389 public static function premium_format_numbers( $num ) {
1390 $num = intval( $num );
1391 $result = '';
1392
1393 if ( $num >= 1000000000 ) {
1394 $tmp = round( ( $num / 1000000 ), 1 );
1395 $result = $tmp . 'B';
1396 return $result;
1397 }
1398
1399 if ( $num >= 1000000 ) {
1400 $tmp = round( ( $num / 1000000 ), 1 );
1401 $result = $tmp . 'M';
1402 return $result;
1403 }
1404
1405 if ( $num >= 1000 ) {
1406 $tmp = round( ( $num / 1000 ), 1 );
1407 $result = $tmp . 'K';
1408
1409 return $result;
1410 }
1411
1412 return round( $num, 1 );
1413 }
1414
1415 /**
1416 * Render Rating Stars
1417 *
1418 * @since 4.10.13
1419 * @access public
1420 *
1421 * @param float $rating rating score.
1422 * @param string $fill_color fill color.
1423 * @param string $empty_color empty color.
1424 * @param float $star_size star size.
1425 */
1426 public static function render_rating_stars( $rating, $fill_color, $empty_color, $star_size ) {
1427
1428 ?>
1429
1430 <span class="premium-fb-rev-stars">
1431 <?php
1432
1433 foreach ( array( 1, 2, 3, 4, 5 ) as $val ) {
1434 $score = round( ( $rating - $val ), 2 );
1435
1436 if ( $score >= -0.2 ) {
1437
1438 ?>
1439 <span class="premium-fb-rev-star"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" version="1.1" width="<?php echo esc_attr( $star_size ); ?>" height="<?php echo esc_attr( $star_size ); ?>" viewBox="0 0 1792 1792"><path d="M1728 647q0 22-26 48l-363 354 86 500q1 7 1 20 0 21-10.5 35.5t-30.5 14.5q-19 0-40-12l-449-236-449 236q-22 12-40 12-21 0-31.5-14.5t-10.5-35.5q0-6 2-20l86-500-364-354q-25-27-25-48 0-37 56-46l502-73 225-455q19-41 49-41t49 41l225 455 502 73q56 9 56 46z" fill="<?php echo esc_attr( $fill_color ); ?>"></path></svg></span>
1440 <?php
1441 } elseif ( $score > -0.8 && $score < -0.2 ) {
1442 ?>
1443 <span class="premium-fb-rev-star"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" version="1.1" width="<?php echo esc_attr( $star_size ); ?>" height="<?php echo esc_attr( $star_size ); ?>" viewBox="0 0 1792 1792"><path d="M1250 957l257-250-356-52-66-10-30-60-159-322v963l59 31 318 168-60-355-12-66zm452-262l-363 354 86 500q5 33-6 51.5t-34 18.5q-17 0-40-12l-449-236-449 236q-23 12-40 12-23 0-34-18.5t-6-51.5l86-500-364-354q-32-32-23-59.5t54-34.5l502-73 225-455q20-41 49-41 28 0 49 41l225 455 502 73q45 7 54 34.5t-24 59.5z" fill="<?php echo esc_attr( $fill_color ); ?>"></path></svg></span>
1444 <?php } else { ?>
1445 <span class="premium-fb-rev-star"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" version="1.1" width="<?php echo esc_attr( $star_size ); ?>" height="<?php echo esc_attr( $star_size ); ?>" viewBox="0 0 1792 1792"><path d="M1201 1004l306-297-422-62-189-382-189 382-422 62 306 297-73 421 378-199 377 199zm527-357q0 22-26 48l-363 354 86 500q1 7 1 20 0 50-41 50-19 0-40-12l-449-236-449 236q-22 12-40 12-21 0-31.5-14.5t-10.5-35.5q0-6 2-20l86-500-364-354q-25-27-25-48 0-37 56-46l502-73 225-455q19-41 49-41t49 41l225 455 502 73q56 9 56 46z" fill="<?php echo esc_attr( $empty_color ); ?>"></path></svg></span>
1446 <?php
1447 }
1448 }
1449 ?>
1450 </span>
1451
1452 <?php
1453 }
1454
1455
1456 /**
1457 * Get SVG Shapes
1458 *
1459 * @since 4.10.13
1460 * @access public
1461 */
1462 public static function get_svg_shapes( $shape = '' ) {
1463
1464 if ( null === self::$shapes ) {
1465
1466 self::$shapes = require PREMIUM_ADDONS_PATH . 'addons/shapes.php';
1467
1468 }
1469
1470 $shapes = self::$shapes;
1471
1472 if ( empty( $shape ) ) {
1473 return $shapes;
1474 } else {
1475 return $shapes[ $shape ]['imagesmall'];
1476 }
1477 }
1478
1479 public static function get_btn_svgs( $style = 'line1' ) {
1480
1481 $html = '';
1482
1483 switch ( $style ) {
1484 case 'line1':
1485 $html = '<div class="premium-btn-line-wrap"><svg class="premium-btn-svg" aria-hidden="true" width="100%" height="9" viewBox="0 0 101 9">
1486 <path d="M.426 1.973C4.144 1.567 17.77-.514 21.443 1.48 24.296 3.026 24.844 4.627 27.5 7c3.075 2.748 6.642-4.141 10.066-4.688 7.517-1.2 13.237 5.425 17.59 2.745C58.5 3 60.464-1.786 66 2c1.996 1.365 3.174 3.737 5.286 4.41 5.423 1.727 25.34-7.981 29.14-1.294" pathLength="1"></path>
1487 </svg></div>';
1488 break;
1489
1490 case 'line3':
1491 $html = '<div class="premium-btn-line-wrap"><svg class="premium-btn-svg" aria-hidden="true" width="100%" height="18" viewBox="0 0 59 18">
1492 <path d="M.945.149C12.3 16.142 43.573 22.572 58.785 10.842" pathLength="1"></path>
1493 </svg></div>';
1494 break;
1495
1496 case 'line4':
1497 $html = '<svg class="premium-btn-svg" aria-hidden="true" width="300%" height="100%" viewBox="0 0 1200 60" preserveAspectRatio="none">
1498 <path d="M0,56.5c0,0,298.666,0,399.333,0C448.336,56.5,513.994,46,597,46c77.327,0,135,10.5,200.999,10.5c95.996,0,402.001,0,402.001,0"></path>
1499 </svg>';
1500 break;
1501
1502 default:
1503 // code...
1504 break;
1505 }
1506
1507 return $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
1508 }
1509
1510 /**
1511 * Add Button Hover Controls
1512 *
1513 * @since 4.10.17
1514 * @access public
1515 *
1516 * @param object $elem widget object.
1517 * @param array $conditions controls conditions.
1518 */
1519 public static function add_btn_hover_controls( $elem, $conditions ) {
1520
1521 $elem->add_control(
1522 'premium_button_hover_effect',
1523 array(
1524 'label' => __( 'Hover Effect', 'premium-addons-for-elementor' ),
1525 'type' => Controls_Manager::SELECT,
1526 'default' => 'none',
1527 'options' => array(
1528 'none' => __( 'None', 'premium-addons-for-elementor' ),
1529 'style1' => __( 'Slide', 'premium-addons-for-elementor' ),
1530 'style2' => __( 'Shutter', 'premium-addons-for-elementor' ),
1531 'style5' => apply_filters( 'pa_pro_label', __( 'In & Out (Pro)', 'premium-addons-for-elementor' ) ),
1532 'style6' => apply_filters( 'pa_pro_label', __( 'Grow (Pro)', 'premium-addons-for-elementor' ) ),
1533 'style7' => apply_filters( 'pa_pro_label', __( 'Double Layers (Pro)', 'premium-addons-for-elementor' ) ),
1534 'style8' => apply_filters( 'pa_pro_label', __( 'Animated Underline (Pro)', 'premium-addons-for-elementor' ) ),
1535 ),
1536 'separator' => 'before',
1537 'label_block' => true,
1538 'condition' => $conditions,
1539 )
1540 );
1541
1542 $elem->add_control(
1543 'button_hover_effect_notice',
1544 array(
1545 'raw' => __( 'Important: You need to set a background to the button to see the effects.', 'premium-addons-for-elementor' ),
1546 'type' => Controls_Manager::RAW_HTML,
1547 'content_classes' => 'elementor-panel-alert elementor-panel-alert-warning',
1548 'condition' => array_merge(
1549 $conditions,
1550 array(
1551 'premium_button_hover_effect!' => 'none',
1552 )
1553 ),
1554 )
1555 );
1556
1557 do_action( 'pa_button_hover_controls', $elem, $conditions );
1558
1559 $elem->add_control(
1560 'premium_button_style1_dir',
1561 array(
1562 'label' => __( 'Slide Direction', 'premium-addons-for-elementor' ),
1563 'type' => Controls_Manager::SELECT,
1564 'default' => 'bottom',
1565 'options' => array(
1566 'bottom' => __( 'Top to Bottom', 'premium-addons-for-elementor' ),
1567 'top' => __( 'Bottom to Top', 'premium-addons-for-elementor' ),
1568 'left' => __( 'Right to Left', 'premium-addons-for-elementor' ),
1569 'right' => __( 'Left to Right', 'premium-addons-for-elementor' ),
1570 ),
1571 'condition' => array_merge(
1572 $conditions,
1573 array(
1574 'premium_button_hover_effect' => 'style1',
1575 )
1576 ),
1577 'label_block' => true,
1578 )
1579 );
1580
1581 $elem->add_control(
1582 'premium_button_style2_dir',
1583 array(
1584 'label' => __( 'Shutter Direction', 'premium-addons-for-elementor' ),
1585 'type' => Controls_Manager::SELECT,
1586 'default' => 'shutouthor',
1587 'options' => array(
1588 'shutinhor' => __( 'Shutter in Horizontal', 'premium-addons-for-elementor' ),
1589 'shutinver' => __( 'Shutter in Vertical', 'premium-addons-for-elementor' ),
1590 'shutoutver' => __( 'Shutter out Horizontal', 'premium-addons-for-elementor' ),
1591 'shutouthor' => __( 'Shutter out Vertical', 'premium-addons-for-elementor' ),
1592 'scshutoutver' => __( 'Scaled Shutter Vertical', 'premium-addons-for-elementor' ),
1593 'scshutouthor' => __( 'Scaled Shutter Horizontal', 'premium-addons-for-elementor' ),
1594 'dshutinver' => __( 'Tilted Left', 'premium-addons-for-elementor' ),
1595 'dshutinhor' => __( 'Tilted Right', 'premium-addons-for-elementor' ),
1596 ),
1597 'condition' => array_merge(
1598 $conditions,
1599 array(
1600 'premium_button_hover_effect' => 'style2',
1601 )
1602 ),
1603 'label_block' => true,
1604 )
1605 );
1606 }
1607
1608 /**
1609 * Add Templates Controls
1610 *
1611 * @since 4.11.25
1612 * @access public
1613 *
1614 * @param string $keyword keyword for templates.
1615 * @param string $demo demo url.
1616 */
1617 public static function add_templates_controls( $element, $keyword, $demo ) {
1618
1619 if ( ! Admin_Helper::check_element_by_key( 'premium-templates' ) ) {
1620 return;
1621 }
1622
1623 $element->add_control(
1624 'premium_templates_links',
1625 array(
1626 'type' => Controls_Manager::RAW_HTML,
1627 'raw' => '<div class="premium-promote-box widget-box">
1628 <div class="premium-promote-ctas">
1629 <a class="premium-promote-demo elementor-button elementor-button-default" href="' . esc_url( $demo ) . '" target="_blank"> ' .
1630 __( 'Widget Demo', 'premium-addons-for-elementor' ) .
1631 '</a>
1632 <a class="premium-promote-upgrade premium-widget-blocks elementor-button elementor-button-default" href="#" data-keyword="' . esc_attr( $keyword ) . '">' .
1633 __( 'Templates', 'premium-addons-for-elementor' ) .
1634 '</a>
1635 </div>
1636 </div>',
1637 )
1638 );
1639 }
1640
1641 /**
1642 * Add Templates Controls
1643 *
1644 * @since 4.11.29
1645 * @access public
1646 *
1647 * @param string $keyword keyword for templates.
1648 * @param string $demo demo url.
1649 */
1650 public static function register_papro_promotion_controls( $element, $keyword ) {
1651
1652 if ( ! self::check_papro_version() ) {
1653
1654 $pro_link = self::get_campaign_link( 'https://premiumaddons.com/pro/#get-pa-pro', $keyword, 'wp-editor', 'get-pro' );
1655
1656 $element->start_controls_section(
1657 'section_pro_features_field',
1658 array(
1659 'label' => __( 'Go Pro for More Features', 'premium-addons-for-elementor' ),
1660 )
1661 );
1662
1663 $element->add_control(
1664 'pa_pro_promotion_notice',
1665 array(
1666 'type' => Controls_Manager::NOTICE,
1667 'notice_type' => 'info',
1668 'dismissible' => false,
1669 'content' => __( '<b>Build smarter and faster</b> with premium widgets, 580+ container blocks, and advanced customization controls — all available in the <a href="' . esc_url( $pro_link ) . '" target="_blank">PA Pro</a>. <b>Save up to 30%!</b>.', 'premium-addons-for-elementor' ),
1670 )
1671 );
1672
1673 $element->end_controls_section();
1674 }
1675 }
1676
1677 /**
1678 * Register Element Feedback Controls
1679 *
1680 * @since 4.11.36
1681 * @access public
1682 *
1683 * @param object $element widget object.
1684 */
1685 public static function register_element_feedback_controls( $element ) {
1686
1687 $element->add_control(
1688 'feedback_message',
1689 array(
1690 'label' => __( 'Feedback & Feature Request', 'premium-addons-for-elementor' ),
1691 'type' => Controls_Manager::TEXTAREA,
1692 'placeholder' => __( 'Share your feedback or feature request here...', 'premium-addons-for-elementor' ),
1693 'label_block' => true,
1694 'render_type' => 'ui',
1695 'ai' => array(
1696 'active' => false,
1697 ),
1698 )
1699 );
1700
1701 $element->add_control(
1702 'feedback_message_submit',
1703 array(
1704 'type' => Controls_Manager::RAW_HTML,
1705 'raw' => '<form onsubmit="submitFeedbackMessage(this,\'' . $element->get_title() . '\');" action="javascript:void(0);"><input type="submit" value="Send Feedback" class="elementor-button" style="background-color: rgba(207, 211, 215, 0.35); color: #000;"></form>',
1706 'label_block' => true,
1707 )
1708 );
1709 }
1710
1711 /**
1712 * Get Button Class
1713 *
1714 * @since 4.10.17
1715 * @access public
1716 *
1717 * @param $settings object widget settings.
1718 *
1719 * @return string $class css class.
1720 */
1721 public static function get_button_class( $settings ) {
1722
1723 $class = '';
1724
1725 $papro_activated = self::check_papro_version();
1726
1727 if ( ! $papro_activated && ! in_array( $settings['premium_button_hover_effect'], array( 'none', 'style1', 'style2' ), true ) ) {
1728 return '';
1729 }
1730
1731 if ( 'style1' === $settings['premium_button_hover_effect'] ) {
1732 $class = 'premium-button-style1-' . $settings['premium_button_style1_dir'];
1733 } elseif ( 'style2' === $settings['premium_button_hover_effect'] ) {
1734 $class = 'premium-button-style2-' . $settings['premium_button_style2_dir'];
1735 } elseif ( 'style5' === $settings['premium_button_hover_effect'] ) {
1736 $class = 'premium-button-style5-' . $settings['premium_button_style5_dir'];
1737 } elseif ( 'style6' === $settings['premium_button_hover_effect'] ) {
1738 $class = 'premium-button-style6';
1739 } elseif ( 'style7' === $settings['premium_button_hover_effect'] ) {
1740 $class = 'premium-button-style7-' . $settings['premium_button_style7_dir'];
1741 } elseif ( 'style8' === $settings['premium_button_hover_effect'] ) {
1742 $class = 'premium-button-' . $settings['underline_style'];
1743 }
1744
1745 return 'premium-button-' . $settings['premium_button_hover_effect'] . ' ' . $class;
1746 }
1747
1748
1749 /**
1750 * Get Empty Query Message
1751 *
1752 * Written in PHP and used to generate the final HTML when the query is empty
1753 *
1754 * @since 4.10.29
1755 * @access protected
1756 *
1757 * @param string $notice empty query notice.
1758 */
1759 public static function render_empty_query_message( $notice ) {
1760
1761 if ( empty( $notice ) ) {
1762 $notice = __( 'The current query has no posts. Please make sure you have published items matching your query.', 'premium-addons-for-elementor' );
1763 }
1764
1765 ?>
1766 <div class="premium-error-notice">
1767 <?php echo wp_kses_post( $notice ); ?>
1768 </div>
1769 <?php
1770 }
1771
1772 /**
1773 * Check Capability
1774 *
1775 * @since 4.10.28
1776 * @access public
1777 *
1778 * @param string $check capability.
1779 */
1780 public static function check_capability( $capability ) {
1781
1782 $post_author_id = get_the_author_meta( 'ID' );
1783
1784 $current_user_can = user_can( $post_author_id, $capability );
1785
1786 return $current_user_can;
1787 }
1788
1789
1790 /**
1791 * Get Allowed Icon Tags
1792 *
1793 * Returns an array of allowed HTML tags.
1794 *
1795 * @since 4.10.69
1796 * @access public
1797 *
1798 * @return array Array of allowed HTML tags.
1799 */
1800 public static function get_allowed_icon_tags() {
1801 return array(
1802 'svg' => array(
1803 'id' => array(),
1804 'class' => array(),
1805 'aria-hidden' => array(),
1806 'aria-labelledby' => array(),
1807 'role' => array(),
1808 'xmlns' => array(),
1809 'width' => array(),
1810 'height' => array(),
1811 'viewbox' => array(),
1812 'data-*' => true,
1813 ),
1814 'g' => array( 'fill' => array() ),
1815 'title' => array( 'title' => array() ),
1816 'path' => array(
1817 'd' => array(),
1818 'fill' => array(),
1819 ),
1820 'i' => array(
1821 'class' => array(),
1822 'id' => array(),
1823 'style' => array(),
1824 ),
1825 );
1826 }
1827
1828 /**
1829 * Get SVG By Icon
1830 *
1831 * @since 4.10.69
1832 * @access public
1833 */
1834 public static function get_svg_by_icon( $icon, $attributes = array() ) {
1835
1836 if ( empty( $icon ) || empty( $icon['value'] ) || empty( $icon['library'] ) ) {
1837 return '';
1838 }
1839
1840 // If icon library is SVG, then go to Elementor. Used for widgets where this function is called in all cases.
1841 if ( false === strpos( $icon['library'], 'fa-' ) ) {
1842
1843 if ( is_string( $attributes ) ) {
1844 $attributes = str_replace( '"', '', $attributes );
1845 }
1846
1847 $svg_html = Icons_Manager::try_get_icon_html( $icon, wp_parse_args( $attributes, array( 'aria-hidden' => 'true' ) ) );
1848
1849 return $svg_html;
1850 }
1851
1852 $icon['font_family'] = 'font-awesome';
1853
1854 $i_class = str_replace( ' ', '-', $icon['value'] );
1855
1856 $svg_html = '<svg ';
1857
1858 $icon = self::get_icon_svg_data( $icon );
1859
1860 if ( ! $icon ) {
1861 // Icons_Manager::render_icon( $icon, array( 'aria-hidden' => 'true' ) );
1862 Icons_Manager::render_icon( $icon, wp_parse_args( $attributes, array( 'aria-hidden' => 'true' ) ) );
1863 return;
1864 }
1865
1866 $view_box = '0 0 ' . $icon['width'] . ' ' . $icon['height'];
1867
1868 if ( is_array( $attributes ) ) {
1869
1870 foreach ( $attributes as $key => $value ) {
1871
1872 if ( 'class' === $key ) {
1873
1874 $svg_html .= 'class="svg-inline--' . $i_class . ' ' . $value . '" ';
1875
1876 } else {
1877 $svg_html .= " {$key}='{$value}' ";
1878 }
1879 }
1880 } else {
1881
1882 $attributes = str_replace( 'class="', 'class="svg-inline--' . $i_class . ' ', $attributes );
1883
1884 $svg_html .= $attributes;
1885 }
1886
1887 $svg_html .= " aria-hidden='true' xmlns='http://www.w3.org/2000/svg' viewBox='{$view_box}'>";
1888
1889 $svg_html .= '<path d="' . esc_attr( $icon['path'] ) . '"></path>';
1890 $svg_html .= '</svg>';
1891
1892 return wp_kses( $svg_html, self::get_allowed_icon_tags() );
1893 }
1894
1895 /**
1896 * Get Elementor Template ID
1897 *
1898 * @since 4.10.80
1899 * @access public
1900 *
1901 * @param string $title template title.
1902 *
1903 * @return string $template_id template ID.
1904 */
1905 public static function get_elementor_template_id( $title ) {
1906
1907 if ( empty( $title ) ) {
1908 return;
1909 }
1910
1911 $cache_key = 'pa_tpl_id_' . md5( $title );
1912
1913 $post_id = wp_cache_get( $cache_key, 'premium_addons' );
1914
1915 if ( false !== $post_id ) {
1916 return $post_id;
1917 }
1918
1919 $args = array(
1920 'post_type' => 'elementor_library',
1921 'post_status' => 'publish',
1922 'posts_per_page' => 1,
1923 'title' => $title,
1924 'suppress_filters' => true,
1925 );
1926
1927 $query = new \WP_Query( $args );
1928
1929 $post_id = '';
1930
1931 if ( $query->have_posts() ) {
1932 $post_id = $query->post->ID;
1933
1934 wp_reset_postdata();
1935 }
1936
1937 wp_cache_set( $cache_key, $post_id, 'premium_addons' );
1938
1939 return $post_id;
1940 }
1941
1942 /**
1943 * Render Elementor Template
1944 *
1945 * @since 4.10.80
1946 * @access public
1947 *
1948 * @param string|int $title Template Title||id.
1949 * @param bool $id indicates if $title is the template title or id.
1950 *
1951 * @return $template_content string HTML Markup of the selected template.
1952 */
1953 public static function render_elementor_template( $title, $id = false ) {
1954
1955 $frontend = Plugin::$instance->frontend;
1956
1957 $custom_temp = apply_filters( 'pa_temp_id', false );
1958
1959 if ( $custom_temp ) {
1960 $id = $title = $custom_temp;
1961 }
1962
1963 if ( ! $id ) {
1964 $id = self::get_elementor_template_id( $title );
1965
1966 if ( ! $id ) {
1967 // To replace the &#8211; in templates names with dash.
1968 $decoded_title = html_entity_decode( $title );
1969 $id = self::get_elementor_template_id( $decoded_title );
1970 }
1971
1972 $id = apply_filters( 'wpml_object_id', $id, 'elementor_library', true );
1973 } else {
1974 $id = $title;
1975 }
1976
1977 // $template_content = $frontend->get_builder_content( $id, true );
1978 $template_content = $frontend->get_builder_content_for_display( $id, true );
1979
1980 return $template_content;
1981 }
1982
1983 /**
1984 * Get Device Type
1985 *
1986 * @since 4.11.50
1987 * @access public
1988 *
1989 * @return string device type.
1990 */
1991 public static function get_device_type() {
1992
1993 static $device_type = null;
1994
1995 // Return cached result if already detected.
1996 if ( null !== $device_type ) {
1997 return $device_type;
1998 }
1999
2000 // Default to desktop.
2001 $device_type = 'desktop';
2002
2003 // Only load Device_Detector if not already loaded.
2004 if ( ! class_exists( 'PremiumAddons\Includes\Helpers\Device_Detector' ) ) {
2005 require_once PREMIUM_ADDONS_PATH . 'includes/helpers/device-detector.php';
2006 }
2007
2008 $detect = new Helpers\Device_Detector();
2009
2010 // Detect device type with priority: tablet > mobile > desktop.
2011 if ( $detect->isTablet() ) {
2012 $device_type = 'tablet';
2013 } elseif ( $detect->isMobile() ) {
2014 $device_type = 'mobile';
2015 }
2016
2017 return $device_type;
2018 }
2019
2020 /**
2021 * Get Widget Class Name
2022 *
2023 * @since 4.11.51
2024 * @access public
2025 *
2026 * @param string $widget_key Widget slug/key, e.g. 'premium-banner'.
2027 * @return string|false Fully-qualified class name on success, false on failure.
2028 */
2029 public static function get_widget_class_name( $widget_key ) {
2030
2031 static $classes_list = null;
2032
2033 $default_namespace = 'PremiumAddons\\Widgets\\';
2034
2035 // load the map once.
2036 if ( null === $classes_list ) {
2037
2038 $map_file = PREMIUM_ADDONS_PATH . 'includes/helpers/widget-class-map.php';
2039
2040 if ( file_exists( $map_file ) ) {
2041
2042 $map = include $map_file;
2043 $classes_list = is_array( $map ) ? $map : array();
2044 } else {
2045 $classes_list = array();
2046 }
2047 }
2048
2049 if ( empty( $widget_key ) || ! is_string( $widget_key ) ) {
2050 return false;
2051 }
2052
2053 if ( ! isset( $classes_list[ $widget_key ] ) ) {
2054 return false;
2055 }
2056
2057 $class_name = $classes_list[ $widget_key ];
2058
2059 if ( is_string( $class_name ) && false !== strpos( $class_name, '\\' ) ) {
2060 return $class_name;
2061 }
2062
2063 // Otherwise treat as short class name and prepend the default namespace.
2064 $short_class = (string) $class_name;
2065 $full_class = rtrim( $default_namespace, '\\' ) . '\\' . ltrim( $short_class, '\\' );
2066
2067 return $full_class;
2068 }
2069
2070 /**
2071 * Get Enabled Widgets
2072 *
2073 * @since 4.11.54
2074 * @access public
2075 *
2076 * @return array enabled widgets.
2077 */
2078 public static function get_enabled_widgets() {
2079
2080 $enabled_elements = Admin_Helper::get_enabled_elements();
2081
2082 $enabled_elements = array_filter(
2083 $enabled_elements,
2084 function ( $value, $key ) {
2085 return ( strpos( $key, 'premium-' ) === 0 || strpos( $key, 'mini-' ) === 0 || strpos( $key, 'woo-' ) === 0 ) && filter_var( $value, FILTER_VALIDATE_BOOLEAN );
2086 },
2087 ARRAY_FILTER_USE_BOTH
2088 );
2089
2090 return array_keys( $enabled_elements );
2091 }
2092
2093 /*
2094 * Get Enabled Widgets Names
2095 *
2096 * @since 4.11.54
2097 * @access public
2098 *
2099 * @return array enabled widgets names.
2100 */
2101 public static function get_enabled_widgets_names() {
2102
2103 static $names_list = null;
2104
2105 $enabled_elements = self::get_enabled_widgets();
2106
2107 $enabled_names = array();
2108
2109 if ( null === $names_list ) {
2110
2111 $map_file = PREMIUM_ADDONS_PATH . 'includes/helpers/widget-name-map.php';
2112
2113 if ( file_exists( $map_file ) ) {
2114
2115 $map = include $map_file;
2116 $names_list = is_array( $map ) ? $map : array();
2117 } else {
2118 $names_list = array();
2119 }
2120 }
2121
2122 foreach ( $enabled_elements as $key ) {
2123
2124 $widget_name = isset( $names_list[ $key ] ) ? $names_list[ $key ] : $key;
2125 $enabled_names[] = $widget_name;
2126 }
2127
2128 return $enabled_names;
2129 }
2130
2131 /**
2132 * Sanitize SVG
2133 *
2134 * Strips dangerous attributes (on* event handlers, javascript: hrefs) and
2135 * disallowed tags from a raw SVG string. Used as sanitize_callback for
2136 * custom_svg controls to prevent Stored XSS (CVE-2026-4790).
2137 *
2138 * @since 4.11.71
2139 * @access public
2140 *
2141 * @param string $svg Raw SVG input.
2142 * @return string Sanitized SVG string.
2143 */
2144 public static function sanitize_svg( $svg ) {
2145
2146 $allowed_tags = array(
2147 'svg' => array(
2148 'xmlns' => array(),
2149 'xmlns:xlink' => array(),
2150 'viewbox' => array(),
2151 'width' => array(),
2152 'height' => array(),
2153 'fill' => array(),
2154 'stroke' => array(),
2155 'class' => array(),
2156 'id' => array(),
2157 'role' => array(),
2158 'aria-hidden' => array(),
2159 'aria-label' => array(),
2160 'focusable' => array(),
2161 'style' => array(),
2162 ),
2163 'circle' => array(
2164 'cx' => array(),
2165 'cy' => array(),
2166 'r' => array(),
2167 'fill' => array(),
2168 'stroke' => array(),
2169 'stroke-width' => array(),
2170 'class' => array(),
2171 'id' => array(),
2172 'style' => array(),
2173 ),
2174 'ellipse' => array(
2175 'cx' => array(),
2176 'cy' => array(),
2177 'rx' => array(),
2178 'ry' => array(),
2179 'fill' => array(),
2180 'stroke' => array(),
2181 'stroke-width' => array(),
2182 'class' => array(),
2183 'id' => array(),
2184 'style' => array(),
2185 ),
2186 'rect' => array(
2187 'x' => array(),
2188 'y' => array(),
2189 'width' => array(),
2190 'height' => array(),
2191 'rx' => array(),
2192 'ry' => array(),
2193 'fill' => array(),
2194 'stroke' => array(),
2195 'stroke-width' => array(),
2196 'class' => array(),
2197 'id' => array(),
2198 'style' => array(),
2199 ),
2200 'line' => array(
2201 'x1' => array(),
2202 'y1' => array(),
2203 'x2' => array(),
2204 'y2' => array(),
2205 'stroke' => array(),
2206 'stroke-width' => array(),
2207 'class' => array(),
2208 'id' => array(),
2209 'style' => array(),
2210 ),
2211 'polyline' => array(
2212 'points' => array(),
2213 'fill' => array(),
2214 'stroke' => array(),
2215 'stroke-width' => array(),
2216 'class' => array(),
2217 'id' => array(),
2218 'style' => array(),
2219 ),
2220 'polygon' => array(
2221 'points' => array(),
2222 'fill' => array(),
2223 'stroke' => array(),
2224 'stroke-width' => array(),
2225 'class' => array(),
2226 'id' => array(),
2227 'style' => array(),
2228 ),
2229 'path' => array(
2230 'd' => array(),
2231 'fill' => array(),
2232 'stroke' => array(),
2233 'stroke-width' => array(),
2234 'fill-rule' => array(),
2235 'clip-rule' => array(),
2236 'class' => array(),
2237 'id' => array(),
2238 'style' => array(),
2239 ),
2240 'g' => array(
2241 'fill' => array(),
2242 'stroke' => array(),
2243 'transform' => array(),
2244 'class' => array(),
2245 'id' => array(),
2246 'style' => array(),
2247 'clip-path' => array(),
2248 ),
2249 'defs' => array(
2250 'class' => array(),
2251 'id' => array(),
2252 ),
2253 'clippath' => array(
2254 'class' => array(),
2255 'id' => array(),
2256 ),
2257 'image' => array(
2258 'x' => array(),
2259 'y' => array(),
2260 'width' => array(),
2261 'height' => array(),
2262 'href' => array(),
2263 'xlink:href' => array(),
2264 'preserveaspectratio' => array(),
2265 'opacity' => array(),
2266 'class' => array(),
2267 'id' => array(),
2268 'style' => array(),
2269 ),
2270 'text' => array(
2271 'x' => array(),
2272 'y' => array(),
2273 'dx' => array(),
2274 'dy' => array(),
2275 'fill' => array(),
2276 'font-size' => array(),
2277 'font-family' => array(),
2278 'text-anchor' => array(),
2279 'class' => array(),
2280 'id' => array(),
2281 'style' => array(),
2282 ),
2283 'tspan' => array(
2284 'x' => array(),
2285 'y' => array(),
2286 'dx' => array(),
2287 'dy' => array(),
2288 'class' => array(),
2289 'id' => array(),
2290 'style' => array(),
2291 ),
2292 'title' => array(),
2293 'desc' => array(),
2294 );
2295
2296 // SVG presentation properties are not on WordPress' safe_style_css
2297 // allow-list, so wp_kses() would strip them out of inline style
2298 // attributes (e.g. fill:none, stroke-width) and break SVGs exported
2299 // by Illustrator/Affinity/Boxy SVG. Allow them for this call only.
2300 $svg_css_props = array(
2301 'fill',
2302 'fill-rule',
2303 'fill-opacity',
2304 'stroke',
2305 'stroke-width',
2306 'stroke-linecap',
2307 'stroke-linejoin',
2308 'stroke-miterlimit',
2309 'stroke-dasharray',
2310 'stroke-dashoffset',
2311 'stroke-opacity',
2312 'clip-rule',
2313 'color',
2314 'opacity',
2315 'stop-color',
2316 'stop-opacity',
2317 'vector-effect',
2318 'paint-order',
2319 'transform',
2320 'transform-origin',
2321 );
2322
2323 $allow_svg_css = function ( $styles ) use ( $svg_css_props ) {
2324 return array_merge( $styles, $svg_css_props );
2325 };
2326
2327 add_filter( 'safe_style_css', $allow_svg_css );
2328
2329 $sanitized = wp_kses( $svg, $allowed_tags );
2330
2331 remove_filter( 'safe_style_css', $allow_svg_css );
2332
2333 return $sanitized;
2334 }
2335
2336 /**
2337 * Resolve the per-item badge text for a given post.
2338 *
2339 * @since 4.11.78
2340 * @param int $post_id Post ID to resolve terms for.
2341 * @param array $settings Parent widget's settings array.
2342 * @return false|string
2343 */
2344 public static function get_per_item_badge_text( $post_id, $settings ) {
2345
2346 if ( 'yes' !== ( $settings['premium_global_badge_switcher'] ?? '' ) ) {
2347 return false;
2348 }
2349
2350 if ( 'yes' !== ( $settings['pa_badge_per_item'] ?? '' ) ) {
2351 return false;
2352 }
2353
2354 $taxonomy = isset( $settings['pa_badge_per_item_source'] ) && '' !== $settings['pa_badge_per_item_source']
2355 ? $settings['pa_badge_per_item_source']
2356 : 'category';
2357
2358 $terms = get_the_terms( $post_id, $taxonomy );
2359
2360 if ( ! $terms || is_wp_error( $terms ) ) {
2361 return '';
2362 }
2363
2364 $names = wp_list_pluck( $terms, 'name' );
2365
2366 return implode( ', ', $names );
2367 }
2368 }
2369