From 3c405f5172a6081483c9e5f4264a4d60e60bc8ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Wed, 5 Apr 2017 16:18:53 +0200 Subject: [PATCH] all: Document the Output Formats feature This commit also adds a new command, docshelper, with some utility funcs that adds a JSON datafiles to /docs/data that would be a pain to create and maintain by hand. Fixes #3242 --- commands/gendocshelper.go | 70 +++++ commands/hugo.go | 1 + docs/content/extras/custom-output-types.md | 135 ---------- docs/content/extras/output-formats.md | 152 +++++++++++ docs/content/templates/blocks.md | 48 ++-- docs/data/docs.json | 255 ++++++++++++++++++ .../shortcodes/datatable-vertical.html | 26 ++ docs/layouts/shortcodes/datatable.html | 23 ++ docshelper/docs.go | 32 +++ media/docshelper.go | 17 ++ media/mediaType.go | 14 + output/docshelper.go | 86 ++++++ output/outputFormat.go | 16 +- 13 files changed, 722 insertions(+), 153 deletions(-) create mode 100644 commands/gendocshelper.go delete mode 100644 docs/content/extras/custom-output-types.md create mode 100644 docs/content/extras/output-formats.md create mode 100644 docs/data/docs.json create mode 100644 docs/layouts/shortcodes/datatable-vertical.html create mode 100644 docs/layouts/shortcodes/datatable.html create mode 100644 docshelper/docs.go create mode 100644 media/docshelper.go create mode 100644 output/docshelper.go diff --git a/commands/gendocshelper.go b/commands/gendocshelper.go new file mode 100644 index 000000000..245c5f221 --- /dev/null +++ b/commands/gendocshelper.go @@ -0,0 +1,70 @@ +// Copyright 2017-present The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package commands + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + + "github.com/spf13/cobra" + "github.com/spf13/hugo/docshelper" +) + +type genDocsHelper struct { + target string + cmd *cobra.Command +} + +func createGenDocsHelper() *genDocsHelper { + g := &genDocsHelper{ + cmd: &cobra.Command{ + Use: "docshelper", + Short: "Generate some data files for the Hugo docs.", + Hidden: true, + }, + } + + g.cmd.RunE = func(cmd *cobra.Command, args []string) error { + return g.generate() + } + + g.cmd.PersistentFlags().StringVarP(&g.target, "dir", "", "docs/data", "Data dir") + + return g +} + +func (g *genDocsHelper) generate() error { + fmt.Println("Generate docs data to", g.target) + + targetFile := filepath.Join(g.target, "docs.json") + + f, err := os.Create(targetFile) + if err != nil { + return err + } + defer f.Close() + + enc := json.NewEncoder(f) + enc.SetIndent("", " ") + + if err := enc.Encode(docshelper.DocProviders); err != nil { + return err + } + + fmt.Println("Done!") + return nil + +} diff --git a/commands/hugo.go b/commands/hugo.go index 7a7344c4c..6f56ea3db 100644 --- a/commands/hugo.go +++ b/commands/hugo.go @@ -197,6 +197,7 @@ func AddCommands() { genCmd.AddCommand(genautocompleteCmd) genCmd.AddCommand(gendocCmd) genCmd.AddCommand(genmanCmd) + genCmd.AddCommand(createGenDocsHelper().cmd) } // initHugoBuilderFlags initializes all common flags, typically used by the diff --git a/docs/content/extras/custom-output-types.md b/docs/content/extras/custom-output-types.md deleted file mode 100644 index 35424a78d..000000000 --- a/docs/content/extras/custom-output-types.md +++ /dev/null @@ -1,135 +0,0 @@ ---- -aliases: -- /doc/custom-output/ -date: 2017-03-22T08:20:13+01:00 -draft: true -menu: - main: - parent: extras -title: Custom Output Types -weight: 5 -toc: true ---- - -**NOTE: This isn't the final docs format, but pulled from the "Sketch of Spec For Custom Output Formats" and will be adaptet to a more end user friendly format.** - -## Media Type - -We add a media type (also known as MIME type and content type). This is a two-part identifier for file formats and format contents transmitted on the Internet. - -For Hugo's use cases, we use the top-level type name/subtype name + suffix. An example would be `application/json+json`. - -Users can define their own media types by using them in an `Output Format` definition (see below). - -The full set of media types will be registered in Go's `mime` package, so they will be recognised by Hugo's development server. - -## Output Format - -A `Page` in Hugo can be rendered to multiple representations on the file system: All will get an HTML page and some of them will get an RSS page (home page, sections etc.). - -When we now create a more formal definition for these output representations, the built-ins mentioned above will be the standard set that can be extended. - -So an `OutputFormat`: - -``` -OutputFormat: - Name - MediaType - Path - IsPlainText (default false) - Protocol - - # And then some optional options - NoUglyURLs - URI # Turn index.x into somevalue.x (similar to `RSSUri` in Hugo `0.19`) -``` - -So: - -* `Name`: The key. -* `Path` - defaults to "", which is the root. Multiple outputs to the same suffix must be separated with a path, ie. "amp" for AMP output. -* `IsPlainText`: Whether to parse the templates with `text/template` or `html/template`. -* `Protocol`: I.e. `webcal://` for calendar files. Defaults to the `baseURL` protocol. - -## Standard Output Formats - -So, according to the above, the current Hugo will look like this: - -| Name | MediaType | Path | IsPlainText -| -------------:|-------------| -----|-----| -| HTML | text/html+html | "" | false | -| RSS | application/rss+xml | "" | false | - -## Layouts - -The current situation (slightly simplified): - -| Kind | Layouts -| ----------------:|:-------------| -| home | index.html, _default/list.html | -| section | section/SECTION.html, SECTION/list.html, _default/section.html, _default/list.html | -| taxonomy | taxonomy/SINGULAR.html,_default/taxonomy.html, _default/list.html | -| taxonomyTerm |taxonomy/SINGULAR.terms.html, _default/terms.html| -| page | TYPE/LAYOUT.html, _default/LAYOUT.html, _default/single.html| - -The above is what the Output Format `HTML` must resolve to. - -So, let us make up some other Output Formats and see how that will have to look: - -| Name | MediaType | Path | IsPlainText -| -------------:|-------------| -----|-----| -| JSON | application/json+json | "" | true | -| AMP | text/html+html | amp | false | - -Both of the above can be handled if we add both `Name` and the `Suffix` to the mix. Let us use the home page as an example: - -| Type | Layouts -| -----------:|:-------------| -| JSON | index.json.json, index.json, _default/list.json.json, _default/list.json -| AMP | index.amp.html, index.html, _default/list.amp.html, _default/list.html - -* The above adds the lower-case `Name` as a prefix to the lookup path. -* The above also assumes that it makes sense to edit the templates with the same suffix as the end result (.html, .json etc.). - -TODO: RSS, 404 etc. - -## Examples - -`config.toml`: - -``` -# Add some custom output type definitions: -[[outputFormats]] -name = "Calendar" -mediaType = "text/calendar+ics" -protocol = "webcal://" -isPlainText = true - -[[outputFormats]] -name = "JSON" -mediaType = "application/json" # Will get its file suffix from the sub-type, i.e. "json" -isPlainText = true - -[[outputFormats]] -name = "AMP" -mediaType = "text/html" -path = "amp" - -``` - -Note that Hugo well hard code a predefined list of the most common output types (not sure what that would be, suggestions welcome) with the obvious identifiers and sensible defaults: So whenever you want them, you can just say "json, yaml, amp ..." etc. - -Page frontmatter: - -``` -title = "My Home Page" -outputs = ["html", "rss", "json", "calendar", "amp" ] -``` - -About the `outputs` in the page frontmatter: - -* If none is provided, it defaults to the current behaviour (HTML + RSS (for the list pages)) -* Is some are provided, no defaults will be added. So, if you want the plain HTML representation, you must be explicit. This way you can have the home page as JSON only if you want. -* The names used are case-insensitive and must match either a definition in `config.toml` or the standard set. - -**Note that it should also be possible to set a list of default output formats in `config.toml`, avoiding the need to repeat the `outputs` list in thousands of pages, with a way to restrict each type to a set of pages (using `Kind`, probably).** diff --git a/docs/content/extras/output-formats.md b/docs/content/extras/output-formats.md new file mode 100644 index 000000000..10d6a3bea --- /dev/null +++ b/docs/content/extras/output-formats.md @@ -0,0 +1,152 @@ +--- +aliases: +- /doc/output-formats/ +- /doc/custom-output/ +date: 2017-03-22T08:20:13+01:00 +menu: + main: + parent: extras +title: Output Formats +weight: 5 +toc: true +--- + +Hugo `0.20` introduced the powerful feature **Custom Output Formats**; Hugo isn't just that "static HTML with an added RSS feed" anymore. _Say hello_ to calendars, e-book formats, Google AMP, and JSON search indexes, to name a few. + +This page describes how to properly configure your site with the media types and output formats you need. + +## Media Types + +A [media type](https://en.wikipedia.org/wiki/Media_type) (also known as MIME type and content type) is a two-part identifier for file formats and format contents transmitted on the Internet. + +This is the full set of built-in media types in Hugo: + +{{< datatable "media" "types" "Type" "Suffix" >}} + +**Note:** + +* It is possible to add custom media types or change the defaults (if you, say, want to change the suffix to `asp` for `text/html`). +* The `Suffix` is the value that will be used for URLs and filenames for that media type in Hugo. +* The `Type` is the identifier that must be used when defining new `Output Formats` (see below). +* The full set of media types will be registered in Hugo's built-in development server to make sure they are recognized by the browser. + +To add or modify a media type, define it in a `mediaTypes` section in your site config (either for all sites or for a given language). + +Example in `config.toml`: + +```toml +[mediaTypes] +[mediaTypes."text/enriched"] +suffix = "enr" +[mediaTypes."text/html"] +suffix = "asp" +``` + +The above example adds one new media type, `text/enriched`, and changes the suffix for the built-in `text/html` media type. + +## Output Formats +Given a media type and some additional configuration, you get an `Output Format`. + +This is the full set of built-in output formats in Hugo: + +{{< datatable "output" "formats" "Name" "MediaType" "Path" "BaseName" "Rel" "Protocol" "IsPlainText" "IsHTML" "NoUgly">}} + +**Note:** + +* A page can be output in as many output formats as you want, and you can have an infinite amount of output formats defined, as long as _they resolve to a unique path on the file system_. In the table above, the best example of this is `AMP` vs. `HTML`: We have given `AMP` a value for `Path` so it doesn't overwrite the `HTML` version, i.e. we can now have both `/index.html` and `/amp/index.html`. +* The `MediaType` must match the `Type` of an already defined media type (see above). +* You can define new or redefine built-in output formats (if you, as an example, want to put `AMP` pages in a different path). + +To add or modify a media type, define it in a `outputFormats` section in your site config (either for all sites or for a given language). + +Example in `config.toml`: + +```toml +[outputFormats.MyEnrichedFormat] +mediaType = "text/enriched" +baseName = "myindex" +isPlainText = true +protocol = "bep://" +``` + +The above example is fictional, but if used for the home page on a site with `baseURL` `http://example.org`, it will produce a plain text home page with the URL `bep://example.org/myindex.enr`. + +All the available configuration options for output formats and their default values: + +Field | Description +--- | --- +**Name** | The output format identifier. This is used to define what output format(s) you want for your pages. +**MediaType**|This must match the `Type` of a defined media type. | +**Path** | Sub path to save the output files. +**BaseName** | The base filename for the list filenames (home page etc.). **Default:** _index_. +**Rel** | Can be used to create `rel` values in `link` tags. **Default:** _alternate_. +**Protocol** | Will replace the "http://" or "https://" in your `baseURL` for this output format. +**IsPlainText** | Use Go's plain text templates parser for the templates. **Default:** _false_. +**IsHTML** | Used in situations only relevant for `HTML` type of formats, page aliases being one example.| +**NoUgly** | If `uglyURLs` is enabled globally, this can be used to turn it off for a given output format. **Default:** _false_. + + +## Output Formats for your pages + +A `Page` in Hugo can be rendered to multiple representations on the file system: In its default configuration all will get an `HTML` page and some of them will get an `RSS` page (home page, sections etc.). + +This can be changed by defining an `outputs` list of output formats in either the `Page` front matter or in the site configuration (either for all sites or per language). + +Example from site config in `config.toml`: + +```toml + [outputs] + home = [ "HTML", "AMP", "RSS"] + page = [ "HTML"] + ``` + Note: + + * The output definition is per `Page` `Kind`(`page`, `home`, `section`, `taxonomy`, `taxonomyTerm`). + * The names used must match the `Name` of a defined `Output Format`. + * Any `Kind` without a definition will get `HTML`. + * These can be overriden per `Page` in front matter (see below). + +A `Page` with `YAML` front matter defining some output formats for that `Page`: + +```yaml +--- + date: "2016-03-19" + outputs: + - html + - amp + - json + --- + ``` + Note that the names used for the output formats are case insensitive. + +## Templates for your Output Formats + +Of course, for a new Output Format to render anything useful, we need a template for it. + +**The fundamental thing to understand about this is that we in `Hugo 0.20` now also look at Output Format´s `Name` and MediaType´s `Suffix` when we choose the templates to use to render a given `Page`.** + +And with so many possible variations, this is best explained with some examples: + + +{{< datatable "output" "layouts" "Example" "OutputFormat" "Suffix" "Template Lookup Order" >}} + +**Note:** + +* All of the above examples can use a base template, see [Blocks]({{< relref "templates/blocks.md" >}}). +* All of the above examples can also include partials. + +Hugo will now also detect the media type and output format of partials, if possible, and use that information to decide if the partial should be parsed as a plain text template or not. + +Hugo will look for the name given, so you can name it whatever you want. But if you want it treated as plain text, you should use the file suffix and, if needed, the name of the Output Format (`[partial name].[OutputFormat].[suffix])`. + +The partial below is a plain text template (Outpuf Format is `CSV`, and since this is the only output format with the suffix `csv`, we don't need to include the Output Format's `Name`): + +``` +{{ partial "mytextpartial.csv" . }} +``` + +Also note that plain text partials can currently only be included in plain text templates, and vice versa. See [this issue](https://github.com/spf13/hugo/issues/3273) for some background. + + + + diff --git a/docs/content/templates/blocks.md b/docs/content/templates/blocks.md index 43535c3a7..f80a3d908 100644 --- a/docs/content/templates/blocks.md +++ b/docs/content/templates/blocks.md @@ -9,29 +9,45 @@ title: Block Templates weight: 80 --- -Go 1.6 includes a powerful new keyword, `block`. This construct allows you to define the outer shell of your pages one or more master template(s), filling in or overriding portions as necessary. +The `block` keyword in Go templates allows you to define the outer shell of your pages one or more master template(s), filling in or overriding portions as necessary. ## Base template lookup -This is the order Hugo searches for a base template: -1. /layouts/_current-path_/_template-name_-baseof.html, e.g. list-baseof.html. -2. /layouts/_current-path_/baseof.html -3. /layouts/_default/_template-name_-baseof.html e.g. list-baseof.html. -4. /layouts/_default/baseof.html +In version `0.20` Hugo introduced custom [Output Formats]({{< relref "extras/output-formats.md" >}}), all of which can have their own templates that also can use a base template if needed. + +This introduced two new terms relevant in the lookup of the templates, the media type's `Suffix` and the output format's `Name`. + +Given the above, Hugo tries to use the most specific base tamplate it finds: + +1. /layouts/_current-path_/_template-name_-baseof.[output-format].[suffix], e.g. list-baseof.amp.html. +1. /layouts/_current-path_/_template-name_-baseof.[suffix], e.g. list-baseof.html. +2. /layouts/_current-path_/baseof.[output-format].[suffix], e.g baseof.amp.html +2. /layouts/_current-path_/baseof.[suffix], e.g baseof.html +3. /layouts/_default/_template-name_-baseof.[output-format].[suffix] e.g. list-baseof.amp.html. +3. /layouts/_default/_template-name_-baseof.[suffix], e.g. list-baseof.html. +4. /layouts/_default/baseof.[output-format].[suffix] +4. /layouts/_default/baseof.[suffix] For each of the steps above, it will first look in the project, then, if theme is set, in the theme's layouts folder. Hugo picks the first base template found. -As an example, with a site using the theme `exampletheme`, when rendering the section list for the section `post`. Hugo picks the `section/post.html` as the template and this template has a `define` section that indicates it needs a base template. This is then the lookup order: - -1. `/layouts/section/post-baseof.html` -2. `/themes/exampletheme/layouts/section/post-baseof.html` -3. `/layouts/section/baseof.html` -4. `/themes/exampletheme/layouts/section/baseof.html` -5. `/layouts/_default/post-baseof.html` -6. `/themes/exampletheme/layouts/_default/post-baseof.html` -7. `/layouts/_default/baseof.html` -8. `/themes/exampletheme/layouts/_default/baseof.html` +As an example, with a site using the theme `exampletheme`, when rendering the section list for the section `post` for the output format `Calendar`. Hugo picks the `section/post.calendar.ics` as the template and this template has a `define` section that indicates it needs a base template. This is then the lookup order: +1. `/layouts/section/post-baseof.calendar.ics` +1. `/layouts/section/post-baseof.ics` +2. `/themes/exampletheme/layouts/section/post-baseof.calendar.ics` +2. `/themes/exampletheme/layouts/section/post-baseof.ics` +3. `/layouts/section/baseof.calendar.ics` +3. `/layouts/section/baseof.ics` +4. `/themes/exampletheme/layouts/section/baseof.calendar.ics` +4. `/themes/exampletheme/layouts/section/baseof.ics` +5. `/layouts/_default/post-baseof.calendar.ics` +5. `/layouts/_default/post-baseof.ics` +6. `/themes/exampletheme/layouts/_default/post-baseof.calendar.ics` +6. `/themes/exampletheme/layouts/_default/post-baseof.ics` +7. `/layouts/_default/baseof.calendar.ics` +7. `/layouts/_default/baseof.ics` +8. `/themes/exampletheme/layouts/_default/baseof.calendar.ics` +8. `/themes/exampletheme/layouts/_default/baseof.ics` ## Define the base template diff --git a/docs/data/docs.json b/docs/data/docs.json new file mode 100644 index 000000000..6bcf6ab3d --- /dev/null +++ b/docs/data/docs.json @@ -0,0 +1,255 @@ +{ + "media": { + "types": [ + { + "Type": "application/javascript", + "String": "application/javascript+js", + "MainType": "application", + "SubType": "javascript", + "Suffix": "js" + }, + { + "Type": "application/json", + "String": "application/json+json", + "MainType": "application", + "SubType": "json", + "Suffix": "json" + }, + { + "Type": "application/rss", + "String": "application/rss+xml", + "MainType": "application", + "SubType": "rss", + "Suffix": "xml" + }, + { + "Type": "application/xml", + "String": "application/xml+xml", + "MainType": "application", + "SubType": "xml", + "Suffix": "xml" + }, + { + "Type": "text/calendar", + "String": "text/calendar+ics", + "MainType": "text", + "SubType": "calendar", + "Suffix": "ics" + }, + { + "Type": "text/css", + "String": "text/css+css", + "MainType": "text", + "SubType": "css", + "Suffix": "css" + }, + { + "Type": "text/csv", + "String": "text/csv+csv", + "MainType": "text", + "SubType": "csv", + "Suffix": "csv" + }, + { + "Type": "text/html", + "String": "text/html+html", + "MainType": "text", + "SubType": "html", + "Suffix": "html" + }, + { + "Type": "text/plain", + "String": "text/plain+txt", + "MainType": "text", + "SubType": "plain", + "Suffix": "txt" + } + ] + }, + "output": { + "formats": [ + { + "MediaType": "text/html+html", + "Name": "AMP", + "Path": "amp", + "BaseName": "index", + "Rel": "amphtml", + "Protocol": "", + "IsPlainText": false, + "IsHTML": true, + "NoUgly": false + }, + { + "MediaType": "text/css+css", + "Name": "CSS", + "Path": "", + "BaseName": "styles", + "Rel": "stylesheet", + "Protocol": "", + "IsPlainText": true, + "IsHTML": false, + "NoUgly": false + }, + { + "MediaType": "text/csv+csv", + "Name": "CSV", + "Path": "", + "BaseName": "index", + "Rel": "alternate", + "Protocol": "", + "IsPlainText": true, + "IsHTML": false, + "NoUgly": false + }, + { + "MediaType": "text/calendar+ics", + "Name": "Calendar", + "Path": "", + "BaseName": "index", + "Rel": "alternate", + "Protocol": "webcal://", + "IsPlainText": true, + "IsHTML": false, + "NoUgly": false + }, + { + "MediaType": "text/html+html", + "Name": "HTML", + "Path": "", + "BaseName": "index", + "Rel": "canonical", + "Protocol": "", + "IsPlainText": false, + "IsHTML": true, + "NoUgly": false + }, + { + "MediaType": "application/json+json", + "Name": "JSON", + "Path": "", + "BaseName": "index", + "Rel": "alternate", + "Protocol": "", + "IsPlainText": true, + "IsHTML": false, + "NoUgly": false + }, + { + "MediaType": "application/rss+xml", + "Name": "RSS", + "Path": "", + "BaseName": "index", + "Rel": "alternate", + "Protocol": "", + "IsPlainText": false, + "IsHTML": false, + "NoUgly": true + } + ], + "layouts": [ + { + "Example": "AMP home, with theme \"demoTheme\".", + "OutputFormat": "AMP", + "Suffix": "html", + "Template Lookup Order": [ + "layouts/index.amp.html", + "layouts/index.html", + "layouts/_default/list.amp.html", + "layouts/_default/list.html", + "demoTheme/layouts/index.amp.html", + "demoTheme/layouts/index.html", + "demoTheme/layouts/_default/list.amp.html", + "demoTheme/layouts/_default/list.html" + ] + }, + { + "Example": "JSON home, no theme.", + "OutputFormat": "JSON", + "Suffix": "json", + "Template Lookup Order": [ + "layouts/index.json.json", + "layouts/index.json", + "layouts/_default/list.json.json", + "layouts/_default/list.json" + ] + }, + { + "Example": "CSV regular, \"layout: demolayout\" in front matter.", + "OutputFormat": "CSV", + "Suffix": "csv", + "Template Lookup Order": [ + "layouts/_default/demolayout.csv.csv", + "layouts/_default/demolayout.csv" + ] + }, + { + "Example": "JSON regular, \"type: demotype\" in front matter.", + "OutputFormat": "CSV", + "Suffix": "csv", + "Template Lookup Order": [ + "layouts/demotype/single.csv.csv", + "layouts/demotype/single.csv", + "layouts/_default/single.csv.csv", + "layouts/_default/single.csv" + ] + }, + { + "Example": "HTML regular.", + "OutputFormat": "HTML", + "Suffix": "html", + "Template Lookup Order": [ + "layouts/_default/single.html.html", + "layouts/_default/single.html" + ] + }, + { + "Example": "AMP regular.", + "OutputFormat": "AMP", + "Suffix": "html", + "Template Lookup Order": [ + "layouts/_default/single.amp.html", + "layouts/_default/single.html" + ] + }, + { + "Example": "Calendar blog section.", + "OutputFormat": "Calendar", + "Suffix": "ics", + "Template Lookup Order": [ + "layouts/section/blog.calendar.ics", + "layouts/section/blog.ics", + "layouts/blog/list.calendar.ics", + "layouts/blog/list.ics", + "layouts/_default/section.calendar.ics", + "layouts/_default/section.ics", + "layouts/_default/list.calendar.ics", + "layouts/_default/list.ics" + ] + }, + { + "Example": "Calendar taxonomy list.", + "OutputFormat": "Calendar", + "Suffix": "ics", + "Template Lookup Order": [ + "layouts/taxonomy/tag.calendar.ics", + "layouts/taxonomy/tag.ics", + "layouts/_default/taxonomy.calendar.ics", + "layouts/_default/taxonomy.ics", + "layouts/_default/list.calendar.ics", + "layouts/_default/list.ics" + ] + }, + { + "Example": "Calendar taxonomy term.", + "OutputFormat": "Calendar", + "Suffix": "ics", + "Template Lookup Order": [ + "layouts/taxonomy/tag.terms.calendar.ics", + "layouts/taxonomy/tag.terms.ics", + "layouts/_default/terms.calendar.ics", + "layouts/_default/terms.ics" + ] + } + ] + } +} diff --git a/docs/layouts/shortcodes/datatable-vertical.html b/docs/layouts/shortcodes/datatable-vertical.html new file mode 100644 index 000000000..1d2629eca --- /dev/null +++ b/docs/layouts/shortcodes/datatable-vertical.html @@ -0,0 +1,26 @@ +{{ $package := (index .Params 0) }} +{{ $listname := (index .Params 1) }} +{{ $list := (index (index .Site.Data.docs $package) $listname) }} +{{ $fields := after 2 .Params }} + + {{ range $list }} + {{ range $k, $v := . }} + {{ $.Scratch.Set $k $v }} + {{ end }} + {{ end }} + + {{ range $i, $_ := $fields }} + + {{ $.Scratch.Set "i" $i }} + + {{ $field := (index $fields ($.Scratch.Get "i") ) }} + + {{ range $list }} + + {{ end }} + + + {{ end }} +
{{ $field }} + {{ index . $field }} +
\ No newline at end of file diff --git a/docs/layouts/shortcodes/datatable.html b/docs/layouts/shortcodes/datatable.html new file mode 100644 index 000000000..f40605404 --- /dev/null +++ b/docs/layouts/shortcodes/datatable.html @@ -0,0 +1,23 @@ +{{ $package := (index .Params 0) }} +{{ $listname := (index .Params 1) }} +{{ $list := (index (index .Site.Data.docs $package) $listname) }} +{{ $fields := after 2 .Params }} + + + + {{ range $fields }} + + {{ end }} + + {{ range $list }} + + {{ range $k, $v := . }} + {{ $.Scratch.Set $k $v }} + {{ end }} + {{ range $fields }} + + {{ end }} + + {{ end }} +
{{ . }}
{{ $.Scratch.Get . }}
+ diff --git a/docshelper/docs.go b/docshelper/docs.go new file mode 100644 index 000000000..3de350f61 --- /dev/null +++ b/docshelper/docs.go @@ -0,0 +1,32 @@ +// Copyright 2017-present The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package docshelper provides some helpers for the Hugo documentation, and +// is of limited interest for the general Hugo user. +package docshelper + +import ( + "encoding/json" +) + +var DocProviders = make(map[string]DocProvider) + +func AddDocProvider(name string, provider DocProvider) { + DocProviders[name] = provider +} + +type DocProvider func() map[string]interface{} + +func (d DocProvider) MarshalJSON() ([]byte, error) { + return json.MarshalIndent(d(), "", " ") +} diff --git a/media/docshelper.go b/media/docshelper.go new file mode 100644 index 000000000..9a84d5c7f --- /dev/null +++ b/media/docshelper.go @@ -0,0 +1,17 @@ +package media + +import ( + "github.com/spf13/hugo/docshelper" +) + +// This is is just some helpers used to create some JSON used in the Hugo docs. +func init() { + docsProvider := func() map[string]interface{} { + docs := make(map[string]interface{}) + + docs["types"] = DefaultTypes + return docs + } + + docshelper.AddDocProvider("media", docsProvider) +} diff --git a/media/mediaType.go b/media/mediaType.go index 942e9070e..6b6f90439 100644 --- a/media/mediaType.go +++ b/media/mediaType.go @@ -14,6 +14,7 @@ package media import ( + "encoding/json" "fmt" "sort" "strings" @@ -177,3 +178,16 @@ func DecodeTypes(maps ...map[string]interface{}) (Types, error) { return m, nil } + +func (t Type) MarshalJSON() ([]byte, error) { + type Alias Type + return json.Marshal(&struct { + Type string + String string + Alias + }{ + Type: t.Type(), + String: t.String(), + Alias: (Alias)(t), + }) +} diff --git a/output/docshelper.go b/output/docshelper.go new file mode 100644 index 000000000..e6b0ed28f --- /dev/null +++ b/output/docshelper.go @@ -0,0 +1,86 @@ +package output + +import ( + "strings" + + "fmt" + + "github.com/spf13/hugo/docshelper" +) + +// This is is just some helpers used to create some JSON used in the Hugo docs. +func init() { + docsProvider := func() map[string]interface{} { + docs := make(map[string]interface{}) + + docs["formats"] = DefaultFormats + docs["layouts"] = createLayoutExamples() + return docs + } + + docshelper.AddDocProvider("output", docsProvider) +} + +func createLayoutExamples() interface{} { + + type Example struct { + Example string + OutputFormat string + Suffix string + Layouts []string `json:"Template Lookup Order"` + } + + var ( + basicExamples []Example + demoLayout = "demolayout" + demoType = "demotype" + ) + + for _, example := range []struct { + name string + d LayoutDescriptor + hasTheme bool + layoutOverride string + f Format + }{ + {`AMP home, with theme "demoTheme".`, LayoutDescriptor{Kind: "home"}, true, "", AMPFormat}, + {"JSON home, no theme.", LayoutDescriptor{Kind: "home"}, false, "", JSONFormat}, + {fmt.Sprintf(`CSV regular, "layout: %s" in front matter.`, demoLayout), LayoutDescriptor{Kind: "page", Layout: demoLayout}, false, "", CSVFormat}, + {fmt.Sprintf(`JSON regular, "type: %s" in front matter.`, demoType), LayoutDescriptor{Kind: "page", Type: demoType}, false, "", CSVFormat}, + {"HTML regular.", LayoutDescriptor{Kind: "page"}, false, "", HTMLFormat}, + {"AMP regular.", LayoutDescriptor{Kind: "page"}, false, "", AMPFormat}, + {"Calendar blog section.", LayoutDescriptor{Kind: "section", Section: "blog"}, false, "", CalendarFormat}, + {"Calendar taxonomy list.", LayoutDescriptor{Kind: "taxonomy", Section: "tag"}, false, "", CalendarFormat}, + {"Calendar taxonomy term.", LayoutDescriptor{Kind: "taxonomyTerm", Section: "tag"}, false, "", CalendarFormat}, + } { + + l := NewLayoutHandler(example.hasTheme) + layouts, _ := l.For(example.d, example.layoutOverride, example.f) + + basicExamples = append(basicExamples, Example{ + Example: example.name, + OutputFormat: example.f.Name, + Suffix: example.f.MediaType.Suffix, + Layouts: makeLayoutsPresentable(layouts)}) + } + + return basicExamples + +} + +func makeLayoutsPresentable(l []string) []string { + var filtered []string + for _, ll := range l { + ll = strings.TrimPrefix(ll, "_text/") + if strings.Contains(ll, "theme/") { + ll = strings.Replace(ll, "theme/", "demoTheme/layouts/", -1) + } else { + ll = "layouts/" + ll + } + if !strings.Contains(ll, "indexes") { + filtered = append(filtered, ll) + } + } + + return filtered +} diff --git a/output/outputFormat.go b/output/outputFormat.go index ed3426411..bd0236278 100644 --- a/output/outputFormat.go +++ b/output/outputFormat.go @@ -14,6 +14,7 @@ package output import ( + "encoding/json" "fmt" "sort" "strings" @@ -299,6 +300,17 @@ func decode(mediaTypes media.Types, input, output interface{}) error { return decoder.Decode(input) } -func (t Format) BaseFilename() string { - return t.BaseName + "." + t.MediaType.Suffix +func (f Format) BaseFilename() string { + return f.BaseName + "." + f.MediaType.Suffix +} + +func (f Format) MarshalJSON() ([]byte, error) { + type Alias Format + return json.Marshal(&struct { + MediaType string + Alias + }{ + MediaType: f.MediaType.String(), + Alias: (Alias)(f), + }) }