Guest User

Untitled

a guest
Sep 1st, 2025
32
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 135.73 KB | Source Code | 0 0
  1. <?php
  2. /**
  3.  * The 'Element Templates' class handles a hierarchical structure of styling templates to provide an easy way for a consistent layout of elements.
  4.  * Derived templates inherit settings from parents and can override values of parent template(s) if not locked.
  5.  *
  6.  * As templates is an often used term (in WP and Enfold Page Templates) UI will be using "Custom Elements"
  7.  *
  8.  * @author      Günter
  9.  * @since       4.8
  10.  */
  11. if( ! defined( 'ABSPATH' ) ) {  exit;  }    // Exit if accessed directly
  12.  
  13.  
  14. if( ! class_exists( 'aviaElementTemplates', false ) )
  15. {
  16.     class aviaElementTemplates
  17.     {
  18.         const POST_TYPE = 'alb_elements';
  19.         const TAXONOMY = 'alb_elements_entries';
  20.  
  21.         /**
  22.          * Holds the instance of this class
  23.          *
  24.          * @since 4.8
  25.          * @var aviaElementTemplates
  26.          */
  27.         static private $_instance = null;
  28.  
  29.         /**
  30.          * Flag if this feature has been enabled
  31.          *
  32.          * @since 4.8
  33.          * @var boolean|null
  34.          */
  35.         protected $enabled;
  36.  
  37.         /**
  38.          * Filterable post type
  39.          *
  40.          * @since 4.8
  41.          * @var string
  42.          */
  43.         protected $el_post_type;
  44.  
  45.         /**
  46.          * Filterable taxonomy
  47.          *
  48.          * @since 4.8
  49.          * @var string
  50.          */
  51.         protected $el_taxonomy;
  52.  
  53.         /**
  54.          * For performance reason only
  55.          *
  56.          * @since 4.8
  57.          * @var boolean|null
  58.          */
  59.         protected $is_edit_element_page;
  60.  
  61.         /**
  62.          * For performance reason only
  63.          *
  64.          * @since 4.8
  65.          * @var boolean|null
  66.          */
  67.         protected $is_element_overview_page;
  68.  
  69.         /**
  70.          * Cache queried templates
  71.          *  [post_id]   => array(
  72.          *                      'shortcode'     => string
  73.          *                      'attr'          => array (  key => value  )
  74.          *                  )
  75.          * @since 4.8
  76.          * @var array
  77.          */
  78.         protected $template_cache;
  79.  
  80.         /**
  81.          * Temp array used on update post to avoid double building of shortcode array
  82.          *      - [0]['template_id']
  83.          *
  84.          * @since 4.8
  85.          * @var array
  86.          */
  87.         protected $cached_update_sc_array;
  88.  
  89.         /**
  90.          * Save the filter value - set in "posts_where" filter hook
  91.          *
  92.          * @since 4.8
  93.          * @var string
  94.          */
  95.         protected $filter_category;
  96.  
  97.         /**
  98.          * Backend editor buttons info
  99.          *
  100.          * @since 4.8
  101.          * @var array
  102.          */
  103.         protected $editor_elements;
  104.  
  105.         /**
  106.          * Message to inform user about expired nonce
  107.          *
  108.          * @since 4.8
  109.          * @var string
  110.          */
  111.         protected $nonce_message;
  112.  
  113.         /**
  114.          * Message to inform user cannot perform requested action
  115.          *
  116.          * @since 4.8
  117.          * @var string
  118.          */
  119.         protected $no_capability;
  120.  
  121.         /**
  122.          * Tab name for condensed tab
  123.          *
  124.          * @since 4.8
  125.          * @var string|null
  126.          */
  127.         protected $custom_element_tab_name;
  128.  
  129.         /**
  130.          * Store filtered option value how to handle custom element for modal group subitems
  131.          *
  132.          * @since 4.8
  133.          * @var string          'first' | 'individually' | 'none'
  134.          */
  135.         protected $subitem_custom_element_handling;
  136.  
  137.         /**
  138.          * Return the instance of this class
  139.          *
  140.          * @since 4.8
  141.          * @return aviaElementTemplates
  142.          */
  143.         static public function instance()
  144.         {
  145.             if( is_null( aviaElementTemplates::$_instance ) )
  146.             {
  147.                 aviaElementTemplates::$_instance = new aviaElementTemplates();
  148.             }
  149.  
  150.             return aviaElementTemplates::$_instance;
  151.         }
  152.  
  153.         /**
  154.          *
  155.          * @since 4.8
  156.          */
  157.         protected function __construct()
  158.         {
  159.             $this->enabled = null;
  160.             $this->el_post_type = null;
  161.             $this->el_taxonomy = null;
  162.             $this->is_edit_element_page = null;
  163.             $this->is_element_overview_page = null;
  164.             $this->template_cache = array();
  165.             $this->cached_update_sc_array = array();
  166.             $this->filter_category = 'all';
  167.             $this->editor_elements = array();
  168.  
  169.             $this->nonce_message = __( 'Session has expired. Your requested action could not be executed. Please try again or reload the page.', 'avia_framework' );
  170.             $this->no_capability = __( 'Sorry, you have not enough rights to perform requested action.', 'avia_framework' );
  171.  
  172.             /**
  173.              * @since 4.8
  174.              */
  175.             $this->custom_element_tab_name = apply_filters( 'avf_custom_element_tab_name', __( 'Custom Elements', 'avia_framework' ) );
  176.  
  177.             $this->subitem_custom_element_handling = null;
  178.  
  179.             if( $this->element_templates_enabled() )
  180.             {
  181.                 $this->register_post_types();
  182.             }
  183.  
  184.             $this->activate_filters();
  185.         }
  186.  
  187.         /**
  188.          * @since 4.8
  189.          */
  190.         public function __destruct()
  191.         {
  192.             unset( $this->template_cache );
  193.             unset( $this->cached_update_sc_array );
  194.             unset( $this->editor_elements );
  195.         }
  196.  
  197.         /**
  198.          * Returns the filtered post type for ALB Element Templates
  199.          *
  200.          * @since 4.8
  201.          * @return string
  202.          */
  203.         public function get_post_type()
  204.         {
  205.             if( is_null( $this->el_post_type ) )
  206.             {
  207.                 /**
  208.                  * @since 4.8
  209.                  * @param string
  210.                  * @return string
  211.                  */
  212.                 $this->el_post_type = apply_filters( 'avf_custom_element_post_type', aviaElementTemplates::POST_TYPE );
  213.             }
  214.  
  215.             return $this->el_post_type;
  216.         }
  217.  
  218.         /**
  219.          * Returns the filtered taxonomy for ALB Element Templates
  220.          *
  221.          * @since 4.8
  222.          * @return string
  223.          */
  224.         public function get_taxonomy()
  225.         {
  226.             if( is_null( $this->el_taxonomy ) )
  227.             {
  228.                 /**
  229.                  * @since 4.8
  230.                  * @param string
  231.                  * @return string
  232.                  */
  233.                 $this->el_taxonomy = apply_filters( 'avf_custom_element_taxonomy', aviaElementTemplates::TAXONOMY );
  234.             }
  235.  
  236.             return $this->el_taxonomy;
  237.         }
  238.  
  239.         /**
  240.          * Attach to filters
  241.          *
  242.          * @since 4.8
  243.          */
  244.         protected function activate_filters()
  245.         {
  246.             add_action( 'avia_import_hook', array( $this, 'handler_avia_import_hook' ), 10 );
  247.  
  248.             //  must hook here to avoid endless loop when saving post
  249.             add_filter( 'wp_insert_post_parent', array( $this, 'handler_wp_insert_post_parent' ), 1000, 4 );
  250.  
  251.             add_filter( 'admin_body_class', array( $this, 'handler_admin_body_class' ) );
  252.             add_filter( 'avf_force_alb_usage', [ $this, 'handler_avf_force_alb_usage' ], 500, 2 );
  253.             add_filter( 'avf_cpt_support_post_types', [ $this, 'handler_avf_cpt_support_post_types' ], 10, 1 );
  254.  
  255.             add_filter( 'page_row_actions', array( $this, 'handler_page_row_actions' ), 10, 2 );
  256.             add_filter( 'display_post_states', array( $this, 'handler_display_post_states' ), 150, 2 );
  257.             add_filter( 'avf_ignore_duplicate_post_types', array( $this, 'handler_avf_ignore_duplicate_post_types' ), 10, 1 );
  258.  
  259.             add_filter( 'manage_edit-alb_elements_columns', array( $this, 'handler_edit_alb_elements_columns'), 10 );
  260.             add_action( 'manage_pages_custom_column', array( $this, 'handler_pages_custom_column'), 10, 2 );
  261.             add_action( 'manage_posts_custom_column', array( $this, 'handler_pages_custom_column'), 10, 2 );
  262.  
  263.             //  add additional dropdown filter boxes - see \wp-admin\includes\class-wp-posts-list-table.php
  264.             add_action( 'restrict_manage_posts', array( $this, 'handler_wp_restrict_manage_posts' ), 10, 2 );
  265.  
  266.             //  modify the query
  267.             add_filter( 'posts_where' , array( $this, 'handler_wp_posts_where' ), 10, 2 );
  268.             add_filter( 'posts_join', array( $this, 'handler_wp_posts_join' ), 10, 2 );
  269.  
  270.             add_action( 'add_meta_boxes', array( $this, 'handler_add_meta_boxes' ), 1000 );
  271.             add_action( 'admin_bar_menu', array( $this, 'handler_admin_bar_menu' ), 100 );
  272.  
  273.             add_action( 'ava_menu_page_added', array( $this, 'handler_ava_menu_page_added' ), 10, 4 );
  274.             add_filter( 'avf_builder_button_params', array( $this, 'handler_avf_builder_button_params' ), 10, 1 );
  275.  
  276.             add_action( 'post_submitbox_start', array( $this, 'handler_wp_post_submitbox_start' ), 10 );
  277.  
  278.             add_action( 'admin_action_duplicate_element_template', array( $this, 'handler_admin_action_duplicate_element_template' ), 10 );
  279.  
  280.             //  ajax callbacks
  281.             add_action( 'wp_ajax_avia_alb_element_check_title', array( $this, 'handler_ajax_element_check_title' ) );
  282.             add_action( 'wp_ajax_avia_alb_element_template_cpt_actions', array( $this, 'handler_ajax_element_template_cpt_actions' ) );
  283.             add_action( 'wp_ajax_avia_alb_element_template_update_content', array( $this, 'handler_ajax_element_template_update_content' ) );
  284.             add_action( 'wp_ajax_avia_alb_element_template_delete', array( $this, 'handler_ajax_element_template_delete' ) );
  285.         }
  286.  
  287.         /**
  288.          * Force register CPT when import a demo
  289.          *
  290.          * @since 6.0
  291.          */
  292.         public function handler_avia_import_hook()
  293.         {
  294.             $this->register_post_types();
  295.         }
  296.  
  297.         /**
  298.          * Filter to force usage of ALB for alb_elements posts. This will also hide the switch button with CSS
  299.          *
  300.          * @since 6.0
  301.          * @param boolean $force_alb
  302.          * @param WP_Post $post
  303.          * @return boolean
  304.          */
  305.         public function handler_avf_force_alb_usage( $force_alb, $post )
  306.         {
  307.             //  security check
  308.             if( ! $post instanceof WP_Post )
  309.             {
  310.                 return $force_alb;
  311.             }
  312.  
  313.             /**
  314.              * e.g. force all posts with this posttype to use ALB
  315.              */
  316.             if( $this->get_post_type() == $post->post_type )
  317.             {
  318.                 $force_alb = true;
  319.             }
  320.  
  321.             return $force_alb;
  322.         }
  323.  
  324.         /**
  325.          * Remove our CPT from list to allow modify edit table and default term settings
  326.          * We handle in this class.
  327.          *
  328.          * @since 6.0
  329.          * @param WP_Post_Type[] $pt_objs
  330.          * @return array
  331.          */
  332.         public function handler_avf_cpt_support_post_types( $pt_objs )
  333.         {
  334.             if( ! empty( $pt_objs ) )
  335.             {
  336.                 unset( $pt_objs[ $this->get_post_type() ] );
  337.             }
  338.  
  339.             return $pt_objs;
  340.         }
  341.  
  342.         /**
  343.          * Check post content for parent element id
  344.          *
  345.          * @since 4.8
  346.          * @param int $post_parent
  347.          * @param int $post_ID
  348.          * @param array $new_postarr
  349.          * @param array $postarr
  350.          */
  351.         public function handler_wp_insert_post_parent( $post_parent, $post_ID, $new_postarr, $postarr )
  352.         {
  353.             if( 0 == $post_ID || $postarr['post_type'] != $this->get_post_type() )
  354.             {
  355.                 return $post_parent;
  356.             }
  357.  
  358.             $shortcode = trim( $postarr['post_content'] );
  359.  
  360.             if( empty( $shortcode ) )
  361.             {
  362.                 return $post_parent;
  363.             }
  364.  
  365.             $this->cached_update_sc_array = $this->get_element_template_info_from_content( $shortcode );
  366.  
  367.             return $this->cached_update_sc_array[0]['template_id'];
  368.         }
  369.  
  370.         /**
  371.          * Add extra classes
  372.          *
  373.          * @since 4.8
  374.          * @param string $classes
  375.          * @return string
  376.          */
  377.         public function handler_admin_body_class( $classes )
  378.         {
  379.             if( ! $this->element_templates_enabled() )
  380.             {
  381.                 $classes .= ' avia-custom-elements-disabled';
  382.                 return $classes;
  383.             }
  384.  
  385.             $classes .= ' avia-custom-elements-enabled';
  386.  
  387.             if( 'admins_only' == $this->element_templates_enabled() && ! $this->current_user_can_manage() )
  388.             {
  389.                 $classes .= ' avia-custom-elements-admins-only';
  390.             }
  391.  
  392.             $modal = avia_get_option( 'alb_locked_modal_options', '' );
  393.  
  394.             switch( $modal )
  395.             {
  396.                 case '':
  397.                     $classes .= ' avia-modal-hide-locked-input-fields avia-modal-hide-element-show-locked-options';
  398.                     break;
  399.                 case 'hide_non_admin':
  400.                     if( ! current_user_can( 'manage_options' ) )
  401.                     {
  402.                         $classes .= ' avia-modal-hide-locked-input-fields avia-modal-hide-element-show-locked-options';
  403.                     }
  404.                     break;
  405.             }
  406.  
  407.             $handling = $this->subitem_custom_element_handling();
  408.  
  409.             switch( $handling )
  410.             {
  411.                 case 'none':
  412.                     $classes .= ' avia-subitem-element-handling avia-subitem-no-element avia-subitem-one-element';
  413.                     break;
  414.                 case 'individually':
  415.                     $classes .= ' avia-subitem-element-handling avia-subitem-individual-element';
  416.                     break;
  417.                 case 'first':
  418.                 default:
  419.                     $classes .= ' avia-subitem-element-handling avia-subitem-one-element';
  420.                     break;
  421.             }
  422.  
  423.             $hierarchical = avia_get_option( 'custom_el_hierarchical_templates' );
  424.  
  425.             switch( $hierarchical )
  426.             {
  427.                 case 'hierarchical':
  428.                     $classes .= ' avia-custom-elements-hierarchical';
  429.                     break;
  430.                 case '':
  431.                 default:
  432.                     $classes .= ' avia-custom-elements-non-hierarchical';
  433.                     break;
  434.             }
  435.  
  436.             global $post;
  437.  
  438.             if( ! $post instanceof WP_Post || $post->post_type != $this->get_post_type() )
  439.             {
  440.                 return $classes;
  441.             }
  442.  
  443.             $terms = get_the_terms( $post->ID, $this->get_taxonomy() );
  444.  
  445.             if( false === $terms )
  446.             {
  447.                 $classes .= ' avia-no-terms';
  448.             }
  449.             else if( is_array( $terms ) )
  450.             {
  451.                 foreach( $terms as $term )
  452.                 {
  453.                     $classes .= ' avia-term-' . $term->slug;
  454.                 }
  455.             }
  456.  
  457.             if( $this->allow_cpt_screens( 'body_class' ) )
  458.             {
  459.                 $classes .= ' avia-allow-cpt-screen';
  460.             }
  461.  
  462.             return $classes;
  463.         }
  464.  
  465.         /**
  466.          * Add a message to backend
  467.          *
  468.          * @since 4.8
  469.          * @param array $params
  470.          * @return array
  471.          */
  472.         public function handler_avf_builder_button_params( array $params )
  473.         {
  474.             if( ! $this->element_templates_enabled() )
  475.             {
  476.                 return $params;
  477.             }
  478.  
  479.             if( ! $this->is_edit_element_page() )
  480.             {
  481.                 return $params;
  482.             }
  483.  
  484.             $params['noteclass'] = 'av-notice-element-templates-cache';
  485.  
  486.             if( $this->allow_cpt_screens( 'builder_button_params' ) )
  487.             {
  488.                 $params['note']  = '<strong>' . __( 'Attention: It is not recommended to edit custom element templates here !', 'avia_framework' ) . '</strong><br />';
  489.                 $params['note'] .= __( 'Not all features provided are supported here and this might break layout. Please edit your elements using the &quot;Edit Element&quot; button from an advanced layout builder page.', 'avia_framework' );
  490.             }
  491.             else
  492.             {
  493.                 $params['note']  = '<strong>' . __( 'Editing a custom element template is not allowed from this screen.', 'avia_framework' ) . '</strong><br />';
  494.                 $params['note'] .= __( 'Please edit your elements using the &quot;Edit Element&quot; button from an advanced layout builder page.', 'avia_framework' );
  495.             }
  496.  
  497.             return $params;
  498.         }
  499.  
  500.  
  501.         /**
  502.          * Add ALB Elements as submenu to Theme Options Page
  503.          *
  504.          * @since 4.8
  505.          * @param string $top_level
  506.          * @param avia_adminpages $adminpages
  507.          * @param string $the_title
  508.          * @param string $menu_title
  509.          */
  510.         public function handler_ava_menu_page_added( $top_level, avia_adminpages $adminpages, $the_title, $menu_title )
  511.         {
  512.             if( ! $this->element_templates_enabled() )
  513.             {
  514.                 return;
  515.             }
  516.  
  517.             if( ! $this->allow_cpt_screens() )
  518.             {
  519.                 return;
  520.             }
  521.  
  522.             $obj = get_post_type_object( $this->get_post_type() );
  523.  
  524.             if( ! $obj instanceof WP_Post_Type )
  525.             {
  526.                 return;
  527.             }
  528.  
  529.             /**
  530.              * Possible WP Bug (WP 5.5.1 - Enfold 4.8)
  531.              *
  532.              * Main menu has capability 'manage_options'.
  533.              * If user has less capabilty than the added menus from here are shown but user cannot access the page because WP rechecks capability of main menu.
  534.              *
  535.              * In this case we have to add our own main menu with less cap.
  536.              */
  537.             $cap_new = $this->get_capability( 'new' );
  538.             $cap_edit = $this->get_capability( 'edit' );
  539.  
  540.             if( ! current_user_can( 'manage_options' ) && ( 'manage_options' != $cap_new || 'manage_options' != $cap_edit ) )
  541.             {
  542.                 $top_level = 'edit.php?post_type=' . $this->get_post_type();
  543.                 $cap = 'manage_options' != $cap_new ? $cap_new : $cap_edit;
  544.  
  545.                 add_menu_page(
  546.                             $the_title . __( ' ALB Elements', 'avia_framework' ),   // page title
  547.                             $menu_title . __( ' ALB Elements', 'avia_framework' ),  // menu title
  548.                             $cap,                                               // capability
  549.                             $top_level,                                         // menu slug (and later also database options key)
  550.                             '',                                                 // executing function
  551.                             "dashicons-admin-home",
  552.                             26
  553.                         );
  554.             }
  555.  
  556.             add_submenu_page(
  557.                             $top_level,                                     //$parent_slug
  558.                             $obj->label,                                    //$page_title
  559.                             $obj->labels->all_items,                        //$menu_title
  560.                             $this->get_capability( 'new' ),                 //$capability
  561.                             'edit.php?post_type=' . $this->get_post_type()
  562.                         );
  563.  
  564.             add_submenu_page(
  565.                             $top_level,                                         //$parent_slug
  566.                             $obj->label,                                        //$page_title
  567.                             $obj->labels->new_item,                             //$menu_title
  568.                             $this->get_capability( 'edit' ),                    //$capability
  569.                             'post-new.php?post_type=' . $this->get_post_type()
  570.                         );
  571.  
  572.         }
  573.  
  574.         /**
  575.          * Add a link to "New" in admin bar
  576.          *
  577.          * @since 4.8
  578.          * @param WP_Admin_Bar $wp_admin_bar
  579.          * @return WP_Admin_Bar
  580.          */
  581.         public function handler_admin_bar_menu( WP_Admin_Bar $wp_admin_bar )
  582.         {
  583.             if( ! $this->element_templates_enabled() )
  584.             {
  585.                 return;
  586.             }
  587.  
  588.             $allow_cpt_screen_opt = current_theme_supports( 'avia-custom-elements-cpt-screen' ) ? avia_get_option( 'custom_el_cpt_screen' ) : '';
  589.  
  590.             /**
  591.              * @since 4.8
  592.              * @param boolean
  593.              * @param string $context               'menu_page' | 'admin_bar_new
  594.              * @return boolean
  595.              */
  596.             $allow_cpt_screen = apply_filters( 'avf_custom_elements_cpt_screen', 'allow_cpt_screen' == $allow_cpt_screen_opt, 'admin_bar_new' );
  597.  
  598.             if( ! $allow_cpt_screen )
  599.             {
  600.                 return;
  601.             }
  602.  
  603.             $obj = get_post_type_object( $this->get_post_type() );
  604.  
  605.             if( ! $obj instanceof WP_Post_Type )
  606.             {
  607.                 return;
  608.             }
  609.  
  610.             if( ! current_user_can( $this->get_capability( 'new' ) ) )
  611.             {
  612.                 return;
  613.             }
  614.  
  615.             $args = array(
  616.                         'id'        => 'new-z' . $this->get_post_type(),
  617.                         'title'     => $obj->labels->singular_name,
  618.                         'href'      => 'post-new.php?post_type=' . $this->get_post_type(),
  619.                         'parent'    => 'new-content'
  620.                     );
  621.  
  622.             $wp_admin_bar->add_node( $args );
  623.  
  624.             return $wp_admin_bar;
  625.         }
  626.  
  627.         /**
  628.          * Remove slug metabox
  629.          *
  630.          * @since 4.8
  631.          */
  632.         public function handler_add_meta_boxes()
  633.         {
  634.             if( $this->is_edit_element_page() )
  635.             {
  636.                 remove_meta_box( 'slugdiv', $this->get_post_type(), 'normal' );
  637.             }
  638.         }
  639.  
  640.         /**
  641.          * Add a duplicate element link to WP actions meta box
  642.          *
  643.          * @since 4.8
  644.          */
  645.         public function handler_wp_post_submitbox_start()
  646.         {
  647.             global $post;
  648.  
  649.             if( ! $this->is_edit_element_page() || ! current_user_can( $this->get_capability( 'new' ) ) )
  650.             {
  651.                 return;
  652.             }
  653.  
  654.             $shortcode = Avia_Builder()->get_posts_alb_content( $post->ID );
  655.  
  656.             if( empty( $shortcode ) )
  657.             {
  658.                 return;
  659.             }
  660.  
  661.             /**
  662.              * Currently copying to another shortcode is not supported (makes only sense for columns)
  663.              *
  664.              * Might be added in a future release if requested
  665.              */
  666.             $sc_array = $this->get_element_template_info_from_content( $shortcode );
  667. //          $sc = Avia_Builder()->shortcode_class[ Avia_Builder()->shortcode[ $sc_array[0]['shortcode'] ] ];
  668. //          $duplicates = isset( $sc->config['duplicate_template'] ) ? $sc->config['duplicate_template'] : array( $sc_array[0]['shortcode'] => '' );
  669.  
  670.             $duplicates = array( $sc_array[0]['shortcode'] => '' );
  671.  
  672.             $notify_url = wp_nonce_url( admin_url( 'edit.php?post_type=' . $this->get_post_type() . '&action=duplicate_element_template&post=' . absint( $post->ID ) ), 'avia-duplicate-element-template_' . $post->ID );
  673.             $notify_text = __( 'Copy to a new draft', 'avia_framework' );
  674.             $html = '';
  675.  
  676.             foreach( $duplicates as $shortcode => $name )
  677.             {
  678.                 $url = $notify_url;
  679.                 $text = $notify_text;
  680.  
  681.                 if( ! empty( $name ) )
  682.                 {
  683.                     $url = add_query_arg( 'new_shortcode', $shortcode, $url );
  684.                     $text .= ' ' . $name;
  685.                 }
  686.  
  687.                 $html .= '<div id="duplicate-action">';
  688.                 $html .=    '<a class="submitduplicate duplication" href="' . esc_url( $url ) . '">';
  689.                 $html .=        esc_html( $text );
  690.                 $html .=    '</a>';
  691.                 $html .= '</div>';
  692.             }
  693.  
  694.             echo $html;
  695.         }
  696.  
  697.  
  698.         /**
  699.          * - Remove not wanted links in overview table
  700.          * - Add a link to duplicate
  701.          *
  702.          * @since 4.8
  703.          * @param array $actions
  704.          * @param WP_Post $post
  705.          * @return array
  706.          */
  707.         public function handler_page_row_actions( array $actions, $post )
  708.         {
  709.             if( ! $post instanceof WP_Post || $post->post_type != $this->get_post_type() )
  710.             {
  711.                 return $actions;
  712.             }
  713.  
  714.             //  Quick Edit Link
  715.             unset( $actions['inline hide-if-no-js'] );
  716.  
  717.             if( ! current_user_can( $this->get_capability( 'new' ) ) )
  718.             {
  719.                 return $actions;
  720.             }
  721.  
  722.             $shortcode = Avia_Builder()->get_posts_alb_content( $post->ID );
  723.  
  724.             if( empty( $shortcode ) )
  725.             {
  726.                 return $actions;
  727.             }
  728.  
  729.             $notify_url = wp_nonce_url( admin_url( 'edit.php?post_type=' . $this->get_post_type() . '&action=duplicate_element_template&amp;post=' . $post->ID ), 'avia-duplicate-element-template_' . $post->ID );
  730.  
  731.             $duplicate  = '<a href="' . $notify_url . '" aria-label="' . esc_attr__( 'Make a duplicate from this element template', 'avia_framework' ) . '" rel="permalink">';
  732.             $duplicate .=       esc_html__( 'Duplicate', 'avia_framework' );
  733.             $duplicate .= '</a>';
  734.  
  735.             $actions['duplicate'] = $duplicate;
  736.  
  737.             return $actions;
  738.         }
  739.  
  740.         /**
  741.          * Remove info set by theme
  742.          *
  743.          * @since 4.8
  744.          * @param array $post_states
  745.          * @param WP_Post $post
  746.          * @return array
  747.          */
  748.         public function handler_display_post_states( array $post_states, $post = null )
  749.         {
  750.             if( ! $post instanceof WP_Post || $post->post_type != $this->get_post_type() )
  751.             {
  752.                 return $post_states;
  753.             }
  754.  
  755.             unset( $post_states['avia_alb'] );
  756.             unset( $post_states['wp_editor'] );
  757.  
  758.             return $post_states;
  759.         }
  760.  
  761.         /**
  762.          * We have a custom duplicate logic - remove Enfold link
  763.          *
  764.          * @since 5.6.9
  765.          * @param array $post_types
  766.          * @return array
  767.          */
  768.         public function handler_avf_ignore_duplicate_post_types( $post_types = [] )
  769.         {
  770.             if( ! is_array( $post_types ) )
  771.             {
  772.                 $post_types = [];
  773.             }
  774.  
  775.             $post_types[] = $this->get_post_type();
  776.  
  777.             return $post_types;
  778.         }
  779.  
  780.         /**
  781.          * Add custom columns
  782.          *
  783.          * @since 4.8
  784.          * @param array $columns
  785.          * @return array
  786.          */
  787.         public function handler_edit_alb_elements_columns( $columns )
  788.         {
  789.             $newcolumns = array(
  790.                                 'cb'            => '',
  791.                                 'title'         => '',
  792.                                 'alb_element'   => __( 'ALB Element', 'avia_framework' ),
  793.                                 'alb_tooltip'   => __( 'Excerpt (= Tooltip)', 'avia_framework' ),
  794.                                 'alb_author'    => __( 'Author', 'avia_framework' )
  795.                             );
  796.  
  797.             unset( $columns['author'] );
  798.  
  799.             return array_merge( $newcolumns, $columns );
  800.         }
  801.  
  802.         /**
  803.          * Add custom values to columns
  804.          *
  805.          * @since 4.8
  806.          * @param string $column
  807.          * @param int $post_id
  808.          */
  809.         public function handler_pages_custom_column( $column, $post_id )
  810.         {
  811.             global $post;
  812.  
  813.             switch( $column )
  814.             {
  815.                 case 'alb_element':
  816.                     echo get_the_term_list( $post_id, $this->get_taxonomy(), '', ', ', '' );
  817.  
  818.                     $content = Avia_Builder()->get_posts_alb_content( $post->ID );
  819.                     if( empty( trim( $content) ) )
  820.                     {
  821.                         break;
  822.                     }
  823.  
  824.                     $sc_array = $this->get_element_template_info_from_content( $content );
  825.  
  826.                     if( array_key_exists( 'select_element_template', $sc_array[0]['attr'] ) && ( 'item' == $sc_array[0]['attr']['select_element_template'] ) )
  827.                     {
  828.                         $main = false;
  829.  
  830.                         $term = get_term_by( 'slug', $sc_array[0]['shortcode'], $this->get_taxonomy() );
  831.                         if( $term instanceof WP_Term )
  832.                         {
  833.                             $link = get_term_link( $term, $this->get_taxonomy() );
  834.  
  835.                             if( ! is_wp_error( $link ) )
  836.                             {
  837.                                 $main = '<a href="' . esc_url( $link ) . '" rel="tag">' . $term->name . '</a>';
  838.                             }
  839.                         }
  840.  
  841.                         if( false === $main )
  842.                         {
  843.                             $sc = Avia_Builder()->shortcode_class[ Avia_Builder()->shortcode[ $sc_array[0]['shortcode'] ] ];
  844.                             $main = $sc->config['name'];
  845.                         }
  846.  
  847.                         echo ' ( ' . $main . ' )';
  848.                     }
  849.                     break;
  850.  
  851.                 case 'alb_tooltip':
  852.                     echo '<p class="avia-element-tooltip">' . esc_html( trim( $post->post_excerpt ) ) . '</p>';
  853.                     break;
  854.  
  855.                 case 'alb_author':
  856.                     $name = get_the_author_meta( 'display_name', $post->post_author );
  857.                     if( empty( $name ) )
  858.                     {
  859.                         $name = get_the_author_meta( 'user_login', $post->post_author );
  860.                     }
  861.  
  862.                     echo '<a href="' . esc_url( get_author_posts_url( $post->post_author ) ) . '">' . esc_html( $name ) . '</a>';
  863.                     break;
  864.             }
  865.  
  866.         }
  867.  
  868.         /**
  869.          * Add our custom select boxes to top of the table before filter button
  870.          *
  871.          * @since 4.8
  872.          * @param string $post_type
  873.          * @return void
  874.          */
  875.         public function handler_wp_restrict_manage_posts( $post_type )
  876.         {
  877.             if ( $post_type != $this->get_post_type() )
  878.             {
  879.                 return;
  880.             }
  881.  
  882.             $taxonomies = get_object_taxonomies( $post_type, 'names' );
  883.  
  884.             if( empty( $taxonomies ) )
  885.             {
  886.                 return;
  887.             }
  888.  
  889.             $terms = get_terms( $taxonomies, array( 'orderby' =>  'name', 'oder' => 'ASC' ) );
  890.  
  891.             if( ! is_array( $terms ) || empty( $terms ) )
  892.             {
  893.                 return;
  894.             }
  895.  
  896.             $last_filter = ( ! is_numeric( $this->filter_category ) ) ? 'all' : (int) $this->filter_category ;
  897.  
  898.             echo    '<label class="screen-reader-text" for="av_elements_filter_elements">' . __( 'Filter by Elements', 'avia_framework' ) . '</label>';
  899.             echo    '<select id="av_elements_filter_elements" class="postform" name="av_elements_filter_elements">';
  900.             echo        '<option value="all" ' . selected( $last_filter, 'all', false ) . '>'. __( 'All ALB Elements', 'avia_framework' ) . '</option>';
  901.  
  902.             foreach ( $terms as $term )
  903.             {
  904.                 echo    '<option value="' . $term->term_id . '" ' . selected( $last_filter, $term->term_id, false ) . '>'. $term->name . '</option>';
  905.             }
  906.  
  907.             echo '<select>';
  908.  
  909.             $args = array(
  910.                     'has_published_posts'   => array( $this->get_post_type() )
  911.                 );
  912.  
  913.             $users = get_users( $args );
  914.             $uids = array();
  915.  
  916.             foreach( $users  as $user )
  917.             {
  918.                 $uids[] = $user->ID;
  919.             }
  920.  
  921.             $sel_user = isset( $_REQUEST['av_elements_filter_user'] ) ? $_REQUEST['av_elements_filter_user'] : 0;
  922.             $sel_user = is_numeric( $sel_user ) && ( (int) $sel_user >= 0 ) ? (int) $sel_user : 0;
  923.  
  924.             $args = array(
  925.                     'show_option_all'   => __( 'All users', 'avia_framework' ),
  926.                     'orderby'           => 'display_name',
  927.                     'order'             => 'ASC',
  928.                     'include'           => $uids,
  929.                     'name'              => 'av_elements_filter_user',
  930.                     'id'                => 'av_elements_filter_user',
  931.                     'class'             => 'postform',
  932.                     'selected'          => $sel_user
  933.                 );
  934.  
  935.             wp_dropdown_users( $args );
  936.         }
  937.  
  938.         /**
  939.          * We add our filter parameters from the select boxes to the WP query
  940.          *
  941.          * @since v1.0.0
  942.          *
  943.          * @param string $where
  944.          * @param WP_Query $query
  945.          * @return string
  946.          */
  947.         public function handler_wp_posts_where( $where, WP_Query $query )
  948.         {
  949.             global $wpdb;
  950.  
  951.             if ( ! $this->is_element_overview_page() )
  952.             {
  953.                 return $where;
  954.             }
  955.  
  956.             if( ( ! isset( $_REQUEST['filter_action'] ) ) || ( 'filter' != strtolower( $_REQUEST['filter_action'] ) ) )
  957.             {
  958.                 return $where;
  959.             }
  960.  
  961.             $filter_category = isset( $_REQUEST['av_elements_filter_elements'] ) ? $_REQUEST['av_elements_filter_elements'] : 'all';
  962.             $this->filter_category = ( ! is_numeric( $filter_category ) ) ? 'all' : (int) $filter_category;
  963.  
  964.             $filter_user = isset( $_REQUEST['av_elements_filter_user'] ) ? $_REQUEST['av_elements_filter_user'] : 0;
  965.  
  966.             if( is_numeric( $filter_user ) && (int) $filter_user > 0 )
  967.             {
  968.                 $filter_user = (int) $filter_user;
  969.                 $where .=   " AND  ( {$wpdb->posts}.post_author = {$filter_user} ) ";
  970.             }
  971.  
  972.             return $where;
  973.         }
  974.  
  975.         /**
  976.          * We add our filter parameters from the select boxes to the WP query
  977.          *
  978.          * @since v1.0.0
  979.          * @global type $wpdb
  980.          *
  981.          * @param string $join
  982.          * @param WP_Query $query
  983.          * @return string
  984.          */
  985.         public function handler_wp_posts_join( $join, WP_Query $query )
  986.         {
  987.             global $wpdb;
  988.  
  989.             if ( ! $this->is_element_overview_page() )
  990.             {
  991.                 return $join;
  992.             }
  993.  
  994.             if( ( ! isset( $_REQUEST['filter_action'] ) ) || ( 'filter' != strtolower( $_REQUEST['filter_action'] ) ) )
  995.             {
  996.                 return $join;
  997.             }
  998.  
  999.             if( 'all' != $this->filter_category )
  1000.             {
  1001.                 $join .=    "   INNER JOIN {$wpdb->term_relationships} AS tr_category
  1002.                                         ON ({$wpdb->posts}.ID = tr_category.object_id)
  1003.                                 INNER JOIN {$wpdb->term_taxonomy} AS tt_category
  1004.                                         ON (tr_category.term_taxonomy_id = tt_category.term_taxonomy_id) AND (tt_category.term_id = {$this->filter_category} )
  1005.                             ";
  1006.             }
  1007.  
  1008.             return $join;
  1009.         }
  1010.  
  1011.         /**
  1012.          * Request to duplicate an element template.
  1013.          * Called from links in element overview page or from single element action meta box
  1014.          * Reroutes to new created element for editing
  1015.          *
  1016.          * @since 4.8
  1017.          */
  1018.         public function handler_admin_action_duplicate_element_template()
  1019.         {
  1020.             if ( empty( $_REQUEST['post'] ) )
  1021.             {
  1022.                 wp_die( esc_html__( 'No element template to duplicate has been supplied!', 'avia_framework' ) );
  1023.             }
  1024.  
  1025.             $element_id = isset( $_REQUEST['post'] ) ? absint( $_REQUEST['post'] ) : '';
  1026.  
  1027.             check_admin_referer( 'avia-duplicate-element-template_' . $element_id );
  1028.  
  1029.             $element = $this->get_post( $element_id );
  1030.  
  1031.             if( ! $element instanceof WP_Post || $element->post_type != $this->get_post_type() )
  1032.             {
  1033.                 wp_die( esc_html__( 'Invalid element template to duplicate has been supplied or template has been deleted.', 'avia_framework' ) );
  1034.             }
  1035.  
  1036.             $postarr = array(
  1037.                     'post_title'    => $element->post_title . ' ' . __( '(Copy)', 'avia_framework' ),
  1038.                     'post_content'  => $element->post_content,
  1039.                     'post_excerpt'  => $element->post_excerpt,
  1040.                     'post_author'   => get_current_user_id(),
  1041.                     'post_status'   => 'draft',
  1042.                     'post_type'     => $element->post_type,
  1043.                     'post_parent'   => $element->post_parent
  1044.                 );
  1045.  
  1046.             $duplicate_id = wp_insert_post( $postarr );
  1047.  
  1048.             if( $duplicate_id instanceof WP_Error || $duplicate_id == 0 )
  1049.             {
  1050.                 wp_die( esc_html__( 'Creating a copy of element template has failed. Please try again', 'avia_framework' ) );
  1051.             }
  1052.  
  1053.             $meta = Avia_Builder()->get_alb_meta_key_names( $element_id, 'save' );
  1054.  
  1055.             if( is_array( $meta ) )
  1056.             {
  1057.                 foreach( $meta as $key )
  1058.                 {
  1059.                     $value = get_post_meta( $element_id, $key, true );
  1060.                     update_post_meta( $duplicate_id, $key, $value );
  1061.                 }
  1062.             }
  1063.  
  1064.             $terms = get_the_terms( $element_id, $this->get_taxonomy() );
  1065.  
  1066.             if( is_array( $terms ) )
  1067.             {
  1068.                 foreach( $terms as $term )
  1069.                 {
  1070.                     wp_set_object_terms( $duplicate_id, $term->term_id, $this->get_taxonomy(), false );
  1071.                 }
  1072.             }
  1073.  
  1074.             // Redirect to the edit screen for the new draft page.
  1075.             wp_redirect( admin_url( 'post.php?action=edit&post=' . $duplicate_id ) );
  1076.  
  1077.             exit;
  1078.         }
  1079.  
  1080.         /**
  1081.          * Checks if the title for a custom element template is unique
  1082.          *
  1083.          * @since 4.8
  1084.          */
  1085.         public function handler_ajax_element_check_title()
  1086.         {
  1087.             header( 'Content-Type: application/json' );
  1088.  
  1089.             $response = array(
  1090.                         '_ajax_nonce'   => wp_create_nonce( 'avia_nonce_loader' ),
  1091.                         'success'       => false,
  1092.                         'message'       => ''
  1093.                     );
  1094.  
  1095.             if( ! $this->current_user_can_manage() )
  1096.             {
  1097.                 $response['message'] = $this->no_capability;
  1098.                 echo json_encode( $response );
  1099.                 exit;
  1100.             }
  1101.  
  1102.             $return = check_ajax_referer( 'avia_nonce_loader', '_ajax_nonce', false );
  1103.  
  1104.             /**
  1105.              * Return error and allow to resend data
  1106.              */
  1107.             if( false === $return )
  1108.             {
  1109.                 $response['success'] = false;
  1110.                 $response['expired_nonce'] = true;
  1111.                 echo json_encode( $response );
  1112.                 exit;
  1113.             }
  1114.  
  1115.             $title = isset( $_REQUEST['title'] ) ? $_REQUEST['title'] : '';
  1116.             $title_new = $this->sanitize_element_title( $title );
  1117.  
  1118.             $id = post_exists( $title_new, '', '', $this->get_post_type() );
  1119.  
  1120.             $message = array();
  1121.  
  1122.             if( $id != 0 )
  1123.             {
  1124.                 $message[] = __( 'This element title already exists.', 'avia_framework' );
  1125.             }
  1126.  
  1127.             if( $title != $title_new )
  1128.             {
  1129.                 $message[] = __( 'Title will be changed to:', 'avia_framework' ) . ' ' . $title_new;
  1130.             }
  1131.  
  1132.             $response['message'] = implode( '<br />', $message );
  1133.  
  1134.             $response['success'] = true;
  1135.             echo json_encode( $response );
  1136.             exit;
  1137.         }
  1138.  
  1139.         /**
  1140.          * Callback handler for Custom Elements CPT actions from modal popup in ALB editor:
  1141.          *
  1142.          *      'new_custom_element':       Creates a new element template based on a clean ALB element
  1143.          *      'update_element_post_data': Updates title and tooltip for an existing element
  1144.          *      'new_element_from_alb':     Creates a new custom element template based on the settings of an ALB element (supports base and item elements)
  1145.          *
  1146.          * Logic for subitem custom elements subitem_custom_element_handling():
  1147.          *      '':                 One template is used for all subitems, creating and editing is managed in base element
  1148.          *      'individually':     Each subitem can have a different template, templates must be created manually and selected for each subitem
  1149.          *      'none':             No templates at all are allowed for subitems
  1150.          *
  1151.          * @since 4.8
  1152.          */
  1153.         public function handler_ajax_element_template_cpt_actions()
  1154.         {
  1155.             header( 'Content-Type: application/json' );
  1156.  
  1157.             $response = array(
  1158.                         '_ajax_nonce'   => wp_create_nonce( 'avia_nonce_loader' ),
  1159.                         'success'       => false,
  1160.                         'message'       => ''
  1161.                     );
  1162.  
  1163.             if( ! $this->current_user_can_manage() )
  1164.             {
  1165.                 $response['message'] = $this->no_capability;
  1166.                 echo json_encode( $response );
  1167.                 exit;
  1168.             }
  1169.  
  1170.             $return = check_ajax_referer( 'avia_nonce_loader', '_ajax_nonce', false );
  1171.  
  1172.             /**
  1173.              * Return error and allow to resend data
  1174.              */
  1175.             if( false === $return )
  1176.             {
  1177.                 $response['expired_nonce'] = $this->nonce_message;
  1178.                 echo json_encode( $response );
  1179.                 exit;
  1180.             }
  1181.  
  1182.             $msg1 = __( 'Custom Element could not be created/updated.', 'avia_framework' ) . ' ';
  1183.             $msg2 = __( 'Not enough information provided.', 'avia_framework' );
  1184.  
  1185.             $subaction = isset( $_REQUEST['subaction'] ) ? $_REQUEST['subaction'] : '';
  1186.             $params = isset( $_REQUEST['modal_params'] ) ? $_REQUEST['modal_params'] : array();
  1187.  
  1188.             if( ! is_array( $params ) || empty( $params ) || ! in_array( $subaction, array( 'new_custom_element', 'update_element_post_data', 'new_element_from_alb' ) ) )
  1189.             {
  1190.                 $response['message'] = $msg1 . $msg2;
  1191.                 echo json_encode( $response );
  1192.                 exit;
  1193.             }
  1194.  
  1195.             $sc = isset( $params['add_new_element_shortcode'] ) ? $params['add_new_element_shortcode'] : '';
  1196.             $sc = explode( ';', $sc );
  1197.             $is_item = false;
  1198.  
  1199.             //  shortcode_handler || shortcode_item;shortcode_handler
  1200.             if( isset( $sc[1] ) && ! empty( $sc[1] ) )
  1201.             {
  1202.                 $shortcode_handler = trim( $sc[1] );
  1203.                 $item_sc = $sc[0];
  1204.                 $is_item = true;
  1205.             }
  1206.             else
  1207.             {
  1208.                 $shortcode_handler = $sc[0];
  1209.                 $item_sc = '';
  1210.             }
  1211.  
  1212.             if( empty( $shortcode_handler ) || ! array_key_exists( $shortcode_handler, Avia_Builder()->shortcode ) )
  1213.             {
  1214.                 $response['message'] = $msg1 . $msg2;
  1215.                 echo json_encode( $response );
  1216.                 exit;
  1217.             }
  1218.  
  1219.             if( $is_item && $this->subitem_custom_element_handling() != 'individually' )
  1220.             {
  1221.                 $response['message'] = $msg1 . $msg2;
  1222.                 echo json_encode( $response );
  1223.                 exit;
  1224.             }
  1225.  
  1226.             $shortcode = Avia_Builder()->shortcode_class[ Avia_Builder()->shortcode[ $shortcode_handler ] ];
  1227.  
  1228.             if( $is_item )
  1229.             {
  1230.                 if( ! isset( $shortcode->config['shortcode_nested'][0] ) || $shortcode->config['shortcode_nested'][0] != $item_sc )
  1231.                 {
  1232.                     $response['message'] = $msg1 . __( 'Illegal shortcode.', 'avia_framework' );
  1233.                     echo json_encode( $response );
  1234.                     exit;
  1235.                 }
  1236.             }
  1237.  
  1238.             $element_id = isset( $params['element_id'] ) ? $params['element_id'] : 0;
  1239.             $template = null;
  1240.             $content = '';
  1241.             $tree = array();
  1242.             $js_templates = array();
  1243.  
  1244.             $title = isset( $params['base_element_title'] ) ? $params['base_element_title'] : $shortcode->config['name'] . ' ' . __( 'Copy', 'avia_framework' );
  1245.             $title = $this->sanitize_element_title( $title );
  1246.             $tooltip = isset( $params['base_element_tooltip'] ) ? $params['base_element_tooltip'] : $shortcode->config['tooltip'];
  1247.             $author = get_current_user_id();
  1248.  
  1249.             if( 'new_custom_element' == $subaction )
  1250.             {
  1251.                 $this->add_element_template_options( $shortcode );
  1252.  
  1253.                 if( $this->is_editable_modal_group_element( $shortcode ) && 'first' == $this->subitem_custom_element_handling() )
  1254.                 {
  1255.                     //  Create the item template for this element
  1256.                     $outer_attr = $shortcode->get_default_sc_args();
  1257.                     $item_attr = $shortcode->get_default_modal_group_args();
  1258.                     $item_content = $this->create_custom_element_shortcode( $shortcode, $item_attr, $outer_attr, true );
  1259.                     $item_tree = ShortcodeHelper::build_shortcode_tree( $item_content );
  1260.                     $tt = ! empty( $tooltip ) ? '(' . $tooltip . ')' : '';
  1261.  
  1262.                     $this->cached_update_sc_array = $this->get_element_template_info_from_content( $item_content );
  1263.  
  1264.                     $postarr = array(
  1265.                                 'post_author'   => $author,
  1266.                                 'post_title'    => sprintf( __( 'Item for: %s', 'avia_framework' ), $title ),
  1267.                                 'post_content'  => $item_content,
  1268.                                 'post_excerpt'  => sprintf( __( 'An item for %s %s', 'avia_framework' ), $title, $tt ),
  1269.                                 'post_status'   => 'publish',
  1270.                                 'post_type'     => $this->get_post_type(),
  1271.                                 'post_parent'   => $this->cached_update_sc_array[0]['template_id']
  1272.                             );
  1273.  
  1274.                     $item_id = wp_insert_post( $postarr );
  1275.  
  1276.                     if( ! is_numeric( $item_id ) || 0 == $item_id )
  1277.                     {
  1278.                         $response['message'] = $msg1;
  1279.                         echo json_encode( $response );
  1280.                         exit;
  1281.                     }
  1282.  
  1283.                     Avia_Builder()->save_posts_alb_content( $item_id, $item_content );
  1284.                     Avia_Builder()->set_alb_builder_status( 'active', $item_id );
  1285.                     Avia_Builder()->save_shortcode_tree( $item_id, $item_tree );
  1286.  
  1287.                     //  assign correct category to element template
  1288.                     $this->updated_post_content( $item_content, $item_id, $item_tree, true );
  1289.  
  1290.                     $item_args = $this->cached_update_sc_array[0]['attr'];
  1291.                     $item_raw_content = $this->cached_update_sc_array[0]['raw_content'];
  1292.  
  1293.                     $js_templates = $this->js_scripts_from_array( $shortcode, $item_args, $item_raw_content, $item_id );
  1294.  
  1295.                     $outer_attr['one_element_template'] = $item_id;
  1296.                     $outer_attr['content'] = array( $item_attr );
  1297.                     $outer_attr['content'][0]['element_template'] = $item_id;
  1298.  
  1299.                     $content = $this->create_custom_element_shortcode( $shortcode, $outer_attr, array(), false );
  1300.                 }
  1301.                 else
  1302.                 {
  1303.                     if( $is_item )
  1304.                     {
  1305.                         $this->set_as_item_template( $shortcode );
  1306.                     }
  1307.  
  1308.                     $content = $shortcode->prepare_editor_element( false, array(), 'element', 'shortcode_only' );
  1309.                 }
  1310.  
  1311.                 $tree = ShortcodeHelper::build_shortcode_tree( $content );
  1312.             }
  1313.             else if( 'update_element_post_data' == $subaction )
  1314.             {
  1315.                 //  check for correct shortcode
  1316.                 $this->load_template_cache( $element_id );
  1317.                 $check_sc = ! $is_item ? $shortcode_handler : $item_sc;
  1318.  
  1319.                 $template = $this->get_template( $element_id, $check_sc );
  1320.  
  1321.                 if( false === $template )
  1322.                 {
  1323.                     $response['message'] = $msg1 . ' ' . __( 'Custom Element and stored Custom Element do not match.', 'avia_framework' );
  1324.                     echo json_encode( $response );
  1325.                     exit;
  1326.                 }
  1327.             }
  1328.             else if( 'new_element_from_alb' == $subaction )
  1329.             {
  1330.                 $this->add_element_template_options( $shortcode );
  1331.  
  1332.                 $ajax_param = isset( $_REQUEST['ajax_param'] ) ? $_REQUEST['ajax_param'] : array();
  1333.                 $outer_attr = isset( $ajax_param['outer_sc_params'] ) ? AviaHelper::clean_attributes_array( $ajax_param['outer_sc_params'] ) : array();
  1334.                 $attr = isset( $ajax_param['sc_params'] ) ? AviaHelper::clean_attributes_array( $ajax_param['sc_params'] ) : array();
  1335.  
  1336.                 if( ! $is_item && $this->is_editable_modal_group_element( $shortcode ) )
  1337.                 {
  1338.                     if( in_array( $this->subitem_custom_element_handling(), array( 'first', 'none' ) ) )
  1339.                     {
  1340.                         //  only keep first subitem (hidden by CSS if necessary)
  1341.                         if( isset( $attr['content'] ) && is_array( $attr['content'] ) && ! empty( $attr['content'] ) )
  1342.                         {
  1343.                             $attr['content'] = array( $attr['content'][0] );
  1344.                         }
  1345.                     }
  1346.                 }
  1347.  
  1348.                 $content = $this->create_custom_element_shortcode( $shortcode, $attr, $outer_attr, $is_item );
  1349.                 $tree = ShortcodeHelper::build_shortcode_tree( $content );
  1350.             }
  1351.  
  1352.             $modal_title = sprintf( __( ' - Customize: %s', 'avia_framework' ), $title );
  1353.             $modal_subitem_title = '';
  1354.             $update_modal_title = $shortcode->config['name'] . $modal_title;
  1355.  
  1356.             if( $is_item )
  1357.             {
  1358.                 $modal_title =  sprintf( __( ' - Customize Item: %s', 'avia_framework' ), $title );
  1359.                 $modal_subitem_title = sprintf( __( ' - Customize: %s', 'avia_framework' ), $title );
  1360.                 $update_modal_title = $shortcode->config['name'] . $modal_subitem_title;
  1361.             }
  1362.  
  1363.             $this->cached_update_sc_array = is_null( $template ) ? $this->get_element_template_info_from_content( $content ) : $template['sc_array'];
  1364.  
  1365.             if( 'update_element_post_data' == $subaction )
  1366.             {
  1367.                 $postarr = array(
  1368.                         'ID'            => $element_id,
  1369.                         'post_title'    => $title,
  1370.                         'post_excerpt'  => $tooltip,
  1371.                         'post_status'   => 'publish',
  1372.                         'post_type'     => $this->get_post_type(),
  1373.                         'post_parent'   => $this->cached_update_sc_array[0]['template_id']
  1374.                     );
  1375.  
  1376.                 $post_id = wp_update_post( $postarr );
  1377.             }
  1378.             else
  1379.             {
  1380.                 $postarr = array(
  1381.                             'post_author'   => $author,
  1382.                             'post_title'    => $title,
  1383.                             'post_content'  => $content,
  1384.                             'post_excerpt'  => $tooltip,
  1385.                             'post_status'   => 'publish',
  1386.                             'post_type'     => $this->get_post_type(),
  1387.                             'post_parent'   => $this->cached_update_sc_array[0]['template_id']
  1388.                         );
  1389.  
  1390.                 $post_id = wp_insert_post( $postarr );
  1391.             }
  1392.  
  1393.             if( ! is_numeric( $post_id ) || 0 == $post_id )
  1394.             {
  1395.                 $response['message'] = $msg;
  1396.                 echo json_encode( $response );
  1397.                 exit;
  1398.             }
  1399.  
  1400.             if( in_array( $subaction, array( 'new_custom_element', 'new_element_from_alb' ) ) )
  1401.             {
  1402.                 Avia_Builder()->save_posts_alb_content( $post_id, $content );
  1403.                 Avia_Builder()->set_alb_builder_status( 'active', $post_id );
  1404.                 Avia_Builder()->save_shortcode_tree( $post_id, $tree );
  1405.  
  1406.                 //  assign correct category to element template
  1407.                 $this->updated_post_content( $content, $post_id, $tree, true );
  1408.             }
  1409.  
  1410.             //  Prepare return values
  1411.             $config = $shortcode->config;
  1412.  
  1413.             if( ! isset( $config['class'] ) )
  1414.             {
  1415.                 $config['class'] = '';
  1416.             }
  1417.  
  1418.             $config['name'] = $title;
  1419.             $config['tooltip'] = $tooltip;
  1420.             $config['class'] .= ' avia-custom-element-button';
  1421.             $config['class'] .= $is_item ? ' avia-custom-element-item' : ' avia-custom-element-base';
  1422.             $config['btn_data'] = array(
  1423.                                     'shortcode_handler'     => $is_item ? $item_sc : $shortcode_handler,
  1424.                                     'base_shortcode'        => $is_item ? $shortcode_handler : '',
  1425.                                     'is_item'               => $is_item ? 'true' : 'false',
  1426.                                     'modal_title'           => $modal_title,
  1427.                                     'modal_subitem_title'   => $modal_subitem_title,
  1428.                                     'element_title'         => $title,
  1429.                                     'element_tooltip'       => $tooltip
  1430.                                 );
  1431.             $config['btn_id'] = 'avia-element-btn-' . $post_id;
  1432.  
  1433.             $templ_id = $config['php_class'] . '_' . $post_id;
  1434.  
  1435.             $args = ! $is_item ? $this->cached_update_sc_array[0]['template_attr'] : $this->cached_update_sc_array[0]['attr'];
  1436.             $raw_content = $this->cached_update_sc_array[0]['raw_content'];
  1437.  
  1438.             $response['tab'] = $config['tab'];
  1439.             $response['default_tab'] = Avia_Builder()->default_sc_btn_tab_name;
  1440.             $response['icons'][ '#' . $config['btn_id'] ] = $this->create_shortcode_button( $config, 500, $templ_id, $post_id );
  1441.  
  1442.             $response['js_templates'] = array_merge( $this->js_scripts_from_array( $shortcode, $args, $raw_content, $post_id ), $js_templates );
  1443.  
  1444.             $response['no_tab_message'] = __( 'Sorry, custom element icon section does not exist currently. Please save page content and reload page to see icon for the new element.', 'avia_framework' );
  1445.  
  1446.  
  1447.             if( $subaction == 'update_element_post_data' )
  1448.             {
  1449.                 $response['message'] = sprintf( __( 'Custom element &quot;%s&quot; successfully updated.', 'avia_framework' ), $title );
  1450.             }
  1451.             else
  1452.             {
  1453.                 $response['message'] = sprintf( __( 'Custom element &quot;%s&quot; successfully created. Continue with setting the options for this element.', 'avia_framework' ), $title );
  1454.             }
  1455.  
  1456.             $response['change_info'] = array(
  1457.                                     'element_id'    => $post_id,
  1458.                                     'title'         => $title,
  1459.                                     'tooltip'       => $tooltip,
  1460.                                     'modal_title'   => $update_modal_title
  1461.                             );
  1462.  
  1463.             $response['success'] = true;
  1464.  
  1465.  
  1466.             /**
  1467.              * @since 4.8.4
  1468.              * @param string $subaction
  1469.              * @param array $response
  1470.              */
  1471.             do_action( 'ava_ajax_cet_cpt_actions_handler', $subaction, $response );
  1472.  
  1473.             echo json_encode( $response );
  1474.             exit;
  1475.         }
  1476.  
  1477.         /**
  1478.          * Update an existing element template.
  1479.          *
  1480.          * @since 4.8
  1481.          */
  1482.         public function handler_ajax_element_template_update_content()
  1483.         {
  1484.             header( 'Content-Type: application/json' );
  1485.  
  1486.             $response = array(
  1487.                         '_ajax_nonce'   => wp_create_nonce( 'avia_nonce_loader' ),
  1488.                         'success'       => false,
  1489.                         'message'       => ''
  1490.                     );
  1491.  
  1492.             if( ! $this->current_user_can_manage() )
  1493.             {
  1494.                 $response['message'] = $this->no_capability;
  1495.                 echo json_encode( $response );
  1496.                 exit;
  1497.             }
  1498.  
  1499.             $return = check_ajax_referer( 'avia_nonce_loader', '_ajax_nonce', false );
  1500.  
  1501.             /**
  1502.              * Return error and allow to resend data
  1503.              */
  1504.             if( false === $return )
  1505.             {
  1506.                 $response['expired_nonce'] = $this->nonce_message;
  1507.                 echo json_encode( $response );
  1508.                 exit;
  1509.             }
  1510.  
  1511.             $msg1 = __( 'Custom Element could not be updated.', 'avia_framework' );
  1512.  
  1513.             $element_id = isset( $_REQUEST['element_id'] ) && is_numeric( $_REQUEST['element_id'] ) ? (int) $_REQUEST['element_id'] : 0;
  1514.             $shortcode = isset( $_REQUEST['shortcode'] ) ? $_REQUEST['shortcode'] : '';
  1515.             $args = isset( $_REQUEST['params'] ) && is_array( $_REQUEST['params'] ) ? $_REQUEST['params'] : array();
  1516.  
  1517.             if( empty( $element_id ) || empty( $shortcode ) || empty( $args ) )
  1518.             {
  1519.                 $response['message'] = $msg1 . ' ' . __( 'Not enough information provided.', 'avia_framework' );
  1520.                 echo json_encode( $response );
  1521.                 exit;
  1522.             }
  1523.  
  1524.             //  always clear unique id's for custom elements
  1525.             $shortcode = Avia_Builder()->element_manager()->clear_element_ids_in_content( wp_unslash( $shortcode ) );
  1526.  
  1527.             $sc_array = $this->get_element_template_info_from_content( $shortcode );
  1528.  
  1529.             $is_item = $sc_array[0]['shortcode'] != $sc_array[0]['template_sc'];
  1530.             $class_sc = ! $is_item ? $sc_array[0]['template_sc'] : $sc_array[0]['shortcode'];
  1531.  
  1532.             $this->load_template_cache( $element_id );
  1533.  
  1534.             $template = $this->get_template( $element_id, $sc_array[0]['template_sc'] );
  1535.  
  1536.             if( false === $template )
  1537.             {
  1538.                 $response['message'] = $msg1 . ' ' . __( 'Custom Element shortcode edited and stored shortcode do not match.', 'avia_framework' );
  1539.                 echo json_encode( $response );
  1540.                 exit;
  1541.             }
  1542.  
  1543.             if( ! array_key_exists( $class_sc, Avia_Builder()->shortcode ) )
  1544.             {
  1545.                 $response['message'] = $msg1 . ' ' . __( 'Shortcode does no longer exist or has been deactivated.', 'avia_framework' );
  1546.                 echo json_encode( $response );
  1547.                 exit;
  1548.             }
  1549.  
  1550.             $shortcode_class = Avia_Builder()->shortcode_class[ Avia_Builder()->shortcode[ $class_sc ] ];
  1551.             $js_templates = array();
  1552.  
  1553.             //  Special case: First Subitem used as custom element template for all subitems
  1554.             if ( $this->is_editable_modal_group_element( $shortcode_class ) && 'first' == $this->subitem_custom_element_handling() ) {
  1555.             $item_template = false;
  1556.  
  1557.                 if ( array_key_exists( 'one_element_template', $args ) && ! empty( $args['one_element_template'] ) ) {
  1558.                         $this->load_template_cache( $args['one_element_template'] );
  1559.  
  1560.                         if ( isset( $sc_array[0]['content'][0]['shortcode'] ) ) {
  1561.                                 $item_template = $this->get_template(
  1562.                                         $args['one_element_template'],
  1563.                                         $sc_array[0]['content'][0]['shortcode']
  1564.                                 );
  1565.                         }
  1566.                 }
  1567.  
  1568.                 if (
  1569.                         isset( $sc_array[0]['content'] )
  1570.                         && is_array( $sc_array[0]['content'] )
  1571.                         && ! empty( $sc_array[0]['content'][0] )
  1572.                         && is_array( $sc_array[0]['content'][0] )
  1573.                 ) {
  1574.                         $item_attr = $sc_array[0]['content'][0]['attr'] ?? [];
  1575.                         $item_attr['content'] = $sc_array[0]['content'][0]['raw_content'] ?? '';
  1576.                         $item_attr['element_template'] = ( false !== $item_template )
  1577.                                 ? ( $item_template['sc_array'][0]['template_id'] ?? '' )
  1578.                                 : '';
  1579.                 } else {
  1580.                         $item_attr = $shortcode_class->get_default_modal_group_args();
  1581.                 }
  1582.  
  1583.                 $outer_attr = ( false === $item_template )
  1584.                         ? $shortcode_class->get_default_sc_args()
  1585.                         : ( $item_template['sc_array'][0]['attr'] ?? [] );
  1586.  
  1587.                 $item_content = $this->create_custom_element_shortcode(
  1588.                         $shortcode_class,
  1589.                         $item_attr,
  1590.                         $outer_attr,
  1591.                         true,
  1592.                         true
  1593.                 );
  1594.  
  1595.                 $item_tree = ShortcodeHelper::build_shortcode_tree( $item_content );
  1596.  
  1597.                 $this->cached_update_sc_array = $this->get_element_template_info_from_content( $item_content );
  1598.  
  1599.                 //  fallback situation or template has been deleted - recreate a new template
  1600.                 if( false === $item_template )
  1601.                 {
  1602.                     $tt = ! empty( $template['post']->post_excerpt ) ? " ({$template['post']->post_excerpt})" : '';
  1603.  
  1604.                     $postarr = array(
  1605.                                 'post_author'   => $template['post']->post_author,
  1606.                                 'post_title'    => sprintf( __( 'Item for: %s', 'avia_framework' ), $template['post']->post_title ),
  1607.                                 'post_content'  => $item_content,
  1608.                                 'post_excerpt'  => sprintf( __( 'An item for %s', 'avia_framework' ), $template['post']->post_title . $tt ),
  1609.                                 'post_status'   => 'publish',
  1610.                                 'post_type'     => $this->get_post_type(),
  1611.                                 'post_parent'   => $this->cached_update_sc_array[0]['template_id']
  1612.                             );
  1613.  
  1614.                     $item_id = wp_insert_post( $postarr );
  1615.                 }
  1616.                 else
  1617.                 {
  1618.                     $postarr = array(
  1619.                                 'ID'            => $args['one_element_template'],
  1620.                                 'post_content'  => $item_content,
  1621.                                 'post_status'   => 'publish',
  1622.                                 'post_type'     => $this->get_post_type(),
  1623.                                 'post_parent'   => $this->cached_update_sc_array[0]['template_id']
  1624.                             );
  1625.  
  1626.                     $item_id = wp_update_post( $postarr );
  1627.                 }
  1628.  
  1629.                 if( ! is_numeric( $item_id ) || 0 == $item_id )
  1630.                 {
  1631.                     $response['message'] = $msg1;
  1632.                     echo json_encode( $response );
  1633.                     exit;
  1634.                 }
  1635.  
  1636.                 Avia_Builder()->save_posts_alb_content( $item_id, $item_content );
  1637.                 Avia_Builder()->set_alb_builder_status( 'active', $item_id );
  1638.                 Avia_Builder()->save_shortcode_tree( $item_id, $item_tree );
  1639.  
  1640.                 //  assign correct category to element template
  1641.                 $this->updated_post_content( $item_content, $item_id, $item_tree, true );
  1642.  
  1643.                 $item_args = $this->cached_update_sc_array[0]['attr'];
  1644.                 $item_raw_content = $this->cached_update_sc_array[0]['raw_content'];
  1645.  
  1646.                 $js_templates = $this->js_scripts_from_array( $shortcode_class, $item_args, $item_raw_content, $item_id );
  1647.  
  1648.                 $sub_content = array();
  1649.  
  1650.                 //  Set all items to same custom element
  1651.                 if( is_array( $sc_array[0]['content'] ) && ! empty( $sc_array[0]['content'] ) )
  1652.                 {
  1653.                     foreach( $sc_array[0]['content'] as &$sub )
  1654.                     {
  1655.                         $attr = $sub['attr'];
  1656.                         $attr['element_template'] = $item_id;
  1657.                         $attr['content'] = $sub['raw_content'];
  1658.  
  1659.                         $sub_content[] = $attr;
  1660.                     }
  1661.                 }
  1662.  
  1663.                 $args['one_element_template'] = $item_id;
  1664.  
  1665.                 $attr = $args;
  1666.                 $attr['content'] = $sub_content;
  1667.  
  1668.                 $shortcode = $this->create_custom_element_shortcode( $shortcode_class, $attr, array(), false, true );
  1669.  
  1670.                 $sc_array = $this->get_element_template_info_from_content( $shortcode );
  1671.             }
  1672.  
  1673.             $this->cached_update_sc_array = $sc_array;
  1674.  
  1675.             $postarr = array(
  1676.                         'ID'            => $element_id,
  1677.                         'post_content'  => $shortcode,
  1678.                         'post_status'   => 'publish',
  1679.                         'post_type'     => $this->get_post_type(),
  1680.                         'post_parent'   => $this->cached_update_sc_array[0]['template_id']
  1681.                     );
  1682.  
  1683.             $post_id = wp_update_post( $postarr );
  1684.  
  1685.             if( ! is_numeric( $post_id ) || 0 == $post_id )
  1686.             {
  1687.                 $response['message'] = $msg1 . ' ' . __( 'Database error on saving.', 'avia_framework' );
  1688.                 echo json_encode( $response );
  1689.                 exit;
  1690.             }
  1691.  
  1692.             $tree = ShortcodeHelper::build_shortcode_tree( $shortcode );
  1693.  
  1694.             Avia_Builder()->save_posts_alb_content( $post_id, $shortcode );
  1695.             Avia_Builder()->set_alb_builder_status( 'active', $post_id );
  1696.             Avia_Builder()->save_shortcode_tree( $post_id, $tree );
  1697.  
  1698.             $args = ! $is_item ? $this->cached_update_sc_array[0]['template_attr'] : $this->cached_update_sc_array[0]['attr'];
  1699.             $raw_content = $this->cached_update_sc_array[0]['raw_content'];
  1700.  
  1701.             $response['js_templates'] = array_merge( $this->js_scripts_from_array( $shortcode_class, $args, $raw_content, $post_id ), $js_templates );
  1702.  
  1703.             $response['message'] = sprintf( __( 'Custom Element &quot;%s&quot; was updated.', 'avia_framework' ), $template['post']->post_title );
  1704.             $response['success'] = true;
  1705.  
  1706.             /**
  1707.              * @used_by         aviaPostCssManagement::handler_ava_reset_css_files()        10
  1708.              * @since 4.8.4
  1709.              * @param array $response
  1710.              */
  1711.             do_action( 'ava_ajax_cet_update_content', $response );
  1712.  
  1713.             echo json_encode( $response );
  1714.             exit;
  1715.         }
  1716.  
  1717.         /**
  1718.          * Delete the element and depending on theme option setting also subitem element
  1719.          *
  1720.          * @since 4.8
  1721.          */
  1722.         public function handler_ajax_element_template_delete()
  1723.         {
  1724.             header( 'Content-Type: application/json' );
  1725.  
  1726.             $response = array(
  1727.                         '_ajax_nonce'   => wp_create_nonce( 'avia_nonce_loader' ),
  1728.                         'success'       => false,
  1729.                         'message'       => ''
  1730.                     );
  1731.  
  1732.             if( ! $this->current_user_can_manage() )
  1733.             {
  1734.                 $response['message'] = $this->no_capability;
  1735.                 echo json_encode( $response );
  1736.                 exit;
  1737.             }
  1738.  
  1739.             $return = check_ajax_referer( 'avia_nonce_loader', '_ajax_nonce', false );
  1740.  
  1741.             /**
  1742.              * Return error and allow to resend data
  1743.              */
  1744.             if( false === $return )
  1745.             {
  1746.                 $response['expired_nonce'] = $this->nonce_message;
  1747.                 echo json_encode( $response );
  1748.                 exit;
  1749.             }
  1750.  
  1751.             $element_id = isset( $_REQUEST['element_id'] ) && is_numeric( $_REQUEST['element_id'] ) ? (int) $_REQUEST['element_id'] : 0;
  1752.             $title = isset( $_REQUEST['title'] ) ? $_REQUEST['title'] : __( 'Unknown element name', 'avia_framework' );
  1753.             $shortcode = isset( $_REQUEST['shortcode'] ) ? $_REQUEST['shortcode'] : '';
  1754.             $base_shortcode = isset( $_REQUEST['baseShortcode'] ) ? $_REQUEST['baseShortcode'] : '';
  1755.             $is_item = isset( $_REQUEST['isItem'] ) && 'true' === $_REQUEST['isItem'];
  1756.  
  1757.             $msg1 = sprintf( __( 'Custom Element &quot;%s&quot; could not be deleted.', 'avia_framework' ), $title );
  1758.             $subitem_msg = '';
  1759.  
  1760.             if( empty( $element_id ) || empty( $shortcode ) || ( $is_item && empty( $base_shortcode ) ) )
  1761.             {
  1762.                 $response['message'] = $msg1 . ' ' . __( 'Not enough information provided.', 'avia_framework' );
  1763.                 echo json_encode( $response );
  1764.                 exit;
  1765.             }
  1766.  
  1767.             $this->load_template_cache( $element_id );
  1768.             $template = $this->get_template( $element_id, $shortcode );
  1769.  
  1770.             if( false === $template )
  1771.             {
  1772.                 $response['message'] = $msg1 . ' ' . __( 'Custom Element shortcode to delete and stored shortcode do not match.', 'avia_framework' );
  1773.                 echo json_encode( $response );
  1774.                 exit;
  1775.             }
  1776.  
  1777.             if( $is_item && 'first' == $this->subitem_custom_element_handling() )
  1778.             {
  1779.                 $response['message'] = $msg1 . ' ' . __( 'Custom subitem elements cannot be deleted when &quot;All subitems use the same custom element template&quot; is selected in theme options. This is the default setting.', 'avia_framework' );
  1780.                 echo json_encode( $response );
  1781.                 exit;
  1782.             }
  1783.  
  1784.             //  check if we need to delete subitem
  1785.             if( ! $is_item && 'first' == $this->subitem_custom_element_handling())
  1786.             {
  1787.                 $sub_id = isset( $template['sc_array'][0]['template_attr']['one_element_template'] ) ? $template['sc_array'][0]['template_attr']['one_element_template'] : '';
  1788.                 $sub_deleted = false;
  1789.  
  1790.                 if( ! empty( $sub_id ) && is_numeric( $sub_id ) )
  1791.                 {
  1792.                     $sub_deleted = wp_delete_post( $sub_id, true );
  1793.  
  1794.                     if( $sub_deleted instanceof WP_Post )
  1795.                     {
  1796.                         $subitem_msg .= ' ' . __( 'Custom element subitem settings have also been deleted.', 'avia_framework' );
  1797.                     }
  1798.                 }
  1799.             }
  1800.  
  1801.             $deleted = wp_delete_post( $element_id, true );
  1802.  
  1803.             if( ! $deleted instanceof WP_Post )
  1804.             {
  1805.                 $response['message'] = $msg1 . ' ' . __( 'Internal database error occured deleting the custom element.', 'avia_framework' );
  1806.                 echo json_encode( $response );
  1807.                 exit;
  1808.             }
  1809.  
  1810.             $response['message'] = sprintf( __( 'Custom Element &quot;%s&quot; was deleted.', 'avia_framework' ), $title ) . $subitem_msg;
  1811.             $response['success'] = true;
  1812.  
  1813.             /**
  1814.              * @used_by         aviaPostCssManagement::handler_ava_reset_css_files()        10
  1815.              * @since 4.8.4
  1816.              * @param array $response
  1817.              */
  1818.             do_action( 'ava_ajax_cet_delete', $response );
  1819.  
  1820.             echo json_encode( $response );
  1821.             exit;
  1822.         }
  1823.  
  1824.         /**
  1825.          * Register the ALB Element Template Post Type and Categories
  1826.          *
  1827.          * @since 4.8
  1828.          */
  1829.         public function register_post_types()
  1830.         {
  1831.             if( post_type_exists( $this->get_post_type() ) )
  1832.             {
  1833.                 return;
  1834.             }
  1835.  
  1836.             $labels = array(
  1837.                         'name'                  => __( 'ALB Custom Elements', 'avia_framework' ),
  1838.                         'singular_name'         => __( 'ALB Custom Element', 'avia_framework' ),
  1839.                         'all_items'             => __( 'All ALB Custom Elements', 'avia_framework' ),
  1840.                         'add_new'               => __( 'Add New', 'avia_framework' ),
  1841.                         'add_new_item'          => __( 'Add New ALB Custom Element', 'avia_framework' ),
  1842.                         'edit_item'             => __( 'Edit ALB Custom Element', 'avia_framework' ),
  1843.                         'new_item'              => __( 'New ALB Custom Element', 'avia_framework' ),
  1844.                         'view_item'             => __( 'View ALB Custom Element', 'avia_framework' ),
  1845.                         'search_items'          => __( 'Search ALB Custom Elements', 'avia_framework' ),
  1846.                         'not_found'             => __( 'No ALB Custom Element found', 'avia_framework' ),
  1847.                         'not_found_in_trash'    => __( 'No ALB Custom Element found in Trash', 'avia_framework' ),
  1848.                         'parent_item_colon'     => ''
  1849.                     );
  1850.  
  1851.             $args = array(
  1852.                         'labels'            => $labels,
  1853.                         'public'            => true,
  1854.                         'show_ui'           => true,
  1855.                         'show_in_menu'      => false,
  1856.                         'capability_type'   => 'post',
  1857.                         'hierarchical'      => true,
  1858.                         'rewrite'           => false,
  1859.                         'query_var'         => true,
  1860.                         'show_in_nav_menus' => false,
  1861.                         'show_in_rest'      => false,               //  set to false to disallow block editor
  1862.                         'taxonomies'        => array(),
  1863.                         'supports'          => array( 'title', 'excerpt', 'editor', 'revisions', 'author' ),
  1864.                         'menu_icon'         => 'dashicons-images-alt2',
  1865.                         'can_export'        => true,
  1866.                         'exclude_from_search' => true
  1867.                     );
  1868.  
  1869.             /**
  1870.              * @since 4.8
  1871.              * @param array $args
  1872.              * @return array
  1873.              */
  1874.             $args = apply_filters( 'avf_custom_elements_cpt_args', $args );
  1875.  
  1876.             register_post_type( $this->get_post_type() , $args );
  1877.  
  1878.  
  1879.             $tax_args = array(
  1880.                             'hierarchical'          => true,
  1881.                             'label'                 => __( 'ALB Custom Element Template Categories', 'avia_framework' ),
  1882.                             'singular_label'        => __( 'ALB Custom Element Template Category', 'avia_framework' ),
  1883.                             'rewrite'               => false,
  1884.                             'show_ui'               => false,           //  hide in admin menu and meta box
  1885.                             'show_in_quick_edit'    => false,
  1886.                             'query_var'             => true,
  1887.                             'show_in_rest'          => false            //  set to false to disallow block editor
  1888.                         );
  1889.  
  1890.             /**
  1891.              * @since 4.8
  1892.              * @param array $tax_args
  1893.              * @return array
  1894.              */
  1895.             $tax_args = apply_filters( 'avf_custom_elements_cpt_tax_args', $tax_args );
  1896.  
  1897.             register_taxonomy( $this->get_taxonomy(), array( $this->get_post_type() ), $tax_args );
  1898.         }
  1899.  
  1900.         /**
  1901.          * Returns, if the feature has been enabled in theme options
  1902.          *
  1903.          * @since 4.8
  1904.          * @return boolean
  1905.          */
  1906.         public function element_templates_enabled()
  1907.         {
  1908.             if( is_null( $this->enabled ) )
  1909.             {
  1910.                 $enabled = avia_get_option( 'alb_element_templates' ) != '';
  1911.  
  1912.                 /**
  1913.                  * @used_by         avia_WPML
  1914.                  * @since 4.8
  1915.                  * @param boolean
  1916.                  * @return boolean
  1917.                  */
  1918.                 $this->enabled = apply_filters( 'avf_element_templates_enabled', $enabled );
  1919.             }
  1920.  
  1921.             return $this->enabled;
  1922.         }
  1923.  
  1924.         /**
  1925.          * Returns the filtered string for a capability to show menus to edit elements.
  1926.          *
  1927.          * @since 4.8
  1928.          * @param string $which             'new' | 'edit'
  1929.          * @return string
  1930.          */
  1931.         public function get_capability( $which = 'new' )
  1932.         {
  1933.             $option = avia_get_option( 'alb_element_templates' );
  1934.  
  1935.             $cap = 'admins_only' == $option ? 'manage_options' : 'edit_posts';
  1936.  
  1937.             /**
  1938.              * Filter the user capability to create and edit ALB Element Templates
  1939.              * Make sure to return a valid capability.
  1940.              *
  1941.              * @since 4.8
  1942.              * @param string $cap
  1943.              * @param string $which             'new' | 'edit'
  1944.              * @param string $option
  1945.              * @return string
  1946.              */
  1947.             return apply_filters( 'avf_custom_elements_user_capability', $cap, $which, $option );
  1948.         }
  1949.  
  1950.         /**
  1951.          * Check if current user has capability to manage custom elements
  1952.          *
  1953.          * @since 4.8
  1954.          * @return boolean
  1955.          */
  1956.         public function current_user_can_manage()
  1957.         {
  1958.             $option = avia_get_option( 'alb_element_templates' );
  1959.  
  1960.             if( '' == $option )
  1961.             {
  1962.                 return false;
  1963.             }
  1964.  
  1965.             return current_user_can( $this->get_capability() );
  1966.         }
  1967.  
  1968.         /**
  1969.          * Check if user allows CPT screens in backend for custom element templates.
  1970.          * This is only intended for developers. Also plugins like WPML might use CPT screens for translating.
  1971.          *
  1972.          * @since 4.8
  1973.          * @param string $context               'menu_page' | 'body_class' | 'builder_button_params'
  1974.          * @return boolean
  1975.          */
  1976.         public function allow_cpt_screens( $context = 'menu_page' )
  1977.         {
  1978.             $allow_cpt_screen_opt = current_theme_supports( 'avia-custom-elements-cpt-screen' ) ? avia_get_option( 'custom_el_cpt_screen' ) : '';
  1979.  
  1980.             /**
  1981.              * @since 4.8
  1982.              * @param boolean
  1983.              * @param string $context               'menu_page' | 'admin_bar_new
  1984.              * @return boolean
  1985.              */
  1986.             $allow_cpt_screen = apply_filters( 'avf_custom_elements_cpt_screen', 'allow_cpt_screen' == $allow_cpt_screen_opt, $context );
  1987.  
  1988.             return $allow_cpt_screen;
  1989.         }
  1990.  
  1991.  
  1992.         /**
  1993.          * Checks if shortcode is an editable element template base
  1994.          *
  1995.          * @since 4.8
  1996.          * @param aviaShortcodeTemplate|array $sc
  1997.          * @return boolean
  1998.          */
  1999.         public function is_editable_base_element( $sc )
  2000.         {
  2001.             if( $sc instanceof aviaShortcodeTemplate )
  2002.             {
  2003.                 return isset( $sc->config['base_element'] ) && 'yes' == $sc->config['base_element'];
  2004.             }
  2005.             else if( is_array( $sc ) )
  2006.             {
  2007.                 return isset( $sc['base_element'] ) && 'yes' == $sc['base_element'];
  2008.             }
  2009.  
  2010.             return false;
  2011.         }
  2012.  
  2013.         /**
  2014.          * Checks if shortcode supports an editable modal group element - and returns a possible index.
  2015.          *
  2016.          * @since 4.8
  2017.          * @param aviaShortcodeTemplate $sc
  2018.          * @param string $result                    'bool' | 'index'
  2019.          * @return boolean|int
  2020.          */
  2021.         public function is_editable_modal_group_element( aviaShortcodeTemplate $sc, $result = 'bool' )
  2022.         {
  2023.             $return = false;
  2024.  
  2025.             foreach( $sc->elements as $key => &$element )
  2026.             {
  2027.                 if( isset( $element['type'] ) && 'modal_group' == $element['type'] )
  2028.                 {
  2029.                     $return = isset( $element['editable_item'] ) && true === $element['editable_item'];
  2030.                     if( $return && $result == 'index' )
  2031.                     {
  2032.                         $return = $key;
  2033.                     }
  2034.                     break;
  2035.                 }
  2036.             }
  2037.  
  2038.             unset( $element );
  2039.  
  2040.             return $return;
  2041.         }
  2042.  
  2043.         /**
  2044.          * Checks if looking for a locked value should be skipped.
  2045.          * When editiong modal group subitems depending on theme option "Custom Elements For Subitems" it might be necessary not to lock options.
  2046.          *
  2047.          * @since 4.8
  2048.          * @param aviaShortcodeTemplate $sc
  2049.          * @return boolean
  2050.          */
  2051.         public function skip_modal_popup_locked_value_check( aviaShortcodeTemplate $sc, array $templates_info )
  2052.         {
  2053.             if( ! $this->is_editable_base_element( $sc ) || ! isset( $sc->config['shortcode_nested'] ) || ! is_array( $sc->config['shortcode_nested'] ) )
  2054.             {
  2055.                 return false;
  2056.             }
  2057.  
  2058.             if( ! isset( $templates_info['shortcode'] ) || ! in_array( $templates_info['shortcode'], $sc->config['shortcode_nested'] ) )
  2059.             {
  2060.                 return false;
  2061.             }
  2062.  
  2063.             if( 'individually' == $this->subitem_custom_element_handling() )
  2064.             {
  2065.                 return false;
  2066.             }
  2067.  
  2068.             if( ( isset( $_REQUEST['edit_element'] ) && ( 'true' === $_REQUEST['edit_element'] ) ) || ( isset( $_REQUEST['post_type'] ) && ( Avia_Element_Templates()->get_post_type() == $_REQUEST['post_type'] ) ) )
  2069.             {
  2070.                 return true;
  2071.             }
  2072.  
  2073.             return false;
  2074.         }
  2075.  
  2076.         /**
  2077.          * Checks if we have a custom element post type
  2078.          *
  2079.          * @since 4.8
  2080.          * @param string $post_type
  2081.          * @return boolean
  2082.          */
  2083.         public function is_element_post_type( $post_type )
  2084.         {
  2085.             return $post_type == $this->get_post_type();
  2086.         }
  2087.  
  2088.         /**
  2089.          * Checks if we are on the list table overview page for ALB Element templates
  2090.          *
  2091.          * @since 4.8
  2092.          * @return boolean
  2093.          */
  2094.         public function is_element_overview_page()
  2095.         {
  2096.             if( is_bool( $this->is_element_overview_page ) )
  2097.             {
  2098.                 return $this->is_element_overview_page;
  2099.             }
  2100.  
  2101.             if( ! function_exists( 'get_current_screen' ) )
  2102.             {
  2103.                 return false;
  2104.             }
  2105.  
  2106.             $this->is_element_overview_page = false;
  2107.  
  2108.             $screens = array(
  2109.                             $this->get_post_type()
  2110.                         );
  2111.  
  2112.             $post_type = '';
  2113.  
  2114.                 //  WP bug: we cannot rely on QuickEdit, that screen object exists
  2115.             $screen = get_current_screen();
  2116.  
  2117.             if( empty( $screen ) || ( ! isset( $screen->post_type ) ) )
  2118.             {
  2119.                 if( isset( $_REQUEST['action'] ) && ( 'inline-save' != $_REQUEST['action'] ) )
  2120.                 {
  2121.                     return $this->is_element_overview_page;
  2122.                 }
  2123.  
  2124.                 if( ! isset( $_REQUEST['post_type'] ) )
  2125.                 {
  2126.                     return $this->is_element_overview_page;
  2127.                 }
  2128.                 $post_type = $_REQUEST['post_type'];
  2129.             }
  2130.             else
  2131.             {
  2132.                 $post_type = $screen->post_type;
  2133.             }
  2134.  
  2135.             if( ! in_array( $post_type, $screens ) )
  2136.             {
  2137.                 return $this->is_element_overview_page;
  2138.             }
  2139.  
  2140.             $this->is_element_overview_page = true;
  2141.  
  2142.             return $this->is_element_overview_page;
  2143.         }
  2144.  
  2145.         /**
  2146.          * Checks if we are on the edit screen for the element (new or edit).
  2147.          *
  2148.          * @since 4.8
  2149.          * @return boolean
  2150.          */
  2151.         public function is_edit_element_page()
  2152.         {
  2153.             if( is_bool( $this->is_edit_element_page ) )
  2154.             {
  2155.                 return $this->is_edit_element_page;
  2156.             }
  2157.  
  2158.             $this->is_edit_element_page = false;
  2159.  
  2160.             if( ! is_admin() && ! wp_doing_ajax() )
  2161.             {
  2162.                 return $this->is_edit_element_page;
  2163.             }
  2164.  
  2165.             if( function_exists( 'get_current_screen' ) )
  2166.             {
  2167.                 $screen = get_current_screen();
  2168.  
  2169.                 if( ! $screen instanceof WP_Screen )
  2170.                 {
  2171.                     return $this->is_edit_element_page;
  2172.                 }
  2173.  
  2174.                 if( $screen->base == 'post' && $screen->post_type == $this->get_post_type() )
  2175.                 {
  2176.                     $this->is_edit_element_page = true;
  2177.                 }
  2178.  
  2179.                 return $this->is_edit_element_page;
  2180.             }
  2181.  
  2182.             /**
  2183.              * Fallback if called too early
  2184.              * ============================
  2185.              */
  2186. //          error_log( 'aviaElementTemplates::is_edit_element_page is called too early. Might return wrong result. Make sure to call when function get_current_screen() is available.');
  2187.  
  2188.             $this->is_edit_element_page = null;
  2189.  
  2190.             if( strpos( $_SERVER['REQUEST_URI'], 'post-new.php' ) !== false )
  2191.             {
  2192.                 if( isset( $_REQUEST['post_type'] ) && $this->get_post_type() == $_REQUEST['post_type'] )
  2193.                 {
  2194.                     return true;
  2195.                 }
  2196.             }
  2197.             else if( strpos( $_SERVER['REQUEST_URI'], 'post.php' ) !== false )
  2198.             {
  2199.                 if( isset( $_REQUEST['action'] ) && 'edit' == $_REQUEST['action'] && isset( $_REQUEST['post'] ) )
  2200.                 {
  2201.                     $post = $this->get_post( $_REQUEST['post'] );
  2202.  
  2203.                     if( $post instanceof WP_Post && $this->get_post_type() == $post->post_type )
  2204.                     {
  2205.                         return true;
  2206.                     }
  2207.                 }
  2208.             }
  2209.  
  2210.             return false;
  2211.         }
  2212.  
  2213.         /**
  2214.          * Checks if we need to add custom element templates options in modal popup editor
  2215.          *
  2216.          * @since 4.8
  2217.          * @param boolean
  2218.          */
  2219.         public function popup_editor_needs_template_options()
  2220.         {
  2221.             if( ( isset( $_REQUEST['edit_element'] ) && ( 'true' === $_REQUEST['edit_element'] ) ) )
  2222.             {
  2223.                 return true;
  2224.             }
  2225.  
  2226.             if( isset( $_REQUEST['post_type'] ) && ( Avia_Element_Templates()->get_post_type() == $_REQUEST['post_type'] ) )
  2227.             {
  2228.                 return true;
  2229.             }
  2230.  
  2231.             return false;
  2232.         }
  2233.  
  2234.         /**
  2235.          * Return filtered option how custom element templates are handled for modal group subitems
  2236.          *
  2237.          * @since 4.8
  2238.          * @return string               'first' | 'individually' | 'none'
  2239.          */
  2240.         public function subitem_custom_element_handling()
  2241.         {
  2242.             if( ! is_null( $this->subitem_custom_element_handling ) )
  2243.             {
  2244.                 return $this->subitem_custom_element_handling;
  2245.             }
  2246.  
  2247.             if( ! $this->element_templates_enabled() )
  2248.             {
  2249.                 $this->subitem_custom_element_handling = 'individually';
  2250.                 return $this->subitem_custom_element_handling;
  2251.             }
  2252.  
  2253.             /**
  2254.              * @since 4.8
  2255.              * @param string
  2256.              * @return string       'first' | 'individually' | 'none'
  2257.              */
  2258.             $handling = apply_filters( 'avf_custom_element_subtype_handling', avia_get_option( 'custom_el_subitem_handling' ) );
  2259.  
  2260.             if( empty( $handling ) || ! in_array( $handling, array( 'first', 'individually', 'none' ) ) )
  2261.             {
  2262.                 $handling = 'first';
  2263.             }
  2264.  
  2265.             $this->subitem_custom_element_handling = $handling;
  2266.  
  2267.             return $this->subitem_custom_element_handling;
  2268.         }
  2269.  
  2270.         /**
  2271.          * Add the container to allow edit element templates in ALB environment
  2272.          *
  2273.          * @since 4.8
  2274.          * @return string
  2275.          */
  2276.         public function add_alb_editor_content()
  2277.         {
  2278.             $output = '';
  2279.  
  2280.             if( $this->element_templates_enabled() && ! $this->is_edit_element_page() )
  2281.             {
  2282.                 $output .=  '<div class="avia_layout_builder_custom_elements">';
  2283.                 $output .=      '<div id="aviaALBCustomElements" class="avia-style avia_alb_custom_elements" data-dragdrop-level="0">';
  2284.                 $output .=      '</div>';
  2285.                 $output .=      '<span class="aviaALBCustomElementShortcodeHeadline">';
  2286.                 $output .=          __( 'Custom Element Shortcode (only used when editing a CET in modal popup):', 'avia_framework' );
  2287.                 $output .=      '</span>';
  2288.                 $output .=      '<textarea id="aviaALBCleanDataCustomElements" name="aviaALBCleanDataCustomElements"></textarea>';
  2289.                 $output .=  '</div>';
  2290.             }
  2291.  
  2292.             return $output;
  2293.         }
  2294.  
  2295.  
  2296.         /**
  2297.          * Add tabs for managing element templates
  2298.          *
  2299.          * @since 4.8
  2300.          * @param string $tabs_title
  2301.          * @param string $tabs_content
  2302.          * @param int $tab_count
  2303.          * @param array $metabox_element
  2304.          * @return void
  2305.          */
  2306.         public function add_visual_editor_custom_element_content( &$tabs_title, &$tabs_content, &$tab_count, &$metabox_element )
  2307.         {
  2308.             if( Avia_Builder()->disable_drag_drop !== false )
  2309.             {
  2310.                 return;
  2311.             }
  2312.  
  2313.             if( ! $this->element_templates_enabled() )
  2314.             {
  2315.                 return;
  2316.             }
  2317.  
  2318.             //  In this case we handle templates like normal shortcode (only templates are added )
  2319.             if( $this->is_edit_element_page() )
  2320.             {
  2321.                 return;
  2322.             }
  2323.  
  2324.             $this->editor_elements = $this->get_all_element_template_shortcode_buttons();
  2325.             $tabs = array();
  2326.             if( empty( $this->editor_elements ) )
  2327.             {
  2328.                 //  Use only one tab - regardless of theme options settings
  2329.                 $tabs[ $this->custom_element_tab_name ] = array();
  2330.             }
  2331.             else
  2332.             {
  2333.                 $tab_order = isset( $metabox_element['tab_order'] ) ? $metabox_element['tab_order'] : array();
  2334.                 $tabs = array_fill_keys( $tab_order, array() );
  2335.             }
  2336.  
  2337.             foreach( $this->editor_elements as $element )
  2338.             {
  2339.                 if( ! $this->is_editable_base_element( $element['sc_class'] ) )
  2340.                 {
  2341.                     continue;
  2342.                 }
  2343.  
  2344.                 $shortcode = $element['sc_class']->config;
  2345.  
  2346.                 if( empty( $shortcode['tinyMCE']['tiny_only'] ) )
  2347.                 {
  2348.                     if( ! isset( $shortcode['tab'] ) )
  2349.                     {
  2350.                         $shortcode['tab'] = Avia_Builder()->default_sc_btn_tab_name;
  2351.                     }
  2352.  
  2353.                     $tabs[ $shortcode['tab'] ][] = $element;
  2354.                 }
  2355.             }
  2356.  
  2357.             $condensed = $this->condense_tabs( $tabs );
  2358.             $class_dropdown = $condensed ? 'avia-condensed' : '';
  2359.  
  2360.             $icon = avia_font_manager::get_frontend_icon( 'arrow-combo', 'svg_entypo-fontello', [ 'aria-hidden' => 'true', 'title' => '', 'desc' => '' ] );
  2361.             $icon_class = avia_font_manager::get_frontend_icon_classes( $icon['font'], 'string' );
  2362.  
  2363.             $init_sc = 'alb';
  2364.  
  2365.             $shortcode_label = array(
  2366.                                 'alb'       => __( 'ALB Elements:', 'avia_framework' ),
  2367.                                 'custom'    => __( 'Custom Elements:', 'avia_framework' ),
  2368.                             );
  2369.  
  2370.             $sel_list  = '';
  2371.  
  2372.             $sel_list .=    '<div id="avia-select-shortcode-type-dropdown" class="avia-fake-tab avia-select-shortcode-type-dropdown-container ' . $class_dropdown . '" data-init_sc_type="' . $init_sc . '">';
  2373.             $sel_list .=        '<ul class="avia-select-shortcode-type-select">';
  2374.             $sel_list .=            '<li class="avia-select-shortcode-type-list-wrap">';
  2375.             $sel_list .=                '<strong>';
  2376.             $sel_list .=                    "<span class='avia-font-entypo-fontello {$icon_class}' {$icon['attr']}>";
  2377.             $sel_list .=                        $icon['svg'];
  2378.             $sel_list .=                    '</span>';
  2379.             $sel_list .=                    "<span class='avia-sc-type-label'>{$shortcode_label[ $init_sc ]}</span>";
  2380.             $sel_list .=                '</strong>';
  2381.             $sel_list .=                '<ul class="avia-select-shortcode-type-list-main">';
  2382.             $sel_list .=                    '<li class="avia-shortcode-type-list-element">';
  2383.             $sel_list .=                        '<a href="#" class="avia-fake-tab shortcode-type-active avia-alb " data-sc_type="alb" title="' . esc_attr( __( 'Select default ALB Elements', 'avia_framework' ) ) . '">' . $shortcode_label['alb'] . '</a>';
  2384.             $sel_list .=                    '</li>';
  2385.             $sel_list .=                    '<li class="avia-shortcode-type-list-element">';
  2386.             $sel_list .=                        '<a href="#" class="avia-fake-tab avia-custom" data-sorting="name_asc" data-sc_type="custom" title="' . esc_attr( __( 'Select Custom Elements', 'avia_framework' ) ) . '">' . $shortcode_label['custom'] . '</a>';
  2387.             $sel_list .=                    '</li>';
  2388.             $sel_list .=                '</ul>';
  2389.             $sel_list .=            '</li>';
  2390.             $sel_list .=        '</ul>';
  2391.             $sel_list .=    '</div>';
  2392.  
  2393.  
  2394.             $tabs_title = $sel_list . $tabs_title;
  2395.  
  2396.             $first = true;
  2397.  
  2398.             foreach( $tabs as $key => $tab )
  2399.             {
  2400.                 $tab_count ++;
  2401.  
  2402.                 $extra = $first && ! $condensed ? 'avia-needs-margin' : '';
  2403.                 $extra .= $condensed ? ' avia-condensed-tab' : '';
  2404.                 $first = false;
  2405.  
  2406.                 $tabs_title .= "<a class='avia-custom-element-tab {$extra}' href='#avia-tab-{$tab_count}'>{$key}</a>";
  2407.                 $tabs_content .= "<div class='avia-tab av-custom-element-tab avia-tab-{$tab_count}' data-custom_content_name='{$key}'>";
  2408.  
  2409.                 if( empty( $tab ) )
  2410.                 {
  2411.                     $tabs_content .= '</div>';
  2412.                     continue;
  2413.                 }
  2414.  
  2415.                 usort( $tab, array( $this, 'sortByOrder' ) );
  2416.  
  2417.                 /**
  2418.                  * @since 4.8.2
  2419.                  * @param array $tab
  2420.                  * @param array $tabs
  2421.                  * @param string $key
  2422.                  * @return array
  2423.                  */
  2424.                 $tab = apply_filters( 'avf_custom_elements_buttons_sort_order', $tab, $tabs, $key );
  2425.  
  2426.                 $sort_order = 0;
  2427.                 foreach( $tab as $element )
  2428.                 {
  2429.                     if( empty( $element['sc_class']->config['invisible'] ) )
  2430.                     {
  2431.                         $sort_order ++;
  2432.                         $shortcode = $element['sc_class']->config;
  2433.  
  2434.                         if( $element['is_item'] )
  2435.                         {
  2436.                             $name = $shortcode['name'];
  2437.                             $shortcode['name'] = ! empty( $element['name_item'] ) ? $element['name_item'] : __( 'Item for:', 'avia_framework' ) . ' ' . $name;
  2438.                             $shortcode['tooltip'] = ! empty( $element['tooltip_item'] ) ? $element['tooltip_item'] : sprintf( __( 'Single item for %s:', 'avia_framework' ), $name ) . ' ' . $shortcode['tooltip'];
  2439.                         }
  2440.  
  2441.                         if( ! empty( $element['title'] ) )
  2442.                         {
  2443.                             $shortcode['name'] = $element['title'];
  2444.                         }
  2445.  
  2446.                         if( ! empty( $element['desc'] ) )
  2447.                         {
  2448.                             $shortcode['tooltip'] = $element['desc'];
  2449.                         }
  2450.  
  2451.                         if( ! isset( $shortcode['class'] ) )
  2452.                         {
  2453.                             $shortcode['class'] = '';
  2454.                         }
  2455.  
  2456.                         $shortcode['class'] .= ' avia-custom-element-button';
  2457.                         $shortcode['class'] .= $element['is_item'] ? ' avia-custom-element-item' : ' avia-custom-element-base';
  2458.                         $shortcode['btn_data'] = array(
  2459.                                                 'shortcode_handler'     => $element['shortcode'],
  2460.                                                 'base_shortcode'        => $element['base_sc'],
  2461.                                                 'is_item'               => $element['is_item'] ? 'true' : 'false',
  2462.                                                 'modal_title'           => $element['modal_title'],
  2463.                                                 'modal_subitem_title'   => $element['modal_subitem_title'],
  2464.                                                 'element_title'         => $element['title'],
  2465.                                                 'element_tooltip'       => $element['desc']
  2466.                                             );
  2467.                         $shortcode['btn_id'] = 'avia-element-btn-' . $element['id'];
  2468.  
  2469.                         $templ_id = $shortcode['php_class'] . '_' . $element['id'];
  2470.  
  2471.                         $tabs_content .= $this->create_shortcode_button( $shortcode, $sort_order, $templ_id, $element['id'] );
  2472.                     }
  2473.                 }
  2474.  
  2475.                 $tabs_content .= '</div>';
  2476.             }
  2477.  
  2478.             $select_class = '';
  2479.             $initial = 'base_elements_only';
  2480.  
  2481.             if( in_array( $this->subitem_custom_element_handling(), array( 'first', 'none' ) ) )
  2482.             {
  2483.                 $select_class = ' avia_hidden';
  2484.             }
  2485.  
  2486.             $tt = 'title="' . __( 'Click button, then hover over the elements to show edit action buttons for each element', 'avia_framework' ) . '"';
  2487.  
  2488.             $footer  = '';
  2489.             $footer .= '<div class="av-custom-element-footer av-custom-element-buttons">';
  2490.             $footer .=  '<div class="av-custom-element-info editing-disabled">' . esc_html( __( 'Editing of custom elements is not allowed on fullscreen mode.', 'avia_framework' ) ) . '</div>';
  2491.             $footer .=  '<div class="av-custom-element-button element-button-add-new button button-primary">' . esc_html( __( 'Add New Custom Element', 'avia_framework' ) ) . '</div>';
  2492.             $footer .=  '<div class="av-custom-element-button element-button-edit button button-primary" ' . $tt . '>' . esc_html( __( 'Edit Custom Elements', 'avia_framework' ) ) . '</div>';
  2493.             $footer .=  '<div class="av-custom-element-button element-button-end-edit button button-primary">' . esc_html( __( 'End Edit Custom Elements', 'avia_framework' ) ) . '</div>';
  2494.             $footer .=  '<div class="av-custom-element-select-buttons avia-form-element avia-style' . $select_class . '">';
  2495.             $footer .=      '<select name="av-filter-sc-element-types" title="' . __( 'Filter the custom element shortcode buttons for editing', 'avia_framework' ) . '" data-initial_select="' . $initial . '">';
  2496.             $footer .=          '<option value="">' . __( 'Show all elements', 'avia_framework' ) . '</option>';
  2497.             $footer .=          '<option value="base_elements_only">' . __( 'Base elements only', 'avia_framework' ) . '</option>';
  2498.             $footer .=          '<option value="item_elements_only">' . __( 'Item elements only', 'avia_framework' ) . '</option>';
  2499.             $footer .=      '</select>';
  2500.             $footer .=  '</div>';
  2501.             $footer .= '</div>';
  2502.  
  2503.             if( $this->current_user_can_manage() )
  2504.             {
  2505.                 $tabs_content .= $footer;
  2506.             }
  2507.         }
  2508.  
  2509.         /**
  2510.          * Sort the shortcode buttons
  2511.          *
  2512.          * @since 4.8
  2513.          * @param array $a
  2514.          * @param array $b
  2515.          * @return boolean
  2516.          */
  2517.         protected function sortByOrder( array $a, array $b )
  2518.         {
  2519.             if( empty( $a['sc_class']->config['order'] ) )
  2520.             {
  2521.                 $a['sc_class']->config['order'] = 10;
  2522.             }
  2523.  
  2524.             if( empty( $b['sc_class']->config['order'] ) )
  2525.             {
  2526.                 $b['sc_class']->config['order'] = 10;
  2527.             }
  2528.  
  2529.             return $b['sc_class']->config['order'] >= $a['sc_class']->config['order'] ? 1 : -1;
  2530.         }
  2531.  
  2532.         /**
  2533.          * Create JS templates
  2534.          *
  2535.          * @since 4.8
  2536.          */
  2537.         public function js_template_editor_elements()
  2538.         {
  2539.             foreach( $this->editor_elements as $element )
  2540.             {
  2541.                 $content = $element['info'][0]['raw_content'];
  2542.                 $args = ! $element['is_item'] ? $element['info'][0]['template_attr'] : $element['info'][0]['attr'];
  2543.  
  2544.                 $scripts = $this->js_scripts_from_array( $element['sc_class'], $args, $content, $element['id'] );
  2545.  
  2546.                 foreach( $scripts as $script )
  2547.                 {
  2548.                     echo $script;
  2549.                 }
  2550.             }
  2551.  
  2552.             $data = array(
  2553.                         'modal_title'           => __( 'Create A New Custom Element Template', 'avia_framework' ),
  2554.                         'modal_button'          => __( 'Create Element', 'avia_framework' ),
  2555.                         'modal_title_update'    => __( 'Update Custom Element Template Data', 'avia_framework' ),
  2556.                         'modal_button_update'   => __( 'Update Element', 'avia_framework' )
  2557.                     );
  2558.  
  2559.             $data = AviaHelper::create_data_string( $data );
  2560.  
  2561.             echo "\n<script type='text/html' id='avia-tmpl-add-new-element-modal-content' {$data}>\n";
  2562.             echo    $this->add_new_modal_popup_content();
  2563.             echo "\n</script>\n\n";
  2564.         }
  2565.  
  2566.         /**
  2567.          * Updates the element template setting to reflect the latest theme options setting in subelements.
  2568.          * This should avoid breaking existing elements in pages if user switches options.
  2569.          *
  2570.          * @since 4.8
  2571.          * @param array $elements
  2572.          * @param array $elementValues
  2573.          * @param aviaShortcodeTemplate $shortcode
  2574.          */
  2575.         public function prepare_popup_subitem_elements( array &$elements, array &$elementValues, aviaShortcodeTemplate $shortcode )
  2576.         {
  2577.             if( 'individually' == $this->subitem_custom_element_handling() )
  2578.             {
  2579.                 return;
  2580.             }
  2581.  
  2582.             $index = $this->is_editable_modal_group_element( $shortcode, 'index' );
  2583.  
  2584.             if( false === $index )
  2585.             {
  2586.                 return;
  2587.             }
  2588.  
  2589.             $template_id = '';
  2590.  
  2591.             foreach( $elements as &$element )
  2592.             {
  2593.                 $break = false;
  2594.                 if( isset( $element['id'] ) && 'one_element_template' == $element['id'] )
  2595.                 {
  2596.                     switch( $this->subitem_custom_element_handling() )
  2597.                     {
  2598.                         case 'first':
  2599.                             if( ! empty( $element['std'] ) )
  2600.                             {
  2601.                                 $template_id = $element['std'];
  2602.                             }
  2603.                             $break = true;
  2604.                             break;
  2605.                         case 'none':
  2606.                             $element['std'] = '';
  2607.                             $break = true;
  2608.                             break;
  2609.                     }
  2610.                 }
  2611.  
  2612.                 if( $break )
  2613.                 {
  2614.                     break;
  2615.                 }
  2616.             }
  2617.  
  2618.             unset( $element );
  2619.  
  2620.             $std = &$elements[ $index ]['std'];
  2621.  
  2622.             if( is_array( $std ) && ! empty( $std ) )
  2623.             {
  2624.                 foreach( $std as &$value )
  2625.                 {
  2626.                     $value['element_template'] = $template_id;
  2627.                 }
  2628.  
  2629.                 unset( $value );
  2630.             }
  2631.  
  2632.             foreach( $elements[ $index ]['subelements'] as &$subelement )
  2633.             {
  2634.                 if( isset( $subelement['id'] ) && 'element_template' == $subelement['id'] )
  2635.                 {
  2636.                     $subelement['std'] = $template_id;
  2637.                     break;
  2638.                 }
  2639.             }
  2640.  
  2641.             unset( $subelement );
  2642.         }
  2643.  
  2644.         /**
  2645.          * Change custom element template settings when opening a modal popup depending on
  2646.          * theme options settings for subitems
  2647.          *
  2648.          * @since 4.8
  2649.          * @param string $shortcode
  2650.          * @param array $elements
  2651.          * @param boolean $template_changed
  2652.          */
  2653.         public function popup_editor_adjust_subitems_settings( &$shortcode, array &$elements, $template_changed )
  2654.         {
  2655.             if( ! $this->element_templates_enabled() )
  2656.             {
  2657.                 return;
  2658.             }
  2659.  
  2660.             if( 'individually' == $this->subitem_custom_element_handling() )
  2661.             {
  2662.                 return;
  2663.             }
  2664.  
  2665.             //  if the ajax request told us that we are fetching the subitem we rely on a correct setting of the element_template value
  2666.             if( ! empty( $_POST['params']['subelement'] ) )
  2667.             {
  2668.                 return;
  2669.             }
  2670.  
  2671.             //  editing from custom post type screen no need to change anything (content should be correct) - used by e.g. WPML to translate
  2672.             if( $this->is_edit_element_page() )
  2673.             {
  2674.                 return;
  2675.             }
  2676.  
  2677.             $sc_array = $this->get_element_template_info_from_content( $shortcode );
  2678.  
  2679.             //  fallback
  2680.             if( ! array_key_exists( $sc_array[0]['shortcode'], Avia_Builder()->shortcode ) )
  2681.             {
  2682.                 return;
  2683.             }
  2684.  
  2685.             $shortcode_class = Avia_Builder()->shortcode_class[ Avia_Builder()->shortcode[ $sc_array[0]['shortcode'] ] ];
  2686.  
  2687.             if( ! $this->is_editable_base_element( $shortcode_class ) || ! $this->is_editable_modal_group_element( $shortcode_class ) )
  2688.             {
  2689.                 return;
  2690.             }
  2691.  
  2692.             //  when editing subitem we can leave unchanged
  2693.             if( $sc_array[0]['shortcode'] != $sc_array[0]['template_sc'] )
  2694.             {
  2695.                 return;
  2696.             }
  2697.  
  2698.             if( ! isset( $sc_array[0]['attr']['element_template'] ) )
  2699.             {
  2700.                 $sc_array[0]['attr']['element_template'] = '';
  2701.             }
  2702.  
  2703.             if( ! isset( $sc_array[0]['template_attr']['element_template'] ) )
  2704.             {
  2705.                 $sc_array[0]['template_attr']['element_template'] = '';
  2706.             }
  2707.  
  2708.             $new_one_template_id = '';
  2709.  
  2710.             if( 'first' == $this->subitem_custom_element_handling() )
  2711.             {
  2712.                 if( ( isset( $_REQUEST['element_post_type'] ) && $_REQUEST['element_post_type'] == $this->get_post_type() ) || ( isset( $_REQUEST['post_type'] ) && $_REQUEST['post_type'] == $this->get_post_type() ) )
  2713.                 {
  2714.                     //  editing a custom element template - no hierarchical templates allowed (hidden with CSS)
  2715.                     $new_one_template_id = isset( $sc_array[0]['template_attr']['one_element_template'] ) ? $sc_array[0]['template_attr']['one_element_template'] : '';
  2716.                 }
  2717.                 else
  2718.                 {
  2719.                     //  editing an element on a page/post/....
  2720.                     $this->load_template_cache( $sc_array[0]['template_attr']['element_template'] );
  2721.                     $template = $this->get_template( $sc_array[0]['template_attr']['element_template'], $sc_array[0]['shortcode'] );
  2722.  
  2723.                     if( false === $template )
  2724.                     {
  2725.                         $new_one_template_id = '';
  2726.                     }
  2727.                     else
  2728.                     {
  2729.                         $new_one_template_id = $template['sc_array'][0]['template_attr']['one_element_template'];
  2730.                     }
  2731.                 }
  2732.             }
  2733.  
  2734.             $sc_array[0]['template_attr']['one_element_template'] = $new_one_template_id;
  2735.  
  2736.             $content = array();
  2737.  
  2738.             if( is_array( $sc_array[0]['content'] ) )
  2739.             {
  2740.                 foreach( $sc_array[0]['content'] as &$subitem )
  2741.                 {
  2742.                     $subitem['attr']['element_template'] = $new_one_template_id;
  2743.  
  2744.                     $sub = $subitem['attr'];
  2745.  
  2746.                     $sub['content'] = isset( $subitem['content'] ) ? $subitem['content'] : '';
  2747.                     if( is_array( $sub['content'] ) )
  2748.                     {
  2749.                         $sub['content'] = isset( $subitem['raw_content'] ) ? $subitem['raw_content'] : '';
  2750.                     }
  2751.  
  2752.                     $content[] = $sub;
  2753.                 }
  2754.  
  2755.                 unset( $subitem );
  2756.             }
  2757.  
  2758.             $outer = $sc_array[0]['template_attr'];
  2759.             $outer['content'] = $content;
  2760.  
  2761.             $shortcode = $this->create_custom_element_shortcode( $shortcode_class, $outer, array(), false, true );
  2762.         }
  2763.  
  2764.         /**
  2765.          * Creates a shortcode button for the backend canvas to add or edit a custom element
  2766.          *
  2767.          * @since 4.8
  2768.          * @param array $shortcode
  2769.          * @param int $sort_order
  2770.          * @param string $templ_id
  2771.          * @param int $element_id
  2772.          * @return string
  2773.          */
  2774.         protected function create_shortcode_button( array $shortcode, $sort_order, $templ_id, $element_id )
  2775.         {
  2776.             $edit = avia_font_manager::get_frontend_icon( 'pencil', 'svg_entypo-fontello', [ 'aria-hidden' => 'true', 'title' => '', 'desc' => '' ] );
  2777.             $edit_class = avia_font_manager::get_frontend_icon_classes( $edit['font'], 'string' );
  2778.  
  2779.             $delete = avia_font_manager::get_frontend_icon( 'cancel', 'svg_entypo-fontello', [ 'aria-hidden' => 'true', 'title' => '', 'desc' => '' ] );
  2780.             $delete_class = avia_font_manager::get_frontend_icon_classes( $delete['font'], 'string' );
  2781.  
  2782.             $clone = avia_font_manager::get_frontend_icon( 'chat', 'svg_entypo-fontello', [ 'aria-hidden' => 'true', 'title' => '', 'desc' => '' ] );
  2783.             $clone_class = avia_font_manager::get_frontend_icon_classes( $clone['font'], 'string' );
  2784.  
  2785.             /**
  2786.              * Add a 4th action button
  2787.              *
  2788.              * @used_by         avia_WPML               10
  2789.              * @since 4.8
  2790.              */
  2791.             $additional = apply_filters( 'avf_cet_additional_sc_action_btn', '', $element_id );
  2792.  
  2793.             $icons  = '';
  2794.             $icons .=   '<div class="avia-custom-elements-actions-overlay avia-font-' . $edit['font'] . '" data-element_id="' . $element_id . '" data-el_template="element_' . $templ_id . '" data-template="' . $templ_id . '">';
  2795.             $icons .=       '<div class="element-sc-action-button element-edit" title="' . esc_html__( 'Edit Custom Element', 'avia_framework' ) . '">';
  2796.             $icons .=           "<span class='$edit_class' {$edit['attr']}>";
  2797.             $icons .=               $edit['svg'];
  2798.             $icons .=           '</span>';
  2799.             $icons .=       '</div>';
  2800.             $icons .=       '<div class="element-sc-action-button element-delete" title="' . esc_html__( 'Delete Custom Element', 'avia_framework' ) . '">';
  2801.             $icons .=           "<span class='$delete_class' {$delete['attr']}>";
  2802.             $icons .=               $delete['svg'];
  2803.             $icons .=           '</span>';
  2804.             $icons .=       '</div>';
  2805.             $icons .=       '<div class="element-sc-action-button element-clone" title="' . esc_html__( 'Clone Custom Element', 'avia_framework' ) . '">';
  2806.             $icons .=           "<span class='$clone_class' {$clone['attr']}>";
  2807.             $icons .=               $clone['svg'];
  2808.             $icons .=           '</span>';
  2809.             $icons .=       '</div>';
  2810.             $icons .=       $additional;
  2811.             $icons .=       '<div class="avia-sc-button-loading avia_loading"></div>';
  2812.             $icons .=   '</div>';
  2813.  
  2814.             $btn = Avia_Builder()->create_shortcode_button( $shortcode, $sort_order, $templ_id );
  2815.  
  2816.             return str_replace( '</a>', $icons . '</a>', $btn );
  2817.         }
  2818.  
  2819.         /**
  2820.          * Returns a script array for "Element Template Edit" and "Use as ALB element"
  2821.          *
  2822.          *      template_id => script
  2823.          *
  2824.          * @since 4.8
  2825.          * @param aviaShortcodeTemplate $shortcode
  2826.          * @param array $args
  2827.          * @param type $content
  2828.          * @param type $element_id
  2829.          * @return array
  2830.          */
  2831.         protected function js_scripts_from_array( aviaShortcodeTemplate $shortcode, array $args, $content, $element_id )
  2832.         {
  2833.             $scripts = array();
  2834.  
  2835.             //  Create base custom element for editing
  2836.             $args[ aviaElementManager::ELEMENT_UID ] = '';
  2837.  
  2838.             $data = array(
  2839.                     'element_post_type' => $this->get_post_type(),
  2840.                     'element_id'        => $element_id
  2841.                 );
  2842.  
  2843.             $template = $shortcode->prepare_editor_element( $content, $args, true, false, $data );
  2844.             if( ! is_array( $template ) )
  2845.             {
  2846.                 $id = "element_{$shortcode->config['php_class']}_{$element_id}";
  2847.                 $scripts[ "#avia-tmpl-{$id}" ] = Avia_Builder()->js_template_script( $template, $id );
  2848.             }
  2849.  
  2850.             //  For using as an "ALB base element" we have to set template to this id
  2851.             $args['element_template'] = $element_id;
  2852.  
  2853.             $template = $shortcode->prepare_editor_element( $content, $args, true );
  2854.             if( ! is_array( $template ) )
  2855.             {
  2856.                 $id = "{$shortcode->config['php_class']}_{$element_id}";
  2857.                 $scripts[ "#avia-tmpl-{$id}" ] = Avia_Builder()->js_template_script( $template, $id );
  2858.             }
  2859.  
  2860.             return $scripts;
  2861.         }
  2862.  
  2863.         /**
  2864.          * Prepares shortcode to create an item template and set the default value(s)
  2865.          *
  2866.          * @since 4.8
  2867.          * @param aviaShortcodeTemplate $sc
  2868.          * @param array $std                    array of default settings for subitems (array of array)
  2869.          */
  2870.         protected function set_as_item_template( aviaShortcodeTemplate $sc, array $std = array() )
  2871.         {
  2872.             if( ! $this->element_templates_enabled() )
  2873.             {
  2874.                 return;
  2875.             }
  2876.  
  2877.             foreach( $sc->elements as &$element )
  2878.             {
  2879.                 if( ! isset( $element['type'] ) || ! isset( $element['id'] ) )
  2880.                 {
  2881.                     continue;
  2882.                 }
  2883.  
  2884.                 if( $element['type'] == 'modal_group' )
  2885.                 {
  2886.                     if( ! isset( $element['std'] ) || ! is_array( $element['std'] ) )
  2887.                     {
  2888.                         $element['std'] = array();
  2889.                     }
  2890.  
  2891.                     if( ! empty( $std ) )
  2892.                     {
  2893.                         $element['std'] = $std;
  2894.                     }
  2895.                     else if( empty( $element['std'] ) )
  2896.                     {
  2897.                         $element['std'][] = $sc->get_default_modal_group_args();
  2898.                     }
  2899.                     else
  2900.                     {
  2901.                         $element['std'] = array( $element['std'][0] );
  2902.                     }
  2903.                 }
  2904.                 else if( $element['id'] == 'select_element_template' )
  2905.                 {
  2906.                     $element['std'] = 'item';
  2907.                 }
  2908.             }
  2909.  
  2910.             unset( $element );
  2911.         }
  2912.  
  2913.         /**
  2914.          * Extends the predefined elements to support editable elements modal popup.
  2915.          * This function does not make any checks. Make sure to call it only when the additional
  2916.          * elements are needed.
  2917.          *
  2918.          * Element must define:
  2919.          *
  2920.          *      'lockable'      => true,
  2921.          *      'editable_el'   => true         //  for modal_group elements to define item templates
  2922.          *
  2923.          * Only on edit ALB elements screen:
  2924.          *      - Adds a checkbox below to allow disabling override
  2925.          *      - Adds a selectbox to allow creating templates for 'modal_group' elements
  2926.          *
  2927.          * @since 4.8
  2928.          * @param aviaShortcodeTemplate $sc
  2929.          */
  2930.         public function add_element_template_options( aviaShortcodeTemplate $sc )
  2931.         {
  2932.             if( ! $this->element_templates_enabled() )
  2933.             {
  2934.                 return;
  2935.             }
  2936.  
  2937.             $sc->elements = $this->element_template_options( $sc->elements );
  2938.         }
  2939.  
  2940.         /**
  2941.          * Extends the predefined elements for subitems to support editable elements modal popup.
  2942.          * See function aviaElementTemplates::add_element_template_options() for more details.
  2943.          * Wrapper for aviaElementTemplates::add_subitem_element_template_options()
  2944.          *
  2945.          * @since 4.8
  2946.          * @param array $sub_elements
  2947.          * @return array
  2948.          */
  2949.         public function add_subitem_element_template_options( array $sub_elements )
  2950.         {
  2951.             if( ! $this->element_templates_enabled() )
  2952.             {
  2953.                 return $sub_elements;
  2954.             }
  2955.  
  2956.             return $this->element_template_options( $sub_elements, true );
  2957.         }
  2958.  
  2959.         /**
  2960.          * Recursive: Scans the element array and subelement array
  2961.          *
  2962.          * @since 4.8
  2963.          * @param array $elements
  2964.          * @param boolean $subelements
  2965.          * @return array
  2966.          */
  2967.         protected function element_template_options( array $elements, $subelements = false )
  2968.         {
  2969.             $new_els = array();
  2970.             $hide = $subelements ? ' avia-hide-on-edit-base-template' : ' avia-hide-on-edit-item-template';
  2971.  
  2972.             foreach( $elements as $element )
  2973.             {
  2974.                 if( ! isset( $element['container_class'] ) )
  2975.                 {
  2976.                     $element['container_class'] = '';
  2977.                 }
  2978.  
  2979.                 if( isset( $element['id'] ) )
  2980.                 {
  2981.                     if( ! isset( $element['type'] ) || $element['type'] != 'modal_group' )
  2982.                     {
  2983.                         $element['container_class'] .= $hide;
  2984.                     }
  2985.                 }
  2986.  
  2987.                 $actions = $this->get_element_options_actions( $element );
  2988.  
  2989.                 if( empty( $actions ) )
  2990.                 {
  2991.                     $new_els[] = $element;
  2992.                     continue;
  2993.                 }
  2994.  
  2995.                 if( in_array( 'editable_item', $actions ) )
  2996.                 {
  2997.                     $subtype = array(
  2998.                                     __( 'Base element template', 'avia_framework' ) => '',
  2999.                                     __( 'Item element template', 'avia_framework' ) => 'item'
  3000.                                 );
  3001.  
  3002.                     $select = array(
  3003.                                     'name'              => __( 'Create an editable template', 'avia_framework' ),
  3004.                                     'desc'              => __( 'Select what template you want to create. If you create an item template any settings for base template you make here will be ignored.', 'avia_framework' ),
  3005.                                     'id'                => 'select_element_template',
  3006.                                     'type'              => 'select',
  3007.                                     'std'               => '',
  3008.                                     'container_class'   => 'av-elements-item-select',
  3009.                                     'subtype'           => $subtype
  3010.                                 );
  3011.  
  3012.                     $new_els[] = $select;
  3013.  
  3014.                     $element['container_class'] .= ' av-select-element-template';
  3015.                     $element['subelements'] = $this->element_template_options( $element['subelements'], true );
  3016.  
  3017.                     if( ! in_array( 'lockable', $actions ) )
  3018.                     {
  3019.                         $new_els[] = $element;
  3020.                     }
  3021.                 }
  3022.  
  3023.                 if( in_array( 'lockable', $actions ) )
  3024.                 {
  3025.                     $desc = '';
  3026.  
  3027.                     if( isset( $element['name'] ) && ! empty( $element['name'] ) )
  3028.                     {
  3029.                         $desc = $element['name'];
  3030.                     }
  3031.                     else if( isset( $element['desc'] ) && ! empty( $element['desc'] ) )
  3032.                     {
  3033.                         $desc = $element['desc'];
  3034.  
  3035.                         //  currently possible for checkboxes
  3036.                         if( is_array( $desc ) )
  3037.                         {
  3038.                             $desc = __( 'this option', 'avia_framework' );
  3039.                         }
  3040.                     }
  3041.  
  3042.                     if( strlen( $desc ) > 50 )
  3043.                     {
  3044.                         $desc = explode( "\n", wordwrap( $desc, 50 ) );
  3045.                         $desc = $desc[0] . '...';
  3046.                     }
  3047.  
  3048.                     if( ! empty( $desc ) )
  3049.                     {
  3050.                         $desc = '&quot;' . $desc . '&quot;';
  3051.                     }
  3052.  
  3053.                     $element['container_class'] .= ' av-lock-element-before';
  3054.  
  3055.                     $new_els[] = $element;
  3056.  
  3057.                     $desc = array(
  3058.                                 'checked'   => sprintf( __( 'Unlock %s', 'avia_framework' ), $desc ),
  3059.                                 'unchecked' => sprintf( __( 'Lock %s', 'avia_framework' ), $desc )
  3060.                             );
  3061.  
  3062.                     $lock = array(
  3063.                                     'desc'              => $desc,
  3064.                                     'id'                => $element['id'] . '__locked',
  3065.                                     'type'              => 'checkbox',
  3066.                                     'container_class'   => 'av-lock-element-checkbox av-multi-checkbox' . $hide,
  3067.                                     'std'               => '',
  3068.                                     'tooltip'           => __( 'Do not allow to change this setting when using this template', 'avia_framework' ),
  3069.                                     'required'          => isset( $element['required'] ) ? $element['required'] : array(),
  3070.                                     'lockable_check'    => $element['id']
  3071.                                 );
  3072.  
  3073.                     $new_els[] = $lock;
  3074.                 }
  3075.             }
  3076.  
  3077.             return $new_els;
  3078.         }
  3079.  
  3080.         /**
  3081.          * Checks for element settings
  3082.          *
  3083.          * @since 4.8
  3084.          * @param array $element
  3085.          * @return array
  3086.          */
  3087.         protected function get_element_options_actions( array $element )
  3088.         {
  3089.             $action = array();
  3090.  
  3091.             if( isset( $element['lockable'] ) && $element['lockable'] === true )
  3092.             {
  3093.                 $action[] = 'lockable';
  3094.             }
  3095.  
  3096.             if( isset( $element['editable_item'] ) && $element['editable_item'] === true )
  3097.             {
  3098.                 $action[] = 'editable_item';
  3099.             }
  3100.  
  3101.             return $action;
  3102.         }
  3103.  
  3104.         /**
  3105.          * - Assign the correct shortcode category to the post.
  3106.          *   Creates the category, if it does not exist.
  3107.          *
  3108.          * @since 4.8
  3109.          * @param string $content
  3110.          * @param int $post_id
  3111.          * @param array $tree
  3112.          * @param boolean $force_update
  3113.          */
  3114.         public function updated_post_content( $content, $post_id, array $tree, $force_update = false )
  3115.         {
  3116.             if( $force_update !== true )
  3117.             {
  3118.                 if( ! isset( $_REQUEST['post_type'] ) || $_REQUEST['post_type'] != $this->get_post_type() )
  3119.                 {
  3120.                     return;
  3121.                 }
  3122.             }
  3123.  
  3124.             $term_id = 0;
  3125.             $el_type = '';
  3126.  
  3127.             if( ! empty( $tree ) )
  3128.             {
  3129.                 $shortcode = $tree[0]['tag'];
  3130.                 $sc = Avia_Builder()->shortcode_class[ Avia_Builder()->shortcode[ $shortcode ] ];
  3131.                 $term = get_term_by( 'slug', $this->cached_update_sc_array[0]['template_sc'], $this->get_taxonomy() );
  3132.  
  3133.                 if( $shortcode == $this->cached_update_sc_array[0]['template_sc'] )
  3134.                 {
  3135.                     $el_type = ! $this->is_editable_modal_group_element( $sc ) ? 'base_element': 'modal_base_element';
  3136.                 }
  3137.                 else
  3138.                 {
  3139.                     $el_type = 'modal_sub_element';
  3140.                 }
  3141.  
  3142.                 if( ! $term instanceof WP_Term )
  3143.                 {
  3144.                     if( $shortcode == $this->cached_update_sc_array[0]['template_sc'] )
  3145.                     {
  3146.                         $term = isset( $sc->config['name_template'] ) ? $sc->config['name_template'] : $sc->config['name'];
  3147.                         $slug = $shortcode;
  3148.                         $description = isset( $sc->config['tooltip_template'] ) ? $sc->config['tooltip_template'] :$sc->config['tooltip'];
  3149.                     }
  3150.                     else
  3151.                     {
  3152.                         $term .= isset( $sc->config['name_item'] ) ? $sc->config['name_item'] : $sc->config['name'] . ' ' . __( 'Item', 'avia_framework' );
  3153.                         $slug = $this->cached_update_sc_array[0]['template_sc'];
  3154.                         $description = isset( $sc->config['tooltip_item'] ) ? $sc->config['tooltip_item'] : __( 'Item:', 'avia_framework' ) . ' ' . $sc->config['tooltip_item'];
  3155.                     }
  3156.  
  3157.                     $args = array(
  3158.                                 'slug'          => $slug,
  3159.                                 'description'   => $description
  3160.                             );
  3161.  
  3162.                     $taxonomy = $this->get_taxonomy();
  3163.  
  3164.                     $result = wp_insert_term( $term, $taxonomy, $args );
  3165.  
  3166.                     $term_id = ( is_array( $result ) && isset( $result['term_id'] ) ) ? (int) $result['term_id'] : 0;
  3167.                 }
  3168.                 else
  3169.                 {
  3170.                     $term_id = $term->term_id;
  3171.                 }
  3172.             }
  3173.  
  3174.             wp_set_object_terms( $post_id, $term_id, $this->get_taxonomy(), false );
  3175.  
  3176.             //  store info for a quick reference for lists or 3rd party plugins like WPML
  3177.             update_post_meta( $post_id, '_av_element_template_type', $el_type );
  3178.         }
  3179.  
  3180.         /**
  3181.          * Returns an array of custom element templates that have a basic shortcode button
  3182.          * (modal group shortcodes are filtered)
  3183.          *
  3184.          * @since 4.8
  3185.          * @return array
  3186.          */
  3187.         protected function get_all_element_template_shortcode_buttons()
  3188.         {
  3189.             $list = array();
  3190.  
  3191.             $args = array(
  3192.                         'post_type'         => $this->get_post_type(),
  3193.                         'post_status'       => array( 'publish', 'pending', 'draft', 'trash' ),
  3194.                         'posts_per_page'    => -1,
  3195.                         'orderby'           => array( 'title' => 'ASC' )
  3196.                     );
  3197.  
  3198.  
  3199.             $q = new WP_Query( $args );
  3200.  
  3201.             $elements = $q->posts;
  3202.  
  3203.             if( empty( $elements ) )
  3204.             {
  3205.                 return $list;
  3206.             }
  3207.  
  3208.             foreach( $elements as $key => $element )
  3209.             {
  3210.                 $sc = trim( Avia_Builder()->get_posts_alb_content( $element->ID ) );
  3211.  
  3212.                 if( empty( $sc ) )
  3213.                 {
  3214.                     continue;
  3215.                 }
  3216.  
  3217.                 $info = $this->get_element_template_info_from_content( $sc );
  3218.                 $shortcode = $info[0]['template_sc'];
  3219.                 $is_item = false;
  3220.                 $base_sc = '';
  3221.                 $modal_title = sprintf( __( ' - Customize: %s', 'avia_framework' ), $element->post_title );
  3222.                 $modal_subitem_title = '';
  3223.  
  3224.                 //  check for a subitem shortcode
  3225.                 if( ! array_key_exists( $shortcode, Avia_Builder()->shortcode ) )
  3226.                 {
  3227.                     $is_item = true;
  3228.                     $base_sc = $info[0]['shortcode'];
  3229.                     $modal_title =  sprintf( __( ' - Customize Item: %s', 'avia_framework' ), $element->post_title );
  3230.                     $modal_subitem_title = sprintf( __( ' - Customize: %s', 'avia_framework' ), $element->post_title );
  3231.  
  3232.                     if( ! array_key_exists( $base_sc, Avia_Builder()->shortcode ) )
  3233.                     {
  3234.                         continue;
  3235.                     }
  3236.  
  3237.                     $sc_class = Avia_Builder()->shortcode_class[ Avia_Builder()->shortcode[ $base_sc ] ];
  3238.  
  3239.                     if( ! $this->is_editable_modal_group_element( $sc_class ) )
  3240.                     {
  3241.                         continue;
  3242.                     }
  3243.                 }
  3244.                 else
  3245.                 {
  3246.                     $sc_class = Avia_Builder()->shortcode_class[ Avia_Builder()->shortcode[ $shortcode ] ];
  3247.                 }
  3248.  
  3249.                 $list[] = array(
  3250.                                 'title'     => $element->post_title,
  3251.                                 'id'        => $element->ID,
  3252.                                 'desc'      => $element->post_excerpt,
  3253.                                 'content'   => $sc,
  3254.                                 'info'      => $info,
  3255.                                 'sc_class'  => $sc_class,
  3256.                                 'shortcode' => $shortcode,
  3257.                                 'is_item'   => $is_item,
  3258.                                 'base_sc'   => $base_sc,
  3259.                                 'modal_title'           => $modal_title,
  3260.                                 'modal_subitem_title'   => $modal_subitem_title
  3261.                             );
  3262.             }
  3263.  
  3264.             return $list;
  3265.         }
  3266.  
  3267.         /**
  3268.          * Returns the input elements for a modal popup to create a new custom element template.
  3269.          * Can be rendered as "content" to modal popur.
  3270.          *
  3271.          * @since 4.8
  3272.          * @return string
  3273.          */
  3274.         protected function add_new_modal_popup_content()
  3275.         {
  3276.             $sc = array();
  3277.             $tooltips = array();
  3278.  
  3279.             //  Make sure to have same ordering as in builder canvas
  3280.             foreach( Avia_Builder()->tabs as $tab => &$tab_content )
  3281.             {
  3282.                 $sc[ $tab ] = array();
  3283.             }
  3284.  
  3285.             unset( $tab_content );
  3286.  
  3287.             foreach( Avia_Builder()->shortcode_class as $shortcode )
  3288.             {
  3289.                 if( ! $this->is_editable_base_element( $shortcode ) )
  3290.                 {
  3291.                     continue;
  3292.                 }
  3293.  
  3294.                 if( ! empty( $shortcode->config['tinyMCE']['tiny_only'] ) )
  3295.                 {
  3296.                     continue;
  3297.                 }
  3298.  
  3299.                 $tab = isset( $shortcode->config['tab'] ) ? $shortcode->config['tab'] : Avia_Builder()->default_sc_btn_tab_name;
  3300.                 $sc[ $tab ][ $shortcode->config['name'] ] = $shortcode;
  3301.             }
  3302.  
  3303.             foreach( $sc as $tab => $shortcodes )
  3304.             {
  3305.                 if( empty( $shortcodes ) )
  3306.                 {
  3307.                     unset( $sc[ $tab ] );
  3308.                 }
  3309.                 else
  3310.                 {
  3311.                     ksort( $shortcodes );
  3312.                     $sc[ $tab ] = $shortcodes;
  3313.                 }
  3314.             }
  3315.  
  3316.             $subtype = array();
  3317.  
  3318.             foreach( $sc as $tab => $shortcodes )
  3319.             {
  3320.                 foreach( $shortcodes as $sc_name => $shortcode )
  3321.                 {
  3322.                     $subtype[ $tab ][ $sc_name ] = $shortcode->config['shortcode'];
  3323.                     $tooltips[ $shortcode->config['shortcode'] ] = $shortcode->config['tooltip'];
  3324.  
  3325.                     if( 'individually' != $this->subitem_custom_element_handling() )
  3326.                     {
  3327.                         continue;
  3328.                     }
  3329.  
  3330.                     if( $this->is_editable_modal_group_element( $shortcode ) )
  3331.                     {
  3332.                         $name = ! empty( $shortcode->config['name_item'] ) ? $shortcode->config['name_item'] : sprintf( __( 'Item for %s: ', 'avia_framework' ),  $shortcode->config['name'] );
  3333.                         $tooltip = ! empty( $shortcode->config['tooltip_item'] ) ? $shortcode->config['tooltip_item'] : sprintf( __( 'Item for %s:', 'avia_framework' ),  $shortcode->config['name'] ) . ' ' . $shortcode->config['tooltip'];
  3334.  
  3335.                         $subtype[ $tab ][ "--- {$name}" ] = $shortcode->config['shortcode_nested'][0] . ';' . $shortcode->config['shortcode'];
  3336.                         $tooltips[ $shortcode->config['shortcode_nested'][0] ] = $tooltip;
  3337.                     }
  3338.                 }
  3339.             }
  3340.  
  3341.             $elements = array(
  3342.  
  3343.                     array(
  3344.                             'name'      => __( 'Create a new element', 'avia_framework' ),
  3345.                             'desc'      => __( 'Select the base element for your new custom element.', 'avia_framework' ),
  3346.                             'id'        => 'add_new_element_shortcode',
  3347.                             'type'      => 'select',
  3348.                             'class'     => 'av_add_new_element_shortcode',
  3349.                             'std'       => '',
  3350.                             'with_first'    => true,
  3351.                             'subtype'   => $subtype,
  3352.                             'tooltips'  => $tooltips
  3353.                         ),
  3354.  
  3355.                 );
  3356.  
  3357.             $elements = array_merge( $elements, $this->modal_popup_element_post_infos() );
  3358.  
  3359.             $desc  = '<br />';
  3360.             $desc .= __( 'A custom element is a new element with predefined option values based on an existing element.', 'avia_framework' ) . '<br/><br/>';
  3361.             $desc .= __( 'When creating a new custom element you can set default values for each option. You can also lock an option so it will no longer be possible to change it once you add a new instance of the new custom element to the page canvas.', 'avia_framework' ) . ' ';
  3362.             $desc .= __( 'If you edit a locked value of a custom element, it will automatically change for all elements on your site.', 'avia_framework' ) . '<br/><br/>';
  3363.             $desc .= __( 'Learn more about custom elements in the', 'avia_framework' ) . ' <a href="https://kriesi.at/documentation/enfold/custom-element-templates/" target="_blank" rel="noopener noreferrer">' . __( 'documentation', 'avia_framework' ) . '.</a>';
  3364.  
  3365.             $elements[] = array(
  3366.                     'name'      => __( 'What is a custom element?', 'avia_framework' ),
  3367.                     'desc'      => $desc,
  3368.                     'id'        => 'add_new_element_shortcode_heading',
  3369.                     'description_class' => 'av-builder-note av-notice',
  3370.                     'type'      => 'heading',
  3371.                     'std'       => '',
  3372.                 );
  3373.  
  3374.  
  3375.             $content = AviaHtmlHelper::render_multiple_elements( $elements );
  3376.  
  3377.             return $content;
  3378.         }
  3379.  
  3380.         /**
  3381.          * Returnns the input fields bound to the element post type
  3382.          *
  3383.          * @since 4.8
  3384.          * @param array $std
  3385.          * @return array
  3386.          */
  3387.         public function modal_popup_element_post_infos( $std = array() )
  3388.         {
  3389.             $title = isset( $std['post_title'] ) ? trin( $std['post_title'] ) : '';
  3390.             $tooltip = isset( $std['post_excerpt'] ) ? trin( $std['post_excerpt'] ) : '';
  3391.  
  3392.  
  3393.             $elements = array(
  3394.                     array(
  3395.                             'name'      => __( 'Name of Element', 'avia_framework' ),
  3396.                             'desc'      => __( 'Enter a custom name for this element. At least 4 characters and limited to 30 characters. Allowed characters are Whitespace, a-zA-Z0-9-_/', 'avia_framework' ),
  3397.                             'id'        => 'base_element_title',
  3398.                             'type'      => 'input',
  3399.                             'container_class' => 'avia-no-special-character-msg',
  3400.                             'class'     => 'avia-elements-check-title',
  3401.                             'std'       => $title,
  3402.                             'attr'      => ' placeholder="' . __( 'Add your Custom Element Name', 'avia_framework' ) . '" ',
  3403.                             'required'  => array( 'add_new_element_shortcode', 'not', '' )
  3404.                         ),
  3405.  
  3406.                     array(
  3407.                             'name'      => __( 'Tooltip', 'avia_framework' ),
  3408.                             'desc'      => __( 'Enter a description text - will be displayed when you hover above the custom element icons in ALB or in selectboxes for custom elements', 'avia_framework' ),
  3409.                             'id'        => 'base_element_tooltip',
  3410.                             'type'      => 'textarea',
  3411.                             'container_class' => 'avia-no-special-character-msg',
  3412.                             'std'       => $tooltip,
  3413.                             'required'  => array( 'add_new_element_shortcode', 'not', '' )
  3414.                         )
  3415.                 );
  3416.  
  3417.             return $elements;
  3418.         }
  3419.  
  3420.  
  3421.         /**
  3422.          * Returns the subtype array with available element templates for modal popup select box.
  3423.          * The current element is put in ( xxx ) and will not be possible to select.
  3424.          *
  3425.          * @since 4.8
  3426.          * @param aviaShortcodeTemplate $shortcode
  3427.          * @param boolean $item_element
  3428.          * @return array
  3429.          */
  3430.         public function get_extended_modal_subtypes_array( aviaShortcodeTemplate $shortcode, $item_element )
  3431.         {
  3432.             $max_level = avia_get_option( 'custom_el_hierarchical_templates' ) != 'hierarchical' ? 0 : -1;
  3433.  
  3434.             $subtypes = array(
  3435.                             __( 'No Custom Element used - all options available', 'avia_framework' )    => '',
  3436.                         );
  3437.  
  3438.             $tooltips = array(
  3439.                             ''  => __( 'All theme options are available and can be changed', 'avia_framework' ),
  3440.                         );
  3441.  
  3442.             if( ! $shortcode instanceof aviaShortcodeTemplate )
  3443.             {
  3444.                 return array(
  3445.                         'subtypes'  => $subtypes,
  3446.                         'tooltips'  => $tooltips
  3447.                     );
  3448.             }
  3449.  
  3450.             $terms = $shortcode->config['shortcode'];
  3451.             if( $item_element && isset( $shortcode->config['shortcode_nested'][0] ) )
  3452.             {
  3453.                 $terms = $shortcode->config['shortcode_nested'][0];
  3454.             }
  3455.  
  3456.             $args = array(
  3457.                         'post_type'     => $this->get_post_type(),
  3458.                         'post_status'   => array( 'publish', 'pending', 'draft', 'trash' ),
  3459.                         'tax_query'     => array(
  3460.                                                 array(
  3461.                                                     'taxonomy' => $this->get_taxonomy(),
  3462.                                                     'field'    => 'slug',
  3463.                                                     'terms'    => $terms,
  3464.                                                 )
  3465.                                             ),
  3466.                         'orderby'       => array(
  3467.                                             'parent'    => 'ASC',
  3468.                                             'title'     => 'ASC'
  3469.                                         ),
  3470.                         'post_parent'   => 0,
  3471.                         'posts_per_page' => -1
  3472.                     );
  3473.  
  3474.  
  3475.             $results = $this->query_modal_templates( $args, 0, 0, $max_level );
  3476.  
  3477.  
  3478.             foreach( $results as $key => $result )
  3479.             {
  3480.                 $subtypes[ $result['title'] ] = $result['id'];
  3481.                 $tooltips[ $result['id'] ] = $result['desc'];
  3482.             }
  3483.  
  3484.             return array(
  3485.                         'subtypes'  => $subtypes,
  3486.                         'tooltips'  => $tooltips
  3487.                     );
  3488.         }
  3489.  
  3490.         /**
  3491.          * Recursive function to indent list elements
  3492.          *
  3493.          * @since 4.8
  3494.          * @param array $args
  3495.          * @param int $parent
  3496.          * @param int $level
  3497.          * @param int $max_level            -1 for unlimited
  3498.          * @return array
  3499.          */
  3500.         protected function query_modal_templates( array $args, $parent, $level, $max_level = 0 )
  3501.         {
  3502.             $list = array();
  3503.  
  3504.             $args['post_parent'] = $parent;
  3505.  
  3506.             $q = new WP_Query( $args );
  3507.  
  3508.             $elements = $q->posts;
  3509.  
  3510.             if( empty( $elements ) )
  3511.             {
  3512.                 return array();
  3513.             }
  3514.  
  3515.             foreach( $elements as $key => $element )
  3516.             {
  3517.                 $indent = ( $level == 0 ) ? '' : str_repeat( '--', $level ) . '> ';
  3518.  
  3519.                 $same_id = false;;
  3520.                 //  check for current edited post id
  3521.                 if( isset( $_REQUEST['edit_element'] ) && $_REQUEST['edit_element'] == 'true' )
  3522.                 {
  3523.                     $same_id = isset( $_REQUEST['element_id'] ) && $_REQUEST['element_id'] == $element->ID;
  3524.                 }
  3525.                 else if( isset( $_REQUEST['post_id'] ) && $_REQUEST['post_id'] == $element->ID )
  3526.                 {
  3527.                     $same_id = true;
  3528.                 }
  3529.  
  3530.                 if( $same_id )
  3531.                 {
  3532.                     $entry = array(
  3533.                                 'title' => '( ' . $indent . $element->post_title . ' )',
  3534.                                 'id'    => - $element->ID,
  3535.                                 'desc'  => $element->post_excerpt
  3536.                             );
  3537.                 }
  3538.                 else
  3539.                 {
  3540.                     $entry = array(
  3541.                                 'title' => $indent . $element->post_title,
  3542.                                 'id'    => $element->ID,
  3543.                                 'desc'  => $element->post_excerpt
  3544.                             );
  3545.                 }
  3546.  
  3547.                 switch( $element->post_status )
  3548.                 {
  3549.                     case 'publish':
  3550.                         break;
  3551.                     case 'pending':
  3552.                         $entry['title'] .= ' - *** ' . __( 'pending review', 'avia_framework' );
  3553.                         break;
  3554.                     case 'draft':
  3555.                         $entry['title'] .= ' - *** ' . __( 'draft', 'avia_framework' );
  3556.                         break;
  3557.                     case 'trash':
  3558.                         $entry['title'] .= ' - *** ' . __( 'trashed', 'avia_framework' );
  3559.                         break;
  3560.                     default:
  3561.                         $entry['title'] .= ' - *** ' . $element->post_status;
  3562.                 }
  3563.  
  3564.                 $list[] = $entry;
  3565.  
  3566.                 if( $max_level < 0 || $level < $max_level )
  3567.                 {
  3568.                     $children = $this->query_modal_templates( $args, $element->ID, $level + 1 );
  3569.  
  3570.                     if( ! empty( $children ) )
  3571.                     {
  3572.                         $list = array_merge( $list, $children );
  3573.                     }
  3574.                 }
  3575.             }
  3576.  
  3577.             return $list;
  3578.         }
  3579.  
  3580.         /**
  3581.          * Scan shortcode for a requested template and loads all templates into local object cache.
  3582.          * Returns the ID of the top template. Called from popup editor - needs to handle only one shortcode (basic or item)
  3583.          *
  3584.          * @since 4.8
  3585.          * @param string $shortcode
  3586.          * @return array
  3587.          */
  3588.         public function load_shortcode_templates( $shortcode )
  3589.         {
  3590.             $info = array(
  3591.                             'template_id'   => 0,
  3592.                             'shortcode'     => '',
  3593.                             'queue'         => null
  3594.                         );
  3595.  
  3596.             if( ! $this->element_templates_enabled() )
  3597.             {
  3598.                 return $info;
  3599.             }
  3600.  
  3601.             $sc_array = $this->get_element_template_info_from_content( $shortcode );
  3602.  
  3603.             if( 0 == $sc_array[0]['template_id'] )
  3604.             {
  3605.                 return $info;
  3606.             }
  3607.  
  3608.             $this->load_template_cache( $sc_array[0]['template_id'] );
  3609.  
  3610.             /**
  3611.              * Check that template has the correct element base
  3612.              */
  3613.             if( $sc_array[0]['template_id'] != 0 )
  3614.             {
  3615.                 if( $this->get_template( $sc_array[0]['template_id'], $sc_array[0]['template_sc'] ) !== false )
  3616.                 {
  3617.                     $info['template_id'] = $sc_array[0]['template_id'];
  3618.                     $info['shortcode'] = $sc_array[0]['template_sc'];
  3619.                     $info['queue'] = $this->create_templates_queue( $info );
  3620.                     return $info;
  3621.                 }
  3622.             }
  3623.  
  3624.             return $info;
  3625.         }
  3626.  
  3627.         /**
  3628.          * Returns a valid hierarchical queue of templates ( parent -> children )
  3629.          * Can be used to iterate over the templates to find first locked element.
  3630.          * Loads templates into cache if necessary.
  3631.          *
  3632.          * @since 4.8
  3633.          * @param array $templates_info
  3634.          * @return array
  3635.          */
  3636.         public function create_templates_queue( array $templates_info )
  3637.         {
  3638.             $queue = array();
  3639.  
  3640.             if( ! $this->element_templates_enabled() )
  3641.             {
  3642.                 return $queue;
  3643.             }
  3644.  
  3645.             while( true )
  3646.             {
  3647.                 //  build queue with untranslated element templates
  3648.                 $cache = $this->get_template( $templates_info['template_id'], $templates_info['shortcode'], false );
  3649.                 if( false === $cache )
  3650.                 {
  3651.                     break;
  3652.                 }
  3653.  
  3654.                 $queue[] = $templates_info['template_id'];
  3655.                 $templates_info['template_id'] = $cache['sc_array'][0]['template_id'];
  3656.             }
  3657.  
  3658.             return array_reverse( $queue );
  3659.         }
  3660.  
  3661.         /**
  3662.          * Returns the template if the template has the correct shortcode
  3663.          *
  3664.          * @since 4.8
  3665.          * @param int $template_id
  3666.          * @param string $shortcode
  3667.          * @param boolean $translate        set to false to check original id
  3668.          * @return false|array
  3669.          */
  3670.         protected function get_template( $template_id, $shortcode, $translate = true )
  3671.         {
  3672.             /**
  3673.              * @used_by             avia_WPML                   10
  3674.              * @since 4.8
  3675.              * @param string|int $template_id
  3676.              * @return string|int
  3677.              */
  3678.             $get_id = true === $translate ? apply_filters( 'avf_custom_element_template_id', $template_id ): $template_id;
  3679.  
  3680.             if( 0 == $get_id || ! array_key_exists( $get_id, $this->template_cache ) || false === $this->template_cache[ $get_id ] )
  3681.             {
  3682.                 //  if translated and translation does not exist try to return requested element as fallback
  3683.                 if( true === $translate && $get_id != $template_id )
  3684.                 {
  3685.                     return $this->get_template( $template_id, $shortcode, false );
  3686.                 }
  3687.  
  3688.                 return false;
  3689.             }
  3690.  
  3691.             $cache = $this->template_cache[ $get_id ];
  3692.  
  3693.             return $cache['sc_array'][0]['template_sc'] == $shortcode ? $cache : false;
  3694.         }
  3695.  
  3696.         /**
  3697.          * Returns the name of the custom element template (= post_title)
  3698.          *
  3699.          * @since 4.8
  3700.          * @param int $element_id
  3701.          * @param string|false
  3702.          */
  3703.         public function get_element_template_name( $template_id )
  3704.         {
  3705.             $this->load_template_cache( $template_id );
  3706.  
  3707.             if( array_key_exists( $template_id, $this->template_cache ) && false !== $this->template_cache[ $template_id ] )
  3708.             {
  3709.                 if( $this->template_cache[ $template_id ]['post'] instanceof WP_Post )
  3710.                 {
  3711.                     return $this->template_cache[ $template_id ]['post']->post_title;
  3712.                 }
  3713.             }
  3714.  
  3715.             return false;
  3716.         }
  3717.  
  3718.         /**
  3719.          * Check in template hierarchie for locked parameter settings.
  3720.          *  - $element['locked'] is empty  => value for $param_id is returned
  3721.          *  - $element['locked'] 1 value   => string is returned
  3722.          *  - $element['locked'] more      => json encoded string with array of values for each id
  3723.          *
  3724.          * Make sure to call this function only when the first template has the same base element.
  3725.          *
  3726.          * @since 4.8
  3727.          * @param array $templates_info
  3728.          * @param string $param_id
  3729.          * @param array $element            by ref for performance reasons only
  3730.          * @param string $return_content    'raw' | 'array' what to return for content
  3731.          * @return mixed|null               null, if no locked value exists, string or json encoded string
  3732.          */
  3733.         public function locked_value( array $templates_info, $param_id, array &$element = array(), $return_content = 'raw' )
  3734.         {
  3735.             if( ! $this->element_templates_enabled() )
  3736.             {
  3737.                 return null;
  3738.             }
  3739.  
  3740.             if( empty( $templates_info['queue'] ) )
  3741.             {
  3742.                 return null;
  3743.             }
  3744.  
  3745.             if( false === $this->get_template( $templates_info['template_id'], $templates_info['shortcode'] ) )
  3746.             {
  3747.                 return null;
  3748.             }
  3749.  
  3750.             $locked_ids = isset( $element['locked'] ) ? $element['locked'] : array();
  3751.  
  3752.             $is_lock_checkbox = false === strpos( $param_id, '__locked' ) ? false : true;
  3753.  
  3754.             // iterate queue till first lock checkbox set
  3755.             foreach( $templates_info['queue'] as $id )
  3756.             {
  3757.                 $templates_info['template_id'] = $id;
  3758.  
  3759.                 if( $is_lock_checkbox )
  3760.                 {
  3761.                     $param_ids = $param_id;
  3762.                 }
  3763.                 else
  3764.                 {
  3765.                     $param_ids = empty( $locked_ids ) ? $param_id : $locked_ids;
  3766.                 }
  3767.  
  3768.                 $val = $this->find_locked_value( $templates_info, $param_ids, $is_lock_checkbox, $return_content );
  3769.                 if( ! is_null( $val ) )
  3770.                 {
  3771.                     return $val;
  3772.                 }
  3773.             }
  3774.  
  3775.             return null;
  3776.         }
  3777.  
  3778.         /**
  3779.          * Check in template hierarchie for the last set value for the option (if element allows a default to copy).
  3780.          * If parameter is locked, then the locked value is returned.
  3781.          *
  3782.          * Make sure to call this function only when the first template has the same base element.
  3783.          *
  3784.          * @since 4.8
  3785.          * @param array $templates_info
  3786.          * @param string $param_id
  3787.          * @param array $element            by ref for performance reasons only
  3788.          * @param array $locked
  3789.          * @return mixed
  3790.          */
  3791.         public function default_value( array $templates_info, $param_id, array &$element = array(), $locked = array() )
  3792.         {
  3793.             $value = isset( $element['std'] ) ? $element['std'] : '';
  3794.  
  3795.             if( ! $this->element_templates_enabled() )
  3796.             {
  3797.                 return $value;
  3798.             }
  3799.  
  3800.             if( isset( $element['tmpl_set_default'] ) && false === $element['tmpl_set_default'] )
  3801.             {
  3802.                 return $value;
  3803.             }
  3804.  
  3805.             //  currently we do not allow to override default for this element
  3806.             if( isset( $element['modal_group'] ) && false === $element['modal_group'] )
  3807.             {
  3808.                 return $value;
  3809.             }
  3810.  
  3811.             if( empty( $templates_info['queue'] ) )
  3812.             {
  3813.                 return $value;
  3814.             }
  3815.  
  3816.             if( false === $this->get_template( $templates_info['template_id'], $templates_info['shortcode'] ) )
  3817.             {
  3818.                 return $value;
  3819.             }
  3820.  
  3821.             //  lock checkbox settings are not used as default
  3822.             if( false !== strpos( $param_id, '__locked' ) )
  3823.             {
  3824.                 return $value;
  3825.             }
  3826.  
  3827.             if( array_key_exists( $param_id, $locked ) )
  3828.             {
  3829.                 return $locked[ $param_id ];
  3830.             }
  3831.  
  3832.  
  3833.             $cache = $this->get_template( end( $templates_info['queue'] ), $templates_info['shortcode'] );
  3834.  
  3835.             if( false === $cache )
  3836.             {
  3837.                 return $value;
  3838.             }
  3839.  
  3840.             $attr = $cache['sc_array'][0]['template_attr'];
  3841.  
  3842.             return isset( $attr[ $param_id ] ) ? $attr[ $param_id ] : '';
  3843.         }
  3844.  
  3845.         /**
  3846.          * Adds the template class to the custom class. Checks first in $atts, then in $default
  3847.          *
  3848.          * Make sure to call this function after loading the locked values !!!
  3849.          *
  3850.          * @since 4.8
  3851.          * @param array $atts
  3852.          * @param array $meta
  3853.          * @param array $default
  3854.          */
  3855.         public function add_template_class( array &$meta, array &$atts, array &$default )
  3856.         {
  3857.             if( ! isset( $meta['el_class'] ) )
  3858.             {
  3859.                 $meta['el_class'] = '';
  3860.             }
  3861.  
  3862.             if( isset( $atts['template_class'] ) && ! empty( $atts['template_class'] ) )
  3863.             {
  3864.                 $meta['el_class'] .= ' ' . $atts['template_class'];
  3865.                 return;
  3866.             }
  3867.  
  3868.             if( isset( $default['template_class'] ) && ! empty( $default['template_class'] ) )
  3869.             {
  3870.                 $meta['el_class'] .= ' ' . $default['template_class'];
  3871.                 return;
  3872.             }
  3873.  
  3874.             return;
  3875.         }
  3876.  
  3877.         /**
  3878.          * Override shortcode attribute values with locked values from templates.
  3879.          * Missing attributes are filled from $default.
  3880.          * The list of locked attributes is returned in $locked;
  3881.          *
  3882.          * @since 4.8
  3883.          * @param array $attr
  3884.          * @param aviaShortcodeTemplate $sc_templ
  3885.          * @param string $shortcode
  3886.          * @param array $default
  3887.          * @param array $locked                 element ids that have been locked
  3888.          * @param string|false $content
  3889.          */
  3890.         public function set_locked_attributes( array &$attr, aviaShortcodeTemplate $sc_templ, $shortcode, array &$default = array(), array &$locked = array(), &$content = null )
  3891.         {
  3892.             if( ! $this->element_templates_enabled() )
  3893.             {
  3894.                 return;
  3895.             }
  3896.  
  3897.             if( ! isset( $attr['element_template'] ) || empty( $attr['element_template'] ) )
  3898.             {
  3899.                 return;
  3900.             }
  3901.  
  3902.             $this->load_template_cache( $attr['element_template'] );
  3903.  
  3904.             $templates_info = array(
  3905.                                         'template_id'   => $attr['element_template'],
  3906.                                         'shortcode'     => $shortcode
  3907.                                     );
  3908.  
  3909.             $templates_info['queue'] = $this->create_templates_queue( $templates_info );
  3910.  
  3911.             $sc_el = &$sc_templ->elements;
  3912.  
  3913.             if( isset( $sc_templ->config['shortcode_nested'] ) && is_array( $sc_templ->config['shortcode_nested'] ) && in_array( $shortcode, $sc_templ->config['shortcode_nested'] ) )
  3914.             {
  3915.                 /**
  3916.                  * We have a modal_group element -> search for subelements
  3917.                  */
  3918.                 foreach( $sc_templ->elements as &$element )
  3919.                 {
  3920.                     if( $element['type'] != 'modal_group' )
  3921.                     {
  3922.                         continue;
  3923.                     }
  3924.  
  3925.                     if( isset( $element['subelements'] ) && is_array( $element['subelements'] ) )
  3926.                     {
  3927.                         $sc_el = &$element['subelements'];
  3928.                         break;
  3929.                     }
  3930.                 }
  3931.  
  3932.                 unset( $element );
  3933.             }
  3934.  
  3935.             foreach( $sc_el as &$element )
  3936.             {
  3937.                 if( ! isset( $element['id'] ) )
  3938.                 {
  3939.                     continue;
  3940.                 }
  3941.  
  3942.                 if( ! array_key_exists( $element['id'], $attr ) )
  3943.                 {
  3944.                     if( ! array_key_exists( $element['id'], $default ) )
  3945.                     {
  3946.                         $default[ $element['id'] ] = isset( $element['std'] ) ? $element['std'] : '';
  3947.                     }
  3948.  
  3949.                     $attr[ $element['id'] ] = $default[ $element['id'] ];
  3950.                 }
  3951.  
  3952.                 $val = $this->locked_value( $templates_info, $element['id'], $element, 'raw' );
  3953.                 if( ! is_null( $val ) )
  3954.                 {
  3955.                     if( ! isset( $element['locked'] ) || empty( $element['locked'] ) )
  3956.                     {
  3957.                         $attr[ $element['id'] ] = $val;
  3958.                         $locked[ $element['id'] ] = $val;
  3959.  
  3960.                         if( 'content' == $element['id'] && ! is_null( $content ) )
  3961.                         {
  3962.                             $content = $val;
  3963.                         }
  3964.                     }
  3965.                     else
  3966.                     {
  3967.                         $el_ids = is_array( $element['locked'] ) ? $element['locked'] : array( $element['locked'] );
  3968.                         $val_array = count( $el_ids ) > 1 ? json_decode( $val ) : $val;
  3969.  
  3970.                         //  $element['locked'] contains only 1 value -> string returned
  3971.                         if( ! is_array( $val_array ) )
  3972.                         {
  3973.                             $val_array = array( $val_array );
  3974.                         }
  3975.  
  3976.                         foreach( $el_ids as $key => $el_id )
  3977.                         {
  3978.                             if( isset( $val_array[ $key ] ) )
  3979.                             {
  3980.                                 $v = isset( $val_array[ $key ] ) ? $val_array[ $key ] : '';
  3981.                                 $attr[ $el_id ] = $v;
  3982.                                 $locked[ $el_id ] = $v;
  3983.                             }
  3984.                         }
  3985.                     }
  3986.                 }
  3987.             }
  3988.  
  3989.             unset( $element );
  3990.  
  3991.             // remove temporary added content again
  3992.             unset( $default['content'] );
  3993.             unset( $attr['content'] );
  3994.  
  3995.         }
  3996.  
  3997.         /**
  3998.          * Checks in a template for locked value and returns it (checkbox "locked" selected).
  3999.          * The lock checkbox value is also returned.
  4000.          *
  4001.          *
  4002.          * @since 4.8
  4003.          * @param array $templates_info
  4004.          * @param string|array $param_ids
  4005.          * @param boolean $is_lock_checkbox
  4006.          * @param string $return_content            'raw' | 'array' what to return for content
  4007.          * @return string|null                      null, if no locked value exists | string | json encoded array
  4008.          */
  4009.         protected function find_locked_value( array $templates_info, $param_ids, $is_lock_checkbox = false, $return_content = 'raw' )
  4010.         {
  4011.             $cache = $this->get_template( $templates_info['template_id'], $templates_info['shortcode'] );
  4012.  
  4013.             if( false === $cache )
  4014.             {
  4015.                 return null;
  4016.             }
  4017.  
  4018.             if( empty( $param_ids ) )
  4019.             {
  4020.                 return null;
  4021.             }
  4022.  
  4023.             if( ! is_array( $param_ids ) )
  4024.             {
  4025.                 $param_ids = array( $param_ids );
  4026.             }
  4027.  
  4028.             $attr = $cache['sc_array'][0]['template_attr'];
  4029.  
  4030.             if( $templates_info['shortcode'] == $cache['sc_array'][0]['shortcode'] )
  4031.             {
  4032.                 $raw_content = isset( $cache['sc_array'][0]['raw_content'] ) ? $cache['sc_array'][0]['raw_content'] : '';
  4033.             }
  4034.             else
  4035.             {
  4036.                 $raw_content = isset( $cache['sc_array'][0]['content'][0]['raw_content'] ) ? $cache['sc_array'][0]['content'][0]['raw_content'] : '';
  4037.             }
  4038.  
  4039.             if( $is_lock_checkbox )
  4040.             {
  4041.                 //  only first id is checked
  4042.                 if( array_key_exists( $param_ids[0], $attr ) && $attr[ $param_ids[0] ] != '' )
  4043.                 {
  4044.                     return $attr[ $param_ids[0] ];
  4045.                 }
  4046.             }
  4047.             else
  4048.             {
  4049.                 //  first index must be lock_checkbox id
  4050.                 $lock_param_id = $param_ids[0] . '__locked';
  4051.  
  4052.                 if( array_key_exists( $lock_param_id, $attr ) && $attr[ $lock_param_id ] != '' )
  4053.                 {
  4054.                     $values = array();
  4055.  
  4056.                     //  Options should exist in template as you have to check a lock checkbox and must save the popup - '' is only fallback
  4057.                     foreach( $param_ids as $param_id )
  4058.                     {
  4059.                         if( 'content' == $param_id )
  4060.                         {
  4061.                             if( 'raw' == $return_content )
  4062.                             {
  4063.                                 $values[] = $raw_content;
  4064.                             }
  4065.                             else
  4066.                             {
  4067.                                 if( ! array_key_exists( $param_id, $attr ) )
  4068.                                 {
  4069.                                     $values[] = array();
  4070.                                 }
  4071.                                 else if( is_array( $attr[ $param_id ] ) )
  4072.                                 {
  4073.                                     $values[] = $attr[ $param_id ];
  4074.                                 }
  4075.                                 else
  4076.                                 {
  4077.                                     $values[] = array( $attr[ $param_id ] );
  4078.                                 }
  4079.                             }
  4080.                         }
  4081.                         else
  4082.                         {
  4083.                             $values[] = array_key_exists( $param_id, $attr ) ? $attr[ $param_id ] : '';
  4084.                         }
  4085.                     }
  4086.  
  4087.                     return count( $values ) > 1 ? json_encode( $values ) : $values[0];
  4088.                 }
  4089.             }
  4090.  
  4091.             return null;
  4092.         }
  4093.  
  4094.         /**
  4095.          * Return the shortcode array, in [0] needed element template id is set for quick reference
  4096.          * and the attributes from shortcode
  4097.          * Also extracts info in case first modal group should be used as template for all other group elements
  4098.          *
  4099.          * @since 4.8
  4100.          * @param string $shortcode         shortcode string
  4101.          * @return array
  4102.          */
  4103.         public function get_element_template_info_from_content( $shortcode )
  4104.         {
  4105.             $template_id = 0;
  4106.             $template_id_first = 0;
  4107.             $sc = '';
  4108.             $sc_first = '';
  4109.             $is_item = false;
  4110.             $is_first_item = false;
  4111.  
  4112.             $sc_array = ShortcodeHelper::shortcode2array( wp_unslash( $shortcode ) );
  4113.  
  4114.             //  fix if shortcode has no attributes (returns string)
  4115.             if( ! is_array( $sc_array[0]['attr'] ) )
  4116.             {
  4117.                 $sc_array[0]['attr'] = array();
  4118.             }
  4119.  
  4120.  
  4121.             if( isset( $sc_array[0]['attr'] )  && is_array( $sc_array[0]['attr'] ) )
  4122.             {
  4123.                 if( array_key_exists( 'element_template', $sc_array[0]['attr'] ) && is_numeric( $sc_array[0]['attr']['element_template'] ) )
  4124.                 {
  4125.                     $template_id = (int) $sc_array[0]['attr']['element_template'];
  4126.                 }
  4127.  
  4128.                 $sc = $sc_array[0]['shortcode'];
  4129.  
  4130.                 //  check for item template
  4131.                 if( array_key_exists( 'select_element_template', $sc_array[0]['attr'] ) && ( 'item' == $sc_array[0]['attr']['select_element_template'] ) )
  4132.                 {
  4133.                     $template_id = 0;
  4134.  
  4135.                     if( isset( $sc_array[0]['content'][0]['attr']['element_template'] ) && is_numeric( $sc_array[0]['content'][0]['attr']['element_template'] ) )
  4136.                     {
  4137.                         $template_id = (int) $sc_array[0]['content'][0]['attr']['element_template'];
  4138.                     }
  4139.  
  4140.                     $sc = $sc_array[0]['content'][0]['shortcode'];
  4141.                     $is_item = true;
  4142.                 }
  4143.  
  4144.                 //  check for first template base for all
  4145.                 if( array_key_exists( 'select_element_template', $sc_array[0]['attr'] ) && ( 'first' == $sc_array[0]['attr']['select_element_template'] ) )
  4146.                 {
  4147.                     $template_id_first = 0;
  4148.  
  4149.                     if( isset( $sc_array[0]['content'][0]['attr']['element_template'] ) && is_numeric( $sc_array[0]['content'][0]['attr']['element_template'] ) )
  4150.                     {
  4151.                         $template_id_first = (int) $sc_array[0]['content'][0]['attr']['element_template'];
  4152.                     }
  4153.  
  4154.                     $sc_first = $sc_array[0]['content'][0]['shortcode'];
  4155.                     $is_first_item = true;
  4156.                 }
  4157.             }
  4158.  
  4159.             $sc_array[0]['template_id'] = $template_id;
  4160.             $sc_array[0]['template_sc'] = $sc;
  4161.  
  4162.             $sc_array[0]['template_id_first'] = $is_first_item ? $template_id_first : null;
  4163.             $sc_array[0]['template_sc_first'] = $is_first_item ? $sc_first : null;
  4164.  
  4165.             if( ! $is_item )
  4166.             {
  4167.                 $sc_array[0]['template_attr'] = $sc_array[0]['attr'];
  4168.                 $sc_array[0]['template_attr']['content'] = $sc_array[0]['content'];
  4169.             }
  4170.             else
  4171.             {
  4172.                 $sc_array[0]['template_attr'] = $sc_array[0]['content'][0]['attr'];
  4173.                 $sc_array[0]['template_attr']['content'] = $sc_array[0]['content'][0]['content'];
  4174.             }
  4175.  
  4176.             if( $is_first_item )
  4177.             {
  4178.                 $sc_array[0]['template_attr_first'] = $sc_array[0]['content'][0]['attr'];
  4179.                 $sc_array[0]['template_attr_first']['content'] = $sc_array[0]['content'][0]['content'];
  4180.             }
  4181.  
  4182.             return $sc_array;
  4183.         }
  4184.  
  4185.         /**
  4186.          * Recursive function - load ALB Element Templates in cache and make sure that all referenced
  4187.          * parent templates are loaded as well. There is no check, if the parent template has the same
  4188.          * base element (could have been changed).
  4189.          *
  4190.          * It is recommended to call this function whenever you try to access templates.
  4191.          *
  4192.          * @since 4.8
  4193.          * @param int $template_id
  4194.          * @param boolean $force_original           force to load original untranslated element template when using translation plugins like WPML
  4195.          */
  4196.         protected function load_template_cache( $template_id, $force_original = false )
  4197.         {
  4198.             if( 0 == $template_id || empty( $template_id ) || ! is_numeric( $template_id ) )
  4199.             {
  4200.                 return;
  4201.             }
  4202.  
  4203.             if( array_key_exists( $template_id, $this->template_cache ) )
  4204.             {
  4205.                 //  $template_id points to a deleted or non alb_elements post ( = error condition !! )
  4206.                 if( false === $this->template_cache[ $template_id ] )
  4207.                 {
  4208.                     return;
  4209.                 }
  4210.  
  4211.                 //  break a possible infinite loop (error situation)
  4212.                 if( $this->template_cache[ $template_id ]['post']->post_parent == $template_id )
  4213.                 {
  4214.                     if( defined( 'WP_DEBUG' ) && WP_DEBUG )
  4215.                     {
  4216.                         error_log( 'error: infinite loop stopped in aviaElementTemplates::load_template_cache post ID:' . $template_id );
  4217.                     }
  4218.  
  4219.                     $this->template_cache[ $template_id ]['post']->post_parent = 0;
  4220.                     return;
  4221.                 }
  4222.  
  4223.                 $this->load_template_cache( $this->template_cache[ $template_id ]['post']->post_parent );
  4224.                 return;
  4225.             }
  4226.  
  4227.             $post = $this->get_post( $template_id, $force_original );
  4228.  
  4229.             if( ! $post instanceof WP_Post )
  4230.             {
  4231.                 $this->template_cache[ $template_id ] = false;
  4232.                 return;
  4233.             }
  4234.  
  4235.             if( $post->post_type != $this->get_post_type() || $post->post_status != 'publish' )
  4236.             {
  4237.                 $this->template_cache[ $post->ID ] = false;
  4238.                 return;
  4239.             }
  4240.  
  4241.             $this->template_cache[ $post->ID ]['post'] = $post;
  4242.             $this->template_cache[ $post->ID ]['sc_array'] = $this->get_element_template_info_from_content( Avia_Builder()->get_posts_alb_content( $post->ID ) );
  4243.  
  4244.             $this->load_template_cache( $this->template_cache[ $post->ID ]['post']->post_parent );
  4245.  
  4246.             //  WPML returns translated CET - but we might need original also
  4247.             if( $post->ID != $template_id )
  4248.             {
  4249.                 $this->load_template_cache( $template_id, true );
  4250.             }
  4251.         }
  4252.  
  4253.         /**
  4254.          * Limit allowed characters for element title and length
  4255.          *
  4256.          * @since 4.8
  4257.          * @param string $title
  4258.          * @param int $length
  4259.          * @return string
  4260.          */
  4261.         protected function sanitize_element_title( $title = '', $length = 30 )
  4262.         {
  4263.             $title_new = AviaHelper::save_string( trim( $title ), ' ', '', 'element_title' );
  4264.  
  4265.             if( strlen( $title_new ) > $length )
  4266.             {
  4267.                 $title_new = substr( $title_new, 0, $length );
  4268.             }
  4269.  
  4270.             return $title_new;
  4271.         }
  4272.  
  4273.         /**
  4274.          * Get ALB Layout Builder Metabox title
  4275.          *
  4276.          * @since 4.8
  4277.          * @return string
  4278.          */
  4279.         public function alb_metabox_title()
  4280.         {
  4281.             if( strpos( $_SERVER['REQUEST_URI'], 'post-new.php' ) !== false )
  4282.             {
  4283.                 return ' - ' .  __( 'Create New Custom Element Template', 'avia_framework' );
  4284.             }
  4285.  
  4286.             if( ! isset( $_REQUEST['post'] ) )
  4287.             {
  4288.                 return '';
  4289.             }
  4290.  
  4291.             $post = $this->get_post( $_REQUEST['post'] );
  4292.  
  4293.             if( ! $post instanceof WP_Post || $post->post_type != $this->get_post_type() )
  4294.             {
  4295.                 return '';
  4296.             }
  4297.  
  4298.             $terms = get_the_terms( $post->ID, $this->get_taxonomy() );
  4299.             if( ! is_array( $terms ) )
  4300.             {
  4301.                 return '';
  4302.             }
  4303.  
  4304.             $title = array();
  4305.  
  4306.             //  Should only return 1 element
  4307.             foreach( $terms as $term )
  4308.             {
  4309.                 $title[] = $term->name;
  4310.             }
  4311.  
  4312.             $prefix = __( 'Edit Custom Element Template:', 'avia_framework' );
  4313.  
  4314.             /**
  4315.              * @used_by                 avia_WPML                   10
  4316.              * @since 4.8
  4317.              * @param string $prefix
  4318.              * @return string
  4319.              */
  4320.             $prefix = apply_filters( 'avf_alb_metabox_title_prefix_cet', $prefix );
  4321.  
  4322.             $title = $prefix . ' ' . implode( ', ', $title );
  4323.  
  4324.             $sc_array = $this->get_element_template_info_from_content( Avia_Builder()->get_posts_alb_content( $post->ID ) );
  4325.  
  4326.             if( array_key_exists( 'select_element_template', $sc_array[0]['attr'] ) && ( 'item' == $sc_array[0]['attr']['select_element_template'] ) )
  4327.             {
  4328.                 $shortcode = $sc_array[0]['shortcode'];
  4329.  
  4330.                 $sc = Avia_Builder()->shortcode_class[ Avia_Builder()->shortcode[ $shortcode ] ];
  4331.  
  4332.                 $title .= ' (' . $sc->config['name'] . ')';
  4333.             }
  4334.  
  4335.             return ' - ' . $title;
  4336.         }
  4337.  
  4338.         /**
  4339.          * Wrapper function for default WP function get_post that is not hooked by e.g. WPML
  4340.          *
  4341.          * @since 4.8
  4342.          * @param int $post_id
  4343.          * @param boolean $force_original           force to load requested ID and not a translated
  4344.          * @return WP_Post|false
  4345.          */
  4346.         protected function get_post( $post_id, $force_original = false )
  4347.         {
  4348.             $args = array(
  4349.                         'numberposts'       => 1,
  4350.                         'include'           => array( $post_id ),
  4351.                         'post_type'         => $this->get_post_type(),
  4352.                         'suppress_filters'  => false
  4353.                     );
  4354.  
  4355.             /**
  4356.              * Allows e.g. WPML to reroute to translated object
  4357.              */
  4358.             if( false === $force_original )
  4359.             {
  4360.                 $posts = get_posts( $args );
  4361.                 $post = is_array( $posts ) && count( $posts ) > 0 ? $posts[0] : false;
  4362.             }
  4363.             else
  4364.             {
  4365.                 $post = get_post( $post_id );
  4366.             }
  4367.  
  4368.             return $post instanceof WP_Post ? $post : false;
  4369.         }
  4370.  
  4371.         /**
  4372.          * Creates a custom element template shortcode based on the attr arrays.
  4373.          * Settings can e.g. come from an existing ALB element edited in canvas.
  4374.          * Locked values override the default settings.
  4375.          * Element templates are reset to "Base Element" when $skip_locking === false (= we create a new independent custom element)
  4376.          *
  4377.          * @since 4.8
  4378.          * @param aviaShortcodeTemplate $shortcode
  4379.          * @param array $attr
  4380.          * @param array $outer_attr
  4381.          * @param boolean $is_item
  4382.          * @param boolean $skip_locking         in case of update locking overrides updated values !!!
  4383.          * @return string
  4384.          */
  4385.         protected function create_custom_element_shortcode( aviaShortcodeTemplate $shortcode, array $attr, array $outer_attr, $is_item, $skip_locking = false )
  4386.         {
  4387.             $item_self_closing = $shortcode->has_modal_group_template() && $shortcode->is_nested_self_closing( $shortcode->config['shortcode_nested'][0] );
  4388.  
  4389.             if( ! $is_item )
  4390.             {
  4391.                 $content = '';
  4392.  
  4393.                 if( isset( $attr['content'] ) )
  4394.                 {
  4395.                     if( ! is_array( $attr['content'] ) )
  4396.                     {
  4397.                         $content = $attr['content'];
  4398.                     }
  4399.                     else
  4400.                     {
  4401.                         $entries = array();
  4402.  
  4403.                         //  content can be array of shortcode strings or array of attributes array
  4404.                         foreach( $attr['content'] as $entry )
  4405.                         {
  4406.                             if( ! is_array( $entry ) )
  4407.                             {
  4408.                                 $entries[] = $entry;
  4409.                                 continue;
  4410.                             }
  4411.  
  4412.                             //  create shortcode strings so we can get locked content
  4413.                             $item_content = '';
  4414.  
  4415.                             if( isset( $entry['content'] ) )
  4416.                             {
  4417.                                 $item_content = $entry['content'];
  4418.                                 unset( $entry['content'] );
  4419.                             }
  4420.  
  4421.                             if( $item_self_closing )
  4422.                             {
  4423.                                 $item_content = null;
  4424.                             }
  4425.  
  4426.                             $entries[] = ShortcodeHelper::create_shortcode_by_array( $shortcode->config['shortcode_nested'][0], $item_content, $entry );
  4427.                         }
  4428.  
  4429.                         $content = implode( "\n", $entries );
  4430.                     }
  4431.                 }
  4432.  
  4433.                 if( ! $skip_locking )
  4434.                 {
  4435.                     $default = $shortcode->get_default_sc_args();
  4436.                     $locked = array();
  4437.                     $this->set_locked_attributes( $attr, $shortcode, $shortcode->config['shortcode'], $default, $locked, $content );
  4438.  
  4439.                     //  reset to base template
  4440.                     $attr['element_template'] = '';
  4441.  
  4442.                     if( $this->is_editable_modal_group_element( $shortcode ) )
  4443.                     {
  4444.                         $items = ShortcodeHelper::shortcode2array( $content, 1 );
  4445.                         $item_def = $shortcode->get_default_modal_group_args();
  4446.                         $item_attr = array();
  4447.  
  4448.                         foreach( $items as &$item )
  4449.                         {
  4450.                             Avia_Element_Templates()->set_locked_attributes( $item['attr'], $shortcode, $shortcode->config['shortcode_nested'][0], $item_def, $locked, $item['content'] );
  4451.  
  4452.                             $item['attr']['content'] = $item['content'];
  4453.  
  4454.                             //  reset to base template
  4455.                             $item['attr']['element_template'] = '';
  4456.  
  4457.                             $item_attr[] = $item['attr'];
  4458.                         }
  4459.  
  4460.                         unset( $item );
  4461.  
  4462.                         $attr['content'] = $item_attr;
  4463.                         $attr['select_element_template'] = '';
  4464.                     }
  4465.                     else
  4466.                     {
  4467.                         $attr['content'] = $content;
  4468.                     }
  4469.                 }
  4470.                 else
  4471.                 {
  4472.                     $attr['content'] = $content;
  4473.                 }
  4474.             }
  4475.             else
  4476.             {
  4477.                 if( ! $skip_locking )
  4478.                 {
  4479.                     $content = isset( $attr['content'] ) ? $attr['content'] : '';
  4480.                     $default = $shortcode->get_default_modal_group_args();
  4481.                     $locked = array();
  4482.  
  4483.                     Avia_Element_Templates()->set_locked_attributes( $attr, $shortcode, $shortcode->config['shortcode_nested'][0], $default, $locked, $content );
  4484.  
  4485.                     $attr['content'] = $content;
  4486.  
  4487.                     //  reset to base template
  4488.                     $attr['element_template'] = '';
  4489.                 }
  4490.  
  4491.                 $outer_attr['content'] = array( $attr );
  4492.                 $outer_attr['select_element_template'] = 'item';
  4493.  
  4494.                 $attr = $outer_attr;
  4495.             }
  4496.  
  4497.             $result = ShortcodeHelper::create_shortcode_from_attributes_array( $shortcode, $attr );
  4498.  
  4499.             //  always clear unique id's for custom elements
  4500.             $result = Avia_Builder()->element_manager()->clear_element_ids_in_content( $result );
  4501.  
  4502.             return $result;
  4503.         }
  4504.  
  4505.         /**
  4506.          * Checks if we need to use 1 tab only for all custom element templates.
  4507.          * In case we have 1 empty tab only we return this as condensed.
  4508.          *
  4509.          * @since 4.8
  4510.          * @param array $tabs
  4511.          * @return boolean
  4512.          */
  4513.         protected function condense_tabs( array &$tabs )
  4514.         {
  4515.             $with_content = array();
  4516.             $no_content = array();
  4517.  
  4518.             foreach( $tabs as $key => $buttons )
  4519.             {
  4520.                 if( ! empty( $buttons ) )
  4521.                 {
  4522.                     $with_content[] = $key;
  4523.                 }
  4524.                 else
  4525.                 {
  4526.                     $no_content[] = $key;
  4527.                 }
  4528.             }
  4529.  
  4530.             switch( count( $with_content ) )
  4531.             {
  4532.                 case 0:
  4533.                     $tabs = array( $this->custom_element_tab_name => array() );
  4534.                     return true;
  4535.                 case 1:
  4536.                     $tabs = array( $this->custom_element_tab_name => $tabs[ $with_content[0] ] );
  4537.                     return true;
  4538.             }
  4539.  
  4540.             foreach( $no_content as $key )
  4541.             {
  4542.                 unset( $tabs[ $key ] );
  4543.             }
  4544.  
  4545.             if( 'group' == avia_get_option( 'custom_el_shortcode_buttons' ) )
  4546.             {
  4547.                 return false;
  4548.             }
  4549.  
  4550.             /**
  4551.              * Maximum number of custom element shortcode buttons to use one tab only
  4552.              *
  4553.              * @since 4.8
  4554.              */
  4555.             $limit = apply_filters( 'avf_max_custom_elements_one_tab_only', 300 );
  4556.  
  4557.             $count = 0;
  4558.             foreach( $tabs as $key => $buttons )
  4559.             {
  4560.                 $count += count( $buttons );
  4561.             }
  4562.  
  4563.             if( $count >= $limit )
  4564.             {
  4565.                 return false;
  4566.             }
  4567.  
  4568.             $condensed = array();
  4569.  
  4570.             foreach( $tabs as $key => $buttons )
  4571.             {
  4572.                 $condensed = array_merge( $condensed, $buttons );
  4573.             }
  4574.  
  4575.             $tabs = array( $this->custom_element_tab_name => $condensed );
  4576.  
  4577.             return true;
  4578.         }
  4579.     }
  4580.  
  4581.     /**
  4582.      * Returns the main instance of aviaElementTemplates to prevent the need to use globals
  4583.      *
  4584.      * @since 4.8
  4585.      * @return aviaElementTemplates
  4586.      */
  4587.     function Avia_Element_Templates()
  4588.     {
  4589.         return aviaElementTemplates::instance();
  4590.     }
  4591.  
  4592.     /**
  4593.      * Activate filter and action hooks
  4594.      */
  4595.     Avia_Element_Templates();
  4596. }
  4597.  
Advertisement
Add Comment
Please, Sign In to add comment