From c297d7451f6e71c9e790802ce807625a260da443 Mon Sep 17 00:00:00 2001 From: spf13 Date: Fri, 6 Jun 2014 16:15:19 -0400 Subject: [PATCH] Adding 'partial' template function to add theme / local awareness to the partials directory. --- docs/content/templates/content.md | 14 +++++------ docs/content/templates/homepage.md | 8 +++---- docs/content/templates/list.md | 12 +++++----- docs/content/templates/partials.md | 30 ++++++++++++++++++++---- docs/content/templates/terms.md | 18 +++++++-------- docs/content/themes/creation.md | 9 ++++++++ docs/content/themes/customizing.md | 10 +++++--- docs/layouts/_default/single.html | 4 ++-- hugolib/template.go | 37 ++++++++++++++++++++++++++++++ 9 files changed, 106 insertions(+), 36 deletions(-) diff --git a/docs/content/templates/content.md b/docs/content/templates/content.md index 3764103f5..a1a1e98f9 100644 --- a/docs/content/templates/content.md +++ b/docs/content/templates/content.md @@ -65,8 +65,8 @@ same as the other types but the directory must be called "\_default". This content template is used for [spf13.com](http://spf13.com). It makes use of [partial templates](/layout/partials) - {{ template "partials/header.html" . }} - {{ template "partials/subheader.html" . }} + {{ partial "header.html" . }} + {{ partial "subheader.html" . }} {{ $baseurl := .Site.BaseUrl }}
@@ -105,8 +105,8 @@ It makes use of [partial templates](/layout/partials) - {{ template "partials/disqus.html" . }} - {{ template "partials/footer.html" . }} + {{ partial "disqus.html" . }} + {{ partial "footer.html" . }} ## project/single.html @@ -114,8 +114,8 @@ This content template is used for [spf13.com](http://spf13.com). It makes use of [partial templates](/layout/partials) - {{ template "partials/header.html" . }} - {{ template "partials/subheader.html" . }} + {{ partial "header.html" . }} + {{ partial "subheader.html" . }} {{ $baseurl := .Site.BaseUrl }}
@@ -152,7 +152,7 @@ It makes use of [partial templates](/layout/partials) {{ end }} - {{ template "partials/footer.html" }} + {{ partial "footer.html" }} Notice how the project/single.html template uses an additional parameter unique to this template. This doesn't need to be defined ahead of time. If the key is diff --git a/docs/content/templates/homepage.md b/docs/content/templates/homepage.md index 874a9c6b7..40430a7f8 100644 --- a/docs/content/templates/homepage.md +++ b/docs/content/templates/homepage.md @@ -54,18 +54,18 @@ It makes use of [partial templates](/templates/partials) and uses a similar appr - {{ template "partials/meta.html" . }} + {{ partial "meta.html" . }} {{ .Site.Title }} - {{ template "partials/head_includes.html" . }} + {{ partial "head_includes.html" . }} - {{ template "partials/subheader.html" . }} + {{ partial "subheader.html" . }}
@@ -75,4 +75,4 @@ It makes use of [partial templates](/templates/partials) and uses a similar appr
- {{ template "partials/footer.html" }} + {{ partial "footer.html" }} diff --git a/docs/content/templates/list.md b/docs/content/templates/list.md index c86245afe..5fc14a0bb 100644 --- a/docs/content/templates/list.md +++ b/docs/content/templates/list.md @@ -107,8 +107,8 @@ It makes use of [partial templates](/templates/partials). All examples use a [view](/templates/views/) called either "li" or "summary" which this example site defined. - {{ template "partials/header.html" . }} - {{ template "partials/subheader.html" . }} + {{ partial "header.html" . }} + {{ partial "subheader.html" . }}
@@ -121,7 +121,7 @@ defined.
- {{ template "partials/footer.html" }} + {{ partial "footer.html" }} ### Example taxonomy template (tag.html) This content template is used for [spf13.com](http://spf13.com). @@ -129,8 +129,8 @@ It makes use of [partial templates](/templates/partials). All examples use a [view](/templates/views/) called either "li" or "summary" which this example site defined. - {{ template "partials/header.html" . }} - {{ template "partials/subheader.html" . }} + {{ partial "header.html" . }} + {{ partial "subheader.html" . }}
@@ -141,7 +141,7 @@ defined.
- {{ template "partials/footer.html" }} + {{ partial "footer.html" }} ## Ordering Content diff --git a/docs/content/templates/partials.md b/docs/content/templates/partials.md index e4766ffe5..c8acdd0fe 100644 --- a/docs/content/templates/partials.md +++ b/docs/content/templates/partials.md @@ -14,9 +14,14 @@ weight: 80 It's not a requirement to have this, but in practice it's very convenient to split out common template portions into a partial template that can be included anywhere. As you create the rest of your templates -you will include templates from the /layout/partials directory. Hugo -doesn't know anything about partials, it's simply a convention that you -may likely find beneficial. +you will include templates from the /layout/partials directory. + +Partials are especially important for themes as it gives users an opportunity +to overwrite just a small part of your theme, while maintaining future compatibility. + +In fact theme developers may want to include a few partials with empty html +files in the theme just so end users have an easy place to inject their +customized content. I've found it helpful to include a header and footer template in @@ -32,6 +37,21 @@ like good names to use for inclusion in your other templates. By ensuring that we only reference [variables](/layout/variables/) used for both nodes and pages we can use the same partials for both. +## Partial vs Template + +Version v0.12 of Hugo introduced the partial call inside the template system. +This is a change to the way partials were handled previously inside the +template system. This is a change to hthe way partials were handled previously. +Previously Hugo didn’t treat partials specially and you could include a partial +template with the `template` call in the standard template language. + +With the addition of the theme system in v0.11 it became apparent that a theme +& override aware partial was needed. + +When using Hugo v0.12 and above please use the `partial` call (and leave out +the “partial/” path). The old approach will still work, but won’t benefit from +the ability to have users override the partial theme file with local layouts. + ## example header.html This header template is used for [spf13.com](http://spf13.com). @@ -40,14 +60,14 @@ This header template is used for [spf13.com](http://spf13.com). - {{ template "partials/meta.html" . }} + {{ partial "meta.html" . }} {{ .Title }} : spf13.com {{ if .RSSlink }}{{ end }} - {{ template "partials/head_includes.html" . }} + {{ partial "head_includes.html" . }} diff --git a/docs/content/templates/terms.md b/docs/content/templates/terms.md index a927c7189..3c9f5445d 100644 --- a/docs/content/templates/terms.md +++ b/docs/content/templates/terms.md @@ -70,8 +70,8 @@ content tagged with each tag. .Data.Terms is an map of terms => [contents] - {{ template "partials/header.html" . }} - {{ template "partials/subheader.html" . }} + {{ partial "header.html" . }} + {{ partial "subheader.html" . }}
@@ -86,7 +86,7 @@ content tagged with each tag.
- {{ template "partials/footer.html" }} + {{ partial "footer.html" }} ## Ordering @@ -97,8 +97,8 @@ number of content assigned to that key or alphabetically. ## Example indexes.html file (alphabetical) - {{ template "partials/header.html" . }} - {{ template "partials/subheader.html" . }} + {{ partial "header.html" . }} + {{ partial "subheader.html" . }}
@@ -111,12 +111,12 @@ number of content assigned to that key or alphabetically.
- {{ template "partials/footer.html" }} + {{ partial "footer.html" }} ## Example indexes.html file (ordered) - {{ template "partials/header.html" . }} - {{ template "partials/subheader.html" . }} + {{ partial "header.html" . }} + {{ partial "subheader.html" . }}
@@ -130,5 +130,5 @@ number of content assigned to that key or alphabetically.
- {{ template "partials/footer.html" }} + {{ partial "footer.html" }} diff --git a/docs/content/themes/creation.md b/docs/content/themes/creation.md index 515fed987..ada6bc58a 100644 --- a/docs/content/themes/creation.md +++ b/docs/content/themes/creation.md @@ -42,6 +42,15 @@ The default single file layout is located at `layouts/_default/single.html`. The default list file layout is located at `layouts/_default/list.html` +### Partial Templates + +Theme creators should liberally use [partial templates](/templates/partials) +throughout their theme files. Not only is a good DRY practice to include shared +code, but partials are a special template type that enables the themes end user +to be able to overwrite just a small piece of a file or inject code into the +theme from their local /layouts. These partial templates are perfect for easy +injection into the theme with minimal maintenance to ensure future +compatibility. ### Static diff --git a/docs/content/themes/customizing.md b/docs/content/themes/customizing.md index d63f3541b..2e65463f4 100644 --- a/docs/content/themes/customizing.md +++ b/docs/content/themes/customizing.md @@ -12,7 +12,6 @@ weight: 40 Hugo themes permit you to supplement or override any template or file from within your working directory. - ## Replacing Static files If you would like to include a different file than the theme ships @@ -27,12 +26,17 @@ in the same relative path /static/js/jQuery.min.js. Anytime Hugo looks for a matching template it will first check the working directory before looking in the theme directory. If you would like to modify a template simply create that template in your local -layouts directory. In the [template documentation](/templates/overview/) +layouts directory. In the [template documentation](/templates/overview) each different template type explains the rules it uses to determine which template to use. +This is especially helpful when the theme creator used [partial +templates](/templates/partials). These partial templates are perfect for easy +injection into the theme with minimal maintenance to ensure future +compatibility. + **warning.. This only works for templates that Hugo knows about. If the -theme creates partial template files in a creatively named directory +theme imports template files in a creatively named directory Hugo won’t know to look for the local /layouts first** ## Replace an archetype diff --git a/docs/layouts/_default/single.html b/docs/layouts/_default/single.html index deb3d1f25..16f6ffc14 100644 --- a/docs/layouts/_default/single.html +++ b/docs/layouts/_default/single.html @@ -1,3 +1,3 @@ -{{ template "partials/header.html" . }} +{{ partial "header.html" . }} {{ .Content }} -{{ template "partials/footer.html" . }} +{{ partial "footer.html" . }} diff --git a/hugolib/template.go b/hugolib/template.go index 5cba59f31..1bf3fe110 100644 --- a/hugolib/template.go +++ b/hugolib/template.go @@ -1,6 +1,7 @@ package hugolib import ( + "bytes" "errors" "html" "html/template" @@ -14,8 +15,11 @@ import ( "github.com/eknkc/amber" "github.com/spf13/hugo/helpers" + jww "github.com/spf13/jwalterweatherman" ) +var localTemplates *template.Template + func Eq(x, y interface{}) bool { return reflect.DeepEqual(x, y) } @@ -192,6 +196,8 @@ func NewTemplate() Template { errors: make([]*templateErr, 0), } + localTemplates = &templates.Template + funcMap := template.FuncMap{ "urlize": helpers.Urlize, "sanitizeurl": helpers.SanitizeUrl, @@ -215,6 +221,7 @@ func NewTemplate() Template { "lower": func(a string) string { return strings.ToLower(a) }, "upper": func(a string) string { return strings.ToUpper(a) }, "title": func(a string) string { return strings.Title(a) }, + "partial": Partial, } templates.Funcs(funcMap) @@ -223,6 +230,36 @@ func NewTemplate() Template { return templates } +func Partial(name string, context interface{}) template.HTML { + if strings.HasPrefix("partials/", name) { + name = name[8:] + } + return ExecuteTemplateToHTML(context, "partials/"+name, "theme/partials/"+name) +} + +func ExecuteTemplate(context interface{}, layouts ...string) *bytes.Buffer { + buffer := new(bytes.Buffer) + worked := false + for _, layout := range layouts { + if localTemplates.Lookup(layout) != nil { + localTemplates.ExecuteTemplate(buffer, layout, context) + worked = true + break + } + } + if !worked { + jww.ERROR.Println("Unable to render", layouts) + jww.ERROR.Println("Expecting to find a template in either the theme/layouts or /layouts in one of the following relative locations", layouts) + } + + return buffer +} + +func ExecuteTemplateToHTML(context interface{}, layouts ...string) template.HTML { + b := ExecuteTemplate(context, layouts...) + return template.HTML(string(b.Bytes())) +} + func (t *GoHtmlTemplate) LoadEmbedded() { t.EmbedShortcodes() t.EmbedTemplates()