plone / diazo

Diazo applies a static HTML theme to a dynamic website
http://diazo.org
Other
41 stars 26 forks source link

Content to content transform #42

Closed ebrehault closed 9 years ago

ebrehault commented 9 years ago

Principle

This PR adds the following rules: replace-content, before-content, after-content, prepend-content and append-content. Those rules allow to modify the content before applying the theme. A typical example would be:

    <!-- local insert -->
    <prepend-content css:to-content="#header" css:content="h2" />

    <!-- insert from external content to content -->
    <append-content css:to-content="#footer" css:content="h2" href="extra.html"/>

    <!-- the resulting content can be used in regular theme rules -->
    <copy css:theme="nav" css:content="h2" />

Objective

In many cases, our Diazo themes are based on big blocks (like header / content / side columns / footer). If we want to dispatch very granularly the content elements, it implies to create a very detailed theme markup, which is difficult to maintain, and lakes of flexibility (for instance if we want to refactor our grid system). Those rules provide a powerful way to re-organize elements which might be located in a quite deep location in the content document, and keeping a quite simple theme page structure. Moreover, they also allow (using the href attributes) to inject external contents into the current content, which can be handy to aggregate contents without needing specific slots in the theme markup.

Implementation

Regarding the implementation, it does not pollute the existing processing, it just adds a new mode (named content2content) in the resulting stylesheet, this mode is called before the existing content2style mode. It should have no impact on performances for existing Diazo themes using the currently existing rules.

@lrowe, please have a look when you have a moment.

lrowe commented 9 years ago

I'm uncomfortable with adding in more rule types. We already support <replace content="..."> (docs). I think it should be possible to extend this to support <before content="..."> and <after content="..."> too, which would avoid the extra pass through the document.

fulv commented 9 years ago

My 2c: while it's great to have <replace content="...">, in all my projects I spend 90% of my diazo work struggling to get some stupid xsl rules to work inside those <replace...> blocks, because everything else is so easy and effortless. So I would highly welcome some way to make this as easy as the rest of diazo is, whether it's before-content, after-content, or <before content="...">, etc.

djay commented 9 years ago

@fulv I have an idea on how to help with that. I find that most of the time the XSL between a <replace> is a mix of html and <xsl copy> statements. That's not nice for two reasons - you need to know xsl which is a different syntax and it kind of defeats the purpose of diazo if your mockup moves out of the .html and into the .xml. I was thinking of a kind of nested diazo where you can do subreplacements inside a replace statement. And you can pick a different part of the .html mockup to be the sub theme. Would be a lot nicer for repeated elements like lists or portlets I think. I haven't come up with a syntax I really like yet though.

djay commented 9 years ago

if it was a content to content replacement, the kind of syntax I'm thinking of would be like

<replace-content css:to-content-children="#newportlets" content:css=".portlet" subtheme=".portletmockup">
   <replace theme="./myportlettitle/text()" content=".portlet_header"/>
   <replace theme="./myportlebody/text()" content=".portlet_body"/>
</replace>
ebrehault commented 9 years ago

@lrowe, <replace content=".."> only allows to inject static html hard-coded into the rules.xml file, if we want to manipulate the content, the only option at the moment is inline XSLT, which is too complex for regular users. That is basically why I worked on implementin those *-content rules. And manipulating the content implies one extra pass through the doc. I will run a quick benchmark to estimate precisely if it does impact the perf or not and I will let you know.

vangheem commented 9 years ago

I hate to pile on, but I also agree that something like this could be very helpful.

lrowe commented 9 years ago

@ebrehault with before/after content your examples would look something like this:

# <prepend-content css:to-content="#header" css:content="h2" />
<before css:content="#header">
    <xsl:apply-templates css:select="h2" />
 </before>

# <append-content css:to-content="#footer" css:content="h2" href="extra.html"/>
<after css:content="#footer">
    <xsl:apply-templates select="document('extra.html', '/')//h2" mode="raw"/>
</after>

As others have said, that xsl:apply-templates is scary, so perhaps we could allow a spelling like this:

<after css:content="#footer">
    <include css:content="h2" href="extra.html"/>
</after>

That would minimise the number of new rule types.

Performance-wise, a second pass through the content document approximately doubles xsl execution time. Diazo is about twice as fast as XDV because it only makes a single pass (benchmark.)

(Edit: removed wrong mode attribute from first xsl:apply-temlplates.)

ebrehault commented 9 years ago

Good, this spelling is probably simple enough (@djay, don't you think?) , so I will work on this approach (=implementing <before content="...">...</before> and <after content="...">...</after> + the <include> syntax).

Note: regarding the perf, I have run JMeter on a vanilla Plone 5 site with or without my Diazo changes, and surprinsingly, the results are slightly better with my changes (no idea why...).

lrowe commented 9 years ago

@ebrehault I would tackle include separately (separate pull request) from before/after content as they are orthogonal. For include I think you can just translate to the appropriate xsl:apply-templates in normalize-rules.xsl.

For before/after content I think this can be implemented by having before/replace/after content/content-children share an xsl:template which then redispatches to per rule templates. So currently in emit-stylesheet.xsl we compile a replace content rule to an xsl:template such as:

# <replace content="//*[@id='foo']"><p>bar</p></replace>
<xsl:template match="//*[@id='foo']">
    <p>bar</p>
</xsl:template>

That should change to two rules, the first bringing that element into the before/replace/after processing:

<xsl:template match="//*[@id='foo']">
    <xsl:apply-templates select="." mode="before-content" />
    <xsl:apply-templates select="." mode="replace-content" />
    <xsl:apply-templates select="." mode="after-content" />
</xsl:template>

<xsl:template match="//*[@id='foo']" mode="replace-content">
    <p>bar</p>
</xsl:template>

And a default replace-content that redispatches to content-children:

<xsl:template match="*" mode="replace-content">
    <xsl:copy>
        <xsl:apply-templates select="@*" />
        <xsl:apply-templates select="." mode="before-content-children" />
        <xsl:apply-templates select="." mode="replace-content-children" />
        <xsl:apply-templates select="." mode="after-content-children" />
    </xsl:copy>
</xsl:template>

<xsl:template match="*" mode="replace-content-children">
    <xsl:apply-templates select="node()" />
</xsl:template>
jensens commented 9 years ago

overall +1 for the feature (let the details to the diazo experts) - I also ended up with xsl in past where the new rules are a big simplification.

ebrehault commented 9 years ago

Replaced by #43 + #44