UPDATE: The add-on is now called SmartyDoc and includes many new features and changes to the API. It is still alpha and undergoing change. Comments are welcome.

I created a new Smarty class extension that some may find useful. It is meant to allow information intended for the document head to be included at any time and from any template regardless of processing order. It also automates things like generating the doctype signature and the basic document structure.

Sorry for the lack of details, but there is more at the wiki including the code:

I did start writing an article for this one but never got around to completing it. I probably will if there is interest

To give an idea of its usage and purpose:

index.php
[php:1:ba9330efeb]$smarty = new SmartyDocInfo();
$smarty->setJavascriptUrl = 'http://localhost/javascript/'; // optional -- there is a full public API as well
$smarty->displayDoc('index.tpl'); // note the custom display method[/php:1:ba9330efeb]
index.tpl

Note that the {doc_info} function and the {doc_raw} block can be called from any template regardless of processing order. The plugins are also registered as non-caching so that the appropriate information is always available in the final document regardless of compilation order or caching.

Last edited by boots on Wed Aug 10, 2005 5:15 am; edited 1 time in total

I was referred to this post from another one of your posts. I haven't tried it yet, but I did want to reply to you to let you know that at least SOMEONE finds this quite interesting.

I'm trying, with some fairly large pet projects, to get my php code to the point where it "knows" as little as possible about the final output.

Additionally, I'm trying to simplify the template writing. Tagging something on at the end that requires some sort of information in the <head> block, for example, would require some gymnastics of sorts before, or some parts of the templates having to "know" about a lot just to handle some rare circumstance.

Something like this might very well be a solution that I could use to better meet both of those goals.

I will TRY to post some more feedback once I take the time to use this. However, it may take a while.

I am just considering adding in some new features and expanding the API a bit to provide some more flexibility and also to allow for the registering of client-side 'modules' (eg: niftyCorners, htmlArea, etc) as I tend to prefer defining locations in my application. Once I decide on something that I think is easy to use I will update the code.

That's pretty darn cool. The only thing I'd add are some additional options, like specifying whether or not the CSS file is <link>'ed or @import'ed. And also things like the id, name and title for the link.

@import is not supported directly, though you can add a {doc_raw} block to do so. I'm considering adding some specific support for css rather than just 'link'. If you look at the code in the class you will see all of the properties supported for each of the tag types. It should be fairly simple to extend, update. At the moment, I don't distinguish XHTML/HTML for these tags, but that should be possible too with some better doctype support.

I've just spent the morning working on some updates which I will release at the latest on monday. These include some of the changes mentioned in my last post, though I haven't completely worked it all out. As part of trying to add-in a way to register client-side modules, I'm wondering how to define things other than paths, such as dependancies and definable automated code injection.

AAH! Monte, you beat me to the punch Anyhow, it was mohrt who gave me the link for this thread. I have wanted something like this for a long time (okay, so a couple years). He summed up some of my comments, but more specifically:

{doc_info}, Importing Stylesheets

* Specify <link> or @import for the stylesheets: That way we can keep our advanced styles from those 4.0 browsers.

* Specify the media type: In the case of the <link> tag, I'd like it to not have a media="" by default. Netscape 4.x (I know, I know. It's old.) will not read LINK style sheets if a media is set. Otherwise, I'd like to be able to pass the media type to the style sheet. OR... Have the media be "screen" by default and passing "none" for the media would cause that attribute to be excluded (so NS4.x browsers will read the style sheet).

In the case of the @import, the default would be screen, and it would be placed as: <style type="text/css" media="foo"> and not @media foo { ... }, seeing that many browsers (read IE-Win) throw fits with this type of media declaration.

* Set the title attribute if the CSS import type is LINK. That's so we can take advantage of ALA's Style Sheet Switcher, or similar style sheet switching method.

* link_type: Use "link" for the <link /> tag or "import" for the @import method. Set to "import" by default.

* media: Set to "screen" automatically. Is excluded if "none" is passed. So media really isn't required as an argument.

* title: Not required and is excluded unless set. Should only be set if the $link_type is "link"

* rel: Not required. Assumes rel is "stylesheet".

* link_id: Excluded if not given a value.

* content_type: Set to "text/css" by default, but allows the user to override this if need be.

* rev: Is excluded by default, but can be set if passed to doc_info.

* target: Is excluded by default, but can be set if passed to doc_info.

The rev and target attributes, along with the rel attribute allow writers to specify links that many assistive devices can take advantage of, like the previous document, next document or the home document: Document structure and metadata, 1.1.5.

I like your ideas and your knowledge of CSS linking appears to exceed mine, so if you have additional insights to share, I'm more than willing to hear them.

As for the linking and the way {doc_info} currently works, there are a few points I'd like to make. You may have missed my last-post where I describe all of the (optional) properties that {doc_info link=...} already supports -- which is basically all the supported attributes for a normal X/HTML <link> element with the caveat that rel= is supplied a default value of "stylesheet". Again, if you look at the $doc_info_types array in the class code you will see that you can alter the defaults as well as provide defaults for other attributes. It should also be noted that if no default is given and if the {doc_info} tag does not supply an attribute, it will not be included in the resulting X/HTML (to avoid things like media=""). So I think that many of the things you suggest are already supported or can be tweaked into the current configuration.

On-the-other-hand, you make some very good cases for providing support for @import and I'm wondering if this should be done via an import=true attribute on the {doc_info link=} or if a new item should be created specifically to deal with stylesheets (instead of overloading link), perhaps {doc_info css='foo.css' import=true}. I've been really thinking about adding support for the latter since it makes more semantic sense to me and also it would allow link= to have more appropriate defaults for links as per your suggestions.

I also have some concern about users allowing to cake in too many details into the the tags which ought rather be automated particularly when it comes to cross-browser support. For example, {doc_info DOCTYPE=XHTML} doesn't make much sense if sending to NN4 while choosing between @import and a hard link seems to be more a matter of browser support rather than design need. It turns out that there is a DOCTYPEHeaderPlugin at the wiki that does some browser sniffing to determine an appropriate DOCTYPE. I would rather that the template (or application) still declared a family/level preference, but that some sort of automated mechanism would then correct for client needs and capabilities. Extending this to linking makes sense to me.

Another minor change is that I presently have {doc_info body="foo()"} to allow users to add onload="" callbacks (you can add as many as you want, they are all strung together in the order they are received). My thought is to rename that {doc_info body_onload=""} or perhaps just {doc_info onload=""}.

{doc_raw} is also getting more features and will now support an optional key= attribute. This allows a given block to be overwritten. There is also a target= that defaults to 'head' but can also accept 'body'. If the latter, the block element data will be inserted just after the <body>. The reason for this is that you may have a common element that many templates want to insert, but really only need one. For example, I presently have a hidden <div> in many of my document templates that is used for overlib. It is not needed for every document, but using old styles of doc creation it wasn't easy to know which documents would need the <div> and moreso, it was nigh-impossible to know which templates wanted to insert the <div> ... which could lead to duplicates. With these changes, {doc_raw target="body" key="foo"}<div id="bar"></div>{/doc_raw} can appear in any number of included templates but the end result will yield only a single <div>.

As a really small detail, does anyone think that SmartyDoc would be a better name for the class itself?
I may take a little more time for the next release, though

I've been working on this class again and have posted a new version at the wiki. It is starting to diverge from the last version and has been renamed to SmartyDoc. This one is PHP5 only and requires Smarty 2.6.10 or better.

I don't have time to do docs at the moment, but hopefully the API is starting to become more evident, particularly since it is starting to include doc comments. This version is still incomplete and things are likely to change again, though I hope that this is starting to resemble something workable.

As for the changes, aside from the API expanding and experiencing some renames, I changed the way that {doc_info link=} works so that it is now more generic. I also added a {doc_info css=} behaviour that makes a first attempt at implementing some of toicontien's suggestions. I also added the ability for {doc_raw} to target either the body or head. More to come.

New in this version is a rudimentary module plugin system. I have hope to provide a more uniform way of having plugins control output. As an inspiration, I wanted to solve the problem posed by Noraz regarding niftyCorners. The way niftyCorners works, you need to be able to target the document head with script code from arbitrary templates. SmartyDocInfo solved that with the {doc_raw} block, but it proved to be rather manual, repetitive and error prone. At that point I wanted to integrate the functionality into a plugin and from there used the SmartyDocInfo API to communicate to the document. This worked to some extent but required a lot of setup work in the plugins.

Instead of further extending the API of the SmartyDoc class (actually called Render_SmartyDoc due to __autoload needs on my side) I created some new classes: an interface, ISmartyDocModule; an abstract class, ASmartyDocModule which implements ISmartyDocModule and provides default null behaviour. As a test case, I built a new custom class, smarty_docmodule_niftyCorners which extends ASmartyDocModule and is in a file named "docmodule.niftyCorners.php" which resides in the plugins directory. There is also a standard function plugin, "function.niftyCorners.php" which also resides there. I could have technically merged the two files into the function.niftyCorners.php file, but want to maintain the separation at this point. At anyrate, the plugin function communicates directly with the module now. The point of doing all this work is that extending Smarty to support new plugin types is difficult in the current design without actually patching Smarty's core code. Under my scheme, the loading of modules is delegated to the standard plugin so while there is more co-ordination and abstraction, we can at least get Smarty's autoload behaviour. The upshot is that all of the actions that are needed (including one-time-only actions) are now handled by the module which is triggered by the plugin....so there is no need for {foo_init} style plugins anymore nor is there a need for the template to explicitly tell {doc_info} about libraries to load -- simply using the {niftyCorners} plugin ensures that everything will happen in the document when it is supposed to.

One thing to note about the plugin modules is that they are given an reference to the instance of the smarty object they are registered to while the smarty object also holds a reference to the module. This way they can communicate freely with each other. Furthermore, instances of the modules held in the smarty object are treated as singletons.

The sample code follows. You'll need to modify it slightly to suit your environment paths and you will need the niftyCorners code from the site linked above to try it out.

As a further demonstration of the SmartyDoc module features, I modified my local {popup} plugin to incorporate this behaviour, thereby eliminating the need for {popup_init}.

{popup} now recognizes two more parameters, 'zindex' and 'id' which default to '1000' and 'overDiv' respectively. There is no way to set the source path at the moment as this is handled in the docmodule -- I'm still figuring out a way to get that configurable.

Anyhow, now divs are automatically created at the body start as needed and the overlib library is loaded only on-demand (ie: {popup} was actually used somewhere in the document) as are all of the associated PHP codes. Lazy loading rocks

Note how this time we target the top of the body content to insert the tags by implementing the rawBodyPre() method rather than the rawHeadPost() method we used for niftyCorners to generate the script tags at the bottom of the document head.

Here's the code. It took under 5 minutes to implement. The only change needed for function.popup.php was the addition of the following two lines at the begining of the function (the extra parameters are handled by the module):

Technically, the rest of the {plugin} code could have been moved to the module's addItem() method as well (as long as references to $smarty were changed to $this->smarty) but I just want to show how straightforward it is to modify an existing plugin so that it can become SmartyDoc aware/capable.

Well... I'm still working on this because I really like the way it is turning out in terms of usability; however, I am at a bit of a cross-road and so I'll try to think outloud a little bit in-case someone has some pertinent suggestions.

I'm considering ways to further modularize the way that the processing occurs so that I can refactor the SmartyDoc API and defer specific behaviours to modules. Most of the document specific features currently in the SmartyDoc class would be replaced with more generic module interaction mechanisms.

In particular, I am presently toying with a modularization of the document creation process where there would be a base document type class which is extended to implement specific doctypes (eg: XHTML_Strict_1.1, etc) These types each define the tags they support and provide methods for tag creation amongst other things. Either in those classes or in separate functional classes, doc info behaviours are defined (with additional attributes than currently implemented). This is sort-of-tricky because I want enough independance so that a docmodule could render into any document type without having to worry about concrete tagging rules or even tag names. Will I need to define a capabilities interface that normalizes doc_info actions and perhaps another that normalizes document type processing and rendering?

An idea is to somehow allow document types to choose to support doc_info properties based on the document type's capabilities and also to allow the document type to render the appropriate tags. This would lead to slightly more brittle doctypes but at least templates could still define relevant but generic doc_info data (really hints at that point) which the document type would then act upon. For example, a 'link' item might be rendered by a XHTML doctype but completely ignored by a mail doctype which could none-the-less retarget a 'title' item to the subject of the mail message. Another important point is that refactoring would allow for a simplification of the SmartyDoc output filter and perhaps improve the event system.

Which leads into to the next thing I am struggling with: should this be further generalized to a generic "block" level? In the scheme I outlined above, there is basically a document layout which is hard-baked into the doctype class. What about arbitrary user-created blocks which could communicate with the template in similar ways and also define their own layouts and events and even plugins? Is there a non-overly complicated way of addressing that? One particular challenge, I think, is finding a convenient way for blocks to contain blocks and when they do, for templates to be able to bubble information up the hierarchy in appropriate ways.

If anyone has ideas, I'm all ears. For those who are following along and testing the various pieces as they are released, thanks! ...but expect more changes

I suspect that it is the newlines after the {doc_info} tags. Smarty doesn't eat whitespace around its tags. You may want to consider using {strip} around them.

I've been working on a new version -- its not really where I'd like it to be yet but I may release it soon anyhow since i've upgraded the plugin modules a little bit and wrote an interesting new plugin that integrates the fabulous scriptaculous library. One thing holding me back is that I have some code to handle formatting and html tag generation in a more uniform manner but it is still very young and I'm not pleased with it.

the SmartyDoc code at the wiki is a little out-of-date compared to my current (private and unreleased) code. I've been hoping to update my various public code projects but I haven't had enough time to get to it.

...

One thing I was pondering is the idea of automatically stitching linked css/js files and {doc_raw} items into single files (one for JS, one for CSS) based on the current compile_id and cache_id -- or perhaps to allow rules determine how/when files are stitched. (The idea there is to improve client performance by limiting the requests made for a given call while still permitting some client caching by not just simply stuffing everything into <script> and <style> elements in the <head>.)

I think that would be great. Also, if so, maybe you could also add XSL stylesheet instruction link(s) and XSLT itself (as how CSS would be shuttled away)...That would be sweet, since XSLT, by affecting formatting, would (if not too large and overwhelming), I think, be a logical option from within the template too.

If so, maybe this could in turn be mixed with an optional parameter to process the XSLT on the server side (for PHP5 users especially who have these functions available to them by default), for the sake of those whose browsers don't perform transformations--whether or not the stylesheet was originally external or in the template. The following at http://www.w3.org/2003/04/xhtml1ToBasic.xsl to change XHTML into XHTML Basic would be a particularly handy one to employ for the sake of readability of a webpage in cell phones.