From 447b4732614a6924c2fb7fc2fc41bf88e6642c48 Mon Sep 17 00:00:00 2001 From: Brian Macdonald Date: Mon, 9 Apr 2018 11:23:10 -0400 Subject: [PATCH 1/7] adds error throwing if a file is in danger of being overwritten --- src/backends/backend.js | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/backends/backend.js b/src/backends/backend.js index 9c3ab44b4a35..8a74db3fee37 100644 --- a/src/backends/backend.js +++ b/src/backends/backend.js @@ -23,6 +23,7 @@ registerBackend('git-gateway', GitGatewayBackend); registerBackend('github', GitHubBackend); registerBackend('test-repo', TestRepoBackend); +const overWriteError = new Error("Oops! Duplicate filename found. Please choose a unique name.") class LocalStorageAuthStore { storageKey = "netlify-cms-user"; @@ -234,7 +235,20 @@ class Backend { .then(this.entryWithFormat(collection, slug)); } - persistEntry(config, collection, entryDraft, MediaFiles, integrations, options = {}) { + checkOverwrite(collection, slug, path, entryDraft) { + const existingEntry = window.repoFilesUnpublished.find(e => { + return e.metaData.collection === collection.get('name') && e.slug === slug; + }) + if (existingEntry) throw overWriteError; + return this.getEntry(collection, slug) + .then(result => { + if (result.data.title) throw overWriteError; + return {path, slug, raw: this.entryToRaw(collection, entryDraft.get("entry"))}; + }) + .catch(console.error); + } + + async persistEntry(config, collection, entryDraft, MediaFiles, integrations, options = {}) { const newEntry = entryDraft.getIn(["entry", "newRecord"]) || false; const parsedData = { @@ -250,11 +264,7 @@ class Backend { } const slug = slugFormatter(collection.get("slug"), entryDraft.getIn(["entry", "data"]), config.get("slug")); const path = selectEntryPath(collection, slug); - entryObj = { - path, - slug, - raw: this.entryToRaw(collection, entryDraft.get("entry")), - }; + entryObj = await this.checkOverwrite(collection, slug, path, entryDraft); } else { const path = entryDraft.getIn(["entry", "path"]); const slug = entryDraft.getIn(["entry", "slug"]); From b7898f70220691bb4beb991e62f46326c8047e71 Mon Sep 17 00:00:00 2001 From: Brian Macdonald Date: Mon, 9 Apr 2018 11:46:26 -0400 Subject: [PATCH 2/7] uses implementation.getentry to avoid front-matter error --- src/backends/backend.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backends/backend.js b/src/backends/backend.js index 8a74db3fee37..b34b06cef947 100644 --- a/src/backends/backend.js +++ b/src/backends/backend.js @@ -240,9 +240,9 @@ class Backend { return e.metaData.collection === collection.get('name') && e.slug === slug; }) if (existingEntry) throw overWriteError; - return this.getEntry(collection, slug) + return this.implementation.getEntry(collection, slug, path) .then(result => { - if (result.data.title) throw overWriteError; + if (result.data !== undefined) throw overWriteError; return {path, slug, raw: this.entryToRaw(collection, entryDraft.get("entry"))}; }) .catch(console.error); From 7e9ba02fcff84b7a7c73e0dd22d0b8b109c18481 Mon Sep 17 00:00:00 2001 From: Brian Macdonald Date: Mon, 9 Apr 2018 13:23:16 -0400 Subject: [PATCH 3/7] removes catch in overwrite so correct error always displays --- src/backends/backend.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/backends/backend.js b/src/backends/backend.js index b34b06cef947..957489afd861 100644 --- a/src/backends/backend.js +++ b/src/backends/backend.js @@ -23,8 +23,6 @@ registerBackend('git-gateway', GitGatewayBackend); registerBackend('github', GitHubBackend); registerBackend('test-repo', TestRepoBackend); -const overWriteError = new Error("Oops! Duplicate filename found. Please choose a unique name.") - class LocalStorageAuthStore { storageKey = "netlify-cms-user"; @@ -235,17 +233,17 @@ class Backend { .then(this.entryWithFormat(collection, slug)); } - checkOverwrite(collection, slug, path, entryDraft) { + async checkOverwrite(collection, slug, path, entryDraft) { + const errorMessage = "Oops, duplicate filename found. Please choose a unique name." const existingEntry = window.repoFilesUnpublished.find(e => { return e.metaData.collection === collection.get('name') && e.slug === slug; }) - if (existingEntry) throw overWriteError; - return this.implementation.getEntry(collection, slug, path) + if (existingEntry) throw (new Error(errorMessage)); + return await this.implementation.getEntry(collection, slug, path) .then(result => { - if (result.data !== undefined) throw overWriteError; - return {path, slug, raw: this.entryToRaw(collection, entryDraft.get("entry"))}; - }) - .catch(console.error); + if (result.data !== undefined) throw (new Error(errorMessage)); + else return {path, slug, raw: this.entryToRaw(collection, entryDraft.get("entry"))}; + }); } async persistEntry(config, collection, entryDraft, MediaFiles, integrations, options = {}) { From 9d2acc64a2bb06484c0902a7ef82fb5955b5364b Mon Sep 17 00:00:00 2001 From: Brian Macdonald Date: Mon, 9 Apr 2018 16:41:15 -0400 Subject: [PATCH 4/7] uses unpublishedEntry for checkOverwrite --- src/backends/backend.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/backends/backend.js b/src/backends/backend.js index 957489afd861..427a22df0477 100644 --- a/src/backends/backend.js +++ b/src/backends/backend.js @@ -15,6 +15,7 @@ import TestRepoBackend from "./test-repo/implementation"; import GitHubBackend from "./github/implementation"; import GitGatewayBackend from "./git-gateway/implementation"; import { registerBackend, getBackend } from 'Lib/registry'; +import { EditorialWorkflowError } from "ValueObjects/errors"; /** * Register internal backends @@ -234,12 +235,13 @@ class Backend { } async checkOverwrite(collection, slug, path, entryDraft) { - const errorMessage = "Oops, duplicate filename found. Please choose a unique name." - const existingEntry = window.repoFilesUnpublished.find(e => { - return e.metaData.collection === collection.get('name') && e.slug === slug; - }) + const errorMessage = "Oops, duplicate filename found. Please choose a unique name."; + const existingEntry = await this.unpublishedEntry(collection, slug).catch(error => ( + (error instanceof EditorialWorkflowError && error.notUnderEditorialWorkflow) ? + Promise.resolve(false) : + Promise.reject(error))); if (existingEntry) throw (new Error(errorMessage)); - return await this.implementation.getEntry(collection, slug, path) + return await this.implentation.getEntry(collection, slug, path) .then(result => { if (result.data !== undefined) throw (new Error(errorMessage)); else return {path, slug, raw: this.entryToRaw(collection, entryDraft.get("entry"))}; From d2c8074d3d9eae586094925488dad8ce4b7f88c6 Mon Sep 17 00:00:00 2001 From: Brian Macdonald Date: Mon, 9 Apr 2018 19:34:49 -0400 Subject: [PATCH 5/7] small format tweak --- src/backends/backend.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/backends/backend.js b/src/backends/backend.js index 427a22df0477..bdd5bb1faa34 100644 --- a/src/backends/backend.js +++ b/src/backends/backend.js @@ -240,12 +240,17 @@ class Backend { (error instanceof EditorialWorkflowError && error.notUnderEditorialWorkflow) ? Promise.resolve(false) : Promise.reject(error))); + if (existingEntry) throw (new Error(errorMessage)); - return await this.implentation.getEntry(collection, slug, path) + + const publishedEntry = await this.implementation.getEntry(collection, slug, path) .then(result => { - if (result.data !== undefined) throw (new Error(errorMessage)); - else return {path, slug, raw: this.entryToRaw(collection, entryDraft.get("entry"))}; + return result.data; }); + + if (publishedEntry) throw (new Error(errorMessage)); + + return {path, slug, raw: this.entryToRaw(collection, entryDraft.get("entry"))}; } async persistEntry(config, collection, entryDraft, MediaFiles, integrations, options = {}) { From b44976001615905361190bccbf0264e15720159f Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Mon, 14 May 2018 15:26:14 -0400 Subject: [PATCH 6/7] catch any valid identifier field --- src/backends/backend.js | 82 ++++++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 29 deletions(-) diff --git a/src/backends/backend.js b/src/backends/backend.js index bdd5bb1faa34..e3420dcc8b26 100644 --- a/src/backends/backend.js +++ b/src/backends/backend.js @@ -41,23 +41,28 @@ class LocalStorageAuthStore { } } -const slugFormatter = (template = "{{slug}}", entryData, slugConfig) => { - const date = new Date(); - - const getIdentifier = (entryData) => { - const validIdentifierFields = ["title", "path"]; - const identifiers = validIdentifierFields.map((field) => - entryData.find((_, key) => key.toLowerCase().trim() === field) - ); +const validIdentifierFields = ["title", "path"]; - const identifier = identifiers.find(ident => ident !== undefined); +const getIdentifierKey = entryData => { + const keys = validIdentifierFields.map(field => { + return entryData.findKey((_, key) => { + return key.toLowerCase().trim() === field; + }); + }); + return keys.find(key => entryData.get(key) !== undefined); +}; - if (identifier === undefined) { - throw new Error("Collection must have a field name that is a valid entry identifier"); - } +const getIdentifier = entryData => { + const key = getIdentifierKey(entryData); + const identifier = entryData.get(key); + if (identifier === undefined) { + throw new Error("Collection must have a field name that is a valid entry identifier"); + } + return identifier; +}; - return identifier; - }; +const slugFormatter = (template = "{{slug}}", entryData, slugConfig) => { + const date = new Date(); const slug = template.replace(/\{\{([^\}]+)\}\}/g, (_, field) => { switch (field) { @@ -234,23 +239,34 @@ class Backend { .then(this.entryWithFormat(collection, slug)); } - async checkOverwrite(collection, slug, path, entryDraft) { - const errorMessage = "Oops, duplicate filename found. Please choose a unique name."; - const existingEntry = await this.unpublishedEntry(collection, slug).catch(error => ( - (error instanceof EditorialWorkflowError && error.notUnderEditorialWorkflow) ? - Promise.resolve(false) : - Promise.reject(error))); + async checkOverwrite(collection, slug, path, entryData) { + const identifierKey = getIdentifierKey(entryData); + const identifierField = collection.get('fields').find(field => { + return field.get('name') === identifierKey; + }); + const identifierLabel = identifierField.get('label'); + const errorMessage = `\ +Duplicate filename found. Please ensure the ${identifierLabel} field is unique from other entries \ +in the ${collection.get('name')} collection.\ +`; + + const existingEntry = await this.unpublishedEntry(collection, slug).catch(error => { + if (error instanceof EditorialWorkflowError && error.notUnderEditorialWorkflow) { + return Promise.resolve(false); + } + return Promise.reject(error); + }); - if (existingEntry) throw (new Error(errorMessage)); + if (existingEntry) { + throw new Error(errorMessage); + } const publishedEntry = await this.implementation.getEntry(collection, slug, path) - .then(result => { - return result.data; - }); - - if (publishedEntry) throw (new Error(errorMessage)); + .then(({ data }) => data); - return {path, slug, raw: this.entryToRaw(collection, entryDraft.get("entry"))}; + if (publishedEntry) { + throw new Error(errorMessage); + } } async persistEntry(config, collection, entryDraft, MediaFiles, integrations, options = {}) { @@ -267,9 +283,17 @@ class Backend { if (!selectAllowNewEntries(collection)) { throw (new Error("Not allowed to create new entries in this collection")); } - const slug = slugFormatter(collection.get("slug"), entryDraft.getIn(["entry", "data"]), config.get("slug")); + const entryData = entryDraft.getIn(['entry', 'data']); + const slug = slugFormatter(collection.get('slug'), entryData, config.get('slug')); const path = selectEntryPath(collection, slug); - entryObj = await this.checkOverwrite(collection, slug, path, entryDraft); + + await this.checkOverwrite(collection, slug, path, entryData); + + entryObj = { + path, + slug, + raw: this.entryToRaw(collection, entryDraft.get('entry')), + }; } else { const path = entryDraft.getIn(["entry", "path"]); const slug = entryDraft.getIn(["entry", "slug"]); From 3c0291600a44d9cab4ecb1159f6e458ecba0d71c Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Wed, 23 May 2018 16:09:15 -0400 Subject: [PATCH 7/7] improve key find logic --- src/backends/backend.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backends/backend.js b/src/backends/backend.js index e3420dcc8b26..e06430c03f29 100644 --- a/src/backends/backend.js +++ b/src/backends/backend.js @@ -49,7 +49,7 @@ const getIdentifierKey = entryData => { return key.toLowerCase().trim() === field; }); }); - return keys.find(key => entryData.get(key) !== undefined); + return keys.find(key => key !== undefined); }; const getIdentifier = entryData => {