Gracefully duplicating content across sections

Background

I’m working on documentation for an open-source project that has a hosted/managed counterpart. The two products are mostly identical, but there are important differences between them. To provide a better experience, we’ve decided to create separate docs for both the open source (OSS) and the hosted/managed (cloud) versions:

http://docs…/oss/
http://docs…/cloud/

To avoid having to manage the same content in two doc sets, I came up with the idea to pull the content of the OSS files into corresponding cloud files, and then use shortcodes to show and hide content based on the context.

Approach

I started by creating files in the content/cloud/ directory to match files in the content/oss/ directory. Each of the cloud .md files has frontmatter, but the content of the file is just a shortcode:

{{< duplicate-oss >}}

The {{< duplicate-oss >}} shortcode uses .GetPage to pull in the content of the the corresponding OSS page:

{{ $path := (replaceRE `^cloud\/` "oss/" .Page.File.Path) }}

{{ $page := .Site.GetPage $path }}
{{ $page.Content }}

This works great! It pulls in the content, but…

To show/hide cloud/oss specific content in each context, I have {{< cloud-only >}} and {{< oss-only >}} shortcodes. These attempt to use .Page.RelPermalink to identify their context:

cloud-only.html
{{- if in .Page.RelPermalink "/cloud/" -}}{{ .Inner }}{{- end -}}
oss-only.html
{{- if in .Page.RelPermalink "/oss/" -}}{{ .Inner }}{{- end -}}

This has a few issues:

Issues

  • By using .GetPage, the “imported” content maintains the scope and data of the OSS page. So even when the content is rendered at http://docs..../cloud/, .Page.RelPermlink returns the relative permalink of the OSS page. So my shortcodes for hiding and showing content don’t work because they always inherit the OSS context.
  • I tried simply pulling in the file contents in the duplicate-oss shortcode and rendering them with markdownify, but markdownify doesn’t render shortcodes and we use shortcodes heavily in our docs.
  • I tried using .Page.Scratch in the duplicate-oss shortcode to identify the context, but since the scratch is applied to the .Page, it affects the rendering in both the OSS and cloud contexts.

Questions

  • Is there a way to inject some type of data into the rendering of .Content that could then be used in shortcode logic?
  • Is there a way to temporarily modify the context of a .Page?
  • Is there another approach that I’m just not seeing?
1 Like

Some more details and examples:

Here are what my content files look like:

content/oss/example.md
---
title: This is the OSS example
---

This is the {{< cloud-only>}}Cloud{{< /cloud-only > }}{{< oss-only >}}OSS{{< /oss-only >}} version of this document.
content/cloud/example.md
---
title: This is the Cloud example
---

{{< duplicate-oss >}}

Pages render as:

/oss/example/

This is the OSS example

This is the OSS version of this document.


/cloud/example/

This is the Cloud example

This is the OSS version of this document.


The titles are different because they’re being pulled from the frontmatter of each page, but the content is rendered the same for both because .GetPage inherits the OSS context.

I haven’t read your entire post, but:

  • You can mount the same content (even remotely from GitHub) into different places in /content
  • Shortcodes have access to all metadata, including .Page.Params.
  • You cannot change .Page, but there are other ways to store state (dict, scratch)
  • Also have a look at cascade which, when combined with my first bullet, would allow you to set front matter “per context”

Thanks @bep. I’m not familiar with mounts or cascade, but I’m looking into them.

  • Shortcodes have access to all metadata, including .Page.Params.

I tried this approach as well, but because of how I’m pulling in the content, I still ran into the same issue. Because the {{< cloud-only >}} shortcode is in the .Content that gets pulled in with $page.Content, .Page.Params returns the parameters of the $page which resolves to the OSS version of the page.

  • You cannot change .Page, but there are other ways to store state (dict, scratch)

Is it possible to pass a dict or a scratch to the $page.Content execution? This is where it would need to happen, but when I tried, it didn’t work.

.Content` is rendered once per output format. And again, I have not read your long post in detail, so I don’t understand the detail in what you’re doing.

@bep Mounts are working, but it’s adding another set of problems and I’m wondering if you have any insights into potential solutions. Most center around frontmatter:

  1. Can I prevenet files in mounted directories from getting built/rendered
  2. Does cascade override existing frontmatter? For example, can I bulk change the menu value or a custom taxonomy in all children pages?
  3. I have aliases for a lot of the docs in our doc set (file location changes over time). All the aliases are pointing to the pages generated from the mounts. Is there a way to keep aliases unique to a source file vs a target file?