Make WordPress Core

Opened 10 years ago

Last modified 4 years ago

#27177 new enhancement

Child themes should inherit parent theme customizer settings on activation / switching

Reported by: krogsgard's profile krogsgard Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 3.8
Component: Themes Keywords: has-patch
Focuses: administration Cc:

Description

Currently, if you install a parent theme and utilize the theme customizer, and then create a child theme and live preview or activate it, it does not inherit any of the settings previously set for the parent theme.

If a child theme doesn't have any existing values from the customizer setting, then it should look for and import those from the parent theme if they exist (perhaps on the switch_theme hook).

The current setup is a discouragement to using a child theme if the user has already invested time using the customizer with their parent theme.

Attachments (2)

27177.diff (2.0 KB) - added by obenland 9 years ago.
35997.patch (751 bytes) - added by sebastian.pisula 8 years ago.
4.5-beta1-36775

Download all attachments as: .zip

Change History (31)

#1 follow-up: @westonruter
10 years ago

Can you provide an example of such a parent theme and child theme that illustrates this problem? A child theme needn't override a parent theme's customizer settings/controls.

#2 in reply to: ↑ 1 @krogsgard
10 years ago

Replying to westonruter:

Can you provide an example of such a parent theme and child theme that illustrates this problem? A child theme needn't override a parent theme's customizer settings/controls.

The theme I tested was a commercial parent theme, but I just tested it with twentyfourteen and a child theme and it does the same thing. I tested the "site title color" and "background color" color settings.

#3 follow-up: @nacin
10 years ago

The customizer simply uses whatever storage the theme declares. If they're using an option, then it depends on how the theme keys its options (get_template() . '_options' versus my_theme_options versus `get_stylesheet() . '_options', for example). For theme mods, they are particular to get_stylesheet(), so a child theme does indeed have its own set of options.

Perhaps a child theme could say "Yes, please inherit the theme mods from the parent" but it's tough to make this uniform, especially at the customizer level.

#4 in reply to: ↑ 3 @westonruter
10 years ago

Right, so this is due to these specific customizer settings being stored in theme mods, as opposed to theme-independent options.

Replying to nacin:

The customizer simply uses whatever storage the theme declares. If they're using an option, then it depends on how the theme keys its options (get_template() . '_options' versus my_theme_options versus `get_stylesheet() . '_options', for example). For theme mods, they are particular to get_stylesheet(), so a child theme does indeed have its own set of options.

Perhaps a child theme could say "Yes, please inherit the theme mods from the parent" but it's tough to make this uniform, especially at the customizer level.

There could be a API function for child themes to opt-in to use the parent theme's theme mods instead of the child themes. This could be implemented via filtering, something like this (untested):

if ( get_stylesheet() !== get_template() ) {
    add_filter( 'pre_update_option_theme_mods_' . get_stylesheet(), function ( $value, $old_value ) {
         update_option( 'theme_mods_' . get_template(), $value );
         return $old_value; // prevent update to child theme mods
    }, 10, 2 );
    add_filter( 'pre_option_theme_mods_' . get_stylesheet(), function ( $default ) {
        return get_option( 'theme_mods_' . get_template(), $default );
    } );
}

#5 follow-up: @tollmanz
10 years ago

We have run into this issue repeatedly at The Theme Foundry. We had a number of users who use a workflow like this:

  1. Install parent theme
  2. Customize theme via the customizer
  3. Run into style issue that cannot be handled via a theme option
  4. Install child theme to add custom CSS
  5. Add options are lost

We tend to store our theme options in theme mods as it is a little easier to manage everything within a single option and using a simpler API.

We have already dealt with this situation with loading templates. If you want to support child theme templates overriding parent theme templates, use get_template_part(), otherwise use locate_template or include/require. A similar approach might work well here. We could have a function, say get_theme_mod_wherever_it_is (ignore the actual name for now), that looks up the child theme mod, uses it if found, otherwise falls back to the parent theme mod. Then, we could then have get_theme_mod_parent, which only ever gets a theme mod from a parent theme. Finally, get_theme_mod can continue to just get the option from the current theme's theme mod.

I think there is great value in standardizing this approach. We went to reinvent the wheel every time this problem comes up, which leads to inconsistency across our themes. Codifying an API to better handle this scenario would improve consistency for all themes.

#6 @obenland
10 years ago

The scenario tollmanz outlined would be the only valid use case for this IMO. Inherit if the previous theme happened to be the parent theme.

#8 in reply to: ↑ 5 @cpres
9 years ago

Replying to tollmanz:

Then, we could then have get_theme_mod_parent, which only ever gets a theme mod from a parent theme. Finally, get_theme_mod can continue to just get the option from the current theme's theme mod.

+1

I was hoping for this exact get_theme_mod_parent functionality for the child theme I'm working on. Losing the mods of the parent make children themes much less of children and more of their own themes with customizations needing to be built from scratch.

@obenland
9 years ago

#9 follow-up: @obenland
9 years ago

Added a proof of concept patch for the occasion when the previously active theme was the parent theme.

This ticket was mentioned in Slack in #core-flow by krogsgard. View the logs.


9 years ago

#11 in reply to: ↑ 9 @karpstrucking
9 years ago

Replying to obenland:

Added a proof of concept patch for the occasion when the previously active theme was the parent theme.

Should we account for a situation where the child theme's mods aren't "empty" before we update them? (eg. they've activated the child theme, made some changes to the mods and then gone back to the parent theme temporarily for some reason)

#12 @greenshady
9 years ago

One thing we need to watch out for is when a child theme developer overwrites the default theme mods of the parent theme. For example, child themes can hook into theme_mod_$mod, check if the user has set anything yet, and return a new default mod if not.

If core begins overwriting mods on theme switch, it'll break things. I know there are several child themes of my parent themes that wouldn't have their correct default theme mods if that were the case (mostly color options). Users will think something is wrong because the child theme they activated will look nothing like it supposed to. It'd just look like the parent theme.

What theme authors really need to be doing is using the options storage method that best suits their needs. Theme mods are specific to the get_stylesheet(). If a theme author wants to store everything based on the parent theme, there are options to do so.

I'd be in favor of a get_theme_mod_parent() function though. Or, another filter that we could introduce is theme_mod_default_$mod (or something to that effect) to give child theme developers time to switch over.

Last edited 9 years ago by greenshady (previous) (diff)

#13 @saas
9 years ago

I agree with @greenshady.

Most likely a child theme can have it's on customizer settings. If it's pre-built (Genesis Framework or Theme Hybrid child themes).

So we have two scenarios.

A child theme with it's own (specific) customizer settings & without customizer settings.

So we need to take care of both scenarios.

Also I will second @tollmanz it happens to me sometimes as well. And its not pretty sight :(.

I think by default WP should try to import parent theme customizer settings into child theme settings.

Unless user specifies otherwise. (A filter which checks whether it should import or not, and child theme can hook into this). So it should cover the issue @karpstrucking highlighted.

Also when importing, WP should provide filter, so in case child theme have it's own set of defaults (different from parent theme), it can override it.

So two checks should be present in core functionality.

#14 follow-ups: @greenshady
9 years ago

As I really dislike the idea of the initial proposal making it into core (it'll break several of my child themes and probably others that have been using theme mods for years), I've been thinking of a possible solution for theme authors who want to opt-into this functionality.

Here's a quick bit of code that I was thinking about for theme authors. It may need some tweaking (untested).

add_action( 'switch_theme', 'jt_switch_theme_update_mods' );

function jt_switch_theme_update_mods() {

	if ( is_child_theme() && false === get_theme_mods() ) {

		$mods = get_option( 'theme_mods_' . get_option( 'template' ) );

		if ( false !== $mods ) {

			foreach ( (array) $mods as $mod => $value ) {

				if ( 'sidebars_widgets' !== $mod )
					set_theme_mod( $mod, $value );
			}
		}
	}
}

#16 @jenniferncastro
9 years ago

I am new to the WordPress World and I am not going to pretend I know all the internal discussions. My view is purely from an "outsider" looking in. As a user of WordPress it really sucks to have used customize to make a bunch of changes to the theme "make" then find out best practice say's to use a child theme to further edit the css. At first I was like okay great everyone says to use a child theme how hard can that be? Well after piecing together sources across the web I was finally successful in getting the child theme installed BUT then I realized "hey, none of my settings in customize carried over"...hmm ok how hard can that be to find solution for? Well, okay here is code right here I will just try that....oh, wait I have Godaddy managed hosting (I know maybe that is my biggest mistake, but that is for another discussion). I can't change the files I need to in order to carry over all my customize settings. Now I am left with re-doing all the changes I already made in customize in the child theme only this time around I am going to skip the customize and edit the css directly. Did not mean to be long-winded my point is this:

The customize panel at the very least should include some kind of warning that if down the road you decide to use a child theme your settings you made in customize are not going to carry over to the child theme and you are better off creating the child theme then either editing the css directly or using the customize panel at that point.

As a reg lover and user of WordPress, I would love to see it standard practice for themes to come with a parent theme and child theme bundled together.

#17 @greenshady
9 years ago

...The customize panel at the very least should include some kind of warning that if down the road you decide to use a child theme your settings you made in customize are not going to carry over to the child theme and you are better off creating the child theme then either editing the css directly or using the customize panel at that point.

This has absolutely nothing to do with the customizer. The title/description of the ticket doesn't accurately describe the "issue". The customizer is an options panel (the UI you see when modifying options) rather than the data storage/retrieval method. This about how your theme stores settings. Your theme author could've chosen to store settings via the Optiona API, which would carry over into the child theme.

Theme authors like myself often need default and saved settings that are per-theme (e.g., stored on a child theme basis). That's what the Theme Mods API is good for. It sounds like your theme needed to store options using the regular Options API.

These are not new things. They've been around for years in WordPress. If you need one method, use it. If you need another method, use it.

#18 in reply to: ↑ 14 @yitwail
8 years ago

Replying to greenshady:

As I really dislike the idea of the initial proposal making it into core (it'll break several of my child themes and probably others that have been using theme mods for years), I've been thinking of a possible solution for theme authors who want to opt-into this functionality.

Here's a quick bit of code that I was thinking about for theme authors. It may need some tweaking (untested).

add_action( 'switch_theme', 'jt_switch_theme_update_mods' );

function jt_switch_theme_update_mods() {

	if ( is_child_theme() && false === get_theme_mods() ) {

		$mods = get_option( 'theme_mods_' . get_option( 'template' ) );

		if ( false !== $mods ) {

			foreach ( (array) $mods as $mod => $value ) {

				if ( 'sidebars_widgets' !== $mod )
					set_theme_mod( $mod, $value );
			}
		}
	}
}

Thanks for this code snippet. It seems to work fine, although I think 'switch_theme' should be changed to 'after_switch_theme'. Also, I'm not sure why you want to exclude 'sidebars_widgets'.

#19 @shirtsandsigns
8 years ago

I agree that this issue comes up mainly when changing to a Child theme after investing some time into customizing a theme and then realizing that direct css styling needs to be altered.

I also agree that in some cases the proposed solutions may cause unintended side effects by overwriting parent theme customizations when that isn't the desired result.

Here is my proposed solution: Add an "Import Parent Theme Customizations" section under the customizer.

This way, if a child theme is loaded it has the option to import any parent theme customizations that may have been made already, but it isn't forced to do so.

Additionally, add a message to the top of the page when a child theme is first activated calling attention to this option with a link to this customizer section. This way people are likely to immediately import the settings, and don't have to go through the anxiety of realizing their entire site has just lost its entire look and feel by accident.

Some of the other proposed solutions could still be implemented just fine for theme authors who want to support automatic behavior, but this would alleviate the common problem that I've come across myself as well where a child theme isn't considered until well after the fact.

Thank you for your time

#20 @SergeyBiryukov
8 years ago

#35997 was marked as a duplicate.

@sebastian.pisula
8 years ago

4.5-beta1-36775

#21 @sebastian.pisula
8 years ago

  • Component changed from Customize to Themes
  • Keywords has-patch added

#22 follow-up: @sebastian.pisula
8 years ago

I added custom patch.

#23 in reply to: ↑ 22 @greenshady
8 years ago

Replying to sebastian.pisula:

I added custom patch.

This patch breaks in regards to child themes setting their own defaults. This needs to be opt-in for child theme authors.

Edit: Actually, why is it part of the else statement for nav menu locations?

Last edited 8 years ago by greenshady (previous) (diff)

This ticket was mentioned in Slack in #themereview by tomusborne. View the logs.


7 years ago

This ticket was mentioned in Slack in #themereview by averta. View the logs.


7 years ago

#26 @johnbillion
6 years ago

#43906 was marked as a duplicate.

This ticket was mentioned in Slack in #core-customize by joyously. View the logs.


6 years ago

#28 @ajtruckle
4 years ago

So is this looked in to? I created my first child theme yesterday after 1+ years of tweaks to main purchased theme. Lost it all.

So gone back to main theme.

What is solution?

#29 in reply to: ↑ 14 @yura1980
4 years ago

Replying to greenshady:

As I really dislike the idea of the initial proposal making it into core (it'll break several of my child themes and probably others that have been using theme mods for years), I've been thinking of a possible solution for theme authors who want to opt-into this functionality.

Here's a quick bit of code that I was thinking about for theme authors. It may need some tweaking (untested).

add_action( 'switch_theme', 'jt_switch_theme_update_mods' );

function jt_switch_theme_update_mods() {

	if ( is_child_theme() && false === get_theme_mods() ) {

		$mods = get_option( 'theme_mods_' . get_option( 'template' ) );

		if ( false !== $mods ) {

			foreach ( (array) $mods as $mod => $value ) {

				if ( 'sidebars_widgets' !== $mod )
					set_theme_mod( $mod, $value );
			}
		}
	}
}

Had to modify this snippet slightly because of theme mods are already exist when action is ran - there can by nav_menus and sidebars.
Make it work by putting following code in the parent theme:

<?php
if( ! function_exists( 'prefix_switch_theme_update_mods' ) ) :
        function prefix_switch_theme_update_mods( $prefix_new_theme ) {

                if ( is_child_theme() ) {
                        $prefix_new_theme_mods = get_theme_mods();

                        //if is child theme and current theme mods are empty - set theme mods from parent theme
                        if( empty( $prefix_new_theme_mods ) || 1 === count( $prefix_new_theme_mods ) || 2 === count( $prefix_new_theme_mods ) ) {
                                $prefix_mods = get_option( 'theme_mods_' . get_template() );

                                if ( ! empty( $prefix_mods ) ) {
                                        foreach ( (array) $prefix_mods as $prefix_mod => $prefix_mod_value ) {
                                                // if ( 'sidebars_widgets' !== $prefix_mod )
                                                set_theme_mod( $prefix_mod, $prefix_mod_value );
                                        }
                                }
                        }
                }
        }
endif;
add_action( 'after_switch_theme', 'prefix_switch_theme_update_mods' );

It checks if theme mods in child theme has lower than 2 options - parent theme mods will be copied.
After switching to child theme this code can be deleted from parent theme.

Hope this will help someone like me )

Note: See TracTickets for help on using tickets.