User:Dantman/Skinning system/Template syntax/xml/html template syntax

See Monobook template for a more final example of the template syntax.

This one might be slightly awkward, though it's most likely a complete pipe dream.

Things to note:

  • Instead of userlangattributes and specialpageattributes lang="mw:user" and lang="mw:special" which get replaced
  • Things like <mw:sitenotice /> to output individual pieces of the skin. (Incorporating the idea of deferring things and making them more expandable as above)
  • <mw:msg key="foo" /> for quick message system outputs
  • The mw:if="foo" attribute is a shorthand for wrapping the whole thing in something like <mw:if key="foo">...</mw:if> and is used to indicate that an element is intended to not be included if that part of the skin is not actually used.
  • mw:loop="key" works on loopable keys, (like the $this->loop above) it's another short code. It's probably equivalent to things like the <mw:footericons>...</mw:footericons> you see, or perhaps <mw:loop key="key">...</mw:loop>.
    • Make note that in this case we have nothing but a <li /> inside to indicate that the loop should simply dump out a <li> with nothing special.
    • Then again there is that <mw:loop for="subtitle"> idea where <mw:this /> (or perhaps <mw:self />) just dumps out pieces when you don't have anything special to do on the contents.
  • In loops things like id="p-*" are context sensitive, they know to expand the * to something relevant to the item in the loop.
  • Likewise class="{generated}...{/generated}" is considered an if block to only output when in the context of a generated sidebar item (a little fancy hooking and keying to play with in the skin system).
  • And similarly {logopath} and {nav.mainpage}. Note that these are context sensitive to their attributes. {logopath} understands it's not only inside of a attribute to escape, but that it's inside of a style="" and inside of a url() and it should escape everything acordingly. In fact url({logopath}) may result in a nice url('...') style output. Likewise {nav.mainpage} understands it's part of a href="" and grabs what would essentially more verbosely be {nav.mainpage.href}.
  • The <script> is a little bit of magic here, usually we would use <script type="<?php $this->text('jsmimetype') ?>">, however since this is a template syntax, and that <script> pattern is common enough, we play up a bit of magic and automatically add the proper type to untyped scripts.
    • The scripts should not be provided using <script>, they should go via the resourceloader. ~ Platonides
  • Also take mw:optional="yes" into account, it's similar to the mw:if and perhaps could be used in many of the mw:if examples, the idea is that if the looped contents have no items, the list itself is omitted instead of output as an empty list. The args to footerlinks may desire a nicer syntax though.
  • Through the example I disagree with xml vs html a bit. html style parsing would be really nice, just some simple no frills parsing, at the very least boolean attributes and attributes without quotes. However xml of course is much easier to get a php library to actually parse.
<div id="globalWrapper">
	<div id="column-content">
		<div id="content" lang="mw:special">
		<a id="top"></a>
		<div id="siteNotice" mw:if="sitenotice"><mw:sitenotice /></div>

		<h1 id="firstHeading" class="firstHeading"><mw:title /></h1>
		<div id="bodyContent">
			<mw:loop for="subtitle">
				<mw:this />
			</mw:loop>
			
			<div class="usermessage" mw:if="newtalk"><mw:newtalk /></div>
			<div id="jump-to-nav" mw:if="showjumplinks"><mw:msg key="jumpto" /> <a href="#column-one"><mw:msg key="jumptonavigation" /></a>, <a href="#searchInput"><mw:msg key="jumptosearch" /></a></div>
			<!-- start content -->
			<mw:bodytext />
			<mw:catlinks />
			<!-- end content -->
			<mw:dataAfterContent />
			<div class="visualClear"></div>
		</div>
	</div>
</div>
<div id="column-one" lang="mw:user">
	<div id="p-cactions" class="portlet">
		<h5><mw:msg key="views" /></h5>
		<div class="pBody">
			<ul mw:loop="content_actions">
				<li />
			</ul>
		</div>
	</div>
	<div class="portlet" id="p-personal">
		<h5><mw:msg key="personaltools" /></h5>
		<div class="pBody">
			<ul lang="mw:user" mw:loop="personal_tools">
				<li />
			</ul>
		</div>
	</div>
	<div class="portlet" id="p-logo">
		<a style="background-image: url({logopath});" href="{nav.mainpage}" mw:tooltip="p-logo" accesskey></a>
	</div>
	<script>if (window.isMSIE55) fixalpha();</script>
	<mw:sidebar>
	<div class="{generated}generated-sidebar{/generated} portlet" id="p-*" mw:tooltip="p-*">
		<h5><mw:header /></h5>
		<div class='pBody'>
			<mw:content />
		</div>
</div><!-- end of the left (by default at least) column -->
<div class="visualClear"></div>
<div id="footer" lang="mw:user">
<mw:footericons icononly>
	<div id="f-*ico" mw:loop>
		<mw:icon />
	</ul>
</mw:footericons>

	<ul id="f-list" mw:optional="yes" mw:loop="footerlinks" mw:looparg="flat">
		<li />
	</ul>
</div>
</div>

I've been experimenting with this and had a mix of success and trouble. I must note that using a html/xml mix that can be parsed into a dom leads to a beautiful situation where your markup is nicer to write (html style booleans and quote-less attributes) and a number of beautiful things become possible. Namely things like lang="mw:user". In the version I have in my experiment lang="mw:*" is very intelligent, it supports mw:user (user lang), mw:content (content lang), mw:page (lang the page uses, this is the same as mw:content in articles, but on special pages in the user's lang it is the same as mw:user), and it handles these very intelligently. Instead of outputting unnecessary lang tags or making guesses that lead to situations where necessary lang tags are omitted, since we have a dom when it goes through the dom, on the few elements in the template with a lang on it it checks to see if the first parent with a lang shares the same lang (making it redundant) and if so strips it out. In other words <div lang="en"><span lang="en">English</span></div> becomes <div lang="en"><span>English</span></div>. I have however had issues doing the parsing the way I want. DOMDocument::loadHTML likes to throw warnings and strip the mw: from tags turning <mw:foo> into <foo>. I've heard of tidy's plugin supporting dom parsing but not everyone has tidy, and not everyone using tidy is using the php plugin (many are potentially using the executable). Additionally tidy never seamed to like <mw:foo> style tags anyways so it would probably screw that up as bad as DOMDocument::loadHTML. html5lib chokes on namespaced tags (well presumably it can handle a few predefined ones like svg). phpQuery and a variety of other libaries fail in various ways at parsing it, or querying it, and some are just wrappers around DOMDocument. The only one I've managed to get working so far is simplehtmldom, which unfortunately after so much of it working, I've found really screws up once you want to insert something before/after a node. It's probably not as efficient as it could be either (speaking just from looking at the api though).

Probably you might want to have a look at Zope Page Templates and its PHP implementation. This is a pretty mature solution and has influenced others a lot.  « Saper // talk »  14:43, 13 March 2011 (UTC)