PluginProbe
Themify Builder / trunk
Themify Builder vtrunk
7.8.1 7.8.0 7.7.9 7.7.8 7.7.7 7.7.6 7.7.5 7.7.4 7.7.3 7.7.2 trunk 7.6.0 7.6.1 7.6.2 7.6.3 7.6.4 7.6.5 7.6.6 7.6.7 7.6.8 7.6.9 7.7.0 7.7.1
themify-builder / classes / class-themify-builder-model.php

class-themify-builder-model.php in Themify Builder trunk, at classes/class-themify-builder-model.php

1,651 lines 64.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Class for interact with DB or data resource and state.
5 *
6 * @package Themify_Builder
7 * @subpackage Themify_Builder/classes
8 */
9 final class Themify_Builder_Model {
10
11 /**
12 * Feature Image Size
13 * @var array
14 */
15 public static $featured_image_size = array();
16
17 /**
18 * Image Width
19 * @var array
20 */
21 public static $image_width = array();
22
23 /**
24 * Image Height
25 * @var array
26 */
27 public static $image_height = array();
28
29 /**
30 * External Link
31 * @var array
32 */
33 public static $external_link = array();
34
35 /**
36 * Lightbox Link
37 * @var array
38 */
39 public static $lightbox_link = array();
40 public static $modules = array();//deprecated use Themify_Builder_Component_Module::load_modules
41 private const TRANSIENT_NAME = 'tb_edit_';
42
43 /**
44 * Active custom post types registered by Builder.
45 *
46 * @var array
47 */
48 public static $builder_cpt = array();
49
50 /**
51 * Directory Registry
52 */
53 private static $modules_registry = array();
54
55 /**
56 * Hook Content cache, used by Post modules
57 */
58 private static $hook_contents=array();
59
60 private function __construct() {
61
62 }
63
64 /**
65 * Get favorite option to module instance
66 * @return array
67 */
68 public static function get_favorite_modules():array {
69 $fv = get_user_option('themify_module_favorite', get_current_user_id());
70 if(!empty($fv)){
71 $fv=json_decode($fv,true);
72 if(!array_key_exists(0, $fv)){
73 $fv=array_keys($fv);
74 }
75 }
76 else{
77 $fv=array();
78 }
79 return $fv;
80 }
81
82 /**
83 * Check whether builder is active or not
84 * @return bool
85 */
86 public static function builder_check():bool {
87 static $is = NULL;
88 if ($is === null) {
89 $is = apply_filters('themify_enable_builder', themify_builder_get('setting-page_builder_is_active', 'builder_is_active'))!=='disable';
90 }
91 return $is;
92 }
93
94
95 /**
96 * Check is the frontend editor page
97 */
98 public static function is_frontend_editor_page(?int $post_id = null):bool {
99 $post_id = $post_id ??self::get_ID();
100 return apply_filters('themify_builder_is_frontend_editor', Themify_Access_Role::check_access_frontend($post_id), $post_id);
101 }
102
103 /**
104 * Check if builder frontend edit being invoked
105 */
106 public static function is_front_builder_activate():bool {
107 static $is = NULL;
108 if ($is === null) {
109 $is = Themify_Builder::$frontedit_active === true || ((isset($_GET['tb-preview']) || (isset($_COOKIE['tb_active']) && !is_admin() && !themify_is_ajax()) && self::is_frontend_editor_page()));
110 if ($is === true) {
111 add_filter('lazyload_is_enabled', '__return_false', 1, 100); //disable jetpack lazy load
112 add_filter('rocket_use_native_lazyload', '__return_false', 1, 100);
113 }
114 }
115 return $is;
116 }
117
118
119 /**
120 * Load general metabox fields
121 */
122 public static function load_general_metabox():void {
123 // Featured Image Size
124 self::$featured_image_size = apply_filters('themify_builder_metabox_featured_image_size', array(
125 'name' => 'feature_size',
126 'title' => __('Image Size', 'themify'),
127 'description' => sprintf(__('Image sizes can be set at <a href="%s">Media Settings</a>', 'themify'), admin_url('options-media.php')),
128 'type' => 'featimgdropdown'
129 ));
130 // Image Width
131 self::$image_width = apply_filters('themify_builder_metabox_image_width', array(
132 'name' => 'image_width',
133 'title' => __('Image Width', 'themify'),
134 'description' => '',
135 'type' => 'textbox',
136 'meta' => array('size' => 'small')
137 ));
138 // Image Height
139 self::$image_height = apply_filters('themify_builder_metabox_image_height', array(
140 'name' => 'image_height',
141 'title' => __('Image Height', 'themify'),
142 'description' => '',
143 'type' => 'textbox',
144 'meta' => array('size' => 'small'),
145 'class' => self::is_img_php_disabled() ? 'builder_show_if_enabled_img_php' : '',
146 ));
147 // External Link
148 self::$external_link = apply_filters('themify_builder_metabox_external_link', array(
149 'name' => 'external_link',
150 'title' => __('External Link', 'themify'),
151 'description' => __('Link Featured Image and Post Title to external URL', 'themify'),
152 'type' => 'textbox',
153 'meta' => array()
154 ));
155 // Lightbox Link
156 self::$lightbox_link = apply_filters('themify_builder_metabox_lightbox_link', array(
157 'name' => 'lightbox_link',
158 'title' => __('Lightbox Link', 'themify'),
159 'description' => __('Link Featured Image to lightbox image, video or external iframe', 'themify'),
160 'type' => 'textbox',
161 'meta' => array()
162 ));
163 }
164
165
166
167 /**
168 * Return frame layout
169 */
170 public static function get_frame_layout():array {
171 $path = THEMIFY_BUILDER_URI . '/img/row-frame/';
172 return array(
173 array('value' => 'none', 'label' => 'none', 'img' => $path . 'none.png'),
174 array('value' => 'corner1', 'label' => 'Rounded Corners', 'img' => $path . 'corner1.svg'),
175 array('value' => 'corner2', 'label' => 'Bracket Corners', 'img' => $path . 'corner2.svg'),
176 array('value' => 'corner3', 'label' => 'Flower Corners', 'img' => $path . 'corner3.svg'),
177 array('value' => 'corner4', 'label' => 'Slant Corners', 'img' => $path . 'corner4.svg'),
178 array('value' => 'slant1', 'label' => 'Slant 1', 'img' => $path . 'slant1.svg'),
179 array('value' => 'slant2', 'label' => 'Slant 2', 'img' => $path . 'slant2.svg'),
180 array('value' => 'slant3', 'label' => 'Slant 3', 'img' => $path . 'slant3.svg'),
181 array('value' => 'arrow1', 'label' => 'Arrow 1', 'img' => $path . 'arrow1.svg'),
182 array('value' => 'arrow2', 'label' => 'Arrow 2', 'img' => $path . 'arrow2.svg'),
183 array('value' => 'arrow3', 'label' => 'Arrow 3', 'img' => $path . 'arrow3.svg'),
184 array('value' => 'arrow4', 'label' => 'Arrow 4', 'img' => $path . 'arrow4.svg'),
185 array('value' => 'arrow5', 'label' => 'Arrow 5', 'img' => $path . 'arrow5.svg'),
186 array('value' => 'arrow6', 'label' => 'Arrow 6', 'img' => $path . 'arrow6.svg'),
187 array('value' => 'cloud1', 'label' => 'Cloud 1', 'img' => $path . 'cloud1.svg'),
188 array('value' => 'cloud2', 'label' => 'Cloud 2', 'img' => $path . 'cloud2.svg'),
189 array('value' => 'curve1', 'label' => 'Curve 1', 'img' => $path . 'curve1.svg'),
190 array('value' => 'curve2', 'label' => 'Curve 2', 'img' => $path . 'curve2.svg'),
191 array('value' => 'mountain1', 'label' => 'Mountain 1', 'img' => $path . 'mountain1.svg'),
192 array('value' => 'mountain2', 'label' => 'Mountain 2', 'img' => $path . 'mountain2.svg'),
193 array('value' => 'mountain3', 'label' => 'Mountain 3', 'img' => $path . 'mountain3.svg'),
194 array('value' => 'wave1', 'label' => 'Wave 1', 'img' => $path . 'wave1.svg'),
195 array('value' => 'wave2', 'label' => 'Wave 2', 'img' => $path . 'wave2.svg'),
196 array('value' => 'wave3', 'label' => 'Wave 3', 'img' => $path . 'wave3.svg'),
197 array('value' => 'wave4', 'label' => 'Wave 4', 'img' => $path . 'wave4.svg'),
198 array('value' => 'ink-splash1', 'label' => 'Ink Splash 1', 'img' => $path . 'ink-splash1.svg'),
199 array('value' => 'ink-splash2', 'label' => 'Ink Splash 2', 'img' => $path . 'ink-splash2.svg'),
200 array('value' => 'zig-zag', 'label' => 'Zig Zag', 'img' => $path . 'zig-zag.svg'),
201 array('value' => 'grass', 'label' => 'Grass', 'img' => $path . 'grass.svg'),
202 array('value' => 'melting', 'label' => 'Melting', 'img' => $path . 'melting.svg'),
203 array('value' => 'lace', 'label' => 'Lace', 'img' => $path . 'lace.svg'),
204 );
205 }
206
207
208 /**
209 * Get Post Types, which ready for an operation
210 * @return array
211 */
212 public static function get_post_types():array {
213
214 // If it's not a product search, proceed: retrieve the post types.
215 $types = get_post_types(array('exclude_from_search' => false));
216 if (themify_is_themify_theme()) {
217 // Exclude pages /////////////////
218 $exclude_pages = themify_builder_get('setting-search_settings_exclude');
219 if (!empty($exclude_pages)) {
220 unset($types['page']);
221 }
222 // Exclude posts /////////////////
223 $exclude_posts = themify_builder_get('setting-search_exclude_post');
224 if (!empty($exclude_posts)) {
225 unset($types['post']);
226 }
227 // Exclude custom post types /////
228 $exclude_types = apply_filters('themify_types_excluded_in_search', get_post_types(array(
229 '_builtin' => false,
230 'public' => true,
231 'exclude_from_search' => false
232 )));
233
234 foreach (array_keys($exclude_types) as $type) {
235 $check = \themify_builder_get('setting-search_exclude_' . $type);
236 if (!empty($check)) {
237 unset($types[$type]);
238 }
239 }
240 }
241 // Exclude Layout and Layout Part custom post types /////
242 unset($types['section'], $types[Themify_Builder_Layouts::LAYOUT_SLUG], $types[Themify_Builder_Layouts::LAYOUT_PART_SLUG], $types['elementor_library']);
243
244 return $types;
245 }
246
247 /**
248 * Check whether builder animation is active
249 * @return boolean|string
250 */
251 public static function is_animation_active() {
252 static $is = NULL;
253 if ($is === null) {
254 $val = \themify_builder_get('setting-page_builder_animation_appearance', 'builder_animation_appearance');
255 $is = $val === 'all' || self::is_front_builder_activate()? false : ('mobile' === $val ? 'm' : true);
256 }
257 return $is;
258 }
259
260 /**
261 * Check whether builder parallax is active
262 * @return boolean|string
263 */
264 public static function is_parallax_active() {
265 static $is = NULL;
266 if ($is === null) {
267 $val = \themify_builder_get('setting-page_builder_animation_parallax_bg', 'builder_animation_parallax_bg');
268 $is = self::is_front_builder_activate() ? true : ($val === 'all'? false : ('mobile' === $val ? 'm' : true));
269 }
270 return $is;
271 }
272
273 /**
274 * Check whether builder scroll effect is active
275 * @return boolean|string
276 */
277 public static function is_scroll_effect_active() {
278 static $is = NULL;
279 if ($is === null) {
280 // check if mobile excludes disabled OR disabled all transitions
281 $val = \themify_builder_get('setting-page_builder_animation_scroll_effect', 'builder_animation_scroll_effect');
282 $is = $val === 'all' || self::is_front_builder_activate()? false : ('mobile' === $val ? 'm' : true);
283 }
284 return $is;
285 }
286
287 /**
288 * Check whether builder sticky scroll is active
289 * @return boolean|string
290 */
291 public static function is_sticky_scroll_active() {
292 static $is = NULL;
293 if ($is === null) {
294 $val = \themify_builder_get('setting-page_builder_animation_sticky_scroll', 'builder_animation_sticky_scroll');
295 $is = $val === 'all' || self::is_front_builder_activate()? false : ('mobile' === $val ? 'm' : true);
296 $is = apply_filters('tb_sticky_scroll_active', $is);
297 }
298 return $is;
299 }
300
301
302 /**
303 * Returns list of colors and thumbnails
304 *
305 * @return array
306 */
307 public static function get_gutters(bool $def=true):array {
308 $gutters=array(
309 'gutter'=>3.2,
310 'narrow'=>1.6,
311 'none'=>0
312 );
313 foreach($gutters as $k=>$v){
314 $val=\themify_builder_get( 'setting-'.$k,'setting-'.$k);
315 if($val!==null && $val!==''){
316 if($v!=$val){
317 $gutters[$k]=$val;
318 }
319 elseif($def===false){
320 unset($gutters[$k]);
321 }
322 }
323 }
324 return $gutters;
325 }
326
327
328
329 /**
330 * Check whether an image script is use or not
331 *
332 * @since 2.4.2 Check if it's a Themify theme or not. If it's not, it's Builder standalone plugin.
333 *
334 * @return boolean
335 */
336 public static function is_img_php_disabled():bool {
337 static $is = NULL;
338 if ($is === null) {
339 $is = \themify_builder_get('setting-img_settings_use', 'image_setting-img_settings_use') ? true : false;
340 }
341 return $is;
342 }
343
344 public static function is_fullwidth_layout_supported():bool {
345 return apply_filters('themify_builder_fullwidth_layout_support', false);
346 }
347
348 /**
349 * Get alt text defined in WP Media attachment by a given URL
350 *
351 * @since 2.2.5
352 *
353 * @param string $image_url
354 *
355 * @return string
356 */
357 public static function get_alt_by_url(string $image_url):string {
358 $attachment_id = themify_get_attachment_id_from_url($image_url);
359 if ($attachment_id && ($alt = get_post_meta($attachment_id, '_wp_attachment_image_alt', true))) {
360 return $alt;
361 }
362 return '';
363 }
364
365 /**
366 * Get all modules settings for used in localize script.
367 *
368 * @access public
369 * @return array
370 */
371 public static function get_modules_localize_settings():array {
372 $return = array();
373 $modules=Themify_Builder_Component_Module::load_modules();
374 foreach ($modules as $id=>$module) {
375 $return[$id]=array();
376
377 if(is_string($module)){
378 $icon = $module::get_module_icon();
379 $name= $module::get_module_name();
380 }else{
381 $icon = $module->get_icon();
382 $name= $module->get_name();
383 }
384 $return[$id]['name']=$name;
385 if ($icon !== '-1') {
386 if ($icon === '') {
387 $icon = $id;
388 }
389 $return[$id]['icon'] = $icon;
390 \themify_get_icon($icon, 'ti');
391 }
392 }
393
394 return $return;
395 }
396
397
398 public static function format_text(string $content):string {
399 global $wp_embed;
400
401 $isLoop = Themify_Builder::$is_loop;
402 Themify_Builder::$is_loop = true;
403 $content = wptexturize($content);
404
405 $pattern = '|<p>\s*(https?://[^\s"]+)\s*</p>|im'; // pattern to check embed url
406 $to = '<p>' . PHP_EOL . '$1' . PHP_EOL . '</p>'; // add line break
407 $content = $wp_embed->run_shortcode($content);
408 $content = preg_replace($pattern, $to, $content);
409 $content = $wp_embed->autoembed($content);
410 $content = do_shortcode(shortcode_unautop($content));
411 Themify_Builder::$is_loop = $isLoop;
412 $content = convert_smilies($content);
413 $content = self::generate_read_more($content);
414
415 // Keep <style> from nested shortcodes (e.g. Layout Part inside Tile text).
416 // Tags can be stripped by later filters/editors, leaving CSS as visible text.
417 return self::preserve_module_styles( $content );
418 }
419
420 /**
421 * Extract stylesheet tags / orphaned Builder CSS rules and keep them applied.
422 */
423 public static function preserve_module_styles( string $content ): string {
424 if ( $content === '' || ( strpos( $content, '<style' ) === false && strpos( $content, '.tb_' ) === false ) ) {
425 return $content;
426 }
427
428 $extracted = '';
429
430 $content = preg_replace_callback( '/<style[^>]*>.*?<\/style>/is', static function ( $m ) use ( &$extracted ) {
431 $extracted .= $m[0];
432 return '';
433 }, $content );
434
435 $content = preg_replace_callback( '/<link[^>]+rel=["\']stylesheet["\'][^>]*\/?>/is', static function ( $m ) use ( &$extracted ) {
436 $extracted .= $m[0];
437 return '';
438 }, $content );
439
440 // Orphaned rules like ".tb_xxxx .ui { background-color:#xxx }" when <style> was stripped.
441 $content = preg_replace_callback( '/(\.tb_[a-zA-Z0-9_]+(?:\s+\.[a-zA-Z0-9_\-]+)*\s*\{\s*[a-zA-Z\-]+\s*:[^{}]*\})/s', static function ( $m ) use ( &$extracted ) {
442 $extracted .= '<style>' . $m[0] . '</style>';
443 return '';
444 }, $content );
445
446 if ( $extracted === '' ) {
447 return $content;
448 }
449
450 if ( ! did_action( 'wp_head' ) ) {
451 add_action( 'wp_head', static function () use ( $extracted ) {
452 echo $extracted; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
453 } );
454 return $content;
455 }
456
457 return $extracted . $content;
458 }
459
460 /*
461 * Generate read more link for text module
462 *
463 * @param string $content
464 * @return string generated load more link in the text.
465 */
466
467 public static function generate_read_more(string $content):string {
468 if (!empty($content) && \strpos($content, '!--more') !== false && \preg_match('/(<|&lt;)!--more(.*?)?--(>|&gt;)/', $content, $matches)) {
469 $text = \trim($matches[2]);
470 $read_more_text = !empty($text) ? $text : apply_filters('themify_builder_more_text', __('More ', 'themify'));
471 $content = \str_replace($matches[0], '<div class="more-text" style="display: none">', $content);
472 $content .= '</div><a href="#" class="module-text-more">' . $read_more_text . '</a>';
473 }
474 return $content;
475 }
476
477
478 public static function is_module_active(string $mod_name):bool {
479 if (themify_is_themify_theme()) {
480 $data = \themify_get_data();
481 $pre = 'setting-page_builder_exc_';
482 } else {
483 $pre = 'builder_exclude_module_';
484 $data = self::get_builder_settings();
485 }
486 return empty($data[$pre . $mod_name]);
487 }
488
489 /**
490 * Get module php files data
491 */
492 public static function get_modules(string $type = 'active',bool $ignore=false) {
493 $directories = self::$modules_registry;
494 $defaultModules=array(
495 'accordion'=>true,
496 'alert'=>true,
497 'box'=>true,
498 'buttons'=>true,
499 'callout'=>true,
500 'code'=>true,
501 'copyright'=>true,
502 'divider'=>true,
503 'fancy-heading'=>true,
504 'feature'=>true,
505 'gallery'=>true,
506 'icon'=>true,
507 'image'=>true,
508 'layout-part'=>true,
509 'link-block'=>true,
510 'login'=>true,
511 'lottie'=>true,
512 'map'=>true,
513 'menu'=>true,
514 'optin'=>true,
515 'overlay-content'=>true,
516 'page-break'=>true,
517 'plain-text'=>true,
518 'post'=>true,
519 'service-menu'=>true,
520 'signup-form'=>true,
521 'slider'=>true,
522 'social-share'=>true,
523 'star'=>true,
524 'tab'=>true,
525 'table'=>true,
526 'testimonial-slider'=>true,
527 'text'=>true,
528 'toc'=>true,
529 'video'=>true,
530 'widget'=>true,
531 'widgetized'=>true
532 );
533 $deprecated=array(
534 'highlight'=>true,
535 'portfolio'=>true,
536 'testimonial'=>true
537 );
538 $isNotSingle=$type === 'active' || $type === 'all';
539 if($isNotSingle===true || isset($deprecated[$type])){
540 foreach($deprecated as $id=>$dir){
541 if(self::is_cpt_active( $id )){
542 $defaultModules[$id]=$dir;
543 }
544 }
545 }
546 unset($deprecated);
547 if($isNotSingle===true){
548 $modules = array();
549 $defaultModules+=$directories;
550 foreach($defaultModules as $id=>$dir){
551 if ($type !== 'active' || self::is_module_active($id)) {
552 $modules[$id] = $dir;
553 }
554 }
555 return $modules;
556 }
557 elseif((isset($defaultModules[$type]) || isset($directories[$type])) && ($ignore===true || self::is_module_active($type))){
558 return isset($defaultModules[$type])?$defaultModules[$type]:$directories[$type];
559 }
560 return '';
561 }
562
563 /**
564 * Check whether theme loop template exist
565 * @param string $template_name
566 * @param string $template_path
567 * @return boolean
568 */
569 public static function is_loop_template_exist(string $template_name, string $template_path):bool {
570 return !locate_template(array(trailingslashit($template_path) . $template_name)) ? false : true;
571 }
572
573 public static function add_module(string $path) {
574
575 if(\strpos($path, '.php') !== false){
576 $path_info = \pathinfo($path);
577 $id = \str_replace('module-', '', $path_info['filename']);
578 self::$modules_registry[$id]=$path_info['dirname'];
579 }
580 elseif (is_dir($path)) {//backward
581 $d = dir($path);
582 while (( false !== ( $entry = $d->read() ))) {
583 if ($entry !== '.' && $entry !== '..' && $entry !== '.svn' && strpos($entry, 'module-') === 0) {
584 $id = str_replace(array('module-','.php'), '', $entry);
585 self::$modules_registry[$id] =rtrim($d->path,'/');
586 }
587 }
588 }
589 }
590
591 public static function get_directory_path($context=''):array {
592 return self::$modules_registry;
593 }
594
595
596 public static function is_cpt_active(string $post_type):bool {
597 return apply_filters("builder_is_{$post_type}_active", \in_array($post_type, self::$builder_cpt, true));
598 }
599
600 public static function builder_cpt_check():void {
601 if(empty(self::$builder_cpt)){
602 global $wpdb;
603 foreach (array('slider', 'highlight', 'testimonial') as $cpt) {
604 if (post_type_exists($cpt)) {
605 self::$builder_cpt[] = $cpt;
606 }
607 else {
608 $post=$wpdb->get_row('SELECT 1 FROM ' . $wpdb->posts . ' WHERE `post_type`="' . $cpt . '" LIMIT 1');
609 if (!empty($post)) {
610 self::$builder_cpt[] = $cpt;
611 }
612 }
613 }
614 }
615 }
616
617 /**
618 * Get a list of post types that can be accessed publicly
619 *
620 * does not include attachments, Builder layouts and layout parts,
621 * and also custom post types in Builder that have their own module.
622 *
623 * @return array of key => label pairs
624 */
625 public static function get_public_post_types():array {
626 $excluded = array('attachment'=>true, Themify_Builder_Layouts::LAYOUT_SLUG=>true, Themify_Builder_Layouts::LAYOUT_PART_SLUG=>true,Themify_Global_Styles::SLUG=>true, Themify_Custom_Fonts::SLUG=>true, 'section'=>true, 'elementor_library'=>true);
627 $result=[];
628 foreach (get_post_types(array('public' => true, 'publicly_queryable' => 'true'), 'objects') as $key => $value) {
629 if (!isset($excluded[$key])) {
630 $result[$key] = $value->labels->singular_name;
631 }
632 }
633 return apply_filters('builder_get_public_post_types', $result);
634 }
635
636 /**
637 * Get a list of taxonomies that can be accessed publicly
638 *
639 * does not include post formats, section categories (used by some themes),
640 * and also custom post types in Builder that have their own module.
641 *
642 * @return array of key => label pairs
643 */
644 public static function get_public_taxonomies():array {
645 $excluded = array('post_format'=>true, 'section-category'=>true,'product_shipping_class'=>true);
646 $result=[];
647 foreach (get_taxonomies(array('public' => true), 'objects') as $key => $value) {
648 if (!isset($excluded[$key])) {
649 $result[$key] = $value->labels->name;
650 }
651 }
652
653 return apply_filters('builder_get_public_taxonomies', $result);
654 }
655
656 /**
657 * Get public taxonomies registered for a post type.
658 *
659 * @since 7.5.0
660 */
661 public static function get_public_taxonomies_for_post_type( string $post_type ): array {
662 $excluded = array(
663 'post_format' => true,
664 'section-category' => true,
665 'product_shipping_class' => true,
666 'product_visibility' => true,
667 );
668 $all = get_taxonomies( array( 'public' => true ), 'objects' );
669 $result = array();
670 foreach ( get_object_taxonomies( $post_type, 'names' ) as $key ) {
671 if ( isset( $excluded[ $key ] ) || ! isset( $all[ $key ] ) ) {
672 continue;
673 }
674 $result[ $key ] = self::get_filter_taxonomy_label( $key, $post_type );
675 }
676 if ( $post_type === 'product' && isset( $result['product_cat'] ) ) {
677 $result = array_merge(
678 array( 'product_cat' => $result['product_cat'] ),
679 array_diff_key( $result, array( 'product_cat' => null ) )
680 );
681 } elseif ( $post_type === 'post' && isset( $result['category'] ) ) {
682 $result = array_merge(
683 array( 'category' => $result['category'] ),
684 array_diff_key( $result, array( 'category' => null ) )
685 );
686 }
687 return apply_filters( 'builder_get_public_taxonomies_for_post_type', $result, $post_type );
688 }
689
690 /**
691 * Label for filter taxonomy dropdown options.
692 */
693 private static function get_filter_taxonomy_label( string $taxonomy, string $post_type ): string {
694 if ( $post_type === 'product' ) {
695 if ( $taxonomy === 'product_cat' ) {
696 return __( 'Product Categories', 'themify' );
697 }
698 if ( $taxonomy === 'product_tag' ) {
699 return __( 'Product Tags', 'themify' );
700 }
701 }
702 $tax = get_taxonomy( $taxonomy );
703 return $tax ? $tax->labels->name : $taxonomy;
704 }
705
706 public static function parse_slug_to_ids(string $slug_string, string $post_type = 'post'):array {
707 $slug_arr = \explode(',', $slug_string);
708 $return = [];
709 if (!empty($slug_arr)) {
710 foreach ($slug_arr as $slug) {
711 $return[] = \is_numeric( $slug ) ? $slug : self::get_id_by_slug(trim($slug), $post_type);
712 }
713 }
714 return $return;
715 }
716
717 public static function get_id_by_slug(string $slug,string $post_type = 'post'):?int{
718 $args = array(
719 'name' => $slug,
720 'post_type' => $post_type,
721 'post_status' => 'publish',
722 'numberposts' => 1,
723 'no_found_rows' => true,
724 'cache_results' => false,
725 'ignore_sticky_posts' => true,
726 'update_post_term_cache' => false,
727 'update_post_meta_cache' => false,
728 'orderby' => 'none'
729 );
730 $my_posts = get_posts($args);
731 return $my_posts ? $my_posts[0]->ID : null;
732 }
733
734 public static function getMapKey():?string {
735 return \themify_builder_get('setting-google_map_key', 'builder_settings_google_map_key');
736 }
737
738 /**
739 * Get Builder Settings
740 */
741 public static function get_builder_settings():array {
742 static $data = null;
743 if ($data === null) {
744 $data = get_option('themify_builder_setting');
745 if (!empty($data) && is_array($data)) {
746 foreach ($data as $name => $value) {
747 $data[$name] = stripslashes($value);
748 }
749 }
750 else {
751 $data = array();
752 }
753 }
754 return $data;
755 }
756
757 /**
758 * Get ID
759 */
760 public static function get_ID() {
761 return \themify_is_shop() ? \themify_shop_pageId() : \get_the_id();
762 }
763
764
765 public static function get_transient_time() {
766 return apply_filters('themify_builder_ticks', MINUTE_IN_SECONDS / 2);
767 }
768
769 public static function set_edit_transient($post_id, $value) {
770 return Themify_Storage::set(self::TRANSIENT_NAME . $post_id, $value, self::get_transient_time());
771 }
772
773 public static function get_edit_transient($post_id) {
774 return Themify_Storage::get(self::TRANSIENT_NAME . $post_id);
775 }
776
777 public static function remove_edit_transient($post_id) {
778 return Themify_Storage::delete(self::TRANSIENT_NAME . $post_id);
779 }
780
781
782 /**
783 * Check if gutenberg active
784 * @return boolean
785 */
786 public static function is_gutenberg_active():bool {
787 static $is = null;
788 if ($is === null) {
789 $is = !self::is_plugin_active('disable-gutenberg/disable-gutenberg.php') && !self::is_plugin_active('classic-editor/classic-editor.php');
790 }
791 return $is;
792 }
793
794
795 /**
796 * Check if we are gutenberg editor
797 * !IMPORTANT can be used only after action "get_current_screen"
798 * @return boolean
799 */
800 public static function is_gutenberg_editor():bool {
801 static $is = null;
802 if ($is === null) {
803 $is = !isset($_GET['classic-editor']) && is_admin() && self::is_gutenberg_active() && get_current_screen()->is_block_editor();
804 }
805 return $is;
806 }
807
808 /**
809 * Plugin Active checking
810 *
811 * @access public
812 * @param string $plugin
813 * @return bool
814 */
815 public static function is_plugin_active(string $plugin):bool {
816 static $plugins = null;
817 static $active_plugins = array();
818 if ($plugins === null) {
819 $plugins = is_multisite() ? get_site_option('active_sitewide_plugins') : false;
820 $active_plugins = (array) apply_filters('active_plugins', get_option('active_plugins'));
821 }
822 return ( $plugins !== false && isset($plugins[$plugin]) ) || in_array($plugin, $active_plugins, true);
823 }
824
825
826 public static function checkUniqId($id):string{
827 static $ids=array();
828 if($id!==null && !isset($ids[$id])){
829 $ids[$id] = true;
830 return $id;
831 }
832 $id=self::generateID();
833 if(isset($ids[$id])){
834 while(isset($ids[$id])){
835 $id = self::generateID();
836 }
837 }
838 $ids[$id] = true;
839 return $id;
840 }
841
842 public static function generateID():string {
843 $hash = '';
844 $alpha_numeric = 'abcdefghijklmnopqrstuvwxyz0123456789';
845 for ($i = 0; $i < 4; ++$i) {
846 $hash .= '' . $alpha_numeric[rand(0, 35)];
847 }
848 $m = microtime();
849 $len = strlen($m);
850 if ($len > 10) {
851 $len = floor($len / 2);
852 }
853 --$len;
854 for ($i = 0; $i < 3; ++$i) {
855 $h = $m[rand(2, $len)];
856 if ($h === '') {
857 $h = $m[rand(2, ( $len - 1))];
858 }
859 $hash .= $h;
860 }
861 return $hash;
862 }
863
864
865 public static function removeElementIds(array &$data):void {
866 //save sticky/unsticky ids
867 $elementIds=$sticky=array();
868 foreach ($data as $i=>&$r) {
869 if(isset($r['element_id'])){
870 $elementIds[$r['element_id']]=$i;
871 }
872 if(isset($r['styling']['unstick_when_el_row_id'])){
873 $sticky[$i.'r']=$r['styling']['unstick_when_el_row_id'];
874 }
875 if(isset($r['styling']['unstick_when_el_mod_id'])){
876 $sticky[$i.'m']=$r['styling']['unstick_when_el_mod_id'];
877 }
878 unset($r['cid'], $r['element_id']);
879
880 if (!empty($r['cols'])) {
881
882 foreach ($r['cols'] as $j=>&$c) {
883
884 unset($c['cid'], $c['element_id']);
885
886 if (!empty($c['modules'])) {
887
888 foreach ($c['modules'] as $mk=>&$m) {
889 if(isset($m['element_id'])){
890 $elementIds[$m['element_id']]=$i.'-'.$j.'-'.$mk;
891 unset($m['element_id']);
892 }
893 if (isset($m['mod_settings']['cid'])) {
894 unset($m['mod_settings']['cid']);
895 }
896 if(isset($m['mod_settings']['unstick_when_el_row_id'])){
897 $sticky[$i.'-'.$j.'-'.$mk.'r']=$m['mod_settings']['unstick_when_el_row_id'];
898 }
899 if(isset($m['mod_settings']['unstick_when_el_mod_id'])){
900 $sticky[$i.'-'.$j.'-'.$mk.'m']=$m['mod_settings']['unstick_when_el_mod_id'];
901 }
902 if (!empty($m['cols'])) {
903 if(isset($m['styling']['unstick_when_el_row_id'])){
904 $sticky[$i.'-'.$j.'-'.$mk.'r']=$m['styling']['unstick_when_el_row_id'];
905 }
906 if(isset($m['styling']['unstick_when_el_mod_id'])){
907 $sticky[$i.'-'.$j.'-'.$mk.'m']=$m['styling']['unstick_when_el_mod_id'];
908 }
909 foreach ($m['cols'] as $sb=>&$sub_col) {
910
911 unset($sub_col['cid'], $sub_col['element_id']);
912
913 if (!empty($sub_col['modules'])) {
914
915 foreach ($sub_col['modules'] as $sm=>&$sub_m) {
916 if(isset($sub_m['element_id'])){
917 $elementIds[$sub_m['element_id']]=$i.'-'.$j.'-'.$mk.'-'.$sb.'-'.$sm;
918 unset($sub_m['element_id']);
919 }
920 if(isset($sub_m['mod_settings']['unstick_when_el_row_id'])){
921 $sticky[$i.'-'.$j.'-'.$mk.'-'.$sb.'-'.$sm.'r']=$sub_m['mod_settings']['unstick_when_el_row_id'];
922 }
923 if(isset($sub_m['mod_settings']['unstick_when_el_mod_id'])){
924 $sticky[$i.'-'.$j.'-'.$mk.'-'.$sb.'-'.$sm.'m']=$sub_m['mod_settings']['unstick_when_el_mod_id'];
925 }
926 if (isset($sub_m['mod_settings']['cid'])) {
927 unset($sub_m['mod_settings']['cid']);
928 }
929 }
930 }
931 }
932 }
933 }
934 }
935 }
936 }
937 }
938 if(!empty($sticky)){
939 foreach($sticky as $v=>$id){
940 if(isset($elementIds[$id])){
941 $newId=self::generateID();
942 $path1=explode('-',$elementIds[$id]);
943 $key=strpos($v,'r',1)!==false?'unstick_when_el_row_id':'unstick_when_el_mod_id';
944 $path2=explode('-',strtr($v,array('r'=>'','m'=>'')));
945 if(isset($path1[4])){
946 if(!isset($data[$path1[0]]['cols'][$path1[1]]['modules'][$path1[2]]['cols'][$path1[3]]['modules'][$path1[4]]['element_id'])){
947 $data[$path1[0]]['cols'][$path1[1]]['modules'][$path1[2]]['cols'][$path1[3]]['modules'][$path1[4]]['element_id']=$newId;
948 }
949 else{
950 $newId=$data[$path1[0]]['cols'][$path1[1]]['modules'][$path1[2]]['cols'][$path1[3]]['modules'][$path1[4]]['element_id'];
951 }
952 }
953 elseif(isset($path1[1])){
954 if(!isset($data[$path1[0]]['cols'][$path1[1]]['modules'][$path1[2]]['element_id'])){
955 $data[$path1[0]]['cols'][$path1[1]]['modules'][$path1[2]]['element_id']=$newId;
956 }
957 else{
958 $newId=$data[$path1[0]]['cols'][$path1[1]]['modules'][$path1[2]]['element_id'];
959 }
960 }
961 else{
962 if(!isset($data[$path1[0]]['element_id'])){
963 $data[$path1[0]]['element_id']=$newId;
964 }
965 else{
966 $newId=$data[$path1[0]]['element_id'];
967 }
968 }
969
970 if(isset($path2[4])){
971 $data[$path2[0]]['cols'][$path2[1]]['modules'][$path2[2]]['cols'][$path2[3]]['modules'][$path2[4]]['mod_settings'][$key]=$newId;
972 }
973 elseif(isset($path2[1])){
974 $mKey=!empty($data[$path2[0]]['cols'][$path2[1]]['modules'][$path2[2]]['cols'])?'styling':'mod_settings';
975 $data[$path2[0]]['cols'][$path2[1]]['modules'][$path2[2]][$mKey][$key]=$newId;
976 }
977 else{
978 $data[$path2[0]]['styling'][$key]=$newId;
979 }
980 }
981 }
982 }
983 unset($sticky,$elementIds);
984 }
985
986
987 /**
988 * Generate an unique Id for each component if it doesn't have and check unique in the builder
989 */
990 public static function generateElementsIds(array &$data):void {
991 foreach ($data as &$r) {
992 $r['element_id'] = self::checkUniqId((isset($r['element_id']) ? $r['element_id'] : null));
993 unset($r['row_order'],$r['cid']);
994 if (!empty($r['cols'])) {
995 foreach ($r['cols'] as &$c) {
996 $c['element_id'] = self::checkUniqId((isset($c['element_id']) ? $c['element_id'] : null));
997 unset($c['column_order'],$c['cid']);
998 if (!empty($c['modules'])) {
999 foreach ($c['modules'] as &$m) {
1000 if (!is_array($m)) {
1001 continue;
1002 }
1003 $m['element_id'] = self::checkUniqId((isset($m['element_id']) ? $m['element_id'] : null));
1004 unset($m['row_order'],$m['mod_settings']['cid']);
1005 if (!empty($m['cols'])) {
1006 foreach ($m['cols'] as &$sub_col) {
1007 $sub_col['element_id'] = self::checkUniqId((isset($sub_col['element_id']) ? $sub_col['element_id'] : null));
1008 unset($sub_col['column_order'],$sub_col['cid']);
1009 if (!empty($sub_col['modules'])) {
1010 foreach ($sub_col['modules'] as &$sub_m) {
1011 $sub_m['element_id'] = self::checkUniqId((isset($sub_m['element_id']) ? $sub_m['element_id'] : null));
1012 unset($sub_m['mod_settings']['cid']);
1013 }
1014 }
1015 }
1016 }
1017 }
1018 }
1019 }
1020 }
1021 }
1022 }
1023
1024 public static function parseTerms(string $terms, $taxonomy=''):array {
1025 $include_by_id = $exclude_by_id = $include_by_slug = $exclude_by_slug = array();
1026 // deal with how category fields are saved
1027 $terms = \preg_replace('/\|[multiple|single]*$/', '', $terms);
1028
1029 if ( $terms === '0' || $terms === '' ) {
1030 return array();
1031 }
1032
1033 $temp_terms = explode(',', $terms);
1034
1035 foreach ($temp_terms as $t) {
1036 $t = trim($t);
1037 $isNumeric = is_numeric($t);
1038
1039 if ( $isNumeric ) {
1040 /* look for term slugs that are pure numeric (so look like term_id) */
1041 $term_id = term_exists( $t );
1042 if ( $term_id ) {
1043 $t = $term_id;
1044 }
1045 }
1046
1047 $exclude = $t[0] === '-';
1048 if ($isNumeric === false) {
1049 if ($exclude===true) {
1050 $exclude_by_slug[] = ltrim($t, '-');
1051 } else {
1052 $include_by_slug[] = $t;
1053 }
1054 } else {
1055 if ($exclude===true) {
1056 $exclude_by_id[] = ltrim($t, '-');
1057 } else {
1058 $include_by_id[] = $t;
1059 }
1060 }
1061 }
1062 return array_filter(compact('include_by_id', 'exclude_by_id', 'include_by_slug', 'exclude_by_slug'));
1063 }
1064
1065 /**
1066 * Build a taxonomy query from a term string.
1067 *
1068 * @param array $args WP_Query arguments array (modified by reference)
1069 * @param string $term Term string (supports IDs/slugs and exclusions)
1070 * @param string $taxonomy Taxonomy name
1071 * @param bool $reset Whether to reset existing tax_query (default true for backward compatibility)
1072 */
1073 public static function parseTermsQuery(array &$args,string $term,string $taxonomy,bool $reset=true):void {
1074 $terms = self::parseTerms($term);
1075 if (!empty( $terms ) ) {
1076 if ( $reset === true || empty( $args['tax_query'] ) || ! is_array( $args['tax_query'] ) ) {
1077 $args['tax_query'] = array();
1078 }
1079 if (!empty($terms['include_by_id']) && !in_array('0', $terms['include_by_id'])) {
1080 $args['tax_query'][] = array(
1081 array(
1082 'taxonomy' => $taxonomy,
1083 'field' => 'id',
1084 'terms' => $terms['include_by_id']
1085 )
1086 );
1087 }
1088 if (!empty($terms['include_by_slug']) && !in_array('0', $terms['include_by_slug'])) {
1089 $args['tax_query'][] = array(
1090 array(
1091 'taxonomy' => $taxonomy,
1092 'field' => 'slug',
1093 'terms' => $terms['include_by_slug']
1094 )
1095 );
1096 }
1097 if (!empty($terms['exclude_by_id'])) {
1098 $args['tax_query'][] = array(
1099 'taxonomy' => $taxonomy,
1100 'field' => 'id',
1101 'terms' => $terms['exclude_by_id'],
1102 'operator' => 'NOT IN'
1103 );
1104 }
1105 if (!empty($terms['exclude_by_slug'])) {
1106 $args['tax_query'][] = array(
1107 'taxonomy' => $taxonomy,
1108 'field' => 'slug',
1109 'terms' => $terms['exclude_by_slug'],
1110 'operator' => 'NOT IN'
1111 );
1112 }
1113 }
1114 }
1115
1116 /**
1117 * Append an Ajax filter taxonomy clause without rewriting the base tax_query.
1118 */
1119 public static function merge_tax_query_with_ajax_filter( array &$args, string $taxonomy, int $term_id ): void {
1120 if ( $taxonomy === '' || $term_id <= 0 ) {
1121 return;
1122 }
1123 $post_type = isset( $args['post_type'] ) ? $args['post_type'] : 'post';
1124 $new_clause = function_exists( 'themify_build_filter_tax_query' )
1125 ? themify_build_filter_tax_query( $taxonomy, $term_id, $post_type )
1126 : array(
1127 array(
1128 'taxonomy' => $taxonomy,
1129 'field' => 'term_id',
1130 'terms' => $term_id,
1131 'operator' => 'IN',
1132 ),
1133 );
1134 if ( empty( $args['tax_query'] ) || ! is_array( $args['tax_query'] ) ) {
1135 $args['tax_query'] = $new_clause;
1136 return;
1137 }
1138 $args['tax_query'] = array(
1139 'relation' => 'AND',
1140 $args['tax_query'],
1141 $new_clause,
1142 );
1143 }
1144
1145 public static function load_appearance_css(string $data):void {
1146 static $is=null;
1147 if ($is===null && $data !== '' && $data != 'bordered' && $data !== 'circle') {
1148 if(Themify_Builder::$frontedit_active === true){
1149 $is=true;
1150 }
1151 else{
1152 $data=trim($data);
1153 if($data!==''){
1154 $arr=array('glossy','rounded','shadow','gradient','embossed');
1155 foreach($arr as $v){
1156 if(\strpos($data,$v)!==false){
1157 $is=true;
1158 self::loadCssModules('app',THEMIFY_BUILDER_CSS_MODULES . 'appearance.css');
1159 break;
1160 }
1161 }
1162 }
1163 }
1164 }
1165 }
1166
1167 public static function load_color_css(string $color) {
1168 static $is=null;
1169 if ($is===null && $color != '' && $color !== 'tb_default_color' && $color !== 'default' && $color !== 'transparent' && $color !== 'outline' && Themify_Builder::$frontedit_active === false) {
1170 $is=true;
1171 self::loadCssModules('color',THEMIFY_BUILDER_CSS_MODULES.'colors.css');
1172 }
1173 }
1174
1175 public static function load_module_self_style(string $slug, string $css, string $alter_url = '', string $media='all') {
1176 if (Themify_Builder::$frontedit_active === false) {
1177 $key = $slug . '_' . str_replace('/', '_', $css);
1178 if ($alter_url === '') {
1179 $alter_url = THEMIFY_BUILDER_CSS_MODULES . $slug . '_styles/' . $css;
1180 }
1181 self::loadCssModules($key, $alter_url . '.css',THEMIFY_VERSION,$media);
1182 }
1183 }
1184
1185 public static function loadCssModules(string $key, string $url, string $v=THEMIFY_VERSION, string $media = 'all') {
1186 if(!is_admin() || themify_is_ajax()){
1187 $key='tb_' . $key;
1188 \themify_enque_style($key, $url, null, $v, $media);
1189 \Themify_Enqueue_Assets::addLocalization('done', $key, true);
1190 }
1191 }
1192
1193 public static function check_plugins_compatible():void {//check compatible of plugins
1194 if (isset($_GET['page']) && $_GET['page'] === 'themify-license') {
1195 return;
1196 }
1197 if (!function_exists('get_plugins')) {
1198 require_once ABSPATH . 'wp-admin/includes/plugin.php';
1199 }
1200 $plugins = get_plugins();
1201 $plugin_root = WP_PLUGIN_DIR;
1202 $needUpdate = false;
1203 $hasUpdater = $updaterUrl = null;
1204 $_messages = array();
1205 $fw = THEMIFY_VERSION;
1206 $dependceFW = array('announcement-bar', 'themify-audio-dock', 'themify-builder-pro', 'themify-icons', 'themify-shortcodes', 'themify-popup', 'themify-portfolio-post', 'themify-event-post', 'themify-store-locator', 'themify-tiles');
1207 foreach ($plugins as $k => $p) {
1208 if (isset($p['Author']) && $p['Author'] === 'Themify') {
1209 $slug = dirname($k);
1210 if (strpos($slug, 'builder-') === 0 || $slug === 'themify-updater' || in_array($slug, $dependceFW, true)) {
1211 if ($slug === 'themify-updater') {
1212 if ($hasUpdater === null) {
1213 $hasUpdater = is_plugin_active($k);
1214 $updaterUrl = $k;
1215 }
1216 } else {
1217 if (!isset($p['Compatibility'])) {
1218 $data = get_file_data($plugin_root . '/' . $k, array('v' => 'Compatibility'), false);
1219 $v = $p['Compatibility'] = $data['v'];
1220 $needUpdate = true;
1221 } else {
1222 $v = $p['Compatibility'];
1223 }
1224 $up = '';
1225 if($v){
1226 $v=explode(',',$v);
1227 }
1228 if (!$v) { // Compatibility header missing, plugin is older than FW 5.0.0 update
1229 $up = 'plugin';
1230 }
1231 elseif (version_compare(trim($v[0]), $fw, '>') || (!empty($v[1]) && version_compare($fw, trim($v[1]), '>='))){ // plugin requires a higher version of FW
1232 $up = 'theme';
1233 }
1234 if ($up !== '') {
1235 if (!isset($_messages[$up])) {
1236 $_messages[$up] = array();
1237 }
1238 $_messages[$up][] = $p['Name'];
1239 }
1240 }
1241 }
1242 }
1243 }
1244 if ($needUpdate === true) {
1245 wp_cache_set('plugins', $plugins, 'plugins');
1246 }
1247 if ($hasUpdater === false && $updaterUrl !== null && !empty($_GET['tf-activate-updater'])) {
1248 $tab = !empty($_messages['theme']) ? 1 : 2;
1249 $hasUpdater = activate_plugins($updaterUrl, add_query_arg(array('page' => 'themify-license', 'promotion' => $tab), admin_url()));
1250 }
1251 unset($needUpdate, $plugins, $dependceFW);
1252
1253 if (!empty($_messages)) {
1254 foreach ($_messages as $k => $msg):?>
1255 <div class="notice notice-error tf_compatible_erros tf_<?php echo $k ?>_erros">
1256 <p><strong><?php echo $k === 'plugin' ? __('The following plugin(s) are not compatible with the activated theme. Please update your plugins:', 'themify') : __('Please update your activated Themify theme or Builder plugin. The following plugin(s) are incompatible:', 'themify'); ?></strong></p>
1257 <p><?php echo implode(', ', $msg); ?></p>
1258 <p>
1259 <?php if ($hasUpdater === true): ?>
1260 <?php $tab = $k === 'plugin' ? 2 : 1; ?>
1261 <a role="button" class="button button-primary" href="<?php echo esc_url( add_query_arg(array('page' => 'themify-license', 'promotion' => $tab), admin_url()) ) ?>"><?php _e('Update them', 'themify') ?></a>
1262 <?php elseif ($hasUpdater === false): ?>
1263 <?php printf(__('%s', 'themify'), '<a role="button" class="button" href="' . esc_url( add_query_arg(array('tf-activate-updater' => 1)) ) . '">' . __('Activate Themify Updater', 'themify') . '</a>') ?></a>
1264 <?php else: ?>
1265 <?php printf(__('Install %s plugin to auto update them.', 'themify'), '<a href="' . esc_url( add_query_arg(array('page' => 'themify-install-plugins'), admin_url('admin.php')) ) . '">' . __('Themify Updater', 'themify') . '</a>') ?></a>
1266 <?php endif; ?>
1267 </p>
1268 </div>
1269 <?php
1270 endforeach;
1271 }
1272 }
1273
1274 /**
1275 * Checks if Builder is disabled for a given post type
1276 */
1277 public static function is_builder_disabled_for_post_type( string $post_type ):bool {
1278 static $cache =array();
1279 if ( ! isset( $cache[ $post_type ] ) ) {
1280 $cache[ $post_type ] = apply_filters( "tb_is_disabled_for_{$post_type}", null );
1281 if ( !isset($cache[ $post_type ]) ) {
1282 $cache[ $post_type ] = themify_builder_check( 'setting-page_builder_disable_' . $post_type, 'builder_disable_tb_' . $post_type );
1283 }
1284 }
1285
1286 return $cache[ $post_type ];
1287 }
1288
1289 /**
1290 * Parses the settings from a module and applies Advanced Query settings on the $query_arg
1291 *
1292 * @return void
1293 */
1294 public static function parse_query_filter(array $module_settings,array &$query_args ) {
1295 if ( ! empty( $module_settings['query_date_to'] ) ) {
1296 $query_args['date_query']['inclusive'] = true;
1297 $query_args['date_query']['before'] = $module_settings['query_date_to'];
1298 }
1299 if ( ! empty( $module_settings['query_date_from'] ) ) {
1300 $query_args['date_query']['inclusive'] = true;
1301 $query_args['date_query']['after'] = $module_settings['query_date_from'];
1302 }
1303
1304 if ( ! empty( $module_settings['query_authors'] ) ) {
1305 $authors_ids = [];
1306 $authors = \explode( ',', $module_settings['query_authors'] );
1307 foreach ( $authors as $author ) {
1308 $author=trim($author);
1309 if ( is_numeric( $author ) ) {
1310 $authors_ids[] = (int) $author;
1311 }
1312 elseif ( $user = get_user_by( 'login', $author ) ) {
1313 $authors_ids[] = $user->ID;
1314 }
1315 }
1316 if ( ! empty( $authors_ids ) ) {
1317 $query_args['author__in'] = $authors_ids;
1318 }
1319 }
1320
1321 if ( ! empty( $module_settings['query_cf_key'] ) ) {
1322 $meta_query = [
1323 'compare' => empty( $module_settings['query_cf_c'] ) ? 'LIKE' : $module_settings['query_cf_c'],
1324 'key' => $module_settings['query_cf_key']
1325 ];
1326 if ( $meta_query['compare'] !== 'NOT EXISTS' && $meta_query['compare'] !== 'EXISTS' && ! empty( $module_settings['query_cf_value'] ) ) {
1327 $meta_query['value'] = $module_settings['query_cf_value'];
1328 }
1329 if ( isset( $module_settings['query_cf_type'] ) ) {
1330 $meta_query['type'] = $module_settings['query_cf_type'];
1331 }
1332
1333 if ( ! isset( $query_args['meta_query'] ) ) {
1334 $query_args['meta_query'] = [];
1335 }
1336 $query_args['meta_query'][] = $meta_query;
1337 }
1338 }
1339
1340 /**
1341 * Setup Hook Content feature for the module
1342 *
1343 * @return void
1344 */
1345 public static function hook_content_start( array $settings ) {
1346 if (!empty( $settings['hook_content'] ) ) {
1347 foreach ( $settings['hook_content'] as $hook ) {
1348 if (!empty( $hook['c'] ) ) {
1349 if(!isset(self::$hook_contents[ $hook['h'] ])){
1350 self::$hook_contents[ $hook['h'] ]=array();
1351 }
1352 self::$hook_contents[ $hook['h'] ][] = [
1353 $hook['c'], /* content of the hook */
1354 isset( $hook['r'] ) ? (int) $hook['r'] : 0, /* show after every [n] */
1355 0, /* counter */
1356 isset( $hook['m'] ) ? (int) $hook['m'] : 0, /* max repeat times */
1357 ];
1358 add_action( $hook['h'], array( __CLASS__, 'hook_content_output' ) );
1359 }
1360 }
1361 }
1362 }
1363
1364 /**
1365 * Remove hooks added by self::hook_content_start and reset cache
1366 *
1367 * @return void
1368 */
1369 public static function hook_content_end(array $settings ) {
1370 self::$hook_contents = null;
1371 if (!empty( $settings['hook_content'] ) ) {
1372 foreach ( $settings['hook_content'] as $hook ) {
1373 if (!empty( $hook['c'] ) ) {
1374 remove_action( $hook['h'], array( __CLASS__, 'hook_content_output' ) );
1375 }
1376 }
1377 }
1378 }
1379
1380 /**
1381 * Display the contents of a hook added in Post modules
1382 *
1383 * @return void
1384 */
1385 public static function hook_content_output() {
1386 $current_filter = \current_filter();
1387 if ( isset( self::$hook_contents[ $current_filter ] ) ) {
1388 foreach ( self::$hook_contents[ $current_filter ] as $index => &$hook ) {
1389
1390 /* check for hook repeat condition */
1391 if ( $hook[1] !== 0 ) {
1392 $hook[2]++;
1393 if ( $hook[2] % $hook[1] !== 0 || ( $hook[3] !== 0 && $hook[2] / $hook[1] > $hook[3] ) ) {
1394 continue;
1395 }
1396 }
1397
1398 echo '<!-- post hook:' , $current_filter , ' -->' , do_shortcode( $hook[0] ) , '<!-- /post hook:' , $current_filter , ' -->';
1399 }
1400 }
1401 }
1402
1403
1404 /**
1405 * Returns an array containing paths to different assets loade by Builder editor
1406 */
1407 public static function get_layouts():array {
1408 $themeLayoutsPath=get_parent_theme_file_path('builder-layouts/layouts.php');
1409 $arr= array(
1410 // Pre-designed layouts
1411 'predesigned' => array(
1412 'title'=>__( 'Pre-designed', 'themify' ),
1413 'url'=>'https://themify.org/public-api/builder-layouts/'
1414 )
1415 );
1416 if(is_file($themeLayoutsPath)){
1417 $themeLayouts=include $themeLayoutsPath;
1418 $arr['theme']=array(
1419 'title'=>__( 'Theme', 'themify' ),
1420 'data'=>$themeLayouts
1421 );
1422 }
1423 return $arr;
1424 }
1425
1426 public static function getReCaptchaOption(string $name,?string $default=''):string{
1427 if($name==='version'){
1428 $contact_key='recapthca_version';
1429 $builder_key='builder_settings_recaptcha_version';
1430 $tf_name='setting-recaptcha_version';
1431 }
1432 elseif($name==='public_key'){
1433 $contact_key='recapthca_public_key';
1434 $builder_key='builder_settings_recaptcha_site_key';
1435 $tf_name='setting-recaptcha_site_key';
1436 }
1437 elseif($name==='private_key'){
1438 $contact_key='recapthca_private_key';
1439 $builder_key='builder_settings_recaptcha_secret_key';
1440 $tf_name='setting-recaptcha_secret_key';
1441 }
1442 if(isset($tf_name)){
1443 $val=\themify_builder_get($tf_name, $builder_key);
1444 if(!empty($val)){
1445 return $val;
1446 }
1447 }
1448 $options = class_exists('Builder_Contact',false)?get_option('builder_contact'):array();
1449 return $options[$contact_key]??$default??'';
1450 }
1451
1452
1453 public static function get_captcha_keys(?string $provider ):array {
1454 if ( $provider === 'recaptcha' ) {
1455 return [
1456 'public' => self::getReCaptchaOption( 'public_key' ),
1457 'private' => self::getReCaptchaOption( 'private_key' ),
1458 'version' => self::getReCaptchaOption( 'version', 'v2' )
1459 ];
1460 } elseif ( $provider === 'hcaptcha' ) {
1461 return [
1462 'public' => \themify_builder_get( 'setting-hcaptcha_site', 'hcaptcha_site' ),
1463 'private' => \themify_builder_get( 'setting-hcaptcha_secret', 'hcaptcha_secret' )
1464 ];
1465 } else if ( $provider === 'turnstile' ) {
1466 return [
1467 'public' => \themify_builder_get( 'setting-turnstile_site', 'turnstile_site' ),
1468 'private' => \themify_builder_get( 'setting-turnstile_secret', 'turnstile_secret' )
1469 ];
1470 } else if ( $provider === 'honeypot' ) {
1471 return [
1472 'public' => '1',
1473 'private' => '1'
1474 ];
1475 }
1476 }
1477
1478 public static function get_captcha_field(?string $provider, string $before = '',string $after = '' ):string {
1479 if ( $provider === 'honeypot' ) {
1480 $output = '<div class="themify_captcha_field themify_honeypot" aria-hidden="true" style="position:absolute!important;left:-9999px!important;top:auto!important;width:1px!important;height:1px!important;overflow:hidden!important;opacity:0!important;z-index:-1!important;pointer-events:none!important"><label>' . esc_html__( 'Website', 'themify' ) . '<input type="text" name="tb_hp" value="" tabindex="-1" autocomplete="off"></label></div>';
1481 return $before . $output . $after;
1482 }
1483
1484 $keys = self::get_captcha_keys( $provider );
1485 $output = '';
1486 if ( empty( $keys['public'] ) || empty( $keys['private'] ) ) {
1487 if ( current_user_can( 'manage_options' ) ) {
1488 $output = sprintf( __('Requires Captcha keys entered at: <a target="_blank" href="%s">Integration API</a>.', 'themify'), admin_url('admin.php?page=themify#setting-integration-api') );
1489 }
1490 } else {
1491 if ( $provider === 'recaptcha' ) {
1492 $output .= '<div class="themify_captcha_field ' . ( 'v2' === $keys['version'] ? 'g-recaptcha' : '' ) .'" data-sitekey="' . esc_attr($keys['public'] ) . '" data-ver="' . esc_attr( $keys['version'] ) . '"></div>';
1493 } else if ( $provider === 'turnstile' ) {
1494 $output .= '<div class="themify_captcha_field turnstile" data-sitekey="' . esc_attr($keys['public'] ) . '"></div>';
1495 } else if( $provider === 'hcaptcha' ) {
1496 $output .= '<div class="themify_captcha_field h-captcha" data-sitekey="' . esc_attr( $keys['public'] ) . '"></div>';
1497 }
1498 }
1499
1500 return $before . $output . $after;
1501 }
1502
1503 /**
1504 * Validate a captcha form
1505 *
1506 * @return true|WP_Error
1507 */
1508 public static function validate_captcha( $provider, $response ) {
1509 if ( $provider === 'honeypot' ) {
1510 if ( ! empty( $response ) ) {
1511 return new WP_Error( 'captcha_test_fail', __( 'Trouble verifying captcha. Please try again.', 'themify' ) );
1512 }
1513 return true;
1514 }
1515
1516 $keys = self::get_captcha_keys( $provider );
1517 if ( empty( $keys['public'] ) || empty( $keys['private'] ) ) {
1518 return new WP_Error( 'missing_captcha_key', __( 'Captcha API keys are not provided.', 'themify' ) );
1519 }
1520
1521 switch ( $provider ) {
1522 case 'recaptcha' :
1523 $url = 'https://www.google.com/recaptcha/api/siteverify';
1524 break;
1525 case 'hcaptcha' :
1526 $url = 'https://hcaptcha.com/siteverify';
1527 break;
1528 case 'turnstile' :
1529 $url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
1530 break;
1531 }
1532
1533 $result = wp_remote_post( $url, [
1534 'body' => [
1535 'secret' => $keys['private'],
1536 'response' => $response
1537 ]
1538 ] );
1539 if ( is_wp_error( $result ) ) {
1540 return $result;
1541 } else if ( ! isset( $result['body'] ) ) {
1542 return new WP_Error( 'captcha_connection_fail', __( 'Trouble verifying captcha. Please try again.', 'themify' ) );
1543 }
1544 $result = json_decode( $result['body'], true );
1545 if ( ! $result['success'] ) {
1546 return new WP_Error( 'captcha_test_fail', __( 'Trouble verifying captcha. Please try again.', 'themify' ) );
1547 }
1548
1549 return true;
1550 }
1551
1552
1553
1554
1555 //deprecated functions
1556
1557
1558 /**
1559 * Set Pre-built Layout version
1560 */
1561 public static function set_current_layouts_version($version) {//deprecated
1562 $key='tbuilder_layouts_version';
1563 delete_transient($key);
1564 return Themify_Storage::set($key,$version);
1565 }
1566
1567 /**
1568 * Get current Pre-built Layout version
1569 */
1570 public static function get_current_layouts_version() {//deprecated
1571 $key='tbuilder_layouts_version';
1572 delete_transient($key);
1573 $current_layouts_version = Themify_Storage::get($key);
1574 if (false === $current_layouts_version) {
1575 self::set_current_layouts_version('0');
1576 $current_layouts_version = '0';
1577 }
1578 return $current_layouts_version;
1579 }
1580
1581 /**
1582 * Check whether layout is pre-built layout or custom
1583 */
1584 public static function is_prebuilt_layout($id) {//deprecated
1585 $protected = get_post_meta($id, '_themify_builder_prebuilt_layout', true);
1586 return isset($protected) && 'yes' === $protected;
1587 }
1588
1589
1590 public static function get_images_from_gallery_shortcode($shortcode) {//deprecated from 2020.06.02,instead of use themify_get_gallery_shortcode
1591 return themify_get_gallery_shortcode($shortcode);
1592 }
1593
1594
1595 public static function get_icon($icon) {//deprecated
1596 return $icon;
1597 }
1598
1599
1600 public static function register_directory($context, $path) {//deprecated use add_module
1601 if($context==='modules'){
1602 self::add_module($path);
1603 }
1604 }
1605
1606 public static function remove_cache($post_id, $tag = false, array $args = array()) {//deprecated
1607 //TFCache::remove_cache($tag, $post_id, $args);
1608 }
1609
1610
1611 public static function register_module($module_class) {//deprecated
1612 $instance = new $module_class();
1613 self::$modules[$instance->slug] = $instance;
1614 }
1615
1616
1617 public static function hasAccess() {//deprecated
1618 return Themify_Access_Role::check_access_backend();
1619 }
1620
1621 public static function localize_js($object_name, $l10n) {//deprecated
1622 foreach ((array) $l10n as $key => $value) {
1623 if (is_scalar($value)) {
1624 $l10n[$key] = html_entity_decode((string) $value, ENT_QUOTES, 'UTF-8');
1625 }
1626 }
1627 $l10n = apply_filters("tb_localize_js_{$object_name}", $l10n);
1628
1629 return $l10n ? "var $object_name = " . wp_json_encode($l10n) . ';' : '';
1630 }
1631
1632
1633 /**
1634 * Returns list of colors and thumbnails
1635 *
1636 * @return array
1637 */
1638 public static function get_colors() {//deprecated moved to js
1639 return array();
1640 }
1641
1642 /**
1643 * Check whether module is active
1644 * @param $name
1645 * @return boolean
1646 */
1647 public static function check_module_active(string $name):bool {//deprecated use Themify_Builder_Component_Module::load_modules
1648 return isset(self::$modules[$name]);
1649 }
1650 }
1651