PluginProbe
Page Builder: Pagelayer – Drag and Drop website builder / 2.2.1
Page Builder: Pagelayer – Drag and Drop website builder v2.2.1
2.2.1 2.2.0 2.1.9 2.1.8 2.1.7 2.1.6 2.1.5 2.1.4 2.1.3 trunk 0.9.0 0.9.1 0.9.2 0.9.3 0.9.4 0.9.5 0.9.6 0.9.7 0.9.8 0.9.9 1.0.0 1.0.2 1.0.3 1.0.4 1.0.5 All 129 releases
← All changes | main/functions.php +2928 -253 1.0.52.2.1 View file →
@@ -41,20 +41,21 @@
41 41 global $pagelayer;
42 42
43 43 // Just so that we have something
44 44 $ip = _pagelayer_getip();
45 + $method = @$pagelayer->ip_method;
46 +
47 + $pagelayer->ip_method = (int) $method;
45 48
46 - $pagelayer['ip_method'] = (int) @$pagelayer['ip_method'];
47 -
48 49 if(isset($_SERVER["REMOTE_ADDR"])){
49 50 $ip = $_SERVER["REMOTE_ADDR"];
50 51 }
51 52
52 - if(isset($_SERVER["HTTP_X_FORWARDED_FOR"]) && @$pagelayer['ip_method'] == 1){
53 + if(isset($_SERVER["HTTP_X_FORWARDED_FOR"]) && $method == 1){
53 54 $ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
54 55 }
55 56
56 - if(isset($_SERVER["HTTP_CLIENT_IP"]) && @$pagelayer['ip_method'] == 2){
57 + if(isset($_SERVER["HTTP_CLIENT_IP"]) && $method == 2){
57 58 $ip = $_SERVER["HTTP_CLIENT_IP"];
58 59 }
59 60
60 61 // Hacking fix for X-Forwarded-For
@@ -139,9 +140,9 @@
139 140
140 141 // For filling in posted values
141 142 function pagelayer_POSTval($name, $default = ''){
142 143
143 - return (!empty($_POST) ? (!isset($_POST[$name]) ? '' : $_POST[$name]) : $default);
144 + return (!empty($_POST) ? (!isset($_POST[$name]) ? '' : esc_html($_POST[$name])) : $default);
144 145
145 146 }
146 147
147 148 function pagelayer_POSTchecked($name, $default = false){
@@ -149,8 +150,15 @@
149 150 return (!empty($_POST) ? (isset($_POST[$name]) ? 'checked="checked"' : '') : (!empty($default) ? 'checked="checked"' : ''));
150 151
151 152 }
152 153
154 +// For check isset value
155 +function pagelayer_isset($var, $name, $default = ''){
156 +
157 + return isset($var[$name]) ? $var[$name] : $default;
158 +
159 +}
160 +
153 161 function pagelayer_POSTselect($name, $value, $default = false){
154 162
155 163 if(empty($_POST)){
156 164 if(!empty($default)){
@@ -167,19 +175,10 @@
167 175 }
168 176
169 177 function pagelayer_inputsec($string){
170 178
171 - if(!get_magic_quotes_gpc()){
179 + $string = addslashes($string);
172 180
173 - $string = addslashes($string);
174 -
175 - }else{
176 -
177 - $string = stripslashes($string);
178 - $string = addslashes($string);
179 -
180 - }
181 -
182 181 // This is to replace ` which can cause the command to be executed in exec()
183 182 $string = str_replace('`', '\`', $string);
184 183
185 184 return $string;
@@ -221,9 +220,9 @@
221 220 }
222 221 return false;
223 222 }
224 223
225 -// Reoort an error
224 +// Report an error
226 225 function pagelayer_report_error($error = array()){
227 226
228 227 if(empty($error)){
229 228 return true;
@@ -387,8 +386,12 @@
387 386
388 387 if(in_array($type, $pagelayer->settings['post_types'])){
389 388 return true;
390 389 }
390 +
391 + if($type == $pagelayer->builder['name']){
392 + return true;
393 + }
391 394
392 395 return false;
393 396
394 397 }
@@ -393,9 +396,26 @@
393 396
394 397 }
395 398
396 399 function pagelayer_shortlink($id){
397 - $link = wp_get_shortlink($id);
400 +
401 + $post = get_post( $id );
402 + if ( ! empty( $post->ID ) ) {
403 + $post_id = $post->ID;
404 + }
405 +
406 + $post_type = get_post_type_object( $post->post_type );
407 +
408 + if ( 'page' === $post->post_type && get_option( 'page_on_front' ) == $post->ID && 'page' === get_option( 'show_on_front' ) ) {
409 + $link = home_url( '/' );
410 + } elseif ( $post_type->public ) {
411 + $link = home_url( '?p=' . $post_id );
412 + }
413 +
414 + if(function_exists('is_post_status_viewable') && !is_post_status_viewable($post_id)){
415 + $link = get_permalink( $post->ID );
416 + }
417 +
398 418 $link .= substr_count($link, '?') > 0 ? '' : '?';
399 419 return $link;
400 420 }
401 421
@@ -404,19 +424,34 @@
404 424 return pagelayer_shortlink($id).'&pagelayer-live=1';
405 425 }
406 426
407 427 // Are we in live mode ?
408 -function pagelayer_is_live(){
428 +function pagelayer_is_live(&$error = array()){
409 429
410 430 global $post;
411 431
412 432 // Are we seeing the post ?
413 433 if(!isset($post) || !isset($post->ID) || empty($post->ID)){
434 + $error[] = 'Post ID is missing or blank - '.@$post->ID;
414 435 return false;
415 436 }
437 +
438 + $parID = $post->ID;
439 +
440 + // Is revision?
441 + if(wp_is_post_revision($post->ID) ){
442 + $parID = wp_get_post_parent_id($post->ID);
443 + }
444 +
445 + // Are you allowed to edit ?
446 + if(!pagelayer_user_can_edit($parID)){
447 + $error[] = 'You dont have editing rights for this page - '.$parID;
448 + return false;
449 + }
416 450
417 451 // Is it the live mode ?
418 452 if(pagelayer_optreq('pagelayer-live')){
453 + $error[] = 'pagelayer-live is missing';
419 454 return true;
420 455 }
421 456
422 457 return false;
@@ -423,12 +458,12 @@
423 458
424 459 }
425 460
426 461 // Are we in live IFRAME mode ?
427 -function pagelayer_is_live_iframe(){
462 +function pagelayer_is_live_iframe(&$error = array()){
428 463
429 464 // Are we seeing the post ?
430 - if(!pagelayer_is_live()){
465 + if(!pagelayer_is_live($error)){
431 466 return false;
432 467 }
433 468
434 469 // Is it the live mode ?
@@ -434,29 +469,95 @@
434 469 // Is it the live mode ?
435 470 if(pagelayer_optreq('pagelayer-iframe')){
436 471 return true;
437 472 }
473 +
474 + $error[] = 'pagelayer-iframe missing in GET';
438 475
439 476 return false;
440 477
441 478 }
442 479
480 +// Are we editing a live template
481 +function pagelayer_is_live_template($post = []){
482 +
483 + // Are we seeing the post ?
484 + if(!pagelayer_is_live()){
485 + return false;
486 + }
487 +
488 + if(!$post){
489 + $post = $GLOBALS['post'];
490 + }
491 +
492 + if($post->post_type == 'pagelayer-template'){
493 + return true;
494 + }
495 +
496 + return false;
497 +
498 +}
499 +
500 +function pagelayer_has_blocks($post = null) {
501 + if ( ! has_blocks( $post ) ) {
502 + return false;
503 + }
504 +
505 + if ( ! is_string( $post ) ) {
506 + $wp_post = get_post( $post );
507 + if ( $wp_post instanceof WP_Post ) {
508 + $post = $wp_post->post_content;
509 + }
510 + }
511 +
512 + return false !== strpos( $post, '<!-- wp:pagelayer/' );
513 +}
514 +
443 515 // Can the current user edit the post ?
444 516 function pagelayer_user_can_edit($post = NULL){
445 517
446 - global $wp_the_query, $current_user;
518 + global $wp_the_query, $current_user, $pagelayer;
519 +
520 + $post = get_post($post);
447 521
448 - if(!isset($post) || empty($post) || $post === NULL){
449 - global $post;
522 + if(empty($post)){
523 + return false;
450 524 }
451 525
452 - wp_get_current_user();
453 -
454 - if(isset($post) && is_object($post) && isset($post->ID) && isset($post->post_author) && isset($current_user) && is_object($current_user) && isset($current_user->ID) && (current_user_can('edit_others_posts', $post->ID) || ($post->post_author == $current_user->ID))){
455 - return true;
526 + // No trash editing
527 + if(get_post_status($post->ID) === 'trash'){
528 + return false;
456 529 }
530 +
531 + // Is pagelayer supposed to edit this ?
532 + if(!pagelayer_supported_type($post->post_type)){
533 + return false;
534 + }
535 +
536 + // Get the post type object
537 + $object = get_post_type_object($post->post_type);
538 +
539 + // Is this type editable by the user ?
540 + if(!current_user_can($object->cap->edit_posts)){
541 + return false;
542 + }
543 +
544 + // Is this type editable ?
545 + if(!isset($object->cap->edit_post)){
546 + return false;
547 + }
548 +
549 + // Can this user edit the post type ?
550 + if(!current_user_can($object->cap->edit_post, $post->ID)){
551 + return false;
552 + }
553 +
554 + // Page for blogs not allowed
555 + if(get_option('page_for_posts') === $post->ID){
556 + return false;
557 + }
457 558
458 - return false;
559 + return true;
459 560
460 561 }
461 562
462 563 // Language sting function
@@ -476,17 +577,81 @@
476 577 function pagelayer_icon_sources(){
477 578 return array();
478 579 }
479 580
581 +// Sets the memory limit
582 +function pagelayer_memory_limit($mb){
583 +
584 + $bytes = ($mb * 1024 * 1024);
585 + $mb_str = (string) $mb.'M';
586 +
587 + // Some servers might have ini_get disabled
588 + if(function_exists('ini_get')){
589 + $memory_limit = @ini_get('memory_limit');
590 + }
591 +
592 + if(empty($memory_limit)){
593 + return;
594 + }
595 +
596 + $memory_limit_bytes = (strpos($memory_limit, 'M') ? (intval($memory_limit) * 1024 * 1024) : intval($memory_limit));
597 +
598 + //$memory_limit_bytes > 0 is for memory limit = -1
599 + if($memory_limit_bytes <= $bytes && $memory_limit_bytes > 0){
600 +
601 + // Some servers might have ini_set disabled
602 + if(function_exists('ini_set')){
603 + @ini_set('memory_limit', $mb_str);
604 + }
605 + }
606 +
607 +}
608 +
609 +// Pagelayer the content
610 +function pagelayer_the_content($content, $dump = false){
611 + global $pagelayer;
612 +
613 + $content = pagelayer_sanitize_content( $content );
614 + $content = do_blocks( $content );
615 + $content = do_shortcode( $content );
616 +
617 + if($dump){
618 + preg_match_all('/<div ([^>]*)pagelayer-id="([^"]*)"([^>]*)>/', $content, $matches);
619 +
620 + foreach($matches[0] as $k => $div){
621 + $id = $matches[2][$k];
622 +
623 + if(empty($pagelayer->data_attr[$id])){
624 + continue;
625 + }
626 +
627 + $data_attr = '<!-- '.pagelayer_serialize_attributes($pagelayer->data_attr[$id]).' -->';
628 + $content = str_replace($div, $div.$data_attr, $content);
629 + }
630 + }
631 +
632 + return $content;
633 +}
634 +
635 +function pagelayer_create_id(){
636 + return pagelayer_RandomString(3).rand(1000, 9999);
637 +}
638 +
480 639 // Loads the shortcodes
481 640 function pagelayer_load_shortcodes(){
482 -
483 - include_once(PAGELAYER_DIR.'/main/shortcode_functions.php');
484 - if(defined('PAGELAYER_PREMIUM')){
485 - include_once(PAGELAYER_DIR.'/main/premium_functions.php');
641 + global $pagelayer, $post;
642 +
643 + if(!empty($pagelayer->shortcode_loaded)){
644 + return;
486 645 }
487 - include_once(PAGELAYER_DIR.'/main/shortcodes.php');
488 -
646 +
647 + pagelayer_memory_limit(128);
648 +
649 + // We have loaded
650 + $pagelayer->shortcode_loaded = 1;
651 +
652 + do_action('pagelayer_before_load_shortcodes');
653 +
489 654 // pQuery
490 655 include_once(PAGELAYER_DIR.'/lib/pquery/IQuery.php');
491 656 include_once(PAGELAYER_DIR.'/lib/pquery/gan_formatter.php');
492 657 include_once(PAGELAYER_DIR.'/lib/pquery/gan_node_html.php');
@@ -494,35 +659,80 @@
494 659 include_once(PAGELAYER_DIR.'/lib/pquery/gan_parser_html.php');
495 660 include_once(PAGELAYER_DIR.'/lib/pquery/gan_selector_html.php');
496 661 include_once(PAGELAYER_DIR.'/lib/pquery/gan_xml2array.php');
497 662 include_once(PAGELAYER_DIR.'/lib/pquery/pQuery.php');
663 +
664 + include_once(PAGELAYER_DIR.'/main/shortcode_functions.php');
498 665
666 + // Apply filter to load custom widgets functions
667 + do_action('pagelayer_load_shortcode_functions');
668 +
669 + include_once(PAGELAYER_DIR.'/main/shortcodes.php');
670 +
499 671 // Apply filter to load custom widgets
500 672 do_action('pagelayer_load_custom_widgets');
673 +
674 + // Render Pagelayer element by blocks
675 + add_action('pre_render_block', 'pagelayer_render_blocks', 10, 2);
676 +
677 + // Add global widget data
678 + if(defined('PAGELAYER_PREMIUM') && !pagelayer_is_gutenberg_editor()){
679 +
680 + // Get global widget templates id by type
681 + $args = [
682 + 'post_type' => $pagelayer->builder['name'],
683 + 'status' => 'publish',
684 + 'meta_key' => 'pagelayer_template_type',
685 + 'meta_value' => array('global_widget', 'section', 'global_section'),
686 + 'posts_per_page' => -1
687 + ];
688 +
689 + $query = new WP_Query($args);
690 +
691 + $tmp_list = [];
692 + $global_widgets = array();
693 + $global_widgets['global_widget'] = array();
694 + $global_widgets['section'] = array();
695 + $global_widgets['global_section'] = array();
696 +
697 + foreach($query->posts as $template){
698 +
699 + // The type
700 + $pagelayer_template_type = get_post_meta($template->ID, 'pagelayer_template_type', true);
701 +
702 + $global_data = [];
703 + $global_data['post_id'] = $template->ID;
704 + $global_data['title'] = $template->post_title;
705 + $global_data['$'] = pagelayer_the_content($template->post_content, true);
706 + $global_widgets[$pagelayer_template_type][$template->ID] = $global_data;
501 707
708 + }
709 +
710 + $pagelayer->global_widgets = $global_widgets['global_widget'];
711 + $pagelayer->saved_sections = $global_widgets['section'];
712 + $pagelayer->global_sections = $global_widgets['global_section'];
713 + }
714 +
715 + do_action('pagelayer_after_load_shortcodes');
502 716 }
503 717
504 718 // Add the shortcodes to the pagelayer list
505 719 function pagelayer_add_shortcode($tag, $params = array()){
506 720
507 - global $pagelayer;
508 -
509 - // Is there a handler function ?
510 - if(!empty($params['func'])){
721 + global $pagelayer, $post;
511 722
512 - if($tag == 'pl_row'){
513 - $inner_tag = 'pl_inner_row';
514 - add_shortcode($inner_tag, 'pagelayer_render_shortcode');
515 - }
516 -
517 - if($tag == 'pl_col'){
518 - $inner_tag = 'pl_inner_col';
519 - add_shortcode($inner_tag, 'pagelayer_render_shortcode');
520 - }
521 -
522 - add_shortcode($tag, 'pagelayer_render_shortcode');//$params['func']);
523 - //unset($params['func']);
723 + if($tag == 'pl_row'){
724 + $inner_tag = 'pl_inner_row';
725 + add_shortcode($inner_tag, 'pagelayer_render_shortcode');
524 726 }
727 +
728 + if($tag == 'pl_col'){
729 + $inner_tag = 'pl_inner_col';
730 + add_shortcode($inner_tag, 'pagelayer_render_shortcode');
731 + }
732 +
733 + add_shortcode($tag, 'pagelayer_render_shortcode');//$params['func']);
734 + //unset($params['func']);
525 735
526 736 // Is there a group ?
527 737 if(empty($params['group'])){
528 738 $params['group'] = 'misc';
@@ -532,11 +742,14 @@
532 742 $params['options'] = [
533 743 'ele_bg_styles' => __pl('ele_bg_styles'),
534 744 'ele_styles' => __pl('ele_styles'),
535 745 'border_styles' => __pl('border_styles'),
746 + 'font_style' => __pl('font_style'),
536 747 'position_styles' => __pl('position_styles'),
537 748 'animation_styles' => __pl('animation_styles'),
749 + 'motion_effects' => __pl('Motion Effects'),
538 750 'responsive_styles' => __pl('responsive_styles'),
751 + 'attributes' => __pl('attributes'),
539 752 'custom_styles' => __pl('custom_styles'),
540 753 ];
541 754
542 755 if(!empty($params['skip_props_cat'])){
@@ -575,22 +788,129 @@
575 788 }
576 789 }
577 790 }
578 791 //die();*/
579 -
792 +
793 + $params = apply_filters( 'pagelayer_shortcode_params', $params, $tag );
794 +
580 795 // Insert the shortcode
581 796 $pagelayer->shortcodes[$tag] = $params;
582 797 $pagelayer->groups[$params['group']][] = $tag;
798 +
799 + // Export the default values
800 + foreach($pagelayer->tabs as $tab){
801 +
802 + if(empty($pagelayer->shortcodes[$tag][$tab])){
803 + continue;
804 + }
805 +
806 + foreach($pagelayer->shortcodes[$tag][$tab] as $section => $Lsection){
807 +
808 + $props = empty($pagelayer->shortcodes[$tag][$section]) ? @$pagelayer->styles[$section] : @$pagelayer->shortcodes[$tag][$section];
809 +
810 + //echo $tab.' - '.$section.' - <br>';
811 +
812 + if(empty($props)){
813 + continue;
814 + }
815 +
816 + // Save customizer params
817 + if( $tag == 'pl_customizer' ){
818 + $pagelayer->customizer_params = array_merge($pagelayer->customizer_params, $props);
819 + }
820 +
821 + foreach($props as $prop => $param){
822 +
823 + // Set default values to export for JS
824 + if(isset($param['export-def']) && isset($param['default']) && $param['export-def'] == 1){
825 + $pagelayer->default_params[$tag][$prop] = $param['default'];
826 + }
827 +
828 + }
829 + }
830 + }
831 +}
583 832
833 +// Add a freemium shortcode i.e. available for render, but not to drag or edit
834 +function pagelayer_freemium_shortcode($tag, $params = array()){
835 +
836 + // If we are the free version, we just allow render and some edits
837 + if(!defined('PAGELAYER_PREMIUM')){
838 +
839 + $params['not_visible'] = 1;
840 + $params['freemium'] = 1;
841 +
842 + $cats = empty($params['styles']) ? array() : $params['styles'];
843 +
844 + if(!empty($params['settings'])){
845 + $cats = array_merge($cats, $params['settings']);
846 + }
847 +
848 + $cats['params'] = $params['name'];
849 + //pagelayer_print($cats);
850 +
851 + foreach($cats as $k => $v){
852 + if(empty($params[$k])) continue;
853 +
854 + foreach($params[$k] as $kk => $vv){
855 +
856 + if(empty($params[$k][$kk]['np'])){
857 + $params[$k][$kk]['pro'] = 1;
858 + }
859 +
860 + }
861 +
862 + }
863 +
864 + }
865 +
866 + return pagelayer_add_shortcode($tag, $params);
584 867 }
585 868
869 +// Returns the permalink values
870 +function pagelayer_permalink($id){
871 +
872 + if(is_numeric($id)){
873 + $id = (int) @$id;
874 + $perma = get_permalink($id);
875 +
876 + if(!empty($perma)){
877 + $id = $perma;
878 + }
879 + }
880 +
881 + $id = apply_filters('pagelayer_permalink', $id);
882 +
883 + return $id;
884 +}
885 +
586 886 // Returns the Image values
587 -function pagelayer_image($id){
887 +function pagelayer_image($id = 0){
588 888
589 889 global $pagelayer;
590 890
591 891 $ret = [];
592 892
893 + if(!empty($id) && is_array($id)){
894 +
895 + foreach($id as $key => $image){
896 + $attachment = pagelayer_image(@$image);
897 + if(!empty($attachment)){
898 + foreach($attachment as $k => $v){
899 + if($key == 'retina'){
900 + $ret['retina-'.$k] = $v;
901 + }else if($key == 'retina_mobile'){
902 + $ret['retina-mobile-'.$k] = $v;
903 + }else{
904 + $ret[$k] = $v;
905 + }
906 + }
907 + }
908 + }
909 +
910 + return $ret;
911 + }
912 +
593 913 // External image ?
594 914 if(pagelayer_is_external_img($id)){
595 915
596 916 $ret['url'] = $id;
@@ -622,18 +942,13 @@
622 942 $title = esc_attr($image->post_title);
623 943 $alt = get_post_meta($id, '_wp_attachment_image_alt', true);
624 944 $alt = empty($alt) ? $image->post_excerpt : $alt;
625 945 $alt = empty($alt) ? $image->post_title : $alt;
626 - $alt = empty($alt) ? '' : trim(strip_tags($alt));
946 + $alt = empty($alt) ? '' : esc_attr(trim(strip_tags($alt)));
627 947 $link = get_attachment_link($id);
628 948 $caption = wp_get_attachment_caption($id);
629 - $caption = !empty($caption) ? $caption : '';
949 + $caption = !empty($caption) ? esc_attr($caption) : '';
630 950
631 - $ret['alt'] = $alt;
632 - $ret['title'] = $title;
633 - $ret['link'] = $link;
634 - $ret['caption'] = $caption;
635 -
636 951 }
637 952
638 953 }
639 954
@@ -645,10 +960,25 @@
645 960 // No image
646 961 if(empty($ret['url'])){
647 962 $ret['url'] = PAGELAYER_URL.'/images/default-image.png';
648 963 }
964 +
965 + // Extract filename without extension
966 + $filename = pathinfo($ret['url'], PATHINFO_FILENAME);
649 967
968 + // Skip title if same as filename
969 + $title = (!empty($title) && $title != $filename) ? $title : '';
970 +
971 + $ret['alt'] = @$alt;
972 + $ret['title'] = @$title;
973 + $ret['link'] = @$link;
974 + $ret['caption'] = @$caption;
975 +
650 976 $ret = apply_filters('pagelayer_image', $ret);
977 +
978 + if(pagelayer_is_default_img($ret['url'])){
979 + $ret['no-image-set'] = 1;
980 + }
651 981
652 982 return $ret;
653 983
654 984 }
@@ -667,8 +997,19 @@
667 997 return false;
668 998
669 999 }
670 1000
1001 +// Checks if the given parameter is the default image
1002 +function pagelayer_is_default_img($img){
1003 +
1004 + if($img == PAGELAYER_URL.'/images/default-image.png'){
1005 + return true;
1006 + }
1007 +
1008 + return false;
1009 +
1010 +}
1011 +
671 1012 // Returns the attachment url
672 1013 function pagelayer_attachment($id){
673 1014
674 1015 $ret = [];
@@ -697,10 +1038,12 @@
697 1038 }
698 1039
699 1040 // Convert the regular URL of a Video to a Embed URL
700 1041 // Todo : Check
701 -function pagelayer_video_url($source){
1042 +function pagelayer_video_url($source, $no_url = false){
702 1043
1044 + global $pagelayer;
1045 +
703 1046 if (!empty($source)) {
704 1047
705 1048 $source = esc_url( $source );
706 1049 $source = str_replace('&amp;', '&', $source);
@@ -705,8 +1048,10 @@
705 1048 $source = esc_url( $source );
706 1049 $source = str_replace('&amp;', '&', $source);
707 1050 $url = parse_url($source);
708 1051 $videoSite ='';
1052 + $videoId ='';
1053 + $vid_atts = [];
709 1054
710 1055 $youtubeRegExp = '/youtube\.com|youtu\.be/is';
711 1056 $vimeoRegExp = '/vimeo\.com/is';
712 1057
@@ -718,8 +1063,10 @@
718 1063
719 1064 switch ($videoSite) {
720 1065 case 'youtube':
721 1066
1067 + $pagelayer->append_yt_api = true;
1068 +
722 1069 if (preg_match('/youtube\.com/is', $source)) {
723 1070
724 1071 if (preg_match('/watch/is', $source)) {
725 1072 parse_str($url['query'], $parameters);
@@ -742,38 +1089,44 @@
742 1089 $videoId = $path[1];
743 1090 }
744 1091
745 1092 }
1093 +
1094 + $vid_atts['type'] = 'youtube';
1095 + $vid_atts['src'] = '//www.youtube.com/embed/'.$videoId;
1096 + $vid_atts['id'] = $videoId;
746 1097
747 - return '//youtube.com/embed/'.$videoId;
748 -
749 1098 break;
750 1099 case 'vimeo':
751 1100
752 1101 if (preg_match('/player\.vimeo\.com/is', $url['host']) && preg_match('/video/is', $url['path'])) {
753 - $path = explode('/', $url['path']);
754 -
755 - if (isset($path[2]) && !empty($path[2])) {
756 - $videoId = $path[2];
757 - }
758 -
1102 + $path = explode('video/', $source);
759 1103 } else if (preg_match('/vimeo\.com/is', $url['host'])) {
760 - $path = explode('/', $url['path']);
761 -
762 - if (isset($path[1]) && !empty($path[1])) {
763 - $videoId = $path[1];
764 - }
765 -
1104 + $path = explode('.com/', $source);
766 1105 }
1106 +
1107 + if(isset($path[1]) && !empty($path[1])) {
1108 + $videoId = $path[1];
1109 + }
1110 +
1111 + $vid_atts['type'] = 'vimeo';
1112 + $vid_atts['src'] = '//player.vimeo.com/video/'.$videoId;
1113 + $vid_atts['id'] = $videoId;
767 1114
768 - return '//player.vimeo.com/video/'.$videoId;
769 -
770 1115 break;
771 1116 default:
1117 +
1118 + $vid_atts['type'] = 'local';
1119 + $vid_atts['src'] = $source;
1120 + $vid_atts['id'] = $videoId;
772 1121
773 - return $source;
774 -
775 1122 }
1123 +
1124 + if(!$no_url){
1125 + return $vid_atts['src'];
1126 + }
1127 +
1128 + return $vid_atts;
776 1129
777 1130 }
778 1131 }
779 1132
@@ -780,8 +1133,12 @@
780 1133
781 1134 // As per the JS specification
782 1135 function pagelayer_escapeHTML($str){
783 1136
1137 + if(is_null($str)){
1138 + return '';
1139 + }
1140 +
784 1141 $replace = [
785 1142 ']' => '&#93;',
786 1143 '[' => '&#91;',
787 1144 //'=' => '&#61;',
@@ -788,9 +1145,10 @@
788 1145 '<' => '&lt;',
789 1146 '>' => '&gt;',
790 1147 '"' => '&quot;',
791 1148 //'&' => '&amp;',
792 - '\'' => '&#39;'
1149 + '\'' => '&#39;',
1150 + '\\' => '&#92;'
793 1151 ];
794 1152
795 1153 $str = str_replace(array_keys($replace), array_values($replace), $str);
796 1154
@@ -798,8 +1156,13 @@
798 1156 }
799 1157
800 1158 // As per the JS specification
801 1159 function pagelayer_unescapeHTML($str){
1160 +
1161 + if(is_null($str)){
1162 + return '';
1163 + }
1164 +
802 1165 $replace = [
803 1166 '#93' => ']',
804 1167 '#91' => '[',
805 1168 //'#61' => '=',
@@ -806,9 +1169,10 @@
806 1169 'lt' => '<',
807 1170 'gt' => '>',
808 1171 'quot' => '"',
809 1172 //'amp' => '&',
810 - '#39' => '\''
1173 + '#39' => '\'',
1174 + '#92' => '\\'
811 1175 ];
812 1176
813 1177 foreach($replace as $k => $v){
814 1178 $str = str_replace('&'.$k.';', $v, $str);
@@ -815,8 +1179,693 @@
815 1179 }
816 1180 return $str;
817 1181 }
818 1182
1183 +// Converts a Unicode code point to its UTF-8 encoded string.
1184 +function pagelayer_codepoint_to_utf8($num) {
1185 + if ($num < 0x80) {
1186 + return chr($num);
1187 + } elseif ($num < 0x800) {
1188 + return chr(0xC0 | ($num >> 6))
1189 + . chr(0x80 | ($num & 0x3F));
1190 + } elseif ($num < 0x10000) {
1191 + return chr(0xE0 | ($num >> 12))
1192 + . chr(0x80 | (($num >> 6) & 0x3F))
1193 + . chr(0x80 | ($num & 0x3F));
1194 + } elseif ($num < 0x110000) {
1195 + return chr(0xF0 | ($num >> 18))
1196 + . chr(0x80 | (($num >> 12) & 0x3F))
1197 + . chr(0x80 | (($num >> 6) & 0x3F))
1198 + . chr(0x80 | ($num & 0x3F));
1199 + }
1200 + return '';
1201 +}
1202 +
1203 +// To make decode entities faster
1204 +function pagelayer_optimized_decode_entities($string, $req = true) {
1205 +
1206 + // Fast replace common HTML entities
1207 + $common_entities_map = [
1208 + '&#93;' => ']', '&#91;' => '[', '&lt;' => '<', '&gt;' => '>', '&amp;' => '&', '&quot;' => '"', '&#39;' => "'",
1209 + '&copy;' => '©', '&reg;' => '®', '&ndash;' => '–', '&mdash;' => '—', '&bull;' => '•',
1210 + '&hellip;' => '…', '&lsquo;' => '‘', '&rsquo;' => '’', '&ldquo;' => '“', '&rdquo;' => '”'
1211 + ];
1212 +
1213 + // Replace common entities first for performance
1214 + $string = str_replace(array_keys($common_entities_map), array_values($common_entities_map), $string);
1215 +
1216 + // Return early if no encoded entities exist
1217 + if(!preg_match('/\\\\u[0-9a-fA-F]{4}|&#x[0-9a-fA-F]+;|&#\d+;/', $string)) {
1218 + return $string;
1219 + }
1220 +
1221 + $string = preg_replace_callback(
1222 + '/\\\\u([0-9a-fA-F]{4})|&#x([0-9a-fA-F]+);|&#([0-9]+);/',
1223 + function ($matches) {
1224 + if (!empty($matches[1])) {
1225 + // Decode \uXXXX Unicode sequences
1226 + return pagelayer_codepoint_to_utf8( hexdec($matches[1]) );
1227 + }elseif (!empty($matches[2])) {
1228 + // Decode hexadecimal HTML entities (&#x6A; → j)
1229 + return pagelayer_codepoint_to_utf8( hexdec($matches[2]) );
1230 + } elseif (!empty($matches[3])) {
1231 + // Decode decimal HTML entities (&#106; → j)
1232 + return pagelayer_codepoint_to_utf8( (int)$matches[3] );
1233 + }
1234 + return $matches[0];
1235 + },
1236 + $string
1237 + );
1238 +
1239 + // Additional decoding to cover remaining cases
1240 + if($req){
1241 + $string = pagelayer_optimized_decode_entities($string, false);
1242 + }
1243 +
1244 + return $string;
1245 +}
1246 +
1247 +// Return true if user can add js content
1248 +function pagelayer_user_can_add_js_content(){
1249 +
1250 + // Unfiltered_html cap needs to be checked for multisite
1251 + if(current_user_can('unfiltered_html')){
1252 + return true;
1253 + }
1254 +
1255 + $pagelayer_js_permission = get_option('pagelayer_js_permission');
1256 + $current_user = wp_get_current_user();
1257 +
1258 + // If not allowed any role by admin
1259 + if(empty($pagelayer_js_permission) || empty($current_user->roles)){
1260 + return false;
1261 + }
1262 +
1263 + foreach($current_user->roles as $role){
1264 + if(in_array($role, $pagelayer_js_permission)){
1265 + return true;
1266 + }
1267 + }
1268 +
1269 + return false;
1270 +}
1271 +
1272 +// Check for XSS codes in our shortcodes submitted
1273 +function pagelayer_xss_content($data){
1274 +
1275 + // Keep a whitespace-preserved copy for the on* event-handler scan.
1276 + $orig = $data;
1277 +
1278 + $data = pagelayer_optimized_decode_entities($data);
1279 +
1280 + // Collapse all whitespace for pattern matching
1281 + $data = preg_split('/\s/', $data);
1282 + $data = implode('', $data);
1283 + //echo $data;
1284 +
1285 + if(preg_match('/["\']javascript\:/is', $data)){
1286 + return 'javascript';
1287 + }
1288 +
1289 + if(preg_match('/["\']vbscript\:/is', $data)){
1290 + return 'vbscript';
1291 + }
1292 +
1293 + if(preg_match('/\-moz\-binding\:/is', $data)){
1294 + return '-moz-binding';
1295 + }
1296 +
1297 + if(preg_match('/expression\(/is', $data)){
1298 + return 'expression';
1299 + }
1300 +
1301 + if(preg_match('/\<(iframe|frame|script|style|link|applet|embed|xml|svg|object|layer|ilayer|meta)/is', $data, $matches)){
1302 + return $matches[1];
1303 + }
1304 +
1305 + // Reject ANY on* event handler attribute. Per the HTML spec every attribute
1306 + // name beginning with "on" (optionally followed by a letter and word chars)
1307 + if(preg_match('/\bon(?:[a-z0-9-]*)?\s*=/i', $orig, $matches)){
1308 + return $matches[0];
1309 + }
1310 +
1311 + return;
1312 +
1313 +}
1314 +
1315 +// Check for XSS codes in our blocks array
1316 +function pagelayer_sanitize_blocks_save_pre($block){
1317 +
1318 + foreach($block as $k => $v){
1319 +
1320 + // Recurse on arrays
1321 + if(is_array($v)){
1322 + $block[$k] = pagelayer_sanitize_blocks_save_pre($v);
1323 +
1324 + // We dont support objects !
1325 + }elseif(is_object($v)){
1326 + $block[$k] = null;
1327 + // Strings
1328 + }else{
1329 +
1330 + if(is_string($v)){
1331 +
1332 + $v = wp_filter_post_kses($v);
1333 +
1334 + while(true){
1335 + $str = '"'.($v);
1336 + $found = pagelayer_xss_content($str);
1337 + //echo (string)$v.'--'.$found."\n";
1338 +
1339 + if(strlen($found) > 0){
1340 + // There is htmlentities?
1341 + if(strpos($v, $found) === false){
1342 + $v = '';
1343 + break;
1344 + }
1345 + $v = str_replace($found, '', $v);
1346 + }else{
1347 + break;
1348 + }
1349 +
1350 + }
1351 +
1352 + }
1353 +
1354 + $block[$k] = $v;
1355 + }
1356 +
1357 + }
1358 +
1359 + return $block;
1360 +}
1361 +
1362 +// Walk a parsed block tree and sanitize every Pagelayer block in it, at any depth
1363 +function pagelayer_sanitize_block_tree($block){
1364 +
1365 + $block_name = isset($block['blockName']) ? $block['blockName'] : '';
1366 +
1367 + // Is pagelayer block ? pagelayer_sanitize_blocks_save_pre() walks the
1368 + // whole subtree, so we are done for this branch
1369 + if(is_string($block_name) && 0 === strpos($block_name, 'pagelayer/')){
1370 + return pagelayer_sanitize_blocks_save_pre($block);
1371 + }
1372 +
1373 + // Any other block can still hold Pagelayer blocks inside it
1374 + if(!empty($block['innerBlocks']) && is_array($block['innerBlocks'])){
1375 + foreach($block['innerBlocks'] as $k => $inner){
1376 + $block['innerBlocks'][$k] = pagelayer_sanitize_block_tree($inner);
1377 + }
1378 + }
1379 +
1380 + return $block;
1381 +}
1382 +
1383 +// Check for XSS codes in our shortcode attributes
1384 +function pagelayer_sanitize_shortcode_atts($content){
1385 +
1386 + // Do we have something suspicious ?
1387 + $tmp_check = pagelayer_xss_content($content);
1388 + if(empty($tmp_check)){
1389 + return $content;
1390 + }
1391 +
1392 + pagelayer_load_shortcodes();
1393 +
1394 + preg_match_all( '/' . get_shortcode_regex() . '/', $content, $matches, PREG_SET_ORDER );
1395 +
1396 + $prefixes = ['pl_'];
1397 +
1398 + $prefixes = apply_filters( 'pagelayer_valid_shortcode_tag', $prefixes);
1399 +
1400 + foreach ($matches as $shortcode) {
1401 +
1402 + $shortcode_name = $shortcode[2];
1403 +
1404 + $vailid = false;
1405 +
1406 + foreach($prefixes as $prefix) {
1407 + if (strpos($shortcode_name, $prefix) === 0) {
1408 + $vailid = true;
1409 + break;
1410 + }
1411 + }
1412 +
1413 + if(!$vailid){
1414 + continue;
1415 + }
1416 +
1417 + $attrs = shortcode_parse_atts( $shortcode[3] );
1418 + $atts = ' ';
1419 +
1420 + foreach($attrs as $key => $value){
1421 +
1422 + // Skip if key contains XSS
1423 + if (!is_numeric($key) && strlen(pagelayer_xss_content($key . '=')) > 0) continue;
1424 +
1425 + $value = wp_filter_post_kses($value);
1426 +
1427 + // Skip if value contains XSS
1428 + if (strlen(pagelayer_xss_content('"' . $value)) > 0) continue;
1429 +
1430 + $atts .= is_numeric($key) ? $value . ' ' : $key . '="' . $value . '" ';
1431 +
1432 + }
1433 +
1434 + $new_shortcode = '[' . $shortcode_name . $atts . ']';
1435 +
1436 + if(!empty($shortcode[5])){
1437 + // Recurse into the inner content so nested shortcodes
1438 + $inner = pagelayer_sanitize_shortcode_atts($shortcode[5]);
1439 + $new_shortcode .= $inner.'[/' . $shortcode_name .']';
1440 + }
1441 +
1442 + // Replace the original shortcode with sanitized attributes
1443 + $content = str_replace($shortcode[0], $new_shortcode, $content);
1444 + }
1445 +
1446 + return $content;
1447 +}
1448 +
1449 +// Scan post content for XSS payloads and return a structured report.
1450 +function pagelayer_xss_scan_post($post_id = 0){
1451 + global $post;
1452 +
1453 + $report = ['found' => false, 'items' => []];
1454 +
1455 + if(empty($post_id)){
1456 + if(!empty($post->ID)){
1457 + $post_id = $post->ID;
1458 + }else{
1459 + return $report;
1460 + }
1461 + }
1462 +
1463 + $content = get_post_field('post_content', $post_id);
1464 + if(empty($content)){
1465 + return $report;
1466 + }
1467 +
1468 + // Scan the raw content (shortcodes are not yet expanded here, so we
1469 + // catch on*= attributes inside [pl_* ele_attributes="onclick=..."])
1470 + $found = pagelayer_xss_content($content);
1471 + if(strlen($found) > 0){
1472 + $report['found'] = true;
1473 + $report['items'][] = $found;
1474 + }
1475 +
1476 + return $report;
1477 +}
1478 +
1479 +// Determine whether the XSS warning should be shown for the current post/user.
1480 +function pagelayer_should_show_xss_warning($post_id = 0){
1481 + global $post;
1482 +
1483 + if(empty($post_id)){
1484 + if(!empty($post->ID)){
1485 + $post_id = $post->ID;
1486 + }else{
1487 + return false;
1488 + }
1489 + }
1490 +
1491 + // Rule 5: user already acknowledged — don't block again
1492 + $view_token = get_transient('pagelayer_xss_view_'.$post_id.'_'.get_current_user_id());
1493 + if(!empty($view_token) && isset($_GET['pl_xss_view']) && $_GET['pl_xss_view'] === $view_token){
1494 + return false;
1495 + }
1496 +
1497 + // Rule 2: the post must contain XSS
1498 + $scan = pagelayer_xss_scan_post($post_id);
1499 + if(empty($scan['found'])){
1500 + return false;
1501 + }
1502 +
1503 + // Rule 3: skip if the current user is the post author
1504 + $author_id = (int) get_post_field('post_author', $post_id);
1505 + $current_user_id = (int) get_current_user_id();
1506 + if($author_id > 0 && $author_id === $current_user_id){
1507 + return false;
1508 + }
1509 +
1510 + // Rule 4: skip if the post author has the JS/unfiltered_html capability.
1511 + // We check this by temporarily switching to the author's context.
1512 + $author_can_js = false;
1513 + if($author_id > 0){
1514 + $author_user = get_userdata($author_id);
1515 + if(!empty($author_user)){
1516 + $author_can_js = user_can($author_user, 'unfiltered_html');
1517 + if(!$author_can_js){
1518 + $author_can_js = pagelayer_user_can_add_js_content_for_user($author_user);
1519 + }
1520 + }
1521 + }
1522 + if($author_can_js){
1523 + return false;
1524 + }
1525 +
1526 + return true;
1527 +}
1528 +
1529 +// Check if a specific user (not the current user) can add JS content.
1530 +function pagelayer_user_can_add_js_content_for_user($user){
1531 + if(empty($user) || !($user instanceof WP_User)){
1532 + return false;
1533 + }
1534 +
1535 + if(user_can($user, 'unfiltered_html')){
1536 + return true;
1537 + }
1538 +
1539 + $pagelayer_js_permission = get_option('pagelayer_js_permission');
1540 + if(empty($pagelayer_js_permission) || empty($user->roles)){
1541 + return false;
1542 + }
1543 +
1544 + foreach($user->roles as $role){
1545 + if(in_array($role, $pagelayer_js_permission)){
1546 + return true;
1547 + }
1548 + }
1549 +
1550 + return false;
1551 +}
1552 +
1553 +// Render the XSS warning page that completely blocks content rendering.
1554 +function pagelayer_render_xss_warning_block($context = 'frontend'){
1555 + global $post;
1556 +
1557 + if(empty($post->ID)){
1558 + return;
1559 + }
1560 +
1561 + if(!pagelayer_should_show_xss_warning($post->ID)){
1562 + return;
1563 + }
1564 +
1565 + // Re-scan to get the items for display
1566 + $scan = pagelayer_xss_scan_post($post->ID);
1567 + if(empty($scan['found'])){
1568 + return;
1569 + }
1570 +
1571 + $items_html = '';
1572 + foreach($scan['items'] as $item){
1573 + $items_html .= '<div class="pagelayer-xss-item">'.htmlspecialchars($item, ENT_QUOTES, 'UTF-8').'</div>';
1574 + }
1575 +
1576 + // Generate a one-time view token so the admin can explicitly consent
1577 + $view_token = wp_generate_password(32, false);
1578 + set_transient('pagelayer_xss_view_'.$post->ID.'_'.get_current_user_id(), $view_token, 3600);
1579 +
1580 + // Build the "view page" URL with the consent token
1581 + $current_url = $_SERVER['REQUEST_URI'] ?? '/';
1582 + $separator = strpos($current_url, '?') !== false ? '&' : '?';
1583 + $view_url = htmlspecialchars($current_url.$separator.'pl_xss_view='.$view_token, ENT_QUOTES, 'UTF-8');
1584 +
1585 + // Build the edit link
1586 + $edit_link = pagelayer_livelink($post->ID);
1587 + $edit_link = htmlspecialchars($edit_link.$separator.'pl_xss_view='.$view_token, ENT_QUOTES, 'UTF-8');
1588 +
1589 + // Get author info for the warning message
1590 + $author_id = (int) get_post_field('post_author', $post->ID);
1591 + $author_name = '';
1592 + if($author_id > 0){
1593 + $author_data = get_userdata($author_id);
1594 + if(!empty($author_data)){
1595 + $author_name = $author_data->display_name;
1596 + }
1597 + }
1598 +
1599 + $warning_title = __pl('xss_warning_title');
1600 + $warning_body = $author_name
1601 + ? sprintf(__pl('xss_warning_body_author'), htmlspecialchars($author_name, ENT_QUOTES, 'UTF-8'))
1602 + : __pl('xss_warning_body');
1603 + $warning_items_label = __pl('xss_warning_items');
1604 + $view_btn = __pl('xss_warning_view_page');
1605 + $edit_btn = __pl('xss_warning_edit_post');
1606 + $warning_blocked = __pl('xss_warning_blocked');
1607 +
1608 + $dashboard_link = admin_url();
1609 + $go_dashboard = __pl('xss_warning_go_dashboard');
1610 +
1611 + // Output the warning page and die() — the malicious post content is
1612 + // NEVER sent to the browser, so inline <script>, <img onerror>, and
1613 + // <iframe onload> payloads cannot execute.
1614 + $status_code = ($context === 'editor') ? 200 : 403;
1615 + if(!headers_sent()){
1616 + status_header($status_code);
1617 + header('Content-Type: text/html; charset=utf-8');
1618 + }
1619 +
1620 + echo <<<HTML
1621 +<!DOCTYPE html>
1622 +<html lang="en">
1623 +<head>
1624 +<meta charset="utf-8">
1625 +<meta name="viewport" content="width=device-width, initial-scale=1">
1626 +<title>{$warning_title}</title>
1627 +<style>
1628 +*{ margin:0; padding:0; box-sizing:border-box; }
1629 +body{
1630 + background:#f0f0f1;
1631 + font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
1632 + display:flex;
1633 + align-items:center;
1634 + justify-content:center;
1635 + min-height:100vh;
1636 + padding:20px;
1637 +}
1638 +#pagelayer-xss-block-dialog{
1639 + background:#fff;
1640 + border-radius:8px;
1641 + max-width:640px;
1642 + width:100%;
1643 + max-height:90vh;
1644 + overflow-y:auto;
1645 + box-shadow:0 4px 24px rgba(0,0,0,0.15);
1646 +}
1647 +.pagelayer-xss-block-header{
1648 + background:#d63638;
1649 + color:#fff;
1650 + padding:20px 24px;
1651 + border-radius:8px 8px 0 0;
1652 + font-size:20px;
1653 + font-weight:700;
1654 + display:flex;
1655 + align-items:center;
1656 + gap:12px;
1657 +}
1658 +.pagelayer-xss-block-body{
1659 + padding:24px;
1660 + color:#1d2327;
1661 + font-size:14px;
1662 + line-height:1.6;
1663 +}
1664 +.pagelayer-xss-block-body p{
1665 + margin:0 0 14px 0;
1666 +}
1667 +.pagelayer-xss-blocked-tag{
1668 + display:inline-block;
1669 + background:#fff4f4;
1670 + border:1px solid #d63638;
1671 + color:#d63638;
1672 + padding:3px 10px;
1673 + border-radius:3px;
1674 + font-size:12px;
1675 + font-weight:600;
1676 + margin-bottom:14px;
1677 +}
1678 +.pagelayer-xss-items{
1679 + background:#fff4f4;
1680 + border:1px solid #d63638;
1681 + border-radius:4px;
1682 + padding:14px;
1683 + margin:14px 0;
1684 + max-height:220px;
1685 + overflow-y:auto;
1686 +}
1687 +.pagelayer-xss-item{
1688 + font-family:monospace;
1689 + font-size:12px;
1690 + padding:6px 8px;
1691 + border-bottom:1px solid #f5cccc;
1692 + word-break:break-all;
1693 + color:#b32d2e;
1694 +}
1695 +.pagelayer-xss-item:last-child{
1696 + border-bottom:none;
1697 +}
1698 +.pagelayer-xss-block-actions{
1699 + padding:18px 24px;
1700 + border-top:1px solid #dcdcde;
1701 + display:flex;
1702 + gap:12px;
1703 + flex-wrap:wrap;
1704 + border-radius:0 0 8px 8px;
1705 +}
1706 +.pagelayer-xss-btn-view{
1707 + background:#2271b1;
1708 + color:#fff;
1709 + border:none;
1710 + border-radius:4px;
1711 + padding:10px 24px;
1712 + font-size:14px;
1713 + cursor:pointer;
1714 + font-weight:600;
1715 + text-decoration:none;
1716 + display:inline-block;
1717 +}
1718 +.pagelayer-xss-btn-view:hover{
1719 + background:#135e96;
1720 +}
1721 +.pagelayer-xss-btn-edit{
1722 + background:#fff;
1723 + color:#2271b1;
1724 + border:1px solid #2271b1;
1725 + border-radius:4px;
1726 + padding:10px 24px;
1727 + font-size:14px;
1728 + cursor:pointer;
1729 + font-weight:600;
1730 + text-decoration:none;
1731 + display:inline-block;
1732 +}
1733 +.pagelayer-xss-btn-edit:hover{
1734 + background:#f0f6fc;
1735 +}
1736 +.pagelayer-xss-btn-dashboard{
1737 + background:transparent;
1738 + color:#646970;
1739 + border:none;
1740 + border-radius:4px;
1741 + padding:10px 16px;
1742 + font-size:13px;
1743 + cursor:pointer;
1744 + text-decoration:none;
1745 + display:inline-block;
1746 + margin-left:auto;
1747 +}
1748 +.pagelayer-xss-btn-dashboard:hover{
1749 + color:#1d2327;
1750 +}
1751 +</style>
1752 +</head>
1753 +<body>
1754 + <div id="pagelayer-xss-block-dialog">
1755 + <div class="pagelayer-xss-block-header">
1756 + <span style="font-size:28px;">&#9888;</span>
1757 + {$warning_title}
1758 + </div>
1759 + <div class="pagelayer-xss-block-body">
1760 + <span class="pagelayer-xss-blocked-tag">{$warning_blocked}</span>
1761 + <p>{$warning_body}</p>
1762 + <p style="font-weight:600; margin-bottom:6px;">{$warning_items_label}:</p>
1763 + <div class="pagelayer-xss-items">{$items_html}</div>
1764 + </div>
1765 + <div class="pagelayer-xss-block-actions">
1766 + <a href="{$view_url}" class="pagelayer-xss-btn-view">{$view_btn}</a>
1767 + <a href="{$edit_link}" class="pagelayer-xss-btn-edit">{$edit_btn}</a>
1768 + <a href="{$dashboard_link}" class="pagelayer-xss-btn-dashboard">{$go_dashboard}</a>
1769 + </div>
1770 + </div>
1771 +</body>
1772 +</html>
1773 +HTML;
1774 +
1775 + // CRITICAL: die() here ensures the malicious post content (the_content,
1776 + // do_shortcode output, block rendering) is NEVER sent to the browser.
1777 + // Without die(), WordPress would continue rendering the template and
1778 + // output the post body — including any <script>/<img onerror>/<iframe
1779 + // onload> payloads — which would execute even if hidden with CSS.
1780 + die();
1781 +}
1782 +
1783 +function pagelayer_getting_started_notice(){
1784 +
1785 + // Is Sitepad setup done?
1786 + $setup_done = get_option('sp_setup_done');
1787 +
1788 + if(defined('SITEPAD') && empty($setup_done)){
1789 + return;
1790 + }
1791 +
1792 + // If SitePad used custom BRAND SM
1793 + if(defined('BRAND_SM_CUSTOM')){
1794 + return;
1795 + }
1796 +
1797 + echo '
1798 +<script type="application/javascript">
1799 +jQuery(document).ready(function(){
1800 + jQuery("#pagelayer-getting-started-notice").click(function(e){
1801 +
1802 + if(jQuery(e.target).hasClass("notice-dismiss")){
1803 + var data;
1804 + jQuery("#pagelayer-getting-started-notice").hide();
1805 + // Save this preference
1806 + jQuery.post("'.admin_url('?pagelayer-getting-started=0&pagelayer_nonce='.wp_create_nonce("pagelayer_getting_started_nonce") ).'", data, function(response) {
1807 + //alert(response);
1808 + });
1809 + return false;
1810 + }
1811 +
1812 + });
1813 +});
1814 +</script>
1815 +
1816 + <div id="pagelayer-getting-started-notice" class="notice notice-success is-dismissible">
1817 + <p style="font-size: 14px; font-weight: 600">';
1818 + if(defined('SITEPAD')){
1819 +
1820 + echo '<a href="'.BRAND_SM_URL.'"><img src="'.BRAND_SM_LOGO .'" style="vertical-align: middle; margin:0px 10px" width="24" /></a>'.__('Thanks for choosing '.BRAND_SM .'. We recommend that you see the short and sweet <a href="'.admin_url('admin.php?page=pagelayer_getting_started').'">Getting Started Video</a> to know the basics of '.BRAND_SM.'.');
1821 +
1822 + }else{
1823 +
1824 + echo '<a href="'.PAGELAYER_WWW_URL.'"><img src="'.PAGELAYER_URL.'/images/pagelayer-logo-256.png" style="vertical-align: middle; margin:0px 10px" width="24" /></a>'.__('Thanks for choosing Pagelayer. We recommend that you see the short and sweet <a href="'.admin_url('admin.php?page=pagelayer_getting_started').'">Getting Started Video</a> to know the basics of Pagelayer.', 'pagelayer');
1825 +
1826 + }
1827 +
1828 + echo '</p>
1829 + </div>';
1830 +
1831 +}
1832 +
1833 +// Show Changelog promo
1834 +function pagelayer_show_changelog_notice(){
1835 +
1836 + // Is Sitepad setup done?
1837 + if(defined('SITEPAD')){
1838 + return;
1839 + }
1840 +
1841 + echo '
1842 +<script type="application/javascript">
1843 +jQuery(document).ready(function(){
1844 + jQuery("#pagelayer-changelog-notice").click(function(e){
1845 +
1846 + if(jQuery(e.target).hasClass("notice-dismiss")){
1847 + var data;
1848 + jQuery("#pagelayer-changelog-notice").hide();
1849 + // Save this preference
1850 + jQuery.post("'.admin_url('?pagelayer-changelog-notice=0&pagelayer_nonce='.wp_create_nonce("pagelayer_changelog_nonce") ).'", data, function(response) {
1851 + //alert(response);
1852 + });
1853 + return false;
1854 + }
1855 +
1856 + });
1857 +});
1858 +</script>
1859 +
1860 + <div id="pagelayer-changelog-notice" class="notice notice-info is-dismissible">
1861 + <p style="font-size: 14px; font-weight: 600">
1862 + <a href="'.PAGELAYER_WWW_URL.'"><img src="'.PAGELAYER_URL.'/images/pagelayer-logo-256.png" style="vertical-align: middle; margin:0px 10px" width="24" /></a>'.__('Empower Your Designs: Pagelayer 1.8.1 - Unleashing Seamless Integration with Gutenberg for Enhanced Website Creation! <a href="'.PAGELAYER_WWW_URL.'/blog/reinventing-pagelayer-for-gutenberg-a-seamless-fusion-of-powerful-page-building-and-wordpress-editor/">Read More</a>.', 'pagelayer') .'
1863 + </p>
1864 + </div>';
1865 +
1866 +}
1867 +
819 1868 // Show promo notice on dashboard
820 1869 function pagelayer_show_promo(){
821 1870
822 1871 global $pagelayer_promo_opts;
@@ -822,62 +1871,70 @@
822 1871 global $pagelayer_promo_opts;
823 1872 $opts = $pagelayer_promo_opts;
824 1873
825 1874 echo '<style>
826 -.pagelayer_promo_button {
827 -background-color: #4CAF50; /* Green */
828 -border: none;
829 -color: white;
830 -padding: 6px 10px;
831 -text-align: center;
832 -text-decoration: none;
833 -display: inline-block;
834 -font-size: 13px;
835 -margin: 4px 2px;
836 --webkit-transition-duration: 0.4s; /* Safari */
1875 +#pagelayer_promo a.pagelayer_promo_button {
1876 +background-color: #4CAF50 !important;
1877 +border: none !important;
1878 +color: white !important;
1879 +padding: 6px 10px !important;
1880 +text-align: center !important;
1881 +text-decoration: none !important;
1882 +display: inline-block !important;
1883 +font-size: 13px !important;
1884 +margin: 4px 2px !important;
1885 +-webkit-transition-duration: 0.4s;
837 1886 transition-duration: 0.4s;
838 -cursor: pointer;
1887 +cursor: pointer !important;
839 1888 }
840 -.pagelayer_promo_button:focus,
841 -.pagelayer_promo_button:hover{
842 -border: none;
843 -color: white;
844 -box-shadow: 0 6px 8px 0 rgba(0,0,0,0.24), 0 9px 25px 0 rgba(0,0,0,0.19);
845 -color: white;
1889 +#pagelayer_promo a.pagelayer_promo_button:focus,
1890 +#pagelayer_promo a.pagelayer_promo_button:hover{
1891 +border: none !important;
1892 +color: white !important;
1893 +box-shadow: 0 6px 8px 0 rgba(0,0,0,0.24), 0 9px 25px 0 rgba(0,0,0,0.19) !important;
846 1894 }
847 -.pagelayer_promo_buy {
848 -color: white;
849 -padding: 8px 12px;
850 -font-size: 14px;
1895 +#pagelayer_promo a.pagelayer_promo_buy {
1896 +color: white !important;
1897 +padding: 8px 12px !important;
1898 +font-size: 14px !important;
1899 +background-color: #4CAF50 !important;
851 1900 }
852 -.pagelayer_promo_button1 {
853 -color: white;
854 -background-color: #4CAF50;
855 -border:3px solid #4CAF50;
1901 +#pagelayer_promo a.pagelayer_promo_button1 {
1902 +color: white !important;
1903 +background-color: #4CAF50 !important;
1904 +border:3px solid #4CAF50 !important;
856 1905 }
857 -.pagelayer_promo_button1:hover {
858 -border:3px solid #4CAF50;
1906 +#pagelayer_promo a.pagelayer_promo_button1:hover {
1907 +border:3px solid #4CAF50 !important;
859 1908 }
860 -.pagelayer_promo_button2 {
861 -color: white;
862 -background-color: #0085ba;
1909 +#pagelayer_promo a.pagelayer_promo_button2 {
1910 +color: white !important;
1911 +background-color: #0085ba !important;
863 1912 }
864 -.pagelayer_promo_button3 {
865 -color: white;
866 -background-color: #365899;
1913 +#pagelayer_promo a.pagelayer_promo_button3 {
1914 +color: white !important;
1915 +background-color: #365899 !important;
867 1916 }
868 -.pagelayer_promo_button4 {
869 -color: white;
870 -background-color: rgb(66, 184, 221);
1917 +#pagelayer_promo a.pagelayer_promo_button4 {
1918 +color: white !important;
1919 +background-color: rgb(66, 184, 221) !important;
871 1920 }
872 -.pagelayer_promo-close{
873 -float:right;
874 -text-decoration:none;
875 -margin: 5px 10px 0px 0px;
1921 +#pagelayer_promo a.pagelayer_promo-close{
1922 +float:right !important;
1923 +text-decoration:none !important;
1924 +margin: 5px 10px 0px 0px !important;
1925 +color: #666 !important;
876 1926 }
877 -.pagelayer_promo-close:hover{
878 -color: red;
1927 +#pagelayer_promo a.pagelayer_promo-close:hover{
1928 +color: red !important;
879 1929 }
1930 +#pagelayer_promo a:not(.pagelayer_promo_button):not(.pagelayer_promo-close) {
1931 +color: #4CAF50 !important;
1932 +text-decoration: underline !important;
1933 +}
1934 +#pagelayer_promo a:not(.pagelayer_promo_button):not(.pagelayer_promo-close):hover {
1935 +color: #3e8e41 !important;
1936 +}
880 1937 </style>
881 1938 <script type="application/javascript">
882 1939 jQuery(document).ready(function(){
883 1940 jQuery("#pagelayer_promo .pagelayer_promo-close").click(function(){
@@ -924,8 +1981,13 @@
924 1981
925 1982 global $pagelayer_promo_opts;
926 1983
927 1984 // There must be an interval
1985 + if(!current_user_can('activate_plugins')){
1986 + return false;
1987 + }
1988 +
1989 + // There must be an interval
928 1990 if(empty($opts['interval'])){
929 1991 return false;
930 1992 }
931 1993
@@ -952,10 +2014,66 @@
952 2014 }
953 2015
954 2016 }
955 2017
2018 +// Show the Pro notice
2019 +function pagelayer_show_pro_notice(){
2020 +
2021 + if(defined('PAGELAYER_PREMIUM')){
2022 + return;
2023 + }
2024 +
2025 + echo '<div class="pagelayer-notice pagelayer-notice-info">'.__('This feature is a part of <a href="'.PAGELAYER_PRO_PRICE_URL.'" target="_blank">Pagelayer Pro</a>. You will need to purchase <a href="'.PAGELAYER_PRO_PRICE_URL.'" target="_blank">Pagelayer Pro</a> to use this feature.').'</div>';
2026 +
2027 +}
2028 +
2029 +// Show the Pro Div
2030 +function pagelayer_show_pro_div($head = '', $message = '', $admin_css = 1){
2031 +
2032 + if(defined('PAGELAYER_PREMIUM')){
2033 + return;
2034 + }
2035 +
2036 + if(basename(get_template_directory()) == 'popularfx'){
2037 + $pro_url = 'https://popularfx.com/pricing?from=pagelayer-plugin';
2038 + $pro_txt = 'PopularFX Pro';
2039 + }else{
2040 + $pro_url = PAGELAYER_PRO_PRICE_URL;
2041 + $pro_txt = 'Pagelayer Pro';
2042 + }
2043 +
2044 + if(!empty($admin_css)){
2045 + wp_enqueue_style( 'pagelayer-admin', PAGELAYER_CSS.'/pagelayer-admin.css', array(), PAGELAYER_VERSION);
2046 + }
2047 +
2048 + echo '<div class="pagelayer-pro-div">';
2049 +
2050 + if(!empty($head)){
2051 + echo '<h1 class="pagelayer-pro-head">'.$head.'</h1>';
2052 + }
2053 +
2054 + echo '<div class="pagelayer-pro-message">';
2055 +
2056 + if(empty($message)){
2057 +
2058 + echo __('This feature is a part of <a href="'.$pro_url.'" target="_blank">'.$pro_txt.'</a>. You will need to purchase <a href="'.$pro_url.'" target="_blank">'.$pro_txt.'</a> to use this feature.');
2059 +
2060 + }else{
2061 +
2062 + echo $message;
2063 + echo ' '.__('This feature is a part of <a href="'.$pro_url.'" target="_blank">'.$pro_txt.'</a>.');
2064 +
2065 + }
2066 +
2067 + echo '</div>
2068 + <center><a class="button-pagelayer" href="'.$pro_url.'" target="_blank" style="text-decoration: none;">'.sprintf(__('Upgrade to %s', 'pagelayer'), $pro_txt).'</a></center>
2069 + </div>';
2070 +
2071 +}
2072 +
956 2073 // Bread Crumbs with links
957 2074 function pagelayer_get_breadcrumb(){
2075 + global $post;
958 2076
959 2077 // Home page
960 2078 $ret = '<a href="'.home_url().'" rel="nofollow">{{home}}</a>';
961 2079
@@ -966,9 +2084,9 @@
966 2084 // Is search query
967 2085 }elseif(is_search()){
968 2086 $ret .= '<b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b><b class="pagelayer-breadcrumb-prefix" if="{{search_prefix}}">{{search_prefix}}</b>';
969 2087 $ret .= '<em>';
970 - $ret .= get_search_query();
2088 + $ret .= '<span>'.get_search_query().'</span>';
971 2089 $ret .= '</em>';
972 2090
973 2091 //Is category or single post
974 2092 }elseif(is_category() || is_single()){
@@ -983,18 +2101,35 @@
983 2101 if(is_single()) {
984 2102 /* if (empty($categories)){
985 2103 $ret .= ' <b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b> ';
986 2104 } */
987 - $ret .= get_the_title();
2105 + $ret .= '<span>'.get_the_title().'</span>';
988 2106 }
989 2107
990 2108 // Is page
991 - }elseif(is_page()){
992 - $ret .= '<b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b>';
993 - $ret .= get_the_title();
2109 + }elseif(is_page() ){
2110 + if(!empty($post->post_parent)){
2111 + $ancestors = array_reverse(get_post_ancestors( $post->ID ));
2112 + $page_on_front = get_option('page_on_front');
2113 +
2114 + foreach( $ancestors as $ancestor ){
2115 +
2116 + if($page_on_front == $ancestor){
2117 + continue;
2118 + }
2119 +
2120 + $ret .= '<b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b><a href="'.get_permalink($ancestor).'">'.get_the_title($ancestor).'</a>';
2121 + }
2122 +
2123 + $ret .=' <b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b><span>'.get_the_title().'</span>';
2124 +
2125 + }else{
2126 + $ret .= '<b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b>';
2127 + $ret .= '<span>'.get_the_title().'</span>';
2128 + }
994 2129 }else{
995 2130 $ret .= '<b class="pagelayer-breadcrumb-sep" if="{{separator}}">{{separator}}</b>';
996 - $ret .= wp_title('', false );
2131 + $ret .= '<span>'.wp_title('', false ).'</span>';
997 2132 }
998 2133
999 2134 // wooCommerce Integration left
1000 2135
@@ -1073,9 +2208,19 @@
1073 2208 return '';
1074 2209 }
1075 2210
1076 2211 function pagelayer_posts($params, $args = []){
2212 + global $post, $wp_query;
1077 2213
2214 + if(isset($params['exc_length'])){
2215 + $exc_length = (int) $params['exc_length'];
2216 + add_filter( 'excerpt_length', function($length) use($exc_length){
2217 + return $exc_length;
2218 + }, 999 );
2219 + }
2220 +
2221 + // add_filter('excerpt_more', 'pagelayer_remove_excerpt_more', 999);
2222 +
1078 2223 // If args is empty
1079 2224 if(empty($args)){
1080 2225 $args = array(
1081 2226 'post_type' => $params['post_type'],
@@ -1081,54 +2226,98 @@
1081 2226 'post_type' => $params['post_type'],
1082 2227 'posts_per_page' => $params['posts_per_page'],
1083 2228 'order' => $params['order']
1084 2229 );
1085 -
1086 - if($params['exc_length']){
1087 - $exc_length = (int) $params['exc_length'];
1088 - add_filter( 'excerpt_length', function($length) use($exc_length){
1089 - return $exc_length;
1090 - }, 999 );
2230 +
2231 + if(!empty($params['paged'])){
2232 + $args['paged'] = $params['paged'];
1091 2233 }
1092 2234
1093 - add_filter('excerpt_more', 'pagelayer_remove_excerpt_more', 999);
1094 -
1095 - if($params['term']){
2235 + if (!empty($params['term']) || !empty($params['exc_term'])) {
2236 + $tax_query = [];
1096 2237
1097 - $term = explode(':', $params['term']);
1098 - //pagelayer_print($term);
1099 - if($term[0] == 'category'){
1100 - $args['category_name'] = $term[1];
2238 + if (!empty($params['term'])) {
2239 + $terms = explode(',', $params['term']);
2240 + $include = array_reduce($terms, function ($carry, $term) {
2241 + list($taxonomy, $slug, $id) = explode(':', $term);
2242 + $carry[$taxonomy][] = $slug;
2243 + return $carry;
2244 + }, []);
2245 +
2246 + $filters = array_filter([
2247 + isset($include['category']) ? [
2248 + 'taxonomy' => 'category',
2249 + 'field' => 'slug',
2250 + 'terms' => $include['category'],
2251 + 'include_children' => false,
2252 + ] : null,
2253 + isset($include['post_tag']) ? [
2254 + 'taxonomy' => 'post_tag',
2255 + 'field' => 'slug',
2256 + 'terms' => $include['post_tag'],
2257 + 'include_children' => false,
2258 + ] : null,
2259 + ]);
2260 +
2261 + if(!empty($filters)){
2262 + $tax_query[] = array_merge(['relation' => 'OR'], $filters);
2263 + }
1101 2264 }
1102 - if($term[0] == 'post_tag'){
1103 - $args['tag'] = $term[1];
2265 +
2266 + // Handle exclusions
2267 + if (!empty($params['exc_term'])) {
2268 + $terms = explode(',', $params['exc_term']);
2269 + $include = array_reduce($terms, function ($carry, $term) {
2270 + list($taxonomy, $slug, $id) = explode(':', $term);
2271 + $carry[$taxonomy][] = $slug;
2272 + return $carry;
2273 + }, []);
2274 +
2275 + $filters = array_filter([
2276 + isset($include['category']) ? [
2277 + 'taxonomy' => 'category',
2278 + 'field' => 'slug',
2279 + 'terms' => $include['category'],
2280 + 'operator' => 'NOT IN',
2281 + 'include_children' => false,
2282 + ] : null,
2283 + isset($include['post_tag']) ? [
2284 + 'taxonomy' => 'post_tag',
2285 + 'field' => 'slug',
2286 + 'terms' => $include['post_tag'],
2287 + 'operator' => 'NOT IN',
2288 + 'include_children' => false,
2289 + ] : null,
2290 + ]);
2291 +
2292 + if(!empty($filters)){
2293 + $tax_query[] = array_merge(['relation' => 'AND'], $filters);
2294 + }
1104 2295 }
1105 - }
1106 2296
1107 - if($params['exc_term']){
1108 -
1109 - $term = explode(':', $params['exc_term']);
1110 - //pagelayer_print($term);
1111 - if($term[0] == 'category'){
1112 - $args['category__not_in'] = $term[2];
2297 + // Apply the combined tax_query
2298 + if(!empty($tax_query)){
2299 + $args['tax_query'] = array_merge(['relation' => 'AND'], $tax_query);
1113 2300 }
1114 - if($term[0] == 'post_tag'){
1115 - $args['tag__not_in'] = $term[2];
1116 - }
1117 2301 }
1118 2302
1119 - if($params['author_name']){
2303 + if(!empty($params['author_name'])){
1120 2304 $author_name = explode(':', $params['author_name']);
1121 2305 $args['author_name'] = $author_name[0];
1122 2306 }
1123 2307
1124 - if($params['exc_author']){
2308 + if(!empty($params['exc_author'])){
1125 2309 $exc_author = explode(':', $params['exc_author']);
1126 2310 $args['author'] = '-'.$exc_author[1];
1127 2311 }
1128 2312
1129 - if(!empty($params['offset'])){
1130 - $args['offset'] = $params['offset'];
2313 + if (!empty($params['offset'])) {
2314 + $args['offset'] = (int) $params['offset'];
2315 +
2316 + // Make Compatible for Infinite load
2317 + if(!empty($params['infinite_types']) && !empty($params['paged']) && $params['paged'] > 1 ){
2318 + $args['offset'] = $args['offset'] + (($params['paged'] - 1) * $params['posts_per_page']);
2319 + }
1131 2320 }
1132 2321
1133 2322 if(!empty($params['ignore_sticky'])){
1134 2323 $args['ignore_sticky_posts'] = $params['ignore_sticky'];
@@ -1137,9 +2326,9 @@
1137 2326 if(!empty($params['orderby'])){
1138 2327 $args['orderby'] = $params['orderby'];
1139 2328 }
1140 2329
1141 - if($params['by_period']){
2330 + if(!empty($params['by_period'])){
1142 2331 $date_arg = array();
1143 2332 switch($params['by_period']){
1144 2333 case 'last_day':
1145 2334 $date_arg['day'] = date('j')-1;
@@ -1164,15 +2353,25 @@
1164 2353 }
1165 2354 }
1166 2355 //pagelayer_print($args);
1167 2356
2357 + // Only see published posts
2358 + $args['post_status'] = 'publish';
2359 +
1168 2360 $postsquery = new WP_Query($args);
1169 2361 $data = '';
1170 2362
2363 + if(!wp_doing_ajax() && !empty($params['infinite_types'])){
2364 + $data.='<div class="pagelayer-post-max" data-max="'.$postsquery->max_num_pages.'"></div>';
2365 + }
2366 +
1171 2367 if(!$postsquery->have_posts()){
1172 - return '<h3>Something went wrong, Please give proper inputs!</h3>';
2368 + return '<h3>No posts found!</h3>';
1173 2369 }
1174 2370
2371 + // To reset the post when the $wp_query->post is empty
2372 + $orig_post = $post;
2373 +
1175 2374 while($postsquery->have_posts()) : $postsquery->the_post();
1176 2375 $data .= '<div class="pagelayer-wposts-col">
1177 2376 <div class="pagelayer-wposts-post">
1178 2377 <div class="pagelayer-wposts-featured">';
@@ -1177,10 +2376,10 @@
1177 2376 <div class="pagelayer-wposts-post">
1178 2377 <div class="pagelayer-wposts-featured">';
1179 2378 $data .= '<a href="'. get_the_permalink() .'">';
1180 2379
1181 - if(isset($params['show_thumb']) && has_post_thumbnail( $postsquery->ID )){
1182 - $data .= '<div class="pagelayer-wposts-thumb"'.(has_post_thumbnail() ? ' style="background:url('.get_the_post_thumbnail_url($postsquery->ID,$params['thumb_size']).')"' : '').'></div>';
2380 + if(!empty($params['show_thumb'])){
2381 + $data .= '<div class="pagelayer-wposts-thumb" style="background:url('.(has_post_thumbnail($postsquery->ID) ? get_the_post_thumbnail_url($postsquery->ID, $params['thumb_size']) : ( empty($params['thumb_img_type']) || empty($params['def_thumb_img']) ? PAGELAYER_URL.'/images/no_screenshot.png' : $params['def_thumb_img']) ).')"></div>';
1183 2382 }
1184 2383 /* if($params['show_thumb'] && has_post_thumbnail( $postsquery->ID )){
1185 2384 $data .= get_the_post_thumbnail_url($postsquery->ID,$params['thumb_size']);
1186 2385 } */
@@ -1186,32 +2385,38 @@
1186 2385 } */
1187 2386 $data .= '</a></div>
1188 2387 <div class="pagelayer-wposts-content">';
1189 2388
1190 - if(isset($params['show_title'])){
2389 + if(!empty($params['show_title'])){
1191 2390 $data .= '<a href="'.esc_url( get_permalink() ).'" rel="bookmark"><div class="pagelayer-wposts-title">'. get_the_title().'</div></a>';
1192 2391 }
1193 2392
1194 2393 $data .= '<div class="pagelayer-wposts-meta">';
1195 2394 $sep = '';
1196 - if(isset($params['meta_sep'])){
1197 - $sep = '<b class="pagelayer-wposts-sep">'.$params['meta_sep'].'</b>';
2395 + if(!empty($params['meta_sep'])){
2396 + $sep = ' <b class="pagelayer-wposts-sep">'.$params['meta_sep'].'</b> ';
1198 2397 }
1199 - if(isset($params['author'])){
2398 + if(!empty($params['author'])){
1200 2399 $data .= '<span class="pagelayer-wposts-author">By <a class="pagelayer-wposts-author-url" href="'.esc_url(get_author_posts_url(get_the_author_meta('ID'))).'">'.esc_html(get_the_author()).'</a></span>'.$sep;
1201 2400 }
1202 - if(isset($params['date'])){
1203 - $data .= '<span class="pagelayer-wposts-date"><time class="pagelayer-wposts-entry-date published updated" datetime="'.get_the_date('c').'"><span class="date-d">'.get_the_date('j').'</span><span class="date-my">'.get_the_date('M, y').'</span></time></span>'.$sep;
2401 + if(!empty($params['date'])){
2402 + $data .= '<span class="pagelayer-wposts-date"><time class="pagelayer-wposts-entry-date published updated" datetime="'.get_the_date('c').'"><span class="date-d">'.get_the_date('j').'</span> <span class="date-my">'.get_the_date('M, y').'</span></time></span>'.$sep;
1204 2403 }
1205 - if(isset($params['category'])){
2404 +
2405 + if(!empty($params['category'])){
1206 2406 $category = get_the_category();
1207 2407 $singlecategory = '';
1208 2408 foreach( $category as $cat ){
1209 2409 $singlecategory .= '<a href="' . get_tag_link( $cat->term_id ) . '">'. $cat->name .'</a>';
1210 2410 }
1211 - $data .= '<span class="pagelayer-wposts-category">' . $singlecategory . '</span>'.$sep;
2411 +
2412 + if(!empty($singlecategory)){
2413 + $data .= '<span class="pagelayer-wposts-category">' . $singlecategory . '</span>'.$sep;
2414 + }
2415 +
1212 2416 }
1213 - if(isset($params['tags'])){
2417 +
2418 + if(!empty($params['tags'])){
1214 2419 $tags = get_the_tags();
1215 2420 $singletag = '';
1216 2421 if(!empty($tags)){
1217 2422 foreach( $tags as $tag ){
@@ -1222,25 +2427,29 @@
1222 2427 }
1223 2428 }
1224 2429
1225 2430 }
1226 - if(isset($params['comments'])){
1227 - $data .= '<span class="pagelayer-wposts-comments"><i class="far fa-comment"></i><a href="' . esc_url( get_permalink() ) . '">' . esc_html(get_comments_number()) . '</a></span>'.$sep;
2431 +
2432 + if(!empty($params['comments']) && comments_open($postsquery->ID)){
2433 + $data .= '<span class="pagelayer-wposts-comments"><a href="' . esc_url( get_permalink() ) . '">' . esc_html(get_comments_number()).' '.__pl('comments').'</a></span>'.$sep;
1228 2434 }
1229 2435
1230 2436 $data .= '</div>';
1231 2437
1232 - if(isset($params['show_content'])){
2438 + if(!empty($params['show_content'])){
1233 2439 $data .= '<div class="pagelayer-wposts-excerpt">';
1234 - if($params['show_content'] == 'excerpt'){
1235 - $data .= do_shortcode(get_the_excerpt());
1236 - }elseif($params['show_content'] == 'full'){
1237 - $data .= do_shortcode(get_the_content());
2440 +
2441 + if($params['show_content'] == 'excerpt'){
2442 + $post_excerpt = get_the_excerpt();
2443 + $trimmed_excerpt = !empty($params['exc_length']) ? wp_trim_words($post_excerpt, (int) $params['exc_length']) : $post_excerpt;
2444 + $data .= pagelayer_the_content($trimmed_excerpt);
2445 + } elseif ($params['show_content'] == 'full') {
2446 + $data .= pagelayer_the_content(get_the_content());
1238 2447 }
1239 2448 $data .= '</div>';
1240 2449 }
1241 2450
1242 - if(isset($params['show_more'])){
2451 + if(!empty($params['show_more'])){
1243 2452 $data .= '<div class="pagelayer-wposts-mdiv"><a class="pagelayer-wposts-more pagelayer-btn-holder pagelayer-ele-link '.$params['btn_type'].' '.$params['size'].' '.$params['icon_position'].'" href="'. get_the_permalink().'">';
1244 2453
1245 2454 if($params['icon']){
1246 2455 $data .= '<i class="'.$params['icon'].' pagelayer-btn-icon"></i>';
@@ -1262,8 +2471,15 @@
1262 2471 //$data .= '<div class="pagelayer-wposts-date"><p>'.get_the_date().'</p></div>';
1263 2472 $data .= '</div></div></div>';
1264 2473 endwhile;
1265 2474
2475 + // In the Gutenberg while adding new page the $wp_query->post was empty
2476 + if ( !isset( $wp_query ) || empty($wp_query->post) ) {
2477 + $GLOBALS['post'] = $orig_post;
2478 + }else{
2479 + wp_reset_postdata();
2480 + }
2481 +
1266 2482 return $data;
1267 2483 }
1268 2484
1269 2485 // Get Post Revision
@@ -1362,9 +2578,9 @@
1362 2578 $data .= '<div class="pagelayer-posts-slider-excerpt">';
1363 2579 if($params['post']['show_excerpt'] == "true"){
1364 2580 if(has_excerpt()){
1365 2581 $excerpt = get_the_excerpt();
1366 - $data .= do_shortcode($excerpt);
2582 + $data .= pagelayer_the_content($excerpt);
1367 2583 }
1368 2584 }
1369 2585 $data .= '</div>';
1370 2586 $data .= '<a class="pagelayer-posts-slider-link" href="'. get_the_permalink().'">Read More</a>';
@@ -1397,8 +2613,12 @@
1397 2613
1398 2614 // Groups
1399 2615 }else{
1400 2616
2617 + if(array_key_exists('hide_drop', $opt_array[$x]) && !empty($opt_array[$x]['hide_drop'])){
2618 + continue;
2619 + }
2620 +
1401 2621 // If Label is there, then its a normal option
1402 2622 if(array_key_exists('label', $opt_array[$x])){
1403 2623 $options .= pagelayer_sel_option($x, $opt_array[$x]['label'], $selected);
1404 2624
@@ -1462,9 +2682,9 @@
1462 2682
1463 2683 function pagelayer_get_post_author(){
1464 2684
1465 2685 $args = [
1466 - 'who' => 'authors',
2686 + 'capability' => array( 'edit_posts' ),
1467 2687 'fields' => [
1468 2688 'ID',
1469 2689 'display_name',
1470 2690 'user_nicename',
@@ -1469,8 +2689,14 @@
1469 2689 'display_name',
1470 2690 'user_nicename',
1471 2691 ]
1472 2692 ];
2693 +
2694 + // Capability queries were only introduced in WP 5.9.
2695 + if( version_compare( $GLOBALS['wp_version'], '5.9-alpha', '<' ) ){
2696 + $args['who'] = 'authors';
2697 + unset( $args['capability'] );
2698 + }
1473 2699
1474 2700 $authors = new \WP_User_Query( $args );
1475 2701
1476 2702 $ret = array();
@@ -1546,10 +2772,67 @@
1546 2772
1547 2773 return $query_args;
1548 2774 }
1549 2775
2776 +// Loads the tags which have parameters of a particular type
2777 +function pagelayer_get_prop_type($types){
2778 +
2779 + global $pagelayer;
2780 +
2781 + if(!is_array($types)){
2782 + $types = [$types];
2783 + }
2784 +
2785 + $ret = [];
2786 +
2787 + // Loop thru all shortcodes
2788 + foreach($pagelayer->shortcodes as $tag => $vvv){
2789 +
2790 + // Lets create the CSS, Classes, Attr. Also clean the dependent atts
2791 + foreach($pagelayer->tabs as $tab){
2792 +
2793 + if(empty($pagelayer->shortcodes[$tag][$tab])){
2794 + continue;
2795 + }
2796 +
2797 + foreach($pagelayer->shortcodes[$tag][$tab] as $section => $Lsection){
2798 +
2799 + $props = empty($pagelayer->shortcodes[$tag][$section]) ? @$pagelayer->styles[$section] : @$pagelayer->shortcodes[$tag][$section];
2800 +
2801 + //echo $tab.' - '.$section.' - <br>';
2802 +
2803 + if(empty($props)){
2804 + continue;
2805 + }
2806 +
2807 + // Loop all props
2808 + foreach($props as $prop => $param){
2809 +
2810 + // Load any attachment values
2811 + if(!in_array($param['type'], $types)){
2812 + continue;
2813 + }
2814 +
2815 + $ret[$tag][$prop] = $param['type'];
2816 +
2817 + }
2818 +
2819 + }
2820 +
2821 + }
2822 +
2823 + }
2824 +
2825 + //r_print($ret);die();
2826 +
2827 + return $ret;
2828 +
2829 +}
2830 +
1550 2831 function pagelayer_export_content($content){
1551 -
2832 +
2833 + global $pagelayer;
2834 +
1552 2835 // Just call do_shortcode so we can get list of media files to export
1553 2836 //do_shortcode($content);
1554 2837
1555 2838 $theme_url = preg_replace('/http(s?):\/\//is', '', get_stylesheet_directory_uri());
@@ -1555,8 +2838,46 @@
1555 2838 $theme_url = preg_replace('/http(s?):\/\//is', '', get_stylesheet_directory_uri());
1556 2839
1557 2840 $content = preg_replace('/http(s?):\/\/'.preg_quote($theme_url, '/').'/is', '{{theme_url}}', $content);
1558 2841
2842 + // Remove unnecessary spaces
2843 + $content = preg_replace('/--\>\s*(?!<!--)/is', '-->', $content);
2844 +
2845 + // Replace links of the theme for images
2846 + $content = pagelayer_export_theme_links($content, get_stylesheet_directory_uri());
2847 +
2848 + ////////////////////
2849 + // Handle Link IDs
2850 + ////////////////////
2851 +
2852 + if(empty($pagelayer->e['link_tags'])){
2853 + $pagelayer->e['link_tags'] = pagelayer_get_prop_type('link');
2854 + //r_print($pagelayer->e['link_tags']);die();
2855 + }
2856 +
2857 + if(empty($pagelayer->e['media_tags'])){
2858 + $pagelayer->e['media_tags'] = pagelayer_get_prop_type(['image', 'video', 'audio', 'media', 'multi_image']);
2859 + //r_print($pagelayer->e['media_tags']);die();
2860 + }
2861 +
2862 + $parsed = parse_blocks($content);
2863 + //r_print($parsed);//die();
2864 +
2865 + $parsed = pagelayer_export_link_ids($parsed);
2866 +
2867 + // Export the media as well
2868 + $parsed = pagelayer_export_media($parsed);
2869 +
2870 + // Reserialize
2871 + $content = serialize_blocks($parsed);
2872 +
2873 + // We store in SP format
2874 + $content = str_replace('<!-- wp:pagelayer', '<!-- sp:pagelayer', $content);
2875 + $content = str_replace('<!-- /wp:pagelayer', '<!-- /sp:pagelayer', $content);
2876 +
2877 + // Just pass the content to
2878 + $content = pagelayer_export_extract_menu_ids($content);
2879 +
1559 2880 // Apply a filter
1560 2881 $content = apply_filters('pagelayer_export_content', $content);
1561 2882
1562 2883 return $content;
@@ -1562,23 +2883,262 @@
1562 2883 return $content;
1563 2884
1564 2885 }
1565 2886
2887 +// Export the menu IDs
2888 +function pagelayer_export_extract_menu_ids($content){
2889 +
2890 + global $pagelayer;
2891 +
2892 + // Any Menu ?
2893 + preg_match_all('/\[pl_wp_menu ([^\]]*)nav_list="(\d*)"([^\]]*)\]/is', $content, $matches);
2894 + //r_print($matches);
2895 +
2896 + if(!empty($matches[2])){
2897 +
2898 + foreach($matches[2] as $k => $v){
2899 + $v = (int)$v;
2900 + $pagelayer->export_menus[$v] = $v;
2901 + }
2902 + //r_print($pagelayer->export_menus);
2903 +
2904 + }
2905 +
2906 + // Also for block format
2907 + preg_match_all('/<!--\s+(?P<closer>\/)?sp:pagelayer\/pl_wp_menu\s+(?P<attrs>{(?:(?:[^}]+|}+(?=})|(?!}\s+\/?-->).)*+)?}\s+)?(?P<void>\/)?-->/s', $content, $matches);
2908 +
2909 + if(!empty($matches['attrs'])){
2910 +
2911 + foreach($matches['attrs'] as $k => $v){
2912 + preg_match('/nav_list"\s*:\s*"(\d*)"/is', $v, $vmatch);
2913 +
2914 + if(!empty($vmatch[1])){
2915 + $vmatch[1] = (int)$vmatch[1];
2916 + $pagelayer->export_menus[$vmatch[1]] = $vmatch[1];
2917 + //r_print($pagelayer->export_menus);
2918 + }
2919 + }
2920 + }
2921 +
2922 + return $content;
2923 +
2924 +}
2925 +
2926 +function pagelayer_export_link_ids($parsed){
2927 +
2928 + global $pagelayer;
2929 +
2930 + foreach($parsed as $k => $v){
2931 +
2932 + if(!empty($v['innerBlocks'])){
2933 + $parsed[$k]['innerBlocks'] = pagelayer_export_link_ids($v['innerBlocks']);
2934 + }
2935 +
2936 + if(!preg_match('/pagelayer/is', $v['blockName'])){
2937 + continue;
2938 + }
2939 +
2940 + $tag = str_replace('pagelayer/', '', $v['blockName']);
2941 + //echo $tag;//die();
2942 +
2943 + // Is there a tag having a link prop
2944 + if(empty($pagelayer->e['link_tags'][$tag])){
2945 + continue;
2946 + }
2947 + //echo $tag;die();
2948 +
2949 + foreach($pagelayer->e['link_tags'][$tag] as $kk => $vv){
2950 + if(!empty($v['attrs'][$kk]) && is_numeric($v['attrs'][$kk])){
2951 + $linked = get_post($v['attrs'][$kk]);
2952 + if(!empty($linked->post_name)){
2953 + $parsed[$k]['attrs'][$kk] = '||link_id|'.$linked->post_type.'|'.$linked->post_name.'||';
2954 + }
2955 + //echo $tag;echo '-'.$kk;r_print($parsed[$k]['attrs']);die();
2956 + }
2957 + }
2958 +
2959 + }
2960 +
2961 + return $parsed;
2962 +
2963 +}
2964 +
2965 +// Replace theme links with theme_url
2966 +function pagelayer_export_theme_links($content, $link){
2967 +
2968 + global $sitepad;
2969 +
2970 + // Theme URL correction code
2971 + $theme_url = preg_replace('/http(s?):\/\//is', '', $link);
2972 +
2973 + $theme_urls[0] = 'http://'.$theme_url;
2974 + $theme_urls[1] = 'https://'.$theme_url;
2975 + $theme_urls[2] = $theme_url;
2976 + $theme_urls[3] = str_replace('/', '\\/', $theme_urls[0]);
2977 + $theme_urls[4] = str_replace('/', '\\/', $theme_urls[1]);
2978 + $theme_urls[5] = str_replace('/', '\\/', $theme_url);
2979 +
2980 + foreach($theme_urls as $k => $v){
2981 + $content = preg_replace_callback('/'.preg_quote($v, '/').'([^"]*)/is', 'pagelayer_export_theme_links_replacer', $content);
2982 + }
2983 +
2984 + $content = str_ireplace($theme_urls, '{{theme_url}}', $content);
2985 +
2986 + return $content;
2987 +}
2988 +
2989 +// This function is a part of pagelayer_export_theme_links
2990 +function pagelayer_export_theme_links_replacer($matches){
2991 + return '{{theme_url}}'.str_replace('\\/', '/', $matches[1]);
2992 +}
2993 +
2994 +// Export media
2995 +function pagelayer_export_media($parsed){
2996 +
2997 + global $pagelayer;
2998 +
2999 + foreach($parsed as $k => $v){
3000 +
3001 + if(!empty($v['innerBlocks'])){
3002 + $parsed[$k]['innerBlocks'] = pagelayer_export_media($v['innerBlocks']);
3003 + }
3004 +
3005 + if(!preg_match('/pagelayer/is', $v['blockName'])){
3006 + continue;
3007 + }
3008 +
3009 + $tag = str_replace('pagelayer/', '', $v['blockName']);
3010 + //echo $tag;//die();
3011 +
3012 + // Is there a tag having a link prop
3013 + if(empty($pagelayer->e['media_tags'][$tag])){
3014 + continue;
3015 + }
3016 + //echo $tag;die();
3017 +
3018 + foreach($pagelayer->e['media_tags'][$tag] as $kk => $vv){
3019 +
3020 + if(empty($v['attrs'][$kk])){
3021 + continue;
3022 + }
3023 +
3024 + $is_array = 0;
3025 +
3026 + // Is it an array
3027 + if(is_array($v['attrs'][$kk])){
3028 +
3029 + $ids = $v['attrs'][$kk];
3030 + $is_array = 1;
3031 +
3032 + // Is it of the format 1,2,3
3033 + }elseif(preg_match('/^((\d*)(,?))*$/is', $v['attrs'][$kk])){
3034 +
3035 + $ids = pagelayer_maybe_explode(',', $v['attrs'][$kk]);
3036 +
3037 + // Its a number or string
3038 + }else{
3039 +
3040 + // Is it our default image ?
3041 + if(preg_match('/pagelayer-pro\/images\/default-image.png$/is', $v['attrs'][$kk])){
3042 + $parsed[$k]['attrs'][$kk] = '';
3043 + continue;
3044 + }
3045 +
3046 + // It should be a number
3047 + if(!is_numeric($v['attrs'][$kk])){
3048 + continue;
3049 + }
3050 +
3051 + $ids = [$v['attrs'][$kk]];
3052 + }
3053 +
3054 + $ret = [];
3055 +
3056 + foreach($ids as $id){
3057 +
3058 + $file = pagelayer_export_media_files($id, $exp_img_url);
3059 +
3060 + // Did it export ?
3061 + if(empty($file) || !file_exists($file)){
3062 + continue;
3063 + }
3064 +
3065 + $ret[] = $exp_img_url;
3066 +
3067 + }
3068 +
3069 + $parsed[$k]['attrs'][$kk] = $is_array ? $ret : implode(',', $ret);
3070 + //echo $tag;echo '-'.$kk;r_print($parsed[$k]['attrs']);die();
3071 + }
3072 +
3073 + }
3074 +
3075 + return $parsed;
3076 +
3077 +}
3078 +
3079 +// Exports the media to the images folder of the current theme
3080 +function pagelayer_export_media_files($id, &$url = ''){
3081 +
3082 + $theme_dir = get_stylesheet_directory();
3083 + $image_dir = $theme_dir.'/images/';
3084 + @mkdir($image_dir);
3085 +
3086 + // Get the file path
3087 + $file = get_attached_file($id);
3088 +
3089 + if(empty($file) || !file_exists($file)){
3090 + return false;
3091 + }
3092 +
3093 + $dest_file = $image_dir.basename($file);
3094 +
3095 + // Copy the file
3096 + copy($file, $dest_file);
3097 +
3098 + // Replace the text
3099 + $url = '{{theme_url}}/images/'.basename($file);
3100 +
3101 + return $dest_file;
3102 +
3103 + //echo $content;
3104 +}
3105 +
1566 3106 // Insert a post which is a Pagelayer Post
1567 -function pagelayer_insert_content($post, &$ret){
3107 +function pagelayer_sanitize_content($content){
1568 3108
3109 + if(empty($content)){
3110 + return $content;
3111 + }
3112 +
1569 3113 // Replace Vars
1570 3114 $template_vars = pagelayer_template_vars();
1571 3115
1572 3116 foreach($template_vars as $key => $val){
1573 - $post['post_content'] = str_replace($key, $val, $post['post_content']);
3117 + $content = str_replace($key, $val, $content);
1574 3118 }
1575 3119
3120 + if(defined('PAGELAYER_BLOCK_PREFIX') && PAGELAYER_BLOCK_PREFIX == 'wp'){
3121 + $content = str_replace('<!-- sp:pagelayer', '<!-- wp:pagelayer', $content);
3122 + $content = str_replace('<!-- /sp:pagelayer', '<!-- /wp:pagelayer', $content);
3123 + }
3124 +
3125 + return $content;
3126 +}
3127 +
3128 +// Insert a post which is a Pagelayer Post
3129 +function pagelayer_insert_content($post, &$ret){
3130 +
3131 + $post = apply_filters('pagelayer_start_insert_content', $post);
3132 +
3133 + $post['post_content'] = pagelayer_sanitize_content($post['post_content']);
1576 3134 //pagelayer_print($post);die();
1577 3135
1578 3136 // Add slashes for safe insert
1579 3137 $post['post_content'] = wp_slash($post['post_content']);
1580 3138
3139 + $post = apply_filters('pagelayer_pre_insert_content', $post);
3140 +
1581 3141 // Now insert / update the post
1582 3142 $ret = wp_insert_post($post);
1583 3143
1584 3144 // Did we save the post ?
@@ -1607,45 +3167,12 @@
1607 3167 return $stored_icons;
1608 3168
1609 3169 }
1610 3170
1611 -// Install the Pro version
1612 -function pagelayer_install_pro(){
1613 -
1614 - global $pagelayer;
1615 -
1616 - // Include the necessary stuff
1617 - include_once( ABSPATH . 'wp-admin/includes/plugin-install.php' );
1618 -
1619 - // Includes necessary for Plugin_Upgrader and Plugin_Installer_Skin
1620 - include_once( ABSPATH . 'wp-admin/includes/file.php' );
1621 - include_once( ABSPATH . 'wp-admin/includes/misc.php' );
1622 - include_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
1623 -
1624 - // Filter to prevent the activate text
1625 - add_filter('install_plugin_complete_actions', 'pagelayer_install_plugin_complete_actions', 10, 3);
1626 -
1627 - $upgrader = new Plugin_Upgrader( new Plugin_Installer_Skin( ) );
1628 - $installed = $upgrader->install(PAGELAYER_API.'download.php?version=latest&license='.$pagelayer->license['license']);
1629 -
1630 - if ( !is_wp_error( $installed ) && $installed ) {
1631 - echo 'Activating Pagelayer Pro !';
1632 - $activate = activate_plugin(PAGELAYER_PRO_BASE);
1633 -
1634 - if ( is_null($activate) ) {
1635 - echo '<div id="message" class="updated"><p>'. __('Done! Pagelayer Pro is now installed and activated.', 'pagelayer'). '</p></div><br />';
1636 - echo '<br><br><b>Done! Pagelayer Pro is now installed and activated.</b>';
1637 - }
1638 - }
1639 -
1640 - return $installed;
1641 -
1642 -}
1643 -
1644 3171 // Prevent pro activate text for installer
1645 3172 function pagelayer_install_plugin_complete_actions($install_actions, $api, $plugin_file){
1646 3173
1647 - if($plugin_file == PAGELAYER_PRO_BASE){
3174 + if($plugin_file == PAGELAYER_PREMIUM_BASE){
1648 3175 return array();
1649 3176 }
1650 3177
1651 3178 return $install_actions;
@@ -1650,38 +3177,8 @@
1650 3177
1651 3178 return $install_actions;
1652 3179 }
1653 3180
1654 -// Load license data
1655 -function pagelayer_load_license(){
1656 -
1657 - global $pagelayer;
1658 -
1659 - // Load license
1660 - $pagelayer->license = get_option('pagelayer_license');
1661 -
1662 - // Update license details as well
1663 - if(!empty($pagelayer->license) && (time() - @$pagelayer->license['last_update']) >= 86400){
1664 -
1665 - $resp = wp_remote_get(PAGELAYER_API.'license.php?license='.$pagelayer->license['license']);
1666 -
1667 - // Did we get a response ?
1668 - if(is_array($resp)){
1669 -
1670 - $tosave = json_decode($resp['body'], true);
1671 -
1672 - // Is it the license ?
1673 - if(!empty($tosave['license'])){
1674 - $tosave['last_update'] = time();
1675 - update_option('pagelayer_license', $tosave);
1676 - }
1677 -
1678 - }
1679 -
1680 - }
1681 -
1682 -}
1683 -
1684 3181 // Handle hexa to rgba and also remove alpha which is ff
1685 3182 function pagelayer_hex8_to_rgba($val){
1686 3183
1687 3184 // If opacity is ff then discard ff
@@ -1756,9 +3253,15 @@
1756 3253 $urls['youtube'] = get_option('pagelayer-youtube-url');
1757 3254 $urls['google'] = get_option('pagelayer-gplus-url');
1758 3255
1759 3256 foreach($urls as $k => $v){
1760 - if(empty($v)) unset($urls[$k]);
3257 +
3258 + if(empty($v)){
3259 + unset($urls[$k]);
3260 + continue;
3261 + }
3262 +
3263 + $urls[$k] = esc_url($v);
1761 3264 }
1762 3265
1763 3266 return $urls;
1764 3267 }
@@ -1765,15 +3268,1187 @@
1765 3268
1766 3269 function pagelayer_get_option($opt){
1767 3270 $ret = get_option($opt);
1768 3271
1769 - $opts = array('pagelayer-address' => '1, My Address, My Street, New York City, NY, USA',
1770 - 'pagelayer-phone' => '+1234567890',
1771 - 'pagelayer-copyright' => '© '.date('Y').' '.get_option('blogname'),
1772 - 'pagelayer_cf_to_email' => 'contact@domain.com');
3272 + $opts = array(
3273 + 'pagelayer-address' => '1, My Address, My Street, New York City, NY, USA',
3274 + 'pagelayer-phone' => '+1234567890',
3275 + 'pagelayer-copyright' => '© '.date('Y').' '.get_option('blogname'),
3276 + 'pagelayer_cf_to_email' => 'contact@domain.com',
3277 + 'pagelayer_cf_success' => __pl('cf_success'),
3278 + 'pagelayer_cf_failed' => __pl('cf_failed'),
3279 + );
1773 3280
1774 3281 if(empty($ret)){
1775 3282 return $opts[$opt];
1776 3283 }
1777 3284
3285 + return wp_kses_post($ret);
3286 +}
3287 +
3288 +// Uploads an image / media
3289 +function pagelayer_upload_media($filename, $blob){
3290 +
3291 + if(empty($blob)){
3292 + return false;
3293 + }
3294 +
3295 + $md5 = md5($blob);
3296 +
3297 + // Do we have this image
3298 + $args = array(
3299 + 'post_type' => 'attachment',
3300 + 'post_status' => 'inherit',
3301 + 'meta_query' => array(
3302 + array(
3303 + 'key' => 'pagelayer_image_md5',
3304 + 'value' => $md5,
3305 + )
3306 + )
3307 + );
3308 +
3309 + $query = new WP_Query($args);
3310 +
3311 + // If we found the image, return
3312 + foreach($query->posts as $ck => $cv){
3313 + return $cv->ID;
3314 + }
3315 +
3316 + $upload = wp_upload_bits($filename, null, $blob);
3317 +
3318 + if( !empty( $upload['error'] ) ) {
3319 + return false;
3320 + }
3321 +
3322 + $file_path = $upload['file'];
3323 + $file_name = basename( $file_path );
3324 + $file_type = wp_check_filetype( $file_name, null );
3325 + $attachment_title = sanitize_file_name( pathinfo( $file_name, PATHINFO_FILENAME ) );
3326 + $wp_upload_dir = wp_upload_dir();
3327 +
3328 + $post_info = array(
3329 + 'guid' => $wp_upload_dir['url'] . '/' . $file_name,
3330 + 'post_mime_type'=> $file_type['type'],
3331 + 'post_title' => $attachment_title,
3332 + 'post_content' => '',
3333 + 'post_status' => 'inherit',
3334 + );
3335 +
3336 + $attach_id = wp_insert_attachment( $post_info, $file_path, $parent_post_id );
3337 + update_post_meta($attach_id, 'pagelayer_image_md5', $md5);
3338 +
3339 + $lib = ABSPATH . 'site-admin/includes/image.php';
3340 + $media = ABSPATH . 'site-admin/includes/media.php';
3341 + $media = file_exists($media) ? $media : ABSPATH . 'wp-admin/includes/media.php';
3342 + $lib = file_exists($lib) ? $lib : ABSPATH . 'wp-admin/includes/image.php';
3343 +
3344 + require_once($media);
3345 + require_once($lib);
3346 +
3347 + $attach_data = wp_generate_attachment_metadata( $attach_id, $file_path );
3348 + wp_update_attachment_metadata( $attach_id, $attach_data );
3349 +
3350 + return $attach_id;
3351 +
3352 +}
3353 +
3354 +// Show the notice of importing the active themes content
3355 +function pagelayer_theme_import_notices($return = false){
3356 +
3357 + $theme = wp_get_theme();
3358 +
3359 + $imported = get_option('pagelayer_theme_'.get_template().'_imported');
3360 + $show = 0;
3361 +
3362 + // We need to import the content
3363 + if(empty($imported) && is_admin() && current_user_can('switch_themes')){
3364 + $show = 1;
3365 + }
3366 +
3367 + $dismissed = get_option('pagelayer_theme_'.get_template().'_dismissed');
3368 +
3369 + // Is this dismissed
3370 + if($dismissed){
3371 + $show = 0;
3372 + }
3373 +
3374 + //$show = 1;
3375 +
3376 + // Is it the importer page ?
3377 + if(!empty($_REQUEST['page']) && $_REQUEST['page'] == 'pagelayer_import'){
3378 + $show = 0;
3379 + }
3380 +
3381 + // Show the message
3382 + if(!empty($show)){
3383 + $str = '
3384 +
3385 +<style>
3386 +.pagelayer_promo_button {
3387 +background-color: #4CAF50; /* Green */
3388 +border: none;
3389 +color: white;
3390 +padding: 6px 10px;
3391 +text-align: center;
3392 +text-decoration: none;
3393 +display: inline-block;
3394 +font-size: 13px;
3395 +margin: 4px 2px;
3396 +-webkit-transition-duration: 0.4s; /* Safari */
3397 +transition-duration: 0.4s;
3398 +cursor: pointer;
3399 +}
3400 +.pagelayer_promo_button:focus,
3401 +.pagelayer_promo_button:hover{
3402 +border: none;
3403 +color: white;
3404 +box-shadow: 0 6px 8px 0 rgba(0,0,0,0.24), 0 9px 25px 0 rgba(0,0,0,0.19);
3405 +color: white;
3406 +}
3407 +.pagelayer_promo_buy {
3408 +color: white;
3409 +padding: 8px 12px;
3410 +font-size: 14px;
3411 +}
3412 +.pagelayer_promo_button1 {
3413 +color: white;
3414 +background-color: #4CAF50;
3415 +border:3px solid #4CAF50;
3416 +}
3417 +.pagelayer_promo_button1:hover {
3418 +border:3px solid #4CAF50;
3419 +}
3420 +.pagelayer_promo_button2 {
3421 +color: white;
3422 +background-color: #0085ba;
3423 +}
3424 +.pagelayer_promo_button3 {
3425 +color: white;
3426 +background-color: #365899;
3427 +}
3428 +.pagelayer_promo_button4 {
3429 +color: white;
3430 +background-color: rgb(66, 184, 221);
3431 +}
3432 +.pagelayer_promo-close{
3433 +float:right;
3434 +text-decoration:none;
3435 +margin: 5px 10px 0px 0px;
3436 +}
3437 +.pagelayer_promo-close:hover{
3438 +color: red;
3439 +}
3440 +</style>
3441 +
3442 +<script type="application/javascript">
3443 + jQuery(document).ready(function(){
3444 + jQuery("#pagelayer_promo .pagelayer_promo-close").click(function(){
3445 + var data;
3446 + jQuery("#pagelayer_promo").hide();
3447 + // Save this preference
3448 + jQuery.post("'.admin_url('?pagelayer_promo=0').'", data, function(response) {
3449 + //alert(response);
3450 + });
3451 + });
3452 + });
3453 +</script>
3454 +
3455 +<div class="notice notice-success" id="pagelayer_promo" style="min-height:30px">
3456 + <a class="pagelayer_promo-close" href="javascript:" aria-label="Dismiss this Notice">
3457 + <span class="dashicons dashicons-dismiss"></span> Dismiss
3458 + </a>';
3459 +
3460 + $str .= apply_filters('pagelayer_theme_import_notice_content', '<p>Please click <a href="'.admin_url('admin.php?page=pagelayer_import').'">here</a> to import your themes content</p>').'
3461 +
3462 +</div>';
3463 +
3464 + // Print it
3465 + if(empty($return)){
3466 + echo $str;
3467 + }else{
3468 + return $str;
3469 + }
3470 + }
3471 +
3472 +}
3473 +
3474 +// Parse the array variables
3475 +function pagelayer_replace_vars( $string, $array, $pre = '', $suf = ''){
3476 +
3477 + $array = (array) $array;
3478 +
3479 + foreach($array as $kk => $vv){
3480 +
3481 + if(is_array($vv)){
3482 + $vv = pagelayer_flat_join($vv);
3483 + }
3484 +
3485 + $string = str_replace($pre.$kk.$suf, $vv, $string);
3486 + }
3487 + return $string;
3488 +}
3489 +
3490 +// Add Attachment to the contact the email
3491 +function pagelayer_cf_email_attachment( &$phpmailer) {
3492 +
3493 + if ( ! empty( $_FILES ) ) {
3494 + foreach ( $_FILES as $attachment ) {
3495 +
3496 + if(empty($attachment['name'])){
3497 + continue;
3498 + }
3499 +
3500 + try {
3501 + $phpmailer->addAttachment($attachment['tmp_name'], $attachment['name'], $encoding = 'base64', $attachment['type'], $disposition = 'attachment');
3502 + } catch ( \Exception $e ) {
3503 + continue;
3504 + }
3505 + }
3506 + }
3507 +
3508 +}
3509 +
3510 +function pagelayer_array_flatten( $input ) {
3511 + if ( ! is_array( $input ) ) {
3512 + return array( $input );
3513 + }
3514 +
3515 + $output = array();
3516 +
3517 + foreach ( $input as $value ) {
3518 + $output = array_merge( $output, pagelayer_array_flatten( $value ) );
3519 + }
3520 +
3521 + return $output;
3522 +}
3523 +
3524 +function pagelayer_flat_join( $array ) {
3525 + $array = pagelayer_array_flatten( $array );
3526 + $output = array();
3527 +
3528 + foreach ( (array) $array as $value ) {
3529 + $output[] = trim( (string) $value );
3530 + }
3531 +
3532 + return implode( ', ', $output );
3533 +}
3534 +
3535 +// Verifies the Google Captcha
3536 +function pagelayer_captcha_verify(){
3537 +
3538 + // If secret key is not there, return
3539 + $captcha_secret = get_option('pagelayer_google_captcha_secret');
3540 +
3541 + if(empty($captcha_secret)){
3542 + return true;
3543 + }
3544 +
3545 + $response = !empty($_POST['g-recaptcha-response']) ? sanitize_text_field($_POST['g-recaptcha-response']) : '';
3546 +
3547 + $ip = pagelayer_getip();
3548 +
3549 + // Is the IP or response not there ?
3550 + if(empty($response) || empty($ip)){
3551 + return false;
3552 + }
3553 +
3554 + $url = 'https://www.google.com/recaptcha/api/siteverify';
3555 +
3556 + // Verify the post
3557 + $req = wp_remote_post($url, array(
3558 + 'timeout' => 20,
3559 + 'body' => array(
3560 + 'secret' => $captcha_secret,
3561 + 'response' => $response,
3562 + 'remoteip' => $ip
3563 + )
3564 + )
3565 + );
3566 +
3567 + // Was there an error posting ?
3568 + if(is_wp_error($req)){
3569 + return false;
3570 + }
3571 +
3572 + // Process the post response
3573 + $resp = wp_remote_retrieve_body($req);
3574 +
3575 + // Is the body valid
3576 + if(empty($resp)){
3577 + return false;
3578 + }
3579 +
3580 + $json = json_decode($resp, true);
3581 +
3582 + if(!empty($json['success']) && // for v2 and v3
3583 + (!isset($json['score']) || $json['score'] >= 0.5 && $json['action'] === 'submit') // For v3
3584 + ){
3585 + return true;
3586 + }
3587 +
3588 + return false;
3589 +}
3590 +
3591 +// Pagelayer serialize attributes
3592 +function pagelayer_serialize_attributes( $block_attributes ) {
3593 + $encoded_attributes = json_encode( $block_attributes, JSON_FORCE_OBJECT);
3594 + $encoded_attributes = preg_replace( '/--/', '\\u002d\\u002d', $encoded_attributes );
3595 + $encoded_attributes = preg_replace( '/</', '\\u003c', $encoded_attributes );
3596 + $encoded_attributes = preg_replace( '/>/', '\\u003e', $encoded_attributes );
3597 + // Regex: /\\"/
3598 + $encoded_attributes = preg_replace( '/\\\\"/', '\\u0022', $encoded_attributes );
3599 +
3600 + return $encoded_attributes;
3601 +}
3602 +
3603 +function pagelayer_enable_giver(){
3604 + global $pagelayer;
3605 + return !empty($pagelayer->settings['enable_giver']) && $pagelayer->settings['enable_giver'] == 1;
3606 +}
3607 +
3608 +function pagelayer_load_font_options(){
3609 + include_once(PAGELAYER_DIR.'/main/font-options.php');
3610 +}
3611 +
3612 +function pagelayer_is_utf8($str) {
3613 + return (bool) preg_match('//u', $str);
3614 +}
3615 +
3616 +// Create blank images
3617 +function pagelayer_create_blank_image($width, $height){
3618 +
3619 + $im = @imagecreate($width, $height);
3620 +
3621 + if(empty($im)){
3622 + return false;
3623 + }
3624 +
3625 + $background_color = imagecolorallocate($im, 220, 220, 220);
3626 + $text_color = imagecolorallocate($im, 110, 110, 110);
3627 + $string = $width.' x '.$height;
3628 + $font_size = 30;
3629 + $sx = ($width / 2) - (strlen($string) * $font_size / 2);
3630 + $sy = ($height / 2) - ($font_size / 2);
3631 + $font = realpath(PAGELAYER_DIR.'/fonts/OpenSans-BoldItalic.ttf');
3632 + imagettftext($im, $font_size, 0, $sx, $sy, $text_color, $font, $string);
3633 + ob_start();
3634 + imagepng($im);
3635 + imagedestroy($im);
3636 +
3637 + return ob_get_clean();
3638 +
3639 +}
3640 +
3641 +// Get category name by post type
3642 +function pagelayer_post_type_category($post_type){
3643 +
3644 + $taxonomies = get_object_taxonomies( $post_type, 'objects');
3645 +
3646 + foreach( $taxonomies as $key => $tax){
3647 + if(!empty($tax->hierarchical)){
3648 + return $key;
3649 + }
3650 + }
3651 +
3652 + return false;
3653 +
3654 +}
3655 +
3656 +// Get tag name by post type
3657 +function pagelayer_post_type_tag($post_type){
3658 +
3659 + $taxonomies = get_object_taxonomies( $post_type, 'objects');
3660 +
3661 + foreach( $taxonomies as $key => $tax){
3662 + if(!empty($tax->show_tagcloud) && empty($tax->hierarchical)){
3663 + return $key;
3664 + }
3665 + }
3666 + return false;
3667 +
3668 +}
3669 +
3670 +// Get checkboxes of post categories.
3671 +function pagelayer_post_cats($post, &$cat_name = ''){
3672 +
3673 + $ret = [];
3674 +
3675 + $cat_name = pagelayer_post_type_category($post->post_type);
3676 +
3677 + if(empty($cat_name)){
3678 + return $ret;
3679 + }
3680 +
3681 + include_once(ABSPATH.PAGELAYER_CMS_DIR_PREFIX.'-admin/includes/template.php');
3682 +
3683 + $args1 = array(
3684 + 'taxonomy' => $cat_name,
3685 + 'checked_ontop' => false,
3686 + 'echo' => false,
3687 + );
3688 +
3689 + $ret['with_checkbox'] = '<ul class="pagelayer-post-category" >'.wp_terms_checklist($post->ID, $args1 ).'</ul>';
3690 +
3691 + $args2 = array(
3692 + 'taxonomy' => $cat_name,
3693 + 'hierarchical' => true,
3694 + 'echo' => 0,
3695 + 'class' => 'pagelayer-add-cat',
3696 + 'name' => 'pagelayer_cat_parent',
3697 + 'style' => 'list',
3698 + 'hide_empty' => 0,
3699 + );
3700 +
3701 + $ret['without_checkbox'] = wp_dropdown_categories( $args2 );
3702 +
1778 3703 return $ret;
3704 +
3705 +}
3706 +
3707 +// Get checkboxes of post tags.
3708 +function pagelayer_post_tags($post, &$tag_name = ''){
3709 +
3710 + $ret = [];
3711 +
3712 + $tag_name = pagelayer_post_type_tag($post->post_type);
3713 +
3714 + if(empty($tag_name)){
3715 + return $ret;
3716 + }
3717 +
3718 + $ret['postTags'] = wp_get_post_terms( $post->ID, $tag_name );
3719 +
3720 + $ret['allTags'] = get_tags(array( 'hide_empty' => false, 'taxonomy' => $tag_name ));
3721 +
3722 + return $ret;
3723 +
3724 +}
3725 +
3726 +// Get users who can edit a particular post type
3727 +function pagelayer_post_authors_by_type($post_type){
3728 +
3729 + global $wp_post_types;
3730 +
3731 + if(empty($wp_post_types[$post_type])){
3732 + $users = get_users( array( 'role__in' => array( 'administrator' ) ) );
3733 + }else{
3734 + $req_role = $wp_post_types[$post_type]->cap->edit_posts;
3735 + $roles__in = [];
3736 + foreach( wp_roles()->roles as $role_slug => $role ){
3737 + if( ! empty( $role['capabilities'][$req_role] ) )
3738 + $roles__in[] = $role_slug;
3739 + }
3740 + $users = get_users( array( 'role__in' => $roles__in ) );
3741 + }
3742 +
3743 + $user_array = [];
3744 +
3745 + foreach ( $users as $user ) {
3746 + $user_array[$user->ID] = $user->display_name;
3747 + }
3748 +
3749 + return $user_array;
3750 +}
3751 +
3752 +function pagelayer_parent_post_prop($post){
3753 + global $wp_post_types;
3754 +
3755 + if(!empty($wp_post_types[$post->post_type]->hierarchical)){
3756 + return pagelayer_list_parents(0, 0, $post, []);
3757 + }else{
3758 + return [];
3759 + }
3760 +}
3761 +
3762 +function pagelayer_list_parents( $parent = 0, $level = 0, $post = null, $values=array('')){
3763 +
3764 + global $wpdb;
3765 +
3766 + $post = get_post( $post );
3767 +
3768 + $items = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_parent, post_title FROM $wpdb->posts
3769 + WHERE post_parent = %d
3770 + AND post_type = '".$post->post_type."'
3771 + AND post_status = 'publish'
3772 + ORDER BY menu_order", $parent ) );
3773 +
3774 + if ( !empty($items) ) {
3775 + foreach ( $items as $item ) {
3776 +
3777 + // A page cannot be its own parent.
3778 + if ( $post && $post->ID && (int) $item->ID === $post->ID ) {
3779 + continue;
3780 + }
3781 +
3782 + $pad = str_repeat( '-', $level );
3783 +
3784 + if(count($values)==0){
3785 + $values[] = '';
3786 + }
3787 +
3788 + $values[' '.$item->ID] = $pad.esc_html( $item->post_title );
3789 +
3790 + $values = pagelayer_list_parents($item->ID, $level + 1, $post, $values );
3791 + }
3792 + }
3793 +
3794 + return $values;
3795 +}
3796 +
3797 +// Maybe explode
3798 +function pagelayer_maybe_explode( $separator = ',', $string = '', $limit = PHP_INT_MAX ){
3799 +
3800 + if(is_array($string)){
3801 + return $string;
3802 + }
3803 +
3804 + return explode($separator, $string, $limit);
3805 +}
3806 +
3807 +// Maybe implode
3808 +function pagelayer_maybe_implode($array, $separator = ','){
3809 +
3810 + if(is_array($array)){
3811 + return @implode($separator, $array);
3812 + }
3813 +
3814 + if(is_null($array)){
3815 + return '';
3816 + }
3817 +
3818 + return $array;
3819 +
3820 +}
3821 +
3822 +// Sanitize text field or array
3823 +function pagelayer_sanitize_text_field($str) {
3824 +
3825 + if ( !is_object( $str ) && !is_array( $str ) ) {
3826 + return sanitize_text_field($str);
3827 + }
3828 +
3829 + if( is_array($str) ){
3830 + return array_map( 'pagelayer_sanitize_text_field', $str);
3831 + }
3832 +
3833 + return $str;
3834 +}
3835 +
3836 +// Sanitize posts data for WP_Query
3837 +function pagelayer_sanitize_posts_data($data, $only_allowed = true) {
3838 +
3839 + $allowed_keys = [
3840 + 'post_type', 'posts_per_page', 'order', 'orderby', 'paged',
3841 + 'filter_by', 'term', 'exc_term', 'cat', 'category_name',
3842 + 'tag', 'author', 'author_name', 'post__in', 'post__not_in',
3843 + 'include', 'exclude', 'search', 's', 'exact', 'sentence',
3844 + 'post_status', 'post_parent', 'offset',
3845 + 'posts_per_archive_page', 'page', 'ignore_sticky_posts'
3846 + ];
3847 +
3848 + $sanitized = [];
3849 +
3850 + foreach($data as $key => $value){
3851 + if($only_allowed && !in_array($key, $allowed_keys)) {
3852 + continue;
3853 + }
3854 +
3855 + $sanitized[$key] = pagelayer_sanitize_text_field($value);
3856 + }
3857 +
3858 + // Security: Restrict post_status to prevent information disclosure
3859 + // Only users who can read private posts or edit others' posts should be able to query non-public statuses
3860 + if(isset($sanitized['post_status'])){
3861 + $requested_status = $sanitized['post_status'];
3862 +
3863 + // If requesting something other than publish, verify permissions
3864 + if ($requested_status !== 'publish') {
3865 + // Check if the user has permission to read private posts or edit others' posts
3866 + // This prevents contributors from seeing titles of private posts they don't own.
3867 + if (!current_user_can('read_private_posts') && !current_user_can('edit_others_posts')) {
3868 + $sanitized['post_status'] = 'publish';
3869 + }
3870 + }
3871 + }else{
3872 + // Default to publish for safety if not specified
3873 + $sanitized['post_status'] = 'publish';
3874 + }
3875 +
3876 + if(isset($sanitized['posts_per_page'])){
3877 + $sanitized['posts_per_page'] = (int) $sanitized['posts_per_page'];
3878 + if ($sanitized['posts_per_page'] > 100) {
3879 + $sanitized['posts_per_page'] = 100;
3880 + }
3881 + }
3882 +
3883 + if(isset($sanitized['paged'])){
3884 + $sanitized['paged'] = (int) $sanitized['paged'];
3885 + }
3886 +
3887 + if(isset($sanitized['offset'])){
3888 + $sanitized['offset'] = (int) $sanitized['offset'];
3889 + }
3890 +
3891 + if(isset($sanitized['post__in']) && is_string($sanitized['post__in'])){
3892 + $sanitized['post__in'] = array_map('intval', explode(',', $sanitized['post__in']));
3893 + }
3894 +
3895 + if(isset($sanitized['post__not_in']) && is_string($sanitized['post__not_in'])){
3896 + $sanitized['post__not_in'] = array_map('intval', explode(',', $sanitized['post__not_in']));
3897 + }
3898 +
3899 + if(isset($sanitized['post_parent'])){
3900 + $sanitized['post_parent'] = (int) $sanitized['post_parent'];
3901 + }
3902 +
3903 + if(isset($sanitized['cat'])){
3904 + $sanitized['cat'] = (int) $sanitized['cat'];
3905 + }
3906 +
3907 + if(isset($sanitized['author'])){
3908 + $sanitized['author'] = (int) $sanitized['author'];
3909 + }
3910 +
3911 + return $sanitized;
3912 +}
3913 +
3914 +// Update nav menu item
3915 +function pagelayer_save_nav_menu_items($items){
3916 +
3917 + // Menu setting keys
3918 + $allowed_post = array('title' => 'post_title');
3919 + $allowed_meta = array('_pagelayer_content');
3920 +
3921 + foreach($items as $id => $values){
3922 +
3923 + $meta_input = array();
3924 + $id = (int) $id;
3925 + $post = get_post($id);
3926 +
3927 + // Post found ?
3928 + if(empty($post)){
3929 + continue;
3930 + }
3931 +
3932 + foreach($values as $kk => $vv){
3933 +
3934 + if(array_key_exists($kk, $allowed_post)){
3935 + $name = $allowed_post[$kk];
3936 + $post->$name = $vv;
3937 + }
3938 +
3939 + if(!in_array($kk, $allowed_meta)){
3940 + continue;
3941 + }
3942 +
3943 + $meta_input[$kk] = stripslashes_deep($vv);
3944 + }
3945 +
3946 + if(isset($meta_input['_pagelayer_content'])){
3947 + $meta_input['_pagelayer_content'] = base64_decode($meta_input['_pagelayer_content']);
3948 + }
3949 +
3950 + $post->meta_input = $meta_input;
3951 + wp_update_post($post);
3952 + }
3953 +}
3954 +
3955 +// Update customizer options
3956 +function pagelayer_save_customizer_options($customizer_options){
3957 + global $pagelayer;
3958 +
3959 + // To load customizer params array
3960 + if(empty($pagelayer->customizer_params)){
3961 + pagelayer_load_shortcodes();
3962 + }
3963 +
3964 + $customizer_options = apply_filters( 'pagelayer_save_customizer_options', $customizer_options );
3965 + $customizer_data = array();
3966 + $customizer_option_data = array();
3967 +
3968 + foreach($pagelayer->customizer_params as $ck => $cv){
3969 + foreach($pagelayer->screens as $sk => $sv){
3970 +
3971 + $screen = (!empty($sv) ? '_'.$sv : '');
3972 + $screen_key = $ck.$screen;
3973 +
3974 + // Save to pagelayer customizer mods
3975 + if( empty($cv['option']) ){
3976 + if( isset($customizer_options[$screen_key])){
3977 + $customizer_data[$screen_key] = $customizer_options[$screen_key];
3978 + }
3979 + continue;
3980 + }
3981 +
3982 + $option_key = $cv['option'].$screen;
3983 +
3984 + // Update option
3985 + if( isset($customizer_options[$screen_key] ) ) {
3986 + update_option( $option_key, $customizer_options[$screen_key]);
3987 + continue;
3988 + }
3989 +
3990 + delete_option($option_key);
3991 + }
3992 + }
3993 +
3994 + // Set Typography settings for global and current post type
3995 + $posttype=array(
3996 + '' => '', // Make this empty for global values
3997 + 'current' => $customizer_options['pagelayer_current_post_type']
3998 + );
3999 +
4000 + $typo = ['font-family', 'font-size', 'font-style', 'font-weight', 'font-variant', 'decoration-line', 'line-height', 'text-transform', 'text-spacing', 'word-spacing'];
4001 +
4002 + foreach($posttype as $type => $tv){
4003 +
4004 + foreach($pagelayer->css_settings as $set => $params){
4005 +
4006 + foreach($pagelayer->screens as $sk => $sv){
4007 +
4008 + $screen = (!empty($sv) ? '_'.$sv : '');
4009 + $post_type = (!empty($tv) ? '_'.$tv : '');
4010 + $post_key = (!empty($type) ? $type.'_' : '');
4011 + $post_key_set = $post_key.$set;
4012 +
4013 + // Create key as per customizer shortcode
4014 + $key = $post_key_set.$screen;
4015 +
4016 + // Create option keys for the global and current post type
4017 + $option_setting = empty($params['key']) ? 'pagelayer_'.$set.'_css'.$post_type : $params['key'].$post_type;
4018 +
4019 + $props = array(
4020 + 'color' => $post_key_set.'_color'.$screen,
4021 + 'padding' => $post_key_set.'_padding'.$screen,
4022 + 'margin' => $post_key_set.'_margin'.$screen,
4023 + 'background-color' => $post_key_set.'_background_color'.$screen,
4024 + );
4025 +
4026 + $globalsetting = array();
4027 +
4028 + // Set Typography
4029 + if(isset($customizer_options[$key])){
4030 + foreach($customizer_options[$key] as $k => $v){
4031 + if($v == 'Default' || empty($v)){
4032 + unset($customizer_options[$key][$k]);
4033 + }
4034 +
4035 + // For sidebar, width default should not be saved
4036 + if($set == 'sidebar' && $k == 'width' && $v == 20){
4037 + unset($customizer_options[$key][$k]);
4038 + }
4039 +
4040 + $globalsetting[$key][$typo[$k]] = $customizer_options[$key][$k];
4041 + unset($customizer_options[$key][$k]);
4042 + }
4043 + }
4044 +
4045 + // Set other props with typography
4046 + foreach($props as $pk => $pv){
4047 +
4048 + if(!isset($customizer_options[$pv])){
4049 + continue;
4050 + }
4051 +
4052 + $globalsetting[$key][$pk] = $customizer_options[$pv];
4053 + unset($customizer_options[$pv]);
4054 +
4055 + }
4056 +
4057 + // Are we to save ?
4058 + if(!empty($globalsetting[$key])){
4059 + update_option($option_setting.$screen, $globalsetting[$key]);
4060 + }else{
4061 + delete_option($option_setting.$screen);
4062 + }
4063 +
4064 + }
4065 +
4066 + }
4067 + }
4068 +
4069 + $customizer_data = apply_filters( 'pagelayer_customizer_options', $customizer_data, $customizer_options);
4070 +
4071 + update_option('pagelayer_customizer_mods', $customizer_data);
4072 +}
4073 +
4074 +// Get customizer options values
4075 +// return array to set customizer block attributes
4076 +function pagelayer_get_customizer_options(){
4077 + global $pagelayer, $post;
4078 +
4079 + $options = array();
4080 +
4081 + // To load customizer params array
4082 + if(empty($pagelayer->customizer_params)){
4083 + pagelayer_load_shortcodes();
4084 + }
4085 +
4086 + // Get values form options
4087 + foreach($pagelayer->customizer_params as $ck => $cv){
4088 +
4089 + if( empty($cv['option']) ){
4090 + continue;
4091 + }
4092 +
4093 + $screens = array('desktop' => '');
4094 +
4095 + if( !empty($cv['screen']) ){
4096 + $screens = $pagelayer->screens;
4097 + }
4098 +
4099 + foreach($screens as $sk => $sv){
4100 +
4101 + $screen = (!empty($sv) ? '_'.$sv : '');
4102 + $screen_key = $cv['option'].$screen;
4103 + $screen_ck = $ck.$screen;
4104 +
4105 + // Get option value
4106 + $c_option = get_option($screen_key);
4107 +
4108 + if(empty($c_option)){
4109 + continue;
4110 + }
4111 +
4112 + $options[$screen_ck] = $c_option;
4113 + }
4114 + }
4115 +
4116 +
4117 + // Get values from pagelayer mods
4118 + $mods = get_option('pagelayer_customizer_mods');
4119 +
4120 + if(!empty($mods)){
4121 + $options = array_merge($options, $mods);
4122 + }
4123 +
4124 + $posttype=array(
4125 + '' => '',
4126 + 'current' => $post->post_type
4127 + );
4128 +
4129 + $typo = ['font-family', 'font-size', 'font-style', 'font-weight', 'font-variant', 'decoration-line', 'line-height', 'text-transform', 'text-spacing', 'word-spacing'];
4130 +
4131 + foreach($posttype as $type => $tv){
4132 +
4133 + foreach($pagelayer->css_settings as $set => $params){
4134 +
4135 + foreach($pagelayer->screens as $sk => $sv){
4136 +
4137 + $screen = (!empty($sv) ? '_'.$sv : '');
4138 + $postprefix = (!empty($tv) ? '_'.$tv : '');
4139 + $postk = (!empty($type) ? $type.'_' : '');
4140 + $post_key_set = $postk.$set;
4141 +
4142 + // Create key as per customizer shortcode
4143 + $key = $post_key_set.$screen;
4144 +
4145 + // Create option keys for the global and current post type
4146 + $setting = empty($params['key']) ? 'pagelayer_'.$set.'_css'.$postprefix : $params['key'].$postprefix;
4147 +
4148 + $props = array(
4149 + 'color' => $post_key_set.'_color'.$screen,
4150 + 'padding' => $post_key_set.'_padding'.$screen,
4151 + 'margin' => $post_key_set.'_margin'.$screen,
4152 + 'background-color' => $post_key_set.'_background_color'.$screen,
4153 + );
4154 +
4155 + $optarray = get_option($setting.$screen);
4156 +
4157 + if(!empty($optarray)){
4158 +
4159 + // Get Typography
4160 + foreach($typo as $tk => $tv){
4161 +
4162 + $tval = '';
4163 +
4164 + if(isset($optarray[$tv])){
4165 + $tval = $optarray[$tv];
4166 + }
4167 +
4168 + $options[$key][$tk] = $tval;
4169 + }
4170 +
4171 + foreach($props as $pk => $pv){
4172 + if(empty( $optarray[$pk])){
4173 + continue;
4174 + }
4175 +
4176 + $options[$pv] = $optarray[$pk];
4177 + }
4178 + }
4179 + }
4180 + }
4181 + }
4182 +
4183 + $options = apply_filters( 'pagelayer_get_customizer_options', $options);
4184 +
4185 + return $options;
4186 +}
4187 +
4188 +// Sanitize style props for colors and fons
4189 +function pagelayer_load_global_palette(){
4190 + global $pagelayer;
4191 +
4192 + // Default global colors
4193 + $pagelayer->global_colors = array(
4194 + 'primary' => array(
4195 + 'title' => 'Primary',
4196 + 'value' => '#007bff',
4197 + ),
4198 + 'secondary' => array(
4199 + 'title' => 'Secondary',
4200 + 'value' => '#6c757d',
4201 + ),
4202 + 'text' => array(
4203 + 'title' => 'Text',
4204 + 'value' => '#1d1d1d',
4205 + ),
4206 + 'accent' => array(
4207 + 'title' => 'Accent',
4208 + 'value' => '#61ce70',
4209 + ),
4210 + );
4211 +
4212 + // Global color
4213 + $global_color = get_option('pagelayer_global_colors');
4214 +
4215 + if(!empty($global_color)){
4216 + $pagelayer->global_colors = json_decode($global_color, true);
4217 + }
4218 +
4219 + // Default global fonts
4220 + $pagelayer->global_fonts = array(
4221 + 'primary' => array(
4222 + 'title' => 'Primary',
4223 + 'value' => $pagelayer->default_font_styles(),
4224 + ),
4225 + 'secondary' => array(
4226 + 'title' => 'Secondary',
4227 + 'value' => $pagelayer->default_font_styles(array('font-family' => 'Roboto')),
4228 + ),
4229 + 'text' => array(
4230 + 'title' => 'Text',
4231 + 'value' => $pagelayer->default_font_styles(array('font-family' => 'Montserrat')),
4232 + ),
4233 + 'accent' => array(
4234 + 'title' => 'Accent',
4235 + 'value' => $pagelayer->default_font_styles(array('font-family' => 'Poppins')),
4236 + ),
4237 + );
4238 +
4239 + // Global font
4240 + $global_font = get_option('pagelayer_global_fonts');
4241 +
4242 + if(!empty($global_font)){
4243 + $pagelayer->global_fonts = json_decode($global_font, true);
4244 + }
4245 +}
4246 +
4247 +// Get global colors
4248 +function pagelayer_sanitize_global_color($val){
4249 + global $pagelayer;
4250 +
4251 + if(!is_string($val) || $val[0] != '$'){
4252 + return $val;
4253 + }
4254 +
4255 + $gkey = substr($val, 1);
4256 + $gcolor = isset($pagelayer->global_colors[$gkey]) ? $gkey : 'primary';
4257 + $val = 'var(--pagelayer-color-'.$gcolor.')';
4258 +
4259 + return $val;
4260 +}
4261 +
4262 +// Sanitize style props for colors and fons
4263 +function pagelayer_sanitize_global_style($val, $val_desk = '', $mode = 'desktop'){
4264 + global $pagelayer;
4265 +
4266 + // Sanitize Global background color
4267 + if( !empty($val['background-color']) && $val['background-color'][0] == '$'){
4268 + $val['background-color'] = pagelayer_sanitize_global_color($val['background-color']);
4269 + }
4270 +
4271 + // Sanitize Global color
4272 + if( !empty($val['color']) && $val['color'][0] == '$'){
4273 + $val['color'] = pagelayer_sanitize_global_color($val['color']);
4274 + }
4275 +
4276 + // Not set global for typo
4277 + if(empty($val['global-font']) && empty($val_desk['global-font'])){
4278 + return $val;
4279 + }
4280 +
4281 + $_desk_global = false;
4282 +
4283 + if(empty($val['global-font'])){
4284 + $font_key = @$val_desk['global-font'];
4285 + $_desk_global = true;
4286 + }else{
4287 + $font_key = $val['global-font'];
4288 + unset($val['global-font']);
4289 + }
4290 +
4291 + if(empty($font_key)){
4292 + return $val;
4293 + }
4294 +
4295 + if(!isset($pagelayer->global_fonts[$font_key])){
4296 + $font_key = 'primary';
4297 + }
4298 +
4299 + $_val = [];
4300 +
4301 + // Apply global typo
4302 + foreach($pagelayer->typo_props as $typo_key){
4303 +
4304 + if(!empty($val[$typo_key])){
4305 + continue;
4306 + }
4307 +
4308 + $global_val = $pagelayer->global_fonts[$font_key]['value'];
4309 +
4310 + if( empty($global_val[$typo_key]) || (is_array($global_val[$typo_key]) && empty($global_val[$typo_key][$mode])) || (!is_array($global_val[$typo_key]) && !empty($_desk_global) && $mode != 'desktop') ){
4311 + continue;
4312 + }
4313 +
4314 + $_val[$typo_key] = 'var(--pagelayer-font-'.$font_key.'-'.$typo_key.')';
4315 + }
4316 +
4317 + // This array contains other values like margin padding so we merge the array
4318 + $val = empty($val) ? array() : $val;
4319 + $val = array_merge($val, $_val);
4320 + return $val;
4321 +}
4322 +
4323 +//Check an empty array
4324 +function pagelayer_is_empty_array($arr){
4325 +
4326 + foreach ($arr as $key => $value) {
4327 + if (trim($value) !== '') return false;
4328 + }
4329 +
4330 + return true;
4331 +}
4332 +
4333 +// Pagelayer load font family
4334 +function pagelayer_load_font_family($font, $font_weight='', $font_style=''){
4335 + global $pagelayer;
4336 +
4337 + // Load global fonts
4338 + if(strripos($font, 'var(') !== false){
4339 + $matches = [];
4340 + preg_match('/(var\(--pagelayer-font-)(\w+)(-font-family\))/i', $font, $matches);
4341 + $font_key = @$matches[2];
4342 + $font = @$pagelayer->global_fonts[$font_key]['value']['font-family'];
4343 + }
4344 +
4345 + // Load global fonts weight
4346 + if(!empty($font_weight) && strripos($font_weight, 'var(') !== false){
4347 + $matches = [];
4348 + preg_match('/(var\(--pagelayer-font-)(\w+)(-font-weight\))/i', $font_weight, $matches);
4349 + $font_key = @$matches[2];
4350 +
4351 + // Convert in string
4352 + $font_weight = @$pagelayer->global_fonts[$font_key]['value']['font-weight'];
4353 + }
4354 +
4355 + // Load global fonts style
4356 + if(!empty($font_style) && strripos($font_style, 'var(') !== false){
4357 + $matches = [];
4358 + preg_match('/(var\(--pagelayer-font-)(\w+)(-font-style\))/i', $font_style, $matches);
4359 + $font_key = @$matches[2];
4360 +
4361 + // Convert in string
4362 + $font_style = @$pagelayer->global_fonts[$font_key]['value']['font-style'];
4363 + }
4364 +
4365 + if(empty($font)){
4366 + return;
4367 + }
4368 +
4369 + $font_weights = array('400' =>'400');
4370 + $font_style = empty($font_style) ? 'normal' : $font_style;
4371 + $font_style = in_array($font_style, ['italic', 'oblique']) ? 'i' : '';
4372 +
4373 + if(!empty($font_weight)){
4374 +
4375 + if(!is_array($font_weight)){
4376 + $font_weight = array($font_weight);
4377 + }
4378 +
4379 + foreach($font_weight as $weight){
4380 + $font_weights[$weight.$font_style] = $weight.$font_style;
4381 + }
4382 + }
4383 +
4384 + foreach($font_weights as $fkey => $fvalue){
4385 + $pagelayer->runtime_fonts[$font][$fkey] = $fvalue;
4386 + }
4387 +
4388 +}
4389 +
4390 +function pagelayer_update_plugin_notice(){
4391 + if(defined('SOFTACULOUS_PLUGIN_UPDATE_NOTICE')){
4392 + return;
4393 + }
4394 +
4395 + $to_update_plugins = apply_filters('softaculous_plugin_update_notice', []);
4396 +
4397 + if(empty($to_update_plugins)){
4398 + return;
4399 + }
4400 +
4401 + /* translators: %1$s is replaced with a "string" of name of plugins, and %2$s is replaced with "string" which can be "is" or "are" based on the count of the plugin */
4402 + $msg = sprintf(__('New versions of %1$s %2$s available. Updating ensures better performance, security, and access to the latest features.', 'pagelayer'), '<b>'.esc_html(implode(', ', $to_update_plugins)).'</b>', (count($to_update_plugins) > 1 ? 'are' : 'is')) . ' <a class="button button-primary" href='.esc_url(admin_url('plugins.php?plugin_status=upgrade')).'>Update Now</a>';
4403 +
4404 + define('SOFTACULOUS_PLUGIN_UPDATE_NOTICE', true); // To make sure other plugins don't return a Notice
4405 + echo '<div class="notice notice-info is-dismissible" id="pagelayer-plugin-update-notice">
4406 + <p>'.$msg. '</p>
4407 + </div>';
4408 +
4409 + wp_register_script('pagelayer-update-notice', '', ['jquery'], '', true);
4410 + wp_enqueue_script('pagelayer-update-notice');
4411 + wp_add_inline_script('pagelayer-update-notice', 'jQuery("#pagelayer-plugin-update-notice").on("click", function(e){
4412 + let target = jQuery(e.target);
4413 +
4414 + if(!target.hasClass("notice-dismiss")){
4415 + return;
4416 + }
4417 +
4418 + var data;
4419 +
4420 + // Hide it
4421 + jQuery("#pagelayer-plugin-update-notice").hide();
4422 +
4423 + // Save this preference
4424 + jQuery.post("'.admin_url('admin-ajax.php?action=pagelayer_close_update_notice').'&pagelayer_nonce='.wp_create_nonce('pagelayer_promo_nonce').'", data, function(response) {
4425 + //alert(response);
4426 + });
4427 + });');
4428 +}
4429 +
4430 +function pagelayer_update_plugin_notice_filter($plugins = []){
4431 + $plugins['pagelayer/pagelayer.php'] = 'Pagelayer';
4432 + return $plugins;
4433 +}
4434 +
4435 +function pagelayer_is_comment_mode(){
4436 +
4437 + if(file_exists(PAGELAYER_DIR.'/comment_mode.php') && !empty($_REQUEST['cmode'])){
4438 + include_once(PAGELAYER_DIR.'/comment_mode.php');
4439 + return true;
4440 + }
4441 +
4442 + return false;
4443 +}
4444 +
4445 +function pagelayer_esc_crlf($value){
4446 +
4447 + // Remove CRLF to prevent header injection
4448 + $value = str_replace(array("\r", "\n", "%0a", "%0d"), '', $value);
4449 +
4450 + // Trim spaces
4451 + $value = trim($value);
4452 +
4453 + return $value;
1779 4454 }