Skip to content

Commit

Permalink
Prevent Overwriting Files with Same Slug Names (decaporg#1239)
Browse files Browse the repository at this point in the history
  • Loading branch information
brianlmacdonald authored and erquhart committed May 23, 2018
1 parent 8d4616b commit b25544e
Showing 1 changed file with 57 additions and 18 deletions.
75 changes: 57 additions & 18 deletions src/backends/backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -23,7 +24,6 @@ registerBackend('git-gateway', GitGatewayBackend);
registerBackend('github', GitHubBackend);
registerBackend('test-repo', TestRepoBackend);


class LocalStorageAuthStore {
storageKey = "netlify-cms-user";

Expand All @@ -41,23 +41,28 @@ class LocalStorageAuthStore {
}
}

const slugFormatter = (template = "{{slug}}", entryData, slugConfig) => {
const date = new Date();
const validIdentifierFields = ["title", "path"];

const getIdentifier = (entryData) => {
const validIdentifierFields = ["title", "path"];
const identifiers = validIdentifierFields.map((field) =>
entryData.find((_, key) => key.toLowerCase().trim() === field)
);

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 => 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) {
Expand Down Expand Up @@ -234,7 +239,37 @@ class Backend {
.then(this.entryWithFormat(collection, slug));
}

persistEntry(config, collection, entryDraft, MediaFiles, integrations, options = {}) {
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);
}

const publishedEntry = await this.implementation.getEntry(collection, slug, path)
.then(({ data }) => data);

if (publishedEntry) {
throw new Error(errorMessage);
}
}

async persistEntry(config, collection, entryDraft, MediaFiles, integrations, options = {}) {
const newEntry = entryDraft.getIn(["entry", "newRecord"]) || false;

const parsedData = {
Expand All @@ -248,12 +283,16 @@ 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);

await this.checkOverwrite(collection, slug, path, entryData);

entryObj = {
path,
slug,
raw: this.entryToRaw(collection, entryDraft.get("entry")),
raw: this.entryToRaw(collection, entryDraft.get('entry')),
};
} else {
const path = entryDraft.getIn(["entry", "path"]);
Expand Down

0 comments on commit b25544e

Please sign in to comment.