Skip to content
This repository has been archived by the owner on Sep 9, 2024. It is now read-only.

Commit

Permalink
feat: open authoring (#938)
Browse files Browse the repository at this point in the history
  • Loading branch information
KaneFreeman authored Nov 1, 2023
1 parent 4ffd38c commit 9f3bd50
Show file tree
Hide file tree
Showing 74 changed files with 1,713 additions and 546 deletions.
4 changes: 3 additions & 1 deletion packages/core/dev-test/backends/github/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ backend:
name: github
branch: main
repo: staticjscms/static-cms-github

auth_scope: repo # this is needed to fork the private repo
open_authoring: true
publish_mode: editorial_workflow
media_folder: assets/upload
public_folder: /assets/upload
media_library:
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/__tests__/backend.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,7 @@ describe('Backend', () => {
mediaFiles: [{ id: '1', draft: true }],
status: WorkflowStatus.DRAFT,
updatedOn: '20230-02-09T00:00:00.000Z',
openAuthoring: false,
});
});
});
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/actions/__tests__/config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -889,7 +889,7 @@ describe('config', () => {
).toThrow("i18n locales 'en, de' are missing the default locale fr");
});

it('should throw is default locale is missing from collection i18n config', () => {
it('should throw if default locale is missing from collection i18n config', () => {
expect(() =>
applyDefaults(
createMockConfig({
Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/actions/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { currentBackend } from '../backend';
import { AUTH_FAILURE, AUTH_REQUEST, AUTH_REQUEST_DONE, AUTH_SUCCESS, LOGOUT } from '../constants';
import { invokeEvent } from '../lib/registry';
import { addSnackbar } from '../store/slices/snackbars';
import { useOpenAuthoring } from './globalUI';

import type { AnyAction } from 'redux';
import type { ThunkDispatch } from 'redux-thunk';
Expand Down Expand Up @@ -57,6 +58,9 @@ export function authenticateUser() {
return Promise.resolve(backend.currentUser())
.then(user => {
if (user) {
if (user.useOpenAuthoring) {
dispatch(useOpenAuthoring());
}
dispatch(authenticate(user));
} else {
dispatch(doneAuthenticating());
Expand Down Expand Up @@ -85,6 +89,9 @@ export function loginUser(credentials: Credentials) {
return backend
.authenticate(credentials)
.then(user => {
if (user.useOpenAuthoring) {
dispatch(useOpenAuthoring());
}
dispatch(authenticate(user));
})
.catch((error: unknown) => {
Expand Down
15 changes: 8 additions & 7 deletions packages/core/src/actions/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,17 @@ function setI18nField<T extends BaseField = UnknownField>(field: T) {

function getI18nDefaults(
collectionOrFileI18n: boolean | Partial<I18nInfo>,
defaultI18n: I18nInfo,
{ default_locale, locales = ['en'], structure = I18N_STRUCTURE_SINGLE_FILE }: Partial<I18nInfo>,
): I18nInfo {
if (typeof collectionOrFileI18n === 'boolean') {
return defaultI18n;
return { default_locale, locales, structure };
} else {
const locales = collectionOrFileI18n.locales || defaultI18n.locales;
const defaultLocale = collectionOrFileI18n.default_locale || locales?.[0];
const mergedI18n: I18nInfo = deepmerge(defaultI18n, collectionOrFileI18n);
mergedI18n.locales = locales ?? [];
mergedI18n.default_locale = defaultLocale;
const mergedI18n: I18nInfo = deepmerge(
{ default_locale, locales, structure },
collectionOrFileI18n,
);
mergedI18n.locales = collectionOrFileI18n.locales ?? locales;
mergedI18n.default_locale = collectionOrFileI18n.default_locale || locales?.[0];
throwOnMissingDefaultLocale(mergedI18n);
return mergedI18n;
}
Expand Down
10 changes: 8 additions & 2 deletions packages/core/src/actions/globalUI.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
/* eslint-disable import/prefer-default-export */
import { THEME_CHANGE } from '../constants';
import { THEME_CHANGE, USE_OPEN_AUTHORING } from '../constants';

export function useOpenAuthoring() {
return {
type: USE_OPEN_AUTHORING,
} as const;
}

export function changeTheme(theme: string) {
return { type: THEME_CHANGE, payload: theme } as const;
}

export type GlobalUIAction = ReturnType<typeof changeTheme>;
export type GlobalUIAction = ReturnType<typeof changeTheme | typeof useOpenAuthoring>;
85 changes: 52 additions & 33 deletions packages/core/src/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1024,13 +1024,18 @@ export class Backend<EF extends BaseField = UnknownField, BC extends BackendClas
}

const user = (await this.currentUser()) as User;
const commitMessage = commitMessageFormatter(newEntry ? 'create' : 'update', config, {
collection,
slug,
path,
authorLogin: user.login,
authorName: user.name,
});
const commitMessage = commitMessageFormatter(
newEntry ? 'create' : 'update',
config,
{
collection,
slug,
path,
authorLogin: user.login,
authorName: user.name,
},
user.useOpenAuthoring,
);

const collectionName = collection.name;

Expand Down Expand Up @@ -1092,11 +1097,16 @@ export class Backend<EF extends BaseField = UnknownField, BC extends BackendClas
async persistMedia(config: ConfigWithDefaults, file: AssetProxy) {
const user = (await this.currentUser()) as User;
const options = {
commitMessage: commitMessageFormatter('uploadMedia', config, {
path: file.path,
authorLogin: user.login,
authorName: user.name,
}),
commitMessage: commitMessageFormatter(
'uploadMedia',
config,
{
path: file.path,
authorLogin: user.login,
authorName: user.name,
},
user.useOpenAuthoring,
),
};
return this.implementation.persistMedia(file, options);
}
Expand All @@ -1119,13 +1129,18 @@ export class Backend<EF extends BaseField = UnknownField, BC extends BackendClas
}

const user = (await this.currentUser()) as User;
const commitMessage = commitMessageFormatter('delete', configState.config, {
collection,
slug,
path,
authorLogin: user.login,
authorName: user.name,
});
const commitMessage = commitMessageFormatter(
'delete',
configState.config,
{
collection,
slug,
path,
authorLogin: user.login,
authorName: user.name,
},
user.useOpenAuthoring,
);

let paths = [path];
if (hasI18n(collection)) {
Expand All @@ -1136,11 +1151,16 @@ export class Backend<EF extends BaseField = UnknownField, BC extends BackendClas

async deleteMedia(config: ConfigWithDefaults, path: string) {
const user = (await this.currentUser()) as User;
const commitMessage = commitMessageFormatter('deleteMedia', config, {
path,
authorLogin: user.login,
authorName: user.name,
});
const commitMessage = commitMessageFormatter(
'deleteMedia',
config,
{
path,
authorLogin: user.login,
authorName: user.name,
},
user.useOpenAuthoring,
);
return this.implementation.deleteFiles([path], commitMessage);
}

Expand Down Expand Up @@ -1177,7 +1197,7 @@ export class Backend<EF extends BaseField = UnknownField, BC extends BackendClas
entryData: UnpublishedEntry,
withMediaFiles: boolean,
) {
const { slug } = entryData;
const { slug, openAuthoring } = entryData;
let extension: string;
if ('files' in collection) {
const file = collection.files.find(f => f?.name === slug);
Expand Down Expand Up @@ -1210,10 +1230,10 @@ export class Backend<EF extends BaseField = UnknownField, BC extends BackendClas
author: entryData.pullRequestAuthor,
status: workflowStatusFromString(entryData.status),
meta: { path: prepareMetaPath(path, collection) },
openAuthoring,
});

const entryWithFormat = this.entryWithFormat(collection)(entry);
return entryWithFormat;
return this.entryWithFormat(collection)(entry);
};

const readAndFormatDataFile = async (dataFile: UnpublishedEntryDiff) => {
Expand All @@ -1223,8 +1243,8 @@ export class Backend<EF extends BaseField = UnknownField, BC extends BackendClas
dataFile.path,
dataFile.id,
);
const entryWithFormat = formatData(data, dataFile.path, dataFile.newFile);
return entryWithFormat;

return formatData(data, dataFile.path, dataFile.newFile);
};

// if the unpublished entry has no diffs, return the original
Expand All @@ -1244,8 +1264,7 @@ export class Backend<EF extends BaseField = UnknownField, BC extends BackendClas
const grouped = await groupEntries(collection, extension, entries as Entry[]);
return grouped[0];
} else {
const entryWithFormat = await readAndFormatDataFile(dataFiles[0]);
return entryWithFormat;
return readAndFormatDataFile(dataFiles[0]);
}
}

Expand All @@ -1261,8 +1280,8 @@ export class Backend<EF extends BaseField = UnknownField, BC extends BackendClas
console.warn(`Missing collection '${collectionName}' for unpublished entry '${id}'`);
return null;
}
const entry = await this.processUnpublishedEntry(collection, entryData, false);
return entry;

return this.processUnpublishedEntry(collection, entryData, false);
}),
)
).filter(Boolean) as Entry[];
Expand Down
5 changes: 3 additions & 2 deletions packages/core/src/backends/bitbucket/API.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import {
} from '@staticcms/core/lib/util/APIUtils';

import type { WorkflowStatus } from '@staticcms/core/constants/publishModes';
import type { DataFile, PersistOptions } from '@staticcms/core/interface';
import type { DataFile, PersistOptions, UnpublishedEntry } from '@staticcms/core/interface';
import type { ApiRequest, FetchError } from '@staticcms/core/lib/util';
import type AssetProxy from '@staticcms/core/valueObjects/AssetProxy';

Expand Down Expand Up @@ -646,7 +646,7 @@ export default class API {
return pullRequests[0];
}

async retrieveUnpublishedEntryData(contentKey: string) {
async retrieveUnpublishedEntryData(contentKey: string): Promise<UnpublishedEntry> {
const { collection, slug } = parseContentKey(contentKey);
const branch = branchFromContentKey(contentKey);
const pullRequest = await this.getBranchPullRequest(branch);
Expand All @@ -665,6 +665,7 @@ export default class API {
.map(d => ({ path: d.path, newFile: d.newFile, id: '' })),
updatedAt,
pullRequestAuthor,
openAuthoring: false,
};
}

Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/backends/bitbucket/implementation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import type {
DisplayURL,
ImplementationFile,
PersistOptions,
UnpublishedEntry,
User,
} from '@staticcms/core/interface';
import type { ApiRequest, AsyncLock, Cursor, FetchError } from '@staticcms/core/lib/util';
Expand Down Expand Up @@ -572,7 +573,7 @@ export default class BitbucketBackend implements BackendClass {
id?: string;
collection?: string;
slug?: string;
}) {
}): Promise<UnpublishedEntry> {
if (id) {
const data = await this.api!.retrieveUnpublishedEntryData(id);
return data;
Expand Down
11 changes: 10 additions & 1 deletion packages/core/src/backends/git-gateway/implementation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import type {
DisplayURLObject,
ImplementationFile,
PersistOptions,
UnpublishedEntry,
User,
} from '@staticcms/core/interface';
import type { ApiRequest, Cursor } from '@staticcms/core/lib/util';
Expand Down Expand Up @@ -563,7 +564,15 @@ export default class GitGateway implements BackendClass {
return this.backend!.unpublishedEntries();
}

unpublishedEntry({ id, collection, slug }: { id?: string; collection?: string; slug?: string }) {
unpublishedEntry({
id,
collection,
slug,
}: {
id?: string;
collection?: string;
slug?: string;
}): Promise<UnpublishedEntry> {
return this.backend!.unpublishedEntry({ id, collection, slug });
}

Expand Down
Loading

0 comments on commit 9f3bd50

Please sign in to comment.