Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove unused WP topic page code #3720

Merged
merged 11 commits into from
Jun 25, 2024
9 changes: 7 additions & 2 deletions baker/SiteBaker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ import {
import { getBakePath } from "@ourworldindata/components"
import { GdocAuthor, getMinimalAuthors } from "../db/model/Gdoc/GdocAuthor.js"
import { DATA_INSIGHTS_ATOM_FEED_NAME } from "../site/gdocs/utils.js"
import { getRedirectsFromDb } from "../db/model/Redirect.js"

type PrefetchedAttachments = {
linkedAuthors: DbEnrichedAuthor[]
Expand Down Expand Up @@ -509,14 +510,18 @@ export class SiteBaker {

private async bakePosts(knex: db.KnexReadonlyTransaction) {
if (!this.bakeSteps.has("wordpressPosts")) return
// TODO: the knex instance should be handed down as a parameter
const alreadyPublishedViaGdocsSlugsSet =
await db.getSlugsWithPublishedGdocsSuccessors(knex)
const redirects = await getRedirectsFromDb(knex)

const postsApi = await getPostsFromSnapshots(
knex,
undefined,
(postrow) => !alreadyPublishedViaGdocsSlugsSet.has(postrow.slug)
(postrow) =>
// Exclude posts that are already published via GDocs
!alreadyPublishedViaGdocsSlugsSet.has(postrow.slug) &&
// Exclude posts that are redirect sources
!redirects.some((row) => row.source.slice(1) === postrow.slug)
)

await pMap(
Expand Down
72 changes: 3 additions & 69 deletions baker/formatWordpressPost.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,16 @@ import React from "react"
import ReactDOMServer from "react-dom/server.js"
import { HTTPS_ONLY } from "../settings/serverSettings.js"
import { GrapherExports } from "../baker/GrapherBakingUtils.js"
import { AllCharts, renderAllCharts } from "../site/blocks/AllCharts.js"
import { FormattingOptions } from "@ourworldindata/types"
import {
FormattedPost,
FullPost,
TocHeading,
WP_BlockType,
parseKeyValueArgs,
} from "@ourworldindata/utils"
import { Footnote } from "../site/Footnote.js"
import { LoadingIndicator } from "@ourworldindata/grapher"
import { PROMINENT_LINK_CLASSNAME } from "../site/blocks/ProminentLink.js"
import { countryProfileSpecs } from "../site/countryProfileProjects.js"
import { DataToken } from "../site/DataToken.js"
import { DEEP_LINK_CLASS, formatImages } from "./formatting.js"
import { replaceIframesWithExplorerRedirectsInWordPressPost } from "./replaceExplorerRedirects.js"
Expand All @@ -27,16 +24,10 @@ import {
} from "../site/blocks/AdditionalInformation.js"
import { renderHelp } from "../site/blocks/Help.js"
import { renderCodeSnippets } from "@ourworldindata/components"
import { renderExpandableParagraphs } from "../site/blocks/ExpandableParagraph.js"
import {
formatUrls,
getBodyHtml,
SUMMARY_CLASSNAME,
} from "../site/formatting.js"
import { formatUrls, getBodyHtml } from "../site/formatting.js"
import { GRAPHER_PREVIEW_CLASS } from "../site/SiteConstants.js"
import { INTERACTIVE_ICON_SVG } from "../site/InteractionNotice.js"
import { renderKeyInsights, renderProminentLinks } from "./siteRenderers.js"
import { KEY_INSIGHTS_CLASS_NAME } from "../site/blocks/KeyInsights.js"
import { renderProminentLinks } from "./siteRenderers.js"
import { RELATED_CHARTS_CLASS_NAME } from "../site/blocks/RelatedCharts.js"
import { KnexReadonlyTransaction } from "../db/db.js"

Expand All @@ -48,15 +39,6 @@ export const formatWordpressPost = async (
): Promise<FormattedPost> => {
let html = post.content

// Inject key insights early so they can be formatted by the embedding
// article. Another option would be to format the content independently,
// which would allow for inclusion further down the formatting pipeline.
// This is however creating issues by running non-idempotent formatting
// functions twice on the same content (e.g. table processing double wraps
// in "tableContainer" divs). On the other hand, rendering key insights last
// would require special care for footnotes.
html = await renderKeyInsights(html, post.id)

// Standardize urls
html = formatUrls(html)

Expand Down Expand Up @@ -111,42 +93,11 @@ export const formatWordpressPost = async (

const cheerioEl = cheerio.load(html)

// Related charts
if (
!countryProfileSpecs.some(
(spec) => post.slug === spec.landingPageSlug
) &&
post.relatedCharts?.length &&
// Render fallback "All charts" block at the top of entries only if
// manual "All charts" block not present in the rest of the document.
// This is to help transitioning towards topic pages, where this block
// is manually added in the content. In that case, we don't want to
// inject it at the top too.
!cheerioEl(`block[type='${WP_BlockType.AllCharts}']`).length
) {
// Mimicking SSR output of additional information block from PHP
const allCharts = `
<block type="additional-information" default-open="false">
<content>
${ReactDOMServer.renderToStaticMarkup(<AllCharts post={post} />)}
</content>
</block>
`
const $summary = cheerioEl(`.${SUMMARY_CLASSNAME}`)
if ($summary.length !== 0) {
$summary.after(allCharts)
} else {
cheerioEl("body > h2:first-of-type, body > h3:first-of-type")
.first()
.before(allCharts)
}
}

// SSR rendering of Gutenberg blocks, before hydration on client
//
// - Note: any post-processing on these blocks runs the risk of hydration
// discrepancies. E.g. the ToC post-processing further below add an "id"
// attribute to elibigle heading tags. In an unbridled version of that
// attribute to eligible heading tags. In an unbridled version of that
// script, the AdditionalInformation block title (h3) would be altered and
// receive an "id" attribute (<h3 id="some-title">). When this block is
// then hydrated on the client, the "id" attribute is missing, since it
Expand All @@ -155,10 +106,8 @@ export const formatWordpressPost = async (
// perspective, the server rendered version is different from the client
// one, hence the discrepancy.
renderAdditionalInformation(cheerioEl)
renderExpandableParagraphs(cheerioEl)
renderCodeSnippets(cheerioEl)
renderHelp(cheerioEl)
renderAllCharts(cheerioEl, post)
await renderProminentLinks(cheerioEl, post.id, knex)

// Extract inline styling
Expand Down Expand Up @@ -407,27 +356,12 @@ export const formatWordpressPost = async (

$heading.attr("id", slug)

if ($heading.closest(`.${KEY_INSIGHTS_CLASS_NAME}`).length) return

$heading.append(`<a class="${DEEP_LINK_CLASS}" href="#${slug}"></a>`)
})

// Extracting the useful information from the HTML
const stickyNavLinks: { text: string; target: string }[] = []
const $stickyNavContents = cheerioEl(".sticky-nav-contents")
const $stickyNavLinks = $stickyNavContents.children().children()
$stickyNavLinks.each((_, element) => {
const $elem = cheerioEl(element)
const text = $elem.text()
const target = $elem.attr("href")
if (text && target) stickyNavLinks.push({ text, target })
})
$stickyNavContents.remove()

return {
...post,
supertitle,
stickyNavLinks,
lastUpdated,
byline,
info,
Expand Down
102 changes: 1 addition & 101 deletions baker/siteRenderers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ import {
FormattedPost,
FullPost,
JsonError,
KeyInsight,
Url,
IndexPost,
mergePartialGrapherConfigs,
Expand All @@ -63,12 +62,7 @@ import {
} from "../db/db.js"
import { getPageOverrides, isPageOverridesCitable } from "./pageOverrides.js"
import { ProminentLink } from "../site/blocks/ProminentLink.js"
import {
KeyInsightsThumbs,
KeyInsightsSlides,
KEY_INSIGHTS_CLASS_NAME,
} from "../site/blocks/KeyInsights.js"
import { formatUrls, KEY_INSIGHTS_H2_CLASSNAME } from "../site/formatting.js"
import { formatUrls } from "../site/formatting.js"

import { GrapherProgrammaticInterface } from "@ourworldindata/grapher"
import { ExplorerProgram } from "../explorer/ExplorerProgram.js"
Expand Down Expand Up @@ -832,97 +826,3 @@ const renderExplorerDefaultThumbnail = (): string => {
<img src={`${BAKED_BASE_URL}/default-thumbnail.jpg`} />
)
}

export const renderKeyInsights = async (
html: string,
containerPostId: number
): Promise<string> => {
const $ = cheerio.load(html)

for (const block of Array.from($("block[type='key-insights']"))) {
const $block = $(block)
// only selecting <title> and <slug> from direct children, to not match
// titles and slugs from individual key insights slides.
const title = $block.find("> title").text()
const slug = $block.find("> slug").text()
if (!title || !slug) {
void logErrorAndMaybeSendToBugsnag(
new JsonError(
`Title or anchor missing for key insights block, content removed in wordpress post with id ${containerPostId}.`
)
)
$block.remove()
continue
}

const keyInsights = extractKeyInsights($, $block, containerPostId)
if (!keyInsights.length) {
void logErrorAndMaybeSendToBugsnag(
new JsonError(
`No valid key insights found within block, content removed in wordpress post with id ${containerPostId}`
)
)
$block.remove()
continue
}
const titles = keyInsights.map((keyInsight) => keyInsight.title)

const rendered = ReactDOMServer.renderToString(
<>
<h2 id={slug} className={KEY_INSIGHTS_H2_CLASSNAME}>
{title}
</h2>
<div className={`${KEY_INSIGHTS_CLASS_NAME}`}>
<div className="block-wrapper">
<KeyInsightsThumbs titles={titles} />
</div>
<KeyInsightsSlides insights={keyInsights} />
</div>
</>
)

$block.replaceWith(rendered)
}
return $.html()
}

export const extractKeyInsights = (
$: CheerioStatic,
$wrapper: Cheerio,
containerPostId: number
): KeyInsight[] => {
const keyInsights: KeyInsight[] = []

for (const block of Array.from(
$wrapper.find("block[type='key-insight']")
)) {
const $block = $(block)
// restrictive children selector not strictly necessary here for now but
// kept for consistency and evolutions of the block. In the future, key
// insights could host other blocks with <title> tags
const $title = $block.find("> title")
const title = $title.text()
const isTitleHidden = $title.attr("is-hidden") === "1"
const slug = $block.find("> slug").text()
const content = $block.find("> content").html()

// "!content" is taken literally here. An empty paragraph will return
// "\n\n<p></p>\n\n" and will not trigger an error. This can be seen
// both as an unexpected behaviour or a feature, depending on the stage
// of work (published or WIP).
if (!title || !slug || !content) {
void logErrorAndMaybeSendToBugsnag(
new JsonError(
`Missing title, slug or content for key insight ${
title || slug
}, content removed in wordpress post with id ${containerPostId}.`
)
)
continue
}

keyInsights.push({ title, isTitleHidden, content, slug })
}

return keyInsights
}
20 changes: 12 additions & 8 deletions db/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,21 +159,25 @@ export const knexRawInsert = async (
/**
* In the backporting workflow, the users create gdoc posts for posts. As long as these are not yet published,
* we still want to bake them from the WP posts. Once the users presses publish there though, we want to stop
* baking them from the wordpress post. This funciton fetches all the slugs of posts that have been published via gdocs,
* to help us exclude them from the baking process.
* baking them from the wordpress post. This function fetches all the slugs of posts that have been published via gdocs,
* to help us exclude them from the baking process. This query used to rely on the gdocSuccessorId field but that fell out of sync.
*/
export const getSlugsWithPublishedGdocsSuccessors = async (
knex: KnexReadonlyTransaction
): Promise<Set<string>> => {
return knexRaw(
knex,
`-- sql
SELECT
slug
FROM
posts_with_gdoc_publish_status
WHERE
isGdocPublished = TRUE`
SELECT
p.slug
FROM
posts p
LEFT JOIN posts_gdocs g on
p.slug = g.slug
WHERE
p.status = "publish"
AND g.published = TRUE
`
).then((rows) => new Set(rows.map((row: any) => row.slug)))
}

Expand Down
7 changes: 0 additions & 7 deletions packages/@ourworldindata/types/src/domainTypes/Site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,3 @@ export interface BreadcrumbItem {
export interface KeyValueProps {
[key: string]: string | boolean | undefined
}

export interface KeyInsight {
title: string
isTitleHidden?: boolean
content: string
slug: string
}
7 changes: 1 addition & 6 deletions packages/@ourworldindata/types/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,7 @@ export {
type UserCountryInformation,
type QueryParams,
} from "./domainTypes/Various.js"
export {
type BreadcrumbItem,
type KeyInsight,
type KeyValueProps,
} from "./domainTypes/Site.js"
export { type BreadcrumbItem, type KeyValueProps } from "./domainTypes/Site.js"
export {
type FormattedPost,
type IndexPost,
Expand Down Expand Up @@ -129,7 +125,6 @@ export {
} from "./domainTypes/ContentGraph.js"
export {
WP_BlockClass,
WP_BlockType,
WP_ColumnStyle,
WP_PostType,
type PostRestApi,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,6 @@ export enum WP_BlockClass {
FullContentWidth = "wp-block-full-content-width", // not an actual WP block yet
}

export enum WP_BlockType {
AllCharts = "all-charts",
}

export interface FormattingOptions {
toc?: boolean
hideAuthors?: boolean
Expand Down
1 change: 0 additions & 1 deletion site/LongFormPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,6 @@ export const LongFormPage = (props: {
pageTitle,
// hideSubheadings: true
})})
runRelatedCharts(${JSON.stringify(post.relatedCharts)})
`,
}}
/>
Expand Down
Loading