Short description
- Change the funky
THEME_settings($settings)to a standard FAPI form alterTHEME_form_system_theme_settings_alter(&$form, $form_state). - The default value for any variable on the theme_settings form can be initialized by placing a line in the theme’s .info file:
settings[var_name] = value - Re-work
theme_get_setting()so that you can read the setting for any theme and not just for the current theme. Also, make sure it reads the default values from the .info file. - Remove
theme_get_settings(). It blows. And we hardly use it.
Long description:
In D6, the way to add custom theme settings to a theme’s admin/appearance/settings/THEME form is to use a theme_settings($settings) function in its theme-settings.php file. And let that function return an array of form fields to be included in a specially-designated fieldset. See http://drupal.org/node/177868 for the full gory details. And they are gory! And painful. Take a look at the nightmare you have to do initialize the default values for that form, too. Ugh!
While d7 has already added a &$form parameter to that function, the whole hook really needs to be re-worked. The best solution is simply to let themes do a full form_alter on it. So lets convert the funky THEME_settings($settings) to a standard THEME_form_system_theme_settings_alter(&$form, $form_state).
Now what about the default values for those custom settings?
While a module can set default values for its variables by using the second paramter of variable_get($name, 'default val'), let's not emulate that for themes — its too annoying to have to have the default value repeated everywhere you want to get the settings value.
So, an easy way to set the default values for the custom theme settings would be to add settings[var_name] = value lines to the theme's .info file. And then to retrieve those values, it would make sense to use the existing function, theme_get_setting($setting_name)
Unfortunately, while looking at adding that "default value" functionality to theme_get_settting, we can see a snag…
theme_get_settings($key) and theme_get_setting($setting_name) are not well-written in D6. From their appearances it looks as if theme_get_settings() would retrieve all of the settings for a theme and theme_get_setting() could be used to retrieve just one. Unfortunately, if you look at the implementation details, you'll see that some settings are generated and stored inside theme_get_setting() and can't be retrieved by theme_get_settings(); specifically, the logo and favicon settings. wtf? Worse, while theme_get_settings() can retrieve settings for any theme, theme_get_setting() can only be used to retrieve a setting for the currently active theme; you can't use it to retrieve a setting from any other theme.
Fortunately for us, theme_get_settings() is only used inside theme_get_setting() and inside system_theme_settings(). So I propose we combine the functionality of theme_get_settings and theme_get_setting inside a re-worked theme_get_setting and get rid of theme_get_settings. And then, our now-much-saner theme_get_setting() can check for settings written in the .info file.
[edit: changed hook_form_theme_settings_alter() to the correct hook_form_system_theme_settings_alter() per the comment I made in #20 below.]
| Comment | File | Size | Author |
|---|---|---|---|
| #43 | list_themes_base_themes_563708_2.patch | 986 bytes | sociotech |
| #40 | list_themes_base_themes.patch | 728 bytes | sociotech |
| #28 | 563708-follow-up-1.patch | 2.4 KB | c960657 |
| #22 | theme-settings-563708-22.patch | 18.92 KB | johnalbin |
| #20 | theme-settings-563708-20.patch | 19.09 KB | johnalbin |
Comments
Comment #1
johnalbinHere are some notes I wrote as I implemented this patch. It should explain my rationale for making various changes.
Aren’t we already on the theme’s settings page (admin/appearance/settings/THEMENAME)?
Plus, the top of the page already says “These options control the display settings for the THEMENAME theme. When your site is displayed using this theme, these settings will be used. By clicking "Reset to defaults," you can choose to use the global settings for this theme.”
Which means the whole fieldset intro text is… well… completely redundant. So I’ve removed the entire fieldset. Themes are no longer restricted to where in the form they can add stuff.
$cache = &drupal_static(__FUNCTION__, array());like we use in many other places in D7.str_replace('/', '_', $key)insystem_theme_settingsis a hold-over from Drupal 5 when the "key" for a sub-theme was different. For example, Minnelli's key was "garland/minnelli" which is why we needed to replace the "/" in Drupal 5. We no longer need that, so I've removed the 3 occurrences of it.theme_get_setting()was completely misplaced.The check for maintenance mode is unnecessary since the search box is set to
''within template_preprocess_maintenance_page(). The check for the search module is already performed insystem_theme_settingswhere the search toggle is displayed. [edit: but actually, we still need to check for the search module before the search box is displayed.] And the user_access check shouldn't be done in a function whose purpose is to return what the setting is (not who has perm for that setting), so I've moved the user access check intotemplate_preprocess_page().I've attached two sample themes which should make testing this functionality easier. They both implement the new .info "settings" values, the THEME_form_theme_settings_alter function, and call theme_get_setting().
Comment #2
johnalbinI'm hopping on a plane to Paris in four hours, but if anyone wants to take this patch and run with it, please do! I'll arrive in Paris on Monday evening and see where we are then.
Comment #3
senpai commentedSubscribing for review
Comment #4
sociotech commentedJohn,
I'll look at this patch today. Sounds promising!
Comment #5
sociotech commentedApplied the patch to HEAD and tested the following functionality with the included themes.
- Verified that theme settings appear for both themes.
- Verified that theme settings from the base theme are inherited by the sub-theme.
- Verified that default values are retrieved from the .info file.
- Verified that, once changed, base and sub-theme theme settings are independent of each other.
- Verified adding my own theme settings using the THEME_form_theme_settings_alter() function, with default values set in the .info file.
- Verified that my own theme settings were correctly inherited.
That's what I can say with my initial testing. I'll try to do some more testing tomorrow.
As far as I can tell at this point, it's looking good for D7 code freeze. This is a critical step forward for true theme inheritance. Great work!
Comment #6
joshmillerPatch applied with fuzz. Attached is simply chasing HEAD. I will follow it up with a code review and testing review.
Josh
Comment #7
robloachSubscribing.com/org.net
Comment #8
joshmillerUser Interface Review
I did quickly look through the code... and found no coding style issues
Comment #9
Zarabadoo commentedI have grabbed a super fresh copy of HEAD and applied the patch posed in #6 and then installed. I did not get any notices or warnings that were mentioned above and things looked to be functioning perfectly.
As for my opinion, I like the way this works and is a definite improvement over 6's settings.
Comment #10
johnalbinLet me explain that now since I didn't do it before. Cabaret is a sub-theme of Liza. And each of those themes adds 1 checkbox to their custom settings form. So Liza shows just its own checkbox. And Cabaret correctly shows its base theme’s checkbox (from Liza) and its own checkbox.
Oh! Looks like I was slightly wrong here: “The check for the search module is already performed in system_theme_settings where the search toggle is displayed.” The check for search module also needs to be done in template_preprocess_page(). Good catch!
Also, I found a small bug with the bit of code that was doing “allow theme_get_setting() to work without having to pass the theme name to it” In Caberet's theme-settings.php, I had
$form['liza_example']['#default_value'] = theme_get_setting('liza_example');but that should have been completely unnecessary since when configuring Caberet, liza's theme-settings.php setup should be using the default values for Caberet and not the default values for Liza.Re-rolled with those 2 fixes.
Comment #11
johnalbinTo make this patch even easier to review, let me write the proposed upgrade docs here:
In Drupal 6, themes could add custom form elements to their “configure theme settings” page at admin/build/themes/settings/THEMENAME. Themes would need to create a theme-settings.php page in their theme directory and use a function with the following syntax:
In Drupal 7, much more flexibility is given to themes to modify the entire theme settings form. In a theme’s theme-settings.php, themes should now use a
THEMENAME_form_system_theme_settings_alter(&$form, $form_state)function. This gives the same power to themes that modules have if they use hook_form_system_theme_settings_alter(). See the “Forms API Quickstart Guide” and “Forms API Reference” on http://api.drupal.org/api/7, as well as the hook_form_FORM_ID_alter() docs to learn the full flexibility of Forms API. Note that themes can no longer use the phptemplate_ prefix to the function; you’ll need to use the actual name of your theme as the prefix.Here’s an example if you had a “foo” theme and wanted to add a textfield whose default value was “blue bikeshed”:
In order to set the default value for any form element you add, you’ll need to add a simple line to your .info file:
settings[SETTING_NAME] = DEFAULT_VALUE. For our foo theme, you’d need to edit the foo.info file and this line:In any of your theme’s php files, you can retrieve the value the user set by calling:
[edit: changed hook_form_theme_settings_alter() to the correct hook_form_system_theme_settings_alter() per the comment I made in #20 below.]
Comment #12
joshmillerI have reviewed this new patch and have found no more problems. JohnAlbin wanted RobLoach to review, but I think he is not around today, so I will try and find more themers to review... if they are happy then RTBC.
Josh
Comment #13
joshmillerTalked to mortendk and zarabadoo (sp?) in the hall and mortendk loves the idea of a form_alter() for theme form. Default values being set in .info was also a high point. zarabadoo plans to review and possibly RTBC it.
Comment #14
Zarabadoo commentedI have looked at this and it all seems to be functioning properly without errors. As said before, I approve completely and will mark this as a reviewed and tested patch.
Comment #15
robloachSorry for missing you guys at the code sprint, Josh and John. My flight left a bit earlier then I had thought. I got a chance to take a look at the patch though, and have to say that it really helps out the themer's experience when getting theme-specific settings. RTBC++.
Comment #16
dries commentedPersonally, I'm not a big fan of storing defaults in .info files. That seems to be a new feature? Why wouldn't we specify the defaults in _hook_alter() like we do elsewhere? If we want to aim for consistency, we should, IMO.
Also, should there be a theme.api.php where we keep such examples?
Comment #18
sociotech commentedDries,
I think being able to store theme defaults in .info files would be a big win for sub-themers. That way, a sub-theme could easily change the base theme defaults without having to add its own theme-settings.php file. It's a lot more accessible and flexible, and it should help encourage the creation of more themes in general.
And conceptually, .info already stores defaults for standard theme settings in the form of which Features to display (logo, name, slogan, etc.). The difference with this patch is that it would also include advanced (custom) theme settings as well. From a themer's point of view, it feels like a very "natural" place to put theme setting defaults for a particular theme or sub-theme.
This feature and the rest of this patch are important steps in cleaning up the theming/inheritance system for D7, so it can enable easier theme creation.
Comment #19
sociotech commentedUm, I'm assuming that this patch is making it into D7 before the end of the code slush. Is that a correct assumption? Is there anything else that needs to be done in order to get this committed?
Comment #20
johnalbinHere's a mostly-straight re-roll of #10 with the addition of a hook_form_system_theme_settings_alter docblock in system.api.php.
Also, the correct name of the hook is hook_form_system_theme_settings_alter, not hook_form_theme_settings_alter like I previously had. Not sure how I managed to bork that in the previous patch.
As for not storing default values in .info files, I have a few points:
variable_get('var_name', 'default_value')construct. Unfortunately, it went nowhere. So the only thing the theme layer could try to be "consistent with" is thevariable_get('var_name', 'default_value'). So we could havetheme_get_setting('var_name', 'default_value'); however…If we were to use a base theme is doing something on behalf of a sub-theme, it would be much more efficient if the sub-theme only had to change the default value for that variable in one place (the .info file in this patch) and not have to override and or rebuild the entire theme function/preprocess/whatever in order to change the default variable value. That's super inefficient.
Comment #21
sunLooks like there was some kind of confusion regarding theme settings variable defaults in .info files. Apparently, that's a hidden "feature" we already have since D6 or earlier, because everything in .info is taken over into the registry.
This patch seems to bring theme_get_setting() more in line with variable_get(), so it returns a default value. That is good for themers, and that will also be good for the final variable defaults API change we all wait for and hopefully see in D8.
Comment #22
johnalbinRemoved 3 lines of comments accidentally left in the hook_form_system_theme_settings_alter docblock after a copy and paste. Identical patch otherwise.
Comment #23
dries commentedI'm still not a fan of abusing info files for settings -- I warned against that trend 3 years ago but was assured it wouldn't happen.
Either way, given that it was already a hidden feature in Drupal 6, I decided to commit this patch.
Comment #24
dave reidIs it really necessary that we document hook_form_system_theme_settings_alter()? We don't document any other hook_form_FORM_ID_alter() and it seems this would be more appropriate for the 'upgrading themes from D6 to D7' handbook.
Comment #25
gábor hojtsyWas not documented in the update guide.
Comment #26
johnalbin@Gábor: It has been now! :-)
@Dave: I'm working right now on a patch that moves the hook_form_system_theme_settings_alter() documentation from the @hooks group to the @themeable group and creates a includes/theme.api.php file. I've got a local api test setup to make sure everything is working properly. I'll post it to a new issue though. Thanks for pointing out that I missed Dries' request to put the docs in a new file!
Comment #27
robloachIs this related? #591794: Allow themes to alter forms and page.
Comment #28
c960657 commentedThere is still a call to theme_get_settings() in search.test - I don't know how the test bot missed that.
Comment #29
dries commentedSomehow, the patch doesn't apply. Requesting retest.
Comment #31
sunThat's because I removed the 'toggle_search' setting, which is in the context of these lines.
Comment #32
sociotech commentedNow that theme_get_settings() is gone, what is the preferred method for getting all of a theme's settings in one call?
Comment #33
jarek foksa commentedSaving custom settings from admin panel does not work if form element is put inside fieldset. Am I doing something wrong here?
theme-settings.php:
mytheme.info:
Comment #34
johnalbinI answered sociotech's question in IRC. He asked because in D6, theme_get_settings() was required as part of a completely-hacky way to get the defaults merged with the saved settings in the db. This issue removes the need for that D6 hack, so theme_get_settings() is no longer needed.
@jarek: really? I've used fieldsets in D6 theme-settings.php; I don't see why they wouldn't work in D7.
@Dave Reid: Here's where I'm working on adding a theme.api.php file and moving hook_form_system_theme_settings_alter() out of the @hook documentation. #610408: Create theme.api.php to help consolidate theme-related docs.
Comment #35
jarek foksa commentedI just had to remove '#tree' => TRUE from fieldset definition. I don't know forms API very well yet, I'm guessing that #tree didn't make sense in this context anyway. So my issue probably was not related to reimplementation of theme_get_setting()
So far I have built quite big form with custom theme settings - works flawlessly.
Comment #36
c960657 commentedThe last theme_get_settings() call was removed in #550742: Remove Search box from theme system, default to block system instead, so I am setting this back to “fixed”.
Comment #38
johnalbinNeeds docs.
Comment #39
jhodgdonWhat sort of documentation does this need -- can you be more specific?
Also, updating component etc. to how we indicate that a patch whose code has been fixed now needs documentation.
Comment #40
sociotech commentedI've been converting themes to D7 and noticed that my base theme's settings weren't being inherited by sub-themes.
I also checked it out with a Garland sub-theme and found the same problem.
So, I traced it back to this line (1155) in theme.inc (HEAD 1.582) in the theme_get_setting() function which is supposed to get all the base themes, which are then used to loop through merging theme settings:
But the $theme_object doesn't have an entry for base_themes, just base_theme. So it fails the test, never gets the base themes, and the base themes are never checked for settings to inherit.
Following back to the list_themes() function in theme.inc that generates the $theme_object, it doesn't look like it ever generates the base_themes information.
I tried the following code to fix the problem in list_themes() and found that it allowed base theme settings to be inherited:
Can anyone else replicate this behavior? Unless I'm missing something, this seems fairly major.
I've attached a patch for testing.
Comment #41
sociotech commentedComment #43
sociotech commentedNew patch chasing HEAD (theme.inc 1.583) that uses internal theme list (modified to use theme names as keys) and should remove dependency on db causing Drupal install failure.
Comment #44
sociotech commented#43: list_themes_base_themes_563708_2.patch queued for re-testing.
Comment #45
johnalbinThis is a symptom of a deeper problem. See #761608: Missing theme settings values because list_themes() has inconsistent theme object data.
So the patch in #43 is the wrong fix. I'm still working on figuring out the right fix over in that issue. :-)
Comment #46
sociotech commentedJohn,
Anything I can do to help out with this issue?
Comment #47
treksler commentedlooking at the following code,
how exactly does theme_get_setting('foo example') here know which theme we are editting?
AFAICS, it does not.
It looks like no matter what theme you are editting, it will always save these settings in the current theme
Shouldn't theme settings files do
and shouldn't this
be this
or perhaps more simply we could just have the theme settings files call
Comment #48
treksler commentedalso, should we not have also changed
$themes[$key]->prefix . '_engine_settings'to
$themes[$key]->prefix . '_form_system_theme_settings_alter'right now there is no way to update the themesettingsapi module because we can't tell whether we are dealing with phptemplate_engine_settings($settings) or phptemplate_engine_settings($form, $form_state)
it would be much clearer if we had phptemplate_engine_settings($settings) and phptemplate_form_system_theme_settings_alter($form, $form_state)
Comment #49
xjmTracking.
Comment #50
aquariumtap commentedre: #40, I think we've identified where the base_themes array is being lost over at #761608: Missing theme settings values because list_themes() has inconsistent theme object data.
Comment #51
dvessel commentedA very late realization but there's a bug related to this: #943212: hook_form_system_theme_settings_alter() vs. THEME_form_system_theme_settings_alter() name-clash
Comment #52
johnalbinOk. There's a patch over in #761608: Missing theme settings values because list_themes() has inconsistent theme object data to work on the follow-up bug mentioned in comment #40 above. Needs reviews!
And Joon has another related issue over in #943212: hook_form_system_theme_settings_alter() vs. THEME_form_system_theme_settings_alter() name-clash
I think we can safely close this issue back up and follow-up in those other issues.
Comment #53.0
(not verified) commentedchanged hook_form_theme_settings_alter() to the correct hook_form_system_theme_settings_alter() per the comment I made in #20 below.
Comment #55
liquidcms commentedUnfortunate that with all these changes they missed allowing these theme variables being allowed to be set in the settings.php file.