WordPress.org

Make WordPress Core

Opened 2 years ago

Last modified 3 months ago

#20509 new feature request

Theme idea - generic.php

Reported by: johnjamesjacoby Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version:
Component: Themes Keywords: needs-patch dev-feedback
Focuses: Cc:

Description (last modified by johnjamesjacoby)

Problem: How do plugins that introduce completely new functionality (I.E. BuddyPress/bbPress) interface with themes, without needing to move template files directly into a theme's folder?

Solution: generic.php


generic.php would be a template file that includes skeletal layout of the theme. In place of the content and the comment logic, is an action; for the sake of discussion, let's name this action 'generic_content'

A concept file is attached for twentyeleven.

This type of ability would help prevent a ton of additional processing that plugins currently need to do to hi-jack the_content output and noop the comment stream, in situations where a plugin needs to output HTML into the main content area of a theme.

Rather than guessing at template names, and hoping 'page.php' or 'index.php' will be close enough, a convention of having themes come with a dedicated template for plugin compatibility would be incredibly helpful.

BuddyPress components have a similar convention already, with a plugin.php having only the header, footer, sidebar, and generic action in them.

The use case is currently small, and there's very little (if anything) needed in WordPress core to make this work. Wanted to post the idea here to get some developer talk going, and get opinions on other possible approaches.

Attachments (3)

generic.php (549 bytes) - added by johnjamesjacoby 2 years ago.
20509.patch (3.5 KB) - added by johnbillion 2 years ago.
generic-test.zip (1.1 KB) - added by johnbillion 2 years ago.

Download all attachments as: .zip

Change History (57)

johnjamesjacoby2 years ago

comment:1 johnjamesjacoby2 years ago

  • Description modified (diff)

comment:2 mordauk2 years ago

I think this is a great idea. Obviously it would be hard to encourage theme developers to all adapt this, but it would make plugins developer's lives much easier for those that did.

If something like this is implemented, it would probably be immensely helpful in convincing developers to include the generic file if all themes on the repo were required to have it.

comment:3 scribu2 years ago

I think this could work.

I had a more radical idea: make all the template files go through generic.php (I called it wrapper.php):

http://scribu.net/wordpress/theme-wrappers.html

comment:4 follow-up: scribu2 years ago

I wonder if we could do a bit more with this. What I'm thinking of:

add_action( 'parse_request', function($wp) {
  if ( isset( $wp->query_vars['foo'] ) ) {
    $wp->is_generic_request = true;
  }
}

This would skip running WP_Query and would load generic.php, making plugin dev's lives even easier.

comment:5 follow-up: rzen2 years ago

I'm for it!

You can be sure that I will add just such a template to StartBox the moment this is a supported feature.

@scribu: I really liked your wrapper.php concept when I read about it. As a theme dev I'm right there with you in dislike for repeated code.

comment:6 in reply to: ↑ 4 johnjamesjacoby2 years ago

Replying to scribu:

This would skip running WP_Query and would load generic.php, making plugin dev's lives even easier.

I think there's a ticket somewhere in the org universe along those lines; maybe in the BuddyPress trac. Years ago we tried nooping the main query loop, and discovered that WordPress hates the idea in several ways.

Would be worth exploring to optionally avoid the added overhead where it might not be needed.

comment:7 in reply to: ↑ 5 johnjamesjacoby2 years ago

Replying to rzen:

As a theme dev I'm right there with you in dislike for repeated code.

Themes have to balance DRY and convenience, and it's not always easy to do (as I'm sure you know.) Genesis is basically built entirely of hooks and is infinitely flexible, but less easy to grok at a glance because of it.

comment:8 sabreuse2 years ago

  • Cc sabreuse@… added

comment:9 mercime2 years ago

  • Cc mercijavier@… added

comment:10 Ipstenu2 years ago

  • Cc ipstenu@… added

comment:11 slaffik2 years ago

  • Cc slaffik added

comment:12 sooskriszta2 years ago

  • Cc vpundir@… added

comment:13 chrisclayton2 years ago

  • Cc chrisclayton added

comment:14 dreamwhisper2 years ago

  • Cc dreamwhisper added

comment:15 toscho2 years ago

  • Cc info@… added

comment:16 garinungkadol2 years ago

  • Cc garinungkadol added

comment:17 follow-ups: johnbillion2 years ago

I like this idea.

I wonder what would be the best way to implement it. It might be nice if plugins could register the path to their bundled generic.php as a fallback file, that way they won't need to mess around with hooking into template_include and checking for the existance of generic.php in the theme.

Example plugin code:

if ( get_query_var( 'my_plugin_query_var' ) )
	register_generic_plugin_file( plugin_dir_path( __FILE__ ) . '/generic.php' );

Then we'd have a function similar to all the other get_*_template() functions used by the template loader which loads the generic template when is_generic_request() is true:

function get_generic_template() {

	$templates = array();

	$templates[] = 'generic.php';

	if ( $fallback = get_generic_plugin_file() )
		$templates[] = $fallback;

	return get_query_template( 'generic', $templates );
}

comment:18 cais2 years ago

  • Cc edward.caissie@… added

comment:19 curtismchale2 years ago

  • Cc curtis@… added

comment:20 in reply to: ↑ 17 johnjamesjacoby2 years ago

Replying to johnbillion:

I like this idea. (...)

That's exactly the kind of supplemental code I was thinking this would need. Thanks for jumping in.

comment:21 in reply to: ↑ 17 chrisclayton2 years ago

Replying to johnbillion:

It might be nice if plugins could register the path to their bundled generic.php as a fallback file, that way they won't need to mess around with hooking into template_include and checking for the existance of generic.php in the theme.

Agreed, would be nice. But, what about a series of default fallbacks too?

Something like:

  • theme/generic-plugin.php (eg. generic-bbpress.php will tell bbpress to use this special template)
  • theme/generic.php (basic generic template for other plugins)
  • the-plugin/generic.php (the plugin that is trying to use generic.php. eg. plugins/bbpress/generic.php)
  • theme/index.php (If all else fails, let's throw it inside index.php instead of leaving users with nothing)

etc.

Last edited 2 years ago by chrisclayton (previous) (diff)

comment:22 follow-up: scribu2 years ago

+1 on falling back to index.php.

-1 to generic-{plugin}.php. It's not useful, since the plugin has to tell WP when to load a generic template, so might as well pass whatever path they want.

Last edited 2 years ago by scribu (previous) (diff)

comment:23 johnbillion2 years ago

There's no point in having a file such as generic-bbpress.php because that's not generic. In this case, the theme would use whatever templating hierarchy bbPress provides. The idea of the generic.php is so a theme can provide a generic template for plugins which it doesn't natively support.

+1 on falling back to index.php. My code above probably doesn't work as expected, it was just to get the idea down.

comment:24 scribu2 years ago

In other words, the idea is for plugins to be able to define a hierarchy from within generic.php.

comment:25 scribu2 years ago

  • Keywords needs-patch added

Actually, that's not the main idea, but it's a possibility:

add_action( 'generic_content', function() {
  echo '<h1>My Plugin</h1>';

  if ( get_query_var( 'my_plugin_list' ) )
    $specific_template = 'my-plugin-list.php';
  else
    $specific_template = 'my-plugin-table.php';


  locate_template( array( $specific_template, 'my-plugin.php' ), true );
}

I think it's time for someone to write a patch, to move the process along.

comment:26 in reply to: ↑ 22 chrisclayton2 years ago

Replying to scribu:

-1 to generic-{plugin}.php. It's not useful, since the plugin has to tell WP when to load a generic template, so might as well pass whatever path they want.

While this will probably be completely different, from experience with BuddyPress (as JJJ mentioned, this idea is somewhat inspired by their plugins.php) not all plugin developers will add additional possible template names (i've seen afew BuddyPress plugins that won't check much further than plugins.php and without filtering (or using conditionals) it's impossible to give it a different look)

Using your example they will do:

add_action( 'generic_content', function() {
  echo '<h1>My Plugin</h1>';
  echo 'my content';
}
Version 2, edited 2 years ago by chrisclayton (previous) (next) (diff)

comment:27 follow-up: scribu2 years ago

generic-{plugin}.php would be useful if you wanted to control the appearance around a specific plugin. This conflicts with register_generic_plugin_file(), which would do the same thing, like it or not.

Speaking of which, register_generic_plugin_file() seems kind of wrong. The whole point is that the plugin can't know what a generic.php template should look like.

BuddyPress could still do something like this:

add_filter( 'template_include', function ( $path )
{
  if ( is_generic_request() && 'index.php' == basename( $path ) )
  {
    // Fall back to plugin.php if there's no generic.php
    return plugin_dir_path( __FILE__ ) . '/plugin.php';
  }

  return $path;
}

Note that this:

// From WP Core
load_template( plugin_dir_path( __FILE__ ) . '/my-template.php' );

is different from this:

// From WP Core
load_template( locate_template( 'generic.php' ) );

...

// From Plugin
add_action( 'generic_content', function() {
  load_template( plugin_dir_path( __FILE__ ) . '/my-template.php' );
} );

In the second case, generic.php encloses my-template.php, making my-template.php a template part, rather than a whole template.

Last edited 2 years ago by scribu (previous) (diff)

comment:28 scribu2 years ago

We want plugins to be able to render template parts, not whole templates.

comment:29 scribu2 years ago

My argument is that register_generic_plugin_file() is a bad idea because it creates confusion.

The assumption should be that, once you've set is_generic_request = true, the 'generic_content' hook should fire no matter what. It's up to Core to figure out how to do that.

comment:30 follow-up: scribu2 years ago

Maybe we can have a fallback generic.php template in Core, like we have for sidebar.php.

comment:31 follow-up: dd322 years ago

Maybe we can have a fallback generic.php template in Core, like we have for sidebar.php.

I have a feeling that would go against (my understanding of) the purpose of the original idea myself.

The part of the problem that this is trying to solve is that you can't just include get_header() / get_sidebar() and get_footer() into a template file and expect the output to look like the theme, it needs to use the themes markup, As soon as we look at having a core-included "backup" we simply move the problem from plugins into core.

I'd have thought that the best backwards compatible option would be to make it easy for the page template to be used (failing that, the index template) through the same action as the generic template hook uses.. so hopefully the plugin/theme shouldn't have to do anything special at all.

comment:32 in reply to: ↑ 30 chrisclayton2 years ago

Replying to scribu:

Maybe we can have a fallback generic.php template in Core, like we have for sidebar.php.

That was actually going to be my originally suggestion in place of index.php but i wanted to do some homework on the core dev's views on including templates in core (considering their trying to deprecate all the other included templates - seemed like it would go against the goals) before i suggested it.

comment:33 in reply to: ↑ 27 johnbillion2 years ago

Replying to scribu:

Speaking of which, register_generic_plugin_file() seems kind of wrong. The whole point is that the plugin can't know what a generic.php template should look like.

I think you're getting confused here. Currently, a plugin such as BuddyPress can contain its own template file for rendering output. Usually, this is going to be a file containing get_header() and get_footer() and some content in between.

This doesn't always work with themes that need to markup in the individual template files around the content area (as you said, "The whole point is that the plugin can't know what a generic.php template should look like."), so the idea is that a theme can include a template file called generic.php which, most of the time, is going to be pretty similar to page.php or index.php, but it'll have a call to do_action('generic_content') in place of the loop.

This generic.php file will be at the top of the template hierarchy for generic requests, but plugins will be able to register their bundled generic template file as a fallback for themes which don't have a generic.php template file. This means that newer themes which include a generic.php template get more control over the layout of a generic request, and older themes without the template file will fall back to the current system where plugins load their own template file.

BuddyPress could still do something like this:

add_filter( 'template_include', function ( $path )
{
  if ( is_generic_request() && 'index.php' == basename( $path ) )
  {
    // Fall back to plugin.php if there's no generic.php
    return plugin_dir_path( __FILE__ ) . '/plugin.php';
  }

  return $path;
}

Correct, but that code should be in core instead of in each plugin, and the plugin should just be able to register its generic.php file to be used in this situation.

// From Plugin
add_action( 'generic_content', function() {
  load_template( plugin_dir_path( __FILE__ ) . '/my-template.php' );
} );

In the second case, generic.php encloses my-template.php, making my-template.php a template part, rather than a whole template.

It's entirely down to the plugin what it does inside the generic_content hook. It can include other tempalte parts or output stuff directly. That's not what this ticket is addressing.

johnbillion2 years ago

johnbillion2 years ago

comment:34 johnbillion2 years ago

Attached is a first pass at a patch and a test plugin.

If you visit example.com/?foo=1 then the generic request template will be located and loaded, and the plugin will output 'Hello world!'.

If your theme contains a generic.php template file (see the earlier attached generic.php from jjj) then this will be used. If not, then the plugin's bundled template/generic.php will be used instead.

comment:35 scribu2 years ago

This means that newer themes which include a generic.php template get more control over the layout of a generic request, and older themes without the template file will fall back to the current system where plugins load their own template file.

Yes, and what I'm suggesting is that the fallback be handled by Core, since plugins don't have more information than Core anyway.

I'd have thought that the best backwards compatible option would be to make it easy for the page template to be used (failing that, the index template) through the same action as the generic template hook uses.. so hopefully the plugin/theme shouldn't have to do anything special at all.

Ok, then let's do that.

comment:36 scribu2 years ago

Regarding 20509.patch:

'is_generic_request' shouldn't be a property of WP_Query, since we'll want to skip WP_Query entirely.

comment:37 follow-up: scribu23 months ago

So, I went ahead and added generic.php to a theme. This is what I ended up with:

https://gist.github.com/2890140

comment:38 DrewAPicture23 months ago

  • Cc xoodrew@… added

comment:39 in reply to: ↑ 37 johnjamesjacoby23 months ago

Replying to scribu:

So, I went ahead and added generic.php to a theme. This is what I ended up with:

https://gist.github.com/2890140

This is really neat. I like how simple it is. I have a problem with the semantics, though.

I'd rather we didn't use template_redirect, but instead ride on the shoulders of template_include and pull in the generic.php or plugin-{slug}.php from that point within WordPress core.

If plugins are expected to develop things to work and look like wordPress core, it needs to be just a bit easier to do it the WordPress way.

comment:40 in reply to: ↑ 31 johnjamesjacoby23 months ago

Replying to dd32:

Maybe we can have a fallback generic.php template in Core, like we have for sidebar.php.

I have a feeling that would go against (my understanding of) the purpose of the original idea myself.

The part of the problem that this is trying to solve is that you can't just include get_header() / get_sidebar() and get_footer() into a template file and expect the output to look like the theme, it needs to use the themes markup, As soon as we look at having a core-included "backup" we simply move the problem from plugins into core.

I'd have thought that the best backwards compatible option would be to make it easy for the page template to be used (failing that, the index template) through the same action as the generic template hook uses.. so hopefully the plugin/theme shouldn't have to do anything special at all.

This is the way that bbPress works, and the problem with this approach is having additional markup for pagination, Previous/Next, and comments. It would be great if themes came with the header, sidebar, footer, and surrounding mark-up, without any of the post/page/WordPress specific bits hard-coded into them.

comment:41 scribu23 months ago

I'd rather we didn't use template_redirect, but instead ride on the shoulders of template_include and pull in the generic.php or plugin-{slug}.php from that point within WordPress core.

I can make it use 'template_include' instead of 'template_redirect', but I don't see how that would help. You'd just do some more processing, only to throw it away.

Also, what do you mean by plugin-{slug}.php?

comment:42 scribu23 months ago

To maybe simplify discussion, what we're trying to implement here is called template inheritance in Django:

Template inheritance allows you to build a base "skeleton" template that contains all the common elements of your site and defines blocks that child templates can override.

https://docs.djangoproject.com/en/1.4/topics/templates/#template-inheritance

And the central idea is that a plugin cannot define a generic skeleton template, so it has to rely on the theme to do that. It can only fill in the content block.

That's why I asked what you meant by plugin-{slug}.php:

  • If you mean an additional skeleton template, that defeats the purpose
  • If you mean the name of a content block, it would be pretty rigid to only allow a single block per plugin.
Last edited 23 months ago by scribu (previous) (diff)

comment:43 scribu23 months ago

Let's call the generic.php template defined in the theme a "skeleton template".

In our case, the skeleton template would have a single block, the content block.

So, let's call the partial template files defined in plugins "partial templates".

Now, do you mean to suggest that a plugin should be able to select between multiple skeleton templates from the theme? Obviously, the fallback template would be page.php

I'll stop guessing now. :P

comment:44 jkudish21 months ago

  • Cc joachim.kudish@… added

comment:45 boonebgorges21 months ago

  • Cc boonebgorges@… added

comment:46 Japh21 months ago

  • Cc japh@… added

comment:47 greenshady19 months ago

  • Cc justin@… added

comment:48 Jayjdk19 months ago

  • Cc kontakt@… added

comment:50 scottbasgaard17 months ago

  • Cc mail@… added

comment:51 johnjamesjacoby16 months ago

Also, what do you mean by plugin-{slug}.php?

I mean plugin-buddypress.php, plugin-bbpress.php, plugin-post-type-switcher.php, etc... Taking the directory name, and using it as a fallback... This allows for themes to target specific plugins opt-in, conditionally. This allows for stuff like:

  • BuddyPress gets a full-page width template.
  • bbPress gets a forum specific sidebar.

Plugins are responsible for filling in their own content areas, and even the content sidebar area. Common elements should be left alone to the theme and WordPress to sort out (header, footer, container, etc...)

Adding plugin-{slug}.php to the template_include filter is actually pretty easy to do in a plugin already (both bbPress and BuddyPress do this) -- but it would be nice to have a way in core to register a callback that determines what those boundaries are to identify when that template should get used VS some other one, rather than having to build all of it every time.

This is not unlike the way the template-loader.php file works already; just a big if statement that says:

  • Let's look for some conditions...
  • If that thing happens...
  • Load a specific template...
  • In a specific expected order...
  • Unless we don't have anything...
  • Then fallback to index.php.

We could go as far as making it something that WordPress core uses and registers itself. Rather than having a huge if statement, we just register filters in priority order to template_include. That cleans up template-loader.php down to a few lines of code, and introduces router functions to handle the above logic.

comment:52 desrosj16 months ago

  • Cc desrosj@… added

comment:53 beaulebens13 months ago

  • Cc beau@… added

comment:54 alex-ye3 months ago

  • Cc nashwan.doaqan@… added
Note: See TracTickets for help on using tickets.