WordPress.org

Make WordPress Core

Opened 9 years ago

Closed 9 years ago

#10467 closed enhancement (fixed)

Add a filter for changing where parent templates are located

Reported by: apeatling Owned by: westi
Milestone: 2.9 Priority: normal
Severity: normal Version: 2.8
Component: Themes Keywords: has-patch, themes, parent themes, buddypress
Focuses: Cc:

Description

I propose adding a filter to get_themes() that will allow plugins to redefine where to look for parent themes, based on the child themes' $template value.

Here is a scenario where this would be useful:

BuddyPress includes a default theme bundled inside of its plugin directory. Currently users have to move this theme to the themes directory. They have to do this on every single update.

If the default theme was a parent theme sitting inside of the BuddyPress plugin directory, it will be updated at the same time as the plugin is updated. This way any child themes would automatically see the updates without the need for moving folders or editing themes.

The change needed for this is very simple. Please see attached patch.

Attachments (7)

parent_theme_root.patch (431 bytes) - added by apeatling 9 years ago.
Parent theme root patch.
parent_theme_root-2.patch (1.2 KB) - added by apeatling 9 years ago.
register_theme_directory.patch (17.7 KB) - added by apeatling 9 years ago.
register_theme_directory-002.patch (17.2 KB) - added by markjaquith 9 years ago.
register_theme_directory-003.patch (17.9 KB) - added by apeatling 9 years ago.
register_theme_directory-004.patch (18.5 KB) - added by apeatling 9 years ago.
register_theme_directory-005.diff (18.7 KB) - added by markjaquith 9 years ago.

Download all attachments as: .zip

Change History (51)

@apeatling
9 years ago

Parent theme root patch.

#1 @dd32
9 years ago

The other option is for plugins to register extra themes somehow, And if the template is one that a plugin has registered, then load it from the plugin DIR instead.

..It could also be a benefit for plugins to register Page templates that way (or similar)

Just throwing another possible thought out there, As i'll run into the same thing in a few months.

#2 @apeatling
9 years ago

@dd32: That is another option, but it sounds as though it would probably be hacky. Doing it this way at least keeps within the standard way of activating and loading themes.

I've updated the patch as the $theme_loc needs to be filtered too so that the "Available Themes" screen accurately reflects the location of the parent template. The variables also need to be unfiltered at the beginning of each iteration, as to not filter the template location of other themes.

#3 @dd32
9 years ago

@dd32: That is another option, but it sounds as though it would probably be hacky. Doing it this way at least keeps within the standard way of activating and loading themes.

I guess your method would work alright, Just as long as its possible to filter both the source dir for the theme, and be able to inject it into the themes array early enough not to set off any alarm bells over incomplete themes.

The way i see it, My method is mearly a wrapper around the filters you're describing basically, much like the plugin activate hooks. But it would mean the Core handles most of the work and its done right everytime, not half-baked by a dodgy plugin or 2. (Of course, its not worth protecting against 1 or 2 dodgies, its a fact of life)

#4 @apeatling
9 years ago

But it would mean the Core handles most of the work and its done right everytime, not half-baked by a dodgy plugin or 2. (Of course, its not worth protecting against 1 or 2 dodgies, its a fact of life)

You do have to protect against this. In the second patch I've added a reset to the themes loop that will undo the filtered values. That way it's only possible to filter the values for a specific theme and not mess up any others.

#5 @westi
9 years ago

  • Cc westi added

I think it would be better to make the functionality explicit.

So make WordPress understand the concept of a number of directories containing themes.

Give plugins the ability to register_theme_folder() where one or more themes lives

#6 @josephscott
9 years ago

+1 to westi's suggestion of just making WP aware of multiple paths that contain themes. With the appropriate filters and hooks so that plugins and themes can tweak them as needed.

#7 @apeatling
9 years ago

That sounds like a better idea for sure.

#8 @ptahdunbar
9 years ago

based on the conversation in the dev meetup:

westi: So make WordPress understand the concept of a number of directories containing themes. Give plugins the ability to register_theme_folder() where one or more themes lives

Will this also be considered for plugins? A primary use case would be themes bundling canonical plugins with the theme.

#9 @strider72
9 years ago

+1 on register_theme_folder() and +! on register_plugin_folder() Great idea! :) :) :)

#10 @westi
9 years ago

I think we should do themes for now.. and consider plugins later - maybe open a separate ticket for that :-)

#11 @scribu
9 years ago

  • Milestone changed from Future Release to 2.9

#12 @markjaquith
9 years ago

This is a really important patch, but it needs to get finished ASAP in order to make it in for 2.9 — Andy, can you finish this up?

#13 @apeatling
9 years ago

I'll concentrate on this tomorrow and post what I have.

#14 @apeatling
9 years ago

I've attached a new patch that allows registration of a new a theme directory. WordPress will then consider this as an additional place to look for themes.

register_theme_directory( 'plugins/my-plugin/bundled-themes' );

All directories must be relative to the content directory.

This was considerably more in-depth than I expected. The hardest part was getting the get_template_directory[_uri]() and get_stylesheet_directory[_uri]() functions to look in varying theme roots depending on the theme in use. Currently get_theme_root() simply returns a filterable "wp-content/themes" value with no theme parameters to allow us to determine where to look.

The solution was to make sure that the $wp_themes global was available and loop through that matching the template or stylesheet name, then use the new theme root/theme root uri value. I'm not completely happy with this method though, it seems quite inefficient. I'm sure there is a better way, but it's a start.

#15 @markjaquith
9 years ago

Updated patch to be relative to WP dir and to remove unnecessary $wp_themes calls. get_themes() returns $wp_themes if it exists, so there's no need to replicate that logic in all the other functions. We can just call get_themes() and it'll give us the cached global variable or it'll dynamically populate it.

Still looking over the patch.

#16 @markjaquith
9 years ago

Works so far. Created wp-content/my-themes dir. Copied default theme there, renamed dir to awesome-theme and changed name of theme. Created child theme in regular themes dir, using Template: awesome-theme. Everything appeared to work normally.

#17 @westi
9 years ago

  • Owner set to westi
  • Status changed from new to reviewing

From a quick review the current issue we need to resolve is calling get_themes() when displaying on the front end.

That is going to scan the theme folder for every page load which is not a scalable solution.

I guess we need to cache more info in the database so the front end stuff can be all options based.

We should only be calling get_themes() when we want to display the list of themes.

#18 @ryan
9 years ago

Remember the last time we accidentally called get_themes() for a front page load? Not good indeed.

#19 @apeatling
9 years ago

I'll take another look at this in the morning. There has to be a better way of resolving the theme root without having to fetch all the themes first.

#20 @apeatling
9 years ago

I've attached another patch that will store an option with all the theme roots stored. The option will update when get_themes() is run. Instead of fetching themes on the front end and looping to find the theme root, the option is fetched. If a match for the theme is found, the theme_root or theme_root_uri is filtered with the theme root value.

#21 @westi
9 years ago

Looking over this now.

Comments:

I am trying to remove all create_function calls in WordPress which are written such that the function body is based on variables as it has been shown that this can be used to perform code evaluation so I would rather not add some more of these.

I guess we may need more code in get_theme_root and get_theme_root_uri instead.

#22 @apeatling
9 years ago

Good to know, can change.

#23 @apeatling
9 years ago

Here's a fourth patch that modifies get_theme_root() and get_theme_root_uri() to allow us to pass an optional template/stylesheet parameter. This avoids the create_function() based filters.

#24 @markjaquith
9 years ago

Should we be using the transient system to store this? We could have a front-end wrapper that grabs it from transient if available, or grabs it live and populates the transient if not.

#25 @ryan
9 years ago

Probably a transient or at least a site option. I don't really want to store this for every blog on wp.com since it will always be the same.

#26 @markjaquith
9 years ago

On for MU we can use a pre_option_ hook to grab it from sitemeta instead, and short circuit updates.

#27 @markjaquith
9 years ago

-005.diff switches to using the transient system with a cache of two hours (there is some chance that someone will shuffle themes around, not hit the admin, and change the output of get_themes()). Fetches go through get_theme_roots() which will check the transient, and if === false, run get_themes() to set it.

#28 @ryan
9 years ago

Looks good to me.

#29 @markjaquith
9 years ago

  • Resolution set to fixed
  • Status changed from reviewing to closed

(In [12025]) Introducing register_theme_directory() which takes a wp-content-relative path and will additionally scan it for themes. Plugins can use this to add themes without requiring copying by the user. props apeatling. fixes #10467

#30 @dd32
9 years ago

  • Resolution fixed deleted
  • Status changed from closed to reopened

Slight issue with this: #10953

Some other thoughts: This would be better accepting an absolute directory..

My reasoning is that plugins are not always relative to the WP_CONTENT_DIR, which really limits its usage for that.. It means that for 100% compatibility, Plugins are going to have to work out a relative link dynamically.. You cant just go hard-coding 'plugins/my-plugin-name/my-theme'.. It changes.. Passing however, dirname(FILE).'/my-theme' would work if it was a absolute path required.

I'm reopening this for feedback on the latter issue..

#31 @scribu
9 years ago

Possibly related: #10959

#32 @westi
9 years ago

(In [12043]) Restore the functionality of the theme_root filter. See #10467.

#33 @scribu
9 years ago

Possibly related: #10974

#34 @ryan
9 years ago

This breaks subdir support. Before, you could put themes in directories one level below the themes directory. Example: wp-content/themes/pub/, wp-content/themes/priv/

#35 @ryan
9 years ago

(In [12117]) Partial fix for theme subdir support. see #10467

#37 @automattor
9 years ago

(In [12119]) Theme subdirs that contain themes are not broken if missing a stylesheet. see #10467

#38 @ryan
9 years ago

(In [12129]) get_themes() and theme root fixes. see #10467

#39 @ryan
9 years ago

Theme roots should always be within WP_CONTENT_DIR, yes? If so, we can trim WP_CONTENT_DIR from the template file paths because repeating it over and over is making the themes array much bigger.

#40 @westi
9 years ago

Default theme root has to be inside WP_CONTENT_DIR but plugins don't have to be and it's plugins that are most likely to want to register an extra theme root - e.g. BuddyPress

#41 follow-up: @ryan
9 years ago

Don't the BuddyPress roots live in the plugins dir inside the content dir?

#42 in reply to: ↑ 41 @westi
9 years ago

Replying to ryan:

Don't the BuddyPress roots live in the plugins dir inside the content dir?

AFAIK yes.

#43 @ryan
9 years ago

I guess WP_PLUGIN_DIR can be defined to be outside of WP_CONTENT_DIR. I don't know if anyone actually does that.

#44 @westi
9 years ago

  • Resolution set to fixed
  • Status changed from reopened to closed

I think with [12226] we have reduced the data size of get_themes() enough for now even though we have the full paths as it works out smaller than it was with 2.8.6

We can probably improve on this but it is not going to be a small change.

Closing this as Fixed for 2.9 and opening a new ticket (#11214) for 3.0 to see if we can improve it even more and remove the full paths.

Note: See TracTickets for help on using tickets.