diff --git a/README.md b/README.md
index fb78edb..0b643d0 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,9 @@
# doc.silverstripe.org
-This repository contains the source code powering [SilverStripe's
-developer documentation website](https://docs.silverstripe.org).
+This repository contains the source code powering [the Silverstripe CMS
+developer documentation website](https://docs.silverstripe.org) and
+[userhelp website](https://userhelp.silverstripe.org).
This application is build on [Gatsby](https://gatsbyjs.com), a static
site generator based on [React](https://reactjs.org). It sources content
@@ -61,6 +62,20 @@ gatsby serve
These commands will give you an exact representation of how the site will run on a production server, with
statically generated html files and server-side rendering.
+## Toggling between docs and userhelp
+
+Whether the application uses the `docs.silverstripe.org` content or `userhelp.silverstripe.org` is determined
+by the environment variable, `DOCS_CONTEXT`. You can set this in the `.env.development` file, or use one of
+the script shortcuts:
+
+```
+yarn dev-docs
+yarn dev-user
+yarn build-docs
+yarn build-user
+```
+
+
## Developing
From within `path/to/ssdocs`, run the command
@@ -85,7 +100,7 @@ your content changes, since the remote repositories are the source of truth.
## Deploying content changes
Once your contribution has been merged into the master branch, it will be deployed to production via a
-Github action in the `silverstripe-framework` repository.
+Github action in the repository that holds the markdown files (e.g. `silverstripe/silverstripe-framework` for docs).
## Deploying app changes
@@ -93,11 +108,12 @@ Once your change is merged in to the `master` branch of this repository, it will
## Contribution
-To contribute an improvement to the https://docs.silverstripe.org functionality or
+To contribute an improvement to the https://docs.silverstripe.org or https://userhelp.silverstripe.org functionality or
theme, submit a pull request on the [GitHub project](https://github.com/silverstripe/doc.silverstripe.org). Any approved pull requests will make
-their way onto the https://docs.silverstripe.org site in the next release.
+their way onto the https://docs.silverstripe.org or https://userhelp.silverstripe.org sites in the next release.
If you wish to edit the documentation content, submit a pull request
on the
-[framework Github project](https://github.com/silverstripe/silverstripe-framework). Updated
-documentation content is uploaded daily to [doc.silverstripe.org](https://docs.silverstripe.org) via a build hook.
\ No newline at end of file
+[framework Github project](https://github.com/silverstripe/silverstripe-framework) or the
+[userhelp documentation](https://github.com/silverstripe/silverstripe-userhelp-content) repository
+or corresponding module.
\ No newline at end of file
diff --git a/gatsby-browser.js b/gatsby-browser.js
index 9306848..e3937fa 100644
--- a/gatsby-browser.js
+++ b/gatsby-browser.js
@@ -2,22 +2,39 @@ require("prismjs/themes/prism-okaidia.css");
require("./src/theme/assets/scss/theme.scss");
require('./src/theme/assets/fontawesome/css/all.css');
require('./src/theme/assets/search/algolia.css');
-const smoothScroll = require('smooth-scroll');
-const Layout = require('./src/components/Layout').default;
+
const React = require('react');
+const Layout = require('./src/components/Layout').default;
+const NodeProvider = require('./src/components/NodeProvider').default;
+const smoothScroll = require('smooth-scroll');
+
if (typeof window !== "undefined") {
smoothScroll('a[href*="#"]')
}
+/**
+ * Ensures the chrome doesn't rerender every page load, which makes the sidebar reset its scroll.
+ */
exports.wrapPageElement = ({ element, props }) => {
- return {element}
+ return (
+
+
+ {element}
+
+
+ );
};
+
exports.onRouteUpdate = ({location}) => {
+ if (window.ga && process.env.NODE_ENV === 'production') {
+ window.ga('send', 'pageview');
+ }
anchorScroll(location);
return true;
};
+
exports.shouldUpdateScroll = ({
routerProps: { location },
}) => {
diff --git a/gatsby-config.js b/gatsby-config.js
index 741eb47..b4b34d8 100644
--- a/gatsby-config.js
+++ b/gatsby-config.js
@@ -1,4 +1,7 @@
const path = require('path');
+const sources = process.env.DOCS_CONTEXT === 'user'
+ ? require('./sources-user')
+ : require('./sources-docs');
module.exports = {
siteMetadata: {
@@ -14,36 +17,14 @@ module.exports = {
`gatsby-plugin-sharp`,
`gatsby-plugin-sitemap`,
`gatsby-plugin-netlify`,
- {
- resolve: `gatsby-source-git`,
- options: {
- name: `4`,
- remote: `https://github.com/silverstripe/silverstripe-framework.git`,
- branch: `4`,
- patterns: `docs/en/**`
- }
- },
- {
- resolve: `gatsby-source-git`,
- options: {
- name: `3`,
- remote: `https://github.com/silverstripe/silverstripe-framework.git`,
- branch: `3.7`,
- patterns: `docs/en/**`
- }
- },
+
+ ...sources,
+
{
resolve: `gatsby-source-filesystem`,
options: {
- name: `watcher--ss3`,
- path: `${__dirname}/.cache/gatsby-source-git/3/docs/en`
- }
- },
- {
- resolve: `gatsby-source-filesystem`,
- options: {
- name: `watcher--ss4`,
- path: `${__dirname}/.cache/gatsby-source-git/4/docs/en`
+ name: `watcher`,
+ path: `${__dirname}/.cache/gatsby-source-git/`
}
},
{
@@ -108,7 +89,7 @@ module.exports = {
// allowed selectors defined in FontAwesome. Everything else in FA should be removed.
extractor: class {
static extract(content) {
- const selectors = [`file-alt`]
+ const selectors = [`fa-file-alt`]
const matches = content.match(/icon(Brand)?: ([a-zA-Z0-9_-]+)/);
if (matches) {
const isBrand = typeof matches[1] !== 'undefined';
diff --git a/gatsby-node.js b/gatsby-node.js
index 20e3a37..42daa5a 100644
--- a/gatsby-node.js
+++ b/gatsby-node.js
@@ -3,31 +3,41 @@ const fs = require('fs');
const { createFilePath } = require(`gatsby-source-filesystem`);
const fileToTitle = require('./src/utils/fileToTitle');
-const createSlug = (filePath, version) => {
- const parts = filePath.split('/');
- const langIndex = parts.indexOf('en');
- parts.splice(langIndex + 1, 0, version);
- return parts
- .map(part => part.replace(/^\d+_/, ''))
- .join('/')
- .toLowerCase()
+const createSlug = ({path, version, thirdparty}) => {
+ const rest = path.split('/');
+ const parts = [
+ 'en',
+ version,
+ // thirdparty modules are explicitly pathed
+ thirdparty,
+ ...rest,
+ ].filter(p => p);
+
+ const slug = parts
+ .map(part => part.replace(/^\d+_/, ''))
+ .join('/')
+ .toLowerCase();
+
+ return `/${slug}/`;
};
-exports.onCreateNode = async ({ node, getNode, getNodesByType, actions, createNodeId, createContentDigest }) => {
+const parseName = name => name.split('--');
+
+exports.onCreateNode = async ({ node, getNode, getNodesByType, actions, createNodeId, createContentDigest }) => {
if (node.internal.type !== 'MarkdownRemark') {
return;
}
const { createNode } = actions;
const fileNode = getNode(node.parent);
- const version = fileNode.sourceInstanceName;
+ const [category, version, thirdparty] = parseName(fileNode.sourceInstanceName);
- // The gatsby-source-filesystem plugins are registered to collect from the same path
+ // The gatsby-sgatsbource-filesystem plugins are registered to collect from the same path
// that the git source writes to, so we get the watch task (hot reload on content changes)
// But we don't want duplicate document pages for each source plugin, so
// we bail out if we already have the file. However, we need to ensure
// the file is injected into the template as a dependency, so when the content changes,
// the pages get refreshed on the fly.
- if (version.match(/^watcher--/)) {
+ if (category === 'watcher') {
const existing = getNodesByType('SilverstripeDocument')
.find(n => n.fileAbsolutePath === node.fileAbsolutePath);
@@ -35,14 +45,14 @@ exports.onCreateNode = async ({ node, getNode, getNodesByType, actions, createNo
// Pair the document with its watched file so we can inject it into the template
// as a dependency.
existing.watchFile___NODE = node.id;
- return;
}
- }
-
+ return;
+ }
+ const basePath = category === 'user' ? `docs/en/userguide` : `docs/en`;
const filePath = createFilePath({
node,
getNode,
- basePath: `docs`
+ basePath,
});
let fileTitle = path.basename(node.fileAbsolutePath, '.md');
const isIndex = fileTitle === 'index';
@@ -50,10 +60,24 @@ exports.onCreateNode = async ({ node, getNode, getNodesByType, actions, createNo
fileTitle = path.basename(path.dirname(node.fileAbsolutePath));
}
const docTitle = fileToTitle(fileTitle);
- const slug = createSlug(filePath, version);
+ const slug = createSlug({
+ path: filePath,
+ version,
+ thirdparty,
+ });
+
const parentSlug = `${path.resolve(slug, '../')}/`;
const unhideSelf = false;
+ // Most of these don't exist in userhelp, so force them into the schema by un-nulling them.
+ const frontmatter = {
+ introduction: ``,
+ icon: `file-alt`,
+ iconBrand: ``,
+ hideChildren: false,
+ ...node.frontmatter,
+ };
+
const docData = {
isIndex,
filePath,
@@ -61,7 +85,8 @@ exports.onCreateNode = async ({ node, getNode, getNodesByType, actions, createNo
slug,
parentSlug,
unhideSelf,
- ...node.frontmatter,
+ category,
+ ...frontmatter,
};
if (!docData.title || docData.title === '') {
diff --git a/gatsby-ssr.js b/gatsby-ssr.js
index e8350e0..9cd58e3 100644
--- a/gatsby-ssr.js
+++ b/gatsby-ssr.js
@@ -1,13 +1,25 @@
-const Layout = require('./src/components/Layout').default;
const React = require('react');
+const Layout = require('./src/components/Layout').default;
+const NodeProvider = require('./src/components/NodeProvider').default;
+const { setCurrentPath } = require('./src/utils/nodes');
+/**
+ * Applies the node provider (static query of all documents)
+ * Ensures the chrome doesn't rerender every page load, which makes the sidebar reset its scroll.
+ */
exports.wrapPageElement = ({ element, props }) => {
- return {element}
+ return (
+
+
+ {element}
+
+
+ );
};
-exports.onRenderBody = ({ setPostBodyComponents, setHeadComponents }) => {
+exports.onRenderBody = ({ setPostBodyComponents, setHeadComponents, pathname }) => {
// Rules that cannot be touched by purgecss because they come in from client side rendering
- setHeadComponents([
+ setHeadComponents([