diff --git a/package-lock.json b/package-lock.json
index d67858977d29..b9222f63681b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,5 +1,5 @@
{
- "name": "decap-cms",
+ "name": "decap-cms-fork",
"version": "0.0.0",
"lockfileVersion": 3,
"requires": true,
diff --git a/packages/decap-cms-core/src/components/Collection/Collection.js b/packages/decap-cms-core/src/components/Collection/Collection.js
index cdc3c0c111a7..04eb3d5b45ff 100644
--- a/packages/decap-cms-core/src/components/Collection/Collection.js
+++ b/packages/decap-cms-core/src/components/Collection/Collection.js
@@ -24,6 +24,7 @@ import {
selectEntriesGroup,
selectViewStyle,
} from '../../reducers/entries';
+import { selectCanCreateNewEntry } from '../../reducers';
const CollectionContainer = styled.div`
margin: ${lengths.pageMargin};
@@ -53,6 +54,9 @@ export class Collection extends React.Component {
sortableFields: PropTypes.array,
sort: ImmutablePropTypes.orderedMap,
onSortClick: PropTypes.func.isRequired,
+ canCreate: PropTypes.bool.isRequired,
+ filterTerm: PropTypes.string,
+ viewStyle: PropTypes.string,
};
componentDidMount() {
@@ -99,9 +103,10 @@ export class Collection extends React.Component {
group,
onChangeViewStyle,
viewStyle,
+ canCreate,
} = this.props;
- let newEntryUrl = collection.get('create') ? getNewEntryUrl(collectionName) : '';
+ let newEntryUrl = canCreate ? getNewEntryUrl(collectionName) : '';
if (newEntryUrl && filterTerm) {
newEntryUrl = getNewEntryUrl(collectionName);
if (filterTerm) {
@@ -167,6 +172,7 @@ function mapStateToProps(state, ownProps) {
const filter = selectEntriesFilter(state.entries, collection.get('name'));
const group = selectEntriesGroup(state.entries, collection.get('name'));
const viewStyle = selectViewStyle(state.entries);
+ const canCreate = selectCanCreateNewEntry(state, name);
return {
collection,
@@ -183,6 +189,7 @@ function mapStateToProps(state, ownProps) {
filter,
group,
viewStyle,
+ canCreate,
};
}
diff --git a/packages/decap-cms-core/src/components/Collection/__tests__/Collection.spec.js b/packages/decap-cms-core/src/components/Collection/__tests__/Collection.spec.js
index eca5af8d4023..12b10702a115 100644
--- a/packages/decap-cms-core/src/components/Collection/__tests__/Collection.spec.js
+++ b/packages/decap-cms-core/src/components/Collection/__tests__/Collection.spec.js
@@ -29,22 +29,26 @@ describe('Collection', () => {
view_filters: [],
view_groups: [],
});
- const props = {
+ const baseProps = {
collections: fromJS([collection]).toOrderedMap(),
collection,
collectionName: collection.get('name'),
t: jest.fn(key => key),
onSortClick: jest.fn(),
+ viewStyle: 'list',
};
it('should render with collection without create url', () => {
+ const props = { ...baseProps, canCreate: false };
const { asFragment } = render(
,
);
expect(asFragment()).toMatchSnapshot();
});
+
it('should render with collection with create url', () => {
+ const props = { ...baseProps, canCreate: false };
const { asFragment } = render(
,
);
@@ -53,8 +57,9 @@ describe('Collection', () => {
});
it('should render with collection with create url and path', () => {
+ const props = { ...baseProps, canCreate: false, filterTerm: 'dir1/dir2' };
const { asFragment } = render(
- ,
+ ,
);
expect(asFragment()).toMatchSnapshot();
@@ -62,8 +67,8 @@ describe('Collection', () => {
it('should render connected component', () => {
const store = mockStore({
- collections: props.collections,
- entries: fromJS({}),
+ collections: baseProps.collections,
+ entries: fromJS({ pages: { entries: fromJS([]) } }),
});
const { asFragment } = renderWithRedux(, {
diff --git a/packages/decap-cms-core/src/components/Collection/__tests__/__snapshots__/Collection.spec.js.snap b/packages/decap-cms-core/src/components/Collection/__tests__/__snapshots__/Collection.spec.js.snap
index 0cb6a889449c..ff2f422cb077 100644
--- a/packages/decap-cms-core/src/components/Collection/__tests__/__snapshots__/Collection.spec.js.snap
+++ b/packages/decap-cms-core/src/components/Collection/__tests__/__snapshots__/Collection.spec.js.snap
@@ -64,11 +64,14 @@ exports[`Collection should render with collection with create url 1`] = `
>
+
-
@@ -98,12 +101,15 @@ exports[`Collection should render with collection with create url and path 1`] =
>
+
-
@@ -134,9 +140,12 @@ exports[`Collection should render with collection without create url 1`] = `
collection="Map { \\"name\\": \\"pages\\", \\"sortable_fields\\": List [], \\"view_filters\\": List [], \\"view_groups\\": List [], \\"create\\": false }"
newentryurl=""
/>
-
+
diff --git a/packages/decap-cms-core/src/constants/configSchema.js b/packages/decap-cms-core/src/constants/configSchema.js
index df7256268257..23f9380bdc85 100644
--- a/packages/decap-cms-core/src/constants/configSchema.js
+++ b/packages/decap-cms-core/src/constants/configSchema.js
@@ -290,6 +290,9 @@ function getConfigSchema() {
minProperties: 1,
},
i18n: i18nCollection,
+ limit: {
+ type: 'number',
+ },
},
required: ['name', 'label'],
oneOf: [{ required: ['files'] }, { required: ['folder', 'fields'] }],
diff --git a/packages/decap-cms-core/src/reducers/index.ts b/packages/decap-cms-core/src/reducers/index.ts
index 946761e2f44f..44b5e10e1018 100644
--- a/packages/decap-cms-core/src/reducers/index.ts
+++ b/packages/decap-cms-core/src/reducers/index.ts
@@ -2,19 +2,20 @@ import { List } from 'immutable';
import auth from './auth';
import config from './config';
+import collections, * as fromCollections from './collections';
import integrations, * as fromIntegrations from './integrations';
import entries, * as fromEntries from './entries';
import cursors from './cursors';
import editorialWorkflow, * as fromEditorialWorkflow from './editorialWorkflow';
import entryDraft from './entryDraft';
-import collections from './collections';
-import search from './search';
import medias from './medias';
import mediaLibrary from './mediaLibrary';
import deploys, * as fromDeploys from './deploys';
import globalUI from './globalUI';
+import search from './search';
import status from './status';
import notifications from './notifications';
+import { FOLDER } from '../constants/collectionTypes';
import type { Status } from '../constants/publishModes';
import type { State, Collection } from '../types/redux';
@@ -42,8 +43,8 @@ export default reducers;
/*
* Selectors
*/
-export function selectEntry(state: State, collection: string, slug: string) {
- return fromEntries.selectEntry(state.entries, collection, slug);
+export function selectEntry(state: State, collectionName: string, slug: string) {
+ return fromEntries.selectEntry(state.entries, collectionName, slug);
}
export function selectEntries(state: State, collection: Collection) {
@@ -77,6 +78,29 @@ export function selectUnpublishedSlugs(state: State, collection: string) {
return fromEditorialWorkflow.selectUnpublishedSlugs(state.editorialWorkflow, collection);
}
+export function selectCanCreateNewEntry(state: State, collectionName: string) {
+ const collection = state.collections.get(collectionName);
+
+ if (!collection || !collection.get('create')) {
+ return false;
+ }
+
+ if (collection.get('type') !== FOLDER) {
+ return fromCollections.selectAllowNewEntries(collection);
+ }
+
+ const limit = collection.get('limit') as number | undefined;
+
+ if (limit === undefined || limit === null) {
+ return true;
+ }
+
+ const entries = fromEntries.selectEntries(state.entries, collection);
+ const entryCount = entries ? entries.size : 0;
+
+ return entryCount < limit;
+}
+
export function selectIntegration(state: State, collection: string | null, hook: string) {
return fromIntegrations.selectIntegration(state.integrations, collection, hook);
}
diff --git a/packages/decap-cms-core/src/types/redux.ts b/packages/decap-cms-core/src/types/redux.ts
index c41bbdd18bb6..76fc3cf1e5f1 100644
--- a/packages/decap-cms-core/src/types/redux.ts
+++ b/packages/decap-cms-core/src/types/redux.ts
@@ -350,6 +350,7 @@ export interface CmsCollection {
view_filters?: ViewFilter[];
view_groups?: ViewGroup[];
i18n?: boolean | CmsI18nConfig;
+ limit?: number;
/**
* @deprecated Use sortable_fields instead
@@ -639,6 +640,7 @@ type CollectionObject = {
nested?: Nested;
meta?: Meta;
i18n: i18n;
+ limit?: number;
};
export type Collection = StaticallyTypedRecord;