From 2d32a9129fc6e8ebde4d8e0797aca74789f7077f Mon Sep 17 00:00:00 2001 From: Paul Robert Lloyd Date: Fri, 29 Mar 2024 22:48:09 +0000 Subject: [PATCH 01/12] fix(endpoint-micropub): prevent slug property from being deleted --- packages/endpoint-micropub/lib/post-data.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/endpoint-micropub/lib/post-data.js b/packages/endpoint-micropub/lib/post-data.js index f5e01eb26..8d4cf7890 100644 --- a/packages/endpoint-micropub/lib/post-data.js +++ b/packages/endpoint-micropub/lib/post-data.js @@ -198,7 +198,7 @@ export const postData = { // Delete all properties, except those required for path creation for (const key in _deletedProperties) { - if (!["mp-slug", "post-type", "published", "type", "url"].includes(key)) { + if (!["post-type", "published", "slug", "type", "url"].includes(key)) { delete properties[key]; } } From 0d5eb3df49e6947b652c61225ecfe26db58731ec Mon Sep 17 00:00:00 2001 From: Paul Robert Lloyd Date: Fri, 29 Mar 2024 23:10:49 +0000 Subject: [PATCH 02/12] feat(endpoint-micropub): revert decoupling slug post property from slug token This reverts commit 306596a4ccf39e78f9d493fa51f3a6ce05c4d3ec. --- .../fixtures/jf2/article-content-provided.jf2 | 3 +- .../jf2/article-slug-provided-empty.jf2 | 6 +++ .../jf2/note-slug-missing-no-name.jf2 | 4 ++ .../jf2/note-slug-provided-unslugified.jf2 | 5 ++ helpers/fixtures/jf2/note-slug-provided.jf2 | 5 ++ packages/endpoint-micropub/lib/jf2.js | 39 +++++++++++++--- packages/endpoint-micropub/lib/utils.js | 30 ++---------- packages/endpoint-micropub/test/unit/jf2.js | 46 ++++++++++++++++++- .../endpoint-micropub/test/unit/post-data.js | 2 +- packages/endpoint-micropub/test/unit/utils.js | 32 ------------- 10 files changed, 103 insertions(+), 69 deletions(-) create mode 100644 helpers/fixtures/jf2/article-slug-provided-empty.jf2 create mode 100644 helpers/fixtures/jf2/note-slug-missing-no-name.jf2 create mode 100644 helpers/fixtures/jf2/note-slug-provided-unslugified.jf2 create mode 100644 helpers/fixtures/jf2/note-slug-provided.jf2 diff --git a/helpers/fixtures/jf2/article-content-provided.jf2 b/helpers/fixtures/jf2/article-content-provided.jf2 index f76d05ad7..3c78b3ff7 100644 --- a/helpers/fixtures/jf2/article-content-provided.jf2 +++ b/helpers/fixtures/jf2/article-content-provided.jf2 @@ -1,6 +1,5 @@ { "type": "entry", "name": "What I had for lunch", - "content": "> I atehad a [cheese](https://en.wikipedia.org/wiki/Cheese) sandwich from https://cafe.example, which was > 10.\n\n-- Me, then.", - "mp-slug": "lunch" + "content": "> I atehad a [cheese](https://en.wikipedia.org/wiki/Cheese) sandwich from https://cafe.example, which was > 10.\n\n-- Me, then." } diff --git a/helpers/fixtures/jf2/article-slug-provided-empty.jf2 b/helpers/fixtures/jf2/article-slug-provided-empty.jf2 new file mode 100644 index 000000000..092afe4a5 --- /dev/null +++ b/helpers/fixtures/jf2/article-slug-provided-empty.jf2 @@ -0,0 +1,6 @@ +{ + "type": "entry", + "name": "What I had for lunch", + "content": "I ate a *cheese* sandwich, which was nice.", + "mp-slug": "" +} diff --git a/helpers/fixtures/jf2/note-slug-missing-no-name.jf2 b/helpers/fixtures/jf2/note-slug-missing-no-name.jf2 new file mode 100644 index 000000000..c6375e3af --- /dev/null +++ b/helpers/fixtures/jf2/note-slug-missing-no-name.jf2 @@ -0,0 +1,4 @@ +{ + "type": "entry", + "content": "I ate a cheese sandwich, which was nice." +} diff --git a/helpers/fixtures/jf2/note-slug-provided-unslugified.jf2 b/helpers/fixtures/jf2/note-slug-provided-unslugified.jf2 new file mode 100644 index 000000000..40e0389e6 --- /dev/null +++ b/helpers/fixtures/jf2/note-slug-provided-unslugified.jf2 @@ -0,0 +1,5 @@ +{ + "type": "entry", + "content": "I ate a cheese sandwich, which was nice.", + "mp-slug": "Cheese sandwich" +} diff --git a/helpers/fixtures/jf2/note-slug-provided.jf2 b/helpers/fixtures/jf2/note-slug-provided.jf2 new file mode 100644 index 000000000..afaec4589 --- /dev/null +++ b/helpers/fixtures/jf2/note-slug-provided.jf2 @@ -0,0 +1,5 @@ +{ + "type": "entry", + "content": "I ate a cheese sandwich, which was nice.", + "mp-slug": "cheese-sandwich" +} diff --git a/packages/endpoint-micropub/lib/jf2.js b/packages/endpoint-micropub/lib/jf2.js index 145215c3b..9c9993a8f 100644 --- a/packages/endpoint-micropub/lib/jf2.js +++ b/packages/endpoint-micropub/lib/jf2.js @@ -1,9 +1,14 @@ -import { getDate } from "@indiekit/util"; +import { getDate, randomString, slugify } from "@indiekit/util"; import { mf2tojf2, mf2tojf2referenced } from "@paulrobertlloyd/mf2tojf2"; import { fetchReferences } from "@paulrobertlloyd/mf2tojf2"; import { markdownToHtml, htmlToMarkdown } from "./markdown.js"; import { reservedProperties } from "./reserved-properties.js"; -import { decodeQueryParameter, relativeMediaPath, toArray } from "./utils.js"; +import { + decodeQueryParameter, + excerptString, + relativeMediaPath, + toArray, +} from "./utils.js"; /** * Create JF2 object from form-encoded request @@ -63,7 +68,7 @@ export const mf2ToJf2 = async (body, requestReferences) => { * @returns {object} Normalised JF2 properties */ export const normaliseProperties = (publication, properties, timeZone) => { - const { me } = publication; + const { me, slugSeparator } = publication; properties.published = getDate(timeZone, properties.published); @@ -91,9 +96,7 @@ export const normaliseProperties = (publication, properties, timeZone) => { properties.video = getVideoProperty(properties, me); } - if (properties["mp-slug"]) { - properties.slug = properties["mp-slug"]; - } + properties.slug = getSlugProperty(properties, slugSeparator); if (properties["mp-syndicate-to"]) { properties["mp-syndicate-to"] = toArray(properties["mp-syndicate-to"]); @@ -222,6 +225,30 @@ export const getVideoProperty = (properties, me) => { })); }; +/** + * Get slug + * @param {object} properties - JF2 properties + * @param {string} separator - Slug separator + * @returns {string} Array containing slug value + */ +export const getSlugProperty = (properties, separator) => { + const suggested = properties["mp-slug"]; + const { name } = properties; + + let string; + if (suggested) { + string = suggested; + } else if (name) { + string = excerptString(name, 5); + } else { + string = randomString(5) + .replace("_", "0") // Slugify function strips any leading underscore + .replace(separator, "0"); // Don’t include slug separator character + } + + return slugify(string, { separator }); +}; + /** * Get `mp-syndicate-to` property * @param {object} properties - JF2 properties diff --git a/packages/endpoint-micropub/lib/utils.js b/packages/endpoint-micropub/lib/utils.js index ec64369ce..c465ffb28 100644 --- a/packages/endpoint-micropub/lib/utils.js +++ b/packages/endpoint-micropub/lib/utils.js @@ -4,7 +4,6 @@ import { getTimeZoneDesignator, isDate, randomString, - slugify, supplant, } from "@indiekit/util"; import newbase60 from "newbase60"; @@ -43,29 +42,6 @@ export const excerptString = (string, n) => { } }; -/** - * Get slug - * @param {object} properties - JF2 properties - * @param {string} separator - Slug separator - * @returns {string} Slug - */ -export const getSlug = (properties, separator) => { - const { name, slug } = properties; - - let string; - if (slug) { - string = slug; - } else if (name) { - string = excerptString(name, 5); - } else { - string = randomString(5) - .replace("_", "0") // Slugify function strips any leading underscore - .replace(separator, "0"); // Don’t use slug separator character - } - - return slugify(string, { separator }); -}; - /** * Get post template properties * @param {object} properties - JF2 properties @@ -134,12 +110,12 @@ export const renderPath = async (path, properties, application, separator) => { .replace(separator, "0") // Don’t use slug separator character .toLowerCase(); + // Add slug token + tokens.slug = properties.slug; + // Add UUID token tokens.uuid = crypto.randomUUID(); - // Add slug token (falls back to name or random if no `mp-slug`) - tokens.slug = getSlug(properties, separator); - // Populate URI template path with properties path = supplant(path, tokens); diff --git a/packages/endpoint-micropub/test/unit/jf2.js b/packages/endpoint-micropub/test/unit/jf2.js index 3771b8044..d7bf0997b 100644 --- a/packages/endpoint-micropub/test/unit/jf2.js +++ b/packages/endpoint-micropub/test/unit/jf2.js @@ -10,6 +10,7 @@ import { getLocationProperty, getPhotoProperty, getVideoProperty, + getSlugProperty, getSyndicateToProperty, normaliseProperties, } from "../../lib/jf2.js"; @@ -286,6 +287,49 @@ describe("endpoint-micropub/lib/jf2", () => { ]); }); + it("Derives slug from `mp-slug` property", () => { + const properties = JSON.parse(getFixture("jf2/note-slug-provided.jf2")); + const result = getSlugProperty(properties, "-"); + + assert.equal(result, "cheese-sandwich"); + }); + + it("Derives slug from unslugified `mp-slug` property", () => { + const properties = JSON.parse( + getFixture("jf2/note-slug-provided-unslugified.jf2"), + ); + const result = getSlugProperty(properties, "-"); + + assert.equal(result, "cheese-sandwich"); + }); + + it("Derives slug, ignoring empty `mp-slug` property", () => { + const properties = JSON.parse( + getFixture("jf2/article-slug-provided-empty.jf2"), + ); + const result = getSlugProperty(properties, "-"); + + assert.equal(result, "what-i-had-for-lunch"); + }); + + it("Derives slug from `name` property", () => { + const properties = JSON.parse( + getFixture("jf2/article-content-provided-text.jf2"), + ); + const result = getSlugProperty(properties, "-"); + + assert.equal(result, "what-i-had-for-lunch"); + }); + + it("Derives slug by generating random string", () => { + const properties = JSON.parse( + getFixture("jf2/note-slug-missing-no-name.jf2"), + ); + const result = getSlugProperty(properties, "-"); + + assert.match(result, /\w{5}/g); + }); + it("Does not add syndication target if no syndicators", () => { const properties = JSON.parse( getFixture("jf2/article-syndicate-to-provided.jf2"), @@ -358,7 +402,7 @@ describe("endpoint-micropub/lib/jf2", () => { assert.equal(result.type, "entry"); assert.equal(result.name, "What I had for lunch"); - assert.equal(result.slug, "lunch"); + assert.equal(result.slug, "what-i-had-for-lunch"); assert.deepEqual(result.content, { html: `
\n

I atehad a cheese sandwich from https://cafe.example, which was > 10.

\n
\n

– Me, then.

`, text: "> I atehad a [cheese](https://en.wikipedia.org/wiki/Cheese) sandwich from https://cafe.example, which was > 10.\n\n-- Me, then.", diff --git a/packages/endpoint-micropub/test/unit/post-data.js b/packages/endpoint-micropub/test/unit/post-data.js index f26657ef1..5a1475e5e 100644 --- a/packages/endpoint-micropub/test/unit/post-data.js +++ b/packages/endpoint-micropub/test/unit/post-data.js @@ -52,7 +52,7 @@ describe("endpoint-micropub/lib/post-data", async () => { const result = await postData.create(application, publication, properties); assert.equal(result.properties["post-type"], "note"); - assert.equal(result.properties["mp-slug"], "foo"); + assert.equal(result.properties.slug, "foo"); assert.equal(result.properties.type, "entry"); assert.equal(result.properties.url, "https://website.example/notes/foo/"); }); diff --git a/packages/endpoint-micropub/test/unit/utils.js b/packages/endpoint-micropub/test/unit/utils.js index 34d229cb1..67e05da5b 100644 --- a/packages/endpoint-micropub/test/unit/utils.js +++ b/packages/endpoint-micropub/test/unit/utils.js @@ -3,7 +3,6 @@ import { before, describe, it, mock } from "node:test"; import { decodeQueryParameter, excerptString, - getSlug, getPostTemplateProperties, relativeMediaPath, renderPath, @@ -35,37 +34,6 @@ describe("endpoint-media/lib/utils", () => { assert.equal(result, "The quick fox jumped over"); }); - it("Derives slug from `slug` property", () => { - const properties = { slug: "cheese-sandwich" }; - const result = getSlug(properties, "-"); - - assert.equal(result, "cheese-sandwich"); - }); - - it("Derives slug from `name` property", () => { - const properties = { name: "Cheese sandwich" }; - const result = getSlug(properties, "-"); - - assert.equal(result, "cheese-sandwich"); - }); - - it("Derives slug, ignoring empty `slug` property", () => { - const properties = { - name: "What I had for lunch", - slug: "", - }; - const result = getSlug(properties, "-"); - - assert.equal(result, "what-i-had-for-lunch"); - }); - - it("Derives slug by generating random string", () => { - const properties = { content: "I ate a cheese sandwich, which was nice." }; - const result = getSlug(properties, "-"); - - assert.match(result, /\w{5}/g); - }); - it("Gets post template properties", () => { const result = getPostTemplateProperties({ name: "foo", From b72424c229b11639da43620bd3680a5052e7d016 Mon Sep 17 00:00:00 2001 From: Paul Robert Lloyd Date: Fri, 29 Mar 2024 23:12:17 +0000 Subject: [PATCH 03/12] fix(endpoint-posts): get saved slug property --- packages/endpoint-posts/views/post-form.njk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/endpoint-posts/views/post-form.njk b/packages/endpoint-posts/views/post-form.njk index e03c09596..1eca35974 100644 --- a/packages/endpoint-posts/views/post-form.njk +++ b/packages/endpoint-posts/views/post-form.njk @@ -81,7 +81,7 @@ {{ input({ classes: "input--width-25", name: "mp-slug", - value: fieldData("mp-slug").value, + value: fieldData("slug").value, label: __("posts.form.mp-slug.label"), optional: true }) | indent(4) }} From 9e61b2bc93e05f0f394d48d866ee1fdb315fada7 Mon Sep 17 00:00:00 2001 From: Paul Robert Lloyd Date: Sat, 30 Mar 2024 13:54:20 +0000 Subject: [PATCH 04/12] feat(endpoint-media): remove random token --- packages/endpoint-media/lib/utils.js | 6 ------ packages/endpoint-media/test/unit/utils.js | 2 -- 2 files changed, 8 deletions(-) diff --git a/packages/endpoint-media/lib/utils.js b/packages/endpoint-media/lib/utils.js index 51b11b67a..c43e5fcd5 100644 --- a/packages/endpoint-media/lib/utils.js +++ b/packages/endpoint-media/lib/utils.js @@ -3,7 +3,6 @@ import { dateTokens, formatDate, getTimeZoneDesignator, - randomString, slugify, supplant, } from "@indiekit/util"; @@ -56,11 +55,6 @@ export const renderPath = async (path, properties, application, separator) => { const count = await mediaTypeCount.get(application, properties); tokens.n = count + 1; - // Add random token - tokens.random = randomString(5) - .replace(separator, "0") // Don’t use slug separator character - .toLowerCase(); - // Add UUID token tokens.uuid = crypto.randomUUID(); diff --git a/packages/endpoint-media/test/unit/utils.js b/packages/endpoint-media/test/unit/utils.js index df1611717..7bb27f403 100644 --- a/packages/endpoint-media/test/unit/utils.js +++ b/packages/endpoint-media/test/unit/utils.js @@ -19,13 +19,11 @@ describe("endpoint-media/lib/util", () => { const dateToken = await renderPath("{yyyy}/{MM}", properties, {}, "-"); const fileToken = await renderPath("{filename}", properties, {}, "-"); const uuidToken = await renderPath("{uuid}", properties, {}, "-"); - const randomToken = await renderPath("{random}", properties, {}, "-"); const md5Token = await renderPath("{md5}", properties, {}, "-"); assert.match(dateToken, /^\d{4}\/\d{2}/); assert.match(fileToken, /^foo-1\.jpg/); assert.match(uuidToken, /^[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}/); - assert.match(randomToken, /^\w{5}/); assert.match(md5Token, /^([\da-f]{32}|[\dA-F]{32})$/); }); }); From 0812023db94ab39143f34136820fb486a5eb30ae Mon Sep 17 00:00:00 2001 From: Paul Robert Lloyd Date: Sat, 30 Mar 2024 14:01:39 +0000 Subject: [PATCH 05/12] feat(endpoint-micropub): remove random token --- packages/endpoint-micropub/lib/post-data.js | 20 +++++-------------- packages/endpoint-micropub/lib/utils.js | 9 +-------- packages/endpoint-micropub/test/unit/utils.js | 6 +++--- 3 files changed, 9 insertions(+), 26 deletions(-) diff --git a/packages/endpoint-micropub/lib/post-data.js b/packages/endpoint-micropub/lib/post-data.js index 8d4cf7890..f2127c92e 100644 --- a/packages/endpoint-micropub/lib/post-data.js +++ b/packages/endpoint-micropub/lib/post-data.js @@ -22,7 +22,7 @@ export const postData = { debug(`create %O`, { draftMode, properties }); const { hasDatabase, posts, timeZone } = application; - const { me, postTypes, slugSeparator, syndicationTargets } = publication; + const { me, postTypes, syndicationTargets } = publication; // Add syndication targets const syndicateTo = getSyndicateToProperty(properties, syndicationTargets); @@ -48,14 +48,8 @@ export const postData = { typeConfig.post.path, properties, application, - slugSeparator, - ); - const url = await renderPath( - typeConfig.post.url, - properties, - application, - slugSeparator, ); + const url = await renderPath(typeConfig.post.url, properties, application); properties.url = getCanonicalUrl(url, me); // Post status @@ -108,7 +102,7 @@ export const postData = { debug(`update ${url} %O`, { operation }); const { posts, timeZone } = application; - const { me, postTypes, slugSeparator } = publication; + const { me, postTypes } = publication; // Read properties let { path: _originalPath, properties } = await this.read(application, url); @@ -149,13 +143,11 @@ export const postData = { typeConfig.post.path, properties, application, - slugSeparator, ); const updatedUrl = await renderPath( typeConfig.post.url, properties, application, - slugSeparator, ); properties.url = getCanonicalUrl(updatedUrl, me); @@ -188,7 +180,7 @@ export const postData = { debug(`delete ${url}`); const { posts, timeZone } = application; - const { postTypes, slugSeparator } = publication; + const { postTypes } = publication; // Read properties const { properties } = await this.read(application, url); @@ -215,7 +207,6 @@ export const postData = { typeConfig.post.path, properties, application, - slugSeparator, ); // Update data in posts collection @@ -240,7 +231,7 @@ export const postData = { debug(`undelete ${url} %O`, { draftMode }); const { posts } = application; - const { postTypes, slugSeparator } = publication; + const { postTypes } = publication; // Read deleted properties const { _deletedProperties } = await this.read(application, url); @@ -257,7 +248,6 @@ export const postData = { typeConfig.post.path, properties, application, - slugSeparator, ); // Post status diff --git a/packages/endpoint-micropub/lib/utils.js b/packages/endpoint-micropub/lib/utils.js index c465ffb28..d04a48c0d 100644 --- a/packages/endpoint-micropub/lib/utils.js +++ b/packages/endpoint-micropub/lib/utils.js @@ -3,7 +3,6 @@ import { formatDate, getTimeZoneDesignator, isDate, - randomString, supplant, } from "@indiekit/util"; import newbase60 from "newbase60"; @@ -79,10 +78,9 @@ export const relativeMediaPath = (url, me) => * @param {string} path - URI template path * @param {object} properties - JF2 properties * @param {object} application - Application configuration - * @param {string} separator - Slug separator * @returns {Promise} Path */ -export const renderPath = async (path, properties, application, separator) => { +export const renderPath = async (path, properties, application) => { const dateObject = new Date(properties.published); const serverTimeZone = getTimeZoneDesignator(); const { locale, timeZone } = application; @@ -105,11 +103,6 @@ export const renderPath = async (path, properties, application, separator) => { const count = await postTypeCount.get(application, properties); tokens.n = count + 1; - // Add random token - tokens.random = randomString(5) - .replace(separator, "0") // Don’t use slug separator character - .toLowerCase(); - // Add slug token tokens.slug = properties.slug; diff --git a/packages/endpoint-micropub/test/unit/utils.js b/packages/endpoint-micropub/test/unit/utils.js index 67e05da5b..a6a4c36db 100644 --- a/packages/endpoint-micropub/test/unit/utils.js +++ b/packages/endpoint-micropub/test/unit/utils.js @@ -62,7 +62,7 @@ describe("endpoint-media/lib/utils", () => { }); it("Renders path from URI template and properties", async () => { - const template = "{yyyy}/{MM}/{uuid}/{random}/{slug}"; + const template = "{yyyy}/{MM}/{uuid}/{slug}"; const properties = { published: "2020-01-01", slug: "foo", @@ -83,11 +83,11 @@ describe("endpoint-media/lib/utils", () => { }, }, }; - const result = await renderPath(template, properties, application, "-"); + const result = await renderPath(template, properties, application); assert.match( result, - /\d{4}\/\d{2}\/[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}\/\w{5}\/foo/, + /\d{4}\/\d{2}\/[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}\/foo/, ); }); From c72cfb16d8f33cd61bad310752a581a4f547d5a2 Mon Sep 17 00:00:00 2001 From: Paul Robert Lloyd Date: Sat, 30 Mar 2024 14:33:32 +0000 Subject: [PATCH 06/12] feat(endpoint-media): remove uuid token --- packages/endpoint-media/lib/utils.js | 3 --- packages/endpoint-media/test/unit/utils.js | 2 -- 2 files changed, 5 deletions(-) diff --git a/packages/endpoint-media/lib/utils.js b/packages/endpoint-media/lib/utils.js index c43e5fcd5..0560fa2c8 100644 --- a/packages/endpoint-media/lib/utils.js +++ b/packages/endpoint-media/lib/utils.js @@ -55,9 +55,6 @@ export const renderPath = async (path, properties, application, separator) => { const count = await mediaTypeCount.get(application, properties); tokens.n = count + 1; - // Add UUID token - tokens.uuid = crypto.randomUUID(); - // Add file extension token tokens.ext = properties.ext; diff --git a/packages/endpoint-media/test/unit/utils.js b/packages/endpoint-media/test/unit/utils.js index 7bb27f403..532098099 100644 --- a/packages/endpoint-media/test/unit/utils.js +++ b/packages/endpoint-media/test/unit/utils.js @@ -18,12 +18,10 @@ describe("endpoint-media/lib/util", () => { it("Renders path from URI template and properties", async () => { const dateToken = await renderPath("{yyyy}/{MM}", properties, {}, "-"); const fileToken = await renderPath("{filename}", properties, {}, "-"); - const uuidToken = await renderPath("{uuid}", properties, {}, "-"); const md5Token = await renderPath("{md5}", properties, {}, "-"); assert.match(dateToken, /^\d{4}\/\d{2}/); assert.match(fileToken, /^foo-1\.jpg/); - assert.match(uuidToken, /^[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}/); assert.match(md5Token, /^([\da-f]{32}|[\dA-F]{32})$/); }); }); From 028e4ef47ea81a6b9e2041938f75c3aded373c40 Mon Sep 17 00:00:00 2001 From: Paul Robert Lloyd Date: Sat, 30 Mar 2024 14:34:53 +0000 Subject: [PATCH 07/12] feat(endpoint-micropub): remove uuid token --- packages/endpoint-micropub/lib/utils.js | 3 --- packages/endpoint-micropub/test/unit/utils.js | 7 ++----- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/endpoint-micropub/lib/utils.js b/packages/endpoint-micropub/lib/utils.js index d04a48c0d..0b8ad6875 100644 --- a/packages/endpoint-micropub/lib/utils.js +++ b/packages/endpoint-micropub/lib/utils.js @@ -106,9 +106,6 @@ export const renderPath = async (path, properties, application) => { // Add slug token tokens.slug = properties.slug; - // Add UUID token - tokens.uuid = crypto.randomUUID(); - // Populate URI template path with properties path = supplant(path, tokens); diff --git a/packages/endpoint-micropub/test/unit/utils.js b/packages/endpoint-micropub/test/unit/utils.js index a6a4c36db..1efe8b8e5 100644 --- a/packages/endpoint-micropub/test/unit/utils.js +++ b/packages/endpoint-micropub/test/unit/utils.js @@ -62,7 +62,7 @@ describe("endpoint-media/lib/utils", () => { }); it("Renders path from URI template and properties", async () => { - const template = "{yyyy}/{MM}/{uuid}/{slug}"; + const template = "{yyyy}/{MM}/{slug}"; const properties = { published: "2020-01-01", slug: "foo", @@ -85,10 +85,7 @@ describe("endpoint-media/lib/utils", () => { }; const result = await renderPath(template, properties, application); - assert.match( - result, - /\d{4}\/\d{2}\/[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}\/foo/, - ); + assert.match(result, /\d{4}\/\d{2}\/foo/); }); it("Convert string to array if not already an array", () => { From 800385f6ce1b3f97f85b3ba64d9d1db36dc4c2b1 Mon Sep 17 00:00:00 2001 From: Paul Robert Lloyd Date: Sat, 30 Mar 2024 14:36:31 +0000 Subject: [PATCH 08/12] docs: update tokens --- docs/configuration/tokens.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/configuration/tokens.md b/docs/configuration/tokens.md index 144271c9a..3bca402f9 100644 --- a/docs/configuration/tokens.md +++ b/docs/configuration/tokens.md @@ -51,8 +51,6 @@ Tokens are available for a number of file properties, with many allowing you to | `ss` | Second (zero-padded), for example `01` | | `t` | UNIX epoch seconds, for example `512969520` | | `T` | UNIX epoch milliseconds, for example `51296952000` | -| `random` | A random 5-character string, for example `w9gwi` | -| `uuid` | A [random UUID][uuid] | | `n` | Incremental count of posts (for type) in the same day, for example `1`. This token requires a [database to be configured](https://getindiekit.com/configuration/#application-mongodburl-url). | ### Post file tokens @@ -72,5 +70,3 @@ The following tokens are only available for media files: | `ext` | File extension of uploaded file, for example `jpg` | | `filename` | Slugified name of uploaded file, for example `flower_1.jpg` for a file with the original name `Flower 1.jpg`. | | `md5` | MD5 checksum of the uploaded file, for example `be7d321488de26f2eb38834af7162164` | - -[uuid]: https://www.rfc-editor.org/rfc/rfc4122.html#section-4.4 From ee17d161c39e22a9cafbbca744a89012fe335ffa Mon Sep 17 00:00:00 2001 From: Paul Robert Lloyd Date: Sat, 30 Mar 2024 15:40:13 +0000 Subject: [PATCH 09/12] refactor(endpoint-media): slugify filename when creating file properties --- packages/endpoint-media/lib/file.js | 15 ++++++++++----- packages/endpoint-media/lib/media-data.js | 6 ++---- packages/endpoint-media/lib/utils.js | 11 +++-------- packages/endpoint-media/test/unit/file.js | 9 ++++++--- packages/endpoint-media/test/unit/utils.js | 8 ++++---- 5 files changed, 25 insertions(+), 24 deletions(-) diff --git a/packages/endpoint-media/lib/file.js b/packages/endpoint-media/lib/file.js index 335b87815..297a3d076 100644 --- a/packages/endpoint-media/lib/file.js +++ b/packages/endpoint-media/lib/file.js @@ -1,26 +1,31 @@ -import { getDate } from "@indiekit/util"; +import path from "node:path"; +import { getDate, slugify } from "@indiekit/util"; import { fileTypeFromBuffer } from "file-type"; /** * Derive properties from file data - * @param {object} timeZone - Application time zone + * @param {object} publication - Publication configuration * @param {object} file - Original file object + * @param {object} timeZone - Application time zone * @returns {Promise} File properties - * @example fileData('brighton-pier.jpg') => { + * @example fileData('Brighton Pier.jpg') => { * ext: '.jpg' * filename: 'brighton-pier.jpg', * 'content-type': image/jpeg, * published: '2020-07-19T22:59:23.497Z', * } */ -export const getFileProperties = async (timeZone, file) => { +export const getFileProperties = async (publication, file, timeZone) => { const { ext } = await fileTypeFromBuffer(file.data); const published = getPublishedProperty(timeZone); + let basename = path.basename(file.name, ext); + basename = slugify(basename, publication.slugSeparator); + return { "content-type": file.mimetype, ext, - filename: file.name, + filename: `${basename}.${ext}`, md5: file.md5, published, }; diff --git a/packages/endpoint-media/lib/media-data.js b/packages/endpoint-media/lib/media-data.js index d6fccf8be..9432f9f88 100644 --- a/packages/endpoint-media/lib/media-data.js +++ b/packages/endpoint-media/lib/media-data.js @@ -18,10 +18,10 @@ export const mediaData = { debug(`create %O`, { file }); const { hasDatabase, media, timeZone } = application; - const { me, postTypes, slugSeparator } = publication; + const { me, postTypes } = publication; // Media properties - const properties = await getFileProperties(timeZone, file); + const properties = await getFileProperties(publication, file, timeZone); // Get post type configuration const type = await getMediaType(file); @@ -44,13 +44,11 @@ export const mediaData = { typeConfig.media.path, properties, application, - slugSeparator, ); const url = await renderPath( typeConfig.media.url || typeConfig.media.path, properties, application, - slugSeparator, ); properties.url = getCanonicalUrl(url, me); diff --git a/packages/endpoint-media/lib/utils.js b/packages/endpoint-media/lib/utils.js index 0560fa2c8..1f6b038be 100644 --- a/packages/endpoint-media/lib/utils.js +++ b/packages/endpoint-media/lib/utils.js @@ -1,9 +1,7 @@ -import { basename as getBasename } from "node:path"; import { dateTokens, formatDate, getTimeZoneDesignator, - slugify, supplant, } from "@indiekit/util"; import newbase60 from "newbase60"; @@ -29,10 +27,9 @@ export const getMediaProperties = (mediaData) => { * @param {string} path - URI template path * @param {object} properties - Media properties * @param {object} application - Application configuration - * @param {string} separator - Slug separator * @returns {Promise} Path */ -export const renderPath = async (path, properties, application, separator) => { +export const renderPath = async (path, properties, application) => { const dateObject = new Date(properties.published); const serverTimeZone = getTimeZoneDesignator(); const { locale, timeZone } = application; @@ -58,10 +55,8 @@ export const renderPath = async (path, properties, application, separator) => { // Add file extension token tokens.ext = properties.ext; - // Add slugified file name token - let basename = getBasename(properties.filename, properties.ext); - basename = slugify(basename, separator); - tokens.filename = `${basename}.${properties.ext}`; + // Add file name token + tokens.filename = properties.filename; // Add md5 token tokens.md5 = properties.md5; diff --git a/packages/endpoint-media/test/unit/file.js b/packages/endpoint-media/test/unit/file.js index 7d8608f8c..bf9c7283f 100644 --- a/packages/endpoint-media/test/unit/file.js +++ b/packages/endpoint-media/test/unit/file.js @@ -18,15 +18,18 @@ describe("endpoint-media/lib/file", () => { }); it("Derives properties from file data", async () => { + const publication = { + slugSeparator: "-", + }; const file = { data: getFixture("file-types/photo.jpg", false), - name: "photo.jpg", + name: "Photo 1.jpg", md5: "be7d321488de26f2eb38834af7162164", }; - const result = await getFileProperties("UTC", file); + const result = await getFileProperties(publication, file, "UTC"); assert.equal(result.ext, "jpg"); - assert.equal(result.filename, "photo.jpg"); + assert.equal(result.filename, "photo-1.jpg"); assert.equal(result.md5, "be7d321488de26f2eb38834af7162164"); assert.equal(isValid(parseISO(result.published)), true); }); diff --git a/packages/endpoint-media/test/unit/utils.js b/packages/endpoint-media/test/unit/utils.js index 532098099..6c9ebe92f 100644 --- a/packages/endpoint-media/test/unit/utils.js +++ b/packages/endpoint-media/test/unit/utils.js @@ -5,7 +5,7 @@ import { renderPath } from "../../lib/utils.js"; describe("endpoint-media/lib/util", () => { const properties = { ext: "jpg", - filename: "Foo 1.jpg", + filename: "foo-1.jpg", md5: "be7d321488de26f2eb38834af7162164", published: "2020-01-01", }; @@ -16,9 +16,9 @@ describe("endpoint-media/lib/util", () => { }); it("Renders path from URI template and properties", async () => { - const dateToken = await renderPath("{yyyy}/{MM}", properties, {}, "-"); - const fileToken = await renderPath("{filename}", properties, {}, "-"); - const md5Token = await renderPath("{md5}", properties, {}, "-"); + const dateToken = await renderPath("{yyyy}/{MM}", properties, {}); + const fileToken = await renderPath("{filename}", properties, {}); + const md5Token = await renderPath("{md5}", properties, {}); assert.match(dateToken, /^\d{4}\/\d{2}/); assert.match(fileToken, /^foo-1\.jpg/); From 99b926c4023622ffd46b619cc4fff81f5d12194e Mon Sep 17 00:00:00 2001 From: Paul Robert Lloyd Date: Fri, 29 Mar 2024 23:15:21 +0000 Subject: [PATCH 10/12] feat(preset-eleventy): remove slug property --- packages/preset-eleventy/lib/post-template.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/preset-eleventy/lib/post-template.js b/packages/preset-eleventy/lib/post-template.js index 7c21351f8..8b3049b91 100644 --- a/packages/preset-eleventy/lib/post-template.js +++ b/packages/preset-eleventy/lib/post-template.js @@ -53,6 +53,7 @@ const getFrontMatter = (properties) => { delete properties.name; // Use `title` delete properties.postStatus; // Use `draft` delete properties.published; // Use `date` + delete properties.slug; // use `page.fileSlug` delete properties.type; // Not required delete properties.url; // Not required From 309514a245afde8b707a6525caf952bcc542ebe1 Mon Sep 17 00:00:00 2001 From: Paul Robert Lloyd Date: Sat, 30 Mar 2024 15:47:55 +0000 Subject: [PATCH 11/12] feat(preset-jekyll): remove slug property --- packages/preset-jekyll/lib/post-template.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/preset-jekyll/lib/post-template.js b/packages/preset-jekyll/lib/post-template.js index bf1f8f808..41dc9375d 100644 --- a/packages/preset-jekyll/lib/post-template.js +++ b/packages/preset-jekyll/lib/post-template.js @@ -54,6 +54,7 @@ const getFrontMatter = (properties) => { delete properties.content; // Shown below front matter delete properties.name; // Use `title` delete properties.post_status; // Use `published` + delete properties.slug; // File path dictates slug delete properties.summary; // Use `excerpt` delete properties.type; // Not required delete properties.url; // Not required From 1ca029240636c76f128c0f5279e27124c8300b24 Mon Sep 17 00:00:00 2001 From: Paul Robert Lloyd Date: Sat, 30 Mar 2024 15:49:25 +0000 Subject: [PATCH 12/12] feat(preset-hugo): remove slug property --- packages/preset-hugo/lib/post-template.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/preset-hugo/lib/post-template.js b/packages/preset-hugo/lib/post-template.js index d4f419cb4..b17f6a14c 100644 --- a/packages/preset-hugo/lib/post-template.js +++ b/packages/preset-hugo/lib/post-template.js @@ -58,6 +58,7 @@ const getFrontMatter = (properties, frontMatterFormat) => { delete properties.name; // Use `title` delete properties.postStatus; // Use `draft` delete properties.published; // Use `date` + delete properties.slug; // File path dictates slug delete properties.type; // Not required delete properties.updated; // Use `lastmod` delete properties.url; // Not required