diff --git a/adminSiteClient/GdocsBreadcrumbsInput.tsx b/adminSiteClient/GdocsManualBreadcrumbsInput.tsx
similarity index 73%
rename from adminSiteClient/GdocsBreadcrumbsInput.tsx
rename to adminSiteClient/GdocsManualBreadcrumbsInput.tsx
index 094e7188cb..e2ef87fa2c 100644
--- a/adminSiteClient/GdocsBreadcrumbsInput.tsx
+++ b/adminSiteClient/GdocsManualBreadcrumbsInput.tsx
@@ -70,7 +70,7 @@ export const BreadcrumbLine = ({
)
}
-export const GdocsBreadcrumbsInput = ({
+export const GdocsManualBreadcrumbsInput = ({
gdoc,
setCurrentGdoc,
errors,
@@ -85,56 +85,63 @@ export const GdocsBreadcrumbsInput = ({
breadcrumbs[breadcrumbs.length - 1].href = undefined
} else breadcrumbs = undefined
- setCurrentGdoc({ ...gdoc, breadcrumbs: breadcrumbs ?? null })
+ setCurrentGdoc({ ...gdoc, manualBreadcrumbs: breadcrumbs ?? null })
}
const setItemAtIndex = (item: BreadcrumbItem, i: number) => {
- const breadcrumbs = [...(gdoc.breadcrumbs ?? [])]
+ const breadcrumbs = [...(gdoc.manualBreadcrumbs ?? [])]
breadcrumbs[i] = item
setBreadcrumbs(breadcrumbs)
}
const removeItemAtIndex = (i: number) => {
- const breadcrumbs = [...(gdoc.breadcrumbs ?? [])]
+ const breadcrumbs = [...(gdoc.manualBreadcrumbs ?? [])]
breadcrumbs.splice(i, 1)
setBreadcrumbs(breadcrumbs)
}
return (
-
- Breadcrumbs
-
- setBreadcrumbs([
- { label: "" },
- ...(gdoc.breadcrumbs ?? []),
- ])
- }
- >
- Add
- breadcrumb
-
-
- {gdoc.breadcrumbs?.map((item, i) => (
+
Breadcrumbs
+ {!!gdoc.breadcrumbs?.length && !gdoc.manualBreadcrumbs?.length && (
+
+ The breadcrumbs for this article will be automatically
+ generated, based on this article's tags and the tag graph.
+ If you want to override these breadcrumbs, you can do so
+ here.
+
+ )}
+
+ setBreadcrumbs([
+ { label: "" },
+ ...(gdoc.manualBreadcrumbs ?? []),
+ ])
+ }
+ >
+ Add
+ breadcrumb
+
+ {gdoc.manualBreadcrumbs?.map((item, i) => (
setItemAtIndex(item, i)}
removeItem={() => removeItemAtIndex(i)}
key={i}
labelError={getPropertyMostCriticalError(
- `breadcrumbs[${i}].label`,
+ `manualBreadcrumbs[${i}].label`,
errors
)}
hrefError={getPropertyMostCriticalError(
- `breadcrumbs[${i}].href`,
+ `manualBreadcrumbs[${i}].href`,
errors
)}
- isLastBreadcrumbItem={i === gdoc.breadcrumbs!.length - 1}
+ isLastBreadcrumbItem={
+ i === gdoc.manualBreadcrumbs!.length - 1
+ }
/>
))}
- {!gdoc.breadcrumbs?.length && No breadcrumbs }
)
}
diff --git a/adminSiteClient/GdocsSettingsForms.tsx b/adminSiteClient/GdocsSettingsForms.tsx
index a191106806..5d555a45df 100644
--- a/adminSiteClient/GdocsSettingsForms.tsx
+++ b/adminSiteClient/GdocsSettingsForms.tsx
@@ -15,7 +15,7 @@ import {
import { GdocsPublishedAt } from "./GdocsDateline.js"
import { GdocsPublicationContext } from "./GdocsPublicationContext.js"
import { Alert } from "antd"
-import { GdocsBreadcrumbsInput } from "./GdocsBreadcrumbsInput.js"
+import { GdocsManualBreadcrumbsInput } from "./GdocsManualBreadcrumbsInput.js"
const GdocCommonErrors = ({
errors,
@@ -155,7 +155,7 @@ export const GdocPostSettings = ({
errors={errors}
description="An optional property to override the excerpt of this post in our atom feed, which is used for the newsletter"
/>
- ,
boolean
> = {
- breadcrumbs: true,
+ breadcrumbs: true, // automatically generated, not actually possible to change via the admin preview
+ manualBreadcrumbs: true,
errors: true,
linkedAuthors: false,
linkedCharts: true,
diff --git a/adminSiteClient/gdocsValidation.ts b/adminSiteClient/gdocsValidation.ts
index 0ad8da3f38..fe64982c67 100644
--- a/adminSiteClient/gdocsValidation.ts
+++ b/adminSiteClient/gdocsValidation.ts
@@ -140,12 +140,12 @@ function validateExcerpt(
}
}
-function validateBreadcrumbs(
+function validateManualBreadcrumbs(
gdoc: OwidGdocPostInterface,
errors: OwidGdocErrorMessage[]
) {
- if (gdoc.breadcrumbs) {
- for (const [i, breadcrumb] of gdoc.breadcrumbs.entries()) {
+ if (gdoc.manualBreadcrumbs) {
+ for (const [i, breadcrumb] of gdoc.manualBreadcrumbs.entries()) {
if (!breadcrumb.label) {
errors.push({
property: `breadcrumbs[${i}].label`,
@@ -155,7 +155,7 @@ function validateBreadcrumbs(
}
// Last item can be missing a href
- if (!breadcrumb.href && i !== gdoc.breadcrumbs.length - 1) {
+ if (!breadcrumb.href && i !== gdoc.manualBreadcrumbs.length - 1) {
errors.push({
property: `breadcrumbs[${i}].href`,
type: OwidGdocErrorMessageType.Error,
@@ -292,7 +292,7 @@ export const getErrors = (gdoc: OwidGdoc): OwidGdocErrorMessage[] => {
if (checkIsGdocPost(gdoc)) {
validateRefs(gdoc, errors)
validateExcerpt(gdoc, errors)
- validateBreadcrumbs(gdoc, errors)
+ validateManualBreadcrumbs(gdoc, errors)
validateAtomFields(gdoc, errors)
} else if (checkIsDataInsight(gdoc)) {
validateApprovedBy(gdoc, errors)
diff --git a/baker/SiteBaker.tsx b/baker/SiteBaker.tsx
index 8a565eb578..6f6decdb44 100644
--- a/baker/SiteBaker.tsx
+++ b/baker/SiteBaker.tsx
@@ -634,7 +634,7 @@ export class SiteBaker {
publishedGdoc.linkedIndicators = attachments.linkedIndicators
if (
- !publishedGdoc.breadcrumbs?.length &&
+ !publishedGdoc.manualBreadcrumbs?.length &&
publishedGdoc.tags?.length
) {
publishedGdoc.breadcrumbs = db.getBestBreadcrumbs(
diff --git a/db/db.ts b/db/db.ts
index ef992fc16c..d64f6d29ad 100644
--- a/db/db.ts
+++ b/db/db.ts
@@ -390,7 +390,7 @@ export const getPublishedGdocPosts = async (
knex,
`-- sql
SELECT
- g.breadcrumbs,
+ g.manualBreadcrumbs,
g.content,
g.createdAt,
g.id,
@@ -426,7 +426,7 @@ export const getPublishedGdocPostsWithTags = async (
knex,
`-- sql
SELECT
- g.breadcrumbs,
+ g.manualBreadcrumbs,
g.content,
g.createdAt,
g.id,
diff --git a/db/migrateWpPostsToArchieMl.ts b/db/migrateWpPostsToArchieMl.ts
index a0938d6c54..e3a9647c39 100644
--- a/db/migrateWpPostsToArchieMl.ts
+++ b/db/migrateWpPostsToArchieMl.ts
@@ -256,7 +256,7 @@ const migrate = async (trx: db.KnexReadWriteTransaction): Promise => {
updatedAt: post.updated_at_in_wordpress,
publicationContext: OwidGdocPublicationContext.listed, // TODO: not all articles are listed, take this from the DB
revisionId: null,
- breadcrumbs: null,
+ manualBreadcrumbs: null,
markdown: null,
}
const archieMlStatsContent = {
diff --git a/db/migration/1736455365750-RenameBreadcrumbsColumn.ts b/db/migration/1736455365750-RenameBreadcrumbsColumn.ts
new file mode 100644
index 0000000000..e23ced8420
--- /dev/null
+++ b/db/migration/1736455365750-RenameBreadcrumbsColumn.ts
@@ -0,0 +1,15 @@
+import { MigrationInterface, QueryRunner } from "typeorm"
+
+export class RenameBreadcrumbsColumn1736455365750
+ implements MigrationInterface
+{
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`-- sql
+ ALTER TABLE posts_gdocs RENAME COLUMN breadcrumbs TO manualBreadcrumbs`)
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`-- sql
+ ALTER TABLE posts_gdocs RENAME COLUMN manualBreadcrumbs TO breadcrumbs`)
+ }
+}
diff --git a/db/model/Gdoc/GdocBase.ts b/db/model/Gdoc/GdocBase.ts
index 754471c247..f16d18128f 100644
--- a/db/model/Gdoc/GdocBase.ts
+++ b/db/model/Gdoc/GdocBase.ts
@@ -80,6 +80,7 @@ export class GdocBase implements OwidGdocBaseInterface {
publicationContext: OwidGdocPublicationContext =
OwidGdocPublicationContext.unlisted
breadcrumbs: BreadcrumbItem[] | null = null
+ manualBreadcrumbs: BreadcrumbItem[] | null = null
tags: DbPlainTag[] | null = null
errors: OwidGdocErrorMessage[] = []
donors: string[] = []
diff --git a/db/model/Gdoc/GdocFactory.ts b/db/model/Gdoc/GdocFactory.ts
index 4e8f9f8140..de175255d2 100644
--- a/db/model/Gdoc/GdocFactory.ts
+++ b/db/model/Gdoc/GdocFactory.ts
@@ -207,7 +207,7 @@ export async function getGdocBaseObjectById(
)
gdoc.tags = tags
- if (!gdoc.breadcrumbs?.length && tags.length) {
+ if (tags.length) {
const parentTagArraysByChildName =
await getParentTagArraysByChildName(knex)
gdoc.breadcrumbs = getBestBreadcrumbs(
@@ -304,7 +304,7 @@ export async function getPublishedGdocBaseObjectBySlug(
[gdoc.id]
)
gdoc.tags = tags
- if (!gdoc.breadcrumbs?.length && tags.length) {
+ if (tags.length) {
const parentTagArraysByChildName =
await getParentTagArraysByChildName(knex)
gdoc.breadcrumbs = getBestBreadcrumbs(
@@ -595,7 +595,7 @@ export function getDbEnrichedGdocFromOwidGdoc(
gdoc: OwidGdoc | GdocBase
): DbEnrichedPostGdoc {
const enrichedGdoc = {
- breadcrumbs: gdoc.breadcrumbs,
+ manualBreadcrumbs: gdoc.manualBreadcrumbs,
content: gdoc.content,
createdAt: gdoc.createdAt,
id: gdoc.id,
diff --git a/packages/@ourworldindata/types/src/dbTypes/PostsGdocs.ts b/packages/@ourworldindata/types/src/dbTypes/PostsGdocs.ts
index 1c7bc650b2..c5e0c63933 100644
--- a/packages/@ourworldindata/types/src/dbTypes/PostsGdocs.ts
+++ b/packages/@ourworldindata/types/src/dbTypes/PostsGdocs.ts
@@ -8,7 +8,7 @@ import { MinimalTag } from "./Tags.js"
export const PostsGdocsTableName = "posts_gdocs"
export interface DbInsertPostGdoc {
- breadcrumbs?: JsonString | null
+ manualBreadcrumbs?: JsonString | null
content: JsonString
createdAt: Date
id: string
@@ -23,10 +23,10 @@ export interface DbInsertPostGdoc {
export type DbRawPostGdoc = Required
export type DbEnrichedPostGdoc = Omit<
DbRawPostGdoc,
- "content" | "breadcrumbs" | "published"
+ "content" | "manualBreadcrumbs" | "published"
> & {
content: OwidGdocContent
- breadcrumbs: BreadcrumbItem[] | null
+ manualBreadcrumbs: BreadcrumbItem[] | null
published: boolean
}
@@ -62,7 +62,7 @@ export function parsePostsGdocsRow(row: DbRawPostGdoc): DbEnrichedPostGdoc {
return {
...row,
content: parsePostGdocContent(row.content),
- breadcrumbs: parsePostsGdocsBreadcrumbs(row.breadcrumbs),
+ manualBreadcrumbs: parsePostsGdocsBreadcrumbs(row.manualBreadcrumbs),
published: !!row.published,
}
}
@@ -77,10 +77,17 @@ export function parsePostsGdocsWithTagsRow(
}
export function serializePostsGdocsRow(row: DbEnrichedPostGdoc): DbRawPostGdoc {
+ // Kind of awkward, but automatic breadcrumbs are part of OwidGdocBaseInterface,
+ // but not part of the DB schema. So we remove them here.
+ if ("breadcrumbs" in row) {
+ delete row.breadcrumbs
+ }
return {
...row,
content: serializePostGdocContent(row.content),
- breadcrumbs: serializePostsGdocsBreadcrumbs(row.breadcrumbs),
+ manualBreadcrumbs: serializePostsGdocsBreadcrumbs(
+ row.manualBreadcrumbs
+ ),
published: row.published ? 1 : 0,
}
}
diff --git a/packages/@ourworldindata/types/src/gdocTypes/Gdoc.ts b/packages/@ourworldindata/types/src/gdocTypes/Gdoc.ts
index a6f35022ea..ca6b902717 100644
--- a/packages/@ourworldindata/types/src/gdocTypes/Gdoc.ts
+++ b/packages/@ourworldindata/types/src/gdocTypes/Gdoc.ts
@@ -87,7 +87,7 @@ export interface OwidGdocBaseInterface {
updatedAt: Date | null
revisionId: string | null
publicationContext: OwidGdocPublicationContext
- breadcrumbs: BreadcrumbItem[] | null
+ manualBreadcrumbs: BreadcrumbItem[] | null
linkedAuthors?: LinkedAuthor[]
linkedDocuments?: Record
linkedCharts?: Record
@@ -96,6 +96,7 @@ export interface OwidGdocBaseInterface {
relatedCharts?: RelatedChart[]
tags?: MinimalTag[] | null
errors?: OwidGdocErrorMessage[]
+ breadcrumbs?: BreadcrumbItem[] | null
markdown: string | null
}
diff --git a/packages/@ourworldindata/utils/src/Util.ts b/packages/@ourworldindata/utils/src/Util.ts
index a81abc69b8..a456eeea21 100644
--- a/packages/@ourworldindata/utils/src/Util.ts
+++ b/packages/@ourworldindata/utils/src/Util.ts
@@ -1932,7 +1932,7 @@ export function isFiniteWithGuard(value: unknown): value is number {
// Use with getParentTagArraysByChildName to collapse all paths to the child into a single array of unique parent tag names
export function getUniqueNamesFromParentTagArrays(
- parentTagArrays: DbPlainTag[][]
+ parentTagArrays: Pick[][]
): string[] {
const tagNames = new Set(
parentTagArrays.flatMap((parentTagArray) =>
diff --git a/site/gdocs/pages/GdocPost.tsx b/site/gdocs/pages/GdocPost.tsx
index 64468b8090..0fc0f892c4 100644
--- a/site/gdocs/pages/GdocPost.tsx
+++ b/site/gdocs/pages/GdocPost.tsx
@@ -47,6 +47,7 @@ export function GdocPost({
publishedAt,
slug,
breadcrumbs,
+ manualBreadcrumbs,
}: OwidGdocPostInterface & {
isPreviewing?: boolean
}) {
@@ -90,7 +91,7 @@ export function GdocPost({
{isDeprecated && content["deprecation-notice"] && (