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 .= '
%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( '', 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; } ?>