After rooting around a bit in PHPTemplate, I realized the same mechanism that lets node.tpl.php be overridden by node-page.tpl.php could be used for other callbacks. The attached patch applies this logic to the phptemplate_page() function. It allows the first arg of the current URL to be used to distinguish page templates.
page-admin.tpl.php, page-node.tpl.php, page-forum.tpl.php, and so on could all be used to vary look and feel of the entire page.
This is my first attempt at a patch to PHPTemplate, so I may be going in a terribly flawed direction. It looks like it works, though, and would make the creation of custom administrative pages in particular a LOT simpler for themers.
Comment | File | Size | Author |
---|---|---|---|
#37 | phptemplate.engine_14.patch | 4.09 KB | eaton |
#26 | phptemplate.engine_12.patch | 3.42 KB | eaton |
#25 | phptemplate.engine_11.patch | 3.42 KB | eaton |
#20 | phptemplate.engine_10.patch | 856 bytes | eaton |
#18 | phptemplate.engine_9.patch | 854 bytes | eaton |
Comments
Comment #1
Dublin Drupaller CreditAttribution: Dublin Drupaller commented+1 million from me for this idea. nice one Eaton
Dub
Comment #2
Dublin Drupaller CreditAttribution: Dublin Drupaller commentedforgot to mention...I tried the patch and it works perfectly.
Dub
Comment #3
pwolanin CreditAttribution: pwolanin commentedI like the generality of the approach, since it also means any module that creates a custom path can have a separately themed section.
Comment #4
bradlis7 CreditAttribution: bradlis7 commentedGreat idea, IMO. I didn't try it, but I don't see any potential problems with the code.
Comment #5
pwolanin CreditAttribution: pwolanin commentedThe patch code looks so obvious I'm surprised it doesn't already work this way!
One question, comparing your patch to the code on this handbook page:
http://drupal.org/node/45951
I'm wondering first if you need to special-case arg(0) == 'node'. Right now, I think your code would look for 'page-node.tpl.php' for every page with URL node/x, no?
Comment #6
eaton CreditAttribution: eaton commentedYes, it would look for page-node.tpl.php right now. I'm not too concerned about that; it could be used to always show an extra region on node pages, for example.
It differs from that theme-snippet in that it only looks at the first arg, and doesn't allow multiple args to be mapped to a single file (node/edit and admin all going to one file, for example). There's nothing, though, that prevents a themer from using the techniques outlined in that theme snippet to override the template file that will be used. This just makes it easier 'by default' to drop a file in and go, without any additional code on a themer's part.
Comment #7
eaton CreditAttribution: eaton commentedI just realized it would be easy enough to put a front-page special case in there, too. if drupal_is_front_page() returns true, it could look for page-front.tpl.php as well. It wouldn't be as robust as comprehensive solutions like frontpage.module, but again, for most folks it would be More Than Good Enough.
Feature creep? Just Right? I think that could be very useful, too, though I'd really like to see it committed, and the simpler it is the easier it is to do that.
Comment #8
jadwigo CreditAttribution: jadwigo commented+1 from me
with this one nobody ever needs to ask for an admin theme
only what will happen when you go to admin/settings or any path with more than one level... if that would work too it would be even cooler
Comment #9
eaton CreditAttribution: eaton commentedAny pages under the /admin menu hierarchy would use the /admin page template. There's nothing stopping us from going even deeper in the hierarchy, but this is a quick and easy approach that doesn't break anything.
Comment #10
Nick Lewis CreditAttribution: Nick Lewis commentedNot to play captain grumpy here, but this patch is a demon we should avoid. I have enough problems getting one page.tpl.php right -- the idea of having a seperate page.tpl.php file for every arg(0) would be nightmarish from a theme development perspective. This is a great snippet of code, but it doesn't belong in the engine. People have enough trouble theming as it is, without then having to worry about maintaining 5 seperate page.tpl.php files.
Again, good snippet -- bad and uncessary default for the engine. What's wrong with just using the template.php file? My 2 cents.
Besides, most of the time, all you need to do is change the body ID of the page.tpl.php if you want wildly different looks. Remember?.
Comment #11
pwolanin CreditAttribution: pwolanin commentedI don't think 5 files are required. If I understand the patch right, page.tpl.php will still be the default if no other temple is found. Thus, the patch will not break existing templeates, only allow a somewhat simpler mechanism for theming admin or other sections for those who care to try.
Comment #12
Nick Lewis CreditAttribution: Nick Lewis commentedWell, that may be true, -- actually, wait no, I just tested the code and I was dead wrong. Did I mention I'm tired?
+1 from me
I still think that this method leads to a world of trouble ( with a capitol T )... but that's just my opinion -- and I'm a CSS snob and zealot.
Code works great, makes sense, and gives people more freedom. So I'm all for it.
Comment #13
Crell CreditAttribution: Crell commentedIf I'm understanding this correctly... would it make sections.module obsolete? And how could you get the same effect with some other engine, such as Smarty? Or is that even possible?
Comment #14
Dries CreditAttribution: Dries commentedA little bit of topic, but from a user point of view, I always wanted to be able to associate a template with my posts (node pages). Here is how I envision this to wok. A designer makes me several templates; in its simplest form that could mean various templates with different color scheme, or maybe with different images. Then, when I'm writing a post, a drop down menu is presented that lets me pick one of the available templates. So when I'm writing about Drupal, I'd select my 'Drupal template' and when I'm writing about 'Photography' I'd select my photography template, or when it is Christmas, I'd select my Christmas template (regardless of the fact I'm writing about Drupal or Photography). So as a writer, I get to choose the template.
Of course, this isn't what this patch is about but I wonder if this patch will make it harder or easier to accomplish that.
Comment #15
pwolanin CreditAttribution: pwolanin commentedI haven't tried it, but doesn't the Taxonomy Theme module do just what Dires wants?
I'm not sure where this kind of module injects intself into the process, but it might be worth checking that this patch won't have the potential to break all the existing theme-switching modules.
Comment #16
Dublin Drupaller CreditAttribution: Dublin Drupaller commentedDries said:
I don't think the patch will affect your "select template for this node" idea, Dries.
As I understand it, the way the page.tpl.php or page-admin.tpl.php works is that it loads the Node content (your posts) and places it within the overall page. So the "select template for this node" option would still work. The patch just gives users a very simple option of controlling and managing the overall, full page layouts (not node layouts) by uploading a page-variation.tpl.php file.
The patch just simplifies that in the same way you can upload a node-forum.tpl.php file or a node-image.tpl.php file to automatically override the default node.tpl.php for specific node types.
incidentally, there is a new, very useful module called the Content Template Module by jjef that is very useful for specifying a bespoke node template for content types. I imagine it wouldn't be too difficult to tweak that module to offer the available templates as a drop down list when creating a node. So you can select a "photography" template or "general post" template as opposed to it happening automatically.
hope that makes sense. I think it's a good patch, but, I would wait until a phptemplate developer double checks it. For example, to ensure that the correct variables are still available to the page-variation.tpl.php file.
Dub
Comment #17
eaton CreditAttribution: eaton commentedThought that one through. Those modules work by switching which theme is active. This patch allows a specific theme to use different template files to render the page. Any theme-switching module, like Sections or Taxonomy Theme, will take precedence over this patch.
In regards to another question, all the same variables and context information that go to a normal page.tpl.php will carry over to page-variation.tpl.php. I agree that feedback from one of the PHPTemplate devs is critical here.
Comment #18
eaton CreditAttribution: eaton commentedI had a longish conversation with Morbus on #drupal about the potential for more complex alternate theming. After some thought, I whipped out this version of the patch. It moves the filename suggestion into an overridable theme function. By default, it will work just like the previous patch. If someone wants to override it (to use one page template for all /forum urls as well as /node/n urls that point to a 'forum' node-type, for example) they can do that in their theme by overriding phptemplate_page_filename().
Not sure if this approach is better or worse, but it's a possibility.
Anyone out there who feels one way or another? Anyone willing to pop this patch (or the original one) to RTBC?
Comment #19
pwolanin CreditAttribution: pwolanin commentedIn terms of this approach vs. the previous, I'd be curious if there is a way to benchmark the relative performance? I think the most recent patch provides more flexibilty, but that's not worth a noticeable performance hit.
In terms of the last patch, a quick question about the naming of the function
phptemplate_page_filename()
. Would it be better to name ittheme_page_filename()
? I'm only making the suggestion since I usually name my theme functions likephptemplate_search_item()
so that I can easily move/reuse them among phptemplate-based themes.Comment #20
eaton CreditAttribution: eaton commentedAfter some examination, I think that providing an overridable theme function for this purpose is unecessary. The idea is to provide a very simple way for theme designers to include different page templates with a minimum of work. If someone's already into the code enough to override the suggested-page function, it should be easily just to implement some custom code in page.tpl.php to do dynamic switching using the 'old' method.
This updated version of the patch simplifies things again. It also checks drupal_is_front_page() and points to page-front.tpl.php if it's true. That allows easy alternate front-page theming regardless of what node or internal url one has mapped.
Comment #21
DaveNotik CreditAttribution: DaveNotik commentedI wanted to be able to usea different template if it's the front page and the user is not logged in -- i.e. page_default.tpl.php (not logged in) versus page_front.tpl.php (logged in) if I was to avoid my own custom logic altogether (not that I should necessarily be able to, though a dedicated designer would appreciate not having to know the necessary PHP).
With Jeff's latest patch, while I still need logic to check if the user is logged in, I can include that logic in the page-front.tpl.php itself, thus keeping things cleaner.
+1
Comment #22
eaton CreditAttribution: eaton commentedHmmmm. Hearing your use case has caused me to reconsider the elimination of the helper function, Dave. In your case, while it does allow you to isolate things, it means that you'll need to duplicate layout code and HTML. Your default page template will be duplicated in page-front for the 'logged in' case, with a second custom version for the 'logged out' case.
If the support function were present, you could simply override it to check global $user, and suggest page-anonymous.tpl.php. Thoughts?
Comment #23
pwolanin CreditAttribution: pwolanin commentedFrom browsing and reaponding to question in the forums, I thiink many people are overwhelmed trying to get a handle on how to use phptemplate. For this reason, I think using the simple approach is far preferrable. It would be much easier to explain to new users, and would probably capture the functionality needed by 90% or more.
Comment #24
eaton CreditAttribution: eaton commentedIs anyone willing to put this at RTBC? I think I'm satisfied with the state of the patch as it stands, and there's a lot of positive feedback by folks who've actually tested...
Comment #25
eaton CreditAttribution: eaton commentedAnnnnd after a longish discussion with chx about additional ways of handling this issue, I present another possible solution.
This version of the patch walks the menu tree of the current page, looking for override files that correspond to the current args. It will fall back to templates of lesser and lesser specificity until it reaches the ultimate default, page.tpl.php. That allows a site to utilize any of the following:
page.tpl.php
page-front.tpl.php
page-admin.tpl.php
page-admin-settings.tpl.php
page-node-edit.tpl.php
And so on. It also modifies phptemplate's core _default callback to support not just ONE suggested file, but an array of them. Previous phptemplate hacks, which used $variables['template_file'] to override the current template file, will still work... That filename is appended to the list of suggestions and is always checked first.
In addtion, $variables['template_files'] (note the plural) can be populated with a replacement array of suggested files for those who want to completely override the menu-arg based system for greater performance or alternate mappings.
It's somewhat more complicated than previous versions, but offers a tremendous amount of flexibility. Testing would be much appreciated.
Comment #26
eaton CreditAttribution: eaton commentedThis version of the patch changes only one thing: $variables['template_files'], if it's set, will be added to the list of suggestions rather than replacing it. This wouldn't cause any additional performance hit unless a theme added piles of suggestions without providing actual files to match them.
Comment #27
adrian CreditAttribution: adrian commentedI am very much for this.
I was almost on the virge of adding it myself a couple of times.
Comment #28
chx CreditAttribution: chx commentedit's real nice, indeed.a much needed feature elegantly implemented in only so many lines of code.
Comment #29
pwolanin CreditAttribution: pwolanin commentedThis sounds great- sorry I have not had a chance to test it yet.
Two quick questions- I assume this walks the unaliased URL? For example, what happens if I alias my first forum (/forum/1) to /general-discussions?
Second, I don't know when this would be useful, but would this enable a separate page template for each node? I.e. page-node-100.tpl.php for /node/100?
Oh, wait, I see in the patch the code below for constucting the suggestions., so I guess the answer is no.
Comment #30
eaton CreditAttribution: eaton commentedActually, it WOULD make that possible. page-node-1.tpl.php is still one of the suggestions, just not page-node-1-edit.tpl.php.
That allows you to create a custom *display* page per node, but not a custom *edit* page. just as an example. It's not perfect, that system, but it is reasonably speedy and doesn't add a lot of overhead.
Comment #31
eaton CreditAttribution: eaton commentedAlso, yes, it walks the unaliased arguments, like /node and /taxonomy rather than the paths you've assigned to them. It's possible for a theme's template file to override this, of course -- the $variables['template_files'] array can be populated with any list of suggested files if a site needs to use a different set of defaults and fallbacks.
Comment #32
m3avrck CreditAttribution: m3avrck commentedDoesn't this need the base_path() before the path_to_theme() in there for compatability with weird setups?
Otherwise, Eaton, what's your address to mail some beer too??? :-D +1 indeed!
Comment #33
chx CreditAttribution: chx commentedReally? I thought that
!is_numeric
kills that.Comment #34
eaton CreditAttribution: eaton commentedNope. The numeric arg IS inserted as a suggestion, but NOT used for the creation of subsequent suggestions. Thus, you get:
page-node.tpl.php
page-node-1.tpl.php
page-node-edit.tpl.php
but not:
page-node-1-edit.tpl.php
That could lead to funkiness if someone used a path that includes numerous numeric args to generate a page (taxonomy/term/1/4 would become page-taxonomy-4.tpl.php), but those cases are relatively rare and special-handling for them would add a lot of code bloat, as well as a massive increase in the number of suggestions.
One possibility (suggested by chx) would be to use a wildcard when constructing subsequent paths. ie:
page-node.tpl.php
page-node-1.tpl.php
page-node-#-edit.tpl.php
In regards to m3avrck's note about base_path, I'm not sure. i used the same path-construction-code that phptemplate already uses to generate the paths themselves; if this code breaks under funky configurations, I believe the old PHPTemplate code would've, too...
Comment #35
drumm- Update the in-code documentation.
- Don't use += on arrays.
Comment #36
tree CreditAttribution: tree commentedphptemplate.engine_12.patch appears to contain two typos: $suggetions is used in two places instead of $suggestions
Comment #37
eaton CreditAttribution: eaton commentedPHPDoc comments updated to reflect the new use of $suggestions, rather than $file. Typos fixed, as well. Thanks!
Comment #38
Dries CreditAttribution: Dries commentedI'd love to hear Adrian's opinion about this patch.
Comment #39
eaton CreditAttribution: eaton commentedAdrian commented earlier, in #27, though I don't think he's seen the minor tweaks that have taken place since then.
Comment #40
Dries CreditAttribution: Dries commentedWhy do we need the
$extension = '.tpl.php'
? Looks like that can be removed/simplified.Comment #41
eaton CreditAttribution: eaton commentedThat default parameter allows modules and themes that directly utilize _phptemplate_callback() to use their own template file extensions. A module might use .widget.php instead of .tpl.php to theme one of its UI elements, for example, distinguishing it from other normal themed .tpl.php files.
Mind you, I don't think anyone actually USES that now.
Comment #42
jadwigo CreditAttribution: jadwigo commented*.tpl.php is a way to distinguish template files from other php files.. and *.php effectively hides the contents from unsuspecting directory hackers.
the only problem with it at the moment is the double extension in apache file uploads.
on the other hand, *.install, *.inc and *.module are already used, so maybe we can drop the extra extension anyway and don't worry about the false idea of security that should be handled by apache's .htaccess anyway in this case
Comment #43
shoq CreditAttribution: shoq commentedThis entire thread answers a number of questions I had, but I still would like an ID solution for the page such as Nick had suggested above. He wrote:
>
How do I do this? Let's say I want the user's blog edit form to have some custom tweaking only at the CSS level. THe path is
../drupal-4.7.2/?q=node/17/edit
How and where could i transpose this into a parent class that gets inserted into page.tpl.php file like this:
This second class will give me more than enough control over many thinks in CSS, without having to tamper with the current ID logic.
Somewhere, i have to decide the class, which I could decide with eaton's patch (I think). But then I have to assign it to a template variable which page.tpl can use (and I don't know how do make those either).
Guidance appreciated here.
Comment #44
shoq CreditAttribution: shoq commentedOops.. forgot what code works here. THe example above should have been
<td class="main-content MYCLASS_HERE" id="content-<?php print $layout ?>">
Comment #45
eaton CreditAttribution: eaton commentedYou're looking to alter the CSS class of in a single template file depending on the current location. This patch looks for different template files entirely depending on the current location. You're better off putting a GetCurrentPageClass() function in your template.inc that determines the class for the current page, and calling it directly in your page.tpl.inc file.
Comment #46
shoq CreditAttribution: shoq commentedYou lost me. Template.INC? (or php?)
What class am I getting via getcurrentpageClass? There IS no useful class yet.. is there?
What i meant was that I could derive a class name from some aspect of the path with a regex, and then create a page classname for the template.
Comment #47
shoq CreditAttribution: shoq commentedOk.. you meant I could write my own getcurrentpageclass() function, I assume. and yes, I could, but BASED ON WHAT? I need something generated from the URL, I assume like comments/add, node/edit, q=node/add/page, etc.
These woudl become classes like comments-add, and node-edit.
I would contruct this in a hacked phptemplate engine file and then assign the class to a new template variable that could be easily used in all subsequent template.phps
WOuldn't something like this work?
Comment #48
eaton CreditAttribution: eaton commentedIt could, though it's unrelated to this patch.
Comment #49
cklester CreditAttribution: cklester commentedAs a newb, I think this is something Drupal desperately needs. I think I should be able to theme a page without having to touch template.php... this patch makes that possible, right? :)
Now, I'm trying to apply the patch but I'm getting an error...
Please help! :)
Comment #50
eaton CreditAttribution: eaton commentedAre you patching against HEAD or 4.7.x? It may have fallen out of sync with head, I'll see if it needs a reroll.
Comment #51
eaton CreditAttribution: eaton commentedStill seems to apply fine on this end -- it sounds like you're applying the patch to an older version of the engine file.
Comment #52
eaton CreditAttribution: eaton commented*poke poke*
This looks good, and the consensus seems to be positive. Anyone? Anyone?
Comment #53
RobRoy CreditAttribution: RobRoy commented*ouch ouch* — +1 — Looks good to me for what it's worth. Great feature. — Rob
Comment #54
rkerr CreditAttribution: rkerr commentedNice solution for a very often-used/requested feature. Would be great to get this committed.
Comment #55
m3avrck CreditAttribution: m3avrck commentedAs a self-proclaimed "drupal-theming-ninja" I love this patch.
Comment #56
moshe weitzman CreditAttribution: moshe weitzman commentedi played with this. works as advertised. nice patch.
Comment #57
drummIn the theme('page') section,
$suggestions[] = $suggestion;
looks like use of an uninitialized array. Please use$suggestions = array(...)
.Making the patch with
cvs diff -uF^f
will make that sort of thing easier to verify.What is the performance hit for stating one file per path argument?
Comment #58
moshe weitzman CreditAttribution: moshe weitzman commented@drumm - $arr[] = i a perfectly valid way to initialize an array. it seems that many people don't realize that, but the PHPDocs say so: "If $arr doesn't exist yet, it will be created. So this is also an alternative way to specify an array." from http://us3.php.net/manual/en/language.types.array.php. Further, this syntax does not even generate a NOTICE.
I leave this open because of neil's performance question.
Comment #59
eaton CreditAttribution: eaton commentedI tested on a smallish drupal installation with devel.module and page timer installed, and there was no difference in execution times before and after the patch.
If there's anyone who would like to see larger scale stress testing, I can look into setting up something larger, but since the code only executes once per page-load, I'm not sure what difference we'd see. This is the same mechanism that the phptemplate exercises whenever a node is rendered (by looking for "$node->type.tpl.php"). The only real task that has to be performed is parsing arg() and checking the existance of a file -- and only once per page load. Just one or two l() calls have a greater impact on page-load speed.
Comment #60
moshe weitzman CreditAttribution: moshe weitzman commentedi think eaton addressed the performance question, and i addressed the array initialization question. set to RTBC.
Comment #61
drummCommitted to HEAD with some code style cleanup.
I did change the array initialization I mentioned earlier. I think it is slightly more expressive because it doesn't double as appending to an array.
Comment #62
tema CreditAttribution: tema commentedI have "Warning: array_reverse(): The argument should be an array in ...\phptemplate.engine on line 339" (and line 340: foreach())
I've trace it and it IS array when values are "page-something" and "node-something", but it's string when it contains "field-something".
I use HEAD versions of Drupal and CCK.
Thanks!
Comment #63
eaton CreditAttribution: eaton commentedtema, it appears that CCK is one of the only modules around that directly uses that callback -- the appropriate course is to fix CCK to work with HEAD.
Comment #64
tema CreditAttribution: tema commentedThank You, Eaton!
I solved this problem by simple adding '(array)' to variable (Line 339):
So will You please to formulate an issue to CCK project?
Comment #65
(not verified) CreditAttribution: commentedComment #66
smanes CreditAttribution: smanes commentedGreat minds, etc . I stumbled on this same solution working with a hack to Smarty's template engine. In my case, I wanted the ability to restyle the layout based on the URL. I'm using URL rewriting so that my pages look like:
I wanted the "/resume", "/house" and "/dogs" pages to have different banners, colors, even different layouts based on the URL. That is, all '/resume' pages could have one look, all '/dogs' pages could have another layout. In addition, I wanted to be able to fine tune the layout to the second and third levels, if necessary, so that each of my dogs' web sites (yes, my dogs have their own web sites -- so shoot me) could each have slightly different layouts, if necessary.
What's nice about the _smarty_callback function is that you can (as noted above) pass it a "suggested" template resource. If it's not available then the software will default to the standard 'page.tpl'. What a nice safety net. You won't generate a Smarty exception if you forgot to create that "suggested" page template.
I haven't built a patch file yet but this mod is pretty easy to insert into smarty.engine with a non-corrupting text editor. Line 223 of the box stock smarty,engine is:
This is where I added the patch:
If the first element of path is /house then Smarty will try to load the template page page-house.tpl in your theme's root directory. Failing that it will load the default page.tpl as usual.
In addition, the path elements are broken out into an array and stuffed into Smarty's variable namespace as 'path_elements' so you can use it to fine tune the page template using Smarty ops, i,e.
Your Smarty template can use this to, for instance, insert a different photo in the banner.