Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/blog/render/load/augment.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var async = require("async");
var _ = require("lodash");
var moment = require("moment");
var debug = require("debug")("blog:render:augment");
var aliasMetadata = require("build/metadata/aliases");
require("moment-timezone");

module.exports = function (req, res, entry, callback) {
Expand All @@ -15,6 +16,10 @@ module.exports = function (req, res, entry, callback) {
var hideDate = res.locals.hide_dates || false;
var dateDisplay = res.locals.date_display || "MMMM D, Y";

if (entry.metadata && typeof entry.metadata === "object") {
aliasMetadata(entry.metadata);
}

entry.formatDate = FormatDate(entry.dateStamp, req.blog.timeZone);
entry.formatUpdated = FormatDate(entry.updated, req.blog.timeZone);
entry.formatCreated = FormatDate(entry.created, req.blog.timeZone);
Expand Down
28 changes: 23 additions & 5 deletions app/blog/render/tests/augment.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ describe("augment", function () {
require('../../tests/util/setup')();

it("adds formatDate function to entries", async function () {

await this.write({path: "/first.txt", content: "Foo"});
await this.template({
'entry.html': '{{#entry}}{{#formatDate}}YYYY{{/formatDate}}{{/entry}}'
Expand All @@ -15,9 +15,9 @@ describe("augment", function () {
expect(res.status).toEqual(200);
expect(body.trim()).toEqual(new Date().getFullYear().toString());
});

it("adds ratio property to thumbnails", async function () {

const image = await require('sharp')({
create: {
width: 100,
Expand All @@ -40,7 +40,7 @@ describe("augment", function () {
});

it("renders entry backlinks", async function () {

await this.write({path: "/first.txt", content: "Foo"});
await this.write({path: "/second.txt", content: "Title: Second\n\n[[first]]"});
await this.template({
Expand All @@ -53,4 +53,22 @@ describe("augment", function () {
expect(res.status).toEqual(200);
expect(body.trim()).toEqual('Second');
});
});

it("aliases metadata for templates", async function () {

await this.write({
path: "/first.txt",
content: ["CustomKey: Value", "", "Body"].join("\n")
});

await this.template({
'entry.html': '{{#entry}}{{metadata.customkey}}{{/entry}}'
});

const res = await this.get('/first');
const body = await res.text();

expect(res.status).toEqual(200);
expect(body.trim()).toEqual('Value');
});
});
34 changes: 19 additions & 15 deletions app/build/metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ const YAML = require("yaml");

const alphaNumericRegEx = /^([a-zA-Z0-9\-_ ]+)$/;

function finalize(html, metadata) {
return {
html,
metadata
};
}

function Metadata (html) {
ensure(html, "string");

Expand Down Expand Up @@ -39,27 +46,26 @@ function Metadata (html) {
mixedCaseMetadata = {};
}

// Map { Permalink } to { permalink }
// Blot uses lowercase metadata keys
const parsedMetadata = {};

Object.keys(mixedCaseMetadata).forEach(mixedCaseKey => {
let key = mixedCaseKey.toLowerCase();
let value = mixedCaseMetadata[mixedCaseKey];

// map 'null' values to empty strings
// otherwise we end up with metadata properties
// that are the string 'null' instead of simply empty
if (value === null) value = "";

metadata[key] = value;
parsedMetadata[mixedCaseKey] = value;
});

// Remove the metadata from the returned HTML
html = html.trim().split("---").slice(2).join("---");

return { html, metadata };
return finalize(html, parsedMetadata);
} catch (e) {
// we need to surface this error with the YAML
return { html, metadata };
return finalize(html, metadata);
}
}

Expand All @@ -71,7 +77,7 @@ function Metadata (html) {
// THIS SHOULD ALSO WORK FOR HTML:
// i.e. <p>Page: yes</p>
for (let i = 0; i < lines.length; i++) {
let line, key, value, firstColon, firstCharacter;
let line, value, firstColon, firstCharacter;

line = lines[i];

Expand Down Expand Up @@ -114,14 +120,14 @@ function Metadata (html) {
break;
}

// The key is lowercased and trimmed
key = line.slice(0, firstColon).trim().toLowerCase();
let key = line.slice(0, firstColon).trim();
let normalizedKey = key.toLowerCase();

// The key contains non-alphanumeric characters, so reject it
if (alphaNumericRegEx.test(key) === false) break;
if (alphaNumericRegEx.test(normalizedKey) === false) break;

// The key contains more than two spaces, so reject it
if (key.split(" ").length > 2) break;
if (normalizedKey.split(" ").length > 2) break;

value = line.slice(firstColon + 1).trim();

Expand All @@ -132,6 +138,7 @@ function Metadata (html) {

if (key.slice(0, 4) === "<!--") {
key = key.slice(4).trim();
normalizedKey = key.toLowerCase();
}

if (value.slice(-3) === "-->") {
Comment on lines 138 to 144

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 Badge Dropping lowercase normalization breaks metadata consumers

The parser now stores metadata under the original key casing (metadata[key] = value) instead of lower‑casing keys, but aliasing is only applied in the render augment step. Earlier build steps still query lower‑case properties such as entry.metadata.page, draft, tags, etc. (see app/build/prepare/index.js), so a typical file containing Page: yes or Draft: yes will now leave entry.page, entry.draft, slug generation, and other logic unset because the lower‑case properties no longer exist. This causes pages, drafts, tag parsing, and permalink resolution to silently fail for any metadata written in mixed case. Consider re‑introducing aliasing (or converting to lower case) before build/prepare runs, or updating all consumers to the new casing.

Useful? React with 👍 / 👎.

Expand Down Expand Up @@ -171,10 +178,7 @@ function Metadata (html) {

html = lines.join("\n");

return {
html: html,
metadata: metadata
};
return finalize(html, metadata);
}

module.exports = Metadata;
37 changes: 37 additions & 0 deletions app/build/metadata/aliases.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
function aliasMetadata(metadata) {
if (!metadata || typeof metadata !== "object") return metadata;

if (Array.isArray(metadata)) {
metadata.forEach(aliasMetadata);
return metadata;
}

Object.keys(metadata).forEach(originalKey => {
const value = metadata[originalKey];

if (value && typeof value === "object") {
aliasMetadata(value);
}

const aliasKey = originalKey.toLowerCase();

if (aliasKey === originalKey) return;

if (Object.prototype.hasOwnProperty.call(metadata, aliasKey)) return;

Object.defineProperty(metadata, aliasKey, {
enumerable: false,
configurable: true,
get() {
return metadata[originalKey];
},
set(newValue) {
metadata[originalKey] = newValue;
}
});
});

return metadata;
}

module.exports = aliasMetadata;
Loading
Loading