first commit

This commit is contained in:
CHIEFSOFT\ameye
2023-12-28 16:20:07 -05:00
commit b114fdf4fa
5377 changed files with 1850677 additions and 0 deletions
@@ -0,0 +1,935 @@
<?php
add_action( 'customize_controls_print_footer_scripts', '\Nimble\sek_print_nimble_input_templates' );
function sek_print_nimble_input_templates() {
// data structure :
// {
// input_type : input_type,
// input_data : input_data,
// input_id : input_id,
// item_model : item_model,
// input_tmpl : wp.template( 'nimble-input___' + input_type )
// }
?>
<script type="text/html" id="tmpl-nimble-input-wrapper">
<# var css_attr = serverControlParams.css_attr,
input_data = data.input_data,
input_type = input_data.input_type,
is_width_100 = true === input_data['width-100'];
// some inputs have a width of 100% even if not specified in the input_data
if ( _.contains( ['color', 'radio', 'textarea'], input_type ) ) {
is_width_100 = true;
}
var width_100_class = is_width_100 ? 'width-100' : '',
hidden_class = 'hidden' === input_type ? 'hidden' : '',
data_transport_attr = !_.isEmpty( input_data.transport ) ? 'data-transport="' + input_data.transport + '"' : '',
input_width = !_.isEmpty( input_data.input_width ) ? input_data.input_width : '';
#>
<div class="{{css_attr.sub_set_wrapper}} {{width_100_class}} {{hidden_class}}" data-input-type="{{input_type}}" <# print(data_transport_attr); #>>
<# if ( input_data.html_before ) { #>
<div class="czr-html-before"><# print(input_data.html_before); #></div>
<# } #>
<# if ( input_data.notice_before_title ) { #>
<span class="czr-notice"><# print(input_data.notice_before_title); #></span><br/>
<# } #>
<# if ( 'hidden' !== input_type ) { #>
<# var title_width = !_.isEmpty( input_data.title_width ) ? input_data.title_width : ''; #>
<div class="customize-control-title {{title_width}}"><# print( input_data.title ); #></div>
<# } #>
<# if ( input_data.notice_before ) { #>
<span class="czr-notice"><# print(input_data.notice_before); #></span>
<# } #>
<?php // nested template, see https://stackoverflow.com/questions/8938841/underscore-js-nested-templates#13649447 ?>
<?php // about print(), see https://underscorejs.org/#template ?>
<div class="czr-input {{input_width}}"><# if ( _.isFunction( data.input_tmpl ) ) { print(data.input_tmpl(data)); } #></div>
<# if ( input_data.notice_after ) { #>
<span class="czr-notice"><# print(input_data.notice_after); #></span>
<# } #>
<# if ( input_data.html_after ) { #>
<div class="czr-html-after"><# print(input_data.html_after); #></div>
<# } #>
</div><?php //css_attr.sub_set_wrapper ?>
</script>
<?php
/* ------------------------------------------------------------------------- *
* PARTS FOR MULTI-ITEMS MODULES
* fixes https://github.com/presscustomizr/nimble-builder/issues/473
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-crud-module-part">
<# var css_attr = serverControlParams.css_attr; #>
<button class="{{css_attr.open_pre_add_btn}}"><?php _e('Add New', 'text_doma'); ?> <span class="fas fa-plus-square"></span></button>
<div class="{{css_attr.pre_add_wrapper}}">
<div class="{{css_attr.pre_add_success}}"><p></p></div>
<div class="{{css_attr.pre_add_item_content}}">
<span class="{{css_attr.cancel_pre_add_btn}} button"><?php _e('Cancel', 'text_doma'); ?></span> <span class="{{css_attr.add_new_btn}} button"><?php _e('Add it', 'text_doma'); ?></span>
</div>
</div>
</script>
<script type="text/html" id="tmpl-nimble-rud-item-part">
<# var css_attr = serverControlParams.css_attr, is_sortable_class ='';
if ( data.is_sortable ) {
is_sortable_class = css_attr.item_sort_handle;
}
#>
<div class="{{css_attr.item_header}} {{is_sortable_class}} czr-custom-model">
<# if ( ( true === data.is_sortable ) ) { #>
<div class="{{css_attr.item_title}} "><h4>{{ data.title }}</h4></div>
<# } else { #>
<div class="{{css_attr.item_title}}"><h4>{{ data.title }}</h4></div>
<# } #>
<div class="{{css_attr.item_btns}}">
<a title="<?php _e('Edit', 'text_doma'); ?>" href="javascript:void(0);" class="fas fa-pencil-alt {{css_attr.edit_view_btn}}"></a>&nbsp;
<# if ( ( true === data.items_are_clonable ) ) { #>
<a title="<?php _e('Clone', 'text_doma'); ?>" href="javascript:void(0);" class="far fa-clone czr-clone-item"></a>&nbsp;
<# } #>
<a title="<?php _e('Remove', 'text_doma'); ?>" href="javascript:void(0);" class="fas fa-trash {{css_attr.display_alert_btn}}"></a>
</div>
<div class="{{css_attr.remove_alert_wrapper}}"></div>
</div>
</script>
<?php
/* ------------------------------------------------------------------------- *
* SUBTEMPLATES
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-subtemplate___range_number">
<?php
// we save the int value + unit
// we want to keep only the numbers when printing the tmpl
// dev note : value.replace(/\D+/g, '') : ''; not working because remove "." which we might use for em for example
?>
<#
var item_model = data.item_model,
input_id = data.input_id,
rawValue = _.has( item_model, input_id ) ? item_model[input_id] : null,
value,
unit;
value = _.isString( rawValue ) ? rawValue.replace(/px|em|%/g,'') : rawValue;
unit = _.isString( rawValue ) ? rawValue.replace(/[0-9]|\.|,/g, '') : 'px';
unit = _.isEmpty( unit ) ? 'px' : unit;
var _step = _.has( data.input_data, 'step' ) ? 'step="' + data.input_data.step + '"' : '',
_saved_unit = _.has( item_model, 'unit' ) ? 'data-unit="' + data.input_data.unit + '"' : '',
_min = _.has( data.input_data, 'min' ) ? 'min="' + data.input_data.min + '"': '',
_max = _.has( data.input_data, 'max' ) ? 'max="' + data.input_data.max + '"': '';
#>
<div class="sek-range-wrapper">
<input data-czrtype="{{input_id}}" type="hidden" data-sek-unit="{{unit}}"/>
<input class="sek-range-input" type="range" <# print(_step); #> <# print(_saved_unit); #> <# print(_min); #> <# print(_max); #>/>
</div>
<div class="sek-number-wrapper">
<input class="sek-pm-input" value="{{value}}" type="number" <# print(_step); #> <# print(_min); #> <# print(_max); #> >
</div>
</script>
<script type="text/html" id="tmpl-nimble-subtemplate___unit_picker">
<div class="sek-unit-wrapper">
<div aria-label="<?php _e('unit', 'text_doma'); ?>" class="sek-ui-button-group" role="group"><button type="button" aria-pressed="false" class="sek-ui-button" title="<?php _e('pixels', 'text_doma'); ?>" data-sek-unit="px">px</button><button type="button" aria-pressed="false" class="sek-ui-button" title="em" data-sek-unit="em">em</button><button type="button" aria-pressed="false" class="sek-ui-button" title="<?php _e('percents', 'text_doma'); ?>" data-sek-unit="%">%</button></div>
</div>
</script>
<script type="text/html" id="tmpl-nimble-subtemplate___number">
<div class="sek-simple-number-wrapper">
<input data-czrtype="{{data.input_id}}" class="sek-pm-input" value="{{value}}" type="number" >
</div>
</script>
<?php
/* ------------------------------------------------------------------------- *
* CODE EDITOR
/* ------------------------------------------------------------------------- */
?>
<?php
// data structure :
// {
// input_type : input_type,
// input_data : input_data,
// input_id : input_id,
// item_model : item_model,
// input_tmpl : wp.template( 'nimble-input___' + input_type )
// }
?>
<script type="text/html" id="tmpl-nimble-input___code_editor">
<#
var item_model = data.item_model,
input_id = data.input_id,
value = _.has( item_model, input_id ) ? item_model[input_id] : null,
code_type = data.input_data.code_type;
#>
<textarea data-czrtype="{{input_id}}" data-editor-code-type="{{code_type}}" class="width-100" name="textarea" rows="10" cols=""></textarea>
</script>
<script type="text/html" id="tmpl-nimble-input___detached_tinymce_editor">
<#
var input_data = data.input_data,
item_model = data.item_model,
input_id = data.input_id,
value = _.has( item_model, input_id ) ? item_model[input_id] : null,
code_type = data.input_data.code_type;
#>
<button type="button" class="button text_editor-button" data-czr-control-id="{{ data.control_id }}" data-czr-input-id="{{input_id}}" data-czr-action="open-tinymce-editor"><?php _e('Edit', 'text_doma'); ?></button>&nbsp;
<button type="button" class="button text_editor-button" data-czr-control-id="{{ data.control_id }}" data-czr-input-id="{{input_id}}" data-czr-action="close-tinymce-editor"><?php _e('Hide editor', 'text_doma'); ?></button>
<input data-czrtype="{{input_id}}" type="hidden" value=""/>
</script>
<script type="text/html" id="tmpl-nimble-input___nimble_tinymce_editor">
<?php
// Added an id attribute for https://github.com/presscustomizr/nimble-builder/issues/403
// needed to instantiate wp.editor.initialize(...)
?>
<#
var item_model = data.item_model,
input_id = data.input_id,
value = _.has( item_model, input_id ) ? item_model[input_id] : null;
#>
<textarea id="textarea-{{input_id}}" data-czrtype="{{input_id}}" class="width-100" name="textarea" rows="10" cols=""></textarea>
</script>
<script type="text/html" id="tmpl-nimble-input___h_alignment">
<#
var input_id = data.input_id;
#>
<div class="sek-h-align-wrapper">
<input data-czrtype="{{input_id}}" type="hidden"/>
<div class="sek-align-icons">
<div data-sek-align="left" title="<?php _e('Align left', 'text_doma'); ?>"><i class="material-icons">format_align_left</i></div>
<div data-sek-align="center" title="<?php _e('Align center', 'text_doma'); ?>"><i class="material-icons">format_align_center</i></div>
<div data-sek-align="right" title="<?php _e('Align right', 'text_doma'); ?>"><i class="material-icons">format_align_right</i></div>
</div>
</div><?php // sek-h-align-wrapper ?>
</script>
<script type="text/html" id="tmpl-nimble-input___h_text_alignment">
<#
var input_id = data.input_id;
#>
<div class="sek-h-align-wrapper">
<input data-czrtype="{{input_id}}" type="hidden"/>
<div class="sek-align-icons">
<div data-sek-align="left" title="<?php _e('Align left', 'text_doma'); ?>"><i class="material-icons">format_align_left</i></div>
<div data-sek-align="center" title="<?php _e('Align center', 'text_doma'); ?>"><i class="material-icons">format_align_center</i></div>
<div data-sek-align="right" title="<?php _e('Align right', 'text_doma'); ?>"><i class="material-icons">format_align_right</i></div>
<div data-sek-align="justify" title="<?php _e('Justified', 'text_doma'); ?>"><i class="material-icons">format_align_justify</i></div>
</div>
</div><?php // sek-h-align-wrapper ?>
</script>
<script type="text/html" id="tmpl-nimble-input___nimblecheck">
<#
var input_id = data.input_id,
item_model = data.item_model,
value = _.has( item_model, input_id ) ? item_model[input_id] : false,
_checked = ( false != value ) ? "checked=checked" : '',
_uniqueId = wp.customize.czr_sektions.guid();
#>
<div class="nimblecheck-wrap">
<input id="nimblecheck-{{_uniqueId}}" data-czrtype="{{input_id}}" type="checkbox" <# print(_checked); #> class="nimblecheck-input">
<label for="nimblecheck-{{_uniqueId}}" class="nimblecheck-label">{{sektionsLocalizedData.i18n['Switch']}}</label>
</div>
</script>
<?php
/* ------------------------------------------------------------------------- *
* ALPHA COLOR
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___wp_color_alpha">
<#
var item_model = data.item_model,
input_id = data.input_id,
value = _.has( item_model, input_id ) ? item_model[input_id] : null;
#>
<input data-czrtype="{{data.input_id}}" class="width-100" data-alpha="true" type="text" value="{{value}}"></input>
</script>
<?php
/* ------------------------------------------------------------------------- *
* SIMPLE SELECT : USED FOR SELECT, FONT PICKER, ICON PICKER, ...
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___simpleselect">
<select data-czrtype="{{data.input_id}}"></select>
</script>
<?php
/* ------------------------------------------------------------------------- *
* SIMPLE SELECT WITH DEVICE SWITCHER
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___simpleselect_deviceswitcher">
<input data-czrtype="{{data.input_id}}" type="hidden"/>
<select></select>
</script>
<?php
/* ------------------------------------------------------------------------- *
* NUMBER
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___number_simple">
<#
var number_tmpl = wp.customize.CZR_Helpers.getInputSubTemplate( 'number' );
if ( _.isFunction( number_tmpl ) ) { print( number_tmpl( data ) ); }
#>
</script>
<?php
/* ------------------------------------------------------------------------- *
* RANGE
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___range_simple">
<div class="sek-range-with-unit-picker-wrapper sek-no-unit-picker">
<#
var range_tmpl = wp.customize.CZR_Helpers.getInputSubTemplate( 'range_number' );
if ( _.isFunction( range_tmpl ) ) { print( range_tmpl( data ) ); }
#>
</div>
</script>
<script type="text/html" id="tmpl-nimble-input___range_with_unit_picker">
<div class="sek-range-with-unit-picker-wrapper">
<#
var range_tmpl = wp.customize.CZR_Helpers.getInputSubTemplate( 'range_number' );
if ( _.isFunction( range_tmpl ) ) { print( range_tmpl( data ) ); }
var unit_tmpl = wp.customize.CZR_Helpers.getInputSubTemplate( 'unit_picker' );
if ( _.isFunction( unit_tmpl ) ) { print( unit_tmpl( data ) ); }
#>
</div>
</script>
<?php
/* ------------------------------------------------------------------------- *
* SPACING
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___spacing">
<input data-czrtype="{{data.input_id}}" type="hidden"/>
<div class="sek-spacing-wrapper">
<div class="sek-pad-marg-inner">
<div class="sek-pm-top-bottom-wrap sek-flex-justify-center">
<div class="sek-flex-center-stretch" data-sek-spacing="margin-top" title="<?php _e('Margin top', 'text_doma'); ?>">
<div class="sek-pm-input-parent">
<input class="sek-pm-input" value="" type="number" >
</div>
</div>
</div>
<div class="sek-pm-middle-wrap sek-flex-justify-center">
<div class="sek-flex-center-stretch sek-pm-margin-left" data-sek-spacing="margin-left" title="<?php _e('Margin left', 'text_doma'); ?>">
<div class="sek-pm-input-parent">
<input class="sek-pm-input" value="" type="number" >
</div>
</div>
<div class="sek-pm-padding-wrapper">
<div class="sek-flex-justify-center">
<div class="sek-flex-center-stretch" data-sek-spacing="padding-top" title="<?php _e('Padding top', 'text_doma'); ?>">
<div class="sek-pm-input-parent">
<input class="sek-pm-input" value="" type="number" >
</div>
</div>
</div>
<div class="sek-flex-justify-center sek-flex-space-between">
<div class="sek-flex-center-stretch" data-sek-spacing="padding-left" title="<?php _e('Padding left', 'text_doma'); ?>">
<div class="sek-pm-input-parent">
<input class="sek-pm-input" value="" type="number" >
</div>
</div>
<div class="sek-flex-center-stretch" data-sek-spacing="padding-right" title="<?php _e('Padding right', 'text_doma'); ?>">
<div class="sek-pm-input-parent">
<input class="sek-pm-input" value="" type="number" >
</div>
</div>
</div>
<div class="sek-flex-justify-center">
<div class="sek-flex-center-stretch" data-sek-spacing="padding-bottom" title="<?php _e('Padding bottom', 'text_doma'); ?>">
<div class="sek-pm-input-parent">
<input class="sek-pm-input" value="" type="number" >
</div>
</div>
</div>
</div>
<div class="sek-flex-center-stretch sek-pm-margin-right" data-sek-spacing="margin-right" title="<?php _e('Margin right', 'text_doma'); ?>">
<div class="sek-pm-input-parent">
<input class="sek-pm-input" value="" type="number" >
</div>
</div>
</div>
<div class="sek-pm-top-bottom-wrap sek-flex-justify-center">
<div class="sek-flex-center-stretch" data-sek-spacing="margin-bottom" title="<?php _e('Margin bottom', 'text_doma'); ?>">
<div class="sek-pm-input-parent">
<input class="sek-pm-input" value="" type="number" >
</div>
</div>
</div>
</div><?php //sek-pad-marg-inner ?>
<#
var unit_tmpl = wp.customize.CZR_Helpers.getInputSubTemplate( 'unit_picker' );
if ( _.isFunction( unit_tmpl ) ) { print( unit_tmpl( data ) ); }
#>
<div class="reset-spacing-wrap"><span class="sek-do-reset"><?php _e('Reset all spacing', 'text_doma' ); ?></span></div>
</div><?php // sek-spacing-wrapper ?>
</script>
<?php
/* ------------------------------------------------------------------------- *
* TEXT
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___text">
<# var input_data = data.input_data; #>
<input data-czrtype="{{data.input_id}}" type="text" value="" placeholder="<# print(input_data.placeholder); #>"></input>
</script>
<?php
/* ------------------------------------------------------------------------- *
* CONTENT PICKER
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___content_picker">
<span data-czrtype="{{data.input_id}}"></span>
</script>
<?php
/* ------------------------------------------------------------------------- *
* UPLOAD
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___upload">
<input data-czrtype="{{data.input_id}}" type="hidden"/>
<div class="{{serverControlParams.css_attr.img_upload_container}}"></div>
</script>
<?php
/* ------------------------------------------------------------------------- *
* BORDERS
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___borders">
<div class="sek-borders">
<div class="sek-border-type-wrapper">
<div aria-label="unit" class="sek-ui-button-group" role="group"><button type="button" aria-pressed="true" class="sek-ui-button is-selected" title="<?php _e('All', 'text_doma'); ?>" data-sek-border-type="_all_"><?php _e('All', 'text_doma'); ?></button><button type="button" aria-pressed="false" class="sek-ui-button" title="<?php _e('Left', 'text_doma'); ?>" data-sek-border-type="left"><?php _e('Left', 'text_doma'); ?></button><button type="button" aria-pressed="false" class="sek-ui-button" title="<?php _e('Top', 'text_doma'); ?>" data-sek-border-type="top"><?php _e('Top', 'text_doma'); ?></button><button type="button" aria-pressed="false" class="sek-ui-button" title="<?php _e('Right', 'text_doma'); ?>" data-sek-border-type="right"><?php _e('Right', 'text_doma'); ?></button><button type="button" aria-pressed="false" class="sek-ui-button" title="<?php _e('Bottom', 'text_doma'); ?>" data-sek-border-type="bottom"><?php _e('Bottom', 'text_doma'); ?></button></div>
</div>
<div class="sek-range-unit-wrapper">
<#
var range_tmpl = wp.customize.CZR_Helpers.getInputSubTemplate( 'range_number' );
if ( _.isFunction( range_tmpl ) ) { print( range_tmpl( data ) ); }
var unit_tmpl = wp.customize.CZR_Helpers.getInputSubTemplate( 'unit_picker' );
if ( _.isFunction( unit_tmpl ) ) { print( unit_tmpl( data ) ); }
#>
</div>
<div class="sek-color-wrapper">
<div class="sek-color-picker"><input class="sek-alpha-color-input" data-alpha="true" type="text" value=""/></div>
<div class="sek-reset-button"><button type="button" class="button sek-reset-button sek-float-right"><?php _e('Reset', 'text_doma'); ?></button></div>
</div>
</div>
</script>
<?php
/* ------------------------------------------------------------------------- *
* BORDER RADIUS
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___border_radius">
<div class="sek-borders">
<div class="sek-border-type-wrapper">
<div aria-label="unit" class="sek-ui-button-group sek-float-left" role="group"><button type="button" aria-pressed="true" class="sek-ui-button is-selected" title="<?php _e('All', 'text_doma'); ?>" data-sek-radius-type="_all_"><?php _e('All', 'text_doma'); ?></button><button type="button" aria-pressed="false" class="sek-ui-button" title="<?php _e('Top left', 'text_doma'); ?>" data-sek-radius-type="top_left"><i class="material-icons">border_style</i></button><button type="button" aria-pressed="false" class="sek-ui-button" title="<?php _e('Top right', 'text_doma'); ?>" data-sek-radius-type="top_right"><i class="material-icons">border_style</i></button><button type="button" aria-pressed="false" class="sek-ui-button" title="<?php _e('Bottom right', 'text_doma'); ?>" data-sek-radius-type="bottom_right"><i class="material-icons">border_style</i></button><button type="button" aria-pressed="false" class="sek-ui-button" title="<?php _e('Bottom left', 'text_doma'); ?>" data-sek-radius-type="bottom_left"><i class="material-icons">border_style</i></button></div>
<div class="sek-reset-button"><button type="button" class="button sek-reset-button sek-float-right"><?php _e('Reset', 'text_doma'); ?></button></div>
</div>
<div class="sek-range-unit-wrapper">
<#
var range_tmpl = wp.customize.CZR_Helpers.getInputSubTemplate( 'range_number' );
if ( _.isFunction( range_tmpl ) ) { print( range_tmpl( data ) ); }
var unit_tmpl = wp.customize.CZR_Helpers.getInputSubTemplate( 'unit_picker' );
if ( _.isFunction( unit_tmpl ) ) { print( unit_tmpl( data ) ); }
#>
</div>
</div>
</script>
<?php
/* ------------------------------------------------------------------------- *
* MODULE OPTION SWITCHER
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___module_option_switcher">
<input data-czrtype="{{data.input_id}}" type="hidden"/>
<div class="sek-content-type-wrapper">
<div aria-label="<?php _e('Option type', 'text_doma'); ?>" class="sek-ui-button-group" role="group">
<button type="button" aria-pressed="false" class="sek-ui-button" title="<?php _e('Module Content', 'text_doma'); ?>" data-sek-option-type="content"><span class="sek-wrap-opt-switch-btn"><i class="material-icons">create</i><span><?php _e('Module Content', 'text_doma'); ?></span></span></button>
<button type="button" aria-pressed="false" class="sek-ui-button" title="<?php _e('Module Settings', 'text_doma'); ?>" data-sek-option-type="settings"><span class="sek-wrap-opt-switch-btn"><i class="material-icons">tune</i><span><?php _e('Module Settings', 'text_doma'); ?></span></span></button>
</div>
</div>
</script>
<?php
/* ------------------------------------------------------------------------- *
* CONTENT SWITCHER
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___content_type_switcher">
<input data-czrtype="{{data.input_id}}" type="hidden"/>
<div class="sek-content-type-wrapper">
<div aria-label="<?php _e('Content type', 'text_doma'); ?>" class="sek-ui-button-group" role="group">
<button type="button" aria-pressed="false" class="sek-ui-button" title="<?php _e('Pick a section', 'text_doma'); ?>" data-sek-content-type="section"><?php _e('Pick a section', 'text_doma'); ?></button>
<button type="button" aria-pressed="false" class="sek-ui-button" title="<?php _e('Pick a module', 'text_doma'); ?>" data-sek-content-type="module"><?php _e('Pick a module', 'text_doma'); ?></button>
<button type="button" aria-pressed="false" class="sek-ui-button" title="<?php _e('Pick a template', 'text_doma'); ?>" data-sek-content-type="template"><?php _e('Pick a template', 'text_doma'); ?>&nbsp;<span class="sek-new-label"><?php _e('New!', 'text_doma'); ?></span></button>
</div>
</div>
</script>
<?php
/* ------------------------------------------------------------------------- *
* MODULE PICKER
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___module_picker">
<input data-czrtype="{{data.input_id}}" type="hidden"/>
<div class="sek-content-type-wrapper">
<#
var icon_img_html = '<i style="color:red">Missing Icon</i>', icon_img_src;
_.each( sektionsLocalizedData.moduleCollection, function( rawModData ) {
//normalizes the module params
var modData = jQuery.extend( true, {}, rawModData ),
defaultModParams = {
'content-type' : 'module',
'content-id' : '',
'title' : '',
'icon' : '',
'font_icon' : '',
'active' : true,
'is_pro' : false
},
modData = jQuery.extend( defaultModParams, modData );
var _assets_version = "<?php echo esc_attr(NIMBLE_ASSETS_VERSION); ?>";
if ( !_.isEmpty( modData['icon'] ) ) {
if ( 'http' === modData['icon'].substring(0, 4) ) {
icon_img_src = modData['icon'];
} else {
icon_img_src = sektionsLocalizedData.moduleIconPath + modData['icon'];
}
icon_img_src = icon_img_src + '?v=' + _assets_version;
icon_img_html = '<img draggable="false" title="' + modData['title'] + '" alt="' + modData['title'] + '" class="nimble-module-icons" src="' + icon_img_src + '"/>';
} else if ( !_.isEmpty( modData['font_icon'] ) ) {
icon_img_html = modData['font_icon'];
}
var title_attr = "<?php _e('Drag and drop or double-click to insert in your chosen target element.', 'text_doma'); ?>",
font_icon_class = !_.isEmpty( modData['font_icon'] ) ? 'is-font-icon' : '',
is_draggable = true !== modData['active'] ? 'false' : 'true',
is_pro_module = modData['is_pro'] ? 'yes' : 'no';
if ( true !== modData['active'] ) {
if ( modData['is_pro'] ) {
title_attr = "<?php _e('Pro feature', 'text_doma'); ?>";
} else {
title_attr = "<?php _e('Available soon ! This module is currently in beta, you can activate it in Site Wide Options > Beta features', 'text_doma'); ?>";
}
}
// "data-sek-eligible-for-module-dropzones" was introduced for https://github.com/presscustomizr/nimble-builder/issues/540
#>
<div draggable="{{is_draggable}}" data-sek-eligible-for-module-dropzones="true" data-sek-content-type="{{modData['content-type']}}" data-sek-content-id="{{modData['content-id']}}" title="{{title_attr}}" data-sek-is-pro-module="{{is_pro_module}}"><div class="sek-module-icon {{font_icon_class}}"><# print(icon_img_html); #></div><div class="sek-module-title"><div class="sek-centered-module-title">{{modData['title']}}</div></div>
<#
if ( 'yes' === is_pro_module ) {
var pro_img_html = '<div class="sek-is-pro"><img src="' + sektionsLocalizedData.czrAssetsPath + 'sek/img/pro_orange.svg" alt="Pro feature"/></div>';
print(pro_img_html);
}
#>
</div>
<#
});//_.each
#>
</div>
</script>
<?php
/* ------------------------------------------------------------------------- *
* SECTION PICKER
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___section_picker">
<input data-czrtype="{{data.input_id}}" type="hidden"/>
<div class="sek-content-type-wrapper">
<#
// June 2020 : the section collection is passed only when rendering pre-built sections
// @see sek_register_prebuilt_section_modules() and sek_get_sections_registration_params()
// For user saved sections, the rendering is done in javascript, not here
// @see @see _dev_control/modules/ui/_10_0_0_UI_module_and_section_pickers.js
var section_collection = ( data.input_data && data.input_data.section_collection ) ? data.input_data.section_collection : [];
// if ( _.isEmpty( section_collection ) ) {
// wp.customize.errare('Error in js template tmpl-nimble-input___section_picker => missing section collection');
// return;
// }
var img_version = sektionsLocalizedData.isDevMode ? Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1) : sektionsLocalizedData.nimbleVersion;
// FOR PREBUILT SECTIONS ONLY, user sections are rendered in javascript @see _dev_control/modules/ui/_10_0_0_UI_module_and_section_pickers.js
_.each( section_collection, function( rawSecParams ) {
//normalizes the params
var section_type = 'content',
secParams = jQuery.extend( true, {}, rawSecParams ),
defaultParams = {
'content-id' : '',
'thumb' : '',
'title' : '',
'section_type' : '',
'height': '',
'active' : true,
'is_pro' : false,
'demo_url' : false
},
secParams = jQuery.extend( defaultParams, secParams );
if ( !_.isEmpty( secParams['section_type'] ) ) {
section_type = secParams['section_type'];
}
var thumbUrl = [ sektionsLocalizedData.baseUrl , '/assets/img/section_assets/thumbs/', secParams['thumb'] , '?ver=' , img_version ].join(''),
styleAttr = 'background: url(' + thumbUrl + ') 50% 50% / cover no-repeat;',
is_draggable = true !== secParams['active'] ? 'false' : 'true',
is_pro_section = secParams['is_pro'] ? 'yes' : 'no';
if ( !_.isEmpty(secParams['height']) ) {
styleAttr = styleAttr + 'height:' + secParams['height'] + ';';
}
#>
<div draggable="{{is_draggable}}" data-sek-content-type="preset_section" data-sek-content-id="{{secParams['content-id']}}" style="<# print(styleAttr); #>" title="{{secParams['title']}}" data-sek-section-type="{{section_type}}" data-sek-is-pro-section="{{is_pro_section}}"><div class="sek-overlay"></div>
<#
if ( 'yes' === is_pro_section ) {
var pro_img_html = '<div class="sek-is-pro"><img src="' + sektionsLocalizedData.czrAssetsPath + 'sek/img/pro_orange.svg" alt="Pro feature"/></div>';
print(pro_img_html);
}
var demo_title = "<?php _e('View in live demo', 'text_doma'); ?>";
if ( secParams['demo_url'] && -1 === secParams['demo_url'].indexOf('http') ) { #>
<div class="sek-demo-link"><a href="https://nimblebuilder.com/nimble-builder-sections?utm_source=usersite&amp;utm_medium=link&amp;utm_campaign=section_demos{{secParams['demo_url']}}" target="_blank" rel="noopener noreferrer">{{demo_title}} <i class="fas fa-external-link-alt"></i></a></div>
<# } else if ( secParams['demo_url'] ) { #>
<div class="sek-demo-link"><a href="{{secParams['demo_url']}}" target="_blank" rel="noopener noreferrer">{{demo_title}} <i class="fas fa-external-link-alt"></i></a></div>
<# } #>
</div>
<#
});//_.each
#>
</div>
</script>
<?php
/* ------------------------------------------------------------------------- *
* BACKGROUND POSITION INPUT
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___bg_position">
<div class="sek-bg-pos-wrapper">
<input data-czrtype="{{data.input_id}}" type="hidden"/>
<label class="sek-bg-pos">
<input type="radio" name="sek-bg-pos" value="top_left">
<span>
<svg width="24" height="24">
<path id="sek-pth" fill-rule="evenodd" d="M14.96 16v-1h-1v-1h-1v-1h-1v-1h-1v-1.001h-1V14h-1v-4-1h5v1h-3v.938h1v.999h1v1h1v1.001h1v1h1V16h-1z" class="sek-svg-bg-pos">
</path>
</svg>
</span>
</label>
<label class="sek-bg-pos">
<input type="radio" name="sek-bg-pos" value="top">
<span>
<svg width="24" height="24">
<path id="sek-pth" fill-rule="evenodd" d="M14.969 12v-1h-1v-1h-1v7h-1v-7h-1v1h-1v1h-1v-1.062h1V9.937h1v-1h1V8h1v.937h1v1h1v1.001h1V12h-1z" class="sek-svg-bg-pos">
</path>
</svg>
</span>
</label>
<label class="sek-bg-pos">
<input type="radio" name="sek-bg-pos" value="top_right">
<span>
<svg width="24" height="24">
<path id="sek-pth" fill-rule="evenodd" d="M9.969 16v-1h1v-1h1v-1h1v-1h1v-1.001h1V14h1v-4-1h-1-4v1h3v.938h-1v.999h-1v1h-1v1.001h-1v1h-1V16h1z" class="sek-svg-bg-pos">
</path>
</svg>
</span>
</label>
<label class="sek-bg-pos">
<input type="radio" name="sek-bg-pos" value="left">
<span>
<svg width="24" height="24">
<path id="sek-pth" fill-rule="evenodd" d="M11.469 9.5h-1v1h-1v1h7v1h-7v1h1v1h1v1h-1.063v-1h-1v-1h-1v-1h-.937v-1h.937v-1h1v-1h1v-1h1.063v1z" class="sek-svg-bg-pos">
</path>
</svg>
</span>
</label>
<label class="sek-bg-pos">
<input type="radio" name="sek-bg-pos" value="center">
<span>
<svg width="24" height="24">
<path id="sek-pth" fill-rule="evenodd" d="M12 9a3 3 0 1 1 0 6 3 3 0 0 1 0-6z" class="sek-svg-bg-pos">
</path>
</svg>
</span>
</label>
<label class="sek-bg-pos">
<input type="radio" name="sek-bg-pos" value="right">
<span>
<svg width="24" height="24">
<path id="sek-pth" fill-rule="evenodd" d="M12.469 14.5h1v-1h1v-1h-7v-1h7v-1h-1v-1h-1v-1h1.062v1h1v1h1v1h.938v1h-.938v1h-1v1h-1v1h-1.062v-1z" class="sek-svg-bg-pos">
</path>
</svg>
</span>
</label>
<label class="sek-bg-pos">
<input type="radio" name="sek-bg-pos" value="bottom_left">
<span>
<svg width="24" height="24">
<path id="sek-pth" fill-rule="evenodd" d="M14.969 9v1h-1v1h-1v1h-1v1h-1v1.001h-1V11h-1v5h5v-1h-3v-.938h1v-.999h1v-1h1v-1.001h1v-1h1V9h-1z" class="sek-svg-bg-pos">
</path>
</svg>
</span>
</label>
<label class="sek-bg-pos">
<input type="radio" name="sek-bg-pos" value="bottom">
<span>
<svg width="24" height="24">
<path id="sek-pth" fill-rule="evenodd" d="M9.969 13v1h1v1h1V8h1v7h1v-1h1v-1h1v1.063h-1v.999h-1v1.001h-1V17h-1v-.937h-1v-1.001h-1v-.999h-1V13h1z" class="sek-svg-bg-pos">
</path>
</svg>
</span>
</label>
<label class="sek-bg-pos">
<input type="radio" name="sek-bg-pos" value="bottom_right">
<span>
<svg width="24" height="24">
<path id="sek-pth" fill-rule="evenodd" d="M9.969 9v1h1v1h1v1h1v1h1v1.001h1V11h1v5h-1-4v-1h3v-.938h-1v-.999h-1v-1h-1v-1.001h-1v-1h-1V9h1z" class="sek-svg-bg-pos">
</path>
</svg>
</span>
</label>
</div><?php // sek-bg-pos-wrapper ?>
</script>
<?php
/* ------------------------------------------------------------------------- *
* BUTTON CHOICE
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___buttons_choice">
<div class="sek-button-choice-wrapper">
<input data-czrtype="{{data.input_id}}" type="hidden"/>
<div aria-label="<?php _e('unit', 'text_doma'); ?>" class="sek-ui-button-group sek-float-right" role="group">
<#
var input_data = data.input_data;
if ( _.isEmpty( input_data.choices ) || !_.isObject( input_data.choices ) ) {
wp.customize.errare( 'Error in buttons_choice js tmpl => missing or invalid input_data.choices');
} else {
_.each( input_data.choices, function( label, choice ) {
#><button type="button" aria-pressed="false" class="sek-ui-button" title="{{label}}" data-sek-choice="{{choice}}">{{label}}</button><#
});
}
#>
</div>
</div>
</script>
<?php
/* ------------------------------------------------------------------------- *
* MULTISELECT, CATEGORY PICKER
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___multiselect">
<select multiple="multiple" data-czrtype="{{data.input_id}}"></select>
</script>
<?php
/* ------------------------------------------------------------------------- *
* GRID LAYOUT
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___grid_layout">
<div class="sek-grid-layout-wrapper">
<input data-czrtype="{{data.input_id}}" type="hidden"/>
<div class="sek-grid-icons">
<div data-sek-grid-layout="list" title="<?php _e('List layout', 'text_doma'); ?>"><i class="material-icons">view_list</i></div>
<div data-sek-grid-layout="grid" title="<?php _e('Grid layout', 'text_doma'); ?>"><i class="material-icons">view_module</i></div>
</div>
</div>
</script>
<?php
/* ------------------------------------------------------------------------- *
* VERTICAL ALIGNMENT
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___v_alignment">
<div class="sek-v-align-wrapper">
<input data-czrtype="{{data.input_id}}" type="hidden"/>
<div class="sek-align-icons">
<div data-sek-align="top" title="<?php _e('Align top', 'text_doma'); ?>"><i class="material-icons">vertical_align_top</i></div>
<div data-sek-align="center" title="<?php _e('Align center', 'text_doma'); ?>"><i class="material-icons">vertical_align_center</i></div>
<div data-sek-align="bottom" title="<?php _e('Align bottom', 'text_doma'); ?>"><i class="material-icons">vertical_align_bottom</i></div>
</div>
</div>
</script>
<?php
/* ------------------------------------------------------------------------- *
* REMOVE BUTTON
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___reset_button">
<div class="sek-button-choice-wrapper">
<input data-czrtype="{{data.input_id}}" type="hidden"/>
<button type="button" aria-pressed="false" class="sek-ui-button sek-float-right" title="<?php _e('Remove now', 'text_doma'); ?>" data-sek-reset-scope="{{data.input_data.scope}}"><?php _e('Remove now', 'text_doma'); ?></button>
</div>
</script>
<?php
/* ------------------------------------------------------------------------- *
* REFRESH PREVIEW BUTTON
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___refresh_preview_button">
<div class="sek-button-choice-wrapper">
<input data-czrtype="{{data.input_id}}" type="hidden"/>
<button type="button" aria-pressed="false" class="sek-refresh-button sek-float-right button button-primary" title="<?php _e('Refresh preview', 'text_doma'); ?>"><?php _e('Refresh preview', 'text_doma'); ?></button>
</div>
</script>
<?php
/* ------------------------------------------------------------------------- *
* REVISION HISTORY / HIDDEN
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___revision_history">
<input data-czrtype="{{data.input_id}}" type="hidden"/>
</script>
<?php
/* ------------------------------------------------------------------------- *
* IMPORT / EXPORT
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___import_export">
<div class="sek-export-btn-wrap">
<div class="customize-control-title width-100"><?php //_e('Export', 'text_doma'); ?></div>
<button type="button" class="button" data-czr-control-id="{{ data.control_id }}" data-czr-input-id="{{data.input_id}}" data-czr-action="sek-export"><?php _e('Export', 'text_doma' ); ?></button>
</div>
<div class="sek-import-btn-wrap">
<div class="customize-control-title width-100"><?php _e('IMPORT', 'text_doma'); ?></div>
<span class="czr-notice"><?php _e('Select the file to import and click on Import button.', 'text_doma' ); ?></span>
<span class="czr-notice"><?php _e('Be sure to import a file generated with Nimble Builder export system.', 'text_doma' ); ?></span>
<?php // <DIALOG FOR LOCAL IMPORT> ?>
<div class="czr-import-dialog czr-local-import notice notice-info">
<div class="czr-import-message"><?php _e('Some of the imported sections need a location that is not active on this page. Sections in missing locations will not be rendered. You can continue importing or assign those sections to a contextually active location.', 'text_doma' ); ?></div>
<button type="button" class="button" data-czr-control-id="{{ data.control_id }}" data-czr-input-id="{{data.input_id}}" data-czr-action="sek-import-as-is"><?php _e('Import without modification', 'text_doma' ); ?></button>
<button type="button" class="button" data-czr-control-id="{{ data.control_id }}" data-czr-input-id="{{data.input_id}}" data-czr-action="sek-import-assign"><?php _e('Import in existing locations', 'text_doma' ); ?></button>
<button type="button" class="button" data-czr-control-id="{{ data.control_id }}" data-czr-input-id="{{data.input_id}}" data-czr-action="sek-cancel-import"><?php _e('Cancel import', 'text_doma' ); ?></button>
</div>
<?php // </DIALOG FOR LOCAL IMPORT> ?>
<?php // <DIALOG FOR GLOBAL IMPORT> ?>
<div class="czr-import-dialog czr-global-import notice notice-info">
<div class="czr-import-message"><?php _e('Some of the imported sections need a location that is not active on this page. For example, if you are importing a global header footer, you need to activate the Nimble site wide header and footer, in "Site wide header and footer" options.', 'text_doma' ); ?></div>
<button type="button" class="button" data-czr-control-id="{{ data.control_id }}" data-czr-input-id="{{data.input_id}}" data-czr-action="sek-import-as-is"><?php _e('Import', 'text_doma' ); ?></button>
<button type="button" class="button" data-czr-control-id="{{ data.control_id }}" data-czr-input-id="{{data.input_id}}" data-czr-action="sek-cancel-import"><?php _e('Cancel import', 'text_doma' ); ?></button>
</div>
<?php // </DIALOG FOR GLOBAL IMPORT> ?>
<div class="sek-uploading"><?php _e( 'Uploading...', 'text_doma' ); ?></div>
<input type="file" name="sek-import-file" class="sek-import-file" />
<input type="hidden" name="sek-skope" value="{{data.input_data.scope}}" />
<button type="button" class="button disabled" data-czr-control-id="{{ data.control_id }}" data-czr-input-id="{{data.input_id}}" data-czr-action="sek-pre-import"><?php _e('Import', 'text_doma' ); ?></button>
</div>
<input data-czrtype="{{data.input_id}}" type="hidden" value="{{data.value}}"/>
</script>
<?php
/* ------------------------------------------------------------------------- *
* INACTIVE
* Sept 2020 introduced an "inactive" input type in order to display pro info for Nimble
* this input should be "hidden" type, and should not trigger an API change.
* when working on https://github.com/presscustomizr/nimble-builder-pro/issues/67
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___inactive">
<input data-czrtype="{{data.input_id}}" type="hidden"/>
</script>
<?php
/* ------------------------------------------------------------------------- *
* SITE TMPL PICKER
/* ------------------------------------------------------------------------- */
?>
<script type="text/html" id="tmpl-nimble-input___site_tmpl_picker">
<div class="sek-button-choice-wrapper">
<input data-czrtype="{{data.input_id}}" type="hidden"/>
<div class="sek-ui-button-group" role="group">
<button type="button" aria-pressed="false" class="sek-ui-button sek-remove-site-tmpl" title="<?php _e('Reset to default', 'text_doma'); ?>"><?php _e('Reset to default', 'text_doma'); ?></button>
<button type="button" aria-pressed="false" class="sek-ui-button sek-pick-site-tmpl" title="<?php _e('Pick a template', 'text_doma'); ?>" data-sek-group-scope="{{data.input_id}}"><?php _e('Pick a template', 'text_doma'); ?></button>
</div>
</div>
</script>
<?php
}//sek_print_nimble_input_templates() @hook 'customize_controls_print_footer_scripts'
?>
@@ -0,0 +1,81 @@
<?php
/* ------------------------------------------------------------------------- *
* SETUP DYNAMIC SERVER REGISTRATION FOR SETTING
/* ------------------------------------------------------------------------- */
// Fired @'after_setup_theme:20'
if ( !class_exists( 'SEK_CZR_Dyn_Register' ) ) :
class SEK_CZR_Dyn_Register {
static $instance;
public $sanitize_callbacks = array();// <= will be populated to cache the callbacks when invoking sek_get_module_sanitize_callbacks().
public static function get_instance( $params ) {
if ( !isset( self::$instance ) && !( self::$instance instanceof SEK_CZR_Dyn_Register ) )
self::$instance = new SEK_CZR_Dyn_Register( $params );
return self::$instance;
}
function __construct( $params = array() ) {
// Schedule the loading the skoped settings class
add_action( 'customize_register', array( $this, 'load_nimble_setting_class' ) );
add_filter( 'customize_dynamic_setting_args', array( $this, 'set_dyn_setting_args' ), 10, 2 );
add_filter( 'customize_dynamic_setting_class', array( $this, 'set_dyn_setting_class') , 10, 3 );
}//__construct
//@action 'customize_register'
function load_nimble_setting_class() {
require_once( NIMBLE_BASE_PATH . '/inc/sektions/seks_setting_class.php' );
}
//@filter 'customize_dynamic_setting_args'
function set_dyn_setting_args( $setting_args, $setting_id ) {
// shall start with "nimble___" or "nimble_global_opts"
// those are the setting that will actually be saved in DB :
// - sektion collections ( local and global skope )
// - global options
// - site template options
if ( 0 === strpos( $setting_id, NIMBLE_OPT_PREFIX_FOR_SEKTION_COLLECTION ) || 0 === strpos( $setting_id, NIMBLE_OPT_NAME_FOR_GLOBAL_OPTIONS ) ) {
//sek_error_log( 'DYNAMICALLY REGISTERING SEK SETTING => ' . $setting_id, $setting_args);
return array(
'transport' => 'refresh',
'type' => 'option',
'default' => array(),
// Only the section collections are sanitized on save
'sanitize_callback' => 0 === strpos( $setting_id, NIMBLE_OPT_PREFIX_FOR_SEKTION_COLLECTION ) ? '\Nimble\sek_sektion_collection_sanitize_cb' : null
//'validate_callback' => '\Nimble\sek_sektion_collection_validate_cb'
);
} else if ( 0 === strpos( $setting_id, NIMBLE_PREFIX_FOR_SETTING_NOT_SAVED ) ) {
//sek_error_log( 'DYNAMICALLY REGISTERING SEK SETTING => ' . $setting_id, $setting_args);
return array(
'transport' => 'refresh',
'type' => '_nimble_ui_',//won't be saved as is,
'default' => array(),
//'sanitize_callback' => array( $this, 'sanitize_callback' ),
//'validate_callback' => '\Nimble\sek_sektion_collection_validate_cb'
);
}
return $setting_args;
//return wp_parse_args( array( 'default' => array() ), $setting_args );
}
//@filter 'customize_dynamic_setting_class'
// We use a custom setting class only for the section collections ( local and global ), not for global options and site template options
function set_dyn_setting_class( $class, $setting_id, $args ) {
//sek_error_log( 'REGISTERING CLASS DYNAMICALLY for setting =>' . $setting_id );
// Setting class for NB global options and Site Template options
if ( 0 === strpos( $setting_id, NIMBLE_OPT_NAME_FOR_GLOBAL_OPTIONS ) ) {
return '\Nimble\Nimble_Options_Setting';
}
// Setting class for NB sektion collections => shall start with 'nimble___'
if ( 0 === strpos( $setting_id, NIMBLE_OPT_PREFIX_FOR_SEKTION_COLLECTION ) ) {
return '\Nimble\Nimble_Collection_Setting';
}
return $class;
}
}//class
endif;
?>
@@ -0,0 +1,327 @@
<?php
////////////////////////////////////////////////////////////////
// GENERIC HELPER FIRED IN ALL AJAX CALLBACKS
// @param $params = array('check_nonce' => true )
function sek_do_ajax_pre_checks( $params = array() ) {
$params = wp_parse_args( $params, array( 'check_nonce' => true ) );
if ( $params['check_nonce'] ) {
$action = 'save-customize_' . get_stylesheet();
if ( !check_ajax_referer( $action, 'nonce', false ) ) {
wp_send_json_error( array(
'code' => 'invalid_nonce',
'message' => __( __CLASS__ . '::' . __FUNCTION__ . ' => check_ajax_referer() failed.' ),
) );
}
}
if ( !is_user_logged_in() ) {
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' => unauthenticated' );
}
if ( !current_user_can( 'customize' ) ) {
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' => user_cant_edit_theme_options');
}
if ( !current_user_can( 'customize' ) ) {
status_header( 403 );
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' => customize_not_allowed' );
} else if ( !isset( $_SERVER['REQUEST_METHOD'] ) || 'POST' !== $_SERVER['REQUEST_METHOD'] ) {
status_header( 405 );
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' => bad_method' );
}
}//sek_do_ajax_pre_checks()
// IMPORT IMG
add_action( 'wp_ajax_sek_import_attachment', '\Nimble\sek_ajax_import_attachment' );
// Fetches the list of revision for a given skope_id
add_action( 'wp_ajax_sek_get_revision_history', '\Nimble\sek_get_revision_history' );
// Fetches the revision for a given post id
add_action( 'wp_ajax_sek_get_single_revision', '\Nimble\sek_get_single_revision' );
// Fetches the category collection to generate the options for a select input
// @see api.czrInputMap.category_picker
add_action( 'wp_ajax_sek_get_post_categories', '\Nimble\sek_get_post_categories' );
// Fetches the code editor params to generate the options for a textarea input
// @see api.czrInputMap.code_editor
add_action( 'wp_ajax_sek_get_code_editor_params', '\Nimble\sek_get_code_editor_params' );
add_action( 'wp_ajax_sek_postpone_feedback', '\Nimble\sek_postpone_feedback_notification' );
// <AJAX TO FETCH INPUT COMPONENTS>
// this dynamic filter is declared on wp_ajax_ac_get_template in the czr_base_fmk
// It allows us to populate the server response with the relevant module html template
// $html = apply_filters( "ac_set_ajax_czr_tmpl___{$module_type}", '', $tmpl );
add_filter( "ac_set_ajax_czr_tmpl___fa_icon_picker_input", '\Nimble\sek_get_fa_icon_list_tmpl', 10, 3 );
// this dynamic filter is declared on wp_ajax_ac_get_template in the czr_base_fmk
// It allows us to populate the server response with the relevant module html template
// $html = apply_filters( "ac_set_ajax_czr_tmpl___{$module_type}", '', $tmpl );
add_filter( "ac_set_ajax_czr_tmpl___font_picker_input", '\Nimble\sek_get_font_list_tmpl', 10, 3 );
// </AJAX TO FETCH INPUT COMPONENTS>
/////////////////////////////////////////////////////////////////
// hook : wp_ajax_sek_import_attachment
function sek_ajax_import_attachment() {
sek_do_ajax_pre_checks( array( 'check_nonce' => false ) );
if ( !isset( $_POST['img_url'] ) || !is_string($_POST['img_url']) ) {
wp_send_json_error( 'missing_or_invalid_img_url_when_importing_image');
}
$id = sek_sideload_img_and_return_attachment_id( sanitize_text_field($_POST['img_url']) );
if ( is_wp_error( $id ) ) {
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' => problem when trying to wp_insert_attachment() for img : ' . sanitize_text_field($_POST['img_url']) . ' | SERVER ERROR => ' . json_encode( $id ) );
} else {
wp_send_json_success([
'id' => $id,
'url' => wp_get_attachment_url( $id )
]);
}
}
////////////////////////////////////////////////////////////////
// REVISIONS
// Fired in __construct()
function sek_get_revision_history() {
sek_do_ajax_pre_checks( array( 'check_nonce' => true ) );
if ( !isset( $_POST['skope_id'] ) || empty( $_POST['skope_id'] ) ) {
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' => missing skope_id' );
}
$rev_list = sek_get_revision_history_from_posts( sanitize_text_field($_POST['skope_id']) );
wp_send_json_success( $rev_list );
}
function sek_get_single_revision() {
sek_do_ajax_pre_checks( array( 'check_nonce' => true ) );
if ( !isset( $_POST['revision_post_id'] ) || empty( $_POST['revision_post_id'] ) ) {
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' => missing revision_post_id' );
}
$revision = sek_get_single_post_revision( sanitize_text_field($_POST['revision_post_id']) );
wp_send_json_success( $revision );
}
////////////////////////////////////////////////////////////////
// POST CATEGORIES => to be used in the category picker select input
// Fired in __construct()
function sek_get_post_categories() {
sek_do_ajax_pre_checks( array( 'check_nonce' => true ) );
$raw_cats = get_categories();
$raw_cats = is_array( $raw_cats ) ? $raw_cats : array();
$cat_collection = array();
foreach( $raw_cats as $cat ) {
$cat_collection[] = array(
'id' => $cat->term_id,
'slug' => $cat->slug,
'name' => sprintf( '%s (%s %s)', $cat->cat_name, $cat->count, __('posts', 'text_doma') )
);
}
wp_send_json_success( $cat_collection );
}
////////////////////////////////////////////////////////////////
// CODE EDITOR PARAMS => to be used in the code editor input
// Fired in __construct()
function sek_get_code_editor_params() {
sek_do_ajax_pre_checks( array( 'check_nonce' => true ) );
$code_type = isset( $_POST['code_type'] ) ? sanitize_text_field($_POST['code_type']) : 'text/html';
$editor_params = nimble_get_code_editor_settings( array(
'type' => $code_type
));
wp_send_json_success( $editor_params );
}
////////////////////////////////////////////////////////////////
// POSTPONE FEEDBACK NOTIFICATION IN CUSTOMIZER
// INSPIRED FROM CORE DISMISS POINTER MECHANISM
// @see wp-admin/includes/ajax-actions.php
// Nov 2020 => DEPRECATED https://github.com/presscustomizr/nimble-builder/issues/701
function sek_postpone_feedback_notification() {
sek_do_ajax_pre_checks( array( 'check_nonce' => true ) );
if ( !isset( $_POST['transient_duration_in_days'] ) ||!is_numeric( $_POST['transient_duration_in_days'] ) ) {
$transient_duration = 7 * DAY_IN_SECONDS;
} else {
$transient_duration = sanitize_text_field($_POST['transient_duration_in_days']) * DAY_IN_SECONDS;
}
set_transient( NIMBLE_FEEDBACK_NOTICE_ID, 'maybe_later', $transient_duration );
wp_die( 1 );
}
////////////////////////////////////////////////////////////////
// FETCH FONT AWESOME ICONS
// hook : ac_set_ajax_czr_tmpl___czr_tiny_mce_editor_module
// this dynamic filter is declared on wp_ajax_ac_get_template
// It allows us to populate the server response with the relevant module html template
// $html = apply_filters( "ac_set_ajax_czr_tmpl___{$module_type}", '', $tmpl );
//
// For czr_tiny_mce_editor_module, we request the font_list tmpl
function sek_get_fa_icon_list_tmpl( $html, $requested_tmpl = '', $posted_params = array() ) {
if ( empty( $requested_tmpl ) ) {
wp_send_json_error( __FUNCTION__ . ' => the requested tmpl is empty' );
}
return wp_json_encode(
sek_retrieve_decoded_font_awesome_icons()
);//will be sent by wp_send_json_success() in ::ac_set_ajax_czr_tmpl()
}
//retrieves faicons:
// 1) from faicons.json if needed (transient doesn't exists, or is new version => set in TC_wfc ) and decodes them
// otherwise
// 2) from the transient set if it exists
function sek_retrieve_decoded_font_awesome_icons() {
// this file must be generated with: https://github.com/presscustomizr/nimble-builder/issues/57
$faicons_json_path = NIMBLE_BASE_PATH . '/assets/faicons.json';
$faicons_transient_name = NIMBLE_FAWESOME_TRANSIENT_ID;
if ( false == get_transient( $faicons_transient_name ) ) {
if ( file_exists( $faicons_json_path ) ) {
$faicons_raw = @file_get_contents( $faicons_json_path );
if ( false === $faicons_raw ) {
$faicons_raw = wp_remote_fopen( $faicons_json_path );
}
$faicons_decoded = json_decode( $faicons_raw, true );
set_transient( $faicons_transient_name , $faicons_decoded , 60*60*24*3000 );
} else {
wp_send_json_error( __FUNCTION__ . ' => the file faicons.json is missing' );
}
}
else {
$faicons_decoded = get_transient( $faicons_transient_name );
}
return $faicons_decoded;
}
////////////////////////////////////////////////////////////////
// FETCH FONT LISTS
// hook : ac_set_ajax_czr_tmpl___czr_tiny_mce_editor_module
// For czr_tiny_mce_editor_module, we request the font_list tmpl
function sek_get_font_list_tmpl( $html, $requested_tmpl = '', $posted_params = array() ) {
if ( empty( $requested_tmpl ) ) {
wp_send_json_error( __FUNCTION__ . ' => the requested tmpl is empty' );
}
return wp_json_encode( array(
'cfonts' => sek_get_cfonts(),
'gfonts' => sek_get_gfonts(),
) );//will be sent by wp_send_json_success() in ::ac_set_ajax_czr_tmpl()
}
function sek_get_cfonts() {
$cfonts = array();
$raw_cfonts = array(
'-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue, Arial, sans-serif',
'Arial Black,Arial Black,Gadget,sans-serif',
'Century Gothic',
'Comic Sans MS,Comic Sans MS,cursive',
'Courier New,Courier New,Courier,monospace',
'Georgia,Georgia,serif',
'Helvetica Neue, Helvetica, Arial, sans-serif',
'Impact,Charcoal,sans-serif',
'Lucida Console,Monaco,monospace',
'Lucida Sans Unicode,Lucida Grande,sans-serif',
'Palatino Linotype,Book Antiqua,Palatino,serif',
'Tahoma,Geneva,sans-serif',
'Times New Roman,Times,serif',
'Trebuchet MS,Helvetica,sans-serif',
'Verdana,Geneva,sans-serif',
);
foreach ( $raw_cfonts as $font ) {
//no subsets for cfonts => epty array()
$cfonts[] = array(
'name' => $font ,
'subsets' => array()
);
}
return apply_filters( 'sek_font_picker_cfonts', $cfonts );
}
//retrieves gfonts:
// 1) from webfonts.json if needed (transient doesn't exists, or is new version => set in TC_wfc ) and decodes them
// otherwise
// 2) from the transiet set if it exists
//
// => Until June 2017, the webfonts have been stored in 'tc_gfonts' transient
// => In June 2017, the Google Fonts have been updated with a new webfonts.json
// generated from : https://www.googleapis.com/webfonts/v1/webfonts?key=AIzaSyBID8gp8nBOpWyH5MrsF7doP4fczXGaHdA
//
// => The transient name is now : czr_gfonts_june_2017
function sek_retrieve_decoded_gfonts() {
if ( false == get_transient( NIMBLE_GFONTS_TRANSIENT_ID ) ) {
$gfont_raw = @file_get_contents( NIMBLE_BASE_PATH ."/assets/webfonts.json" );
if ( $gfont_raw === false ) {
$gfont_raw = wp_remote_fopen( NIMBLE_BASE_PATH ."/assets/webfonts.json" );
}
$gfonts_decoded = json_decode( $gfont_raw, true );
set_transient( NIMBLE_GFONTS_TRANSIENT_ID , $gfonts_decoded , 60*60*24*3000 );
}
else {
$gfonts_decoded = get_transient( NIMBLE_GFONTS_TRANSIENT_ID );
}
return $gfonts_decoded;
}
//@return the google fonts
function sek_get_gfonts( $what = null ) {
//checks if transient exists or has expired
$gfonts_decoded = sek_retrieve_decoded_gfonts();
$gfonts = array();
//$subsets = array();
// $subsets['all-subsets'] = sprintf( '%1$s ( %2$s %3$s )',
// __( 'All languages' , 'text_doma' ),
// count($gfonts_decoded['items']) + count( get_cfonts() ),
// __('fonts' , 'text_doma' )
// );
foreach ( $gfonts_decoded['items'] as $font ) {
foreach ( $font['variants'] as $variant ) {
$name = str_replace( ' ', '+', $font['family'] );
$gfonts[] = array(
'name' => $name . ':' .$variant
//'subsets' => $font['subsets']
);
}
//generates subset list : subset => font number
// foreach ( $font['subsets'] as $sub ) {
// $subsets[$sub] = isset($subsets[$sub]) ? $subsets[$sub]+1 : 1;
// }
}
//finalizes the subset array
// foreach ( $subsets as $subset => $font_number ) {
// if ( 'all-subsets' == $subset )
// continue;
// $subsets[$subset] = sprintf('%1$s ( %2$s %3$s )',
// $subset,
// $font_number,
// __('fonts' , 'text_doma' )
// );
// }
return ('subsets' == $what) ? apply_filters( 'sek_font_picker_gfonts_subsets ', $subsets ) : apply_filters( 'sek_font_picker_gfonts', $gfonts ) ;
}
?>
@@ -0,0 +1,269 @@
<?php
add_action( 'customize_register', '\Nimble\sek_catch_export_action', PHP_INT_MAX );
function sek_catch_export_action( $wp_customize ) {
if ( current_user_can( 'customize' ) ) {
if ( isset( $_REQUEST['sek_export_nonce'] ) ) {
sek_maybe_export();
}
}
}
// fire from sek_catch_export_action() @hook 'customize_register'
function sek_maybe_export() {
$nonce = 'save-customize_' . get_stylesheet();
if ( !isset( $_REQUEST['sek_export_nonce'] ) ) {
sek_error_log( __FUNCTION__ . ' => missing nonce.');
return;
}
if ( !isset( $_REQUEST['skope_id']) || empty( $_REQUEST['skope_id'] ) ) {
sek_error_log( __FUNCTION__ . ' => missing or empty skope_id.');
return;
}
if ( !isset( $_REQUEST['active_locations'] ) || empty( $_REQUEST['active_locations'] ) ) {
sek_error_log( __FUNCTION__ . ' => missing active locations param.');
return;
}
if ( !wp_verify_nonce( $_REQUEST['sek_export_nonce'], $nonce ) ) {
sek_error_log( __FUNCTION__ . ' => invalid none.');
return;
}
if ( !is_user_logged_in() ) {
sek_error_log( __FUNCTION__ . ' => user not logged in.');
return;
}
if ( !current_user_can( 'customize' ) ) {
sek_error_log( __FUNCTION__ . ' => missing customize capabilities.');
return;
}
$seks_data = sek_get_skoped_seks( sanitize_text_field($_REQUEST['skope_id']) );
//sek_error_log('EXPORT BEFORE FILTER ? ' . $_REQUEST['skope_id'] , $seks_data );
// the filter 'nimble_pre_export' is used to :
// replace image id by the absolute url
// clean level ids and replace them with a placeholder string
$seks_data = apply_filters( 'nimble_pre_export', $seks_data );
// March 2021 : make sure text input are sanitized like in #544 #792
//$seks_data = sek_sektion_collection_sanitize_cb( $seks_data );
$theme_name = sanitize_title_with_dashes( get_stylesheet() );
//sek_error_log('EXPORT AFTER FILTER ?', $seks_data );
$export = array(
'data' => $seks_data,
'metas' => array(
'skope_id' => sanitize_text_field($_REQUEST['skope_id']),
'version' => NIMBLE_VERSION,
// is sent as a string : "__after_header,__before_main_wrapper,loop_start,__before_footer"
'active_locations' => is_string( $_REQUEST['active_locations'] ) ? explode( ',', sanitize_text_field($_REQUEST['active_locations']) ) : array(),
'date' => date("Y-m-d"),
'theme' => $theme_name
)
);
//sek_error_log('$export ?', $export );
$skope_id = str_replace('skp__', '', sanitize_text_field($_REQUEST['skope_id']) );
$filename = $theme_name . '_' . $skope_id . '.nimblebuilder';
// Set the download headers.
header( 'Content-disposition: attachment; filename=' . $filename );
header( 'Content-Type: application/octet-stream; charset=' . get_option( 'blog_charset' ) );
echo wp_json_encode( $export );
// Start the download.
die();
}
// Ajax action before processing the export
// control that all required fields are there
// This is to avoid a white screen when generating the download window afterwards
add_action( 'wp_ajax_sek_pre_export_checks', '\Nimble\sek_ajax_pre_export_checks' );
function sek_ajax_pre_export_checks() {
//sek_error_log('PRE EXPORT CHECKS ?', $_POST );
$action = 'save-customize_' . get_stylesheet();
if ( !check_ajax_referer( $action, 'nonce', false ) ) {
wp_send_json_error( 'check_ajax_referer_failed' );
}
if ( !is_user_logged_in() ) {
wp_send_json_error( 'user_unauthenticated' );
}
if ( !current_user_can( 'customize' ) ) {
status_header( 403 );
wp_send_json_error( 'customize_not_allowed' );
} else if ( !isset( $_SERVER['REQUEST_METHOD'] ) || 'POST' !== $_SERVER['REQUEST_METHOD'] ) {
status_header( 405 );
wp_send_json_error( 'bad_ajax_method' );
}
if ( !isset( $_POST['skope_id'] ) || empty( $_POST['skope_id'] ) ) {
wp_send_json_error( 'missing_skope_id' );
}
if ( !isset( $_POST['active_locations'] ) || empty( $_POST['active_locations'] ) ) {
wp_send_json_error( 'no_active_locations_to_export' );
}
wp_send_json_success();
}
// EXPORT FILTER
add_filter( 'nimble_pre_export', '\Nimble\sek_parse_img_and_clean_id' );
function sek_parse_img_and_clean_id( $seks_data ) {
$new_seks_data = array();
foreach ( $seks_data as $key => $value ) {
if ( is_array($value) ) {
$new_seks_data[$key] = sek_parse_img_and_clean_id( $value );
} else {
switch( $key ) {
case 'bg-image' :
case 'img' :
if ( is_int( $value ) && (int)$value > 0 ) {
$value = '__img_url__' . wp_get_attachment_url((int)$value);
}
break;
case 'id' :
if ( is_string( $value ) && false !== strpos( $value, '__nimble__' ) ) {
$value = '__rep__me__';
}
break;
}
$new_seks_data[$key] = $value;
}
}
return $new_seks_data;
}
// fetch the content from a user imported file
add_action( 'wp_ajax_sek_get_manually_imported_file_content', '\Nimble\sek_ajax_get_manually_imported_file_content' );
function sek_ajax_get_manually_imported_file_content() {
// sek_error_log(__FUNCTION__ . ' AJAX $_POST ?', $_POST );
// sek_error_log(__FUNCTION__ . ' AJAX $_FILES ?', $_FILES );
// sek_error_log(__FUNCTION__ . ' AJAX $_REQUEST ?', $_REQUEST );
$action = 'save-customize_' . get_stylesheet();
if ( !check_ajax_referer( $action, 'nonce', false ) ) {
wp_send_json_error( 'check_ajax_referer_failed' );
}
if ( !is_user_logged_in() ) {
wp_send_json_error( 'user_unauthenticated' );
}
if ( !current_user_can( 'customize' ) ) {
status_header( 403 );
wp_send_json_error( 'customize_not_allowed' );
} else if ( !isset( $_SERVER['REQUEST_METHOD'] ) || 'POST' !== $_SERVER['REQUEST_METHOD'] ) {
status_header( 405 );
wp_send_json_error( 'bad_ajax_method' );
}
if ( !isset( $_FILES['file_candidate'] ) || empty( $_FILES['file_candidate'] ) ) {
wp_send_json_error( 'missing_file_candidate' );
}
if ( !isset( $_POST['skope'] ) || empty( $_POST['skope'] ) ) {
wp_send_json_error( 'missing_skope' );
}
// load WP upload if not done yet
if ( !function_exists( 'wp_handle_upload' ) ) {
require_once( ABSPATH . 'wp-admin/includes/file.php' );
}
// @see https://codex.wordpress.org/Function_Reference/wp_handle_upload
// Important => always run unlink( $file['file'] ) before sending the json success or error
// otherwise WP will write the file in the /wp-content folder
$file = wp_handle_upload(
$_FILES['file_candidate'],
array(
'test_form' => false,
'test_type' => false,
'mimes' => array(
'text' => 'text/plain',
//'nimblebuilder' => 'text/plain',
'json' => 'application/json',
'nimblebuilder' => 'application/json'
)
)
);
// Make sure we have an uploaded file.
if ( isset( $file['error'] ) ) {
unlink( $file['file'] );
wp_send_json_error( 'import_file_error' );
return;
}
if ( !file_exists( $file['file'] ) ) {
unlink( $file['file'] );
wp_send_json_error( 'import_file_do_not_exist' );
return;
}
// Get the upload data.
$raw = file_get_contents( $file['file'] );
//$raw_unserialized_data = @unserialize( $raw );
$raw_unserialized_data = json_decode( $raw, true );
// VALIDATE IMPORTED CONTENT
// data structure :
// $raw_unserialized_data = array(
// 'data' => $seks_data,
// 'metas' => array(
// 'skope_id' => $_REQUEST['skope_id'],
// 'version' => NIMBLE_VERSION,
// // is sent as a string : "__after_header,__before_main_wrapper,loop_start,__before_footer"
// 'active_locations' => is_string( $_REQUEST['active_locations'] ) ? explode( ',', $_REQUEST['active_locations'] ) : array(),
// 'date' => date("Y-m-d")
// )
// );
// check import structure
if ( !is_array( $raw_unserialized_data ) || empty( $raw_unserialized_data['data']) || !is_array( $raw_unserialized_data['data'] ) || empty( $raw_unserialized_data['metas'] ) || !is_array( $raw_unserialized_data['metas'] ) ) {
unlink( $file['file'] );
wp_send_json_error( 'invalid_import_content' );
return;
}
// check version
// => current Nimble Version must be at least import version
if ( !empty( $raw_unserialized_data['metas']['version'] ) && version_compare( NIMBLE_VERSION, $raw_unserialized_data['metas']['version'], '<' ) ) {
unlink( $file['file'] );
wp_send_json_error( 'nimble_builder_needs_update' );
return;
}
$maybe_import_images = true;
// in a pre-import-check context, we don't need to sniff and upload images
if ( array_key_exists( 'pre_import_check', $_POST ) && true === sek_booleanize_checkbox_val( sanitize_text_field($_POST['pre_import_check']) ) ) {
$maybe_import_images = false;
}
// april 2020 : introduced for https://github.com/presscustomizr/nimble-builder/issues/663
if ( array_key_exists( 'import_img', $_POST ) && false === sek_booleanize_checkbox_val( sanitize_text_field($_POST['import_img']) ) ) {
$maybe_import_images = false;
}
// Make sure NB decodes encoded rich text before sending to the customizer
// see #544 and #791
$raw_unserialized_data['data'] = sek_sniff_and_decode_richtext( $raw_unserialized_data['data'] );
$imported_content = array(
//'data' => apply_filters( 'nimble_pre_import', $raw_unserialized_data['data'], $do_import_images ),
'data' => sek_maybe_import_imgs( $raw_unserialized_data['data'], $maybe_import_images ),
'metas' => $raw_unserialized_data['metas'],
// the image import errors won't block the import
// they are used when notifying user in the customizer
'img_errors' => !empty( Nimble_Manager()->img_import_errors ) ? implode(',', Nimble_Manager()->img_import_errors) : array()
);
// Remove the uploaded file
// Important => always run unlink( $file['file'] ) before sending the json success or error
// otherwise WP will write the file in the /wp-content folder
unlink( $file['file'] );
// Send
wp_send_json_success( $imported_content );
}
?>
@@ -0,0 +1,324 @@
<?php
////////////////////////////////////////////////////////////////
// Fetches the user saved templates
add_action( 'wp_ajax_sek_get_all_saved_tmpl', '\Nimble\sek_ajax_get_all_saved_templates' );
// @hook wp_ajax_sek_get_user_saved_templates
function sek_ajax_get_all_saved_templates() {
sek_do_ajax_pre_checks( array( 'check_nonce' => true ) );
$decoded_templates = sek_get_all_saved_templates();
if ( is_array($decoded_templates) ) {
wp_send_json_success( $decoded_templates );
} else {
if ( !empty( $decoded_templates ) ) {
sek_error_log( __FUNCTION__ . ' error => invalid templates returned', $decoded_templates );
wp_send_json_error( __FUNCTION__ . ' error => invalid templates returned' );
}
}
}
////////////////////////////////////////////////////////////////
// Fetches the api templates
add_action( 'wp_ajax_sek_get_all_api_tmpl', '\Nimble\sek_ajax_get_all_api_templates' );
// @hook wp_ajax_sek_get_user_saved_templates
function sek_ajax_get_all_api_templates() {
sek_do_ajax_pre_checks( array( 'check_nonce' => true ) );
$decoded_templates = sek_get_all_api_templates();
if ( is_array($decoded_templates) ) {
wp_send_json_success( $decoded_templates );
} else {
if ( !empty( $decoded_templates ) ) {
sek_error_log( __FUNCTION__ . ' error => invalid templates returned', $decoded_templates );
wp_send_json_error( __FUNCTION__ . ' error => invalid templates returned' );
}
}
}
////////////////////////////////////////////////////////////////
// TEMPLATE GET CONTENT + METAS
// Fetches the json of a given user template
add_action( 'wp_ajax_sek_get_user_tmpl_json', '\Nimble\sek_ajax_sek_get_user_tmpl_json' );
// @hook wp_ajax_sek_get_user_saved_templates
function sek_ajax_sek_get_user_tmpl_json() {
sek_do_ajax_pre_checks( array( 'check_nonce' => true ) );
// We must have a tmpl_post_name
if ( empty( $_POST['tmpl_post_name']) || !is_string( $_POST['tmpl_post_name'] ) ) {
wp_send_json_error( __FUNCTION__ . '_missing_tmpl_post_name' );
}
// if ( !isset( $_POST['skope_id'] ) || empty( $_POST['skope_id'] ) ) {
// wp_send_json_error( __FUNCTION__ . '_missing_skope_id' );
// }
$tmpl_post = sek_get_saved_tmpl_post( sanitize_text_field($_POST['tmpl_post_name']) );
if ( !is_wp_error( $tmpl_post ) && $tmpl_post && is_object( $tmpl_post ) ) {
$tmpl_decoded = maybe_unserialize( $tmpl_post->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( $tmpl_decoded ) && !empty( $tmpl_decoded['data'] ) && is_array( $tmpl_decoded['data'] ) ) {
//$tmpl_decoded['data'] = json_decode( wp_unslash( $tmpl_decoded['data'], true ) );
$tmpl_decoded['data'] = sek_maybe_import_imgs( $tmpl_decoded['data'], $do_import_images = true );
// the image import errors won't block the import
// they are used when notifying user in the customizer
$tmpl_decoded['img_errors'] = !empty( Nimble_Manager()->img_import_errors ) ? implode(',', Nimble_Manager()->img_import_errors) : array();
// Make sure we decode encoded rich text before sending to the customizer
// see #544 and #791
$tmpl_decoded['data'] = sek_sniff_and_decode_richtext( $tmpl_decoded['data'] );
// added March 2021 for site templates #478
// If property '__inherits_group_skope_tmpl_when_exists__' has been saved by mistake in the template, make sure it's unset now
if ( array_key_exists('__inherits_group_skope_tmpl_when_exists__', $tmpl_decoded['data'] ) ) {
unset( $tmpl_decoded['data']['__inherits_group_skope_tmpl_when_exists__'] );
}
wp_send_json_success( $tmpl_decoded );
} else {
wp_send_json_error( __FUNCTION__ . '_invalid_tmpl_post_data' );
}
} else {
wp_send_json_error( __FUNCTION__ . '_tmpl_post_not_found' );
}
}
add_action( 'wp_ajax_sek_get_api_tmpl_json', '\Nimble\sek_ajax_sek_get_api_tmpl_json' );
// @hook wp_ajax_sek_get_user_saved_templates
function sek_ajax_sek_get_api_tmpl_json() {
sek_do_ajax_pre_checks( array( 'check_nonce' => true ) );
// We must have a tmpl_post_name
if ( empty( $_POST['api_tmpl_name']) || !is_string( $_POST['api_tmpl_name'] ) ) {
wp_send_json_error( __FUNCTION__ . '_missing_tmpl_post_name' );
}
$tmpl_name = sanitize_text_field($_POST['api_tmpl_name']);
// Pro Template case
$is_pro_tmpl = array_key_exists('api_tmpl_is_pro', $_POST ) && 'yes' === sanitize_text_field($_POST['api_tmpl_is_pro']);
if ( $is_pro_tmpl ) {
$pro_key_status = apply_filters( 'nimble_pro_key_status_OK', 'nok' );
if ( 'pro_key_status_ok' !== $pro_key_status ) {
wp_send_json_error( $pro_key_status );
return;
}
}
$raw_tmpl_data = sek_get_single_tmpl_api_data( $tmpl_name, $is_pro_tmpl );// <= returns an unserialized array, in which the template['data'] is NOT a JSON, unlike for user saved templates
// If the api returned a pro license key problem, bail now and return the api string message
if ( $is_pro_tmpl && is_string( $raw_tmpl_data ) && !empty( $raw_tmpl_data ) ) {
wp_send_json_error( $raw_tmpl_data );
} else if ( !is_array( $raw_tmpl_data) || empty( $raw_tmpl_data ) ) {
sek_error_log( __FUNCTION__ . ' problem when getting template : ' . $tmpl_name );
wp_send_json_error( __FUNCTION__ . '_invalid_template_'. $tmpl_name );
}
//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 : ' . $tmpl_name, $raw_tmpl_data );
wp_send_json_error( __FUNCTION__ . '_missing_data_property_for_template_' . $tmpl_name );
} 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();
// Make sure we decode encoded rich text before sending to the customizer
// see #544 and #791
$raw_tmpl_data['data'] = sek_sniff_and_decode_richtext( $raw_tmpl_data['data'] );
// added March 2021 for site templates #478
// If property '__inherits_group_skope_tmpl_when_exists__' has been saved by mistake in the template, make sure it's unset now
if ( array_key_exists('__inherits_group_skope_tmpl_when_exists__', $raw_tmpl_data['data'] ) ) {
unset( $raw_tmpl_data['data']['__inherits_group_skope_tmpl_when_exists__'] );
}
wp_send_json_success( $raw_tmpl_data );
}
//return [];
}
////////////////////////////////////////////////////////////////
// TEMPLATE SAVE
// introduced in april 2020 for https://github.com/presscustomizr/nimble-builder/issues/655
add_action( 'wp_ajax_sek_save_user_template', '\Nimble\sek_ajax_save_user_template' );
/////////////////////////////////////////////////////////////////
// hook : wp_ajax_sek_save_user_template
function sek_ajax_save_user_template() {
sek_do_ajax_pre_checks( array( 'check_nonce' => true ) );
$is_edit_metas_only_case = isset( $_POST['edit_metas_only'] ) && 'yes' === sanitize_text_field($_POST['edit_metas_only']);
// TMPL DATA => the nimble content
if ( !$is_edit_metas_only_case && empty( $_POST['tmpl_data']) ) {
wp_send_json_error( __FUNCTION__ . '_missing_template_data' );
}
if ( !$is_edit_metas_only_case && !is_string( $_POST['tmpl_data'] ) ) {
wp_send_json_error( __FUNCTION__ . '_template_data_must_be_a_json_stringified' );
}
// TMPL METAS
// We must have a title
if ( empty( $_POST['tmpl_title']) ) {
wp_send_json_error( __FUNCTION__ . '_missing_template_title' );
}
if ( !is_string( $_POST['tmpl_description'] ) ) {
wp_send_json_error( __FUNCTION__ . '_template_description_must_be_a_string' );
}
if ( !isset( $_POST['skope_id'] ) || empty( $_POST['skope_id'] ) ) {
wp_send_json_error( __FUNCTION__ . '_missing_skope_id' );
}
if ( !isset( $_POST['tmpl_locations'] ) || empty( $_POST['tmpl_locations'] ) ) {
wp_send_json_error( __FUNCTION__ . '_missing_tmpl_locations' );
}
if ( $is_edit_metas_only_case ) {
$tmpl_data = [];
} else {
// clean level ids and replace them with a placeholder string
$tmpl_data = json_decode( wp_unslash( $_POST['tmpl_data'] ), true );
$tmpl_data = sek_template_save_clean_id( $tmpl_data );
// added March 2021 for site templates #478
// If property '__inherits_group_skope_tmpl_when_exists__' has been set to the template, make sure it's unset now
if ( array_key_exists('__inherits_group_skope_tmpl_when_exists__', $tmpl_data ) ) {
unset( $tmpl_data['__inherits_group_skope_tmpl_when_exists__'] );
}
}
// make sure description and title are clean before DB
$tmpl_title = sek_maybe_encode_richtext( sanitize_text_field($_POST['tmpl_title']) );
$tmpl_description = sek_maybe_encode_richtext( sanitize_text_field($_POST['tmpl_description']) );
// sanitize tmpl_locations
$tmpl_locations = [];
if ( is_array($_POST['tmpl_locations']) ) {
foreach($_POST['tmpl_locations'] as $loc ) {
$tmpl_locations[] = sanitize_text_field($loc);
}
}
// sek_error_log('json decode ?', json_decode( wp_unslash( $_POST['sek_data'] ), true ) );
$template_to_save = array(
'data' => $tmpl_data,//<= array
'tmpl_post_name' => ( !empty( $_POST['tmpl_post_name'] ) && is_string( $_POST['tmpl_post_name'] ) ) ? sanitize_text_field($_POST['tmpl_post_name']) : null,
'metas' => array(
'title' => $tmpl_title,
'description' => $tmpl_description,
'skope_id' => sanitize_text_field($_POST['skope_id']),
'version' => NIMBLE_VERSION,
// is sent as a string : "__after_header,__before_main_wrapper,loop_start,__before_footer"
'tmpl_locations' => $tmpl_locations,
'tmpl_header_location' => isset( $_POST['tmpl_header_location'] ) ? sanitize_text_field($_POST['tmpl_header_location']) : '',
'tmpl_footer_location' => isset( $_POST['tmpl_footer_location'] ) ? sanitize_text_field($_POST['tmpl_footer_location']) : '',
'date' => date("Y-m-d"),
'theme' => sanitize_title_with_dashes( get_stylesheet() ),
// for api templates
'is_pro_tmpl' => false,
'thumb_url' => ''
)
);
$saved_template_post = sek_update_user_tmpl_post( $template_to_save, $is_edit_metas_only_case );
if ( is_wp_error( $saved_template_post ) || is_null($saved_template_post) || empty($saved_template_post) ) {
wp_send_json_error( __FUNCTION__ . ' => error when invoking sek_update_user_tmpl_post()' );
} else {
// sek_error_log( 'ALORS CE POST?', $saved_template_post );
wp_send_json_success( [ 'tmpl_post_id' => $saved_template_post->ID ] );
}
//sek_error_log( __FUNCTION__ . '$_POST' , $_POST);
}
// SAVE FILTER
function sek_template_save_clean_id( $tmpl_data = array() ) {
$new_tmpl_data = array();
if ( !is_array( $tmpl_data ) ) {
sek_error_log( __FUNCTION__ . ' error => tmpl_data should be an array');
return array();
}
$level = null;
if ( isset($tmpl_data['level'] ) ) {
$level = $tmpl_data['level'];
}
foreach ( $tmpl_data as $key => $value ) {
if ( is_array($value) ) {
$new_tmpl_data[$key] = sek_template_save_clean_id( $value );
} else {
switch( $key ) {
// we want to replace ids for all levels but locations
// only section, columns and modules have an id which starts by __nimble__, for ex : __nimble__2024500518bf
// locations id are like : loop_start
case 'id' :
if ( 'location' !== $level && is_string( $value ) && false !== strpos( $value, '__nimble__' ) ) {
$value = '__rep__me__';
}
break;
}
$new_tmpl_data[$key] = $value;
}
}
return $new_tmpl_data;
}
////////////////////////////////////////////////////////////////
// TEMPLATE REMOVE
// introduced in may 2020 for https://github.com/presscustomizr/nimble-builder/issues/655
add_action( 'wp_ajax_sek_remove_user_template', '\Nimble\sek_ajax_remove_user_template' );
/////////////////////////////////////////////////////////////////
// hook : wp_ajax_sek_remove_user_template
function sek_ajax_remove_user_template() {
//sek_error_log( __FUNCTION__ . ' ALORS YEAH IN REMOVAL ? ?', $_POST );
sek_do_ajax_pre_checks( array( 'check_nonce' => true ) );
// We must have a tmpl_post_name
if ( empty( $_POST['tmpl_post_name']) || !is_string( $_POST['tmpl_post_name'] ) ) {
wp_send_json_error( __FUNCTION__ . '_missing_tmpl_post_name' );
}
$tmpl_post_name = sanitize_text_field($_POST['tmpl_post_name']);
// if ( !isset( $_POST['skope_id'] ) || empty( $_POST['skope_id'] ) ) {
// wp_send_json_error( __FUNCTION__ . '_missing_skope_id' );
// }
$tmpl_post_to_remove = sek_get_saved_tmpl_post( $tmpl_post_name );
//sek_error_log( __FUNCTION__ . ' => so $tmpl_post_to_remove ' . $_POST['tmpl_post_name'], $tmpl_post_to_remove );
if ( $tmpl_post_to_remove && is_object( $tmpl_post_to_remove ) ) {
// the CPT is moved to Trash instead of permanently deleted when using wp_delete_post()
$r = wp_trash_post( $tmpl_post_to_remove->ID );
if ( is_wp_error( $r ) ) {
wp_send_json_error( __FUNCTION__ . '_removal_error' );
}
// Added April 2021 for stie templates #478
do_action('nb_on_remove_saved_tmpl_post', $tmpl_post_name );
} else {
wp_send_json_error( __FUNCTION__ . '_tmpl_post_not_found' );
}
if ( is_wp_error( $tmpl_post_to_remove ) || is_null($tmpl_post_to_remove) || empty($tmpl_post_to_remove) ) {
wp_send_json_error( __FUNCTION__ . '_removal_error' );
} else {
// sek_error_log( 'ALORS CE POST?', $saved_template_post );
wp_send_json_success( [ 'tmpl_post_removed' => $tmpl_post_name ] );
}
//sek_error_log( __FUNCTION__ . '$_POST' , $_POST);
}
?>
@@ -0,0 +1,266 @@
<?php
////////////////////////////////////////////////////////////////
// Fetches the user saved sections
add_action( 'wp_ajax_sek_get_all_saved_sections', '\Nimble\sek_ajax_get_all_saved_sections' );
// @hook wp_ajax_sek_get_user_saved_sections
function sek_ajax_get_all_saved_sections() {
sek_do_ajax_pre_checks( array( 'check_nonce' => true ) );
$decoded_sections = sek_get_all_saved_sections();
if ( is_array($decoded_sections) ) {
wp_send_json_success( $decoded_sections );
} else {
if ( !empty( $decoded_sections ) ) {
sek_error_log( __FUNCTION__ . ' error => invalid sections returned', $decoded_sections );
wp_send_json_error( __FUNCTION__ . ' error => invalid sections returned' );
}
}
}
// Fetches the preset_sections
add_action( 'wp_ajax_sek_get_single_api_section_data', '\Nimble\sek_ajax_get_single_api_section_data' );
////////////////////////////////////////////////////////////////
// PRESET SECTIONS
// Fired in __construct()
// hook : 'wp_ajax_sek_get_preset_sektions'
function sek_ajax_get_single_api_section_data() {
sek_do_ajax_pre_checks( array( 'check_nonce' => true ) );
// September 2020 => force update every 24 hours so users won't miss a new pre-build section
// Note that the refresh should have take place on 'upgrader_process_complete'
// always force refresh when developing
sek_do_ajax_pre_checks( array( 'check_nonce' => true ) );
// We must have a api_section_id
if ( empty( $_POST['api_section_id']) || !is_string( $_POST['api_section_id'] ) ) {
wp_send_json_error( __FUNCTION__ . '_missing_api_section_id' );
}
$api_section_id = sanitize_text_field($_POST['api_section_id']);
$is_pro_section_id = sek_is_pro() && is_string($api_section_id) && 'pro_' === substr($api_section_id,0,4);
$pro_key_status = apply_filters( 'nimble_pro_key_status_OK', 'nok' );
if ( $is_pro_section_id && 'pro_key_status_ok' !== $pro_key_status ) {
wp_send_json_error( $pro_key_status );
return;
}
$raw_api_sec_data = sek_api_get_single_section_data( $api_section_id );// <= returns an unserialized array
// When injecting a pro section, NB checks the validity of the key.
// if the api response is not an array, there was a problem when checking the key
// and in this case the response is a string like : 'Expired.'
if ( $is_pro_section_id && is_string($raw_api_sec_data) && !empty($raw_api_sec_data) ) {
wp_send_json_error( $raw_api_sec_data );
return;
}
if( !is_array( $raw_api_sec_data) || empty( $raw_api_sec_data ) ) {
sek_error_log( __FUNCTION__ . ' problem when getting section : ' . $api_section_id );
wp_send_json_error( 'Error : empty or invalid section data : '. $api_section_id );
return;
}
//sek_error_log( __FUNCTION__ . ' api section data', $raw_api_sec_data );
if ( !isset($raw_api_sec_data['collection'] ) || empty( $raw_api_sec_data['collection'] ) ) {
sek_error_log( __FUNCTION__ . ' problem => missing or invalid data property for section : ' . $api_section_id, $raw_api_sec_data );
wp_send_json_error( 'Error : missing_data_property_for_section : ' . $api_section_id );
} else {
// $tmpl_decoded = $raw_api_sec_data;
$raw_api_sec_data['collection'] = sek_maybe_import_imgs( $raw_api_sec_data['collection'], $do_import_images = true );
//$raw_api_sec_data['img_errors'] = !empty( Nimble_Manager()->img_import_errors ) ? implode(',', Nimble_Manager()->img_import_errors) : array();
// Make sure we decode encoded rich text before sending to the customizer
// see #544 and #791
$raw_api_sec_data['collection'] = sek_sniff_and_decode_richtext( $raw_api_sec_data['collection'] );
wp_send_json_success( $raw_api_sec_data );
}
}
////////////////////////////////////////////////////////////////
// SECTION GET CONTENT + METAS
// Fetches the json of a given user section
add_action( 'wp_ajax_sek_get_user_section_json', '\Nimble\sek_ajax_sek_get_user_section_json' );
// @hook wp_ajax_sek_get_user_saved_sections
function sek_ajax_sek_get_user_section_json() {
sek_do_ajax_pre_checks( array( 'check_nonce' => true ) );
// We must have a section_post_name
if ( empty( $_POST['section_post_name']) || !is_string( $_POST['section_post_name'] ) ) {
wp_send_json_error( __FUNCTION__ . '_missing_section_post_name' );
}
// if ( !isset( $_POST['skope_id'] ) || empty( $_POST['skope_id'] ) ) {
// wp_send_json_error( __FUNCTION__ . '_missing_skope_id' );
// }
$section_post = sek_get_saved_section_post( sanitize_text_field($_POST['section_post_name']) );
if ( !is_wp_error( $section_post ) && $section_post && is_object( $section_post ) ) {
$section_decoded = maybe_unserialize( $section_post->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( $section_decoded ) && !empty( $section_decoded['data'] ) && is_string( $section_decoded['data'] ) ) {
$section_decoded['data'] = json_decode( wp_unslash( $section_decoded['data'], true ) );
}
// Make sure we decode encoded rich text before sending to the customizer
// see #544 and #791
$section_decoded['data'] = sek_sniff_and_decode_richtext( $section_decoded['data'] );
wp_send_json_success( $section_decoded );
} else {
wp_send_json_error( __FUNCTION__ . '_section_post_not_found' );
}
}
////////////////////////////////////////////////////////////////
// SECTION SAVE
// introduced in april 2020 for https://github.com/presscustomizr/nimble-builder/issues/655
// ENABLED WHEN CONSTANT NIMBLE_SECTION_SAVE_ENABLED === true
add_action( 'wp_ajax_sek_save_user_section', '\Nimble\sek_ajax_save_user_section' );
/////////////////////////////////////////////////////////////////
// hook : wp_ajax_sek_save_user_section
function sek_ajax_save_user_section() {
sek_do_ajax_pre_checks( array( 'check_nonce' => true ) );
$is_edit_metas_only_case = isset( $_POST['edit_metas_only'] ) && 'yes' === sanitize_text_field($_POST['edit_metas_only']);
// TMPL DATA => the nimble content
if ( !$is_edit_metas_only_case && empty( $_POST['section_data']) ) {
wp_send_json_error( __FUNCTION__ . '_missing_section_data' );
}
if ( !$is_edit_metas_only_case && !is_string( $_POST['section_data'] ) ) {
wp_send_json_error( __FUNCTION__ . '_section_data_must_be_a_json_stringified' );
}
// TMPL METAS
// We must have a title
if ( empty( $_POST['section_title']) ) {
wp_send_json_error( __FUNCTION__ . '_missing_section_title' );
}
if ( !is_string( $_POST['section_description'] ) ) {
wp_send_json_error( __FUNCTION__ . '_section_description_must_be_a_string' );
}
if ( !isset( $_POST['skope_id'] ) || empty( $_POST['skope_id'] ) ) {
wp_send_json_error( __FUNCTION__ . '_missing_skope_id' );
}
// if ( !isset( $_POST['active_locations'] ) || empty( $_POST['active_locations'] ) ) {
// wp_send_json_error( __FUNCTION__ . '_missing_active_locations' );
// }
if ( $is_edit_metas_only_case ) {
$seks_data = [];
} else {
// clean level ids and replace them with a placeholder string
$seks_data = json_decode( wp_unslash( $_POST['section_data'] ), true );
$seks_data = sek_section_save_clean_id( $seks_data );
}
// make sure description and title are clean before DB
$sec_title = sek_maybe_encode_richtext( sanitize_text_field($_POST['section_title']) );
$sec_description = sek_maybe_encode_richtext( sanitize_text_field($_POST['section_description']) );
$section_to_save = array(
'data' => $seks_data,//<= json stringified
// the section post name is provided only when updating
'section_post_name' => ( !empty( $_POST['section_post_name'] ) && is_string( $_POST['section_post_name'] ) ) ? sanitize_text_field($_POST['section_post_name']) : null,
'metas' => array(
'title' => $sec_title,
'description' => $sec_description,
'skope_id' => sanitize_text_field($_POST['skope_id']),
'version' => NIMBLE_VERSION,
// is sent as a string : "__after_header,__before_main_wrapper,loop_start,__before_footer"
//'active_locations' => is_array( $_POST['active_locations'] ) ? $_POST['active_locations'] : array(),
'date' => date("Y-m-d"),
'theme' => sanitize_title_with_dashes( get_stylesheet() )
)
);
$saved_section_post = sek_update_saved_section_post( $section_to_save, $is_edit_metas_only_case );
if ( is_wp_error( $saved_section_post ) || is_null($saved_section_post) || empty($saved_section_post) ) {
wp_send_json_error( __FUNCTION__ . ' => error when invoking sek_update_saved_section_post()' );
} else {
wp_send_json_success( [ 'section_post_id' => $saved_section_post->ID ] );
}
}
// SAVE FILTER
function sek_section_save_clean_id( $seks_data = array() ) {
$new_seks_data = array();
if ( !is_array( $seks_data ) ) {
sek_error_log( __FUNCTION__ . ' error => seks_data should be an array');
return array();
}
foreach ( $seks_data as $key => $value ) {
if ( is_array($value) ) {
$new_seks_data[$key] = sek_section_save_clean_id( $value );
} else {
switch( $key ) {
case 'id' :
if ( is_string( $value ) && false !== strpos( $value, '__nimble__' ) ) {
$value = '__rep__me__';
}
break;
}
$new_seks_data[$key] = $value;
}
}
return $new_seks_data;
}
////////////////////////////////////////////////////////////////
// SECTION REMOVE
// introduced in may 2020 for https://github.com/presscustomizr/nimble-builder/issues/655
// ENABLED WHEN CONSTANT NIMBLE_SECTION_SAVE_ENABLED === true
add_action( 'wp_ajax_sek_remove_user_section', '\Nimble\sek_ajax_remove_user_section' );
/////////////////////////////////////////////////////////////////
// hook : wp_ajax_sek_remove_user_section
function sek_ajax_remove_user_section() {
sek_do_ajax_pre_checks( array( 'check_nonce' => true ) );
// We must have a section_post_name
if ( empty( $_POST['section_post_name']) || !is_string( $_POST['section_post_name'] ) ) {
wp_send_json_error( __FUNCTION__ . '_missing_section_post_name' );
}
// if ( !isset( $_POST['skope_id'] ) || empty( $_POST['skope_id'] ) ) {
// wp_send_json_error( __FUNCTION__ . '_missing_skope_id' );
// }
$section_post_to_remove = sek_get_saved_section_post( sanitize_text_field($_POST['section_post_name']) );
if ( $section_post_to_remove && is_object( $section_post_to_remove ) ) {
// the CPT is moved to Trash instead of permanently deleted when using wp_delete_post()
$r = wp_trash_post( $section_post_to_remove->ID );
if ( is_wp_error( $r ) ) {
wp_send_json_error( __FUNCTION__ . '_removal_error' );
}
} else {
wp_send_json_error( __FUNCTION__ . '_section_post_not_found' );
}
if ( is_wp_error( $section_post_to_remove ) || is_null($section_post_to_remove) || empty($section_post_to_remove) ) {
wp_send_json_error( __FUNCTION__ . '_removal_error' );
} else {
wp_send_json_success( [ 'section_post_removed' => sanitize_text_field($_POST['section_post_name']) ] );
}
}
?>
@@ -0,0 +1,13 @@
<?php
// WP 5.0.0 compat. until the bug is fixed
// this hook fires before the customize changeset is inserter / updated in database
// Removing the wp_targeted_link_rel callback from the 'content_save_pre' filter prevents corrupting the changeset JSON
// more details in this ticket : https://core.trac.wordpress.org/ticket/45292
add_action( 'customize_save_validation_before', '\Nimble\sek_remove_callback_wp_targeted_link_rel' );
function sek_remove_callback_wp_targeted_link_rel( $wp_customize ) {
if ( false !== has_filter( 'content_save_pre', 'wp_targeted_link_rel' ) ) {
remove_filter( 'content_save_pre', 'wp_targeted_link_rel' );
}
};
?>
@@ -0,0 +1,442 @@
<?php
////////////////////////////////////////////////////////////////
// SEK Front Class
if ( !class_exists( 'SEK_Front_Construct' ) ) :
class SEK_Front_Construct {
static $instance;
public $seks_posts = [];// <= march 2020 : used to cache the current local and global sektion posts
public $model = array();//<= when rendering, the current level model
public $parent_model = array();//<= when rendering, the current parent model
public $default_models = array();// <= will be populated to cache the default models when invoking sek_get_default_module_model
public $cached_input_lists = array(); // <= will be populated to cache the input_list of each registered module. Useful when we need to get info like css_selector for a particular input type or id.
public $ajax_action_map = array();
public $default_locations = [
'loop_start' => array( 'priority' => 10 ),
'before_content' => array(),
'after_content' => array(),
'loop_end' => array( 'priority' => 10 ),
];
public $registered_locations = [];
// the 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
];
// the model used when saving a location in db
public $default_location_model = [
'id' => '',
'level' => 'location',
'collection' => [],
'options' => [],
'ver_ini' => NIMBLE_VERSION
];
public $rendered_levels = [];//<= stores the ids of the level rendered with ::render()
public static function get_instance( $params ) {
if ( !isset( self::$instance ) && !( self::$instance instanceof Sek_Nimble_Manager ) ) {
self::$instance = new Sek_Nimble_Manager( $params );
// this hook is used to add_action( 'nimble_front_classes_ready', array( $this, 'sek_register_nimble_global_locations') );
do_action( 'nimble_front_classes_ready', self::$instance );
}
return self::$instance;
}
// store the local and global options
public $local_options = '_not_cached_yet_';
public $local_options_without_tmpl_inheritance = '_not_cached_yet_';//Introduced for site templates, when using function sek_is_inheritance_locally_disabled()
public $global_nimble_options = '_not_cached_yet_';
public $img_smartload_enabled = 'not_cached';
public $video_bg_lazyload_enabled = 'not_cached';//<= for https://github.com/presscustomizr/nimble-builder/issues/287
public $has_local_header_footer = '_not_cached_yet_';//used in sek_maybe_set_local_nimble_header() and sek_maybe_set_local_nimble_footer()
public $has_global_header_footer = '_not_cached_yet_';//used in sek_maybe_set_local_nimble_header() and sek_maybe_set_local_nimble_footer()
public $recaptcha_enabled = '_not_cached_yet_';//enabled in the global options
public $recaptcha_badge_displayed = '_not_cached_yet_';//enabled in the global options
// option key as saved in db => module_type
// is used in _1_6_5_sektions_generate_UI_global_options.js and when normalizing the global option in sek_normalize_global_options_with_defaults()
public static $global_options_map = [
'global_header_footer' => 'sek_global_header_footer',
'global_text' => 'sek_global_text',
'site_templates' => 'sek_site_tmpl_pickers',
'widths' => 'sek_global_widths',
'breakpoint' => 'sek_global_breakpoint',
'performances' => 'sek_global_performances',
'recaptcha' => 'sek_global_recaptcha',
'global_revisions' => 'sek_global_revisions',
'global_reset' => 'sek_global_reset',
'global_imp_exp' => 'sek_global_imp_exp',
'beta_features' => 'sek_global_beta_features'// may 2021 not rendered anymore in ::controls customizer
];
// option key as saved in db => module_type
// is used in _1_6_4_sektions_generate_UI_local_skope_options.js and when normalizing the global option in sek_normalize_local_options_with_defaults()
public static $local_options_map = [
'template' => 'sek_local_template',
'local_header_footer' => 'sek_local_header_footer',
'widths' => 'sek_local_widths',
'custom_css' => 'sek_local_custom_css',
'local_performances' => 'sek_local_performances',
'local_reset' => 'sek_local_reset',
'import_export' => 'sek_local_imp_exp',
'local_revisions' => 'sek_local_revisions'
];
// introduced when implementing import/export feature
// @see https://github.com/presscustomizr/nimble-builder/issues/411
public $img_import_errors = [];
// stores the active module collection
// @see populated in sek_populate_collection_of_contextually_active_modules()
// list of modules displayed on local + global sektions for a givent page.
// populated 'wp'@PHP_INT_MAX and used to
// 1) determine which module should be registered when not customizing or ajaxing. See sek_register_modules_when_not_customizing_and_not_ajaxing()
// 2) determine which assets ( css / js ) is needed for this context. see ::sek_enqueue_front_assets
//
// updated for https://github.com/presscustomizr/nimble-builder/issues/612
public $contextually_active_modules = 'not_set';
public static $ui_picker_modules = [
// UI CONTENT PICKER
'sek_content_type_switcher_module',
'sek_module_picker_module'
];
// JUNE 2020
// PREBUILT AND USER SECTION MODULES ARE REGISTERED IN add_action( 'after_setup_theme', '\Nimble\sek_schedule_module_registration', 50 );
// with sek_register_prebuilt_section_modules(); and sek_register_user_sections_module();
public static $ui_level_modules = [
// UI LEVEL MODULES
'sek_mod_option_switcher_module',
'sek_level_bg_module',
'sek_level_text_module',
'sek_level_border_module',
//'sek_level_section_layout_module',<// deactivated for now. Replaced by sek_level_width_section
'sek_level_height_module',
'sek_level_spacing_module',
'sek_level_spacing_module_for_columns',
'sek_level_width_module',
'sek_level_width_column',
'sek_level_width_section',
'sek_level_anchor_module',
'sek_level_visibility_module',
'sek_level_breakpoint_module'
];
public static $ui_local_global_options_modules = [
// local skope options modules
'sek_local_template',
'sek_local_widths',
'sek_local_custom_css',
'sek_local_reset',
'sek_local_performances',
'sek_local_header_footer',
'sek_local_revisions',
'sek_local_imp_exp',
// global options modules
'sek_global_text',
'sek_global_widths',
'sek_global_breakpoint',
'sek_global_header_footer',
'sek_global_performances',
'sek_global_recaptcha',
'sek_global_revisions',
'sek_global_reset',
'sek_global_imp_exp',
'sek_global_beta_features',
// site template options module
'sek_site_tmpl_pickers'
];
// Is merged with front module when sek_is_header_footer_enabled() === true
// @see sek_register_modules_when_customizing_or_ajaxing
// and sek_register_modules_when_not_customizing_and_not_ajaxing
public static $ui_front_beta_modules = [];
// introduced for https://github.com/presscustomizr/nimble-builder/issues/456
public $global_sections_rendered = false;
// introduced for https://github.com/presscustomizr/nimble-builder/issues/494
// september 2019
// this guid is used to differentiate dynamically rendered content from static content that may include a Nimble generated HTML structure
// an attribute "data-sek-preview-level-guid" is added to each rendered level when customizing or ajaxing
// @see ::render() method
// otherwise the preview UI can be broken
public $preview_level_guid = '_preview_level_guid_not_set_';
// March 2020 : introduction of individual stylesheet for some modules
// October 2020 : implementation of dynamic stylesheet concatenation when generating stylesheets
public $big_module_stylesheet_map = [
'czr_quote_module' => 'quote-module',
'czr_icon_module' => 'icon-module',
'czr_img_slider_module' => 'img-slider-module',
'czr_accordion_module' => 'accordion-module',
'czr_menu_module' => 'menu-module',
'czr_post_grid_module' => 'post-grid-module',
'czr_simple_form_module' => 'simple-form-module',
'czr_image_module' => 'image-module',
'czr_special_img_module' => 'special-image-module',
'czr_advanced_list_module' => 'advanced-list-module',
'czr_social_icons_module' => 'social-icons-module',
'czr_button_module' => 'button-module',
'czr_heading_module' => 'heading-module',
'czr_gallery_module' => 'gallery-module',
];
// March 2020, for https://github.com/presscustomizr/nimble-builder/issues/629
public $google_fonts_print_candidates = 'not_set';// will cache the google font candidates to print in ::_setup_hook_for_front_css_printing_or_enqueuing()
public $css_loader_html = '<div class="sek-css-loader sek-mr-loader"><div></div><div></div><div></div></div>';
// March 2020, for https://github.com/presscustomizr/nimble-builder/issues/649
public $nimble_customizing_or_content_is_printed_on_this_page = false;//<= tells if any Nimble Content has been printed.
// October 2020
public $page_has_local_or_global_sections = 'not_set';//<= set @wp_enqueue_script, used to determine if we should load css, js and fonts assets or not.
// feb 2021, introduced for #478
public $page_has_local_sections = 'not_set';
public $page_has_global_sections = 'not_set';
// April 2020 for https://github.com/presscustomizr/nimble-builder/issues/679
public $is_content_restricted = false; //<= set at 'wp'
// May 2020
// those location properties are set when walking Nimble content on rendering
// @see #705 prevent lazyloading images when in header section.
public $current_location_is_header = false;
public $current_location_is_footer = false;
// September 2020 for https://github.com/presscustomizr/nimble-builder-pro/issues/67
public $local_levels_custom_css = '';
public $global_levels_custom_css = '';
// October 2020
public $rendering = false;//<= set to true when rendering NB content
// October 2020
public $emitted_js_event = [];//<= collection of unique js event emitted with a script like nb_.emit('nb-needs-parallax')
// October 2020, for https://github.com/presscustomizr/nimble-builder/issues/751
public $partial_front_scripts = [
'slider-module' => 'nb-needs-swiper',
'menu-module' => 'nb-needs-menu-js',
'front-parallax' => 'nb-needs-parallax',
'accordion-module' => 'nb-needs-accordion'
];
// janv 2021 => will populate the modules stylesheets already concatenated, so that NB doesn't concatenate a module stylesheet twice for the local css and for the global css (if any)
// see in inc\sektions\_front_dev_php\dyn_css_builder_and_google_fonts_printer\5_0_1_class-sek-dyn-css-builder.php
public $concatenated_module_stylesheets = [];
// April 2021 => added some properties when implementing late escape for attributes
// @see ::render() and base-tmpl PHP files
public $level_css_classes;
public $level_custom_anchor;
public $level_custom_attr;
/////////////////////////////////////////////////////////////////
// <CONSTRUCTOR>
function __construct( $params = array() ) {
if ( did_action('nimble_manager_ready') )
return;
// INITIALIZE THE REGISTERED LOCATIONS WITH THE DEFAULT LOCATIONS
$this->registered_locations = $this->default_locations;
// AJAX
$this->_schedule_front_ajax_actions();
// FRONT ASSETS
$this->_schedule_front_assets_printing();
// CUSTOOMIZER PREVIEW ASSETS
$this->_schedule_preview_assets_printing();
// RENDERING
$this->_schedule_front_rendering();
// RENDERING
$this->_setup_hook_for_front_css_printing_or_enqueuing();
// LOADS SIMPLE FORM
$this->_setup_simple_forms();
// REGISTER NIMBLE WIDGET ZONES
add_action( 'widgets_init', array( $this, 'sek_nimble_widgets_init' ) );
do_action('nimble_manager_ready');
// MAYBE REGISTER PRO UPSELL MODUlES
add_filter('nb_level_module_collection', function( $module_collection ) {
if ( is_array($module_collection) && ( sek_is_pro() || sek_is_upsell_enabled() ) ) {
array_push($module_collection, 'sek_level_cust_css_level' );
array_push($module_collection, 'sek_level_animation_module' );
}
return $module_collection;
});
// see #838
// prevents using persistent cache object systems like Memcached which override the default WP class WP_Object_Cache () which is normally refreshed on each page load )
add_action('init', array( $this, 'sek_clear_cached_objects_when_customizing') );
// FLUSH CACHE OBJECT ON POST SAVE / UPDATE
// for https://github.com/presscustomizr/nimble-builder/issues/867
add_action( 'save_post', array( $this, 'sek_flush_object_cache_on_post_update') );
}//__construct
// @init
public function sek_clear_cached_objects_when_customizing() {
if ( skp_is_customizing() ) {
// Make sure cached objects are cleaned
wp_cache_flush();
}
}
// @save_post
function sek_flush_object_cache_on_post_update() {
wp_cache_flush();
}
// @fired @hook 'widgets_init'
// Creates 10 widget zones
public function sek_nimble_widgets_init() {
if ( sek_is_widget_module_disabled() )
return;
$number_of_widgets = apply_filters( 'nimble_number_of_wp_widgets', 10 );
// Header/footer, widgets module, menu module have been beta tested during 5 months and released in June 2019, in version 1.8.0
$defaults = array(
'name' => '',
'id' => '',
'description' => '',
'class' => '',
'before_widget' => '<aside id="%1$s" class="widget %2$s">',
'after_widget' => '</aside>',
'before_title' => '<h2 class="widget-title">',
'after_title' => '</h2>',
);
for ( $i=1; $i < ( intval( $number_of_widgets) + 1 ); $i++ ) {
$args['id'] = NIMBLE_WIDGET_PREFIX . $i;//'nimble-widget-area-'
$args['name'] = sprintf( __('Nimble widget area #%1$s', 'text_domain_to_replace' ), $i );
$args['description'] = $args['name'];
$args = wp_parse_args( $args, $defaults );
register_sidebar( $args );
}
}
// Invoked @'after_setup_theme'
static function sek_get_front_module_collection() {
$front_module_collection = [
// FRONT MODULES
'czr_simple_html_module',
'czr_tiny_mce_editor_module' => array(
'czr_tiny_mce_editor_module',
'czr_tinymce_child',
'czr_font_child'
),
'czr_image_module' => array(
'czr_image_module',
'czr_image_main_settings_child',
'czr_image_borders_corners_child'
),
'czr_heading_module' => array(
'czr_heading_module',
'czr_heading_child',
'czr_heading_spacing_child',
'czr_font_child'
),
'czr_spacer_module',
'czr_divider_module',
'czr_icon_module' => array(
'czr_icon_module',
'czr_icon_settings_child',
'czr_icon_spacing_border_child',
),
'czr_map_module',
'czr_quote_module' => array(
'czr_quote_module',
'czr_quote_quote_child',
'czr_quote_cite_child',
'czr_quote_design_child',
),
'czr_button_module' => array(
'czr_button_module',
'czr_btn_content_child',
'czr_btn_design_child',
'czr_font_child'
),
// simple form father + children
'czr_simple_form_module' => array(
'czr_simple_form_module',
'czr_simple_form_fields_child',
'czr_simple_form_button_child',
'czr_simple_form_design_child',
'czr_simple_form_fonts_child',
'czr_simple_form_submission_child'
),
'czr_post_grid_module' => array(
'czr_post_grid_module',
'czr_post_grid_main_child',
'czr_post_grid_thumb_child',
'czr_post_grid_metas_child',
'czr_post_grid_fonts_child'
),
// widgets module, menu module have been beta tested during 5 months and released in June 2019, in version 1.8.0
'czr_menu_module' => array(
'czr_menu_module',
'czr_menu_content_child',
'czr_menu_mobile_options',
'czr_font_child'
),
//'czr_menu_design_child',
'czr_social_icons_module' => array(
'czr_social_icons_module',
'czr_social_icons_settings_child',
'czr_social_icons_style_child'
),
'czr_img_slider_module' => array(
'czr_img_slider_module',
'czr_img_slider_collection_child',
'czr_img_slider_opts_child'
),
'czr_accordion_module' => array(
'czr_accordion_module',
'czr_accordion_collection_child',
'czr_accordion_opts_child'
),
'czr_gallery_module' => array(
'czr_gallery_module',
'czr_gallery_collection_child',
'czr_gallery_opts_child'
),
'czr_shortcode_module',
];
if ( !sek_is_widget_module_disabled() ) {
$front_module_collection[] = 'czr_widget_area_module';
}
return apply_filters( 'sek_get_front_module_collection', $front_module_collection );
}
}//class
endif;
?>
@@ -0,0 +1,387 @@
<?php
if ( !class_exists( 'SEK_Front_Ajax' ) ) :
class SEK_Front_Ajax extends SEK_Front_Construct {
// Fired in __construct()
function _schedule_front_ajax_actions() {
add_action( 'wp_ajax_sek_get_content', array( $this, 'sek_get_level_content_for_injection' ) );
// Returns the customize url for the edit button when using Gutenberg editor
// implemented for https://github.com/presscustomizr/nimble-builder/issues/449
// @see assets/admin/js/nimble-gutenberg.js
add_action( 'wp_ajax_sek_get_customize_url_for_nimble_edit_button', array( $this, 'sek_get_customize_url_for_nimble_edit_button' ) );
// This is the list of accepted actions
$this->ajax_action_map = array(
'sek-add-section',
'sek-remove-section',
'sek-duplicate-section',
// fired when dropping a module or a preset_section
'sek-add-content-in-new-nested-sektion',
'sek-add-content-in-new-sektion',
// add, duplicate, remove column is a re-rendering of the parent sektion collection
'sek-add-column',
'sek-remove-column',
'sek-duplicate-column',
'sek-resize-columns',
'sek-refresh-columns-in-sektion',
'sek-add-module',
'sek-remove-module',
'sek-duplicate-module',
'sek-refresh-modules-in-column',
'sek-refresh-stylesheet',
'sek-refresh-level'
);
}
////////////////////////////////////////////////////////////////
// GENERIC HELPER FIRED IN ALL AJAX CALLBACKS
// @param $params = array('check_nonce' => true )
function sek_do_ajax_pre_checks( $params = array() ) {
$params = wp_parse_args( $params, array( 'check_nonce' => true ) );
if ( $params['check_nonce'] ) {
$action = 'save-customize_' . get_stylesheet();
if ( !check_ajax_referer( $action, 'nonce', false ) ) {
wp_send_json_error( array(
'code' => 'invalid_nonce',
'message' => __( __CLASS__ . '::' . __FUNCTION__ . ' => check_ajax_referer() failed.' ),
) );
}
}
if ( !is_user_logged_in() ) {
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' => unauthenticated' );
}
if ( !current_user_can( 'customize' ) ) {
status_header( 403 );
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' => customize_not_allowed' );
} else if ( !isset( $_SERVER['REQUEST_METHOD'] ) || 'POST' !== $_SERVER['REQUEST_METHOD'] ) {
status_header( 405 );
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' => bad_method' );
}
}//sek_do_ajax_pre_checks()
// hook : 'wp_ajax_sek_get_html_for_injection'
function sek_get_level_content_for_injection( $params ) {
$this->sek_do_ajax_pre_checks( array( 'check_nonce' => false ) );
if ( !isset( $_POST['location_skope_id'] ) || empty( $_POST['location_skope_id'] ) ) {
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' => missing skope_id' );
}
// introduced for https://github.com/presscustomizr/nimble-builder/issues/494
// september 2019
// this guid is used to differentiate dynamically rendered content from static content that may include a Nimble generated HTML structure
// an attribute "data-sek-preview-level-guid" is added to each rendered level when customizing or ajaxing
// otherwise the preview UI can be broken
if ( !isset( $_POST['preview-level-guid'] ) || empty( $_POST['preview-level-guid'] ) ) {
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' => missing preview-level-guid' );
}
if ( !isset( $_POST['sek_action'] ) || empty( $_POST['sek_action'] ) ) {
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' => missing sek_action' );
}
$sek_action = sanitize_text_field($_POST['sek_action']);
$exported_setting_validities = array();
// CHECK THE SETTING VALIDITIES BEFORE RENDERING
// When a module has been registered with a sanitize_callback, we can collect the possible problems here before sending the response.
// Then, on ajax.done(), in SekPreviewPrototype::schedulePanelMsgReactions, we will send the setting validities object to the panel
if ( is_customize_preview() ) {
global $wp_customize;
// prepare the setting validities so we can pass them when sending the ajax response
$setting_validities = $wp_customize->validate_setting_values( $wp_customize->unsanitized_post_values() );
$raw_exported_setting_validities = array_map( array( $wp_customize, 'prepare_setting_validity_for_js' ), $setting_validities );
// filter the setting validity to only keep the __nimble__ prefixed ui settings
$exported_setting_validities = array();
foreach( $raw_exported_setting_validities as $setting_id => $validity ) {
// don't consider the not Nimble UI settings, not starting with __nimble__
if ( false === strpos( $setting_id , NIMBLE_PREFIX_FOR_SETTING_NOT_SAVED ) )
continue;
$exported_setting_validities[ $setting_id ] = $validity;
}
}
$html = '';
// is this action possible ?
if ( in_array( $sek_action, $this->ajax_action_map ) ) {
$content_type = null;
if ( array_key_exists( 'content_type', $_POST ) && is_string( $_POST['content_type'] ) ) {
$content_type = sanitize_text_field($_POST['content_type']);
}
// This 'preset_section' === $content_type statement has been introduced when implementing support for multi-section pre-build sections
// @see https://github.com/presscustomizr/nimble-builder/issues/489
if ( 'preset_section' === $content_type ) {
switch ( $sek_action ) {
// when 'sek-add-content-in-new-sektion' is fired, the section has already been populated with a column and a module
case 'sek-add-content-in-new-sektion' :
case 'sek-add-content-in-new-nested-sektion' :
if ( 'preset_section' === $content_type ) {
if ( !array_key_exists( 'collection_of_preset_section_id', $_POST ) || !is_array( $_POST['collection_of_preset_section_id'] ) || empty( $_POST['collection_of_preset_section_id'] ) ) {
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' ' . $sek_action .' => missing param collection_of_preset_section_id when injecting a preset section' );
break;
}
foreach ( $_POST['collection_of_preset_section_id'] as $preset_section_id ) {
$html .= $this->sek_ajax_fetch_content( $sek_action, sanitize_text_field($preset_section_id ));
}
// 'module' === $content_type
} else {
$html = $this->sek_ajax_fetch_content( $sek_action );
}
break;
default :
$html = $this->sek_ajax_fetch_content( $sek_action );
break;
}
} else {
$html = $this->sek_ajax_fetch_content( $sek_action );
}
//sek_error_log(__CLASS__ . '::' . __FUNCTION__ , $html );
if ( is_wp_error( $html ) ) {
wp_send_json_error( $html );
} else {
$response = array(
'contents' => $html,
'setting_validities' => $exported_setting_validities
);
wp_send_json_success( apply_filters( 'sek_content_results', $response, $sek_action ) );
}
} else {
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' => this ajax action ( ' . $sek_action . ' ) is not listed in the map ' );
}
}//sek_get_content_for_injection()
// hook : add_filter( "sek_set_ajax_content___{$action}", array( $this, 'sek_ajax_fetch_content' ) );
// $_POST looks like Array
// (
// [action] => sek_get_content
// [withNonce] => false
// [id] => __nimble__0b7c85561448ab4eb8adb978
// [skope_id] => skp__post_page_home
// [sek_action] => sek-add-section
// [SEKFrontNonce] => 3713b8ac5c
// [customized] => {\"nimble___loop_start[skp__post_page_home]\":{...}}
// )
// @return string
// @param $sek_action is $_POST['sek_action']
// @param $maybe_preset_section_id is used when injecting a collection of preset sections
private function sek_ajax_fetch_content( $sek_action = '', $maybe_preset_section_id = '' ) {
//sek_error_log( __CLASS__ . '::' . __FUNCTION__ . ' POST ?', $_POST );
// Important Notes :
// 1) at this stage => the $_POST['customized'] has already been updated
// so invoking sek_get_skoped_seks() will ensure that we get the latest data
// How $_POST['customized'] is getting populated without a full refresh of the preview ?
// a) Each time the main collection setting id is updated ( @see CZRSeksPrototype::mayBeUpdateSektionsSetting() ), api.Setting.prototype.preview sends a 'setting' event to the preview
// ( note that api.Setting.prototype.preview is overriden by NB to send other events )
// b) when the core customize-preview receives the event, it updates the customized dirties
// c) then when ajaxing, the $_POST['customized'] param is added by WP core with $.ajaxPrefilter() in customize-preview.js
//
// 2) since 'wp' hook has not been fired yet, we need to use the posted skope_id param.
$sektionSettingValue = sek_get_skoped_seks( sanitize_text_field($_POST['location_skope_id']) );
if ( !is_array( $sektionSettingValue ) ) {
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' => invalid sektionSettingValue => it should be an array().' );
return;
}
if ( empty( $sek_action ) ) {
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' => invalid sek_action param' );
return;
}
$sektion_collection = array_key_exists('collection', $sektionSettingValue) ? $sektionSettingValue['collection'] : array();
if ( !is_array( $sektion_collection ) ) {
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' => invalid sektion_collection => it should be an array().' );
return;
}
$candidate_id = '';
$collection = array();
$level_model = array();
$is_stylesheet = false;
switch ( $sek_action ) {
case 'sek-add-section' :
case 'sek-duplicate-section' :
if ( array_key_exists( 'is_nested', $_POST ) && true === json_decode( sanitize_text_field($_POST['is_nested']) ) ) {
// we need to set the parent_mode here to access it later in the ::render method to calculate the column width.
$this->parent_model = sek_get_level_model( sanitize_text_field($_POST[ 'in_sektion' ]), $sektion_collection );
$level_model = sek_get_level_model( sanitize_text_field($_POST[ 'in_column' ]), $sektion_collection );
} else {
//$level_model = sek_get_level_model( $_POST[ 'id' ], $sektion_collection );
$level_model = sek_get_level_model( sanitize_text_field($_POST[ 'id' ]), $sektion_collection );
}
break;
// This $content_type var has been introduced when implementing support for multi-section pre-build sections
// @see https://github.com/presscustomizr/nimble-builder/issues/489
// when 'sek-add-content-in-new-sektion' is fired, the section has already been populated with a column and a module
case 'sek-add-content-in-new-sektion' :
case 'sek-add-content-in-new-nested-sektion' :
$content_type = null;
if ( array_key_exists( 'content_type', $_POST ) && is_string( $_POST['content_type'] ) ) {
$content_type = sanitize_text_field($_POST['content_type']);
}
if ( 'preset_section' === $content_type ) {
if ( !array_key_exists( 'collection_of_preset_section_id', $_POST ) || !is_array( $_POST['collection_of_preset_section_id'] ) ) {
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' ' . $sek_action .' => missing param collection_of_preset_section_id when injecting a preset section' );
break;
}
if ( !is_string( $maybe_preset_section_id ) || empty( $maybe_preset_section_id ) ) {
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' ' . $sek_action .' => invalid preset section id' );
break;
}
$level_id = $maybe_preset_section_id;
// module content type case.
// the level id has been passed the regular way
} else {
$level_id = sanitize_text_field($_POST[ 'id' ]);
}
if ( array_key_exists( 'is_nested', $_POST ) && true === json_decode( sanitize_text_field($_POST['is_nested']) ) ) {
// we need to set the parent_mode here to access it later in the ::render method to calculate the column width.
$this->parent_model = sek_get_level_model( sanitize_text_field($_POST[ 'in_sektion' ]), $sektion_collection );
$level_model = sek_get_level_model( sanitize_text_field($_POST[ 'in_column' ]), $sektion_collection );
} else {
$level_model = sek_get_level_model( $level_id, $sektion_collection );
}
break;
//only used for nested section
case 'sek-remove-section' :
if ( !array_key_exists( 'is_nested', $_POST ) || true !== json_decode( sanitize_text_field($_POST['is_nested'] )) ) {
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' ' . $sek_action .' => the section must be nested in this ajax action' );
break;
} else {
// we need to set the parent_model here to access it later in the ::render method to calculate the column width.
$this->parent_model = sek_get_parent_level_model( sanitize_text_field($_POST[ 'in_column' ]), $sektion_collection );
$level_model = sek_get_level_model( sanitize_text_field($_POST[ 'in_column' ]), $sektion_collection );
}
break;
// We re-render the entire parent sektion collection in all cases
case 'sek-add-column' :
case 'sek-remove-column' :
case 'sek-duplicate-column' :
case 'sek-refresh-columns-in-sektion' :
if ( !array_key_exists( 'in_sektion', $_POST ) || empty( $_POST['in_sektion'] ) ) {
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' ' . $sek_action .' => missing in_sektion param' );
break;
}
// sek_error_log('sektion_collection', $sektion_collection );
$level_model = sek_get_level_model( sanitize_text_field($_POST[ 'in_sektion' ]), $sektion_collection );
break;
// We re-render the entire parent column collection
case 'sek-add-module' :
case 'sek-remove-module' :
case 'sek-refresh-modules-in-column' :
case 'sek-duplicate-module' :
if ( !array_key_exists( 'in_column', $_POST ) || empty( $_POST['in_column'] ) ) {
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' ' . $sek_action .' => missing in_column param' );
break;
}
if ( !array_key_exists( 'in_sektion', $_POST ) || empty( $_POST[ 'in_sektion' ] ) ) {
$this->parent_model = sek_get_parent_level_model( sanitize_text_field($_POST[ 'in_column' ]), $sektion_collection );
} else {
$this->parent_model = sek_get_level_model( sanitize_text_field($_POST[ 'in_sektion' ]), $sektion_collection );
}
$level_model = sek_get_level_model( sanitize_text_field($_POST[ 'in_column' ]), $sektion_collection );
break;
case 'sek-resize-columns' :
if ( !array_key_exists( 'resized_column', $_POST ) || empty( $_POST['resized_column'] ) ) {
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' ' . $sek_action .' => missing resized_column' );
break;
}
$is_stylesheet = true;
break;
case 'sek-refresh-stylesheet' :
$is_stylesheet = true;
break;
case 'sek-refresh-level' :
if ( !array_key_exists( 'id', $_POST ) || empty( $_POST['id'] ) ) {
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' ' . $sek_action .' => missing level id' );
break;
}
if ( !empty( $_POST['level'] ) && 'column' === sanitize_text_field($_POST['level']) ) {
// we need to set the parent_mode here to access it later in the ::render method to calculate the column width.
$this->parent_model = sek_get_parent_level_model( sanitize_text_field($_POST['id']), $sektion_collection );
}
$level_model = sek_get_level_model( sanitize_text_field($_POST[ 'id' ]), $sektion_collection );
break;
}//Switch sek_action
// sek_error_log('LEVEL MODEL WHEN AJAXING', $level_model );
ob_start();
if ( $is_stylesheet ) {
$r = $this->print_or_enqueue_seks_style( sanitize_text_field($_POST['location_skope_id']) );
} else {
if ( 'no_match' == $level_model ) {
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' ' . $sek_action . ' => missing level model' );
ob_end_clean();
return;
}
if ( empty( $level_model ) || !is_array( $level_model ) ) {
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' => empty or invalid $level_model' );
ob_end_clean();
return;
}
// note that in the case of a sektion nested inside a column, the parent_model has been set in the switch{ case : ... } above ,so we can access it in the ::render method to calculate the column width.
$r = $this->render( $level_model );
}
$html = ob_get_clean();
if ( is_wp_error( $r ) ) {
return $r;
} else {
// the $html content should not be empty when ajaxing a template
// it can be empty when ajaxing a stylesheet
if ( !$is_stylesheet && empty( $html ) ) {
// return a new WP_Error that will be intercepted in sek_get_level_content_for_injection
$html = new \WP_Error( 'ajax_fetch_content_error', __CLASS__ . '::' . __FUNCTION__ . ' => no content returned for sek_action : ' . $sek_action );
}
return apply_filters( "sek_set_ajax_content", $html, $sek_action );// this is sent with wp_send_json_success( apply_filters( 'sek_content_results', $html, $sek_action ) );
}
}
////////////////////////////////////////////////////////////////
// USED TO PRINT THE BUTTON EDIT WITH NIMBLE ON POSTS AND PAGES
// when using Gutenberg editor
// implemented for https://github.com/presscustomizr/nimble-builder/issues/449
function sek_get_customize_url_for_nimble_edit_button() {
$this->sek_do_ajax_pre_checks( array( 'check_nonce' => false ) );
if ( !isset( $_POST['nimble_edit_post_id'] ) || empty( $_POST['nimble_edit_post_id'] ) ) {
wp_send_json_error( __CLASS__ . '::' . __FUNCTION__ . ' => missing post_id' );
}
$post_id = sanitize_text_field($_POST['nimble_edit_post_id']);
// Build customize_url
// @see function sek_get_customize_url_when_is_admin()
$return_url_after_customization = '';//"/wp-admin/post.php?post={$post_id}&action=edit";
$customize_url = sek_get_customize_url_for_post_id( $post_id, $return_url_after_customization );
wp_send_json_success( $customize_url );
}
}//class
endif;
?>
File diff suppressed because one or more lines are too long
@@ -0,0 +1,326 @@
<?php
if ( !class_exists( 'SEK_Front_Assets_Customizer_Preview' ) ) :
class SEK_Front_Assets_Customizer_Preview extends SEK_Front_Assets {
// Fired in __construct()
function _schedule_preview_assets_printing() {
add_action( 'wp_footer', array( $this, 'sek_customizr_js_stuff' ), PHP_INT_MAX );
// Load customize preview js
add_action( 'customize_preview_init', array( $this, 'sek_schedule_customize_preview_assets' ) );
}//_schedule_preview_assets_printing
// @'wp_footer'
function sek_customizr_js_stuff() {
if ( !sek_current_user_can_access_nb_ui() )
return;
if( !skp_is_customizing() )
return;
ob_start();
?>
(function(w, d){
nb_.listenTo( 'nb-app-ready', function() {
//PREVIEWED DEVICE ?
//Listen to the customizer previewed device
var _setPreviewedDevice = function() {
wp.customize.preview.bind( 'previewed-device', function( device ) {
nb_.previewedDevice = device;// desktop, tablet, mobile
});
};
if ( wp.customize.preview ) {
_setPreviewedDevice();
} else {
wp.customize.bind( 'preview-ready', function() {
_setPreviewedDevice();
});
}
// REVEAL BG IMAGE ON CHANGE ?
jQuery( function($) {
$('body').on( 'sek-level-refreshed', '[data-sek-level="location"]', function( evt, params ) {
var matches = document.querySelectorAll('div.sek-has-bg');
if ( !nb_.isObject( matches ) || matches.length < 1 )
return;
var imgSrc;
matches.forEach( function(el) {
if ( !nb_.isObject(el) )
return;
// Maybe reveal BG if lazyload is on
if ( nb_.isCustomizing() ) {
nb_.mayBeRevealBG.call(el);
}
});
});
});
});
}(window, document));
<?php
$script = ob_get_clean();
wp_add_inline_script( 'nb-js-init', $script );
}
// enqueue / print customize preview assets
// hook : 'customize_preview_init'
function sek_schedule_customize_preview_assets() {
if ( !sek_current_user_can_access_nb_ui() )
return;
// we don't need those assets when previewing a customize changeset
// added when fixing https://github.com/presscustomizr/nimble-builder/issues/351
if ( sek_is_customize_previewing_a_changeset_post() )
return;
// Load preview ui js tmpl
add_action( 'wp_footer', array( $this, 'sek_print_ui_tmpl' ) );
wp_enqueue_style(
'sek-preview',
sprintf(
'%1$s/assets/czr/sek/css/%2$s' ,
NIMBLE_BASE_URL,
sek_is_dev_mode() ? 'sek-preview.css' : 'sek-preview.min.css'
),
array( 'sek-base' ),
NIMBLE_ASSETS_VERSION,
'all'
);
// Communication between preview and customizer panel
wp_enqueue_script(
'sek-customize-preview',
sprintf(
'%1$s/assets/czr/sek/js/%2$s' ,
NIMBLE_BASE_URL,
sek_is_dev_mode() ? 'ccat-sek-preview.js' : 'ccat-sek-preview.min.js'
),
array( 'customize-preview', 'underscore'),
NIMBLE_ASSETS_VERSION,
true
);
wp_localize_script(
'sek-customize-preview',
'sekPreviewLocalized',
array(
'i18n' => array(
"You've reached the maximum number of columns allowed in this section." => __( "You've reached the maximum number of columns allowed in this section.", 'text_doma'),
"Moving elements between global and local sections is not allowed." => __( "Moving elements between global and local sections is not allowed.", 'text_doma'),
'Something went wrong, please refresh this page.' => __('Something went wrong, please refresh this page.', 'text_doma'),
'Insert here' => __('Insert here', 'text_doma'),
'This content has been created with the WordPress editor.' => __('This content has been created with the WordPress editor.', 'text_domain' ),
'Insert a new section' => __('Insert a new section', 'text_doma' ),
'@location' => __('@location', 'text_domain_to_be'),
'Insert a new global section' => __('Insert a new global section', 'text_doma' ),
'section' => __('section', 'text_doma'),
'header section' => __('header section', 'text_doma'),
'footer section' => __('footer section', 'text_doma'),
'(global)' => __('(global)', 'text_doma'),
'nested section' => __('nested section', 'text_doma'),
'Shift-click to visit the link' => __('Shift-click to visit the link', 'text_doma'),
'External links are disabled when customizing' => __('External links are disabled when customizing', 'text_doma'),
'Link deactivated while previewing' => __('Link deactivated while previewing', 'text_doma')
),
'isDevMode' => sek_is_dev_mode(),
'isPreviewUIDebugMode' => isset( $_GET['preview_ui_debug'] ) || NIMBLE_IS_PREVIEW_UI_DEBUG_MODE,
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'frontNonce' => array( 'id' => 'SEKFrontNonce', 'handle' => wp_create_nonce( 'sek-front-nonce' ) ),
'registeredModules' => CZR_Fmk_Base()->registered_modules,
// introduced for https://github.com/presscustomizr/nimble-builder/issues/494
// september 2019
// this guid is used to differentiate dynamically rendered content from static content that may include a Nimble generated HTML structure
// an attribute "data-sek-preview-level-guid" is added to each rendered level when customizing or ajaxing
// when generating the ui, we check if the localized guid matches the one rendered server side
// otherwise the preview UI can be broken
'previewLevelGuid' => $this->sek_get_preview_level_guid(),
// Assets id
'googleFontsStyleId' => NIMBLE_GOOGLE_FONTS_STYLESHEET_ID,
'globalOptionsStyleId' => NIMBLE_GLOBAL_OPTIONS_STYLESHEET_ID
)
);
wp_enqueue_script( 'jquery-ui-sortable' );
wp_enqueue_style(
'ui-sortable',
'//code.jquery.com/ui/1.12.1/themes/smoothness/jquery-ui.css',
array(),
null,//time(),
$media = 'all'
);
wp_enqueue_script( 'jquery-ui-resizable' );
}
//'wp_footer' in the preview frame
function sek_print_ui_tmpl() {
?>
<script type="text/html" id="sek-tmpl-add-content-button">
<# //console.log( 'data', data ); #>
<div class="sek-add-content-button <# if ( data.is_last ) { #>is_last<# } #>">
<div class="sek-add-content-button-wrapper">
<# var hook_location = '', btn_title = true !== data.is_global_location ? sekPreviewLocalized.i18n['Insert a new section'] : sekPreviewLocalized.i18n['Insert a new global section'], addContentBtnWidth = true !== data.is_global_location ? '83px' : '113px' #>
<# if ( data.location ) {
hook_location = ['(' , sekPreviewLocalized.i18n['@location'] , ':"',data.location , '")'].join('');
} #>
<button title="{{btn_title}} {{hook_location}}" data-sek-click-on="add-content" data-sek-add="section" class="sek-add-content-btn" style="--sek-add-content-btn-width:{{addContentBtnWidth}};">
<span class="sek-click-on-button-icon sek-click-on">+</span><span class="action-button-text">{{btn_title}}</span>
</button>
</div>
</div>
</script>
<?php
$icon_right_side_class = is_rtl() ? 'sek-dyn-left-icons' : 'sek-dyn-right-icons';
$icon_left_side_class = is_rtl() ? 'sek-dyn-right-icons' : 'sek-dyn-left-icons';
?>
<script type="text/html" id="sek-dyn-ui-tmpl-section">
<?php //<# console.log( 'data', data ); #> ?>
<div class="sek-dyn-ui-wrapper sek-section-dyn-ui">
<div class="sek-dyn-ui-inner <?php echo esc_attr($icon_left_side_class); ?>">
<div class="sek-dyn-ui-icons">
<?php if ( sek_is_dev_mode() ) : ?>
<i class="sek-to-json fas fa-code"></i>
<?php endif; ?>
<?php
// Code before implementing https://github.com/presscustomizr/nimble-builder/issues/521 :
/* <# if ( true !== data.is_first_section_in_parent ) { #>
<i data-sek-click-on="move-section-up" class="material-icons sek-click-on" title="<?php _e( 'Move section up', 'text_domain' ); ?>">keyboard_arrow_up</i>
<# } #>
<# if ( true !== data.is_last_section_in_parent ) { #>
<i data-sek-click-on="move-section-down" class="material-icons sek-click-on" title="<?php _e( 'Move section down', 'text_domain' ); ?>">keyboard_arrow_down</i>
<# } #>*/
?>
<i data-sek-click-on="move-section-up" class="material-icons sek-click-on" title="<?php _e( 'Move section up', 'text_domain' ); ?>">keyboard_arrow_up</i>
<i data-sek-click-on="move-section-down" class="material-icons sek-click-on" title="<?php _e( 'Move section down', 'text_domain' ); ?>">keyboard_arrow_down</i>
<?php // if this is a nested section, it has the is_nested property set to true. We don't want to make it draggable for the moment. @todo ?>
<# if ( !data.is_nested ) { #>
<# if ( true !== data.is_global_location ) { #>
<i class="fas fa-arrows-alt sek-move-section" title="<?php _e( 'Drag section', 'text_domain' ); ?>"></i>
<# } #>
<# } #>
<i data-sek-click-on="edit-options" class="material-icons sek-click-on" title="<?php _e( 'Edit section settings', 'text_domain' ); ?>">tune</i>
<# if ( data.can_have_more_columns ) { #>
<i data-sek-click-on="add-column" class="material-icons sek-click-on" title="<?php _e( 'Add a column', 'text_domain' ); ?>">view_column</i>
<# } #>
<i data-sek-click-on="duplicate" class="material-icons sek-click-on" title="<?php _e( 'Duplicate section', 'text_domain' ); ?>">filter_none</i>
<i data-sek-click-on="toggle-save-section-ui" class="sek-save far fa-save" title="<?php _e( 'Save this section', 'text_domain' ); ?>"></i>
<i data-sek-click-on="remove" class="material-icons sek-click-on" title="<?php _e( 'Remove section', 'text_domain' ); ?>">delete_forever</i>
</div>
</div><?php // .sek-dyn-ui-inner ?>
<div class="sek-dyn-ui-location-type" data-sek-click-on="edit-options" title="<?php _e( 'Edit section settings', 'text_domain' ); ?>">
<div class="sek-dyn-ui-location-inner">
<div class="sek-dyn-ui-hamb-menu-wrapper sek-collapsed">
<div class="sek-ham__toggler-spn-wrapper"><span class="line line-1"></span><span class="line line-2"></span><span class="line line-3"></span></div>
</div>
<#
var section_title = true !== data.is_global_location ? sekPreviewLocalized.i18n['section'] : sekPreviewLocalized.i18n['section (global)'];
var section_title = !data.is_nested ? sekPreviewLocalized.i18n['section'] : sekPreviewLocalized.i18n['nested section'];
if ( true === data.is_header_location && !data.is_nested ) {
section_title = sekPreviewLocalized.i18n['header section'];
} else if ( true === data.is_footer_location && !data.is_nested ) {
section_title = sekPreviewLocalized.i18n['footer section'];
}
section_title = true !== data.is_global_location ? section_title : [ section_title, sekPreviewLocalized.i18n['(global)'] ].join(' ');
#>
<div class="sek-dyn-ui-level-type">{{section_title}}</div>
</div><?php // .sek-dyn-ui-location-inner ?>
<div class="sek-minimize-ui" title="<?php _e('Hide this menu if you need to access behind', 'text-domain'); ?>"><i class="far fa-eye-slash"></i></div>
</div><?php // .sek-dyn-ui-location-type ?>
</div><?php // .sek-dyn-ui-wrapper ?>
</script>
<script type="text/html" id="sek-dyn-ui-tmpl-column">
<?php //<# console.log( 'data', data ); #> ?>
<?php
// when a column has nested section(s), its ui might be hidden by deeper columns.
// that's why a CSS class is added to position it on the top right corner, instead of bottom right
// @see https://github.com/presscustomizr/nimble-builder/issues/488
?>
<# var has_nested_section_class = true === data.has_nested_section ? 'sek-col-has-nested-section' : ''; #>
<div class="sek-dyn-ui-wrapper sek-column-dyn-ui {{has_nested_section_class}}">
<div class="sek-dyn-ui-inner <?php echo esc_attr($icon_right_side_class); ?>">
<div class="sek-dyn-ui-icons">
<i class="fas fa-arrows-alt sek-move-column" title="<?php _e( 'Move column', 'text_domain' ); ?>"></i>
<i data-sek-click-on="edit-options" class="material-icons sek-click-on" title="<?php _e( 'Edit column settings', 'text_domain' ); ?>">tune</i>
<# if ( !data.parent_is_last_allowed_nested ) { #>
<i data-sek-click-on="add-section" class="material-icons sek-click-on" title="<?php _e( 'Add a nested section', 'text_domain' ); ?>">account_balance_wallet</i>
<# } #>
<# if ( data.parent_can_have_more_columns ) { #>
<i data-sek-click-on="duplicate" class="material-icons sek-click-on" title="<?php _e( 'Duplicate column', 'text_domain' ); ?>">filter_none</i>
<# } #>
<i data-sek-click-on="pick-content" data-sek-content-type="module" class="material-icons sek-click-on" title="<?php _e( 'Add a module', 'text_domain' ); ?>">add_circle_outline</i>
<# if ( !data.parent_is_single_column ) { #>
<i data-sek-click-on="remove" class="material-icons sek-click-on" title="<?php _e( 'Remove column', 'text_domain' ); ?>">delete_forever</i>
<# } #>
</div>
</div><?php // .sek-dyn-ui-inner ?>
<div class="sek-dyn-ui-location-type" data-sek-click-on="edit-options" title="<?php _e( 'Edit column settings', 'text_domain' ); ?>">
<div class="sek-minimize-ui" title="<?php _e('Hide this menu if you need to access behind', 'text-domain'); ?>"><i class="far fa-eye-slash"></i></div>
<div class="sek-dyn-ui-location-inner">
<div class="sek-dyn-ui-hamb-menu-wrapper sek-collapsed">
<div class="sek-ham__toggler-spn-wrapper"><span class="line line-1"></span><span class="line line-2"></span><span class="line line-3"></span></div>
</div>
<div class="sek-dyn-ui-level-type"><?php _e( 'column', 'text_domain' ); ?></div>
</div><?php // .sek-dyn-ui-location-inner ?>
</div><?php // .sek-dyn-ui-location-type ?>
</div><?php // .sek-dyn-ui-wrapper ?>
</script>
<script type="text/html" id="sek-dyn-ui-tmpl-module">
<div class="sek-dyn-ui-wrapper sek-module-dyn-ui">
<div class="sek-dyn-ui-inner <?php echo esc_attr($icon_left_side_class); ?>">
<div class="sek-dyn-ui-icons">
<i class="fas fa-arrows-alt sek-move-module" title="<?php _e( 'Move module', 'text_domain' ); ?>"></i>
<i data-sek-click-on="edit-module" class="fas fa-pencil-alt sek-tip sek-click-on" title="<?php _e( 'Edit module content', 'text_domain' ); ?>"></i>
<i data-sek-click-on="edit-options" class="material-icons sek-click-on" title="<?php _e( 'Edit module settings', 'text_domain' ); ?>">tune</i>
<i data-sek-click-on="duplicate" class="material-icons sek-click-on" title="<?php _e( 'Duplicate module', 'text_domain' ); ?>">filter_none</i>
<i data-sek-click-on="remove" class="material-icons sek-click-on" title="<?php _e( 'Remove module', 'text_domain' ); ?>">delete_forever</i>
</div>
</div><?php // .sek-dyn-ui-inner ?>
<#
var module_name = !_.isEmpty( data.module_name ) ? data.module_name + ' ' + '<?php _e("module", "text_domain"); ?>' : '<?php _e("module", "text_domain"); ?>';
#>
<div class="sek-dyn-ui-location-type" data-sek-click-on="edit-module" title="<?php _e( 'Edit module settings', 'text_domain' ); ?>">
<div class="sek-dyn-ui-location-inner">
<div class="sek-dyn-ui-hamb-menu-wrapper sek-collapsed">
<div class="sek-ham__toggler-spn-wrapper"><span class="line line-1"></span><span class="line line-2"></span><span class="line line-3"></span></div>
</div>
<div class="sek-dyn-ui-level-type">{{module_name}}</div>
</div>
<div class="sek-minimize-ui" title="<?php _e('Hide this menu if you need to access behind', 'text-domain'); ?>"><i class="far fa-eye-slash"></i></div>
</div>
</div><?php // .sek-dyn-ui-wrapper ?>
</script>
<script type="text/html" id="sek-dyn-ui-tmpl-wp-content">
<div class="sek-dyn-ui-wrapper sek-wp-content-dyn-ui">
<div class="sek-dyn-ui-inner">
<div class="sek-dyn-ui-icons">
<i class="fas fa-pencil-alt sek-edit-wp-content" title="<?php _e( 'Edit this WordPress content', 'text_domain' ); ?>"></i>
</div>
</div><?php // .sek-dyn-ui-inner ?>
<span class="sek-dyn-ui-location-type" title="<?php _e( 'Edit module settings', 'text_domain' ); ?>">
<i class="fab fa-wordpress sek-edit-wp-content" title="<?php _e( 'Edit this WordPress content', 'text_domain' ); ?>"> <?php _e( 'WordPress content', 'text_domain'); ?></i>
</span>
</div><?php // .sek-dyn-ui-wrapper ?>
</script>
<?php
}
}//class
endif;
?>
@@ -0,0 +1,229 @@
<?php
if ( !class_exists( 'SEK_Front_Render_Css' ) ) :
class SEK_Front_Render_Css extends SEK_Front_Render {
// Fired in __construct()
function _setup_hook_for_front_css_printing_or_enqueuing() {
add_action( 'wp_enqueue_scripts', array( $this, 'print_or_enqueue_seks_style'), PHP_INT_MAX );
// wp_add_inline_style for global CSS
add_action( 'wp_head', array( $this, 'sek_enqueue_global_css' ) );
}
// Can be fired :
// 1) on wp_enqueue_scripts or wp_head
// 2) when ajaxing, for actions 'sek-resize-columns', 'sek-refresh-stylesheet'
function print_or_enqueue_seks_style( $skope_id = null ) {
// when this method is fired in a customize preview context :
// - the skope_id has to be built. Since we are after 'wp', this is not a problem.
// - the css rules are printed inline in the <head>
// - we set to hook to wp_head
//
// when the method is fired in an ajax refresh scenario, like 'sek-refresh-stylesheet'
// - the skope_id must be passed as param
// - the css rules are printed inline in the <head>
// - we set the hook to ''
// AJAX REQUESTED STYLESHEET
if ( ( !is_null( $skope_id ) && !empty( $skope_id ) ) && ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
if ( !isset($_POST['local_skope_id']) ) {
sek_error_log( __CLASS__ . '::' . __FUNCTION__ . ' => error missing local_skope_id');
return;
}
$local_skope_id = sanitize_text_field($_POST['local_skope_id']);
// Feb 2021 => for site template #478
$local_skope_id = apply_filters( 'nb_set_skope_id_before_generating_local_front_css', $local_skope_id );
$css_handler_instance = $this->_instantiate_css_handler( array( 'skope_id' => $skope_id, 'is_global_stylesheet' => NIMBLE_GLOBAL_SKOPE_ID === $skope_id ) );
$this->sek_get_global_css_for_ajax();
}
// in a front normal context, the css is enqueued from the already written file.
else {
// Feb 2021 => for site template #478
$local_skope_id = apply_filters( 'nb_set_skope_id_before_generating_local_front_css', skp_build_skope_id() );
// LOCAL SECTIONS STYLESHEET
$this->_instantiate_css_handler( array( 'skope_id' => $local_skope_id ) );
// GLOBAL SECTIONS STYLESHEET
// Can hold rules for global sections and global styling
$this->_instantiate_css_handler( array( 'skope_id' => NIMBLE_GLOBAL_SKOPE_ID, 'is_global_stylesheet' => true ) );
}
$google_fonts_print_candidates = $this->sek_get_gfont_print_candidates( $local_skope_id );
// GOOGLE FONTS
if ( !empty( $google_fonts_print_candidates ) ) {
// When customizing we get the google font content
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
$this->sek_get_gfont_in_ajax( $google_fonts_print_candidates );
} else {
// preload implemented for https://github.com/presscustomizr/nimble-builder/issues/629
if ( !skp_is_customizing() && sek_preload_google_fonts_on_front() ) {
add_action( 'wp_head', array( $this, 'sek_gfont_print_with_preload') );
} else {
// March 2020 added param display=swap => Ensure text remains visible during webfont load #572
wp_enqueue_style(
NIMBLE_GOOGLE_FONTS_STYLESHEET_ID,
sprintf( '//fonts.googleapis.com/css?family=%s&display=swap', $google_fonts_print_candidates ),
array(),
null,
'all'
);
}
}
}
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && empty( $local_skope_id ) ) {
sek_error_log( __CLASS__ . '::' . __FUNCTION__ . ' => the skope_id should not be empty' );
}
}//print_or_enqueue_seks_style
// @param params = array( array( 'skope_id' => NIMBLE_GLOBAL_SKOPE_ID, 'is_global_stylesheet' => true ) )
// fired @'wp_enqueue_scripts'
private function _instantiate_css_handler( $params = array() ) {
$params = wp_parse_args( $params, array( 'skope_id' => '', 'is_global_stylesheet' => false ) );
// Print inline or enqueue ?
$print_mode = Sek_Dyn_CSS_Handler::MODE_FILE;
if ( is_customize_preview() ) {
$print_mode = Sek_Dyn_CSS_Handler::MODE_INLINE;
}
// Which hook ?
$fire_at_hook = '';
if ( !defined( 'DOING_AJAX' ) && is_customize_preview() ) {
$fire_at_hook = 'wp_head';
}
$css_handler_instance = new Sek_Dyn_CSS_Handler( array(
'id' => $params['skope_id'],
'skope_id' => $params['skope_id'],
// property "is_global_stylesheet" has been added when fixing https://github.com/presscustomizr/nimble-builder/issues/273
'is_global_stylesheet' => $params['is_global_stylesheet'],
'mode' => $print_mode,
//these are taken in account only when 'mode' is 'file'
'force_write' => true, //<- write if the file doesn't exist
'force_rewrite' => is_user_logged_in() && current_user_can( 'customize' ), //<- write even if the file exists
'hook' => $fire_at_hook
));
return $css_handler_instance;
}
// When ajaxing, the link#sek-gfonts-{$this->id} gets removed from the dom and replaced by this string
// March 2020 added param display=swap => Ensure text remains visible during webfont load #572
function sek_get_gfont_in_ajax( $print_candidates ) {
if ( !empty( $print_candidates ) ) {
printf('<link rel="stylesheet" id="%1$s" href="%2$s">',
esc_attr(NIMBLE_GOOGLE_FONTS_STYLESHEET_ID),
esc_url("//fonts.googleapis.com/css?family={$print_candidates}&display=swap")
);
}
}
// hook : wp_footer
// fired on front only when not customizing
// March 2020 preload implemented for https://github.com/presscustomizr/nimble-builder/issues/629
// March 2020 added param display=swap => Ensure text remains visible during webfont load #572
function sek_gfont_print_with_preload( $print_candidates = '' ) {
// Check that current page has Nimble content before printing any Google fonts
// For https://github.com/presscustomizr/nimble-builder/issues/649
if ( !Nimble_Manager()->page_has_local_or_global_sections )
return;
// print candidates must be fetched when sek_preload_google_fonts_on_front()
$print_candidates = $this->sek_get_gfont_print_candidates();
if ( !empty( $print_candidates ) ) {
ob_start();
?>
nb_.preloadOrDeferAsset( { id : '<?php echo esc_attr(NIMBLE_GOOGLE_FONTS_STYLESHEET_ID); ?>', as : 'style', href : '//fonts.googleapis.com/css?family=<?php echo esc_attr($print_candidates); ?>&display=swap', scriptEl : document.currentScript } );
<?php
$script = ob_get_clean();
wp_register_script( 'nb_preload_gfonts', '');
wp_enqueue_script( 'nb_preload_gfonts' );
wp_add_inline_script( 'nb_preload_gfonts', $script );
}
}
// invoked when ajaxing during customization
function sek_get_global_css_for_ajax() {
// During customization, always rebuild the css from fresh values instead of relying on the saved option
// because on first call we get the customized option value, but on another one quickly after, we get the current option value in the database
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
$global_css = $this->sek_build_global_options_inline_css();
if ( is_string( $global_css ) && !empty( $global_css ) ) {
printf('<style id="%1$s">%2$s</style>', NIMBLE_GLOBAL_OPTIONS_STYLESHEET_ID, $global_css );
}
}
}
// hook wp_enqueue_script
function sek_enqueue_global_css() {
$global_css = get_option(NIMBLE_OPT_FOR_GLOBAL_CSS);
// following https://developer.wordpress.org/reference/functions/wp_add_inline_script/#comment-5304
wp_register_style( NIMBLE_GLOBAL_OPTIONS_STYLESHEET_ID, false );
wp_enqueue_style( NIMBLE_GLOBAL_OPTIONS_STYLESHEET_ID );
wp_add_inline_style( NIMBLE_GLOBAL_OPTIONS_STYLESHEET_ID, $global_css );
}
// Maybe update global inline style with a filter
// @return a css string
// This CSS is the one generated by global options like global text, global width, global breakpoint
function sek_build_global_options_inline_css() {
return apply_filters('nimble_set_global_inline_style', '');
}
//@return string
// sek_model is passed when customizing in SEK_Front_Render_Css::print_or_enqueue_seks_style()
function sek_get_gfont_print_candidates( $local_skope_id = null ) {
// return the cache version if already set
if ( 'not_set' !== Nimble_Manager()->google_fonts_print_candidates )
return Nimble_Manager()->google_fonts_print_candidates;
$local_skope_id = is_null( $local_skope_id ) ? apply_filters( 'maybe_set_skope_id_for_site_template_css', skp_build_skope_id() ) : $local_skope_id;
// local sections
$local_seks = sek_get_skoped_seks( $local_skope_id );
// global sections
$global_seks = sek_get_skoped_seks( NIMBLE_GLOBAL_SKOPE_ID );
// global options
$global_options = get_option( NIMBLE_OPT_NAME_FOR_GLOBAL_OPTIONS );
$print_candidates = '';
$ffamilies = array();
// Let's build the collection of google fonts from local sections, global sections, global options
if ( is_array( $local_seks ) && !empty( $local_seks['fonts'] ) && is_array( $local_seks['fonts'] ) ) {
$ffamilies = $local_seks['fonts'];
}
if ( is_array( $global_seks ) && !empty( $global_seks['fonts'] ) && is_array( $global_seks['fonts'] ) ) {
$ffamilies = array_merge( $ffamilies, $global_seks['fonts'] );
}
if ( is_array( $global_options ) && !empty( $global_options['fonts'] ) && is_array( $global_options['fonts'] ) ) {
$ffamilies = array_merge( $ffamilies, $global_options['fonts'] );
}
// remove duplicate if any
$ffamilies = array_unique( $ffamilies );
if ( !empty( $ffamilies ) ) {
$ffamilies = implode( "|", $ffamilies );
$print_candidates = str_replace( '|', '%7C', $ffamilies );
$print_candidates = str_replace( '[gfont]', '' , $print_candidates );
}
// cache now
Nimble_Manager()->google_fonts_print_candidates = $print_candidates;
return Nimble_Manager()->google_fonts_print_candidates;
}
}//class
endif;
?>
@@ -0,0 +1,12 @@
<?php
if ( !class_exists( '\Nimble\Sek_Nimble_Manager' ) ) :
final class Sek_Nimble_Manager extends Sek_Simple_Form {}
endif;
function Nimble_Manager( $params = array() ) {
return Sek_Nimble_Manager::get_instance( $params );
}
Nimble_Manager();
?>
@@ -0,0 +1,75 @@
<?php
namespace Nimble;
if ( !defined( 'ABSPATH' ) ) {
exit;
}
// @return bool
function sek_is_dev_mode() {
return ( defined( 'NIMBLE_DEV' ) && NIMBLE_DEV ) || ( defined( 'WP_DEBUG' ) && WP_DEBUG );
}
// @return bool
// Nov 2020 : helper used to display NB CPT in admin
function sek_is_cpt_debug_mode() {
return isset( $_GET['nimble_cpt_debug'] ) || (defined('NIMBLE_CPT_DEBUG_MODE') && NIMBLE_CPT_DEBUG_MODE);
}
if ( !defined( 'NIMBLE_CPT' ) ) { define( 'NIMBLE_CPT' , 'nimble_post_type' ); }
if ( !defined( 'NIMBLE_TEMPLATE_CPT' ) ) { define( 'NIMBLE_TEMPLATE_CPT' , 'nimble_template' ); }
if ( !defined( 'NIMBLE_SECTION_CPT' ) ) { define( 'NIMBLE_SECTION_CPT' , 'nimble_section' ); }
if ( !defined( 'NIMBLE_PREFIX_FOR_SAVED_TMPL' ) ) { define( 'NIMBLE_PREFIX_FOR_SAVED_TMPL' , 'nb_tmpl_' ); }
if ( !defined( 'NIMBLE_PREFIX_FOR_SAVED_SECTION' ) ) { define( 'NIMBLE_PREFIX_FOR_SAVED_SECTION' , 'nb_section_' ); }
if ( !defined( 'NIMBLE_DEPREC_ONE_CSS_FOLDER_NAME' ) ) { define( 'NIMBLE_DEPREC_ONE_CSS_FOLDER_NAME' , 'sek_css' ); }//<= folder name deprecated in july 2020
if ( !defined( 'NIMBLE_DEPREC_TWO_CSS_FOLDER_NAME' ) ) { define( 'NIMBLE_DEPREC_TWO_CSS_FOLDER_NAME' , 'nb_css' ); }//<= folder name deprecated in october 2020
if ( !defined( 'NIMBLE_CSS_FOLDER_NAME' ) ) { define( 'NIMBLE_CSS_FOLDER_NAME' , 'nimble_css' ); }
if ( !defined( 'NIMBLE_OPT_FOR_MODULE_CSS_READING_STATUS' ) ) { define( 'NIMBLE_OPT_FOR_MODULE_CSS_READING_STATUS' , 'nimble_module_css_read_status' ); }
if ( !defined( 'NIMBLE_OPT_SEKTION_POST_INDEX' ) ) { define( 'NIMBLE_OPT_SEKTION_POST_INDEX' , 'nimble_posts_index' ); }
if ( !defined( 'NIMBLE_OPT_PREFIX_FOR_SEKTION_COLLECTION' ) ) { define( 'NIMBLE_OPT_PREFIX_FOR_SEKTION_COLLECTION' , 'nimble___' ); }
if ( !defined( 'NIMBLE_GLOBAL_SKOPE_ID' ) ) { define( 'NIMBLE_GLOBAL_SKOPE_ID' , 'skp__global' ); }
if ( !defined( 'NIMBLE_OPT_NAME_FOR_GLOBAL_OPTIONS' ) ) { define( 'NIMBLE_OPT_NAME_FOR_GLOBAL_OPTIONS' , 'nimble_global_opts' ); }// <= name updated in march 2021 was __nimble_options__
//if ( !defined( 'NIMBLE_OPT_NAME_FOR_SAVED_SEKTIONS' ) ) { define( 'NIMBLE_OPT_NAME_FOR_SAVED_SEKTIONS' , 'nimble_saved_sektions' ); } //<= June 2020 to be removed
if ( !defined( 'NIMBLE_OPT_NAME_FOR_MOST_USED_FONTS' ) ) { define( 'NIMBLE_OPT_NAME_FOR_MOST_USED_FONTS' , 'nimble_most_used_fonts' ); }
if ( !defined( 'NIMBLE_OPT_FOR_GLOBAL_CSS' ) ) { define( 'NIMBLE_OPT_FOR_GLOBAL_CSS' , 'nimble_global_css' ); }
if ( !defined( 'NIMBLE_OPT_NAME_FOR_SECTION_JSON' ) ) { define( 'NIMBLE_OPT_NAME_FOR_SECTION_JSON' , 'nimble_prebuild_sections' ); }// <= name updated in march 2021, was nb_prebuild_section_json
if ( !defined( 'NIMBLE_OPT_NAME_FOR_BACKWARD_FIXES' ) ) { define( 'NIMBLE_OPT_NAME_FOR_BACKWARD_FIXES' , 'nb_backward_fixes' ); }
if ( !defined( 'NIMBLE_OPT_NAME_FOR_SHORTCODE_PARSING' ) ) { define( 'NIMBLE_OPT_NAME_FOR_SHORTCODE_PARSING' , 'nb_shortcodes_parsed_in_czr' ); }
if ( !defined( 'NIMBLE_OPT_NAME_FOR_DISABLING_WIDGET_MODULE' ) ) { define( 'NIMBLE_OPT_NAME_FOR_DISABLING_WIDGET_MODULE' , 'nb_widgets_disabled_in_czr' ); }
if ( !defined( 'NIMBLE_OPT_NAME_FOR_DEBUG_MODE' ) ) { define( 'NIMBLE_OPT_NAME_FOR_DEBUG_MODE' , 'nb_debug_mode_active' ); }
if ( !defined( 'NIMBLE_PREFIX_FOR_SETTING_NOT_SAVED' ) ) { define( 'NIMBLE_PREFIX_FOR_SETTING_NOT_SAVED' , '__nimble__' ); }
if ( !defined( 'NIMBLE_WIDGET_PREFIX' ) ) { define( 'NIMBLE_WIDGET_PREFIX' , 'nimble-widget-area-' ); }
if ( !defined( 'NIMBLE_ASSETS_VERSION' ) ) { define( 'NIMBLE_ASSETS_VERSION', sek_is_dev_mode() ? time() : NIMBLE_VERSION ); }
if ( !defined( 'NIMBLE_MODULE_ICON_PATH' ) ) { define( 'NIMBLE_MODULE_ICON_PATH' , NIMBLE_BASE_URL . '/assets/czr/sek/icons/modules/' ); }
if ( !defined( 'NIMBLE_DETACHED_TINYMCE_TEXTAREA_ID') ) { define( 'NIMBLE_DETACHED_TINYMCE_TEXTAREA_ID' , 'czr-customize-content_editor' ); }
// TRANSIENTS ID
if ( !defined( 'NIMBLE_WELCOME_NOTICE_ID' ) ) { define ( 'NIMBLE_WELCOME_NOTICE_ID', 'nimble-welcome-notice-12-2018' ); }
//mt_rand(0, 65535) . 'test-nimble-feedback-notice-04-2019'
if ( !defined( 'NIMBLE_FEEDBACK_NOTICE_ID' ) ) { define ( 'NIMBLE_FEEDBACK_NOTICE_ID', 'nimble-feedback-notice-01-2022' ); }
if ( !defined( 'NIMBLE_FAWESOME_TRANSIENT_ID' ) ) { define ( 'NIMBLE_FAWESOME_TRANSIENT_ID', 'sek_font_awesome_october_2021' ); }
if ( !defined( 'NIMBLE_GFONTS_TRANSIENT_ID' ) ) { define ( 'NIMBLE_GFONTS_TRANSIENT_ID', 'sek_gfonts_march_2020' ); }
if ( !defined( 'NIMBLE_FEEDBACK_STATUS_TRANSIENT_ID' ) ) { define ( 'NIMBLE_FEEDBACK_STATUS_TRANSIENT_ID', 'nimble_feedback_status' ); }
if ( !defined( 'NIMBLE_API_CHECK_TRANSIENT_ID' ) ) { define ( 'NIMBLE_API_CHECK_TRANSIENT_ID', 'nimble_version_check_for_api' ); }
if ( !defined( 'NIMBLE_GOOGLE_FONTS_STYLESHEET_ID' ) ) { define ( 'NIMBLE_GOOGLE_FONTS_STYLESHEET_ID', 'sek-gfonts-local-and-global' ); }
if ( !defined( 'NIMBLE_GLOBAL_OPTIONS_STYLESHEET_ID' ) ) { define ( 'NIMBLE_GLOBAL_OPTIONS_STYLESHEET_ID', 'nimble-global-inline-style' ); }
if ( !defined( "NIMBLE_DATA_API_URL_V2" ) ) { define( "NIMBLE_DATA_API_URL_V2",
( defined('NIMBLE_FETCH_API_LOCALLY') && NIMBLE_FETCH_API_LOCALLY && defined('NIMBLE_LOCAL_API_URL') ) ? NIMBLE_LOCAL_API_URL : 'https://api.nimblebuilder.com/wp-json/nimble/v2/cravan'
); }
if ( !defined( 'NIMBLE_PRO_URL' ) ) { define ( 'NIMBLE_PRO_URL', 'https://presscustomizr.com/nimble-builder-pro' ); }
// @return bool
function sek_is_debug_mode() {
return isset( $_GET['nimble_debug'] ) || sek_booleanize_checkbox_val( get_option( NIMBLE_OPT_NAME_FOR_DEBUG_MODE ) );
}
?>
@@ -0,0 +1,133 @@
<?php
/* ------------------------------------------------------------------------- *
* LOCATIONS UTILITIES
/* ------------------------------------------------------------------------- */
// @return array
function sek_get_locations() {
if ( !is_array( Nimble_Manager()->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;
}
?>
@@ -0,0 +1,59 @@
<?php
// @return bool
// march 2020 introduced for https://github.com/presscustomizr/nimble-builder/issues/629
// Firefox doesn not support preload
// IE is supposed to support it, but tests show that google fonts may not be loaded on each page refresh
function sek_preload_google_fonts_on_front() {
// When preload is active, browser support is checked with javascript
// with a fallback on regular style fetching
// if(strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== FALSE) // 'Internet explorer'
// return;
// elseif(strpos($_SERVER['HTTP_USER_AGENT'], 'Firefox') !== FALSE) // 'Mozilla Firefox'
// return;
$glob_perf = sek_get_global_option_value( 'performances' );
if ( !is_null( $glob_perf ) && is_array( $glob_perf ) && !empty( $glob_perf['preload_google_fonts'] ) ) {
return sek_booleanize_checkbox_val( $glob_perf['preload_google_fonts'] );
}
return false;
}
// @return bool
// march 2020 introduced for https://github.com/presscustomizr/nimble-builder/issues/635
function sek_load_front_assets_dynamically() {
$glob_perf = sek_get_global_option_value( 'performances' );
if ( !is_null( $glob_perf ) && is_array( $glob_perf ) && !empty( $glob_perf['load_assets_in_ajax'] ) ) {
return !skp_is_customizing() && sek_booleanize_checkbox_val( $glob_perf['load_assets_in_ajax'] );
}
return false;
}
// Adds defer attribute to enqueued / registered scripts.
// fired @wp_enqueue_scripts
function sek_defer_script($handle) {
// Adds defer attribute to enqueued / registered scripts.
wp_script_add_data( $handle, 'defer', true );
}
// oct 2020 => introduction of a normalized way to emit a js event to NB front api
// in particular to make sure NB doesn't print a <script> twice to emit the same event
function sek_emit_js_event( $event = '', $echo = true ) {
$emitted = Nimble_Manager()->emitted_js_event;
if ( !is_string($event) || in_array($event, $emitted) )
return;
$emitted[] = $event;
Nimble_Manager()->emitted_js_event = $emitted;
if ( $echo ) {
$html = sprintf('(function(){if(window.nb_){nb_.emit("%1$s");}})();', $event );
wp_register_script( 'nb_emit_' . $event, '');
wp_enqueue_script( 'nb_emit_' . $event );
wp_add_inline_script( 'nb_emit_' . $event, $html );
} else {
$html = sprintf('<script>(function(){if(window.nb_){nb_.emit("%1$s");}})();</script>', $event );
return $html;
}
}
?>
@@ -0,0 +1,315 @@
<?php
/* ------------------------------------------------------------------------- *
* IMAGE HELPER
/* ------------------------------------------------------------------------- */
// @see https://codex.wordpress.org/Function_Reference/get_intermediate_image_sizes
// used in sek_get_select_options_for_input_id()
function sek_get_img_sizes() {
global $_wp_additional_image_sizes;
$sizes = array();
$to_return = array(
'original' => __('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('<img %1$s src="%2$s" data-sek-src="%3$s" %4$s>',
$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;
}
?>
@@ -0,0 +1,158 @@
<?php
/* ------------------------------------------------------------------------- *
* Page Menu for menu module
/* ------------------------------------------------------------------------- */
/**
* Display or retrieve list of pages with optional home link.
* Modified copy of wp_page_menu()
* @return string html menu
*/
function sek_page_menu_fallback( $args = array() ) {
$defaults = array('show_home' => 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 .= '<li ' . $class . '><a href="' . home_url( '/' ) . '">' . $args['link_before'] . $text . $args['link_after'] . '</a></li>';
// If the front page is a page, add it to the exclude list
if (get_option('show_on_front') == 'page') {
if ( !empty( $list_args['exclude'] ) ) {
$list_args['exclude'] .= ',';
} else {
$list_args['exclude'] = '';
}
$list_args['exclude'] .= get_option('page_on_front');
}
}
$list_args['echo'] = false;
$list_args['title_li'] = '';
// limit the number of pages displayed, home excluded from the count if included
$list_args['number'] = 4;
$menu .= str_replace( array( "\r", "\n", "\t" ), '', sek_list_pages( $list_args ) );
// if ( $menu )
// $menu = '<ul>' . $menu . '</ul>';
//$menu = '<div class="' . esc_attr($args['menu_class']) . '">' . $menu . "</div>\n";
if ( $menu ) {
$menu = '<ul class="' . esc_attr( $args['menu_class'] ) . '">' . $menu . '</ul>';
}
//$menu = apply_filters( 'wp_page_menu', $menu, $args );
if ( $args['echo'] )
echo wp_kses_post($menu);
else
return $menu;
}
/**
* Retrieve or display list of pages in list (li) format.
* Modified copy of wp_list_pages
* @return string HTML list of pages.
*/
function sek_list_pages( $args = '' ) {
$defaults = array(
'depth' => 0,
'show_date' => '',
'date_format' => get_option( 'date_format' ),
'child_of' => 0,
'exclude' => '',
'title_li' => __( 'Pages', 'text_domain_to_replace' ),
'echo' => 1,
'authors' => '',
'sort_column' => 'menu_order, post_title',
'link_before' => '',
'link_after' => '',
'walker' => ''
);
$r = wp_parse_args( $args, $defaults );
$output = '';
$current_page = 0;
// sanitize, mostly to keep spaces out
$r['exclude'] = preg_replace( '/[^0-9,]/', '', $r['exclude'] );
// Allow plugins to filter an array of excluded pages (but don't put a nullstring into the array)
$exclude_array = ( $r['exclude'] ) ? explode( ',', $r['exclude'] ) : array();
$r['exclude'] = implode( ',', apply_filters( 'wp_list_pages_excludes', $exclude_array ) );
// Query pages.
$r['hierarchical'] = 0;
$pages = get_pages( $r );
if ( !empty( $pages ) ) {
if ( $r['title_li'] ) {
$output .= '<li class="pagenav">' . $r['title_li'] . '<ul>';
}
global $wp_query;
if ( is_page() || is_attachment() || $wp_query->is_posts_page ) {
$current_page = get_queried_object_id();
} elseif ( is_singular() ) {
$queried_object = get_queried_object();
if ( is_post_type_hierarchical( $queried_object->post_type ) ) {
$current_page = $queried_object->ID;
}
}
$output .= sek_walk_page_tree( $pages, $r['depth'], $current_page, $r );
if ( $r['title_li'] ) {
$output .= '</ul></li>';
}
}
$html = apply_filters( 'wp_list_pages', $output, $r );
if ( $r['echo'] ) {
echo wp_kses_post($html);
} else {
return $html;
}
}
/**
* Retrieve HTML list content for page list.
*
* @uses Walker_Page to create HTML list content.
* @since 2.1.0
* @see Walker_Page::walk() for parameters and return description.
*/
function sek_walk_page_tree( $pages, $depth, $current_page, $r ) {
// if ( empty($r['walker']) )
// $walker = new Walker_Page;
// else
// $walker = $r['walker'];
$walker = new \Walker_Page;
foreach ( (array) $pages as $page ) {
if ( $page->post_parent ) {
$r['pages_with_children'][ $page->post_parent ] = true;
}
}
$args = array( $pages, $depth, $r, $current_page );
return call_user_func_array(array($walker, 'walk'), $args);
}
function sek_get_user_created_menus() {
// if ( !skp_is_customizing() )
// return array();
$all_menus = get_terms( 'nav_menu', array( 'hide_empty' => true ) );
$user_menus = array();
foreach ( $all_menus as $menu_obj ) {
if ( is_string( $menu_obj->slug ) && !empty( $menu_obj->slug ) && !empty( $menu_obj->name ) ) {
$user_menus[ $menu_obj->slug ] = $menu_obj->name;
}
}
// sek_error_log( 'sek_get_user_created_menus', array_merge(
// array( 'nimble_page_menu' => __('Default page menu', 'text_domain_to_replace') )
// , $user_menus ) );
return array_merge(
array( 'nimble_page_menu' => __('Default page menu', 'text_domain_to_replace') )
, $user_menus );
}
?>
@@ -0,0 +1,748 @@
<?php
/* ------------------------------------------------------------------------- *
* MODULES COLLECTION
/* ------------------------------------------------------------------------- */
// introduced when implementing the level tree #359
function sek_get_module_collection() {
return apply_filters( 'sek_get_module_collection', array(
array(
'content-type' => 'preset_section',
'content-id' => 'two_columns',
'title' => __( 'Two Columns', 'text_doma' ),
'icon' => 'Nimble_2-columns_icon.svg'
),
array(
'content-type' => 'preset_section',
'content-id' => 'three_columns',
'title' => __( 'Three Columns', 'text_doma' ),
'icon' => 'Nimble_3-columns_icon.svg'
),
array(
'content-type' => 'preset_section',
'content-id' => 'four_columns',
'title' => __( 'Four Columns', 'text_doma' ),
'icon' => 'Nimble_4-columns_icon.svg'
),
array(
'content-type' => 'module',
'content-id' => 'czr_tiny_mce_editor_module',
'title' => __( 'Rich Text Editor', 'text_doma' ),
'icon' => 'Nimble_rich-text-editor_icon.svg'
),
array(
'content-type' => 'module',
'content-id' => 'czr_image_module',
'title' => __( 'Image', 'text_doma' ),
'icon' => 'Nimble__image_icon.svg'
),
array(
'content-type' => 'module',
'content-id' => 'czr_gallery_module',
'title' => __( 'Image gallery', 'text_doma' ),
'icon' => 'Nimble_gallery_icon.svg'
),
array(
'content-type' => 'module',
'content-id' => 'czr_heading_module',
'title' => __( 'Heading', 'text_doma' ),
'icon' => 'Nimble__heading_icon.svg'
),
array(
'content-type' => 'module',
'content-id' => 'czr_icon_module',
'title' => __( 'Icon', 'text_doma' ),
'icon' => 'Nimble__icon_icon.svg'
),
array(
'content-type' => 'module',
'content-id' => 'czr_special_img_module',
'title' => __( 'Nimble Image', 'text_doma' ),
'icon' => 'Nimble_img_icon.svg',
'is_pro' => !sek_is_pro(),
'active' => sek_is_pro()
),
array(
'content-type' => 'module',
'content-id' => 'czr_button_module',
'title' => __( 'Button', 'text_doma' ),
'icon' => 'Nimble_button_icon.svg'
),
array(
'content-type' => 'module',
'content-id' => 'czr_img_slider_module',
'title' => __( 'Image & Text Carousel', 'text_doma' ),
'icon' => 'Nimble_slideshow_icon.svg'
),
array(
'content-type' => 'module',
'content-id' => 'czr_accordion_module',
'title' => __( 'Accordion', 'text_doma' ),
'icon' => 'Nimble_accordion_icon.svg'
),
array(
'content-type' => 'module',
'content-id' => 'czr_advanced_list_module',
'title' => __( 'Advanced List', 'text_doma' ),
'icon' => 'Nimble__advanced_list_icon.svg',
'is_pro' => !sek_is_pro(),
'active' => sek_is_pro()
),
array(
'content-type' => 'module',
'content-id' => 'czr_simple_html_module',
'title' => __( 'Html Content', 'text_doma' ),
'icon' => 'Nimble_html_icon.svg'
),
array(
'content-type' => 'module',
'content-id' => 'czr_post_grid_module',
'title' => __( 'Post Grid', 'text_doma' ),
'icon' => 'Nimble_posts-list_icon.svg'
),
array(
'content-type' => 'module',
'content-id' => 'czr_quote_module',
'title' => __( 'Quote', 'text_doma' ),
'icon' => 'Nimble_quote_icon.svg'
),
array(
'content-type' => 'module',
'content-id' => 'czr_shortcode_module',
'title' => __( 'Shortcode', 'text_doma' ),
'icon' => 'Nimble_shortcode_icon.svg'
),
array(
'content-type' => 'module',
'content-id' => 'czr_spacer_module',
'title' => __( 'Spacer', 'text_doma' ),
'icon' => 'Nimble__spacer_icon.svg'
),
array(
'content-type' => 'module',
'content-id' => 'czr_divider_module',
'title' => __( 'Divider', 'text_doma' ),
'icon' => 'Nimble__divider_icon.svg'
),
array(
'content-type' => 'module',
'content-id' => 'czr_map_module',
'title' => __( 'Map', 'text_doma' ),
'icon' => 'Nimble_map_icon.svg'
),
array(
'content-type' => 'module',
'content-id' => 'czr_widget_area_module',
'title' => __( 'WordPress widget area', 'text_doma' ),
'font_icon' => '<i class="fab fa-wordpress-simple"></i>',
'active' => !sek_is_widget_module_disabled()
),
array(
'content-type' => 'module',
'content-id' => 'czr_social_icons_module',
'title' => __( 'Social Profiles', 'text_doma' ),
'icon' => 'Nimble_social_icon.svg'
),
array(
'content-type' => 'module',
'content-id' => 'czr_simple_form_module',
'title' => __( 'Simple Contact Form', 'text_doma' ),
'icon' => 'Nimble_contact-form_icon.svg'
),
array(
'content-type' => 'module',
'content-id' => 'czr_menu_module',
'title' => __( 'Menu', 'text_doma' ),
'font_icon' => '<i class="material-icons">menu</i>'
//'active' => sek_are_beta_features_enabled()
)
));
}
// September 2020 : filter the collection of modules
// Removes pro upsell modules if sek_is_upsell_enabled() is false
// filter declared in inc/sektions/_front_dev_php/_constants_and_helper_functions/0_0_5_modules_helpers.php
add_filter('sek_get_module_collection', function( $collection ) {
if ( sek_is_upsell_enabled() )
return $collection;
$filtered = [];
foreach ($collection as $mod => $mod_data) {
if ( array_key_exists('is_pro', $mod_data) && $mod_data['is_pro'] )
continue;
$filtered[] = $mod_data;
}
return $filtered;
});
// @return void()
// Fired in 'wp_enqueue_scripts'
// Recursively sniff the local and global sections to populate Nimble_Manager()->contextually_active_modules
// introduced for https://github.com/presscustomizr/nimble-builder/issues/612
function sek_populate_collection_of_contextually_active_modules( $skope_id = '', $recursive_data = null, $module_collection = null ) {
$skope_id = empty( $skope_id ) ? skp_get_skope_id() : $skope_id;
if ( is_null( $recursive_data ) ) {
$local_skope_settings = sek_get_skoped_seks( $skope_id );
$local_collection = ( is_array( $local_skope_settings ) && !empty( $local_skope_settings['collection'] ) ) ? $local_skope_settings['collection'] : array();
$global_skope_settings = sek_get_skoped_seks( NIMBLE_GLOBAL_SKOPE_ID );
$global_collection = ( is_array( $global_skope_settings ) && !empty( $global_skope_settings['collection'] ) ) ? $global_skope_settings['collection'] : array();
$recursive_data = array_merge( $local_collection, $global_collection );
}
if ( is_null( $module_collection ) ) {
// make sure Nimble_Manager()->contextually_active_modules is initialized as an array before starting populating it.
$module_collection = 'not_set' === Nimble_Manager()->contextually_active_modules ? [] : Nimble_Manager()->contextually_active_modules;
}
foreach ($recursive_data as $key => $value) {
if ( is_array( $value ) && array_key_exists('module_type', $value) ) {
$module_type = $value['module_type'];
if ( !array_key_exists($module_type, $module_collection) ) {
$module_collection[$module_type] = [];
}
if ( !in_array( $value['id'], $module_collection[$module_type] ) ) {
$module_collection[$module_type][] = $value['id'];
}
} else if ( is_array( $value ) ) {
$module_collection = sek_populate_collection_of_contextually_active_modules( $skope_id, $value, $module_collection);
}
}
Nimble_Manager()->contextually_active_modules = $module_collection;
return Nimble_Manager()->contextually_active_modules;
}
// return the cached collection or build it when needed
function sek_get_collection_of_contextually_active_modules( $skope_id = '' ) {
$skope_id = empty( $skope_id ) ? skp_get_skope_id() : $skope_id;
if ( 'not_set' === Nimble_Manager()->contextually_active_modules ) {
return sek_populate_collection_of_contextually_active_modules( $skope_id );
}
return Nimble_Manager()->contextually_active_modules;
}
/* ------------------------------------------------------------------------- *
* REGISTERED MODULES => GET PROPERTY
/* ------------------------------------------------------------------------- */
// Helper
function sek_get_registered_module_type_property( $module_type, $property = '' ) {
// check introduced since https://github.com/presscustomizr/nimble-builder/issues/432
// may not be mandatory
if ( !class_exists('\Nimble\CZR_Fmk_Base') ) {
sek_error_log( __FUNCTION__ . ' => error => CZR_Fmk_Base not loaded' );
return;
}
// registered modules
$registered_modules = CZR_Fmk_Base()->registered_modules;
if ( !array_key_exists( $module_type, $registered_modules ) ) {
sek_error_log( __FUNCTION__ . ' => ' . $module_type . ' not registered.' );
return;
}
if ( array_key_exists( $property , $registered_modules[ $module_type ] ) ) {
return $registered_modules[ $module_type ][$property];
}
return;
}
/* ------------------------------------------------------------------------- *
* GET THE INPUT VALUE OF A GIVEN MODULE MODEL
/* ------------------------------------------------------------------------- */
// Recursive helper
// Handles simple model and multidimensional module model ( father - children ), like
// Array
// (
// [quote_content] => Array
// (
// [quote_text] => Hey, careful, man, there's a beverage here!
// [quote_font_size_css] => Array
// (
// [desktop] => 29px
// [mobile] => 12px
// )
// [quote_letter_spacing_css] => 7
// [quote___flag_important] => 1
// )
// [cite_content] => Array
// (
// [cite_text] => The Dude in <a href="https://www.imdb.com/title/tt0118715/quotes/qt0464770" rel="nofollow noopener noreferrer" target="_blank">The Big Lebowski</a>
// [cite_font_style_css] => italic
// )
// [design] => Array
// (
// [quote_design] => border-before
// )
// )
// Helper
// @param $input_id ( string )
// @param $module_model ( array )
function sek_get_input_value_in_module_model( $input_id, $module_model ) {
if ( !is_string( $input_id ) ) {
sek_error_log( __FUNCTION__ . ' => error => the $input_id param should be a string', $module_model);
return;
}
if ( !is_array( $module_model ) ) {
sek_error_log( __FUNCTION__ . ' => error => the $module_model param should be an array', $module_model );
return;
}
$input_value = '_not_set_';
foreach ( $module_model as $key => $data ) {
if ( $input_value !== '_not_set_' )
break;
if ( $input_id === $key ) {
$input_value = $data;
break;
} else {
if ( is_array( $data ) ) {
$input_value = sek_get_input_value_in_module_model( $input_id, $data );
}
}
}
return $input_value;
}
/* ------------------------------------------------------------------------- *
* REGISTERED MODULES => DEFAULT MODULE MODEL
/* ------------------------------------------------------------------------- */
// @param (string) module_type
// Walk the registered modules tree and generates the module default if not already cached
// used :
// - in sek_normalize_module_value_with_defaults(), when preprocessing the module model before printing the module template. @see SEK_Front::render()
// - when setting the css of a level option. @see for example : sek_add_css_rules_for_bg_border_background()
// @return array()
function sek_get_default_module_model( $module_type = '' ) {
$default = array();
if ( empty( $module_type ) || is_null( $module_type ) )
return $default;
// check introduced since https://github.com/presscustomizr/nimble-builder/issues/432
// may not be mandatory
if ( !class_exists('\Nimble\CZR_Fmk_Base') ) {
sek_error_log( __FUNCTION__ . ' => error => CZR_Fmk_Base not loaded' );
return $default;
}
// Did we already cache it ?
$default_models = Nimble_Manager()->default_models;
if ( !empty( $default_models[ $module_type ] ) ) {
$default = $default_models[ $module_type ];
} else {
$registered_modules = CZR_Fmk_Base()->registered_modules;
if ( !array( $registered_modules ) || !CZR_Fmk_Base()->czr_is_module_registered($module_type) ) {
sek_error_log( __FUNCTION__ . ' => ' . $module_type . ' is not registered in the $CZR_Fmk_Base_fn()->registered_modules;' );
return $default;
}
// Is this module a father ?
if ( !empty( $registered_modules[ $module_type ]['is_father'] ) && true === $registered_modules[ $module_type ]['is_father'] ) {
if ( empty( $registered_modules[ $module_type ][ 'children' ] ) ) {
sek_error_log( __FUNCTION__ . ' => ' . $module_type . ' missing children modules' );
return $default;
}
if ( !is_array( $registered_modules[ $module_type ][ 'children' ] ) ) {
sek_error_log( __FUNCTION__ . ' => ' . $module_type . ' children modules should be an array' );
return $default;
}
foreach ( $registered_modules[ $module_type ][ 'children' ] as $opt_group => $child_mod_type ) {
if ( empty( $registered_modules[ $child_mod_type ][ 'tmpl' ] ) ) {
sek_error_log( __FUNCTION__ . ' => ' . $child_mod_type . ' => missing "tmpl" property => impossible to build the father default model.' );
continue;
}
$default[$opt_group] = _sek_build_default_model( $registered_modules[ $child_mod_type ][ 'tmpl' ] );
}
}
// Not father module case
else {
if ( empty( $registered_modules[ $module_type ][ 'tmpl' ] ) ) {
sek_error_log( __FUNCTION__ . ' => ' . $module_type . ' => missing "tmpl" property => impossible to build the default model.' );
return $default;
}
// Build
$default = _sek_build_default_model( $registered_modules[ $module_type ][ 'tmpl' ] );
}
// Cache
$default_models[ $module_type ] = $default;
Nimble_Manager()->default_models = $default_models;
//sek_error_log( __FUNCTION__ . ' => $default_models', $default_models );
}
return $default;
}
// @return array() default model
// Walk recursively the 'tmpl' property of the module
// 'tmpl' => array(
// 'pre-item' => array(
// 'social-icon' => array(
// 'input_type' => 'select',
// 'title' => __('Select an icon', 'text_doma')
// ),
// ),
// 'mod-opt' => array(
// 'social-size' => array(
// 'input_type' => 'number',
// 'title' => __('Size in px', 'text_doma'),
// 'step' => 1,
// 'min' => 5,
// 'transport' => 'postMessage'
// )
// ),
// 'item-inputs' => array(
// 'item-inputs' => array(
// 'tabs' => array(
// array(
// 'title' => __('Content', 'text_doma'),
// //'attributes' => 'data-sek-device="desktop"',
// 'inputs' => array(
// 'content' => array(
// 'input_type' => 'detached_tinymce_editor',
// 'title' => __('Content', 'text_doma')
// ),
// 'h_alignment_css' => array(
// 'input_type' => 'h_text_alignment',
// 'title' => __('Alignment', 'text_doma'),
// 'default' => is_rtl() ? 'right' : 'left',
// 'refresh_markup' => false,
// 'refresh_stylesheet' => true
// )
// )
// )
// )
// )
function _sek_build_default_model( $module_tmpl_data, $default_model = null ) {
$default_model = is_array( $default_model ) ? $default_model : array();
//error_log( print_r( $module_tmpl_data , true ) );
foreach( $module_tmpl_data as $key => $data ) {
if ( 'pre-item' === $key )
continue;
if ( is_array( $data ) && array_key_exists( 'input_type', $data ) ) {
$default_model[ $key ] = array_key_exists( 'default', $data ) ? $data[ 'default' ] : '';
}
if ( is_array( $data ) ) {
$default_model = _sek_build_default_model( $data, $default_model );
}
}
return $default_model;
}
/* ------------------------------------------------------------------------- *
* REGISTERED MODULES => INPUT LIST
/* ------------------------------------------------------------------------- */
// @param (string) module_type
// Walk the registered modules tree and generates the module input list if not already cached
// used :
// - when filtering 'sek_add_css_rules_for_input_id' @see Sek_Dyn_CSS_Builder::sek_css_rules_sniffer_walker()
// @return array()
function sek_get_registered_module_input_list( $module_type = '' ) {
$input_list = array();
if ( empty( $module_type ) || is_null( $module_type ) )
return $input_list;
// check introduced since https://github.com/presscustomizr/nimble-builder/issues/432
// may not be mandatory
if ( !class_exists('\Nimble\CZR_Fmk_Base') ) {
sek_error_log( __FUNCTION__ . ' => error => CZR_Fmk_Base not loaded' );
return $input_list;
}
// Did we already cache it ?
$cached_input_lists = Nimble_Manager()->cached_input_lists;
if ( !empty( $cached_input_lists[ $module_type ] ) ) {
$input_list = $cached_input_lists[ $module_type ];
} else {
$registered_modules = CZR_Fmk_Base()->registered_modules;
// sek_error_log( __FUNCTION__ . ' => registered_modules', $registered_modules );
if ( sek_is_dev_mode() && !array( $registered_modules ) || !array_key_exists( $module_type, $registered_modules ) ) {
sek_error_log( __FUNCTION__ . ' => ' . $module_type . ' is not registered in the $CZR_Fmk_Base_fn()->registered_modules;' );
return $input_list;
}
// Is this module a father ?
if ( !empty( $registered_modules[ $module_type ]['is_father'] ) && true === $registered_modules[ $module_type ]['is_father'] ) {
if ( empty( $registered_modules[ $module_type ][ 'children' ] ) ) {
sek_error_log( __FUNCTION__ . ' => ' . $module_type . ' missing children modules' );
return $input_list;
}
if ( !is_array( $registered_modules[ $module_type ][ 'children' ] ) ) {
sek_error_log( __FUNCTION__ . ' => ' . $module_type . ' children modules should be an array' );
return $input_list;
}
$temp = array();
foreach ( $registered_modules[ $module_type ][ 'children' ] as $opt_group => $child_mod_type ) {
if ( empty( $registered_modules[ $child_mod_type ][ 'tmpl' ] ) ) {
sek_error_log( __FUNCTION__ . ' => ' . $child_mod_type . ' => missing "tmpl" property => impossible to build the master input_list.' );
continue;
}
// $temp[$opt_group] = _sek_build_input_list( $registered_modules[ $child_mod_type ][ 'tmpl' ] );
// $input_list = array_merge( $input_list, $temp[$opt_group] );
$input_list[$opt_group] = _sek_build_input_list( $registered_modules[ $child_mod_type ][ 'tmpl' ] );
}
} else {
if ( empty( $registered_modules[ $module_type ][ 'tmpl' ] ) ) {
sek_error_log( __FUNCTION__ . ' => ' . $module_type . ' => missing "tmpl" property => impossible to build the input_list.' );
return $input_list;
}
// Build
$input_list = _sek_build_input_list( $registered_modules[ $module_type ][ 'tmpl' ] );
}
// if ( empty( $registered_modules[ $module_type ][ 'tmpl' ] ) ) {
// sek_error_log( __FUNCTION__ . ' => ' . $module_type . ' => missing "tmpl" property => impossible to build the input_list.' );
// return $input_list;
// }
// // Build
// $input_list = _sek_build_input_list( $registered_modules[ $module_type ][ 'tmpl' ] );
// Cache
$cached_input_lists[ $module_type ] = $input_list;
Nimble_Manager()->cached_input_lists = $cached_input_lists;
// sek_error_log( __FUNCTION__ . ' => $cached_input_lists', $cached_input_lists );
}
return $input_list;
}
// @return array() default model
// Walk recursively the 'tmpl' property of the module
// 'tmpl' => array(
// 'pre-item' => array(
// 'social-icon' => array(
// 'input_type' => 'select',
// 'title' => __('Select an icon', 'text_doma')
// ),
// ),
// 'mod-opt' => array(
// 'social-size' => array(
// 'input_type' => 'number',
// 'title' => __('Size in px', 'text_doma'),
// 'step' => 1,
// 'min' => 5,
// 'transport' => 'postMessage'
// )
// ),
// 'item-inputs' => array(
// 'item-inputs' => array(
// 'tabs' => array(
// array(
// 'title' => __('Content', 'text_doma'),
// //'attributes' => 'data-sek-device="desktop"',
// 'inputs' => array(
// 'content' => array(
// 'input_type' => 'detached_tinymce_editor',
// 'title' => __('Content', 'text_doma')
// ),
// 'h_alignment_css' => array(
// 'input_type' => 'h_text_alignment',
// 'title' => __('Alignment', 'text_doma'),
// 'default' => is_rtl() ? 'right' : 'left',
// 'refresh_markup' => false,
// 'refresh_stylesheet' => true
// )
// )
// )
// )
// )
// Build the input list from item-inputs and modop-inputs
function _sek_build_input_list( $module_tmpl_data, $input_list = null ) {
$input_list = is_array( $input_list ) ? $input_list : array();
//sek_error_log( '_sek_build_input_list', print_r( $module_tmpl_data , true ) );
foreach( $module_tmpl_data as $key => $data ) {
if ( 'pre-item' === $key )
continue;
if ( is_array( $data ) && array_key_exists( 'input_type', $data ) ) {
// each input_id of a module should be unique
if ( array_key_exists( $key, $input_list ) ) {
sek_error_log( __FUNCTION__ . ' => error => duplicated input_id found => ' . $key );
} else {
$input_list[ $key ] = $data;
}
} else if ( is_array( $data ) ) {
$input_list = _sek_build_input_list( $data, $input_list );
}
}
return $input_list;
}
/* ------------------------------------------------------------------------- *
* NORMALIZE MODULE VALUE WITH DEFAULT
* preprocessing the module model before printing the module template.
* used before rendering or generating css
/* ------------------------------------------------------------------------- */
// @return array() $normalized_model
function sek_normalize_module_value_with_defaults( $raw_module_model ) {
$normalized_model = $raw_module_model;
if ( empty( $normalized_model['module_type'] ) ) {
sek_error_log( __FUNCTION__ . ' => missing module type', $normalized_model );
}
$module_type = $normalized_model['module_type'];
$is_father = sek_get_registered_module_type_property( $module_type, 'is_father' );
$raw_module_value = ( !empty( $raw_module_model['value'] ) && is_array( $raw_module_model['value'] ) ) ? $raw_module_model['value'] : array();
// reset the model value and rewrite it normalized with the defaults
$normalized_model['value'] = array();
if ( $is_father ) {
$children = sek_get_registered_module_type_property( $module_type, 'children' );
if ( empty( $children ) ) {
sek_error_log( __FUNCTION__ . ' => ' . $module_type . ' missing children modules' );
return $default;
}
if ( !is_array( $children ) ) {
sek_error_log( __FUNCTION__ . ' => ' . $module_type . ' children modules should be an array' );
return $default;
}
foreach ( $children as $opt_group => $child_mod_type ) {
$children_value = ( !empty( $raw_module_value[$opt_group] ) && is_array( $raw_module_value[$opt_group] ) ) ? $raw_module_value[$opt_group] : array();
$normalized_model['value'][ $opt_group ] = _sek_normalize_single_module_values( $children_value, $child_mod_type );
}
} else {
$normalized_model['value'] = _sek_normalize_single_module_values( $raw_module_value, $module_type );
}
//sek_error_log('sek_normalize_single_module_values for module type ' . $module_type , $normalized_model );
return $normalized_model;
}
// @return array()
function _sek_normalize_single_module_values( $raw_module_value, $module_type ) {
$default_value_model = sek_get_default_module_model( $module_type );//<= walk the registered modules tree and generates the module default if not already cached
// reset the model value and rewrite it normalized with the defaults
$module_values = array();
if ( czr_is_multi_item_module( $module_type ) ) {
foreach ( $raw_module_value as $item ) {
$module_values[] = wp_parse_args( $item, $default_value_model );
}
} else {
$module_values = wp_parse_args( $raw_module_value, $default_value_model );
}
return $module_values;
}
// Returns an array of allowed HTML tags and attributes when securing form echoed with wp_kses()
function sek_get_allowed_html_in_forms() {
$allowed = array(
'div' => array(),
'span' => array(),
'form' => array(
'action' => true,
'method' => true,
'post' => true
),
'button' => array(
'disabled' => true,
'name' => true,
'type' => true,
'value' => true,
),
'input' => array(
'alt' => true,
'capture' => true,
'checked' => true,
'disabled' => true,
'list' => true,
'name' => true,
'placeholder' => true,
'readonly' => true,
'type' => true,
'value' => true,
),
'label' => array(
'for' => true,
),
'textarea' => array(
'cols' => true,
'disabled' => true,
'maxlength' => true,
'minlength' => true,
'name' => true,
'placeholder' => true,
'readonly' => true,
'rows' => true,
'spellcheck' => true,
'wrap' => true,
),
);
$allowed = array_map(
function ($to_map) {
$attr = array(
'aria-checked' => true,
'aria-describedby' => true,
'aria-details' => true,
'aria-disabled' => true,
'aria-hidden' => true,
'aria-invalid' => true,
'aria-label' => true,
'aria-labelledby' => true,
'aria-live' => true,
'aria-relevant' => true,
'aria-required' => true,
'aria-selected' => true,
'class' => true,
'data-*' => true,
'id' => true,
'inputmode' => true,
'role' => true,
'style' => true,
'tabindex' => true,
'title' => true,
);
return array_merge( $attr, (array) $to_map );
},
$allowed
);
return $allowed;
}
?>
@@ -0,0 +1,249 @@
<?php
/* ------------------------------------------------------------------------- *
* BREAKPOINTS HELPER
/* ------------------------------------------------------------------------- */
function sek_get_global_custom_breakpoint() {
$global_breakpoint_data = sek_get_global_option_value('breakpoint');
if ( is_null( $global_breakpoint_data ) || empty( $global_breakpoint_data['global-custom-breakpoint'] ) )
return;
if ( empty( $global_breakpoint_data[ 'use-custom-breakpoint'] ) || false === sek_booleanize_checkbox_val( $global_breakpoint_data[ 'use-custom-breakpoint'] ) )
return;
return intval( $global_breakpoint_data['global-custom-breakpoint'] );
}
// @return bool
// introduced for https://github.com/presscustomizr/nimble-builder/issues/564
// Let us know if we need to apply the user defined custom breakpoint to all by-device customizations, like alignment
// false by default.
function sek_is_global_custom_breakpoint_applied_to_all_customizations_by_device() {
$global_breakpoint_data = sek_get_global_option_value('breakpoint');
if ( is_null( $global_breakpoint_data ) || empty( $global_breakpoint_data['global-custom-breakpoint'] ) )
return false;
if ( empty( $global_breakpoint_data[ 'use-custom-breakpoint'] ) || false === sek_booleanize_checkbox_val( $global_breakpoint_data[ 'use-custom-breakpoint'] ) )
return false;
// We need a custom breakpoint > 1
if ( intval( $global_breakpoint_data['global-custom-breakpoint'] ) <= 1 )
return;
// apply-to-all option is unchecked by default
// returns true when user has checked the apply to all option
return array_key_exists('apply-to-all', $global_breakpoint_data ) && sek_booleanize_checkbox_val( $global_breakpoint_data[ 'apply-to-all' ] ) ;
}
// invoked when filtering 'sek_add_css_rules_for__section__options'
// param 'for_responsive_columns' has been introduced for https://github.com/presscustomizr/nimble-builder/issues/564
// so we can differentiate when the custom breakpoint is requested for column responsiveness or for css rules generation
// when for columns, we always apply the custom breakpoint defined by the user
// otherwise, when generating CSS rules like alignment, the custom breakpoint is applied if user explicitely checked the 'apply_to_all' option
// 'for_responsive_columns' is set to true when sek_get_closest_section_custom_breakpoint() is invoked from Nimble_Manager()::render()
// @param params array(
// 'section_model' => array(),
// 'for_responsive_columns' => bool
// )
function sek_get_section_custom_breakpoint( $params ) {
if ( !is_array( $params ) )
return;
$params = wp_parse_args( $params, array(
'section_model' => array(),
'for_responsive_columns' => false
));
$section = $params['section_model'];
if ( !is_array( $section ) )
return;
if ( empty($section['id']) )
return;
$options = empty( $section[ 'options' ] ) ? array() : $section['options'];
if ( empty( $options[ 'breakpoint' ] ) )
return;
if ( empty( $options[ 'breakpoint' ][ 'use-custom-breakpoint'] ) || false === sek_booleanize_checkbox_val( $options[ 'breakpoint' ][ 'use-custom-breakpoint'] ) )
return;
// assign default value if use-custom-breakpoint is checked but there's no breakpoint set.
// this can also occur if the custom breakpoint is left to default in the customizer ( default values are not considered when saving )
if ( empty( $options[ 'breakpoint' ][ 'custom-breakpoint' ] ) ) {
if ( array_key_exists('custom-breakpoint', $options[ 'breakpoint' ] ) ) {
// this is the case when user has emptied the setting
$custom_breakpoint = 1;// added when fixing https://github.com/presscustomizr/nimble-builder/issues/623
} else {
$custom_breakpoint = Sek_Dyn_CSS_Builder::$breakpoints['md'];//768
}
} else {
$custom_breakpoint = intval( $options[ 'breakpoint' ][ 'custom-breakpoint' ] );
}
if ( $custom_breakpoint <= 0 )
return 1;
// 1) When the breakpoint is requested for responsive columns, we always return the custom value
if ( $params['for_responsive_columns'] )
return $custom_breakpoint;
// 2) Otherwise ( other CSS rules generation case, like alignment ) we make sure that user want to apply the custom breakpoint also to other by-device customizations
return sek_is_section_custom_breakpoint_applied_to_all_customizations_by_device( $options[ 'breakpoint' ] ) ? $custom_breakpoint : null;
}
// @return bool
// introduced for https://github.com/presscustomizr/nimble-builder/issues/564
// Let us know if we need to apply the user defined custom breakpoint to all by-device customizations, like alignment
// false by default.
// @param $section_breakpoint_options = array(
// 'use-custom-breakpoint' => bool
// 'custom-breakpoint' => int
// 'apply-to-all' => bool
// )
function sek_is_section_custom_breakpoint_applied_to_all_customizations_by_device( $section_breakpoint_options ) {
if ( !is_array( $section_breakpoint_options ) || empty( $section_breakpoint_options ) )
return;
if ( empty( $section_breakpoint_options[ 'use-custom-breakpoint'] ) || false === sek_booleanize_checkbox_val( $section_breakpoint_options[ 'use-custom-breakpoint'] ) )
return;
// We need a custom breakpoint > 1
// Make sure the custom breakpoint has not been emptied, otherwise assign a minimal value of 1px
// fixes : https://github.com/presscustomizr/nimble-builder/issues/623
$custom_breakpoint = empty( $section_breakpoint_options['custom-breakpoint'] ) ? 1 : $section_breakpoint_options['custom-breakpoint'];
if ( intval( $custom_breakpoint ) <= 1 )
return;
// apply-to-all option is unchecked by default
// returns true when user has checked the apply to all option
return array_key_exists('apply-to-all', $section_breakpoint_options ) && sek_booleanize_checkbox_val( $section_breakpoint_options[ 'apply-to-all' ] );
}
// Recursive helper
// Is also used when building the dyn_css or when firing sek_add_css_rules_for_spacing()
// @param id : mandatory
// @param collection : optional <= that's why if missing we must walk all collections : local and global
function sek_get_closest_section_custom_breakpoint( $params ) {
$params = wp_parse_args( $params, array(
'searched_level_id' => '',
'collection' => 'not_set',
'skope_id' => '',
'last_section_breakpoint_found' => 0,
'last_regular_section_breakpoint_found' => 0,
'last_nested_section_breakpoint_found' => 0,
'searched_level_id_found' => false,
// the 'for_responsive_columns' param has been introduced for https://github.com/presscustomizr/nimble-builder/issues/564
// so we can differentiate when the custom breakpoint is requested for column responsiveness or for css rules generation
// when for columns, we always apply the custom breakpoint defined by the user
// otherwise, when generating CSS rules like alignment, the custom breakpoint is applied if user explicitely checked the 'apply_to_all' option
// 'for_responsive_columns' is set to true when sek_get_closest_section_custom_breakpoint() is invoked from Nimble_Manager()::render()
'for_responsive_columns' => false
) );
extract( $params, EXTR_OVERWRITE );
if ( !is_string( $searched_level_id ) || empty( $searched_level_id ) ) {
sek_error_log( __FUNCTION__ . ' => missing or invalid child_level_id param.');
return $last_section_breakpoint_found;;
}
if ( $searched_level_id_found ) {
return $last_section_breakpoint_found;
}
// When no collection is provided, we must walk all collections, local and global.
if ( 'not_set' === $collection ) {
if ( empty( $skope_id ) ) {
if ( is_array( $_POST ) && !empty( $_POST['location_skope_id'] ) ) {
$skope_id = sanitize_text_field($_POST['location_skope_id']);
} else {
// When fired during an ajax 'customize_save' action, the skp_get_skope_id() is determined with $_POST['local_skope_id']
// @see add_filter( 'skp_get_skope_id', '\Nimble\sek_filter_skp_get_skope_id', 10, 2 );
$skope_id = skp_get_skope_id();
}
}
if ( empty( $skope_id ) || '_skope_not_set_' === $skope_id ) {
sek_error_log( __FUNCTION__ . ' => the skope_id should not be empty.');
}
$local_skope_settings = sek_get_skoped_seks( $skope_id );
$local_collection = ( is_array( $local_skope_settings ) && !empty( $local_skope_settings['collection'] ) ) ? $local_skope_settings['collection'] : array();
$global_skope_settings = sek_get_skoped_seks( NIMBLE_GLOBAL_SKOPE_ID );
$global_collection = ( is_array( $global_skope_settings ) && !empty( $global_skope_settings['collection'] ) ) ? $global_skope_settings['collection'] : array();
$collection = array_merge( $local_collection, $global_collection );
}
// Loop collections
foreach ( $collection as $level_data ) {
//sek_error_log($last_section_breakpoint_found . ' MATCH ? => LEVEL ID AND TYPE => ' . $level_data['level'] . ' | ' . $level_data['id'] );
// stop here and return if a match was recursively found
if ( $searched_level_id_found )
break;
if ( 'section' == $level_data['level'] ) {
$section_maybe_custom_breakpoint = intval( sek_get_section_custom_breakpoint( array( 'section_model' => $level_data, 'for_responsive_columns' => $for_responsive_columns ) ) );
if ( !empty( $level_data['is_nested'] ) && $level_data['is_nested'] ) {
$last_nested_section_breakpoint_found = $section_maybe_custom_breakpoint;
} else {
$last_nested_section_breakpoint_found = 0;//reset last nested breakpoint
$last_regular_section_breakpoint_found = $section_maybe_custom_breakpoint;
}
}
if ( array_key_exists( 'id', $level_data ) && $searched_level_id == $level_data['id'] ) {
//match found, break this loop
if ( $last_nested_section_breakpoint_found >= 1 ) {
$last_section_breakpoint_found = $last_nested_section_breakpoint_found;
} else if ( $last_regular_section_breakpoint_found >= 1 ) {
$last_section_breakpoint_found = $last_regular_section_breakpoint_found;
} else {
$last_section_breakpoint_found = 0;
}
$searched_level_id_found = true;
break;
}
if ( !$searched_level_id_found && array_key_exists( 'collection', $level_data ) && is_array( $level_data['collection'] ) ) {
$collection = $level_data['collection'];
$recursive_params = compact(
'searched_level_id',
'collection',
'skope_id',
'last_section_breakpoint_found',
'last_regular_section_breakpoint_found',
'last_nested_section_breakpoint_found',
'searched_level_id_found',
'for_responsive_columns'
);
$recursive_values = sek_get_closest_section_custom_breakpoint( $recursive_params );
if ( is_array($recursive_values) ) {
extract( $recursive_values );
} else {
$last_section_breakpoint_found = $recursive_values;
$searched_level_id_found = true;
break;
}
}
}
// Returns a breakpoint int if found or an array
// => this way we can determine if we continue or not to walk recursively
return $searched_level_id_found ? $last_section_breakpoint_found : compact(
'searched_level_id_found',
'last_section_breakpoint_found',
'last_regular_section_breakpoint_found',
'last_nested_section_breakpoint_found'
);
}
?>
@@ -0,0 +1,212 @@
<?php
/* ------------------------------------------------------------------------- *
* LOCAL OPTIONS HELPERS
/* ------------------------------------------------------------------------- */
// @param $option_name = string
// 'nimble_front_classes_ready' is fired when Nimble_Manager() is instanciated
function sek_get_local_option_value( $option_name = '', $skope_id = null ) {
if ( empty($option_name) ) {
sek_error_log( __FUNCTION__ . ' => invalid option name' );
return array();
}
if ( !skp_is_customizing() && did_action('nimble_front_classes_ready') && '_not_cached_yet_' !== Nimble_Manager()->local_options ) {
$local_options = Nimble_Manager()->local_options;
} else {
// use the provided skope_id if in the signature
$skope_id = ( !empty( $skope_id ) && is_string( $skope_id ))? $skope_id : skp_get_skope_id();
$localSkopeNimble = sek_get_skoped_seks( $skope_id );
$local_options = ( is_array( $localSkopeNimble ) && !empty( $localSkopeNimble['local_options'] ) && is_array( $localSkopeNimble['local_options'] ) ) ? $localSkopeNimble['local_options'] : array();
// Cache only after 'wp' && 'nimble_front_classes_ready'
// never cache when doing ajax
if ( did_action('nimble_front_classes_ready') && did_action('wp') && !defined('DOING_AJAX') ) {
Nimble_Manager()->local_options = $local_options;
}
}
// maybe normalizes with default values
$values = ( !empty( $local_options ) && !empty( $local_options[ $option_name ] ) ) ? $local_options[ $option_name ] : null;
if ( did_action('nimble_front_classes_ready') ) {
$values = sek_normalize_local_options_with_defaults( $option_name, $values );
}
return $values;
}
// Introduced for site templates, when using function sek_is_inheritance_locally_disabled()
// needed because we can't rely on sek_get_skoped_seks() to get current local sections data, because this function returns the inherited data
// @param $option_name = string
// 'nimble_front_classes_ready' is fired when Nimble_Manager() is instanciated
function sek_get_local_option_value_without_inheritance( $option_name = '', $skope_id = null ) {
if ( empty($option_name) ) {
sek_error_log( __FUNCTION__ . ' => invalid option name' );
return array();
}
if ( !skp_is_customizing() && did_action('nimble_front_classes_ready') && '_not_cached_yet_' !== Nimble_Manager()->local_options_without_tmpl_inheritance ) {
$local_options_without_tmpl_inheritance = Nimble_Manager()->local_options_without_tmpl_inheritance;
} else {
// use the provided skope_id if in the signature
$skope_id = ( !empty( $skope_id ) && is_string( $skope_id ))? $skope_id : skp_get_skope_id();
$localSkopeNimble = sek_get_seks_without_group_inheritance( $skope_id );
if ( skp_is_customizing() && NIMBLE_GLOBAL_SKOPE_ID != $skope_id ) {
// when customizing, let us filter the value with the 'customized' ones
$localSkopeNimble = apply_filters(
'sek_get_skoped_seks',
$localSkopeNimble,
$skope_id,
''
);
}
$local_options_without_tmpl_inheritance = ( is_array( $localSkopeNimble ) && !empty( $localSkopeNimble['local_options'] ) && is_array( $localSkopeNimble['local_options'] ) ) ? $localSkopeNimble['local_options'] : array();
// Cache only after 'wp' && 'nimble_front_classes_ready'
// never cache when doing ajax
if ( did_action('nimble_front_classes_ready') && did_action('wp') && !defined('DOING_AJAX') ) {
Nimble_Manager()->local_options_without_tmpl_inheritance = $local_options_without_tmpl_inheritance;
}
}
// maybe normalizes with default values
$values = ( !empty( $local_options_without_tmpl_inheritance ) && !empty( $local_options_without_tmpl_inheritance[ $option_name ] ) ) ? $local_options_without_tmpl_inheritance[ $option_name ] : null;
if ( did_action('nimble_front_classes_ready') ) {
$values = sek_normalize_local_options_with_defaults( $option_name, $values );
}
return $values;
}
// @return array() $normalized_values
// @see _1_6_4_sektions_generate_UI_local_skope_options.js
function sek_normalize_local_options_with_defaults( $option_name, $raw_module_values ) {
if ( empty($option_name) ) {
sek_error_log( __FUNCTION__ . ' => invalid option name' );
return array();
}
$normalized_values = ( !empty($raw_module_values) && is_array( $raw_module_values ) ) ? $raw_module_values : array();
// map the option key as saved in db ( @see _1_6_4_sektions_generate_UI_local_skope_options.js ) and the module type
$local_option_map = SEK_Front_Construct::$local_options_map;
if ( !array_key_exists($option_name, $local_option_map) ) {
sek_error_log( __FUNCTION__ . ' => invalid option name', $option_name );
return $raw_module_values;
} else {
$module_type = $local_option_map[$option_name];
}
// normalize with the defaults
// class_exists check introduced since https://github.com/presscustomizr/nimble-builder/issues/432
// may not be mandatory
if ( class_exists('\Nimble\CZR_Fmk_Base') ) {
if( CZR_Fmk_Base()->czr_is_module_registered($module_type) ) {
$normalized_values = _sek_normalize_single_module_values( $normalized_values, $module_type );
}
}
return $normalized_values;
}
/* ------------------------------------------------------------------------- *
* GLOBAL OPTIONS HELPERS
/* ------------------------------------------------------------------------- */
// @param $option_name = string
// 'nimble_front_classes_ready' is fired when Nimble_Manager() is instanciated
function sek_get_global_option_value( $option_name = '' ) {
if ( empty($option_name) ) {
sek_error_log( __FUNCTION__ . ' => invalid option name' );
return array();
}
if ( !skp_is_customizing() && did_action('nimble_front_classes_ready') && '_not_cached_yet_' !== Nimble_Manager()->global_nimble_options ) {
$global_nimble_options = Nimble_Manager()->global_nimble_options;
} else {
$global_nimble_options = get_option( NIMBLE_OPT_NAME_FOR_GLOBAL_OPTIONS );
//sek_error_log(' SOOO OPTIONS ?', $global_nimble_options );
// cache when nimble is ready
// this hook is fired when Nimble_Manager() is instanciated
// never cache when doing ajax
if ( did_action('nimble_front_classes_ready') && !defined('DOING_AJAX') ) {
Nimble_Manager()->global_nimble_options = $global_nimble_options;
}
}
// maybe normalizes with default values
$values = ( is_array( $global_nimble_options ) && !empty( $global_nimble_options[ $option_name ] ) ) ? $global_nimble_options[ $option_name ] : null;
if ( did_action('nimble_front_classes_ready') ) {
$values = sek_normalize_global_options_with_defaults( $option_name, $values );
}
return $values;
}
// @see _1_6_5_sektions_generate_UI_global_options.js
// @return array() $normalized_values
function sek_normalize_global_options_with_defaults( $option_name, $raw_module_values ) {
if ( empty($option_name) ) {
sek_error_log( __FUNCTION__ . ' => invalid option name' );
return array();
}
$normalized_values = ( !empty($raw_module_values) && is_array( $raw_module_values ) ) ? $raw_module_values : array();
// map the option key as saved in db ( @see _1_6_5_sektions_generate_UI_global_options.js ) and the module type
$global_option_map = SEK_Front_Construct::$global_options_map;
//sek_error_log('SEK_Front_Construct::$global_options_map', SEK_Front_Construct::$global_options_map );
if ( !array_key_exists($option_name, $global_option_map) ) {
sek_error_log( __FUNCTION__ . ' => invalid option name', $option_name );
return $raw_module_values;
} else {
$module_type = $global_option_map[$option_name];
}
// normalize with the defaults
// class_exists check introduced since https://github.com/presscustomizr/nimble-builder/issues/432
// may not be mandatory
if ( class_exists('\Nimble\CZR_Fmk_Base') ) {
if( CZR_Fmk_Base()->czr_is_module_registered($module_type) ) {
$normalized_values = _sek_normalize_single_module_values( $normalized_values, $module_type );
}
} else {
sek_error_log( __FUNCTION__ . ' => error => CZR_Fmk_Base not loaded' );
}
return $normalized_values;
}
// // @see _1_6_5_sektions_generate_UI_site_tmpl_options.js
// // @return array() $normalized_values
// function sek_normalize_site_tmpl_options_with_defaults( $option_name, $raw_module_values ) {
// if ( empty($option_name) ) {
// sek_error_log( __FUNCTION__ . ' => invalid option name' );
// return array();
// }
// $normalized_values = ( !empty($raw_module_values) && is_array( $raw_module_values ) ) ? $raw_module_values : array();
// // map the option key as saved in db ( @see _1_6_5_sektions_generate_UI_global_options.js ) and the module type
// $site_tmpl_options_map = SEK_Front_Construct::$site_tmpl_options_map;
// //sek_error_log('SEK_Front_Construct::$global_options_map', SEK_Front_Construct::$global_options_map );
// if ( !array_key_exists($option_name, $site_tmpl_options_map) ) {
// sek_error_log( __FUNCTION__ . ' => invalid option name', $option_name );
// return $raw_module_values;
// } else {
// $module_type = $site_tmpl_options_map[$option_name];
// }
// // normalize with the defaults
// // class_exists check introduced since https://github.com/presscustomizr/nimble-builder/issues/432
// // may not be mandatory
// if ( class_exists('\Nimble\CZR_Fmk_Base') ) {
// if( CZR_Fmk_Base()->czr_is_module_registered($module_type) ) {
// $normalized_values = _sek_normalize_single_module_values( $normalized_values, $module_type );
// }
// } else {
// sek_error_log( __FUNCTION__ . ' => error => CZR_Fmk_Base not loaded' );
// }
// return $normalized_values;
// }
?>
@@ -0,0 +1,457 @@
<?php
/* ------------------------------------------------------------------------- *
* Dynamic variables parsing
/* ------------------------------------------------------------------------- */
function sek_find_pattern_match($matches) {
$replace_values = apply_filters( 'sek_template_tags', array(
'home_url' => 'home_url',
'year_now' => date("Y"),
'site_title' => 'get_bloginfo',
'the_title' => 'sek_get_the_title',
'the_archive_title' => 'sek_get_the_archive_title',// works for authors, CPT, taxonomies
'the_archive_description' => 'sek_get_the_archive_description',// works for authors, CPT, taxonomies
'the_content' => 'sek_get_the_content',
'the_tags' => 'sek_get_the_tags',
'the_categories' => 'sek_get_the_categories',
'the_author_link' => 'sek_get_the_author_link',
'the_author_name' => 'sek_get_the_author_name',
'the_author_avatar' => 'sek_get_the_author_avatar',
'the_author_bio' => 'sek_get_the_author_bio',
'the_published_date' => 'sek_get_the_published_date',
'the_modified_date' => 'sek_get_the_modified_date',
'the_comments' => 'sek_get_the_comments',
'the_previous_post_link' => 'sek_get_previous_post_link',
'the_next_post_link' => 'sek_get_next_post_link',
'the_comment_number' => 'sek_get_the_comment_number',
'the_search_query' => 'sek_get_search_query',
'the_search_results_number' => 'sek_get_search_results_nb'
));
// Are we good after the filter ?
if ( !is_array($replace_values) )
return;
//sek_error_log('$matches ??', $matches );
if ( !is_array($matches) || empty($matches[1]) )
return;
//$data = html_entity_decode($matches[1], ENT_QUOTES, get_bloginfo( 'charset' ) );
$data = explode(' ', $matches[1] );
// Filter so that {{the_categories sep="/"}} becomes array('the_categories', 'sep="/"' ) with no empty entries
// => the first entry is the template tag name, the other entries are the callback arguments ( to implement April 2021 )
$new_data = array_filter($data, function($value) {
if ( !is_string($value) )
return false;
$value = ltrim($value);
return !is_null($value) && !empty($value) && preg_match("/[a-z]/i", $value) ;
});
if ( isset($new_data[0]) && array_key_exists( $new_data[0], $replace_values ) ) {
// @todo => authorize arguments passed as an array
$dyn_content = $replace_values[$new_data[0]];
$fn_name = $dyn_content;// <= typically not namespaced if WP core function, or function added with a filter from a child theme for example
$namespaced_fn_name = __NAMESPACE__ . '\\' . $dyn_content; // <= namespaced if Nimble Builder function, introduced in october 2019 for https://github.com/presscustomizr/nimble-builder/issues/401
if ( function_exists( $namespaced_fn_name ) ) {
return $namespaced_fn_name();//<= @TODO use call_user_func() here + handle the case when the callback is a method
} else if ( function_exists( $fn_name ) ) {
return $fn_name();//<= @TODO use call_user_func() here + handle the case when the callback is a method
} else if ( is_string($dyn_content) ) {
return $dyn_content;
} else {
return null;
}
}
return null;
}
// fired @filter 'nimble_parse_template_tags'
function sek_parse_template_tags( $val ) {
//the pattern could also be '!\{\{(\w+)\}\}!', but adding \s? allows us to allow spaces around the term inside curly braces
//see https://stackoverflow.com/questions/959017/php-regex-templating-find-all-occurrences-of-var#comment71815465_959026
//return is_string( $val ) ? preg_replace_callback( '!\{\{\s?(\w+)\s?\}\}!', '\Nimble\sek_find_pattern_match', $val) : $val;
return is_string( $val ) ? preg_replace_callback( '!\{\{\s?(.*?)\s?\}\}!', '\Nimble\sek_find_pattern_match', $val) : $val;
}
add_filter( 'nimble_parse_template_tags', '\Nimble\sek_parse_template_tags' );
// CALLBACKS WHEN IS_ARCHIVE()
function sek_get_the_archive_title() {
$is_archive = is_archive();
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) {
$is_archive = sek_get_posted_query_param_when_customizing( 'is_archive' );
}
if ( !$is_archive ) {
return sek_get_tmpl_tag_error( $tag = 'the_archive_title', $msg = __('It can be used in archive pages only.', 'text_doma') );
}
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) {
$title = sek_get_posted_query_param_when_customizing( 'the_archive_title' );
} else {
add_filter('get_the_archive_title_prefix', '__return_false');
$title = get_the_archive_title();
remove_filter('get_the_archive_title_prefix', '__return_false');
}
return $title;
}
function sek_get_the_archive_description() {
$is_archive = is_archive();
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) {
$is_archive = sek_get_posted_query_param_when_customizing( 'is_archive' );
}
if ( !$is_archive ) {
return sek_get_tmpl_tag_error( $tag = 'the_archive_description', $msg = __('It can be used in archive pages only.', 'text_doma') );
}
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) {
$title = sek_get_posted_query_param_when_customizing( 'the_archive_description' );
} else {
$title = get_the_archive_description();
}
return $title;
}
// CALLBACKS WHEN IS_SINGULAR()
function sek_get_next_post_link() {
$is_singular = is_singular();
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) {
$is_singular = sek_get_posted_query_param_when_customizing( 'is_singular' );
}
if ( !$is_singular ) {
return sek_get_tmpl_tag_error( $tag = 'the_next_post_link', $msg = __('It can only be used in single pages or single posts.', 'text_doma') );
}
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) {
$title = sek_get_posted_query_param_when_customizing( 'the_next_post_link' );
} else {
$title = get_next_post_link( $format = '%link' );
}
if ( empty( $title ) ) {
return '';
} else {
return sprintf( '<span class="sek-next-post-link">%1$s</span>', $title );
}
}
function sek_get_previous_post_link() {
$is_singular = is_singular();
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) {
$is_singular = sek_get_posted_query_param_when_customizing( 'is_singular' );
}
if ( !$is_singular ) {
return sek_get_tmpl_tag_error( $tag = 'the_previous_post_link', $msg = __('It can only be used in single pages or single posts.', 'text_doma') );
}
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) {
$title = sek_get_posted_query_param_when_customizing( 'the_previous_post_link' );
} else {
$title = get_previous_post_link( $format = '%link' );
}
if ( empty( $title ) ) {
return '';
} else {
return sprintf( '<span class="sek-previous-post-link">%1$s</span>', $title );
}
}
function sek_get_the_comments() {
$is_singular = is_singular();
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) {
$is_singular = sek_get_posted_query_param_when_customizing( 'is_singular' );
}
if ( !$is_singular ) {
return sek_get_tmpl_tag_error( $tag = 'the_comments', $msg = __('It can only be used in single pages or single posts.', 'text_doma') );
}
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) {
return sprintf('<div class="nimble-notice-in-preview"><i class="fas fa-info-circle"></i>&nbsp;%1$s</div>',
__('Comment template can not be refreshed while customizing', 'text_doma')
);
}
ob_start();
//load_template( $tmpl_path, false );
if ( comments_open() || get_comments_number() ) {
add_filter('comments_template', '\Nimble\sek_set_nb_comments_template_path');
comments_template();
remove_filter('comments_template', '\Nimble\sek_set_nb_comments_template_path');
}
return ob_get_clean();
}
//@filter 'comments_template'
function sek_set_nb_comments_template_path( $original_path ) {
//@to do => make this path overridable
$nb_path = sek_get_templates_dir() . "/wp/comments-template.php";
if ( file_exists( $nb_path ) ) {
return $nb_path;
}
return $original_path;
}
function sek_get_the_published_date() {
$is_singular = is_singular();
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) {
$is_singular = sek_get_posted_query_param_when_customizing( 'is_singular' );
}
if ( !$is_singular ) {
return sek_get_tmpl_tag_error( $tag = 'the_published_date', $msg = __('It can only be used in single pages or single posts.', 'text_doma') );
}
$post_id = sek_get_post_id_on_front_and_when_customizing();
$published_date = get_the_date( get_option('date_format'), $post_id);
$machine_readable_published_date = esc_attr( get_the_date( 'c' , $post_id ) );
return sprintf( '<time class="sek-published-date" datetime="%1$s">%2$s</time>',
$machine_readable_published_date,
$published_date
);
}
function sek_get_the_modified_date() {
$is_singular = is_singular();
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) {
$is_singular = sek_get_posted_query_param_when_customizing( 'is_singular' );
}
if ( !$is_singular ) {
return sek_get_tmpl_tag_error( $tag = 'the_modified_date', $msg = __('It can only be used in single pages or single posts.', 'text_doma') );
}
$post_id = sek_get_post_id_on_front_and_when_customizing();
$modified_date = get_the_modified_date( get_option('date_format'), $post_id );
$machine_readable_modified_date = esc_attr( get_the_modified_date( 'c' ), $post_id );
return sprintf( '<time class="sek-modified-date" datetime="%1$s">%2$s</time>',
$machine_readable_modified_date ,
$modified_date
);
}
function sek_get_the_tags( $separator = ' &middot; ') {
$is_singular = is_singular();
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) {
$is_singular = sek_get_posted_query_param_when_customizing( 'is_singular' );
}
if ( !$is_singular ) {
return sek_get_tmpl_tag_error( $tag = 'the_tags', $msg = __('It can only be used in single pages or single posts.', 'text_doma') );
}
return sprintf( '<span class="sek-post-tags">%1$s</span>', get_the_tag_list( $before = '', $sep = $separator, $after = '', $post_id = sek_get_post_id_on_front_and_when_customizing() ) );
}
function sek_get_the_categories( $separator = ' / ') {
$is_singular = is_singular();
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) {
$is_singular = sek_get_posted_query_param_when_customizing( 'is_singular' );
}
if ( !$is_singular ) {
return sek_get_tmpl_tag_error( $tag = 'the_categories', $msg = __('It can only be used in single pages or single posts.', 'text_doma') );
}
return sprintf( '<span class="sek-post-category">%1$s</span>', get_the_category_list( $separator, '', $post_id = sek_get_post_id_on_front_and_when_customizing() ) );
}
function sek_get_the_comment_number() {
$is_singular = is_singular();
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) {
$is_singular = sek_get_posted_query_param_when_customizing( 'is_singular' );
}
if ( !$is_singular ) {
return sek_get_tmpl_tag_error( $tag = 'the_comment_number', $msg = __('It can only be used in single pages or single posts.', 'text_doma') );
}
return sprintf( '<span class="sek-post-comment-number">%1$s</span>', get_comments_number_text( $zero = false, $one = false, $more = false, $post_id = sek_get_post_id_on_front_and_when_customizing() ) );
}
// AUTHOR DATA
// 2 CASES : singular or author archives
function sek_get_the_author_link() {
$author_id = sek_get_author_id_on_front_and_when_customizing();
if ( $author_id ) {
$display_name = get_the_author_meta( 'display_name', $author_id );
return sprintf(
'<a href="%1$s" title="%2$s" class="sek-author-link" rel="author">%3$s</a>',
esc_url( get_author_posts_url( $author_id, get_the_author_meta( 'user_nicename', $author_id ) ) ),
/* translators: %s: Author's display name. */
esc_attr( sprintf( __( 'Posts by %s' ), $display_name ) ),
$display_name
);
}
return null;
}
function sek_get_the_author_name() {
$author_id = sek_get_author_id_on_front_and_when_customizing();
if ( $author_id ) {
return sprintf( '<span class="sek-author-name">%1$s</span>', get_the_author_meta( 'display_name', $author_id ) );
}
return null;
}
function sek_get_the_author_avatar() {
$author_id = sek_get_author_id_on_front_and_when_customizing();
if ( $author_id ) {
return get_avatar( get_the_author_meta( 'ID', $author_id ), '85' );
}
return null;
}
function sek_get_the_author_bio() {
$author_id = sek_get_author_id_on_front_and_when_customizing();
if ( $author_id ) {
return sprintf( '<span class="sek-author-description">%1$s</span>', get_the_author_meta( 'description', $author_id ) );
}
return null;
}
// introduced in october 2019 for https://github.com/presscustomizr/nimble-builder/issues/401
function sek_get_the_title() {
$is_singular = is_singular();
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) {
$is_singular = sek_get_posted_query_param_when_customizing( 'is_singular' );
}
if ( !$is_singular ) {
return sek_get_tmpl_tag_error( $tag = 'the_title', $msg = __('It can only be used in single pages or single posts.', 'text_doma') );
}
return get_the_title( sek_get_post_id_on_front_and_when_customizing() );
}
// introduced in october 2019 for https://github.com/presscustomizr/nimble-builder/issues/401
function sek_get_the_content() {
$is_singular = is_singular();
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) {
$is_singular = sek_get_posted_query_param_when_customizing( 'is_singular' );
}
if ( !$is_singular ) {
return sek_get_tmpl_tag_error( $tag = 'the_content', $msg = __('It can only be used in single pages or single posts.', 'text_doma') );
}
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) {
$post_id = (int)sek_get_posted_query_param_when_customizing( 'post_id' );
if ( is_int($post_id) ) {
$post_object = get_post( $post_id );
return !empty( $post_object ) ? apply_filters( 'the_content', $post_object->post_content ) : null;
}
} else {
$post_object = get_post();
return !empty( $post_object ) ? apply_filters( 'the_content', $post_object->post_content ) : null;
}
return null;
}
// CALLBACKS WHEN IS_SEARCH()
function sek_get_search_query() {
$is_search = is_search();
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) {
$is_search = sek_get_posted_query_param_when_customizing( 'is_search' );
$search_query = sek_get_posted_query_param_when_customizing( 'the_search_query' );
} else {
$search_query = get_search_query();
}
if ( !$is_search ) {
return sek_get_tmpl_tag_error( $tag = 'the_search_query', $msg = __('It can only be used in search results page.', 'text_doma') );
}
return sprintf( '<span class="sek-search-query">%1$s</span>', esc_html( $search_query ) );
}
function sek_get_search_results_nb() {
$is_search = is_search();
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) {
$is_search = sek_get_posted_query_param_when_customizing( 'is_search' );
$search_res_nb = sek_get_posted_query_param_when_customizing( 'the_search_results_nb' );
} else {
global $wp_query;
$search_res_nb = (int)$wp_query->found_posts;
}
if ( !$is_search ) {
return sek_get_tmpl_tag_error( $tag = 'the_search_results_number', $msg = __('It can only be used in search results page.', 'text_doma') );
}
return sprintf( '<span class="sek-search-results-number">%1$s</span>', esc_html( $search_res_nb ) );
}
//////////////////////////////////////////////////
///// HELPERS
/////////////////////////////////////////////////
function sek_get_author_id_on_front_and_when_customizing() {
$is_singular = is_singular();
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) {
$is_singular = sek_get_posted_query_param_when_customizing( 'is_singular' );
}
if ( $is_singular ) {
$post_id = sek_get_post_id_on_front_and_when_customizing();
$post_object = get_post( $post_id );
if ( empty( $post_object ) || !is_object( $post_object ) ) {
$author_id = null;
}
$author_id = $post_object->post_author;
} else {
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) {
$author_id = sek_get_posted_query_param_when_customizing( 'the_author_id' );
} else {
global $authordata;
$author_id = isset( $authordata->ID ) ? $authordata->ID : 0;
}
}
return $author_id;
}
// @return the post id in all cases
// when performing ajax action, we need the posted query params made available from the ajax params
function sek_get_post_id_on_front_and_when_customizing() {
if ( defined( 'DOING_AJAX' ) && DOING_AJAX && skp_is_customizing() ) {
$post_id = (int)sek_get_posted_query_param_when_customizing( 'post_id' );
} else {
$post_id = get_the_ID();
}
return is_int($post_id) ? $post_id : null;
}
// recursively sanitize an array of posted ($_POST) query_params to be used when customzing
// @param params (array)
function sek_sanitize_query_params_array( $params = array()) {
foreach ($params as $prm => $val) {
if ( is_array($val) ) {
if ( empty($val) ) {
$sanitized_query_params[$prm] = [];
} else {
$sanitized_query_params[$prm] = sek_sanitize_query_params_array($params);
}
} else {
$sanitized_query_params[$prm] = sanitize_text_field($val);
}
}
return $sanitized_query_params;
}
// introduced in october 2019 for https://github.com/presscustomizr/nimble-builder/issues/401
// Possible params as of October 2019
// @see inc/czr-skope/_dev/1_1_0_skop_customizer_preview_load_assets.php::
// 'is_singular' => $wp_query->is_singular,
// 'post_id' => get_the_ID()
function sek_get_posted_query_param_when_customizing( $param ) {
if ( isset( $_POST['czr_query_params'] ) ) {
$query_params = json_decode( wp_unslash( $_POST['czr_query_params'] ), true );
if ( array_key_exists( $param, $query_params ) ) {
if ( !is_array($query_params[$param]) ) {
return sanitize_text_field($query_params[$param]);
} else {
return sek_sanitize_query_params_array($query_params[$param]);
}
} else {
sek_error_log( __FUNCTION__ . ' => invalid param requested');
return null;
}
}
return null;
}
function sek_get_tmpl_tag_error( $tag, $msg ) {
if ( !skp_is_customizing() )
return;
return sprintf('<div class="nimble-notice-in-preview nimble-inline-notice-in-preview"><i class="fas fa-info-circle"></i> %1$s %2$s</div>',
'{{' . $tag . '}} ' . __('could not be printed.', 'text_doma'),
$msg
);
}
?>
@@ -0,0 +1,128 @@
<?php
/* ------------------------------------------------------------------------- *
* TEMPLATE OVERRIDE HELPERS
/* ------------------------------------------------------------------------- */
// TEMPLATES PATH
// added for #532, october 2019
/**
* Returns the path to the NIMBLE templates directory
* inspîred from /wp-content/plugins/easy-digital-downloads/includes/template-functions.php
*/
function sek_get_templates_dir() {
return NIMBLE_BASE_PATH . "/tmpl";
}
// added for #532, october 2019
/* Returns the template directory name.
* inspîred from /wp-content/plugins/easy-digital-downloads/includes/template-functions.php
*/
function sek_get_theme_template_dir_name() {
return trailingslashit( apply_filters( 'nimble_templates_dir', 'nimble_templates' ) );
}
// added for #532, october 2019
/**
* Returns a list of paths to check for template locations
* inspîred from /wp-content/plugins/easy-digital-downloads/includes/template-functions.php
*/
function sek_get_theme_template_base_paths() {
$template_dir = sek_get_theme_template_dir_name();
$file_paths = array(
1 => trailingslashit( get_stylesheet_directory() ) . $template_dir,
10 => trailingslashit( get_template_directory() ) . $template_dir
);
$file_paths = apply_filters( 'nimble_template_paths', $file_paths );
// sort the file paths based on priority
ksort( $file_paths, SORT_NUMERIC );
return array_map( 'trailingslashit', $file_paths );
}
// @return path string
// added for #400
// @param params = array(
// 'file_name' string 'nimble_template.php',
// 'folder' => string 'page-templates', 'header', 'footer'
// )
// @param
function sek_maybe_get_overriden_local_template_path( $params = array() ) {
if ( empty( $params ) || !is_array( $params ))
return;
$params = wp_parse_args( $params, array( 'file_name' => '', 'folder' => 'page-templates' ) );
if ( !in_array( $params['folder'] , array( 'page-templates', 'header', 'footer' ) ) )
return;
$overriden_template_path = '';
// try locating this template file by looping through the template paths
// inspîred from /wp-content/plugins/easy-digital-downloads/includes/template-functions.php
foreach( sek_get_theme_template_base_paths() as $path_candidate ) {
if( file_exists( $path_candidate . $params['folder'] . '/' . $params['file_name'] ) ) {
$overriden_template_path = $path_candidate . $params['folder'] . '/' . $params['file_name'];
break;
}
}
return $overriden_template_path;
}
// @return mixed null || string
function sek_get_locale_template(){
$template_path = null;
$local_template_data = sek_get_local_option_value( 'template' );
if ( !empty( $local_template_data ) && !empty( $local_template_data['local_template'] ) && 'default' !== $local_template_data['local_template'] ) {
$template_file_name = $local_template_data['local_template'];
$template_file_name_with_php_extension = $template_file_name . '.php';
// Set the default template_path first
$template_path = sek_get_templates_dir() . "/page-templates/{$template_file_name_with_php_extension}";
// Make this filtrable
// (this filter is used in Hueman theme to assign a specific template)
$template_path = apply_filters( 'nimble_get_locale_template_path', $template_path, $template_file_name );
// Use an override if any
// Default page tmpl path looks like : NIMBLE_BASE_PATH . "/tmpl/page-template/nimble_template.php",
$overriden_template_path = sek_maybe_get_overriden_local_template_path( array( 'file_name' => $template_file_name_with_php_extension, 'folder' => 'page-templates' ) );
if ( !empty( $overriden_template_path ) ) {
$template_path = $overriden_template_path;
}
if ( !file_exists( $template_path ) ) {
sek_error_log( __FUNCTION__ .' the custom template does not exist', $template_path );
$template_path = null;
}
}
return $template_path;
}
/* ------------------------------------------------------------------------- *
* HEADER FOOTER
/* ------------------------------------------------------------------------- */
// fired by sek_maybe_set_local_nimble_footer() @get_footer()
// fired by sek_maybe_set_local_nimble_header() @get_header()
function sek_page_uses_nimble_header_footer() {
// cache the properties if not done yet
Nimble_Manager()->sek_maybe_set_nimble_header_footer();
return true === Nimble_Manager()->has_local_header_footer || true === Nimble_Manager()->has_global_header_footer;
}
// DEPRECATED SINCE Nimble v1.3.0, november 2018
// was used in the Hueman theme before version 3.4.9
function render_content_sections_for_nimble_template() {
Nimble_Manager()->render_nimble_locations(
array_keys( Nimble_Manager()->default_locations ),//array( 'loop_start', 'before_content', 'after_content', 'loop_end'),
array( 'fallback_location' => 'loop_start' )
);
}
?>
@@ -0,0 +1,114 @@
<?php
// Recursively walk the level tree until a match is found
// @param id = the id of the level for which the model shall be returned
// @param $collection = sek_get_skoped_seks( $skope_id )['collection']; <= the root collection must always be provided, so we are sure it's
function sek_get_level_model( $id, $collection = array() ) {
$_data = 'no_match';
if ( !is_array( $collection ) ) {
sek_error_log( __FUNCTION__ . ' => invalid collection param when getting model for id : ' . $id );
return $_data;
}
foreach ( $collection as $level_data ) {
// stop here and return if a match was recursively found
if ( 'no_match' != $_data )
break;
if ( array_key_exists( 'id', $level_data ) && $id === $level_data['id'] ) {
$_data = $level_data;
} else {
if ( array_key_exists( 'collection', $level_data ) && is_array( $level_data['collection'] ) ) {
$_data = sek_get_level_model( $id, $level_data['collection'] );
}
}
}
return $_data;
}
// Recursive helper
// Typically used when ajaxing
// Is also used when building the dyn_css or when firing sek_add_css_rules_for_spacing()
// @param id : mandatory
// @param collection : optional <= that's why if missing we must walk all collections : local and global
function sek_get_parent_level_model( $child_level_id = '', $collection = array(), $skope_id = '' ) {
$_parent_level_data = 'no_match';
if ( !is_string( $child_level_id ) || empty( $child_level_id ) ) {
sek_error_log( __FUNCTION__ . ' => missing or invalid child_level_id param.');
return $_parent_level_data;
}
// When no collection is provided, we must walk all collections, local and global.
if ( empty( $collection ) ) {
if ( empty( $skope_id ) ) {
if ( is_array( $_POST ) && !empty( $_POST['location_skope_id'] ) ) {
$skope_id = sanitize_text_field($_POST['location_skope_id']);
} else {
// When fired during an ajax 'customize_save' action, the skp_get_skope_id() is determined with $_POST['local_skope_id']
// @see add_filter( 'skp_get_skope_id', '\Nimble\sek_filter_skp_get_skope_id', 10, 2 );
$skope_id = skp_get_skope_id();
}
}
if ( empty( $skope_id ) || '_skope_not_set_' === $skope_id ) {
sek_error_log( __FUNCTION__ . ' => the skope_id should not be empty.');
}
$local_skope_settings = sek_get_skoped_seks( $skope_id );
$local_collection = ( is_array( $local_skope_settings ) && !empty( $local_skope_settings['collection'] ) ) ? $local_skope_settings['collection'] : array();
$global_skope_settings = sek_get_skoped_seks( NIMBLE_GLOBAL_SKOPE_ID );
$global_collection = ( is_array( $global_skope_settings ) && !empty( $global_skope_settings['collection'] ) ) ? $global_skope_settings['collection'] : array();
$collection = array_merge( $local_collection, $global_collection );
}
foreach ( $collection as $level_data ) {
// stop here and return if a match was recursively found
if ( 'no_match' !== $_parent_level_data )
break;
if ( array_key_exists( 'collection', $level_data ) && is_array( $level_data['collection'] ) ) {
foreach ( $level_data['collection'] as $child_level_data ) {
if ( array_key_exists( 'id', $child_level_data ) && $child_level_id == $child_level_data['id'] ) {
$_parent_level_data = $level_data;
//match found, break this loop
break;
} else {
$_parent_level_data = sek_get_parent_level_model( $child_level_id, $level_data['collection'], $skope_id );
}
}
}
}
return $_parent_level_data;
}
// Return the skope id in which a level will be rendered
// For that, walk the collections local and global to see if there's a match
// Fallback skope is local.
// used for example in the simple form module to print the hidden skope id, needed on submission.
// Recursive helper
// @param id : mandatory
// @param collection : optional <= that's why if missing we must walk all collections : local and global
function sek_get_level_skope_id( $level_id = '' ) {
$level_skope_id = skp_get_skope_id();
if ( !is_string( $level_id ) || empty( $level_id ) ) {
sek_error_log( __FUNCTION__ . ' => missing or invalid child_level_id param.');
return $level_skope_id;
}
$local_skope_settings = sek_get_skoped_seks( $level_skope_id );
$local_collection = ( is_array( $local_skope_settings ) && !empty( $local_skope_settings['collection'] ) ) ? $local_skope_settings['collection'] : array();
// if the level id has not been found in the local sections, we know it's a global level.
// In dev mode, always make sure that the level id is found in the global locations.
if ( 'no_match' === sek_get_level_model( $level_id, $local_collection ) ) {
$level_skope_id = NIMBLE_GLOBAL_SKOPE_ID;
if ( sek_is_dev_mode() ) {
$global_skope_settings = sek_get_skoped_seks( NIMBLE_GLOBAL_SKOPE_ID );
$global_collection = ( is_array( $global_skope_settings ) && !empty( $global_skope_settings['collection'] ) ) ? $global_skope_settings['collection'] : array();
if ( 'no_match' === sek_get_level_model( $level_id, $global_collection ) ) {
sek_error_log( __FUNCTION__ . ' => warning, a level id ( ' . $level_id .' ) was not found in local and global sections.');
}
}
}
return $level_skope_id;
}
?>
@@ -0,0 +1,130 @@
<?php
// return bool
// count the number of global section created, no matter if they are header footer or other global locations
// can be used to determine if we need to render Nimble Builder assets on front. See ::sek_enqueue_front_assets()
function sek_has_global_sections() {
if ( skp_is_customizing() )
return true;
if ( 'not_set' !== Nimble_Manager()->page_has_global_sections )
return Nimble_Manager()->page_has_global_sections;
$maybe_global_sek_post = sek_get_seks_post( NIMBLE_GLOBAL_SKOPE_ID, 'global' );
$nb_section_created = 0;
if ( is_object($maybe_global_sek_post) ) {
$seks_data = maybe_unserialize($maybe_global_sek_post->post_content);
$seks_data = is_array( $seks_data ) ? $seks_data : array();
$nb_section_created = sek_count_not_empty_sections_in_page( $seks_data );
}
// cache now
Nimble_Manager()->page_has_global_sections = $nb_section_created > 0;
return Nimble_Manager()->page_has_global_sections;
}
// @return bool
// added for https://github.com/presscustomizr/nimble-builder/issues/436
// initially used to determine if a post or a page has been customized with Nimble Builder => if so, we add an edit link in the post/page list
// when used in admin, the skope_id must be provided
// can be used to determine if we need to render Nimble Builder assets on front. See ::sek_enqueue_front_assets()
// March 2021 => fixed : function sek_local_skope_has_nimble_sections() => when customzing => seks_data param should be provided after being filtered with customized values
function sek_local_skope_has_nimble_sections( $skope_id = '', $seks_data = null ) {
$skope_id = empty( $skope_id ) ? skp_get_skope_id() : $skope_id;
if ( NIMBLE_GLOBAL_SKOPE_ID === $skope_id ) {
sek_error_log( __FUNCTION__ . ' => error => function should not be used with global skope id' );
return false;
}
if ( 'not_set' !== Nimble_Manager()->page_has_local_sections )
return Nimble_Manager()->page_has_local_sections;
$nb_section_created = 0;
// When the collection is provided use it otherwise get it
if ( is_null($seks_data) || !is_array($seks_data) ) {
$seks_data = sek_get_skoped_seks( $skope_id );
}
if ( is_array( $seks_data ) ) {
$nb_section_created = sek_count_not_empty_sections_in_page( $seks_data );
}
// cache now
Nimble_Manager()->page_has_local_sections = $nb_section_created > 0;
return Nimble_Manager()->page_has_local_sections;
}
// @return boolean
// Indicates if a section level contains at least on module
// Used in SEK_Front_Render::render() to maybe print a css class on the section level
function sek_section_has_modules( $model, $has_module = null ) {
$has_module = is_null( $has_module ) ? false : (bool)$has_module;
foreach ( $model as $level_data ) {
// stop here and return if a match was recursively found
if ( true === $has_module )
break;
if ( is_array( $level_data ) && array_key_exists( 'collection', $level_data ) && is_array( $level_data['collection'] ) ) {
foreach ( $level_data['collection'] as $child_level_data ) {
if ( 'module'== $child_level_data['level'] ) {
$has_module = true;
//match found, break this loop
break;
} else {
$has_module = sek_section_has_modules( $child_level_data, $has_module );
}
}
}
}
return $has_module;
}
/* ------------------------------------------------------------------------- *
* HAS USER STARTED CREATING SECTIONS ?
/* ------------------------------------------------------------------------- */
// @return a boolean
// Used to check if we should render the welcome notice in sek_render_welcome_notice()
function sek_site_has_nimble_sections_created() {
$sek_post_query_vars = array(
'post_type' => NIMBLE_CPT,
'post_status' => get_post_stati(),
//'name' => sanitize_title(),
'posts_per_page' => -1,
'no_found_rows' => true,
'cache_results' => true,
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
'lazy_load_term_meta' => false,
);
$query = new \WP_Query( $sek_post_query_vars );
//sek_error_log('DO WE HAVE SECTIONS ?', $query );
return is_array( $query->posts ) && !empty( $query->posts );
}
// recursive helper to count the number of sections in a given set of sections data
function sek_count_not_empty_sections_in_page( $seks_data, $count = 0 ) {
if ( !is_array( $seks_data ) ) {
sek_error_log( __FUNCTION__ . ' => invalid seks_data param');
return $count;
}
foreach ( $seks_data as $key => $data ) {
if ( is_array( $data ) ) {
if ( !empty( $data['level'] ) && 'section' === $data['level'] ) {
if ( !empty( $data['collection'] ) ) {
$count++;
}
} else {
$count = sek_count_not_empty_sections_in_page( $data, $count );
}
}
}
return $count;
}
?>
@@ -0,0 +1,91 @@
<?php
// /* ------------------------------------------------------------------------- *
// * FEEDBACK NOTIF
// /* ------------------------------------------------------------------------- */
// Invoked when printing the review note in the plugin table, in the 'plugin_row_meta'
// Since this is a quite heavy check, NB stores it in a 7 days long transient
function sek_get_feedback_notif_status() {
if ( sek_feedback_notice_is_dismissed() )
return;
// Check if we already stored the status in a transient first
$transient_name = NIMBLE_FEEDBACK_STATUS_TRANSIENT_ID;
$transient_value = get_transient( $transient_name );
if ( false != $transient_value ) {
return $transient_value;
}
// If transient not set or expired, let's set it and return the feedback status
// $start_version = get_option( 'nimble_started_with_version', NIMBLE_VERSION );
// Bail if user started after v2.1.20, October 22nd 2020 ( set on November 23th 2020 )
// if ( !version_compare( $start_version, '3.1.12', '<=' ) )
// return;
$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;
$customized_pages = 0;
$nb_section_created = 0;
// the global var is easier to handle for array when populated recursively
global $modules_used;
$module_used = array();
foreach ( $query->posts as $post_object ) {
$seks_data = maybe_unserialize($post_object->post_content);
$seks_data = is_array( $seks_data ) ? $seks_data : array();
$nb_section_created += sek_count_not_empty_sections_in_page( $seks_data );
sek_populate_list_of_modules_used( $seks_data );
$customized_pages++;
}
if ( !is_array( $modules_used ) || !is_numeric( $nb_section_created ) || !is_numeric($customized_pages) )
return;
$modules_used = array_unique($modules_used);
$transient_value = 'not_eligible';
// sek_error_log('$section_created ??', $nb_section_created );
// sek_error_log('$modules_used ?? ' . count($modules_used), $modules_used );
// sek_error_log('$customized_pages ??', $customized_pages );
//version_compare( $this->wp_version, '4.1', '>=' )
if ( $customized_pages > 1 && $nb_section_created > 1 && count($modules_used) > 1 ) {
$transient_value = 'eligible';
}
set_transient( $transient_name, $transient_value, 7 * DAY_IN_SECONDS );
return $transient_value;
}
// recursive helper to generate a list of module used in a given set of sections data
function sek_populate_list_of_modules_used( $seks_data ) {
global $modules_used;
if ( !is_array( $seks_data ) ) {
sek_error_log( __FUNCTION__ . ' => invalid seks_data param');
return $count;
}
foreach ( $seks_data as $key => $data ) {
if ( is_array( $data ) ) {
if ( !empty( $data['level'] ) && 'module' === $data['level'] && !empty( $data['module_type'] ) ) {
$modules_used[] = $data['module_type'];
} else {
//$modules_used = array_merge( $modules_used, sek_populate_list_of_modules_used( $data, $modules_used ) );
sek_populate_list_of_modules_used( $data, $modules_used );
}
}
}
}
?>
@@ -0,0 +1,78 @@
<?php
/* ------------------------------------------------------------------------- *
* reCAPTCHA HELPER
/* ------------------------------------------------------------------------- */
// @return boolean
// reCaptcha is enabled globally
// deactivated when customizing
function sek_is_recaptcha_globally_enabled() {
if ( did_action('nimble_front_classes_ready') && '_not_cached_yet_' !== Nimble_Manager()->recaptcha_enabled ) {
return Nimble_Manager()->recaptcha_enabled;
}
$recaptcha_enabled = false;
$glob_recaptcha_opts = sek_get_global_option_value( 'recaptcha' );
if ( !is_null( $glob_recaptcha_opts ) && is_array( $glob_recaptcha_opts ) && !empty( $glob_recaptcha_opts['enable'] ) ) {
$recaptcha_enabled = sek_booleanize_checkbox_val( $glob_recaptcha_opts['enable'] ) && !empty( $glob_recaptcha_opts['public_key'] ) && !empty($glob_recaptcha_opts['private_key'] );
}
// CACHE when not doing ajax
if ( !defined( 'DOING_AJAX') || true !== DOING_AJAX ) {
Nimble_Manager()->recaptcha_enabled = $recaptcha_enabled;
}
return $recaptcha_enabled;
}
// @return boolean
// reCaptcha is enabled globally
// deactivated when customizing
function sek_is_recaptcha_badge_globally_displayed() {
if ( did_action('nimble_front_classes_ready') && '_not_cached_yet_' !== Nimble_Manager()->recaptcha_badge_displayed ) {
return Nimble_Manager()->recaptcha_badge_displayed;
}
$display_badge = false;//disabled by default @see sek_get_module_params_for_sek_global_recaptcha()
$glob_recaptcha_opts = sek_get_global_option_value( 'recaptcha' );
if ( !is_null( $glob_recaptcha_opts ) && is_array( $glob_recaptcha_opts ) && !empty( $glob_recaptcha_opts['badge'] ) ) {
$display_badge = sek_booleanize_checkbox_val( $glob_recaptcha_opts['badge'] ) && sek_is_recaptcha_globally_enabled();
}
// CACHE when not doing ajax
if ( !defined( 'DOING_AJAX') || true !== DOING_AJAX ) {
Nimble_Manager()->recaptcha_badge_displayed = $display_badge;
}
return $display_badge;
}
// @return bool
// used to print reCaptcha js for the form module
function sek_front_sections_include_a_form( $bool = false, $recursive_data = null ) {
if ( !$bool ) {
if ( is_null( $recursive_data ) ) {
$local_skope_settings = sek_get_skoped_seks( skp_get_skope_id() );
$local_collection = ( is_array( $local_skope_settings ) && !empty( $local_skope_settings['collection'] ) ) ? $local_skope_settings['collection'] : array();
$global_skope_settings = sek_get_skoped_seks( NIMBLE_GLOBAL_SKOPE_ID );
$global_collection = ( is_array( $global_skope_settings ) && !empty( $global_skope_settings['collection'] ) ) ? $global_skope_settings['collection'] : array();
$recursive_data = array_merge( $local_collection, $global_collection );
}
foreach ($recursive_data as $key => $value) {
if ( is_array( $value ) && array_key_exists('module_type', $value) && 'czr_simple_form_module' === $value['module_type'] ) {
$bool = true;
break;
} else if ( is_array( $value ) ) {
$bool = sek_front_sections_include_a_form( $bool, $value );
}
}
}
return $bool;
}
?>
@@ -0,0 +1,375 @@
<?php
// /* ------------------------------------------------------------------------- *
// * NIMBLE API
// /* ------------------------------------------------------------------------- */
// Nimble api returns a set of value structured as follow
// return array(
// 'timestamp' => 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']);
// }
?>
@@ -0,0 +1,249 @@
<?php
// This file has been introduced on May 21st 2019 => 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'
)
)
]
]);
}
?>
@@ -0,0 +1,133 @@
<?php
add_action( 'admin_bar_menu', '\Nimble\sek_add_customize_link', 1000 );
function sek_add_customize_link() {
if ( !sek_current_user_can_access_nb_ui() )
return;
global $wp_admin_bar;
// Don't show for users who can't access the customizer
if ( !current_user_can( 'customize' ) )
return;
$return_customize_url = '';
$customize_url = '';
if ( is_admin() ) {
if ( !is_admin_bar_showing() )
return;
$customize_url = sek_get_customize_url_when_is_admin();
} else {
global $wp_customize;
// Don't show if the user cannot edit a given customize_changeset post currently being previewed.
if ( is_customize_preview() && $wp_customize->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( '<span class="sek-nimble-icon" title="%3$s"><img src="%1$s" alt="%2$s"/><span class="sek-nimble-admin-bar-title">%4$s</span></span>',
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;
}
?>
@@ -0,0 +1,375 @@
<?php
// introduced for #799
function sek_maybe_optimize_options() {
$bw_fixes_options = get_option( NIMBLE_OPT_NAME_FOR_BACKWARD_FIXES );
$bw_fixes_options = is_array( $bw_fixes_options ) ? $bw_fixes_options : array();
// March 13th 2021 => 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;
}
?>
@@ -0,0 +1,446 @@
<?php
// SEKTION POST
register_post_type( NIMBLE_CPT , array(
'labels' => 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();
}
?>
@@ -0,0 +1,371 @@
<?php
///////////////////////////////////////////////////////
/// SITE TEMPLATES
// Feb 2021 => 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);
?>
@@ -0,0 +1,269 @@
<?php
/* ------------------------------------------------------------------------- *
* SAVED SECTIONS
/* ------------------------------------------------------------------------- */
// SAVED SECTION POST TYPE
// CPT for section : 'nimble_section'
register_post_type( NIMBLE_SECTION_CPT , array(
'labels' => 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 );
}
?>
@@ -0,0 +1,322 @@
<?php
/* ------------------------------------------------------------------------- *
* SAVED TEMPLATES
/* ------------------------------------------------------------------------- */
// SAVED TEMPLATES POST TYPE
// CPT for template : 'nimble_template'
register_post_type( NIMBLE_TEMPLATE_CPT , array(
'labels' => 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 );
}
?>
@@ -0,0 +1,94 @@
<?php
// Nov 2020
// When sek_is_cpt_debug_mode() = isset( $_GET['nimble_cpt_debug'] ) || (defined('NIMBLE_CPT_DEBUG_MODE') && NIMBLE_CPT_DEBUG_MODE);
// NB custom post types for skoped sections, user saved sections and templates are set to "public" in the WP admin
// To properly vizualise and edit the CPT we need :
// 1) to filter content before it's rendered in the CPT admin editor, so it's rendered as a JSON
// This is done with 'content_edit_pre'
// 2) to disable the rich editor when editing NB CPT, to prevent any html tags insertion done by rich text editor ( done at 'current_screen' )
// 3) before db insertion, to make sur the NB CPT is turned from JSON to serialized value ( done with 'wp_insert_post_data' )
// Disable rich editor when editing NB custom post types
add_action( 'current_screen', function() {
if ( !sek_is_cpt_debug_mode() || !is_admin() || skp_is_customizing() )
return;
global $pagenow;
global $typenow;
if ( 'post.php' === $pagenow && in_array($typenow, [NIMBLE_CPT,NIMBLE_SECTION_CPT,NIMBLE_TEMPLATE_CPT]) ) {
add_filter( 'user_can_richedit' , '__return_false', 50 );
}
});
// Jsonify
add_filter( 'content_edit_pre', function( $content, $post_id ) {
if ( !sek_is_cpt_debug_mode() )
return $content;
$post_type = get_post_type( $post_id );
if ( in_array($post_type, [NIMBLE_CPT,NIMBLE_SECTION_CPT,NIMBLE_TEMPLATE_CPT]) ) {
return wp_json_encode(maybe_unserialize($content), JSON_PRETTY_PRINT);
}
return $content;
}, 10, 2 );
// BEFORE INSERTION / UPDATE
// @see wp-includes/post.php
// Reformat edited custom post type when updating from the editor
// We need to make sure that the reformating occurs ONLY when this is a manual update
// - not an update when customizing => 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);
?>
@@ -0,0 +1,58 @@
<?php
/* ------------------------------------------------------------------------- *
* REVISION HELPERS
/* ------------------------------------------------------------------------- */
/**
* Fetch the revisions of the `nimble_post_type` post for a given {skope_id}
* @param string $skope_id optional
* @return string $skope_level optional
*/
function sek_get_revision_history_from_posts( $skope_id = '', $skope_level = 'local' ) {
//sek_error_log('skope_id in sek_get_seks_post => ' . $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 );
}
?>
@@ -0,0 +1,136 @@
<?php
/* ------------------------------------------------------------------------- *
* SANIIZATION AND VALIDATION HELPERS
* used before saving NB main settings in DB
* used before saving user template in DB
* added March 2021 for https://github.com/presscustomizr/nimble-builder/issues/792, after fixing formatting issues : #544 #791
/* ------------------------------------------------------------------------- */
// Uses the sanitize_callback function specified on module registration if any
// Recursively loop on the local or global main NB collection and fire the sanitize callback
// the $setting_instance param is passed when sanitizing the customizer settings. Not used when sanitizing a user template
function sek_sektion_collection_sanitize_cb( $setting_data, $setting_instance = null ) {
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 ) ) {
$san_callback = sek_get_registered_module_type_property( $setting_data['module_type'], 'sanitize_callback' );
if ( !empty( $san_callback ) && is_string( $san_callback ) && function_exists( $san_callback ) && array_key_exists('value', $setting_data ) ) {
//sek_error_log('SANITIZE ??', $san_callback );
$setting_data['value'] = $san_callback( $setting_data['value'] );
}
} else {
foreach( $setting_data as $k => $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;
}
?>
@@ -0,0 +1,461 @@
<?php
// Filter the local skope id when invoking skp_get_skope_id in a customize_save ajax action
add_filter( 'skp_get_skope_id', '\Nimble\sek_filter_skp_get_skope_id', 10, 2 );
function sek_filter_skp_get_skope_id( $skope_id, $level ) {
// When ajaxing, @see the js callback on 'save-request-params', core hooks for the save query
// api.bind('save-request-params', function( query ) {
// $.extend( query, { local_skope_id : api.czr_skopeBase.getSkopeProperty( 'skope_id' ) } );
// });
// implemented to fix : https://github.com/presscustomizr/nimble-builder/issues/242
if ( 'local' === $level && is_array( $_POST ) && !empty( $_POST['local_skope_id'] ) && 'customize_save' === sanitize_text_field($_POST['action']) ) {
$skope_id = sanitize_text_field($_POST['local_skope_id']);
}
return $skope_id;
}
//@return string
function sek_get_seks_setting_id( $skope_id = '' ) {
if ( empty( $skope_id ) ) {
sek_error_log( __FUNCTION__ . ' => empty skope id or location => collection setting id impossible to build' );
}
return NIMBLE_OPT_PREFIX_FOR_SEKTION_COLLECTION . "[{$skope_id}]";
}
/* ------------------------------------------------------------------------- *
* HELPER FOR CHECKBOX OPTIONS
/* ------------------------------------------------------------------------- */
function sek_is_checked( $val ) {
//cast to string if array
$val = is_array($val) ? $val[0] : $val;
return sek_booleanize_checkbox_val( $val );
}
function sek_booleanize_checkbox_val( $val ) {
if ( !$val || is_array( $val ) ) {
return false;
}
if ( is_bool( $val ) && $val )
return true;
switch ( (string) $val ) {
case 'off':
case '' :
case 'false' :
return false;
case 'on':
case '1' :
case 'true' :
return true;
default : return false;
}
}
/* ------------------------------------------------------------------------- *
* Nimble Widgets Areas
/* ------------------------------------------------------------------------- */
// @return the list of Nimble registered widget areas
function sek_get_registered_widget_areas() {
global $wp_registered_sidebars;
$widget_areas = array();
if ( is_array( $wp_registered_sidebars ) && !empty( $wp_registered_sidebars ) ) {
foreach ( $wp_registered_sidebars as $registered_sb ) {
$id = $registered_sb['id'];
if ( !sek_is_nimble_widget_id( $id ) )
continue;
$widget_areas[ $id ] = $registered_sb['name'];
}
}
return $widget_areas;
}
// @return bool
// @ param $id string
function sek_is_nimble_widget_id( $id ) {
// NIMBLE_WIDGET_PREFIX = nimble-widget-area-
return NIMBLE_WIDGET_PREFIX === substr( $id, 0, strlen( NIMBLE_WIDGET_PREFIX ) );
}
// @return bool
// introduced for #883
function sek_is_widget_module_disabled() {
return sek_booleanize_checkbox_val( get_option( NIMBLE_OPT_NAME_FOR_DISABLING_WIDGET_MODULE ) );
}
/* ------------------------------------------------------------------------- *
* Beta Features
/* ------------------------------------------------------------------------- */
// December 2018 => preparation of the header / footer feature
// The beta features can be control by a constant
// and by a global option
function sek_are_beta_features_enabled() {
$global_beta_feature = sek_get_global_option_value( 'beta_features');
if ( is_array( $global_beta_feature ) && array_key_exists('beta-enabled', $global_beta_feature ) ) {
return (bool)$global_beta_feature['beta-enabled'];
}
return NIMBLE_BETA_FEATURES_ENABLED;
}
/* ------------------------------------------------------------------------- *
* PRO
/* ------------------------------------------------------------------------- */
function sek_is_pro() {
return defined('NB_PRO_VERSION');
}
/* ------------------------------------------------------------------------- *
* VERSION HELPERS
/* ------------------------------------------------------------------------- */
/**
* Returns a boolean
* check if user started to use the plugin before ( strictly < ) the requested version
* @param $_ver : string free version
*/
function sek_user_started_before_version( $requested_version ) {
$started_with = get_option( 'nimble_started_with_version' );
//the transient is set in HU_utils::hu_init_properties()
if ( !$started_with )
return false;
if ( !is_string( $requested_version ) )
return false;
return version_compare( $started_with , $requested_version, '<' );
}
/* ------------------------------------------------------------------------- *
* VARIOUS HELPERS
/* ------------------------------------------------------------------------- */
function sek_text_truncate( $text, $max_text_length, $more, $strip_tags = true ) {
if ( !$text )
return '';
if ( $strip_tags )
$text = strip_tags( $text );
if ( !$max_text_length )
return $text;
$end_substr = $text_length = strlen( $text );
if ( $text_length > $max_text_length ) {
$text .= ' ';
$end_substr = strpos( $text, ' ' , $max_text_length);
$end_substr = ( FALSE !== $end_substr ) ? $end_substr : $max_text_length;
$text = trim( substr( $text , 0 , $end_substr ) );
}
if ( $more && $end_substr < $text_length )
return $text . ' ' .$more;
return $text;
}
// @return a bool
// typically when
// previewing a changeset on front with a link generated in the publish menu of the customizer
// looking like : mysite.com/?customize_changeset_uuid=67862e7f-427c-4183-b3f7-62eb86f79899
// in this case the $_REQUEST super global, doesn't include a customize_messenger_channel paral
// added when fixing https://github.com/presscustomizr/nimble-builder/issues/351
function sek_is_customize_previewing_a_changeset_post() {
return !( defined('DOING_AJAX') && DOING_AJAX ) && is_customize_preview() && !isset( $_REQUEST['customize_messenger_channel']);
}
// @return string theme name
// always return the parent theme name
function sek_get_parent_theme_slug() {
$theme_slug = get_option( 'stylesheet' );
// $_REQUEST['theme'] is set both in live preview and when we're customizing a non active theme
$theme_slug = sanitize_text_field( isset($_REQUEST['theme']) ? $_REQUEST['theme'] : $theme_slug ); //old wp versions
$theme_slug = sanitize_text_field( isset($_REQUEST['customize_theme']) ? $_REQUEST['customize_theme'] : $theme_slug );
//gets the theme name (or parent if child)
$theme_data = wp_get_theme( $theme_slug );
if ( $theme_data->parent() ) {
$theme_slug = $theme_data->parent()->Name;
}
return sanitize_file_name( strtolower( $theme_slug ) );
}
function sek_error_log( $title, $content = null ) {
// Know in which function sek_error_log() was called
// debug_backtrace()[1]['function'];
$backtrace = debug_backtrace();
//error_log( print_r( $backtrace, true ) );
$content = is_null( $content ) ? '' : $content;
if ( !sek_is_dev_mode() )
return;
$btrace = '';
if ( is_array($backtrace) && isset($backtrace[1]) ) {
if ( !empty( $backtrace[1]['file'] ) && !empty( $backtrace[1]['line'] ) ) {
$btrace = $btrace . "\n ====> " . $backtrace[1]['file'] . '#' . $backtrace[1]['line'];
}
if ( !empty( $backtrace[1]['class'] ) && !empty( $backtrace[1]['function'] ) ) {
$btrace = $btrace . "\n ====> " . $backtrace[1]['class'] . '::' . $backtrace[1]['function'];
} else if ( !empty( $backtrace[1]['function']) ) {
$btrace = $btrace . "\n ====> " . $backtrace[1]['function'];
}
}
if ( is_null( $content ) ) {
error_log( '<' . $title . '>' );
if ( !empty($btrace) ) {
error_log( '// backtrace :' . $btrace . '//' );
}
} else {
error_log( '<' . $title . '>' );
error_log( print_r( $content, true ) );
if ( !empty($btrace) ) {
error_log( '// backtrace :' . $btrace . '//' );
}
error_log( '</' . $title . '>' );
}
}
// /* ------------------------------------------------------------------------- *
// * HELPERS FOR ADMIN AND API TO DETERMINE / CHECK CURRENT THEME NAME
// /* ------------------------------------------------------------------------- */
// @return bool
function sek_is_presscustomizr_theme( $theme_name ) {
$bool = false;
if ( is_string( $theme_name ) ) {
foreach ( ['customizr', 'hueman'] as $pc_theme ) {
// handle the case when the theme name looks like customizr-4.1.29
if ( !$bool && $pc_theme === substr( $theme_name, 0, strlen($pc_theme) ) ) {
$bool = true;
}
}
}
return $bool;
}
// @return the theme name string, exact if customizr or hueman
function sek_maybe_get_presscustomizr_theme_name( $theme_name ) {
if ( is_string( $theme_name ) ) {
foreach ( ['customizr', 'hueman'] as $pc_theme ) {
// handle the case when the theme name looks like customizr-4.1.29
if ( $pc_theme === substr( $theme_name, 0, strlen($pc_theme) ) ) {
$theme_name = $pc_theme;
}
}
}
return $theme_name;
}
// @return a string
function sek_get_th_start_ver( $theme_name ) {
if ( !in_array( $theme_name, ['customizr', 'hueman'] ) )
return '';
$start_ver = '';
switch( $theme_name ) {
case 'customizr' :
$start_ver = defined( 'CZR_USER_STARTED_USING_FREE_THEME' ) ? CZR_USER_STARTED_USING_FREE_THEME : '';
break;
case 'hueman' :
$start_ver = get_transient( 'started_using_hueman' );
break;
}
return $start_ver;
}
/* ------------------------------------------------------------------------- *
* STRIP SCRIPT TAG WHEN CUSTOMIZING
* to prevent customizer breakages. See https://github.com/presscustomizr/nimble-builder/issues/688
/* ------------------------------------------------------------------------- */
function sek_strip_script_tags_and_print_js_inline( $html, $model ) {
if ( !is_string( $html ) )
return $html;
if ( skp_is_customizing() ) {
// June 2020 => added a notice for https://github.com/presscustomizr/nimble-builder/issues/710
$script_notice = sprintf('<div class="nimble-notice-in-preview"><i class="fas fa-info-circle"></i>&nbsp;%1$s</div>',
__('Custom javascript code is not executed when customizing.', 'text-doma')
);
return preg_replace('#<script(.*?)>(.*?)</script>#is', $script_notice, $html);
} else {
$js = "";
// Get the script tags content
preg_match_all('/<script>(.*?)<\/script>/s', $html, $matches);
foreach ($matches[1] as $value) {
if (!empty($value)) {
$js .= $value .";\n";
}
}
// Remove the scripts from the content
$html = preg_replace('#<script(.*?)</script>#is', '', $html);
// Print scripts inline safely
if ( !empty($js) ) {
wp_register_script( 'nb_module_custom_js_' . $model['id'], '');
wp_enqueue_script( 'nb_module_custom_js_' . $model['id'] );
wp_add_inline_script( 'nb_module_custom_js_' . $model['id'], $js );
}
return $html;
}
}
function sek_strip_script_tags( $html = '' ) {
if (!is_string( $html ) ) {
return $html;
}
return preg_replace('#<script(.*?)>(.*?)</script>#is', '', $html);
}
// @return bool
// Introduced May 2020
function sek_current_user_can_access_nb_ui() {
return apply_filters('nb_user_has_access', true );
}
/* ------------------------------------------------------------------------- *
* TRANSIENTS
/* ------------------------------------------------------------------------- */
// July 2020 : introduced for https://github.com/presscustomizr/nimble-builder/issues/730
function sek_clean_transients_like( $transient_string ) {
global $wpdb;
$where_like = '%'.$transient_string.'%';
$sql = "SELECT `option_name` AS `name`, `option_value` AS `value`
FROM $wpdb->options
WHERE `option_name` LIKE '$where_like'
ORDER BY `option_name`";
$results = $wpdb->get_results( $sql );
$transients = array();
// TRANSIENTS
foreach ( $results as $result ) {
if ( 0 === strpos( $result->name, '_transient' ) ) {
if ( 0 === strpos( $result->name, '_transient_timeout_') ) {
$transients['transient_timeout'][ $result->name ] = $result->value;
} else {
$transients['transient'][ $result->name ] = maybe_unserialize( $result->value );
}
}
}
//sek_error_log('CLEAN PAST TRANSIENTS ' . $transient_string, $transients );
// Clean the transients found
// transient name looks like _transient_section_json_transient_2.1.5
// when deleted, it also removes the associated _transient_timeout_... option
foreach ($transients as $group => $list) {
if ( 'transient' === $group && is_array($list) ) {
foreach ($list as $name => $val) {
// the transient name does not include the "_transient_" prefix
$trans_name = substr($name, strlen('_transient_') );
delete_transient( $trans_name );
}
}
}
}
/* ------------------------------------------------------------------------- *
* OPTIONS CLEANING
/* ------------------------------------------------------------------------- */
// introduced for https://github.com/presscustomizr/nimble-builder/issues/826
function sek_clean_options_starting_like( $opt_string ) {
global $wpdb;
$where_like = '%'.$opt_string.'%';
$sql = "SELECT `option_name` AS `name`, `option_value` AS `value`
FROM $wpdb->options
WHERE `option_name` LIKE '$where_like'
ORDER BY `option_name`";
$results = $wpdb->get_results( $sql );
if ( !is_array( $results ) )
return;
foreach ( $results as $result ) {
if ( 0 === strpos( $result->name, $opt_string ) ) {
delete_option( $result->name );
}
}
}
// July 2020 : introduced for https://github.com/presscustomizr/nimble-builder/issues/720
// @param $features (string) list of features
function sek_get_pro_notice_for_czr_input( $features = '' ) {
if ( !sek_is_upsell_enabled() )
return '';
return sprintf( '<hr/><p class="sek-pro-notice"><img class="sek-pro-icon" src="%1$s"/><span class="sek-pro-notice-icon-bef-text"><img src="%2$s"/></span><span class="sek-pro-notice-text">%3$s : %4$s<br/><br/>%5$s</span><p>',
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('<a href="%1$s" rel="noopener noreferrer" title="%2$s" target="_blank">%2$s <i class="fas fa-external-link-alt"></i></a>',
'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();
}
?>
@@ -0,0 +1,432 @@
<?php
if ( !class_exists( '\Nimble\Sek_Simple_Form' ) ) :
class Sek_Simple_Form extends SEK_Front_Render_Css {
private $form;
private $fields;
private $mailer;
private $form_composition;
function _setup_simple_forms() {
//Hooks
add_action( 'parse_request', array( $this, 'simple_form_parse_request' ), 20 );
add_action( 'wp_enqueue_scripts', array( $this, 'maybe_enqueue_recaptcha_scripts' ), 0 );
add_filter( 'body_class', array( $this, 'set_the_recaptcha_badge_visibility_class') );
// Note : form input need to be prefixed to avoid a collision with reserved WordPress input
// @see : https://stackoverflow.com/questions/15685020/wordpress-form-submission-and-the-404-error-page#16636051
$this->form_composition = array(
'nimble_simple_cf' => array(
'type' => 'hidden',
'value' => 'nimble_simple_cf'
),
'nimble_recaptcha_resp' => array(
'type' => 'hidden',
'value' => ''
),
'nimble_skope_id' => array(
'type' => 'hidden',
'value' => ''
),
'nimble_level_id' => array(
'type' => 'hidden',
'value' => ''
),
'nimble_name' => array(
'label' => __( 'Name', 'text_doma' ),
'required' => true,
'type' => 'text',
'wrapper_tag' => 'div'
),
'nimble_email' => array(
'label' => __( 'Email', 'text_doma' ),
'required' => true,
'type' => 'email',
'wrapper_tag' => 'div'
),
'nimble_subject' => array(
'label' => __( 'Subject', 'text_doma' ),
'type' => 'text',
'wrapper_tag' => 'div'
),
'nimble_message' => array(
'label' => __( 'Message', 'text_doma' ),
'required' => true,
'additional_attrs' => array( 'rows' => "10", 'cols' => "50" ),
'type' => 'textarea',
'wrapper_tag' => 'div'
),
'nimble_privacy' => array(
'label' => __( 'I have read and agree to the privacy policy.', 'text_doma' ),
'type' => 'checkbox',
'required' => true,
'value' => false,
//'additional_attrs' => array( 'class' => 'sek-btn' ),
'wrapper_tag' => 'div',
'wrapper_class' => array( 'sek-form-field', 'sek-privacy-wrapper' )
),
'nimble_submit' => array(
'type' => 'submit',
'value' => __( 'Submit', 'text_doma' ),
'additional_attrs' => array( 'class' => 'sek-btn' ),
'wrapper_tag' => 'div',
'wrapper_class' => array( 'sek-form-field', 'sek-form-btn-wrapper' )
)
);
}//_setup_simple_forms
//@hook: parse_request
function simple_form_parse_request() {
if ( !isset( $_POST['nimble_simple_cf'] ) )
return;
// get the module options
// we are before 'wp', so let's use the posted skope_id and level_id to get our $module_user_values
$module_model = array();
if ( isset( $_POST['nimble_skope_id'] ) && '_skope_not_set_' !== sanitize_text_field($_POST['nimble_skope_id']) ) {
$local_sektions = sek_get_skoped_seks( sanitize_text_field($_POST['nimble_skope_id']) );
if ( is_array( $local_sektions ) && !empty( $local_sektions ) ) {
$sektion_collection = array_key_exists('collection', $local_sektions) ? $local_sektions['collection'] : array();
}
if ( is_array($sektion_collection) && !empty( $sektion_collection ) && isset( $_POST['nimble_level_id'] ) ) {
$module_model = sek_get_level_model( sanitize_text_field($_POST['nimble_level_id']), $sektion_collection );
$module_model = sek_normalize_module_value_with_defaults( $module_model );
}
} else {
sek_error_log( __FUNCTION__ . ' => skope_id problem');
return;
}
if ( empty( $module_model ) ) {
sek_error_log( __FUNCTION__ . ' => invalid module model');
return;
}
//update the form with the posted values
foreach ( $this->form_composition as $name => $field ) {
$form_composition[ $name ] = $field;
if ( isset( $_POST[ $name ] ) ) {
$form_composition[ $name ][ 'value' ] = sanitize_text_field($_POST[ $name ]);
}
}
//set the form composition according to the user's options
$form_composition = $this->_set_form_composition( $form_composition, $module_model );
//generate fields
$this->fields = $this->simple_form_generate_fields( $form_composition );
//generate form
$this->form = $this->simple_form_generate_form( $this->fields, $module_model );
//mailer
$this->mailer = new Sek_Mailer( $this->form );
$this->mailer->maybe_send( $form_composition, $module_model );
}
// Fired @hook wp_enqueue_scripts
// @return void()
function maybe_enqueue_recaptcha_scripts() {
// enabled if
// - not customizing
// - global 'recaptcha' options has the following values
// - enabled === true
// - public_key entered
// - private_key entered
// - the current page does not include a form in a local or global location
if ( skp_is_customizing() || !sek_is_recaptcha_globally_enabled() || !sek_front_sections_include_a_form() )
return;
// @todo, we don't handle the case when reCaptcha is globally enabled but disabled for a particular form.
$global_recaptcha_opts = sek_get_global_option_value('recaptcha');
$global_recaptcha_opts = is_array( $global_recaptcha_opts ) ? $global_recaptcha_opts : array();
$url = add_query_arg(
array( 'render' => esc_attr( $global_recaptcha_opts['public_key'] ) ),
'https://www.google.com/recaptcha/api.js'
);
wp_enqueue_script( 'google-recaptcha', $url, array(), '3.0', true );
add_action('wp_head', array( $this, 'print_recaptcha_inline_js'), PHP_INT_MAX );
}
// @hook wp_footer
// printed only when sek_is_recaptcha_globally_enabled()
// AND
// sek_front_sections_include_a_form()
function print_recaptcha_inline_js() {
ob_start();
?>
if ( sekFrontLocalized.recaptcha_public_key ) {
!( function( grecaptcha, sitekey ) {
var recaptcha = {
execute: function() {
var _action = ( window.sekFrontLocalized && sekFrontLocalized.skope_id ) ? sekFrontLocalized.skope_id.replace( 'skp__' , 'nimble_form__' ) : 'nimble_builder_form';
grecaptcha.execute(
sitekey,
// see https://developers.google.com/recaptcha/docs/v3#actions
{ action: _action }
).then( function( token ) {
var forms = document.getElementsByTagName( 'form' );
for ( var i = 0; i < forms.length; i++ ) {
var fields = forms[ i ].getElementsByTagName( 'input' );
for ( var j = 0; j < fields.length; j++ ) {
var field = fields[ j ];
if ( 'nimble_recaptcha_resp' === field.getAttribute( 'name' ) ) {
field.setAttribute( 'value', token );
break;
}
}
}
} );
}
};
grecaptcha.ready( recaptcha.execute );
})( grecaptcha, sekFrontLocalized.recaptcha_public_key );
} else {
if ( window.console && window.console.log ) {
console.log('Nimble Builder form error => missing reCAPTCHA key');
}
}
<?php
$script = ob_get_clean();
wp_register_script( 'nb_recaptcha_js', '');
wp_enqueue_script( 'nb_recaptcha_js' );
wp_add_inline_script( 'nb_recaptcha_js', $script );
}
// @hook body_class
public function set_the_recaptcha_badge_visibility_class( $classes ) {
// Shall we print the badge ?
// @todo : we don't handle the case when recaptcha badge is globally displayed but the current page has disabled recaptcha
$classes[] = !sek_is_recaptcha_badge_globally_displayed() ? 'sek-hide-rc-badge' : 'sek-show-rc-badge';
return $classes;
}
// Rendering
// Invoked from the tmpl
// @return string
// @param module_options is the module level "value" property. @see tmpl/modules/simple_form_module_tmpl.php
function get_simple_form_html( $module_model ) {
// sek_error_log('$module_model ?', $module_model );
// sek_error_log('$this->fields ?', $this->fields );
// sek_error_log('$this->form ?', $this->form );
// sek_error_log('$this->mailer ?', $this->mailer );
// sek_error_log('$_POST ?', $_POST );
$html = '';
//set the form composition according to the user's options
$form_composition = $this->_set_form_composition( $this->form_composition, $module_model );
//generate fields
$fields = isset( $this->fields ) ? $this->fields : $this->simple_form_generate_fields( $form_composition );
//generate form
$form = isset( $this->form ) ? $this->form : $this->simple_form_generate_form( $fields, $module_model );
$module_id = is_array( $module_model ) && array_key_exists('id', $module_model ) ? $module_model['id'] : '';
ob_start();
?>
<div id="sek-form-respond">
<?php
$echo_form = true;
// When loading the page after a send attempt, focus on the module html element with a javascript animation
// In this case, don't echo the form, but only the user defined message which should be displayed after submitting the form
if ( !is_null( $this->mailer ) ) {
// Make sure we target the right form if several forms are displayed in a page
$current_form_has_been_submitted = isset( $_POST['nimble_level_id'] ) && sanitize_text_field($_POST['nimble_level_id']) === $module_id;
if ( 'sent' == $this->mailer->get_status() && $current_form_has_been_submitted ) {
$echo_form = false;
}
}
if ( !$echo_form ) {
ob_start();
?>
nb_.listenTo( 'nb-jquery-loaded', function() {
jQuery( function($) {
var $elToFocusOn = $('div[data-sek-id="<?php echo esc_attr($module_id); ?>"]' );
if ( $elToFocusOn.length > 0 ) {
var _do = function() {
$('html, body').animate({
scrollTop : $elToFocusOn.offset().top - ( $(window).height() / 2 ) + ( $elToFocusOn.outerHeight() / 2 )
}, 'slow');
};
try { _do(); } catch(er) {}
}
});
});
<?php
$script = ob_get_clean();
wp_register_script( 'nb_simple_form_js', '');
wp_enqueue_script( 'nb_simple_form_js' );
wp_add_inline_script( 'nb_simple_form_js', $script );
$message = $this->mailer->get_message( $this->mailer->get_status(), $module_model );
if ( !empty($message) ) {
$class = 'sek-mail-failure';
switch( $this->mailer->get_status() ) {
case 'sent' :
$class = 'sek-mail-success';
break;
case 'not_sent' :
$class = '';
break;
case 'aborted' :
$class = 'sek-mail-aborted';
break;
}
printf( '<div class="sek-form-message %1$s">%2$s</div>', esc_attr($class), wp_kses_post($message) );
}
} else {
// If we're in the regular case ( not after submission ), echo the form
echo $form;//The output is late escaped in tmpl/modules/simple_form_module_tmpl.php, as this function only returns the html content
}
?>
</div>
<?php
return ob_get_clean();
}
//set the fields to render
private function _set_form_composition( $form_composition, $module_model = array() ) {
$user_form_composition = array();
if ( !is_array( $module_model ) ) {
sek_error_log( __CLASS__ . '::' . __FUNCTION__ . ' => ERROR : invalid module options array');
return $user_form_composition;
}
$module_user_values = array_key_exists( 'value', $module_model ) ? $module_model['value'] : array();
//sek_error_log( '$module_model', $module_model );
$form_fields_options = empty( $module_user_values['form_fields'] ) ? array() : $module_user_values['form_fields'];
$form_button_options = empty( $module_user_values['form_button'] ) ? array() : $module_user_values['form_button'];
$form_submission_options = empty( $module_user_values['form_submission'] ) ? array() : $module_user_values['form_submission'];
foreach ( $form_composition as $field_id => $field_data ) {
//sek_error_log( '$field_data', $field_data );
switch ( $field_id ) {
case 'nimble_name':
if ( !empty( $form_fields_options['show_name_field'] ) && sek_is_checked( $form_fields_options['show_name_field'] ) ) {
$user_form_composition[$field_id] = $field_data;
$user_form_composition[$field_id]['required'] = sek_is_checked( $form_fields_options['name_field_required'] );
$user_form_composition[$field_id]['label'] = esc_attr( $form_fields_options['name_field_label'] );
}
break;
case 'nimble_subject':
if ( !empty( $form_fields_options['show_subject_field'] ) && sek_is_checked( $form_fields_options['show_subject_field'] ) ) {
$user_form_composition[$field_id] = $field_data;
$user_form_composition[$field_id]['required'] = sek_is_checked( $form_fields_options['subject_field_required'] );
$user_form_composition[$field_id]['label'] = esc_attr( $form_fields_options['subject_field_label'] );
}
break;
case 'nimble_message':
if ( !empty( $form_fields_options['show_message_field'] ) && sek_is_checked( $form_fields_options['show_message_field'] ) ) {
$user_form_composition[$field_id] = $field_data;
$user_form_composition[$field_id]['required'] = sek_is_checked( $form_fields_options['message_field_required'] );
$user_form_composition[$field_id]['label'] = esc_attr( $form_fields_options['message_field_label'] );
}
break;
case 'nimble_email':
$user_form_composition[$field_id] = $field_data;
$user_form_composition[$field_id]['label'] = esc_attr( $form_fields_options['email_field_label'] );
break;
case 'nimble_privacy':
if ( !empty( $form_fields_options['show_privacy_field'] ) && sek_is_checked( $form_fields_options['show_privacy_field'] ) ) {
$user_form_composition[$field_id] = $field_data;
$user_form_composition[$field_id]['required'] = sek_is_checked( $form_fields_options['privacy_field_required'] );
// prevent users running script in this field while customizing
$user_form_composition[$field_id]['label'] = sek_strip_script_tags_and_print_js_inline( $form_fields_options['privacy_field_label'], $module_model );
// Feb 2021 : now saved as a json to fix emojis issues
// see fix for https://github.com/presscustomizr/nimble-builder/issues/544
// to ensure retrocompatibility with data previously not saved as json, we need to perform a json validity check
$user_form_composition[$field_id]['label'] = sek_maybe_decode_richtext( $user_form_composition[$field_id]['label'] );
}
break;
//'additional_attrs' => array( 'class' => 'sek-btn' ),
case 'nimble_submit':
$user_form_composition[$field_id] = $field_data;
$visual_effect_class = '';
//visual effect classes
if ( array_key_exists( 'use_box_shadow', $form_button_options ) && true === sek_booleanize_checkbox_val( $form_button_options['use_box_shadow'] ) ) {
$visual_effect_class = ' box-shadow';
if ( array_key_exists( 'push_effect', $form_button_options ) && true === sek_booleanize_checkbox_val( $form_button_options['push_effect'] ) ) {
$visual_effect_class .= ' push-effect';
}
}
$user_form_composition[$field_id]['additional_attrs']['class'] = 'sek-btn' . $visual_effect_class;
// Feb 2021 : now saved as a json to fix emojis issues
// see fix for https://github.com/presscustomizr/nimble-builder/issues/544
// to ensure retrocompatibility with data previously not saved as json, we need to perform a json validity check
$user_form_composition[$field_id]['value'] = sek_maybe_decode_richtext( $form_fields_options['button_text'] );
break;
case 'nimble_skope_id':
$user_form_composition[$field_id] = $field_data;
// When the form is submitted, we grab the skope_id from the posted value, because it is too early to build it.
// of course we don't need to set this input value when customizing.
$skope_id = '';
if ( !skp_is_customizing() ) {
$skope_id = isset( $_POST['nimble_skope_id'] ) ? sanitize_text_field($_POST['nimble_skope_id']) : sek_get_level_skope_id( $module_model['id'] );
}
// always use the posted skope_id
// => in a scenario in which we post the form several times, the skp_get_skope_id() won't be available after the first submit action
$user_form_composition[$field_id]['value'] = $skope_id;
break;
case 'nimble_level_id':
$user_form_composition[$field_id] = $field_data;
$user_form_composition[$field_id]['value'] = $module_model['id'];
break;
// print the recaptcha input field if
// 1) reCAPTCHA enabled in the global options AND properly setup with non empty keys
// 2) reCAPTCHA enabled for this particular form
case 'nimble_recaptcha_resp' :
if ( !skp_is_customizing() && sek_is_recaptcha_globally_enabled() && 'disabled' !== $form_submission_options['recaptcha_enabled'] ) {
$user_form_composition[$field_id] = $field_data;
}
break;
default:
$user_form_composition[$field_id] = $field_data;
break;
}
}
return $user_form_composition;
}
//generate the fields
function simple_form_generate_fields( $form_composition = array() ) {
$form_composition = empty( $form_composition ) ? $this->form_composition : $form_composition;
$fields_ = array();
$id_suffix = rand();
foreach ( $form_composition as $name => $field ) {
$field = wp_parse_args( $field, array( 'type' => 'text' ) );
if ( class_exists( $class = '\Nimble\Sek_Input_' . ucfirst( $field['type'] ) ) ) {
$fields_ [] = new Sek_Field (
new $class( array_merge( array( 'id_suffix'=> $id_suffix ), $field, array( 'name' => $name ) ) ),
$field
);
}
}
return $fields_;
}
//generate the fields
function simple_form_generate_form( $fields, $module_model ) {
$form = new Sek_Form( [
'action' => is_array( $module_model ) && !empty( $module_model['id']) ? '#' . $module_model['id'] :'#',
'method' => 'post'
] );
$form->add_fields( $fields );
return $form;
}
}//Sek_Simple_Form
endif;
?>
@@ -0,0 +1,195 @@
<?php
/*
*&
*
*/
if ( !class_exists( '\Nimble\Sek_Form' ) ) :
class Sek_Form {
private $fields;
private $attributes;
// Sek_Form is instantiated from Sek_Simple_Form::simple_form_generate_form
//$form = new Sek_Form( [
// 'action' => is_array( $module_model ) && !empty( $module_model['id']) ? $module_model['id'] :'#',
// 'method' => 'post'
// ] );
public function __construct( $args = array() ) {
$this->fields = array();
$this->attributes = wp_parse_args( $args, array(
'action' => '#',// the action attribute doesn't have to be specified when form data is sent to the same page
// see https://developer.mozilla.org/en-US/docs/Learn/Forms/Sending_and_retrieving_form_data,
// for the moment we use the parent module id, example : #__nimble__cd4d5b307a3b
'method' => 'post'
//TODO: add html callback
) );
}
public function add_field( Sek_Field $field ) {
$this->fields[ sanitize_key( $field->get_input()->get_data('name') ) ] = $field;
}
public function add_fields( array $fields ) {
foreach ($fields as $field) {
$this->add_field( $field );
}
}
public function get_fields() {
return $this->fields;
}
public function get_field( $field_name ) {
if ( is_array( $this->fields ) && array_key_exists(sanitize_key( $field_name ), $this->fields) ) {
return $this->fields[ sanitize_key( $field_name ) ];
}
return null;
}
//make sure fields are well formed
public function has_invalid_field() {
$has_invalid_field = false;
foreach ( $this->fields as $form_field ) {
if ( false !== $has_invalid_field )
continue;
$input = $form_field->get_input();
$value = $input->get_value();
$filter = $input->get_data( 'filter' );
$can_be_empty = true !== $input->get_data( 'required' );
if ( $can_be_empty && !$value ) {
continue;
}
if ( $filter && !filter_var( $value, $filter ) ) {
$has_invalid_field = $input->get_data('label');
break;
}
}
return $has_invalid_field;
}
public function get_attributes_html() {
return implode( ' ', array_map(
function ($k, $v) {
return sanitize_key( $k ) .'="'. esc_attr( $v ) .'"';
},
array_keys( $this->attributes ), $this->attributes
) );
}
public function __toString() {
return $this->html();
}
public function html() {
$fields = '';
foreach ($this->fields as $name => $field) {
$fields .= $field;
}
// The form output is late escaped on rendering in tmpl\modules\simple_form_module_tmpl.php
return sprintf('<form %1$s>%2$s</form>',
$this->get_attributes_html(),
$fields
);
}
}//Sek_Form
endif;
/*
* Field class definition
*
* label and/or wrapper + input field
*/
if ( !class_exists( '\Nimble\Sek_Field' ) ) :
class Sek_Field {
private $input;
private $data;
public function __construct( Sek_Input_Interface $input, $args = array() ) {
$this->input = $input;
$this->data = wp_parse_args( $args, [
'wrapper_tag' => '',
'wrapper_class' => array( 'sek-form-field' ),
'label' => '',
//TODO: allow callbacks
'before_label' => '',
'after_label' => '',
'before_input' => '',
'after_input' => '',
]);
}
public function get_input() {
return $this->input;
}
public function __toString() {
return $this->html();
}
public function html() {
$label = $this->data[ 'label' ];
//label stuff
if ( $label ) {
if ( true == $this->input->get_data( 'required' ) ) {
$label .= ' *';
//$label .= ' ' . esc_html__( '(required)', 'text_doma' );
}
$label = sprintf( '%1$s<label for="%2$s">%3$s</label>%4$s',
$this->data[ 'before_label' ],
esc_attr( $this->input->get_data( 'id' ) ),
wp_specialchars_decode($label),
$this->data[ 'after_label' ]
);
}
//the input
if ( !empty($this->data['type']) && 'checkbox' === $this->data['type'] ) {
$html = sprintf( '%s%s%s%s',
$this->data[ 'before_input' ],
$this->input,
$this->data[ 'after_input' ],
$label
);
} else {
$html = sprintf( '%s%s%s%s',
$label,
$this->data[ 'before_input' ],
$this->input,
$this->data[ 'after_input' ]
);
}
//any wrapper?
if ( $this->data[ 'wrapper_tag' ] ) {
$wrapper_tag = tag_escape( $this->data[ 'wrapper_tag' ] );
$wrapper_class = $this->data[ 'wrapper_class' ] ? ' class="'. implode( ' ', array_map('sanitize_html_class', $this->data[ 'wrapper_class' ] ) ) .'"' : '';
$html = sprintf( '<%1$s%2$s>%3$s</%1$s>',
$wrapper_tag,
esc_attr($wrapper_class),
$html
);
}
return $html;
}
}
endif;
?>
@@ -0,0 +1,290 @@
<?php
/*
*
* Input objects definition
*
*/
interface Sek_Input_Interface {
public function sanitize( $value );
public function escape( $value );
public function get_value();
public function set_value( $value );
public function get_data( $data_key );
public function html();
}
abstract class Sek_Input_Abstract implements Sek_Input_Interface {
private $data;
protected $attributes = array( 'id', 'name', 'required' );
public function __construct( $args ) {
//no name no party
//TODO: raise exception
if ( !isset( $args['name'] ) ) {
error_log( __FUNCTION__ . ' => contact form input name not set' );
return;
}
$defaults = array(
'name' => '',
'id' => '',
'id_suffix' => null,
'additional_attrs' => array(),
'sanitize_cb' => array( $this, 'sanitize' ),
'escape_cb' => array( $this, 'escape' ),
'required' => false,
'filter' => '',
'value' => ''
);
$data = wp_parse_args( $args, $defaults );
$data[ 'id_suffix' ] = is_null( $data[ 'id_suffix' ] ) ? rand() : $data[ 'id_suffix' ];
$data[ 'id' ] = empty( $data[ 'id' ] ) ? $data[ 'name' ] : $data[ 'id' ];
$data[ 'id' ] = $data[ 'id' ] . $data[ 'id_suffix' ];
$data[ 'additional_attrs' ] = is_array( $data[ 'additional_attrs' ] ) ? $data[ 'additional_attrs' ] : array();
$this->data = $data;
if ( $data[ 'value' ] ) {
$this->set_value( $data[ 'value' ] );
}
}
public function sanitize( $value ) {
return $value;
}
public function escape( $value ) {
return esc_attr( $value );
}
public function get_value() {
$data = (array)$this->data;
$value = $this->data['escape_cb']( $data['value'] );
if ( skp_is_customizing() ) {
$field_name = $this->get_data('name');
switch( $field_name ) {
case 'nimble_name' :
$value = '';
break;
case 'nimble_email' :
$value = '';
break;
case 'nimble_subject' :
$value = '';
break;
// case 'nimble_message' :
// $value = __('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus.', 'text-domain');
// break;
}
}
return $value;
}
public function set_value( $value ) {
$this->data['value'] = $this->data['sanitize_cb']( $value );
}
public function get_data( $data_key ){
return isset( $this->data[ $data_key ] ) ? $this->data[ $data_key ] : null;
}
public function get_attributes_html() {
$attributes = array_merge(
array_intersect_key(
array_filter( $this->data ),
array_flip( $this->attributes )
),
$this->data[ 'additional_attrs' ]
);
if ( skp_is_customizing() ) {
$attributes['value'] = array_key_exists('value', $attributes ) ? $attributes['value'] : '';
}
$attributes = array_map(
function ($k, $v) {
switch ( $k ) {
case 'value':
$v = $this->get_value();
break;
default:
$v = esc_attr( $v );
break;
}
// 'required' attribute doesn't need a value : <input name="nimble_email" id="nimble_email1163989492" type="text" required/>
return 'required' === $k ? 'required' : sanitize_key( $k ) .'="'. $v .'"';
},
array_keys($attributes), $attributes
);
return implode( ' ', $attributes );
}
public function __toString() {
return $this->html();
}
}//end abstract class
if ( !class_exists( '\Nimble\Sek_Input_Basic' ) ) :
class Sek_Input_Basic extends Sek_Input_Abstract {
public function __construct( $args ) {
$this->attributes = array_merge( $this->attributes, array( 'value', 'type' ) );
parent::__construct( $args );
}
public function html() {
return sprintf( '<input %s/>',
$this->get_attributes_html()
);
}
}
endif;
if ( !class_exists( '\Nimble\Sek_Input_Hidden' ) ) :
class Sek_Input_Hidden extends Sek_Input_Basic {
public function __construct( $args ) {
$args[ 'type' ] = 'hidden';
parent::__construct( $args );
}
}
endif;
if ( !class_exists( '\Nimble\Sek_Input_Checkbox' ) ) :
class Sek_Input_Checkbox extends Sek_Input_Basic {
public function __construct( $args ) {
$args[ 'type' ] = 'checkbox';
parent::__construct( $args );
}
}
endif;
if ( !class_exists( '\Nimble\Sek_Input_Text' ) ) :
class Sek_Input_Text extends Sek_Input_Basic {
public function __construct( $args ) {
$args = is_array( $args ) ? $args : array();
$args[ 'type' ] = 'text';
$args[ 'filter' ] = FILTER_SANITIZE_STRING;
parent::__construct( $args );
}
public function sanitize( $value ) {
return sanitize_text_field( $value );
}
public function escape( $value ) {
return esc_html( $value );
}
}
endif;
if ( !class_exists( '\Nimble\Sek_Input_Email' ) ) :
class Sek_Input_Email extends Sek_Input_Basic {
public function __construct($args) {
$args = is_array( $args ) ? $args : array();
$args[ 'type' ] = 'text';
$args[ 'filter' ] = FILTER_SANITIZE_EMAIL;
parent::__construct( $args );
}
public function sanitize( $value ) {
if ( !is_email( $value ) ) {
return '';
}
return sanitize_email($value);
}
public function escape( $value ) {
return esc_html( $value );
}
}
endif;
if ( !class_exists( '\Nimble\Sek_Input_URL' ) ) :
class Sek_Input_URL extends Sek_Input_Basic {
public function __construct($args) {
$args = is_array( $args ) ? $args : array();
$args[ 'type' ] = 'url';
$args[ 'filter' ] = FILTER_SANITIZE_URL;
parent::__construct( $args );
}
public function sanitize($value) {
return esc_url_raw( $value );
}
public function escape( $value ){
return esc_url( $value );
}
}
endif;
if ( !class_exists( '\Nimble\Sek_Input_Submit' ) ) :
class Sek_Input_Submit extends Sek_Input_Basic {
public function __construct($args) {
$args = is_array( $args ) ? $args : array();
$args[ 'type' ] = 'submit';
$args = wp_parse_args($args, [
'value' => esc_html__( 'Contact', 'text_doma' ),
]);
parent::__construct( $args );
}
public function escape( $value ) {
return esc_html( $value );
}
}
endif;
if ( !class_exists( '\Nimble\Sek_Input_Textarea' ) ) :
class Sek_Input_Textarea extends Sek_Input_Abstract {
public function __construct($args) {
$args = is_array( $args ) ? $args : array();
$args[ 'type' ] = 'textarea';
parent::__construct( $args );
}
public function sanitize( $value ) {
return wp_kses_post($value);
}
public function escape( $value ) {
return $this->sanitize( $value );
}
public function html() {
return sprintf( '<textarea %1$s>%2$s</textarea>',
$this->get_attributes_html(),
$this->get_value()
);
}
}
endif;
?>
@@ -0,0 +1,333 @@
<?php
/*
*
* Mailer class definition
*
*/
if ( !class_exists( '\Nimble\Sek_Mailer' ) ) :
class Sek_Mailer {
private $form;
private $status;
private $messages;
private $invalid_field = false;
public $recaptcha_errors = '_no_error_';//will store array( 'endpoint' => $endpoint, 'request' => $request, 'response' => '' );
public function __construct( Sek_Form $form ) {
$this->form = $form;
$this->messages = array(
//status => message
//'not_sent' => __( 'Message was not sent. Try Again.', 'text_doma'),
//'sent' => __( 'Thanks!Your message has been sent.', 'text_doma'),
'aborted' => __( 'Please supply correct information.', 'text_doma') //<-todo too much generic
);
$this->status = 'init';
// Validate reCAPTCHA if submitted
// When sek_is_recaptcha_globally_enabled(), the hidden input 'nimble_recaptcha_resp' is rendered with a value set to a token remotely fetched with a js script
// @see print_recaptcha_inline_js
// on submission, we get the posted token value, and validate it with a remote http request to the google api
if ( isset( $_POST['nimble_recaptcha_resp'] ) ) {
if ( !$this->validate_recaptcha( sanitize_text_field($_POST['nimble_recaptcha_resp']) ) ) {
$this->status = 'recaptcha_fail';
if ( sek_is_dev_mode() ) {
sek_error_log('reCAPTCHA failure', $this->recaptcha_errors );
}
}
}
}
//@return bool
private function validate_recaptcha( $recaptcha_token ) {
$is_valid = false;
$endpoint = 'https://www.google.com/recaptcha/api/siteverify';
$global_recaptcha_opts = sek_get_global_option_value('recaptcha');
$global_recaptcha_opts = is_array( $global_recaptcha_opts ) ? $global_recaptcha_opts : array();
// the user did not enter the key yet.
// let's validate
if ( empty($global_recaptcha_opts['private_key']) )
return true;
//$public = $global_recaptcha_opts['public_key'];
$request = array(
'body' => array(
'secret' => $global_recaptcha_opts['private_key'],
'response' => $recaptcha_token
),
);
// cache the recaptcha_errors
$response = wp_remote_post( esc_url_raw( $endpoint ), $request );
if ( is_array( $response ) ) {
$maybe_recaptcha_errors = wp_remote_retrieve_body( $response );
$maybe_recaptcha_errors = json_decode( $maybe_recaptcha_errors );
$maybe_recaptcha_errors = is_object($maybe_recaptcha_errors) ? (array)$maybe_recaptcha_errors : $maybe_recaptcha_errors;
if ( is_array( $maybe_recaptcha_errors ) && isset( $maybe_recaptcha_errors['error-codes'] ) && is_array( $maybe_recaptcha_errors['error-codes'] ) ) {
$this->recaptcha_errors = implode(', ', $maybe_recaptcha_errors['error-codes'] );
}
}
//sek_error_log('reCAPTCHA response ?', $response );
// There
if ( 200 != wp_remote_retrieve_response_code( $response ) ) {
$this->recaptcha_errors = sprintf( __('There was a problem when performing the reCAPTCHA http request.') );
return $is_valid;
}
// At this point, we can check the score if there not already an error messages, like a re-submission problem for example
if ( '_no_error_' === $this->recaptcha_errors ) {
$response_body = wp_remote_retrieve_body( $response );
$response_body = json_decode( $response_body, true );
// see https://developers.google.com/recaptcha/docs/v3#score
$score = isset( $response_body['score'] ) ? $response_body['score'] : 0;
// get the user defined threshold
// must be normalized to be 0 >= threshold >= 1
$user_score_threshold = array_key_exists('score', $global_recaptcha_opts) ? $global_recaptcha_opts['score'] : 0.5;
$user_score_threshold = !is_numeric( $user_score_threshold ) ? 0.5 : $user_score_threshold;
$user_score_threshold = $user_score_threshold > 1 ? 1 : $user_score_threshold;
$user_score_threshold = $user_score_threshold < 0 ? 0 : $user_score_threshold;
$user_score_threshold = apply_filters( 'nimble_recaptcha_score_treshold', $user_score_threshold );
$is_valid = $is_human = $user_score_threshold < $score;
if ( !$is_valid ) {
$this->recaptcha_errors = sprintf( __('Google reCAPTCHA returned a score of %s, which is lower than your threshold of %s.', 'text_dom' ), $score, $user_score_threshold );
}
}
return $is_valid;
}
// Depending on the user options, some fields might exists in the $form object
// We need to check their existence ( @see https://github.com/presscustomizr/nimble-builder/issues/399 )
public function maybe_send( $form_composition, $module_model ) {
// the captcha validation has been made on Sek_Mailer instantiation
if ( 'recaptcha_fail' === $this->status ) {
return;
}
//sek_error_log('$form_composition ??', $form_composition );
//sek_error_log('$module_model ??', $module_model );
//sek_error_log('$this->form ??', $form_composition , $this->form );
$invalid_field = $this->form->has_invalid_field();
if ( false !== $invalid_field ) {
$this->status = 'aborted';
$this->invalid_field = $invalid_field;
return;
}
$module_user_values = array_key_exists( 'value', $module_model ) ? $module_model['value'] : array();
//sek_error_log( '$module_model', $module_model );
$submission_options = empty( $module_user_values['form_submission'] ) ? array() : $module_user_values['form_submission'];
//<-allow html?->TODO: turn into option
$allow_html = true;
$sender_email = $this->form->get_field('nimble_email')->get_input()->get_value();
// Define a default sender name + make sure the field exists
// fixes https://github.com/presscustomizr/nimble-builder/issues/513
$sender_name = __('Someone', 'text_doma');
$sender_name_is_set = false;
if ( is_array( $form_composition ) && array_key_exists( 'nimble_name', $form_composition ) ) {
$sender_name_candidate = sprintf( '%1$s', $this->form->get_field('nimble_name')->get_input()->get_value() );
if ( !empty( $sender_name_candidate ) ) {
$sender_name = $sender_name_candidate;
$sender_name_is_set = true;
}
}
$sender_body_message = null === $this->form->get_field('nimble_message') ? '' : $this->form->get_field('nimble_message')->get_input()->get_value();
if ( array_key_exists( 'recipients', $submission_options ) ) {
$recipient = $submission_options['recipients'];
} else {
$recipient = get_option( 'admin_email' );
}
if ( array_key_exists( 'nimble_subject' , $form_composition ) ) {
$subject = $this->form->get_field('nimble_subject')->get_input()->get_value();
} else if ( $sender_name_is_set ) {
$subject = sprintf( __( '%1$s sent a message from %2$s', 'text_doma' ), $sender_name, get_bloginfo( 'name' ) );
} else {
$subject = sprintf( __( 'Someone sent a message from %1$s', 'text_doma' ), get_bloginfo( 'name' ) );
}
// $sender_website = sprintf( __( 'Website: %1$s %2$s', 'text_doma' ),
// $this->form->get_field('website')->get_input()->get_value(),
// $allow_html ? '<br><br><br>': "\r\n\r\n\r\n"
// );
// the sender's email is written in the email's header reply-to field.
// But it is also written inside the message body following this issue, https://github.com/presscustomizr/nimble-builder/issues/218
$before_message = sprintf( '%1$s: %2$s &lt;%3$s&gt;', __('From', 'text_doma'), $sender_name, $sender_email );//$sender_website;
$before_message .= sprintf( '<br>%1$s: %2$s', __('Subject', 'text_doma'), $subject );
$after_message = '';
if ( array_key_exists( 'email_footer', $submission_options ) ) {
// Feb 2021 : now saved as a json to fix emojis issues
// see fix for https://github.com/presscustomizr/nimble-builder/issues/544
// to ensure retrocompatibility with data previously not saved as json, we need to perform a json validity check
$email_footer = sek_maybe_decode_richtext( $submission_options['email_footer'] );
$email_footer = sek_strip_script_tags( $email_footer );
} else {
$email_footer = sprintf( __( 'This e-mail was sent from a contact form on %1$s (<a href="%2$s" target="_blank">%2$s</a>)', 'text_doma' ),
get_bloginfo( 'name' ),
get_site_url( 'url' )
);
}
if ( !empty( $sender_body_message ) ) {
$sender_body_message = sprintf( '<br><br>%1$s: <br>%2$s',
__('Message body', 'text_doma'),
//$allow_html ? '<br><br>': "\r\n\r\n",
$sender_body_message
);
}
$body = sprintf( '%1$s%2$s%3$s%4$s%5$s',
$before_message,
$sender_body_message,
$after_message,
$allow_html ? '<br><br>--<br>': "\r\n\r\n--\r\n",
$email_footer
);
$headers = array();
$headers[] = $allow_html ? 'Content-Type: text/html' : '';
$headers[] = 'charset=UTF-8'; //TODO: maybe turn into option
$headers[] = sprintf( 'From: %1$s <%2$s>', $sender_name, $this->get_from_email() );
$headers[] = sprintf( 'Reply-To: %1$s <%2$s>', $sender_name, $sender_email );
$this->status = wp_mail( $recipient, $subject, $body, $headers ) ? 'sent' : 'not_sent';
}
public function get_status() {
return $this->status;
}
public function get_message( $status, $module_model ) {
$module_user_values = array_key_exists( 'value', $module_model ) ? $module_model['value'] : array();
//sek_error_log( '$module_model', $module_model );
$submission_options = empty( $module_user_values['form_submission'] ) ? array() : $module_user_values['form_submission'];
$submission_message = isset( $this->messages[ $status ] ) ? $this->messages[ $status ] : '';
// the check with strlen( preg_replace('/\s+/' ... ) allow user to "hack" the custom submission message with a blank space
// because if the field is empty it will fallback on the default value
switch( $status ) {
case 'not_sent' :
if ( array_key_exists( 'failure_message', $submission_options ) && !empty( $submission_options['failure_message'] ) && 0 < strlen( preg_replace('/\s+/', '', $submission_options['failure_message'] ) ) ) {
$submission_message = $submission_options['failure_message'];
}
break;
case 'sent' :
if ( array_key_exists( 'success_message', $submission_options ) && !empty( $submission_options['success_message'] ) && 0 < strlen( preg_replace('/\s+/', '', $submission_options['success_message'] ) ) ) {
$submission_message = $submission_options['success_message'];
}
break;
case 'aborted' :
if ( array_key_exists( 'error_message', $submission_options ) && !empty( $submission_options['error_message'] ) && 0 < strlen( preg_replace('/\s+/', '', $submission_options['error_message'] ) ) ) {
$submission_message = $submission_options['error_message'];
}
if ( false !== $this->invalid_field ) {
$submission_message = sprintf( __( '%1$s : <strong>%2$s</strong>.', 'text-domain' ), $submission_message, $this->invalid_field );
}
break;
case 'recaptcha_fail' :
$global_recaptcha_opts = sek_get_global_option_value('recaptcha');
$global_recaptcha_opts = is_array( $global_recaptcha_opts ) ? $global_recaptcha_opts : array();
if ( true === sek_booleanize_checkbox_val($global_recaptcha_opts['show_failure_message']) ) {
$submission_message = !empty($global_recaptcha_opts['failure_message']) ? $global_recaptcha_opts['failure_message'] : '';
}
break;
}
if ( '_no_error_' !== $this->recaptcha_errors && current_user_can( 'customize' ) ) {
$submission_message .= sprintf( '<br/>%s : <i>%s</i>', __('reCAPTCHA problem (only visible by a logged in administrator )', 'text_doma'), $this->recaptcha_errors );
}
return $submission_message;
}
// inspired from wpcf7
private function get_from_email() {
$admin_email = get_option( 'admin_email' );
$sitename = strtolower( sanitize_text_field($_SERVER['SERVER_NAME']) );
if ( in_array( $sitename, array( 'localhost', '127.0.0.1' ) ) ) {
return $admin_email;
}
if ( substr( $sitename, 0, 4 ) == 'www.' ) {
$sitename = substr( $sitename, 4 );
}
if ( strpbrk( $admin_email, '@' ) == '@' . $sitename ) {
return $admin_email;
}
return 'wordpress@' . $sitename;
}
}//Sek_Mailer
endif;
// inspired by wpcf7
function sek_simple_form_mail_template() {
$template = array(
'subject' =>
sprintf( __( '%1$s: new contact request', 'text_doma' ),
get_bloginfo( 'name' )
),
'sender' => sprintf( '[your-name] <%s>', simple_form_from_email() ),
'body' =>
/* translators: %s: [your-name] <[your-email]> */
sprintf( __( 'From: %s', 'text_doma' ),
'[your-name] <[your-email]>' ) . "\n"
/* translators: %s: [your-subject] */
. sprintf( __( 'Subject: %s', 'text_doma' ),
'[your-subject]' ) . "\n\n"
. __( 'Message Body:', 'text_doma' )
. "\n" . '[your-message]' . "\n\n"
. '-- ' . "\n"
/* translators: 1: blog name, 2: blog URL */
. sprintf(
__( 'This e-mail was sent from a contact form on %1$s (%2$s)', 'text_doma' ),
get_bloginfo( 'name' ),
get_bloginfo( 'url' ) ),
'recipient' => get_option( 'admin_email' ),
'additional_headers' => 'Reply-To: [your-email]',
'attachments' => '',
'use_html' => 0,
'exclude_blank' => 0,
);
return $template;
}//simple_form_mail_template
?>
@@ -0,0 +1,751 @@
<?php
namespace Nimble;
if ( !defined( 'ABSPATH' ) ) {
exit;
}
/**
* Sek Dyn CSS Builder: class responsible for building Stylesheet from a sek model
*/
class Sek_Dyn_CSS_Builder {
/*min widths, considering CSS min widths BP:
$grid-breakpoints: (
xs: 0,
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px
)
we could have a constant array since php 5.6
*/
public static $breakpoints = [
'xs' => 0,
'sm' => 576,
'md' => 768,
'lg' => 992,
'xl' => 1200
];
const COLS_MOBILE_BREAKPOINT = 'md';
private $collection;//the collection of css rules
private $module_types = [];
private $sek_model;
// property "is_global_stylesheet" has been added when fixing https://github.com/presscustomizr/nimble-builder/issues/273
private $is_global_stylesheet;
private $parent_level_model = array();
public $customizer_active_locations = '_not_set_';//June 2020 => added to prevent printing css for not active locations
public $current_sniffed_location = '_not_set_';//June 2020 => added to prevent printing css for not active locations
public $sniffed_locations = [];//oct 2020 => will populate a collection of location sniffed while parsing the sek_model
public $sniffed_modules = [];//oct 2020 => will populate a collection of modules sniffed while parsing the sek_model
private $needs_wp_comments_stylesheet = false;//April 2021 => for site templates
public function __construct( $sek_model = array(), $is_global_stylesheet = false ) {
$this->sek_model = $sek_model;
// June 2020 : this property is set when saving the customizer
// and used to determine if we need to generate css for a given location
// typically useful when a local header is populated with sections but not used on the page. While still present in the collection of location, we don't want to generate css for it.
$raw_customizer_active_locations = ( isset($_POST['active_locations']) && is_array($_POST['active_locations']) ) ? $_POST['active_locations'] : '_not_set_';
// sanitize now and cache
if ( !is_array($raw_customizer_active_locations) ) {
$this->customizer_active_locations = sanitize_text_field($raw_customizer_active_locations);
} else {
$_active_locations = [];
foreach($raw_customizer_active_locations as $loc ) {
$_active_locations[] = sanitize_text_field($loc);
}
$this->customizer_active_locations = $_active_locations;
}
$this->is_global_stylesheet = $is_global_stylesheet;
// set the css rules for columns
/* ------------------------------------------------------------------------- *
* SCHEDULE CSS RULES FILTERING
/* ------------------------------------------------------------------------- */
// filter fired in sek_css_rules_sniffer_walker()
add_filter( 'sek_add_css_rules_for_level_options', array( $this, 'sek_add_rules_for_column_width' ), 10, 2 );
$this->sek_css_rules_sniffer_walker();
}
// Fired in the constructor
// Walk the level tree and build rules when needed
// The rules are filtered when some conditions are met.
// This allows us to schedule the css rules addition remotely :
// - from the module registration php file
// - from the generic input types ( @see sek_add_css_rules_for_generic_css_input_types() )
public function sek_css_rules_sniffer_walker( $level = null, $parent_level = array() ) {
$level = is_null( $level ) ? $this->sek_model : $level;
$level = is_array( $level ) ? $level : array();
// The parent level is set when the function is invoked recursively, from a level where we actually have a 'level' property
if ( !empty( $parent_level ) ) {
$this->parent_level_model = $parent_level;
}
foreach ( $level as $key => $entry ) {
// Let's cache the currently sniffed location
if ( is_array($entry) && isset($entry['level']) && 'location' === $entry['level'] ) {
$this->current_sniffed_location = $entry['id'];
$this->sniffed_locations[ $this->current_sniffed_location ] = [];
}
// When saving in the customizer, the active locations are passed in $_POST
// so we can determine if a location is currently active or not, and if not, we don't need to generate CSS for it.
// Oct 2020 : case of the global stylesheet :
// The global stylesheet may be inactive on a given customization, which means that the customizer_active_locations won't include any global locations.
// But this does not mean that the global stylesheet is inactive on other pages.
// That's why we only verify the active location condition when !$this->is_global_stylesheet
if ( !$this->is_global_stylesheet && '_not_set_' !== $this->customizer_active_locations && is_array($this->customizer_active_locations) && '_not_set_' !== $this->current_sniffed_location && !in_array($this->current_sniffed_location, $this->customizer_active_locations ) ) {
continue;
}
$rules = array();
// INPUT CSS RULES <= used in front modules only
// When we are inside the associative arrays of
// - the module 'value'
// - or the level 'options' entries <= NOT ANYMORE
// the keys are not integer.
// We want to filter each input
// which makes it possible to target for example the font-family. Either in module values or in level options
if ( is_string( $key ) && 1 < strlen( $key ) ) {
// we need to have a level model set
if ( !empty( $parent_level ) && is_array( $parent_level ) && !empty( $parent_level['module_type'] ) ) {
// Populates the sniffed module collection for later use
$current_location_modules = $this->sniffed_locations[ $this->current_sniffed_location ];
$current_location_modules = is_array($current_location_modules) ? $current_location_modules : [];
if ( !in_array( $parent_level['module_type'], $current_location_modules ) ) {
$this->sniffed_modules[] = $parent_level['module_type'];
$this->sniffed_locations[ $this->current_sniffed_location ][] = $parent_level['module_type'];
}
// If the current level is a module, check if the module has generic css ( *_css suffixed ) selectors specified on registration
// $module_level_css_selectors = null;
// $registered_input_list = null;
$module_level_css_selectors = sek_get_registered_module_type_property( $parent_level['module_type'], 'css_selectors' );
$registered_input_list = sek_get_registered_module_input_list( $parent_level['module_type'] );
if ( 'value' === $key && is_array( $entry ) ) {
$is_father = sek_get_registered_module_type_property( $parent_level['module_type'], 'is_father' );
$father_mod_type = $parent_level['module_type'];
// If the module has children ( the module is_father ), let's loop on each option group
if ( $is_father ) {
$children = sek_get_registered_module_type_property( $father_mod_type, 'children' );
// Loop on the children
foreach ( $entry as $opt_group_type => $input_candidates ) {
if ( !is_array( $children ) ) {
sek_error_log( 'Father module ' . $father_mod_type . ' has invalid children');
continue;
}
if ( empty( $children[$opt_group_type] ) ) {
sek_error_log( 'Father module ' . $father_mod_type . ' has a invalid child for option group : '. $opt_group_type);
continue;
}
// The module type of the currently looped child
$child_mod_type = $children[ $opt_group_type ];
// If the child module has no css_selectors set, we fallback on the father css_selector
$child_css_selector = sek_get_registered_module_type_property( $child_mod_type, 'css_selectors' );
$child_css_selector = empty( $child_css_selector ) ? $module_level_css_selectors : $child_css_selector;
// Is is a multi-item module ?
$is_multi_items_module = true === sek_get_registered_module_type_property( $child_mod_type, 'is_crud' );
if ( $is_multi_items_module ) {
foreach ( $input_candidates as $item_input_list ) {
$rules = $this->sek_loop_on_input_candidates_and_maybe_generate_css_rules( $rules, array(
'input_list' => $item_input_list,
'registered_input_list' => $registered_input_list[ $opt_group_type ],// <= the full list of input for the module
'parent_module_level' => $parent_level,// <= the parent module level. can be one of those array( 'location', 'section', 'column', 'module' )
'module_css_selector' => $child_css_selector, //a default set of css_se
'is_multi_items' => true
) );
$rules = apply_filters( "sek_add_css_rules_for_single_item_in_module_type___{$child_mod_type}", $rules, array(
'input_list' => wp_parse_args( $item_input_list, sek_get_default_module_model( $child_mod_type ) ),
'parent_module_type' => $child_mod_type,// 'registered_input_list' => $registered_input_list[ $opt_group_type ],// <= the full list of input for the module
'parent_module_id' => $parent_level['id'],// <= the parent module level id, used to increase the CSS specificity
'module_css_selector' => $child_css_selector //a default set of css_se
) );
}
} else {
$rules = $this->sek_loop_on_input_candidates_and_maybe_generate_css_rules( $rules, array(
'input_list' => $input_candidates,
'registered_input_list' => $registered_input_list[ $opt_group_type ],// <= the full list of input for the module
'parent_module_level' => $parent_level,// <= the parent module level. can be one of those array( 'location', 'section', 'column', 'module' )
'module_css_selector' => $child_css_selector //a default set of css_selectors might have been specified on module registration
));
}
}//foreach
} //if ( $is_father )
else {
// Is is a multi-item module ?
$is_multi_items_module = true === sek_get_registered_module_type_property( $father_mod_type, 'is_crud' );
if ( $is_multi_items_module ) {
foreach ( $entry as $item_input_list ) {
$rules = $this->sek_loop_on_input_candidates_and_maybe_generate_css_rules( $rules, array(
'input_list' => $item_input_list,
'registered_input_list' => $registered_input_list,// <= the full list of input for the module
'parent_module_level' => $parent_level,// <= the parent module level. can be one of those array( 'location', 'section', 'column', 'module' )
'module_css_selector' => $module_level_css_selectors, //a default set of css_se
'is_multi_items' => true
) );
$rules = apply_filters( "sek_add_css_rules_for_multi_item_module_type___{$father_mod_type}", $rules, array(
'input_list' => wp_parse_args( $item_input_list, sek_get_default_module_model( $father_mod_type ) ),
'parent_module_type' => $father_mod_type,// <= the full list of input for the module
'parent_module_id' => $parent_level['id'],// <= the parent module level id, used to increase the CSS specificity
'module_css_selector' => $module_level_css_selectors, //a default set of css_se
) );
}
} else {
$rules = $this->sek_loop_on_input_candidates_and_maybe_generate_css_rules( $rules, array(
'input_list' => $entry,
'registered_input_list' => $registered_input_list,// <= the full list of input for the module
'parent_module_level' => $parent_level,// <= the parent module level. can be one of those array( 'location', 'section', 'column', 'module' )
'module_css_selector' => $module_level_css_selectors //a default set of css_selectors might have been specified on module registration
));
}
}//if is_father
}//if
}//if
}//if
// INPUT TEXT LEVEL CSS RULES
// @added in sept 2019 for https://github.com/presscustomizr/nimble-builder/issues/499
// When we are inside the associative arrays of the level 'options'
// the keys are not integer.
// We want to filter each input
// which makes it possible to target for example the font-family. Either in module values or in level options
if ( is_string( $key ) && 1 < strlen( $key ) && 'options' === $key ) {
// we need to have a level model set
if ( !empty( $parent_level ) && is_array( $parent_level ) ) {
if ( is_array( $entry ) ) {
// Level options are structured as an associative array of option groups
// $entry = array(
// 'text' => array(
// font_size_css => ...
// color_css => ...
// ),
// 'bg' => array(),
// ...
// )
foreach ( $entry as $opt_group_type => $input_candidates ) {
if ( 'level_text' !== $opt_group_type )
continue;
$level_text_registered_input_list = sek_get_registered_module_input_list( 'sek_level_text_module' );
$level_text_css_selectors = sek_get_registered_module_type_property( 'sek_level_text_module', 'css_selectors' );
$rules = $this->sek_loop_on_input_candidates_and_maybe_generate_css_rules( $rules, array(
'input_list' => $input_candidates,
'registered_input_list' => $level_text_registered_input_list,// <= the full list of input for the module
'parent_module_level' => $parent_level,// <= the parent module level. can be one of those array( 'location', 'section', 'column', 'module' )
'module_css_selector' => $level_text_css_selectors //a default set of css_selectors might have been specified on module registration
));
}
}//if
}//if
}//if
// LEVEL CSS RULES
if ( is_array( $entry ) ) {
// Populate rules for sections / columns / modules
// Location level are excluded
if ( !empty( $entry[ 'level' ] ) && 'location' != $entry[ 'level' ] ) {
$level_type = $entry[ 'level' ];
$rules = apply_filters( "sek_add_css_rules_for__{$level_type}__options", $rules, $entry );
// build rules for level options => section / column / module
// param is_global_stylesheet says that we're building the global stylesheet
// introduced for the custom CSS, to know if we're building CSS for a local or a global section
// @see https://github.com/presscustomizr/nimble-builder-pro/issues/67
$rules = apply_filters( 'sek_add_css_rules_for_level_options', $rules, $entry, $this->is_global_stylesheet );
}
// populate rules for modules values
if ( !empty( $entry[ 'level' ] ) && 'module' === $entry['level'] ) {
if ( !empty( $entry['module_type'] ) ) {
$module_type = $entry['module_type'];
// populate module types list so we can add their stylesheet afterward
$this->module_types[] = $module_type;
// build rules for modules
// applying sek_normalize_module_value_with_defaults() allows us to access all the value properties of the module without needing to check their existence
$rules = apply_filters( "sek_add_css_rules_for_module_type___{$module_type}", $rules, sek_normalize_module_value_with_defaults( $entry ) );
}
}
} // if ( is_array( $entry ) ) {
// POPULATE THE CSS RULES COLLECTION
if ( !empty( $rules ) ) {
//@TODO: MAKE SURE RULE ARE NORMALIZED
foreach( $rules as $rule ) {
if ( !is_array( $rule ) ) {
sek_error_log( __CLASS__ . '::' . __FUNCTION__ . ' => a css rule should be represented by an array', $rule );
continue;
}
if ( empty( $rule['selector']) ) {
sek_error_log( __CLASS__ . '::' . __FUNCTION__ . '=> a css rule is missing the selector param', $rule );
continue;
}
$this->sek_populate(
$rule[ 'selector' ],
$rule[ 'css_rules' ],
$rule[ 'mq' ]
);
}//foreach
}
// keep walking if the current $entry is an array
// make sure that the parent_level_model is set right before jumping down to the next level
if ( is_array( $entry ) ) {
// Can we set a parent level ?
if ( !empty( $entry['level'] ) && in_array( $entry['level'], array( 'location', 'section', 'column', 'module' ) ) ) {
$parent_level = $entry;
}
// Let's go recursive
$this->sek_css_rules_sniffer_walker( $entry, $parent_level );
}
// Reset the parent level model because it might have been modified after walking the sublevels
if ( !empty( $parent_level ) ) {
$this->parent_level_model = $parent_level;
}
}//foreach
}//sek_css_rules_sniffer_walker()
// @param $rules // <= the in-progress global array of css rules to be populated
// @param $params= array()
// @return array of css rules*
// The input ids prefixed with '_css' are eligible for automaric CSS rules generation.
// @see add_filter( "sek_add_css_rules_for_input_id", '\Nimble\sek_add_css_rules_for_css_sniffed_input_id', 10, 1 );
function sek_loop_on_input_candidates_and_maybe_generate_css_rules( $rules, $params ) {
// normalize params
$default_params = array(
'input_list' => array(),
'registered_input_list' => array(),// <= the full list of input for the module
'parent_module_level' => array(),// <= the parent module level. can be one of those array( 'location', 'section', 'column', 'module' )
'module_css_selector' => '',//a default set of css_selectors might have been specified on module registration
'is_multi_items' => false
);
$params = wp_parse_args( $params, $default_params );
// FOR MULTI-ITEM MODULES=> add the item-id
// a multi-item module has a unique id for each item
// An item looks like :
// Array
// (
// [id] => 34913f6eef98
// [icon] => fab fa-accusoft
// [color_css] => #dd9933
// )
$item_id = null;
if ( $params['is_multi_items'] ) {
if ( !is_array( $params['input_list'] ) || !isset($params['input_list']['id']) ) {
sek_error_log( __FUNCTION__ . ' => Error => each item of a multi-item module must have an id', $params );
} else {
$item_id = $params['input_list']['id'];
}
}
foreach( $params['input_list'] as $input_id_candidate => $_input_val ) {
if ( false !== strpos( $input_id_candidate, '_css') ) {
$rules = apply_filters( 'sek_add_css_rules_for_input_id', $rules, array(
'css_val' => $_input_val,//string or array(), //<= the css property value
'input_id' => $input_id_candidate,//string// <= the unique input_id as it as been declared on module registration
'registered_input_list' => $params['registered_input_list'],// <= the full list of input for the module
'parent_module_level' => $params['parent_module_level'],// <= the parent module level. can be one of those array( 'location', 'section', 'column', 'module' )
'module_css_selector' => $params['module_css_selector'],// <= a default set of css_selectors might have been specified on module registration
'is_multi_items' => $params['is_multi_items'],// <= for multi-item modules, the input selectors will be made specific for each item-id. In module templates, we'll use data-sek-item-id="%5$s"
// implemented to allow CSS rules to be generated on a per-item basis
// for https://github.com/presscustomizr/nimble-builder/issues/78
'item_id' => $item_id // <= a multi-item module has a unique id for each item
));
} else {
// added April 2021 for site templates
// sniff if we need to add the comments css => looking for {{the_comments}}
if ( !$this->is_global_stylesheet && is_string($_input_val) ) {
preg_replace_callback( '!\{\{\s?(.*?)\s?\}\}!', array( $this, "sek_sniff_wp_comment_template_tag" ), $_input_val);
}
}
}
return $rules;
}
// added Arpil 2021 for site templates
public function sek_sniff_wp_comment_template_tag($matches) {
//sek_error_log('$matches ??', $matches );
if ( !is_array($matches) || empty($matches[1]) )
return;
if ( !$this->needs_wp_comments_stylesheet && is_string($matches[1] ) ) {
$this->needs_wp_comments_stylesheet = false !== strpos($matches[1], 'the_comments');
}
}
// @return void()
// populates the css rules ::collection property, organized by media queries
public function sek_populate( $selector, $css_rules, $mq = '' ) {
if ( !is_string( $selector ) )
return;
if ( !is_string( $css_rules ) )
return;
// Assign a default media device
//TODO: allowed media query?
$mq_device = 'all_devices';
// If a media query is requested, build it
if ( !empty( $mq ) ) {
if ( false === strpos($mq, 'max') && false === strpos($mq, 'min')) {
error_log( __FUNCTION__ . ' ' . __CLASS__ . ' => the media queries only accept max-width and min-width rules');
} else {
$mq_device = $mq;
}
}
// if the media query for this device is not yet added, add it
if ( !isset( $this->collection[ $mq_device ] ) ) {
$this->collection[ $mq_device ] = array();
}
if ( !isset( $this->collection[ $mq_device ][ $selector ] ) ) {
$this->collection[ $mq_device ][ $selector ] = array();
}
$this->collection[ $mq_device ][ $selector ][] = $css_rules;
}//sek_populate
// @return string
public static function sek_maybe_wrap_in_media_query( $css, $mq_device = 'all_devices' ) {
if ( 'all_devices' === $mq_device ) {
return $css;
}
if ( false === strpos($mq_device, '(') || false === strpos($mq_device, ')') ) {
sek_error_log( __CLASS__ . '::' . __FUNCTION__ . ' => missing parenthesis in the media queries', $mq_device );
return $css;
}
return sprintf( '@media%1$s{%2$s}', $mq_device, $css);
}
// sorts the media queries from all_devices to the smallest width
// This doesn't make the difference between max-width and min-width
// @return integer
public static function user_defined_array_key_sort_fn($a, $b) {
if ( 'all_devices' === $a ) {
return -1;
}
if ( 'all_devices' === $b ) {
return 1;
}
$a_int = (int)preg_replace('/[^0-9]/', '', $a) * 1;
$b_int = (int)preg_replace('/[^0-9]/', '', $b) * 1;
return $b_int - $a_int;
}
//@returns a stringified stylesheet, ready to be printed on the page or in a file
public function get_stylesheet() {
$css = '';
// CONCATENATE MODULE STYLESHEETS
// Oct 2020 => https://github.com/presscustomizr/nimble-builder/issues/749
$this->module_types = array_unique($this->module_types);
$modules_css = '';
$base_uri = NIMBLE_BASE_PATH . '/assets/front/css/modules/';
global $wp_filesystem;
$reading_issue = false;
$read_attempt = false;
$concatenated_module_stylesheets = Nimble_Manager()->concatenated_module_stylesheets;
foreach (Nimble_Manager()->big_module_stylesheet_map as $module_type => $stylesheet_name ) {
if ( $reading_issue )
continue;
if ( !in_array($module_type , $this->module_types ) )
continue;
// abort if the module stylesheet has already been concatenated in another stylesheet
if ( in_array( $module_type, $concatenated_module_stylesheets ) )
continue;
$uri = sprintf( '%1$s%2$s%3$s',
$base_uri ,
$stylesheet_name,
sek_is_dev_mode() ? '.css' : '.min.css'
);
$uri = wp_normalize_path($uri);
$read_attempt = true;
//sek_error_log('$uri ??' . $module_type . $stylesheet_name, $uri );
if ( $wp_filesystem->exists($uri) && $wp_filesystem->is_readable($uri) ) {
$modules_css .= $wp_filesystem->get_contents($uri);
// add this stylesheet to the already concatenated list
$concatenated_module_stylesheets[] = $module_type;
} else {
$reading_issue = true;
}
}//foreach
// COMMENTS CSS
$comments_css = '';
//if ( apply_filters('include_comments_css', true ) ) {
if ( !$this->is_global_stylesheet && $this->needs_wp_comments_stylesheet ) {
$read_attempt = true;
$uri = sprintf( '%1$s%2$s%3$s',
NIMBLE_BASE_PATH . '/assets/front/css/',
'sek-wp-comments',
sek_is_dev_mode() ? '.css' : '.min.css'
);
$uri = wp_normalize_path($uri);
//sek_error_log('$uri ??' . $module_type . $stylesheet_name, $uri );
if ( $wp_filesystem->exists($uri) && $wp_filesystem->is_readable($uri) ) {
$comments_css = $wp_filesystem->get_contents($uri);
} else {
$reading_issue = true;
}
}
// update the list of concatenated module stylesheets so that NB doesn't concatenate a module stylesheet twice for the local css and for the global css
Nimble_Manager()->concatenated_module_stylesheets = array_unique($concatenated_module_stylesheets);
if ( $read_attempt ) {
if ( $reading_issue ) {
update_option( NIMBLE_OPT_FOR_MODULE_CSS_READING_STATUS, 'failed');
sek_error_log( __CLASS__ . '::' . __FUNCTION__ . ' => reading issue => impossible to concatenate module stylesheets');
} else {
update_option( NIMBLE_OPT_FOR_MODULE_CSS_READING_STATUS, 'OK');
}
}
//sek_error_log('$modules_css ??', $modules_css );
// ORGANIZE CSS RULES BY MEDIA QUERIES
$collection = apply_filters( 'nimble_css_rules_collection_before_printing_stylesheet', $this->collection );
if ( is_array( $collection ) && !empty( $collection ) ) {
// Sort the collection by media queries
uksort( $collection, array( get_called_class(), 'user_defined_array_key_sort_fn' ) );
// process
foreach ( $collection as $mq_device => $selectors ) {
$_css = '';
foreach ( $selectors as $selector => $css_rules ) {
$css_rules = is_array( $css_rules ) ? implode( ';', $css_rules ) : $css_rules;
$_css .= $selector . '{' . $css_rules . '}';
$_css = str_replace(';;', ';', $_css);//@fixes https://github.com/presscustomizr/nimble-builder/issues/137
}
$_css = self::sek_maybe_wrap_in_media_query( $_css, $mq_device );
$css .= $_css;
}
}
// CONCATENATE MODULE CSS + GENERATED CSS
return apply_filters( 'nimble_get_dynamic_stylesheet',
$modules_css . $comments_css . $css,
$this->is_global_stylesheet,
$this->sniffed_locations,
$this->sniffed_modules
);
}
// Helper
// @return css string including media queries
// @used for example when generating the rules for used defined section widths locally and globally
public static function sek_generate_css_stylesheet_for_a_set_of_rules( $rules ) {
$rules_collection = array();
$css = '';
if ( empty( $rules ) || !is_array( $rules ) )
return $css;
// POPULATE THE CSS RULES COLLECTION
foreach( $rules as $rule ) {
if ( !is_array( $rule ) ) {
sek_error_log( __CLASS__ . '::' . __FUNCTION__ . ' => a css rule should be represented by an array', $rule );
continue;
}
if ( empty($rule['selector']) || !is_string( $rule['selector'] ) ) {
sek_error_log( __CLASS__ . '::' . __FUNCTION__ . '=> a css rule is missing the selector param', $rule );
continue;
}
$selector = $rule[ 'selector' ];
$css_rules = $rule[ 'css_rules' ];
$mq = $rule[ 'mq' ];
if ( !is_string( $css_rules ) )
continue;
// Assign a default media device
//TODO: allowed media query?
$mq_device = 'all_devices';
// If a media query is requested, build it
if ( !empty( $mq ) ) {
if ( false === strpos($mq, 'max') && false === strpos($mq, 'min')) {
error_log( __FUNCTION__ . ' ' . __CLASS__ . ' => the media queries only accept max-width and min-width rules');
} else {
$mq_device = $mq;
}
}
// if the media query for this device is not yet added, add it
if ( !isset( $rules_collection[ $mq_device ] ) ) {
$rules_collection[ $mq_device ] = array();
}
if ( !isset( $rules_collection[ $mq_device ][ $selector ] ) ) {
$rules_collection[ $mq_device ][ $selector ] = array();
}
$rules_collection[ $mq_device ][ $selector ][] = $css_rules;
}//foreach
// GENERATE CSS
if ( is_array( $rules_collection ) && !empty( $rules_collection ) ) {
// Sort the collection by media queries
// get_called_class() is supported by php >= 5.3.0. Nimble needs 5.4
// @see https://developer.wordpress.org/reference/functions/add_action/
uksort( $rules_collection, array( get_called_class(), 'user_defined_array_key_sort_fn' ) );
// process
foreach ( $rules_collection as $mq_device => $selectors ) {
$_css = '';
foreach ( $selectors as $selector => $css_rules ) {
$css_rules = is_array( $css_rules ) ? implode( ';', $css_rules ) : $css_rules;
$_css .= $selector . '{' . $css_rules . '}';
$_css = str_replace(';;', ';', $_css);//@fixes https://github.com/presscustomizr/nimble-builder/issues/137
}
$_css = self::sek_maybe_wrap_in_media_query( $_css, $mq_device );
$css .= $_css;
}
}
return $css;
}//sek_generate_css_stylesheet_for_a_set_of_rules()
// hook : sek_add_css_rules_for_level_options
// fired this class constructor
public function sek_add_rules_for_column_width( $rules, $column ) {
if ( !is_array( $column ) )
return $rules;
if ( empty( $column['level'] ) || 'column' !== $column['level'] || empty( $column['id'] ) )
return $rules;
$width = null;
// First try to find a width value in options, then look in the previous width property for backward compatibility
// After implementing https://github.com/presscustomizr/nimble-builder/issues/279
$column_options = isset( $column['options'] ) ? $column['options'] : array();
//sek_error_log( 'COLUMN MODEL WHEN ADDING RULES ?', $column_options );
if ( !empty( $column_options['width'] ) && !empty( $column_options['width']['custom-width'] ) ) {
$width_candidate = (float)$column_options['width']['custom-width'];
if ( $width_candidate < 0 || $width_candidate > 100 ) {
sek_error_log( __FUNCTION__ . ' => invalid width value for column id : ' . $column['id'] );
} else {
$width = $width_candidate;
}
} else {
// Backward compat since June 2019
// After implementing https://github.com/presscustomizr/nimble-builder/issues/279
$width = empty( $column[ 'width' ] ) || !is_numeric( $column[ 'width' ] ) ? '' : $column['width'];
}
// width
if ( empty( $width ) )
return $rules;
// define a default breakpoint : 768
$breakpoint = self::$breakpoints[ self::COLS_MOBILE_BREAKPOINT ];
// Does the parent section have a custom breakpoint set ?
$parent_section = sek_get_parent_level_model( $column['id'] );
if ( 'no_match' === $parent_section ) {
sek_error_log( __FUNCTION__ . ' => $parent_section not found for column id : ' . $column['id'] );
return $rules;
}
$section_custom_breakpoint = intval( sek_get_section_custom_breakpoint( array( 'section_model' => $parent_section, 'for_responsive_columns' => true ) ) );
if ( $section_custom_breakpoint >= 1 ) {
$breakpoint = $section_custom_breakpoint;
} else {
// Is there a global custom breakpoint set ?
$global_custom_breakpoint = intval( sek_get_global_custom_breakpoint() );
if ( $global_custom_breakpoint >= 1 ) {
$breakpoint = $global_custom_breakpoint;
}
}
// Note : the css selector must be specific enough to override the possible parent section ( or global ) custom breakpoint one.
// @see sek_add_css_rules_for_level_breakpoint()
$rules[] = array(
'selector' => sprintf( '[data-sek-id="%1$s"] .sek-sektion-inner > .sek-column[data-sek-id="%2$s"]', $parent_section['id'], $column['id'] ),
'css_rules' => sprintf( '-ms-flex: 0 0 %1$s%%;flex: 0 0 %1$s%%;max-width: %1$s%%', $width ),
'mq' => "(min-width:{$breakpoint}px)"
);
return $rules;
}
}//end class
?>
@@ -0,0 +1,778 @@
<?php
/**
* Sek Dyn CSS Handler: class responsible for enqueuing/writing CSS file or enqueuing/printing inline CSS
*/
class Sek_Dyn_CSS_Handler {
/**
* CSS files base dir constant
* Relative dir in the WordPress uploads dir
*
* @access public
*/
const CSS_BASE_DIR = NIMBLE_CSS_FOLDER_NAME;
/**
* Functioning mode constant
* @access public
*/
const MODE_INLINE = 'inline';
/**
* Functioning mode constant
* @access public
*/
const MODE_FILE = 'file';
/**
* CSS resource ID
*
* Holds the CSS resource ID
* Will be used to generate both the file name and the CSS handle when enqueued_or_printed
* Usually set to skope_id
*
* @access private
* @var string
*/
private $id;
/**
* Requested skope_id
*
* Will be used as id
* Must be provided
*
* @access private
* @var string
*/
private $skope_id;
// property "is_global_stylesheet" has been added when fixing https://github.com/presscustomizr/nimble-builder/issues/273
private $is_global_stylesheet;
/**
* the CSS
*
* Holds the CSS string: whether to inline print or to write in the proper file
*
* @access private
* @var string
*/
private $css_string_to_enqueue_or_print = '';
/**
* CSS enqueuing / inline printing status
*
* Hold the enqueuing status
*
* @access private
* @var bool
*/
private $enqueued_or_printed = false;
/**
* Enqueuing hook
*
* Holds the wp action hook name at whose occurrence the CSS will be enqueued_or_printed
*
* @access private
* @var string
*/
private $hook;
/**
* Enqueuing hook priority
*
* Holds the wp action hook priority at whose occurrence the CSS will be enqueued
* (see the $hook param)
*
* @var int
*/
private $priority = 10;
/**
* Enqueuing dependencies
*
* Holds the style dependencies for this CSS
*
* @access private
* @var array
*/
private $dep = array();
/**
* Functioning mode
*
* Holds the object functioning mode: MODE_FILE or MODE_INLINE
*
* @access private
* @var string
*/
private $mode;
/**
* File writing flag
*
* Indicates if we need to only write, not print or enqueuing
* This is used when saving the customizer options + writing the css file.
*
* @access private
* @var bool
*/
private $customizer_save = false;
/**
* File writing flag
*
* Holds whether or not the file writing should be forced before enqueuing if the file doesn't exist
* This is valid only when $mode == MODE_FILE
*
* @access private
* @var bool
*/
private $force_write = false;
/**
* File writing flag
*
* Holds whether or not the file writing should be forced before enqueuing even if the file exists
* This is valid only when $mode == MODE_FILE
*
* @access private
* @var bool
*/
private $force_rewrite = false;
/**
* File status
*
* Holds the file existence status (true|false)
*
* @access private
* @var bool
*/
private $file_exists = false;
/**
* CSS file base PATH
*
* Holds the CSS relative base path
* This is simply CSS_BASE_DIR in single sites, while its structure takes in account network and site id in multisites
*
* @access private
* @var string
*/
private $relative_base_path;
/**
* CSS file base URI
*
* Holds the CSS folder URI
*
* @access private
* @var string
*/
private $base_uri;
/**
* CSS file base URL
*
* Holds the CSS folder URL
*
* @access private
* @var string
*/
private $base_url;
/**
* CSS file URL
*
* Holds the CSS file URL
*
* @access private
* @var string
*/
private $url;
/**
* CSS file URI
*
* Holds the CSS file URI
*
* @access private
* @var string
*/
private $uri;
private $builder;//will hold the Sek_Dyn_CSS_Builder instance
public $sek_model = 'no_set';
/**
* Sek Dyn CSS Handler constructor.
*
* Initializing the object.
*
* @access public
* @param array $args Optional.
*
*/
public function __construct( $args = array() ) {
$defaults = array(
'id' => 'sek-'.rand(),
'skope_id' => '',
// property "is_global_stylesheet" has been added when fixing https://github.com/presscustomizr/nimble-builder/issues/273
'is_global_stylesheet' => false,
'mode' => self::MODE_FILE,
'css_string_to_enqueue_or_print' => $this->css_string_to_enqueue_or_print,
'dep' => $this->dep,
'hook' => '',
'priority' => $this->priority,
'customizer_save' => false,//<= used when saving the customizer settins => we want to write the css file on Nimble_Collection_Setting::update()
'force_write' => $this->force_write,
'force_rewrite' => $this->force_rewrite
);
$args = wp_parse_args( $args, $defaults );
//normalize some parameters
$args[ 'dep' ] = is_array( $args[ 'dep' ] ) ? $args[ 'dep' ] : array();
$args[ 'priority'] = is_numeric( $args[ 'priority' ] ) ? $args[ 'priority' ] : $this->priority;
//turn $args into object properties
foreach ( $args as $key => $value ) {
if ( property_exists( $this, $key ) && array_key_exists( $key, $defaults) ) {
$this->$key = $value;
}
}
if ( empty( $this->skope_id ) ) {
sek_error_log( __CLASS__ . '::' . __FUNCTION__ .' => __construct => skope_id not provided' );
return;
}
//build no parameterized properties
$this->_sek_dyn_css_set_properties();
// Introduced March 2021 for #478
if ( 'delete' !== $this->mode ) {
// Possible scenarii :
// 1) customizing :
// the css is always printed inline. If there's already an existing css file for this skope_id, it's not enqueued.
// 2) saving in the customizer :
// the css file is written in a "force_rewrite" mode, meaning that any existing css file gets re-written.
// There's no enqueing scheduled, 'customizer_save' mode.
// 3) front, user logged in + 'customize' capabilities :
// the css file is re-written on each page load + enqueued. If writing a css file is not possible, we fallback on inline printing.
// 4) front, user not logged in :
// the default behavior is that the css file is enqueued.
// It should have been written when saving in the customizer. If no file available, we try to write it. If writing a css file is not possible, we fallback on inline printing.
if ( is_customize_preview() || !$this->_sek_dyn_css_file_exists_is_readable_and_has_content() || $this->force_rewrite || $this->customizer_save ) {
$this->sek_model = sek_get_skoped_seks( $this->skope_id );
// on front, when no stylesheet is available, the fallback hook must be set to wp_head, because the hook property might be empty
// fixes https://github.com/presscustomizr/nimble-builder/issues/328
if ( !is_customize_preview() && !$this->_sek_dyn_css_file_exists_is_readable_and_has_content() ) {
$this->hook = 'wp_head';
}
//build stylesheet
$this->builder = new Sek_Dyn_CSS_Builder( $this->sek_model, $this->is_global_stylesheet );
// now that the stylesheet is ready let's cache it
// Note that esc_html() cannot be used because `div &gt; span` is not interpreted properly.
$this->css_string_to_enqueue_or_print = (string)strip_tags($this->builder->get_stylesheet());
}
// Do we have any rules to print / enqueue ?
// If yes, print in the dom or enqueue depending on the current context ( customization or front )
// If not, delete any previouly created stylesheet
//hook setup for printing or enqueuing
//bail if "customizer_save" == true, typically when saving the customizer settings @see Nimble_Collection_Setting::update()
if ( !$this->customizer_save ) {
// when not customizing, we write and enqueue :
// - if the file already exists,
// - or if we just have generated the CSS because the file had been deleted
if ( !empty($this->css_string_to_enqueue_or_print) || $this->_sek_dyn_css_file_exists_is_readable_and_has_content() ) {
$this->_schedule_css_and_fonts_enqueuing_or_printing_maybe_on_custom_hook();
} else {
$this->sek_dyn_css_delete_file_if_empty();
}
} else {
//sek_error_log( __CLASS__ . '::' . __FUNCTION__ .' ?? => $this->css_string_to_enqueue_or_print => ', $this->css_string_to_enqueue_or_print );
if ( !empty($this->css_string_to_enqueue_or_print) ) {
$this->sek_dyn_css_maybe_write_css_file();
} else {
// When customizing, the stylesheet is always generated.
// So if it is empty, it means we have to delete it
$this->sek_dyn_css_delete_file();
}
}
// Maybe update global inline style now with a filter
// This CSS is the one generated by global options like global text, global width, global breakpoint
// It is printed @wp_head inline
// for better performances on front, NB only wants to re-generate this style when customizing, and we user is logged in ( force_rewrite )
// see https://github.com/presscustomizr/nimble-builder/issues/750
if ( $this->is_global_stylesheet ) {
if ( is_customize_preview() || $this->force_rewrite || $this->customizer_save ) {
$global_style = Nimble_Manager()->sek_build_global_options_inline_css();
//sek_error_log('SOO GLOBAL INLINE CSS?', $global_style );
update_option( NIMBLE_OPT_FOR_GLOBAL_CSS, $global_style, 'no' );
}
}
}//if 'delete' !== $this->mode
}//__construct
/*
* Private methods
*/
/**
*
* Build these instance properties based on the params passed on instantiation
* called in the constructor
*
* @access private
*
*/
private function _sek_dyn_css_set_properties() {
$this->_sek_dyn_css_require_wp_filesystem();
$this->relative_base_path = $this->_sek_dyn_css_build_relative_base_path();
$this->base_uri = $this->_sek_dyn_css_build_base_uri();
$this->base_url = $this->_sek_dyn_css_build_base_url();
$this->uri = $this->_sek_dyn_css_build_uri();
$this->url = $this->_ssl_maybe_fix_url( $this->_sek_dyn_css_build_url() );
$this->file_exists = $this->_sek_dyn_css_file_exists_is_readable_and_has_content();
if ( self::MODE_FILE == $this->mode ) {
if ( !$this->_sek_dyn_css_write_file_is_possible() ) {
$this->mode = self::MODE_INLINE;
}
}
// July 2020 remove previous folder
// see https://github.com/presscustomizr/nimble-builder/issues/727
// if ( 'done' != get_transient( 'nimble_update_css_folder_name_0720' ) ) {
// set_transient( 'nimble_update_css_folder_name_0720', 'done', 30 * YEAR_IN_SECONDS );
// }
$upload_dir = wp_get_upload_dir();
$prev_folder_path = $this->_sek_dyn_css_build_relative_base_path( NIMBLE_DEPREC_ONE_CSS_FOLDER_NAME );
$previous_folder_one = wp_normalize_path( trailingslashit( $upload_dir['basedir'] ) . $prev_folder_path );
global $wp_filesystem;
if ( $wp_filesystem->exists( $previous_folder_one ) ) {
$wp_filesystem->rmdir( $previous_folder_one, true );
}
// October 2020 remove previous folder when implementing dynamic module stylesheet concatenation
$prev_folder_path = $this->_sek_dyn_css_build_relative_base_path( NIMBLE_DEPREC_TWO_CSS_FOLDER_NAME );
$previous_folder_two = wp_normalize_path( trailingslashit( $upload_dir['basedir'] ) . $prev_folder_path );
global $wp_filesystem;
if ( $wp_filesystem->exists( $previous_folder_two ) ) {
$wp_filesystem->rmdir( $previous_folder_two, true );
}
}
/**
* replace http: URL with https: URL
* @fix https://github.com/presscustomizr/nimble-builder/issues/188
* @param string $url
* @return string
*/
private function _ssl_maybe_fix_url($url) {
// only fix if source URL starts with http://
if ( is_ssl() && is_string($url) && stripos($url, 'http://') === 0 ) {
$url = 'https' . substr($url, 4);
}
return $url;
}
/**
*
* Maybe setup hooks
* called in the constructor
*
* @access private
*
*/
private function _schedule_css_and_fonts_enqueuing_or_printing_maybe_on_custom_hook() {
if ( $this->hook ) {
add_action( $this->hook, array( $this, 'sek_dyn_css_enqueue_or_print_and_google_fonts_print' ), $this->priority );
} else {
//enqueue or print
$this->sek_dyn_css_enqueue_or_print_and_google_fonts_print();
}
}
/**
* Enqueue CSS.
*
* Either enqueue the CSS file or add inline style, depending on the object mode property.
* The inline enqueuing is also the fall-back if anything goes wrong while trying to enqueuing the file.
*
* This method can also write the file under some circumstances (see when the object force_write || force_rewrite are enabled)
*
* @access public
* @return void()
*/
public function sek_dyn_css_enqueue_or_print_and_google_fonts_print() {
// CSS FILE
//case enqueue file : front end + user with customize caps not logged in
if ( self::MODE_FILE == $this->mode ) {
//in case we need to write the file before enqueuing
//1) $this->css_string_to_enqueue_or_print must exists
//2) we might need to force the rewrite even if the file exists or to write it if the file doesn't exist
if ( $this->css_string_to_enqueue_or_print ) {
if ( $this->force_rewrite || ( !$this->file_exists && $this->force_write ) ) {
$this->file_exists = $this->sek_dyn_css_maybe_write_css_file();
}
}
//if the file exists
if ( $this->file_exists ) {
//this resource version is built upon the file last modification time
wp_enqueue_style( "sek-dyn-{$this->id}", $this->url, $this->dep, filemtime($this->uri) );
$this->enqueued_or_printed = true;
}
}// if ( self::MODE_FILE )
//if $this->mode != 'file' or the file enqueuing didn't go through (fall back)
//print inline style
if ( $this->css_string_to_enqueue_or_print && !$this->enqueued_or_printed ) {
$dep = array_pop( $this->dep );
if ( !$dep || wp_style_is( $dep, 'done' ) || !wp_style_is( $dep, 'done' ) && ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
// only fired when doing ajax during customization in order to return a refreshed partial stylesheet
printf( '<style id="sek-%1$s" media="all">%2$s</style>',
esc_attr($this->id),
// Note that esc_html() cannot be used because `div &gt; span` is not interpreted properly.
strip_tags($this->css_string_to_enqueue_or_print)
);
} else {
wp_add_inline_style( $dep , $this->css_string_to_enqueue_or_print );
}
$this->mode = self::MODE_INLINE;
$this->enqueued_or_printed = true;
}
}
/*
* Public 'actions'
*/
/**
*
* Write the CSS to the disk, if we can
*
* @access public
*
* @return bool TRUE if the CSS file has been written, FALSE otherwise
*/
public function sek_dyn_css_maybe_write_css_file() {
global $wp_filesystem;
$error = false;
$base_uri = $this->base_uri;
// Can we create the folder?
if ( !$wp_filesystem->is_dir( $base_uri ) ) {
$error = !wp_mkdir_p( $base_uri );
}
if ( $error ) {
return false;
}
if ( !file_exists( $index_path = wp_normalize_path( trailingslashit( $base_uri ) . 'index.php' ) ) ) {
// predefined mode settings for WP files
// Make sure NB uses the proper FS_CHMOD_DIR value
// fixes https://github.com/presscustomizr/nimble-builder/issues/862
// doc : https://wordpress.org/support/article/editing-wp-config-php/#override-of-default-file-permissions
// doc : https://wordpress.stackexchange.com/questions/253274/use-of-undefined-constant-fs-chmod-dir-assumed-fs-chmod-dir
$chmod_dir = ( 0755 & ~ umask() );
if ( defined( 'FS_CHMOD_DIR' ) ) {
$chmod_dir = FS_CHMOD_DIR;
}
$wp_filesystem->put_contents( $index_path, "<?php\n// Silence is golden.\n", $chmod_dir );
}
if ( !wp_is_writable( $base_uri ) ) {
return false;
}
//actual write try and update the file_exists status
$this->file_exists = $wp_filesystem->put_contents(
$this->uri,
$this->css_string_to_enqueue_or_print,//secured earlier with strip_tags()
// predefined mode settings for WP files
FS_CHMOD_FILE
);
//return whether or not the writing succeeded
return $this->file_exists;
}
/**
*
* Maybe remove the CSS file from the disk, if it exists and if empty
* Note : July 2020 => function updated for https://github.com/presscustomizr/nimble-builder/issues/727
*
* @return bool TRUE if the CSS file has been deleted (or didn't exist already), FALSE otherwise
*/
public function sek_dyn_css_delete_file_if_empty() {
global $wp_filesystem;
if ( $this->_sek_dyn_css_file_exists_and_is_empty() ) {
$this->file_exists != $wp_filesystem->delete( $this->uri );
return !$this->file_exists;
}
return !$this->file_exists;
}
/**
*
* Remove the CSS file from the disk, if it exists, and even if not empty
* Note : July 2020 => function updated for https://github.com/presscustomizr/nimble-builder/issues/727
*
* @return bool TRUE if the CSS file has been deleted (or didn't exist already), FALSE otherwise
*/
public function sek_dyn_css_delete_file() {
global $wp_filesystem;
if ( $this->file_exists ) {
$this->file_exists != $wp_filesystem->delete( $this->uri );
//sek_error_log('CSS HANDLER => REMOVE FILE => ' . $this->uri);
return !$this->file_exists;
}
return !$this->file_exists;
}
/*
* Private helpers
*/
/**
*
* Retrieve the actual CSS file existence on the file system
*
* @access private
*
* @return bool TRUE if the CSS file exists, FALSE otherwise
*/
private function _sek_dyn_css_file_exists_is_readable_and_has_content() {
global $wp_filesystem;
if ( $wp_filesystem->exists( $this->uri ) ) {
$file_content = $wp_filesystem->get_contents( $this->uri );
return $wp_filesystem->is_readable( $this->uri ) && !empty( $file_content );
} else {
return false;
}
}
// Note : July 2020 => function introduced for https://github.com/presscustomizr/nimble-builder/issues/727
private function _sek_dyn_css_file_exists_and_is_empty() {
global $wp_filesystem;
if ( $wp_filesystem->exists( $this->uri ) ) {
$file_content = $wp_filesystem->get_contents( $this->uri );
return empty( $file_content );
} else {
return false;
}
}
/**
*
* Build normalized URI of the CSS file
*
* @access private
*
* @return string The absolute CSS file URI
*/
private function _sek_dyn_css_build_uri() {
if ( !isset( $this->base_uri ) ) {
$this->_sek_dyn_css_build_base_uri();
}
//sek_error_log('///////////////////ALORS CSS FILE NAME ??', $this->id );
return wp_normalize_path( trailingslashit( $this->base_uri ) . "{$this->id}.css" );
}
/**
*
* Build the URL of the CSS file
*
* @access private
*
* @return string The absolute CSS file URL
*/
private function _sek_dyn_css_build_url() {
if ( !isset( $this->base_url ) ) {
$this->_sek_dyn_css_build_base_url();
}
return trailingslashit( $this->base_url ) . "{$this->id}.css";
}
/**
*
* Build the URI of the CSS base directory
*
* @access private
*
* @return string The absolute CSS base directory URI
*/
private function _sek_dyn_css_build_base_uri() {
//since 4.5.0
$upload_dir = wp_get_upload_dir();
$relative_base_path = isset( $this->relative_base_path ) ? $this->relative_base_path : $this->_sek_dyn_css_build_relative_base_path();
return wp_normalize_path( trailingslashit( $upload_dir['basedir'] ) . $relative_base_path );
}
/**
*
* Build the URL of the CSS base directory
*
* @access private
*
* @return string The absolute CSS base directory URL
*/
private function _sek_dyn_css_build_base_url() {
//since 4.5.0
$upload_dir = wp_get_upload_dir();
$relative_base_path = isset( $this->relative_base_path ) ? $this->relative_base_path : $this->_sek_dyn_css_build_relative_base_path();
return set_url_scheme( trailingslashit( $upload_dir['baseurl'] ) . $relative_base_path );
}
/**
*
* Retrieve the relative path (to the 'uploads' dir ) of the CSS base directory
* July 2020 => added a $base_dir param for #727
*
* @access private
*
* @return string The relative path (to the 'uploads' dir) of the CSS base directory
*/
private function _sek_dyn_css_build_relative_base_path( $base_dir = null ) {
$css_base_dir = is_null($base_dir) ? self::CSS_BASE_DIR : $base_dir;
if ( is_multisite() ) {
$site = get_site();
$network_id = $site->site_id;
$site_id = $site->blog_id;
$css_dir = trailingslashit( $css_base_dir ) . trailingslashit( $network_id ) . $site_id;
}
return $css_base_dir;
}
/**
*
* Checks whether or not we can write to the disk
*
* @access private
*
* @return bool Whether or not we have filesystem credentials
*/
//TODO: try to extend this to other methods e.g. FTP when FTP credentials are already defined
private function _sek_dyn_css_write_file_is_possible() {
$upload_dir = wp_get_upload_dir();
//Note: if the 'uploads' dir has not been created, this check will not pass, hence no file will never be created
//unless something else creates the 'uploads' dir
if ( 'direct' === get_filesystem_method( array(), $upload_dir['basedir'] ) ) {
$creds = request_filesystem_credentials( '', '', false, false, array() );
/* initialize the API */
if ( !WP_Filesystem($creds) ) {
/* any problems and we exit */
return false;
}
return true;
}
return false;
}
/**
*
* Simple helper to require the WordPress filesystem relevant file
*
* @access private
*/
private function _sek_dyn_css_require_wp_filesystem() {
global $wp_filesystem;
// Initialize the WordPress filesystem.
if ( empty( $wp_filesystem ) ) {
require_once( ABSPATH . '/wp-admin/includes/file.php' );
WP_Filesystem();
}
}
}
?>
@@ -0,0 +1,595 @@
<?php
// filter declared in Sek_Dyn_CSS_Builder::sek_css_rules_sniffer_walker()
// $rules = apply_filters( "sek_add_css_rules_for_input_id", $rules, $key, $entry, $this->parent_level );
// the rules are filtered if ( false !== strpos( $input_id_candidate, '_css') )
// Example of input id candidate filtered : 'h_alignment_css'
// @see function sek_loop_on_input_candidates_and_maybe_generate_css_rules( $params ) {}
add_filter( "sek_add_css_rules_for_input_id", '\Nimble\sek_add_css_rules_for_css_sniffed_input_id', 10, 2 );
//@param $params = array()
//@param $rules <= the in-progress global array of css rules to be populated
function sek_add_css_rules_for_css_sniffed_input_id( $rules, $params ) {
// normalize params
$default_params = array(
'css_val' => '',//string or array(), //<= the css property value
'input_id' => '',//string// <= the unique input_id as it as been declared on module registration
'registered_input_list' => array(),// <= the full list of input for the module
'parent_module_level' => array(),// <= the parent level. name is misleading because can be module but also other levels array( 'location', 'section', 'column', 'module' )
'module_css_selector' => '',//<= a default set of css_selectors might have been specified on module registration
'is_multi_items' => false,// <= for multi-item modules, the input selectors will be made specific for each item-id. In module templates, we'll use data-sek-item-id="%5$s"
'item_id' => '' // <= a multi-item module has a unique id for each item
);
$params = wp_parse_args( $params, $default_params );
// map variables
$value = $params['css_val'];
$input_id = $params['input_id'];
$registered_input_list = $params['registered_input_list'];
$parent_level = $params['parent_module_level'];
$module_level_css_selectors = $params['module_css_selector'];
$is_multi_items = $params['is_multi_items'];
$item_id = $params['item_id'];
if ( !is_string( $input_id ) || empty( $input_id ) ) {
sek_error_log( __FUNCTION__ . ' => missing input_id', $parent_level);
return $rules;
}
if ( !is_array( $registered_input_list ) || empty( $registered_input_list ) ) {
sek_error_log( __FUNCTION__ . ' => missing input_list', $parent_level);
return $rules;
}
if ( empty( $registered_input_list[ $input_id ] ) ) {
sek_error_log( __FUNCTION__ . ' => missing input_id : ' . $input_id, $parent_level);
return $rules;
}
$input_registration_params = $registered_input_list[ $input_id ];
if ( !is_string( $input_registration_params['css_identifier'] ) || empty( $input_registration_params['css_identifier'] ) ) {
sek_error_log( __FUNCTION__ . ' => missing css_identifier for parent level', $parent_level );
sek_error_log('$registered_input_list', $registered_input_list );
return $rules;
}
// Make sure we have the right specificity depending on the level type
// When styling a section, the specificity has to be > to the global option one
// This is important in particular to make sure that text CSS rules follow the rule : Global < Section < Module
if ( 'section' === $parent_level['level'] ) {
$selector = sprintf( '.nb-loc [data-sek-id="%1$s"] [data-sek-level]', $parent_level['id'] );
} else {
// for modules and columns
$selector = sprintf( '.nb-loc .sek-row [data-sek-id="%1$s"]', $parent_level['id'] );
}
// for multi-items module, each item has a unique id allowing us to identify it
// implemented to allow CSS rules to be generated on a per-item basis
// for https://github.com/presscustomizr/nimble-builder/issues/78
if ( $is_multi_items ) {
$selector = sprintf( '.nb-loc [data-sek-id="%1$s"] [data-sek-item-id="%2$s"]', $parent_level['id'], $item_id );
}
$css_identifier = $input_registration_params['css_identifier'];
// SPECIFIC CSS SELECTOR AT MODULE LEVEL
// are there more specific css selectors specified on module registration ?
if ( !is_null( $module_level_css_selectors ) && !empty( $module_level_css_selectors ) ) {
if ( is_array( $module_level_css_selectors ) ) {
$new_selectors = array();
foreach ( $module_level_css_selectors as $spec_selector ) {
$new_selectors[] = $selector . ' ' . $spec_selector;
}
$new_selectors = implode(',', $new_selectors );
$selector = $new_selectors;
} else if ( is_string( $module_level_css_selectors ) ) {
$selector .= ' ' . $module_level_css_selectors;
}
}
// for a module level, increase the default specifity to the .sek-module-inner container by default
// @fixes https://github.com/presscustomizr/nimble-builder/issues/85
else if ( 'module' === $parent_level['level'] ) {
$selector .= ' .sek-module-inner';
}
// SPECIFIC CSS SELECTOR AT INPUT LEVEL
// => Overrides the module level specific selector, if it was set.
if ( 'module' === $parent_level['level'] ) {
//$start = microtime(true) * 1000;
if ( !is_array( $registered_input_list ) || empty( $registered_input_list ) ) {
sek_error_log( __FUNCTION__ . ' => missing input list' );
} else if ( is_array( $registered_input_list ) && empty( $registered_input_list[ $input_id ] ) ) {
sek_error_log( __FUNCTION__ . ' => missing input id ' . $input_id . ' in input list for module type ' . $parent_level['module_type'] );
}
if ( is_array( $registered_input_list ) && !empty( $registered_input_list[ $input_id ] ) && !empty( $registered_input_list[ $input_id ]['css_selectors'] ) ) {
// reset the selector to the level id selector, in case it was previously set specifically at the module level
$selector = '.nb-loc .sek-row [data-sek-id="'.$parent_level['id'].'"]';
if ( $is_multi_items ) {
$selector = sprintf( '[data-sek-id="%1$s"] [data-sek-item-id="%2$s"]', $parent_level['id'], $item_id );
}
$input_level_css_selectors = $registered_input_list[ $input_id ]['css_selectors'];
$new_selectors = array();
if ( is_array( $input_level_css_selectors ) ) {
foreach ( $input_level_css_selectors as $spec_selector ) {
$new_selectors[] = $selector . ' ' . $spec_selector;
}
} else if ( is_string( $input_level_css_selectors ) ) {
$new_selectors[] = $selector . ' ' . $input_level_css_selectors;
}
$new_selectors = implode(',', $new_selectors );
$selector = $new_selectors;
//sek_error_log( '$input_level_css_selectors', $selector );
}
// sek_error_log( 'input_id', $input_id );
// sek_error_log( '$registered_input_list', $registered_input_list );
// $end = microtime(true) * 1000;
// $time_elapsed_secs = $end - $start;
// sek_error_log('$time_elapsed_secs to get module params', $time_elapsed_secs );
}
$mq = null;
$properties_to_render = array();
switch ( $css_identifier ) {
// 2 cases for the font_size
// 1) we save it as a string : '16px'
// 2) we save it as an array of strings by devices : [ 'desktop' => '55px', 'tablet' => '40px', 'mobile' => '36px']
case 'font_size' :
if ( is_string( $value ) ) { // <= simple
$numeric = sek_extract_numeric_value($value);
if ( !empty( $numeric ) ) {
$properties_to_render['font-size'] = $value;
}
} else if ( is_array( $value ) ) { // <= by device
$important = false;
if ( 'module' === $parent_level['level'] && !empty( $parent_level['value'] ) ) {
$important = sek_is_flagged_important( $input_id, $parent_level['value'], $registered_input_list );
}
$value = wp_parse_args( $value, array(
'desktop' => '16px',
'tablet' => '',
'mobile' => ''
));
$rules = sek_set_mq_css_rules( array(
'value' => $value,
'css_property' => 'font-size',
'selector' => $selector,
'is_important' => $important,
'level_id' => $parent_level['id']
), $rules );
}
break;
case 'line_height' :
$properties_to_render['line-height'] = $value;
break;
case 'font_weight' :
$properties_to_render['font-weight'] = $value;
break;
case 'font_style' :
$properties_to_render['font-style'] = $value;
break;
case 'text_decoration' :
$properties_to_render['text-decoration'] = $value;
break;
case 'text_transform' :
$properties_to_render['text-transform'] = $value;
break;
case 'letter_spacing' :
$properties_to_render['letter-spacing'] = $value . 'px';
break;
case 'color' :
$properties_to_render['color'] = $value;
break;
case 'color_hover' :
//$selector = '[data-sek-id="'.$parent_level['id'].'"]:hover';
// Add ':hover to each selectors'
$new_selectors = array();
$exploded = explode(',', $selector);
foreach ( $exploded as $sel ) {
$new_selectors[] = $sel.':hover';
$new_selectors[] = $sel.':focus';
}
$selector = implode(',', $new_selectors);
$properties_to_render['color'] = $value;
break;
case 'background_color' :
$properties_to_render['background-color'] = $value;
break;
case 'h_flex_alignment' :
$important = false;
if ( 'module' === $parent_level['level'] && !empty( $parent_level['value'] ) ) {
$important = sek_is_flagged_important( $input_id, $parent_level['value'], $registered_input_list );
}
// convert to flex
$flex_ready_value = array();
foreach( $value as $device => $val ) {
switch ( $val ) {
case 'left' :
$h_align_value = sprintf('justify-content:flex-start%1$s;-webkit-box-pack:start%1$s;-ms-flex-pack:start%1$s;', $important ? '!important' : '' );
break;
case 'center' :
$h_align_value = sprintf('justify-content:center%1$s;-webkit-box-pack:center%1$s;-ms-flex-pack:center%1$s;', $important ? '!important' : '' );
break;
case 'right' :
$h_align_value = sprintf('justify-content:flex-end%1$s;-webkit-box-pack:end%1$s;-ms-flex-pack:end%1$s;', $important ? '!important' : '' );
break;
default :
$h_align_value = sprintf('justify-content:center%1$s;-webkit-box-pack:center%1$s;-ms-flex-pack:center%1$s;', $important ? '!important' : '' );
break;
}
$flex_ready_value[$device] = $h_align_value;
}
$flex_ready_value = wp_parse_args( $flex_ready_value, array(
'desktop' => '',
'tablet' => '',
'mobile' => ''
));
$rules = sek_set_mq_css_rules_supporting_vendor_prefixes( array(
'css_rules_by_device' => $flex_ready_value,
'selector' => $selector,
'level_id' => $parent_level['id']
), $rules );
break;
// handles simple or by device option
case 'h_alignment' :
if ( is_string( $value ) ) {// <= simple
$properties_to_render['text-align'] = $value;
} else if ( is_array( $value ) ) {// <= by device
$important = false;
if ( 'module' === $parent_level['level'] && !empty( $parent_level['value'] ) ) {
$important = sek_is_flagged_important( $input_id, $parent_level['value'], $registered_input_list );
}
$value = wp_parse_args( $value, array(
'desktop' => '',
'tablet' => '',
'mobile' => ''
));
$rules = sek_set_mq_css_rules( array(
'value' => $value,
'css_property' => 'text-align',
'selector' => $selector,
'is_important' => $important,
'level_id' => $parent_level['id'],
), $rules );
}
break;
// -webkit-box-align:end;
// -ms-flex-align:end;
// align-items:flex-end;
case 'v_alignment' :
switch ( $value ) {
case 'top' :
$v_align_value = "flex-start";
$v_vendor_value = "start";
break;
case 'center' :
$v_align_value = "center";
$v_vendor_value = "center";
break;
case 'bottom' :
$v_align_value = "flex-end";
$v_vendor_value = "end";
break;
default :
$v_align_value = "center";
$v_vendor_value = "center";
break;
}
$properties_to_render['align-items'] = $v_align_value;
$properties_to_render['-webkit-box-align'] = $v_vendor_value;
$properties_to_render['-ms-flex-align'] = $v_vendor_value;
break;
case 'font_family' :
$ffamily = sek_extract_css_font_family_from_customizer_option( $value );
if ( !empty( $ffamily ) ) {
$properties_to_render['font-family'] = $ffamily;
}
break;
/* Spacer */
// The unit should be included in the $value
case 'height' :
if ( is_string( $value ) ) { // <= simple
$numeric = sek_extract_numeric_value($value);
if ( !empty( $numeric ) ) {
$unit = sek_extract_unit( $value );
$unit = '%' === $unit ? 'vh' : $unit;
$properties_to_render['height'] = $numeric . $unit;
}
} else if ( is_array( $value ) ) { // <= by device
$important = false;
if ( 'module' === $parent_level['level'] && !empty( $parent_level['value'] ) ) {
$important = sek_is_flagged_important( $input_id, $parent_level['value'], $registered_input_list );
}
$value = wp_parse_args( $value, array(
'desktop' => '20px',
'tablet' => '',
'mobile' => ''
));
// replace % by vh when needed
$ready_value = $value;
foreach ($value as $device => $num_unit ) {
$numeric = sek_extract_numeric_value( $num_unit );
if ( !empty( $numeric ) ) {
$unit = sek_extract_unit( $num_unit );
$unit = '%' === $unit ? 'vh' : $unit;
$ready_value[$device] = $numeric . $unit;
}
}
$rules = sek_set_mq_css_rules(array(
'value' => $ready_value,
'css_property' => 'height',
'selector' => $selector,
'is_important' => $important,
'level_id' => $parent_level['id']
), $rules );
}
break;
/* Quote border */
case 'border_width' :
$numeric = sek_extract_numeric_value( $value );
if ( 0 === intval($numeric) || !empty( $numeric ) ) {
$unit = sek_extract_unit( $value );
$properties_to_render['border-width'] = $numeric . $unit;
}
break;
case 'border_color' :
$properties_to_render['border-color'] = $value ? $value : '';
break;
/* Divider */
case 'border_top_width' :
$numeric = sek_extract_numeric_value( $value );
if ( !empty( $numeric ) ) {
$unit = sek_extract_unit( $value );
$unit = '%' === $unit ? 'vh' : $unit;
$properties_to_render['border-top-width'] = $numeric . $unit;
//$properties_to_render['border-top-width'] = $value > 0 ? $value . 'px' : '1px';
}
break;
case 'border_top_style' :
$properties_to_render['border-top-style'] = $value ? $value : 'solid';
break;
case 'border_top_color' :
$properties_to_render['border-top-color'] = $value ? $value : '#5a5a5a';
break;
case 'border_radius' :
if ( is_string( $value ) ) {
$numeric = sek_extract_numeric_value( $value );
if ( !empty( $numeric ) ) {
$unit = sek_extract_unit( $value );
$properties_to_render['border-radius'] = $numeric . $unit;
}
} else if ( is_array( $value ) ) {
$rules = sek_generate_css_rules_for_border_radius_options( $rules, $value, $selector );
}
break;
case 'width' :
if ( is_string( $value ) ) { // <= simple
$numeric = sek_extract_numeric_value($value);
if ( !empty( $numeric ) ) {
$unit = sek_extract_unit( $value );
$properties_to_render['width'] = $numeric . $unit;
}
} else if ( is_array( $value ) ) { // <= by device
$important = false;
if ( 'module' === $parent_level['level'] && !empty( $parent_level['value'] ) ) {
$important = sek_is_flagged_important( $input_id, $parent_level['value'], $registered_input_list );
}
$value = wp_parse_args( $value, array(
'desktop' => '100%',
'tablet' => '',
'mobile' => ''
));
// replace % by vh when needed
$ready_value = $value;
foreach ($value as $device => $num_unit ) {
$numeric = sek_extract_numeric_value( $num_unit );
if ( !empty( $numeric ) ) {
$unit = sek_extract_unit( $num_unit );
$ready_value[$device] = $numeric . $unit;
}
}
$rules = sek_set_mq_css_rules(array(
'value' => $ready_value,
'css_property' => 'width',
'selector' => $selector,
'is_important' => $important,
'level_id' => $parent_level['id']
), $rules );
}
break;
case 'v_spacing' :
if ( is_string( $value ) ) { // <= simple
$numeric = sek_extract_numeric_value($value);
if ( !empty( $numeric ) ) {
$unit = sek_extract_unit( $value );
$unit = '%' === $unit ? 'vh' : $unit;
$properties_to_render = array(
'margin-top' => $numeric . $unit,
'margin-bottom' => $numeric . $unit
);
}
} else if ( is_array( $value ) ) { // <= by device
$important = false;
if ( 'module' === $parent_level['level'] && !empty( $parent_level['value'] ) ) {
$important = sek_is_flagged_important( $input_id, $parent_level['value'], $registered_input_list );
}
$value = wp_parse_args( $value, array(
'desktop' => '15px',
'tablet' => '',
'mobile' => ''
));
// replace % by vh when needed
$ready_value = $value;
foreach ($value as $device => $num_unit ) {
$numeric = sek_extract_numeric_value( $num_unit );
if ( !empty( $numeric ) ) {
$unit = sek_extract_unit( $num_unit );
$unit = '%' === $unit ? 'vh' : $unit;
$ready_value[$device] = $numeric . $unit;
}
}
$rules = sek_set_mq_css_rules(array(
'value' => $ready_value,
'css_property' => 'margin-top',
'selector' => $selector,
'is_important' => $important,
'level_id' => $parent_level['id']
), $rules );
$rules = sek_set_mq_css_rules(array(
'value' => $ready_value,
'css_property' => 'margin-bottom',
'selector' => $selector,
'is_important' => $important,
'level_id' => $parent_level['id']
), $rules );
}
break;
//not used at the moment, but it might if we want to display the divider as block (e.g. a div instead of a span)
case 'h_alignment_block' :
switch ( $value ) {
case 'right' :
$properties_to_render = array(
'margin-right' => '0'
);
break;
case 'left' :
$properties_to_render = array(
'margin-left' => '0'
);
break;
default :
$properties_to_render = array(
'margin-left' => 'auto',
'margin-right' => 'auto'
);
}
break;
case 'padding_margin_spacing' :
$default_unit = 'px';
$rules_candidates = $value;
//add unit and sanitize padding (cannot have negative padding)
$unit = !empty( $rules_candidates['unit'] ) ? $rules_candidates['unit'] : $default_unit;
$unit = 'percent' == $unit ? '%' : $unit;
$new_filtered_rules = array();
foreach ( $rules_candidates as $k => $v) {
if ( 'unit' !== $k ) {
$new_filtered_rules[ $k ] = $v;
}
}
$properties_to_render = $new_filtered_rules;
array_walk( $properties_to_render,
function( &$val, $key, $unit ) {
//make sure paddings are positive values
if ( FALSE !== strpos( 'padding', $key ) ) {
$val = abs( $val );
}
$val .= $unit;
}, $unit );
break;
// May 2020 => @todo those media query css rules doesn't take into account the custom breakpoint if set
case 'spacing_with_device_switcher' :
if ( !empty( $value ) && is_array( $value ) ) {
$rules = sek_generate_css_rules_for_spacing_with_device_switcher( $rules, $value, $selector );
}
break;
// The default is simply there to let us know if a css_identifier is missing
default :
sek_error_log( __FUNCTION__ . ' => the css_identifier : ' . $css_identifier . ' has no css rules defined for input id ' . $input_id );
break;
}//switch
// when the module has an '*_flag_important' input,
// => check if the input_id belongs to the list of "important_input_list"
// => and maybe flag the css rules with !important
if ( !empty( $properties_to_render ) ) {
$important = false;
if ( 'module' === $parent_level['level'] && !empty( $parent_level['value'] ) ) {
$important = sek_is_flagged_important( $input_id, $parent_level['value'], $registered_input_list );
}
$css_rules = '';
foreach ( $properties_to_render as $prop => $prop_val ) {
$css_rules .= sprintf( '%1$s:%2$s%3$s;', $prop, $prop_val, $important ? '!important' : '' );
}//end foreach
$rules[] = array(
'selector' => $selector,
'css_rules' => $css_rules,
'mq' => $mq
);
}
return $rules;
}//sek_add_css_rules_for_css_sniffed_input_id
// @return boolean
// Recursive
// Check if a *_flag_important input id is part of the registered input list of the module
// then verify is the provided input_id is part of the list of input that should be set to important => 'important_input_list'
// Example of a *_flag_important input:
// 'quote___flag_important' => array(
// 'input_type' => 'nimblecheck',
// 'title' => __( 'Make those style options win if other rules are applied.', 'text_doma' ),
// 'default' => 0,
// 'refresh_markup' => false,
// 'refresh_stylesheet' => true,
// 'title_width' => 'width-80',
// 'input_width' => 'width-20',
// // declare the list of input_id that will be flagged with !important when the option is checked
// // @see sek_add_css_rules_for_css_sniffed_input_id
// // @see sek_is_flagged_important
// 'important_input_list' => array(
// 'quote_font_family_css',
// 'quote_font_size_css',
// 'quote_line_height_css',
// 'quote_font_weight_css',
// 'quote_font_style_css',
// 'quote_text_decoration_css',
// 'quote_text_transform_css',
// 'quote_letter_spacing_css',
// 'quote_color_css',
// 'quote_color_hover_css'
// )
// )
function sek_is_flagged_important( $input_id, $module_value, $registered_input_list ) {
$important = false;
if ( !is_array( $registered_input_list ) || empty( $registered_input_list ) ) {
sek_error_log( __FUNCTION__ . ' => error => the $registered_input_list param should be an array not empty');
return $important;
}
// loop on the registered input list and try to find a *_flag_important input id
foreach ( $registered_input_list as $_id => $input_data ) {
if ( is_string( $_id ) && false !== strpos( $_id, '_flag_important' ) ) {
if ( empty( $input_data[ 'important_input_list' ] ) ) {
sek_error_log( __FUNCTION__ . ' => error => missing important_input_list for input id ' . $_id );
} else {
$important_list_candidate = $input_data[ 'important_input_list' ];
if ( in_array( $input_id, $important_list_candidate ) ) {
$important = sek_booleanize_checkbox_val( sek_get_input_value_in_module_model( $_id, $module_value ) );
}
}
}
}
return $important;
}
?>
@@ -0,0 +1,996 @@
<?php
namespace Nimble;
if ( !defined( 'ABSPATH' ) ) {
exit;
}
// @return array() for css rules
// $rules[] = array(
// 'selector' => '[data-sek-id="'.$level['id'].'"]',
// 'css_rules' => 'border-radius:'.$numeric . $unit.';',
// 'mq' =>null
// );
//
// @param $border_options is an array looking like :
// [borders] => Array
// (
// [_all_] => Array
// (
// [wght] => 55px
// [col] => #359615
// )
// [top] => Array
// (
// [wght] => 6em
// [col] => #dd3333
// )
// [bottom] => Array
// (
// [wght] => 76%
// [col] => #eeee22
// )
// @param $border_type is a string. solid, dashed, ...
function sek_generate_css_rules_for_multidimensional_border_options( $rules, $border_settings, $border_type, $css_selectors = '' ) {
if ( !is_array( $rules ) )
return array();
$default_data = array( 'wght' => '1px', 'col' => '#000000' );
if ( array_key_exists('_all_', $border_settings) ) {
$default_data = wp_parse_args( $border_settings['_all_'] , $default_data );
}
$css_rules = array();
foreach ( $border_settings as $border_dimension => $data ) {
if ( !is_array( $data ) ) {
sek_error_log( __FUNCTION__ . " => ERROR, the border setting should be an array formed like : array( 'wght' => '1px', 'col' => '#000000' )");
}
$data = wp_parse_args( $data, $default_data );
$border_properties = array();
// border width
$numeric = sek_extract_numeric_value( $data['wght'] );
if ( is_numeric( $numeric ) ) {
$unit = sek_extract_unit( $data['wght'] );
// $unit = '%' === $unit ? 'vw' : $unit;
$border_properties[] = $numeric . $unit;
//border type
$border_properties[] = $border_type;
//border color
//(needs validation: we need a sanitize hex or rgba color)
if ( !empty( $data[ 'col' ] ) ) {
$border_properties[] = $data[ 'col' ];
}
$css_property = 'border';
if ( '_all_' !== $border_dimension ) {
$css_property = 'border-' . $border_dimension;
}
$css_rules[] = "{$css_property}:" . implode( ' ', array_filter( $border_properties ) );
//sek_error_log('CSS RULES FOR BORDERS', implode( ';', array_filter( $css_rules ) ));
}//if ( !empty( $numeric ) )
}//foreach
//append border rules
$rules[] = array(
'selector' => $css_selectors,
'css_rules' => implode( ';', array_filter( $css_rules ) ),//"border:" . implode( ' ', array_filter( $border_properties ) ),
'mq' =>null
);
return $rules;
}
// @return array() for css rules
// $rules[] = array(
// 'selector' => '[data-sek-id="'.$level['id'].'"]',
// 'css_rules' => 'border-radius:'.$numeric . $unit.';',
// 'mq' =>null
// );
//
// @param border_radius_settings is an array looking like :
// border-radius] => Array
// (
// [_all_] => 207px
// [bottom_right] => 360px
// [top_left] => 448px
// [bottom_left] => 413px
// )
function sek_generate_css_rules_for_border_radius_options( $rules, $border_radius_settings, $css_selectors = '' ) {
if ( !is_array( $rules ) )
return array();
if ( empty( $border_radius_settings ) )
return $rules;
$default_radius = '0px';
if ( array_key_exists('_all_', $border_radius_settings ) ) {
$default_val = sek_extract_numeric_value( $border_radius_settings['_all_'] );
if ( is_numeric( $default_val ) ) {
$unit = sek_extract_unit( $border_radius_settings['_all_'] );
$default_radius = $default_val . $unit;
}
}
// Make sure that the $border_radius_setting is normalize so we can generate a proper border-radius value
// For example a user value of ( '_all_' => '3px', 'top_right' => '6px', 'bottom_left' => '3px' )
// should be normalized to ( '_all_' => '3px', 'top_left' => '3px', 'top_right' => '6px', 'bottom_right' => '3px', 'bottom_left' => '3px' )
$radius_dimensions_default = array(
'top_left' => $default_radius,
'top_right' => $default_radius,
'bottom_right' => $default_radius,
'bottom_left' => $default_radius
);
$css_rules = '';
if ( array_key_exists( '_all_', $border_radius_settings ) && 1 === count( $border_radius_settings ) ) {
$css_rules = "border-radius:" . $default_radius.';';
} else {
// Build the normalized array for multidimensional css properties
$normalized_border_radius_values = array();
foreach ( $radius_dimensions_default as $dim => $default_radius) {
if ( array_key_exists( $dim, $border_radius_settings ) ) {
$numeric = sek_extract_numeric_value( $border_radius_settings[$dim] );
if ( is_numeric( $numeric ) ) {
$unit = sek_extract_unit( $border_radius_settings[$dim] );
$normalized_border_radius_values[] = $numeric . $unit;
} else {
$normalized_border_radius_values[] = $default_radius;
}
} else {
$normalized_border_radius_values[] = $default_radius;
}
}
$css_rules = "border-radius:" . implode( ' ', array_filter( $normalized_border_radius_values ) ).';';
}
if ( !empty( $css_rules ) ) {
//append border radius rules
$rules[] = array(
'selector' => $css_selectors,
'css_rules' => $css_rules,
'mq' => null
);
}
return $rules;
}
// @return array() for css rules
// $rules[] = array(
// 'selector' => '[data-sek-id="'.$level['id'].'"]',
// 'css_rules' => '',
// 'mq' =>null
// );
//
// @param $spacing_settings is an array looking like :
// Array
// (
// [desktop] => Array
// (
// [padding-top] => 2
// )
// )
// May 2020 => @todo those media query css rules doesn't take into account the custom breakpoint if set
function sek_generate_css_rules_for_spacing_with_device_switcher( $rules, $spacing_settings, $css_selectors = '', $custom_column_breakpoint = false ) {
//spacing
if ( empty( $spacing_settings ) || !is_array( $spacing_settings ) )
return $rules;
$default_unit = 'px';
//not mobile first
$_desktop_rules = $_mobile_rules = $_tablet_rules = null;
if ( !empty( $spacing_settings['desktop'] ) ) {
$_desktop_rules = array( 'rules' => $spacing_settings['desktop'] );
}
// POPULATES AN ARRAY FROM THE RAW SAVED OPTIONS
$_pad_marg = array(
'desktop' => array(),
'tablet' => array(),
'mobile' => array()
);
foreach( array_keys( $_pad_marg ) as $device ) {
if ( !empty( $spacing_settings[ $device ] ) ) {
$rules_candidates = $spacing_settings[ $device ];
//add unit and sanitize padding (cannot have negative padding)
$unit = !empty( $rules_candidates['unit'] ) ? $rules_candidates['unit'] : $default_unit;
$unit = 'percent' == $unit ? '%' : $unit;
$new_filtered_rules = array();
foreach ( $rules_candidates as $k => $v) {
if ( 'unit' !== $k ) {
$new_filtered_rules[ $k ] = $v;
}
}
$_pad_marg[ $device ] = array( 'rules' => $new_filtered_rules );
array_walk( $_pad_marg[ $device ][ 'rules' ],
function( &$val, $key, $unit ) {
//make sure paddings are positive values
if ( FALSE !== strpos( 'padding', $key ) ) {
$val = abs( $val );
}
$val .= $unit;
}, $unit );
}
}
// Default breakpoint
// may be overriden by user defined once
$mobile_breakpoint = Sek_Dyn_CSS_Builder::$breakpoints['sm'];//max-width: 576
$tablet_breakpoint = Sek_Dyn_CSS_Builder::$breakpoints['md'];// 768
// since https://github.com/presscustomizr/nimble-builder/issues/552,
// we need the parent_level id ( <=> the level on which the CSS rule is applied ) to determine if there's any inherited custom breakpoint to use
// Exceptions :
// - when generating media queries for local options, the level_id is set to '_excluded_from_section_custom_breakpoint_', @see sek_add_raw_local_widths_css()
if ( false !== $custom_column_breakpoint ) {
$tablet_breakpoint = $custom_column_breakpoint;// default is Sek_Dyn_CSS_Builder::$breakpoints['md'] <=> max-width: 768
}
/*
* TABLETS AND MOBILES WILL INHERIT UPPER MQ LEVELS IF NOT OTHERWISE SPECIFIED
*/
// Sek_Dyn_CSS_Builder::$breakpoints = [
// 'xs' => 0,
// 'sm' => 576,
// 'md' => 768,
// 'lg' => 992,
// 'xl' => 1200
// ];
if ( !empty( $_pad_marg[ 'desktop' ] ) ) {
// if ( false !== $custom_column_breakpoint ) {
// // added for https://github.com/presscustomizr/nimble-builder/issues/665
// $_pad_marg[ 'desktop' ][ 'mq' ] = "(min-width: {$tablet_breakpoint}px)";
// } else {
// $_pad_marg[ 'desktop' ][ 'mq' ] = null;
// }
$_pad_marg[ 'desktop' ][ 'mq' ] = null;
}
if ( !empty( $_pad_marg[ 'tablet' ] ) ) {
$_pad_marg[ 'tablet' ][ 'mq' ] = '(max-width:'. ( $tablet_breakpoint - 1 ) . 'px)'; //max-width: 767
}
if ( !empty( $_pad_marg[ 'mobile' ] ) ) {
$_pad_marg[ 'mobile' ][ 'mq' ] = '(max-width:'. ( $mobile_breakpoint - 1 ) . 'px)'; //max-width: 575
}
foreach( array_filter( $_pad_marg ) as $_spacing_rules ) {
$css_rules = implode(';',
array_map( function( $key, $value ) {
return "$key:{$value};";
}, array_keys( $_spacing_rules[ 'rules' ] ), array_values( $_spacing_rules[ 'rules' ] )
) );
if ( array_key_exists('mq', $_spacing_rules) ) {
$rules[] = array(
'selector' => $css_selectors,//'[data-sek-id="'.$level['id'].'"]',
'css_rules' => $css_rules,
'mq' =>$_spacing_rules[ 'mq' ]
);
}
}
return $rules;
}
/****************************************************
* BREAKPOINT / CSS GENERATION WITH MEDIA QUERIES
****************************************************/
// This function is invoked when sniffing the input rules.
// It's a generic helper to generate media query css rule
// @return an array of css rules looking like
// $rules[] = array(
// 'selector' => $selector,
// 'css_rules' => $css_rules,
// 'mq' => $mq
// );
// @params params(array). Example
// array(
// 'value' => $ready_value,(array)
// 'css_property' => 'height',(string or array of properties)
// 'selector' => $selector,(string)
// 'is_important' => $important,(bool),
// 'level_id' => ''
// )
//
// params['value'] = Array
// (
// [desktop] => 5em
// [tablet] => 4em
// [mobile] => 25px
// )
function sek_set_mq_css_rules( $params, $rules ) {
// TABLETS AND MOBILES WILL INHERIT UPPER MQ LEVELS IF NOT OTHERWISE SPECIFIED
// Sek_Dyn_CSS_Builder::$breakpoints = [
// 'xs' => 0,
// 'sm' => 576,
// 'md' => 768,
// 'lg' => 992,
// 'xl' => 1200
// ];
$params = wp_parse_args( $params, array(
'value' => array(),
'css_property' => '',
'selector' => '',
'is_important' => false,
'level_id' => '' //<= added for https://github.com/presscustomizr/nimble-builder/issues/552
));
$mobile_breakpoint = Sek_Dyn_CSS_Builder::$breakpoints['sm'];//max-width: 576
$tablet_breakpoint = Sek_Dyn_CSS_Builder::$breakpoints['md'];// 768
// since https://github.com/presscustomizr/nimble-builder/issues/552,
// we need the parent_level id ( <=> the level on which the CSS rule is applied ) to determine if there's any inherited custom breakpoint to use
// Exceptions :
// - when generating media queries for local options, the level_id is set to '_excluded_from_section_custom_breakpoint_', @see sek_add_raw_local_widths_css()
if ( '_excluded_from_section_custom_breakpoint_' !== $params['level_id'] ) {
if ( empty( $params['level_id'] ) ) {
sek_error_log( __FUNCTION__ . ' => missing level id, needed to determine if there is a custom breakpoint to use', $params );
} else {
$level_id = $params['level_id'];
$tablet_breakpoint = sek_get_section_or_global_tablet_breakpoint_for_css_rules( $level_id, $for_responsive_columns = false );// default is Sek_Dyn_CSS_Builder::$breakpoints['md'] <=> max-width: 768
// If user define breakpoint ( => always for tablet ) is < to $mobile_breakpoint, make sure $mobile_breakpoint is reset to tablet_breakpoint
$mobile_breakpoint = $mobile_breakpoint >= $tablet_breakpoint ? $tablet_breakpoint : $mobile_breakpoint;
}
}
$css_value_by_devices = $params['value'];
$media_q = array('desktop' => null , 'tablet' => null , 'mobile' => null );
if ( !empty( $css_value_by_devices ) ) {
if ( !empty( $css_value_by_devices[ 'desktop' ] ) ) {
$media_q[ 'desktop' ] = null;
}
if ( !empty( $css_value_by_devices[ 'tablet' ] ) ) {
$media_q[ 'tablet' ] = '(max-width:'. ( $tablet_breakpoint - 1 ) . 'px)'; // default is max-width: 767
}
if ( !empty( $css_value_by_devices[ 'mobile' ] ) ) {
$media_q[ 'mobile' ] = '(max-width:'. ( $mobile_breakpoint - 1 ) . 'px)'; // default is max-width: 575
}
// $css_value_by_devices looks like
// array(
// 'desktop' => '30px',
// 'tablet' => '',
// 'mobile' => ''
// );
foreach ( $css_value_by_devices as $device => $val ) {
if ( !in_array( $device, array( 'desktop', 'tablet', 'mobile' ) ) ) {
sek_error_log( __FUNCTION__ . ' => error => unknown device : ' . $device );
continue;
}
if ( !empty( $val ) ) {
// the css_property can be an array
// this is needed for example to write properties supporting several vendor prefixes
$css_property = $params['css_property'];
if ( is_array( $css_property ) ) {
$css_rules_array = array();
foreach ( $css_property as $property ) {
$css_rules_array[] = sprintf( '%1$s:%2$s%3$s;', $property, $val, $params['is_important'] ? '!important' : '' );
}
$css_rules = implode( '', $css_rules_array );
} else {
$css_rules = sprintf( '%1$s:%2$s%3$s;', $css_property, $val, $params['is_important'] ? '!important' : '' );
}
$rules[] = array(
'selector' => $params['selector'],
'css_rules' => $css_rules,
'mq' => $media_q[ $device ]
);
}
}
} else {
sek_error_log( __FUNCTION__ . ' Error => missing css rules ');
}
return $rules;
}
// Additional version of sek_set_mq_css_rules() created in July 2019
// It does not replace the old, but allow another type of rule generation by device
// => this version uses a param "css_rules_by_device" which describe the complete rule ( like padding-top:5em; ) for each device, instead of spliting value and property like the previous one
// => it fixes the problem of vendor prefixes for which the value is not written the same.
// For example, a top alignment in flex is written this way :
// -webkit-box-align:start;
// -ms-flex-align:start;
// align-items:flex-start;
//
// In this case, the param css_rules_by_device will be : "align-items:flex-start;-webkit-box-align:start;-ms-flex-align:start;";
//
// This function is invoked when sniffing the input rules.
// It's a generic helper to generate media query css rule
// @return an array of css rules looking like
// $rules[] = array(
// 'selector' => $selector,
// 'css_rules' => $css_rules,
// 'mq' => $mq
// );
// @params params(array). Example
// array(
// 'css_rules_by_device' => array of css rules by devices
// 'selector' => $selector,(string)
// 'level_id' => ''
// )
// )
// params['value'] = Array
// (
// [desktop] => padding-top:5em;
// [tablet] => padding-top:4em
// [mobile] => padding-top:25px
// )
function sek_set_mq_css_rules_supporting_vendor_prefixes( $params, $rules ) {
// TABLETS AND MOBILES WILL INHERIT UPPER MQ LEVELS IF NOT OTHERWISE SPECIFIED
// Sek_Dyn_CSS_Builder::$breakpoints = [
// 'xs' => 0,
// 'sm' => 576,
// 'md' => 768,
// 'lg' => 992,
// 'xl' => 1200
// ];
$params = wp_parse_args( $params, array(
'css_rules_by_device' => array(),
'selector' => '',
'level_id' => array() //<= added for https://github.com/presscustomizr/nimble-builder/issues/552
));
$mobile_breakpoint = Sek_Dyn_CSS_Builder::$breakpoints['sm'];//max-width: 576
$tablet_breakpoint = Sek_Dyn_CSS_Builder::$breakpoints['md'];// 768
// since https://github.com/presscustomizr/nimble-builder/issues/552,
// we need the parent_level id ( <=> the level on which the CSS rule is applied ) to determine if there's any inherited custom breakpoint to use
// Exceptions :
// - when generating media queries for local options, the level_id is set to '_excluded_from_section_custom_breakpoint_', @see sek_add_raw_local_widths_css()
if ( '_excluded_from_section_custom_breakpoint_' !== $params['level_id'] ) {
if ( empty( $params['level_id'] ) ) {
sek_error_log( __FUNCTION__ . ' => missing level id, needed to determine if there is a custom breakpoint to use', $params );
} else {
$level_id = $params['level_id'];
$tablet_breakpoint = sek_get_section_or_global_tablet_breakpoint_for_css_rules( $level_id, $for_responsive_columns = false );// default is Sek_Dyn_CSS_Builder::$breakpoints['md'] <=> max-width: 768
// If user define breakpoint ( => always for tablet ) is < to $mobile_breakpoint, make sure $mobile_breakpoint is reset to tablet_breakpoint
$mobile_breakpoint = $mobile_breakpoint >= $tablet_breakpoint ? $tablet_breakpoint : $mobile_breakpoint;
}
}
$css_rules_by_device = $params['css_rules_by_device'];
$media_q = array('desktop' => null , 'tablet' => null , 'mobile' => null );
if ( !empty( $css_rules_by_device ) ) {
if ( !empty( $css_rules_by_device[ 'desktop' ] ) ) {
$media_q[ 'desktop' ] = null;
}
if ( !empty( $css_rules_by_device[ 'tablet' ] ) ) {
$media_q[ 'tablet' ] = '(max-width:'. ( $tablet_breakpoint - 1 ) . 'px)'; //max-width: 767
}
if ( !empty( $css_rules_by_device[ 'mobile' ] ) ) {
$media_q[ 'mobile' ] = '(max-width:'. ( $mobile_breakpoint - 1 ) . 'px)'; //max-width: 575
}
foreach ( $css_rules_by_device as $device => $rules_for_device ) {
$rules[] = array(
'selector' => $params['selector'],
'css_rules' => $rules_for_device,
'mq' => $media_q[ $device ]
);
}
} else {
sek_error_log( __FUNCTION__ . ' Error => missing css rules ');
}
return $rules;
}
// BREAKPOINT HELPER
// A custom breakpoint can be set globally or by section
// It replaces the default tablet breakpoint ( 768 px )
//
// the 'for_responsive_columns' param has been introduced for https://github.com/presscustomizr/nimble-builder/issues/564
// so we can differentiate when the custom breakpoint is requested for column responsiveness or for css rules generation
// when for columns, we always apply the custom breakpoint defined by the user
// otherwise, when generating CSS rules like alignment, the custom breakpoint is applied if user explicitely checked the 'apply_to_all' option
// 'for_responsive_columns' is set to true when sek_get_closest_section_custom_breakpoint() is invoked from Nimble_Manager()::render()
function sek_get_section_or_global_tablet_breakpoint_for_css_rules( $level_id = '', $for_responsive_columns = false ) {
//sek_error_log('ALORS CLOSEST PARENT SECTION MODEL ?' . $level_id , sek_get_closest_section_custom_breakpoint( $level_id ) );
// define a default breakpoint : 768
$tablet_breakpoint = Sek_Dyn_CSS_Builder::$breakpoints[Sek_Dyn_CSS_Builder::COLS_MOBILE_BREAKPOINT];//COLS_MOBILE_BREAKPOINT = 'md' <=> 768px
// Is there a custom breakpoint set by a parent section?
// Order :
// 1) custom breakpoint set on a nested section
// 2) custom breakpoint set on a regular section
//sek_error_log('WE SEARCH FOR => ' . $level_id );
$closest_section_custom_breakpoint = sek_get_closest_section_custom_breakpoint( array( 'searched_level_id' => $level_id, 'for_responsive_columns' => false ) );
//sek_error_log('WE FOUND A BREAKPOINT ', $closest_section_custom_breakpoint );
if ( is_array( $closest_section_custom_breakpoint ) ) {
// we do this check because sek_get_closest_section_custom_breakpoint() uses an array when recursively looping
// but returns number when a match is found
$closest_section_custom_breakpoint = 0;
} else {
$closest_section_custom_breakpoint = intval( $closest_section_custom_breakpoint );
}
if ( $closest_section_custom_breakpoint >= 1 ) {
$tablet_breakpoint = $closest_section_custom_breakpoint;
} else {
// 1) Is there a global custom breakpoint set ?
// 2) shall we apply this global custom breakpoint to all customizations or only to responsive columns ? https://github.com/presscustomizr/nimble-builder/issues/564
if ( sek_is_global_custom_breakpoint_applied_to_all_customizations_by_device() ) {
$global_custom_breakpoint = intval( sek_get_global_custom_breakpoint() );
if ( $global_custom_breakpoint >= 1 ) {
$tablet_breakpoint = $global_custom_breakpoint;
}
}
}
return intval( $tablet_breakpoint );
}
//HELPERS
/**
* SASS COLOR DARKEN/LIGHTEN UTILS
*/
/**
* Darken hex color
*/
function sek_darken_hex( $hex, $percent, $make_prop_value = true ) {
$hsl = sek_hex2hsl( $hex );
$dark_hsl = sek_darken_hsl( $hsl, $percent );
return sek_hsl2hex( $dark_hsl, $make_prop_value );
}
/**
*Lighten hex color
*/
function sek_lighten_hex($hex, $percent, $make_prop_value = true) {
$hsl = sek_hex2hsl( $hex );
$light_hsl = sek_lighten_hsl( $hsl, $percent );
return sek_hsl2hex( $light_hsl, $make_prop_value );
}
/**
* Darken rgb color
*/
function sek_darken_rgb( $rgb, $percent, $array = false, $make_prop_value = false ) {
$hsl = sek_rgb2hsl( $rgb, true );
$dark_hsl = sek_darken_hsl( $hsl, $percent );
return sek_hsl2rgb( $dark_hsl, $array, $make_prop_value );
}
/**
* Lighten rgb color
*/
function sek_lighten_rgb($rgb, $percent, $array = false, $make_prop_value = false ) {
$hsl = sek_rgb2hsl( $rgb, true );
$light_hsl = sek_lighten_hsl( $hsl, $percent );
return sek_hsl2rgb( $light_hsl, $array, $make_prop_value );
}
/**
* Darken/Lighten hsl
*/
function sek_darken_hsl( $hsl, $percentage, $array = true ) {
$percentage = trim( $percentage, '% ' );
$hsl[2] = ( $hsl[2] * 100 ) - $percentage;
$hsl[2] = ( $hsl[2] < 0 ) ? 0: $hsl[2]/100;
if ( !$array ) {
$hsl = implode( ",", $hsl );
}
return $hsl;
}
/**
* Lighten hsl
*/
function sek_lighten_hsl( $hsl, $percentage, $array = true ) {
$percentage = trim( $percentage, '% ' );
$hsl[2] = ( $hsl[2] * 100 ) + $percentage;
$hsl[2] = ( $hsl[2] > 100 ) ? 1 : $hsl[2]/100;
if ( !$array ) {
$hsl = implode( ",", $hsl );
}
return $hsl;
}
/**
* Convert hexadecimal to rgb
*/
function sek_hex2rgb( $hex, $array = false, $make_prop_value = false ) {
$hex = trim( $hex, '# ' );
if ( 3 == strlen( $hex ) ) {
$r = hexdec( substr( $hex, 0, 1 ) . substr( $hex, 0, 1 ) );
$g = hexdec( substr( $hex, 1, 1 ) . substr( $hex, 1, 1 ) );
$b = hexdec( substr( $hex, 2, 1 ) . substr( $hex, 2, 1 ) );
}
else {
$r = hexdec( substr( $hex, 0, 2 ) );
$g = hexdec( substr( $hex, 2, 2 ) );
$b = hexdec( substr( $hex, 4, 2 ) );
}
$rgb = array( $r, $g, $b );
if ( !$array ) {
$rgb = implode( ",", $rgb );
$rgb = $make_prop_value ? "rgb($rgb)" : $rgb;
}
return $rgb;
}
/**
* Convert hexadecimal to rgba
*/
function sek_hex2rgba( $hex, $alpha = 0.7, $array = false, $make_prop_value = false ) {
$rgb = $rgba = sek_hex2rgb( $hex, $_array = true );
$rgba[] = $alpha;
if ( !$array ) {
$rgba = implode( ",", $rgba );
$rgba = $make_prop_value ? "rgba($rgba)" : $rgba;
}
return $rgba;
}
/**
* Convert rgb to rgba
*/
function sek_rgb2rgba( $rgb, $alpha = 0.7, $array = false, $make_prop_value = false ) {
$rgb = is_array( $rgb ) ? $rgb : explode( ',', $rgb );
$rgb = is_array( $rgb) ? $rgb : array( $rgb );
$rgb = $rgba = count( $rgb ) < 3 ? array_pad( $rgb, 3, 255 ) : $rgb;
$rgba[] = $alpha;
if ( !$array ) {
$rgba = implode( ",", $rgba );
$rgba = $make_prop_value ? "rgba($rgba)" : $rgba;
}
return $rgba;
}
/*
* Following heavily based on
* https://github.com/mexitek/phpColors
* MIT License
*/
/**
* Convert rgb to hexadecimal
*/
function sek_rgb2hex( $rgb, $make_prop_value = false ) {
$rgb = is_array( $rgb ) ? $rgb : explode( ',', $rgb );
$rgb = is_array( $rgb) ? $rgb : array( $rgb );
$rgb = count( $rgb ) < 3 ? array_pad( $rgb, 3, 255 ) : $rgb;
// Convert RGB to HEX
$hex[0] = str_pad( dechex( $rgb[0] ), 2, '0', STR_PAD_LEFT );
$hex[1] = str_pad( dechex( $rgb[1] ), 2, '0', STR_PAD_LEFT );
$hex[2] = str_pad( dechex( $rgb[2] ), 2, '0', STR_PAD_LEFT );
$hex = implode( '', $hex );
return $make_prop_value ? "#{$hex}" : $hex;
}
/**
* Convert rgba to rgb array + alpha
* @param $rgba string example rgba(221,51,51,0.72)
* @return array()
*/
function sek_rgba2rgb_a( $rgba ) {
$rgba = is_array( $rgba ) ? $rgba : explode( ',', $rgba );
$rgba = is_array( $rgba) ? $rgba : array( $rgba );
// make sure we remove all parenthesis remaining
$rgba = array_map( function( $val ) {
return str_replace(array('(', ')' ), '', $val);
}, array_values( $rgba ) );
$rgb = array_slice( $rgba, 0, 3 );
// remove everything but numbers
$rgb = array_map( function( $_val ) {
return preg_replace('/[^0-9]/', '', $_val);
}, array_values( $rgb ) );
return array(
$rgb,
// https://github.com/presscustomizr/nimble-builder/issues/303
isset( $rgba[3] ) ? $rgba[3] : 1
);
}
/*
* heavily based on
* phpColors
*/
/**
* Convert rgb to hsl
*/
function sek_rgb2hsl( $rgb, $array = false ) {
$rgb = is_array( $rgb ) ? $rgb : explode( ',', $rgb );
$rgb = is_array( $rgb) ? $rgb : array( $rgb );
$rgb = count( $rgb ) < 3 ? array_pad( $rgb, 3, 255 ) : $rgb;
$deltas = array();
// check if numeric following https://github.com/presscustomizr/nimble-builder/issues/303
$RGB = array(
'R' => is_numeric($rgb[0]) ? ( $rgb[0] / 255 ) : 1,
'G' => is_numeric($rgb[1]) ? ( $rgb[1] / 255 ) : 1,
'B' => is_numeric($rgb[2]) ? ( $rgb[2] / 255 ) : 1,
);
$min = min( array_values( $RGB ) );
$max = max( array_values( $RGB ) );
$span = $max - $min;
$H = $S = 0;
$L = ($max + $min)/2;
if ( 0 != $span ) {
if ( $L < 0.5 ) {
$S = $span / ( $max + $min );
}
else {
$S = $span / ( 2 - $max - $min );
}
foreach ( array( 'R', 'G', 'B' ) as $var ) {
$deltas[$var] = ( ( ( $max - $RGB[$var] ) / 6 ) + ( $span / 2 ) ) / $span;
}
if ( $max == $RGB['R'] ) {
$H = $deltas['B'] - $deltas['G'];
}
else if ( $max == $RGB['G'] ) {
$H = ( 1 / 3 ) + $deltas['R'] - $deltas['B'];
}
else if ( $max == $RGB['B'] ) {
$H = ( 2 / 3 ) + $deltas['G'] - $deltas['R'];
}
if ( $H<0 ) {
$H++;
}
if ( $H>1 ) {
$H--;
}
}
$hsl = array( $H*360, $S, $L );
if ( !$array ) {
$hsl = implode( ",", $hsl );
}
return $hsl;
}
/**
* Convert hsl to rgb
*/
function sek_hsl2rgb( $hsl, $array=false, $make_prop_value = false ) {
list($H,$S,$L) = array( $hsl[0]/360, $hsl[1], $hsl[2] );
$rgb = array_fill( 0, 3, $L * 255 );
if ( 0 !=$S ) {
if ($L < 0.5 ) {
$var_2 = $L * ( 1 + $S );
} else {
$var_2 = ( $L + $S ) - ( $S * $L );
}
$var_1 = 2 * $L - $var_2;
$rgb[0] = sek_hue2rgbtone( $var_1, $var_2, $H + ( 1/3 ) );
$rgb[1] = sek_hue2rgbtone( $var_1, $var_2, $H );
$rgb[2] = sek_hue2rgbtone( $var_1, $var_2, $H - ( 1/3 ) );
}
if ( !$array ) {
$rgb = implode(",", $rgb);
$rgb = $make_prop_value ? "rgb($rgb)" : $rgb;
}
return $rgb;
}
/**
* Convert hsl to hex
*/
function sek_hsl2hex( $hsl = array(), $make_prop_value = false ) {
$rgb = sek_hsl2rgb( $hsl, $array = true );
return sek_rgb2hex( $rgb, $make_prop_value );
}
/**
* Convert hex to hsl
*/
function sek_hex2hsl( $hex ) {
$rgb = sek_hex2rgb( $hex, true );
return sek_rgb2hsl( $rgb, true );
}
/**
* Convert hue to rgb
*/
function sek_hue2rgbtone( $v1, $v2, $vH ) {
$_to_return = $v1;
if( $vH < 0 ) {
$vH += 1;
}
if( $vH > 1 ) {
$vH -= 1;
}
if ( (6*$vH) < 1 ) {
$_to_return = ($v1 + ($v2 - $v1) * 6 * $vH);
}
elseif ( (2*$vH) < 1 ) {
$_to_return = $v2;
}
elseif ( (3*$vH) < 2 ) {
$_to_return = ($v1 + ($v2-$v1) * ( (2/3)-$vH ) * 6);
}
return round( 255 * $_to_return );
}
/*
* Returns the complementary hsl color
*/
function sek_rgb_invert( $rgb ) {
// Adjust Hue 180 degrees
// $hsl[0] += ($hsl[0]>180) ? -180:180;
$rgb_inverted = array(
255 - $rgb[0],
255 - $rgb[1],
255 - $rgb[2]
);
return $rgb_inverted;
}
/**
* Returns the complementary hsl color
*/
function sek_hex_invert( $hex, $make_prop_value = true ) {
$rgb = sek_hex2rgb( $hex, $array = true );
$rgb_inverted = sek_rgb_invert( $rgb );
return sek_rgb2hex( $rgb_inverted, $make_prop_value );
}
// 1.5em => em
// -2px => px
function sek_extract_unit( $value ) {
// remove numbers, dot, comma
$unit = preg_replace('/[0-9]|\.|,/', '', $value );
// remove hyphens
$unit = str_replace('-', '', $unit );
return 0 === preg_match( "/(px|em|%)/i", $unit ) ? 'px' : $unit;
}
// 1.5em => 1.5
// note : using preg_replace('/[^0-9]/', '', $data); would remove the dots or comma.
function sek_extract_numeric_value( $value ) {
if ( !is_scalar( $value ) )
return null;
$numeric = preg_replace('/px|em|%/', '', $value);
return is_numeric( $numeric ) ? $numeric : null;
}
// Return a font family string, usable in a css stylesheet
// [cfont]Helvetica Neue, Helvetica, Arial, sans-serif => Helvetica Neue, Helvetica, Arial, sans-serif
// [gfont]Assistant:regular => Assistant
function sek_extract_css_font_family_from_customizer_option( $family ) {
//sek_error_log( __FUNCTION__ . ' font-family', $value );
// Preprocess the selected font family
// font: [font-stretch] [font-style] [font-variant] [font-weight] [font-size]/[line-height] [font-family];
// special treatment for font-family
if ( false != strstr( $family, '[gfont]') ) {
$split = explode(":", $family);
$family = $split[0];
//only numbers for font-weight. 400 is default
$properties_to_render['font-weight'] = $split[1] ? preg_replace('/\D/', '', $split[1]) : '';
$properties_to_render['font-weight'] = empty($properties_to_render['font-weight']) ? 400 : $properties_to_render['font-weight'];
$properties_to_render['font-style'] = ( $split[1] && strstr($split[1], 'italic') ) ? 'italic' : 'normal';
}
if ( 'none' === $family ) {
$family = '';
} else {
$family = false != strstr( $family, '[cfont]') ? $family : "'" . str_replace( '+' , ' ' , $family ) . "'";
$family = str_replace( array( '[gfont]', '[cfont]') , '' , $family );
}
return $family;
}
?>
@@ -0,0 +1,407 @@
<?php
// The base fmk is loaded @after_setup_theme:10
add_action( 'after_setup_theme', '\Nimble\sek_schedule_module_registration', 50 );
// On front we register only the necessary modules
// When customizing, we register all of them
// On admin, we register none
function sek_schedule_module_registration() {
// we load all modules when :
// 1) customizing
// 2) doing ajax <=> customizing
// 3) isset( $_POST['nimble_simple_cf'] ) <= when a contact form is submitted.
// Note about 3) => We should in fact load the necessary modules that we can determined with the posted skope_id. To be improved.
// 3 fixes https://github.com/presscustomizr/nimble-builder/issues/433
if ( isset( $_POST['nimble_simple_cf'] ) ) {
sek_register_modules_when_customizing_or_ajaxing();
} else if ( skp_is_customizing() || ( defined('DOING_AJAX') && DOING_AJAX ) ) {
sek_register_modules_when_customizing_or_ajaxing();
// prebuilt sections are registered from a JSON since https://github.com/presscustomizr/nimble-builder/issues/431
sek_register_prebuilt_section_modules();
// June 2020 : for https://github.com/presscustomizr/nimble-builder/issues/520
sek_register_user_sections_module();
} else {
// Condition !is_admin() added in april 2020
// fixes https://github.com/presscustomizr/nimble-builder/issues/658
if ( !is_admin() ) {
add_action( 'wp', '\Nimble\sek_register_active_modules_on_front', PHP_INT_MAX );
}
}
}
// @return void();
// @hook 'after_setup_theme'
function sek_register_modules_when_customizing_or_ajaxing() {
$modules = array_merge(
SEK_Front_Construct::$ui_picker_modules,
// June 2020 filter added for https://github.com/presscustomizr/nimble-builder-pro/issues/6
apply_filters( 'nb_level_module_collection', SEK_Front_Construct::$ui_level_modules ),
SEK_Front_Construct::$ui_local_global_options_modules,
SEK_Front_Construct::sek_get_front_module_collection()
);
// widgets module, menu module have been beta tested during 5 months and released in June 2019, in version 1.8.0
if ( sek_are_beta_features_enabled() ) {
$modules = array_merge( $modules, SEK_Front_Construct::$ui_front_beta_modules );
}
sek_do_register_module_collection( $modules );
}
// @return void();
// @hook 'wp'@PHP_INT_MAX
function sek_register_active_modules_on_front() {
sek_register_modules_when_not_customizing_and_not_ajaxing();
}
// @param $skope_id added in april 2020 for https://github.com/presscustomizr/nimble-builder/issues/657
function sek_register_modules_when_not_customizing_and_not_ajaxing( $skope_id = '' ) {
$contextually_actives_raw = sek_get_collection_of_contextually_active_modules( $skope_id );
$contextually_actives_raw = array_keys( $contextually_actives_raw );
$contextually_actives_candidates = array();
$front_modules = array_merge( SEK_Front_Construct::sek_get_front_module_collection(), SEK_Front_Construct::$ui_front_beta_modules );
// we need to get all children when the module is a father.
// This will be flatenized afterwards
foreach ( $contextually_actives_raw as $module_name ) {
// Parent module with children
if ( array_key_exists( $module_name, $front_modules ) ) {
// get the list of childrent, includes the parent too.
// @see ::sek_get_front_module_collection()
$contextually_actives_candidates[] = $front_modules[ $module_name ];
}
// Simple module with no children
if ( in_array( $module_name, $front_modules ) ) {
$contextually_actives_candidates[] = $module_name;
}
}
$modules = array_merge(
$contextually_actives_candidates,
apply_filters( 'nb_level_module_collection', SEK_Front_Construct::$ui_level_modules ),
SEK_Front_Construct::$ui_local_global_options_modules
);
sek_do_register_module_collection( $modules );
}
// @return void();
function sek_do_register_module_collection( $modules ) {
$module_candidates = array();
// flatten the array
// because can be formed this way after filter when including child
// [0] => Array
// (
// [0] => czr_post_grid_module
// [1] => czr_post_grid_main_child
// [2] => czr_post_grid_thumb_child
// [3] => czr_post_grid_metas_child
// [4] => czr_post_grid_fonts_child
// )
// [1] => sek_level_bg_module
// [2] => sek_level_border_module
foreach ($modules as $key => $value) {
if ( is_array( $value ) ) {
$module_candidates = array_merge( $module_candidates, $value );
} else {
$module_candidates[] = $value;
}
}
// remove duplicated modules, typically 'czr_font_child'
$module_candidates = array_unique( $module_candidates );
foreach ( $module_candidates as $module_name ) {
// Was previously written "\Nimble\sek_get_module_params_for_{$module_name}";
// But this syntax can lead to function_exists() return false even if the function exists
// Probably due to a php version issue. Bug detected with php version 5.6.38
// bug report detailed here https://github.com/presscustomizr/nimble-builder/issues/234
$fn = "Nimble\sek_get_module_params_for_{$module_name}";
if ( function_exists( $fn ) ) {
$params = apply_filters( "nimble_module_params_for_{$module_name}", $fn() );
if ( is_array( $params ) ) {
CZR_Fmk_Base()->czr_pre_register_dynamic_module( $params );
} else {
error_log( __FUNCTION__ . ' Module registration params should be an array');
}
} else {
error_log( __FUNCTION__ . ' missing params callback fn for module ' . $module_name );
}
}
}
// SINGLE MODULE PARAMS STUCTURE
// 'dynamic_registration' => true,
// 'module_type' => 'sek_column_layouts_sec_picker_module',
// 'name' => __('Empty sections with columns layout', 'text_doma'),
// 'tmpl' => array(
// 'item-inputs' => array(
// 'sections' => array(
// 'input_type' => 'section_picker',
// 'title' => __('Drag-and-drop or double-click a section to insert it into a drop zone of the preview page.', 'text_doma'),
// 'width-100' => true,
// 'title_width' => 'width-100',
// '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'
// ),
// )
// )
// )
// )
// @return void();
// @hook 'after_setup_theme'
function sek_register_prebuilt_section_modules() {
$registration_params = sek_get_sections_registration_params();
$default_module_params = array(
'dynamic_registration' => true,
'module_type' => '',
'name' => '',
'tmpl' => array(
'item-inputs' => array(
'sections' => array(
'input_type' => 'section_picker',
'title' => __('Drag-and-drop or double-click a section to insert it into a drop zone of the preview page.', 'text_doma'),
'width-100' => true,
'title_width' => 'width-100',
'section_collection' => array()
)
)
)
);
foreach ( $registration_params as $module_type => $module_params ) {
$module_params = wp_parse_args( $module_params, array(
'name' => '',
'section_collection' => array()
));
// normalize the module params
$normalized_params = $default_module_params;
$normalized_params['module_type'] = $module_type;
$normalized_params['name'] = $module_params['name'];
$normalized_params['tmpl']['item-inputs']['sections']['section_collection'] = $module_params['section_collection'];
CZR_Fmk_Base()->czr_pre_register_dynamic_module( $normalized_params );
}
}
// @return void();
// @hook 'after_setup_theme'
// June 2020 for https://github.com/presscustomizr/nimble-builder/issues/520
function sek_register_user_sections_module() {
$normalized_params = array(
'dynamic_registration' => true,
'module_type' => 'sek_my_sections_sec_picker_module',
'name' => __('My sections', 'text-doma'),
'tmpl' => array(
'item-inputs' => array(
'sections' => array(
'input_type' => 'section_picker',
'title' => __('Drag-and-drop or double-click a section to insert it into a drop zone of the preview page.', 'text_doma'),
'width-100' => true,
'title_width' => 'width-100',
'section_collection' => array()
)
)
)
);
CZR_Fmk_Base()->czr_pre_register_dynamic_module( $normalized_params );
}
// HELPERS
// Used when registering a select input in a module
// @return an array of options that will be used to populate the select input in js
function sek_get_select_options_for_input_id( $input_id ) {
$options = array();
switch( $input_id ) {
case 'img_hover_effect' :
$options = array(
'none' => __('No effect', 'text_doma' ),
'opacity' => __('Opacity', 'text_doma' ),
'zoom-out' => __('Zoom out', 'text_doma' ),
'zoom-in' => __('Zoom in', 'text_doma' ),
'move-up' =>__('Move up', 'text_doma' ),
'move-down' =>__('Move down', 'text_doma' ),
'blur' =>__('Blur', 'text_doma' ),
'grayscale' =>__('Grayscale', 'text_doma' ),
'reverse-grayscale' =>__('Reverse grayscale', 'text_doma' )
);
break;
case 'img-size' :
$options = sek_get_img_sizes();
break;
// ALL MODULES
case 'link-to' :
$options = array(
'no-link' => __('No link', 'text_doma' ),
'url' => __('Site content or custom url', 'text_doma' ),
);
break;
// FEATURED PAGE MODULE
case 'img-type' :
$options = array(
'none' => __( 'No image', 'text_doma' ),
'featured' => __( 'Use the page featured image', 'text_doma' ),
'custom' => __( 'Use a custom image', 'text_doma' ),
);
break;
case 'content-type' :
$options = array(
'none' => __( 'No text', 'text_doma' ),
'page-excerpt' => __( 'Use the page excerpt', 'text_doma' ),
'custom' => __( 'Use a custom text', 'text_doma' ),
);
break;
// HEADING MODULE
case 'heading_tag':
$options = array(
/* Not totally sure these should be localized as they strictly refer to html tags */
'h1' => __('H1', 'text_doma' ),
'h2' => __('H2', 'text_doma' ),
'h3' => __('H3', 'text_doma' ),
'h4' => __('H4', 'text_doma' ),
'h5' => __('H5', 'text_doma' ),
'h6' => __('H6', 'text_doma' ),
);
break;
// CSS MODIFIERS INPUT ID
case 'font_weight_css' :
$options = array(
'normal' => __( 'normal', 'text_doma' ),
'bold' => __( 'bold', 'text_doma' ),
'bolder' => __( 'bolder', 'text_doma' ),
'lighter' => __( 'lighter', 'text_doma' ),
100 => 100,
200 => 200,
300 => 300,
400 => 400,
500 => 500,
600 => 600,
700 => 700,
800 => 800,
900 => 900
);
break;
case 'font_style_css' :
$options = array(
'inherit' => __( 'inherit', 'text_doma' ),
'italic' => __( 'italic', 'text_doma' ),
'normal' => __( 'normal', 'text_doma' ),
'oblique' => __( 'oblique', 'text_doma' )
);
break;
case 'text_decoration_css' :
$options = array(
'none' => __( 'none', 'text_doma' ),
'inherit' => __( 'inherit', 'text_doma' ),
'line-through' => __( 'line-through', 'text_doma' ),
'overline' => __( 'overline', 'text_doma' ),
'underline' => __( 'underline', 'text_doma' )
);
break;
case 'text_transform_css' :
$options = array(
'none' => __( 'none', 'text_doma' ),
'inherit' => __( 'inherit', 'text_doma' ),
'capitalize' => __( 'capitalize', 'text_doma' ),
'uppercase' => __( 'uppercase', 'text_doma' ),
'lowercase' => __( 'lowercase', 'text_doma' )
);
break;
// SPACING MODULE
case 'css_unit' :
$options = array(
'px' => __('Pixels', 'text_doma' ),
'em' => __('Em', 'text_doma'),
'percent' => __('Percents', 'text_doma' )
);
break;
//QUOTE MODULE
case 'quote_design' :
$options = array(
'none' => __( 'Text only', 'text_doma' ),
'border-before' => __( 'Side Border', 'text_doma' ),
'quote-icon-before' => __( 'Quote Icon', 'text_doma' ),
);
break;
// LEVELS UI : LAYOUT BACKGROUND BORDER HEIGHT WIDTH
case 'boxed-wide' :
$options = array(
'boxed' => __('Boxed', 'text_doma'),
'fullwidth' => __('Full Width', 'text_doma')
);
break;
case 'height-type' :
$options = array(
'auto' => __('Adapt to content', 'text_doma'),
'custom' => __('Custom', 'text_doma' )
);
break;
case 'width-type' :
$options = array(
'default' => __('Default', 'text_doma'),
'custom' => __('Custom', 'text_doma' )
);
break;
case 'bg-scale' :
$options = array(
'default' => __('Default', 'text_doma'),
'auto' => __('Automatic', 'text_doma'),
'cover' => __('Scale to fill', 'text_doma'),
'contain' => __('Fit', 'text_doma'),
);
break;
case 'bg-position' :
$options = array(
'default' => __('default', 'text_doma'),
);
break;
case 'border-type' :
$options = array(
'none' => __('none', 'text_doma'),
'solid' => __('solid', 'text_doma'),
'double' => __('double', 'text_doma'),
'dotted' => __('dotted', 'text_doma'),
'dashed' => __('dashed', 'text_doma')
);
break;
default :
sek_error_log( __FUNCTION__ . ' => no case set for input id : '. $input_id );
break;
}
return $options;
}
?>
@@ -0,0 +1,191 @@
<?php
namespace Nimble;
if ( !defined( 'ABSPATH' ) ) {
exit;
}
/* ------------------------------------------------------------------------- *
* LOAD AND REGISTER SIMPLE HTML MODULE
/* ------------------------------------------------------------------------- */
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_czr_simple_html_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_simple_html_module',
'name' => __( 'Html Content', 'text_doma' ),
'sanitize_callback' => '\Nimble\sanitize_callback__czr_simple_html_module',
'tmpl' => array(
'item-inputs' => array(
'html_content' => array(
'input_type' => 'code_editor',
'title' => __( 'HTML Content' , 'text_doma' ),
'refresh_markup' => '.sek-module-inner'
//'code_type' => 'text/html' //<= use 'text/css' to instantiate the code mirror as CSS editor, which by default will be an HTML editor
),
'h_alignment_css' => array(
'input_type' => 'horizAlignmentWithDeviceSwitcher',
'title' => __( 'Horizontal text alignment', 'text_doma' ),
'default' => array( 'desktop' => 'left' ),
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'h_alignment',
//'css_selectors' => '.sek-module-inner',
'title_width' => 'width-100',
'width-100' => true,
'html_before' => '<hr/><h3>' . __('ALIGNMENT') .'</h3>'
),
'font_family_css' => array(
'input_type' => 'font_picker',
'title' => __('Font family', 'text_doma'),
'default' => '',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'refresh_fonts' => true,
'css_identifier' => 'font_family',
'html_before' => '<hr/><h3>' . __('TEXT OPTIONS') .'</h3>',
'html_after' => sprintf('<span class="czr-notice"><i class="far fa-lightbulb"></i> %1s => <a href="%2s" target="_blank" rel="noopener noreferrer">%2$s</a></span><hr/>',
__('Find inspiration'),
'https://fonts.google.com/?sort=popularity'
)
),
'font_size_css' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __( 'Font size', 'text_doma' ),
// the default value is commented to fix https://github.com/presscustomizr/nimble-builder/issues/313
// => as a consequence, when a module uses the font child module, the default font-size rule must be defined in the module SCSS file.
//'default' => array( 'desktop' => '16px' ),
'min' => 0,
'max' => 100,
'title_width' => 'width-100',
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'font_size'
),//16,//"14px",
'line_height_css' => array(
'input_type' => 'range_with_unit_picker',
'title' => __( 'Line height', 'text_doma' ),
'default' => '1.5em',
'min' => 0,
'max' => 10,
'step' => 0.1,
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'line_height'
),//24,//"20px",
'color_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __('Text color', 'text_doma'),
'default' => '',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'width-100' => true,
'css_identifier' => 'color'
),//"#000000",
'color_hover_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __('Text color on mouse over', 'text_doma'),
'default' => '',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'width-100' => true,
'title_width' => 'width-100',
'css_identifier' => 'color_hover'
),//"#000000",
'font_weight_css' => array(
'input_type' => 'simpleselect',
'title' => __('Font weight', 'text_doma'),
'default' => 400,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'font_weight',
'choices' => sek_get_select_options_for_input_id( 'font_weight_css' )
),//null,
'font_style_css' => array(
'input_type' => 'simpleselect',
'title' => __('Font style', 'text_doma'),
'default' => 'inherit',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'font_style',
'choices' => sek_get_select_options_for_input_id( 'font_style_css' )
),//null,
'text_decoration_css' => array(
'input_type' => 'simpleselect',
'title' => __('Text decoration', 'text_doma'),
'default' => 'none',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'text_decoration',
'choices' => sek_get_select_options_for_input_id( 'text_decoration_css' )
),//null,
'text_transform_css' => array(
'input_type' => 'simpleselect',
'title' => __('Text transform', 'text_doma'),
'default' => 'none',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'text_transform',
'choices' => sek_get_select_options_for_input_id( 'text_transform_css' )
),//null,
'letter_spacing_css' => array(
'input_type' => 'range_simple',
'title' => __( 'Letter spacing', 'text_doma' ),
'default' => 0,
'min' => 0,
'step' => 1,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'letter_spacing',
'width-100' => true,
),//0,
// Note : always use the suffix '_flag_important' to name an input controling the !important css flag @see Nimble\sek_add_css_rules_for_css_sniffed_input_id
'fonts___flag_important' => array(
'input_type' => 'nimblecheck',
'title' => __('Apply the style options in priority (uses !important).', 'text_doma'),
'default' => 0,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'title_width' => 'width-80',
'input_width' => 'width-20',
// declare the list of input_id that will be flagged with !important when the option is checked
// @see sek_add_css_rules_for_css_sniffed_input_id
// @see Nsek_is_flagged_important
'important_input_list' => array(
'font_family_css',
'font_size_css',
'line_height_css',
'font_weight_css',
'font_style_css',
'text_decoration_css',
'text_transform_css',
'letter_spacing_css',
'color_css',
'color_hover_css'
)
)
)
),
'render_tmpl_path' => "simple_html_module_tmpl.php",
'placeholder_icon' => 'code'
);
}
/* ------------------------------------------------------------------------- *
* SANITIZATION
/* ------------------------------------------------------------------------- */
function sanitize_callback__czr_simple_html_module( $value ) {
if ( is_array($value) && array_key_exists( 'html_content', $value ) && is_string( $value[ 'html_content' ] ) ) {
if ( !current_user_can( 'unfiltered_html' ) ) {
$value[ 'html_content' ] = wp_kses_post( $value[ 'html_content' ] );
}
// convert into a json to prevent emoji breaking global json data structure
// fix for https://github.com/presscustomizr/nimble-builder/issues/544
$value[ 'html_content' ] = sek_maybe_encode_richtext( $value[ 'html_content' ] );
}
return $value;
}
?>
@@ -0,0 +1,94 @@
<?php
/* ------------------------------------------------------------------------- *
* TEXT EDITOR FATHER MODULE
/* ------------------------------------------------------------------------- */
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_czr_tiny_mce_editor_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_tiny_mce_editor_module',
'is_father' => true,
'children' => array(
'main_settings' => 'czr_tinymce_child',
'font_settings' => 'czr_font_child'
),
'name' => __('Text Editor', 'text_doma'),
'starting_value' => array(
'main_settings' => array(
'content' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor.'
)
),
'sanitize_callback' => '\Nimble\sek_sanitize_czr_tiny_mce_editor_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'css_selectors' => array(
// this list is limited to the most commonly used tags in the editor.
// note that Hx headings have a default style set in _heading.scss
'.sek-module-inner',
'.sek-module-inner p',
'.sek-module-inner a',
'.sek-module-inner li'
),
'render_tmpl_path' => "tinymce_editor_module_tmpl.php",
'placeholder_icon' => 'short_text'
);
}
/* ------------------------------------------------------------------------- *
* SANITIZATION
/* ------------------------------------------------------------------------- */
// convert into a json to prevent emoji breaking global json data structure
// fix for https://github.com/presscustomizr/nimble-builder/issues/544
function sek_sanitize_czr_tiny_mce_editor_module( $content ) {
if ( is_array($content) && !empty($content['main_settings']) && is_array($content['main_settings']) ) {
$editor_content = !empty($content['main_settings']['content']) ? $content['main_settings']['content'] : '';
$content['main_settings']['content'] = sek_maybe_encode_richtext($editor_content);
}
//sek_error_log( 'ALORS MODULE CONTENT ?', $content );
return $content;
}
/* ------------------------------------------------------------------------- *
* TEXT EDITOR CONTENT CHILD
/* ------------------------------------------------------------------------- */
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_czr_tinymce_child() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_tinymce_child',
'name' => __('Content', 'text_doma'),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'content' => array(
'input_type' => 'detached_tinymce_editor',
'title' => __('Content', 'text_doma'),
'default' => '',
'refresh_markup' => '.sek-module-inner [data-sek-input-type="detached_tinymce_editor"]',
),
'h_alignment_css' => array(
'input_type' => 'horizTextAlignmentWithDeviceSwitcher',
'title' => __('Alignment', 'text_doma'),
'default' => array( 'desktop' => is_rtl() ? 'right' : 'left' ),
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'h_alignment',
'title_width' => 'width-100',
'width-100' => true,
),
'autop' => array(
'input_type' => 'nimblecheck',
'title' => __('Automatically convert text into paragraph', 'text_doma'),
'default' => true,
'title_width' => 'width-80',
'input_width' => 'width-20',
'notice_after' => __('WordPress wraps the editor text inside "p" tags by default. You can disable this behaviour by unchecking this option.', 'text-domain')
),
)
),
'render_tmpl_path' =>'',
);
}
?>
@@ -0,0 +1,441 @@
<?php
/* ------------------------------------------------------------------------- *
* LOAD AND REGISTER IMAGE MODULE
/* ------------------------------------------------------------------------- */
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_czr_image_module() {
$css_selectors = '.sek-module-inner img';
return array(
'dynamic_registration' => true,
'module_type' => 'czr_image_module',
'is_father' => true,
'children' => array(
'main_settings' => 'czr_image_main_settings_child',
'borders_corners' => 'czr_image_borders_corners_child'
),
'name' => __('Image', 'text_doma'),
'starting_value' => array(
'main_settings' => array(
'img' => NIMBLE_BASE_URL . '/assets/img/default-img.png',
'custom_width' => ''
)
),
'sanitize_callback' => '\Nimble\sanitize_cb__czr_image_module',
// 'validate_callback' => '\Nimble\czr_image_module_sanitize_validate',
'render_tmpl_path' => "image_module_tmpl.php",
'placeholder_icon' => 'short_text'
);
}
/* ------------------------------------------------------------------------- *
* SANITIZATION
/* ------------------------------------------------------------------------- */
// convert into a json to prevent emoji breaking global json data structure
// fix for https://github.com/presscustomizr/nimble-builder/issues/544
function sanitize_cb__czr_image_module( $value ) {
if ( !is_array( $value ) )
return $value;
if ( is_array( $value ) && !empty($value['main_settings']) && is_array( $value['main_settings'] ) && array_key_exists( 'heading_title', $value['main_settings'] ) ) {
//$value['content'][ 'button_text' ] = sanitize_text_field( $value['content'][ 'button_text' ] );
// convert into a json to prevent emoji breaking global json data structure
// fix for https://github.com/presscustomizr/nimble-builder/issues/544
$value['main_settings']['heading_title'] = sek_maybe_encode_richtext($value['main_settings']['heading_title']);
}
return $value;
}
/* ------------------------------------------------------------------------- *
* MAIN SETTINGS
/* ------------------------------------------------------------------------- */
function sek_get_module_params_for_czr_image_main_settings_child() {
$pro_text = '';
if ( !sek_is_pro() ) {
$pro_text = sek_get_pro_notice_for_czr_input( __('set a specific header logo on mobiles, shrink header logo when scrolling down the page, ...', 'text-doma') );
}
return array(
'dynamic_registration' => true,
'module_type' => 'czr_image_main_settings_child',
'name' => __( 'Image main settings', 'text_doma' ),
//'sanitize_callback' => '\Nimble\sanitize_callback__czr_simple_form_module',
// 'starting_value' => array(
// 'button_text' => __('Click me','text_doma'),
// 'color_css' => '#ffffff',
// 'bg_color_css' => '#020202',
// 'bg_color_hover' => '#151515', //lighten 15%,
// 'use_custom_bg_color_on_hover' => 0,
// 'border_radius_css' => '2',
// 'h_alignment_css' => 'center',
// 'use_box_shadow' => 1,
// 'push_effect' => 1
// ),
//'css_selectors' => array( '.sek-module-inner .sek-simple-form-wrapper' ),
'tmpl' => array(
'item-inputs' => array(
'img' => array(
'input_type' => 'upload',
'title' => __('Pick an image', 'text_doma'),
'default' => ''
),
'use-post-thumb' => array(
'input_type' => 'nimblecheck',
'title' => __('Use the contextual post thumbnail', 'text_doma'),
'title_width' => 'width-80',
'input_width' => 'width-20',
'refresh_markup' => true,
'default' => 0,
'notice_after' => __('When enabled and possible, Nimble will use the post thumbnail.', 'text_doma'),
),
'img-size' => array(
'input_type' => 'simpleselect',
'title' => __('Select the image size', 'text_doma'),
'default' => 'large',
'choices' => sek_get_select_options_for_input_id( 'img-size' ),
'notice_before' => __('Select a size for this image among those generated by WordPress.', 'text_doma' )
),
'link-to' => array(
'input_type' => 'simpleselect',
'title' => __('Schedule an action on click or tap', 'text_doma'),
'default' => 'no-link',
'choices' => array(
'no-link' => __('No click action', 'text_doma' ),
'img-lightbox' =>__('Lightbox : enlarge the image, and dim out the rest of the content', 'text_doma' ),
'url' => __('Link to site content or custom url', 'text_doma' ),
'img-file' => __('Link to image file', 'text_doma' ),
'img-page' =>__('Link to image page', 'text_doma' )
),
'title_width' => 'width-100',
'width-100' => true,
'notice_after' => __('Note that some click actions are disabled during customization.', 'text_doma' ),
),
'link-pick-url' => array(
'input_type' => 'content_picker',
'title' => __('Link url', 'text_doma'),
'default' => array()
),
'link-custom-url' => array(
'input_type' => 'text',
'title' => __('Custom link url', 'text_doma'),
'default' => ''
),
'link-target' => array(
'input_type' => 'nimblecheck',
'title' => __('Open link in a new browser tab', 'text_doma'),
'default' => false,
'title_width' => 'width-80',
'input_width' => 'width-20',
),
'h_alignment_css' => array(
'input_type' => 'horizAlignmentWithDeviceSwitcher',
'title' => __('Alignment', 'text_doma'),
'default' => array( 'desktop' => 'center' ),
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'h_alignment',
'title_width' => 'width-100',
'width-100' => true,
'css_selectors'=> 'figure'
),
'use_custom_title_attr' => array(
'input_type' => 'nimblecheck',
'title' => __('Set the text displayed when the mouse is held over', 'text_doma'),
'default' => false,
'title_width' => 'width-80',
'input_width' => 'width-20',
'notice_after' => __('If not specified, Nimble will use by order of priority the caption, the description, and the image title. Those properties can be edited for each image in the media library.')
),
'heading_title' => array(
'input_type' => 'text',
'title' => __('Custom text displayed on mouse hover', 'text_domain_to' ),
'default' => '',
'title_width' => 'width-100',
'width-100' => true
),
'use_custom_width' => array(
'input_type' => 'nimblecheck',
'title' => __( 'Custom image width', 'text_doma' ),
'default' => 0,
'refresh_stylesheet' => true,
'html_before' => '<hr/>'
),
'custom_width' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __('Width', 'text_doma'),
'min' => 1,
'max' => 100,
//'unit' => '%',
'default' => array( 'desktop' => '100%' ),
'max' => 500,
'width-100' => true,
'title_width' => 'width-100',
'refresh_markup' => false,
'refresh_stylesheet' => true
),
'use_custom_height' => array(
'input_type' => 'nimblecheck',
'title' => __( 'Custom image max height', 'text_doma' ),
'default' => 0,
'refresh_stylesheet' => true
),
'custom_height' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __('Height', 'text_doma'),
'min' => 1,
'max' => 100,
//'unit' => '%',
'default' => array( 'desktop' => '100%' ),
'max' => 500,
'width-100' => true,
'title_width' => 'width-100',
'refresh_markup' => false,
'refresh_stylesheet' => true
),
'use_box_shadow' => array(
'input_type' => 'nimblecheck',
'title' => __( 'Apply a shadow', 'text_doma' ),
'default' => 0,
'html_before' => '<hr/>'
),
'img_hover_effect' => array(
'input_type' => 'simpleselect',
'title' => __('Mouse over effect', 'text_doma'),
'default' => 'none',
'choices' => sek_get_select_options_for_input_id( 'img_hover_effect' ),
'html_after' => $pro_text
)
)
),
'render_tmpl_path' => '',
);
}
/* ------------------------------------------------------------------------- *
* IMAGE BORDERS AND BORDER RADIUS
/* ------------------------------------------------------------------------- */
function sek_get_module_params_for_czr_image_borders_corners_child() {
$css_selectors = '.sek-module-inner img';
return array(
'dynamic_registration' => true,
'module_type' => 'czr_image_borders_corners_child',
'name' => __( 'Borders and corners', 'text_doma' ),
//'sanitize_callback' => '\Nimble\sanitize_callback__czr_simple_form_module',
// 'starting_value' => array(
// 'button_text' => __('Click me','text_doma'),
// 'color_css' => '#ffffff',
// 'bg_color_css' => '#020202',
// 'bg_color_hover' => '#151515', //lighten 15%,
// 'use_custom_bg_color_on_hover' => 0,
// 'border_radius_css' => '2',
// 'h_alignment_css' => 'center',
// 'use_box_shadow' => 1,
// 'push_effect' => 1
// ),
//'css_selectors' => array( '.sek-module-inner .sek-simple-form-wrapper' ),
'tmpl' => array(
'item-inputs' => array(
'border-type' => array(
'input_type' => 'simpleselect',
'title' => __('Border', 'text_doma'),
'default' => 'none',
'choices' => sek_get_select_options_for_input_id( 'border-type' ),
'refresh_markup' => false,
'refresh_stylesheet' => true
),
'borders' => array(
'input_type' => 'borders',
'title' => __('Borders', 'text_doma'),
'min' => 0,
'max' => 100,
'default' => array(
'_all_' => array( 'wght' => '1px', 'col' => '#000000' )
),
'refresh_markup' => false,
'refresh_stylesheet' => true,
'width-100' => true,
'title_width' => 'width-100',
'css_selectors'=> $css_selectors
),
'border_radius_css' => array(
'input_type' => 'border_radius',
'title' => __( 'Rounded corners', 'text_doma' ),
'default' => array( '_all_' => '0px' ),
'width-100' => true,
'title_width' => 'width-100',
'min' => 0,
'max' => 500,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'border_radius',
'css_selectors'=> $css_selectors
),
)
),
'render_tmpl_path' => '',
);
}
/* ------------------------------------------------------------------------- *
* SCHEDULE CSS RULES FILTERING
/* ------------------------------------------------------------------------- */
add_filter( 'sek_add_css_rules_for_module_type___czr_image_module', '\Nimble\sek_add_css_rules_for_czr_image_module', 10, 2 );
// filter documented in Sek_Dyn_CSS_Builder::sek_css_rules_sniffer_walker
// Note : $complete_modul_model has been normalized
// @return populated $rules
function sek_add_css_rules_for_czr_image_module( $rules, $complete_modul_model ) {
if ( empty( $complete_modul_model['value'] ) )
return $rules;
$value = $complete_modul_model['value'];
$main_settings = $complete_modul_model['value']['main_settings'];
$borders_corners_settings = $complete_modul_model['value']['borders_corners'];
// WIDTH
if ( sek_booleanize_checkbox_val( $main_settings['use_custom_width'] ) ) {
$width = $main_settings[ 'custom_width' ];
$css_rules = '';
if ( isset( $width ) && FALSE !== $width ) {
$numeric = sek_extract_numeric_value( $width );
if ( !empty( $numeric ) ) {
$unit = sek_extract_unit( $width );
$css_rules .= 'width:' . $numeric . $unit . ';';
}
// same treatment as in sek_add_css_rules_for_css_sniffed_input_id() => 'width'
if ( is_string( $width ) ) {
$numeric = sek_extract_numeric_value($width);
if ( !empty( $numeric ) ) {
$unit = sek_extract_unit( $width );
$css_rules .= 'width:' . $numeric . $unit . ';';
}
} else if ( is_array( $width ) ) {
$width = wp_parse_args( $width, array(
'desktop' => '100%',
'tablet' => '',
'mobile' => ''
));
// replace % by vh when needed
$ready_value = $width;
foreach ($width as $device => $num_unit ) {
$numeric = sek_extract_numeric_value( $num_unit );
if ( !empty( $numeric ) ) {
$unit = sek_extract_unit( $num_unit );
$ready_value[$device] = $numeric . $unit;
}
}
$rules = sek_set_mq_css_rules(array(
'value' => $ready_value,
'css_property' => 'width',
'selector' => '[data-sek-id="'.$complete_modul_model['id'].'"] .sek-module-inner img',
'is_important' => false,
'level_id' => $complete_modul_model['id']
), $rules );
// to fix https://github.com/presscustomizr/nimble-builder/issues/754
$rules = sek_set_mq_css_rules(array(
'value' => $ready_value,
'css_property' => 'max-width',
'selector' => '[data-sek-id="'.$complete_modul_model['id'].'"] .sek-module-inner img',
'is_important' => false,
'level_id' => $complete_modul_model['id']
), $rules );
}
}//if
if ( !empty( $css_rules ) ) {
$rules[] = array(
'selector' => '[data-sek-id="'.$complete_modul_model['id'].'"] .sek-module-inner img',
'css_rules' => $css_rules,
'mq' =>null
);
}
}// Width
// HEIGHT
if ( sek_booleanize_checkbox_val( $main_settings['use_custom_height'] ) ) {
$height = $main_settings[ 'custom_height' ];
$css_rules = '';
if ( isset( $height ) && FALSE !== $height ) {
$numeric = sek_extract_numeric_value( $height );
if ( !empty( $numeric ) ) {
$unit = sek_extract_unit( $height );
$css_rules .= 'max-height:' . $numeric . $unit . ';';
}
// same treatment as in sek_add_css_rules_for_css_sniffed_input_id() => 'width'
if ( is_string( $height ) ) {
$numeric = sek_extract_numeric_value($height);
if ( !empty( $numeric ) ) {
$unit = sek_extract_unit( $height );
$css_rules .= 'max-height:' . $numeric . $unit . ';';
}
} else if ( is_array( $height ) ) {
$height = wp_parse_args( $height, array(
'desktop' => '100%',
'tablet' => '',
'mobile' => ''
));
// replace % by vh when needed
$ready_value = $height;
foreach ($height as $device => $num_unit ) {
$numeric = sek_extract_numeric_value( $num_unit );
if ( !empty( $numeric ) ) {
$unit = sek_extract_unit( $num_unit );
$ready_value[$device] = $numeric . $unit;
}
}
$rules = sek_set_mq_css_rules(array(
'value' => $ready_value,
'css_property' => 'max-height',
'selector' => '[data-sek-id="'.$complete_modul_model['id'].'"] .sek-module-inner figure',
'is_important' => false,
'level_id' => $complete_modul_model['id']
), $rules );
}
}//if
if ( !empty( $css_rules ) ) {
$rules[] = array(
'selector' => '[data-sek-id="'.$complete_modul_model['id'].'"] .sek-module-inner img',
'css_rules' => $css_rules,
'mq' =>null
);
}
}// height
// BORDERS
$border_settings = $borders_corners_settings[ 'borders' ];
$border_type = $borders_corners_settings[ 'border-type' ];
$has_border_settings = 'none' != $border_type && !empty( $border_type );
//border width + type + color
if ( $has_border_settings ) {
$rules = sek_generate_css_rules_for_multidimensional_border_options(
$rules,
$border_settings,
$border_type,
'[data-sek-id="'.$complete_modul_model['id'].'"] .sek-module-inner img'
);
}
return $rules;
}
?>
@@ -0,0 +1,327 @@
<?php
/* ------------------------------------------------------------------------- *
* LOAD AND REGISTER SOCIAL ICONS MODULE
/* ------------------------------------------------------------------------- */
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_czr_social_icons_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_social_icons_module',
'is_father' => true,
'children' => array(
'icons_collection' => 'czr_social_icons_settings_child',
'icons_style' => 'czr_social_icons_style_child'
),
'name' => __('Social Icons', 'text_doma'),
'starting_value' => array(
'icons_collection' => array(
array( 'icon' => 'fab fa-facebook', 'color_css' => '#3b5998' ),
array( 'icon' => 'fab fa-twitter', 'color_css' => '#1da1f2' ),
array( 'icon' => 'fab fa-instagram', 'color_css' => '#262626' )
)
),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'css_selectors' => array( '.sek-social-icons-wrapper' ),//array( '.sek-icon i' ),
'render_tmpl_path' => "social_icons_tmpl.php",
// Nimble will "sniff" if we need font awesome
// No need to enqueue font awesome here
// 'front_assets' => array(
// 'czr-font-awesome' => array(
// 'type' => 'css',
// //'handle' => 'czr-font-awesome',
// 'src' => NIMBLE_BASE_URL . '/assets/front/fonts/css/fontawesome-all.min.css'
// //'deps' => array()
// )
// )
);
}
/* ------------------------------------------------------------------------- *
* MAIN SETTINGS
/* ------------------------------------------------------------------------- */
function sek_get_module_params_for_czr_social_icons_settings_child() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_social_icons_settings_child',
'is_crud' => true,
'name' => __( 'Icon collection', 'text_doma' ),
//'sanitize_callback' => '\Nimble\sanitize_callback__czr_simple_form_module',
'css_selectors' => array( '.sek-social-icon' ),//array( '.sek-icon i' ),
'tmpl' => array(
'pre-item' => array(
// 'page-id' => array(
// 'input_type' => 'content_picker',
// 'title' => __('Pick a page', 'text_doma')
// ),
'icon' => array(
'input_type' => 'fa_icon_picker',
'title' => __('Select an icon', 'text_doma')
),
'link' => array(
'input_type' => 'text',
'title' => __('Social link url', 'text_doma'),
'notice_after' => __('Enter the full url of your social profile (must be valid url).', 'text_doma'),
'placeholder' => __('http://...,mailto:...,...', 'text_doma')
)
),
'item-inputs' => array(
'icon' => array(
'input_type' => 'fa_icon_picker',
'title' => __('Select an icon', 'text_doma')
),
'link' => array(
'input_type' => 'text',
'default' => '',
'title' => __('Social link url', 'text_doma'),
'notice_after' => __('Enter the full url of your social profile (must be valid url).', 'text_doma'),
'placeholder' => __('http://...,mailto:...,...', 'text_doma')
),
'title_attr' => array(
'input_type' => 'text',
'default' => '',
'title' => __('Title', 'text_domain_to_be_replaced'),
'notice_after' => __('This is the text displayed on mouse over.', 'text_domain_to_be_replaced'),
),
'link_target' => array(
'input_type' => 'nimblecheck',
'title' => __('Open link in a new browser tab', 'text_doma'),
'default' => false,
'title_width' => 'width-80',
'input_width' => 'width-20',
),
'color_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __('Color', 'text_doma'),
'width-100' => true,
'default' => '#707070',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'color'
),
'use_custom_color_on_hover' => array(
'input_type' => 'nimblecheck',
'title' => __( 'Set a custom icon color on mouse hover', 'text_doma' ),
'title_width' => 'width-80',
'input_width' => 'width-20',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'default' => false,
),
'social_color_hover' => array(
'input_type' => 'wp_color_alpha',
'title' => __('Hover color', 'text_doma'),
'width-100' => true,
'default' => '#969696',
'refresh_markup' => false,
'refresh_stylesheet' => true,
//'css_identifier' => 'color_hover'
)
)
),
'render_tmpl_path' => '',
);
}
/* ------------------------------------------------------------------------- *
* SOCIAL ICONS STYLING
/* ------------------------------------------------------------------------- */
function sek_get_module_params_for_czr_social_icons_style_child() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_social_icons_style_child',
'name' => __( 'Design options : size, spacing, alignment,...', 'text_doma' ),
//'sanitize_callback' => '\Nimble\sanitize_callback__czr_simple_form_module',
// 'starting_value' => array(
// 'button_text' => __('Click me','text_doma'),
// 'color_css' => '#ffffff',
// 'bg_color_css' => '#020202',
// 'bg_color_hover' => '#151515', //lighten 15%,
// 'use_custom_bg_color_on_hover' => 0,
// 'border_radius_css' => '2',
// 'h_alignment_css' => 'center',
// 'use_box_shadow' => 1,
// 'push_effect' => 1
// ),
'css_selectors' => array( '.sek-social-icons-wrapper' ),//array( '.sek-icon i' ),
'tmpl' => array(
'item-inputs' => array(
'font_size_css' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __( 'Icons size', 'text_doma' ),
// the default value is commented to fix https://github.com/presscustomizr/nimble-builder/issues/313
// => as a consequence, when a module uses the font child module, the default font-size rule must be defined in the module SCSS file.
//'default' => array( 'desktop' => '16px' ),
'min' => 0,
'max' => 100,
'title_width' => 'width-100',
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'font_size',
'css_selectors' => '.sek-module-inner .sek-social-icons-wrapper > li .sek-social-icon',
),//16,//"14px",
'line_height_css' => array(
'input_type' => 'range_with_unit_picker',
'title' => __( 'Line height', 'text_doma' ),
'default' => '1.5em',
'min' => 0,
'max' => 10,
'step' => 0.1,
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'line_height',
'css_selectors' => '.sek-module-inner .sek-social-icons-wrapper > li .sek-social-icon',
),//24,//"20px",
'h_alignment_css' => array(
'input_type' => 'horizAlignmentWithDeviceSwitcher',
'title' => __( 'Horizontal alignment', 'text_doma' ),
'default' => array( 'desktop' => 'center' ),//consistent with SCSS
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'h_alignment',
//'css_selectors' => '.sek-module-inner',
'title_width' => 'width-100',
'width-100' => true,
),
'space_between_icons' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __('Space between icons', 'text_doma'),
'min' => 1,
'max' => 100,
//'unit' => 'px',
'default' => array( 'desktop' => '8px' ),
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'title_width' => 'width-100'
),
'spacing_css' => array(
'input_type' => 'spacingWithDeviceSwitcher',
'title' => __( 'Spacing of the icons wrapper', 'text_doma' ),
'default' => array('desktop' => array('margin-bottom' => '10', 'margin-top' => '10', 'unit' => 'px')),//consistent with SCSS
'title_width' => 'width-100',
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'spacing_with_device_switcher',
'css_selectors' => '.sek-module-inner .sek-social-icons-wrapper',
// 'css_selectors'=> '.sek-icon i'
)
)
),
'render_tmpl_path' => '',
);
}
/* ------------------------------------------------------------------------- *
* SCHEDULE CSS RULES FILTERING
/* ------------------------------------------------------------------------- */
// PER ITEM CSS DESIGN => FILTERING OF EACH ITEM MODEL, TARGETING THE ID ( [data-sek-item-id="893af157d5e3"] )
add_filter( 'sek_add_css_rules_for_single_item_in_module_type___czr_social_icons_settings_child', '\Nimble\sek_add_css_rules_for_items_in_czr_social_icons_settings_child', 10, 2 );
// filter documented in Sek_Dyn_CSS_Builder::sek_css_rules_sniffer_walker
// Note : $complete_modul_model has been normalized
// @return populated $rules
// @param $params
// Array
// (
// [input_list] => Array
// (
// [icon] => fab fa-acquisitions-incorporated
// [link] => https://twitter.com/home
// [title_attr] => Follow me on twitter
// [link_target] =>
// [color_css] => #dd9933
// [use_custom_color_on_hover] =>
// [social_color_hover] => #dd3333
// [id] => 62316ab99b4d
// )
// [parent_module_id] =>
// [module_type] => czr_social_icons_settings_child
// [module_css_selector] => Array
// (
// [0] => .sek-social-icon
// )
// )
function sek_add_css_rules_for_items_in_czr_social_icons_settings_child( $rules, $params ) {
//sek_error_log('SOCIAL ITEMS PARAMS?', $params );
// $item_input_list = wp_parse_args( $item_input_list, $default_value_model );
$item_model = isset( $params['input_list'] ) ? $params['input_list'] : array();
// COLOR ON HOVER
$icon_color = $item_model['color_css'];
if ( sek_booleanize_checkbox_val( $item_model['use_custom_color_on_hover'] ) ) {
$color_hover = $item_model['social_color_hover'];
} else {
// Build the lighter rgb from the user picked bg color
if ( 0 === strpos( $icon_color, 'rgba' ) ) {
list( $rgb, $alpha ) = sek_rgba2rgb_a( $icon_color );
$color_hover_rgb = sek_lighten_rgb( $rgb, $percent=15, $array = true );
$color_hover = sek_rgb2rgba( $color_hover_rgb, $alpha, $array = false, $make_prop_value = true );
} else if ( 0 === strpos( $icon_color, 'rgb' ) ) {
$color_hover = sek_lighten_rgb( $icon_color, $percent=15 );
} else {
$color_hover = sek_lighten_hex( $icon_color, $percent=15 );
}
}
$color_hover_selector = sprintf( '[data-sek-id="%1$s"] [data-sek-item-id="%2$s"] .sek-social-icon:hover', $params['parent_module_id'], $item_model['id'] );
$rules[] = array(
'selector' => $color_hover_selector,
'css_rules' => 'color:' . $color_hover . ';',
'mq' =>null
);
return $rules;
}
// GLOBAL CSS DESIGN => FILTERING OF THE ENTIRE MODULE MODEL
add_filter( 'sek_add_css_rules_for_module_type___czr_social_icons_module', '\Nimble\sek_add_css_rules_for_czr_social_icons_module', 10, 2 );
// filter documented in Sek_Dyn_CSS_Builder::sek_css_rules_sniffer_walker
// Note : $complete_modul_model has been normalized
// @return populated $rules
function sek_add_css_rules_for_czr_social_icons_module( $rules, $complete_modul_model ) {
if ( empty( $complete_modul_model['value'] ) || !is_array( $complete_modul_model['value'] ) )
return $rules;
$value = $complete_modul_model['value'];
$icons_style = $value['icons_style'];
// HORIZONTAL SPACE BETWEEN ICONS
$padding_right = $icons_style['space_between_icons'];
$padding_right = is_array( $padding_right ) ? $padding_right : array();
$defaults = array(
'desktop' => '15px',// <= this value matches the static CSS rule and the input default for the module
'tablet' => '',
'mobile' => ''
);
$padding_right = wp_parse_args( $padding_right, $defaults );
$padding_right_ready_val = $padding_right;
foreach ($padding_right as $device => $num_unit ) {
$num_val = sek_extract_numeric_value( $num_unit );
$padding_right_ready_val[$device] = '';
// Leave the device value empty if === to default
// Otherwise it will print a duplicated dynamic css rules, already hardcoded in the static stylesheet
// fixes https://github.com/presscustomizr/nimble-builder/issues/419
if ( !empty( $num_unit ) && $num_val.'px' !== $defaults[$device].'' ) {
$unit = sek_extract_unit( $num_unit );
$num_val = $num_val < 0 ? 0 : $num_val;
$padding_right_ready_val[$device] = $num_val . $unit;
}
}
$rules = sek_set_mq_css_rules( array(
'value' => $padding_right_ready_val,
'css_property' => 'padding-right',
'selector' => implode(',', array(
'[data-sek-id="'.$complete_modul_model['id'].'"] .sek-module-inner .sek-social-icons-wrapper > *:not(:last-child)',
)),
'is_important' => false,
'level_id' => $complete_modul_model['id']
), $rules );
return $rules;
}
?>
@@ -0,0 +1,165 @@
<?php
/* ------------------------------------------------------------------------- *
* TEXT EDITOR FATHER MODULE
/* ------------------------------------------------------------------------- */
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_czr_heading_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_heading_module',
'is_father' => true,
'children' => array(
'main_settings' => 'czr_heading_child',
'font_settings' => 'czr_font_child',
'spacing' => 'czr_heading_spacing_child'
),
'name' => __('Heading', 'text_doma'),
'starting_value' => array(
'main_settings' => array(
'heading_text' => 'This is a heading.'
)
),
'css_selectors' => array( '.sek-module-inner > .sek-heading' ),
'sanitize_callback' => '\Nimble\sek_sanitize_czr_heading_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'render_tmpl_path' => "heading_module_tmpl.php",
'placeholder_icon' => 'short_text'
);
}
/* ------------------------------------------------------------------------- *
* TEXT EDITOR CONTENT CHILD
/* ------------------------------------------------------------------------- */
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_czr_heading_child() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_heading_child',
'name' => __('Content', 'text_doma'),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'heading_text' => array(
'input_type' => 'nimble_tinymce_editor',
'editor_params' => array(
'media_button' => false,
'includedBtns' => 'basic_btns',
'height' => 50
),
'title' => __( 'Heading text', 'text_doma' ),
'default' => '',
'width-100' => true,
'refresh_markup' => '.sek-heading [data-sek-input-type="textarea"]'
//'notice_before' => __( 'You may use some html tags like a, br, span with attributes like style, id, class ...', 'text_doma'),
),
'heading_tag' => array(
'input_type' => 'simpleselect',
'title' => __( 'Heading tag', 'text_doma' ),
'default' => 'h1',
'choices' => sek_get_select_options_for_input_id( 'heading_tag' )
),
'h_alignment_css' => array(
'input_type' => 'horizTextAlignmentWithDeviceSwitcher',
'title' => __('Alignment', 'text_doma'),
'default' => array( 'desktop' => 'center'),
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'h_alignment',
'title_width' => 'width-100',
'width-100' => true,
),
'heading_title' => array(
'input_type' => 'text',
'title' => __('Display a tooltip text when the mouse is held over', 'text_domain_to' ),
'default' => '',
'title_width' => 'width-100',
'width-100' => true,
'notice_after' => __('Not previewable during customization', 'text_domain_to')
),
'link-to' => array(
'input_type' => 'nimblecheck',
'title' => __('Turn into a link', 'text_doma'),
'default' => false,
'title_width' => 'width-80',
'input_width' => 'width-20'
),
'link-pick-url' => array(
'input_type' => 'content_picker',
'title' => __('Link url', 'text_doma'),
'default' => array()
),
'link-custom-url' => array(
'input_type' => 'text',
'title' => __('Custom link url', 'text_doma'),
'default' => ''
),
'link-target' => array(
'input_type' => 'nimblecheck',
'title' => __('Open link in a new browser tab', 'text_doma'),
'default' => false,
'title_width' => 'width-80',
'input_width' => 'width-20',
)
)
),
'render_tmpl_path' =>'',
);
}
/* ------------------------------------------------------------------------- *
* SANITIZATION
/* ------------------------------------------------------------------------- */
function sek_sanitize_czr_heading_module( $content ) {
if ( is_array($content) && is_array($content['main_settings']) ) {
// main heading text
if ( !empty($content['main_settings']['heading_text']) ) {
// https://wordpress.org/support/article/roles-and-capabilities/#unfiltered_html
if ( !current_user_can( 'unfiltered_html' ) ) {
$value['main_settings'][ 'heading_text' ] = wp_kses_post( $content['main_settings']['heading_text'] );
}
// convert into a json to prevent emoji breaking global json data structure
// fix for https://github.com/presscustomizr/nimble-builder/issues/544
$content['main_settings']['heading_text'] = sek_maybe_encode_richtext($content['main_settings']['heading_text']);
}
if ( !empty($content['main_settings']['heading_title']) ) {
$content['main_settings']['heading_title'] = sek_maybe_encode_richtext($content['main_settings']['heading_title']);
}
}
return $content;
}
/* ------------------------------------------------------------------------- *
* HEADING SPACING CHILD
/* ------------------------------------------------------------------------- */
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_czr_heading_spacing_child() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_heading_spacing_child',
'name' => __('Spacing', 'text_doma'),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'spacing_css' => array(
'input_type' => 'spacingWithDeviceSwitcher',
'title' => __( 'Margin and padding', 'text_doma' ),
'default' => array('desktop' => array('margin-bottom' => '0.6', 'margin-top' => '0.6', 'unit' => 'em')),
'width-100' => true,
'title_width' => 'width-100',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'spacing_with_device_switcher',
//'css_selectors'=> ''
)
)
),
'render_tmpl_path' =>'',
);
}
?>
@@ -0,0 +1,37 @@
<?php
/* ------------------------------------------------------------------------- *
* LOAD AND REGISTER SPACER MODULE
/* ------------------------------------------------------------------------- */
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_czr_spacer_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_spacer_module',
'name' => __('Spacer', 'text_doma'),
'css_selectors' => array( '.sek-module-inner > *' ),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'height_css' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'min' => 0,
'max' => 100,
'step' => 1,
'title' => __('Space', 'text_doma'),
'default' => array( 'desktop' => '20px' ),
'width-100' => true,
'title_width' => 'width-100',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_selectors' => array( '.sek-spacer' ),
'css_identifier' => 'height'
),
)
),
'render_tmpl_path' => "spacer_module_tmpl.php",
);
}
?>
@@ -0,0 +1,98 @@
<?php
/* ------------------------------------------------------------------------- *
* LOAD AND REGISTER DIVIDER MODULE
/* ------------------------------------------------------------------------- */
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_czr_divider_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_divider_module',
'name' => __('Divider', 'text_doma'),
'css_selectors' => array( '.sek-divider' ),
'tmpl' => array(
'item-inputs' => array(
'border_top_width_css' => array(
'input_type' => 'range_with_unit_picker',
'title' => __('Weight', 'text_doma'),
'min' => 1,
'max' => 50,
//'unit' => 'px',
'default' => '1px',
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'border_top_width'
),
'border_top_style_css' => array(
'input_type' => 'simpleselect',
'title' => __('Style', 'text_doma'),
'default' => 'solid',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'border_top_style',
'choices' => sek_get_select_options_for_input_id( 'border-type' )
),
'border_top_color_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __('Color', 'text_doma'),
'width-100' => true,
'default' => '#5a5a5a',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'border_top_color'
),
'width_css' => array(
'input_type' => 'range_with_unit_picker',
'title' => __('Width', 'text_doma'),
'min' => 1,
'max' => 100,
//'unit' => '%',
'default' => '100%',
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'width'
),
'border_radius_css' => array(
'input_type' => 'border_radius',
'title' => __( 'Rounded corners', 'text_doma' ),
'default' => array( '_all_' => '0px' ),
'width-100' => true,
'title_width' => 'width-100',
'min' => 0,
'max' => 500,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'border_radius'
//'css_selectors'=> ''
),
'h_alignment_css' => array(
'input_type' => 'horizAlignmentWithDeviceSwitcher',
'title' => __('Alignment', 'text_doma'),
'default' => array( 'desktop' => 'center' ),
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_selectors' => '.sek-module-inner',
'css_identifier' => 'h_alignment',
'title_width' => 'width-100',
'width-100' => true,
),
'v_spacing_css' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __('Space before and after', 'text_doma'),
'min' => 1,
'max' => 100,
'default' => array( 'desktop' => '15px' ),
'width-100' => true,
'title_width' => 'width-100',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'v_spacing'
),
)
),
'render_tmpl_path' => "divider_module_tmpl.php",
);
}
?>
@@ -0,0 +1,302 @@
<?php
/* ------------------------------------------------------------------------- *
* LOAD AND REGISTER ICON MODULE
/* ------------------------------------------------------------------------- */
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_czr_icon_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_icon_module',
'is_father' => true,
'children' => array(
'icon_settings' => 'czr_icon_settings_child',
'spacing_border' => 'czr_icon_spacing_border_child'
),
'name' => __('Icon', 'text_doma'),
'starting_value' => array(
'icon_settings' => array(
'icon' => 'far fa-star',
'font_size_css' => '40px',
'color_css' => '#707070',
'color_hover' => '#969696'
)
),
// 'sanitize_callback' => '\Nimble\sanitize_callback__czr_icon_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'css_selectors' => array( '.sek-icon-wrapper' ),//array( '.sek-icon i' ),
'render_tmpl_path' => "icon_module_tmpl.php",
// Nimble will "sniff" if we need font awesome
// 'front_assets' => array(
// 'czr-font-awesome' => array(
// 'type' => 'css',
// //'handle' => 'czr-font-awesome',
// 'src' => NIMBLE_BASE_URL . '/assets/front/fonts/css/fontawesome-all.min.css'
// //'deps' => array()
// )
// )
);
}
/* ------------------------------------------------------------------------- *
* MAIN ICON SETTINGS : ICON, SIZE, COLOR, LINK
/* ------------------------------------------------------------------------- */
function sek_get_module_params_for_czr_icon_settings_child() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_icon_settings_child',
'name' => __( 'Icon settings', 'text_doma' ),
//'sanitize_callback' => '\Nimble\sanitize_callback__czr_simple_form_module',
// 'starting_value' => array(
// 'button_text' => __('Click me','text_doma'),
// 'color_css' => '#ffffff',
// 'bg_color_css' => '#020202',
// 'bg_color_hover' => '#151515', //lighten 15%,
// 'use_custom_bg_color_on_hover' => 0,
// 'border_radius_css' => '2',
// 'h_alignment_css' => 'center',
// 'use_box_shadow' => 1,
// 'push_effect' => 1
// ),
'css_selectors' => array( '.sek-icon-wrapper' ),//array( '.sek-icon i' ),
'tmpl' => array(
'item-inputs' => array(
'icon' => array(
'input_type' => 'fa_icon_picker',
'title' => __('Select an Icon', 'text_doma'),
//'default' => 'no-link'
),
'link-to' => array(
'input_type' => 'simpleselect',
'title' => __('Link to', 'text_doma'),
'default' => 'no-link',
'choices' => sek_get_select_options_for_input_id( 'link-to' )
),
'link-pick-url' => array(
'input_type' => 'content_picker',
'title' => __('Link url', 'text_doma'),
'default' => array()
),
'link-custom-url' => array(
'input_type' => 'text',
'title' => __('Custom link url', 'text_doma'),
'default' => ''
),
'link-target' => array(
'input_type' => 'nimblecheck',
'title' => __('Open link in a new browser tab', 'text_doma'),
'default' => false,
'title_width' => 'width-80',
'input_width' => 'width-20',
),
'font_size_css' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __('Size', 'text_doma'),
'default' => array( 'desktop' => '16px' ),
'min' => 0,
'max' => 100,
'title_width' => 'width-100',
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'font_size'
),
'h_alignment_css' => array(
'input_type' => 'horizAlignmentWithDeviceSwitcher',
'title' => __('Alignment', 'text_doma'),
'default' => array( 'desktop' => 'center' ),
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'h_alignment',
'css_selectors' => '.sek-icon',
'title_width' => 'width-100',
'width-100' => true,
),
'color_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __('Color', 'text_doma'),
'width-100' => true,
'default' => '#707070',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'color'
),
'use_custom_color_on_hover' => array(
'input_type' => 'nimblecheck',
'title' => __( 'Set a custom icon color on mouse hover', 'text_doma' ),
'title_width' => 'width-80',
'input_width' => 'width-20',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'default' => 0,
),
'color_hover' => array(
'input_type' => 'wp_color_alpha',
'title' => __('Hover color', 'text_doma'),
'width-100' => true,
'default' => '#969696',
'refresh_markup' => false,
'refresh_stylesheet' => true,
//'css_identifier' => 'color_hover'
)
)
),
'render_tmpl_path' => '',
);
}
/* ------------------------------------------------------------------------- *
* ICON SPACING BORDER SHADOW
/* ------------------------------------------------------------------------- */
function sek_get_module_params_for_czr_icon_spacing_border_child() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_icon_spacing_border_child',
'name' => __( 'Icon options for background, spacing, border, shadow', 'text_doma' ),
//'sanitize_callback' => '\Nimble\sanitize_callback__czr_simple_form_module',
// 'starting_value' => array(
// 'button_text' => __('Click me','text_doma'),
// 'color_css' => '#ffffff',
// 'bg_color_css' => '#020202',
// 'bg_color_hover' => '#151515', //lighten 15%,
// 'use_custom_bg_color_on_hover' => 0,
// 'border_radius_css' => '2',
// 'h_alignment_css' => 'center',
// 'use_box_shadow' => 1,
// 'push_effect' => 1
// ),
'css_selectors' => array( '.sek-icon-wrapper' ),//array( '.sek-icon i' ),
'tmpl' => array(
'item-inputs' => array(
'spacing_css' => array(
'input_type' => 'spacingWithDeviceSwitcher',
'title' => __( 'Spacing', 'text_doma' ),
'default' => array( 'desktop' => array() ),
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'spacing_with_device_switcher',
// 'css_selectors'=> '.sek-icon i'
),
'bg_color_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __( 'Background color', 'text_doma' ),
'width-100' => true,
'title_width' => 'width-100',
'default' => '',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'background_color',
// 'css_selectors'=> '.sek-icon i'
),
'border-type' => array(
'input_type' => 'simpleselect',
'title' => __('Border', 'text_doma'),
'default' => 'none',
'choices' => sek_get_select_options_for_input_id( 'border-type' ),
'refresh_markup' => false,
'refresh_stylesheet' => true
),
'borders' => array(
'input_type' => 'borders',
'title' => __('Borders', 'text_doma'),
'min' => 0,
'max' => 100,
'default' => array(
'_all_' => array( 'wght' => '1px', 'col' => '#000000' )
),
'refresh_markup' => false,
'refresh_stylesheet' => true,
'width-100' => true,
'title_width' => 'width-100',
// 'css_selectors'=> '.sek-icon i'
),
'border_radius_css' => array(
'input_type' => 'border_radius',
'title' => __( 'Rounded corners', 'text_doma' ),
'default' => array( '_all_' => '0px' ),
'width-100' => true,
'title_width' => 'width-100',
'min' => 0,
'max' => 500,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'border_radius',
// 'css_selectors'=> '.sek-icon i'
),
'use_box_shadow' => array(
'input_type' => 'nimblecheck',
'title' => __( 'Apply a shadow', 'text_doma' ),
'default' => 0,
),
)
),
'render_tmpl_path' => '',
);
}
/* ------------------------------------------------------------------------- *
* SCHEDULE CSS RULES FILTERING
/* ------------------------------------------------------------------------- */
add_filter( 'sek_add_css_rules_for_module_type___czr_icon_module', '\Nimble\sek_add_css_rules_for_icon_front_module', 10, 2 );
// filter documented in Sek_Dyn_CSS_Builder::sek_css_rules_sniffer_walker
// Note : $complete_modul_model has been normalized
// @return populated $rules
function sek_add_css_rules_for_icon_front_module( $rules, $complete_modul_model ) {
if ( empty( $complete_modul_model['value'] ) )
return $rules;
$value = $complete_modul_model['value'];
$icon_settings = $value['icon_settings'];
// COLOR ON HOVER
$icon_color = $icon_settings['color_css'];
if ( sek_booleanize_checkbox_val( $icon_settings['use_custom_color_on_hover'] ) ) {
$color_hover = $icon_settings['color_hover'];
$rules[] = array(
'selector' => '[data-sek-id="'.$complete_modul_model['id'].'"] .sek-icon i:hover',
'css_rules' => 'color:' . $color_hover . ';',
'mq' =>null
);
}
// BORDERS
$border_settings = $value[ 'spacing_border' ][ 'borders' ];
$border_type = $value[ 'spacing_border' ][ 'border-type' ];
$has_border_settings = 'none' != $border_type && !empty( $border_type );
//border width + type + color
if ( $has_border_settings ) {
$rules = sek_generate_css_rules_for_multidimensional_border_options(
$rules,
$border_settings,
$border_type,
'[data-sek-id="'.$complete_modul_model['id'].'"] .sek-icon-wrapper'
);
}
return $rules;
}
?>
@@ -0,0 +1,66 @@
<?php
/* ------------------------------------------------------------------------- *
* LOAD AND REGISTER MAP MODULE
/* ------------------------------------------------------------------------- */
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_czr_map_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_map_module',
'name' => __('Map', 'text_doma'),
// 'sanitize_callback' => '\Nimble\sanitize_callback__czr_gmap_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
//'css_selectors' => array( '.sek-module-inner' ),
'starting_value' => array(
'address' => 'Nice, France',
'zoom' => 10,
'height_css' => '200px'
),
'tmpl' => array(
'item-inputs' => array(
'address' => array(
'input_type' => 'text',
'title' => __( 'Address', 'text_doma'),
'width-100' => true,
'default' => '',
),
'zoom' => array(
'input_type' => 'range_simple',
'title' => __( 'Zoom', 'text_doma' ),
'min' => 1,
'max' => 20,
'unit' => '',
'default' => 10,
'width-100' => true
),
'height_css' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __( 'Height', 'text_doma' ),
'min' => 1,
'max' => 600,
'default' => array( 'desktop' => '200px' ),
'width-100' => true,
'title_width' => 'width-100',
'css_selectors' => array( '.sek-embed::before' ),
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'height'
),
'lazyload' => array(
'input_type' => 'nimblecheck',
'title' => __('Lazy load', 'text_doma'),
'default' => true,
'title_width' => 'width-80',
'input_width' => 'width-20',
'notice_after' => sprintf('%1$s <br/><strong>%2$s</strong>',
__( 'With the lazy load option enabled, Nimble loads the map when it becomes visible while scrolling. This improves your page load performances.', 'text_dom'),
__( 'If you use a cache plugin, make sure that this option does not conflict with your caching options.', 'text_dom')
),
)
)
),
'render_tmpl_path' => "map_module_tmpl.php",
);
}
?>
@@ -0,0 +1,539 @@
<?php
/* ------------------------------------------------------------------------- *
* LOAD AND REGISTER QUOTE MODULE
/* ------------------------------------------------------------------------- */
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_czr_quote_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_quote_module',
'is_father' => true,
'children' => array(
'quote_content' => 'czr_quote_quote_child',
'cite_content' => 'czr_quote_cite_child',
'design' => 'czr_quote_design_child'
),
'name' => __('Quote', 'text_doma' ),
'sanitize_callback' => __NAMESPACE__ . '\sanitize_callback__czr_quote_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'starting_value' => array(
'quote_content' => array(
'quote_text' => __('Hey, careful, man, there\'s a beverage here!','text_doma'),
),
'cite_content' => array(
'cite_text' => sprintf( __('The Dude in %1s', 'text_doma'), '<a href="https://www.imdb.com/title/tt0118715/quotes/qt0464770" rel="nofollow noopener noreferrer" target="_blank">The Big Lebowski</a>' ),
'cite_font_style_css' => 'italic',
),
'design' => array(
'quote_design' => 'border-before'
)
),
'css_selectors' => array( '.sek-module-inner' ),
'render_tmpl_path' => "quote_module_tmpl.php",
// Nimble will "sniff" if we need font awesome
// No need to enqueue font awesome here
// 'front_assets' => array(
// 'czr-font-awesome' => array(
// 'type' => 'css',
// 'src' => NIMBLE_BASE_URL . '/assets/front/fonts/css/fontawesome-all.min.css'
// )
// )
);
}
/* ------------------------------------------------------------------------- *
* QUOTE CONTENT AND FONT
/* ------------------------------------------------------------------------- */
function sek_get_module_params_for_czr_quote_quote_child() {
$quote_font_selectors = array( '.sek-quote .sek-quote-content', '.sek-quote .sek-quote-content *');
return array(
'dynamic_registration' => true,
'module_type' => 'czr_quote_quote_child',
'name' => __( 'Quote content', 'text_doma' ),
//'sanitize_callback' => '\Nimble\sanitize_callback__czr_simple_form_module',
//'css_selectors' =>'',
'tmpl' => array(
'item-inputs' => array(
'quote_text' => array(
'input_type' => 'nimble_tinymce_editor',
'editor_params' => array(
'media_button' => false,
'includedBtns' => 'basic_btns',
),
'title' => __( 'Main quote content', 'text_doma' ),
'default' => '',
'width-100' => true,
//'notice_before' => __( 'You may use some html tags like a, br,p, div, span with attributes like style, id, class ...', 'text_doma'),
'refresh_markup' => '.sek-quote-content'
),
'quote_font_family_css' => array(
'input_type' => 'font_picker',
'title' => __( 'Font family', 'text_doma' ),
'default' => '',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'refresh_fonts' => true,
'css_identifier' => 'font_family',
'css_selectors' => $quote_font_selectors,
'html_after' => sprintf('<span class="czr-notice"><i class="far fa-lightbulb"></i> %1s => <a href="%2s" target="_blank" rel="noopener noreferrer">%2$s</a></span><hr/>',
__('Find inspiration'),
'https://fonts.google.com/?sort=popularity'
)
),
'quote_font_size_css' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __( 'Font size', 'text_doma' ),
'default' => array( 'desktop' => '1.2em' ),
'min' => 0,
'max' => 100,
'width-100' => true,
'title_width' => 'width-100',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'font_size',
'css_selectors' => $quote_font_selectors,
),//16,//"14px",
'quote_line_height_css' => array(
'input_type' => 'range_with_unit_picker',
'title' => __( 'Line height', 'text_doma' ),
'default' => '1.5em',
'min' => 0,
'max' => 10,
'step' => 0.1,
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'line_height',
'css_selectors' => $quote_font_selectors,
),//24,//"20px",
'quote_color_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __( 'Text color', 'text_doma' ),
'default' => '',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'width-100' => true,
'css_identifier' => 'color',
'css_selectors' => $quote_font_selectors,
),//"#000000",
'quote_color_hover_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __( 'Text color on mouse over', 'text_doma' ),
'default' => '',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'width-100' => true,
'title_width' => 'width-100',
'css_identifier' => 'color_hover',
'css_selectors' => $quote_font_selectors,
),//"#000000",
'quote_font_weight_css' => array(
'input_type' => 'simpleselect',
'title' => __( 'Font weight', 'text_doma' ),
'default' => 400,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'font_weight',
'css_selectors' => $quote_font_selectors,
'choices' => sek_get_select_options_for_input_id( 'font_weight_css' )
),//null,
'quote_font_style_css' => array(
'input_type' => 'simpleselect',
'title' => __( 'Font style', 'text_doma' ),
'default' => 'inherit',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'font_style',
'css_selectors' => $quote_font_selectors,
'choices' => sek_get_select_options_for_input_id( 'font_style_css' )
),//null,
'quote_text_decoration_css' => array(
'input_type' => 'simpleselect',
'title' => __( 'Text decoration', 'text_doma' ),
'default' => 'none',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'text_decoration',
'css_selectors' => $quote_font_selectors,
'choices' => sek_get_select_options_for_input_id( 'text_decoration_css' )
),//null,
'quote_text_transform_css' => array(
'input_type' => 'simpleselect',
'title' => __( 'Text transform', 'text_doma' ),
'default' => 'none',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'text_transform',
'css_selectors' => $quote_font_selectors,
'choices' => sek_get_select_options_for_input_id( 'text_transform_css' )
),//null,
'quote_letter_spacing_css' => array(
'input_type' => 'range_simple',
'title' => __( 'Letter spacing', 'text_doma' ),
'default' => 0,
'min' => 0,
'step' => 1,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'letter_spacing',
'css_selectors' => $quote_font_selectors,
'width-100' => true,
),//0,
// Note : always use the suffix '_flag_important' to name an input controling the !important css flag @see Nimble\sek_add_css_rules_for_css_sniffed_input_id
'quote___flag_important' => array(
'input_type' => 'nimblecheck',
'title' => __( 'Make those style options win if other rules are applied.', 'text_doma' ),
'default' => 0,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'title_width' => 'width-80',
'input_width' => 'width-20',
// declare the list of input_id that will be flagged with !important when the option is checked
// @see sek_add_css_rules_for_css_sniffed_input_id
// @see sek_is_flagged_important
'important_input_list' => array(
'quote_font_family_css',
'quote_font_size_css',
'quote_line_height_css',
'quote_font_weight_css',
'quote_font_style_css',
'quote_text_decoration_css',
'quote_text_transform_css',
'quote_letter_spacing_css',
'quote_color_css',
'quote_color_hover_css'
)
)
)
),
'render_tmpl_path' => '',
);
}
/* ------------------------------------------------------------------------- *
* CITE CONTENT AND FONT
/* ------------------------------------------------------------------------- */
function sek_get_module_params_for_czr_quote_cite_child() {
$cite_font_selectors = array( '.sek-cite', '.sek-cite *');
return array(
'dynamic_registration' => true,
'module_type' => 'czr_quote_cite_child',
'name' => __( 'Cite content', 'text_doma' ),
//'sanitize_callback' => '\Nimble\sanitize_callback__czr_simple_form_module',
//'css_selectors' =>'',
'tmpl' => array(
'item-inputs' => array(
'cite_text' => array(
'input_type' => 'nimble_tinymce_editor',
'editor_params' => array(
'media_button' => false,
'includedBtns' => 'basic_btns',
'height' => 50
),
'refresh_markup' => '.sek-cite',
'title' => __( 'Cite text', 'text_doma' ),
'default' => '',
'width-100' => true,
//'notice_before' => __( 'You may use some html tags like a, br, span with attributes like style, id, class ...', 'text_doma'),
),
'cite_font_family_css' => array(
'input_type' => 'font_picker',
'title' => __( 'Font family', 'text_doma' ),
'default' => '',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'refresh_fonts' => true,
'css_identifier' => 'font_family',
'css_selectors' => $cite_font_selectors,
'html_after' => sprintf('<span class="czr-notice"><i class="far fa-lightbulb"></i> %1s => <a href="%2s" target="_blank" rel="noopener noreferrer">%2$s</a></span><hr/>',
__('Find inspiration'),
'https://fonts.google.com/?sort=popularity'
)
),
'cite_font_size_css' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __( 'Font size', 'text_doma' ),
'default' => array( 'desktop' => '14px' ),
'min' => 0,
'max' => 100,
'width-100' => true,
'title_width' => 'width-100',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'font_size',
'css_selectors' => $cite_font_selectors,
),//16,//"14px",
'cite_line_height_css' => array(
'input_type' => 'range_with_unit_picker',
'title' => __( 'Line height', 'text_doma' ),
'default' => '1.5em',
'min' => 0,
'max' => 10,
'step' => 0.1,
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'line_height',
'css_selectors' => $cite_font_selectors,
),//24,//"20px",
'cite_color_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __( 'Text color', 'text_doma' ),
'default' => '',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'width-100' => true,
'css_identifier' => 'color',
'css_selectors' => $cite_font_selectors,
),//"#000000",
'cite_color_hover_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __( 'Text color on mouse over', 'text_doma' ),
'default' => '',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'width-100' => true,
'title_width' => 'width-100',
'css_identifier' => 'color_hover',
'css_selectors' => $cite_font_selectors,
),//"#000000",
'cite_font_weight_css' => array(
'input_type' => 'simpleselect',
'title' => __( 'Font weight', 'text_doma' ),
'default' => 'normal',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'font_weight',
'css_selectors' => $cite_font_selectors,
'choices' => sek_get_select_options_for_input_id( 'font_weight_css' )
),//null,
'cite_font_style_css' => array(
'input_type' => 'simpleselect',
'title' => __( 'Font style', 'text_doma' ),
'default' => 'inherit',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'font_style',
'css_selectors' => $cite_font_selectors,
'choices' => sek_get_select_options_for_input_id( 'font_style_css' )
),//null,
'cite_text_decoration_css' => array(
'input_type' => 'simpleselect',
'title' => __( 'Text decoration', 'text_doma' ),
'default' => 'none',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'text_decoration',
'css_selectors' => $cite_font_selectors,
'choices' => sek_get_select_options_for_input_id( 'text_decoration_css' )
),//null,
'cite_text_transform_css' => array(
'input_type' => 'simpleselect',
'title' => __( 'Text transform', 'text_doma' ),
'default' => 'none',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'text_transform',
'css_selectors' => $cite_font_selectors,
'choices' => sek_get_select_options_for_input_id( 'text_transform_css' )
),//null,
'cite_letter_spacing_css' => array(
'input_type' => 'range_simple',
'title' => __( 'Letter spacing', 'text_doma' ),
'default' => 0,
'min' => 0,
'step' => 1,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'letter_spacing',
'css_selectors' => $cite_font_selectors,
'width-100' => true,
),//0,
// Note : always use the suffix '_flag_important' to name an input controling the !important css flag @see Nimble\sek_add_css_rules_for_css_sniffed_input_id
'cite___flag_important' => array(
'input_type' => 'nimblecheck',
'title' => __( 'Make those style options win if other rules are applied.', 'text_doma' ),
'default' => 0,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'title_width' => 'width-80',
'input_width' => 'width-20',
// declare the list of input_id that will be flagged with !important when the option is checked
// @see sek_add_css_rules_for_css_sniffed_input_id
// @see Nsek_is_flagged_important
'important_input_list' => array(
'cite_font_family_css',
'cite_font_size_css',
'cite_line_height_css',
'cite_font_weight_css',
'cite_font_style_css',
'cite_text_decoration_css',
'cite_text_transform_css',
'cite_letter_spacing_css',
'cite_color_css',
'cite_color_hover_css'
)
)
)
),
'render_tmpl_path' => '',
);
}
/* ------------------------------------------------------------------------- *
* DESIGN
/* ------------------------------------------------------------------------- */
function sek_get_module_params_for_czr_quote_design_child() {
$cite_font_selectors = array( '.sek-quote-design .sek-cite', '.sek-quote-design .sek-cite a' );
return array(
'dynamic_registration' => true,
'module_type' => 'czr_quote_design_child',
'name' => __( 'Design', 'text_doma' ),
//'sanitize_callback' => '\Nimble\sanitize_callback__czr_simple_form_module',
//'css_selectors' =>'',
'tmpl' => array(
'item-inputs' => array(
'quote_design' => array(
'input_type' => 'simpleselect',
'title' => __( 'Design', 'text_doma' ),
'default' => 'none',
'choices' => sek_get_select_options_for_input_id( 'quote_design' )
),
'border_width_css' => array(
'input_type' => 'range_with_unit_picker',
'title' => __( 'Border weight', 'text_doma' ),
'min' => 1,
'max' => 80,
'default' => '5px',
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'border_width',
'css_selectors' => '.sek-quote.sek-quote-design.sek-border-before'
),
'border_color_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __( 'Border Color', 'text_doma' ),
'width-100' => true,
'default' => 'rgba(0,0,0,0.1)',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'border_color',
'css_selectors' => '.sek-quote.sek-quote-design.sek-border-before'
),
'icon_size_css' => array(
'input_type' => 'range_with_unit_picker',
'title' => __( 'Icon Size', 'text_doma' ),
'default' => '50px',
'min' => 0,
'max' => 100,
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'font_size',
'css_selectors' => array( '.sek-quote.sek-quote-design.sek-quote-icon-before::before' )
),
'icon_color_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __( 'Icon Color', 'text_doma' ),
'width-100' => true,
'default' => '#ccc',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'color',
'css_selectors' => '.sek-quote.sek-quote-design.sek-quote-icon-before::before'
)
)
),
'render_tmpl_path' => '',
);
}
/* ------------------------------------------------------------------------- *
* SCHEDULE CSS RULES FILTERING
/* ------------------------------------------------------------------------- */
add_filter( 'sek_add_css_rules_for_module_type___czr_quote_module', '\Nimble\sek_add_css_rules_for_czr_quote_module', 10, 2 );
// filter documented in Sek_Dyn_CSS_Builder::sek_css_rules_sniffer_walker
// Note : $complete_modul_model has been normalized
// @return populated $rules
function sek_add_css_rules_for_czr_quote_module( $rules, $complete_modul_model ) {
if ( empty( $complete_modul_model['value'] ) )
return $rules;
// BACKGROUND
$value = $complete_modul_model['value'];
$design_settings = $value['design'];
if ( 'quote-icon-before' === $design_settings['quote_design'] && '50px' !== $design_settings['icon_size_css'] ) {
if ( is_rtl() ) {
$css_rule = sprintf('padding-right: calc( 10px + 0.7 * %1$s )', $design_settings['icon_size_css']);
} else {
$css_rule = sprintf('padding-left: calc( 10px + 0.7 * %1$s )', $design_settings['icon_size_css']);
}
$rules[] = array(
'selector' => '[data-sek-id="'.$complete_modul_model['id'].'"] .sek-module-inner .sek-quote-icon-before .sek-quote-inner',
'css_rules' => $css_rule,
'mq' =>null
);
}
return $rules;
}
/* ------------------------------------------------------------------------- *
* SANITIZATION
/* ------------------------------------------------------------------------- */
// convert into a json to prevent emoji breaking global json data structure
// fix for https://github.com/presscustomizr/nimble-builder/issues/544
function sanitize_callback__czr_quote_module( $value ) {
if ( !is_array( $value ) )
return $value;
if ( array_key_exists( 'quote_content', $value ) && is_array( $value['quote_content'] ) && !empty($value['quote_content']['quote_text']) ) {
//sanitize quote_text
if ( !current_user_can( 'unfiltered_html' ) ) {
$value['quote_content']['quote_text'] = wp_kses_post( $value['quote_content']['quote_text'] );
}
// convert into a json to prevent emoji breaking global json data structure
// fix for https://github.com/presscustomizr/nimble-builder/issues/544
$value['quote_content']['quote_text'] = sek_maybe_encode_richtext($value['quote_content']['quote_text']);
}
if ( array_key_exists( 'cite_content', $value ) && is_array( $value['cite_content'] ) && !empty($value['cite_content']['cite_text']) ) {
//sanitize quote_text
if ( !current_user_can( 'unfiltered_html' ) ) {
$value['cite_content']['cite_text'] = wp_kses_post( $value['cite_content']['cite_text'] );
}
// convert into a json to prevent emoji breaking global json data structure
// fix for https://github.com/presscustomizr/nimble-builder/issues/544
$value['cite_content']['cite_text'] = sek_maybe_encode_richtext($value['cite_content']['cite_text']);
}
return $value;
}
?>
@@ -0,0 +1,443 @@
<?php
/* ------------------------------------------------------------------------- *
* LOAD AND REGISTER BUTTON MODULE
/* ------------------------------------------------------------------------- */
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_czr_button_module() {
$css_selectors = '.sek-btn';
$css_font_selectors = '.sek-btn';
return array(
'dynamic_registration' => true,
'module_type' => 'czr_button_module',
'is_father' => true,
'children' => array(
'content' => 'czr_btn_content_child',
'design' => 'czr_btn_design_child',
'font' => 'czr_font_child'
),
'name' => __( 'Button', 'text_doma' ),
'sanitize_callback' => '\Nimble\sanitize_callback__czr_button_module',
'starting_value' => array(
'content' => array(
'button_text' => __('Click me','text_doma'),
),
'design' => array(
'bg_color_css' => '#020202',
'bg_color_hover' => '#151515', //lighten 15%,
'use_custom_bg_color_on_hover' => 0,
'border_radius_css' => '2',
'h_alignment_css' => 'center',
'use_box_shadow' => 1,
'push_effect' => 1,
),
'font' => array(
'color_css' => '#ffffff',
)
),
'css_selectors' => array( '.sek-module-inner .sek-btn' ),
'render_tmpl_path' => "button_module_tmpl.php"
);
}
/* ------------------------------------------------------------------------- *
* BUTTON CONTENT
/* ------------------------------------------------------------------------- */
function sek_get_module_params_for_czr_btn_content_child() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_btn_content_child',
'name' => __( 'Button content', 'text_doma' ),
//'sanitize_callback' => '\Nimble\sanitize_callback__czr_simple_form_module',
//'css_selectors' =>'',
'tmpl' => array(
'item-inputs' => array(
'button_text' => array(
'input_type' => 'nimble_tinymce_editor',
'editor_params' => array(
'media_button' => false,
'includedBtns' => 'basic_btns_nolink',
'height' => 45
),
'title' => __( 'Button text', 'text_doma' ),
'default' => '',
'width-100' => true,
'refresh_markup' => '.sek-btn-text'
),
'btn_text_on_hover' => array(
'input_type' => 'text',
'title' => __( 'Tooltip text on mouse hover', 'text_doma' ),
'default' => '',
'width-100' => true,
'title_width' => 'width-100',
'notice_after' => __( 'Not previewable when customizing.', 'text_doma')
),
'link-to' => array(
'input_type' => 'simpleselect',
'title' => __('Link to', 'text_doma'),
'default' => 'no-link',
'choices' => sek_get_select_options_for_input_id( 'link-to' )
),
'link-pick-url' => array(
'input_type' => 'content_picker',
'title' => __('Link url', 'text_doma'),
'default' => array()
),
'link-custom-url' => array(
'input_type' => 'text',
'title' => __('Custom link url', 'text_doma'),
'default' => ''
),
'link-target' => array(
'input_type' => 'nimblecheck',
'title' => __('Open link in a new browser tab', 'text_doma'),
'default' => false,
'title_width' => 'width-80',
'input_width' => 'width-20',
),
'icon' => array(
'input_type' => 'fa_icon_picker',
'title' => __( 'Icon next to the button text', 'text_doma' ),
//'default' => 'no-link'
),
'icon-side' => array(
'input_type' => 'buttons_choice',
'title' => __("Icon's position", 'text_doma'),
'default' => 'left',
'choices' => array( 'left' => __('Left', 'text-domain'), 'right' => __('Right', 'text-domain') )
),
)
),
'render_tmpl_path' => '',
);
}
/* ------------------------------------------------------------------------- *
* BUTTON DESIGN
/* ------------------------------------------------------------------------- */
function sek_get_module_params_for_czr_btn_design_child() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_btn_design_child',
'name' => __( 'Button design', 'text_doma' ),
//'sanitize_callback' => '\Nimble\sanitize_callback__czr_simple_form_module',
//'css_selectors' =>'',
'tmpl' => array(
'item-inputs' => array(
'bg_color_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __( 'Background color', 'text_doma' ),
'width-100' => true,
'default' => '#020202',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'background_color',
//'css_selectors'=> $css_selectors
),
'use_custom_bg_color_on_hover' => array(
'input_type' => 'nimblecheck',
'title' => __( 'Set a custom background color on mouse hover', 'text_doma' ),
'title_width' => 'width-80',
'input_width' => 'width-20',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'default' => 0,
),
'bg_color_hover' => array(
'input_type' => 'wp_color_alpha',
'title' => __( 'Background color on mouse hover', 'text_doma' ),
'width-100' => true,
'default' => '',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'notice_after' => __( 'You can also customize the text color on mouseover in the group of text settings below.', 'text_doma')
//'css_identifier' => 'background_color_hover',
//'css_selectors'=> $css_selectors
),
'border-type' => array(
'input_type' => 'simpleselect',
'title' => __('Border', 'text_doma'),
'default' => 'none',
'choices' => sek_get_select_options_for_input_id( 'border-type' ),
'refresh_markup' => false,
'refresh_stylesheet' => true
),
'borders' => array(
'input_type' => 'borders',
'title' => __('Borders', 'text_doma'),
'min' => 0,
'max' => 100,
'default' => array(
'_all_' => array( 'wght' => '1px', 'col' => '#000000' )
),
'refresh_markup' => false,
'refresh_stylesheet' => true,
'width-100' => true,
'title_width' => 'width-100',
'css_selectors'=> '.sek-icon i'
),
'border_radius_css' => array(
'input_type' => 'border_radius',
'title' => __( 'Rounded corners', 'text_doma' ),
'default' => array( '_all_' => '0px' ),
'width-100' => true,
'title_width' => 'width-100',
'min' => 0,
'max' => 500,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'border_radius',
//'css_selectors'=> $css_selectors
),
'h_alignment_css' => array(
'input_type' => 'horizAlignmentWithDeviceSwitcher',
'title' => __( 'Button alignment', 'text_doma' ),
'default' => array( 'desktop' => 'center' ),
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'h_alignment',
'css_selectors' => '.sek-module-inner',
'title_width' => 'width-100',
'width-100' => true,
),
'spacing_css' => array(
'input_type' => 'spacing',
'title' => __( 'Spacing', 'text_doma' ),
'default' => array(
'padding-top' => .5,
'padding-bottom' => .5,
'padding-right' => 1,
'padding-left' => 1,
'margin-top' => .5,
'margin-bottom' => .5,
'unit' => 'em'
),
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'padding_margin_spacing',
'css_selectors'=> '.sek-module-inner .sek-btn'
),
'use_box_shadow' => array(
'input_type' => 'nimblecheck',
'title' => __( 'Apply a shadow', 'text_doma' ),
'default' => 1,
'title_width' => 'width-80',
'input_width' => 'width-20',
),
'push_effect' => array(
'input_type' => 'nimblecheck',
'title' => __( 'Push visual effect', 'text_doma' ),
'default' => 1,
'title_width' => 'width-80',
'input_width' => 'width-20',
),
'width-type' => array(
'input_type' => 'simpleselect',
'title' => __('Width : auto or custom', 'text_doma'),
'default' => 'default',
'choices' => sek_get_select_options_for_input_id( 'height-type' ),
'html_before' => '<hr/>',
'refresh_markup' => false,
'refresh_stylesheet' => true,
),
'custom-width' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __('Custom width', 'text_doma'),
'min' => 0,
'max' => 500,
'default' => array( 'desktop' => '150px' ),
'width-100' => true,
'title_width' => 'width-100',
'refresh_markup' => false,
'refresh_stylesheet' => true,
),
'h_inner_align_css' => array(
'input_type' => 'horizTextAlignmentWithDeviceSwitcher',
'title' => __( 'Text alignment', 'text_doma' ),
'default' => array( 'desktop' => 'center' ),
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'h_alignment',
'css_selectors' => '.sek-btn .sek-btn-text',
'title_width' => 'width-100',
'width-100' => true,
),
'height-type' => array(
'input_type' => 'simpleselect',
'title' => __('Height : auto or custom', 'text_doma'),
'default' => 'default',
'choices' => sek_get_select_options_for_input_id( 'height-type' ),
'html_before' => '<hr/>',
'refresh_markup' => false,
'refresh_stylesheet' => true,
),
'custom-height' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __('Custom height', 'text_doma'),
'min' => 0,
'max' => 500,
'default' => array( 'desktop' => '40px' ),
'width-100' => true,
'title_width' => 'width-100',
'refresh_markup' => false,
'refresh_stylesheet' => true,
),
)
),
'render_tmpl_path' => '',
);
}
/* ------------------------------------------------------------------------- *
* SANITIZATION
/* ------------------------------------------------------------------------- */
// convert into a json to prevent emoji breaking global json data structure
// fix for https://github.com/presscustomizr/nimble-builder/issues/544
function sanitize_callback__czr_button_module( $value ) {
if ( is_array( $value ) && !empty($value['content']) && is_array( $value['content'] ) && array_key_exists( 'button_text', $value['content'] ) ) {
//$value['content'][ 'button_text' ] = sanitize_text_field( $value['content'][ 'button_text' ] );
// convert into a json to prevent emoji breaking global json data structure
// fix for https://github.com/presscustomizr/nimble-builder/issues/544
$value['content']['button_text'] = sek_maybe_encode_richtext($value['content']['button_text']);
}
return $value;
}
/* ------------------------------------------------------------------------- *
* SCHEDULE CSS RULES FILTERING
/* ------------------------------------------------------------------------- */
add_filter( 'sek_add_css_rules_for_module_type___czr_button_module', '\Nimble\sek_add_css_rules_for_button_front_module', 10, 2 );
// filter documented in Sek_Dyn_CSS_Builder::sek_css_rules_sniffer_walker
// Note : $complete_modul_model has been normalized
// @return populated $rules
function sek_add_css_rules_for_button_front_module( $rules, $complete_modul_model ) {
if ( empty( $complete_modul_model['value'] ) )
return $rules;
// BACKGROUND
$value = $complete_modul_model['value'];
$design_settings = $value['design'];
$bg_color = $design_settings['bg_color_css'];
if ( sek_booleanize_checkbox_val( $design_settings['use_custom_bg_color_on_hover'] ) ) {
$bg_color_hover = $design_settings['bg_color_hover'];
} else {
// Build the lighter rgb from the user picked bg color
if ( 0 === strpos( $bg_color, 'rgba' ) ) {
list( $rgb, $alpha ) = sek_rgba2rgb_a( $bg_color );
$bg_color_hover_rgb = sek_lighten_rgb( $rgb, $percent=15, $array = true );
$bg_color_hover = sek_rgb2rgba( $bg_color_hover_rgb, $alpha, $array = false, $make_prop_value = true );
} else if ( 0 === strpos( $bg_color, 'rgb' ) ) {
$bg_color_hover = sek_lighten_rgb( $bg_color, $percent=15 );
} else {
$bg_color_hover = sek_lighten_hex( $bg_color, $percent=15 );
}
}
$rules[] = array(
'selector' => '.nb-loc .sek-row [data-sek-id="'.$complete_modul_model['id'].'"] .sek-module-inner .sek-btn:hover, .nb-loc .sek-row [data-sek-id="'.$complete_modul_model['id'].'"] .sek-module-inner .sek-btn:focus',
'css_rules' => 'background-color:' . $bg_color_hover . ';',
'mq' =>null
);
// BORDERS
$border_settings = $design_settings[ 'borders' ];
$border_type = $design_settings[ 'border-type' ];
$has_border_settings = 'none' != $border_type && !empty( $border_type );
//border width + type + color
if ( $has_border_settings ) {
$rules = sek_generate_css_rules_for_multidimensional_border_options(
$rules,
$border_settings,
$border_type,
'[data-sek-id="'.$complete_modul_model['id'].'"] .sek-module-inner .sek-btn'
);
}
// CUSTOM WIDTH BY DEVICE
if ( !empty( $design_settings[ 'width-type' ] ) ) {
if ( 'custom' == $design_settings[ 'width-type' ] && array_key_exists( 'custom-width', $design_settings ) ) {
$user_custom_width_value = $design_settings[ 'custom-width' ];
if ( !empty( $user_custom_width_value ) && !is_array( $user_custom_width_value ) ) {
sek_error_log( __FUNCTION__ . ' => error => the width option should be an array( {device} => {number}{unit} )');
}
$user_custom_width_value = is_array( $user_custom_width_value ) ? $user_custom_width_value : array();
$user_custom_width_value = wp_parse_args( $user_custom_width_value, array(
'desktop' => '100%',
'tablet' => '',
'mobile' => ''
));
$width_value = $user_custom_width_value;
foreach ( $user_custom_width_value as $device => $num_unit ) {
$numeric = sek_extract_numeric_value( $num_unit );
if ( !empty( $numeric ) ) {
$unit = sek_extract_unit( $num_unit );
$width_value[$device] = $numeric . $unit;
}
}
$rules = sek_set_mq_css_rules(array(
'value' => $width_value,
'css_property' => 'width',
'selector' => '[data-sek-id="'.$complete_modul_model['id'].'"] .sek-module-inner .sek-btn',
'level_id' => $complete_modul_model['id']
), $rules );
}
}
// CUSTOM HEIGHT BY DEVICE
if ( !empty( $design_settings[ 'height-type' ] ) ) {
if ( 'custom' === $design_settings[ 'height-type' ] ) {
$custom_user_height = array_key_exists( 'custom-height', $design_settings ) ? $design_settings[ 'custom-height' ] : array();
if ( !is_array( $custom_user_height ) ) {
sek_error_log( __FUNCTION__ . ' => error => the height option should be an array( {device} => {number}{unit} )', $custom_user_height);
}
$custom_user_height = is_array( $custom_user_height ) ? $custom_user_height : array();
$custom_user_height = wp_parse_args( $custom_user_height, array(
'desktop' => '40px',//<= consistent with default
'tablet' => '',
'mobile' => ''
));
$height_value = $custom_user_height;
foreach ( $custom_user_height as $device => $num_unit ) {
$numeric = sek_extract_numeric_value( $num_unit );
if ( !empty( $numeric ) ) {
$unit = sek_extract_unit( $num_unit );
$unit = '%' === $unit ? 'vh' : $unit;
$height_value[$device] = $numeric . $unit;
}
}
$rules = sek_set_mq_css_rules(array(
'value' => $height_value,
'css_property' => 'height',
'selector' => '[data-sek-id="'.$complete_modul_model['id'].'"] .sek-module-inner .sek-btn',
'level_id' => $complete_modul_model['id']
), $rules );
}
}
return $rules;
}
?>
@@ -0,0 +1,128 @@
<?php
/* ------------------------------------------------------------------------- *
* LOAD AND REGISTER MENU MODULE
/* ------------------------------------------------------------------------- */
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_czr_menu_module() {
$css_selectors = '.sek-btn';
$css_font_selectors = '.sek-btn';
return array(
'dynamic_registration' => true,
'module_type' => 'czr_menu_module',
'is_father' => true,
'children' => array(
'content' => 'czr_menu_content_child',
//'design' => 'czr_menu_design_child',
'font' => 'czr_font_child',
'mobile_options' => 'czr_menu_mobile_options'
),
'name' => __( 'Menu', 'text_doma' ),
'sanitize_callback' => '\Nimble\sanitize_callback__czr_button_module',
'starting_value' => array(
// 'content' => array(
// 'button_text' => __('Click me','text_doma'),
// ),
// 'design' => array(
// 'bg_color_css' => '#020202',
// 'bg_color_hover' => '#151515', //lighten 15%,
// 'use_custom_bg_color_on_hover' => 0,
// 'border_radius_css' => '2',
// 'h_alignment_css' => 'center',
// 'use_box_shadow' => 1,
// 'push_effect' => 1,
// ),
// 'font' => array(
// 'color_css' => '#ffffff',
// )
),
'css_selectors' => array( '.sek-menu-module li > a', '.nb-search-expand-inner input', '[data-sek-is-mobile-vertical-menu="yes"] .nb-mobile-search input', '.nb-arrow-for-mobile-menu' ),//<=@see tmpl/modules/menu_module_tmpl.php
'render_tmpl_path' => "menu_module_tmpl.php"
);
}
/* ------------------------------------------------------------------------- *
* MENU CONTENT
/* ------------------------------------------------------------------------- */
function sek_get_module_params_for_czr_menu_content_child() {
$pro_text = '';
if ( !sek_is_pro() ) {
$pro_text = sek_get_pro_notice_for_czr_input( __('search icon next to the menu, sticky header, hamburger color, ...', 'text-doma') );
}
return array(
'dynamic_registration' => true,
'module_type' => 'czr_menu_content_child',
'name' => __( 'Menu content', 'text_doma' ),
//'sanitize_callback' => '\Nimble\sanitize_callback__czr_simple_form_module',
//'css_selectors' =>'',
'tmpl' => array(
'item-inputs' => array(
'menu-id' => array(
'input_type' => 'simpleselect',
'title' => __('Select a menu', 'text_doma'),
'default' => 'no-link',
'choices' => sek_get_user_created_menus(),
'notice_after' => sprintf( __( 'You can create and edit menus in the %1$s. If you just created a new menu, publish and refresh the customizer to see in the dropdown list.', 'text_doma'),
sprintf( '<a href="#" onclick="%1$s">%2$s</a>',
"javascript:wp.customize.panel('nav_menus', function( _p_ ){ _p_.focus(); })",
__('menu panel', 'text_doma')
)
),
),
// alignment of items on desktops devices ( when items are horizontal ), is controled with selector .sek-nav-collapse
// janv 2021 : alignement of menu items in the vertical mobile mnenu with '[data-sek-is-mobile-vertical-menu="yes"] .sek-nav li a'
'h_alignment_css' => array(
'input_type' => 'horizAlignmentWithDeviceSwitcher',
'title' => __('Menu items alignment', 'text_doma'),
'default' => array( 'desktop' => 'right', 'tablet' => 'left' ),
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'h_flex_alignment',
'css_selectors' => array( '.sek-nav-collapse', '[data-sek-is-mobile-vertical-menu="yes"] .sek-nav li a' ),
'title_width' => 'width-100',
'width-100' => true,
),
'hamb_h_alignment_css' => array(
'input_type' => 'horizAlignmentWithDeviceSwitcher',
'title' => __('Hamburger button alignment', 'text_doma'),
'default' => array( 'desktop' => 'center' ),
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'h_flex_alignment',
'css_selectors' => array( '.sek-nav-wrap' ),
'title_width' => 'width-100',
'width-100' => true,
'html_after' => $pro_text
),
),
),
'render_tmpl_path' => '',
);
}
/* ------------------------------------------------------------------------- *
* MOBILE OPTIONS
/* ------------------------------------------------------------------------- */
function sek_get_module_params_for_czr_menu_mobile_options() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_menu_mobile_options',
'name' => __( 'Settings for mobile devices', 'text_doma' ),
//'sanitize_callback' => '\Nimble\sanitize_callback__czr_simple_form_module',
//'css_selectors' =>'',
'tmpl' => array(
'item-inputs' => array(
'expand_below' => array(
'input_type' => 'nimblecheck',
'title' => sprintf('%s %s', '<i class="material-icons sek-level-option-icon">devices</i>', __('On mobile devices, expand the menu in full width below the menu hamburger icon.', 'text_doma') ),
'default' => true,
'title_width' => 'width-80',
'input_width' => 'width-20'
),
),
),
'render_tmpl_path' => '',
);
}
?>
@@ -0,0 +1,161 @@
<?php
/* ------------------------------------------------------------------------- *
* GENERIC FONT CHILD MODULE
/* ------------------------------------------------------------------------- */
function sek_get_module_params_for_czr_font_child() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_font_child',
'name' => __( 'Text settings : font, color, size, ...', 'text_doma' ),
//'sanitize_callback' => '\Nimble\sanitize_callback__czr_simple_form_module',
// 'starting_value' => array(
// 'button_text' => __('Click me','text_doma'),
// 'color_css' => '#ffffff',
// 'bg_color_css' => '#020202',
// 'bg_color_hover' => '#151515', //lighten 15%,
// 'use_custom_bg_color_on_hover' => 0,
// 'border_radius_css' => '2',
// 'h_alignment_css' => 'center',
// 'use_box_shadow' => 1,
// 'push_effect' => 1
// ),
//'css_selectors' => '',
'tmpl' => array(
'item-inputs' => array(
'font_family_css' => array(
'input_type' => 'font_picker',
'title' => __('Font family', 'text_doma'),
'default' => '',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'refresh_fonts' => true,
'css_identifier' => 'font_family',
'html_after' => sprintf('<span class="czr-notice"><i class="far fa-lightbulb"></i> %1s => <a href="%2s" target="_blank" rel="noopener noreferrer">%2$s</a></span><hr/>',
__('Find inspiration'),
'https://fonts.google.com/?sort=popularity'
)
),
'font_size_css' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __( 'Font size', 'text_doma' ),
// the default value is commented to fix https://github.com/presscustomizr/nimble-builder/issues/313
// => as a consequence, when a module uses the font child module, the default font-size rule must be defined in the module SCSS file.
//'default' => array( 'desktop' => '16px' ),
'min' => 0,
'max' => 100,
'title_width' => 'width-100',
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'font_size'
),//16,//"14px",
'line_height_css' => array(
'input_type' => 'range_with_unit_picker',
'title' => __( 'Line height', 'text_doma' ),
'default' => '1.5em',
'min' => 0,
'max' => 10,
'step' => 0.1,
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'line_height'
),//24,//"20px",
'color_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __('Text color', 'text_doma'),
'default' => '',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'width-100' => true,
'css_identifier' => 'color'
),//"#000000",
'color_hover_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __('Text color on mouse over', 'text_doma'),
'default' => '',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'width-100' => true,
'title_width' => 'width-100',
'css_identifier' => 'color_hover'
),//"#000000",
'font_weight_css' => array(
'input_type' => 'simpleselect',
'title' => __('Font weight', 'text_doma'),
'default' => 400,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'font_weight',
'choices' => sek_get_select_options_for_input_id( 'font_weight_css' )
),//null,
'font_style_css' => array(
'input_type' => 'simpleselect',
'title' => __('Font style', 'text_doma'),
'default' => 'inherit',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'font_style',
'choices' => sek_get_select_options_for_input_id( 'font_style_css' )
),//null,
'text_decoration_css' => array(
'input_type' => 'simpleselect',
'title' => __('Text decoration', 'text_doma'),
'default' => 'none',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'text_decoration',
'choices' => sek_get_select_options_for_input_id( 'text_decoration_css' )
),//null,
'text_transform_css' => array(
'input_type' => 'simpleselect',
'title' => __('Text transform', 'text_doma'),
'default' => 'none',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'text_transform',
'choices' => sek_get_select_options_for_input_id( 'text_transform_css' )
),//null,
'letter_spacing_css' => array(
'input_type' => 'range_simple',
'title' => __( 'Letter spacing', 'text_doma' ),
'default' => 0,
'min' => 0,
'step' => 1,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'letter_spacing',
'width-100' => true,
),//0,
// Note : always use the suffix '_flag_important' to name an input controling the !important css flag @see Nimble\sek_add_css_rules_for_css_sniffed_input_id
'fonts___flag_important' => array(
'input_type' => 'nimblecheck',
'title' => __('Apply the style options in priority (uses !important).', 'text_doma'),
'default' => 0,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'title_width' => 'width-80',
'input_width' => 'width-20',
// declare the list of input_id that will be flagged with !important when the option is checked
// @see sek_add_css_rules_for_css_sniffed_input_id
// @see Nsek_is_flagged_important
'important_input_list' => array(
'font_family_css',
'font_size_css',
'line_height_css',
'font_weight_css',
'font_style_css',
'text_decoration_css',
'text_transform_css',
'letter_spacing_css',
'color_css',
'color_hover_css'
)
)
)
),
'render_tmpl_path' => '',
);
}
?>
@@ -0,0 +1,36 @@
<?php
/* ------------------------------------------------------------------------- *
* LOAD AND REGISTER WIDGET ZONE MODULE
/* ------------------------------------------------------------------------- */
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_czr_widget_area_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_widget_area_module',
'name' => __('Widget Zone', 'text_doma'),
//'css_selectors' => array( '.sek-module-inner > *' ),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'widget-area-id' => array(
'input_type' => 'simpleselect',
'title' => __('Select a widget area', 'text_doma'),
'default' => 'no-link',
'choices' => array(),
'refresh_preview' => true,// <= so that the partial refresh links are displayed
'html_before' => '<span class="czr-notice">' . __('This module allows you to embed any WordPress widgets in your Nimble sections.', 'text_doma') . '<br/>' . __('1) Select a widget area in the dropdown list,', 'text_doma') . '<br/>' . sprintf( __( '2) once selected an area, you can add and edit the WordPress widgets in it in the %1$s.', 'text_doma'),
sprintf( '<a href="#" onclick="%1$s"><strong>%2$s</strong></a>',
"javascript:wp.customize.panel('widgets', function( _p_ ){ _p_.focus(); })",
__('widget panel', 'text_doma')
)
) . '</span><br/>'
)
)
),
'render_tmpl_path' => "widget_area_module_tmpl.php",
);
}
?>
@@ -0,0 +1,657 @@
<?php
/* ------------------------------------------------------------------------- *
* LOAD AND REGISTER IMG SLIDER MODULE
/* ------------------------------------------------------------------------- */
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_czr_img_slider_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_img_slider_module',
'is_father' => true,
'children' => array(
'img_collection' => 'czr_img_slider_collection_child',
'slider_options' => 'czr_img_slider_opts_child'
),
'name' => __('Image & Text Carousel', 'text_doma'),
'starting_value' => array(
'img_collection' => array(
array( 'img' => NIMBLE_BASE_URL . '/assets/img/default-img.png' ),
array( 'img' => NIMBLE_BASE_URL . '/assets/img/default-img.png' ),
array( 'img' => NIMBLE_BASE_URL . '/assets/img/default-img.png' )
)
),
'sanitize_callback' => '\Nimble\sanitize_cb__czr_img_slider_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'css_selectors' => array( '[data-sek-swiper-id]' ),//array( '.sek-icon i' ),
'render_tmpl_path' => "img_slider_tmpl.php",
// 'front_assets' => array(
// 'czr-font-awesome' => array(
// 'type' => 'css',
// //'handle' => 'czr-font-awesome',
// 'src' => NIMBLE_BASE_URL . '/assets/front/fonts/css/fontawesome-all.min.css'
// //'deps' => array()
// )
// )
);
}
/* ------------------------------------------------------------------------- *
* SANITIZATION
/* ------------------------------------------------------------------------- */
// convert into a json to prevent emoji breaking global json data structure
// fix for https://github.com/presscustomizr/nimble-builder/issues/544
function sanitize_cb__czr_img_slider_module( $value ) {
if ( !is_array( $value ) )
return $value;
if ( !empty($value['img_collection']) && is_array( $value['img_collection'] ) ) {
foreach( $value['img_collection'] as $key => $data ) {
if ( array_key_exists( 'text_content', $data ) && is_string( $data['text_content'] ) ) {
$value['img_collection'][$key]['text_content'] = sek_maybe_encode_richtext( $data['text_content'] );
}
}
}
return $value;
}
/* ------------------------------------------------------------------------- *
* MAIN SETTINGS
/* ------------------------------------------------------------------------- */
function sek_get_module_params_for_czr_img_slider_collection_child() {
$text_content_selector = array( '.sek-slider-text-content', '.sek-slider-text-content *' );
$pro_text = '';
if ( !sek_is_pro() ) {
$pro_text = __( 'add transition options with effects like fade or flip, link slides individually to any content.', 'text-doma');
$pro_text = sek_get_pro_notice_for_czr_input( $pro_text );
}
return array(
'dynamic_registration' => true,
'module_type' => 'czr_img_slider_collection_child',
'is_crud' => true,
'name' => sprintf('<i class="material-icons" style="font-size: 1.2em;">photo_library</i> %1$s', __( 'Slide collection', 'text_doma' ) ),
'starting_value' => array(
'img' => NIMBLE_BASE_URL . '/assets/img/default-img.png'
),
//'sanitize_callback' => '\Nimble\sanitize_callback__czr_simple_form_module',
//'css_selectors' => array( '.sek-social-icon' ),//array( '.sek-icon i' ),
'tmpl' => array(
'pre-item' => array(
// 'page-id' => array(
// 'input_type' => 'content_picker',
// 'title' => __('Pick a page', 'text_doma')
// ),
'img' => array(
'input_type' => 'upload',
'title' => __('Pick an image', 'text_doma'),
'default' => ''
),
),
'item-inputs' => array(
'tabs' => array(
array(
'title' => __( 'Image', 'text_doma' ),
'inputs' => array(
'img' => array(
'input_type' => 'upload',
'title' => __('Pick an image', 'text_doma'),
'default' => ''
),
'img-size' => array(
'input_type' => 'simpleselect',
'title' => __('Select the image size', 'text_doma'),
'default' => 'large',
'choices' => sek_get_select_options_for_input_id( 'img-size' ),
'notice_before' => __('Select a size for this image among those generated by WordPress.', 'text_doma' )
),
'title_attr' => array(
'input_type' => 'text',
'default' => '',
'title' => __('Title', 'text_domain_to_be_replaced'),
'notice_after' => sprintf( __('This is the text displayed on mouse over. You can use the following template tags referring to the image attributes : %1$s', 'text_domain_to_be_replaced'), '&#123;&#123;title&#125;&#125;, &#123;&#123;caption&#125;&#125;, &#123;&#123;description&#125;&#125;' ),
'html_after' => $pro_text
)
)
),
array(
'title' => __( 'Text', 'text_doma' ),
'inputs' => array(
'enable_text' => array(
'input_type' => 'nimblecheck',
'title' => __('Add text content', 'text_doma'),
'default' => false,
'title_width' => 'width-80',
'input_width' => 'width-20',
'notice_after' => __('Note : you can adjust the text color and / or use a color overlay to improve accessibility of your text content.', 'text_doma')
),
'text_content' => array(
'input_type' => 'nimble_tinymce_editor',
'editor_params' => array(
'media_button' => false,
'includedBtns' => 'basic_btns',
),
'title' => __( 'Text content', 'text_doma' ),
'default' => '',
'width-100' => true,
'refresh_markup' => '.sek-slider-text-content',
'notice_before' => sprintf( __('You may use some html tags in the "text" tab of the editor. You can also use the following template tags referring to the image attributes : %1$s', 'text_domain_to_be_replaced'), '&#123;&#123;title&#125;&#125;, &#123;&#123;caption&#125;&#125;, &#123;&#123;description&#125;&#125;' )
),
'color_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __( 'Text color', 'text_doma' ),
'default' => '#e2e2e2',// why this light grey ? => if set to white ( #fff ), the text is not visible when no image is picked, which might be difficult to understand for users
'refresh_markup' => false,
'refresh_stylesheet' => true,
'width-100' => true,
'css_identifier' => 'color',
'css_selectors' => $text_content_selector,
),//"#000000",
'font_family_css' => array(
'input_type' => 'font_picker',
'title' => __( 'Font family', 'text_doma' ),
'default' => '',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'refresh_fonts' => true,
'css_identifier' => 'font_family',
'css_selectors' => $text_content_selector,
'html_before' => '<hr/><h3>' . __('FONT OPTIONS') .'</h3>',
'html_after' => sprintf('<span class="czr-notice"><i class="far fa-lightbulb"></i> %1s => <a href="%2s" target="_blank" rel="noopener noreferrer">%2$s</a></span><hr/>',
__('Find inspiration'),
'https://fonts.google.com/?sort=popularity'
)
),
'font_size_css' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'default' => array( 'desktop' => '16px' ),
'title_width' => 'width-100',
'title' => __( 'Font size', 'text_doma' ),
'min' => 0,
'max' => 100,
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'font_size',
'css_selectors' => $text_content_selector,
),//16,//"14px",
'line_height_css' => array(
'input_type' => 'range_with_unit_picker',
'title' => __( 'Line height', 'text_doma' ),
'default' => '1.5em',
'min' => 0,
'max' => 10,
'step' => 0.1,
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'line_height',
'css_selectors' => $text_content_selector,
),//24,//"20px",
'h_alignment_css' => array(
'input_type' => 'horizTextAlignmentWithDeviceSwitcher',
'title' => __('Horizontal alignment', 'text_doma'),
'default' => array( 'desktop' => 'center'),
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'h_alignment',
'title_width' => 'width-100',
'width-100' => true,
'css_selectors' => array( '.sek-slider-text-content' ),
'html_before' => '<hr/><h3>' . __('ALIGNMENTS') .'</h3>'
),
'v_alignment' => array(
'input_type' => 'verticalAlignWithDeviceSwitcher',
'title' => __('Vertical alignment', 'text_doma'),
'default' => array( 'desktop' => 'center' ),
'refresh_markup' => false,
'refresh_stylesheet' => true,
//'css_identifier' => 'v_alignment',
'title_width' => 'width-100',
'width-100' => true,
),
'spacing_css' => array(
'input_type' => 'spacingWithDeviceSwitcher',
'title' => __( 'Spacing of the text content', 'text_doma' ),
'default' => array('desktop' => array(
'padding-bottom' => '5',
'padding-top' => '5',
'padding-right' => '5',
'padding-left' => '5',
'unit' => '%')
),//consistent with SCSS
'title_width' => 'width-100',
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'spacing_with_device_switcher',
'css_selectors' => array( '.sek-slider-text-content' ),
'html_before' => '<hr/><h3>' . __('SPACING') .'</h3>'
)
)
),
array(
'title' => __( 'Color overlay', 'text_doma' ),
'inputs' => array(
'apply-overlay' => array(
'input_type' => 'nimblecheck',
'notice_after' => __('A color overlay is usually recommended when displaying text content on top of the image. You can customize the color and transparency in the global design settings of the carousel.', 'text_doma' ),
'title' => __('Apply a color overlay', 'text_doma'),
'default' => false,
'title_width' => 'width-80',
'input_width' => 'width-20',
'html_before' => '<hr/><h3>' . __('COLOR OVERLAY') .'</h3>'
),
'color-overlay' => array(
'input_type' => 'wp_color_alpha',
'title' => __('Overlay Color', 'text_doma'),
'width-100' => true,
'default' => '#000000',
'refresh_markup' => false,
'refresh_stylesheet' => true
),
'opacity-overlay' => array(
'input_type' => 'range_simple',
'title' => __('Opacity (in percents)', 'text_doma'),
'orientation' => 'horizontal',
'min' => 0,
'max' => 100,
// 'unit' => '%',
'default' => '30',
'width-100' => true,
'title_width' => 'width-100',
'refresh_markup' => false,
'refresh_stylesheet' => true
)
)
)
)//'tabs'
)//'item-inputs'
),
'render_tmpl_path' => '',
);
}
/* ------------------------------------------------------------------------- *
* SLIDER OPTIONS
/* ------------------------------------------------------------------------- */
function sek_get_module_params_for_czr_img_slider_opts_child() {
$pro_text = '';
if ( !sek_is_pro() ) {
$pro_text = __( 'add transition options with effects like fade and flip, link slides individually to any content.', 'text-doma');
$pro_text = sek_get_pro_notice_for_czr_input( $pro_text );
}
return array(
'dynamic_registration' => true,
'module_type' => 'czr_img_slider_opts_child',
'name' => sprintf('<i class="material-icons" style="font-size: 1.2em;">tune</i> %1$s', __( 'Slider options : height, autoplay, navigation...', 'text_doma' ) ),
//'sanitize_callback' => '\Nimble\sanitize_callback__czr_simple_form_module',
// 'starting_value' => array(
// 'button_text' => __('Click me','text_doma'),
// 'color_css' => '#ffffff',
// 'bg_color_css' => '#020202',
// 'bg_color_hover' => '#151515', //lighten 15%,
// 'use_custom_bg_color_on_hover' => 0,
// 'border_radius_css' => '2',
// 'h_alignment_css' => 'center',
// 'use_box_shadow' => 1,
// 'push_effect' => 1
// ),
//'css_selectors' => array( '.sek-social-icons-wrapper' ),//array( '.sek-icon i' ),
'tmpl' => array(
'item-inputs' => array(
'tabs' => array(
array(
'title' => __( 'General', 'text_doma' ),
'inputs' => array(
'image-layout' => array(
'input_type' => 'simpleselect',
'title' => __('Image layout', 'text_doma'),
'default' => 'width-100',
'choices' => array(
'nimble-wizard' => __('Nimble wizard', 'text_doma' ),
'cover' => __('Images fill space and are centered without being stretched', 'text_doma'),
'width-100' => __('Adapt images to carousel\'s width', 'text_doma' ),
'height-100' => __('Adapt images to carousel\'s height', 'text_doma' ),
),
'title_width' => 'width-100',
'width-100' => true,
'notice_before' => __('Nimble wizard ensures that the images fill all available space of the carousel in any devices, without blank spaces on the edges, and without stretching the images.', 'text_doma' ),
),
'autoplay' => array(
'input_type' => 'nimblecheck',
'title' => __('Autoplay', 'text_doma'),
'default' => false,
'title_width' => 'width-80',
'input_width' => 'width-20',
'notice_after' => __('Note that the autoplay is disabled during customization.', 'text_doma' ),
),
'autoplay_delay' => array(
'input_type' => 'range_simple',
'title' => __( 'Delay between each slide in milliseconds (ms)', 'text_doma' ),
'min' => 1,
'max' => 30000,
'step' => 500,
'unit' => '',
'default' => 3000,
'width-100' => true,
'title_width' => 'width-100'
),
'pause_on_hover' => array(
'input_type' => 'nimblecheck',
'title' => __('Pause autoplay on mouse over', 'text_doma'),
'default' => true,
'title_width' => 'width-80',
'input_width' => 'width-20'
),
'infinite_loop' => array(
'input_type' => 'nimblecheck',
'title' => __('Infinite loop', 'text_doma'),
'default' => true,
'title_width' => 'width-80',
'input_width' => 'width-20'
),
// added dec 2019 for https://github.com/presscustomizr/nimble-builder/issues/570
'lazy_load' => array(
'input_type' => 'nimblecheck',
'title' => __('Lazy load images', 'text_doma'),
'default' => true,
'title_width' => 'width-80',
'input_width' => 'width-20',
'notice_after' => __('Lazy loading images improves page load performances.', 'text_doma' ),
'html_before' => $pro_text
),
'bg_color_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __( 'Background color', 'text_doma' ),
'width-100' => true,
'title_width' => 'width-100',
'default' => '',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'background_color',
'css_selectors'=> '.swiper-slide'
)
)//inputs
),
array(
'title' => __( 'Height', 'text_doma' ),
'inputs' => array(
'height-type' => array(
'input_type' => 'simpleselect',
'title' => __('Height : auto or custom', 'text_doma'),
'default' => 'custom',
'choices' => sek_get_select_options_for_input_id( 'height-type' ),// auto, custom
'refresh_markup' => false,
'refresh_stylesheet' => true,
'html_before' => '<hr/><h3>' . __('SLIDER HEIGHT') .'</h3>'
),
'custom-height' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __('Custom height', 'text_doma'),
'min' => 0,
'max' => 1000,
'default' => array( 'desktop' => '400px', 'mobile' => '200px' ),
'width-100' => true,
'title_width' => 'width-100',
'refresh_markup' => false,
'refresh_stylesheet' => true,
)
)
),
array(
'title' => __( 'Navigation', 'text_doma' ),
'inputs' => array(
'nav_type' => array(
'input_type' => 'simpleselect',
'title_width' => 'width-100',
'width-100' => true,
'default' => 'arrows',
'choices' => array(
'arrows_dots' => __('Arrows and bullets', 'text_doma'),
'arrows' => __('Arrows only', 'text_doma'),
'dots' => __('Bullets only', 'text_doma'),
'none' => __('None', 'text_doma')
),
'html_before' => '<hr/><h3>' . __('NAVIGATION') .'</h3>'
),
'hide_nav_on_mobiles' => array(
'input_type' => 'nimblecheck',
'title' => __('Hide arrows and bullets on mobiles', 'text_doma'),
'default' => false,
'title_width' => 'width-80',
'input_width' => 'width-20'
),
// 'arrows_size' => array(
// 'input_type' => 'range_simple_device_switcher',
// 'title' => __( 'Size of the arrows', 'text_doma' ),
// 'default' => array( 'desktop' => '18'),
// 'min' => 1,
// 'max' => 50,
// 'step' => 1,
// 'width-100' => true,
// 'title_width' => 'width-100'
// ),//null,
'arrows_color_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __('Color of the navigation arrows', 'text_doma'),
'width-100' => true,
'title_width' => 'width-100',
'default' => '#ffffff',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'border_color',
'css_selectors' => array('.sek-swiper-nav .sek-swiper-arrows .sek-chevron')
),
// 'dots_size' => array(
// 'input_type' => 'range_simple_device_switcher',
// 'title' => __( 'Size of the dots', 'text_doma' ),
// 'default' => array( 'desktop' => '16'),
// 'min' => 1,
// 'max' => 50,
// 'step' => 1,
// 'width-100' => true,
// 'title_width' => 'width-100'
// ),//null,
'dots_color_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __('Color of the active pagination bullet', 'text_doma'),
'width-100' => true,
'title_width' => 'width-100',
'default' => '#ffffff',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'background_color',
'css_selectors' => array('.swiper-pagination-bullet-active')
),
)//inputs
)
)//tabs
)
),
'render_tmpl_path' => '',
);
}
/* ------------------------------------------------------------------------- *
* SCHEDULE CSS RULES FILTERING
/* ------------------------------------------------------------------------- */
// PER ITEM CSS DESIGN => FILTERING OF EACH ITEM MODEL, TARGETING THE ID ( [data-sek-item-id="893af157d5e3"] )
add_filter( 'sek_add_css_rules_for_single_item_in_module_type___czr_img_slider_collection_child', '\Nimble\sek_add_css_rules_for_items_in_czr_img_slider_collection_child', 10, 2 );
// filter documented in Sek_Dyn_CSS_Builder::sek_css_rules_sniffer_walker
// Note : $complete_modul_model has been normalized
// @return populated $rules
// @param $params
// Array
// (
// [input_list] => Array
// (
// [icon] => fab fa-acquisitions-incorporated
// [link] => https://twitter.com/home
// [title_attr] => Follow me on twitter
// [link_target] =>
// [color_css] => #dd9933
// [use_custom_color_on_hover] =>
// [social_color_hover] => #dd3333
// [id] => 62316ab99b4d
// )
// [parent_module_id] =>
// [module_type] => czr_img_slider_collection_child
// [module_css_selector] => Array
// (
// [0] => .sek-social-icon
// )
// )
function sek_add_css_rules_for_items_in_czr_img_slider_collection_child( $rules, $params ) {
// $item_input_list = wp_parse_args( $item_input_list, $default_value_model );
$item_model = isset( $params['input_list'] ) ? $params['input_list'] : array();
$all_defaults = sek_get_default_module_model( 'czr_img_slider_collection_child');
// Default :
// [v_alignment] => Array
// (
// [desktop] => center
// )
// VERTICAL ALIGNMENT
if ( !empty( $item_model[ 'v_alignment' ] ) && $all_defaults['v_alignment'] != $item_model[ 'v_alignment' ] ) {
if ( !is_array( $item_model[ 'v_alignment' ] ) ) {
sek_error_log( __FUNCTION__ . ' => error => the v_alignment option should be an array( {device} => {alignment} )');
}
$v_alignment_value = is_array( $item_model[ 'v_alignment' ] ) ? $item_model[ 'v_alignment' ] : array();
$v_alignment_value = wp_parse_args( $v_alignment_value, array(
'desktop' => 'center',
'tablet' => '',
'mobile' => ''
));
$mapped_values = array();
foreach ( $v_alignment_value as $device => $align_val ) {
switch ( $align_val ) {
case 'top' :
$mapped_values[$device] = "align-items:flex-start;-webkit-box-align:start;-ms-flex-align:start;";
break;
case 'center' :
$mapped_values[$device] = "align-items:center;-webkit-box-align:center;-ms-flex-align:center;";
break;
case 'bottom' :
$mapped_values[$device] = "align-items:flex-end;-webkit-box-align:end;-ms-flex-align:end";
break;
}
}
$rules = sek_set_mq_css_rules_supporting_vendor_prefixes( array(
'css_rules_by_device' => $mapped_values,
'selector' => sprintf( '[data-sek-id="%1$s"] [data-sek-item-id="%2$s"] .sek-slider-text-wrapper', $params['parent_module_id'], $item_model['id'] ),
'level_id' => $params['parent_module_id']
), $rules );
}//Vertical alignment
//Background overlay?
// 1) a background image should be set
// 2) the option should be checked
if ( sek_is_checked( $item_model[ 'apply-overlay'] ) ) {
//(needs validation: we need a sanitize hex or rgba color)
$bg_color_overlay = isset( $item_model[ 'color-overlay' ] ) ? $item_model[ 'color-overlay' ] : null;
if ( $bg_color_overlay ) {
//overlay pseudo element
$bg_overlay_css_rules = 'background-color:'.$bg_color_overlay;
//opacity
//validate/sanitize
$bg_overlay_opacity = isset( $item_model[ 'opacity-overlay' ] ) ? filter_var( $item_model[ 'opacity-overlay' ], FILTER_VALIDATE_INT, array( 'options' =>
array( "min_range"=>0, "max_range"=>100 ) )
) : FALSE;
$bg_overlay_opacity = FALSE !== $bg_overlay_opacity ? filter_var( $bg_overlay_opacity / 100, FILTER_VALIDATE_FLOAT ) : $bg_overlay_opacity;
$bg_overlay_css_rules = FALSE !== $bg_overlay_opacity ? $bg_overlay_css_rules . ';opacity:' . $bg_overlay_opacity . ';' : $bg_overlay_css_rules;
$rules[] = array(
'selector' => sprintf( '[data-sek-id="%1$s"] [data-sek-item-id="%2$s"][data-sek-has-overlay="true"] .sek-carousel-img::after', $params['parent_module_id'], $item_model['id'] ),
'css_rules' => $bg_overlay_css_rules,
'mq' =>null
);
}
}// BG Overlay
return $rules;
}
// GLOBAL CSS DESIGN => FILTERING OF THE ENTIRE MODULE MODEL
add_filter( 'sek_add_css_rules_for_module_type___czr_img_slider_module', '\Nimble\sek_add_css_rules_for_czr_img_slider_module', 10, 2 );
// filter documented in Sek_Dyn_CSS_Builder::sek_css_rules_sniffer_walker
// Note : $complete_modul_model has been normalized
// @return populated $rules
function sek_add_css_rules_for_czr_img_slider_module( $rules, $complete_modul_model ) {
if ( empty( $complete_modul_model['value'] ) || !is_array( $complete_modul_model['value'] ) )
return $rules;
$value = $complete_modul_model['value'];
$slider_options = $value['slider_options'];
$selector = '[data-sek-id="'.$complete_modul_model['id'].'"] .sek-module-inner .swiper .swiper-wrapper';
// CUSTOM HEIGHT BY DEVICE
if ( !empty( $slider_options[ 'height-type' ] ) ) {
if ( 'custom' === $slider_options[ 'height-type' ] ) {
$custom_user_height = array_key_exists( 'custom-height', $slider_options ) ? $slider_options[ 'custom-height' ] : array();
if ( !is_array( $custom_user_height ) ) {
sek_error_log( __FUNCTION__ . ' => error => the height option should be an array( {device} => {number}{unit} )', $custom_user_height);
}
$custom_user_height = is_array( $custom_user_height ) ? $custom_user_height : array();
// DEFAULTS :
// array(
// 'desktop' => '400px',
// 'tablet' => '',
// 'mobile' => '200px'
// );
$all_defaults = sek_get_default_module_model( 'czr_img_slider_module');
$slider_defaults = $all_defaults['slider_options'];
$defaults = $slider_defaults['custom-height'];
$custom_user_height = wp_parse_args( $custom_user_height, $defaults );
if ( $defaults != $custom_user_height ) {
$height_value = $custom_user_height;
foreach ( $custom_user_height as $device => $num_unit ) {
$numeric = sek_extract_numeric_value( $num_unit );
if ( !empty( $numeric ) ) {
$unit = sek_extract_unit( $num_unit );
$unit = '%' === $unit ? 'vh' : $unit;
$height_value[$device] = $numeric . $unit;
}
}
$rules = sek_set_mq_css_rules(array(
'value' => $height_value,
'css_property' => 'height',
'selector' => $selector,
'level_id' => $complete_modul_model['id']
), $rules );
}
}// if custom height
else {
$rules[] = array(
'selector' => $selector,
'css_rules' => 'height:auto;',
'mq' =>null
);
}
}// Custom height rules
return $rules;
}
?>
@@ -0,0 +1,537 @@
<?php
/* ------------------------------------------------------------------------- *
* LOAD AND REGISTER ACCORDION MODULE
/* ------------------------------------------------------------------------- */
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_czr_accordion_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_accordion_module',
'is_father' => true,
'children' => array(
'accord_collec' => 'czr_accordion_collection_child',
'accord_opts' => 'czr_accordion_opts_child'
),
'name' => __('Accordion', 'text_doma'),
'starting_value' => array(
'accord_collec' => array(
array('text_content' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor.'),
array('text_content' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor.'),
array('text_content' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor.')
)
),
'sanitize_callback' => '\Nimble\sanitize_cb__czr_accordion_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'css_selectors' => array( '[data-sek-accordion-id]' ),//array( '.sek-icon i' ),
'render_tmpl_path' => "accordion_tmpl.php",
// 'front_assets' => array(
// 'czr-font-awesome' => array(
// 'type' => 'css',
// //'handle' => 'czr-font-awesome',
// 'src' => NIMBLE_BASE_URL . '/assets/front/fonts/css/fontawesome-all.min.css'
// //'deps' => array()
// )
// )
);
}
/* ------------------------------------------------------------------------- *
* SANITIZATION
/* ------------------------------------------------------------------------- */
// convert into a json to prevent emoji breaking global json data structure
// fix for https://github.com/presscustomizr/nimble-builder/issues/544
function sanitize_cb__czr_accordion_module( $value ) {
if ( !is_array( $value ) )
return $value;
if ( !empty($value['accord_collec']) && is_array( $value['accord_collec'] ) ) {
foreach( $value['accord_collec'] as $key => $data ) {
if ( array_key_exists( 'text_content', $data ) && is_string( $data['text_content'] ) ) {
$value['accord_collec'][$key]['text_content'] = sek_maybe_encode_richtext( $data['text_content'] );
}
if ( array_key_exists( 'title_text', $data ) && is_string( $data['title_text'] ) ) {
$value['accord_collec'][$key]['title_text'] = sek_maybe_encode_richtext( $data['title_text'] );
}
}
}
return $value;
}
/* ------------------------------------------------------------------------- *
* MAIN SETTINGS
/* ------------------------------------------------------------------------- */
function sek_get_module_params_for_czr_accordion_collection_child() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_accordion_collection_child',
'is_crud' => true,
'name' => sprintf('<i class="material-icons" style="font-size: 1.2em;">toc</i> %1$s', __( 'Item collection', 'text_doma' ) ),
'starting_value' => array(
'text_content' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor.'
),
//'sanitize_callback' => '\Nimble\sanitize_callback__czr_simple_form_module',
//'css_selectors' => array( '.sek-social-icon' ),//array( '.sek-icon i' ),
'tmpl' => array(
'pre-item' => array(
// 'page-id' => array(
// 'input_type' => 'content_picker',
// 'title' => __('Pick a page', 'text_doma')
// ),
'img' => array(
'input_type' => 'upload',
'title' => __('Pick an image', 'text_doma'),
'default' => ''
),
),
'item-inputs' => array(
'tabs' => array(
array(
'title' => __( 'Title', 'text_doma' ),
'inputs' => array(
'title_text' => array(
'input_type' => 'nimble_tinymce_editor',
'editor_params' => array(
'media_button' => false,
'includedBtns' => 'basic_btns',
'height' => 50
),
'title' => __( 'Heading text', 'text_doma' ),
'default' => '',
'width-100' => true,
'refresh_markup' => '.sek-inner-accord-title',
'notice_before' => __( 'You may use some html tags like a, br, span with attributes like style, id, class ...', 'text_doma'),
),
'title_attr' => array(
'input_type' => 'text',
'default' => '',
'title' => __('Title on mouse over', 'text_domain_to_be_replaced'),
'notice_after' => __('This is the text displayed on mouse over.' )
),
)
),
array(
'title' => __( 'Content', 'text_doma' ),
'inputs' => array(
'text_content' => array(
'input_type' => 'nimble_tinymce_editor',
'editor_params' => array(
'media_button' => true,
'includedBtns' => 'basic_btns_with_lists',
),
'title' => __( 'Text content', 'text_doma' ),
'default' => '',
'width-100' => true,
'refresh_markup' => '.sek-accord-content',
'notice_before' => __('You may use some html tags in the "text" tab of the editor.', 'text_domain_to_be_replaced')
),
'h_alignment_css' => array(
'input_type' => 'horizTextAlignmentWithDeviceSwitcher',
'title' => __('Horizontal alignment', 'text_doma'),
'default' => array( 'desktop' => 'center'),
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'h_alignment',
'title_width' => 'width-100',
'width-100' => true,
'css_selectors' => array( '.sek-accord-content' )
)
)
),
)//'tabs'
)//'item-inputs'
),
'render_tmpl_path' => '',
);
}
/* ------------------------------------------------------------------------- *
* ACCORDION OPTIONS
/* ------------------------------------------------------------------------- */
function sek_get_module_params_for_czr_accordion_opts_child() {
$title_content_selector = array( '.sek-accord-item .sek-accord-title *' );
$main_content_selector = array( '.sek-accord-item .sek-accord-content', '.sek-accord-item .sek-accord-content *' );
return array(
'dynamic_registration' => true,
'module_type' => 'czr_accordion_opts_child',
'name' => sprintf('<i class="material-icons" style="font-size: 1.2em;">tune</i> %1$s', __( 'Accordion options : font style, borders, background, ...', 'text_doma' ) ),
//'sanitize_callback' => '\Nimble\sanitize_callback__czr_simple_form_module',
// 'starting_value' => array(
// 'button_text' => __('Click me','text_doma'),
// 'color_css' => '#ffffff',
// 'bg_color_css' => '#020202',
// 'bg_color_hover' => '#151515', //lighten 15%,
// 'use_custom_bg_color_on_hover' => 0,
// 'border_radius_css' => '2',
// 'h_alignment_css' => 'center',
// 'use_box_shadow' => 1,
// 'push_effect' => 1
// ),
//'css_selectors' => array( '.sek-social-icons-wrapper' ),//array( '.sek-icon i' ),
'tmpl' => array(
'item-inputs' => array(
'tabs' => array(
array(
'title' => __( 'General', 'text_doma' ),
'inputs' => array(
'first_expanded' => array(
'input_type' => 'nimblecheck',
'title' => __('Display first item expanded', 'text_doma'),
'default' => true,
'title_width' => 'width-80',
'input_width' => 'width-20'
),
'one_expanded' => array(
'input_type' => 'nimblecheck',
'title' => __('Display one item expanded at a time', 'text_doma'),
'default' => true,
'title_width' => 'width-80',
'input_width' => 'width-20'
),
'border_width_css' => array(
'input_type' => 'range_with_unit_picker',
'title' => __( 'Border weight', 'text_doma' ),
'min' => 0,
'max' => 80,
'default' => '1px',
'width-100' => true,
//'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'border_width',
'css_selectors' => '.sek-accord-wrapper .sek-accord-item',
'html_before' => '<hr/><h3>' . __('BORDER') .'</h3>'
),
'border_color_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __( 'Border color', 'text_doma' ),
'width-100' => true,
'default' => '#e3e3e3',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'border_color',
'css_selectors' => '.sek-accord-wrapper .sek-accord-item'
),
)//inputs
),
array(
'title' => __( 'Title style', 'text_doma' ),
'inputs' => array(
'title_bg_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __('Backround color', 'text_doma'),
'width-100' => true,
'title_width' => 'width-100',
'default' => '#ffffff',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'background_color',
'css_selectors' => '.sek-accord-wrapper .sek-accord-item .sek-accord-title',
'html_before' => '<h3>' . __('COLOR AND BACKGROUND') .'</h3>'
),
'color_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __( 'Text color', 'text_doma' ),
'default' => '#565656',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'width-100' => true,
'css_identifier' => 'color',
'css_selectors' => $title_content_selector
),//"#000000",
'color_active_css' => array(
'input_type' => 'wp_color_alpha',
'title_width' => 'width-100',
'title' => __( 'Text color when active', 'text_doma' ),
'default' => '#1e261f',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'width-100' => true,
'css_identifier' => 'color',
'css_selectors' => array( '.sek-accord-item .sek-accord-title:hover *', '[data-sek-expanded="true"] .sek-accord-title *')
),//"#000000",
'font_family_css' => array(
'input_type' => 'font_picker',
'title' => __( 'Font family', 'text_doma' ),
'default' => '',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'refresh_fonts' => true,
'css_identifier' => 'font_family',
'css_selectors' => $title_content_selector,
'html_before' => '<hr/><h3>' . __('FONT OPTIONS') .'</h3>',
'html_after' => sprintf('<span class="czr-notice"><i class="far fa-lightbulb"></i> %1s => <a href="%2s" target="_blank" rel="noopener noreferrer">%2$s</a></span><hr/>',
__('Find inspiration'),
'https://fonts.google.com/?sort=popularity'
)
),
'font_size_css' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'default' => array( 'desktop' => '16px' ),
'title_width' => 'width-100',
'title' => __( 'Font size', 'text_doma' ),
'min' => 0,
'max' => 100,
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'font_size',
'css_selectors' => $title_content_selector
),//16,//"14px",
'line_height_css' => array(
'input_type' => 'range_with_unit_picker',
'title' => __( 'Line height', 'text_doma' ),
'default' => '1.5em',
'min' => 0,
'max' => 10,
'step' => 0.1,
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'line_height',
'css_selectors' => $title_content_selector
),//24,//"20px",
'title_border_w_css' => array(
'input_type' => 'range_with_unit_picker',
'title' => __( 'Border bottom weight', 'text_doma' ),
'min' => 0,
'max' => 80,
'default' => '1px',
'title_width' => 'width-100',
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'border_width',
'css_selectors' => '.sek-accord-wrapper .sek-accord-item .sek-accord-title',
'html_before' => '<hr/><h3>' . __('BORDER BOTTOM') .'</h3>'
),
'title_border_c_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __( 'Border bottom color', 'text_doma' ),
'width-100' => true,
'default' => '#e3e3e3',
//'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'border_color',
'css_selectors' => '.sek-accord-wrapper .sek-accord-item .sek-accord-title'
),
'spacing_css' => array(
'input_type' => 'spacingWithDeviceSwitcher',
'title' => __( 'Spacing', 'text_doma' ),
'default' => array('desktop' => array('padding-top' => '15', 'padding-right' => '20', 'padding-left' => '20', 'padding-bottom' => '15', 'unit' => 'px')),//consistent with SCSS
'title_width' => 'width-100',
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'spacing_with_device_switcher',
'css_selectors' => '.sek-accord-item .sek-accord-title',
'html_before' => '<hr/><h3>' . __('SPACING') .'</h3>'
)
)
),
array(
'title' => __( 'Content style', 'text_doma' ),
'inputs' => array(
'ct_bg_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __('Backround color', 'text_doma'),
'width-100' => true,
'title_width' => 'width-100',
'default' => '#f2f2f2',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'background_color',
'css_selectors' => array('.sek-accord-item .sek-accord-content'),
'html_before' => '<h3>' . __('COLOR AND BACKGROUND') .'</h3>'
),
'ct_color_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __( 'Text color', 'text_doma' ),
'default' => '#1e261f',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'width-100' => true,
'css_identifier' => 'color',
'css_selectors' => $main_content_selector
),//"#000000",
'ct_font_family_css' => array(
'input_type' => 'font_picker',
'title' => __( 'Font family', 'text_doma' ),
'default' => '',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'refresh_fonts' => true,
'css_identifier' => 'font_family',
'css_selectors' => $main_content_selector,
'html_before' => '<hr/><h3>' . __('FONT OPTIONS') .'</h3>',
'html_after' => sprintf('<span class="czr-notice"><i class="far fa-lightbulb"></i> %1s => <a href="%2s" target="_blank" rel="noopener noreferrer">%2$s</a></span><hr/>',
__('Find inspiration'),
'https://fonts.google.com/?sort=popularity'
)
),
'ct_font_size_css' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'default' => array( 'desktop' => '16px' ),
'title_width' => 'width-100',
'title' => __( 'Font size', 'text_doma' ),
'min' => 0,
'max' => 100,
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'font_size',
'css_selectors' => $main_content_selector
),//16,//"14px",
'ct_line_height_css' => array(
'input_type' => 'range_with_unit_picker',
'title' => __( 'Line height', 'text_doma' ),
'default' => '1.5em',
'min' => 0,
'max' => 10,
'step' => 0.1,
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'line_height',
'css_selectors' => $main_content_selector
),//24,//"20px",
'ct_spacing_css' => array(
'input_type' => 'spacingWithDeviceSwitcher',
'title' => __( 'Spacing', 'text_doma' ),
'default' => array('desktop' => array('padding-top' => '15', 'padding-right' => '20', 'padding-left' => '20', 'padding-bottom' => '15', 'unit' => 'px')),//consistent with SCSS
'title_width' => 'width-100',
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'spacing_with_device_switcher',
'css_selectors' => '.sek-accord-item .sek-accord-content',
'html_before' => '<hr/><h3>' . __('SPACING') .'</h3>'
)
)//inputs
)
)//tabs
)
),
'render_tmpl_path' => '',
);
}
/* ------------------------------------------------------------------------- *
* SCHEDULE CSS RULES FILTERING
/* ------------------------------------------------------------------------- */
// PER ITEM CSS DESIGN => FILTERING OF EACH ITEM MODEL, TARGETING THE ID ( [data-sek-item-id="893af157d5e3"] )
//add_filter( 'sek_add_css_rules_for_single_item_in_module_type___czr_accordion_collection_child', '\Nimble\sek_add_css_rules_for_items_in_czr_accordion_collection_child', 10, 2 );
// filter documented in Sek_Dyn_CSS_Builder::sek_css_rules_sniffer_walker
// Note : $complete_modul_model has been normalized
// @return populated $rules
// @param $params
// Array
// (
// [input_list] => Array
// (
// [icon] => fab fa-acquisitions-incorporated
// [link] => https://twitter.com/home
// [title_attr] => Follow me on twitter
// [link_target] =>
// [color_css] => #dd9933
// [use_custom_color_on_hover] =>
// [social_color_hover] => #dd3333
// [id] => 62316ab99b4d
// )
// [parent_module_id] =>
// [module_type] => czr_accordion_collection_child
// [module_css_selector] => Array
// (
// [0] => .sek-social-icon
// )
// )
function sek_add_css_rules_for_items_in_czr_accordion_collection_child( $rules, $params ) {
// $item_input_list = wp_parse_args( $item_input_list, $default_value_model );
$item_model = isset( $params['input_list'] ) ? $params['input_list'] : array();
// VERTICAL ALIGNMENT
// if ( !empty( $item_model[ 'v_alignment' ] ) ) {
// if ( !is_array( $item_model[ 'v_alignment' ] ) ) {
// sek_error_log( __FUNCTION__ . ' => error => the v_alignment option should be an array( {device} => {alignment} )');
// }
// $v_alignment_value = is_array( $item_model[ 'v_alignment' ] ) ? $item_model[ 'v_alignment' ] : array();
// $v_alignment_value = wp_parse_args( $v_alignment_value, array(
// 'desktop' => 'center',
// 'tablet' => '',
// 'mobile' => ''
// ));
// $mapped_values = array();
// foreach ( $v_alignment_value as $device => $align_val ) {
// switch ( $align_val ) {
// case 'top' :
// $mapped_values[$device] = "flex-start";
// break;
// case 'center' :
// $mapped_values[$device] = "center";
// break;
// case 'bottom' :
// $mapped_values[$device] = "flex-end";
// break;
// }
// }
// $rules = sek_set_mq_css_rules( array(
// 'value' => $mapped_values,
// 'css_property' => 'align-items',
// 'selector' => sprintf( '[data-sek-id="%1$s"] [data-sek-item-id="%2$s"] .sek-slider-text-wrapper', $params['parent_module_id'], $item_model['id'] )
// ), $rules );
// }//Vertical alignment
return $rules;
}
// GLOBAL CSS DESIGN => FILTERING OF THE ENTIRE MODULE MODEL
add_filter( 'sek_add_css_rules_for_module_type___czr_accordion_module', '\Nimble\sek_add_css_rules_for_czr_accordion_module', 10, 2 );
// filter documented in Sek_Dyn_CSS_Builder::sek_css_rules_sniffer_walker
// Note : $complete_modul_model has been normalized
// @return populated $rules
function sek_add_css_rules_for_czr_accordion_module( $rules, $complete_modul_model ) {
if ( empty( $complete_modul_model['value'] ) || !is_array( $complete_modul_model['value'] ) )
return $rules;
$value = $complete_modul_model['value'];
$defaults = sek_get_default_module_model( 'czr_accordion_module');
$accord_defaults = $defaults['accord_opts'];
$accord_opts = $value['accord_opts'];
//sek_error_log('sek_get_default_module_model() ?', sek_get_default_module_model( 'czr_accordion_module') );
// TEXT COLOR ( for the plus / minus icon )
if ( !empty( $accord_opts[ 'color_css' ] ) && $accord_defaults[ 'color_css' ] != $accord_opts[ 'color_css' ] ) {
$rules[] = array(
'selector' => sprintf( '[data-sek-id="%1$s"] .sek-module-inner .sek-accord-wrapper .sek-accord-item .expander span', $complete_modul_model['id'] ),
'css_rules' => 'background:'. $accord_opts[ 'color_css' ] .';',
'mq' =>null
);
}
// ACTIVE / HOVER TEXT COLOR ( for the plus / minus icon )
if ( !empty( $accord_opts[ 'color_active_css' ] ) && $accord_defaults[ 'color_active_css' ] != $accord_opts[ 'color_active_css' ] ) {
$rules[] = array(
'selector' => sprintf( '[data-sek-id="%1$s"] .sek-module-inner .sek-accord-wrapper [data-sek-expanded="true"] .sek-accord-title .expander span, [data-sek-id="%1$s"] .sek-module-inner .sek-accord-wrapper .sek-accord-item .sek-accord-title:hover .expander span', $complete_modul_model['id'] ),
'css_rules' => sprintf('background:%s;', $accord_opts[ 'color_active_css' ] ),
'mq' =>null
);
}
return $rules;
}
?>
@@ -0,0 +1,83 @@
<?php
/* ------------------------------------------------------------------------- *
* LOAD AND REGISTER SHORTCODE MODULE
/* ------------------------------------------------------------------------- */
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_czr_shortcode_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_shortcode_module',
'name' => __('Shortcode', 'text_doma'),
'css_selectors' => array( '.sek-module-inner > *' ),
'sanitize_callback' => '\Nimble\sek_sanitize_czr_shortcode_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'text_content' => array(
'input_type' => 'nimble_tinymce_editor',
'editor_params' => array(
'media_button' => true,
'includedBtns' => 'basic_btns_with_lists',
),
'title' => __( 'Write the shortcode(s) in the text editor', 'text_doma' ),
'default' => '',
'width-100' => true,
'title_width' => 'width-100',
'refresh_markup' => '.sek-shortcode-content',
'notice_before' => __('A shortcode is a WordPress-specific code that lets you display predefined items. For example a trivial shortcode for a gallery looks like this [gallery].') . '<br/><br/>',
'notice_after' => __('You may use some html tags in the "text" tab of the editor.', 'text_domain_to_be_replaced')
),
'refresh_button' => array(
'input_type' => 'refresh_preview_button',
'title' => __( '' , 'text_doma' ),
'refresh_markup' => false,
'refresh_stylesheet' => false,
),
'lazyload' => array(
'input_type' => 'nimblecheck',
'title' => __('Enable image lazy-loading', 'text_doma'),
'default' => false,
'title_width' => 'width-80',
'input_width' => 'width-20'
),
// flex-box should be enabled by user and not active by default.
// It's been implemented primarily to ease centering ( see https://github.com/presscustomizr/nimble-builder/issues/565 )
// When enabled, it can create layout issues like : https://github.com/presscustomizr/nimble-builder/issues/576
'use_flex' => array(
'input_type' => 'nimblecheck',
'title' => __('Use a flex-box wrapper', 'text_doma'),
'default' => false,
'title_width' => 'width-80',
'input_width' => 'width-20',
'notice_after' => __('Flex-box is a CSS standard used to specify the layout of HTML pages. Using flex-box can make it easier to center the content of shortcodes.', 'text_doma')
),
'h_alignment_css' => array(
'input_type' => 'horizAlignmentWithDeviceSwitcher',
'title' => __( 'Horizontal alignment', 'text_doma' ),
'default' => array( 'desktop' => 'center' ),
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'h_flex_alignment',
'css_selectors' => '.sek-module-inner > .sek-shortcode-content',
'title_width' => 'width-100',
'width-100' => true,
'html_before' => '<hr/><h3>' . __('ALIGNMENT') .'</h3>'
)
)
),
'render_tmpl_path' => "shortcode_module_tmpl.php",
);
}
/* ------------------------------------------------------------------------- *
* SANITIZATION
/* ------------------------------------------------------------------------- */
// convert into a json to prevent emoji breaking global json data structure
// fix for https://github.com/presscustomizr/nimble-builder/issues/544
function sek_sanitize_czr_shortcode_module( $content ) {
if ( is_array($content) && !empty($content['text_content']) ) {
$content['text_content'] = sek_maybe_encode_richtext($content['text_content']);
}
return $content;
}
?>
@@ -0,0 +1,729 @@
<?php
/* ------------------------------------------------------------------------- *
* LOAD AND REGISTER GALLERY MODULE
/* ------------------------------------------------------------------------- */
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_czr_gallery_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_gallery_module',
'is_father' => true,
'children' => array(
'gallery_collec' => 'czr_gallery_collection_child',
'gallery_opts' => 'czr_gallery_opts_child'
),
'name' => __('Gallery', 'text_doma'),
'starting_value' => array(
'gallery_collec' => array(
array( 'img' => NIMBLE_BASE_URL . '/assets/img/default-img.png' ),
array( 'img' => NIMBLE_BASE_URL . '/assets/img/default-img.png' ),
array( 'img' => NIMBLE_BASE_URL . '/assets/img/default-img.png' ),
array( 'img' => NIMBLE_BASE_URL . '/assets/img/default-img.png' ),
array( 'img' => NIMBLE_BASE_URL . '/assets/img/default-img.png' ),
array( 'img' => NIMBLE_BASE_URL . '/assets/img/default-img.png' )
)
),
'sanitize_callback' => '\Nimble\sanitize_cb__czr_gallery_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'css_selectors' => array( '.sek-gal-wrapper' ),//array( '.sek-icon i' ),
'render_tmpl_path' => "gallery_tmpl.php",
// 'front_assets' => array(
// 'czr-font-awesome' => array(
// 'type' => 'css',
// //'handle' => 'czr-font-awesome',
// 'src' => NIMBLE_BASE_URL . '/assets/front/fonts/css/fontawesome-all.min.css'
// //'deps' => array()
// )
// )
);
}
/* ------------------------------------------------------------------------- *
* SANITIZATION
/* ------------------------------------------------------------------------- */
// convert into a json to prevent emoji breaking global json data structure
// fix for https://github.com/presscustomizr/nimble-builder/issues/544
function sanitize_cb__czr_gallery_module( $value ) {
if ( !is_array( $value ) )
return $value;
if ( !empty($value['gallery_collec']) && is_array( $value['gallery_collec'] ) ) {
foreach( $value['gallery_collec'] as $key => $data ) {
if ( array_key_exists( 'custom_caption', $data ) && is_string( $data['custom_caption'] ) ) {
$value['gallery_collec'][$key]['custom_caption'] = sek_maybe_encode_richtext( $data['custom_caption'] );
}
// if ( array_key_exists( 'title_text', $data ) && is_string( $data['title_text'] ) ) {
// $value['gallery_collec'][$key]['title_text'] = sek_maybe_encode_richtext( $data['title_text'] );
// }
}
}
return $value;
}
/* ------------------------------------------------------------------------- *
* MAIN SETTINGS
/* ------------------------------------------------------------------------- */
function sek_get_module_params_for_czr_gallery_collection_child() {
return array(
'dynamic_registration' => true,
'module_type' => 'czr_gallery_collection_child',
'is_crud' => true,
'name' => sprintf('<i class="material-icons" style="font-size: 1.2em;">toc</i> %1$s', __( 'Image collection', 'text_doma' ) ),
// 'starting_value' => array(
// 'custom_caption' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor.'
// ),
//'sanitize_callback' => '\Nimble\sanitize_callback__czr_simple_form_module',
//'css_selectors' => array( '.sek-social-icon' ),//array( '.sek-icon i' ),
'tmpl' => array(
'pre-item' => array(
// 'page-id' => array(
// 'input_type' => 'content_picker',
// 'title' => __('Pick a page', 'text_doma')
// ),
'img' => array(
'input_type' => 'upload',
'title' => __('Pick an image', 'text_doma'),
'default' => ''
),
),
'item-inputs' => array(
'img' => array(
'input_type' => 'upload',
'title' => __('Pick an image', 'text_doma'),
'default' => ''
),
'custom_caption' => array(
'input_type' => 'text',
'title' => __('Image title displayed as tooltip and in the lightbox popup', 'text_domain_to' ),
'default' => '',
'title_width' => 'width-100',
'width-100' => true,
'notice_after' => __('When left empty, Nimble Builder will use by order of priority the image caption, description, and image title. Those properties can be edited for each image in the media library.')
),
)//'item-inputs'
),
'render_tmpl_path' => '',
);
}
/* ------------------------------------------------------------------------- *
* GALLERY OPTIONS
/* ------------------------------------------------------------------------- */
function sek_get_module_params_for_czr_gallery_opts_child() {
$title_content_selector = array( '.sek-accord-item .sek-accord-title *' );
$main_content_selector = array( '.sek-accord-item .sek-accord-content', '.sek-accord-item .sek-accord-content *' );
$pro_text = '';
if ( !sek_is_pro() ) {
$pro_text = sprintf( __( '%1$s + cool additional options', 'text-doma'),
sprintf( '<a href="%1$s" target="_blank" rel="noopener noreferrer" style="text-decoration:underline">%2$s</a>',
'https://nimblebuilder.com/gallery-examples/#masonry',
__('masonry galleries', 'text-doma')
)
);
$pro_text = sek_get_pro_notice_for_czr_input( $pro_text );
}
return array(
'dynamic_registration' => true,
'module_type' => 'czr_gallery_opts_child',
'name' => sprintf('<i class="material-icons" style="font-size: 1.2em;">tune</i> %1$s', __( 'Gallery options', 'text_doma' ) ),
//'sanitize_callback' => '\Nimble\sanitize_callback__czr_simple_form_module',
// 'starting_value' => array(
// 'button_text' => __('Click me','text_doma'),
// 'color_css' => '#ffffff',
// 'bg_color_css' => '#020202',
// 'bg_color_hover' => '#151515', //lighten 15%,
// 'use_custom_bg_color_on_hover' => 0,
// 'border_radius_css' => '2',
// 'h_alignment_css' => 'center',
// 'use_box_shadow' => 1,
// 'push_effect' => 1
// ),
//'css_selectors' => array( '.sek-social-icons-wrapper' ),//array( '.sek-icon i' ),
'tmpl' => array(
'item-inputs' => array(
'img_size' => array(
'input_type' => 'simpleselect',
'title' => __('Select the image size', 'text_doma'),
'default' => 'large',
'choices' => sek_get_select_options_for_input_id( 'img-size' ),
'notice_before' => __('Select a size for this image among those generated by WordPress.', 'text_doma' ),
'html_after' => '<hr/>'
),
'columns' => array(
'input_type' => 'range_simple_device_switcher',
'title' => __( 'Number of columns', 'text_doma' ),
'default' => array( 'desktop' => '3', 'tablet' => '2', 'mobile' => '1' ),
'min' => 1,
'max' => 24,
'step' => 1,
'width-100' => true,
'title_width' => 'width-100',
'refresh_stylesheet' => true, //<= some CSS rules are layout dependant
'html_after' => $pro_text
),//null,
'custom-rows-columns' => array(
'input_type' => 'nimblecheck',
'title' => __('Define custom row and column dimensions', 'text_doma'),
'title_width' => 'width-80',
'input_width' => 'width-20',
//'refresh_markup' => true,
'refresh_stylesheet' => true,
'default' => 0,
'html_before' => '<hr/>'
//'notice_after' => __('When enabled and possible, Nimble will use the post thumbnail.', 'text_doma'),
),
'column_width' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __( 'Custom column width', 'text_doma' ),
'min' => 0,
'max' => 1000,
'default' => array( 'desktop' => '200px', 'tablet' => '150px', 'mobile' => '100px' ),
'width-100' => true,
'title_width' => 'width-100',
'refresh_markup' => false,
'refresh_stylesheet' => true
),//null,
'raw_height' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __( 'Custom raw height', 'text_doma' ),
'min' => 0,
'max' => 1000,
'default' => array( 'desktop' => '200px', 'tablet' => '150px', 'mobile' => '100px' ),
'width-100' => true,
'title_width' => 'width-100',
'refresh_markup' => false,
'refresh_stylesheet' => true
),//null,
'column_gap' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __( 'Space between columns', 'text_doma' ),
'min' => 0,
'max' => 100,
'default' => array( 'desktop' => '5px' ),
'width-100' => true,
'title_width' => 'width-100',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'html_before' => '<hr/>'
),//null,
'row_gap' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __( 'Space between rows', 'text_doma' ),
'min' => 0,
'max' => 100,
'default' => array( 'desktop' => '5px' ),
'width-100' => true,
'title_width' => 'width-100',
'refresh_markup' => false,
'refresh_stylesheet' => true
),//null,
'link-to' => array(
'input_type' => 'simpleselect',
'title' => __('Schedule an action on click or tap', 'text_doma'),
'default' => 'img-lightbox',
'choices' => array(
'no-link' => __('No click action', 'text_doma' ),
'img-lightbox' =>__('Lightbox : enlarge the image, and dim out the rest of the content', 'text_doma' ),
'img-file' => __('Link to image file', 'text_doma' ),
'img-page' =>__('Link to image page', 'text_doma' )
),
'title_width' => 'width-100',
'width-100' => true,
'notice_after' => __('Note that some click actions are disabled during customization.', 'text_doma' ),
'html_before' => '<hr/>'
),
'link-target' => array(
'input_type' => 'nimblecheck',
'title' => __('Open link in a new browser tab', 'text_doma'),
'default' => false,
'title_width' => 'width-80',
'input_width' => 'width-20',
)
)
),
'render_tmpl_path' => '',
);
}
/* ------------------------------------------------------------------------- *
* SCHEDULE CSS RULES FILTERING
/* ------------------------------------------------------------------------- */
// GLOBAL CSS DESIGN => FILTERING OF THE ENTIRE MODULE MODEL
add_filter( 'sek_add_css_rules_for_module_type___czr_gallery_module', '\Nimble\sek_add_css_rules_for_czr_gallery_module', 10, 2 );
// filter documented in Sek_Dyn_CSS_Builder::sek_css_rules_sniffer_walker
// Note : $complete_modul_model has been normalized
// @return populated $rules
function sek_add_css_rules_for_czr_gallery_module( $rules, $complete_modul_model ) {
if ( empty( $complete_modul_model['value'] ) || !is_array( $complete_modul_model['value'] ) )
return $rules;
$value = $complete_modul_model['value'];
$main_settings = $value['gallery_opts'];
//sek_error_log('sek_get_default_module_model() ?', sek_get_default_module_model( 'czr_gallery_module') );
// TABLET AND MOBILE BREAKPOINT SETUP
$mobile_breakpoint = Sek_Dyn_CSS_Builder::$breakpoints['sm'];// 576
$tablet_breakpoint = Sek_Dyn_CSS_Builder::$breakpoints['md'];// 768
$custom_tablet_breakpoint = $tablet_breakpoint;
// Is there a global custom breakpoint set ?
$global_custom_breakpoint = intval( sek_get_global_custom_breakpoint() );
$has_global_custom_breakpoint = $global_custom_breakpoint >= 1;
// Does the parent section have a custom breakpoint set ?
$section_custom_breakpoint = intval( sek_get_closest_section_custom_breakpoint( array( 'searched_level_id' => $complete_modul_model['id'] ) ) );
$has_section_custom_breakpoint = $section_custom_breakpoint >= 1;
// Use section breakpoint in priority, then global one
if ( $has_section_custom_breakpoint ) {
$custom_tablet_breakpoint = $section_custom_breakpoint;
} else if ( $has_global_custom_breakpoint ) {
$custom_tablet_breakpoint = $global_custom_breakpoint;
}
$tablet_breakpoint = $custom_tablet_breakpoint;
// If user define breakpoint ( => always for tablet ) is < to $mobile_breakpoint, make sure $mobile_breakpoint is reset to tablet_breakpoint
$mobile_breakpoint = $mobile_breakpoint >= $tablet_breakpoint ? $tablet_breakpoint : $mobile_breakpoint;
$tab_bp_val = $tablet_breakpoint - 1;// -1 to avoid "blind" spots @see https://github.com/presscustomizr/nimble-builder/issues/551
$mob_bp_val = $mobile_breakpoint - 1;// -1 to avoid "blind" spots @see https://github.com/presscustomizr/nimble-builder/issues/551
// GRID LAYOUT
// NUMBER OF COLUMNS BY DEVICE IN CASE OF A CUSTOM BREAKPOINT, GLOBAL OR FOR THE SECTION
// Get the default breakpoint values
// BASE CSS RULES
// .sek-gal-items.sek-all-col-1 {
// -ms-grid-columns: minmax(0,1fr);
// grid-template-columns: repeat(1, minmax(0,1fr));
// }
// .sek-gal-items.sek-all-col-2 {
// -ms-grid-columns: minmax(0,1fr) 20px minmax(0,1fr);
// grid-template-columns: repeat(2, minmax(0,1fr));
// grid-column-gap: 20px;
// grid-row-gap: 20px;
// }
$col_nb_gap_map = [
'col-1' => null,
'col-2' => '10px',
'col-3' => '10px',
'col-4' => '10px',
'col-5' => '10px',
'col-6' => '10px',
'col-7' => '10px',
'col-8' => '10px',
'col-9' => '10px',
'col-10' => '5px',
'col-11' => '5px',
'col-12' => '5px',
'col-13' => '5px',
'col-14' => '5px',
'col-15' => '5px',
'col-16' => '5px',
'col-17' => '5px',
'col-18' => '5px',
'col-19' => '5px',
'col-20' => '5px',
'col-21' => '5px',
'col-22' => '5px',
'col-23' => '5px',
'col-24' => '5px'
];
if ( !isset(Nimble_Manager()->generic_gallery_grid_css_rules_written) ) {
foreach ($col_nb_gap_map as $col_nb_index => $col_gap) {
$col_nb = intval( str_replace('col-', '', $col_nb_index ) );
$ms_grid_columns = [];
// Up to 24 columns
for ($j=1; $j <= $col_nb; $j++) {
if ( $j > 1 ) {
$ms_grid_columns[] = $col_gap;
}
$ms_grid_columns[] = 'minmax(0,1fr)';
}
$ms_grid_columns = implode(' ', $ms_grid_columns);
$grid_template_columns = "repeat({$col_nb}, minmax(0,1fr))";
$col_css_rules = [
'-ms-grid-columns:' . $ms_grid_columns,
'grid-template-columns:' . $grid_template_columns
];
if ( $col_nb > 1 ) {
$col_css_rules[] = 'grid-column-gap:'.$col_gap;
$col_css_rules[] = 'grid-row-gap:'.$col_gap;
}
$rules[] = array(
'selector' => '.sek-gal-wrapper .sek-gal-items.sek-all-col-'.$col_nb,
'css_rules' => implode(';', $col_css_rules),
'mq' =>null
);
}
Nimble_Manager()->generic_gallery_grid_css_rules_written = true;
}
// MEDIA QUERIES
$main_settings['columns'] = is_array($main_settings['columns']) ? $main_settings['columns'] : [];
$cols_by_device = wp_parse_args(
$main_settings['columns'],
[ 'desktop' => '3', 'tablet' => '2', 'mobile' => '1' ]// as per registration params
);
if ( sek_is_pro() && array_key_exists('min_column_width', $main_settings ) ) {
$min_column_width_by_device = wp_parse_args(
$main_settings['min_column_width'],
[ 'desktop' => '250', 'tablet' => '250', 'mobile' => '250' ]
);
}
// Normalize column and row dimensions
// will be used later on
$col_width_by_device = $main_settings['column_width'];
$col_width_by_device = is_array( $col_width_by_device ) ? $col_width_by_device : [];
$col_width_by_device = wp_parse_args(
$main_settings['column_width'],
[ 'desktop' => '200px', 'tablet' => '200px', 'mobile' => '200px' ]
);
// replace % by vh when needed
$col_width_by_device_with_unit = $col_width_by_device;
foreach ($col_width_by_device as $device => $num_unit ) {
$numeric = sek_extract_numeric_value( $num_unit );
$numeric = $numeric < 0 ? '0' : $numeric;
$col_width_by_device_with_unit[$device] = '';
// Leave the device value empty if === to default
// Otherwise it will print a duplicated dynamic css rules, already hardcoded in the static stylesheet
// fixes https://github.com/presscustomizr/nimble-builder/issues/419
//if ( !empty( $num_unit ) && $numeric.'px' !== $defaults[$device].'' ) {
if ( !empty( $num_unit ) ) {
$unit = sek_extract_unit( $num_unit );
$col_width_by_device_with_unit[$device] = $numeric . $unit;
}
}
$raw_height_by_device = wp_parse_args(
$main_settings['raw_height'],
[ 'desktop' => '200px', 'tablet' => '200px', 'mobile' => '200px' ]
);
// replace % by vh when needed
$raw_height_by_device_with_unit = $raw_height_by_device;
foreach ($raw_height_by_device as $device => $num_unit ) {
$numeric = sek_extract_numeric_value( $num_unit );
$numeric = $numeric < 0 ? '0' : $numeric;
$raw_height_by_device_with_unit[$device] = '';
// Leave the device value empty if === to default
// Otherwise it will print a duplicated dynamic css rules, already hardcoded in the static stylesheet
// fixes https://github.com/presscustomizr/nimble-builder/issues/419
//if ( !empty( $num_unit ) && $numeric.'px' !== $defaults[$device].'' ) {
if ( !empty( $num_unit ) ) {
$unit = sek_extract_unit( $num_unit );
$raw_height_by_device_with_unit[$device] = $numeric . $unit;
}
}
$col_css_rules = '';
foreach ( $cols_by_device as $device => $col_nb ) {
$col_nb = intval($col_nb);
// First define the media queries using custom user breakpoints
switch( $device ) {
case 'desktop' :
$media_qu = "(min-width:{$tablet_breakpoint}px)";
break;
case 'tablet' :
if ( $mobile_breakpoint >= ( $tab_bp_val ) ) {
$media_qu = "(max-width:{$tab_bp_val}px)";
} else {
$media_qu = "(min-width:{$mob_bp_val}px) and (max-width:{$tab_bp_val}px)";
}
break;
case 'mobile' :
$media_qu = "(max-width:{$mob_bp_val}px)";
break;
}
// Then define the selector + css rules by device
// SELECTOR
$selector = sprintf('[data-sek-id="%1$s"] .sek-gal-wrapper .sek-gal-items.sek-%2$s-col-%3$s',
$complete_modul_model['id'],
$device,
$col_nb
);
$has_custom_row_and_column_dimensions = sek_booleanize_checkbox_val( $main_settings['custom-rows-columns'] );
// Custom row and column dimension is not compatible with masonry
if ( array_key_exists('masonry_on', $main_settings ) ) {
$has_custom_row_and_column_dimensions = $has_custom_row_and_column_dimensions && !sek_booleanize_checkbox_val( $main_settings['masonry_on'] );
}
$has_auto_fill_column = array_key_exists('auto_fill', $main_settings ) && sek_booleanize_checkbox_val( $main_settings['auto_fill'] );//<= pro
// CSS RULES
// .sek-gal-items.sek-desktop-col-1 {
// -ms-grid-columns: minmax(0,1fr);
// grid-template-columns: repeat(1, minmax(0,1fr));
// }
// .sek-gal-items.sek-desktop-col-2 {
// -ms-grid-columns: minmax(0,1fr) 20px minmax(0,1fr);
// grid-template-columns: repeat(2, minmax(0,1fr));
// grid-column-gap: 20px;
// grid-row-gap: 20px;
// }
// July 2021 : introduction of the auto-fill rule in pro
if ( sek_is_pro() && $has_auto_fill_column ) {
$min_col_width = 250;
if ( array_key_exists($device, $min_column_width_by_device ) ) {
$min_col_width = intval( $min_column_width_by_device[$device] );
}
$grid_template_columns = "repeat(auto-fill, minmax({$min_col_width}px,1fr));";
// in this case, no need to add '-ms-grid-columns' rule
$col_css_rules = [
'grid-template-columns:' . $grid_template_columns
];
} else {
$ms_grid_columns = [];
// Up to 24 columns
for ($i=1; $i <= $col_nb; $i++) {
if ( $i > 1 ) {
$col_gap = array_key_exists('col-'.$col_nb, $col_nb_gap_map ) ? $col_nb_gap_map['col-'.$col_nb] : '5px';
$ms_grid_columns[] = $col_gap;
}
$ms_grid_columns[] = 'minmax(0,1fr)';
}
$ms_grid_columns = implode(' ', $ms_grid_columns);
$grid_template_columns = "repeat({$col_nb}, minmax(0,1fr))";
$col_css_rules = [
'-ms-grid-columns:' . $ms_grid_columns,
'grid-template-columns:' . $grid_template_columns
];
}
if ( $col_nb > 1 ) {
$col_gap = array_key_exists('col-'.$col_nb, $col_nb_gap_map ) ? $col_nb_gap_map['col-'.$col_nb] : '5px';
$col_css_rules[] = 'grid-column-gap:'.$col_gap;
$col_css_rules[] = 'grid-row-gap:'.$col_gap;
}
// Column width and row height
if ( $has_custom_row_and_column_dimensions ) {
if ( !$has_auto_fill_column ) {
$norm_col_nb = $col_nb > 0 ? $col_nb : 1;
if ( array_key_exists($device, $col_width_by_device_with_unit ) ) {
$col_width = $col_width_by_device_with_unit[$device];
}
if ( !empty($col_width) ) {
$grid_template_columns = "repeat({$norm_col_nb}, {$col_width});";
$col_css_rules[] = 'grid-template-columns:' . $grid_template_columns;
}
}
if ( array_key_exists($device, $raw_height_by_device_with_unit ) ) {
$raw_height = $raw_height_by_device_with_unit[$device];
}
if ( !empty($raw_height) ) {
$col_css_rules[] = 'grid-auto-rows:' . $raw_height;
$col_css_rules[] = '-ms-grid-rows:' . $raw_height;
$col_css_rules[] = 'grid-template-rows:' . $raw_height;
}
}
$col_css_rules_ready = [];
if ( 'desktop' != $device ) {
foreach ($col_css_rules as $col_rule) {
$col_css_rules_ready[] = $col_rule .= '';//!important';
}
} else {
$col_css_rules_ready = $col_css_rules;
}
$col_css_rules_ready = implode(';', $col_css_rules_ready);
$rules[] = array(
'selector' => $selector,
'css_rules' => $col_css_rules_ready,
'mq' => $media_qu
);
}// end foreach
// END OF GRID LAYOUT
// COLUMN AND ROW GAP
// Horizontal Gap
$gap = $main_settings['column_gap'];
$gap = is_array( $gap ) ? $gap : array();
$defaults = array(
'desktop' => '10px',// <= this value matches the static CSS rule and the input default for the module
'tablet' => '',
'mobile' => ''
);
$gap = wp_parse_args( $gap, $defaults );
// replace % by vh when needed
$gap_ready_value = $gap;
foreach ($gap as $device => $num_unit ) {
$numeric = sek_extract_numeric_value( $num_unit );
$numeric = $numeric < 0 ? '0' : $numeric;
$gap_ready_value[$device] = '';
// Leave the device value empty if === to default
// Otherwise it will print a duplicated dynamic css rules, already hardcoded in the static stylesheet
// fixes https://github.com/presscustomizr/nimble-builder/issues/419
//if ( !empty( $num_unit ) && $numeric.'px' !== $defaults[$device].'' ) {
if ( !empty( $num_unit ) ) {
$unit = sek_extract_unit( $num_unit );
$gap_ready_value[$device] = $numeric . $unit;
}
}
// for grid layout => gap between columns
// for list layout => gap between image and content
$rules = sek_set_mq_css_rules(array(
'value' => $gap_ready_value,
'css_property' => 'grid-column-gap',
'selector' => implode( ',', [
'.nb-loc [data-sek-id="'.$complete_modul_model['id'].'"] .sek-module-inner .sek-gal-wrapper .sek-gal-items'
] ),
'is_important' => false,
'level_id' => $complete_modul_model['id']
), $rules );
// Vertical Gap => common to list and grid layout
$v_gap = $main_settings['row_gap'];
$v_gap = is_array( $v_gap ) ? $v_gap : array();
$defaults = array(
'desktop' => '10px',// <= this value matches the static CSS rule and the input default for the module
'tablet' => '',
'mobile' => ''
);
$v_gap = wp_parse_args( $v_gap, $defaults );
// replace % by vh when needed
$v_gap_ready_value = $v_gap;
foreach ($v_gap as $device => $num_unit ) {
$numeric = sek_extract_numeric_value( $num_unit );
$numeric = $numeric < 0 ? 0 : $numeric;
$v_gap_ready_value[$device] = '';
// Leave the device value empty if === to default
// Otherwise it will print a duplicated dynamic css rules, already hardcoded in the static stylesheet
// fixes https://github.com/presscustomizr/nimble-builder/issues/419
//if ( !empty( $num_unit ) && $numeric.'px' !== $defaults[$device].'' ) {
if ( !empty( $num_unit ) ) {
$unit = sek_extract_unit( $num_unit );
$v_gap_ready_value[$device] = $numeric . $unit;
}
}
$rules = sek_set_mq_css_rules(array(
'value' => $v_gap_ready_value,
'css_property' => 'grid-row-gap',
'selector' => '.nb-loc [data-sek-id="'.$complete_modul_model['id'].'"] .sek-module-inner .sek-gal-wrapper .sek-gal-items',
'is_important' => false,
'level_id' => $complete_modul_model['id']
), $rules );
// END OF COLUMN AND ROW GAP
return $rules;
}
// PER ITEM CSS DESIGN => FILTERING OF EACH ITEM MODEL, TARGETING THE ID ( [data-sek-item-id="893af157d5e3"] )
//add_filter( 'sek_add_css_rules_for_single_item_in_module_type___czr_gallery_collection_child', '\Nimble\sek_add_css_rules_for_items_in_czr_gallery_collection_child', 10, 2 );
// filter documented in Sek_Dyn_CSS_Builder::sek_css_rules_sniffer_walker
// Note : $complete_modul_model has been normalized
// @return populated $rules
// @param $params
// Array
// (
// [input_list] => Array
// (
// [icon] => fab fa-acquisitions-incorporated
// [link] => https://twitter.com/home
// [title_attr] => Follow me on twitter
// [link_target] =>
// [color_css] => #dd9933
// [use_custom_color_on_hover] =>
// [social_color_hover] => #dd3333
// [id] => 62316ab99b4d
// )
// [parent_module_id] =>
// [module_type] => czr_gallery_collection_child
// [module_css_selector] => Array
// (
// [0] => .sek-social-icon
// )
// )
function sek_add_css_rules_for_items_in_czr_gallery_collection_child( $rules, $params ) {
// $item_input_list = wp_parse_args( $item_input_list, $default_value_model );
$item_model = isset( $params['input_list'] ) ? $params['input_list'] : array();
// VERTICAL ALIGNMENT
// if ( !empty( $item_model[ 'v_alignment' ] ) ) {
// if ( !is_array( $item_model[ 'v_alignment' ] ) ) {
// sek_error_log( __FUNCTION__ . ' => error => the v_alignment option should be an array( {device} => {alignment} )');
// }
// $v_alignment_value = is_array( $item_model[ 'v_alignment' ] ) ? $item_model[ 'v_alignment' ] : array();
// $v_alignment_value = wp_parse_args( $v_alignment_value, array(
// 'desktop' => 'center',
// 'tablet' => '',
// 'mobile' => ''
// ));
// $mapped_values = array();
// foreach ( $v_alignment_value as $device => $align_val ) {
// switch ( $align_val ) {
// case 'top' :
// $mapped_values[$device] = "flex-start";
// break;
// case 'center' :
// $mapped_values[$device] = "center";
// break;
// case 'bottom' :
// $mapped_values[$device] = "flex-end";
// break;
// }
// }
// $rules = sek_set_mq_css_rules( array(
// 'value' => $mapped_values,
// 'css_property' => 'align-items',
// 'selector' => sprintf( '[data-sek-id="%1$s"] [data-sek-item-id="%2$s"] .sek-slider-text-wrapper', $params['parent_module_id'], $item_model['id'] )
// ), $rules );
// }//Vertical alignment
return $rules;
}
?>
@@ -0,0 +1,87 @@
<?php
/* ------------------------------------------------------------------------- *
* CONTENT TYPE SWITCHER
/* ------------------------------------------------------------------------- */
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_content_type_switcher_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_content_type_switcher_module',
'name' => __('Select a content type', 'text_doma'),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'content_type' => array(
'input_type' => 'content_type_switcher',
'title' => '',//__('Which type of content would you like to drop in your page ?', 'text_doma'),
'width-100' => true,
'title_width' => 'width-100',
'notice_after' => sprintf(
__('Note : you can %1$s to replace your default theme template. Or design your own %2$s.', 'nimble-builder'),
sprintf('<a href="#" onclick="%2$s" title="%1$s">%1$s</a>',
__('use the Nimble page template', 'nimble-builder'),
"javascript:wp.customize.section('__localOptionsSection', function( _s_ ){_s_.container.find('.accordion-section-title').first().trigger('click');})"
),
sprintf('<a href="#" onclick="%2$s" title="%1$s">%1$s</a>',
__('header and footer', 'nimble-builder'),
"javascript:wp.customize.section('__globalOptionsSectionId', function( _s_ ){ _s_.focus(); })"
)
)
)
)
)
);
}
/* ------------------------------------------------------------------------- *
* MODULE PICKER MODULE
/* ------------------------------------------------------------------------- */
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_module_picker_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_module_picker_module',
'name' => __('Pick a module', 'text_doma'),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'module_id' => array(
'input_type' => 'module_picker',
'title' => __('Drag-and-drop or double-click a module to insert it into a drop zone of the preview page.', 'text_doma'),
'width-100' => true,
'title_width' => 'width-100'
)
)
)
);
}
/* ------------------------------------------------------------------------- *
* SEKTION PICKER MODULES
/* ------------------------------------------------------------------------- */
// now registered with sek_register_prebuilt_section_modules() in add_action( 'after_setup_theme', '\Nimble\sek_schedule_module_registration', 50 );
// and then populated in AJAX from a local JSON since https://github.com/presscustomizr/nimble-builder/issues/431
// FOR SAVED SECTIONS
// function sek_get_module_params_for_sek_my_sections_sec_picker_module() {
// return array(
// 'dynamic_registration' => true,
// 'module_type' => 'sek_my_sections_sec_picker_module',
// 'name' => __('My sections', 'text_doma'),
// 'tmpl' => array(
// 'item-inputs' => array(
// 'my_sections' => array(
// 'input_type' => 'section_picker',
// 'title' => __('Drag-and-drop or double-click a section to insert it into a drop zone of the preview page.', 'text_doma'),
// 'width-100' => true,
// 'title_width' => 'width-100'
// )
// )
// )
// );
// }
?>
@@ -0,0 +1,23 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_mod_option_switcher_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_mod_option_switcher_module',
//'name' => __('Option switcher', 'text_doma'),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'content_type' => array(
'input_type' => 'module_option_switcher',
'title' => '',//__('Which type of content would you like to drop in your page ?', 'text_doma'),
'width-100' => true,
'title_width' => 'width-100',
)
)
)
);
}
?>
@@ -0,0 +1,364 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_level_bg_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_level_bg_module',
//'name' => __('Background', 'text_doma'),
// 'starting_value' => array(
// 'bg-color-overlay' => '#000000',
// 'bg-opacity-overlay' => '40'
// ),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'bg-color' => array(
'input_type' => 'wp_color_alpha',
'title' => __('Background color', 'text_doma'),
'width-100' => true,
'default' => '',
),
'bg-image' => array(
'input_type' => 'upload',
'title' => __('Image', 'text_doma'),
'default' => '',
'notice_after' => sprintf( __('To ensure better performances, use optimized images for your backgrounds. You can also enable the lazy loading option in the %1$s.', 'text_doma'),
sprintf( '<a href="#" onclick="%1$s">%2$s</a>',
"javascript:wp.customize.section('__globalOptionsSectionId', function( _s_ ){ _s_.focus(); })",
__('site wide options', 'text_doma')
)
),
'refresh_markup' => true,
'html_before' => '<hr/><h3>' . __('Image background', 'text-doma') .'</h3>'
),
'bg-use-post-thumb' => array(
'input_type' => 'nimblecheck',
'title' => __('Use the contextual post thumbnail', 'text_doma'),
'title_width' => 'width-80',
'input_width' => 'width-20',
'refresh_markup' => true,
'default' => 0,
'notice_after' => __('When enabled and possible, Nimble will use the post thumbnail.', 'text_doma'),
),
'bg-position' => array(
'input_type' => 'bgPositionWithDeviceSwitcher',
'title' => __('Image position', 'text_doma'),
'default' => array( 'desktop' => 'center' ),
'title_width' => 'width-100',
),
// 'bg-parallax' => array(
// 'input_type' => 'nimblecheck',
// 'title' => __('Parallax scrolling', 'text_doma')
// ),
'bg-attachment' => array(
'input_type' => 'nimblecheck',
'title' => __('Fixed background', 'text_doma'),
'title_width' => 'width-80',
'input_width' => 'width-20',
'refresh_markup' => true,
'default' => 0
),
'bg-parallax' => array(
'input_type' => 'nimblecheck',
'title' => __('Parallax effect on scroll', 'text_doma'),
'title_width' => 'width-80',
'input_width' => 'width-20',
'default' => 0,
'notice_after' => __('When enabled, the background image moves slower than the page elements on scroll. This effect is not enabled on mobile devices.', 'text_doma'),
'refresh_markup' => true,
),
'bg-parallax-force' => array(
'input_type' => 'range_simple',
'title' => __('Parallax force (in percents)', 'text_doma'),
'orientation' => 'horizontal',
'min' => 0,
'max' => 100,
// 'unit' => '%',
'default' => '60',
'width-100' => true,
'title_width' => 'width-100',
'notice_after' => __('Customize the magnitude of the visual effect when scrolling.', 'text_doma'),
'refresh_markup' => true
),
'bg-scale' => array(
'input_type' => 'simpleselect',
'title' => __('Scale', 'text_doma'),
'default' => 'cover',
'choices' => sek_get_select_options_for_input_id( 'bg-scale' )
),
'bg-repeat' => array(
'input_type' => 'simpleselect',
'title' => __('Repeat', 'text_doma'),
'default' => 'no-repeat',
'choices' => array(
'default' => __('Default', 'text_dom'),
'no-repeat' => __('No repeat', 'text_dom'),
'repeat' => __('Repeat', 'text_dom'),
'repeat-x' => __('Repeat x', 'text_dom'),
'repeat-y' => __('Repeat y', 'text_dom'),
'round' => __('Round', 'text_dom'),
'space' => __('Space', 'text_dom'),
)
),
'bg-use-video' => array(
'input_type' => 'nimblecheck',
'title' => __('Use a video background', 'text_doma'),
'title_width' => 'width-80',
'input_width' => 'width-20',
'default' => 0,
//'notice_after' => __('', 'text_doma'),
'refresh_markup' => true,
'html_before' => '<hr/><h3>' . __('Video background', 'text-doma') .'</h3>'
),
'bg-video' => array(
'input_type' => 'text',
'title' => __('Video link', 'text_doma'),
'default' => '',
'refresh_markup' => true,
'notice_after' => __('Video link from YouTube, Vimeo, or a self-hosted file ( mp4 format is recommended )', 'text_doma'),
),
'bg-video-loop' => array(
'input_type' => 'nimblecheck',
'title' => __('Loop infinitely', 'text_doma'),
'title_width' => 'width-80',
'input_width' => 'width-20',
'default' => 1,
//'notice_after' => __('', 'text_doma'),
'refresh_markup' => true,
),
'bg-video-delay-start' => array(
'input_type' => 'number_simple',
'title' => __('Play after a delay', 'text_doma'),
'default' => '',
'refresh_markup' => true,
'notice_after' => __('Set an optional delay in seconds before playing the video', 'text-doma')
),
'bg-video-on-mobile' => array(
'input_type' => 'nimblecheck',
'title' => __('Play on mobile devices', 'text_doma'),
'title_width' => 'width-80',
'input_width' => 'width-20',
'default' => 0,
'notice_after' => __('Not recommended if you don\'t use a self-hosted video file', 'text_doma'),
'refresh_markup' => true,
),
'bg-video-start-time' => array(
'input_type' => 'number_simple',
'title' => __('Start time', 'text_doma'),
'default' => '',
'refresh_markup' => true
),
'bg-video-end-time' => array(
'input_type' => 'number_simple',
'title' => __('End time', 'text_doma'),
'default' => '',
'refresh_markup' => true,
'notice_after' => __('Set an optional start and end time in seconds', 'text-doma')
),
'bg-apply-overlay' => array(
'input_type' => 'nimblecheck',
'title' => __('Apply a background overlay', 'text_doma'),
'title_width' => 'width-80',
'input_width' => 'width-20',
'default' => 0,
'html_before' => '<hr/><h3>' . __('Overlay color', 'text-doma') .'</h3>'
),
'bg-color-overlay' => array(
'input_type' => 'wp_color_alpha',
'title' => __('Overlay Color', 'text_doma'),
'width-100' => true,
'default' => '#000000'
),
'bg-opacity-overlay' => array(
'input_type' => 'range_simple',
'title' => __('Opacity (in percents)', 'text_doma'),
'orientation' => 'horizontal',
'min' => 0,
'max' => 100,
// 'unit' => '%',
'default' => '40',
'width-100' => true,
'title_width' => 'width-100'
),
)//item-inputs
)//tmpl
);
}
/* ------------------------------------------------------------------------- *
* SCHEDULE CSS RULES FILTERING
/* ------------------------------------------------------------------------- */
add_filter( 'sek_add_css_rules_for_level_options', '\Nimble\sek_add_css_rules_for_level_background', 10, 3 );
function sek_add_css_rules_for_level_background( $rules, $level ) {
$options = empty( $level[ 'options' ] ) ? array() : $level['options'];
// $default_value_model = Array
// (
// [bg-color] =>
// [bg-image] =>
// [bg-position] => center
// [bg-attachment] => 0
// [bg-scale] => default
// [bg-apply-overlay] => 0
// [bg-color-overlay] =>
// [bg-opacity-overlay] => 50
// [border-width] => 1
// [border-type] => none
// [border-color] =>
// [shadow] => 0
// )
$default_value_model = sek_get_default_module_model( 'sek_level_bg_module' );
$bg_options = ( !empty( $options[ 'bg' ] ) && is_array( $options[ 'bg' ] ) ) ? $options[ 'bg' ] : array();
$bg_options = wp_parse_args( $bg_options , is_array( $default_value_model ) ? $default_value_model : array() );
if ( empty( $bg_options ) )
return $rules;
$background_properties = array();
$bg_property_selector = '[data-sek-id="'.$level['id'].'"]';
/* The general syntax of the background property is:
* https://www.webpagefx.com/blog/web-design/background-css-shorthand/
* background: [background-image] [background-position] / [background-size] [background-repeat] [background-attachment] [background-origin] [background-clip] [background-color];
*/
// Img background
if ( !empty( $bg_options[ 'bg-image'] ) && is_numeric( $bg_options[ 'bg-image'] ) ) {
// deactivated when customizing @see function sek_is_img_smartload_enabled()
//$background_properties[ 'background-image' ] = 'url("'. wp_get_attachment_url( $bg_options[ 'bg-image'] ) .'")';
// Img Bg Position
// 'center' is the default value. the CSS rule is declared in assets/front/scss/sek-base.scss
if ( !empty( $bg_options[ 'bg-position'] ) && 'center' != $bg_options[ 'bg-position'] ) {
$pos_map = array(
'top_left' => '0% 0%',
'top' => '50% 0%',
'top_right' => '100% 0%',
'left' => '0% 50%',
'center' => '50% 50%',
'right' => '100% 50%',
'bottom_left' => '0% 100%',
'bottom' => '50% 100%',
'bottom_right'=> '100% 100%'
);
// Retro-compat for old bg-position option without device switcher
if ( is_string( $bg_options[ 'bg-position'] ) ) {
$raw_pos = $bg_options[ 'bg-position'];
$background_properties[ 'background-position' ] = array_key_exists($raw_pos, $pos_map) ? $pos_map[ $raw_pos ] : $pos_map[ 'center' ];
} else if ( is_array( $bg_options[ 'bg-position'] ) ) {
$mapped_bg_options = array();
// map option with css value
foreach ($bg_options[ 'bg-position'] as $device => $user_val ) {
if ( !in_array( $device, array( 'desktop', 'tablet', 'mobile' ) ) ) {
sek_error_log( __FUNCTION__ . ' => error => unknown device : ' . $device );
continue;
}
$mapped_bg_options[$device] = array_key_exists($user_val, $pos_map) ? $pos_map[ $user_val ] : $pos_map[ 'center' ];
}
$rules = sek_set_mq_css_rules(array(
'value' => $mapped_bg_options,
'css_property' => 'background-position',
'selector' => $bg_property_selector,
'level_id' => $level['id']
), $rules );
}
}
// background size
// 'cover' is the default value. the CSS rule is declared in assets/front/scss/sek-base.scss
if ( !empty( $bg_options['bg-scale'] ) && 'default' != $bg_options['bg-scale'] && 'cover' != $bg_options['bg-scale'] ) {
//When specifying a background-size value, it must immediately follow the background-position value.
$background_properties['background-size'] = $bg_options['bg-scale'];
}
// add no-repeat by default?
// 'no-repeat' is the default value. the CSS rule is declared in assets/front/scss/sek-base.scss
if ( !empty( $bg_options['bg-repeat'] ) && 'default' != $bg_options['bg-repeat'] ) {
$background_properties['background-repeat'] = $bg_options['bg-repeat'];
}
// write the bg-attachment rule only if true <=> set to "fixed"
if ( !empty( $bg_options['bg-attachment'] ) && sek_is_checked( $bg_options['bg-attachment'] ) ) {
$background_properties['background-attachment'] = 'fixed';
}
}
//background color (needs validation: we need a sanitize hex or rgba color)
if ( !empty( $bg_options['bg-color'] ) ) {
$background_properties['background-color'] = $bg_options[ 'bg-color' ];
}
//build background rule
if ( !empty( $background_properties ) ) {
$background_css_rules = '';
foreach ($background_properties as $bg_prop => $bg_css_val ) {
$background_css_rules .= sprintf('%1$s:%2$s;', $bg_prop, $bg_css_val );
}
$rules[] = array(
'selector' => $bg_property_selector,
'css_rules' => $background_css_rules,
'mq' =>null
);
}
//Background overlay?
// 1) a background image or video should be set
// 2) the option should be checked
if ( ( !empty( $bg_options['bg-image']) || sek_is_checked( $bg_options['bg-use-post-thumb'] ) || ( sek_is_checked( $bg_options['bg-use-video'] ) && !empty( $bg_options['bg-video'] ) ) ) && !empty( $bg_options[ 'bg-apply-overlay'] ) && sek_is_checked( $bg_options[ 'bg-apply-overlay'] ) ) {
//(needs validation: we need a sanitize hex or rgba color)
$bg_color_overlay = isset( $bg_options[ 'bg-color-overlay' ] ) ? $bg_options[ 'bg-color-overlay' ] : null;
if ( $bg_color_overlay ) {
//overlay pseudo element
$bg_overlay_css_rules = 'content:"";display:block;position:absolute;top:0;left:0;right:0;bottom:0;background-color:'.$bg_color_overlay;
//opacity
//validate/sanitize
$bg_overlay_opacity = isset( $bg_options[ 'bg-opacity-overlay' ] ) ? filter_var( $bg_options[ 'bg-opacity-overlay' ], FILTER_VALIDATE_INT, array( 'options' =>
array( "min_range"=>0, "max_range"=>100 ) )
) : FALSE;
$bg_overlay_opacity = FALSE !== $bg_overlay_opacity ? filter_var( $bg_overlay_opacity / 100, FILTER_VALIDATE_FLOAT ) : $bg_overlay_opacity;
$bg_overlay_css_rules = FALSE !== $bg_overlay_opacity ? $bg_overlay_css_rules . ';opacity:' . $bg_overlay_opacity : $bg_overlay_css_rules;
// nov 2019 : added new selector '> .sek-bg-video-wrapper' for https://github.com/presscustomizr/nimble-builder/issues/287
$rules[] = array(
'selector' => implode(',', array( '[data-sek-id="'.$level['id'].'"]::before', '[data-sek-id="'.$level['id'].'"] > .sek-bg-video-wrapper::after' ) ),
'css_rules' => $bg_overlay_css_rules,
'mq' =>null
);
//we have to also:
// 1) make '[data-sek-id="'.$level['id'].'"] to be relative positioned (to make the overlay absolute element referring to it)
// 2) make any '[data-sek-id="'.$level['id'].'"] first child to be relative (not to the resizable handle div)
$rules[] = array(
'selector' => '[data-sek-id="'.$level['id'].'"]',
'css_rules' => 'position:relative',
'mq' => null
);
$first_child_selector = '[data-sek-id="'.$level['id'].'"]>*';
//in the preview we still want some elements to be absoluted positioned
//1) the .ui-resizable-handle (jquery-ui)
//2) the block overlay
//3) the add content button
if ( is_customize_preview() ) {
$first_child_selector .= ':not(.ui-resizable-handle):not(.sek-dyn-ui-wrapper):not(.sek-add-content-button)';
}
$rules[] = array(
'selector' => $first_child_selector,
'css_rules' => 'position:relative',
'mq' =>null
);
}
}//if ( !empty( $bg_options[ 'bg-apply-overlay'] ) && sek_is_checked( $bg_options[ 'bg-apply-overlay'] ) ) {}
return $rules;
}
?>
@@ -0,0 +1,169 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_level_text_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_level_text_module',
//'name' => __('Text', 'text_doma'),
// 'starting_value' => array(
// 'bg-color-overlay' => '#000000',
// 'bg-opacity-overlay' => '40'
// ),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'css_selectors' => array( '', // <= this first empty selector generates a selector looking like [data-sek-id="__nimble__27f2dc680c0c"], which allows us to style text not wrapped in any specific html tags
'p', 'a', '.sek-btn', 'button', 'input', 'select', 'optgroup', 'textarea' ),
'tmpl' => array(
'item-inputs' => array(
'h_alignment_css' => array(
'input_type' => 'horizTextAlignmentWithDeviceSwitcher',
'title' => __('Alignment', 'text_doma'),
'default' => array( 'desktop' => is_rtl() ? 'right' : 'left' ),
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'h_alignment',
'title_width' => 'width-100',
'width-100' => true,
'html_before' => sprintf( '<span class="czr-notice">%1$s<br/>%2$s</span><hr>',
__('Note : some modules have text settings in their module content tab. Those settings are applied first, before those below.'),
__('Text styling is always inherited in this order : section > column > module settings > module content')
)
),
'font_family_css' => array(
'input_type' => 'font_picker',
'title' => __('Font family', 'text_doma'),
'default' => '',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'refresh_fonts' => true,
'css_identifier' => 'font_family',
'html_after' => sprintf('<span class="czr-notice"><i class="far fa-lightbulb"></i> %1s => <a href="%2s" target="_blank" rel="noopener noreferrer">%2$s</a></span><hr/>',
__('Find inspiration'),
'https://fonts.google.com/?sort=popularity'
)
),
'font_size_css' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __( 'Font size', 'text_doma' ),
// the default value is commented to fix https://github.com/presscustomizr/nimble-builder/issues/313
// => as a consequence, when a module uses the font child module, the default font-size rule must be defined in the module SCSS file.
//'default' => array( 'desktop' => '16px' ),
'min' => 0,
'max' => 100,
'title_width' => 'width-100',
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'font_size'
),//16,//"14px",
'line_height_css' => array(
'input_type' => 'range_with_unit_picker',
'title' => __( 'Line height', 'text_doma' ),
'default' => '1.5em',
'min' => 0,
'max' => 10,
'step' => 0.1,
'width-100' => true,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'line_height'
),//24,//"20px",
'color_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __('Text color', 'text_doma'),
'default' => '',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'width-100' => true,
'css_identifier' => 'color'
),//"#000000",
'color_hover_css' => array(
'input_type' => 'wp_color_alpha',
'title' => __('Text color on mouse over', 'text_doma'),
'default' => '',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'width-100' => true,
'title_width' => 'width-100',
'css_identifier' => 'color_hover'
),//"#000000",
'font_weight_css' => array(
'input_type' => 'simpleselect',
'title' => __('Font weight', 'text_doma'),
'default' => 400,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'font_weight',
'choices' => sek_get_select_options_for_input_id( 'font_weight_css' )
),//null,
'font_style_css' => array(
'input_type' => 'simpleselect',
'title' => __('Font style', 'text_doma'),
'default' => 'inherit',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'font_style',
'choices' => sek_get_select_options_for_input_id( 'font_style_css' )
),//null,
'text_decoration_css' => array(
'input_type' => 'simpleselect',
'title' => __('Text decoration', 'text_doma'),
'default' => 'none',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'text_decoration',
'choices' => sek_get_select_options_for_input_id( 'text_decoration_css' )
),//null,
'text_transform_css' => array(
'input_type' => 'simpleselect',
'title' => __('Text transform', 'text_doma'),
'default' => 'none',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'text_transform',
'choices' => sek_get_select_options_for_input_id( 'text_transform_css' )
),//null,
'letter_spacing_css' => array(
'input_type' => 'range_simple',
'title' => __( 'Letter spacing', 'text_doma' ),
'default' => 0,
'min' => 0,
'step' => 1,
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'letter_spacing',
'width-100' => true,
)//0,
// Note : always use the suffix '_flag_important' to name an input controling the !important css flag @see Nimble\sek_add_css_rules_for_css_sniffed_input_id
// 'fonts___flag_important' => array(
// 'input_type' => 'nimblecheck',
// 'title' => __('Apply the style options in priority (uses !important).', 'text_doma'),
// 'default' => 0,
// 'refresh_markup' => false,
// 'refresh_stylesheet' => true,
// 'title_width' => 'width-80',
// 'input_width' => 'width-20',
// // declare the list of input_id that will be flagged with !important when the option is checked
// // @see sek_add_css_rules_for_css_sniffed_input_id
// // @see Nsek_is_flagged_important
// 'important_input_list' => array(
// 'font_family_css',
// 'font_size_css',
// 'line_height_css',
// 'font_weight_css',
// 'font_style_css',
// 'text_decoration_css',
// 'text_transform_css',
// 'letter_spacing_css',
// 'color_css',
// 'color_hover_css'
// )
// )
)//item-inputs
)//tmpl
);
}
?>
@@ -0,0 +1,124 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_level_border_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_level_border_module',
//'name' => __('Borders', 'text_doma'),
'starting_value' => array(
'borders' => array(
'_all_' => array( 'wght' => '1px', 'col' => '#000000' )
)
),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'border-type' => array(
'input_type' => 'simpleselect',
'title' => __('Border shape', 'text_doma'),
'default' => 'none',
'choices' => sek_get_select_options_for_input_id( 'border-type' )
),
'borders' => array(
'input_type' => 'borders',
'title' => __('Borders', 'text_doma'),
'min' => 0,
'max' => 100,
'default' => array(
'_all_' => array( 'wght' => '1px', 'col' => '#000000' )
),
'width-100' => true,
'title_width' => 'width-100'
),
'border-radius' => array(
'input_type' => 'border_radius',
'title' => __( 'Rounded corners', 'text_doma' ),
'default' => array( '_all_' => '0px' ),
'width-100' => true,
'title_width' => 'width-100',
'min' => 0,
'max' => 500,
//'refresh_markup' => false,
//'refresh_stylesheet' => true,
//'css_identifier' => 'border_radius',
//'css_selectors'=> $css_selectors
),
'shadow' => array(
'input_type' => 'nimblecheck',
'title' => __('Apply a shadow', 'text_doma'),
'title_width' => 'width-80',
'input_width' => 'width-20',
'default' => 0,
'refresh_markup' => true
)
)//item-inputs
)//tmpl
);
}
/* ------------------------------------------------------------------------- *
* SCHEDULE CSS RULES FILTERING
/* ------------------------------------------------------------------------- */
add_filter( 'sek_add_css_rules_for_level_options', '\Nimble\sek_add_css_rules_for_border', 10, 3 );
function sek_add_css_rules_for_border( $rules, $level ) {
$options = empty( $level[ 'options' ] ) ? array() : $level['options'];
// $default_value_model = Array
// (
// [bg-color] =>
// [bg-image] =>
// [bg-position] => center
// [bg-attachment] => 0
// [bg-scale] => default
// [bg-apply-overlay] => 0
// [bg-color-overlay] =>
// [bg-opacity-overlay] => 50
// [border-type] => 'solid'
// [borders] => Array
// (
// [_all_] => Array
// (
// [wght] => 55px
// [col] => #359615
// )
// [top] => Array
// (
// [wght] => 6em
// [col] => #dd3333
// )
// [bottom] => Array
// (
// [wght] => 76%
// [col] => #eeee22
// )
// [shadow] => 0
// )
$default_value_model = sek_get_default_module_model( 'sek_level_border_module' );
$normalized_border_options = ( !empty( $options[ 'border' ] ) && is_array( $options[ 'border' ] ) ) ? $options[ 'border' ] : array();
$normalized_border_options = wp_parse_args( $normalized_border_options , is_array( $default_value_model ) ? $default_value_model : array() );
if ( empty( $normalized_border_options ) )
return $rules;
$border_settings = !empty( $normalized_border_options[ 'borders' ] ) ? $normalized_border_options[ 'borders' ] : FALSE;
$border_type = $normalized_border_options[ 'border-type' ];
$has_border_settings = FALSE !== $border_settings && is_array( $border_settings ) && !empty( $border_type ) && 'none' != $border_type;
//border width + type + color
if ( $has_border_settings ) {
$rules = sek_generate_css_rules_for_multidimensional_border_options( $rules, $border_settings, $border_type, '[data-sek-id="'.$level['id'].'"]' );
}
$has_border_radius = !empty( $options[ 'border' ] ) && is_array( $options[ 'border' ] ) && !empty( $options[ 'border' ]['border-radius'] );
if ( $has_border_radius ) {
$radius_settings = $normalized_border_options['border-radius'];
$rules = sek_generate_css_rules_for_border_radius_options( $rules, $normalized_border_options['border-radius'], '[data-sek-id="'.$level['id'].'"]' );
}
return $rules;
}
?>
@@ -0,0 +1,171 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_level_height_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_level_height_module',
//'name' => __('Height options', 'text_doma'),
'starting_value' => array(
'custom-height' => array( 'desktop' => '50%' ),
),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'height-type' => array(
'input_type' => 'simpleselect',
'title' => __('Height : auto or custom', 'text_doma'),
'default' => 'default',
'choices' => sek_get_select_options_for_input_id( 'height-type' )
),
'custom-height' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __('Custom height', 'text_doma'),
'min' => 0,
'max' => 500,
'default' => array( 'desktop' => '50%' ),
'width-100' => true,
'title_width' => 'width-100',
'notice_before' => 'Note that when using a custom height, the inner content can be larger than the parent container, in particular on mobile devices. To prevent this problem, preview your page with the device switcher icons. You can also activate the overflow hidden option below.'
),
// implemented to fix https://github.com/presscustomizr/nimble-builder/issues/365
'overflow_hidden' => array(
'input_type' => 'nimblecheck',
'title' => __('Overflow hidden', 'text_doma'),
'default' => 0,
'title_width' => 'width-80',
'input_width' => 'width-20',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'notice_after' => __('Hide the content when it is too big to fit in its parent container.', 'text_doma')
),
'v_alignment' => array(
'input_type' => 'verticalAlignWithDeviceSwitcher',
'title' => __('Inner vertical alignment', 'text_doma'),
'default' => array( 'desktop' => 'center' ),
'refresh_markup' => false,
'refresh_stylesheet' => true,
//'css_identifier' => 'v_alignment',
'title_width' => 'width-100',
'width-100' => true,
),
'zindex' => array(
'input_type' => 'range_simple',
'title' => __('z-index', 'text_doma'),
'orientation' => 'horizontal',
'min' => 0,
'max' => 100,
// 'unit' => '%',
'default' => '0',
'width-100' => true,
'title_width' => 'width-100'
),
)
)//tmpl
);
}
/* ------------------------------------------------------------------------- *
* SCHEDULE CSS RULES FILTERING
/* ------------------------------------------------------------------------- */
add_filter( 'sek_add_css_rules_for_level_options', '\Nimble\sek_add_css_rules_for_level_height', 10, 3 );
function sek_add_css_rules_for_level_height( $rules, $level ) {
$options = empty( $level[ 'options' ] ) ? array() : $level['options'];
if ( empty( $options[ 'height' ] ) )
return $rules;
$height_options = is_array( $options[ 'height' ] ) ? $options[ 'height' ] : array();
// VERTICAL ALIGNMENT
if ( !empty( $height_options[ 'v_alignment' ] ) ) {
if ( !is_array( $height_options[ 'v_alignment' ] ) ) {
sek_error_log( __FUNCTION__ . ' => error => the v_alignment option should be an array( {device} => {alignment} )');
}
$v_alignment_value = is_array( $height_options[ 'v_alignment' ] ) ? $height_options[ 'v_alignment' ] : array();
$v_alignment_value = wp_parse_args( $v_alignment_value, array(
'desktop' => 'center',
'tablet' => '',
'mobile' => ''
));
$mapped_values = array();
foreach ( $v_alignment_value as $device => $align_val ) {
switch ( $align_val ) {
case 'top' :
$mapped_values[$device] = "align-items:flex-start;-webkit-box-align:start;-ms-flex-align:start;";
break;
case 'center' :
$mapped_values[$device] = "align-items:center;-webkit-box-align:center;-ms-flex-align:center;";
break;
case 'bottom' :
$mapped_values[$device] = "align-items:flex-end;-webkit-box-align:end;-ms-flex-align:end";
break;
}
}
$rules = sek_set_mq_css_rules_supporting_vendor_prefixes( array(
'css_rules_by_device' => $mapped_values,
'selector' => '[data-sek-id="'.$level['id'].'"]',
'level_id' => $level['id']
), $rules );
}
// CUSTOM HEIGHT BY DEVICE
if ( !empty( $height_options[ 'height-type' ] ) ) {
if ( 'custom' === $height_options[ 'height-type' ] ) {
$custom_user_height = array_key_exists( 'custom-height', $height_options ) ? $height_options[ 'custom-height' ] : array();
$selector = '[data-sek-id="'.$level['id'].'"]';
if ( !is_array( $custom_user_height ) ) {
sek_error_log( __FUNCTION__ . ' => error => the height option should be an array( {device} => {number}{unit} )', $custom_user_height);
}
$custom_user_height = is_array( $custom_user_height ) ? $custom_user_height : array();
$custom_user_height = wp_parse_args( $custom_user_height, array(
'desktop' => '50%',
'tablet' => '',
'mobile' => ''
));
$height_value = $custom_user_height;
foreach ( $custom_user_height as $device => $num_unit ) {
$numeric = sek_extract_numeric_value( $num_unit );
if ( !empty( $numeric ) ) {
$unit = sek_extract_unit( $num_unit );
$unit = '%' === $unit ? 'vh' : $unit;
$height_value[$device] = $numeric . $unit;
}
}
$rules = sek_set_mq_css_rules(array(
'value' => $height_value,
'css_property' => 'height',
'selector' => $selector,
'level_id' => $level['id']
), $rules );
}
}
// OVERFLOW HIDDEN
// implemented to fix https://github.com/presscustomizr/nimble-builder/issues/365
if ( !empty( $height_options[ 'overflow_hidden' ] ) && sek_booleanize_checkbox_val( $height_options[ 'overflow_hidden' ] ) ) {
$rules[] = array(
'selector' => '[data-sek-id="'.$level['id'].'"]',
'css_rules' => 'overflow:hidden',
'mq' =>null
);
}
// Z-INDEX
// implemented to fix https://github.com/presscustomizr/nimble-builder/issues/365
if ( !empty( $height_options[ 'zindex' ] ) ) {
$numeric = sek_extract_numeric_value( $height_options[ 'zindex' ] );
$rules[] = array(
'selector' => '[data-sek-id="'.$level['id'].'"]',
'css_rules' => 'position:relative;z-index:' . $numeric,
'mq' =>null
);
}
//error_log( print_r($rules, true) );
return $rules;
}
?>
@@ -0,0 +1,34 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_level_section_layout_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_level_section_layout_module',
//'name' => __('Section Layout', 'text_doma'),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'boxed-wide' => array(
'input_type' => 'simpleselect',
'title' => __('Boxed or full width', 'text_doma'),
'refresh_markup' => true,
'refresh_stylesheet' => false,
'default' => 'fullwidth',
'choices' => sek_get_select_options_for_input_id( 'boxed-wide' )
),
/* suspended, needs more thoughts
'boxed-width' => array(
'input_type' => 'range_slider',
'title' => __('Custom boxed width', 'text_doma'),
'orientation' => 'horizontal',
'min' => 500,
'max' => 1600,
'unit' => 'px'
),*/
)
)//tmpl
);
}
?>
@@ -0,0 +1,276 @@
<?php
/* ------------------------------------------------------------------------- *
* SPACING MODULE
/* ------------------------------------------------------------------------- */
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_level_spacing_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_level_spacing_module',
//'name' => __('Spacing options', 'text_doma'),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'pad_marg' => array(
'input_type' => 'spacingWithDeviceSwitcher',
'title' => __('Set padding and margin', 'text_doma'),
'title_width' => 'width-100',
'width-100' => true,
'default' => array( 'desktop' => array() ),
'has_device_switcher' => true
)
)
)
);
}
/* ------------------------------------------------------------------------- *
* SCHEDULE CSS RULES FILTERING
/* ------------------------------------------------------------------------- */
add_filter( 'sek_add_css_rules_for_level_options', '\Nimble\sek_add_css_rules_for_spacing', 10, 2 );
// hook : sek_dyn_css_builder_rules
// @return array() of css rules
function sek_add_css_rules_for_spacing( $rules, $level ) {
$options = empty( $level[ 'options' ] ) ? array() : $level['options'];
//spacing
if ( empty( $options[ 'spacing' ] ) || empty( $options[ 'spacing' ][ 'pad_marg' ] ) )
return $rules;
$pad_marg_options = $options[ 'spacing' ][ 'pad_marg' ];
// array( desktop => array( margin-right => 10, padding-top => 5, unit => 'px' ) )
if ( !is_array( $pad_marg_options ) )
return $rules;
// GENERATE SPACING RULES BY DEVICE
// SPECIFIC CASE FOR COLUMNS see https://github.com/presscustomizr/nimble-builder/issues/665
if ( 'column' === $level['level'] ) {
// Get the parent section level model
$parent_column_section = sek_get_parent_level_model( $level['id'] );
if ( 'no_match' === $parent_column_section ) {
sek_error_log( __FUNCTION__ . ' => $parent_column_section not found for level id : ' . $level['id'] );
return $rules;
}
// COLUMN BREAKPOINT
// define a default breakpoint : 768
$column_breakpoint = Sek_Dyn_CSS_Builder::$breakpoints[Sek_Dyn_CSS_Builder::COLS_MOBILE_BREAKPOINT];//COLS_MOBILE_BREAKPOINT = 'md' <=> 768
// Is there a global custom breakpoint set ?
$global_custom_breakpoint = intval( sek_get_global_custom_breakpoint() );
$has_global_custom_breakpoint = $global_custom_breakpoint >= 1;
// Does the parent section have a custom breakpoint set ?
$section_custom_breakpoint = intval( sek_get_section_custom_breakpoint( array( 'section_model' => $parent_column_section, 'for_responsive_columns' => true ) ) );
$has_section_custom_breakpoint = $section_custom_breakpoint >= 1;
if ( $has_section_custom_breakpoint ) {
$column_breakpoint = $section_custom_breakpoint;
} else if ( $has_global_custom_breakpoint ) {
$column_breakpoint = $global_custom_breakpoint;
}
// PADDING / MARGIN RULES
$rules = sek_generate_css_rules_for_spacing_with_device_switcher( $rules, $pad_marg_options, '[data-sek-id="'.$level['id'].'"]', $custom_column_breakpoint = $column_breakpoint );
// ADAPT COLUMN WIDTH IF A MARGIN IS SET, BY DEVICE
// april 2020 : fixes https://github.com/presscustomizr/nimble-builder/issues/665
// if the column has a positive ( > 0 ) margin-right and / or a margin-left set , let's adapt the column widths so we fit the 100%
foreach (['desktop', 'tablet', 'mobile'] as $_device ) {
$pad_marg_options[$_device] = !empty($pad_marg_options[$_device]) ? $pad_marg_options[$_device] : array();
$rules = sek_process_column_width_for_device( array(
'device' => $_device,
'rules' => $rules,
'pad_marg_options' => $pad_marg_options,
'level' => $level,
'parent_section' => $parent_column_section,
'column_breakpoint' => $column_breakpoint,
'has_global_custom_breakpoint' => $has_global_custom_breakpoint,
'has_section_custom_breakpoint' => $has_section_custom_breakpoint
));
}
} else {
// OTHER LEVEL CASES : SECTION, MODULE
$rules = sek_generate_css_rules_for_spacing_with_device_switcher( $rules, $pad_marg_options, '[data-sek-id="'.$level['id'].'"]' );
}
return $rules;
}
// @return array of $rules
function sek_process_column_width_for_device( $params ) {
$_device = $params['device'];
$rules = $params['rules'];
$pad_marg_options = $params['pad_marg_options'];
$level = $params['level'];
$parent_section = $params['parent_section'];
$column_breakpoint = $params['column_breakpoint'];
$has_global_custom_breakpoint = $params['has_global_custom_breakpoint'];
$has_section_custom_breakpoint = $params['has_section_custom_breakpoint'];
// RECURSIVELY CALCULATE TOTAL HORIZONTAL MARGIN FOR THE DEVICE, OR THE ONE INHERITED FROM WIDER ONES
// implemented for https://github.com/presscustomizr/nimble-builder/issues/665
$total_horizontal_margin_with_unit = sek_get_maybe_inherited_total_horizontal_margins( $_device, $pad_marg_options );
//sek_error_log('soo ?$total_horizontal_margin_with_unit ' .$_device . $level['id'] . ' | ' . $total_horizontal_margin_with_unit );
// When no horizontal margin, no need to add a custom css rule for column width
// + it can break rendering on Edge see https://github.com/presscustomizr/nimble-builder/issues/690
$margin_without_unit = preg_replace("/[^0-9]/", "", $total_horizontal_margin_with_unit );
// WRITE RULES
switch ( $_device ) {
case 'desktop':
// no valid parent section ? stop here
if ( !is_array( $parent_section ) || empty( $parent_section) )
return $rules;
$col_number = ( array_key_exists( 'collection', $parent_section ) && is_array( $parent_section['collection'] ) ) ? count( $parent_section['collection'] ) : 1;
$col_number = 12 < $col_number ? 12 : $col_number;
$col_width_in_percent = 100/$col_number;
$col_suffix = floor( $col_width_in_percent );
//sek_error_log('$parent_section', $parent_section );
// DO WE HAVE A COLUMN WIDTH FOR THE COLUMN ?
// if not, let's get the col suffix from the parent section
// First try to find a width value in options, then look in the previous width property for backward compatibility
// After implementing https://github.com/presscustomizr/nimble-builder/issues/279
$column_options = isset( $level['options'] ) ? $level['options'] : array();
$custom_width = null;
if ( !empty( $column_options['width'] ) && !empty( $column_options['width']['custom-width'] ) ) {
$width_candidate = (float)$column_options['width']['custom-width'];
if ( $width_candidate < 0 || $width_candidate > 100 ) {
sek_error_log( __FUNCTION__ . ' => invalid width value for column id : ' . $level['id'] );
} else {
$custom_width = $width_candidate;
}
} else {
// Backward compat since June 2019
// After implementing https://github.com/presscustomizr/nimble-builder/issues/279
$custom_width = ( !empty( $level[ 'width' ] ) && is_numeric( $level[ 'width' ] ) ) ? $level['width'] : null;
}
if ( !is_null( $custom_width ) ) {
$col_width_in_percent = $custom_width;
}
// bail here if we messed up the column width
if ( $col_suffix < 1 )
return $rules;
// define a default selector
// will be more specific depending on the fact that a local or global custom breakpoint is set
$selector = sprintf('[data-sek-level="location"] [data-sek-id="%1$s"] .sek-sektion-inner > .sek-col-%2$s[data-sek-id="%3$s"]', $parent_section['id'], $col_suffix, $level['id'] );
// SELECTOR DEPENDING ON THE CUSTOM BREAKPOINT
if ( $has_section_custom_breakpoint ) {
// In this case, we need to use ".sek-section-custom-breakpoint-col-{}"
// @see sek_add_css_rules_for_sections_breakpoint
$selector = sprintf('[data-sek-level="location"] [data-sek-id="%1$s"] .sek-sektion-inner > .sek-section-custom-breakpoint-col-%2$s[data-sek-id="%3$s"]', $parent_section['id'], $col_suffix, $level['id'] );
} else if ( $has_global_custom_breakpoint ) {
// In this case, we need to use ".sek-global-custom-breakpoint-col-{}"
// @see sek_add_css_rules_for_sections_breakpoint
$selector = sprintf('[data-sek-level="location"] [data-sek-id="%1$s"] .sek-sektion-inner > .sek-global-custom-breakpoint-col-%2$s[data-sek-id="%3$s"]', $parent_section['id'], $col_suffix, $level['id'] );
}
// Format width in percent with 3 digits after decimal
$col_width_in_percent = number_format( $col_width_in_percent, 3 );
// When no horizontal margin, no need to add a custom css rule for column width
// + it can break rendering on Edge see https://github.com/presscustomizr/nimble-builder/issues/690
if ( 0 === (int)$margin_without_unit ) {
$responsive_css_rules_for_desktop = sprintf( '-ms-flex: 0 0 %1$s%%;flex: 0 0 %1$s%%;max-width: %1$s%%', $col_width_in_percent );
} else {
$responsive_css_rules_for_desktop = sprintf( '-ms-flex: 0 0 calc(%1$s%% - %2$s) ;flex: 0 0 calc(%1$s%% - %2$s);max-width: calc(%1$s%% - %2$s)', $col_width_in_percent, $total_horizontal_margin_with_unit );
}
// we need to override the rule defined in : Sek_Dyn_CSS_Builder::sek_add_rules_for_column_width
// that's why we use a long specific selector here
$rules[] = array(
'selector' => $selector,
'css_rules' => $responsive_css_rules_for_desktop,
'mq' => "(min-width: {$column_breakpoint}px)"
);
break;
case 'tablet':
// the horizontal margin should be subtracted also to the column width of 100%, below the mobile breakpoint: basically the margin should be always subtracted to the column width for each viewport it is set
// @see https://github.com/presscustomizr/nimble-builder/issues/217
// When no horizontal margin, no need to add a custom css rule for column width
// + it can break rendering on Edge see https://github.com/presscustomizr/nimble-builder/issues/690
if ( 0 === (int)$margin_without_unit ) {
$responsive_css_rules_for_100_percent_width = '-ms-flex: 0 0 100%;flex: 0 0 100%;max-width:100%';
} else {
$responsive_css_rules_for_100_percent_width = sprintf( '-ms-flex: 0 0 calc(100%% - %1$s) ;flex: 0 0 calc(100%% - %1$s);max-width: calc(100%% - %1$s)', $total_horizontal_margin_with_unit );
}
//sek_error_log('FOR TABLET =>'. $responsive_css_rules_for_100_percent_width );
$rules[] = array(
'selector' => sprintf('.sek-sektion-inner > [data-sek-id="%1$s"]', $level['id'] ),
'css_rules' => $responsive_css_rules_for_100_percent_width,
'mq' => "(max-width: {$column_breakpoint}px)"
);
break;
// If user define column breakpoint ( the tablet one ) is < to $mobile_breakpoint, make sure $mobile_breakpoint inherit tablet ones
case 'mobile':
$mobile_breakpoint = Sek_Dyn_CSS_Builder::$breakpoints['sm'];//max-width: 576
//$mobile_breakpoint = $mobile_breakpoint >= $column_breakpoint ? $column_breakpoint : $mobile_breakpoint;
//if ( $mobile_breakpoint < $column_breakpoint ) {
// the horizontal margin should be subtracted also to the column width of 100%, below the mobile breakpoint: basically the margin should be always subtracted to the column width for each viewport it is set
// @see https://github.com/presscustomizr/nimble-builder/issues/217
// When no horizontal margin, no need to add a custom css rule for column width
// + it can break rendering on Edge see https://github.com/presscustomizr/nimble-builder/issues/690
if ( 0 === (int)$margin_without_unit ) {
$responsive_css_rules_for_100_percent_width = '-ms-flex: 0 0 100%;flex: 0 0 100%;max-width:100%';
} else {
$responsive_css_rules_for_100_percent_width = sprintf( '-ms-flex: 0 0 calc(%1$s%% - %2$s) ;flex: 0 0 calc(%1$s%% - %2$s);max-width: calc(%1$s%% - %2$s)', 100, $total_horizontal_margin_with_unit );
}
$rules[] = array(
'selector' => sprintf('.sek-sektion-inner > [data-sek-id="%1$s"]', $level['id'] ),
'css_rules' => $responsive_css_rules_for_100_percent_width,
'mq' => "(max-width: {$mobile_breakpoint}px)"
);
//}
break;
}//switch device
//sek_error_log('padding margin', $rules );
return $rules;
}
// Recursive helper to get the total horizontal margin of the current device
// or the one inherited from the parent device
// implemented for https://github.com/presscustomizr/nimble-builder/issues/665
function sek_get_maybe_inherited_total_horizontal_margins( $_device, $pad_marg_options ) {
$total_horizontal_margin_with_unit = '0px';
if ( array_key_exists('margin-left', $pad_marg_options[$_device] ) || array_key_exists('margin-right', $pad_marg_options[$_device] ) ) {
$margin_left = array_key_exists('margin-left', $pad_marg_options[$_device] ) ? $pad_marg_options[$_device]['margin-left'] : 0;
$margin_right = array_key_exists('margin-right', $pad_marg_options[$_device] ) ? $pad_marg_options[$_device]['margin-right'] : 0;
$device_unit = array_key_exists('unit', $pad_marg_options[$_device] ) ? $pad_marg_options[$_device]['unit'] : 'px';
$total_horizontal_margin = (int)$margin_left + (int)$margin_right;
// IF NO HORIZONTAL MARGIN, LET'S STOP HERE
if ( $total_horizontal_margin <= 0 ) {
return $total_horizontal_margin_with_unit;
}
return $total_horizontal_margin . $device_unit;//example : 20px
} else {
$device_hierarchy = array( 'mobile' , 'tablet', 'desktop' );
$device_index = array_search( $_device, $device_hierarchy );
if ( $device_index < ( count( $device_hierarchy ) - 1 ) ) {
$next_device = $device_hierarchy[ $device_index + 1 ];
return sek_get_maybe_inherited_total_horizontal_margins( $next_device, $pad_marg_options );
}
}
return $total_horizontal_margin_with_unit;
}
?>
@@ -0,0 +1,28 @@
<?php
/* ------------------------------------------------------------------------- *
* SPACING MODULE ( SPECIFIC FOR COLUMNS ). See https://github.com/presscustomizr/nimble-builder/issues/868
/* ------------------------------------------------------------------------- */
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_level_spacing_module_for_columns() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_level_spacing_module_for_columns',
//'name' => __('Spacing options', 'text_doma'),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'pad_marg' => array(
'input_type' => 'spacingWithDeviceSwitcher',
'title' => __('Set padding and margin', 'text_doma'),
'title_width' => 'width-100',
'width-100' => true,
'default' => array( 'desktop' => array('padding-left' => '10', 'padding-right' => '10') ),
'has_device_switcher' => true
)
)
)
);
}
?>
@@ -0,0 +1,120 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_level_width_column() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_level_width_column',
//'name' => __('Column width', 'text_doma'),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'custom-width' => array(
'input_type' => 'range_simple',
'title' => __('Column width in percent', 'text_doma'),
'min' => 0,
'max' => 100,
'default' => '_not_set_',
'width-100' => true,
'title_width' => 'width-100',
'refresh_stylesheet' => true
)
// 'h_alignment' => array(
// 'input_type' => 'horizAlignmentWithDeviceSwitcher',
// 'title' => __('Horizontal alignment', 'text_doma'),
// 'default' => array( 'desktop' => 'center' ),
// 'refresh_markup' => false,
// 'refresh_stylesheet' => true,
// 'css_identifier' => 'h_alignment',
// 'title_width' => 'width-100',
// 'width-100' => true,
// )
)
)//tmpl
);
}
/* ------------------------------------------------------------------------- *
* SCHEDULE CSS RULES FILTERING
/* ------------------------------------------------------------------------- */
add_filter( 'sek_add_css_rules_for__column__options', '\Nimble\sek_add_css_rules_for_column_width', 10, 3 );
function sek_add_css_rules_for_column_width( $rules, $column ) {
$options = empty( $column[ 'options' ] ) ? array() : $column['options'];
if ( empty( $options[ 'width' ] ) || !is_array( $options[ 'width' ] ) )
return $rules;
$width_options = is_array( $options[ 'width' ] ) ? $options[ 'width' ] : array();
// ALIGNMENT BY DEVICE
// if ( !empty( $width_options[ 'h_alignment' ] ) ) {
// if ( !is_array( $width_options[ 'h_alignment' ] ) ) {
// sek_error_log( __FUNCTION__ . ' => error => the h_alignment option should be an array( {device} => {alignment} )');
// }
// $h_alignment_value = is_array( $width_options[ 'h_alignment' ] ) ? $width_options[ 'h_alignment' ] : array();
// $h_alignment_value = wp_parse_args( $h_alignment_value, array(
// 'desktop' => '',
// 'tablet' => '',
// 'mobile' => ''
// ));
// $mapped_values = array();
// foreach ( $h_alignment_value as $device => $align_val ) {
// switch ( $align_val ) {
// case 'left' :
// $mapped_values[$device] = "flex-start";
// break;
// case 'center' :
// $mapped_values[$device] = "center";
// break;
// case 'right' :
// $mapped_values[$device] = "flex-end";
// break;
// }
// }
// $rules = sek_set_mq_css_rules( array(
// 'value' => $mapped_values,
// 'css_property' => 'align-self',
// 'selector' => '[data-sek-id="'.$column['id'].'"]'
// ), $rules );
// }
// CUSTOM WIDTH
if ( !empty( $width_options[ 'width-type' ] ) ) {
if ( 'custom' == $width_options[ 'width-type' ] && array_key_exists( 'custom-width', $width_options ) ) {
$user_custom_width_value = $width_options[ 'custom-width' ];
$selector = '[data-sek-id="'.$column['id'].'"]';
if ( !empty( $user_custom_width_value ) && !is_array( $user_custom_width_value ) ) {
sek_error_log( __FUNCTION__ . ' => error => the width option should be an array( {device} => {number}{unit} )');
}
$user_custom_width_value = is_array( $user_custom_width_value ) ? $user_custom_width_value : array();
$user_custom_width_value = wp_parse_args( $user_custom_width_value, array(
'desktop' => '100%',
'tablet' => '',
'mobile' => ''
));
$width_value = $user_custom_width_value;
foreach ( $user_custom_width_value as $device => $num_unit ) {
$numeric = sek_extract_numeric_value( $num_unit );
if ( !empty( $numeric ) ) {
$unit = sek_extract_unit( $num_unit );
$width_value[$device] = $numeric . $unit;
}
}
$rules = sek_set_mq_css_rules(array(
'value' => $width_value,
'css_property' => 'width',
'selector' => $selector,
'level_id' => $column['id']
), $rules );
}
}
//error_log( print_r($rules, true) );
return $rules;
}
?>
@@ -0,0 +1,126 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_level_width_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_level_width_module',
//'name' => __('Width options', 'text_doma'),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'width-type' => array(
'input_type' => 'simpleselect',
'title' => __('Width : 100% or custom', 'text_doma'),
'default' => 'default',
'choices' => sek_get_select_options_for_input_id( 'width-type' )
),
'custom-width' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __('Custom width', 'text_doma'),
'min' => 0,
'max' => 500,
'default' => array( 'desktop' => '100%' ),
'width-100' => true,
'title_width' => 'width-100',
),
'h_alignment' => array(
'input_type' => 'horizAlignmentWithDeviceSwitcher',
'title' => __('Horizontal alignment', 'text_doma'),
'default' => array( 'desktop' => 'center' ),
'refresh_markup' => false,
'refresh_stylesheet' => true,
'css_identifier' => 'h_alignment',
'title_width' => 'width-100',
'width-100' => true,
'notice_after' => __('Horizontal alignment can only be applied with a custom module width < to the parent column\'s width'),
)
)
)//tmpl
);
}
/* ------------------------------------------------------------------------- *
* SCHEDULE CSS RULES FILTERING
/* ------------------------------------------------------------------------- */
add_filter( 'sek_add_css_rules_for__module__options', '\Nimble\sek_add_css_rules_for_module_width', 10, 3 );
function sek_add_css_rules_for_module_width( $rules, $module ) {
$options = empty( $module[ 'options' ] ) ? array() : $module['options'];
if ( empty( $options[ 'width' ] ) || !is_array( $options[ 'width' ] ) )
return $rules;
$width_options = is_array( $options[ 'width' ] ) ? $options[ 'width' ] : array();
// ALIGNMENT BY DEVICE
if ( !empty( $width_options[ 'h_alignment' ] ) ) {
if ( !is_array( $width_options[ 'h_alignment' ] ) ) {
sek_error_log( __FUNCTION__ . ' => error => the h_alignment option should be an array( {device} => {alignment} )');
}
$h_alignment_value = is_array( $width_options[ 'h_alignment' ] ) ? $width_options[ 'h_alignment' ] : array();
$h_alignment_value = wp_parse_args( $h_alignment_value, array(
'desktop' => '',
'tablet' => '',
'mobile' => ''
));
$mapped_values = array();
foreach ( $h_alignment_value as $device => $align_val ) {
switch ( $align_val ) {
case 'left' :
$mapped_values[$device] = "align-self:flex-start;-ms-grid-row-align:start;-ms-flex-item-align:start;";
break;
case 'center' :
$mapped_values[$device] = "align-self:center;-ms-grid-row-align:center;-ms-flex-item-align:center;";
break;
case 'right' :
$mapped_values[$device] = "align-self:flex-end;-ms-grid-row-align:end;-ms-flex-item-align:end;";
break;
}
}
$rules = sek_set_mq_css_rules_supporting_vendor_prefixes( array(
'css_rules_by_device' => $mapped_values,
'selector' => '[data-sek-id="'.$module['id'].'"]',
'level_id' => $module['id']
), $rules );
}
// CUSTOM WIDTH BY DEVICE
if ( !empty( $width_options[ 'width-type' ] ) ) {
if ( 'custom' == $width_options[ 'width-type' ] && array_key_exists( 'custom-width', $width_options ) ) {
$user_custom_width_value = $width_options[ 'custom-width' ];
$selector = '[data-sek-id="'.$module['id'].'"]';
if ( !empty( $user_custom_width_value ) && !is_array( $user_custom_width_value ) ) {
sek_error_log( __FUNCTION__ . ' => error => the width option should be an array( {device} => {number}{unit} )');
}
$user_custom_width_value = is_array( $user_custom_width_value ) ? $user_custom_width_value : array();
$user_custom_width_value = wp_parse_args( $user_custom_width_value, array(
'desktop' => '100%',
'tablet' => '',
'mobile' => ''
));
$width_value = $user_custom_width_value;
foreach ( $user_custom_width_value as $device => $num_unit ) {
$numeric = sek_extract_numeric_value( $num_unit );
if ( !empty( $numeric ) ) {
$unit = sek_extract_unit( $num_unit );
$width_value[$device] = $numeric . $unit;
}
}
$rules = sek_set_mq_css_rules(array(
'value' => $width_value,
'css_property' => 'width',
'selector' => $selector,
'level_id' => $module['id']
), $rules );
}
}
//error_log( print_r($rules, true) );
return $rules;
}
?>
@@ -0,0 +1,171 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_level_width_section() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_level_width_section',
//'name' => __('Width options', 'text_doma'),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
// 'starting_value' => array(
// 'outer-section-width' => '100%',
// 'inner-section-width' => '100%'
// ),
'tmpl' => array(
'item-inputs' => array(
'use-custom-outer-width' => array(
'input_type' => 'nimblecheck',
'title' => __('Define a custom outer width for this section', 'text_doma'),
'default' => 0,
'title_width' => 'width-80',
'input_width' => 'width-20',
'refresh_markup' => true,
'refresh_stylesheet' => true
),
'outer-section-width' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __('Outer section width', 'text_doma'),
'min' => 0,
'max' => 1500,
'default' => array( 'desktop' => '100%' ),
'width-100' => true,
'title_width' => 'width-100'
),
'use-custom-inner-width' => array(
'input_type' => 'nimblecheck',
'title' => __('Define a custom inner width for this section', 'text_doma'),
'default' => 0,
'title_width' => 'width-80',
'input_width' => 'width-20',
'refresh_markup' => true,
'refresh_stylesheet' => true
),
'inner-section-width' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __('Inner section width', 'text_doma'),
'min' => 0,
'max' => 1500,
'default' => array( 'desktop' => '100%' ),
'width-100' => true,
'title_width' => 'width-100'
)
)
)//tmpl
);
}
/* ------------------------------------------------------------------------- *
* SCHEDULE CSS RULES FILTERING
/* ------------------------------------------------------------------------- */
// Data structure since v1.1.0. Oct 2018
// [width] => Array
// (
// [use-custom-outer-width] => 1
// [outer-section-width] => Array
// (
// [desktop] => 99%
// [mobile] => 66%
// [tablet] => 93%
// )
// [use-custom-inner-width] => 1
// [inner-section-width] => Array
// (
// [desktop] => 98em
// [tablet] => 11em
// [mobile] => 8em
// )
// )
// The inner and outer widths can be set at 3 levels :
// 1) global
// 2) skope ( local )
// 3) section
// And for 3 different types of devices : desktop, tablet, mobiles.
//
// Nimble implements an inheritance for both logic, determined by the css selectors, and the media query rules.
// For example, an inner width of 85% applied for skope will win against the global one, but can be overriden by a specific inner width set at a section level.
add_filter( 'sek_add_css_rules_for__section__options', '\Nimble\sek_add_css_rules_for_section_width', 10, 3 );
function sek_add_css_rules_for_section_width( $rules, $section ) {
$options = empty( $section[ 'options' ] ) ? array() : $section['options'];
if ( empty( $options[ 'width' ] ) || !is_array( $options[ 'width' ] ) )
return $rules;
$width_options = $options[ 'width' ];
$user_defined_widths = array();
if ( !empty( $width_options[ 'use-custom-outer-width' ] ) && true === sek_booleanize_checkbox_val( $width_options[ 'use-custom-outer-width' ] ) ) {
$user_defined_widths['outer-section-width'] = 'body .nb-loc [data-sek-id="'.$section['id'].'"]';
}
if ( !empty( $width_options[ 'use-custom-inner-width' ] ) && true === sek_booleanize_checkbox_val( $width_options[ 'use-custom-inner-width' ] ) ) {
$user_defined_widths['inner-section-width'] = 'body .nb-loc [data-sek-id="'.$section['id'].'"] > .sek-container-fluid > .sek-sektion-inner';
}
if ( empty( $user_defined_widths ) )
return $rules;
// Note that the option 'outer-section-width' and 'inner-section-width' can be empty when set to a value === default
// @see js czr_setions::normalizeAndSanitizeSingleItemInputValues()
foreach ( $user_defined_widths as $width_opt_name => $selector ) {
if ( !empty( $width_options[ $width_opt_name ] ) && !is_array( $width_options[ $width_opt_name ] ) ) {
sek_error_log( __FUNCTION__ . ' => error => the width option should be an array( {device} => {number}{unit} )');
}
// $width_options[ $width_opt_name ] should be an array( {device} => {number}{unit} )
// If not set in the width options , it means that it is equal to default
$user_custom_width_value = ( empty( $width_options[ $width_opt_name ] ) || !is_array( $width_options[ $width_opt_name ] ) ) ? array('desktop' => '100%') : $width_options[ $width_opt_name ];
$user_custom_width_value = wp_parse_args( $user_custom_width_value, array(
'desktop' => '100%',
'tablet' => '',
'mobile' => ''
));
$max_width_value = $user_custom_width_value;
$margin_value = array();
foreach ( $user_custom_width_value as $device => $num_unit ) {
$padding_of_the_parent_container[$device] = 'inherit';
$numeric = sek_extract_numeric_value( $num_unit );
if ( !empty( $numeric ) ) {
$unit = sek_extract_unit( $num_unit );
$max_width_value[$device] = $numeric . $unit;
$margin_value[$device] = '0 auto';
}
}
$rules = sek_set_mq_css_rules(array(
'value' => $max_width_value,
'css_property' => 'max-width',
'selector' => $selector,
'level_id' => $section['id']
), $rules );
// when customizing the inner section width, we need to reset the default padding rules for .sek-container-fluid {padding-right:10px; padding-left:10px}
// @see assets/front/scss/_grid.scss
if ( 'inner-section-width' === $width_opt_name ) {
$rules = sek_set_mq_css_rules(array(
'value' => $padding_of_the_parent_container,
'css_property' => 'padding-left',
'selector' => 'body .nb-loc [data-sek-id="'.$section['id'].'"] > .sek-container-fluid',
'level_id' => $section['id']
), $rules );
$rules = sek_set_mq_css_rules(array(
'value' => $padding_of_the_parent_container,
'css_property' => 'padding-right',
'selector' => 'body .nb-loc [data-sek-id="'.$section['id'].'"] > .sek-container-fluid',
'level_id' => $section['id']
), $rules );
}
if ( !empty( $margin_value ) ) {
$rules = sek_set_mq_css_rules(array(
'value' => $margin_value,
'css_property' => 'margin',
'selector' => $selector,
'level_id' => $section['id']
), $rules );
}
}//foreach
return $rules;
}
?>
@@ -0,0 +1,31 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_level_anchor_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_level_anchor_module',
//'name' => __('Set a custom anchor', 'text_doma'),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'custom_anchor' => array(
'input_type' => 'text',
'title' => __('Custom anchor', 'text_doma'),
'default' => '',
'notice_after' => __('Note : white spaces, numbers and special characters are not allowed when setting a CSS ID.'),
'refresh_markup' => true
),
'custom_css_classes' => array(
'input_type' => 'text',
'title' => __('Custom CSS classes', 'text_doma'),
'default' => '',
'notice_after' => __('Note : you can add several custom CSS classes separated by a white space.'),
'refresh_markup' => true
)
)
)//tmpl
);
}
?>
@@ -0,0 +1,127 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_level_visibility_module() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_level_visibility_module',
//'name' => __('Set visibility on devices', 'text_doma'),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'desktops' => array(
'input_type' => 'nimblecheck',
'title' => sprintf('<i class="material-icons" style="font-size: 1.2em;">desktop_mac</i> %1$s', __('Visible on desktop devices', 'text_doma') ),
'default' => 1,
'title_width' => 'width-80',
'input_width' => 'width-20',
'refresh_markup' => false,
'refresh_stylesheet' => true
),
'tablets' => array(
'input_type' => 'nimblecheck',
'title' => sprintf('<i class="material-icons" style="font-size: 1.2em;">tablet_mac</i> %1$s', __('Visible on tablet devices', 'text_doma') ),
'default' => 1,
'title_width' => 'width-80',
'input_width' => 'width-20',
'refresh_markup' => false,
'refresh_stylesheet' => true
),
'mobiles' => array(
'input_type' => 'nimblecheck',
'title' => sprintf('<i class="material-icons" style="font-size: 1.2em;">phone_iphone</i> %1$s', __('Visible on mobile devices', 'text_doma') ),
'default' => 1,
'title_width' => 'width-80',
'input_width' => 'width-20',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'notice_after' => __('Note that those options are not applied during the live customization of your site, but only when the changes are published.', 'text_domain')
),
)
)//tmpl
);
}
/* ------------------------------------------------------------------------- *
* SCHEDULE CSS RULES FILTERING
/* ------------------------------------------------------------------------- */
// levels are visible by default
// the default CSS rule should be :
// @media (min-width:768px){
// [data-sek-level="location"] .sek-hidden-on-desktops { display: none; }
// }
// @media (min-width:575px) and (max-width:767px){
// [data-sek-level="location"] .sek-hidden-on-tablets { display: none; }
// }
// @media (max-width:575px){
// [data-sek-level="location"] .sek-hidden-on-mobiles { display: none; }
// }
//
// Dec 2019 : since issue https://github.com/presscustomizr/nimble-builder/issues/555, we use a dynamic CSS rule generation instead of static CSS
add_filter( 'sek_add_css_rules_for_level_options', '\Nimble\sek_add_css_rules_for_level_visibility', 10, 3 );
function sek_add_css_rules_for_level_visibility( $rules, $level ) {
$options = empty( $level[ 'options' ] ) ? array() : $level['options'];
if ( empty( $options[ 'visibility' ] ) )
return $rules;
$visibility_options = is_array( $options[ 'visibility' ] ) ? $options[ 'visibility' ] : array();
// Get the default breakpoint values
$mobile_breakpoint = Sek_Dyn_CSS_Builder::$breakpoints['sm'];// 576
$tablet_breakpoint = Sek_Dyn_CSS_Builder::$breakpoints['md'];// 768
// nested section should inherit the custom breakpoint of the parent
// @fixes https://github.com/presscustomizr/nimble-builder/issues/554
$custom_tablet_breakpoint = intval( sek_get_closest_section_custom_breakpoint( array( 'searched_level_id' => $level['id'] ) ) );
if ( $custom_tablet_breakpoint >= 1 ) {
$tablet_breakpoint = $custom_tablet_breakpoint;
}
// If user define breakpoint ( => always for tablet ) is < to $mobile_breakpoint, make sure $mobile_breakpoint is reset to tablet_breakpoint
$mobile_breakpoint = $mobile_breakpoint >= $tablet_breakpoint ? $tablet_breakpoint : $mobile_breakpoint;
$visibility_value = array(
'desktop' => ( array_key_exists('desktops', $visibility_options ) && true !== sek_booleanize_checkbox_val( $visibility_options['desktops'] ) ) ? 'hide' : '',
'tablet' => ( array_key_exists('tablets', $visibility_options ) && true !== sek_booleanize_checkbox_val( $visibility_options['tablets'] ) ) ? 'hide' : '',
'mobile' => ( array_key_exists('mobiles', $visibility_options ) && true !== sek_booleanize_checkbox_val( $visibility_options['mobiles'] ) ) ? 'hide' : ''
);
$mob_bp_val = $mobile_breakpoint - 1;// -1 to avoid "blind" spots @see https://github.com/presscustomizr/nimble-builder/issues/551
foreach ( $visibility_value as $device => $visibility_val ) {
if ( 'hide' !== $visibility_val )
continue;
switch( $device ) {
case 'desktop' :
$media_qu = "(min-width:{$tablet_breakpoint}px)";
break;
case 'tablet' :
$tab_bp_val = $tablet_breakpoint - 1;// -1 to avoid "blind" spots @see https://github.com/presscustomizr/nimble-builder/issues/551
if ( $mobile_breakpoint >= ( $tab_bp_val ) ) {
$media_qu = "(max-width:{$tab_bp_val}px)";
} else {
$media_qu = "(min-width:{$mob_bp_val}px) and (max-width:{$tab_bp_val}px)";
}
break;
case 'mobile' :
$media_qu = "(max-width:{$mob_bp_val}px)";
break;
}
/* WHEN CUSTOMIZING MAKE SURE WE CAN SEE THE LEVELS, EVEN IF SETUP TO BE HIDDEN WITH THE CURRENT PREVIEWED DEVICE */
$rules[] = array(
'selector' => '.customizer-preview [data-sek-level="location"] [data-sek-id="'.$level['id'].'"]',
'css_rules' => 'display: -ms-flexbox;display: -webkit-box;display: flex;-webkit-filter: grayscale(50%);filter: grayscale(50%);-webkit-filter: gray;filter: gray;opacity: 0.7;',
'mq' => $media_qu
);
$rules[] = array(
'selector' => '[data-sek-level="location"] [data-sek-id="'.$level['id'].'"]',
'css_rules' => 'display:none',
'mq' => $media_qu
);
}
return $rules;
}
?>
@@ -0,0 +1,128 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_level_breakpoint_module() {
$global_custom_breakpoint = sek_get_global_custom_breakpoint();
return array(
'dynamic_registration' => true,
'module_type' => 'sek_level_breakpoint_module',
//'name' => __('Set a custom breakpoint', 'text_doma'),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'use-custom-breakpoint' => array(
'input_type' => 'nimblecheck',
'title' => __('Use a custom breakpoint for responsive columns', 'text_doma'),
'default' => 0,
'title_width' => 'width-80',
'input_width' => 'width-20',
'refresh_markup' => true,
'refresh_stylesheet' => true
),
'custom-breakpoint' => array(
'input_type' => 'range_simple',
'title' => __( 'Define a custom breakpoint in pixels', 'text_doma' ),
'default' => $global_custom_breakpoint > 0 ? $global_custom_breakpoint : 768,
'min' => 1,
'max' => 2000,
'step' => 1,
'refresh_markup' => true,
'refresh_stylesheet' => true,
//'css_identifier' => 'letter_spacing',
'width-100' => true,
'title_width' => 'width-100',
'notice_after' => __( 'This is the viewport width from which columns are rearranged vertically. The default breakpoint is 768px.', 'text_doma')
),//0,
'apply-to-all' => array(
'input_type' => 'nimblecheck',
'title' => __('Apply this breakpoint to all by-device customizations', 'text_doma'),
'default' => 0,
'title_width' => 'width-80',
'input_width' => 'width-20',
'notice_after' => sprintf(
__( '%s When enabled, this custom breakpoint is applied not only to responsive columns but also to all by-device customizations, like alignment for example.', 'text_doma'),
'<span class="sek-mobile-device-icons"><i class="sek-switcher preview-desktop"></i>&nbsp;<i class="sek-switcher preview-tablet"></i>&nbsp;<i class="sek-switcher preview-mobile"></i></span>'
)
),
'reverse-col-at-breakpoint' => array(
'input_type' => 'nimblecheck',
'title' => __('Reverse the columns direction on devices smaller than the breakpoint.', 'text_doma'),
'default' => 0,
'title_width' => 'width-80',
'input_width' => 'width-20',
'refresh_markup' => true,
'refresh_stylesheet' => true
)
)
)//tmpl
);
}
/* ------------------------------------------------------------------------- *
* SCHEDULE CSS RULES FILTERING
/* ------------------------------------------------------------------------- */
add_filter( 'sek_add_css_rules_for__section__options', '\Nimble\sek_add_css_rules_for_sections_breakpoint', 10, 3 );
function sek_add_css_rules_for_sections_breakpoint( $rules, $section ) {
// nested section should inherit the custom breakpoint of the parent
// @fixes https://github.com/presscustomizr/nimble-builder/issues/554
// Is there a custom breakpoint set by a parent section?
// Order :
// 1) custom breakpoint set on a nested section
// 2) custom breakpoint set on a regular section
// the 'for_responsive_columns' param has been introduced for https://github.com/presscustomizr/nimble-builder/issues/564
// so we can differentiate when the custom breakpoint is requested for column responsiveness or for css rules generation
// when for columns, we always apply the custom breakpoint defined by the user
// otherwise, when generating CSS rules like alignment, the custom breakpoint is applied if user explicitely checked the 'apply_to_all' option
// 'for_responsive_columns' is set to true when sek_get_closest_section_custom_breakpoint() is invoked from Nimble_Manager()::render()
$custom_breakpoint = intval( sek_get_closest_section_custom_breakpoint( array(
'searched_level_id' => $section['id'],
'for_responsive_columns' => true
)));
// sek_error_log('SECTION ??', $section );
// sek_error_log('$custom_breakpoint??', is_int($custom_breakpoint) );
if ( is_int($custom_breakpoint) && $custom_breakpoint > 0 ) {
$col_number = ( array_key_exists( 'collection', $section ) && is_array( $section['collection'] ) ) ? count( $section['collection'] ) : 1;
$col_number = 12 < $col_number ? 12 : $col_number;
$col_width_in_percent = 100/$col_number;
$col_suffix = floor( $col_width_in_percent );
$responsive_css_rules = sprintf( '-ms-flex: 0 0 %1$s%%;flex: 0 0 %1$s%%;max-width: %1$s%%', $col_suffix );
$rules[] = array(
'selector' => '[data-sek-id="'.$section['id'].'"] .sek-sektion-inner > .sek-section-custom-breakpoint-col-'.$col_suffix,
'css_rules' => $responsive_css_rules,
'mq' => "(min-width: {$custom_breakpoint}px)"
);
}
// maybe set the reverse column order on mobile devices ( smaller than the breakpoint )
if ( isset( $section[ 'options' ] ) && isset( $section[ 'options' ]['breakpoint'] ) && array_key_exists( 'reverse-col-at-breakpoint', $section[ 'options' ]['breakpoint'] ) ) {
$default_md_breakpoint = '768';
if ( class_exists('\Nimble\Sek_Dyn_CSS_Builder') ) {
$default_md_breakpoint = Sek_Dyn_CSS_Builder::$breakpoints['md'];
}
$breakpoint = $custom_breakpoint > 0 ? $custom_breakpoint : $default_md_breakpoint;
$breakpoint = $breakpoint - 1;//fixes https://github.com/presscustomizr/nimble-builder/issues/559
// selector uses ">" syntax to make sure the reverse-column rule is not inherited by a nested section
$rules[] = array(
'selector' => '[data-sek-id="'.$section['id'].'"] > .sek-container-fluid > .sek-sektion-inner',
'css_rules' => "-ms-flex-direction: column-reverse;flex-direction: column-reverse;",
'mq' => "(max-width: {$breakpoint}px)"
);
// when using column-reverse for the parent section we need to set the flex:auto to children column
// otherwise, columns may lose their height. See https://github.com/presscustomizr/nimble-builder/issues/622
$rules[] = array(
'selector' => '[data-sek-id="'.$section['id'].'"] .sek-sektion-inner > [data-sek-level="column"]',
'css_rules' => '-webkit-box-flex: 1;-ms-flex: auto;flex: auto;',
'mq' => "(max-width: {$breakpoint}px)"
);
}
return $rules;
}
?>
@@ -0,0 +1,32 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_level_cust_css_level() {
$pro_text = '';
if ( !sek_is_pro() ) {
$pro_text = sek_get_pro_notice_for_czr_input( __('custom CSS on a per level basis (section, column, module ).', 'text-doma') );
}
return array(
'dynamic_registration' => true,
'module_type' => 'sek_level_cust_css_level',
//'name' => __('Width options', 'text_doma'),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
// 'starting_value' => array(
// 'outer-section-width' => '100%',
// 'inner-section-width' => '100%'
// ),
'tmpl' => array(
'item-inputs' => array(
'custom_css' => array(
'input_type' => 'inactive',
'default' => '',
'refresh_markup' => false,
'refresh_stylesheet' => false,
'html_before' => $pro_text . '<hr/>'
)
)
)//tmpl
);
}
?>
@@ -0,0 +1,32 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_level_animation_module() {
$pro_text = '';
if ( !sek_is_pro() ) {
$pro_text = sek_get_pro_notice_for_czr_input( __('add animations to anything', 'text-doma') );
}
return array(
'dynamic_registration' => true,
'module_type' => 'sek_level_animation_module',
//'name' => __('Width options', 'text_doma'),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
// 'starting_value' => array(
// 'outer-section-width' => '100%',
// 'inner-section-width' => '100%'
// ),
'tmpl' => array(
'item-inputs' => array(
'anim_type' => array(
'input_type' => 'inactive',
'default' => '',
'refresh_markup' => false,
'refresh_stylesheet' => false,
'html_before' => $pro_text . '<hr/>'
)
)
)//tmpl
);
}
?>
@@ -0,0 +1,30 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_local_template() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_local_template',
//'name' => __('Template for the current page', 'text_doma'),
'starting_value' => array(),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'local_template' => array(
'input_type' => 'simpleselect',
'title' => __('Select a template', 'text_doma'),
'default' => 'default',
'width-100' => true,
'choices' => array(
'default' => __('Default theme template','text_doma'),
'nimble_template' => __('Nimble Builder template','text_doma')
),
'refresh_preview' => true,
'notice_before_title' => __('Use Nimble Builder\'s template to display content created only with Nimble Builder on this page. Your theme\'s default template will be overriden','text_doma')
//'notice_after' => __('When you select Nimble Builder\'s template, only the Nimble sections are displayed.')
)
)
)//tmpl
);
}
?>
@@ -0,0 +1,193 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_local_widths() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_local_widths',
//'name' => __('Width settings of the sections in the current page', 'text_doma'),
// 'starting_value' => array(
// 'outer-section-width' => '100%',
// 'inner-section-width' => '100%'
// ),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'use-custom-outer-width' => array(
'input_type' => 'nimblecheck',
'title' => __('Define a custom outer width for the sections of this page', 'text_doma'),
'default' => 0,
'title_width' => 'width-80',
'input_width' => 'width-20',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'refresh_preview' => true,
'notice_before_title' => sprintf( __( 'The inner and outer widths of the sections displayed in this page can be set here. It will override in the %1$s. You can also set a custom inner and outer width for each single sections.', 'text_doma'),
sprintf( '<a href="#" onclick="%1$s">%2$s</a>',
"javascript:wp.customize.section('__globalOptionsSectionId', function( _s_ ){ _s_.focus(); })",
__('site wide options', 'text_doma')
)
),
),
'outer-section-width' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __('Outer sections width', 'text_doma'),
'min' => 0,
'max' => 1500,
'default' => array( 'desktop' => '100%' ),
'width-100' => true,
'title_width' => 'width-100',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'notice_after' => __('This option will be inherited by all Nimble sections of the currently previewed page, unless for sections with a specific width option.')
),
'use-custom-inner-width' => array(
'input_type' => 'nimblecheck',
'title' => __('Define a custom inner width for the sections of this page', 'text_doma'),
'default' => 0,
'title_width' => 'width-80',
'input_width' => 'width-20',
'refresh_markup' => false,
'refresh_stylesheet' => true
),
'inner-section-width' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __('Inner sections width', 'text_doma'),
'min' => 0,
'max' => 1500,
'default' => array( 'desktop' => '100%' ),
'width-100' => true,
'title_width' => 'width-100',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'notice_after' => __('This option will be inherited by all Nimble sections of the currently previewed page, unless for sections with a specific width option.')
)
)
)//tmpl
);
}
// Add user local custom inner and outer widths for the sections
// Data structure since v1.1.0. Oct 2018
// [width] => Array
// (
// [use-custom-outer-width] => 1
// [outer-section-width] => Array
// (
// [desktop] => 99%
// [mobile] => 66%
// [tablet] => 93%
// )
// [use-custom-inner-width] => 1
// [inner-section-width] => Array
// (
// [desktop] => 98em
// [tablet] => 11em
// [mobile] => 8em
// )
// )
// The inner and outer widths can be set at 3 levels :
// 1) global
// 2) skope ( local )
// 3) section
// And for 3 different types of devices : desktop, tablet, mobiles.
//
// Nimble implements an inheritance for both logic, determined by the css selectors, and the media query rules.
// For example, an inner width of 85% applied for skope will win against the global one, but can be overriden by a specific inner width set at a section level.
add_filter( 'nimble_get_dynamic_stylesheet', '\Nimble\sek_add_raw_local_widths_css', 10, 2 );
// @filter 'nimble_get_dynamic_stylesheet'
// this filter is declared in Sek_Dyn_CSS_Builder::get_stylesheet() with 2 parameters
// apply_filters( 'nimble_get_dynamic_stylesheet', $css, $this->is_global_stylesheet );
function sek_add_raw_local_widths_css( $css, $is_global_stylesheet ) {
// the local width rules must be restricted to the local stylesheet
if ( $is_global_stylesheet )
return $css;
$css = is_string( $css ) ? $css : '';
// 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_options = sek_get_skoped_seks( !empty( $_POST['local_skope_id'] ) ? sanitize_text_field($_POST['local_skope_id']) : skp_build_skope_id() );
if ( !is_array( $local_options ) || empty( $local_options['local_options']) || empty( $local_options['local_options']['widths'] ) )
return $css;
$width_options = $local_options['local_options']['widths'];
$user_defined_widths = array();
if ( !empty( $width_options[ 'use-custom-outer-width' ] ) && true === sek_booleanize_checkbox_val( $width_options[ 'use-custom-outer-width' ] ) ) {
$user_defined_widths['outer-section-width'] = '.nb-loc [data-sek-level="section"]:not([data-sek-is-nested="true"])';
}
if ( !empty( $width_options[ 'use-custom-inner-width' ] ) && true === sek_booleanize_checkbox_val( $width_options[ 'use-custom-inner-width' ] ) ) {
$user_defined_widths['inner-section-width'] = '.nb-loc [data-sek-level="section"] > .sek-container-fluid > .sek-sektion-inner';
}
$rules = array();
// Note that the option 'outer-section-width' and 'inner-section-width' can be empty when set to a value === default
// @see js czr_setions::normalizeAndSanitizeSingleItemInputValues()
foreach ( $user_defined_widths as $width_opt_name => $selector ) {
if ( !empty( $width_options[ $width_opt_name ] ) && !is_array( $width_options[ $width_opt_name ] ) ) {
sek_error_log( __FUNCTION__ . ' => error => the width option should be an array( {device} => {number}{unit} )');
}
// $width_options[ $width_opt_name ] should be an array( {device} => {number}{unit} )
// If not set in the width options , it means that it is equal to default
$user_custom_width_value = ( empty( $width_options[ $width_opt_name ] ) || !is_array( $width_options[ $width_opt_name ] ) ) ? array('desktop' => '100%') : $width_options[ $width_opt_name ];
$user_custom_width_value = wp_parse_args( $user_custom_width_value, array(
'desktop' => '100%',
'tablet' => '',
'mobile' => ''
));
$max_width_value = $user_custom_width_value;
$margin_value = array();
foreach ( $user_custom_width_value as $device => $num_unit ) {
$numeric = sek_extract_numeric_value( $num_unit );
$padding_of_the_parent_container[$device] = 'inherit';
if ( !empty( $numeric ) ) {
$unit = sek_extract_unit( $num_unit );
$max_width_value[$device] = $numeric . $unit;
$margin_value[$device] = '0 auto';
}
}
$rules = sek_set_mq_css_rules(array(
'value' => $max_width_value,
'css_property' => 'max-width',
'selector' => $selector,
'level_id' => '_excluded_from_section_custom_breakpoint_' //<= introduced in dec 2019 : https://github.com/presscustomizr/nimble-builder/issues/564
), $rules );
// when customizing the inner section width, we need to reset the default padding rules for .sek-container-fluid {padding-right:10px; padding-left:10px}
// @see assets/front/scss/_grid.scss
if ( 'inner-section-width' === $width_opt_name ) {
$rules = sek_set_mq_css_rules(array(
'value' => $padding_of_the_parent_container,
'css_property' => 'padding-left',
'selector' => '.nb-loc [data-sek-level="section"] > .sek-container-fluid',
'level_id' => '_excluded_from_section_custom_breakpoint_' //<= introduced in dec 2019 : https://github.com/presscustomizr/nimble-builder/issues/564
), $rules );
$rules = sek_set_mq_css_rules(array(
'value' => $padding_of_the_parent_container,
'css_property' => 'padding-right',
'selector' => '.nb-loc [data-sek-level="section"] > .sek-container-fluid',
'level_id' => '_excluded_from_section_custom_breakpoint_' //<= introduced in dec 2019 : https://github.com/presscustomizr/nimble-builder/issues/564
), $rules );
}
if ( !empty( $margin_value ) ) {
$rules = sek_set_mq_css_rules(array(
'value' => $margin_value,
'css_property' => 'margin',
'selector' => $selector,
'level_id' => '_excluded_from_section_custom_breakpoint_' //<= introduced in dec 2019 : https://github.com/presscustomizr/nimble-builder/issues/564
), $rules );
}
}//foreach
$width_options_css = Sek_Dyn_CSS_Builder::sek_generate_css_stylesheet_for_a_set_of_rules( $rules );
return is_string( $width_options_css ) ? $css . $width_options_css : $css;
}
?>
@@ -0,0 +1,52 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_local_custom_css() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_local_custom_css',
//'name' => __('Custom CSS for the sections of the current page', 'text_doma'),
// 'starting_value' => array(
// 'local_custom_css' => sprintf( '/* %1$s */', __('Add your own CSS code here', 'text_doma' ) )
// ),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'local_custom_css' => array(
'input_type' => 'code_editor',
'title' => __( 'Custom css' , 'text_doma' ),
'default' => sprintf( '/* %1$s */', __('Add your own CSS code here', 'text_doma' ) ),
'code_type' => 'text/css',// 'text/html' //<= use 'text/css' to instantiate the code mirror as CSS editor, which by default will be an HTML editor
'notice_before_title' => __('The CSS code added below will only be applied to the currently previewed page, not site wide.', 'text_doma'),
'refresh_markup' => false,
'refresh_stylesheet' => false,
'refresh_preview' => false,
'refresh_css_via_post_message' => true
)
)
)//tmpl
);
}
// add user local custom css
// this filter is declared in Sek_Dyn_CSS_Builder::get_stylesheet() with 2 parameters
// apply_filters( 'nimble_get_dynamic_stylesheet', $css, $this->is_global_stylesheet );
add_filter( 'nimble_get_dynamic_stylesheet', '\Nimble\sek_add_raw_local_custom_css', 10, 2 );
//@filter 'nimble_get_dynamic_stylesheet'
function sek_add_raw_local_custom_css( $css, $is_global_stylesheet ) {
// the local custom css must be restricted to the local stylesheet
if ( $is_global_stylesheet )
return $css;
// 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_options = sek_get_skoped_seks( !empty( $_POST['local_skope_id'] ) ? sanitize_text_field($_POST['local_skope_id']) : skp_build_skope_id() );
if ( is_array( $local_options ) && !empty( $local_options['local_options']) && !empty( $local_options['local_options']['custom_css'] ) ) {
$options = $local_options['local_options']['custom_css'];
if ( !empty( $options['local_custom_css'] ) ) {
$css .= $options['local_custom_css'];
}
}
return $css;
}
?>
@@ -0,0 +1,44 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_local_reset() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_local_reset',
//'name' => __('Reset the sections of the current page', 'text_doma'),
'tmpl' => array(
'item-inputs' => array(
// Added April 2021 for #478
// 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()
'inherit_group_scope' => array(
'input_type' => 'nimblecheck',
'title' => __('After removal : inherit the site template if specified', 'text_doma'),
'default' => 1,
'title_width' => 'width-80',
'input_width' => 'width-20',
'refresh_markup' => false,
'refresh_stylesheet' => false,
'refresh_preview' => true,
'html_after' => sprintf('<span class="czr-notice"><i class="far fa-lightbulb"></i> <a href="%2$s" target="_blank" rel="noopener noreferrer">%1$s</a></span>',
__('How to use site templates with Nimble Builder ?'),
'https://docs.presscustomizr.com/article/428-how-to-use-site-templates-with-nimble-builder'
),
'notice_after' => __('If a site template is defined for this context, this page will inherit the site template by default, unless this option is unchecked.', 'text_doma'),
//'notice_after' => __( 'Check this option if you want to keep the existing sections of this page, and combine them with the imported ones.', 'text_doma'),
),
'reset_local' => array(
'input_type' => 'reset_button',
'title' => __( 'Remove all sections and Nimble Builder options of this page' , 'text_doma' ),
'scope' => 'local',
'html_before' => '<hr/>',
'notice_after' => __('This will remove the options and sections created for the currently previewed page only. All other sections and options in other contexts will be preserved.', 'text_doma'),
'refresh_markup' => false,
'refresh_stylesheet' => false,
)
)
)//tmpl
);
}
?>
@@ -0,0 +1,36 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_local_performances() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_local_performances',
//'name' => __('Performance optimizations', 'text_doma'),
// 'starting_value' => array(
// 'local_custom_css' => sprintf( '/* %1$s */', __('Add your own CSS code here', 'text_doma' ) )
// ),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'local-img-smart-load' => array(
'input_type' => 'simpleselect',
'title' => __('Select how you want to load the images in the sections of this page.', 'text_doma'),
'default' => 'inherit',
'choices' => array(
'inherit' => __('Inherit the site wide option', 'text_domain' ),
'yes' => __('Load images on scroll ( optimized )', 'text_domain' ),
'no' => __('Load all images on page load ( not optimized )', 'text_domain' )
),
//'refresh_preview' => true,
'notice_after' => sprintf('%1$s <br/><strong>%2$s</strong>',
__( 'When you select "Load images on scroll", images below the window are loaded dynamically when scrolling. This can improve performance by reducing the weight of long web pages including multiple images.', 'text_dom'),
__( 'If you use a cache plugin, make sure that this option does not conflict with your caching options.', 'text_dom')
),
'width-100' => true,
'title_width' => 'width-100'
)
)
)//tmpl
);
}
?>
@@ -0,0 +1,44 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_local_header_footer() {
$pro_text = '';
if ( !sek_is_pro() ) {
$pro_text = sek_get_pro_notice_for_czr_input( __('sticky header, header over content, sticky footer, search icon, hamburger color, ...', 'text-doma') );
}
return array(
'dynamic_registration' => true,
'module_type' => 'sek_local_header_footer',
//'name' => __('Page header', 'text_doma'),
// 'starting_value' => array(
// 'local_custom_css' => sprintf( '/* %1$s */', __('Add your own CSS code here', 'text_doma' ) )
// ),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'header-footer' => array(
'input_type' => 'simpleselect',
'title' => __('Select a header and a footer for this page', 'text_doma'),
'default' => 'inherit',
'choices' => array(
'inherit' => __('Inherit the site wide option', 'text_domain' ),
'theme' => __('Use the active theme\'s header and footer', 'text_domain' ),
'nimble_global' => __('Nimble site wide header and footer', 'text_domain' ),
'nimble_local' => __('Nimble specific header and footer for this page', 'text_domain' )
),
'refresh_preview' => true,
'width-100' => true,
'title_width' => 'width-100',
'notice_after' => sprintf( __( 'This option overrides the site wide header and footer options set in the %1$s for this page only.', 'text_doma'),
sprintf( '<a href="#" onclick="%1$s">%2$s</a>',
"javascript:wp.customize.section('__globalOptionsSectionId', function( _s_ ){ _s_.focus(); })",
__('site wide options', 'text_doma')
)
),
'html_after' => $pro_text
)
)
)//tmpl
);
}
?>
@@ -0,0 +1,29 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_local_revisions() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_local_revisions',
//'name' => __('Revision history', 'text_doma'),
// 'starting_value' => array(
// 'local_custom_css' => sprintf( '/* %1$s */', __('Add your own CSS code here', 'text_doma' ) )
// ),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'local_revisions' => array(
'input_type' => 'revision_history',
'title' => __('Browse your revision history', 'text_doma'),
'refresh_markup' => false,
'refresh_stylesheet' => false,
'width-100' => true,
'title_width' => 'width-100',
'notice_before' => __('This is the revision history of the sections of the currently customized page.', 'text_doma'),
'notice_after' => __('Select a revision from the drop-down list to preview it. You can then restore it by clicking the Publish button at the top of the page.', 'text_doma')
)
)
)//tmpl
);
}
?>
@@ -0,0 +1,53 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_local_imp_exp() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_local_imp_exp',
//'name' => __('Export / Import', 'text_doma'),
// 'starting_value' => array(
// 'local_custom_css' => sprintf( '/* %1$s */', __('Add your own CSS code here', 'text_doma' ) )
// ),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'import_export' => array(
'input_type' => 'import_export',
'scope' => 'local',
'title' => __('EXPORT', 'text_doma'),
'refresh_markup' => false,
'refresh_stylesheet' => false,
'width-100' => true,
'title_width' => 'width-100',
//'notice_before' => __('Make sure you import a file generated with Nimble Builder export system.', 'text_doma'),
// 'notice_after' => __('Select a revision from the drop-down list to preview it. You can then restore it by clicking the Publish button at the top of the page.', 'text_doma')
),
'keep_existing_sections' => array(
'input_type' => 'nimblecheck',
'title' => __('Combine the imported sections with the current ones.', 'text_doma'),
'default' => 0,
'title_width' => 'width-80',
'input_width' => 'width-20',
'refresh_markup' => false,
'refresh_stylesheet' => false,
'refresh_preview' => true,
'notice_after' => __( 'Check this option if you want to keep the existing sections of this page, and combine them with the imported ones.', 'text_doma'),
),
// april 2020 : introduced for https://github.com/presscustomizr/nimble-builder/issues/663
'import_img' => array(
'input_type' => 'nimblecheck',
'title' => __('Import images in your media library.', 'text_doma'),
'default' => 1,
'title_width' => 'width-80',
'input_width' => 'width-20',
'refresh_markup' => false,
'refresh_stylesheet' => false,
'refresh_preview' => true,
'notice_after' => __( 'When this option is unchecked, Nimble Builder will not import images and use instead the url of the original images.', 'text_doma'),
)
)
)//tmpl
);
}
?>
@@ -0,0 +1,246 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_global_text() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_global_text',
//'name' => __('Global text', 'text_doma'),
// 'starting_value' => array(
// 'global_custom_css' => sprintf( '/* %1$s */', __('Add your own CSS code here', 'text_doma' ) )
// ),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'default_font_family' => array(
'input_type' => 'font_picker',
'title' => __('Font family', 'text_doma'),
'default' => '',
'refresh_stylesheet' => true,
'refresh_fonts' => true,
'refresh_preview' => false,
'html_before' => '<h3>' . __('GLOBAL TEXT STYLE') .'</h3>',
'html_after' => sprintf('<span class="czr-notice"><i class="far fa-lightbulb"></i> %1s => <a href="%2s" target="_blank" rel="noopener noreferrer">%2$s</a></span><hr/>',
__('Find inspiration'),
'https://fonts.google.com/?sort=popularity'
)
),
'default_font_size' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __( 'Font size', 'text_doma' ),
// the default value is commented to fix https://github.com/presscustomizr/nimble-builder/issues/313
// => as a consequence, when a module uses the font child module, the default font-size rule must be defined in the module SCSS file.
//'default' => array( 'desktop' => '16px' ),
'min' => 0,
'max' => 100,
'title_width' => 'width-100',
'width-100' => true,
'refresh_stylesheet' => true,
'refresh_preview' => false,
),//16,//"14px",
'default_line_height' => array(
'input_type' => 'range_with_unit_picker',
'title' => __( 'Line height', 'text_doma' ),
'default' => '1.5em',
'min' => 0,
'max' => 10,
'step' => 0.1,
'width-100' => true,
'refresh_stylesheet' => true,
'refresh_preview' => false,
),//24,//"20px",
'default_color' => array(
'input_type' => 'wp_color_alpha',
'title' => __('Text color', 'text_doma'),
'default' => '',
'refresh_stylesheet' => true,
'refresh_preview' => false,
'width-100' => true,
'notice_before' => __('Inherits your active theme\'s option when not set.', 'text_doma')
),//"#000000",
'links_color' => array(
'input_type' => 'wp_color_alpha',
'title' => __('Links color', 'text_doma'),
'default' => '',
'refresh_stylesheet' => true,
'refresh_preview' => false,
'width-100' => true,
'notice_before' => __('Inherits your active theme\'s option when not set.', 'text_doma'),
'html_before' => '<hr/><h3>' . __('GLOBAL STYLE OPTIONS FOR LINKS') .'</h3>'
),//"#000000",
'links_color_hover' => array(
'input_type' => 'wp_color_alpha',
'title' => __('Links color on mouse hover', 'text_doma'),
'default' => '',
'refresh_stylesheet' => true,
'refresh_preview' => false,
'width-100' => true,
'notice_before' => __('Inherits your active theme\'s option when not set.', 'text_doma'),
'title_width' => 'width-100'
),//"#000000",
'links_underlining' => array(
'input_type' => 'simpleselect',
'title' => __('Link underlining', 'text_doma'),
'default' => 'inherit',
'refresh_stylesheet' => true,
'refresh_preview' => false,
'choices' => array(
'inherit' => __('Default', 'text_doma'),
'underlined' => __( 'Underlined', 'text_doma'),
'not_underlined' => __( 'Not underlined', 'text_doma'),
)
),//null,
'links_underlining_hover' => array(
'input_type' => 'simpleselect',
'title' => __('Link underlining on mouse hover', 'text_doma'),
'default' => 'inherit',
'refresh_stylesheet' => true,
'refresh_preview' => false,
'choices' => array(
'inherit' => __('Default', 'text_doma'),
'underlined' => __( 'Underlined', 'text_doma'),
'not_underlined' => __( 'Not underlined', 'text_doma'),
)
),//null,
'headings_font_family' => array(
'input_type' => 'font_picker',
'title' => __('Font family', 'text_doma'),
'default' => '',
'refresh_stylesheet' => true,
'refresh_fonts' => true,
'refresh_preview' => false,
'html_before' => '<hr/><h3>' . __('GLOBAL STYLE OPTIONS FOR HEADINGS') .'</h3>',
'html_after' => sprintf('<span class="czr-notice"><i class="far fa-lightbulb"></i> %1s => <a href="%2s" target="_blank" rel="noopener noreferrer">%2$s</a></span><hr/>',
__('Find inspiration'),
'https://fonts.google.com/?sort=popularity'
)
),
)
)//tmpl
);
}
// Nimble implements an inheritance for both logic, determined by the css selectors, and the media query rules.
// For example, an inner width of 85% applied for skope will win against the global one, but can be overriden by a specific inner width set at a section level.
// October 2020 => it's better to write this global style inline than to hook in filter 'nimble_get_dynamic_stylesheet', as we do for local width for example, implying that we may create a small useless global stylesheet.
// Because :
// 1) if user doesn't use any global header / footer, which is the most common case, we save an http request for a global stylesheet
// 2) the css rules generated for global text are very short and do not justify a new stylesheet
add_filter( 'nimble_set_global_inline_style', '\Nimble\sek_add_raw_global_text_css' );
// @hook 'wp_head'
function sek_add_raw_global_text_css( $global_css = '') {
$global_options = get_option( NIMBLE_OPT_NAME_FOR_GLOBAL_OPTIONS );
if ( !is_array( $global_options ) || empty( $global_options['global_text'] ) || !is_array( $global_options['global_text'] ) )
return $global_css;
$text_options = $global_options['global_text'];
if ( !is_array( $text_options ) )
return $global_css;
$rules = array();
// SELECTORS
$default_text_selector = '.nb-loc [data-sek-level], .nb-loc [data-sek-level] p, .nb-loc [data-sek-level] .sek-btn, .nb-loc [data-sek-level] button, .nb-loc [data-sek-level] input, .nb-loc [data-sek-level] select, .nb-loc [data-sek-level] optgroup, .nb-loc [data-sek-level] textarea, .nb-loc [data-sek-level] ul, .nb-loc [data-sek-level] ol, .nb-loc [data-sek-level] li';
$links_selector = '.nb-loc [data-sek-level] .sek-module-inner a';
$links_hover_selector = '.nb-loc [data-sek-level] .sek-module-inner a:hover';
$headings_selector = '.nb-loc [data-sek-level] h1, .nb-loc [data-sek-level] h2, .nb-loc [data-sek-level] h3, .nb-loc [data-sek-level] h4, .nb-loc [data-sek-level] h5, .nb-loc [data-sek-level] h6';
// DEFAULT TEXT OPTIONS
// Font Family
if ( !empty( $text_options['default_font_family'] ) && 'none' !== $text_options['default_font_family'] ) {
$rules[] = array(
'selector' => $default_text_selector,
'css_rules' => sprintf( '%1$s:%2$s;', 'font-family', sek_extract_css_font_family_from_customizer_option( $text_options['default_font_family'] ) ),
'mq' => null
);
}
// Font size by devices
// @see sek_add_css_rules_for_css_sniffed_input_id()
if ( !empty( $text_options['default_font_size'] ) ) {
$default_font_size = $text_options['default_font_size'];
$default_font_size = !is_array($default_font_size) ? array() : $default_font_size;
$default_font_size = wp_parse_args( $default_font_size, array(
'desktop' => '16px',
'tablet' => '',
'mobile' => ''
));
$rules = sek_set_mq_css_rules( array(
'value' => $default_font_size,
'css_property' => 'font-size',
'selector' => $default_text_selector,
'is_important' => false,
'level_id' => '_excluded_from_section_custom_breakpoint_' //<= introduced in dec 2019 : https://github.com/presscustomizr/nimble-builder/issues/564
), $rules );
}
// Line height
if ( !empty( $text_options['default_line_height'] ) ) {
$rules[] = array(
'selector' => $default_text_selector,
'css_rules' => sprintf( '%1$s:%2$s;', 'line-height', $text_options['default_line_height'] ),
'mq' => null
);
}
// Color
if ( !empty( $text_options['default_color'] ) ) {
$rules[] = array(
'selector' => $default_text_selector,
'css_rules' => sprintf( '%1$s:%2$s;', 'color', $text_options['default_color'] ),
'mq' => null
);
}
// LINKS OPTIONS
// Color
if ( !empty( $text_options['links_color'] ) ) {
$rules[] = array(
'selector' => $links_selector,
'css_rules' => sprintf( '%1$s:%2$s;', 'color', $text_options['links_color'] ),
'mq' => null
);
}
// Color on hover
if ( !empty( $text_options['links_color_hover'] ) ) {
$rules[] = array(
'selector' => $links_hover_selector,
'css_rules' => sprintf( '%1$s:%2$s;', 'color', $text_options['links_color_hover'] ),
'mq' => null
);
}
// Underline
if ( !empty( $text_options['links_underlining'] ) && 'inherit' !== $text_options['links_underlining'] ) {
$rules[] = array(
'selector' => $links_selector,
'css_rules' => sprintf( '%1$s:%2$s;', 'text-decoration', 'underlined' === $text_options['links_underlining'] ? 'underline' : 'solid' ),
'mq' => null
);
}
// Underline on hover
if ( !empty( $text_options['links_underlining_hover'] ) && 'inherit' !== $text_options['links_underlining_hover'] ) {
$rules[] = array(
'selector' => $links_hover_selector,
'css_rules' => sprintf( '%1$s:%2$s;', 'text-decoration', 'underlined' === $text_options['links_underlining_hover'] ? 'underline' : 'solid' ),
'mq' => null
);
}
// HEADINGS OPTIONS
// Font Family
if ( !empty( $text_options['headings_font_family'] ) && 'none' !== $text_options['headings_font_family'] ) {
$rules[] = array(
'selector' => $headings_selector,
'css_rules' => sprintf( '%1$s:%2$s;', 'font-family', sek_extract_css_font_family_from_customizer_option( $text_options['headings_font_family'] ) ),
'mq' => null
);
}
$global_css = is_string($global_css) ? $global_css : '';
$global_text_options_css = Sek_Dyn_CSS_Builder::sek_generate_css_stylesheet_for_a_set_of_rules( $rules );
if ( is_string( $global_text_options_css ) && !empty( $global_text_options_css ) ) {
$global_css .= $global_text_options_css;
}
return $global_css;
}
?>
@@ -0,0 +1,63 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_global_breakpoint() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_global_breakpoint',
//'name' => __('Site wide breakpoint options', 'text_doma'),
// 'starting_value' => array(
// ),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'use-custom-breakpoint' => array(
'input_type' => 'nimblecheck',
'title' => __('Use a global custom breakpoint for responsive columns', 'text_doma'),
'default' => 0,
'title_width' => 'width-80',
'input_width' => 'width-20',
'notice_before_title' => __( 'This is the viewport width from which columns are rearranged vertically. The default global breakpoint is 768px. A custom breakpoint can also be set for each section.', 'text_doma')
),
'global-custom-breakpoint' => array(
'input_type' => 'range_simple',
'title' => __( 'Define a custom breakpoint in pixels', 'text_doma' ),
'default' => 768,
'min' => 1,
'max' => 2000,
'step' => 1,
'refresh_markup' => true,
'refresh_stylesheet' => true,
//'css_identifier' => 'letter_spacing',
'width-100' => true,
'title_width' => 'width-100'
),
'apply-to-all' => array(
'input_type' => 'nimblecheck',
'title' => __('Apply this breakpoint to all by-device customizations', 'text_doma'),
'default' => 0,
'title_width' => 'width-80',
'input_width' => 'width-20',
'notice_after' => sprintf(
__( '%s When enabled, this custom breakpoint is applied not only to responsive columns but also to all by-device customizations, like alignment for example.', 'text_doma'),
'<span class="sek-mobile-device-icons"><i class="sek-switcher preview-desktop"></i>&nbsp;<i class="sek-switcher preview-tablet"></i>&nbsp;<i class="sek-switcher preview-mobile"></i></span>'
)
),
)
)//tmpl
);
}
add_filter('nimble_set_global_inline_style', '\Nimble\sek_write_global_custom_breakpoint' );
function sek_write_global_custom_breakpoint($global_css = '') {
$custom_breakpoint = sek_get_global_custom_breakpoint();
if ( $custom_breakpoint >= 1 ) {
$global_css = is_string($global_css) ? $global_css : '';
$css = '@media (min-width:' . $custom_breakpoint . 'px) {.sek-global-custom-breakpoint-col-8 {-ms-flex: 0 0 8.333%;flex: 0 0 8.333%;max-width: 8.333%;}.sek-global-custom-breakpoint-col-9 {-ms-flex: 0 0 9.090909%;flex: 0 0 9.090909%;max-width: 9.090909%;}.sek-global-custom-breakpoint-col-10 {-ms-flex: 0 0 10%;flex: 0 0 10%;max-width: 10%;}.sek-global-custom-breakpoint-col-11 {-ms-flex: 0 0 11.111%;flex: 0 0 11.111%;max-width: 11.111%;}.sek-global-custom-breakpoint-col-12 {-ms-flex: 0 0 12.5%;flex: 0 0 12.5%;max-width: 12.5%;}.sek-global-custom-breakpoint-col-14 {-ms-flex: 0 0 14.285%;flex: 0 0 14.285%;max-width: 14.285%;}.sek-global-custom-breakpoint-col-16 {-ms-flex: 0 0 16.666%;flex: 0 0 16.666%;max-width: 16.666%;}.sek-global-custom-breakpoint-col-20 {-ms-flex: 0 0 20%;flex: 0 0 20%;max-width: 20%;}.sek-global-custom-breakpoint-col-25 {-ms-flex: 0 0 25%;flex: 0 0 25%;max-width: 25%;}.sek-global-custom-breakpoint-col-30 {-ms-flex: 0 0 30%;flex: 0 0 30%;max-width: 30%;}.sek-global-custom-breakpoint-col-33 {-ms-flex: 0 0 33.333%;flex: 0 0 33.333%;max-width: 33.333%;}.sek-global-custom-breakpoint-col-40 {-ms-flex: 0 0 40%;flex: 0 0 40%;max-width: 40%;}.sek-global-custom-breakpoint-col-50 {-ms-flex: 0 0 50%;flex: 0 0 50%;max-width: 50%;}.sek-global-custom-breakpoint-col-60 {-ms-flex: 0 0 60%;flex: 0 0 60%;max-width: 60%;}.sek-global-custom-breakpoint-col-66 {-ms-flex: 0 0 66.666%;flex: 0 0 66.666%;max-width: 66.666%;}.sek-global-custom-breakpoint-col-70 {-ms-flex: 0 0 70%;flex: 0 0 70%;max-width: 70%;}.sek-global-custom-breakpoint-col-75 {-ms-flex: 0 0 75%;flex: 0 0 75%;max-width: 75%;}.sek-global-custom-breakpoint-col-80 {-ms-flex: 0 0 80%;flex: 0 0 80%;max-width: 80%;}.sek-global-custom-breakpoint-col-83 {-ms-flex: 0 0 83.333%;flex: 0 0 83.333%;max-width: 83.333%;}.sek-global-custom-breakpoint-col-90 {-ms-flex: 0 0 90%;flex: 0 0 90%;max-width: 90%;}.sek-global-custom-breakpoint-col-100 {-ms-flex: 0 0 100%;flex: 0 0 100%;max-width: 100%;}}';
$global_css .= $css;
}
return $global_css;
}
?>
@@ -0,0 +1,185 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_global_widths() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_global_widths',
//'name' => __('Site wide width options', 'text_doma'),
// 'starting_value' => array(
// ),
// 'sanitize_callback' => 'function_prefix_to_be_replaced_sanitize_callback__czr_social_module',
// 'validate_callback' => 'function_prefix_to_be_replaced_validate_callback__czr_social_module',
'tmpl' => array(
'item-inputs' => array(
'use-custom-outer-width' => array(
'input_type' => 'nimblecheck',
'title' => __('Define a custom outer width for the sections site wide', 'text_doma'),
'default' => 0,
'title_width' => 'width-80',
'input_width' => 'width-20',
'refresh_markup' => false,
'refresh_stylesheet' => true,
'refresh_preview' => true,
'notice_before_title' => sprintf( __( 'The inner and outer widths of your sections can be set globally here, but also overriden in the %1$s, and for each sections.', 'text_doma'),
sprintf( '<a href="#" onclick="%1$s">%2$s</a>',
"javascript:wp.customize.section('__localOptionsSection', function( _s_ ){_s_.container.find('.accordion-section-title').first().trigger('click');})",
__('current page options', 'text_doma')
)
),
),
'outer-section-width' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __('Outer sections width', 'text_doma'),
'min' => 0,
'max' => 1500,
'default' => array( 'desktop' => '100%' ),
'width-100' => true,
'title_width' => 'width-100',
'notice_after' => __('This option will be inherited by all Nimble sections of your site, unless for pages or sections with specific width options.')
),
'use-custom-inner-width' => array(
'input_type' => 'nimblecheck',
'title' => __('Define a custom inner width for the sections site wide', 'text_doma'),
'default' => 0,
'title_width' => 'width-80',
'input_width' => 'width-20',
'refresh_markup' => false,
'refresh_stylesheet' => true
),
'inner-section-width' => array(
'input_type' => 'range_with_unit_picker_device_switcher',
'title' => __('Inner sections width', 'text_doma'),
'min' => 0,
'max' => 1500,
'default' => array( 'desktop' => '100%' ),
'width-100' => true,
'title_width' => 'width-100',
'notice_after' => __('This option will be inherited by all Nimble sections of your site, unless for pages or sections with specific width options.')
)
)
)//tmpl
);
}
// Add user site wide custom inner and outer widths for the sections
// Data structure since v1.1.0. Oct 2018
// [width] => Array
// (
// [use-custom-outer-width] => 1
// [outer-section-width] => Array
// (
// [desktop] => 99%
// [mobile] => 66%
// [tablet] => 93%
// )
// [use-custom-inner-width] => 1
// [inner-section-width] => Array
// (
// [desktop] => 98em
// [tablet] => 11em
// [mobile] => 8em
// )
// )
// The inner and outer widths can be set at 3 levels :
// 1) global
// 2) skope ( local )
// 3) section
// And for 3 different types of devices : desktop, tablet, mobiles.
//
// Nimble implements an inheritance for both logic, determined by the css selectors, and the media query rules.
// For example, an inner width of 85% applied for skope will win against the global one, but can be overriden by a specific inner width set at a section level.
// October 2020 => it's better to write this global style inline than to hook in filter 'nimble_get_dynamic_stylesheet', as we do for local width for example, implying that we may create a global stylesheet.
// Because :
// 1) if user doesn't use any global header / footer, which is the most common case, we save an http request for a global stylesheet
// 2) the css rules generated for custom section widths are very short and do not justify a new stylesheet
add_filter('nimble_set_global_inline_style', '\Nimble\sek_write_global_custom_section_widths', 1000 );
function sek_write_global_custom_section_widths($global_css = '') {
$global_options = get_option( NIMBLE_OPT_NAME_FOR_GLOBAL_OPTIONS );
if ( !is_array( $global_options ) || empty( $global_options['widths'] ) || !is_array( $global_options['widths'] ) )
return $global_css;
$width_options = $global_options['widths'];
$user_defined_widths = array();
if ( !empty( $width_options[ 'use-custom-outer-width' ] ) && true === sek_booleanize_checkbox_val( $width_options[ 'use-custom-outer-width' ] ) ) {
$user_defined_widths['outer-section-width'] = '[data-sek-level="section"]';
}
if ( !empty( $width_options[ 'use-custom-inner-width' ] ) && true === sek_booleanize_checkbox_val( $width_options[ 'use-custom-inner-width' ] ) ) {
$user_defined_widths['inner-section-width'] = '[data-sek-level="section"] > .sek-container-fluid > .sek-sektion-inner';
}
$rules = array();
// Note that the option 'outer-section-width' and 'inner-section-width' can be empty when set to a value === default
// @see js czr_setions::normalizeAndSanitizeSingleItemInputValues()
foreach ( $user_defined_widths as $width_opt_name => $selector ) {
if ( !empty( $width_options[ $width_opt_name ] ) && !is_array( $width_options[ $width_opt_name ] ) ) {
sek_error_log( __FUNCTION__ . ' => error => the width option should be an array( {device} => {number}{unit} )');
}
// $width_options[ $width_opt_name ] should be an array( {device} => {number}{unit} )
// If not set in the width options , it means that it is equal to default
$user_custom_width_value = ( empty( $width_options[ $width_opt_name ] ) || !is_array( $width_options[ $width_opt_name ] ) ) ? array('desktop' => '100%') : $width_options[ $width_opt_name ];
$user_custom_width_value = wp_parse_args( $user_custom_width_value, array(
'desktop' => '100%',
'tablet' => '',
'mobile' => ''
));
$max_width_value = $user_custom_width_value;
$margin_value = array();
foreach ( $user_custom_width_value as $device => $num_unit ) {
$numeric = sek_extract_numeric_value( $num_unit );
$padding_of_the_parent_container[$device] = 'inherit';
if ( !empty( $numeric ) ) {
$unit = sek_extract_unit( $num_unit );
$max_width_value[$device] = $numeric . $unit;
$margin_value[$device] = '0 auto';
}
}
$rules = sek_set_mq_css_rules(array(
'value' => $max_width_value,
'css_property' => 'max-width',
'selector' => $selector,
'level_id' => '_excluded_from_section_custom_breakpoint_' //<= introduced in dec 2019 : https://github.com/presscustomizr/nimble-builder/issues/564
), $rules );
// when customizing the inner section width, we need to reset the default padding rules for .sek-container-fluid {padding-right:10px; padding-left:10px}
// @see assets/front/scss/_grid.scss
if ( 'inner-section-width' === $width_opt_name ) {
$rules = sek_set_mq_css_rules(array(
'value' => $padding_of_the_parent_container,
'css_property' => 'padding-left',
'selector' => '[data-sek-level="section"] > .sek-container-fluid',
'level_id' => '_excluded_from_section_custom_breakpoint_' //<= introduced in dec 2019 : https://github.com/presscustomizr/nimble-builder/issues/564
), $rules );
$rules = sek_set_mq_css_rules(array(
'value' => $padding_of_the_parent_container,
'css_property' => 'padding-right',
'selector' => '[data-sek-level="section"] > .sek-container-fluid',
'level_id' => '_excluded_from_section_custom_breakpoint_' //<= introduced in dec 2019 : https://github.com/presscustomizr/nimble-builder/issues/564
), $rules );
}
if ( !empty( $margin_value ) ) {
$rules = sek_set_mq_css_rules(array(
'value' => $margin_value,
'css_property' => 'margin',
'selector' => $selector,
'level_id' => '_excluded_from_section_custom_breakpoint_' //<= introduced in dec 2019 : https://github.com/presscustomizr/nimble-builder/issues/564
), $rules );
}
}//foreach
$global_css = is_string($global_css) ? $global_css : '';
$width_options_css = Sek_Dyn_CSS_Builder::sek_generate_css_stylesheet_for_a_set_of_rules( $rules );
if ( is_string( $width_options_css ) && !empty( $width_options_css ) ) {
$global_css .= $width_options_css;
}
return $global_css;
}
?>
@@ -0,0 +1,23 @@
<?php
//Fired in add_action( 'after_setup_theme', 'sek_register_modules', 50 );
function sek_get_module_params_for_sek_global_reset() {
return array(
'dynamic_registration' => true,
'module_type' => 'sek_global_reset',
//'name' => __('Reset global scope sections', 'text_doma'),
'tmpl' => array(
'item-inputs' => array(
'reset_global' => array(
'input_type' => 'reset_button',
'title' => __( 'Remove the sections displayed globally' , 'text_doma' ),
'scope' => 'global',
'notice_after' => __('This will remove the sections displayed on global scope locations. Local scope sections will not be impacted.', 'text_doma'),
'refresh_markup' => false,
'refresh_stylesheet' => false,
)
)
)//tmpl
);
}
?>

Some files were not shown because too many files have changed in this diff Show More