registered_locations ) ) { sek_error_log( __FUNCTION__ . ' error => the registered locations must be an array'); return Nimble_Manager()->default_locations; } //sek_error_log( __FUNCTION__ .' => locations ?', array_merge( Nimble_Manager()->default_locations, Nimble_Manager()->registered_locations ) ); return apply_filters( 'sek_get_locations', Nimble_Manager()->registered_locations ); } // @return array of "local" content locations => locations with the following characterictics : // - sections in this location are specific to a given skope id // - header and footer locations are excluded function sek_get_local_content_locations() { $locations = array(); $all_locations = sek_get_locations(); if ( is_array( $all_locations ) ) { foreach ( $all_locations as $loc_id => $loc_data) { // Normalizes with the default model used to register a location // public $default_registered_location_model = [ // 'priority' => 10, // 'is_global_location' => false, // 'is_header_location' => false, // 'is_footer_location' => false // ]; $loc_data = wp_parse_args( $loc_data, Nimble_Manager()->default_registered_location_model ); if ( true === $loc_data['is_header_location'] || true === $loc_data['is_footer_location'] ) continue; if ( !sek_is_global_location( $loc_id ) ) { $locations[$loc_id] = $loc_data; } } } return $locations; } // DEPRECATED IN V1.4.0. // Kept for retro compatibility function sek_get_local_locations() { return sek_get_local_content_locations(); } // @return an array of "global" locations => in which the sections are displayed site wide function sek_get_global_locations() { $locations = array(); $all_locations = sek_get_locations(); if ( is_array( $all_locations ) ) { foreach ( $all_locations as $loc_id => $loc_data) { if ( sek_is_global_location( $loc_id ) ) { $locations[$loc_id] = $loc_data; } } } return $locations; } // @param location_id (string) function sek_get_registered_location_property( $location_id, $property_name = '' ) { $all_locations = sek_get_locations(); $default_property_val = 'not_set'; //sek_error_log( __FUNCTION__ .' => locations ?', $all_locations ); if ( !isset( $all_locations[$location_id] ) || !is_array( $all_locations[$location_id] ) ) { sek_error_log( __FUNCTION__ . ' error => the location ' . $location_id . ' is invalid or not registered.'); return $default_property_val; } if ( empty( $property_name ) || !is_string( $property_name ) ) { sek_error_log( __FUNCTION__ . ' error => the requested property for location ' . $location_id . ' is invalid'); return $default_property_val; } $location_params = wp_parse_args( $all_locations[$location_id], Nimble_Manager()->default_registered_location_model ); return array_key_exists($property_name, $location_params) ? $location_params[$property_name] : $default_property_val; } // @return bool function sek_is_global_location( $location_id ) { if ( !is_string( $location_id ) || empty( $location_id ) ) { sek_error_log( __FUNCTION__ . ' error => missing or invalid location_id param' ); return false; } $is_global_location = sek_get_registered_location_property( $location_id, 'is_global_location' ); return 'not_set' === $is_global_location ? false : true === $is_global_location; } // @param $location_id ( string ). Example '__after_header' function register_location( $location_id, $params = array() ) { $params = is_array( $params ) ? $params : array(); $params = wp_parse_args( $params, Nimble_Manager()->default_registered_location_model ); $registered_locations = Nimble_Manager()->registered_locations; if ( is_array( $registered_locations ) ) { $registered_locations[$location_id] = $params; } Nimble_Manager()->registered_locations = $registered_locations; //sek_error_log( __FUNCTION__ .' => Nimble_Manager()->registered_locations', Nimble_Manager()->registered_locations ); } // @return array // @used when populating the customizer localized params // @param $skope_id optional. Specified when we need to differentiate the local and global locations function sek_get_default_location_model( $skope_id = null ) { $is_global_skope = NIMBLE_GLOBAL_SKOPE_ID === $skope_id; if ( $is_global_skope ) { $defaut_sektions_value = [ 'collection' => [], 'fonts' => [] ];//global_options are saved in a specific option => NIMBLE_OPT_NAME_FOR_GLOBAL_OPTIONS } else { // March 2021 property '__inherits_group_skope_tmpl_when_exists__' added for site templates #478 $defaut_sektions_value = [ 'collection' => [], 'local_options' => [], 'fonts' => [], '__inherits_group_skope_tmpl_when_exists__' => true ]; } foreach( sek_get_locations() as $location_id => $params ) { $is_global_location = sek_is_global_location( $location_id ); if ( $is_global_skope && !$is_global_location ) continue; if ( !$is_global_skope && $is_global_location ) continue; $location_model = wp_parse_args( [ 'id' => $location_id ], Nimble_Manager()->default_location_model ); if ( $is_global_location ) { $location_model[ 'is_global_location' ] = true; } $defaut_sektions_value['collection'][] = $location_model; } return $defaut_sektions_value; } ?> introduction of a normalized way to emit a js event to NB front api // in particular to make sure NB doesn't print a ', $event ); return $html; } } ?> __('Original image dimensions', 'text_doma') ); foreach ( get_intermediate_image_sizes() as $_size ) { $first_to_upper_size = ucfirst(strtolower($_size)); $first_to_upper_size = preg_replace_callback( '/[.!?].*?\w/', '\Nimble\sek_img_sizes_preg_replace_callback', $first_to_upper_size ); if ( in_array( $_size, array('thumbnail', 'medium', 'medium_large', 'large') ) ) { $sizes[ $_size ]['width'] = get_option( "{$_size}_size_w" ); $sizes[ $_size ]['height'] = get_option( "{$_size}_size_h" ); $sizes[ $_size ]['title'] = $first_to_upper_size; //$sizes[ $_size ]['crop'] = (bool) get_option( "{$_size}_crop" ); } elseif ( isset( $_wp_additional_image_sizes[ $_size ] ) ) { $sizes[ $_size ] = array( 'width' => $_wp_additional_image_sizes[ $_size ]['width'], 'height' => $_wp_additional_image_sizes[ $_size ]['height'], 'title' => $first_to_upper_size //'crop' => $_wp_additional_image_sizes[ $_size ]['crop'], ); } } foreach ( $sizes as $_size => $data ) { $to_return[ $_size ] = $data['title'] . ' - ' . $data['width'] . ' x ' . $data['height']; } return $to_return; } function sek_img_sizes_preg_replace_callback( $matches ) { return strtoupper( $matches[0] ); } /* ------------------------------------------------------------------------- * * SMART LOAD HELPER FOR IMAGES AND VIDEOS /* ------------------------------------------------------------------------- */ /** * callback of preg_replace_callback in SEK_Front_Render::sek_maybe_process_img_for_js_smart_load * @return string */ function nimble_regex_callback( $matches ) { // bail if the img has already been parsed for swiper slider lazyloading ( https://github.com/presscustomizr/nimble-builder/issues/596 ) if ( false !== strpos( $matches[0], 'data-srcset' ) || false !== strpos( $matches[0], 'data-src' ) ) { return $matches[0]; // bail if already parsed by this regex or if smartload is disabled } else if ( false !== strpos( $matches[0], 'data-sek-src' ) || preg_match('/ data-skip-lazyload *= *"true" */', $matches[0]) ) { return $matches[0]; // otherwise go ahead and parse } else { return apply_filters( 'nimble_img_smartloaded', // june 2020 : the spaces before strings to replace ensure we don't replace attributes that include "srcset" or "sizes" // which can happen if another plugin has already added lazy load attributes // see https://github.com/presscustomizr/nimble-builder/issues/723 str_replace( array(' srcset=', ' sizes='), array(' data-sek-srcset=', ' data-sek-sizes='), sprintf('', $matches[1], 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7', $matches[2], $matches[3] ) ) ); } } // @return boolean // img smartload can be set globally with 'global-img-smart-load' and locally with 'local-img-smart-load' // the local option wins // if local is set to inherit, return the global option // This option is cached // deactivated when customizing function sek_is_img_smartload_enabled() { // condition added in april 2020 when implementing yoast compat https://github.com/presscustomizr/nimble-builder/issues/657 if ( is_admin() && !skp_is_customizing() ) return false; if ( 'not_cached' !== Nimble_Manager()->img_smartload_enabled ) { return Nimble_Manager()->img_smartload_enabled; } $is_img_smartload_enabled = false; // LOCAL OPTION // we use the ajaxily posted skope_id when available <= typically in a customizing ajax action 'sek-refresh-stylesheet' // otherwise we fallback on the normal utility skp_build_skope_id() $local_performances_data = sek_get_local_option_value( 'local_performances' ); $local_smartload = 'inherit'; if ( !is_null( $local_performances_data ) && is_array( $local_performances_data ) ) { if ( !empty( $local_performances_data['local-img-smart-load'] ) && 'inherit' !== $local_performances_data['local-img-smart-load'] ) { $local_smartload = 'yes' === $local_performances_data['local-img-smart-load']; } } if ( 'inherit' !== $local_smartload ) { $is_img_smartload_enabled = $local_smartload; } else { // GLOBAL OPTION $glob_performances_data = sek_get_global_option_value( 'performances' ); if ( !is_null( $glob_performances_data ) && is_array( $glob_performances_data ) && !empty( $glob_performances_data['global-img-smart-load'] ) ) { $is_img_smartload_enabled = sek_booleanize_checkbox_val( $glob_performances_data['global-img-smart-load'] ); } } // CACHE THE OPTION Nimble_Manager()->img_smartload_enabled = $is_img_smartload_enabled; return Nimble_Manager()->img_smartload_enabled; } // @return boolean // video background lazy load can be set globally with 'global-bg-video-lazy-load' // implemented in nov 2019 for https://github.com/presscustomizr/nimble-builder/issues/287 // This option is cached function sek_is_video_bg_lazyload_enabled() { // if ( skp_is_customizing() ) // return false; if ( 'not_cached' !== Nimble_Manager()->video_bg_lazyload_enabled ) { return Nimble_Manager()->video_bg_lazyload_enabled; } $is_video_bg_lazyload_enabled = false; $glob_performances_data = sek_get_global_option_value( 'performances' ); if ( !is_null( $glob_performances_data ) && is_array( $glob_performances_data ) && !empty( $glob_performances_data['global-bg-video-lazy-load'] ) ) { $is_video_bg_lazyload_enabled = sek_booleanize_checkbox_val( $glob_performances_data['global-bg-video-lazy-load'] ); } // CACHE THE OPTION Nimble_Manager()->video_bg_lazyload_enabled = $is_video_bg_lazyload_enabled; return Nimble_Manager()->video_bg_lazyload_enabled; } /* ------------------------------------------------------------------------- * * DISABLE SUPPORT FOR BROWSER NATIVE LAZY LOADING /* ------------------------------------------------------------------------- */ // Disabled when rendering NB content only // attribute loading="lazy" was introduced in WP 5.5, Oct 2020 // => see why NB disable it here :https://github.com/presscustomizr/nimble-builder/issues/747 add_filter( 'wp_lazy_loading_enabled', function($default) { return Nimble_Manager()->rendering ? false : $default; }); /* ------------------------------------------------------------------------- * * IMPORT IMAGE IF NOT ALREADY IN MEDIA LIB /* ------------------------------------------------------------------------- */ // @return attachment id or WP_Error // this method uses download_url() // it first checks if the media already exists in the media library function sek_sideload_img_and_return_attachment_id( $img_url ) { // Set variables for storage, fix file filename for query strings. preg_match( '/[^\?]+\.(jpe?g|jpe|gif|png)\b/i', $img_url, $matches ); $filename = basename( $matches[0] ); // prefix with nimble_asset_ if not done yet // for example, when importing a file, the img might already have the nimble_asset_ prefix if it's been uploaded by Nimble if ( 'nimble_asset_' !== substr($filename, 0, strlen('nimble_asset_') ) ) { $filename = 'nimble_asset_' . $filename; } // remove the extension $img_title = preg_replace( '/\.[^.]+$/', '', trim( $filename ) ); //sek_error_log( __FUNCTION__ . ' ALORS img_title?', preg_replace( '/\.[^.]+$/', '', trim( $img_title ) ) ); // Make sure this img has not already been uploaded // Meta query on the alt property, better than the title // because of https://github.com/presscustomizr/nimble-builder/issues/435 $args = array( 'posts_per_page' => 1, 'post_type' => 'attachment', 'post_status' => 'inherit', //'name' => $img_title, 'meta_query' => array( array( 'key' => '_wp_attachment_image_alt', 'value' => $img_title, 'compare' => '=' ), ), ); $get_attachment = new \WP_Query( $args ); //error_log( print_r( $get_attachment->posts, true ) ); if ( is_array( $get_attachment->posts ) && array_key_exists(0, $get_attachment->posts) ) { //wp_send_json_error( __CLASS__ . '::' . __CLASS__ . '::' . __FUNCTION__ . ' => file already uploaded : ' . $relative_path ); $img_id_already_uploaded = $get_attachment->posts[0]->ID; } // stop now and return the id if the attachment was already uploaded if ( isset($img_id_already_uploaded) ) { //sek_error_log( __FUNCTION__ . ' ALREADY UPLOADED ?', $img_id_already_uploaded ); return $img_id_already_uploaded; } // Insert the media // Prepare the file_array that we will pass to media_handle_sideload() $file_array = array(); $file_array['name'] = $filename; // Download file to temp location. $file_array['tmp_name'] = download_url( $img_url ); // If error storing temporarily, return the error. if ( is_wp_error( $file_array['tmp_name'] ) ) { sek_error_log( __FUNCTION__ . ' error when firing download_url() for image : ' . $img_url ); return $file_array['tmp_name']; } // Do the validation and storage stuff. $id = media_handle_sideload( $file_array, 0 ); // If error storing permanently, unlink. if ( is_wp_error( $id ) ) { sek_error_log( __FUNCTION__ . ' error when firing media_handle_sideload() for image : ' . $img_url ); @unlink( $file_array['tmp_name'] ); } else { // Store the title as image alt property // so we can identify it uniquely next time when checking if already uploaded // of course, if the alt property has been manually modified meanwhile, the image will be loaded again // fixes https://github.com/presscustomizr/nimble-builder/issues/435 add_post_meta( $id, '_wp_attachment_image_alt', $img_title, true ); } return $id; } // IMPORT IMG HELPER // recursive //add_filter( 'nimble_pre_import', '\Nimble\sek_maybe_import_imgs' ); function sek_maybe_import_imgs( $seks_data, $do_import_images = true ) { $new_seks_data = array(); // Reset img_import_errors Nimble_Manager()->img_import_errors = []; foreach ( $seks_data as $key => $value ) { if ( is_array($value) ) { $new_seks_data[$key] = sek_maybe_import_imgs( $value, $do_import_images ); } else { if ( is_string( $value ) && false !== strpos( $value, '__img_url__' ) && sek_is_img_url( $value ) ) { $url = str_replace( '__img_url__', '', $value ); // april 2020 : new option to skip importing images // introduced for https://github.com/presscustomizr/nimble-builder/issues/663 if ( !$do_import_images ) { $value = $url; } else { //sek_error_log( __FUNCTION__ . ' URL?', $url ); $id = sek_sideload_img_and_return_attachment_id( $url ); if ( is_wp_error( $id ) ) { $value = null; $img_errors = Nimble_Manager()->img_import_errors; $img_errors[] = $url; Nimble_Manager()->img_import_errors = $img_errors; } else { $value = $id; } } } else if ( is_string( $value ) && false !== strpos( $value, '__default_img_medium__' ) ) { $value = NIMBLE_BASE_URL . '/assets/img/default-img.png'; } $new_seks_data[$key] = $value; } } return $new_seks_data; } // @return bool function sek_is_img_url( $url = '' ) { if ( is_string( $url ) ) { if ( preg_match( '/\.(jpg|jpeg|png|gif)/i', $url ) ) { return true; } } return false; } /* ------------------------------------------------------------------------- * * REMOVE IMAGE STYLE ATTRIBUTE * Used in image module, slider module, special image module /* ------------------------------------------------------------------------- */ // Nov 2020 : removes any additional styles added by a theme ( Twenty Twenty one ) or a plugin to the image // because otherwise it overrides NB builder custom width, height, and custom width when scrolling ( NB Pro ) // hook : 'wp_get_attachment_image_attributes' function sek_remove_image_style_attr( $attr ) { if ( is_array($attr) && isset($attr['style']) ) { unset($attr['style']); } return $attr; } ?> true, 'sort_column' => 'menu_order, post_title', 'menu_class' => 'menu', 'echo' => true, 'link_before' => '', 'link_after' => ''); $args = wp_parse_args( $args, $defaults ); $args = apply_filters( 'wp_page_menu_args', $args ); $menu = ''; $list_args = $args; // Show Home in the menu if ( !empty($args['show_home']) ) { if ( true === $args['show_home'] || '1' === $args['show_home'] || 1 === $args['show_home'] ) { $text = __('Home' , 'text_domain_to_replace'); } else { $text = $args['show_home']; } $class = ''; if ( is_front_page() && !is_paged() ) { $class = 'class="current_page_item"'; } $menu .= '
  • ' . $args['link_before'] . $text . $args['link_after'] . '
  • '; // If the front page is a page, add it to the exclude list if (get_option('show_on_front') == 'page') { if ( !empty( $list_args['exclude'] ) ) { $list_args['exclude'] .= ','; } else { $list_args['exclude'] = ''; } $list_args['exclude'] .= get_option('page_on_front'); } } $list_args['echo'] = false; $list_args['title_li'] = ''; // limit the number of pages displayed, home excluded from the count if included $list_args['number'] = 4; $menu .= str_replace( array( "\r", "\n", "\t" ), '', sek_list_pages( $list_args ) ); // if ( $menu ) // $menu = ''; //$menu = '
    ' . $menu . "
    \n"; if ( $menu ) { $menu = ''; } //$menu = apply_filters( 'wp_page_menu', $menu, $args ); if ( $args['echo'] ) echo wp_kses_post($menu); else return $menu; } /** * Retrieve or display list of pages in list (li) format. * Modified copy of wp_list_pages * @return string HTML list of pages. */ function sek_list_pages( $args = '' ) { $defaults = array( 'depth' => 0, 'show_date' => '', 'date_format' => get_option( 'date_format' ), 'child_of' => 0, 'exclude' => '', 'title_li' => __( 'Pages', 'text_domain_to_replace' ), 'echo' => 1, 'authors' => '', 'sort_column' => 'menu_order, post_title', 'link_before' => '', 'link_after' => '', 'walker' => '' ); $r = wp_parse_args( $args, $defaults ); $output = ''; $current_page = 0; // sanitize, mostly to keep spaces out $r['exclude'] = preg_replace( '/[^0-9,]/', '', $r['exclude'] ); // Allow plugins to filter an array of excluded pages (but don't put a nullstring into the array) $exclude_array = ( $r['exclude'] ) ? explode( ',', $r['exclude'] ) : array(); $r['exclude'] = implode( ',', apply_filters( 'wp_list_pages_excludes', $exclude_array ) ); // Query pages. $r['hierarchical'] = 0; $pages = get_pages( $r ); if ( !empty( $pages ) ) { if ( $r['title_li'] ) { $output .= ''; } } $html = apply_filters( 'wp_list_pages', $output, $r ); if ( $r['echo'] ) { echo wp_kses_post($html); } else { return $html; } } /** * Retrieve HTML list content for page list. * * @uses Walker_Page to create HTML list content. * @since 2.1.0 * @see Walker_Page::walk() for parameters and return description. */ function sek_walk_page_tree( $pages, $depth, $current_page, $r ) { // if ( empty($r['walker']) ) // $walker = new Walker_Page; // else // $walker = $r['walker']; $walker = new \Walker_Page; foreach ( (array) $pages as $page ) { if ( $page->post_parent ) { $r['pages_with_children'][ $page->post_parent ] = true; } } $args = array( $pages, $depth, $r, $current_page ); return call_user_func_array(array($walker, 'walk'), $args); } function sek_get_user_created_menus() { // if ( !skp_is_customizing() ) // return array(); $all_menus = get_terms( 'nav_menu', array( 'hide_empty' => true ) ); $user_menus = array(); foreach ( $all_menus as $menu_obj ) { if ( is_string( $menu_obj->slug ) && !empty( $menu_obj->slug ) && !empty( $menu_obj->name ) ) { $user_menus[ $menu_obj->slug ] = $menu_obj->name; } } // sek_error_log( 'sek_get_user_created_menus', array_merge( // array( 'nimble_page_menu' => __('Default page menu', 'text_domain_to_replace') ) // , $user_menus ) ); return array_merge( array( 'nimble_page_menu' => __('Default page menu', 'text_domain_to_replace') ) , $user_menus ); } ?> 'preset_section', 'content-id' => 'two_columns', 'title' => __( 'Two Columns', 'text_doma' ), 'icon' => 'Nimble_2-columns_icon.svg' ), array( 'content-type' => 'preset_section', 'content-id' => 'three_columns', 'title' => __( 'Three Columns', 'text_doma' ), 'icon' => 'Nimble_3-columns_icon.svg' ), array( 'content-type' => 'preset_section', 'content-id' => 'four_columns', 'title' => __( 'Four Columns', 'text_doma' ), 'icon' => 'Nimble_4-columns_icon.svg' ), array( 'content-type' => 'module', 'content-id' => 'czr_tiny_mce_editor_module', 'title' => __( 'Rich Text Editor', 'text_doma' ), 'icon' => 'Nimble_rich-text-editor_icon.svg' ), array( 'content-type' => 'module', 'content-id' => 'czr_image_module', 'title' => __( 'Image', 'text_doma' ), 'icon' => 'Nimble__image_icon.svg' ), array( 'content-type' => 'module', 'content-id' => 'czr_gallery_module', 'title' => __( 'Image gallery', 'text_doma' ), 'icon' => 'Nimble_gallery_icon.svg' ), array( 'content-type' => 'module', 'content-id' => 'czr_heading_module', 'title' => __( 'Heading', 'text_doma' ), 'icon' => 'Nimble__heading_icon.svg' ), array( 'content-type' => 'module', 'content-id' => 'czr_icon_module', 'title' => __( 'Icon', 'text_doma' ), 'icon' => 'Nimble__icon_icon.svg' ), array( 'content-type' => 'module', 'content-id' => 'czr_special_img_module', 'title' => __( 'Nimble Image', 'text_doma' ), 'icon' => 'Nimble_img_icon.svg', 'is_pro' => !sek_is_pro(), 'active' => sek_is_pro() ), array( 'content-type' => 'module', 'content-id' => 'czr_button_module', 'title' => __( 'Button', 'text_doma' ), 'icon' => 'Nimble_button_icon.svg' ), array( 'content-type' => 'module', 'content-id' => 'czr_img_slider_module', 'title' => __( 'Image & Text Carousel', 'text_doma' ), 'icon' => 'Nimble_slideshow_icon.svg' ), array( 'content-type' => 'module', 'content-id' => 'czr_accordion_module', 'title' => __( 'Accordion', 'text_doma' ), 'icon' => 'Nimble_accordion_icon.svg' ), array( 'content-type' => 'module', 'content-id' => 'czr_advanced_list_module', 'title' => __( 'Advanced List', 'text_doma' ), 'icon' => 'Nimble__advanced_list_icon.svg', 'is_pro' => !sek_is_pro(), 'active' => sek_is_pro() ), array( 'content-type' => 'module', 'content-id' => 'czr_simple_html_module', 'title' => __( 'Html Content', 'text_doma' ), 'icon' => 'Nimble_html_icon.svg' ), array( 'content-type' => 'module', 'content-id' => 'czr_post_grid_module', 'title' => __( 'Post Grid', 'text_doma' ), 'icon' => 'Nimble_posts-list_icon.svg' ), array( 'content-type' => 'module', 'content-id' => 'czr_quote_module', 'title' => __( 'Quote', 'text_doma' ), 'icon' => 'Nimble_quote_icon.svg' ), array( 'content-type' => 'module', 'content-id' => 'czr_shortcode_module', 'title' => __( 'Shortcode', 'text_doma' ), 'icon' => 'Nimble_shortcode_icon.svg' ), array( 'content-type' => 'module', 'content-id' => 'czr_spacer_module', 'title' => __( 'Spacer', 'text_doma' ), 'icon' => 'Nimble__spacer_icon.svg' ), array( 'content-type' => 'module', 'content-id' => 'czr_divider_module', 'title' => __( 'Divider', 'text_doma' ), 'icon' => 'Nimble__divider_icon.svg' ), array( 'content-type' => 'module', 'content-id' => 'czr_map_module', 'title' => __( 'Map', 'text_doma' ), 'icon' => 'Nimble_map_icon.svg' ), array( 'content-type' => 'module', 'content-id' => 'czr_widget_area_module', 'title' => __( 'WordPress widget area', 'text_doma' ), 'font_icon' => '', 'active' => !sek_is_widget_module_disabled() ), array( 'content-type' => 'module', 'content-id' => 'czr_social_icons_module', 'title' => __( 'Social Profiles', 'text_doma' ), 'icon' => 'Nimble_social_icon.svg' ), array( 'content-type' => 'module', 'content-id' => 'czr_simple_form_module', 'title' => __( 'Simple Contact Form', 'text_doma' ), 'icon' => 'Nimble_contact-form_icon.svg' ), array( 'content-type' => 'module', 'content-id' => 'czr_menu_module', 'title' => __( 'Menu', 'text_doma' ), 'font_icon' => 'menu' //'active' => sek_are_beta_features_enabled() ) )); } // September 2020 : filter the collection of modules // Removes pro upsell modules if sek_is_upsell_enabled() is false // filter declared in inc/sektions/_front_dev_php/_constants_and_helper_functions/0_0_5_modules_helpers.php add_filter('sek_get_module_collection', function( $collection ) { if ( sek_is_upsell_enabled() ) return $collection; $filtered = []; foreach ($collection as $mod => $mod_data) { if ( array_key_exists('is_pro', $mod_data) && $mod_data['is_pro'] ) continue; $filtered[] = $mod_data; } return $filtered; }); // @return void() // Fired in 'wp_enqueue_scripts' // Recursively sniff the local and global sections to populate Nimble_Manager()->contextually_active_modules // introduced for https://github.com/presscustomizr/nimble-builder/issues/612 function sek_populate_collection_of_contextually_active_modules( $skope_id = '', $recursive_data = null, $module_collection = null ) { $skope_id = empty( $skope_id ) ? skp_get_skope_id() : $skope_id; if ( is_null( $recursive_data ) ) { $local_skope_settings = sek_get_skoped_seks( $skope_id ); $local_collection = ( is_array( $local_skope_settings ) && !empty( $local_skope_settings['collection'] ) ) ? $local_skope_settings['collection'] : array(); $global_skope_settings = sek_get_skoped_seks( NIMBLE_GLOBAL_SKOPE_ID ); $global_collection = ( is_array( $global_skope_settings ) && !empty( $global_skope_settings['collection'] ) ) ? $global_skope_settings['collection'] : array(); $recursive_data = array_merge( $local_collection, $global_collection ); } if ( is_null( $module_collection ) ) { // make sure Nimble_Manager()->contextually_active_modules is initialized as an array before starting populating it. $module_collection = 'not_set' === Nimble_Manager()->contextually_active_modules ? [] : Nimble_Manager()->contextually_active_modules; } foreach ($recursive_data as $key => $value) { if ( is_array( $value ) && array_key_exists('module_type', $value) ) { $module_type = $value['module_type']; if ( !array_key_exists($module_type, $module_collection) ) { $module_collection[$module_type] = []; } if ( !in_array( $value['id'], $module_collection[$module_type] ) ) { $module_collection[$module_type][] = $value['id']; } } else if ( is_array( $value ) ) { $module_collection = sek_populate_collection_of_contextually_active_modules( $skope_id, $value, $module_collection); } } Nimble_Manager()->contextually_active_modules = $module_collection; return Nimble_Manager()->contextually_active_modules; } // return the cached collection or build it when needed function sek_get_collection_of_contextually_active_modules( $skope_id = '' ) { $skope_id = empty( $skope_id ) ? skp_get_skope_id() : $skope_id; if ( 'not_set' === Nimble_Manager()->contextually_active_modules ) { return sek_populate_collection_of_contextually_active_modules( $skope_id ); } return Nimble_Manager()->contextually_active_modules; } /* ------------------------------------------------------------------------- * * REGISTERED MODULES => GET PROPERTY /* ------------------------------------------------------------------------- */ // Helper function sek_get_registered_module_type_property( $module_type, $property = '' ) { // check introduced since https://github.com/presscustomizr/nimble-builder/issues/432 // may not be mandatory if ( !class_exists('\Nimble\CZR_Fmk_Base') ) { sek_error_log( __FUNCTION__ . ' => error => CZR_Fmk_Base not loaded' ); return; } // registered modules $registered_modules = CZR_Fmk_Base()->registered_modules; if ( !array_key_exists( $module_type, $registered_modules ) ) { sek_error_log( __FUNCTION__ . ' => ' . $module_type . ' not registered.' ); return; } if ( array_key_exists( $property , $registered_modules[ $module_type ] ) ) { return $registered_modules[ $module_type ][$property]; } return; } /* ------------------------------------------------------------------------- * * GET THE INPUT VALUE OF A GIVEN MODULE MODEL /* ------------------------------------------------------------------------- */ // Recursive helper // Handles simple model and multidimensional module model ( father - children ), like // Array // ( // [quote_content] => Array // ( // [quote_text] => Hey, careful, man, there's a beverage here! // [quote_font_size_css] => Array // ( // [desktop] => 29px // [mobile] => 12px // ) // [quote_letter_spacing_css] => 7 // [quote___flag_important] => 1 // ) // [cite_content] => Array // ( // [cite_text] => The Dude in The Big Lebowski // [cite_font_style_css] => italic // ) // [design] => Array // ( // [quote_design] => border-before // ) // ) // Helper // @param $input_id ( string ) // @param $module_model ( array ) function sek_get_input_value_in_module_model( $input_id, $module_model ) { if ( !is_string( $input_id ) ) { sek_error_log( __FUNCTION__ . ' => error => the $input_id param should be a string', $module_model); return; } if ( !is_array( $module_model ) ) { sek_error_log( __FUNCTION__ . ' => error => the $module_model param should be an array', $module_model ); return; } $input_value = '_not_set_'; foreach ( $module_model as $key => $data ) { if ( $input_value !== '_not_set_' ) break; if ( $input_id === $key ) { $input_value = $data; break; } else { if ( is_array( $data ) ) { $input_value = sek_get_input_value_in_module_model( $input_id, $data ); } } } return $input_value; } /* ------------------------------------------------------------------------- * * REGISTERED MODULES => DEFAULT MODULE MODEL /* ------------------------------------------------------------------------- */ // @param (string) module_type // Walk the registered modules tree and generates the module default if not already cached // used : // - in sek_normalize_module_value_with_defaults(), when preprocessing the module model before printing the module template. @see SEK_Front::render() // - when setting the css of a level option. @see for example : sek_add_css_rules_for_bg_border_background() // @return array() function sek_get_default_module_model( $module_type = '' ) { $default = array(); if ( empty( $module_type ) || is_null( $module_type ) ) return $default; // check introduced since https://github.com/presscustomizr/nimble-builder/issues/432 // may not be mandatory if ( !class_exists('\Nimble\CZR_Fmk_Base') ) { sek_error_log( __FUNCTION__ . ' => error => CZR_Fmk_Base not loaded' ); return $default; } // Did we already cache it ? $default_models = Nimble_Manager()->default_models; if ( !empty( $default_models[ $module_type ] ) ) { $default = $default_models[ $module_type ]; } else { $registered_modules = CZR_Fmk_Base()->registered_modules; if ( !array( $registered_modules ) || !CZR_Fmk_Base()->czr_is_module_registered($module_type) ) { sek_error_log( __FUNCTION__ . ' => ' . $module_type . ' is not registered in the $CZR_Fmk_Base_fn()->registered_modules;' ); return $default; } // Is this module a father ? if ( !empty( $registered_modules[ $module_type ]['is_father'] ) && true === $registered_modules[ $module_type ]['is_father'] ) { if ( empty( $registered_modules[ $module_type ][ 'children' ] ) ) { sek_error_log( __FUNCTION__ . ' => ' . $module_type . ' missing children modules' ); return $default; } if ( !is_array( $registered_modules[ $module_type ][ 'children' ] ) ) { sek_error_log( __FUNCTION__ . ' => ' . $module_type . ' children modules should be an array' ); return $default; } foreach ( $registered_modules[ $module_type ][ 'children' ] as $opt_group => $child_mod_type ) { if ( empty( $registered_modules[ $child_mod_type ][ 'tmpl' ] ) ) { sek_error_log( __FUNCTION__ . ' => ' . $child_mod_type . ' => missing "tmpl" property => impossible to build the father default model.' ); continue; } $default[$opt_group] = _sek_build_default_model( $registered_modules[ $child_mod_type ][ 'tmpl' ] ); } } // Not father module case else { if ( empty( $registered_modules[ $module_type ][ 'tmpl' ] ) ) { sek_error_log( __FUNCTION__ . ' => ' . $module_type . ' => missing "tmpl" property => impossible to build the default model.' ); return $default; } // Build $default = _sek_build_default_model( $registered_modules[ $module_type ][ 'tmpl' ] ); } // Cache $default_models[ $module_type ] = $default; Nimble_Manager()->default_models = $default_models; //sek_error_log( __FUNCTION__ . ' => $default_models', $default_models ); } return $default; } // @return array() default model // Walk recursively the 'tmpl' property of the module // 'tmpl' => array( // 'pre-item' => array( // 'social-icon' => array( // 'input_type' => 'select', // 'title' => __('Select an icon', 'text_doma') // ), // ), // 'mod-opt' => array( // 'social-size' => array( // 'input_type' => 'number', // 'title' => __('Size in px', 'text_doma'), // 'step' => 1, // 'min' => 5, // 'transport' => 'postMessage' // ) // ), // 'item-inputs' => array( // 'item-inputs' => array( // 'tabs' => array( // array( // 'title' => __('Content', 'text_doma'), // //'attributes' => 'data-sek-device="desktop"', // 'inputs' => array( // 'content' => array( // 'input_type' => 'detached_tinymce_editor', // 'title' => __('Content', 'text_doma') // ), // 'h_alignment_css' => array( // 'input_type' => 'h_text_alignment', // 'title' => __('Alignment', 'text_doma'), // 'default' => is_rtl() ? 'right' : 'left', // 'refresh_markup' => false, // 'refresh_stylesheet' => true // ) // ) // ) // ) // ) function _sek_build_default_model( $module_tmpl_data, $default_model = null ) { $default_model = is_array( $default_model ) ? $default_model : array(); //error_log( print_r( $module_tmpl_data , true ) ); foreach( $module_tmpl_data as $key => $data ) { if ( 'pre-item' === $key ) continue; if ( is_array( $data ) && array_key_exists( 'input_type', $data ) ) { $default_model[ $key ] = array_key_exists( 'default', $data ) ? $data[ 'default' ] : ''; } if ( is_array( $data ) ) { $default_model = _sek_build_default_model( $data, $default_model ); } } return $default_model; } /* ------------------------------------------------------------------------- * * REGISTERED MODULES => INPUT LIST /* ------------------------------------------------------------------------- */ // @param (string) module_type // Walk the registered modules tree and generates the module input list if not already cached // used : // - when filtering 'sek_add_css_rules_for_input_id' @see Sek_Dyn_CSS_Builder::sek_css_rules_sniffer_walker() // @return array() function sek_get_registered_module_input_list( $module_type = '' ) { $input_list = array(); if ( empty( $module_type ) || is_null( $module_type ) ) return $input_list; // check introduced since https://github.com/presscustomizr/nimble-builder/issues/432 // may not be mandatory if ( !class_exists('\Nimble\CZR_Fmk_Base') ) { sek_error_log( __FUNCTION__ . ' => error => CZR_Fmk_Base not loaded' ); return $input_list; } // Did we already cache it ? $cached_input_lists = Nimble_Manager()->cached_input_lists; if ( !empty( $cached_input_lists[ $module_type ] ) ) { $input_list = $cached_input_lists[ $module_type ]; } else { $registered_modules = CZR_Fmk_Base()->registered_modules; // sek_error_log( __FUNCTION__ . ' => registered_modules', $registered_modules ); if ( sek_is_dev_mode() && !array( $registered_modules ) || !array_key_exists( $module_type, $registered_modules ) ) { sek_error_log( __FUNCTION__ . ' => ' . $module_type . ' is not registered in the $CZR_Fmk_Base_fn()->registered_modules;' ); return $input_list; } // Is this module a father ? if ( !empty( $registered_modules[ $module_type ]['is_father'] ) && true === $registered_modules[ $module_type ]['is_father'] ) { if ( empty( $registered_modules[ $module_type ][ 'children' ] ) ) { sek_error_log( __FUNCTION__ . ' => ' . $module_type . ' missing children modules' ); return $input_list; } if ( !is_array( $registered_modules[ $module_type ][ 'children' ] ) ) { sek_error_log( __FUNCTION__ . ' => ' . $module_type . ' children modules should be an array' ); return $input_list; } $temp = array(); foreach ( $registered_modules[ $module_type ][ 'children' ] as $opt_group => $child_mod_type ) { if ( empty( $registered_modules[ $child_mod_type ][ 'tmpl' ] ) ) { sek_error_log( __FUNCTION__ . ' => ' . $child_mod_type . ' => missing "tmpl" property => impossible to build the master input_list.' ); continue; } // $temp[$opt_group] = _sek_build_input_list( $registered_modules[ $child_mod_type ][ 'tmpl' ] ); // $input_list = array_merge( $input_list, $temp[$opt_group] ); $input_list[$opt_group] = _sek_build_input_list( $registered_modules[ $child_mod_type ][ 'tmpl' ] ); } } else { if ( empty( $registered_modules[ $module_type ][ 'tmpl' ] ) ) { sek_error_log( __FUNCTION__ . ' => ' . $module_type . ' => missing "tmpl" property => impossible to build the input_list.' ); return $input_list; } // Build $input_list = _sek_build_input_list( $registered_modules[ $module_type ][ 'tmpl' ] ); } // if ( empty( $registered_modules[ $module_type ][ 'tmpl' ] ) ) { // sek_error_log( __FUNCTION__ . ' => ' . $module_type . ' => missing "tmpl" property => impossible to build the input_list.' ); // return $input_list; // } // // Build // $input_list = _sek_build_input_list( $registered_modules[ $module_type ][ 'tmpl' ] ); // Cache $cached_input_lists[ $module_type ] = $input_list; Nimble_Manager()->cached_input_lists = $cached_input_lists; // sek_error_log( __FUNCTION__ . ' => $cached_input_lists', $cached_input_lists ); } return $input_list; } // @return array() default model // Walk recursively the 'tmpl' property of the module // 'tmpl' => array( // 'pre-item' => array( // 'social-icon' => array( // 'input_type' => 'select', // 'title' => __('Select an icon', 'text_doma') // ), // ), // 'mod-opt' => array( // 'social-size' => array( // 'input_type' => 'number', // 'title' => __('Size in px', 'text_doma'), // 'step' => 1, // 'min' => 5, // 'transport' => 'postMessage' // ) // ), // 'item-inputs' => array( // 'item-inputs' => array( // 'tabs' => array( // array( // 'title' => __('Content', 'text_doma'), // //'attributes' => 'data-sek-device="desktop"', // 'inputs' => array( // 'content' => array( // 'input_type' => 'detached_tinymce_editor', // 'title' => __('Content', 'text_doma') // ), // 'h_alignment_css' => array( // 'input_type' => 'h_text_alignment', // 'title' => __('Alignment', 'text_doma'), // 'default' => is_rtl() ? 'right' : 'left', // 'refresh_markup' => false, // 'refresh_stylesheet' => true // ) // ) // ) // ) // ) // Build the input list from item-inputs and modop-inputs function _sek_build_input_list( $module_tmpl_data, $input_list = null ) { $input_list = is_array( $input_list ) ? $input_list : array(); //sek_error_log( '_sek_build_input_list', print_r( $module_tmpl_data , true ) ); foreach( $module_tmpl_data as $key => $data ) { if ( 'pre-item' === $key ) continue; if ( is_array( $data ) && array_key_exists( 'input_type', $data ) ) { // each input_id of a module should be unique if ( array_key_exists( $key, $input_list ) ) { sek_error_log( __FUNCTION__ . ' => error => duplicated input_id found => ' . $key ); } else { $input_list[ $key ] = $data; } } else if ( is_array( $data ) ) { $input_list = _sek_build_input_list( $data, $input_list ); } } return $input_list; } /* ------------------------------------------------------------------------- * * NORMALIZE MODULE VALUE WITH DEFAULT * preprocessing the module model before printing the module template. * used before rendering or generating css /* ------------------------------------------------------------------------- */ // @return array() $normalized_model function sek_normalize_module_value_with_defaults( $raw_module_model ) { $normalized_model = $raw_module_model; if ( empty( $normalized_model['module_type'] ) ) { sek_error_log( __FUNCTION__ . ' => missing module type', $normalized_model ); } $module_type = $normalized_model['module_type']; $is_father = sek_get_registered_module_type_property( $module_type, 'is_father' ); $raw_module_value = ( !empty( $raw_module_model['value'] ) && is_array( $raw_module_model['value'] ) ) ? $raw_module_model['value'] : array(); // reset the model value and rewrite it normalized with the defaults $normalized_model['value'] = array(); if ( $is_father ) { $children = sek_get_registered_module_type_property( $module_type, 'children' ); if ( empty( $children ) ) { sek_error_log( __FUNCTION__ . ' => ' . $module_type . ' missing children modules' ); return $default; } if ( !is_array( $children ) ) { sek_error_log( __FUNCTION__ . ' => ' . $module_type . ' children modules should be an array' ); return $default; } foreach ( $children as $opt_group => $child_mod_type ) { $children_value = ( !empty( $raw_module_value[$opt_group] ) && is_array( $raw_module_value[$opt_group] ) ) ? $raw_module_value[$opt_group] : array(); $normalized_model['value'][ $opt_group ] = _sek_normalize_single_module_values( $children_value, $child_mod_type ); } } else { $normalized_model['value'] = _sek_normalize_single_module_values( $raw_module_value, $module_type ); } //sek_error_log('sek_normalize_single_module_values for module type ' . $module_type , $normalized_model ); return $normalized_model; } // @return array() function _sek_normalize_single_module_values( $raw_module_value, $module_type ) { $default_value_model = sek_get_default_module_model( $module_type );//<= walk the registered modules tree and generates the module default if not already cached // reset the model value and rewrite it normalized with the defaults $module_values = array(); if ( czr_is_multi_item_module( $module_type ) ) { foreach ( $raw_module_value as $item ) { $module_values[] = wp_parse_args( $item, $default_value_model ); } } else { $module_values = wp_parse_args( $raw_module_value, $default_value_model ); } return $module_values; } // Returns an array of allowed HTML tags and attributes when securing form echoed with wp_kses() function sek_get_allowed_html_in_forms() { $allowed = array( 'div' => array(), 'span' => array(), 'form' => array( 'action' => true, 'method' => true, 'post' => true ), 'button' => array( 'disabled' => true, 'name' => true, 'type' => true, 'value' => true, ), 'input' => array( 'alt' => true, 'capture' => true, 'checked' => true, 'disabled' => true, 'list' => true, 'name' => true, 'placeholder' => true, 'readonly' => true, 'type' => true, 'value' => true, ), 'label' => array( 'for' => true, ), 'textarea' => array( 'cols' => true, 'disabled' => true, 'maxlength' => true, 'minlength' => true, 'name' => true, 'placeholder' => true, 'readonly' => true, 'rows' => true, 'spellcheck' => true, 'wrap' => true, ), ); $allowed = array_map( function ($to_map) { $attr = array( 'aria-checked' => true, 'aria-describedby' => true, 'aria-details' => true, 'aria-disabled' => true, 'aria-hidden' => true, 'aria-invalid' => true, 'aria-label' => true, 'aria-labelledby' => true, 'aria-live' => true, 'aria-relevant' => true, 'aria-required' => true, 'aria-selected' => true, 'class' => true, 'data-*' => true, 'id' => true, 'inputmode' => true, 'role' => true, 'style' => true, 'tabindex' => true, 'title' => true, ); return array_merge( $attr, (array) $to_map ); }, $allowed ); return $allowed; } ?> 1 if ( intval( $global_breakpoint_data['global-custom-breakpoint'] ) <= 1 ) return; // apply-to-all option is unchecked by default // returns true when user has checked the apply to all option return array_key_exists('apply-to-all', $global_breakpoint_data ) && sek_booleanize_checkbox_val( $global_breakpoint_data[ 'apply-to-all' ] ) ; } // invoked when filtering 'sek_add_css_rules_for__section__options' // param 'for_responsive_columns' has been introduced for https://github.com/presscustomizr/nimble-builder/issues/564 // so we can differentiate when the custom breakpoint is requested for column responsiveness or for css rules generation // when for columns, we always apply the custom breakpoint defined by the user // otherwise, when generating CSS rules like alignment, the custom breakpoint is applied if user explicitely checked the 'apply_to_all' option // 'for_responsive_columns' is set to true when sek_get_closest_section_custom_breakpoint() is invoked from Nimble_Manager()::render() // @param params array( // 'section_model' => array(), // 'for_responsive_columns' => bool // ) function sek_get_section_custom_breakpoint( $params ) { if ( !is_array( $params ) ) return; $params = wp_parse_args( $params, array( 'section_model' => array(), 'for_responsive_columns' => false )); $section = $params['section_model']; if ( !is_array( $section ) ) return; if ( empty($section['id']) ) return; $options = empty( $section[ 'options' ] ) ? array() : $section['options']; if ( empty( $options[ 'breakpoint' ] ) ) return; if ( empty( $options[ 'breakpoint' ][ 'use-custom-breakpoint'] ) || false === sek_booleanize_checkbox_val( $options[ 'breakpoint' ][ 'use-custom-breakpoint'] ) ) return; // assign default value if use-custom-breakpoint is checked but there's no breakpoint set. // this can also occur if the custom breakpoint is left to default in the customizer ( default values are not considered when saving ) if ( empty( $options[ 'breakpoint' ][ 'custom-breakpoint' ] ) ) { if ( array_key_exists('custom-breakpoint', $options[ 'breakpoint' ] ) ) { // this is the case when user has emptied the setting $custom_breakpoint = 1;// added when fixing https://github.com/presscustomizr/nimble-builder/issues/623 } else { $custom_breakpoint = Sek_Dyn_CSS_Builder::$breakpoints['md'];//768 } } else { $custom_breakpoint = intval( $options[ 'breakpoint' ][ 'custom-breakpoint' ] ); } if ( $custom_breakpoint <= 0 ) return 1; // 1) When the breakpoint is requested for responsive columns, we always return the custom value if ( $params['for_responsive_columns'] ) return $custom_breakpoint; // 2) Otherwise ( other CSS rules generation case, like alignment ) we make sure that user want to apply the custom breakpoint also to other by-device customizations return sek_is_section_custom_breakpoint_applied_to_all_customizations_by_device( $options[ 'breakpoint' ] ) ? $custom_breakpoint : null; } // @return bool // introduced for https://github.com/presscustomizr/nimble-builder/issues/564 // Let us know if we need to apply the user defined custom breakpoint to all by-device customizations, like alignment // false by default. // @param $section_breakpoint_options = array( // 'use-custom-breakpoint' => bool // 'custom-breakpoint' => int // 'apply-to-all' => bool // ) function sek_is_section_custom_breakpoint_applied_to_all_customizations_by_device( $section_breakpoint_options ) { if ( !is_array( $section_breakpoint_options ) || empty( $section_breakpoint_options ) ) return; if ( empty( $section_breakpoint_options[ 'use-custom-breakpoint'] ) || false === sek_booleanize_checkbox_val( $section_breakpoint_options[ 'use-custom-breakpoint'] ) ) return; // We need a custom breakpoint > 1 // Make sure the custom breakpoint has not been emptied, otherwise assign a minimal value of 1px // fixes : https://github.com/presscustomizr/nimble-builder/issues/623 $custom_breakpoint = empty( $section_breakpoint_options['custom-breakpoint'] ) ? 1 : $section_breakpoint_options['custom-breakpoint']; if ( intval( $custom_breakpoint ) <= 1 ) return; // apply-to-all option is unchecked by default // returns true when user has checked the apply to all option return array_key_exists('apply-to-all', $section_breakpoint_options ) && sek_booleanize_checkbox_val( $section_breakpoint_options[ 'apply-to-all' ] ); } // Recursive helper // Is also used when building the dyn_css or when firing sek_add_css_rules_for_spacing() // @param id : mandatory // @param collection : optional <= that's why if missing we must walk all collections : local and global function sek_get_closest_section_custom_breakpoint( $params ) { $params = wp_parse_args( $params, array( 'searched_level_id' => '', 'collection' => 'not_set', 'skope_id' => '', 'last_section_breakpoint_found' => 0, 'last_regular_section_breakpoint_found' => 0, 'last_nested_section_breakpoint_found' => 0, 'searched_level_id_found' => false, // the 'for_responsive_columns' param has been introduced for https://github.com/presscustomizr/nimble-builder/issues/564 // so we can differentiate when the custom breakpoint is requested for column responsiveness or for css rules generation // when for columns, we always apply the custom breakpoint defined by the user // otherwise, when generating CSS rules like alignment, the custom breakpoint is applied if user explicitely checked the 'apply_to_all' option // 'for_responsive_columns' is set to true when sek_get_closest_section_custom_breakpoint() is invoked from Nimble_Manager()::render() 'for_responsive_columns' => false ) ); extract( $params, EXTR_OVERWRITE ); if ( !is_string( $searched_level_id ) || empty( $searched_level_id ) ) { sek_error_log( __FUNCTION__ . ' => missing or invalid child_level_id param.'); return $last_section_breakpoint_found;; } if ( $searched_level_id_found ) { return $last_section_breakpoint_found; } // When no collection is provided, we must walk all collections, local and global. if ( 'not_set' === $collection ) { if ( empty( $skope_id ) ) { if ( is_array( $_POST ) && !empty( $_POST['location_skope_id'] ) ) { $skope_id = sanitize_text_field($_POST['location_skope_id']); } else { // When fired during an ajax 'customize_save' action, the skp_get_skope_id() is determined with $_POST['local_skope_id'] // @see add_filter( 'skp_get_skope_id', '\Nimble\sek_filter_skp_get_skope_id', 10, 2 ); $skope_id = skp_get_skope_id(); } } if ( empty( $skope_id ) || '_skope_not_set_' === $skope_id ) { sek_error_log( __FUNCTION__ . ' => the skope_id should not be empty.'); } $local_skope_settings = sek_get_skoped_seks( $skope_id ); $local_collection = ( is_array( $local_skope_settings ) && !empty( $local_skope_settings['collection'] ) ) ? $local_skope_settings['collection'] : array(); $global_skope_settings = sek_get_skoped_seks( NIMBLE_GLOBAL_SKOPE_ID ); $global_collection = ( is_array( $global_skope_settings ) && !empty( $global_skope_settings['collection'] ) ) ? $global_skope_settings['collection'] : array(); $collection = array_merge( $local_collection, $global_collection ); } // Loop collections foreach ( $collection as $level_data ) { //sek_error_log($last_section_breakpoint_found . ' MATCH ? => LEVEL ID AND TYPE => ' . $level_data['level'] . ' | ' . $level_data['id'] ); // stop here and return if a match was recursively found if ( $searched_level_id_found ) break; if ( 'section' == $level_data['level'] ) { $section_maybe_custom_breakpoint = intval( sek_get_section_custom_breakpoint( array( 'section_model' => $level_data, 'for_responsive_columns' => $for_responsive_columns ) ) ); if ( !empty( $level_data['is_nested'] ) && $level_data['is_nested'] ) { $last_nested_section_breakpoint_found = $section_maybe_custom_breakpoint; } else { $last_nested_section_breakpoint_found = 0;//reset last nested breakpoint $last_regular_section_breakpoint_found = $section_maybe_custom_breakpoint; } } if ( array_key_exists( 'id', $level_data ) && $searched_level_id == $level_data['id'] ) { //match found, break this loop if ( $last_nested_section_breakpoint_found >= 1 ) { $last_section_breakpoint_found = $last_nested_section_breakpoint_found; } else if ( $last_regular_section_breakpoint_found >= 1 ) { $last_section_breakpoint_found = $last_regular_section_breakpoint_found; } else { $last_section_breakpoint_found = 0; } $searched_level_id_found = true; break; } if ( !$searched_level_id_found && array_key_exists( 'collection', $level_data ) && is_array( $level_data['collection'] ) ) { $collection = $level_data['collection']; $recursive_params = compact( 'searched_level_id', 'collection', 'skope_id', 'last_section_breakpoint_found', 'last_regular_section_breakpoint_found', 'last_nested_section_breakpoint_found', 'searched_level_id_found', 'for_responsive_columns' ); $recursive_values = sek_get_closest_section_custom_breakpoint( $recursive_params ); if ( is_array($recursive_values) ) { extract( $recursive_values ); } else { $last_section_breakpoint_found = $recursive_values; $searched_level_id_found = true; break; } } } // Returns a breakpoint int if found or an array // => this way we can determine if we continue or not to walk recursively return $searched_level_id_found ? $last_section_breakpoint_found : compact( 'searched_level_id_found', 'last_section_breakpoint_found', 'last_regular_section_breakpoint_found', 'last_nested_section_breakpoint_found' ); } ?> invalid option name' ); return array(); } if ( !skp_is_customizing() && did_action('nimble_front_classes_ready') && '_not_cached_yet_' !== Nimble_Manager()->local_options ) { $local_options = Nimble_Manager()->local_options; } else { // use the provided skope_id if in the signature $skope_id = ( !empty( $skope_id ) && is_string( $skope_id ))? $skope_id : skp_get_skope_id(); $localSkopeNimble = sek_get_skoped_seks( $skope_id ); $local_options = ( is_array( $localSkopeNimble ) && !empty( $localSkopeNimble['local_options'] ) && is_array( $localSkopeNimble['local_options'] ) ) ? $localSkopeNimble['local_options'] : array(); // Cache only after 'wp' && 'nimble_front_classes_ready' // never cache when doing ajax if ( did_action('nimble_front_classes_ready') && did_action('wp') && !defined('DOING_AJAX') ) { Nimble_Manager()->local_options = $local_options; } } // maybe normalizes with default values $values = ( !empty( $local_options ) && !empty( $local_options[ $option_name ] ) ) ? $local_options[ $option_name ] : null; if ( did_action('nimble_front_classes_ready') ) { $values = sek_normalize_local_options_with_defaults( $option_name, $values ); } return $values; } // Introduced for site templates, when using function sek_is_inheritance_locally_disabled() // needed because we can't rely on sek_get_skoped_seks() to get current local sections data, because this function returns the inherited data // @param $option_name = string // 'nimble_front_classes_ready' is fired when Nimble_Manager() is instanciated function sek_get_local_option_value_without_inheritance( $option_name = '', $skope_id = null ) { if ( empty($option_name) ) { sek_error_log( __FUNCTION__ . ' => invalid option name' ); return array(); } if ( !skp_is_customizing() && did_action('nimble_front_classes_ready') && '_not_cached_yet_' !== Nimble_Manager()->local_options_without_tmpl_inheritance ) { $local_options_without_tmpl_inheritance = Nimble_Manager()->local_options_without_tmpl_inheritance; } else { // use the provided skope_id if in the signature $skope_id = ( !empty( $skope_id ) && is_string( $skope_id ))? $skope_id : skp_get_skope_id(); $localSkopeNimble = sek_get_seks_without_group_inheritance( $skope_id ); if ( skp_is_customizing() && NIMBLE_GLOBAL_SKOPE_ID != $skope_id ) { // when customizing, let us filter the value with the 'customized' ones $localSkopeNimble = apply_filters( 'sek_get_skoped_seks', $localSkopeNimble, $skope_id, '' ); } $local_options_without_tmpl_inheritance = ( is_array( $localSkopeNimble ) && !empty( $localSkopeNimble['local_options'] ) && is_array( $localSkopeNimble['local_options'] ) ) ? $localSkopeNimble['local_options'] : array(); // Cache only after 'wp' && 'nimble_front_classes_ready' // never cache when doing ajax if ( did_action('nimble_front_classes_ready') && did_action('wp') && !defined('DOING_AJAX') ) { Nimble_Manager()->local_options_without_tmpl_inheritance = $local_options_without_tmpl_inheritance; } } // maybe normalizes with default values $values = ( !empty( $local_options_without_tmpl_inheritance ) && !empty( $local_options_without_tmpl_inheritance[ $option_name ] ) ) ? $local_options_without_tmpl_inheritance[ $option_name ] : null; if ( did_action('nimble_front_classes_ready') ) { $values = sek_normalize_local_options_with_defaults( $option_name, $values ); } return $values; } // @return array() $normalized_values // @see _1_6_4_sektions_generate_UI_local_skope_options.js function sek_normalize_local_options_with_defaults( $option_name, $raw_module_values ) { if ( empty($option_name) ) { sek_error_log( __FUNCTION__ . ' => invalid option name' ); return array(); } $normalized_values = ( !empty($raw_module_values) && is_array( $raw_module_values ) ) ? $raw_module_values : array(); // map the option key as saved in db ( @see _1_6_4_sektions_generate_UI_local_skope_options.js ) and the module type $local_option_map = SEK_Front_Construct::$local_options_map; if ( !array_key_exists($option_name, $local_option_map) ) { sek_error_log( __FUNCTION__ . ' => invalid option name', $option_name ); return $raw_module_values; } else { $module_type = $local_option_map[$option_name]; } // normalize with the defaults // class_exists check introduced since https://github.com/presscustomizr/nimble-builder/issues/432 // may not be mandatory if ( class_exists('\Nimble\CZR_Fmk_Base') ) { if( CZR_Fmk_Base()->czr_is_module_registered($module_type) ) { $normalized_values = _sek_normalize_single_module_values( $normalized_values, $module_type ); } } return $normalized_values; } /* ------------------------------------------------------------------------- * * GLOBAL OPTIONS HELPERS /* ------------------------------------------------------------------------- */ // @param $option_name = string // 'nimble_front_classes_ready' is fired when Nimble_Manager() is instanciated function sek_get_global_option_value( $option_name = '' ) { if ( empty($option_name) ) { sek_error_log( __FUNCTION__ . ' => invalid option name' ); return array(); } if ( !skp_is_customizing() && did_action('nimble_front_classes_ready') && '_not_cached_yet_' !== Nimble_Manager()->global_nimble_options ) { $global_nimble_options = Nimble_Manager()->global_nimble_options; } else { $global_nimble_options = get_option( NIMBLE_OPT_NAME_FOR_GLOBAL_OPTIONS ); //sek_error_log(' SOOO OPTIONS ?', $global_nimble_options ); // cache when nimble is ready // this hook is fired when Nimble_Manager() is instanciated // never cache when doing ajax if ( did_action('nimble_front_classes_ready') && !defined('DOING_AJAX') ) { Nimble_Manager()->global_nimble_options = $global_nimble_options; } } // maybe normalizes with default values $values = ( is_array( $global_nimble_options ) && !empty( $global_nimble_options[ $option_name ] ) ) ? $global_nimble_options[ $option_name ] : null; if ( did_action('nimble_front_classes_ready') ) { $values = sek_normalize_global_options_with_defaults( $option_name, $values ); } return $values; } // @see _1_6_5_sektions_generate_UI_global_options.js // @return array() $normalized_values function sek_normalize_global_options_with_defaults( $option_name, $raw_module_values ) { if ( empty($option_name) ) { sek_error_log( __FUNCTION__ . ' => invalid option name' ); return array(); } $normalized_values = ( !empty($raw_module_values) && is_array( $raw_module_values ) ) ? $raw_module_values : array(); // map the option key as saved in db ( @see _1_6_5_sektions_generate_UI_global_options.js ) and the module type $global_option_map = SEK_Front_Construct::$global_options_map; //sek_error_log('SEK_Front_Construct::$global_options_map', SEK_Front_Construct::$global_options_map ); if ( !array_key_exists($option_name, $global_option_map) ) { sek_error_log( __FUNCTION__ . ' => invalid option name', $option_name ); return $raw_module_values; } else { $module_type = $global_option_map[$option_name]; } // normalize with the defaults // class_exists check introduced since https://github.com/presscustomizr/nimble-builder/issues/432 // may not be mandatory if ( class_exists('\Nimble\CZR_Fmk_Base') ) { if( CZR_Fmk_Base()->czr_is_module_registered($module_type) ) { $normalized_values = _sek_normalize_single_module_values( $normalized_values, $module_type ); } } else { sek_error_log( __FUNCTION__ . ' => error => CZR_Fmk_Base not loaded' ); } return $normalized_values; } // // @see _1_6_5_sektions_generate_UI_site_tmpl_options.js // // @return array() $normalized_values // function sek_normalize_site_tmpl_options_with_defaults( $option_name, $raw_module_values ) { // if ( empty($option_name) ) { // sek_error_log( __FUNCTION__ . ' => invalid option name' ); // return array(); // } // $normalized_values = ( !empty($raw_module_values) && is_array( $raw_module_values ) ) ? $raw_module_values : array(); // // map the option key as saved in db ( @see _1_6_5_sektions_generate_UI_global_options.js ) and the module type // $site_tmpl_options_map = SEK_Front_Construct::$site_tmpl_options_map; // //sek_error_log('SEK_Front_Construct::$global_options_map', SEK_Front_Construct::$global_options_map ); // if ( !array_key_exists($option_name, $site_tmpl_options_map) ) { // sek_error_log( __FUNCTION__ . ' => invalid option name', $option_name ); // return $raw_module_values; // } else { // $module_type = $site_tmpl_options_map[$option_name]; // } // // normalize with the defaults // // class_exists check introduced since https://github.com/presscustomizr/nimble-builder/issues/432 // // may not be mandatory // if ( class_exists('\Nimble\CZR_Fmk_Base') ) { // if( CZR_Fmk_Base()->czr_is_module_registered($module_type) ) { // $normalized_values = _sek_normalize_single_module_values( $normalized_values, $module_type ); // } // } else { // sek_error_log( __FUNCTION__ . ' => error => CZR_Fmk_Base not loaded' ); // } // return $normalized_values; // } ?> 'home_url', 'year_now' => date("Y"), 'site_title' => 'get_bloginfo', 'the_title' => 'sek_get_the_title', 'the_archive_title' => 'sek_get_the_archive_title',// works for authors, CPT, taxonomies 'the_archive_description' => 'sek_get_the_archive_description',// works for authors, CPT, taxonomies 'the_content' => 'sek_get_the_content', 'the_tags' => 'sek_get_the_tags', 'the_categories' => 'sek_get_the_categories', 'the_author_link' => 'sek_get_the_author_link', 'the_author_name' => 'sek_get_the_author_name', 'the_author_avatar' => 'sek_get_the_author_avatar', 'the_author_bio' => 'sek_get_the_author_bio', 'the_published_date' => 'sek_get_the_published_date', 'the_modified_date' => 'sek_get_the_modified_date', 'the_comments' => 'sek_get_the_comments', 'the_previous_post_link' => 'sek_get_previous_post_link', 'the_next_post_link' => 'sek_get_next_post_link', 'the_comment_number' => 'sek_get_the_comment_number', 'the_search_query' => 'sek_get_search_query', 'the_search_results_number' => 'sek_get_search_results_nb' )); // Are we good after the filter ? if ( !is_array($replace_values) ) return; //sek_error_log('$matches ??', $matches ); if ( !is_array($matches) || empty($matches[1]) ) return; //$data = html_entity_decode($matches[1], ENT_QUOTES, get_bloginfo( 'charset' ) ); $data = explode(' ', $matches[1] ); // Filter so that {{the_categories sep="/"}} becomes array('the_categories', 'sep="/"' ) with no empty entries // => the first entry is the template tag name, the other entries are the callback arguments ( to implement April 2021 ) $new_data = array_filter($data, function($value) { if ( !is_string($value) ) return false; $value = ltrim($value); return !is_null($value) && !empty($value) && preg_match("/[a-z]/i", $value) ; }); if ( isset($new_data[0]) && array_key_exists( $new_data[0], $replace_values ) ) { // @todo => authorize arguments passed as an array $dyn_content = $replace_values[$new_data[0]]; $fn_name = $dyn_content;// <= typically not namespaced if WP core function, or function added with a filter from a child theme for example $namespaced_fn_name = __NAMESPACE__ . '\\' . $dyn_content; // <= namespaced if Nimble Builder function, introduced in october 2019 for https://github.com/presscustomizr/nimble-builder/issues/401 if ( function_exists( $namespaced_fn_name ) ) { return $namespaced_fn_name();//<= @TODO use call_user_func() here + handle the case when the callback is a method } else if ( function_exists( $fn_name ) ) { return $fn_name();//<= @TODO use call_user_func() here + handle the case when the callback is a method } else if ( is_string($dyn_content) ) { return $dyn_content; } else { return null; } } return null; } // fired @filter 'nimble_parse_template_tags' function sek_parse_template_tags( $val ) { //the pattern could also be '!\{\{(\w+)\}\}!', but adding \s? allows us to allow spaces around the term inside curly braces //see https://stackoverflow.com/questions/959017/php-regex-templating-find-all-occurrences-of-var#comment71815465_959026 //return is_string( $val ) ? preg_replace_callback( '!\{\{\s?(\w+)\s?\}\}!', '\Nimble\sek_find_pattern_match', $val) : $val; return is_string( $val ) ? preg_replace_callback( '!\{\{\s?(.*?)\s?\}\}!', '\Nimble\sek_find_pattern_match', $val) : $val; } add_filter( 'nimble_parse_template_tags', '\Nimble\sek_parse_template_tags' ); // CALLBACKS WHEN IS_ARCHIVE() function sek_get_the_archive_title() { $is_archive = is_archive(); if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) { $is_archive = sek_get_posted_query_param_when_customizing( 'is_archive' ); } if ( !$is_archive ) { return sek_get_tmpl_tag_error( $tag = 'the_archive_title', $msg = __('It can be used in archive pages only.', 'text_doma') ); } if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) { $title = sek_get_posted_query_param_when_customizing( 'the_archive_title' ); } else { add_filter('get_the_archive_title_prefix', '__return_false'); $title = get_the_archive_title(); remove_filter('get_the_archive_title_prefix', '__return_false'); } return $title; } function sek_get_the_archive_description() { $is_archive = is_archive(); if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) { $is_archive = sek_get_posted_query_param_when_customizing( 'is_archive' ); } if ( !$is_archive ) { return sek_get_tmpl_tag_error( $tag = 'the_archive_description', $msg = __('It can be used in archive pages only.', 'text_doma') ); } if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) { $title = sek_get_posted_query_param_when_customizing( 'the_archive_description' ); } else { $title = get_the_archive_description(); } return $title; } // CALLBACKS WHEN IS_SINGULAR() function sek_get_next_post_link() { $is_singular = is_singular(); if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) { $is_singular = sek_get_posted_query_param_when_customizing( 'is_singular' ); } if ( !$is_singular ) { return sek_get_tmpl_tag_error( $tag = 'the_next_post_link', $msg = __('It can only be used in single pages or single posts.', 'text_doma') ); } if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) { $title = sek_get_posted_query_param_when_customizing( 'the_next_post_link' ); } else { $title = get_next_post_link( $format = '%link' ); } if ( empty( $title ) ) { return ''; } else { return sprintf( '%1$s', $title ); } } function sek_get_previous_post_link() { $is_singular = is_singular(); if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) { $is_singular = sek_get_posted_query_param_when_customizing( 'is_singular' ); } if ( !$is_singular ) { return sek_get_tmpl_tag_error( $tag = 'the_previous_post_link', $msg = __('It can only be used in single pages or single posts.', 'text_doma') ); } if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) { $title = sek_get_posted_query_param_when_customizing( 'the_previous_post_link' ); } else { $title = get_previous_post_link( $format = '%link' ); } if ( empty( $title ) ) { return ''; } else { return sprintf( '%1$s', $title ); } } function sek_get_the_comments() { $is_singular = is_singular(); if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) { $is_singular = sek_get_posted_query_param_when_customizing( 'is_singular' ); } if ( !$is_singular ) { return sek_get_tmpl_tag_error( $tag = 'the_comments', $msg = __('It can only be used in single pages or single posts.', 'text_doma') ); } if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) { return sprintf('
     %1$s
    ', __('Comment template can not be refreshed while customizing', 'text_doma') ); } ob_start(); //load_template( $tmpl_path, false ); if ( comments_open() || get_comments_number() ) { add_filter('comments_template', '\Nimble\sek_set_nb_comments_template_path'); comments_template(); remove_filter('comments_template', '\Nimble\sek_set_nb_comments_template_path'); } return ob_get_clean(); } //@filter 'comments_template' function sek_set_nb_comments_template_path( $original_path ) { //@to do => make this path overridable $nb_path = sek_get_templates_dir() . "/wp/comments-template.php"; if ( file_exists( $nb_path ) ) { return $nb_path; } return $original_path; } function sek_get_the_published_date() { $is_singular = is_singular(); if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) { $is_singular = sek_get_posted_query_param_when_customizing( 'is_singular' ); } if ( !$is_singular ) { return sek_get_tmpl_tag_error( $tag = 'the_published_date', $msg = __('It can only be used in single pages or single posts.', 'text_doma') ); } $post_id = sek_get_post_id_on_front_and_when_customizing(); $published_date = get_the_date( get_option('date_format'), $post_id); $machine_readable_published_date = esc_attr( get_the_date( 'c' , $post_id ) ); return sprintf( '', $machine_readable_published_date, $published_date ); } function sek_get_the_modified_date() { $is_singular = is_singular(); if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) { $is_singular = sek_get_posted_query_param_when_customizing( 'is_singular' ); } if ( !$is_singular ) { return sek_get_tmpl_tag_error( $tag = 'the_modified_date', $msg = __('It can only be used in single pages or single posts.', 'text_doma') ); } $post_id = sek_get_post_id_on_front_and_when_customizing(); $modified_date = get_the_modified_date( get_option('date_format'), $post_id ); $machine_readable_modified_date = esc_attr( get_the_modified_date( 'c' ), $post_id ); return sprintf( '', $machine_readable_modified_date , $modified_date ); } function sek_get_the_tags( $separator = ' · ') { $is_singular = is_singular(); if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) { $is_singular = sek_get_posted_query_param_when_customizing( 'is_singular' ); } if ( !$is_singular ) { return sek_get_tmpl_tag_error( $tag = 'the_tags', $msg = __('It can only be used in single pages or single posts.', 'text_doma') ); } return sprintf( '%1$s', get_the_tag_list( $before = '', $sep = $separator, $after = '', $post_id = sek_get_post_id_on_front_and_when_customizing() ) ); } function sek_get_the_categories( $separator = ' / ') { $is_singular = is_singular(); if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) { $is_singular = sek_get_posted_query_param_when_customizing( 'is_singular' ); } if ( !$is_singular ) { return sek_get_tmpl_tag_error( $tag = 'the_categories', $msg = __('It can only be used in single pages or single posts.', 'text_doma') ); } return sprintf( '%1$s', get_the_category_list( $separator, '', $post_id = sek_get_post_id_on_front_and_when_customizing() ) ); } function sek_get_the_comment_number() { $is_singular = is_singular(); if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) { $is_singular = sek_get_posted_query_param_when_customizing( 'is_singular' ); } if ( !$is_singular ) { return sek_get_tmpl_tag_error( $tag = 'the_comment_number', $msg = __('It can only be used in single pages or single posts.', 'text_doma') ); } return sprintf( '%1$s', get_comments_number_text( $zero = false, $one = false, $more = false, $post_id = sek_get_post_id_on_front_and_when_customizing() ) ); } // AUTHOR DATA // 2 CASES : singular or author archives function sek_get_the_author_link() { $author_id = sek_get_author_id_on_front_and_when_customizing(); if ( $author_id ) { $display_name = get_the_author_meta( 'display_name', $author_id ); return sprintf( '', esc_url( get_author_posts_url( $author_id, get_the_author_meta( 'user_nicename', $author_id ) ) ), /* translators: %s: Author's display name. */ esc_attr( sprintf( __( 'Posts by %s' ), $display_name ) ), $display_name ); } return null; } function sek_get_the_author_name() { $author_id = sek_get_author_id_on_front_and_when_customizing(); if ( $author_id ) { return sprintf( '%1$s', get_the_author_meta( 'display_name', $author_id ) ); } return null; } function sek_get_the_author_avatar() { $author_id = sek_get_author_id_on_front_and_when_customizing(); if ( $author_id ) { return get_avatar( get_the_author_meta( 'ID', $author_id ), '85' ); } return null; } function sek_get_the_author_bio() { $author_id = sek_get_author_id_on_front_and_when_customizing(); if ( $author_id ) { return sprintf( '%1$s', get_the_author_meta( 'description', $author_id ) ); } return null; } // introduced in october 2019 for https://github.com/presscustomizr/nimble-builder/issues/401 function sek_get_the_title() { $is_singular = is_singular(); if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) { $is_singular = sek_get_posted_query_param_when_customizing( 'is_singular' ); } if ( !$is_singular ) { return sek_get_tmpl_tag_error( $tag = 'the_title', $msg = __('It can only be used in single pages or single posts.', 'text_doma') ); } return get_the_title( sek_get_post_id_on_front_and_when_customizing() ); } // introduced in october 2019 for https://github.com/presscustomizr/nimble-builder/issues/401 function sek_get_the_content() { $is_singular = is_singular(); if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) { $is_singular = sek_get_posted_query_param_when_customizing( 'is_singular' ); } if ( !$is_singular ) { return sek_get_tmpl_tag_error( $tag = 'the_content', $msg = __('It can only be used in single pages or single posts.', 'text_doma') ); } if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) { $post_id = (int)sek_get_posted_query_param_when_customizing( 'post_id' ); if ( is_int($post_id) ) { $post_object = get_post( $post_id ); return !empty( $post_object ) ? apply_filters( 'the_content', $post_object->post_content ) : null; } } else { $post_object = get_post(); return !empty( $post_object ) ? apply_filters( 'the_content', $post_object->post_content ) : null; } return null; } // CALLBACKS WHEN IS_SEARCH() function sek_get_search_query() { $is_search = is_search(); if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) { $is_search = sek_get_posted_query_param_when_customizing( 'is_search' ); $search_query = sek_get_posted_query_param_when_customizing( 'the_search_query' ); } else { $search_query = get_search_query(); } if ( !$is_search ) { return sek_get_tmpl_tag_error( $tag = 'the_search_query', $msg = __('It can only be used in search results page.', 'text_doma') ); } return sprintf( '%1$s', esc_html( $search_query ) ); } function sek_get_search_results_nb() { $is_search = is_search(); if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) { $is_search = sek_get_posted_query_param_when_customizing( 'is_search' ); $search_res_nb = sek_get_posted_query_param_when_customizing( 'the_search_results_nb' ); } else { global $wp_query; $search_res_nb = (int)$wp_query->found_posts; } if ( !$is_search ) { return sek_get_tmpl_tag_error( $tag = 'the_search_results_number', $msg = __('It can only be used in search results page.', 'text_doma') ); } return sprintf( '%1$s', esc_html( $search_res_nb ) ); } ////////////////////////////////////////////////// ///// HELPERS ///////////////////////////////////////////////// function sek_get_author_id_on_front_and_when_customizing() { $is_singular = is_singular(); if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) { $is_singular = sek_get_posted_query_param_when_customizing( 'is_singular' ); } if ( $is_singular ) { $post_id = sek_get_post_id_on_front_and_when_customizing(); $post_object = get_post( $post_id ); if ( empty( $post_object ) || !is_object( $post_object ) ) { $author_id = null; } $author_id = $post_object->post_author; } else { if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) { $author_id = sek_get_posted_query_param_when_customizing( 'the_author_id' ); } else { global $authordata; $author_id = isset( $authordata->ID ) ? $authordata->ID : 0; } } return $author_id; } // @return the post id in all cases // when performing ajax action, we need the posted query params made available from the ajax params function sek_get_post_id_on_front_and_when_customizing() { if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) { $post_id = (int)sek_get_posted_query_param_when_customizing( 'post_id' ); } else { $post_id = get_the_ID(); } return is_int($post_id) ? $post_id : null; } // recursively sanitize an array of posted ($_POST) query_params to be used when customzing // @param params (array) function sek_sanitize_query_params_array( $params = array()) { foreach ($params as $prm => $val) { if ( is_array($val) ) { if ( empty($val) ) { $sanitized_query_params[$prm] = []; } else { $sanitized_query_params[$prm] = sek_sanitize_query_params_array($params); } } else { $sanitized_query_params[$prm] = sanitize_text_field($val); } } return $sanitized_query_params; } // introduced in october 2019 for https://github.com/presscustomizr/nimble-builder/issues/401 // Possible params as of October 2019 // @see inc/czr-skope/_dev/1_1_0_skop_customizer_preview_load_assets.php:: // 'is_singular' => $wp_query->is_singular, // 'post_id' => get_the_ID() function sek_get_posted_query_param_when_customizing( $param ) { if ( isset( $_POST['czr_query_params'] ) ) { $query_params = json_decode( wp_unslash( $_POST['czr_query_params'] ), true ); if ( array_key_exists( $param, $query_params ) ) { if ( !is_array($query_params[$param]) ) { return sanitize_text_field($query_params[$param]); } else { return sek_sanitize_query_params_array($query_params[$param]); } } else { sek_error_log( __FUNCTION__ . ' => invalid param requested'); return null; } } return null; } function sek_get_tmpl_tag_error( $tag, $msg ) { if ( !skp_is_customizing() ) return; return sprintf('
    %1$s %2$s
    ', '{{' . $tag . '}} ' . __('could not be printed.', 'text_doma'), $msg ); } ?> trailingslashit( get_stylesheet_directory() ) . $template_dir, 10 => trailingslashit( get_template_directory() ) . $template_dir ); $file_paths = apply_filters( 'nimble_template_paths', $file_paths ); // sort the file paths based on priority ksort( $file_paths, SORT_NUMERIC ); return array_map( 'trailingslashit', $file_paths ); } // @return path string // added for #400 // @param params = array( // 'file_name' string 'nimble_template.php', // 'folder' => string 'page-templates', 'header', 'footer' // ) // @param function sek_maybe_get_overriden_local_template_path( $params = array() ) { if ( empty( $params ) || !is_array( $params )) return; $params = wp_parse_args( $params, array( 'file_name' => '', 'folder' => 'page-templates' ) ); if ( !in_array( $params['folder'] , array( 'page-templates', 'header', 'footer' ) ) ) return; $overriden_template_path = ''; // try locating this template file by looping through the template paths // inspîred from /wp-content/plugins/easy-digital-downloads/includes/template-functions.php foreach( sek_get_theme_template_base_paths() as $path_candidate ) { if( file_exists( $path_candidate . $params['folder'] . '/' . $params['file_name'] ) ) { $overriden_template_path = $path_candidate . $params['folder'] . '/' . $params['file_name']; break; } } return $overriden_template_path; } // @return mixed null || string function sek_get_locale_template(){ $template_path = null; $local_template_data = sek_get_local_option_value( 'template' ); if ( !empty( $local_template_data ) && !empty( $local_template_data['local_template'] ) && 'default' !== $local_template_data['local_template'] ) { $template_file_name = $local_template_data['local_template']; $template_file_name_with_php_extension = $template_file_name . '.php'; // Set the default template_path first $template_path = sek_get_templates_dir() . "/page-templates/{$template_file_name_with_php_extension}"; // Make this filtrable // (this filter is used in Hueman theme to assign a specific template) $template_path = apply_filters( 'nimble_get_locale_template_path', $template_path, $template_file_name ); // Use an override if any // Default page tmpl path looks like : NIMBLE_BASE_PATH . "/tmpl/page-template/nimble_template.php", $overriden_template_path = sek_maybe_get_overriden_local_template_path( array( 'file_name' => $template_file_name_with_php_extension, 'folder' => 'page-templates' ) ); if ( !empty( $overriden_template_path ) ) { $template_path = $overriden_template_path; } if ( !file_exists( $template_path ) ) { sek_error_log( __FUNCTION__ .' the custom template does not exist', $template_path ); $template_path = null; } } return $template_path; } /* ------------------------------------------------------------------------- * * HEADER FOOTER /* ------------------------------------------------------------------------- */ // fired by sek_maybe_set_local_nimble_footer() @get_footer() // fired by sek_maybe_set_local_nimble_header() @get_header() function sek_page_uses_nimble_header_footer() { // cache the properties if not done yet Nimble_Manager()->sek_maybe_set_nimble_header_footer(); return true === Nimble_Manager()->has_local_header_footer || true === Nimble_Manager()->has_global_header_footer; } // DEPRECATED SINCE Nimble v1.3.0, november 2018 // was used in the Hueman theme before version 3.4.9 function render_content_sections_for_nimble_template() { Nimble_Manager()->render_nimble_locations( array_keys( Nimble_Manager()->default_locations ),//array( 'loop_start', 'before_content', 'after_content', 'loop_end'), array( 'fallback_location' => 'loop_start' ) ); } ?> invalid collection param when getting model for id : ' . $id ); return $_data; } foreach ( $collection as $level_data ) { // stop here and return if a match was recursively found if ( 'no_match' != $_data ) break; if ( array_key_exists( 'id', $level_data ) && $id === $level_data['id'] ) { $_data = $level_data; } else { if ( array_key_exists( 'collection', $level_data ) && is_array( $level_data['collection'] ) ) { $_data = sek_get_level_model( $id, $level_data['collection'] ); } } } return $_data; } // Recursive helper // Typically used when ajaxing // Is also used when building the dyn_css or when firing sek_add_css_rules_for_spacing() // @param id : mandatory // @param collection : optional <= that's why if missing we must walk all collections : local and global function sek_get_parent_level_model( $child_level_id = '', $collection = array(), $skope_id = '' ) { $_parent_level_data = 'no_match'; if ( !is_string( $child_level_id ) || empty( $child_level_id ) ) { sek_error_log( __FUNCTION__ . ' => missing or invalid child_level_id param.'); return $_parent_level_data; } // When no collection is provided, we must walk all collections, local and global. if ( empty( $collection ) ) { if ( empty( $skope_id ) ) { if ( is_array( $_POST ) && !empty( $_POST['location_skope_id'] ) ) { $skope_id = sanitize_text_field($_POST['location_skope_id']); } else { // When fired during an ajax 'customize_save' action, the skp_get_skope_id() is determined with $_POST['local_skope_id'] // @see add_filter( 'skp_get_skope_id', '\Nimble\sek_filter_skp_get_skope_id', 10, 2 ); $skope_id = skp_get_skope_id(); } } if ( empty( $skope_id ) || '_skope_not_set_' === $skope_id ) { sek_error_log( __FUNCTION__ . ' => the skope_id should not be empty.'); } $local_skope_settings = sek_get_skoped_seks( $skope_id ); $local_collection = ( is_array( $local_skope_settings ) && !empty( $local_skope_settings['collection'] ) ) ? $local_skope_settings['collection'] : array(); $global_skope_settings = sek_get_skoped_seks( NIMBLE_GLOBAL_SKOPE_ID ); $global_collection = ( is_array( $global_skope_settings ) && !empty( $global_skope_settings['collection'] ) ) ? $global_skope_settings['collection'] : array(); $collection = array_merge( $local_collection, $global_collection ); } foreach ( $collection as $level_data ) { // stop here and return if a match was recursively found if ( 'no_match' !== $_parent_level_data ) break; if ( array_key_exists( 'collection', $level_data ) && is_array( $level_data['collection'] ) ) { foreach ( $level_data['collection'] as $child_level_data ) { if ( array_key_exists( 'id', $child_level_data ) && $child_level_id == $child_level_data['id'] ) { $_parent_level_data = $level_data; //match found, break this loop break; } else { $_parent_level_data = sek_get_parent_level_model( $child_level_id, $level_data['collection'], $skope_id ); } } } } return $_parent_level_data; } // Return the skope id in which a level will be rendered // For that, walk the collections local and global to see if there's a match // Fallback skope is local. // used for example in the simple form module to print the hidden skope id, needed on submission. // Recursive helper // @param id : mandatory // @param collection : optional <= that's why if missing we must walk all collections : local and global function sek_get_level_skope_id( $level_id = '' ) { $level_skope_id = skp_get_skope_id(); if ( !is_string( $level_id ) || empty( $level_id ) ) { sek_error_log( __FUNCTION__ . ' => missing or invalid child_level_id param.'); return $level_skope_id; } $local_skope_settings = sek_get_skoped_seks( $level_skope_id ); $local_collection = ( is_array( $local_skope_settings ) && !empty( $local_skope_settings['collection'] ) ) ? $local_skope_settings['collection'] : array(); // if the level id has not been found in the local sections, we know it's a global level. // In dev mode, always make sure that the level id is found in the global locations. if ( 'no_match' === sek_get_level_model( $level_id, $local_collection ) ) { $level_skope_id = NIMBLE_GLOBAL_SKOPE_ID; if ( sek_is_dev_mode() ) { $global_skope_settings = sek_get_skoped_seks( NIMBLE_GLOBAL_SKOPE_ID ); $global_collection = ( is_array( $global_skope_settings ) && !empty( $global_skope_settings['collection'] ) ) ? $global_skope_settings['collection'] : array(); if ( 'no_match' === sek_get_level_model( $level_id, $global_collection ) ) { sek_error_log( __FUNCTION__ . ' => warning, a level id ( ' . $level_id .' ) was not found in local and global sections.'); } } } return $level_skope_id; } ?>page_has_global_sections ) return Nimble_Manager()->page_has_global_sections; $maybe_global_sek_post = sek_get_seks_post( NIMBLE_GLOBAL_SKOPE_ID, 'global' ); $nb_section_created = 0; if ( is_object($maybe_global_sek_post) ) { $seks_data = maybe_unserialize($maybe_global_sek_post->post_content); $seks_data = is_array( $seks_data ) ? $seks_data : array(); $nb_section_created = sek_count_not_empty_sections_in_page( $seks_data ); } // cache now Nimble_Manager()->page_has_global_sections = $nb_section_created > 0; return Nimble_Manager()->page_has_global_sections; } // @return bool // added for https://github.com/presscustomizr/nimble-builder/issues/436 // initially used to determine if a post or a page has been customized with Nimble Builder => if so, we add an edit link in the post/page list // when used in admin, the skope_id must be provided // can be used to determine if we need to render Nimble Builder assets on front. See ::sek_enqueue_front_assets() // March 2021 => fixed : function sek_local_skope_has_nimble_sections() => when customzing => seks_data param should be provided after being filtered with customized values function sek_local_skope_has_nimble_sections( $skope_id = '', $seks_data = null ) { $skope_id = empty( $skope_id ) ? skp_get_skope_id() : $skope_id; if ( NIMBLE_GLOBAL_SKOPE_ID === $skope_id ) { sek_error_log( __FUNCTION__ . ' => error => function should not be used with global skope id' ); return false; } if ( 'not_set' !== Nimble_Manager()->page_has_local_sections ) return Nimble_Manager()->page_has_local_sections; $nb_section_created = 0; // When the collection is provided use it otherwise get it if ( is_null($seks_data) || !is_array($seks_data) ) { $seks_data = sek_get_skoped_seks( $skope_id ); } if ( is_array( $seks_data ) ) { $nb_section_created = sek_count_not_empty_sections_in_page( $seks_data ); } // cache now Nimble_Manager()->page_has_local_sections = $nb_section_created > 0; return Nimble_Manager()->page_has_local_sections; } // @return boolean // Indicates if a section level contains at least on module // Used in SEK_Front_Render::render() to maybe print a css class on the section level function sek_section_has_modules( $model, $has_module = null ) { $has_module = is_null( $has_module ) ? false : (bool)$has_module; foreach ( $model as $level_data ) { // stop here and return if a match was recursively found if ( true === $has_module ) break; if ( is_array( $level_data ) && array_key_exists( 'collection', $level_data ) && is_array( $level_data['collection'] ) ) { foreach ( $level_data['collection'] as $child_level_data ) { if ( 'module'== $child_level_data['level'] ) { $has_module = true; //match found, break this loop break; } else { $has_module = sek_section_has_modules( $child_level_data, $has_module ); } } } } return $has_module; } /* ------------------------------------------------------------------------- * * HAS USER STARTED CREATING SECTIONS ? /* ------------------------------------------------------------------------- */ // @return a boolean // Used to check if we should render the welcome notice in sek_render_welcome_notice() function sek_site_has_nimble_sections_created() { $sek_post_query_vars = array( 'post_type' => NIMBLE_CPT, 'post_status' => get_post_stati(), //'name' => sanitize_title(), 'posts_per_page' => -1, 'no_found_rows' => true, 'cache_results' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, 'lazy_load_term_meta' => false, ); $query = new \WP_Query( $sek_post_query_vars ); //sek_error_log('DO WE HAVE SECTIONS ?', $query ); return is_array( $query->posts ) && !empty( $query->posts ); } // recursive helper to count the number of sections in a given set of sections data function sek_count_not_empty_sections_in_page( $seks_data, $count = 0 ) { if ( !is_array( $seks_data ) ) { sek_error_log( __FUNCTION__ . ' => invalid seks_data param'); return $count; } foreach ( $seks_data as $key => $data ) { if ( is_array( $data ) ) { if ( !empty( $data['level'] ) && 'section' === $data['level'] ) { if ( !empty( $data['collection'] ) ) { $count++; } } else { $count = sek_count_not_empty_sections_in_page( $data, $count ); } } } return $count; } ?> NIMBLE_CPT, 'post_status' => get_post_stati(), //'name' => sanitize_title(), 'posts_per_page' => -1, 'no_found_rows' => true, 'cache_results' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, 'lazy_load_term_meta' => false, ); $query = new \WP_Query( $sek_post_query_vars ); if ( !is_array( $query->posts ) || empty( $query->posts ) ) return; $customized_pages = 0; $nb_section_created = 0; // the global var is easier to handle for array when populated recursively global $modules_used; $module_used = array(); foreach ( $query->posts as $post_object ) { $seks_data = maybe_unserialize($post_object->post_content); $seks_data = is_array( $seks_data ) ? $seks_data : array(); $nb_section_created += sek_count_not_empty_sections_in_page( $seks_data ); sek_populate_list_of_modules_used( $seks_data ); $customized_pages++; } if ( !is_array( $modules_used ) || !is_numeric( $nb_section_created ) || !is_numeric($customized_pages) ) return; $modules_used = array_unique($modules_used); $transient_value = 'not_eligible'; // sek_error_log('$section_created ??', $nb_section_created ); // sek_error_log('$modules_used ?? ' . count($modules_used), $modules_used ); // sek_error_log('$customized_pages ??', $customized_pages ); //version_compare( $this->wp_version, '4.1', '>=' ) if ( $customized_pages > 1 && $nb_section_created > 1 && count($modules_used) > 1 ) { $transient_value = 'eligible'; } set_transient( $transient_name, $transient_value, 7 * DAY_IN_SECONDS ); return $transient_value; } // recursive helper to generate a list of module used in a given set of sections data function sek_populate_list_of_modules_used( $seks_data ) { global $modules_used; if ( !is_array( $seks_data ) ) { sek_error_log( __FUNCTION__ . ' => invalid seks_data param'); return $count; } foreach ( $seks_data as $key => $data ) { if ( is_array( $data ) ) { if ( !empty( $data['level'] ) && 'module' === $data['level'] && !empty( $data['module_type'] ) ) { $modules_used[] = $data['module_type']; } else { //$modules_used = array_merge( $modules_used, sek_populate_list_of_modules_used( $data, $modules_used ) ); sek_populate_list_of_modules_used( $data, $modules_used ); } } } } ?>recaptcha_enabled ) { return Nimble_Manager()->recaptcha_enabled; } $recaptcha_enabled = false; $glob_recaptcha_opts = sek_get_global_option_value( 'recaptcha' ); if ( !is_null( $glob_recaptcha_opts ) && is_array( $glob_recaptcha_opts ) && !empty( $glob_recaptcha_opts['enable'] ) ) { $recaptcha_enabled = sek_booleanize_checkbox_val( $glob_recaptcha_opts['enable'] ) && !empty( $glob_recaptcha_opts['public_key'] ) && !empty($glob_recaptcha_opts['private_key'] ); } // CACHE when not doing ajax if ( !defined( 'DOING_AJAX') || true !== DOING_AJAX ) { Nimble_Manager()->recaptcha_enabled = $recaptcha_enabled; } return $recaptcha_enabled; } // @return boolean // reCaptcha is enabled globally // deactivated when customizing function sek_is_recaptcha_badge_globally_displayed() { if ( did_action('nimble_front_classes_ready') && '_not_cached_yet_' !== Nimble_Manager()->recaptcha_badge_displayed ) { return Nimble_Manager()->recaptcha_badge_displayed; } $display_badge = false;//disabled by default @see sek_get_module_params_for_sek_global_recaptcha() $glob_recaptcha_opts = sek_get_global_option_value( 'recaptcha' ); if ( !is_null( $glob_recaptcha_opts ) && is_array( $glob_recaptcha_opts ) && !empty( $glob_recaptcha_opts['badge'] ) ) { $display_badge = sek_booleanize_checkbox_val( $glob_recaptcha_opts['badge'] ) && sek_is_recaptcha_globally_enabled(); } // CACHE when not doing ajax if ( !defined( 'DOING_AJAX') || true !== DOING_AJAX ) { Nimble_Manager()->recaptcha_badge_displayed = $display_badge; } return $display_badge; } // @return bool // used to print reCaptcha js for the form module function sek_front_sections_include_a_form( $bool = false, $recursive_data = null ) { if ( !$bool ) { if ( is_null( $recursive_data ) ) { $local_skope_settings = sek_get_skoped_seks( skp_get_skope_id() ); $local_collection = ( is_array( $local_skope_settings ) && !empty( $local_skope_settings['collection'] ) ) ? $local_skope_settings['collection'] : array(); $global_skope_settings = sek_get_skoped_seks( NIMBLE_GLOBAL_SKOPE_ID ); $global_collection = ( is_array( $global_skope_settings ) && !empty( $global_skope_settings['collection'] ) ) ? $global_skope_settings['collection'] : array(); $recursive_data = array_merge( $local_collection, $global_collection ); } foreach ($recursive_data as $key => $value) { if ( is_array( $value ) && array_key_exists('module_type', $value) && 'czr_simple_form_module' === $value['module_type'] ) { $bool = true; break; } else if ( is_array( $value ) ) { $bool = sek_front_sections_include_a_form( $bool, $value ); } } } return $bool; } ?> empty skope id or location => collection setting id impossible to build' ); } return NIMBLE_OPT_PREFIX_FOR_SEKTION_COLLECTION . "[{$skope_id}]"; } /* ------------------------------------------------------------------------- * * HELPER FOR CHECKBOX OPTIONS /* ------------------------------------------------------------------------- */ function sek_is_checked( $val ) { //cast to string if array $val = is_array($val) ? $val[0] : $val; return sek_booleanize_checkbox_val( $val ); } function sek_booleanize_checkbox_val( $val ) { if ( !$val || is_array( $val ) ) { return false; } if ( is_bool( $val ) && $val ) return true; switch ( (string) $val ) { case 'off': case '' : case 'false' : return false; case 'on': case '1' : case 'true' : return true; default : return false; } } /* ------------------------------------------------------------------------- * * Nimble Widgets Areas /* ------------------------------------------------------------------------- */ // @return the list of Nimble registered widget areas function sek_get_registered_widget_areas() { global $wp_registered_sidebars; $widget_areas = array(); if ( is_array( $wp_registered_sidebars ) && !empty( $wp_registered_sidebars ) ) { foreach ( $wp_registered_sidebars as $registered_sb ) { $id = $registered_sb['id']; if ( !sek_is_nimble_widget_id( $id ) ) continue; $widget_areas[ $id ] = $registered_sb['name']; } } return $widget_areas; } // @return bool // @ param $id string function sek_is_nimble_widget_id( $id ) { // NIMBLE_WIDGET_PREFIX = nimble-widget-area- return NIMBLE_WIDGET_PREFIX === substr( $id, 0, strlen( NIMBLE_WIDGET_PREFIX ) ); } // @return bool // introduced for #883 function sek_is_widget_module_disabled() { return sek_booleanize_checkbox_val( get_option( NIMBLE_OPT_NAME_FOR_DISABLING_WIDGET_MODULE ) ); } /* ------------------------------------------------------------------------- * * Beta Features /* ------------------------------------------------------------------------- */ // December 2018 => preparation of the header / footer feature // The beta features can be control by a constant // and by a global option function sek_are_beta_features_enabled() { $global_beta_feature = sek_get_global_option_value( 'beta_features'); if ( is_array( $global_beta_feature ) && array_key_exists('beta-enabled', $global_beta_feature ) ) { return (bool)$global_beta_feature['beta-enabled']; } return NIMBLE_BETA_FEATURES_ENABLED; } /* ------------------------------------------------------------------------- * * PRO /* ------------------------------------------------------------------------- */ function sek_is_pro() { return defined('NB_PRO_VERSION'); } /* ------------------------------------------------------------------------- * * VERSION HELPERS /* ------------------------------------------------------------------------- */ /** * Returns a boolean * check if user started to use the plugin before ( strictly < ) the requested version * @param $_ver : string free version */ function sek_user_started_before_version( $requested_version ) { $started_with = get_option( 'nimble_started_with_version' ); //the transient is set in HU_utils::hu_init_properties() if ( !$started_with ) return false; if ( !is_string( $requested_version ) ) return false; return version_compare( $started_with , $requested_version, '<' ); } /* ------------------------------------------------------------------------- * * VARIOUS HELPERS /* ------------------------------------------------------------------------- */ function sek_text_truncate( $text, $max_text_length, $more, $strip_tags = true ) { if ( !$text ) return ''; if ( $strip_tags ) $text = strip_tags( $text ); if ( !$max_text_length ) return $text; $end_substr = $text_length = strlen( $text ); if ( $text_length > $max_text_length ) { $text .= ' '; $end_substr = strpos( $text, ' ' , $max_text_length); $end_substr = ( FALSE !== $end_substr ) ? $end_substr : $max_text_length; $text = trim( substr( $text , 0 , $end_substr ) ); } if ( $more && $end_substr < $text_length ) return $text . ' ' .$more; return $text; } // @return a bool // typically when // previewing a changeset on front with a link generated in the publish menu of the customizer // looking like : mysite.com/?customize_changeset_uuid=67862e7f-427c-4183-b3f7-62eb86f79899 // in this case the $_REQUEST super global, doesn't include a customize_messenger_channel paral // added when fixing https://github.com/presscustomizr/nimble-builder/issues/351 function sek_is_customize_previewing_a_changeset_post() { return !( defined('DOING_AJAX') && DOING_AJAX ) && is_customize_preview() && !isset( $_REQUEST['customize_messenger_channel']); } // @return string theme name // always return the parent theme name function sek_get_parent_theme_slug() { $theme_slug = get_option( 'stylesheet' ); // $_REQUEST['theme'] is set both in live preview and when we're customizing a non active theme $theme_slug = sanitize_text_field( isset($_REQUEST['theme']) ? $_REQUEST['theme'] : $theme_slug ); //old wp versions $theme_slug = sanitize_text_field( isset($_REQUEST['customize_theme']) ? $_REQUEST['customize_theme'] : $theme_slug ); //gets the theme name (or parent if child) $theme_data = wp_get_theme( $theme_slug ); if ( $theme_data->parent() ) { $theme_slug = $theme_data->parent()->Name; } return sanitize_file_name( strtolower( $theme_slug ) ); } function sek_error_log( $title, $content = null ) { // Know in which function sek_error_log() was called // debug_backtrace()[1]['function']; $backtrace = debug_backtrace(); //error_log( print_r( $backtrace, true ) ); $content = is_null( $content ) ? '' : $content; if ( !sek_is_dev_mode() ) return; $btrace = ''; if ( is_array($backtrace) && isset($backtrace[1]) ) { if ( !empty( $backtrace[1]['file'] ) && !empty( $backtrace[1]['line'] ) ) { $btrace = $btrace . "\n ====> " . $backtrace[1]['file'] . '#' . $backtrace[1]['line']; } if ( !empty( $backtrace[1]['class'] ) && !empty( $backtrace[1]['function'] ) ) { $btrace = $btrace . "\n ====> " . $backtrace[1]['class'] . '::' . $backtrace[1]['function']; } else if ( !empty( $backtrace[1]['function']) ) { $btrace = $btrace . "\n ====> " . $backtrace[1]['function']; } } if ( is_null( $content ) ) { error_log( '<' . $title . '>' ); if ( !empty($btrace) ) { error_log( '// backtrace :' . $btrace . '//' ); } } else { error_log( '<' . $title . '>' ); error_log( print_r( $content, true ) ); if ( !empty($btrace) ) { error_log( '// backtrace :' . $btrace . '//' ); } error_log( '' ); } } // /* ------------------------------------------------------------------------- * // * HELPERS FOR ADMIN AND API TO DETERMINE / CHECK CURRENT THEME NAME // /* ------------------------------------------------------------------------- */ // @return bool function sek_is_presscustomizr_theme( $theme_name ) { $bool = false; if ( is_string( $theme_name ) ) { foreach ( ['customizr', 'hueman'] as $pc_theme ) { // handle the case when the theme name looks like customizr-4.1.29 if ( !$bool && $pc_theme === substr( $theme_name, 0, strlen($pc_theme) ) ) { $bool = true; } } } return $bool; } // @return the theme name string, exact if customizr or hueman function sek_maybe_get_presscustomizr_theme_name( $theme_name ) { if ( is_string( $theme_name ) ) { foreach ( ['customizr', 'hueman'] as $pc_theme ) { // handle the case when the theme name looks like customizr-4.1.29 if ( $pc_theme === substr( $theme_name, 0, strlen($pc_theme) ) ) { $theme_name = $pc_theme; } } } return $theme_name; } // @return a string function sek_get_th_start_ver( $theme_name ) { if ( !in_array( $theme_name, ['customizr', 'hueman'] ) ) return ''; $start_ver = ''; switch( $theme_name ) { case 'customizr' : $start_ver = defined( 'CZR_USER_STARTED_USING_FREE_THEME' ) ? CZR_USER_STARTED_USING_FREE_THEME : ''; break; case 'hueman' : $start_ver = get_transient( 'started_using_hueman' ); break; } return $start_ver; } /* ------------------------------------------------------------------------- * * STRIP SCRIPT TAG WHEN CUSTOMIZING * to prevent customizer breakages. See https://github.com/presscustomizr/nimble-builder/issues/688 /* ------------------------------------------------------------------------- */ function sek_strip_script_tags_and_print_js_inline( $html, $model ) { if ( !is_string( $html ) ) return $html; if ( skp_is_customizing() ) { // June 2020 => added a notice for https://github.com/presscustomizr/nimble-builder/issues/710 $script_notice = sprintf('
     %1$s
    ', __('Custom javascript code is not executed when customizing.', 'text-doma') ); return preg_replace('#(.*?)#is', $script_notice, $html); } else { $js = ""; // Get the script tags content preg_match_all('/#is', '', $html); // Print scripts inline safely if ( !empty($js) ) { wp_register_script( 'nb_module_custom_js_' . $model['id'], ''); wp_enqueue_script( 'nb_module_custom_js_' . $model['id'] ); wp_add_inline_script( 'nb_module_custom_js_' . $model['id'], $js ); } return $html; } } function sek_strip_script_tags( $html = '' ) { if (!is_string( $html ) ) { return $html; } return preg_replace('#(.*?)#is', '', $html); } // @return bool // Introduced May 2020 function sek_current_user_can_access_nb_ui() { return apply_filters('nb_user_has_access', true ); } /* ------------------------------------------------------------------------- * * TRANSIENTS /* ------------------------------------------------------------------------- */ // July 2020 : introduced for https://github.com/presscustomizr/nimble-builder/issues/730 function sek_clean_transients_like( $transient_string ) { global $wpdb; $where_like = '%'.$transient_string.'%'; $sql = "SELECT `option_name` AS `name`, `option_value` AS `value` FROM $wpdb->options WHERE `option_name` LIKE '$where_like' ORDER BY `option_name`"; $results = $wpdb->get_results( $sql ); $transients = array(); // TRANSIENTS foreach ( $results as $result ) { if ( 0 === strpos( $result->name, '_transient' ) ) { if ( 0 === strpos( $result->name, '_transient_timeout_') ) { $transients['transient_timeout'][ $result->name ] = $result->value; } else { $transients['transient'][ $result->name ] = maybe_unserialize( $result->value ); } } } //sek_error_log('CLEAN PAST TRANSIENTS ' . $transient_string, $transients ); // Clean the transients found // transient name looks like _transient_section_json_transient_2.1.5 // when deleted, it also removes the associated _transient_timeout_... option foreach ($transients as $group => $list) { if ( 'transient' === $group && is_array($list) ) { foreach ($list as $name => $val) { // the transient name does not include the "_transient_" prefix $trans_name = substr($name, strlen('_transient_') ); delete_transient( $trans_name ); } } } } /* ------------------------------------------------------------------------- * * OPTIONS CLEANING /* ------------------------------------------------------------------------- */ // introduced for https://github.com/presscustomizr/nimble-builder/issues/826 function sek_clean_options_starting_like( $opt_string ) { global $wpdb; $where_like = '%'.$opt_string.'%'; $sql = "SELECT `option_name` AS `name`, `option_value` AS `value` FROM $wpdb->options WHERE `option_name` LIKE '$where_like' ORDER BY `option_name`"; $results = $wpdb->get_results( $sql ); if ( !is_array( $results ) ) return; foreach ( $results as $result ) { if ( 0 === strpos( $result->name, $opt_string ) ) { delete_option( $result->name ); } } } // July 2020 : introduced for https://github.com/presscustomizr/nimble-builder/issues/720 // @param $features (string) list of features function sek_get_pro_notice_for_czr_input( $features = '' ) { if ( !sek_is_upsell_enabled() ) return ''; return sprintf( '

    %3$s : %4$s

    %5$s

    ', NIMBLE_BASE_URL.'/assets/czr/sek/img/pro_white.svg?ver='.NIMBLE_VERSION, NIMBLE_BASE_URL.'/assets/img/nimble/nimble_icon.svg?ver='.NIMBLE_VERSION, __('Unlock more features with Nimble Builder Pro', 'text-doma'), $features, sprintf('%2$s ', 'https://presscustomizr.com/nimble-builder-pro/?utm_source=usersite&utm_medium=link&utm_campaign=czr', __('Go Pro', 'text-doma') ) ); } // September 2020 : filter the collection of pre-built sections // Removes pro upsell modules if sek_is_upsell_enabled() is false // filter declared in _front_dev_php/_constants_and_helper_functions/0_5_2_sektions_local_sektion_data.php add_filter('sek_get_raw_section_registration_params', function( $collection ) { if ( sek_is_pro() ) return $collection; if ( sek_is_upsell_enabled() ) return $collection; $filtered = []; foreach ($collection as $section_group_name => $group_data) { $filtered[$section_group_name] = $group_data; foreach ( $group_data['section_collection'] as $sec_key => $sec_data) { if ( array_key_exists('is_pro', $sec_data) && $sec_data['is_pro'] ) { unset($filtered[$section_group_name]['section_collection'][$sec_key]); } } } return $filtered; }); // inspired from https://stackoverflow.com/questions/1846202/php-how-to-generate-a-random-unique-alphanumeric-string-for-use-in-a-secret-l // equivalent of js customizer method CZRSeksPrototype::guid() // introduced feb 2021 for https://github.com/presscustomizr/nimble-builder/issues/478 // used when generating id server side for a site template function sek_generate_level_guid() { return NIMBLE_PREFIX_FOR_SETTING_NOT_SAVED . substr( strval( md5( uniqid( rand(), true) ) ),0, 12 );//__nimble__4cdf8be5ce8f } function sek_is_upsell_enabled() { return !sek_is_pro(); } ?> time(), // 'library' => array( // 'sections' => array( // 'registration_params' => sek_get_sections_registration_params(), // 'json_collection' => sek_get_json_collection() // ), // 'templates' => array() // ), // 'latest_posts' => $post_data, // 'cta' => array( 'started_before' => $go_pro_if_started_before, 'html' => $go_pro_html ) // // 'testtest' => $_GET, // // 'testreferer' => $_SERVER => to get the // ); // @return array|false Info data, or false. // api data is refreshed on plugin update and theme switch // @$what param can be 'latest_posts_and_start_msg', 'templates', 'single_section' function sek_get_nimble_api_data( $params ) { $params = is_array($params) ? $params : []; $params = wp_parse_args( $params, [ 'what' => '', 'tmpl_name' => '', 'section_id' => '', 'force_update' => false ]); $what = $params['what']; $tmpl_name = $params['tmpl_name']; $section_id = $params['section_id']; $force_update = $params['force_update']; $wp_cache_key = 'nimble_api_data_'. $what . $tmpl_name . $section_id; // We must have a "what" if ( is_null($what) || !is_string($what) ) { sek_error_log( __FUNCTION__ . ' => error => $what param not set'); return false; } // If a single template is requested, a valid template name must be provided if ( 'single_tmpl' === $what && ( empty($tmpl_name) || !is_string($tmpl_name) ) ) { sek_error_log( __FUNCTION__ . ' => error => invalid $tmpl_name param'); return false; } // If a single section is requested, a valid section id must be provided if ( 'single_section' === $what && ( empty($section_id) || !is_string($section_id) ) ) { sek_error_log( __FUNCTION__ . ' => error => invalid $section_id param'); return false; } $cached_api_data = wp_cache_get( $wp_cache_key ); if ( $cached_api_data && is_array($cached_api_data) && !empty($cached_api_data) ) { return $cached_api_data; } $transient_name = ''; $transient_duration = 7 * DAY_IN_SECONDS; switch ( $what ) { case 'latest_posts_and_start_msg': $transient_name = 'nimble_api_posts'; $transient_duration = 7 * DAY_IN_SECONDS; break; case 'all_tmpl': $transient_name = 'nimble_api_all_tmpl'; $transient_duration = 5 * DAY_IN_SECONDS; break; case 'single_tmpl': $transient_name = 'nimble_api_tmpl_' . $tmpl_name; $transient_duration = 2 * DAY_IN_SECONDS; break; case 'single_section': $transient_name = 'nimble_api_section_' . $section_id; break; default: sek_error_log( __FUNCTION__ . ' => error => invalid $what param => ' . $what ); break; } if ( empty( $transient_name ) ) { return false; } $theme_slug = sek_get_parent_theme_slug(); $version_transient_value = get_transient( NIMBLE_API_CHECK_TRANSIENT_ID ); $expected_version_transient_value = NIMBLE_VERSION . '_' . $theme_slug; $api_needs_update = $version_transient_value != $expected_version_transient_value; $api_transient_data = maybe_unserialize( get_transient( $transient_name ) ); // set this constant in wp_config.php $force_update = ( defined( 'NIMBLE_FORCE_UPDATE_API_DATA') && NIMBLE_FORCE_UPDATE_API_DATA ) ? true : $force_update; if ( true === $force_update ) { sek_error_log( __FUNCTION__ . ' API is in force update mode. API data requested => ' . $transient_name ); } $api_data = $api_transient_data; $invalid_transient_data = false; // When requesting a single_section with sek_api_get_single_section_data, the expected returned data are formed like // [ // [timestamp] => 1621256718 // [single_section] => [] // ] // When requesting a single_tmpl with sek_get_single_tmpl_api_data, the expected returned data are formed like // [ // [timestamp] => 1621256718 // [single_tmpl] => [] // ] // If a problem occured when getting a pro section or template, single_section or single_tmpl is a string, not an array // in this case, we need to re-connect to the api // see https://github.com/presscustomizr/nimble-builder-pro/issues/193 if ( 'single_section' === $what && is_array( $api_data ) && array_key_exists('single_section', $api_data ) && !is_array($api_data['single_section'] ) ) { $invalid_transient_data = true; } if ( 'single_tmpl' === $what && is_array( $api_data ) && array_key_exists('single_tmpl', $api_data ) && !is_array($api_data['single_tmpl'] ) ) { $invalid_transient_data = true; } // Connect to remote NB api when : // 1) api data transient is not set or has expired ( false === $api_transient_data ) // 2) force_update param is true // 3) NB has been updated to a new version ( $api_needs_update case ) // 4) Theme has been changed ( $api_needs_update case ) // 5) API DATA is not an array ( for https://github.com/presscustomizr/nimble-builder-pro/issues/193 ) // 6) Invalid transient data ( for https://github.com/presscustomizr/nimble-builder-pro/issues/193 ) if ( $force_update || false === $api_data || !is_array($api_data) || $api_needs_update || $invalid_transient_data ) { $query_params = apply_filters( 'nimble_api_query_params', [ 'timeout' => ( $force_update ) ? 25 : 8, 'body' => [ 'api_version' => NIMBLE_VERSION, 'site_lang' => get_bloginfo( 'language' ), 'what' => $what,// 'single_tmpl', 'all_tmpl', 'latest_posts_and_start_msg', 'single_section' 'tmpl_name' => $tmpl_name, 'section_id' => $section_id ] ] ); //sek_error_log('CALL TO REMOTE API NOW FOR DATA => ' . $transient_name . ' | ' . $force_update . ' | ' . $api_needs_update, $query_params ); $response = wp_remote_get( NIMBLE_DATA_API_URL_V2, $query_params ); if ( is_wp_error( $response ) || 200 !== (int) wp_remote_retrieve_response_code( $response ) ) { // set the transient to '_api_error_', so that we don't hammer the api if not reachable. next call will be done after transient expiration $api_data = '_api_error_'; sek_error_log( __FUNCTION__ . ' error with api response'); } $api_data = json_decode( wp_remote_retrieve_body( $response ), true ); if ( empty( $api_data ) || !is_array( $api_data ) ) { sek_error_log( __FUNCTION__ . ' invalid api data after json decode', $api_data ); // set the transient to '_api_error_', so that we don't hammer the api if not reachable. next call will be done after transient expiration $api_data = '_api_error_'; } // When requesting a single_section with sek_api_get_single_section_data, the expected returned data are formed like // [ // [timestamp] => 1621256718 // [single_section] => [] // ] // When requesting a single_tmpl with sek_get_single_tmpl_api_data, the expected returned data are formed like // [ // [timestamp] => 1621256718 // [single_tmpl] => [] // ] // If a problem occured when getting a pro section or template, single_section or single_tmpl is a string, not an array // in this case, we don't want to sage the api data like this as transient because user will need the transient to expire before getting the correct data ( see https://github.com/presscustomizr/nimble-builder-pro/issues/193 ) if ( 'single_section' === $what && array_key_exists('single_section', $api_data ) && !is_array($api_data['single_section'] ) ) { sek_error_log( __FUNCTION__ . ' invalid single section api data', $api_data); $api_data = '_api_error_'; } if ( 'single_tmpl' === $what && array_key_exists('single_tmpl', $api_data ) && !is_array($api_data['single_tmpl'] ) ) { sek_error_log( __FUNCTION__ . ' invalid single tmpl api data', $api_data); $api_data = '_api_error_'; } // if the api could not be reached, let's retry in 2 minutes with a short transient duration set_transient( $transient_name, $api_data, '_api_error_' === $api_data ? 2 * MINUTE_IN_SECONDS : $transient_duration ); // The api data will be refreshed on next plugin update, or next theme switch. Or if $transient_name has expired. // $expected_version_transient_value = NIMBLE_VERSION . '_' . $theme_slug; set_transient( NIMBLE_API_CHECK_TRANSIENT_ID, $expected_version_transient_value, 100 * DAY_IN_SECONDS ); }//if ( $force_update || false === $api_data ) // if api_error a new api call will be done when the relevant transient will expire if ( '_api_error_' === $api_data ) { sek_error_log( __FUNCTION__ . ' API data value is _api_error_ for transient data : ' . $transient_name ); } $api_data = '_api_error_' === $api_data ? null : $api_data; wp_cache_set( $wp_cache_key, $api_data ); //sek_error_log('API DATA for ' . $transient_name, $api_data ); return $api_data; } ////////////////////////////////////////////////// /// TEMPLATE DATA function sek_get_all_tmpl_api_data( $force_update = false ) { // set this constant in wp_config.php $force_update = ( defined( 'NIMBLE_FORCE_UPDATE_API_DATA') && NIMBLE_FORCE_UPDATE_API_DATA ) ? true : $force_update; // To avoid a possible refresh, hence a reconnection to the api when opening the customizer // Let's use the data saved as options // Those data are updated on plugin install, plugin update( upgrader_process_complete ), theme switch // @see https://github.com/presscustomizr/nimble-builder/issues/441 $api_data = sek_get_nimble_api_data([ 'what' => 'all_tmpl', 'force_update' => $force_update ]); $api_data = is_array( $api_data ) ? $api_data : []; //sek_error_log('TMPL DATA ?', $tmpl_data); if ( empty($api_data) || !array_key_exists('lib', $api_data) || !is_array($api_data['lib']) || empty($api_data['lib']['templates']) || !is_array($api_data['lib']['templates']) ) { sek_error_log( __FUNCTION__ . ' => error => no json_collection' ); return array(); } //return []; return maybe_unserialize( $api_data['lib']['templates'] ); } function sek_get_single_tmpl_api_data( $tmpl_name, $is_pro_tmpl = false, $force_update = false ) { // set this constant in wp_config.php $force_update = ( defined( 'NIMBLE_FORCE_UPDATE_API_DATA') && NIMBLE_FORCE_UPDATE_API_DATA ) ? true : $force_update; // To avoid a possible refresh, hence a reconnection to the api when opening the customizer // Let's use the data saved as options // Those data are updated on plugin install, plugin update( upgrader_process_complete ), theme switch // @see https://github.com/presscustomizr/nimble-builder/issues/441 $api_data = sek_get_nimble_api_data([ 'what' => 'single_tmpl', 'tmpl_name' => $tmpl_name, 'force_update' => $force_update ]); // The api should return an array if ( !is_array( $api_data ) || !array_key_exists( 'single_tmpl', $api_data ) ) { return __('Problem when fetching template'); } // If the api returned a pro license key problem, bail now and return the api string message if ( $is_pro_tmpl && is_string( $api_data['single_tmpl'] ) ) { return $api_data['single_tmpl']; } $api_data = wp_parse_args( $api_data, [ 'timestamp' => '', 'single_tmpl' => null ]); //sek_error_log('TMPL DATA ?', $tmpl_data); if ( empty($api_data['single_tmpl']) ) { sek_error_log( __FUNCTION__ . ' => error => empty template for ' . $tmpl_name ); return array(); } if ( !is_array( $api_data['single_tmpl'] ) ) { sek_error_log( __FUNCTION__ . ' => invalid template for ' . $tmpl_name ); return array(); } if ( !array_key_exists( 'data', $api_data['single_tmpl'] ) || !array_key_exists( 'metas',$api_data['single_tmpl'] ) ) { sek_error_log( __FUNCTION__ . ' => error => invalid template data for ' . $tmpl_name ); return array(); } //return []; return maybe_unserialize( $api_data['single_tmpl'] ); } ////////////////////////////////////////////////// /// SINGLE PRESET SECTION DATA function sek_api_get_single_section_data( $api_section_id, $force_update = false ) { // set this constant in wp_config.php $force_update = ( defined( 'NIMBLE_FORCE_UPDATE_API_DATA') && NIMBLE_FORCE_UPDATE_API_DATA ) ? true : $force_update; // To avoid a possible refresh, hence a reconnection to the api when opening the customizer // Let's use the data saved as options // Those data are updated on plugin install, plugin update( upgrader_process_complete ), theme switch // @see https://github.com/presscustomizr/nimble-builder/issues/441 $api_data = sek_get_nimble_api_data([ 'what' => 'single_section', 'section_id' => $api_section_id, 'force_update' => $force_update ]); $api_data = is_array( $api_data ) ? $api_data : []; $api_data = wp_parse_args( $api_data, [ 'timestamp' => '', 'single_section' => null ]); //sek_error_log('SECTION DATA ?', $api_data); if ( empty($api_data['single_section']) ) { sek_error_log( __FUNCTION__ . ' => error => empty section data for ' . $api_section_id ); return array(); } // if ( !array_key_exists( 'data', $api_data['single_tmpl'] ) || !array_key_exists( 'metas',$api_data['single_tmpl'] ) ) { // sek_error_log( __FUNCTION__ . ' => error => invalid section data for ' . $api_section_id ); // return array(); // } //return []; return maybe_unserialize( $api_data['single_section'] ); } ////////////////////////////////////////////////// /// LATESTS POSTS // @return array of posts function sek_get_latest_posts_api_data( $force_update = false ) { // set this constant in wp_config.php $force_update = ( defined( 'NIMBLE_FORCE_UPDATE_API_DATA') && NIMBLE_FORCE_UPDATE_API_DATA ) ? true : $force_update; $api_data = sek_get_nimble_api_data([ 'what' => 'latest_posts_and_start_msg', 'force_update' => $force_update ]); $api_data = is_array( $api_data ) ? $api_data : []; $api_data = wp_parse_args( $api_data, [ 'timestamp' => '', 'latest_posts' => null ]); if ( !is_array( $api_data['latest_posts'] ) || empty( $api_data['latest_posts'] ) ) { sek_error_log( __FUNCTION__ . ' => error => no latest_posts' ); return []; } return $api_data['latest_posts']; } // @return html string function sek_start_msg_from_api( $theme_name, $force_update = false ) { if ( !sek_is_presscustomizr_theme( $theme_name ) ) { return ''; } // set this constant in wp_config.php $force_update = ( defined( 'NIMBLE_FORCE_UPDATE_API_DATA') && NIMBLE_FORCE_UPDATE_API_DATA ) ? true : $force_update; $api_data = sek_get_nimble_api_data( [ 'what' => 'latest_posts_and_start_msg', 'force_update' => $force_update ]); $api_data = is_array( $api_data ) ? $api_data : []; $api_data = wp_parse_args( $api_data, [ 'timestamp' => '', 'start_msg' => null ]); $msg = ''; $api_msg = isset( $api_data['start_msg'] ) ? $api_data['start_msg'] : null; if ( !is_null($api_msg) && is_string($api_msg) ) { $msg = $api_msg; } return $msg; } // Attempt to refresh the api template data => will store in a transient if not done yet, to make it faster to render in the customizer // add_action( 'wp_head', '\Nimble\sek_maybe_refresh_nimble_api_tmpl_data'); // function sek_maybe_refresh_nimble_api_tmpl_data() { // if ( skp_is_customizing() || false !== get_transient( 'nimble_api_all_tmpl' ) ) // return; // sek_get_nimble_api_data(['what' => 'all_tmpl']); // } ?> back to the local data // after problem was reported when fetching data remotely : https://github.com/presscustomizr/nimble-builder/issues/445 ///////////////////////////////////////////////////////////// // REGISTRATION PARAMS FOR PRESET SECTIONS // @return array() function sek_get_sections_registration_params( $force_update = false ) { // JULY 2020 => not stored in a transient anymore. For https://github.com/presscustomizr/nimble-builder/issues/730 // + clean previously created transients $bw_fixes_options = get_option( NIMBLE_OPT_NAME_FOR_BACKWARD_FIXES ); $bw_fixes_options = is_array( $bw_fixes_options ) ? $bw_fixes_options : array(); if ( !array_key_exists('clean_section_params_transient_0720', $bw_fixes_options ) || 'done' != $bw_fixes_options['clean_section_params_transient_0720'] ) { sek_clean_transients_like( 'section_params_transient' ); $bw_fixes_options['clean_section_params_transient_0720'] = 'done'; // flag as done update_option( NIMBLE_OPT_NAME_FOR_BACKWARD_FIXES, $bw_fixes_options ); } // $section_params_transient_name = 'section_params_transient_' . NIMBLE_VERSION; // $registration_params = get_transient( $section_params_transient_name ); // // Refresh every 30 days, unless force_update set to true // if ( $force_update || false === $registration_params ) { // $registration_params = sek_get_raw_section_registration_params(); // set_transient( $section_params_transient_name, $registration_params, 30 * DAY_IN_SECONDS ); // } $registration_params = sek_get_raw_section_registration_params(); return $registration_params; } function sek_get_raw_section_registration_params() { return apply_filters( 'sek_get_raw_section_registration_params', [ 'sek_intro_sec_picker_module' => [ 'name' => __('Sections for an introduction', 'text_doma'), 'section_collection' => array( array( 'content-id' => 'intro_three', 'title' => __('1 columns, call to action, full-width background', 'text-domain' ), 'thumb' => 'intro_three.jpg', 'demo_url' => '#intro-one' ), array( 'content-id' => 'intro_one', 'title' => __('1 column, full-width background', 'text-domain' ), 'thumb' => 'intro_one.jpg', 'demo_url' => '#intro-two' ), array( 'content-id' => 'intro_two', 'title' => __('2 columns, call to action, full-width background', 'text-domain' ), 'thumb' => 'intro_two.jpg', 'demo_url' => '#intro-three' ), array( 'content-id' => 'pro_intro_two', 'title' => __('3 columns, call to actions', 'text-domain' ), 'thumb' => 'pro_intro_two.jpg', 'active' => sek_is_pro(), 'is_pro' => true, 'demo_url' => 'https://nimblebuilder.com/special-image-demo?utm_source=usersite&utm_medium=link&utm_campaign=section_demos' ), array( 'content-id' => 'pro_intro_one', 'title' => __('2 columns, call to actions, image carousel', 'text-domain' ), 'thumb' => 'pro_intro_one.jpg', 'active' => sek_is_pro(), 'is_pro' => true, 'demo_url' => '#intro-four' ) ) ], 'sek_post_grids_sec_picker_module' => [ 'name' => __('Post lists sections', 'text_doma'), 'section_collection' => array( array( 'content-id' => 'grid_one', 'title' => __('Simple post grid', 'text-domain' ), 'thumb' => 'grid_one.jpg', 'demo_url' => 'https://nimblebuilder.com/post-grid-sections?utm_source=usersite&utm_medium=link&utm_campaign=section_demos#grid-one' //'height' => '188px' ), array( 'content-id' => 'grid_two', 'title' => __('Posts on two columns', 'text-domain' ), 'thumb' => 'grid_two.jpg', 'demo_url' => 'https://nimblebuilder.com/post-grid-sections?utm_source=usersite&utm_medium=link&utm_campaign=section_demos#grid-two' //'height' => '188px' ), array( 'content-id' => 'pro_grid_one', 'title' => __('Masonry post grid', 'text-domain' ), 'thumb' => 'pro_grid_one.jpg', 'demo_url' => 'https://nimblebuilder.com/post-grid-sections?utm_source=usersite&utm_medium=link&utm_campaign=section_demos#pro-grid-one', 'active' => sek_is_pro(), 'is_pro' => true //'height' => '188px' ) ) ], 'sek_features_sec_picker_module' => [ 'name' => __('Sections for services and features', 'text_doma'), 'section_collection' => array( array( 'content-id' => 'features_one', 'title' => __('3 columns with icon and call to action', 'text-domain' ), 'thumb' => 'features_one.jpg', 'demo_url' => '#service-one' //'height' => '188px' ), array( 'content-id' => 'features_two', 'title' => __('3 columns with icon', 'text-domain' ), 'thumb' => 'features_two.jpg', 'demo_url' => '#service-two' //'height' => '188px' ) ) ], 'sek_about_sec_picker_module' => [ 'name' => __('About us sections', 'text_doma'), 'section_collection' => array( array( 'content-id' => 'about_one', 'title' => __('A simple about us section with 2 columns', 'text-domain' ), 'thumb' => 'about_one.jpg', 'demo_url' => '#about-one' //'height' => '188px' ) ) ], 'sek_contact_sec_picker_module' => [ 'name' => __('Contact-us sections', 'text_doma'), 'section_collection' => array( array( 'content-id' => 'contact_one', 'title' => __('A contact form and a Google map', 'text-domain' ), 'thumb' => 'contact_one.jpg', 'demo_url' => '#contact-one' //'height' => '188px' ), array( 'content-id' => 'contact_two', 'title' => __('A contact form with an image background', 'text-domain' ), 'thumb' => 'contact_two.jpg', 'demo_url' => '#contact-two' //'height' => '188px' ) ) ], 'sek_team_sec_picker_module' => [ 'name' => __('Sections for teams', 'text_doma'), 'section_collection' => array( array( 'content-id' => 'team_one', 'title' => __('4 column', 'text-domain' ), 'thumb' => 'team_one.jpg', 'demo_url' => '#team-one' ), array( 'content-id' => 'team_two', 'title' => __('3 columns', 'text-domain' ), 'thumb' => 'team_two.jpg', 'height' => '180px', 'demo_url' => '#team-two' ), array( 'content-id' => 'pro_team_one', 'title' => __('3 columns, call to action', 'text-domain' ), 'thumb' => 'pro_team_one.jpg', 'active' => sek_is_pro(), 'height' => '180px', 'is_pro' => true, 'demo_url' => '#team-three' ) ) ], 'sek_column_layouts_sec_picker_module' => [ 'name' => __('Empty sections with columns layout', 'text_doma'), 'section_collection' => array( array( 'content-id' => 'two_columns', 'title' => __('two columns layout', 'text-domain' ), 'thumb' => 'two_columns.jpg' ), array( 'content-id' => 'three_columns', 'title' => __('three columns layout', 'text-domain' ), 'thumb' => 'three_columns.jpg' ), array( 'content-id' => 'four_columns', 'title' => __('four columns layout', 'text-domain' ), 'thumb' => 'four_columns.jpg' ), ) ], // pre-built sections for header and footer 'sek_header_sec_picker_module' => [ 'name' => __('Header sections', 'text_doma'), 'section_collection' => array( array( 'content-id' => 'header_one', 'title' => __('simple header with a logo on the left and a menu on the right', 'text-domain' ), 'thumb' => 'header_one.jpg', 'height' => '33px', 'section_type' => 'header' ), array( 'content-id' => 'header_two', 'title' => __('simple header with a logo on the right and a menu on the left', 'text-domain' ), 'thumb' => 'header_two.jpg', 'height' => '33px', 'section_type' => 'header' ) ) ], 'sek_footer_sec_picker_module' => [ 'name' => __('Footer sections', 'text_doma'), 'section_collection' => array( array( 'content-id' => 'footer_pro_one', 'title' => __('simple 2 columns footer', 'text-domain' ), 'thumb' => 'footer_pro_one.jpg', 'section_type' => 'footer', 'height' => '75px', 'active' => sek_is_pro(), 'is_pro' => true ), array( 'content-id' => 'footer_with_social_links_one', 'title' => __('footer with dynamic date, site title and social links', 'text-domain' ), 'thumb' => 'footer_with_social_links_one.jpg', 'section_type' => 'footer', 'height' => '51px' ), array( 'content-id' => 'footer_one', 'title' => __('simple 3 columns footer', 'text-domain' ), 'thumb' => 'footer_one.jpg', 'section_type' => 'footer', 'height' => '75px' ) ) ] ]); } ?>changeset_post_id() && !current_user_can( get_post_type_object( 'customize_changeset' )->cap->edit_post, $wp_customize->changeset_post_id() ) ) { return; } $current_url = ( is_ssl() ? 'https://' : 'http://' ) . sanitize_text_field($_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']); if ( is_customize_preview() && $wp_customize->changeset_uuid() ) { $current_url = remove_query_arg( 'customize_changeset_uuid', $current_url ); } $customize_url = add_query_arg( 'url', urlencode( $current_url ), wp_customize_url() ); if ( is_customize_preview() ) { $customize_url = add_query_arg( array( 'changeset_uuid' => $wp_customize->changeset_uuid() ), $customize_url ); } } if ( empty( $customize_url ) ) return; $customize_url = add_query_arg( array( 'autofocus' => array( 'section' => '__content_picker__' ) ), $customize_url ); $wp_admin_bar->add_menu( array( 'id' => 'nimble_customize', 'title' => sprintf( '%2$s%4$s', NIMBLE_BASE_URL.'/assets/img/nimble/nimble_icon.svg?ver='.NIMBLE_VERSION, __('Nimble Builder','text_domain_to_replace'), __('Add sections in live preview with Nimble Builder', 'text_domain'), apply_filters( 'nb_admin_bar_title', __( 'Build with Nimble Builder', 'text_domain' ) ) ), 'href' => $customize_url, 'meta' => array( 'class' => 'hide-if-no-customize', ), ) ); }//sek_add_customize_link // returns a customize link when is_admin() for posts and terms // inspired from wp-includes/admin-bar.php#wp_admin_bar_edit_menu() // @param $post is a post object function sek_get_customize_url_when_is_admin( $post = null ) { global $tag, $user_id; $customize_url = ''; $current_screen = get_current_screen(); $post = is_null( $post ) ? get_post() : $post; // July 2019 => Don't display the admin button in post and pages, where we already have the edit button next to the post title // if ( 'post' == $current_screen->base // && 'add' != $current_screen->action // && ( $post_type_object = get_post_type_object( $post->post_type ) ) // && current_user_can( 'read_post', $post->ID ) // && ( $post_type_object->public ) // && ( $post_type_object->show_in_admin_bar ) ) // { // if ( 'draft' == $post->post_status ) { // $preview_link = get_preview_post_link( $post ); // $customize_url = esc_url( $preview_link ); // } else { // $customize_url = get_permalink( $post->ID ); // } // } else if ( 'edit' == $current_screen->base && ( $post_type_object = get_post_type_object( $current_screen->post_type ) ) && ( $post_type_object->public ) && ( $post_type_object->show_in_admin_bar ) && ( get_post_type_archive_link( $post_type_object->name ) ) && !( 'post' === $post_type_object->name && 'posts' === get_option( 'show_on_front' ) ) ) { $customize_url = get_post_type_archive_link( $current_screen->post_type ); } elseif ( 'term' == $current_screen->base && isset( $tag ) && is_object( $tag ) && !is_wp_error( $tag ) && ( $tax = get_taxonomy( $tag->taxonomy ) ) && $tax->public ) { $customize_url = get_term_link( $tag ); } elseif ( 'user-edit' == $current_screen->base && isset( $user_id ) && ( $user_object = get_userdata( $user_id ) ) && $user_object->exists() && $view_link = get_author_posts_url( $user_object->ID ) ) { $customize_url = $view_link; } if ( !empty( $customize_url ) ) { $return_customize_url = add_query_arg( 'return', urlencode( remove_query_arg( wp_removable_query_args(), wp_unslash( $_SERVER['REQUEST_URI'] ) ) ), wp_customize_url() ); $customize_url = add_query_arg( 'url', urlencode( $customize_url ), $return_customize_url ); } return $customize_url; } // introduced for https://github.com/presscustomizr/nimble-builder/issues/436 function sek_get_customize_url_for_post_id( $post_id, $return_url = '' ) { // Build customize_url // @see function sek_get_customize_url_when_is_admin() $customize_url = get_permalink( $post_id ); $return_url = empty( $return_url ) ? $customize_url : $return_url; $return_customize_url = add_query_arg( 'return', urlencode( remove_query_arg( wp_removable_query_args(), wp_unslash( $return_url ) ) ), wp_customize_url() ); $customize_url = add_query_arg( 'url', urlencode( $customize_url ), $return_customize_url ); $customize_url = add_query_arg( array( 'autofocus' => array( 'section' => '__content_picker__' ) ), $customize_url ); return $customize_url; } ?> removed previous option used to store api post news, now handled with a transient. if ( !array_key_exists('optimize_opts_0321_2', $bw_fixes_options ) || 'done' != $bw_fixes_options['optimize_opts_0321_2'] ) { delete_option( 'nimble_api_news_data' ); // flag as done $bw_fixes_options['optimize_opts_0321_2'] = 'done'; update_option( NIMBLE_OPT_NAME_FOR_BACKWARD_FIXES, $bw_fixes_options ); } if ( !array_key_exists('optimize_opts_0321', $bw_fixes_options ) || 'done' != $bw_fixes_options['optimize_opts_0321'] ) { $current_global_opts = get_option('__nimble_options__'); if ( false !== $current_global_opts ) { update_option( NIMBLE_OPT_NAME_FOR_GLOBAL_OPTIONS, $current_global_opts, 'no' ); delete_option( '__nimble_options__' ); } // delete previous option for prebuild section json // => the option will be re-created with autoload set to "no" and renamed "nimble_prebuild_sections" delete_option( 'nb_prebuild_section_json' ); // flag as done $bw_fixes_options['optimize_opts_0321'] = 'done'; update_option( NIMBLE_OPT_NAME_FOR_BACKWARD_FIXES, $bw_fixes_options ); } // If the move in post index has been done, let's update to autoload = false the previous post_id options LIKE nimble___skp__post_page_*****, nimble___skp__tax_product_cat_***** // As of March 2021, event if those previous options are not used anymore, let's keep them in DB to cover potential retro-compat problems // in a future release, if no regression was reported, we'll remove them forever. if ( array_key_exists('move_in_post_index_0321', $bw_fixes_options ) && 'done' === $bw_fixes_options['move_in_post_index_0321'] ) { if ( !array_key_exists('fix_skope_opt_autoload_0321', $bw_fixes_options ) || 'done' != $bw_fixes_options['fix_skope_opt_autoload_0321'] ) { // MOVE ALL OPTIONS LIKE nimble___skp__post_page_*****, nimble___skp__tax_product_cat_***** in a new option ( NIMBLE_OPT_SEKTION_POST_INDEX ), not autoloaded global $wpdb; $results = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}options WHERE autoload = 'yes' and option_name like 'nimble___skp_%'", ARRAY_A ); if ( is_array( $results ) ) { foreach( $results as $old_opt_data ) { if ( !is_array($old_opt_data) ) continue; if ( empty($old_opt_data['option_name']) || empty($old_opt_data['option_value']) ) continue; // update it with autoload set to "no" update_option( $old_opt_data['option_name'], (int)$old_opt_data['option_value'], 'no' ); } } // flag as done $bw_fixes_options['fix_skope_opt_autoload_0321'] = 'done'; update_option( NIMBLE_OPT_NAME_FOR_BACKWARD_FIXES, $bw_fixes_options ); } } if ( !array_key_exists('move_in_post_index_0321', $bw_fixes_options ) || 'done' != $bw_fixes_options['move_in_post_index_0321'] ) { // MOVE ALL OPTIONS LIKE nimble___skp__post_page_*****, nimble___skp__tax_product_cat_***** in a new option ( NIMBLE_OPT_SEKTION_POST_INDEX ), not autoloaded global $wpdb; $results = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}options WHERE autoload = 'yes' and option_name like 'nimble___skp_%'", ARRAY_A ); if ( is_array( $results ) ) { // Populate the new option ( it should not exists at this point ) $nb_posts_index = get_option(NIMBLE_OPT_SEKTION_POST_INDEX); $nb_posts_index = is_array($nb_posts_index) ? $nb_posts_index : []; foreach( $results as $old_opt_data ) { if ( !is_array($old_opt_data) ) continue; if ( empty($old_opt_data['option_name']) || empty($old_opt_data['option_value']) ) continue; $nb_posts_index[ $old_opt_data['option_name'] ] = (int)$old_opt_data['option_value']; } // update it with autoload set to "no" update_option( NIMBLE_OPT_SEKTION_POST_INDEX, $nb_posts_index, 'no'); } // flag as done $bw_fixes_options['move_in_post_index_0321'] = 'done'; update_option( NIMBLE_OPT_NAME_FOR_BACKWARD_FIXES, $bw_fixes_options ); } } // JULY 2020 => NOT FIRED ANYMORE ( because introduced in oct 2018 ) => DEACTIVATED IN nimble-builder.php // fired @wp_loaded // Note : if fired @plugins_loaded, invoking wp_update_post() generates php notices function sek_maybe_do_version_mapping() { // if ( !is_user_logged_in() || !current_user_can( 'customize' ) ) // return; // //delete_option(NIMBLE_OPT_NAME_FOR_GLOBAL_OPTIONS); // $global_options = get_option( NIMBLE_OPT_NAME_FOR_GLOBAL_OPTIONS ); // $global_options = is_array( $global_options ) ? $global_options : array(); // $global_options['retro_compat_mappings'] = isset( $global_options['retro_compat_mappings'] ) ? $global_options['retro_compat_mappings'] : array(); // // To 1_0_4 was introduced in december 2018 // // It's related to a modification of the skope_id when home is a static page // if ( !array_key_exists( 'to_1_4_0', $global_options['retro_compat_mappings'] ) || 'done' != $global_options['retro_compat_mappings']['to_1_4_0'] ) { // $status_to_1_4_0 = sek_do_compat_to_1_4_0(); // //sek_error_log('$status_1_0_4_to_1_1_0 ' . $status_1_0_4_to_1_1_0, $global_options ); // $global_options['retro_compat_mappings']['to_1_4_0'] = 'done'; // } // // 1_0_4_to_1_1_0 introduced in October 2018 // if ( !array_key_exists( '1_0_4_to_1_1_0', $global_options['retro_compat_mappings'] ) || 'done' != $global_options['retro_compat_mappings']['1_0_4_to_1_1_0'] ) { // $status_1_0_4_to_1_1_0 = sek_do_compat_1_0_4_to_1_1_0(); // //sek_error_log('$status_1_0_4_to_1_1_0 ' . $status_1_0_4_to_1_1_0, $global_options ); // $global_options['retro_compat_mappings']['1_0_4_to_1_1_0'] = 'done'; // } // update_option( NIMBLE_OPT_NAME_FOR_GLOBAL_OPTIONS, $global_options ); } //////////////////////////////////////////////////////////////// // RETRO COMPAT => to 1.4.0 // It's related to a modification of the skope_id when home is a static page // Was skp__post_page_home // Now is skp__post_page_{$static_home_page_id} // This was introduced to facilitate the compatibility of Nimble Builder with multilanguage plugins like polylang // => Allows user to create a different home page for each languages // // If the current home page is not a static page, we don't have to do anything // If not, the sections currently saved for skope skp__post_page_home, must be moved to skope skp__post_page_{$static_home_page_id} // => this means that we need to update the post_id saved for option NIMBLE_OPT_PREFIX_FOR_SEKTION_COLLECTION . 'skp__post_page_{$static_home_page_id}'; // to the value of the one saved for option NIMBLE_OPT_PREFIX_FOR_SEKTION_COLLECTION . 'skp__post_page_home'; function sek_do_compat_to_1_4_0() { if ( 'page' === get_option( 'show_on_front' ) ) { $home_page_id = (int)get_option( 'page_on_front' ); if ( 0 < $home_page_id ) { // get the post id storing the current sections on home // @see sek_get_seks_post() $current_option_name = NIMBLE_OPT_PREFIX_FOR_SEKTION_COLLECTION . 'skp__post_page_home'; $post_id_storing_home_page_sections = (int)get_option( $current_option_name ); if ( $post_id_storing_home_page_sections > 0 ) { $new_option_name = NIMBLE_OPT_PREFIX_FOR_SEKTION_COLLECTION . "skp__post_page_{$home_page_id}"; update_option( $new_option_name, $post_id_storing_home_page_sections, 'no' ); } } } } //////////////////////////////////////////////////////////////// // RETRO COMPAT 1.0.4 to 1.1.0 // Introduced when upgrading from version 1.0.4 to version 1.1 +. October 2018. // 1) Retro compat for image and tinymce module, turned multidimensional ( father - child logic ) since 1.1+ // 2) Ensure each level has a "ver_ini" property set to 1.0.4 function sek_do_compat_1_0_4_to_1_1_0() { $sek_post_query_vars = array( 'post_type' => NIMBLE_CPT, 'post_status' => get_post_stati(), //'name' => sanitize_title(), 'posts_per_page' => -1, 'no_found_rows' => true, 'cache_results' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, 'lazy_load_term_meta' => false, ); $query = new \WP_Query( $sek_post_query_vars ); if ( !is_array( $query->posts ) || empty( $query->posts ) ) return; $status = 'success'; foreach ($query->posts as $post_object ) { if ( $post_object ) { $seks_data = maybe_unserialize( $post_object->post_content ); } $seks_data = is_array( $seks_data ) ? $seks_data : array(); if ( empty( $seks_data ) ) continue; $seks_data = sek_walk_levels_and_do_map_compat_1_0_4_to_1_1_0( $seks_data ); $new_post_data = array( 'ID' => $post_object->ID, 'post_title' => $post_object->post_title, 'post_name' => sanitize_title( $post_object->post_title ), 'post_type' => NIMBLE_CPT, 'post_status' => 'publish', 'post_content' => maybe_serialize( $seks_data ) ); //sek_error_log('$new_post_data ??', $seks_data ); $r = wp_update_post( wp_slash( $new_post_data ), true ); if ( is_wp_error( $r ) ) { $status = 'error'; sek_error_log( __FUNCTION__ . ' => error', $r ); } }//foreach return $status; } // Recursive helper // Sniff the modules that need a compatibility mapping // do the mapping // @return an updated sektions collection function sek_walk_levels_and_do_map_compat_1_0_4_to_1_1_0( $seks_data ) { $new_seks_data = array(); foreach ( $seks_data as $key => $value ) { // Set level ver_ini // If the ver_ini property is not set, it means the level has been created with the previous version of Nimble ( v1.0.4 ) // Let's add it if ( is_array($value) && array_key_exists('level', $value) && !array_key_exists('ver_ini', $value) ) { $value['ver_ini'] = '1.0.4'; } $new_seks_data[$key] = $value; // LEVEL OPTIONS mapping // remove spacing // remove layout // copy all background related options ( bg-* ) from "bg_border" to "bg" // options => array( // spacing => array(), // height => array(), // bg_border => array() // ) if ( !empty( $value ) && is_array( $value ) && 'options' === $key ) { // bail if the mapping has already been done if ( array_key_exists( 'bg', $value ) ) continue; $new_seks_data[$key] = array(); foreach( $value as $_opt_group => $_opt_group_data ) { if ( 'layout' === $_opt_group ) continue; if ( 'bg_border' === $_opt_group ) { foreach ( $_opt_group_data as $input_id => $val ) { if ( false !== strpos( $input_id , 'bg-' ) ) { $new_seks_data[$key]['bg'][$input_id] = $val; } } } if ( 'spacing' === $_opt_group ) { $new_seks_data[$key]['spacing'] = array( 'pad_marg' => sek_map_compat_1_0_4_to_1_1_0_do_level_spacing_mapping( $_opt_group_data ) ); } } } // end of Level mapping // MODULE mapping else if ( is_array( $value ) && array_key_exists('module_type', $value ) ) { $new_seks_data[$key] = $value; // Assign a default value to the new_value in case we have no matching case $new_value = $value['value']; switch ( $value['module_type'] ) { case 'czr_image_module': if ( is_array( $value['value'] ) ) { // make sure we don't map twice if ( array_key_exists( 'main_settings', $value['value'] ) || array_key_exists( 'borders_corners', $value['value'] ) ) break; $new_value = array( 'main_settings' => array(), 'borders_corners' => array() ); foreach ( $value['value'] as $input_id => $input_data ) { // make sure we don't map twice if ( in_array( $input_id, array( 'main_settings', 'borders_corners' ) ) ) break; switch ($input_id) { case 'border-type': case 'borders': case 'border_radius_css': $new_value['borders_corners'][$input_id] = $input_data; break; default: $new_value['main_settings'][$input_id] = $input_data; break; } } } break; case 'czr_tiny_mce_editor_module': if ( is_array( $value['value'] ) ) { // make sure we don't map twice if ( array_key_exists( 'main_settings', $value['value'] ) || array_key_exists( 'font_settings', $value['value'] ) ) break; $new_value = array( 'main_settings' => array(), 'font_settings' => array() ); foreach ( $value['value'] as $input_id => $input_data ) { // make sure we don't map twice if ( in_array( $input_id, array( 'main_settings', 'font_settings' ) ) ) break; switch ($input_id) { case 'content': case 'h_alignment_css': $new_value['main_settings'][$input_id] = $input_data; break; default: $new_value['font_settings'][$input_id] = $input_data; break; } } } break; default : $new_value = $value['value']; break; } $new_seks_data[$key]['value'] = $new_value; } // End of module mapping // go recursive if possible else if ( is_array($value) ) { $new_seks_data[$key] = sek_walk_levels_and_do_map_compat_1_0_4_to_1_1_0( $value ); } } return $new_seks_data; } // mapping from // [spacing] => Array // ( // [desktop_pad_marg] => Array // ( // [padding-top] => 20 // [padding-bottom] => 20 // ) // [desktop_unit] => em // [tablet_pad_marg] => Array // ( // [padding-left] => 30 // [padding-right] => 30 // ) // [tablet_unit] => percent // ) // to // [spacing] => Array // ( // [pad_marg] => Array // ( // [desktop] => Array // ( // [padding-top] => 20 // [padding-bottom] => 20 // [unit] => em // ) // [tablet] => Array // ( // [padding-right] => 30 // [padding-left] => 30 // [unit] => % // ) // ) // ) function sek_map_compat_1_0_4_to_1_1_0_do_level_spacing_mapping( $old_user_data ) { $old_data_structure = array( 'desktop_pad_marg', 'desktop_unit', 'tablet_pad_marg', 'tablet_unit', 'mobile_pad_marg', 'mobile_unit' ); //sek_error_log('$old_user_data', $old_user_data); $mapped_data = array(); foreach ( $old_data_structure as $old_key ) { if ( false !== strpos( $old_key , 'pad_marg' ) ) { $device = str_replace('_pad_marg', '', $old_key ); if ( array_key_exists( $old_key, $old_user_data ) ) { $mapped_data[$device] = $old_user_data[$old_key]; } } if ( false !== strpos( $old_key , 'unit' ) ) { $device = str_replace('_unit', '', $old_key ); if ( array_key_exists( $old_key, $old_user_data ) ) { $mapped_data[$device] = is_array( $mapped_data[$device] ) ? $mapped_data[$device] : array(); $mapped_data[$device]['unit'] = 'percent' === $old_user_data[$old_key] ? '%' : $old_user_data[$old_key]; } } } return $mapped_data; } ?> array( 'name' => sek_is_cpt_debug_mode() ? __( '[NB debug] skoped section') : __( 'NB skoped section'), 'singular_name' => __( 'NB skoped section') ), 'public' => sek_is_cpt_debug_mode(), 'hierarchical' => false, 'rewrite' => false, 'query_var' => false, 'delete_with_user' => false, 'can_export' => true, //'_builtin' => true, /* internal use only. don't use this when registering your own post type. */ 'supports' => sek_is_cpt_debug_mode() ? array( 'editor', 'title', 'revisions' ) : array( 'title', 'revisions' ), 'capabilities' => array( 'delete_posts' => 'edit_theme_options', 'delete_post' => 'edit_theme_options', 'delete_published_posts' => 'edit_theme_options', 'delete_private_posts' => 'edit_theme_options', 'delete_others_posts' => 'edit_theme_options', 'edit_post' => 'edit_theme_options', 'edit_posts' => 'edit_theme_options', 'edit_others_posts' => 'edit_theme_options', 'edit_published_posts' => 'edit_theme_options', 'read_post' => 'read', 'read_private_posts' => 'read', 'publish_posts' => 'edit_theme_options', ) )); // Returns the id of the post in which the local collection is stored // This option NIMBLE_OPT_SEKTION_POST_INDEX is updated when publishing in the customizer and may also be updated when getting the collection in sek_get_seks_post() // introduced for #799 function sek_get_nb_post_id_from_index( $skope_id ) { $nb_posts_index = get_option(NIMBLE_OPT_SEKTION_POST_INDEX); $option_name = NIMBLE_OPT_PREFIX_FOR_SEKTION_COLLECTION . $skope_id; $post_id = 0; // Backward compat => march 2021, NB introduces a new option 'nimble_posts_index' dedicated to store the NB post_id associated to a skope_id. // For previous user, a backward compatibility code is ran on each load at 'wp_loaded', to transfer all previous options to the new one. // if the transfer went wrong, or if the option 'nimble_posts_index' was deleted, we can attempt to get the post_id from the previous option if ( !is_array( $nb_posts_index ) ) { $post_id = get_option( $option_name ); } else { if ( array_key_exists( $option_name, $nb_posts_index ) ) { $post_id = (int)$nb_posts_index[$option_name]; } } return $post_id; } // Associates a skope_id to a NB post id in the NB post index option // introduced for #799 function sek_set_nb_post_id_in_index( $skope_id, $post_id ) { $nb_posts_index = get_option(NIMBLE_OPT_SEKTION_POST_INDEX); $nb_posts_index = is_array($nb_posts_index) ? $nb_posts_index : []; $option_name = NIMBLE_OPT_PREFIX_FOR_SEKTION_COLLECTION . $skope_id; $nb_posts_index[$option_name] = (int)$post_id; update_option( NIMBLE_OPT_SEKTION_POST_INDEX, $nb_posts_index, 'no'); } // Associates a skope_id to a NB post id in the NB post index option // introduced for #799 function sek_remove_nb_post_id_in_index( $skope_id ) { $nb_posts_index = get_option(NIMBLE_OPT_SEKTION_POST_INDEX); $nb_posts_index = is_array($nb_posts_index) ? $nb_posts_index : []; $option_name = NIMBLE_OPT_PREFIX_FOR_SEKTION_COLLECTION . $skope_id; if ( array_key_exists( $option_name, $nb_posts_index ) ) { unset( $nb_posts_index[$option_name] ); } update_option( NIMBLE_OPT_SEKTION_POST_INDEX, $nb_posts_index, 'no'); } // @return int function sek_get_index_for_api() { $nb_posts_index = get_option(NIMBLE_OPT_SEKTION_POST_INDEX); $nb_posts_index = is_array( $nb_posts_index ) ? $nb_posts_index : []; return count( $nb_posts_index ); } /** * Fetch the `nimble_post_type` post for a given {skope_id} * * @since 4.7.0 * * @param string $stylesheet Optional. A theme object stylesheet name. Defaults to the current theme. * @return WP_Post|null The skope post or null if none exists. */ function sek_get_seks_post( $skope_id = '', $skope_level = 'local' ) { if ( empty( $skope_id ) ) { $skope_id = skp_get_skope_id( $skope_level ); } $cached_seks_posts = Nimble_Manager()->seks_posts; if ( !is_array($cached_seks_posts) ) { sek_error_log( __FUNCTION__ .' => error => $cached_seks_posts must be an array' ); $cached_seks_posts = array(); } if ( !skp_is_customizing() && array_key_exists( $skope_id, $cached_seks_posts ) && !empty( $cached_seks_posts[$skope_id] ) ) { return $cached_seks_posts[$skope_id]; } //sek_error_log('sek_get_seks_post => ' . $skope_id . ' skope level : ' . $skope_level ); $sek_post_query_vars = array( 'post_type' => NIMBLE_CPT, 'post_status' => get_post_stati(),//'publish' 'name' => sanitize_title( NIMBLE_OPT_PREFIX_FOR_SEKTION_COLLECTION . $skope_id ), 'posts_per_page' => 1, 'no_found_rows' => true, 'cache_results' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, 'lazy_load_term_meta' => false, ); $post = null; // First attempt to query the post with its ID stored in the index // If no results or post has been trashed, NB will try to get it with a query by name + update the index of skoped post ids $post_id = sek_get_nb_post_id_from_index( $skope_id ); if ( !is_int( $post_id ) ) { error_log( 'sek_get_seks_post => post_id !is_int() for options => ' . NIMBLE_OPT_PREFIX_FOR_SEKTION_COLLECTION . $skope_id ); } // if the options has not been set yet, it will return (int) 0 // id #1 is already taken by the 'Hello World' post. // skip this check when in NIMBLE_CPT_DEBUG_MODE if ( 1 > $post_id && !( defined( "NIMBLE_CPT_DEBUG_MODE" ) && NIMBLE_CPT_DEBUG_MODE ) ) { //error_log( 'sek_get_seks_post => post_id is not valid for options => ' . NIMBLE_OPT_PREFIX_FOR_SEKTION_COLLECTION . $skope_id ); return; } if ( is_int( $post_id ) && $post_id > 0 ) { $post = get_post( $post_id ); } $no_post_found = !$post || -1 !== $post_id; $post_trashed = !empty($post) && is_object($post) && 'trash' === $post->post_status; // `-1` indicates no post exists; no query necessary. // always query post when in NIMBLE_CPT_DEBUG_MODE if ( $no_post_found || $post_trashed ) { $query = new \WP_Query( $sek_post_query_vars ); $post = $query->post; $post_id = $post ? $post->ID : -1; /* * Cache the lookup. See sek_update_sek_post(). * @todo This should get cleared if a skope post is added/removed. */ sek_set_nb_post_id_in_index( $skope_id, (int)$post_id ); } if ( !skp_is_customizing() ) { $cached_seks_posts[$skope_id] = $post; Nimble_Manager()->seks_posts = $cached_seks_posts; return $cached_seks_posts[$skope_id]; } else { return $post; } } function sek_set_ids( $collection ) { if ( is_array( $collection ) ) { // if ( array_key_exists('level', $collection ) && in_array( $collection['level'], ['section', 'column', 'module'] ) && array_key_exists('id', $collection ) ) { // $collection['id'] = sek_generate_level_guid(); // } foreach( $collection as $key => $data ) { if ( '__rep__me__' === $data && 'id' === $key ) { $collection[$key] = sek_generate_level_guid(); } else if ( is_array( $data ) ) { $collection[$key] = sek_set_ids($data); } } } return $collection; } /** * Fetch the saved collection of sektion for a given skope_id / location * * @since 4.7.0 * * @param string $stylesheet Optional. A theme object stylesheet name. Defaults to the current theme. * @return array => the skope setting items */ function sek_get_skoped_seks( $skope_id = '', $location_id = '', $skope_level = 'local' ) { if ( empty( $skope_id ) ) { $skope_id = skp_get_skope_id( $skope_level ); } $is_global_skope = NIMBLE_GLOBAL_SKOPE_ID === $skope_id; $is_cached = false; $cache_key = 'nimble_get_skoped_seks_' . $skope_id; $cached = wp_cache_get( $cache_key ); // use the cached value when available ( after did_action('wp') ) if ( did_action('wp') ) { if ( is_array( $cached ) ) { $is_cached = true; $seks_data = $cached; } } // If not cached get the seks data from the skoped post if ( !$is_cached ) { $default_collection = sek_get_default_location_model( $skope_id ); // Feb 2021 : filter skope id now // if the current context has no local sektions set and a site template set, replace the skope id by the group skope id // if ( !$is_global_skope ) { // $skope_id = apply_filters( 'nb_set_skope_id_before_caching_local_sektions', $skope_id ); // //sek_error_log('alors local skope id for fetching local sections ?', $skope_id ); // } $seks_data = sek_get_seks_without_group_inheritance( $skope_id ); // March 2021 : added for site templates #478 // Use site template if // - ! global skope // - no local skoped sections // - a site template is defined for this "group" skope if ( 'local' === $skope_level && !$is_global_skope ) { $seks_data = sek_maybe_get_seks_for_group_site_template( $skope_id, $seks_data ); } // normalizes // [ 'collection' => [], 'local_options' => [], "fonts": [], '__inherits_group_skope_tmpl_when_exists__' => true ]; $seks_data = wp_parse_args( $seks_data, $default_collection ); // Maybe add missing registered locations $seks_data = sek_maybe_add_incomplete_locations( $seks_data, $is_global_skope ); // cache now wp_cache_set( $cache_key, $seks_data ); //sek_error_log('/////////////////////////// CACHED for skope ' . $skope_id); }//end if if ( skp_is_customizing() ) { // when customizing, let us filter the value with the 'customized' ones $seks_data = apply_filters( 'sek_get_skoped_seks', $seks_data, $skope_id, $location_id ); if ( 'local' === $skope_level && !$is_global_skope ) { $seks_data = is_array( $seks_data ) ? $seks_data : array(); if ( !array_key_exists( '__inherits_group_skope_tmpl_when_exists__', $seks_data ) ) { // Retro-compat => make sure we set property '__inherits_group_skope_tmpl_when_exists__' to false if it's not set yet, because NB bases group inheritance on it $seks_data['__inherits_group_skope_tmpl_when_exists__'] = false; } $seks_data = sek_maybe_get_seks_for_group_site_template( $skope_id, $seks_data ); } $default_collection = sek_get_default_location_model( $skope_id ); $seks_data = wp_parse_args( $seks_data, $default_collection ); // Maybe add missing registered locations when customizing // December 2020 => needed when importing an entire template $seks_data = sek_maybe_add_incomplete_locations( $seks_data, $is_global_skope ); } // if a location is specified, return specifically the sections of this location if ( array_key_exists( 'collection', $seks_data ) && !empty( $location_id ) ) { // sek_error_log( 'sek_get_skoped_seks() location => ' . $location_id . array_key_exists( 'collection', $seks_data ) ); if ( !array_key_exists( $location_id, sek_get_locations() ) ) { error_log( __FUNCTION__ . ' Error => location ' . $location_id . ' is not registered in the available locations' ); } else { $seks_data = sek_get_level_model( $location_id, $seks_data['collection'] ); } } //sek_error_log( __FUNCTION__ . ' THEERE !'); return 'no_match' === $seks_data ? Nimble_Manager()->default_location_model : $seks_data; } // Return and cache the local or group skope seks data // Without inheritance because not filtered with the group site template content function sek_get_seks_without_group_inheritance( $skope_id ) { if ( empty($skope_id) || !is_string($skope_id) ) { sek_error_log( 'Error missing skope id'); return []; } $cache_key = 'nimble_seks_data_for_skope_' . $skope_id; $cached = wp_cache_get( $cache_key ); if ( is_array($cached) ) { return $cached; } $is_global_skope = NIMBLE_GLOBAL_SKOPE_ID === $skope_id; $seks_data = array(); $post = sek_get_seks_post( $skope_id, $is_global_skope ? 'global' : 'local' );//Cached if ( $post ) { $seks_data = maybe_unserialize( $post->post_content ); $seks_data = is_array( $seks_data ) ? $seks_data : array(); if ( !$is_global_skope && !array_key_exists( '__inherits_group_skope_tmpl_when_exists__', $seks_data ) ) { // Retro-compat => make sure we set property '__inherits_group_skope_tmpl_when_exists__' to false if it's not set yet, because NB bases group inheritance on it $seks_data['__inherits_group_skope_tmpl_when_exists__'] = false; } } // normalizes // [ 'collection' => [], 'local_options' => [], "fonts": [], '__inherits_group_skope_tmpl_when_exists__' => true ]; $default_collection = sek_get_default_location_model( $skope_id ); $seks_data = wp_parse_args( $seks_data, $default_collection ); wp_cache_set( $cache_key, $seks_data ); return $seks_data; } // make sure the locations in the skoped locations tree match the registered locations for the context function sek_maybe_add_incomplete_locations( $seks_data, $is_global_skope ) { // Maybe add missing registered locations $maybe_incomplete_locations = []; foreach( $seks_data['collection'] as $location_data ) { if ( !empty( $location_data['id'] ) ) { $maybe_incomplete_locations[] = $location_data['id']; } } foreach( sek_get_locations() as $loc_id => $params ) { if ( !in_array( $loc_id, $maybe_incomplete_locations ) ) { if ( ( sek_is_global_location( $loc_id ) && $is_global_skope ) || ( !sek_is_global_location( $loc_id ) && !$is_global_skope ) ) { $seks_data['collection'][] = wp_parse_args( [ 'id' => $loc_id ], Nimble_Manager()->default_location_model ); } } } return $seks_data; } /** * Update the `nimble_post_type` post for a given "{$skope_id}" * Inserts a `nimble_post_type` post when one doesn't yet exist. * * @since 4.7.0 * * } * @return WP_Post|WP_Error Post on success, error on failure. */ function sek_update_sek_post( $seks_data, $args = array() ) { $args = wp_parse_args( $args, array( 'skope_id' => '' ) ); if ( !is_array( $seks_data ) ) { error_log( 'sek_update_sek_post => $seks_data is not an array' ); return new \WP_Error( 'sek_update_sek_post => $seks_data is not an array'); } $skope_id = $args['skope_id']; if ( empty( $skope_id ) ) { error_log( 'sek_update_sek_post => empty skope_id' ); return new \WP_Error( 'sek_update_sek_post => empty skope_id'); } $post_title = NIMBLE_OPT_PREFIX_FOR_SEKTION_COLLECTION . $skope_id; //sek_error_log('IN UPDATE SEK_POST ?', $seks_data ); $post_data = array( 'post_title' => $post_title, 'post_name' => sanitize_title( $post_title ), 'post_type' => NIMBLE_CPT, 'post_status' => 'publish', 'post_content' => maybe_serialize( $seks_data ) ); // Update post if it already exists, otherwise create a new one. $post = sek_get_seks_post( $skope_id, NIMBLE_GLOBAL_SKOPE_ID !== $skope_id ? 'local' : 'global' ); if ( $post ) { $post_data['ID'] = $post->ID; $r = wp_update_post( wp_slash( $post_data ), true ); } else { //sek_error_log('IINSERT NEW POST ', $post_title ); $r = wp_insert_post( wp_slash( $post_data ), true ); if ( !is_wp_error( $r ) ) { $post_id = $r;//$r is the post ID sek_set_nb_post_id_in_index( $skope_id, (int)$post_id ); // Trigger creation of a revision. This should be removed once #30854 is resolved. if ( 0 === count( wp_get_post_revisions( $r ) ) ) { wp_save_post_revision( $r ); } } } if ( is_wp_error( $r ) ) { return $r; } return get_post( $r ); } // Introduced March 2021 for #478 // Removes the post id in the skope index + removes the post in DB + remove the stylesheet function sek_remove_seks_post( $skope_id = null ) { if ( is_null( $skope_id ) || empty( $skope_id ) ) { sek_error_log( __FUNCTION__ . ' => error => empty skope_id' ); return new \WP_Error( 'sek_update_sek_post => empty skope_id'); } //$post_title = NIMBLE_OPT_PREFIX_FOR_SEKTION_COLLECTION . $skope_id; $tmpl_post_to_remove = sek_get_seks_post( $skope_id ); //sek_error_log( __FUNCTION__ . ' => so $tmpl_post_to_remove => ' . $skope_id, $tmpl_post_to_remove ); // Remove the associated post id in the skope index sek_remove_nb_post_id_in_index( $skope_id ); // Remove the post in DB if ( $tmpl_post_to_remove && is_object( $tmpl_post_to_remove ) ) { // the CPT is permanently deleted instead of moved to Trash when using wp_trash_post() $r = wp_delete_post( $tmpl_post_to_remove->ID ); if ( is_wp_error( $r ) ) { sek_error_log( __FUNCTION__ . '=> _removal_error', $r ); } } else { // TMPL POST NOT FOUND //sek_error_log( __FUNCTION__ . '=> _tmpl_post_not_found' ); } // Remove the corresponding stylesheet $css_handler_instance = new Sek_Dyn_CSS_Handler( array( 'id' => $skope_id, 'skope_id' => $skope_id, 'mode' => 'delete' )); $css_handler_instance->sek_dyn_css_delete_file(); } ?> experimental for https://github.com/presscustomizr/nimble-builder/issues/478 /* ------------------------------------------------------------------------- * * SITE TEMPLATES OPTIONS HELPERS /* ------------------------------------------------------------------------- */ // @return array of site template info for a given group skope function sek_get_site_tmpl_params_for_skope( $group_skope = null ) { if ( is_null($group_skope) || !is_string($group_skope) || empty($group_skope) ) return; $cache_key = 'nimble_site_tmpl_params_for_skope_'.$group_skope; $cached = wp_cache_get( $cache_key ); if ( false !== $cached ) return $cached; $default_params = [ 'site_tmpl_id' => '_no_site_tmpl_', 'site_tmpl_source' => 'user_tmpl', 'site_tmpl_title' => '' ]; $opts = sek_get_global_option_value( 'site_templates' ); $site_tmpl_params = $default_params; if ( is_array( $opts) && !empty( $opts[$group_skope] ) && is_array($opts[$group_skope]) ) { $site_tmpl_params = wp_parse_args( $opts[$group_skope], $site_tmpl_params ); } // DO SOME CHECKS BEFORE RETURNING THE PARAMS $site_tmpl_id = $site_tmpl_params['site_tmpl_id']; $site_tmpl_source = $site_tmpl_params['site_tmpl_source']; // Check that id is well formed if ( is_null( $site_tmpl_id ) || !is_string( $site_tmpl_id ) ) { sek_error_log( 'Error => invalid tmpl post id', $tmpl_params ); return $default_params; } //sek_error_log('site_templates params ?' . $group_skope, $opts ); // Check that tmpl source is OK if ( !in_array($site_tmpl_source, ['user_tmpl', 'api_tmpl'] ) ) { sek_error_log( 'Error => invalid tmpl source', $tmpl_params ); return $default_params; } wp_cache_set($cache_key, $site_tmpl_params); return $site_tmpl_params; } /* ------------------------------------------------------------------------- * * SITE TEMPLATES SKOPE HELPER /* ------------------------------------------------------------------------- */ // when registering site template global options the suffix '_for_site_tmpl' is added to 'no group skope' scopes : 'skp__search_for_site_tmpl', 'skp__404_for_site_tmpl', 'skp__date_for_site_tmpl' // see sek_get_module_params_for_sek_site_tmpl_pickers() function sek_get_group_skope_for_site_tmpl() { $group_skope = skp_get_skope_id( 'group' ); if ( '_skope_not_set_' === $group_skope ) { $skope_id = skp_get_skope_id(); if ( sek_is_no_group_skope( $skope_id ) ) { $group_skope = $skope_id . '_for_site_tmpl'; } else { if ( defined('NIMBLE_DEV') && NIMBLE_DEV ) { //sek_error_log('group skope could not be set'); } } } return $group_skope; } // @return bool // no group skope are array( 'home', 'search', '404', 'date' ); function sek_is_no_group_skope( $skope_id = null ) { if ( is_null( $skope_id ) ) { $skope_id = skp_get_skope_id(); } $skope_id_without_prefix = str_replace( 'skp__', '', $skope_id ); $skope_with_no_group = skp_get_no_group_skope_list(); return in_array( $skope_id_without_prefix, $skope_with_no_group ); } //@return bool // Tells if the local NB skope has been customized function sek_local_skope_has_been_customized( $skope_id = '', $local_seks_data = null ) { $skope_id = empty( $skope_id ) ? skp_get_skope_id() : $skope_id; if ( NIMBLE_GLOBAL_SKOPE_ID === $skope_id ) { sek_error_log( __FUNCTION__ . ' => error => function should not be used with global skope id' ); return false; } // When the collection is provided use it otherwise get it if ( is_null($local_seks_data) || !is_array($local_seks_data) ) { $local_seks_data = sek_get_skoped_seks( $skope_id ); } // normally, we should get an array from the previous function if ( !is_array( $local_seks_data ) ) return false; // the local skoped data include property '__inherits_group_skope_tmpl_when_exists__' since site template implementation april 2021 // If not, it means that we may have a local customized skoped data if ( is_array($local_seks_data) && !array_key_exists( '__inherits_group_skope_tmpl_when_exists__', $local_seks_data ) ) { sek_error_log( __FUNCTION__ . ' => error => missing property __inherits_group_skope_tmpl_when_exists__' ); return true; } // When a page has not been locally customized, property __inherits_group_skope_tmpl_when_exists__ is true ( @see sek_get_default_location_model() ) // As soon as the main local setting id is modified, __inherits_group_skope_tmpl_when_exists__ is set to false ( see js control::updateAPISetting ) // After a reset case, NB sets __inherits_group_skope_tmpl_when_exists__ back to true ( see js control::resetCollectionSetting ) // Note : If this property is set to true => NB removes the local skope post in Nimble_Collection_Setting::update() return is_array($local_seks_data) && array_key_exists( '__inherits_group_skope_tmpl_when_exists__', $local_seks_data ) && !$local_seks_data['__inherits_group_skope_tmpl_when_exists__']; } //@return bool function sek_is_static_front_page_on_front_and_when_customizing() { if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) { $is_front_page = sek_get_posted_query_param_when_customizing( 'is_front_page' ); } else { $is_front_page = is_front_page(); } return $is_front_page && 'page' == get_option( 'show_on_front' ); } // @return boolean // solves the problem of preventing group template inheritance after a local reset function sek_is_inheritance_locally_disabled() { $local_reset_data = sek_get_local_option_value_without_inheritance( 'local_reset' ); if ( is_array( $local_reset_data ) && array_key_exists( 'inherit_group_scope', $local_reset_data ) && !sek_booleanize_checkbox_val($local_reset_data['inherit_group_scope'] ) ) { return true; } return false; } /* ------------------------------------------------------------------------- * * SITE TEMPLATES CSS /* ------------------------------------------------------------------------- */ // filter declared in inc\sektions\_front_dev_php\8_4_1_sektions_front_class_render_css.php //@hook 'nb_set_skope_id_before_generating_local_front_css' function sek_set_skope_id_before_generating_local_front_css($skope_id) { if ( NIMBLE_GLOBAL_SKOPE_ID === $skope_id ) { sek_error_log( __FUNCTION__ . ' => error => function should not be used with global skope id' ); return $skope_id; } // if is viewing front page, we don't want to inherit 'skp__all_page' scope if ( sek_is_static_front_page_on_front_and_when_customizing() ) return $skope_id; // checkbox 'inherit_group_scope' set to true by default if ( sek_is_inheritance_locally_disabled() ) { return $skope_id; } // When a page has not been locally customized, property __inherits_group_skope_tmpl_when_exists__ is true ( @see sek_get_default_location_model() ) // As soon as the main local setting id is modified, __inherits_group_skope_tmpl_when_exists__ is set to false ( see js control::updateAPISetting ) // After a reset case, NB sets __inherits_group_skope_tmpl_when_exists__ back to true ( see js control:: resetCollectionSetting ) // Note : If this property is set to true => NB removes the local skope post in Nimble_Collection_Setting::update() if ( !sek_local_skope_has_been_customized( $skope_id ) ) { $group_site_tmpl_data = sek_get_group_site_template_data();//<= is cached when called $has_group_skope_template_data = !( !$group_site_tmpl_data || empty($group_site_tmpl_data) ); if ( $has_group_skope_template_data ) { $group_skope = sek_get_group_skope_for_site_tmpl(); if ( !empty($group_skope) && '_skope_not_set_' !== $group_skope ) { $skope_id = $group_skope; } } } return $skope_id; } add_filter( 'nb_set_skope_id_before_generating_local_front_css', '\Nimble\sek_set_skope_id_before_generating_local_front_css'); /* ------------------------------------------------------------------------- * * SITE TEMPLATES CONTENT /* ------------------------------------------------------------------------- */ // Called in sek_get_skoped_seks() function sek_maybe_get_seks_for_group_site_template( $skope_id, $local_seks_data ) { // NB will only inherit group skope for local sektions if ( NIMBLE_GLOBAL_SKOPE_ID === $skope_id ) { sek_error_log( __FUNCTION__ . ' => error => function should not be used with global skope id' ); return $local_seks_data; } // if is viewing front page, we don't want to inherit 'skp__all_page' scope if ( sek_is_static_front_page_on_front_and_when_customizing() ) return $local_seks_data; // checkbox 'inherit_group_scope' set to true by default if ( sek_is_inheritance_locally_disabled() ) { return $local_seks_data; } // When a page has not been locally customized, property __inherits_group_skope_tmpl_when_exists__ is true ( @see sek_get_default_location_model() ) // As soon as the main local setting id is modified, __inherits_group_skope_tmpl_when_exists__ is set to false ( see js control::updateAPISetting ) // After a reset case, NB sets __inherits_group_skope_tmpl_when_exists__ back to true ( see js control:: resetCollectionSetting ) // Note : If this property is set to true => NB removes the local skope post in Nimble_Collection_Setting::update() if ( sek_local_skope_has_been_customized($skope_id, $local_seks_data) ) { return $local_seks_data; } $group_site_tmpl_data = sek_get_group_site_template_data(); if ( !$group_site_tmpl_data || empty($group_site_tmpl_data) ) return $local_seks_data; return $group_site_tmpl_data; } // @return null || array // get and cache the group site template data function sek_get_group_site_template_data( $group_skope = null ) { // When ajaxing while customizing, no need to get the group site template data if ( skp_is_customizing() && defined( 'DOING_AJAX' ) && DOING_AJAX ) return; $group_site_tmpl_data = []; if ( is_null( $group_skope ) ) { $group_skope = sek_get_group_skope_for_site_tmpl(); } $cache_key = 'nimble_group_site_template_data_' . $group_skope; $cached = wp_cache_get( $cache_key ); if ( false !== $cached ) { return $cached; } // Site template params are structured as follow : // [ // 'site_tmpl_id' : '_no_site_tmpl_', // 'site_tmpl_source' : 'user_tmpl', // 'site_tmpl_title' : '' //]; $tmpl_params = sek_get_site_tmpl_params_for_skope( $group_skope ); if ( '_no_site_tmpl_' === $tmpl_params['site_tmpl_id'] ) { wp_cache_set( $cache_key, $group_site_tmpl_data ); return; } $site_tmpl_id = $tmpl_params['site_tmpl_id']; $site_tmpl_source = $tmpl_params['site_tmpl_source']; // Is this group template already saved ? // For example, for pages, there should be a nimble CPT post named nimble___skp__all_page $post = sek_get_seks_post( $group_skope ); // if not, let's insert it if ( !$post ) { $current_tmpl_post = null; $current_tmpl_data = null; switch ($site_tmpl_source) { case 'user_tmpl': $current_tmpl_post = sek_get_saved_tmpl_post( $site_tmpl_id ); if ( $current_tmpl_post ) { $raw_tmpl_data = maybe_unserialize( $current_tmpl_post->post_content ); if ( is_array($raw_tmpl_data) && isset($raw_tmpl_data['data']) && is_array($raw_tmpl_data['data']) && !empty($raw_tmpl_data['data']) ) { $current_tmpl_data = $raw_tmpl_data['data']; $current_tmpl_data = sek_set_ids( $current_tmpl_data ); } } break; case 'api_tmpl': $raw_tmpl_data = sek_get_single_tmpl_api_data( $site_tmpl_id ); if( !is_array( $raw_tmpl_data) || empty( $raw_tmpl_data ) ) { sek_error_log( ' problem when getting template : ' . $site_tmpl_id ); } //sek_error_log( __FUNCTION__ . ' api template collection', $raw_tmpl_data ); if ( !isset($raw_tmpl_data['data'] ) || empty( $raw_tmpl_data['data'] ) ) { sek_error_log( __FUNCTION__ . ' problem => missing or invalid data property for template : ' .$site_tmpl_id, $raw_tmpl_data ); } else { // $tmpl_decoded = $raw_tmpl_data; $raw_tmpl_data['data'] = sek_maybe_import_imgs( $raw_tmpl_data['data'], $do_import_images = true ); //$raw_tmpl_data['img_errors'] = !empty( Nimble_Manager()->img_import_errors ) ? implode(',', Nimble_Manager()->img_import_errors) : array(); $current_tmpl_data = sek_set_ids( $raw_tmpl_data['data'] ); } break; } if( !is_null($current_tmpl_data) ) { //sek_error_log('SITE TEMPLATE => UPDATE OR INSERT GROUP SKOPE POST => ' .$group_skope ); $post = sek_update_sek_post( $current_tmpl_data, [ 'skope_id' => $group_skope ]); } }//if ( !$post ) { if ( $post ) { $group_site_tmpl_data = maybe_unserialize( $post->post_content ); } wp_cache_set( $cache_key, $group_site_tmpl_data ); return $group_site_tmpl_data; } // @return bool function sek_has_group_site_template_data() { $group_skope = sek_get_group_skope_for_site_tmpl(); $cache_key = 'nimble_has_group_site_template_' . $group_skope; $cached = wp_cache_get($cache_key); if ( 'yes' === $cached || 'no' === $cached ) { return 'yes' === $cached; } $group_site_tmpl_data = sek_get_group_site_template_data();//<= is cached when called $has_group_skope_template_data = !( !$group_site_tmpl_data || empty($group_site_tmpl_data) ); wp_cache_set( $cache_key, $has_group_skope_template_data ? 'yes' : 'no' ); return $has_group_skope_template_data; } /* ------------------------------------------------------------------------- * * SITE TEMPLATES SAVE CUSTOMIZER ACTION /* ------------------------------------------------------------------------- */ // Action declared in class Nimble_Options_Setting // When a site template is modified, the following action removes the skoped post + removes the corresponding CSS stylesheet // For example, when the page site template is changed, we need to remove the associated skoped post named 'nimble___skp__all_page' // This post has been inserted when running sek_maybe_get_seks_for_group_site_template(), fired from sek_get_skoped_seks() //@'nb_on_save_customizer_global_options' function sek_on_save_customizer_global_options( $opt_name, $value ) { $current_site_tmpl_opts = sek_get_global_option_value( 'site_templates' ); if ( !is_array( $value ) || !is_array($current_site_tmpl_opts) ) return; // NB stores the site template id as a concatenation of template source + '___' + template name // Ex : user_tmpl___landing-page-for-services $updated_site_templates = isset($value['site_templates']) ? $value['site_templates'] : []; foreach( $current_site_tmpl_opts as $group_skope => $current_tmpl_params ) { if ( !is_array($current_tmpl_params) || !array_key_exists('site_tmpl_id', $current_tmpl_params ) ) continue; $current_tmpl_id = $current_tmpl_params['site_tmpl_id']; $updated_tmpl_params = []; if ( array_key_exists( $group_skope, $updated_site_templates ) ) { $updated_tmpl_params = $updated_site_templates[$group_skope]; } else { //sek_error_log('GROUP SKOPE POST TO REMOVE BECAUSE NO MORE TEMPLATE SET => ' . $group_skope . ' | ' . $current_tmpl_id ); sek_remove_seks_post( $group_skope );//Removes the post id in the skope index + removes the post in DB + remove the stylesheet } if ( is_array($updated_tmpl_params) && array_key_exists('site_tmpl_id', $updated_tmpl_params ) && $updated_tmpl_params['site_tmpl_id'] != $current_tmpl_params['site_tmpl_id'] ) { //sek_error_log('GROUP SKOPE POST TO REMOVE BECAUSE TEMPLATE UPDATED => ' . $group_skope . ' | ' . $updated_site_templates[$group_skope] ); sek_remove_seks_post( $group_skope );//Removes the post id in the skope index + removes the post in DB + remove the stylesheet } } } add_action('nb_on_save_customizer_global_options', '\Nimble\sek_on_save_customizer_global_options', 10, 2); /* ------------------------------------------------------------------------- * * SITE TEMPLATES : UPDATED TEMPLATE IN CUSTOMIZER /* ------------------------------------------------------------------------- */ // Action fired during server ajax callback sek_update_user_tmpl_post // Solves the problem of template synchronization between the group skope post ( in which the chosen template is saved with permanent level ids ), and the current state of the template // Solution => each time a template is updated, NB checks if the template is being used by a group skope // if so, then the group skope post is removed ( along with the index and the css stylesheet ) // // When will the removed skope post be re-inserted ? // next time the group skope will be printed ( for example skp__all_page in a single page ), NB checks if a template is assigned to this group skope, and tries to get the skope post. // If the group skope post is not found, NB attempts to re-insert it //@hook 'nb_on_update_user_tmpl_post' function sek_on_update_or_remove_user_tmpl_post( $site_tmpl_id ) { if ( is_null( $site_tmpl_id ) || !is_string( $site_tmpl_id ) ) return; $site_tmpl_opts = sek_get_global_option_value( 'site_templates' ); if ( !is_array($site_tmpl_opts) ) return; foreach( $site_tmpl_opts as $group_skope => $tmpl_params ) { if ( !is_array($tmpl_params) || !array_key_exists('site_tmpl_id', $tmpl_params ) ) continue; if ( $site_tmpl_id === $tmpl_params['site_tmpl_id'] ) { //sek_error_log('UPDATED OR REMOVED TEMPLATE => REMOVE GROUP SKOPE POST ' . $group_skope . ' for template ' . $tmpl_name ); sek_remove_seks_post( $group_skope );//Removes the post id in the skope index + removes the post in DB + remove the stylesheet } } } add_action('nb_on_update_user_tmpl_post', '\Nimble\sek_on_update_or_remove_user_tmpl_post', 10, 1); add_action('nb_on_remove_saved_tmpl_post', '\Nimble\sek_on_update_or_remove_user_tmpl_post', 10, 1); ?> array( 'name' => sek_is_cpt_debug_mode() ? __( '[NB debug] user prebuilt sections') : __( 'NB user prebuilt sections'), 'singular_name' => __( 'NB User prebuilt sections') ), 'public' => sek_is_cpt_debug_mode(), 'hierarchical' => false, 'rewrite' => false, 'query_var' => false, 'delete_with_user' => false, 'can_export' => true, //'_builtin' => true, /* internal use only. don't use this when registering your own post type. */ 'supports' => sek_is_cpt_debug_mode() ? array( 'editor', 'title', 'revisions' ) : array( 'title', 'revisions' ), 'capabilities' => array( 'delete_posts' => 'edit_theme_options', 'delete_post' => 'edit_theme_options', 'delete_published_posts' => 'edit_theme_options', 'delete_private_posts' => 'edit_theme_options', 'delete_others_posts' => 'edit_theme_options', 'edit_post' => 'edit_theme_options', 'edit_posts' => 'edit_theme_options', 'edit_others_posts' => 'edit_theme_options', 'edit_published_posts' => 'edit_theme_options', 'read_post' => 'read', 'read_private_posts' => 'read', 'publish_posts' => 'edit_theme_options', ) )); /** * Fetch the 'nimble_section' post for a given post_name * * @return WP_Post|null */ function sek_get_saved_section_post( $section_post_name ) { $cache_group = 'nimble_section_post'; $section_post = wp_cache_get( $section_post_name, $cache_group ); // is it cached already ? if ( $section_post && is_object($section_post) && NIMBLE_SECTION_CPT === get_post_type( $section_post->id ) ) { return $section_post; } $section_post_query = new \WP_Query( array( 'post_type' => NIMBLE_SECTION_CPT, 'post_status' => get_post_stati(), 'name' => sanitize_title( $section_post_name ), 'posts_per_page' => 1, 'no_found_rows' => true, 'cache_results' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, 'lazy_load_term_meta' => false, ) ); if ( !empty( $section_post_query->posts ) ) { $section_post = $section_post_query->posts[0]; wp_cache_set( $section_post_name, $section_post, $cache_group ); return $section_post; } return null; } // // @return the saved section data collection // // NOT USED // function sek_get_saved_section_data( $section_post_name ) { // $sek_post = sek_get_saved_section_post( $section_post_name ); // $section_data = array(); // if ( $sek_post ) { // $section_data_decoded = maybe_unserialize( $sek_post->post_content ); // // The section data are described as an array // // array( // // 'title' => '', // // 'description' => '', // // 'id' => '', // // 'type' => 'content',//in the future will be used to differentiate header, content and footer sections // // 'creation_date' => date("Y-m-d H:i:s"), // // 'update_date' => '', // // 'data' => array(),<= this is where we describe the columns and options // // 'nimble_version' => NIMBLE_VERSION // // ) // if ( is_array( $section_data_decoded ) && !empty( $section_data_decoded['data'] ) && is_string( $section_data_decoded['data'] ) ) { // $section_data = json_decode( wp_unslash( $section_data_decoded['data'], true ) ); // } // } // return $section_data; // } // invoked on 'wp_ajax_sek_get_user_saved_sections' // @return an unserialized array of all sections saved by user function sek_get_all_saved_sections() { $sek_post_query_vars = array( 'post_type' => NIMBLE_SECTION_CPT, 'post_status' => 'publish', //'name' => sanitize_title(), 'posts_per_page' => -1, 'no_found_rows' => true, 'cache_results' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, 'lazy_load_term_meta' => false, 'orderby' => 'modified', 'order' => 'DESC' ); $query = new \WP_Query( $sek_post_query_vars ); $collection = array(); if ( is_wp_error( $query ) ) { wp_send_json_error( __FUNCTION__ . '_error_when_querying_nimble_sections' ); } if ( !is_array( $query->posts ) || empty( $query->posts ) ) { return $collection; } foreach ( $query->posts as $post_object ) { $content = maybe_unserialize( $post_object->post_content ); // Structure of $content : // array( // 'data' => $_POST['section_data'],//<= json stringified // 'section_post_name' => ( !empty( $_POST['section_post_name'] ) && is_string( $_POST['section_post_name'] ) ) ? $_POST['section_post_name'] : null, // 'metas' => array( // 'title' => $_POST['section_title'], // 'description' => $_POST['section_description'], // 'skope_id' => $_POST['skope_id'], // 'version' => NIMBLE_VERSION, // // is sent as a string : "__after_header,__before_main_wrapper,loop_start,__before_footer" // 'active_locations' => is_string( $_POST['active_locations'] ) ? explode( ',', $_POST['active_locations'] ) : array(), // 'date' => date("Y-m-d"), // 'theme' => sanitize_title_with_dashes( get_stylesheet() ) // ) // ); if ( !is_array($content) ) { sek_error_log(__FUNCTION__ . ' error in content structure for section post name : ' . $post_object->post_name ); continue; } if ( empty($content['metas']) ) { sek_error_log(__FUNCTION__ . ' error => missing metas for section post name : ' . $post_object->post_name ); continue; } // When updating a section, we only need to return title and description $collection[$post_object->post_name] = array( 'title' => !empty($content['metas']['title']) ? sek_maybe_decode_richtext( $content['metas']['title'] ) : '', 'description' => !empty($content['metas']['description']) ? sek_maybe_decode_richtext( $content['metas']['description'] ) : '', 'last_modified_date' => mysql2date( 'Y-m-d H:i:s', $post_object->post_modified ) ); } return $collection; } // Update the 'nimble_section' post // Inserts a 'nimble_section' post when one doesn't yet exist. // $section_data = array( // 'data' => $_POST['section_data'],//<= json stringified // 'section_post_name' => ( !empty( $_POST['section_post_name'] ) && is_string( $_POST['section_post_name'] ) ) ? $_POST['section_post_name'] : null, // 'metas' => array( // 'title' => $_POST['section_title'], // 'description' => $_POST['section_description'], // 'skope_id' => $_POST['skope_id'], // 'version' => NIMBLE_VERSION, // // is sent as a string : "__after_header,__before_main_wrapper,loop_start,__before_footer" // 'active_locations' => is_string( $_POST['active_locations'] ) ? explode( ',', $_POST['active_locations'] ) : array(), // 'date' => date("Y-m-d"), // 'theme' => sanitize_title_with_dashes( get_stylesheet() ) // ) // ); // @return WP_Post|WP_Error Post on success, error on failure. function sek_update_saved_section_post( $section_data, $is_edit_metas_only_case = false ) { if ( !is_array( $section_data ) ) { sek_error_log( __FUNCTION__ . ' => $section_data is not an array' ); return new \WP_Error( __FUNCTION__ . ' => $section_data is not an array'); } if ( !isset( $section_data['data']) || !isset( $section_data['metas']) ) { sek_error_log( __FUNCTION__ . ' => invalid $section_data' ); return new \WP_Error( __FUNCTION__ . ' => invalid $section_data'); } $section_data = wp_parse_args( $section_data, array( 'data' => array(), 'section_post_name' => null, 'metas' => array( 'title' => '', 'description' => '', 'skope_id' => '', 'version' => NIMBLE_VERSION, //'active_locations' => array(), 'date' => '', 'theme' => '' ) )); // the section post name is provided only when updating $is_update_case = !is_null($section_data['section_post_name']); // $post_name_to_update will be used when user updates an existing section if ( !is_null($section_data['section_post_name']) ) { $section_post_name = $section_data['section_post_name']; } else { $section_post_name = NIMBLE_PREFIX_FOR_SAVED_SECTION . sanitize_title( $section_data['metas']['title'] );//nb_section_my-section-name } // Update the post name now $section_data['section_post_name'] = $section_post_name; // Update post if it already exists, otherwise create a new one. $current_section_post = null; if ( $is_update_case ) { // When this is an update case, we fetch the existing tmpl_post in order to later get its id $current_section_post = sek_get_saved_section_post( $section_post_name ); // if this is an update case + editing metas only, then we use the current content if ( $is_edit_metas_only_case && isset($current_section_post->post_content) ) { //sek_error_log('IS EDIT METAS ONLY ?'); $current_section_data = maybe_unserialize( $current_section_post->post_content ); if ( is_array($current_section_data) && isset($current_section_data['data']) && is_array($current_section_data['data']) && !empty($current_section_data['data']) ) { $section_data['data'] = $current_section_data['data']; } } } // March 2021 : make sure text input are sanitized like in #544 #792 $section_data = sek_sektion_collection_sanitize_cb( $section_data ); $new_or_updated_post_data = array( 'post_title' => $section_post_name, 'post_name' => $section_post_name, 'post_type' => NIMBLE_SECTION_CPT, 'post_status' => 'publish', 'post_content' => maybe_serialize( $section_data ) ); if ( $current_section_post && is_object($current_section_post) ) { $new_or_updated_post_data['ID'] = $current_section_post->ID; $r = wp_update_post( wp_slash( $new_or_updated_post_data ), true ); } else { $r = wp_insert_post( wp_slash( $new_or_updated_post_data ), true ); if ( !is_wp_error( $r ) ) { $post_id = $r;//$r is the post ID // Trigger creation of a revision. This should be removed once #30854 is resolved. if ( 0 === count( wp_get_post_revisions( $r ) ) ) { wp_save_post_revision( $r ); } } } if ( is_wp_error( $r ) ) { return $r; } return get_post( $r ); } ?> array( 'name' => sek_is_cpt_debug_mode() ? __( '[NB debug] user templates') : __( 'NB user templates'), 'singular_name' => __( 'NB user templates') ), 'public' => sek_is_cpt_debug_mode(), 'hierarchical' => false, 'rewrite' => false, 'query_var' => false, 'delete_with_user' => false, 'can_export' => true, //'_builtin' => true, /* internal use only. don't use this when registering your own post type. */ 'supports' => sek_is_cpt_debug_mode() ? array( 'editor', 'title', 'revisions' ) : array( 'title', 'revisions' ), 'capabilities' => array( 'delete_posts' => 'edit_theme_options', 'delete_post' => 'edit_theme_options', 'delete_published_posts' => 'edit_theme_options', 'delete_private_posts' => 'edit_theme_options', 'delete_others_posts' => 'edit_theme_options', 'edit_post' => 'edit_theme_options', 'edit_posts' => 'edit_theme_options', 'edit_others_posts' => 'edit_theme_options', 'edit_published_posts' => 'edit_theme_options', 'read_post' => 'read', 'read_private_posts' => 'read', 'publish_posts' => 'edit_theme_options', ) )); /** * Fetch the 'nimble_template' post for a given post_name * * @return WP_Post|null */ function sek_get_saved_tmpl_post( $tmpl_post_name ) { $cache_group = 'nimble_template_post'; $template_post = wp_cache_get( $tmpl_post_name, $cache_group ); // is it cached already ? if ( $template_post && is_object($template_post) && NIMBLE_TEMPLATE_CPT === get_post_type( $template_post->id ) ) { return $template_post; } $tmpl_post_query = new \WP_Query( array( 'post_type' => NIMBLE_TEMPLATE_CPT, 'post_status' => get_post_stati(), 'name' => sanitize_title( $tmpl_post_name ), 'posts_per_page' => 1, 'no_found_rows' => true, 'cache_results' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, 'lazy_load_term_meta' => false, ) ); if ( !empty( $tmpl_post_query->posts ) ) { $template_post = $tmpl_post_query->posts[0]; wp_cache_set( $tmpl_post_name, $template_post, $cache_group ); return $template_post; } return null; } // // @return the saved template data collection // // NOT USED // function sek_get_saved_template_data( $tmpl_post_name ) { // $sek_post = sek_get_saved_template_post( $tmpl_post_name ); // $tmpl_data = array(); // if ( $sek_post ) { // $tmpl_data_decoded = maybe_unserialize( $sek_post->post_content ); // // The section data are described as an array // // array( // // 'title' => '', // // 'description' => '', // // 'id' => '', // // 'type' => 'content',//in the future will be used to differentiate header, content and footer sections // // 'creation_date' => date("Y-m-d H:i:s"), // // 'update_date' => '', // // 'data' => array(),<= this is where we describe the columns and options // // 'nimble_version' => NIMBLE_VERSION // // ) // if ( is_array( $tmpl_data_decoded ) && !empty( $tmpl_data_decoded['data'] ) && is_string( $tmpl_data_decoded['data'] ) ) { // $tmpl_data = json_decode( wp_unslash( $tmpl_data_decoded['data'], true ) ); // } // } // return $tmpl_data; // } // invoked on 'wp_ajax_sek_get_user_saved_templates' // @return an unserialized array of all templates saved by user function sek_get_all_saved_templates() { $sek_post_query_vars = array( 'post_type' => NIMBLE_TEMPLATE_CPT, 'post_status' => 'publish', //'name' => sanitize_title(), 'posts_per_page' => -1, 'no_found_rows' => true, 'cache_results' => true, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, 'lazy_load_term_meta' => false, 'orderby' => 'modified', 'order' => 'DESC' ); $query = new \WP_Query( $sek_post_query_vars ); $collection = array(); if ( is_wp_error( $query ) ) { wp_send_json_error( __FUNCTION__ . '_error_when_querying_nimble_templates' ); } if ( !is_array( $query->posts ) || empty( $query->posts ) ) { return $collection; } foreach ( $query->posts as $post_object ) { $content = maybe_unserialize( $post_object->post_content ); // sek_error_log( __FUNCTION__ . ' TYPE ?', gettype($post_object->post_content ) ); // sek_error_log( __FUNCTION__ . ' POST OBJECT ?', $post_object->post_content ); // Structure of $content : // array( // 'data' => $_POST['tmpl_data'],//<= json stringified // 'tmpl_post_name' => ( !empty( $_POST['tmpl_post_name'] ) && is_string( $_POST['tmpl_post_name'] ) ) ? $_POST['tmpl_post_name'] : null, // 'metas' => array( // 'title' => $_POST['tmpl_title'], // 'description' => $_POST['tmpl_description'], // 'skope_id' => $_POST['skope_id'], // 'version' => NIMBLE_VERSION, // // is sent as a string : "__after_header,__before_main_wrapper,loop_start,__before_footer" // 'tmpl_locations' => is_string( $_POST['tmpl_locations'] ) ? explode( ',', $_POST['tmpl_locations'] ) : array(), // 'date' => date("Y-m-d"), // 'theme' => sanitize_title_with_dashes( get_stylesheet() ) // ) // ); if ( !is_array($content) ) { sek_error_log(__FUNCTION__ . ' error in content structure for template post name : ' . $post_object->post_name ); continue; } if ( empty($content['metas']) ) { sek_error_log(__FUNCTION__ . ' error => missing metas for template post name : ' . $post_object->post_name ); continue; } // When updating a template, we only need to return title and description $collection[$post_object->post_name] = array( 'title' => !empty($content['metas']['title']) ? sek_maybe_decode_richtext( $content['metas']['title'] ) : '', 'description' => !empty($content['metas']['description']) ? sek_maybe_decode_richtext( $content['metas']['description'] ) : '', 'last_modified_date' => mysql2date( 'Y-m-d H:i:s', $post_object->post_modified ) ); } //sek_error_log('GET ALL SAVED TMPL', $collection ); return $collection; } // invoked on 'wp_ajax_sek_get_all_api_tmpl' // @return an unserialized array of api templates function sek_get_all_api_templates() { $raw_tmpl = sek_get_all_tmpl_api_data(); $collection = []; if( !is_array( $raw_tmpl) ) return $collection; foreach ( $raw_tmpl as $tmpl_cpt_post_name => $metas) { if ( !is_array( $metas ) || empty($metas) ) continue; $metas = wp_parse_args( $metas, [ 'title' => '', 'description' => '', 'date' => '', 'thumb_url' => '', 'is_pro_tmpl' => false, 'demo_url' => false, 'is_site_tmpl' => false ]); $collection[$tmpl_cpt_post_name] = [ 'title' => sek_maybe_decode_richtext( $metas['title'] ), 'description' => sek_maybe_decode_richtext( $metas['description'] ), 'last_modified_date' => mysql2date( 'Y-m-d', $metas['date'] ), 'thumb_url' => !empty( $metas['thumb_url'] ) ? $metas['thumb_url'] : '', 'is_pro_tmpl' => !empty( $metas['is_pro_tmpl'] ) ? $metas['is_pro_tmpl'] : false, 'demo_url' => !empty( $metas['demo_url'] ) ? $metas['demo_url'] : false, 'is_site_tmpl' => array_key_exists('is_site_tmpl', $metas ) && $metas['is_site_tmpl'] ]; } return $collection; } // Update the 'nimble_template' post // Inserts a 'nimble_template' post when one doesn't yet exist. // $tmpl_data = array( // 'data' => $_POST['tmpl_data'],//<= json stringified // 'tmpl_post_name' => ( !empty( $_POST['tmpl_post_name'] ) && is_string( $_POST['tmpl_post_name'] ) ) ? $_POST['tmpl_post_name'] : null, // 'metas' => array( // 'title' => $_POST['tmpl_title'], // 'description' => $_POST['tmpl_description'], // 'skope_id' => $_POST['skope_id'], // 'version' => NIMBLE_VERSION, // // is sent as a string : "__after_header,__before_main_wrapper,loop_start,__before_footer" // 'tmpl_locations' => is_string( $_POST['tmpl_locations'] ) ? explode( ',', $_POST['tmpl_locations'] ) : array(), // 'date' => date("Y-m-d"), // 'theme' => sanitize_title_with_dashes( get_stylesheet() ) // ) // ); // @return WP_Post|WP_Error Post on success, error on failure. function sek_update_user_tmpl_post( $tmpl_data, $is_edit_metas_only_case = false ) { if ( !is_array( $tmpl_data ) ) { sek_error_log( __FUNCTION__ . ' => $tmpl_data is not an array' ); return new \WP_Error( __FUNCTION__ . ' => $tmpl_data is not an array'); } if ( !isset( $tmpl_data['data']) || !isset( $tmpl_data['metas']) ) { sek_error_log( __FUNCTION__ . ' => invalid $tmpl_data' ); return new \WP_Error( __FUNCTION__ . ' => invalid $tmpl_data'); } $tmpl_data = wp_parse_args( $tmpl_data, array( 'data' => array(), 'tmpl_post_name' => null, 'metas' => array( 'title' => '', 'description' => '', 'skope_id' => '', 'version' => NIMBLE_VERSION, 'tmpl_locations' => array(), 'tmpl_header_location' => '', 'tmpl_footer_location' => '', 'date' => '', 'theme' => '', // for api templates 'is_pro_tmpl' => false, 'thumb_url' => '' ) )); // the template post name is provided only when updating $is_update_case = !is_null($tmpl_data['tmpl_post_name']); // $post_name_to_update will be used when user updates an existing template if ( !is_null($tmpl_data['tmpl_post_name']) ) { $tmpl_post_name = $tmpl_data['tmpl_post_name']; } else { $tmpl_post_name = NIMBLE_PREFIX_FOR_SAVED_TMPL . sanitize_title( $tmpl_data['metas']['title'] );//nb_tmpl_my-template-name } //sek_error_log('$tmpl_data??', $tmpl_data ); // Update the post name now $tmpl_data['tmpl_post_name'] = $tmpl_post_name; //sek_error_log('serialized $tmpl_data??', maybe_serialize( $tmpl_data ) ); // Update post if it already exists, otherwise create a new one. $current_tmpl_post = null; if ( $is_update_case ) { // When this is an update case, we fetch the existing tmpl_post in order to later get its id $current_tmpl_post = sek_get_saved_tmpl_post( $tmpl_post_name ); // if this is an update case + editing metas only, then we use the current content if ( $is_edit_metas_only_case && isset($current_tmpl_post->post_content) ) { $current_tmpl_data = maybe_unserialize( $current_tmpl_post->post_content ); if ( is_array($current_tmpl_data) && isset($current_tmpl_data['data']) && is_array($current_tmpl_data['data']) && !empty($current_tmpl_data['data']) ) { $tmpl_data['data'] = $current_tmpl_data['data']; } } } // March 2021 : make sure text input are sanitized like in #544 #792 $tmpl_data = sek_sektion_collection_sanitize_cb( $tmpl_data ); $new_or_updated_post_data = array( 'post_title' => $tmpl_post_name, 'post_name' => $tmpl_post_name, 'post_type' => NIMBLE_TEMPLATE_CPT, 'post_status' => 'publish', 'post_content' => maybe_serialize( $tmpl_data ) ); // sek_error_log( __FUNCTION__ . ' => so $is_edit_metas_only_case ' . $is_edit_metas_only_case ); // sek_error_log( __FUNCTION__ . ' => so $tmpl_data for skope ' . $tmpl_post_name, $current_tmpl_data['data'] ); $r = ''; if ( $current_tmpl_post && is_object($current_tmpl_post) ) { $new_or_updated_post_data['ID'] = $current_tmpl_post->ID; $r = wp_update_post( wp_slash( $new_or_updated_post_data ), true ); } else { $r = wp_insert_post( wp_slash( $new_or_updated_post_data ), true ); if ( !is_wp_error( $r ) ) { $post_id = $r;//$r is the post ID // Trigger creation of a revision. This should be removed once #30854 is resolved. if ( 0 === count( wp_get_post_revisions( $r ) ) ) { wp_save_post_revision( $r ); } } } // Added March 2021 for stie templates #478 do_action('nb_on_update_user_tmpl_post', $tmpl_post_name ); if ( is_wp_error( $r ) ) { return $r; } return get_post( $r ); } ?> check if skp_is_customzing() and DOING_AJAX // - not an insertion of the revision post type => check on $data['post_type'] // Note that the post status can be 'publish', 'draft', 'pending' add_filter( 'wp_insert_post_data', function( $data, $postarr, $unsanitized_postarr ) { global $pagenow; // error_log(' PAGE NOW ?'.$pagenow ); // error_log(' POST TYPE FROM DATA ?'. $data['post_type']); // error_log(' POST STATUS ?'. $data['post_status'] ); // we must be in an single CPT edit screen // prevent processing data when restoring a revision if ( 'post.php' !== $pagenow ) return $data; if ( !sek_is_cpt_debug_mode() || skp_is_customizing() || (defined('DOING_AJAX') && DOING_AJAX) ) return $data; // $data should be An array of slashed, sanitized, and processed post data. // @see wp-includes/post.php if ( !is_array($data) ) return $data; $post_type = 'not_set'; if ( !empty($data['post_type']) ) { $post_type = $data['post_type']; } // make sure we only process nimble CPT post type. Not the 'revision' post types. if ( !in_array( $post_type, [NIMBLE_CPT,NIMBLE_SECTION_CPT,NIMBLE_TEMPLATE_CPT] ) ) return $data; // Stop here if the post is being removed if ( 'trash' == $data['post_status'] ) return $data; //sek_error_log('$data ??', $data); $pre_content = isset($data['post_content']) ? $data['post_content'] : null; //sek_error_log('is SERIALIZED ?', is_serialized( $pre_content )); // Serialized if content has been jsonified in the editor // Important : this check is needed in a scenario when the post has been trashed and is restored. In this case the content is already serialized. if ( isset($pre_content) && !empty($pre_content) && !is_serialized( $pre_content ) ) { $pre_content = json_decode( wp_unslash( $pre_content ), true ); // Check if content is a valid json ? if ( json_last_error() == JSON_ERROR_NONE ) { // if no json error, serialize $data['post_content'] = maybe_serialize( $pre_content ); //sek_error_log('VALID JSON => new post_content ??', $data['post_content'] ); } else { //sek_error_log('INVALID JSON', json_last_error() ); return new \WP_Error( 'db_insert_error', __('Could not insert NB template into the database : invalid JSON'), json_last_error() ); } } return wp_slash($data); }, 10 , 3); ?> ' . $skope_id ); if ( empty( $skope_id ) ) { $skope_id = skp_get_skope_id( $skope_level ); } // We need a valid skope_id if ( defined('DOING_AJAX') && DOING_AJAX && '_skope_not_set_' === $skope_id ) { wp_send_json_error( __FUNCTION__ . ' => invalid skope id' ); } $post_id = sek_get_nb_post_id_from_index($skope_id); $raw_revision_history = array(); if ( -1 !== $post_id ) { $args = array( 'post_parent' => $post_id, // id 'post_type' => 'revision', 'post_status' => 'inherit' ); $raw_revision_history = get_children($args); } $revision_history = array(); if ( is_array( $raw_revision_history ) ) { foreach ($raw_revision_history as $post_id => $post_object ) { $revision_history[$post_id] = $post_object->post_date; } } return $revision_history; } /** * Fetch the revisions of the `nimble_post_type` post for a given revision post id * @param string $skope_id optional * @return string $skope_level optional */ function sek_get_single_post_revision( $post_id = null ) { // We need a valid post_id if ( defined('DOING_AJAX') && DOING_AJAX && ( is_null( $post_id ) || !is_numeric( (int)$post_id ) ) ) { wp_send_json_error( __FUNCTION__ . ' => invalid post id' ); } $post = get_post( (int)$post_id ); if ( is_wp_error( $post ) ) { wp_send_json_error( __FUNCTION__ . ' => post does not exist' ); return; } return maybe_unserialize( $post->post_content ); } ?> $data ) { $setting_data[$k] = sek_sektion_collection_sanitize_cb( $data, $setting_instance ); } } } } //return new \WP_Error( 'required', __( 'Error in a sektion', 'text_doma' ), $setting_data ); return $setting_data; } // Uses the validate_callback function specified on module registration if any // @return validity object function sek_sektion_collection_validate_cb( $validity, $setting_data, $setting_instance = null ) { $validated = true; if ( !is_array( $setting_data ) ) { return $setting_data; } else { if ( !is_array( $setting_data ) ) { return $setting_data; } else { if ( array_key_exists('module_type', $setting_data ) ) { $validation_callback = sek_get_registered_module_type_property( $setting_data['module_type'], 'validate_callback' ); if ( !empty( $validation_callback ) && is_string( $validation_callback ) && function_exists( $validation_callback ) && array_key_exists('value', $setting_data ) ) { $validated = $validation_callback( $setting_data ); } } else { foreach( $setting_data as $k => $data ) { $validated = sek_sektion_collection_validate_cb($validity, $data, $setting_instance); } } } } //return new \WP_Error( 'required', __( 'Error in a sektion', 'text_doma' ), $setting_data ); if ( true !== $validated ) { if ( is_wp_error( $validated ) ) { $validation_msg = $validation_msg->get_error_message(); $validity->add( is_null( $setting_instance ) ? 'nimble_validation_error' : 'nimble_validation_error_in_' . $setting_instance->id, $validation_msg ); } } return $validity; } // @return bool function sek_is_json( $string ){ if ( !is_string( $string ) ) return false; json_decode($string); return (json_last_error() == JSON_ERROR_NONE); } // @return string function sek_maybe_decode_richtext( $string ){ if ( !is_string($string) ) return $string; $json_decoded_candidate = json_decode($string, true); if ( json_last_error() == JSON_ERROR_NONE ) { // https://stackoverflow.com/questions/6465263/how-to-reverse-htmlentities // added to fix regression https://github.com/presscustomizr/nimble-builder/issues/791 $json_decoded_candidate = html_entity_decode($json_decoded_candidate, ENT_QUOTES, get_bloginfo( 'charset' ) ); //sek_error_log('DECODED DECODED ?', $json_decoded_candidate ); return $json_decoded_candidate; } return $string; } // @return string function sek_maybe_encode_richtext( $string ){ if ( !is_string($string) ) return $string; // only encode if not already encoded if ( !sek_is_json($string) ) { // https://stackoverflow.com/questions/6465263/how-to-reverse-htmlentities // added to fix regression https://github.com/presscustomizr/nimble-builder/issues/791 $string = htmlentities($string, ENT_COMPAT, get_bloginfo( 'charset' ) );//reversed with html_entity_decode //$string = wp_encode_emoji( $string ); $string = wp_json_encode($string); //sek_error_log('JSON ENCODED ?', $string ); } return $string; } // Feb 2021 added to fix regression https://github.com/presscustomizr/nimble-builder/issues/791 // Recursive function sek_sniff_and_decode_richtext( $seks_data ) { if ( is_array( $seks_data ) ) { foreach( $seks_data as $key => $data ) { if ( is_array( $data ) ) { $seks_data[$key] = sek_sniff_and_decode_richtext( $data ); } else { if ( is_string($data) ) { $seks_data[$key] = sek_maybe_decode_richtext( $data ); } } } } return $seks_data; } ?>