I have been attempting to create a theme reset button for the Wordpress theme customizer that automatically removes all of the theme_mod settings. I have tried multiple ways to do this and have never been able to get it to work. As seen here.
After multiple failed attempts using the remove_theme_mods approach I begin to wonder if that is my problem other than the ajax and javascript being faulty and not properly binding the button.
Since I save all defaults into one big array so when my theme is installed it automatically has values populated on the theme customizer page, and the theme has a specific look... I was thinking I could try a different approach to instead remove the theme settings I just over ride them, maybe with a custom control? Possibly by somehow assigning these default values to all settings? I really hope someone can help me get this going. If you have any ideas I would appreciate it very very much.
Here is an example of how I assign the default values for the theme:
function newtheme_get_theme_mods() {
$defaults = array(
'newtheme_responsive' => true,
'newtheme_hero_title' => 'Beautiful and Elegant',
'newtheme_hero_description' => 'The best theme in the world',
'newtheme_header_logo' => '/wp-content/themes/newtheme/img/logo.png',
'newtheme_header_background_color' => 'rgba(35,35,35, 0.9)'
return $defaults;
}
Create a Class Theme and store theme settings in database with the serialized object of class theme. On changing the theme or restoring the theme load that serialized object in your Theme class constructor and then the class will use the settings accordingly. That is some thing I have done with my project so far.
As answered on Stack Exchange
The problem with using remove_theme_mods for showing defaults in the Customizer is
the Customizer is a preview which you can exit without saving,
the individual theme_mods are filtered, but not the entire theme_mod array
theme_mods includes menus and widgets.
I also wanted a reset button, but I chose instead to create a Preset control, and one of the presets is "defaults". This way uses a select, so there is no problem with the button not working (because bind is for value changes and buttons don't change their values).
The trick is to use ajax to retrieve the chosen preset, and then loop over the values in javascript, assigning them to the settings so that those changes will trigger the refresh of the preview. My code includes filters so that child themes can add in more options and presets. And the presets can be subsets of the options available.
Here is PHP for the Preset control (just a normal select, but a settingless control):
$wp_customize->add_control( 'option_presets', array(
'label' => __( 'Use preset theme options', 'mytheme' ),
'description' => __( 'Theme options will be set to the preset values.', 'mytheme' ),
'section' => 'mytheme_section',
'settings' => array(),
'type' => 'select',
'capability' => 'edit_theme_options',
'choices' => mytheme_option_presets_choices(),
) );
Here is the rest of the PHP functions.
/**
* Supply list of choices for option presets.
*/
function mytheme_option_presets_choices() {
return apply_filters( 'mytheme_option_presets_choices', array(
'none' => __( 'Select preset', 'mytheme' ),
'defaults' => __( 'Defaults', 'mytheme' ),
'dark' => __( 'Dark', 'mytheme' ),
) );
}
/**
* Sanitize an option preset choice.
*/
function mytheme_sanitize_option_presets_choice( $input ) {
$valid = mytheme_option_presets_choices();
return array_key_exists( $input, $valid ) ? $input : 'none';
}
/**
* Get the preset values for the chosen option preset.
*/
function mytheme_option_preset( $which ) {
$values = array();
if ( 'defaults' === $which ) {
$values = mytheme_default_values();
}
if ( 'dark' === $which ) {
$values = array(
'body_textcolor' => '#f9f7f7',
'background_color' => '#444244',
'header_textcolor' => '#bf9a07',
'area_classes' => array(
'sidebar' => 'semi-black',
'widgets' => 'box',
),
);
}
return apply_filters( 'mytheme_option_preset', $values, $which );
}
/**
* Add a nonce for Customizer for option presets.
*/
function mytheme_refresh_nonces( $nonces ) {
$nonces['mytheme-customize-presets'] = wp_create_nonce( 'mytheme-customize-presets' );
return $nonces;
}
add_filter( 'customize_refresh_nonces', 'mytheme_refresh_nonces' );
/**
* Ajax handler for supplying option preset values.
*/
function mytheme_ajax_option_preset_values() {
check_ajax_referer( 'mytheme-customize-presets', 'option_presets_nonce' );
if ( ! current_user_can( 'edit_theme_options' ) ) {
wp_die( -1 );
}
if ( empty( $_POST['option_preset'] ) ) {
wp_send_json_error( 'mytheme_missing_preset_parameter' );
}
$preset = sanitize_text_field( wp_unslash( $_POST['option_preset'] ) );
$values = mytheme_option_preset( $preset );
if ( empty( $values ) ) {
wp_send_json_error( array( 'message' => __( 'No preset found.', 'mytheme' ) ) );
}
else { // Flatten the array.
foreach ($values as $key => $avalue) {
if ( is_array( $avalue ) ) {
unset( $values[$key] );
foreach ($avalue as $subkey => $subvalue) {
$values[$key . '[' . $subkey . ']'] = $subvalue;
}
}
}
wp_send_json_success( array( 'values' => $values ) );
}
}
add_action( 'wp_ajax_mytheme_option_preset', 'mytheme_ajax_option_preset_values' );
And then just a little bit of Javascript to make the ajax request. This is queued on the 'customize_controls_enqueue_scripts' action. (I left out the display of the error message.)
wp.customize.control( 'option_presets', function( control ) {
control.element = new wp.customize.Element( control.container.find( 'select' ) );
control.element.bind( function( preset ) {
var request = wp.ajax.post( 'mytheme_option_preset', {
option_presets_nonce: wp.customize.settings.nonce['mytheme-customize-presets'],
wp_customize: 'on',
customize_theme: wp.customize.settings.theme.stylesheet,
option_preset: preset
} );
request.done( function( response ) {
_.each( response.values, function( value, id ) {
var setting = wp.customize( id );
if ( setting ) {
setting.set( value );
}
} );
} );
} );
} );
Related
I am working on a new plugin. I am dealing with a problem that I have outlined in the title. My intention is to redirect the user to the My Pages page after the user clicks the "Delete page" button.
Here is my code:
function custom_admin_bar_delete_link( $wp_admin_bar ) {
global $post;
if( is_admin() || ! is_object( $post ) )
return;
if ( ! current_user_can( 'delete_pages' ) )
return;
if ( $post->post_type != 'page' )
return;
$args = array(
'id' => 'delete_link',
'title' => 'Delete this page',
'href' => get_delete_post_link( $post->ID ),
'meta' => array( 'class' => 'delete-link' )
);
$wp_admin_bar->add_node( $args );
}
add_action( 'admin_bar_menu', 'custom_admin_bar_delete_link', 999 );
function custom_page_delete_redirect( $location, $post_id ) {
$post = get_post( $post_id );
if ( 'page' === $post->post_type && 'trash' === get_post_status( $post_id ) ) {
return admin_url( 'edit.php?post_type=page' );
}
return $location;
}
add_filter( 'wp_redirect', 'custom_page_delete_redirect', 10, 2 );
Thank you.
Without thinking too hard about it, I would add an ID to your button and just write some jQuery to do the redirect.
(The PHP: NOT tested)
EDIT:
Per the Wordpress documentation, you should be able to add an ID:
https://developer.wordpress.org/reference/classes/wp_admin_bar/add_node/
$args = array(
'id' => 'delete_link',
'title' => 'Delete this page',
'href' => get_delete_post_link( $post->ID ),
'id' => "someid",
'meta' => array( 'class' => 'delete-link' )
);
The jQuery:
EDIT:
How are you adding the jQuery? You should probably save it in a separate file in your plugin folder (maybe in a subfolder named "js") and enqueue it in your plugin. The 2nd answer to this question would be a good place to start:
https://wordpress.stackexchange.com/questions/42641/how-to-include-a-simple-jquery-file-into-a-wordpress-plugin
jQuery(document).ready(function($){
$('body').on('click','#someid',function(event){
// window.alert('jquery executing');
setTimeout(function(){window.location.href = "http://example.com/page/";}, 500);
});
});
Thoughts: I added a timeout, because Wordpress loads slowly sometimes, and I've had to do this for other applications in Wordpress, but try without and see if it performs without the timeout.
Have a look at this answer:
https://wordpress.stackexchange.com/questions/132196/get-delete-post-link-redirect
The question is similar, and this may get you where you need to go.
I'm creating custom elements for WPBakery.
I have a folder called vc-elements which contains two files:
hero.php
text-image.php
On the WordPress admin side, I want both elements to be visible. To do this, in functions.php I'm running:
add_action( 'vc_before_init', 'vc_before_init_actions' );
function vc_before_init_actions() {
// Link to VC elements's folder
if( function_exists('vc_set_shortcodes_templates_dir') ){
vc_set_shortcodes_templates_dir( get_template_directory() . 'vc-elements' );
}
}
But in the admin side, neither of the two blocks show?
Previously I had:
function vc_before_init_actions() {
require_once( get_template_directory().'/vc-elements/hero.php' );
}
Which showed the hero block in the admin. But when I added:
function vc_before_init_actions() {
require_once( get_template_directory().'/vc-elements/hero.php' );
require_once( get_template_directory().'/vc-elements/text-image.php' );
}
In the admin side, the hero element is replaced by the text image element - only one shows at one time. Why's this?
Are you building this with a parent or child theme? I'm not sure about your markup.
Usually, when I add a custom module I need to check if the plugin exists..
<?php
/**
* Adds new shortcode "myprefix_say_hello" and registers it to
* the Visual Composer plugin
*
*/
if ( ! class_exists( 'EZ_VC_Product_Widget' ) ) {
class EZ_VC_Product_Widget {
/**
* Main constructor
*/
public function __construct() {
// Registers the shortcode in WordPress
add_shortcode( 'ez_product', array( 'EZ_VC_Product_Widget', 'output' ) );
// Map shortcode to Visual Composer
if ( function_exists( 'vc_lean_map' ) ) {
vc_lean_map( 'ez_product', array( 'EZ_VC_Product_Widget',
'map' ) );
}
}
/**
* Shortcode output
*/
public static function output( $atts, $content = null ) {
// Extract shortcode attributes (based on the vc_lean_map function - see next function)
extract( vc_map_get_attributes( 'ez_product', $atts ) );
// Define output
$product = wc_get_product($id);
if ( $id && $product) {
global $post;
$post = $product->get_post_data();
setup_postdata( $post );
ob_start();
get_template_part('template-parts/product-module');
wp_reset_postdata();
return ob_get_clean();
}
else
return '';
}
/**
* Map shortcode to VC
*
* This is an array of all your settings which become the shortcode attributes ($atts)
* for the output. See the link below for a description of all available parameters.
*
* #since 1.0.0
* #link https://kb.wpbakery.com/docs/inner-api/vc_map/
*/
public static function map() {
return array(
'name' => esc_html__( 'EZ Product', 'shopkeeper' ),
'description' => esc_html__( 'Shortcode outputs a single product.', 'shopkeeper' ),
'base' => 'ez_product',
"icon" => get_stylesheet_directory_uri() . "/vc_extend/ez_shortcode_icon.png",
'params' => array(
array(
'type' => 'autocomplete',
'heading' => esc_html__( 'Select identificator', 'js_composer' ),
'param_name' => 'id',
'description' => esc_html__( 'Input product ID or product SKU or product title to see suggestions', 'js_composer' ),
),
array(
'type' => 'hidden',
// This will not show on render, but will be used when defining value for autocomplete
'param_name' => 'sku',
),
),
);
}
}
}
add_action('vc_before_init', function(){
new EZ_VC_Product_Widget;
});
/**
* #action wp_ajax_vc_get_autocomplete_suggestion - since 4.4 used to
hook ajax requests for autocomplete suggestions
*/
add_action( 'wp_ajax_vc_get_autocomplete_suggestion',
'ez_vc_get_autocomplete_suggestion' );
/**
* #since 4.4
*/
function ez_vc_get_autocomplete_suggestion() {
vc_user_access()->checkAdminNonce()->validateDie()->wpAny(
'edit_posts', 'edit_pages' )->validateDie();
$query = vc_post_param( 'query' );
$shortcode = wp_strip_all_tags( vc_post_param( 'shortcode' ) );
if ( $shortcode == 'ez_product') {
$tag = 'product';//wp_strip_all_tags( vc_post_param( 'shortcode'
)
);
$param_name = vc_post_param( 'param' );
vc_render_suggestion( $query, $tag, $param_name );
exit;
}
}
Hi I am trying to change the way Fusion Builder container works. I want to add more custom fields, change the display html and the default variables.
I need to override the /plugin/fusion-builder/shorcode/fusion-container.php
I am using a child theme of avada. What is the correct way to override it?
Just right after I posted the question, I got the answer in GitHub:
https://github.com/Theme-Fusion/Fusion-Builder-Sample-Add-On/issues/34
The solution is to add to the functions.php in your child template the following code:
/**
* Filter already set maps, add in a new option to container.
*/
function filter_available_element( $fusion_builder_elements ) {
if ( isset( $fusion_builder_elements['fusion_builder_container'] ) ) {
$fusion_builder_elements['fusion_builder_container']['params'][] = array(
'type' => 'radio_button_set',
'heading' => esc_attr__( 'Container Design Mode', 'fusion-builder' ),
'description' => esc_attr__( 'Controls whether the container should be dark or light.', 'fusion-builder' ),
'param_name' => 'container_mode',
'value' => array(
'light' => esc_attr__( 'Light', 'fusion-builder' ),
'dark' => esc_attr__( 'Dark', 'fusion-builder' ),
),
'default' => 'light',
'group' => esc_attr__( 'Design', 'fusion-builder' ),
);
}
return $fusion_builder_elements;
}
add_filter( 'fusion_builder_all_elements', 'filter_available_element' );
/**
* Filter the parameters, check for container_mode and if set add to class parameter.
*/
function filter_container_parameters( $out, $pairs, $atts, $shortcode ) {
// If set, use it, otherwise set to default.
$out['container_mode'] = isset( $atts['container_mode'] ) ? $atts['container_mode'] : 'light';
// Set to class parameter which container already has and uses.
if ( ! isset( $out['class'] ) || '' === $out['class'] ) {
$out['class'] = 'my-class-' . $out['container_mode'];
} else {
$out['class'] .= ' my-class-' . $out['container_mode'];
}
return $out;
}
add_filter( 'shortcode_atts_fusion_builder_container', 'filter_container_parameters', 10, 4 );
I'm being requested by my client to add a custom field that they will be able to enter in a url. The post itself is a custom plugin custom post type, this is the code I have for this portion:
register_post_type( 'storylist',
array(
'labels' => $labels,
'public' => false,
'exclude_from_search' => true,
'publicly_queryable' => false,
'show_ui' => true,
'supports' => array('title'),
)
);
add_filter( 'rwmb_meta_boxes', 'c_register_meta_boxes' );
}
function c_register_meta_boxes( $boxes ){
$prefix = 'c_rwmb_';
$boxes[] = array(
'id' => 'view',
'title' => __('View Link', 'c_rwmb' ),
'post_types' => array('storylist'),
'context' => 'normal',
'priority' => 'high',
'fields' => array(
array(
'name' => __('View URL', 'c_rwmb' ),
'id' => $prefix . 'view_url',
'type' => 'text',
'size' => 60,
'clone' => false
),
)
);
return $meta_boxes;
}
Now the problem is when I go to the post, I do not see the custom meta field even showing up, is there something that I'm missing?
The custom post type("storylist") comes from the plugin right? Then you don't need to register the custom post again. You just needs to add meta field for this post type and save its value while updating the post. Once I had a experience to enable/disable sidebar using custom field. I shared my code. Hope this will help you.
<?php
add_action('admin_init','add_metabox_post_sidebar');
add_action('save_post','save_metabox_post_sidebar');
/*
* Funtion to add a meta box to enable/disable the posts.
*/
function add_metabox_post_sidebar()
{
add_meta_box("Enable Sidebar", "Enable Sidebar", "enable_sidebar_posts", "post", "side", "high");
}
function enable_sidebar_posts(){
global $post;
$check=get_post_custom($post->ID );
$checked_value = isset( $check['post_sidebar'] ) ? esc_attr( $check['post_sidebar'][0] ) : 'no';
?>
<label for="post_sidebar">Enable Sidebar:</label>
<input type="checkbox" name="post_sidebar" id="post_sidebar" <?php if($checked_value=="yes"){echo "checked=checked"; } ?> >
<p><em>( Check to enable sidebar. )</em></p>
<?php
}
/*
* Save the Enable/Disable sidebar meta box value
*/
function save_metabox_post_sidebar($post_id)
{
// Bail if we're doing an auto save
if( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;
// if our current user can't edit this post, bail
if( !current_user_can( 'edit_post' ) ) return;
$checked_value = isset( $_POST['post_sidebar'] ) ? 'yes' : 'no';
update_post_meta( $post_id, 'post_sidebar', $checked_value );
}
?>
Here I have added a custom field called 'post_sidebar' for post type "post" you can change your own and change your post type in this line add_meta_box("Enable Sidebar", "Enable Sidebar", "enable_sidebar_posts", "post", "side", "high"); from "post" to "storylist".
Just for somebody who needs to add MetaBox fields via plugin, because the question is:
How to add custom fields to a WordPress Plugin
function gffgfg_add_boxes( $meta_boxes ) {
$prefix = 'some prefix';
$meta_boxes[] = array(
//metabox array
);
return $meta_boxes;
}
add_filter( 'rwmb_meta_boxes', 'gffgfg_add_boxes', 999 ); // 999
I have created a custom control for the theme customizer which is a simple button and label. I am going to be using it as a theme reset button that will clear the theme mod settings to their original state. Now that I have added the control and have it showing up on the customizer, I am not sure where I am supposed to add the code to reset the settings.
So far I have only created customizer settings for css and text changes. To remove the settings I will be using the remove theme mods function.
<?php remove_theme_mods() ?>
So my question is do is how exactly do I use this button to execute the remove_mods function as seen above? The documentation on that function is very minimal.
If there is another way to reset the theme mod settings to default and this is not the right approach than please chime in.
Here is the code I have created my custom button with.
function newtheme_customize_reset_control($wp_customize) {
/**
* Reset Control
*
*/
class newtheme_Customize_reset_Control extends WP_Customize_Control {
public $type = 'button';
public function render_content() {
?>
<label>
<span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
<div>
<?php _e( 'Reset Settings' ); ?>
</div>
</label>
<?php
}
}
}
add_action( 'customize_register', 'newtheme_customize_reset_control', 1, 1 );
In theme customizer you can register your custom javascript to wordpress theme customizer
add_action('customize_preview_init', 'your_live_preview_function');
public static function your_live_preview_function() {
wp_enqueue_script(
'your-theme_customizer', //Give the script an ID
get_template_directory_uri() . '/js/your-customizer-javascript-file.js', //Define it's JS file
array('jquery', 'customize-preview'), //Define dependencies
rand(1, 1000), //Define a version (optional) (but I use it for development as random so don't have to worry about cache etc.
true //Specify whether to put in footer (leave this true)
);
}
and inside your javascript file you can do something like this
( function( $ ) {
wp.customize(
'your_reset_button_control_id',
function( value ) {
value.bind(
function( to ) {
jQuery.post( ajax_url,
{
action: 'your_ajax_action_for_calling_reset',
reset_value: to
},
function( response ) {
jQuery( '.reset-info' ).html( response );
}
);
}
);
}
);
} )( jQuery );
and inside ajax you can do something like this
add_action('wp_ajax_your_ajax_action_for_calling_reset', 'your_ajax_action_for_calling_reset_callback_function');
function your_ajax_action_for_calling_reset_callback_function(){
$reset_value = esc_attr($_POST['reset_value']);
if($reset_value){
remove_theme_mods() ;
}
}
Haaaaaa hope it helps.
The problem with using remove_theme_mods for showing defaults in the Customizer is
the Customizer is a preview which you can exit without saving,
the individual theme_mods are filtered, but not the entire theme_mod array
theme_mods includes menus and widgets.
I also wanted a reset button, but I chose instead to create a Preset control, and one of the presets is "defaults". This way uses a select, so there is no problem with the button not working (because bind is for value changes and buttons don't change their values).
The trick is to use ajax to retrieve the chosen preset, and then loop over the values in javascript, assigning them to the settings so that those changes will trigger the refresh of the preview. My code includes filters so that child themes can add in more options and presets. And the presets can be subsets of the options available.
Here is PHP for the Preset control (just a normal select, but a settingless control):
$wp_customize->add_control( 'option_presets', array(
'label' => __( 'Use preset theme options', 'mytheme' ),
'description' => __( 'Theme options will be set to the preset values.', 'mytheme' ),
'section' => 'mytheme_section',
'settings' => array(),
'type' => 'select',
'capability' => 'edit_theme_options',
'choices' => mytheme_option_presets_choices(),
) );
Here is the rest of the PHP functions.
/**
* Supply list of choices for option presets.
*/
function mytheme_option_presets_choices() {
return apply_filters( 'mytheme_option_presets_choices', array(
'none' => __( 'Select preset', 'mytheme' ),
'defaults' => __( 'Defaults', 'mytheme' ),
'dark' => __( 'Dark', 'mytheme' ),
) );
}
/**
* Sanitize an option preset choice.
*/
function mytheme_sanitize_option_presets_choice( $input ) {
$valid = mytheme_option_presets_choices();
return array_key_exists( $input, $valid ) ? $input : 'none';
}
/**
* Get the preset values for the chosen option preset.
*/
function mytheme_option_preset( $which ) {
$values = array();
if ( 'defaults' === $which ) {
$values = mytheme_default_values();
}
if ( 'dark' === $which ) {
$values = array(
'body_textcolor' => '#f9f7f7',
'background_color' => '#444244',
'header_textcolor' => '#bf9a07',
'area_classes' => array(
'sidebar' => 'semi-black',
'widgets' => 'box',
),
);
}
return apply_filters( 'mytheme_option_preset', $values, $which );
}
/**
* Add a nonce for Customizer for option presets.
*/
function mytheme_refresh_nonces( $nonces ) {
$nonces['mytheme-customize-presets'] = wp_create_nonce( 'mytheme-customize-presets' );
return $nonces;
}
add_filter( 'customize_refresh_nonces', 'mytheme_refresh_nonces' );
/**
* Ajax handler for supplying option preset values.
*/
function mytheme_ajax_option_preset_values() {
check_ajax_referer( 'mytheme-customize-presets', 'option_presets_nonce' );
if ( ! current_user_can( 'edit_theme_options' ) ) {
wp_die( -1 );
}
if ( empty( $_POST['option_preset'] ) ) {
wp_send_json_error( 'mytheme_missing_preset_parameter' );
}
$preset = sanitize_text_field( wp_unslash( $_POST['option_preset'] ) );
$values = mytheme_option_preset( $preset );
if ( empty( $values ) ) {
wp_send_json_error( array( 'message' => __( 'No preset found.', 'mytheme' ) ) );
}
else { // Flatten the array.
foreach ($values as $key => $avalue) {
if ( is_array( $avalue ) ) {
unset( $values[$key] );
foreach ($avalue as $subkey => $subvalue) {
$values[$key . '[' . $subkey . ']'] = $subvalue;
}
}
}
wp_send_json_success( array( 'values' => $values ) );
}
}
add_action( 'wp_ajax_mytheme_option_preset', 'mytheme_ajax_option_preset_values' );
And then just a little bit of Javascript to make the ajax request. This is queued on the 'customize_controls_enqueue_scripts' action. (I left out the display of the error message.)
wp.customize.control( 'option_presets', function( control ) {
control.element = new wp.customize.Element( control.container.find( 'select' ) );
control.element.bind( function( preset ) {
var request = wp.ajax.post( 'mytheme_option_preset', {
option_presets_nonce: wp.customize.settings.nonce['mytheme-customize-presets'],
wp_customize: 'on',
customize_theme: wp.customize.settings.theme.stylesheet,
option_preset: preset
} );
request.done( function( response ) {
_.each( response.values, function( value, id ) {
var setting = wp.customize( id );
if ( setting ) {
setting.set( value );
}
} );
} );
} );
} );