Template skeleton!

Nao

  • Dadman with a boy
  • Posts: 16,082
Template skeleton!
« on September 6th, 2011, 12:08 PM »
I'd like a few opinions on the sidebar thingy... I'm posting in the public topic willingly, BTW.

Here's a small screenshot of the Warm skin. I've modified it to put the sidebar on the left of the *page*, as opposed to the left of the *content area* (i.e. surrounded by footer, header and nav.)

I just figured that forcing a sidebar onto users wasn't too big a deal (as it helps separate the main content from the asides, really, that's what it's all about), but forcing a sidebar layout onto themers wasn't as cool. It's not too hard to force the sidebar to show on the *right*, but then I thought, what if the themer wants a layout where the sidebar is outside of the header area? I think it's doable but it's hard. And most likely will require creating a new theme just for that aspect. Themes are harder to maintain than skins, so I tried to figure out a solution and it struck me that the layer system is either too simple, or too complicated.

The current layer structure makes the order of calling templates functions as such:

- template_html_above
    - template_body_above
        - template_header
        - template_menu
        - template_linktree
        - template_sidebar_above
            - "sidebar" sub-templates
        - template_sidebar_below
        - template_main_above
            - "top" sub-templates
            - "main" sub-templates (i.e. main content)
        - template_main_below
        - template_footer
    - template_body_below
- template_html_below

In order to have the sidebar shown next to the rest of the page, I'd have to move the sidebar layer out of this loop. Although I'm not too scared at the idea of 'hacking' into the system for the good cause, I'm just wondering if there isn't a smooth way to do it.

Could we consider doing something like a 'skeleton' for the template functions...?
For instance, a $context['skeleton'] array of arrays, that pretty much holds the template structure. We already have similar things with the $context['sub_template'] variable, but it only holds the main content area structure.

I added a skin variable called <sidebar> and a theme variable called $settings['sidebar_position'], they both do the same thing: tell Wedge whether the skin wants the sidebar to be outside the flow, or next to the content. By default it's next to the content. So, right now, in template_body_above(), I just have two occurrences of this:

Code: [Select]
if ($settings['sidebar_position'] === 'left_page')
template_sidebar();

(The other being 'left_content', of course, and positioned in the correct place. And template_sidebar is a function defined in Subs.php that basically calls sidebar_above, the sidebar sub-templates, and sidebar_below.)

Although it works, I can't help feeling this is 'wrong'. Plus, it will force the skin to redefine the 'sidebar' and 'content' blocks, because in the default skin, they're linked together. Not a big deal of course, but what I probably don't like here, is that a themer can't simply switch between two sidebar positions through a single variable being set -- they'll also need to redefine the actual HTML content to match it... Not only that, but I'm not even sure it's not going to break stuff. I have the sidebar + content stuck inside an 'edge' div that serves as display:table and is also used by the onresize sidebar toggler. If I remove it, the toggler will stop working, but the 'edge' div is closed at the end of the 'content' block... And there's no 'block' for the entire page that shows on the right of the sidebar when in left_page mode. Meaning, the 'edge' div never gets closed properly just by redefining the blocks.

I can't really think of a way to get out of this loop of complicated crap. And I definitely don't want to live another week like last week, where a simple remark ("allow embedding in sentences doesn't work") led into a nightmare with a complete overhaul of the auto-embedding code. In the end I'm happy with it (the feature finally works as expected and the actual logic of the embedding process is a bit better), but... I'm just not ready to jump straight back into another nightmare, see.
Posted: September 6th, 2011, 11:15 AM

Update -- I thought it didn't validate, but it does. The magic of nesting sort of fixed itself actually: the edge div closer is still in the same place (after the main content block), but this time it's seen as a closer for the wedge id, not edge. It should be totally broken because the wedge div is not even behaving like a table cell, but... bah. I guess these can be fixed in Warm/index.css anyway.

Still, I don't like hacks... :^^;:

I could actually allow skin.xml files to hold PHP code -- such as a template_body_above_override() function allowing for new sidebar layouts -- but it also mean eval. And, uhh... Let's just say I'd rather do without evals.
Posted: September 6th, 2011, 11:45 AM

Lol... A modified sidebar location will freeze IE9 (100% CPU). But not IE7 or IE8, mind you! :lol:
Nice bug. Sasuga Maikurosofuto da na...

📎 sidebar-left.jpg - 35.73 kB, 640x318, viewed 488 times.

Re: Template skeleton!
« Reply #1, on September 6th, 2011, 03:55 PM »
Okay... The "too long, didn't read" version then:

Is anyone interested in having themes where the sidebar is to the left of the page and the header, rather than to the left of the content (and thus below the header and above the footer)?

If yes, would you be willing to have a 'hackish' (and a bit ugly) way of doing that in the default theme (i.e. within a new skin, which is much easier to maintain in the long run), or would you rather create a new theme for this kind of detail?

Arantor

  • As powerful as possible, as complex as necessary.
  • Posts: 14,278
Re: Template skeleton!
« Reply #2, on September 6th, 2011, 03:57 PM »
Hahahaha, just as I finally get round to posting, you've beaten me to it!


Yeah, eval is an evil beast at the best of times.

I like the notion of having the sidebar outside of the content and encapsulating the overall page in the manner demonstrated.

Hmm. I think I'd prefer it not to be an entirely new theme if possible, such that if it can be done inside a skin, that would seem to me to be preferable, ugliness aside. But I'm no themer, I don't make themes, so my view on the mechanics of it are mostly irrelevant.
Re: Template skeleton!
« Reply #3, on September 6th, 2011, 04:08 PM »
Well, in the case of Wedge, the actual subtemplate list is an array as well, such that multiple sub templates can exist.

There are already pages where this happens; on the board index, for example, each item of the info center is a separate template that's added to the sidebar template list.

I don't recall anywhere else I split the templates up, but that's mostly just down to not having gotten round to it.

In other news, I made use of this multiple list facility in WedgeDesk earlier today to add a new item to the end of the unread/unreadreplies page without having to fake it up as a template layer as SimpleDesk had to, so it can definitely be done.
When we unite against a common enemy that attacks our ethos, it nurtures group solidarity. Trolls are sensational, yes, but we keep everyone honest. | Game Memorial

Nao

  • Dadman with a boy
  • Posts: 16,082
Re: Template skeleton!
« Reply #4, on September 6th, 2011, 05:00 PM »
Changing the layout should only be useful for the sidebar, I suppose. I mean, I don't see any possible reasons for someone wanting to show the footer at the beginning or the header at the right of the page... :P
Although I suppose one could want to make a three-column layout where the header or footer is the third column. That doesn't make much sense to me, but never underestimate the power of craziness, sometimes it leads to innovation... :P

Doing a skeleton really is something I'd like to do at this point. I considered it for the first time a few months ago when I built upon the layer system. I just found it... A bit too complicated for my taste, and at the time I didn't even consider some alternative solutions for the implementation that make sense to me only after working through the mess that is the template layer system. (Although it has to be said that when used correctly, layers do things that a skeleton could hardly do.)

For instance... Imagine a skeleton saying basically, "header, sidebar, content, footer". If I want to add data to the footer through the skeleton, I can't. I'll have to do it above it or below it, considering that 'footer' includes its own wrap code (<div></div>). With a layer system, we "only" need to do something like "header_above header_content header_below sidebar_above sidebar_content sidebar_below content_above content_content content_below footer_above footer_content footer_below". Now I can easily add my code in "footer_content" without fear of breaking it.
Of course, a logical solution is to make way for template hooks in strategic positions like these, so I'm not too worried, and would actually, at this point, be more interesting in getting rid of layers and manually adding hooks in the skeleton, or rather in the related functions (content, sidebar, etc.)

If anything, it's probably easier to understand for anyone having a first look at index.template.php... A function called skeleton(), with some HTML and some {calls} like this, followed with a series of functions sharing the same name, really makes a lot of sense...
Then again, how do we do that without eval()?

We can't do echo '...' because then we'll need to capture the buffer at ob_sessrewrite time and call the functions one at a time there -- and I'm pretty sure it'd break many things because some items expect to be called before ob_sessrewrite is done, eh.
We can do return '...' then, and analyze the code. We echo the thing as it goes, but we stop on each {, extract the following string, make sure it's a valid function, and we call it. Then we discard the following } and we resume the templating process...

I like that idea. What do you think of it?
Plus, it makes it much easier to change the template layout in skins, really. We can very simply put that code into a <skeleton><![CDATA[<html>]]></skeleton> and replace the code we got from skeleton()...

We can do something like this to begin with, and then determine whether we need to keep the template layer code around.
Posted: September 6th, 2011, 04:58 PM

Oh, in other news (© Pete), I renamed template_header() and template_footer() to start_output() and finish_output()... I don't know about you, but I always found these two functions to have VERY misleading names. Especially when you consider that template_footer() never was there to show a footer at all... Just to finish the layers in reverse order. :^^;:

Arantor

  • As powerful as possible, as complex as necessary.
  • Posts: 14,278
Re: Template skeleton!
« Reply #5, on September 6th, 2011, 05:07 PM »
Quote
Then again, how do we do that without eval()?
Variable functions are a truly wonderful thing. :P

Code: [Select]
$skeleton = "header, sidebar, content, footer";

$list = explode(',', $skeleton);
foreach ($list as $item)
{
  $item = trim($item);
  $item();
}

It's faster than eval though naturally not as fast as calling the function directly, but you don't really have a lot of choice at this point, and it would go through the normal template execution flow anyway, meaning that the buffer process is essentially unchanged, since to a degree this is how template layers themselves work.

The one thing that seems critical to me is that the list of items can be manipulated, and ideally more easily than fudging around with a string; that's one thing I like about the current setup is that you can add new items cleanly to an existing content area.
Quote
I renamed template_header() and template_footer() to start_output() and finish_output()
Works for me.

Nao

  • Dadman with a boy
  • Posts: 16,082
Re: Template skeleton!
« Reply #6, on September 6th, 2011, 05:12 PM »
Quote from Arantor on September 6th, 2011, 05:07 PM
Variable functions are a truly wonderful thing. :P
...And, quoting myself:
"extract the following string, make sure it's a valid function, and we call it."
:whistle:
Quote
$skeleton = "header, sidebar, content, footer";
I was thinking about something closer to the templating system like, REAL html with custom tags in it... :^^;:

The only 'trick' is that it couldn't have any PHP in it, really. We're only talking about the main structure, not overriding anything else...
Quote
The one thing that seems critical to me is that the list of items can be manipulated, and ideally more easily than fudging around with a string; that's one thing I like about the current setup is that you can add new items cleanly to an existing content area.
Maybe something like this..

<layer:header>
<menu>
<linktree>
<layer:main>
<layer:sidebar>
<footer>

I don't know... Something that basically tells Wedge whether to start a layer or a sub-template at that point. Or it could simply recognize whether <footer> is stand-alone function, or a layer... Etc.

Arantor

  • As powerful as possible, as complex as necessary.
  • Posts: 14,278
Re: Template skeleton!
« Reply #7, on September 6th, 2011, 05:15 PM »
Quote
"extract the following string, make sure it's a valid function, and we call it.
Yes... but it absolutely does not require eval at all ;)

I thought the whole point was that the skeleton would dictate which parts to call and in what order, nor intermixing the order with the content therein...
Quote
I don't know... Something that basically tells Wedge whether to start a layer or a sub-template at that point. Or it could simply recognize whether <footer> is stand-alone function, or a layer... Etc.
How is that different to now, then? A layer is an array in the template list, a standalone function is a string item.

Nao

  • Dadman with a boy
  • Posts: 16,082
Re: Template skeleton!
« Reply #8, on September 6th, 2011, 05:30 PM »
Quote from Arantor on September 6th, 2011, 05:15 PM
Yes... but it absolutely does not require eval at all ;)
Never said it did :p
Quote
I thought the whole point was that the skeleton would dictate which parts to call and in what order, nor intermixing the order with the content therein...
Yep... But it has to have some content in it, which is where it gets bitchy.

So, as an example, here's the code from the body layer's 'above' section...

Code: [Select]
function template_body_above()
{
global $settings;

if ($settings['sidebar_position'] === 'left_page')
template_sidebar();

echo '
<div id="wedge">', !empty($settings['forum_width']) ? '<div id="wrapper" style="width: ' . $settings['forum_width'] . '">' : '';

// Show the header area.
template_header();

// Show the menu here, according to the menu sub-template.
template_menu();

// Show the navigation tree.
theme_linktree();

// The main content should go here.
echo '

<div id="content"><div class="frame">';

if ($settings['sidebar_position'] === 'left_content')
template_sidebar();
}

Please look at <div id=content>... Where exactly would you put that in a skeleton situation? In what function, exactly? If we can get rid of it, does it mean we're getting rid of the body layer as well? What of the wedge div, do we have to create a new template_* function just for this call...?

Arantor

  • As powerful as possible, as complex as necessary.
  • Posts: 14,278
Re: Template skeleton!
« Reply #9, on September 6th, 2011, 05:38 PM »
Quote
Never said it did
*ahem*
Quote
Then again, how do we do that without eval()?
That's just me being facetious at this point because I don't think there is a good way to do it that doesn't involve either eval, or using fun functions everywhere.

There is where you're getting into semantics. As far as I'm concerned, #content is used to represent the content, and thus it encapsulates all of it. Surely, then, the ideal place is the layer that surrounds the page content, which would be the body layer?

I see what you're getting at, and you're probably right. As I said, a lot of this stuff is mostly hypothetical from my perspective; I don't make themes, I only know how I'd want to interact with things as a modder, and do so in a way that doesn't foul up themes.

Nao

  • Dadman with a boy
  • Posts: 16,082
Re: Template skeleton!
« Reply #10, on September 6th, 2011, 08:09 PM »
Okay... Busy IRL, but the more I think about it, the more it strikes me as relatively simple.

To hell with template layers as such.

A template system should have this kind of structure, which can *easily* be replicated in a pseudo-XML file, and thus overridden through skin.xml:

Code: [Select]
$templates = array(
  'html' => array(
    'body' => array(
      'header' => 'header',
      'menu' => 'menu',
      'nav' => 'linktree',
      'wrapper' => array(
        'sidebar' => array(
          'rss' => 'rss_feed',
        ),
        'top' => array(
          'subtemplate1' => 'subtemplate1',
        ),
        'main' => array(
          'content' => 'subtemplate1',
        ),
      ),
      'footer' => array(
        'sub1' => 'wedge_credits',
        'sub2' => 'my_mod_credits',
      ),
    ),
  ),
);

Or (sidebar in the left side)

Code: [Select]
$templates = array(
  'html' => array(
    'body' => array(
      'wrapper' => array(
        'sidebar' => array(
          'rss_feed',
        ),
        'header',
        'menu',
        'linktree',
        'top' => array(
          'subtemplate1',
        ),
        'main' => array(
          'content',
        ),
        'footer' => array(
          'wedge_credits',
          'my_mod_credits',
        ),
      ),
    ),
  ),
);

And there you go...

The idea, as you can see, is that a layer is just an array. Multiple nested arrays means multiple layers. If we go through the layers recursively, each time we meet an array, we take its key, and then test for "template_KEY_above", if there, we call it. Then we go through the array and call its subtemplates (I added random keys and values, which we use can be discussed, and the other can be used for something else), until we meet an array etc, and then when the loop is over, we just test for "template_KEY_below", and if there, we call it...

You can easily add sub-templates to any of the systems by calling a custom insert function we'll provide -- said function will simply look recursively for the 'layer' we want, and add the stuff there, or it could even add a subtemplate before or after another, etc...
It's a bit harder to remove layers for this but again -- it's just an array, it's easily to manipulate. We can have a function for that, too, we can basically keep the structure inside a layer while removing said layer. We can remove everything and just rebuild a barebones template with hideChrome(), etc...[1]

This way, we can also easily add layers that aren't in the theme. We can also add layers in the theme that have no above and below functions -- we can leave it up to a mod to add them... (Although they could also use the override hook for that. I don't think I implemented it for layers, though...)

Anyway. It's a whole new world to me, suddenly... And quite exciting. I really like the idea of having a basic structure that can be modified by a skin. All extra layers and subtemplates added by mods and such should add their stuff relative to the existing skeleton -- e.g. if they want to add a sidebar subtemplate, they'll just call a function that searches for the 'sidebar' array and then adds their subtemplate entry to it.

What do you think, guys...?
Posted: September 6th, 2011, 08:07 PM

(Edited to differentiate between the two sidebar layouts. The wrapper layer is just that extra div which is made into a table and allows us to have a sidebar that behaves like a table cell i.e. it takes the entire height...)
 1. Note for later: remove the sidebar hack from hideChrome...
Re: Template skeleton!
« Reply #11, on September 6th, 2011, 09:59 PM »
Great. I'll try to implement that tonight or tomorrow. :)

Mod compatibility with what? SMF? Well, it won't be the first rewrite they'll have to go through... ;)

Arantor

  • As powerful as possible, as complex as necessary.
  • Posts: 14,278
Re: Template skeleton!
« Reply #12, on September 6th, 2011, 10:10 PM »
No, the long standing question of themes and mods being compatible.

For the record, file edits are going to be supported and with no less functionality than on SMF (I.e. Install on custom themes, as well as updating custom themes later on if you install mods) but the preferred method will be hooks simply because it's cleaner and more likely to be compatible.

Nao

  • Dadman with a boy
  • Posts: 16,082
Re: Template skeleton!
« Reply #13, on September 7th, 2011, 12:01 AM »
I'd tend to say, 'screw mods if they're not gonna behave'... :P
I'm doing my best to ensure they'll have an easier time, so they'd better not piss me off :niark:

Okay, in template_footer() (now finish_output(), although it may be going away at some point), I have this:

 * - Theme dirs and paths are re-established from the master values (as opposed to being modified through any other page)

It's in your comment, Pete.
So, are you sure this is what the code does? I'd tend to say yes, but... What's the point, really?
I mean, why would a mod modify the theme URL right in the middle of the page? Did SMF implement that after they noticed the variable being abused?

Apart from that... It's a little buggy, but my implementation seems to work fine, at least on regular pages.
SMF added tons of hacks to the layer system. I'm gonna have to trust them and include them in my own, but... meh.

The rest will come tomorrow...

Arantor

  • As powerful as possible, as complex as necessary.
  • Posts: 14,278
Re: Template skeleton!
« Reply #14, on September 7th, 2011, 12:32 AM »
Quote
I mean, why would a mod modify the theme URL right in the middle of the page? Did SMF implement that after they noticed the variable being abused?
Oh... ooh, I looked at this again. The code is a little bit different to how it was when I documented it - it's now much clearer as to what it's doing, as opposed to my creative interpretation back then (a year ago!)

If a theme indicates that the default images should be used, and that the theme is indicating that it uses the default templates, reset the theme dirs to the master default directory. I don't recall a theme ever setting it, however, not even the default theme. I see no reason, really, why that actually needs to remain.

Mind you, there is some creative variable abuse inside SMF, like $scripturl, which can actually be redefined after creation depending on circumstance, so don't take it for granted that it is what you will expect it to be...