1
0
mirror of synced 2025-12-19 18:10:59 -05:00

Merge branch 'main' into toggle-images

This commit is contained in:
Sarah Schneider
2021-04-23 13:59:35 -04:00
committed by GitHub
25 changed files with 583 additions and 30 deletions

View File

@@ -1,4 +1,5 @@
{ {
"FEATURE_TEST_TRUE": true, "FEATURE_TEST_TRUE": true,
"FEATURE_TEST_FALSE": false "FEATURE_TEST_FALSE": false,
"FEATURE_NEW_SITETREE": false
} }

View File

@@ -68,6 +68,14 @@
{% include featured-links %} {% include featured-links %}
{% endif %} {% endif %}
{{ renderedPage }} {{ renderedPage }}
{% if tocItems and tocItems.length %}
{% if page.documentType == "category" or page.relativePath == "github/index.md" %}
{% include generic-toc-list %}
{% else %}
{% include generic-toc-items %}
{% endif %}
{% endif %}
</div> </div>
</div> </div>

View File

@@ -1,3 +1,16 @@
{% if FEATURE_NEW_SITETREE %}
<nav class="breadcrumbs f5" aria-label="Breadcrumb">
{% for breadcrumb in breadcrumbs %}
{% if breadcrumb.href == '' %}
<span title="{{ breadcrumb.documentType }}: {{ breadcrumb.title }}">{{ breadcrumb.title }}</span>
{% else %}
<a title="{{ breadcrumb.documentType }}: {{ breadcrumb.title }}" href="{{ breadcrumb.href }}" class="d-inline-block {% if breadcrumb.href == currentPath %}text-gray-light{% endif %}">
{{ breadcrumb.title }}
</a>
{% endif %}
{% endfor %}
</nav>
{% else %}
<nav class="breadcrumbs f5" aria-label="Breadcrumb"> <nav class="breadcrumbs f5" aria-label="Breadcrumb">
{% for breadcrumb in breadcrumbs %} {% for breadcrumb in breadcrumbs %}
{% if breadcrumb[1].href == '' %} {% if breadcrumb[1].href == '' %}
@@ -9,3 +22,4 @@
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</nav> </nav>
{% endif %}

View File

@@ -0,0 +1,34 @@
{% for categoryPage in currentProductTree.childPages %}
{% if categoryPage.href == currentPath %}{% assign currentCategory = categoryPage %}{% endif %}
{% endfor %}
{% if currentCategory.page.shortTitle and currentCategory.page.shortTitle != '' %}{% assign currentCategoryTitle = currentCategory.page.shortTitle %}{% else %}{% assign currentCategoryTitle = currentCategory.page.title %}{% endif %}
{% assign maxArticles = 10 %}
<div class="py-6 all-articles-list">
<h2 class="font-mktg mb-4">{{ currentCategoryTitle }} docs</h2>
<div class="d-flex gutter flex-wrap">
{% for childPage in currentCategory.childPages %}
{% unless childPage.page.hidden %}
<div class="col-12 col-lg-4 mb-6 height-full">
<h4 class="mb-3"><a href="{{ childPage.href }}">{{ childPage.page.title }}</a></h4>
<ul class="list-style-none">
{% for grandchildPage in childPage.childPages %}
<li class="mb-3 {% if forloop.index > maxArticles %}d-none{% endif %}">
<a href="{{ grandchildPage.href }}">
{{ grandchildPage.page.title }}
</a>
</li>
{% endfor %}
{% assign numArticles = childPage.childPages | obj_size %}
{% if numArticles > maxArticles %}
<button class="js-all-articles-show-more btn-link Link--secondary">Show {{ numArticles | minus: maxArticles }} more {% octicon "chevron-up" class="v-align-text-bottom" %}</button>
{% endif %}
</ul>
</div>
{% endunless %}
{% endfor %}
</div>
</div>

View File

@@ -0,0 +1,12 @@
{% if tocItems %}
{% for tocItem in tocItems %}
{% assign title = tocItem.title %}
{% assign fullPath = tocItem.fullPath %}
{% assign intro = tocItem.intro %}
{% include liquid-tags/link-with-intro %}
{% endfor %}
{% endif %}

View File

@@ -0,0 +1,24 @@
{% if tocItems %}
<ul>
{% for tocItem in tocItems %}
{% assign title = tocItem.title %}
{% assign fullPath = tocItem.fullPath %}
{% assign intro = tocItem.intro %}
<li>{% include liquid-tags/link %}
{% if tocItem.childTocItems %}
{% unless page.relativePath == "github/index.md" %}
<ul>
{% for childItem in tocItem.childTocItems %}
{% assign title = childItem.title %}
{% assign fullPath = childItem.fullPath %}
<li>{% include liquid-tags/link %}</li>
{% endfor %}
</ul>
{% endunless %}
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}

View File

@@ -0,0 +1,46 @@
{% assign maxArticles = 10 %}
{% if currentProductTree.page.shortTitle and currentProductTree.page.shortTitle != '' %}{% assign productTitle = currentProductTree.page.shortTitle %}{% else %}{% assign productTitle = currentProductTree.page.title %}{% endif %}
<div class="py-6 all-articles-list">
<h2 class="font-mktg mb-4">All {{ productTitle }} docs</h2>
<div class="d-flex gutter flex-wrap">
{% for childPage in currentProductTree.childPages %}
{% if childPage.page.documentType == "article" %}{% assign standaloneCategory = true %}{% else %}{% assign standaloneCategory = false %}{% endif %}
{% unless standaloneCategory %}
<div class="col-12 col-lg-4 mb-6 height-full">
<h4 class="mb-3"><a href="{{ childPage.href }}">{{ childPage.page.title }}</a></h4>
{% if childPage.childPages and childPage.childPages[0].page.documentType == "mapTopic" %}
<ul class="list-style-none">
{% for grandchildPage in childPage.childPages %}
{% unless grandchildPage.page.hidden %}
{% assign numArticles = childPage.childPages | obj_size %}
<li class="mb-3 {% if forloop.index > maxArticles %}d-none{% endif %}">
<a href="{{ grandchildPage.href }}">
{{ grandchildPage.page.title }}
</a>
</li>
{% if numArticles > maxArticles %}
<button class="js-all-articles-show-more btn-link Link--secondary">Show {{ numArticles | minus: maxArticles }} more {% octicon "chevron-up" class="v-align-text-bottom" %}</button>
{% endif %}
{% endunless %}
{% endfor %}
</ul>
{% else %}
<ul class="list-style-none">
{% assign numArticles = childPage.childPages | obj_size %}
{% for grandchildPage in childPage.childPages %}
<li class="mb-3 {% if forloop.index > maxArticles %}d-none{% endif %}"><a href="{{ grandchildPage.href }}">{{ grandchildPage.page.title }}</a></li>
{% endfor %}
</ul>
{% if numArticles > maxArticles %}
<button class="js-all-articles-show-more btn-link Link--secondary">Show {{ numArticles | minus: maxArticles }} more {% octicon "chevron-up" class="v-align-text-bottom" %}</button>
{% endif %}
{% endif %}
</div>
{% endunless %}
{% endfor %}
</div>
</div>

View File

@@ -0,0 +1,74 @@
<!--
Styling note:
Categories, Maptopics, and Articles list items get a class of `active` when they correspond to content
hierarchy of the current page. If an item's URL is also the same as the current URL, the item
also gets an `is-current-page` class.
-->
{% include all-products-link %}
{% unless currentProductTree.page.hidden %}
{% if currentProductTree.renderedShortTitle %}{% assign productTitle = currentProductTree.renderedShortTitle %}{% else %}{% assign productTitle = currentProductTree.renderedFullTitle %}{% endif %}
<li title="" class="sidebar-product mb-2">
<a href="{{currentProductTree.href}}" class="pl-4 pr-5 pb-1 f4">{{ productTitle }}</a>
</li>
<li>
<ul class="sidebar-categories list-style-none">
{% for childPage in currentProductTree.childPages %}
{% if childPage.page.documentType == "article" %}{% assign standaloneCategory = true %}{% else %}{% assign standaloneCategory = false %}{% endif %}
<li class="sidebar-category py-1 {% if currentPath contains childPage.href %}active {% if currentPath == childPage.href %}is-current-page {% endif %}{% endif %}{% if standaloneCategory %}standalone-category{% endif %}">
{% if childPage.renderedShortTitle %}{% assign childTitle = childPage.renderedShortTitle %}{% else %}{% assign childTitle = childPage.renderedFullTitle %}{% endif %}
{% if standaloneCategory %}
<a href="{{childPage.href}}" class="pl-4 pr-2 py-2 f6 text-uppercase d-block flex-auto mr-3">{{ childTitle }}</a>
{% else %}
<details class="dropdown-withArrow details details-reset" {% if currentPath contains childPage.href or forloop.index < 4 %}open{% endif %}>
<summary>
<div class="d-flex flex-justify-between">
<a href="{{childPage.href}}" class="pl-4 pr-2 py-2 f6 text-uppercase d-block flex-auto mr-3">{{ childTitle }}</a>
{% if forloop.index < 4 %}
<svg xmlns="http://www.w3.org/2000/svg" class="octicon flex-shrink-0 arrow mr-3" style="margin-top:7px" viewBox="0 0 16 16" width="16" height="16"> <path fill-rule="evenodd" clip-rule="evenodd" d="M12.7803 6.21967C13.0732 6.51256 13.0732 6.98744 12.7803 7.28033L8.53033 11.5303C8.23744 11.8232 7.76256 11.8232 7.46967 11.5303L3.21967 7.28033C2.92678 6.98744 2.92678 6.51256 3.21967 6.21967C3.51256 5.92678 3.98744 5.92678 4.28033 6.21967L8 9.93934L11.7197 6.21967C12.0126 5.92678 12.4874 5.92678 12.7803 6.21967Z"></path></svg>
{% endif %}
</div>
</summary>
{% endif %}
<!-- some categories have maptopics with child articles -->
{% if currentPath contains childPage.href or forloop.index < 4 %}
{% if childPage.childPages[0].page.documentType == "mapTopic" %}
<ul class="sidebar-topics list-style-none position-relative">
{% for grandchildPage in childPage.childPages %}
{% if grandchildPage.renderedShortTitle %}{% assign grandchildTitle = grandchildPage.renderedShortTitle %}{% else %}{% assign grandchildTitle = grandchildPage.renderedFullTitle %}{% endif %}
<li class="sidebar-maptopic {% if currentPath contains grandchildPage.href %}active {% if currentPath == grandchildPage.href %}is-current-page{% endif %}{% endif %}">
<a href="{{grandchildPage.href}}" class="pl-4 pr-5 py-2">{{ grandchildTitle }} </a>
<ul class="sidebar-articles my-2">
{% for greatgrandchildPage in grandchildPage.childPages %}
{% if greatgrandchildPage.renderedShortTitle %}{% assign greatgrandchildTitle = greatgrandchildPage.renderedShortTitle %}{% else %}{% assign greatgrandchildTitle = greatgrandchildPage.renderedFullTitle %}{% endif %}
<li class="sidebar-article {% if currentPath contains greatgrandchildPage.href %}active {% if currentPath == greatgrandchildPage.href %}is-current-page{% endif %}{% endif %}">
<a href="{{greatgrandchildPage.href}}" class="pl-6 pr-5 py-1{% if forloop.last %} pb-2{% endif %}">{{ greatgrandchildTitle }}</a>
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
<!-- some categories have no maptopics, only articles -->
{% elsif childPage.childPages[0].page.documentType == "article" %}
<ul class="sidebar-articles list-style-none">
{% for grandchildPage in childPage.childPages %}
{% if grandchildPage.renderedShortTitle %}{% assign grandchildTitle = grandchildPage.renderedShortTitle %}{% else %}{% assign grandchildTitle = grandchildPage.renderedFullTitle %}{% endif %}
<li class="sidebar-article {% if currentPath contains grandchildPage.href %}active {% if currentPath == grandchildPage.href %}is-current-page{% endif %}{% endif %}">
<a href="{{grandchildPage.href}}" class="pl-4 pr-5 py-1{% if forloop.last %} pb-2{% endif %}">{{ grandchildTitle }}</a>
</li>
{% endfor %}
</ul>
{% endif %}
{% endif %}
</details>
</li>
{% endfor %}
</ul>
</li>
{% endunless %}

View File

@@ -15,7 +15,11 @@
</ul> </ul>
{% else %} {% else %}
<ul class="sidebar-products"> <ul class="sidebar-products">
{% if FEATURE_NEW_SITETREE %}
{% include sidebar-product %}
{% else %}
{% include sidebar-specific-product %} {% include sidebar-specific-product %}
{% endif %}
</ul> </ul>
{% endif %} {% endif %}
</nav> </nav>

View File

@@ -150,11 +150,22 @@
{% endif %} {% endif %}
<div class="container-xl px-3 px-md-6 mt-6"> <div class="container-xl px-3 px-md-6 mt-6">
{% if FEATURE_NEW_SITETREE %}
{% if page.documentType == "category" %}
{% include category-articles-list %}
{% endif %}
{% if page.documentType == "product" %}
{% include product-articles-list %}
{% endif %}
{% endif %}
{% unless FEATURE_NEW_SITETREE %}
{% if currentCategory %} {% if currentCategory %}
{% include all-articles-category %} {% include all-articles-category %}
{% else %} {% else %}
{% include all-articles-product %} {% include all-articles-product %}
{% endif %} {% endif %}
{% endunless %}
</div> </div>
{% include support-section %} {% include support-section %}

View File

@@ -20,7 +20,7 @@ module.exports = async function createTree (originalPath, langObj) {
const localizedBasePath = path.posix.join(__dirname, '..', langObj.dir, 'content') const localizedBasePath = path.posix.join(__dirname, '..', langObj.dir, 'content')
// Initialize the Page! This is where the file reads happen. // Initialize the Page! This is where the file reads happen.
let page = await Page.init({ const page = await Page.init({
basePath: localizedBasePath, basePath: localizedBasePath,
relativePath, relativePath,
languageCode: langObj.code languageCode: langObj.code
@@ -29,17 +29,10 @@ module.exports = async function createTree (originalPath, langObj) {
if (!page) { if (!page) {
// Do not throw an error if Early Access is not available. // Do not throw an error if Early Access is not available.
if (relativePath.startsWith('early-access')) return if (relativePath.startsWith('early-access')) return
// If a translated path doesn't exist, fall back to the English so there is parity between // Do not throw an error if translated page is not available.
// the English tree and the translated trees. if (langObj.code !== 'en') return
if (langObj.code !== 'en') {
page = await Page.init({
basePath: basePath,
relativePath,
languageCode: langObj.code
})
}
if (!page) throw Error(`Cannot initialize page for ${filepath}`) throw Error(`Cannot initialize page for ${filepath} in ${langObj.code}`)
} }
// Create the root tree object on the first run, and create children recursively. // Create the root tree object on the first run, and create children recursively.

View File

@@ -1,7 +1,7 @@
// add a new redirect string to redirect_from frontmatter // add a new redirect string to redirect_from frontmatter
module.exports = function addRedirectToFrontmatter (redirectFromData, newRedirectString) { module.exports = function addRedirectToFrontmatter (redirectFromData, newRedirectString) {
if (Array.isArray(redirectFromData)) { if (Array.isArray(redirectFromData) && !redirectFromData.includes(newRedirectString)) {
redirectFromData.push(newRedirectString) redirectFromData.push(newRedirectString)
} else if (typeof redirectFromData === 'string') { } else if (typeof redirectFromData === 'string') {
redirectFromData = [redirectFromData] redirectFromData = [redirectFromData]

View File

@@ -0,0 +1,55 @@
const path = require('path')
const renderOpts = { textOnly: true, encodeEntities: true }
// This middleware exposes a list of all categories and child articles at /categories.json.
// GitHub Support uses this for internal ZenDesk search functionality.
module.exports = async function categoriesForSupport (req, res, next) {
const englishSiteTree = req.context.siteTree.en
const allCategories = []
await Promise.all(Object.keys(englishSiteTree).map(async (version) => {
await Promise.all(englishSiteTree[version].childPages.map(async (productPage) => {
if (productPage.page.relativePath.startsWith('early-access')) return
if (!productPage.childPages) return
await Promise.all(productPage.childPages.map(async (categoryPage) => {
// We can't get the rendered titles from middleware/render-tree-titles
// here because that middleware only runs on the current version, and this
// middleware processes all versions.
const name = categoryPage.page.title.includes('{')
? await categoryPage.page.renderProp('title', req.context, renderOpts)
: categoryPage.page.title
allCategories.push({
name,
published_articles: await findArticlesPerCategory(categoryPage, [], req.context)
})
}))
}))
}))
return res.json(allCategories)
}
async function findArticlesPerCategory (currentPage, articlesArray, context) {
if (currentPage.page.documentType === 'article') {
const title = currentPage.page.title.includes('{')
? await currentPage.page.renderProp('title', context, renderOpts)
: currentPage.page.title
articlesArray.push({
title,
slug: path.basename(currentPage.href)
})
}
if (!currentPage.childPages) return articlesArray
// Run recursively to find any articles deeper in the tree.
await Promise.all(currentPage.childPages.map(async (childPage) => {
await findArticlesPerCategory(childPage, articlesArray, context)
}))
return articlesArray
}

View File

@@ -10,7 +10,7 @@ const {
getPathWithoutLanguage getPathWithoutLanguage
} = require('../lib/path-utils') } = require('../lib/path-utils')
const productNames = require('../lib/product-names') const productNames = require('../lib/product-names')
const warmServer = require('../lib/warm-server') const warmServer = process.env.FEATURE_NEW_SITETREE ? require('../lib/warm-server2') : require('../lib/warm-server')
const featureFlags = Object.keys(require('../feature-flags')) const featureFlags = Object.keys(require('../feature-flags'))
const builtAssets = require('../lib/built-asset-urls') const builtAssets = require('../lib/built-asset-urls')
const searchVersions = require('../lib/search/versions') const searchVersions = require('../lib/search/versions')
@@ -27,7 +27,7 @@ module.exports = async function contextualize (req, res, next) {
// make feature flag environment variables accessible in layouts // make feature flag environment variables accessible in layouts
req.context.process = { env: {} } req.context.process = { env: {} }
featureFlags.forEach(featureFlagName => { featureFlags.forEach(featureFlagName => {
req.context.process.env[featureFlagName] = process.env[featureFlagName] req.context[featureFlagName] = process.env[featureFlagName]
}) })
// define each context property explicitly for code-search friendliness // define each context property explicitly for code-search friendliness

View File

@@ -0,0 +1,38 @@
module.exports = async function breadcrumbs (req, res, next) {
if (!req.context.page) return next()
if (req.context.page.hidden) return next()
req.context.breadcrumbs = []
// Return an empty array on the landing page.
if (req.context.page.documentType === 'homepage') {
return next()
}
const currentSiteTree = req.context.siteTree[req.context.currentLanguage][req.context.currentVersion]
await createBreadcrumb(
// Array of child pages on the root, i.e., the product level.
currentSiteTree.childPages,
req.context
)
return next()
}
async function createBreadcrumb (pageArray, context) {
// Find each page in the siteTree's array of child pages that starts with the requested path.
const childPage = pageArray.find(page => context.currentPath.startsWith(page.href))
context.breadcrumbs.push({
documentType: childPage.page.documentType,
href: childPage.href,
title: childPage.renderedShortTitle || childPage.renderedFullTitle
})
// Recursively loop through the siteTree and create each breadcrumb, until we reach the
// point where the current siteTree page is the same as the requested page. Then stop.
if (childPage.childPages && context.currentPath !== childPage.href) {
createBreadcrumb(childPage.childPages, context)
}
}

View File

@@ -0,0 +1,10 @@
module.exports = function currentProductTree (req, res, next) {
if (!req.context.page) return next()
if (req.context.page.documentType === 'homepage') return next()
const currentSiteTree = req.context.siteTree[req.context.currentLanguage][req.context.currentVersion]
req.context.currentProductTree = currentSiteTree.childPages.find(page => req.context.currentPath.startsWith(page.href))
return next()
}

View File

@@ -0,0 +1,55 @@
module.exports = async function breadcrumbs (req, res, next) {
if (!req.context.page) return next()
if (!req.context.page.hidden) return next()
req.context.breadcrumbs = []
// Return an empty array on the landing page.
if (req.context.page.documentType === 'homepage') {
return next()
}
const earlyAccessProduct = req.context.siteTree[req.language][req.context.currentVersion].childPages.find(childPage => childPage.page.relativePath === 'early-access/index.md')
if (!earlyAccessProduct) return next()
// Create initial landing page breadcrumb
req.context.breadcrumbs.push({
documentType: earlyAccessProduct.page.documentType,
href: '',
title: earlyAccessProduct.page.title
})
// If this is the Early Access landing page, return now
if (req.context.currentPath === earlyAccessProduct.href) {
return next()
}
// Otherwise, create breadcrumbs
await createBreadcrumb(
earlyAccessProduct.childPages,
req.context
)
return next()
}
async function createBreadcrumb (pageArray, context) {
// Find each page in the siteTree's array of child pages that starts with the requested path.
const childPage = pageArray.find(page => context.currentPath.startsWith(page.href))
// Gray out product breadcrumb links and `Articles` categories
const hideHref = childPage.page.documentType === 'product' ||
(childPage.page.documentType === 'category' && childPage.page.relativePath.endsWith('/articles/index.md'))
context.breadcrumbs.push({
documentType: childPage.page.documentType,
href: hideHref ? '' : childPage.href,
title: await childPage.page.renderTitle(context, { textOnly: true, encodeEntities: true })
})
// Recursively loop through the siteTree and create each breadcrumb, until we reach the
// point where the current siteTree page is the same as the requested page. Then stop.
if (childPage.childPages && context.currentPath !== childPage.href) {
createBreadcrumb(childPage.childPages, context)
}
}

View File

@@ -7,7 +7,7 @@ module.exports = function earlyAccessContext (req, res, next) {
// Get a list of all hidden pages per version // Get a list of all hidden pages per version
const earlyAccessPageLinks = uniq(Object.values(req.context.pages) const earlyAccessPageLinks = uniq(Object.values(req.context.pages)
.filter(page => page.hidden && page.relativePath.startsWith('early-access') && page.relativePath !== 'early-access/index.md') .filter(page => page.hidden && page.relativePath.startsWith('early-access') && !page.relativePath.endsWith('index.md'))
.map(page => page.permalinks) .map(page => page.permalinks)
.flat()) .flat())
// Get links for the current version // Get links for the current version

View File

@@ -0,0 +1,59 @@
const { sortBy } = require('lodash')
module.exports = async function genericToc (req, res, next) {
if (!req.context.page) return next()
if (req.context.page.hidden) return next()
if (req.context.currentLayoutName !== 'default') return next()
if (req.context.page.documentType === 'homepage' || req.context.page.documentType === 'article') return next()
const currentSiteTree = req.context.siteTree[req.context.currentLanguage][req.context.currentVersion]
// Find the array of child pages that start with the requested path.
const currentPageInSiteTree = findPageInSiteTree(currentSiteTree.childPages, req.context.currentPath)
req.context.tocItems = sortBy(
await getUnsortedTocItems(currentPageInSiteTree.childPages, req.context),
// Sort by the ordered array of `children` in the frontmatter.
currentPageInSiteTree.page.children
)
return next()
}
// Recursively loop through the siteTree until we reach the point where the
// current siteTree page is the same as the requested page. Then stop.
function findPageInSiteTree (pageArray, currentPath) {
const childPage = pageArray.find(page => currentPath.startsWith(page.href))
if (childPage.href === currentPath) {
return childPage
}
return findPageInSiteTree(childPage.childPages, currentPath)
}
async function getUnsortedTocItems (pageArray, context) {
return Promise.all(pageArray.map(async (childPage) => {
// return an empty string if it's a hidden link on a non-hidden page (hidden links on hidden pages are OK)
if (childPage.page.hidden && !context.page.hidden) {
return ''
}
const fullPath = childPage.href
// Titles are already rendered by middleware/contextualizers/render-tree-titles.js.
const title = childPage.renderedFullTitle
const intro = await childPage.page.renderProp('intro', context, { unwrap: true })
if (!childPage.childPages) {
return { fullPath, title, intro }
}
const childTocItems = sortBy(
await getUnsortedTocItems(childPage.childPages, context),
// Sort by the ordered array of `children` in the frontmatter.
childPage.page.children
)
return { fullPath, title, intro, childTocItems }
}))
}

View File

@@ -3,20 +3,18 @@ const layouts = require('../../lib/layouts')
module.exports = function layoutContext (req, res, next) { module.exports = function layoutContext (req, res, next) {
if (!req.context.page) return next() if (!req.context.page) return next()
let layoutName const layoutOptsByType = {
if (req.context.page.layout) {
// Layouts can be specified with a `layout` frontmatter value. // Layouts can be specified with a `layout` frontmatter value.
// Any invalid layout values will be caught by frontmatter schema validation. // Any invalid layout values will be caught by frontmatter schema validation.
layoutName = req.context.page.layout string: req.context.page.layout,
// A `layout: false` value means use no layout. // A `layout: false` value means use no layout.
} else if (req.context.page.layout === false) { boolean: '',
layoutName = '' // For all other files (like articles and the homepage), use the `default` layout.
// If undefined, use either the default layout or the generic-toc layout. undefined: 'default'
} else if (req.context.page.layout === undefined) {
layoutName = 'default'
} }
const layoutName = layoutOptsByType[typeof (req.context.page.layout)]
// Attach to the context object // Attach to the context object
req.context.currentLayoutName = layoutName req.context.currentLayoutName = layoutName
req.context.currentLayout = layouts[layoutName] req.context.currentLayout = layouts[layoutName]

View File

@@ -0,0 +1,33 @@
const { sortBy } = require('lodash')
const renderOpts = { textOnly: true, encodeEntities: true }
module.exports = async function renderTreeTitles (req, res, next) {
if (!req.context.page) return next()
if (req.context.page.documentType === 'homepage') return next()
await renderLiquidInTitles(req.context.siteTree[req.context.currentLanguage][req.context.currentVersion], req.context)
return next()
}
// Add new props to each siteTree page here...
async function renderLiquidInTitles (pageInTree, context) {
// We _only_ need to render the titles and shortTitles that contain Liquid.
pageInTree.renderedFullTitle = pageInTree.page.title.includes('{')
? await pageInTree.page.renderProp('title', context, renderOpts)
: pageInTree.page.title
if (pageInTree.page.shortTitle) {
pageInTree.renderedShortTitle = pageInTree.page.shortTitle.includes('{')
? await pageInTree.page.renderProp('shortTitle', context, renderOpts)
: pageInTree.page.shortTitle
}
if (!pageInTree.childPages) return
pageInTree.page.childPages = sortBy(
await Promise.all(pageInTree.childPages.map(async (childPage) => await renderLiquidInTitles(childPage, context))),
// Sort by the ordered array of `children` in the frontmatter.
pageInTree.page.children
)
}

View File

@@ -97,7 +97,12 @@ module.exports = function (app) {
app.use(asyncMiddleware(instrument('./archived-enterprise-versions'))) app.use(asyncMiddleware(instrument('./archived-enterprise-versions')))
app.use(instrument('./robots')) app.use(instrument('./robots'))
app.use(/(\/.*)?\/early-access$/, instrument('./contextualizers/early-access-links')) app.use(/(\/.*)?\/early-access$/, instrument('./contextualizers/early-access-links'))
if (!process.env.FEATURE_NEW_SITETREE) {
app.use('/categories.json', asyncMiddleware(instrument('./categories-for-support-team'))) app.use('/categories.json', asyncMiddleware(instrument('./categories-for-support-team')))
}
if (process.env.FEATURE_NEW_SITETREE) {
app.use('/categories.json', asyncMiddleware(instrument('./categories-for-support')))
}
app.use(instrument('./loaderio-verification')) app.use(instrument('./loaderio-verification'))
app.get('/_500', asyncMiddleware(instrument('./trigger-error'))) app.get('/_500', asyncMiddleware(instrument('./trigger-error')))
@@ -111,8 +116,20 @@ module.exports = function (app) {
app.use(instrument('./contextualizers/webhooks')) app.use(instrument('./contextualizers/webhooks'))
app.use(asyncMiddleware(instrument('./contextualizers/whats-new-changelog'))) app.use(asyncMiddleware(instrument('./contextualizers/whats-new-changelog')))
app.use(instrument('./contextualizers/layout')) app.use(instrument('./contextualizers/layout'))
if (!process.env.FEATURE_NEW_SITETREE) {
app.use(asyncMiddleware(instrument('./breadcrumbs'))) app.use(asyncMiddleware(instrument('./breadcrumbs')))
app.use(asyncMiddleware(instrument('./early-access-breadcrumbs'))) app.use(asyncMiddleware(instrument('./early-access-breadcrumbs')))
}
if (process.env.FEATURE_NEW_SITETREE) {
app.use(asyncMiddleware(instrument('./contextualizers/render-tree-titles')))
app.use(instrument('./contextualizers/current-product-tree'))
app.use(asyncMiddleware(instrument('./contextualizers/generic-toc')))
app.use(asyncMiddleware(instrument('./contextualizers/breadcrumbs')))
app.use(asyncMiddleware(instrument('./contextualizers/early-access-breadcrumbs')))
}
app.use(asyncMiddleware(instrument('./enterprise-server-releases'))) app.use(asyncMiddleware(instrument('./enterprise-server-releases')))
app.use(asyncMiddleware(instrument('./dev-toc'))) app.use(asyncMiddleware(instrument('./dev-toc')))
app.use(asyncMiddleware(instrument('./featured-links'))) app.use(asyncMiddleware(instrument('./featured-links')))

View File

@@ -0,0 +1,44 @@
#!/usr/bin/env node
const fs = require('fs')
const path = require('path')
const readFrontmatter = require('../../lib/read-frontmatter')
const earlyAccessDir = path.posix.join(process.cwd(), 'content', 'early-access')
const { sentenceCase } = require('change-case')
updateOrCreateToc(earlyAccessDir)
console.log('Updated Early Access TOCs!')
function updateOrCreateToc (directory) {
const children = fs.readdirSync(directory)
.filter(subpath => !subpath.endsWith('index.md'))
if (!children.length) return
const tocFile = path.posix.join(directory, 'index.md')
let content, data
if (fs.existsSync(tocFile)) {
const matter = readFrontmatter(fs.readFileSync(tocFile, 'utf8'))
content = matter.content
data = matter.data
} else {
content = ''
data = {
title: sentenceCase(path.basename(directory)),
versions: '*',
hidden: true
}
}
data.children = children.map(child => `/${child.replace('.md', '')}`)
const newContents = readFrontmatter.stringify(content, data, { lineWidth: 10000 })
fs.writeFileSync(tocFile, newContents)
children.forEach(child => {
if (child.endsWith('.md')) return
updateOrCreateToc(path.posix.join(directory, child))
})
}

View File

@@ -3,6 +3,7 @@
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
const walk = require('walk-sync') const walk = require('walk-sync')
const stripHtmlComments = require('strip-html-comments')
const languages = require('../../lib/languages') const languages = require('../../lib/languages')
const frontmatter = require('../../lib/read-frontmatter') const frontmatter = require('../../lib/read-frontmatter')
const addRedirectToFrontmatter = require('../../lib/redirects/add-redirect-to-frontmatter') const addRedirectToFrontmatter = require('../../lib/redirects/add-redirect-to-frontmatter')
@@ -26,6 +27,10 @@ const categoryIndexFiles = fullDirectoryPaths.map(fullDirectoryPath => walk(full
categoryIndexFiles.forEach(categoryIndexFile => { categoryIndexFiles.forEach(categoryIndexFile => {
let categoryIndexContent = fs.readFileSync(categoryIndexFile, 'utf8') let categoryIndexContent = fs.readFileSync(categoryIndexFile, 'utf8')
if (categoryIndexFile.endsWith('github/getting-started-with-github/index.md')) {
categoryIndexContent = stripHtmlComments(categoryIndexContent.replace(/\n<!--/g, '<!--'))
}
// find array of TOC link strings // find array of TOC link strings
const rawItems = categoryIndexContent.match(linksArray) const rawItems = categoryIndexContent.match(linksArray)
if (!rawItems || !rawItems[0].includes('topic_link_in_list')) return if (!rawItems || !rawItems[0].includes('topic_link_in_list')) return
@@ -79,7 +84,9 @@ categoryIndexFiles.forEach(categoryIndexFile => {
// Read the article file so we can add a redirect from its old path // Read the article file so we can add a redirect from its old path
const articleContents = frontmatter(fs.readFileSync(newArticlePath, 'utf8')) const articleContents = frontmatter(fs.readFileSync(newArticlePath, 'utf8'))
addRedirectToFrontmatter(articleContents.data.redirect_from, `${oldTopicDirectory}/${article}`)
if (!articleContents.data.redirect_from) articleContents.data.redirect_from = []
addRedirectToFrontmatter(articleContents.data.redirect_from, `${oldTopicDirectory.replace(/^.*?\/content\//, '/')}/${article}`)
// Write the article with updated frontmatter // Write the article with updated frontmatter
fs.writeFileSync(newArticlePath, frontmatter.stringify(articleContents.content.trim(), articleContents.data, { lineWidth: 10000 })) fs.writeFileSync(newArticlePath, frontmatter.stringify(articleContents.content.trim(), articleContents.data, { lineWidth: 10000 }))

View File

@@ -6,6 +6,7 @@ const walk = require('walk-sync')
const frontmatter = require('../../lib/read-frontmatter') const frontmatter = require('../../lib/read-frontmatter')
const getDocumentType = require('../../lib/get-document-type') const getDocumentType = require('../../lib/get-document-type')
const languages = require('../../lib/languages') const languages = require('../../lib/languages')
const extendedMarkdownTags = Object.keys(require('../../lib/liquid-tags/extended-markdown').tags)
const linkString = /{% [^}]*?link.*? (\/.*?) ?%}/m const linkString = /{% [^}]*?link.*? (\/.*?) ?%}/m
const linksArray = new RegExp(linkString.source, 'gm') const linksArray = new RegExp(linkString.source, 'gm')
@@ -72,8 +73,23 @@ indexFiles
] ]
} }
// Remove the Table of Contents section and leave any body text before it.
let newContent = content
.replace(/^#*? Table of contents[\s\S]*/im, '')
.replace('<div hidden>', '')
.replace(linksArray, '')
const linesArray = newContent
.split('\n')
const newLinesArray = linesArray
.filter((line, index) => /\S/.test(line) || (extendedMarkdownTags.find(tag => (linesArray[index - 1] && linesArray[index - 1].includes(tag)) || (linesArray[index + 1] && linesArray[index + 1].includes(tag)))))
.filter(line => !/^<!--\s+?-->$/m.test(line))
newContent = newLinesArray.join('\n')
// Index files should no longer have body content, so we write an empty string // Index files should no longer have body content, so we write an empty string
fs.writeFileSync(indexFile, frontmatter.stringify('', data, { lineWidth: 10000 })) fs.writeFileSync(indexFile, frontmatter.stringify(newContent, data, { lineWidth: 10000 }))
}) })
function getLinks (linkItemArray) { function getLinks (linkItemArray) {