In drupal 6 one of the new great features is support for registering a theme implemenation via a module's hook_theme - and being able to specify a path and template file to use instead of a theme function.
This is great for module developers who would like to use clean templates to theme elements or even pages it provides, in a much cleaner way than what was possible in previous versions. The ability to specify a path and even a pattern to be used when determining which template file within that path to use - can make it a lot easier to for an example utilize templates located under a module's directory and also allow other modules to preprocess it's variables.
When a theme implementation utilizing a template is called via theme() - the following code determines which render function and extension to use, to enable support for other theme engines than the default PHPTemplate engine:
// default render function and extension.
$render_function = 'theme_render_template';
$extension = '.tpl.php';
// Run through the theme engine variables, if necessary
global $theme_engine;
if (isset($theme_engine)) {
// If theme or theme engine is implementing this, it may have
// a different extension and a different renderer.
if ($hooks[$hook]['type'] != 'module') {
if (function_exists($theme_engine .'_render_template')) {
$render_function = $theme_engine .'_render_template';
}
$extension_function = $theme_engine .'_extension';
if (function_exists($extension_function)) {
$extension = $extension_function();
}
}
}
However, as you can see above, the appropriate renderer and extension is only fetched if $hooks[$hook]['type'] (which is automatically derived) is not 'module'. In other words; unless the theme implementation belongs to a theme or theme engine, it will use the default $render_function and $extension when attempting to utilize the specified template. This means that implementing a template via a module's hook_theme is only possible with the PHPTemplate engine.
Is there some kind of logical reasoning behind this limitation? Is Drupal not supposed to be theme engine independant?
It seems a bit unnecessary going to so much trouble having Drupal support any theme engine for the site theme, but limit it to only support PHPTemplate for modules.
I can understand a guideline for contributed modules to only use a certain theme engine, but you should also understand that not every production environment out there would like to use PHPTemplate as their theme engine. If an organization have Smarty as their theme standard and that is what their designers and developers are used to - it should not be necessary having to relate to two completely different engines. With this additionally not being outlined/documentated anywhere - it can cause a great deal of frustration and wasted time figuring out why a module's theme implementation is not working.
In case anyone else stumbles upon this problem and are looking for a fix: I managed to find a temporary workaround to this by setting the second parameter to hook_theme() in my module, $type, to be passed by reference when the hook is called from _theme_process_registry() - and changing that $type to 'theme' instead of 'module'. As this is done before the 'type' is set/derived in _theme_process_registry() - this enables theme() to get the appropriate render function / extension - and thus work with other theme engines than PHPTemplate. It is however important to note that this should be done conditionally - i.e. only changing $type for the specific page(s) you want to utilze the template for. Otherwise it can result in a WSOD. I did this by checking the request URI before overriding the type.
Suggested solution:
Imo. Drupal should remain theme engine independant/neutral for both themes and modules - and core should therefore not be locked to PHPTemplate the way it is for modules today. I would therefore suggest either a removal of the 'type' check in theme() allowing it to fetch the appropriate render function and extension regardless of what type the implementation it is - or - allowing developers to specify the engine to use via hook_theme() the same way the template and path can be given. The latter would at least give developers the option to override it without turning to dirty tricks such as the one I described above.
This is my first submitted Drupal issue - so forgive me if I have chosen the wrong category for this. I found bug to be more suitable than any of the other options.
Comments
Comment #1
merlinofchaos commentedDrupal core ships only with PHPTemplate. Therefore, to use theme engines that aren't PHPTemplate in modules, we would have to introduce dependencies upon theme engines for modules. When I designed this piece of code, that seemed like a very very very very very rare occurance.
The fact that it's only just now coming up in a very specialized environment confirms this.
The easy workaround, of course, is that if you need your modules to provide themes implemented in a theme engine, don't implement them in the module and put them in the theme. Your second option is to implement theme functions and use a glue function in your theme function to call to your template. The theme can register these normally, and your glue function can handle the job of making your theme engine available to your module.
In any case, this is absolutely not a bug, this is as designed. At best it is a feature request, because you're asking for a feature that does not exist. Personally, I do not think it should be fixed, because I do not believe that contributed modules should use anything but the core theme engine, I do not believe we want module dependencies on theme engines, and your use case is extremely rare and relatively easily worked around.
I'm not going to 'by design' this yet, but I believe dvessel is likely to concur with me on this one.
Comment #2
sigveio commentedWell... why should I not be able to use a Smarty based template located within my module's directory, without having to put it in my theme directory or use a glue via template.php? A module should be able to theme output via templates, using any theme engine, without having to have bits of code or templates located outside the module. Using the glue method also means that the preprocess functionality will not be available.
I do not disagree that contributed modules should stick to the default engine, that is absolutely fine - but imo. that should be a guideline for contributed modules and not a hardcoded limitation in Drupal. Whether to use a different theme engine should be entirely up to the developer when it comes to modules, just like it is for themes today. If not removing the check completely, how about adding the possibility for a clean override? E.g. allowing developers to provide a 'theme engine' option as well as the current 'template' and 'path' when registering a theme implementation in hook_theme().
I disagree to this being such a specialized case. Quite a few people out there use Smarty as their theme engine, and wanting to theme output via registering a template in hook_theme() - using template files within the module's own directory - is not necessarily that uncommon. Looking at the documentation for hook_theme() - one can also see that one of the default paths Drupal looks for templates implemented this way, is under the module's directory. It is in other words by design that it should be possible to have selfcontained modules including the templates it uses - right? Why should this feature not be available to those who do not use PHPTemplate?
Comment #3
dvessel commentedI initially thought this would be a nice feature but I do agree with Earl. For projects where you have control from the module down to the theme, Merlinofchaos has a reasonable approach. To open this up and allow the dependency on the two layers could break sites very easily. Possibly if we had a dependency check from module to themes like we do for modules to other modules it would help but I'm not sure it's worth it.
Comment #4
dvessel commentedThe only issue I have is that disabling the module could break the site. Packing the engine with the module isn't too appealing to me but it might be more due to how things normally work and what I'm used to.
But what happens if the theme wants to override the template from your engine packed with the module? If there was a dependency check, I might think differently.
Comment #5
sigveio commentedSorry, I didnt mean to change it back to a bug report - it was set as default in the issue settings for some reason.
I am unsure whether to let it change status back to active when I post - so I will leave that at default for now. If you want me to leave status as "by design" - let me know please.
I am not suggesting that a module should be packed with the engine - as in having the entire Smarty engine within the module's directory. I am merely saying that a site that already have the Smarty engine installed under themes/engines, should be able to deploy a module that uses templates to theme it's output and at the same time letting that module be selfcontained - in the sense that it does not depend on having template files or code outside the module's own directory. Having to put the module's templates in a specific theme's directory, or glue it together via template.php is not a good way of doing it. That means that a module can not be easily and cleanly deployed by adding it to the modules directory and enabeling it, and it also means that one would have to put the templates or template.php function in every installed theme if the site utilize more than one theme / allows users to switch between them.
As for overriding the templates... The theme preprocessors are ran last, afaik. Letting the module provide one or more templates on it's own / independantly / from it's own directory - means that it can provide a default set in a clean way, while each theme can still override it by simply using the preprocess functionality to change the template suggestions if necessary. Right?
Comment #6
sigveio commentedJust to add a note: When saying 'Smarty' that is just an example - whether people prefer PHPTemplate or Smarty themsevles is not the issue at hand here. This could be about any other theme engine for that matter. I know a lot of you prefer PHPTemplate, but it is not necessarily the holy grail for everyone and not to mention for all future. Drupal developers should be free to choose the best tool for the job, also when it comes to selecting theme engine for a module's output, without having to use the mentioned dodgy and inconvenient methods. I think I mention a few valid points in my previous reply regarding the downside of these.
The problem with contributed modules using different engines is understandable, but that could be solved by either having a guideline/best practice for those (and for an example refuse commit to SVN with template files other than .tpl.php) - or alternatively a dependency check as you have mentioned already.
I found a dirty workaround to the problem that allowed me to keep the same theme functionality I would get with PHPTemplate, by overriding the theme implementation type as mentioned in my initial post - but this is obviously far from an ideal solution. The reason I am taking this issue here is because I firmly believe removing the current limitation in the theme system could help make Drupal an even more flexible and powerful solution for everyone - and that developers choosing a different theme engine should not be forced to use these kind of ugly workarounds to get the job done properly.
Lastly I'd just like to point out that I very much appreciate the effort you guys do for this great community, and I sincerely hope you will give this issue some serious thought. Thank you for your time. :)
Comment #7
dvessel commentedI have been thinking about this but I just can't see it working without complications.
But the template will still depend on the add-on engine whether it's inside the theme or in the module. How could that be separated? All I can imagine is confusion if there are no safeguards for checking on dependencies and noting that for the site operator. I'll mention it again that I think it would be nice to have this ability but it has to be done cleaning from the site operators perspective too.
Comment #8
sigveio commentedYes, the default template provided by the module would depend on the theme engine it was designed for. For contributed modules I imagine the easiest solution, apart from having PHPTemplate as the recommended standard, would be to have a dependency check just like we have with module to module dependencies today. E.g. being able to specify 'theme engine' in the module's .info file and refuse installation of the module unless that theme engine is present.
If a given theme engine is not found, Drupal could also fall back to attempt using the default engine and if no matching template is found for that either - it could be handled gracefully by displaying a 'page not found', 'template not found' or similar along with a more informative log entry. There are plenty of other modules having dependencies that can break the module or prevent it from working properly if missing. Take most of the modules implementing 3rd party WYSIWYG editors for an example. If a module requires a different theme engine than what the administrator has installed, it is not much more difficult to download and install the theme engine than it is to download for an example the MCE editor and drop that in place.
In order to allow themes to override the module's templates independantly from the theme engine used by the module, it would also be necessary to move the render function and extension check to run after the preprocessors are ran - which would be sensible either way if you ask me. For theme implementations specifying a template, one could introduce an automatically derived value for 'theme engine' in the theme registry and allow the preprocessors to alter this before Drupal determines which function and extension to use when rendering the given template.
Comment #9
dvessel commentedhoesi, that would then assume that themes would depend on multiple theme engines (something we currently do not support). There was an issue for that but it's getting more and more complicated and the potential overhead on that alone could be troublesome. There would be a lot to iron out.
Comment #10
sigveio commentedEh? What would?
I think you are missunderstanding me in that case, because that is not what I am saying and I can't see why that would be necessary. :/
- Dependency check from modules to template engines - meaning a module cannot be installed if the required engine is missing.
- If a module depending on a 3rd party engine is installed, and that theme engine is removed by an ignorant admin - Drupal could still handle that gracefully instead of breaking completely.
- Alternatively, if necessary, one could also have an interface for enabling/disabling theme engines just like modules, and thus have a two way dependency check
As a worst case scenario: Let's say I was to download a module requiring the smarty engine, then install smarty and enable the module having the smarty dependancy. If the module has registered a theme implementation with a template and set the 'theme engine' to 'smarty' - and I then went ahead and deleted the smarty engine folder from my install - not knowing what I am doing - this could still be handled gracefully if the system is designed properly. If drupal does not find the specified theme engine, it could do a number of things:
- Attempt to look for a .php.tpl instead (if you would like a possible fallback), if that fails;
- Display a simple error message, page not found, template not found - or some other form of visual feedback to the user (also preventing WSOD)
- Log a more sensible message for the administrator in the site log
- Set an error for the site 'status' page so that they are more likely to see it
Modules would typically only supply theme implementations for the module's own page callbacks or other content such as blocks - so if for some reason the module were unable to render a template and return output, it would not have to break the site.
Comment #11
dvessel commentedThat assumes right there that it would require multiple engines. The extension within foo.tpl.php makes it a phptemplate template.
At any rate, this would never happen in 6. And I'm doubtful for 7 too. If we could possible rethink how theme engines work something like this could be accomplished. Maybe removing the requirements of explicitly setting a theme engine for the theme and detecting it automatically based on the extension itself and allowing multiple engines that way by allowing specific engines for each template. I don't see it being used much as Earl mentions but who knows.
And feel free to change the status back to active if you'll be coding this.
Comment #12
sigveio commentedI was merely playing with the thought that one could have a Drupal attempt to fall back on PHPTemplate in case someone were to for an example specify the wrong theme engine for a given template - however it would be more sensible to leave this out and have drupal fail (gracefully) instead. We wouldn't want to leave that kind of mistakes undetected. Either way, that part is not what should be focused on here. Forget about it please.
In order to achieve a working theme independancy for modules in Drupal, the following would be necessary from what I can see:
- Having dependency check from module to theme engine (and vise versa if needed).
- Letting modules specify 'theme engine' when registering a template based theme implementation in hook_theme().
- If the module does not register a 'theme engine' along with the 'template' property, the default (PHPTemplate) is assumed.
- The theme system's check for render function and extension should be moved to run after all the preprocessors, in order to;
- Let the preprocessors override the 'theme engine' along with 'template' and so on - allowing a complete override of template and engine to use for a given theme implementation.
- Use the 'theme engine' value to determine which render function and template extension to use (much like the current check, only based on 'theme engine' from the registry instead of the global)
Now a module can have a dependancy on a certain theme engine for it's template based theme implementations, while a theme using a different engine (for an example the default PHPTemplate) can still override this and theme the module's output using a different template and even a different theme engine.
It is most likely a lot more efficient if someone with more experience and a deeper knowledge of the Drupal theme system, such as yourself, does this. I would however be happy to help if this is needed.
I would like to have a general agreement on this before taking it a step further though, in order to not waste precious time on coding something that will be insantly rejected because someone does not see the need for this flexibility. :)
Comment #13
dvessel commentedWell, this is not my itch to scratch. Don't count on me to implement this. I would change my mind if I saw more running into this issue to allow a better theming environment. Like I said, it's an interesting idea but that's all it is to me at this point. If someone did this in a clean way, I'd +1 it. I'm sorry, but that's all I have to say on this.
Comment #14
sigveio commentedFair enough dvessel, thank you very much for taking the time to discuss this with me anyway though - it's appreciated!
I'm in the middle of upgrading a large site from Drupal 4.7.x to 6.x - which will keep me fairly busy till at least mid December I reckon. A lot of heavy custom modules to upgrade and improve for D6.
What is the rough timeframe for contributions to core before it is freezed for the next release?
Comment #15
effulgentsia commentedDefinitely out of scope for D7. Seems like a reasonable thing to have on the table for D8, especially since we already have better dependency management in D7, and can make it better in D8 (i.e., allowing a module to specify that it has a dependency on a particular theme engine).
Comment #16
sigveio commentedI agree, thanks for the update. :)
Comment #17
sigveio commented*shameless bump*
Getting Drupal 8.x fully theme engine independent would be a nice addition. I would contribute myself, but currently I'm unfortunately not in a position where I have the time at my disposal. If someone else would be willing to have a go at it, that would be greatly appreciated. Thanks. <3
Comment #18
marcingy commentedDuplicate of #1537050: [meta] Should we keep / improve multiple theme engine functionality? which has much more detailed discussions on this matter.