Skip to content

Commit

Permalink
feat: add pre save/ post save hooks (#3812)
Browse files Browse the repository at this point in the history
  • Loading branch information
barthc authored May 27, 2020
1 parent 946f7a0 commit 812716e
Show file tree
Hide file tree
Showing 7 changed files with 43 additions and 29 deletions.
4 changes: 0 additions & 4 deletions packages/netlify-cms-backend-test/src/implementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,6 @@ export default class TestBackend implements Implementation {
const unpubEntry = {
...unpubStore[existingEntryIndex],
data: raw,
title: options.parsedData && options.parsedData.title,
description: options.parsedData && options.parsedData.description,
mediaFiles: assetProxies.map(this.normalizeAsset),
};

Expand All @@ -242,8 +240,6 @@ export default class TestBackend implements Implementation {
metaData: {
collection: options.collectionName as string,
status: (options.status || this.options.initialWorkflowStatus) as string,
title: options.parsedData && options.parsedData.title,
description: options.parsedData && options.parsedData.description,
},
slug,
mediaFiles: assetProxies.map(this.normalizeAsset),
Expand Down
23 changes: 15 additions & 8 deletions packages/netlify-cms-core/src/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -702,18 +702,16 @@ export class Backend {
async persistEntry({
config,
collection,
entryDraft,
entryDraft: draft,
assetProxies,
usedSlugs,
unpublished = false,
status,
}: PersistArgs) {
const newEntry = entryDraft.getIn(['entry', 'newRecord']) || false;
const modifiedData = await this.invokePreSaveEvent(draft.get('entry'));
const entryDraft = (modifiedData && draft.setIn(['entry', 'data'], modifiedData)) || draft;

const parsedData = {
title: entryDraft.getIn(['entry', 'data', 'title'], 'No Title') as string,
description: entryDraft.getIn(['entry', 'data', 'description'], 'No Description!') as string,
};
const newEntry = entryDraft.getIn(['entry', 'newRecord']) || false;

let entryObj: {
path: string;
Expand Down Expand Up @@ -782,7 +780,6 @@ export class Backend {
const updatedOptions = { unpublished, status };
const opts = {
newEntry,
parsedData,
commitMessage,
collectionName,
useWorkflow,
Expand All @@ -795,6 +792,8 @@ export class Backend {

await this.implementation.persistEntry(entryObj, assetProxies, opts);

await this.invokePostSaveEvent(entryDraft.get('entry'));

if (!useWorkflow) {
await this.invokePostPublishEvent(entryDraft.get('entry'));
}
Expand All @@ -804,7 +803,7 @@ export class Backend {

async invokeEventWithEntry(event: string, entry: EntryMap) {
const { login, name } = (await this.currentUser()) as User;
await invokeEvent({ name: event, data: { entry, author: { login, name } } });
return await invokeEvent({ name: event, data: { entry, author: { login, name } } });
}

async invokePrePublishEvent(entry: EntryMap) {
Expand All @@ -823,6 +822,14 @@ export class Backend {
await this.invokeEventWithEntry('postUnpublish', entry);
}

async invokePreSaveEvent(entry: EntryMap) {
return await this.invokeEventWithEntry('preSave', entry);
}

async invokePostSaveEvent(entry: EntryMap) {
await this.invokeEventWithEntry('postSave', entry);
}

async persistMedia(config: Config, file: AssetProxy) {
const user = (await this.currentUser()) as User;
const options = {
Expand Down
9 changes: 8 additions & 1 deletion packages/netlify-cms-core/src/lib/__tests__/registry.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,14 @@ describe('registry', () => {
});

describe('eventHandlers', () => {
const events = ['prePublish', 'postPublish', 'preUnpublish', 'postUnpublish'];
const events = [
'prePublish',
'postPublish',
'preUnpublish',
'postUnpublish',
'preSave',
'postSave',
];

describe('registerEventListener', () => {
it('should throw error on invalid event', () => {
Expand Down
11 changes: 9 additions & 2 deletions packages/netlify-cms-core/src/lib/registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@ import produce from 'immer';
import { oneLine } from 'common-tags';
import EditorComponent from 'ValueObjects/EditorComponent';

const allowedEvents = ['prePublish', 'postPublish', 'preUnpublish', 'postUnpublish'];
const allowedEvents = [
'prePublish',
'postPublish',
'preUnpublish',
'postUnpublish',
'preSave',
'postSave',
];
const eventHandlers = {};
allowedEvents.forEach(e => {
eventHandlers[e] = [];
Expand Down Expand Up @@ -213,7 +220,7 @@ export async function invokeEvent({ name, data }) {
const handlers = registry.eventHandlers[name];
for (const { handler, options } of handlers) {
try {
await handler(data, options);
return await handler(data, options);
} catch (e) {
console.warn(`Failed running handler for event ${name} with message: ${e.message}`);
}
Expand Down
4 changes: 4 additions & 0 deletions packages/netlify-cms-core/src/types/immutable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ export interface StaticallyTypedRecord<T> {
keys: [K1, K2, K3],
defaultValue?: V,
): T[K1][K2][K3];
setIn<K1 extends keyof T, K2 extends keyof T[K1], V extends T[K1][K2]>(
keys: [K1, K2],
value: V,
): StaticallyTypedRecord<T>;
toJS(): T;
isEmpty(): boolean;
some<K extends keyof T>(predicate: (value: T[K], key: K, iter: this) => boolean): boolean;
Expand Down
1 change: 0 additions & 1 deletion packages/netlify-cms-lib-util/src/implementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ export type Entry = { path: string; slug: string; raw: string };

export type PersistOptions = {
newEntry?: boolean;
parsedData?: { title: string; description: string };
commitMessage: string;
collectionName?: string;
useWorkflow?: boolean;
Expand Down
20 changes: 7 additions & 13 deletions website/content/docs/beta-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -399,25 +399,19 @@ CMS.registerEventListener({
name: 'prePublish',
handler: ({ author, entry }) => console.log(JSON.stringify({ author, data: entry.get('data') })),
});
```

CMS.registerEventListener({
name: 'postPublish',
handler: ({ author, entry }) => console.log(JSON.stringify({ author, data: entry.get('data') })),
});
CMS.registerEventListener({
name: 'preUnpublish',
handler: ({ author, entry }) => console.log(JSON.stringify({ author, data: entry.get('data') })),
});
Supported events are `prePublish`, `postPublish`, `preUnpublish`, `postUnpublish`, `preSave` and `postSave`. The `preSave` hook can be used to modify the entry data like so:

```javascript
CMS.registerEventListener({
name: 'postUnpublish',
handler: ({ author, entry }) => console.log(JSON.stringify({ author, data: entry.get('data') })),
name: 'preSave',
handler: ({ entry }) => {
return entry.get('data').set('title', 'new title');
},
});
```

**Note:** Supported events are `prePublish`, `postPublish`, `preUnpublish` and `postUnpublish`.

## Dynamic Default Values

When linking to `/admin/#/collections/posts/new` you can pass URL parameters to pre-populate an entry.
Expand Down

0 comments on commit 812716e

Please sign in to comment.