Compare commits

..

1 Commits

Author SHA1 Message Date
Nick Stokoe
d78b403dff unpack-whenwe-json.js - convert html to markdown 2022-12-27 18:16:27 +00:00
379 changed files with 70 additions and 16389 deletions

View File

@@ -1,22 +0,0 @@
# Cross-platform formatting config
# See https://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
[{*.{js,json,css,scss,html,jsx},.babelrc}]
# Set default charset
charset = utf-8
# 4 space indentation
indent_style = space
indent_size = 2
[*.{yml,yaml}]
indent_style = space
indent_size = 2

3
.gitignore vendored
View File

@@ -1,3 +1,2 @@
/out/ /out/
/node_modules/ /node_modules/
/_site/

View File

@@ -1,53 +0,0 @@
# WhenWe
Whenwe is dead. Long love Whenwe?
A "when-we" is a nostagic reminisce. This is an archive of when-wes, currently.
However, it may grow into a database of mini-biographies. Time will tell...
## Structure
- `src/` - the website content and templates
- `bin/` - helper scripts
Everything in the top directory are configs and documentation, like this.
## Requirements
The site is built using [Eleventy][eleventy]. We assume you have Git, NodeJS and NPM installed.
## Installation for development or deployment
git clone $whenwe_repo_url
cd whenwe
npm install
## Development
npm run server
Now you should be able to visit the development site in your browser at http://localhost:8080
If you edit the content in `src/` - it should rebuild the site. Your browser should refresh automatically, but if not, refresh it manually.
See the [README.md](./src/) in `src/` for more information.
## Deployment
Currently the destination is configured in package.json, as part of the definition of the `deploy` run-script. To change the destination, change that.
`rsync` is used for deploying the site. Therefore you need to have that installed and on the path.
You will need `ssh` access to the destination. Setting that up is outside the scope of this document, but if you have your own web space, you will probably know about this already, and if not, you will need the assistance of someone who does. Setting up your ssh client depends on your OS - there are guides online.
But given that, the deploy process goes like this:
npm run build
npm run deploy
## Issues and questions
These can be submitted via the issue tracker attached to this repository. You may need to create an account.
[eleventy]: https://www.11ty.dev/

View File

@@ -4,17 +4,11 @@
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"build": "rm -rf _site && eleventy --pathprefix '~nick/whenwe/'",
"deploy": "bin/deploy nick@mixian.noodlefactory.co.uk:public_html/whenwe/",
"server": "eleventy --serve",
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"node-html-markdown": "^1.3.0" "node-html-markdown": "^1.3.0"
},
"devDependencies": {
"@11ty/eleventy": "^1.0.2"
} }
} }

View File

@@ -1,328 +0,0 @@
const data = require("./whenwe-tidied.json");
const fs = require("fs");
//const sh = require("sanitize-html");
const {
NodeHtmlMarkdown,
NodeHtmlMarkdownOptions,
} = require("node-html-markdown");
const nhm = new NodeHtmlMarkdown({
useLinkReferenceDefinitions: true,
useInlineLinks: true,
});
function date(datestr) {
if (datestr) {
const [Y, M, D, h, m, s] = datestr.split(/[^0-9]/);
return new Date(Date.UTC.call(null, Y, M - 1, D, h, m, s)).toISOString();
} else return "";
}
const tag_index = {
// 1: { id: 1, name: "", slug: "" },
};
const user_index = {
"6975f732f0a00f00018346d1": {
id: "6975f732f0a00f00018346d1",
name: "Janet Woolley",
slug: "janet",
},
};
const meta_index = {};
const author_index = {};
const ghost_data = {
meta: {
exported_on: new Date().valueOf(),
version: "5.0.0", // Ghost version the import is valid for
},
data: {
posts: [],
// Optionally define post metadata
posts_meta: [
/*
{
post_id: "1234", // This must be the same as the post it references
feature_image_alt: "A group of people waving at the camera",
feature_image_caption: "The team says hello!",
},
*/
],
// Define the tags
tags: [],
// Relate posts to tags
posts_tags: [],
// Define the users
/*
users: [
{
id: "5678", // Unique ID for this author
name: "Jo Bloggs",
slug: "jo-blogs",
email: "jo@example.com",
profile_image: "/content/images/2025/scenic-background.jpg",
roles: [
"Contributor", // Contributor | Author| Editor | Administrator
],
},
],
*/
// Relate posts to authors
},
};
function convertCase(
str, //: string,
format, // 'camel' | 'pascal' | 'snake' | 'kebab'
) {
const sanitiseString = (str) =>
str
.trim()
.replace(/[^a-zA-Z0-9\s]/g, "")
.replace(/\s+/g, " ");
const formatted = sanitiseString(str);
switch (format) {
case "camel":
return formatted
.toLowerCase()
.replace(/ (\\w)/g, (_, char) => char.toUpperCase());
case "pascal":
return formatted.replace(/(?:^| )(\w)/g, (_, char) => char.toUpperCase());
case "snake":
return formatted.toLowerCase().replace(/\s+/g, "_");
case "kebab":
return formatted.toLowerCase().replace(/\s+/g, "-");
default:
throw new Error("Unsupported format type");
}
}
function mk_tag(name, id, slug) {
id ??= convertCase(name, "kebab");
slug ??= id;
tag_index[id] ??= { id, name, slug };
return id;
}
function mk_author(post_id, author_id) {
author_index[post_id] = { post_id, author_id };
}
function mk_meta(post_id, feature_image_caption, feature_image_alt) {
if (feature_image_alt || feature_image_caption)
meta_index[post_id] = {
feature_image_alt,
feature_image_caption,
post_id,
};
}
function img_path(filename) {
if (!filename) throw new Error("No filename");
return "content/images/" + filename.trim();
}
function img(filename, height, width, title, alt) {
if (!filename) return undefined;
if (typeof filename !== "string")
throw new Error("not a string: " + filename);
return {
row: 0,
src: img_path(filename),
width: width ?? 100,
height: height ?? 100,
filename: filename,
};
}
function sanitize(body) {
return nhm
.translate(body)
.replaceAll(/https?:\/[^"]*?\/public\//g, "content/images/2026/01/")
.replaceAll(/[?]itok=[A-Za-z0-9_-]*/g, "");
}
function sanitize_html(body) {
return body
.replaceAll(/https?:\/[^"]*?\/public\//g, "content/images/2026/01/")
.replaceAll(/[?]itok=[^ ]*/g, "");
}
for (const node of data) {
let body = sanitize_html(node.body.und[0].safe_value);
const id = Number(node.nid);
const lexical = {
root: {
children: [],
direction: "ltr",
format: "",
indent: 0,
type: "root",
version: 1,
},
};
let author = node.field_original_author?.und?.[0]?.value;
let feature_image = node.field_featured_image?.und?.[0]?.filename;
let tags = [];
mk_meta(
id,
node.field_featured_image?.und?.[0]?.title,
node.field_featured_image?.und?.[0]?.alt,
);
mk_author(id, "6975f732f0a00f00018346d1");
const category_id = node.field_category?.und?.[0]?.tid;
if (category_id) {
tags.push(
mk_tag(
"category-" + category_id,
"Category " + category_id,
"category-" + category_id,
),
);
}
/*
lexical.root.children.push({
children: [
{
type: "markdown",
version: 1,
markdown: sanitize(body),
},
],
direction: "ltr",
format: "",
indent: 0,
type: "paragraph",
version: 1,
});
*/
switch (node.type) {
case "article":
{
tags.push(mk_tag("Story", "story", "story"));
let images = node.field_basic_image_image?.und;
if (images) {
// console.error(">>", images);
/*
images = images.map((image) =>
img(image.filename, img.height, image.width, img.title, img.alt),
);
*/
images = images.map(
(image) =>
`
<div class="kg-gallery-image">
<img src="${img_path(image.filename)}" width="${image.width}" height="${image.height}" loading="lazy" alt="${image.alt}" title="${image.title}">
</div>
`,
);
body += `
<hr>
<figure class="kg-card kg-gallery-card kg-width-wide">
<div class="kg-gallery-container">
<div class="kg-gallery-row">
${images.join("")}
</div>
</div>
<figcaption></figcaption>
</figure>
`;
/*
lexical.root.children.push({
type: "gallery",
version: 1,
images,
caption: "",
});
*/
}
}
break;
case "person":
{
const surname_at_birth = node.field_surname_at_birth?.und?.[0]?.value;
const other_surnames = node.field_other_surnames?.und?.[0]?.value;
tags.push(mk_tag("Person", "person", "person"));
if (surname_at_birth) {
tags.push(
mk_tag(
surname_at_birth,
"surname-" + convertCase(surname_at_birth, "kebab"),
),
);
}
if (other_surnames) {
tags.push(
mk_tag(
other_surnames,
"surname-" + convertCase(other_surnames, "kebab"),
),
);
}
/*
forename_at_birth: node.field_forename_at_birth?.und?.[0]?.value,
other_forenames: node.field_other_forenames?.und?.[0]?.value,
title: node.field_title?.und?.[0]?.value,
date_of_birth: date(node.field_date_of_birth?.und?.[0]?.value),
date_of_death: date(node.field_date_of_death?.und?.[0]?.value),
parent_of: node.field_parent_of?.und?.[0]?.value,
child_of: node.field_child_of?.und?.[0]?.value,
partner_of: node.field_partner_of?.und?.[0]?.value,
// lifetime: node.field_lifetime?.und?.[0]?.value,
*/
}
break;
}
for (const tag_id of tags) {
ghost_data.data.posts_tags.push({
post_id: id,
tag_id,
});
}
ghost_data.data.posts.push({
id,
type: "post",
title: node.title,
slug: node.path.alias.replace(/^.*[/]/, ""),
html: body,
feature_image: img(feature_image)?.src,
created_at: new Date(Number(node.created) * 1000).toISOString(),
updated_at: new Date(Number(node.changed) * 1000).toISOString(),
status: "draft",
});
// const author = node.field_original_author?.und?.[0]?.value;
/*
{
id: "1234", // The post ID, which is refered to in other places in this file
title: "My Blog Post Title",
slug: "my-blog-post-title",
html: "<p>Hello world, this is an article</p>", // You could use `lexical` instead to to represent your content
comment_id: "1234-old-cms-post-id", // The ID from the old CMS, which can be output in the theme
feature_image: "/content/images/2024/waving.jpg",
type: "post", // post | page
status: "published", // published | draft
visibility: "public", // public | members | paid
created_at: "2025-06-30 15:31:36",
updated_at: "2025-07-02 08:22:14",
published_at: "2025-06-30 15:35:36",
custom_excerpt: "My custom excerpt",
},*/
}
ghost_data.data.tags = Object.values(tag_index);
ghost_data.data.posts_authors = Object.values(author_index);
ghost_data.data.posts_meta = Object.values(meta_index);
console.log(JSON.stringify(ghost_data, null, 2));

69
unpack-whenwe-json.js Normal file
View File

@@ -0,0 +1,69 @@
const data = require('./whenwe.json');
const fs = require('fs');
const path = require('path');
const { NodeHtmlMarkdown, NodeHtmlMarkdownOptions } = require('node-html-markdown');
const nhm = new NodeHtmlMarkdown();
function toYaml(data, body) {
const frontmatter = Object.keys(data).sort().map(key => key+': '+(data[key] ?? '')).join("\n");
return frontmatter + "\n---\n" + nhm.translate(body);
}
function date(datestr) {
if (datestr) {
const [Y,M,D,h,m,s] = datestr.split(/[^0-9]/)
return new Date(Date.UTC.call(null, Y,M-1,D,h,m,s)).toISOString();
}
else
return '';
}
data.forEach((node, ix) => {
const lang = 'und';
const filepath = path.join('out', 'node.type');
const body = node.body.und[0].value;
const filename = `${node.uuid}.yml`;
fs.mkdirSync(path.join(__dirname, filepath), { recursive: true });
const item = {
ix: ix,
nid: Number(node.nid),
type: node.type,
title: node.title,
uuid: node.uuid,
created: new Date(Number(node.created)*1000).toISOString(),
changed: new Date(Number(node.changed)*1000).toISOString(),
path: node.path.alias,
comment_count: node.comment_count,
};
switch(node.type) {
case 'article':
Object.assign(item, {
original_author: node.field_original_author?.und?.[0]?.value,
featured_image: node.field_featured_image?.und?.[0]?.filename,
images: node.field_basic_image_image?.und?.map(item => item.filename),
category: node.field_category?.und?.[0]?.tid,
});
break;
case 'person':
Object.assign(item, {
forename_at_birth: node.field_forename_at_birth?.und?.[0]?.value,
surname_at_birth: node.field_surname_at_birth?.und?.[0]?.value,
other_surnames: node.field_other_surnames?.und?.[0]?.value,
other_forenames: node.field_other_forenames?.und?.[0]?.value,
title: node.field_title?.und?.[0]?.value,
date_of_birth: date(node.field_date_of_birth?.und?.[0]?.value),
date_of_death: date(node.field_date_of_death?.und?.[0]?.value),
parent_of: node.field_parent_of?.und?.[0]?.value,
child_of: node.field_child_of?.und?.[0]?.value,
partner_of: node.field_partner_of?.und?.[0]?.value,
// lifetime: node.field_lifetime?.und?.[0]?.value,
featured_image: node.field_featured_image?.und?.[0]?.filename,
});
break;
}
fs.writeFileSync(path.join(filepath, filename), toYaml(item, body));
console.log(item.title);
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 829 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 931 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 769 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 813 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 670 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 802 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 693 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 631 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 945 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 840 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 662 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 866 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 601 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 581 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 601 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 662 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 908 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 807 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 717 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 911 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 627 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 695 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 625 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 830 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 943 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 732 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 994 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 732 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 699 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 668 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 743 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 614 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 628 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 985 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 976 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 846 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 829 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 792 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 878 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 741 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 805 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 705 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 730 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 896 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 999 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 826 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 707 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 651 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 996 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 673 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 855 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1018 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 980 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 994 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 938 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 757 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 831 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 563 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 651 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 821 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 562 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 431 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 801 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 583 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 758 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 745 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 497 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 776 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 830 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 797 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 663 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 612 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 618 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 941 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1001 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 670 KiB

Some files were not shown because too many files have changed in this diff Show More