@stackbit/cms-core 1.0.10-develop.1 → 1.0.10-develop.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/consts.d.ts +1 -0
  3. package/dist/consts.d.ts.map +1 -1
  4. package/dist/consts.js +2 -1
  5. package/dist/consts.js.map +1 -1
  6. package/dist/content-store.d.ts +25 -12
  7. package/dist/content-store.d.ts.map +1 -1
  8. package/dist/content-store.js +259 -40
  9. package/dist/content-store.js.map +1 -1
  10. package/dist/types/content-store-documents.d.ts +2 -0
  11. package/dist/types/content-store-documents.d.ts.map +1 -1
  12. package/dist/types/content-store-types.d.ts +2 -1
  13. package/dist/types/content-store-types.d.ts.map +1 -1
  14. package/dist/types/search-filter.d.ts +6 -3
  15. package/dist/types/search-filter.d.ts.map +1 -1
  16. package/dist/utils/csi-to-api-docs-converter.d.ts +2 -1
  17. package/dist/utils/csi-to-api-docs-converter.d.ts.map +1 -1
  18. package/dist/utils/csi-to-api-docs-converter.js +2 -2
  19. package/dist/utils/csi-to-api-docs-converter.js.map +1 -1
  20. package/dist/utils/csi-to-store-docs-converter.d.ts.map +1 -1
  21. package/dist/utils/csi-to-store-docs-converter.js +3 -1
  22. package/dist/utils/csi-to-store-docs-converter.js.map +1 -1
  23. package/dist/utils/document-utils.d.ts +5 -0
  24. package/dist/utils/document-utils.d.ts.map +1 -0
  25. package/dist/utils/document-utils.js +46 -0
  26. package/dist/utils/document-utils.js.map +1 -0
  27. package/dist/utils/filtered-entities.d.ts +34 -0
  28. package/dist/utils/filtered-entities.d.ts.map +1 -0
  29. package/dist/utils/filtered-entities.js +177 -0
  30. package/dist/utils/filtered-entities.js.map +1 -0
  31. package/dist/utils/model-utils.d.ts +3 -0
  32. package/dist/utils/model-utils.d.ts.map +1 -1
  33. package/dist/utils/model-utils.js +8 -1
  34. package/dist/utils/model-utils.js.map +1 -1
  35. package/dist/utils/remove-hidden.d.ts +29 -0
  36. package/dist/utils/remove-hidden.d.ts.map +1 -0
  37. package/dist/utils/remove-hidden.js +199 -0
  38. package/dist/utils/remove-hidden.js.map +1 -0
  39. package/dist/utils/search-utils.d.ts.map +1 -1
  40. package/dist/utils/search-utils.js +72 -20
  41. package/dist/utils/search-utils.js.map +1 -1
  42. package/dist/utils/site-map.d.ts.map +1 -1
  43. package/dist/utils/site-map.js.map +1 -1
  44. package/dist/utils/store-to-csi-docs-converter.d.ts.map +1 -1
  45. package/dist/utils/store-to-csi-docs-converter.js +1 -0
  46. package/dist/utils/store-to-csi-docs-converter.js.map +1 -1
  47. package/dist/utils/tree-views.d.ts +8 -0
  48. package/dist/utils/tree-views.d.ts.map +1 -1
  49. package/dist/utils/tree-views.js +32 -1
  50. package/dist/utils/tree-views.js.map +1 -1
  51. package/package.json +5 -5
  52. package/src/consts.ts +2 -0
  53. package/src/content-store.ts +278 -39
  54. package/src/types/content-store-documents.ts +2 -0
  55. package/src/types/content-store-types.ts +3 -1
  56. package/src/types/search-filter.ts +7 -3
  57. package/src/utils/csi-to-api-docs-converter.ts +4 -2
  58. package/src/utils/csi-to-store-docs-converter.ts +3 -2
  59. package/src/utils/document-utils.ts +39 -0
  60. package/src/utils/filtered-entities.ts +247 -0
  61. package/src/utils/model-utils.ts +7 -0
  62. package/src/utils/search-utils.ts +94 -21
  63. package/src/utils/site-map.ts +0 -1
  64. package/src/utils/store-to-csi-docs-converter.ts +1 -0
  65. package/src/utils/tree-views.ts +43 -0
  66. package/dist/utils/custom-search-filters.d.ts +0 -12
  67. package/dist/utils/custom-search-filters.d.ts.map +0 -1
  68. package/dist/utils/custom-search-filters.js +0 -46
  69. package/dist/utils/custom-search-filters.js.map +0 -1
@@ -59,7 +59,7 @@ import { mapAssetsToLocalizedApiImages, mapDocumentsToLocalizedApiObjects, mapSt
59
59
  import { mapDocumentsToApiDocuments } from './utils/store-to-api-v2-docs-converter';
60
60
  import { convertOperationField, createDocumentRecursively, CreateDocumentThunk, getCreateDocumentThunk } from './utils/create-update-csi-docs';
61
61
  import { mergeObjectWithDocument } from './utils/duplicate-document';
62
- import { normalizeModels, validateModels } from './utils/model-utils';
62
+ import { getModelMap, normalizeModels, validateModels } from './utils/model-utils';
63
63
  import { IMAGE_MODEL } from './common/common-schema';
64
64
  import { getDocumentObjectFromPreset, getPresetFromDocument } from './utils/preset-utils';
65
65
  import { BackCompatContentSourceInterface, backwardCompatibleContentSource } from './utils/backward-compatibility';
@@ -68,7 +68,7 @@ import { GitService } from './services';
68
68
  import { getAssetSourcesForClient } from './utils/asset-sources-utils';
69
69
  import { deleteDocumentHooked, publishDocumentHooked, updateDocumentHooked } from './utils/document-hooks';
70
70
  import { resolveCustomActionsById, getGlobalAndBulkAPIActions, runCustomAction } from './utils/custom-actions';
71
- import { getSanitizedTreeViews } from './utils/tree-views';
71
+ import { getSanitizedTreeViews, removeHiddenTreeViews } from './utils/tree-views';
72
72
  import { getModelFieldAtFieldPath } from './utils/field-path-utils';
73
73
  import {
74
74
  logCreateDocument,
@@ -82,6 +82,8 @@ import {
82
82
  import { ContentEngine, PluginRef, contentEngine } from 'content-engine';
83
83
  import { mapDocumentVersionsToApiDocumentVersions } from './utils/csi-to-api-docs-converter';
84
84
  import { NoopFileCache, FileCache } from './utils/file-cache';
85
+ import { getContentSourceFilteredModelsForUser, getFilteredAssetsForUser, getFilteredDocumentsForUser } from './utils/filtered-entities';
86
+ import { STACKBIT_PRESET_MODEL_NAME } from './consts';
85
87
 
86
88
  export type HandleConfigAssets = <T extends Model>({ models, presets }: { models?: T[]; presets?: PresetMap }) => Promise<{ models: T[]; presets: PresetMap }>;
87
89
 
@@ -129,8 +131,6 @@ type ContentStoreEvent =
129
131
  contentChanges: CSITypes.ContentChanges;
130
132
  };
131
133
 
132
- export const StackbitPresetModelName = 'stackbitPreset';
133
-
134
134
  export class ContentStore {
135
135
  private readonly logger: CSITypes.Logger;
136
136
  private readonly userLogger: CSITypes.Logger;
@@ -389,7 +389,7 @@ export class ContentStore {
389
389
  const presets = _.reduce(
390
390
  contentSourceData.csiDocuments,
391
391
  (result: Record<string, Preset>, csiDocument) => {
392
- if (csiDocument.modelName === StackbitPresetModelName) {
392
+ if (csiDocument.modelName === STACKBIT_PRESET_MODEL_NAME) {
393
393
  const preset = getPresetFromDocument({
394
394
  srcType: contentSourceData.srcType,
395
395
  srcProjectId: contentSourceData.srcProjectId,
@@ -511,7 +511,7 @@ export class ContentStore {
511
511
  // find first content source that supports presets
512
512
  for (let i = 0; i < contentSources.length; i++) {
513
513
  const contentSourceDataRaw = contentSourceRawDataArr[i];
514
- if (contentSourceDataRaw?.csiModelMap?.[StackbitPresetModelName]) {
514
+ if (contentSourceDataRaw?.csiModelMap?.[STACKBIT_PRESET_MODEL_NAME]) {
515
515
  this.presetsContentSource = contentSourceDataRaw.instance;
516
516
  // reload presets from content source only if needed
517
517
  if (init || !contentSourceIds || contentSourceIds.includes(contentSourceDataRaw.id)) {
@@ -1064,7 +1064,7 @@ export class ContentStore {
1064
1064
  contentChangesFull.deletedDocumentIds.forEach((docId) => {
1065
1065
  // remove preset, make sure there is something to remove first because
1066
1066
  // were explicitly calling onContentChange from deletePreset as well
1067
- if (this.presets[docId] && contentSourceData.csiDocumentMap[docId]?.modelName === StackbitPresetModelName) {
1067
+ if (this.presets[docId] && contentSourceData.csiDocumentMap[docId]?.modelName === STACKBIT_PRESET_MODEL_NAME) {
1068
1068
  presetsUpdated = true;
1069
1069
  const preset = this.presets[docId]!;
1070
1070
  const model = contentSourceData.modelMap[preset.modelName];
@@ -1194,7 +1194,7 @@ export class ContentStore {
1194
1194
  contentSourceData.documents.splice(dataIndex, 1, document);
1195
1195
  contentSourceData.csiDocuments.splice(dataIndex, 1, csiDocument);
1196
1196
  }
1197
- if (csiDocument.modelName === StackbitPresetModelName) {
1197
+ if (csiDocument.modelName === STACKBIT_PRESET_MODEL_NAME) {
1198
1198
  presetsUpdated = true;
1199
1199
  const preset = getPresetFromDocument({
1200
1200
  srcType: contentSourceData.srcType,
@@ -1461,17 +1461,32 @@ export class ContentStore {
1461
1461
  return getAssetSourcesForClient(this.stackbitConfig);
1462
1462
  }
1463
1463
 
1464
- getModels(): Record<string, Record<string, Record<string, Model | ImageModel>>> {
1464
+ getModels({ user }: { user?: ContentStoreTypes.User } = {}): Record<string, Record<string, Record<string, Model | ImageModel>>> {
1465
+ const configDelegate = createConfigDelegate({
1466
+ contentSourceDataById: this.contentSourceDataById,
1467
+ logger: this.userLogger
1468
+ });
1465
1469
  return _.reduce(
1466
1470
  this.contentSourceDataById,
1467
1471
  (result: Record<string, Record<string, Record<string, Model | ImageModel>>>, contentSourceData) => {
1468
1472
  const contentSourceType = contentSourceData.instance.getContentSourceType();
1469
1473
  const srcProjectId = contentSourceData.instance.getProjectId();
1470
- const filteredModels = _.omitBy(contentSourceData.modelMap, (model) => model.name === StackbitPresetModelName);
1474
+
1475
+ const filteredModels = getContentSourceFilteredModelsForUser({
1476
+ user,
1477
+ configDelegate,
1478
+ contentSourceData,
1479
+ filterModel: this.stackbitConfig?.filterModel
1480
+ });
1481
+
1482
+ const modelsMap = getModelMap({ models: filteredModels });
1483
+
1471
1484
  // if `projectId` is number (even as string) e.g., '1234', _.set() will create an array of length 1235 and insert the item at the end.
1472
1485
  // _.setWith(..., Object) ensures the values are always created as object keys, not as array indexes.
1473
- _.setWith(result, [contentSourceType, srcProjectId], filteredModels, Object);
1486
+
1487
+ _.setWith(result, [contentSourceType, srcProjectId], modelsMap, Object);
1474
1488
  _.setWith(result, [contentSourceType, srcProjectId, '__image_model'], IMAGE_MODEL, Object);
1489
+
1475
1490
  return result;
1476
1491
  },
1477
1492
  {}
@@ -1661,13 +1676,45 @@ export class ContentStore {
1661
1676
  };
1662
1677
  }
1663
1678
 
1664
- getSiteMapEntries({ locale }: { locale?: string } = {}): CSITypes.SiteMapEntry[] {
1679
+ getSiteMapEntries({ locale, user }: { locale?: string; user?: ContentStoreTypes.User } = {}): CSITypes.SiteMapEntry[] {
1665
1680
  const siteMapEntries = _.reduce(
1666
1681
  this.siteMapEntryGroups,
1667
1682
  (accum: CSITypes.SiteMapEntry[], siteMapEntryGroup) => {
1668
1683
  return _.reduce(
1669
1684
  siteMapEntryGroup,
1670
1685
  (accum: CSITypes.SiteMapEntry[], siteMapEntry) => {
1686
+ if (!_.isEmpty(locale)) {
1687
+ // filter out in wrong locale
1688
+ if (siteMapEntry.locale && siteMapEntry.locale !== locale) {
1689
+ return accum;
1690
+ }
1691
+ }
1692
+ if ('document' in siteMapEntry) {
1693
+ // check for hidden documents
1694
+ const contentSourceId = getContentSourceId(siteMapEntry.document.srcType, siteMapEntry.document.srcProjectId);
1695
+ const document = getContentSourceDataByIdOrThrow(contentSourceId, this.contentSourceDataById)?.documentMap[
1696
+ siteMapEntry.document.id
1697
+ ];
1698
+ if (document) {
1699
+ const [filteredDocument] = getFilteredDocumentsForUser({
1700
+ user,
1701
+ documents: [document],
1702
+ filterModel: this.stackbitConfig?.filterModel,
1703
+ filterDocument: this.stackbitConfig?.filterDocument,
1704
+ contentSourceDataById: this.contentSourceDataById,
1705
+ assetSources: this.stackbitConfig?.assetSources ?? [],
1706
+ createConfigDelegate: getCreateConfigDelegateThunk({
1707
+ getContentSourceDataById: () => this.contentSourceDataById,
1708
+ logger: this.userLogger
1709
+ }),
1710
+ logger: this.userLogger,
1711
+ customActionRunStateMap: this.customActionRunStateMap
1712
+ });
1713
+ if (filteredDocument && filteredDocument.hidden) {
1714
+ return accum;
1715
+ }
1716
+ }
1717
+ }
1671
1718
  if (!siteMapEntry.label) {
1672
1719
  const fieldLabelValue = getDocumentFieldLabelValueForSiteMapEntry({
1673
1720
  siteMapEntry,
@@ -1688,11 +1735,51 @@ export class ContentStore {
1688
1735
  []
1689
1736
  );
1690
1737
 
1691
- return _.isEmpty(locale) ? siteMapEntries : siteMapEntries.filter((siteMapEntry) => !siteMapEntry.locale || siteMapEntry.locale === locale);
1738
+ if (user && this.stackbitConfig?.transformSitemap) {
1739
+ const configDelegate = createConfigDelegate({
1740
+ contentSourceDataById: this.contentSourceDataById,
1741
+ logger: this.userLogger
1742
+ });
1743
+ return this.stackbitConfig.transformSitemap({ ...configDelegate, sitemap: _.cloneDeep(siteMapEntries), userContext: user });
1744
+ }
1745
+
1746
+ return siteMapEntries;
1692
1747
  }
1693
1748
 
1694
- getTreeViews(): CSITypes.TreeViewNode[] {
1695
- return this.treeViews;
1749
+ getTreeViews({ user }: { user?: ContentStoreTypes.User } = {}): CSITypes.TreeViewNode[] {
1750
+ let treeViews = this.treeViews;
1751
+ if (this.stackbitConfig?.transformTreeViews && user) {
1752
+ const configDelegate = createConfigDelegate({
1753
+ contentSourceDataById: this.contentSourceDataById,
1754
+ logger: this.userLogger
1755
+ });
1756
+ treeViews = this.stackbitConfig.transformTreeViews({ ...configDelegate, treeViews: _.cloneDeep(treeViews), userContext: user });
1757
+ }
1758
+ return removeHiddenTreeViews({
1759
+ treeViews,
1760
+ getDocumentForUser: (opts) => {
1761
+ const contentSourceId = getContentSourceId(opts.srcType, opts.srcProjectId);
1762
+ const document = getContentSourceDataByIdOrThrow(contentSourceId, this.contentSourceDataById)?.documentMap[opts.srcDocumentId];
1763
+ if (!document || !user) {
1764
+ return document;
1765
+ }
1766
+ const [filteredDocument] = getFilteredDocumentsForUser({
1767
+ user,
1768
+ documents: [document],
1769
+ filterModel: this.stackbitConfig?.filterModel,
1770
+ filterDocument: this.stackbitConfig?.filterDocument,
1771
+ contentSourceDataById: this.contentSourceDataById,
1772
+ assetSources: this.stackbitConfig?.assetSources ?? [],
1773
+ createConfigDelegate: getCreateConfigDelegateThunk({
1774
+ getContentSourceDataById: () => this.contentSourceDataById,
1775
+ logger: this.userLogger
1776
+ }),
1777
+ logger: this.userLogger,
1778
+ customActionRunStateMap: this.customActionRunStateMap
1779
+ });
1780
+ return filteredDocument;
1781
+ }
1782
+ });
1696
1783
  }
1697
1784
 
1698
1785
  getSiteMapEntriesForDocument({
@@ -1719,15 +1806,35 @@ export class ContentStore {
1719
1806
  getDocument({
1720
1807
  srcDocumentId,
1721
1808
  srcProjectId,
1722
- srcType
1809
+ srcType,
1810
+ user
1723
1811
  }: {
1724
1812
  srcDocumentId: string;
1725
1813
  srcProjectId: string;
1726
1814
  srcType: string;
1815
+ user?: ContentStoreTypes.User;
1727
1816
  }): ContentStoreTypes.Document | undefined {
1728
1817
  const contentSourceId = getContentSourceId(srcType, srcProjectId);
1729
1818
  const contentSourceData = this.getContentSourceDataByIdOrThrow(contentSourceId);
1730
- return contentSourceData.documentMap[srcDocumentId];
1819
+ const document = contentSourceData.documentMap[srcDocumentId];
1820
+ if (!document) {
1821
+ return document;
1822
+ }
1823
+ const [filteredDocument] = getFilteredDocumentsForUser({
1824
+ user,
1825
+ documents: [document],
1826
+ filterModel: this.stackbitConfig?.filterModel,
1827
+ filterDocument: this.stackbitConfig?.filterDocument,
1828
+ contentSourceDataById: this.contentSourceDataById,
1829
+ assetSources: this.stackbitConfig?.assetSources ?? [],
1830
+ createConfigDelegate: getCreateConfigDelegateThunk({
1831
+ getContentSourceDataById: () => this.contentSourceDataById,
1832
+ logger: this.userLogger
1833
+ }),
1834
+ logger: this.userLogger,
1835
+ customActionRunStateMap: this.customActionRunStateMap
1836
+ });
1837
+ return filteredDocument ?? document;
1731
1838
  }
1732
1839
 
1733
1840
  getDocumentsByContext({ context, srcProjectId, srcType }: { context: any; srcProjectId?: string; srcType: string }): ContentStoreTypes.Document[] {
@@ -1748,21 +1855,36 @@ export class ContentStore {
1748
1855
  );
1749
1856
  }
1750
1857
 
1751
- getDocuments({ locale }: { locale?: string } = {}): ContentStoreTypes.Document[] {
1752
- return _.reduce(
1858
+ getDocuments({ locale, user }: { locale?: string; user?: ContentStoreTypes.User } = {}): ContentStoreTypes.Document[] {
1859
+ const documents = _.reduce(
1753
1860
  this.contentSourceDataById,
1754
1861
  (documents: ContentStoreTypes.Document[], contentSourceData) => {
1755
1862
  const currentDocuments = _.isEmpty(locale)
1756
1863
  ? contentSourceData.documents
1757
1864
  : contentSourceData.documents.filter((document) => !document.locale || document.locale === locale);
1758
- const filteredDocuments = currentDocuments.filter((document) => document.srcModelName !== StackbitPresetModelName);
1865
+ const filteredDocuments = currentDocuments.filter((document) => document.srcModelName !== STACKBIT_PRESET_MODEL_NAME);
1759
1866
  return documents.concat(filteredDocuments);
1760
1867
  },
1761
1868
  []
1762
1869
  );
1870
+ const filteredDocuments = getFilteredDocumentsForUser({
1871
+ user,
1872
+ documents,
1873
+ filterModel: this.stackbitConfig?.filterModel,
1874
+ filterDocument: this.stackbitConfig?.filterDocument,
1875
+ contentSourceDataById: this.contentSourceDataById,
1876
+ assetSources: this.stackbitConfig?.assetSources ?? [],
1877
+ createConfigDelegate: getCreateConfigDelegateThunk({
1878
+ getContentSourceDataById: () => this.contentSourceDataById,
1879
+ logger: this.userLogger
1880
+ }),
1881
+ logger: this.userLogger,
1882
+ customActionRunStateMap: this.customActionRunStateMap
1883
+ });
1884
+ return filteredDocuments;
1763
1885
  }
1764
1886
 
1765
- getApiDocuments({ documentSpecs }: { documentSpecs?: DocumentSpecifier[] } = {}): {
1887
+ getApiDocuments({ documentSpecs, user }: { documentSpecs?: DocumentSpecifier[]; user?: ContentStoreTypes.User } = {}): {
1766
1888
  documents: ContentStoreTypes.APIDocument[];
1767
1889
  } {
1768
1890
  let filteredDocuments: ContentStoreTypes.Document[];
@@ -1771,7 +1893,7 @@ export class ContentStore {
1771
1893
  filteredDocuments = documentSpecs?.reduce((acc: ContentStoreTypes.Document[], docSpec) => {
1772
1894
  const contentSourceId = getContentSourceId(docSpec.srcType, docSpec.srcProjectId);
1773
1895
  const document = this.contentSourceDataById[contentSourceId]?.documentMap[docSpec.srcDocumentId];
1774
- if (document && document.srcModelName !== StackbitPresetModelName) {
1896
+ if (document && document.srcModelName !== STACKBIT_PRESET_MODEL_NAME) {
1775
1897
  acc.push(document);
1776
1898
  }
1777
1899
  return acc;
@@ -1781,12 +1903,27 @@ export class ContentStore {
1781
1903
  filteredDocuments = _.reduce(
1782
1904
  this.contentSourceDataById,
1783
1905
  (accDocuments: ContentStoreTypes.Document[], contentSourceData) => {
1784
- return accDocuments.concat(contentSourceData.documents.filter((document) => document.srcModelName !== StackbitPresetModelName));
1906
+ return accDocuments.concat(contentSourceData.documents.filter((document) => document.srcModelName !== STACKBIT_PRESET_MODEL_NAME));
1785
1907
  },
1786
1908
  []
1787
1909
  );
1788
1910
  }
1789
1911
 
1912
+ filteredDocuments = getFilteredDocumentsForUser({
1913
+ user,
1914
+ documents: filteredDocuments,
1915
+ filterModel: this.stackbitConfig?.filterModel,
1916
+ filterDocument: this.stackbitConfig?.filterDocument,
1917
+ contentSourceDataById: this.contentSourceDataById,
1918
+ assetSources: this.stackbitConfig?.assetSources ?? [],
1919
+ createConfigDelegate: getCreateConfigDelegateThunk({
1920
+ getContentSourceDataById: () => this.contentSourceDataById,
1921
+ logger: this.userLogger
1922
+ }),
1923
+ logger: this.userLogger,
1924
+ customActionRunStateMap: this.customActionRunStateMap
1925
+ });
1926
+
1790
1927
  return {
1791
1928
  documents: mapDocumentsToApiDocuments({
1792
1929
  documents: filteredDocuments,
@@ -1802,8 +1939,8 @@ export class ContentStore {
1802
1939
  return contentSourceData.assetMap[srcAssetId];
1803
1940
  }
1804
1941
 
1805
- getAssets({ locale }: { locale?: string }): ContentStoreTypes.Asset[] {
1806
- return _.reduce(
1942
+ getAssets({ locale, user }: { locale?: string; user?: ContentStoreTypes.User } = {}): ContentStoreTypes.Asset[] {
1943
+ const assets = _.reduce(
1807
1944
  this.contentSourceDataById,
1808
1945
  (assets: ContentStoreTypes.Asset[], contentSourceData) => {
1809
1946
  const currentAssets = _.isEmpty(locale)
@@ -1813,9 +1950,25 @@ export class ContentStore {
1813
1950
  },
1814
1951
  []
1815
1952
  );
1953
+ const filteredAssets = getFilteredAssetsForUser({
1954
+ user,
1955
+ assets,
1956
+ filterAsset: this.stackbitConfig?.filterAsset,
1957
+ contentSourceDataById: this.contentSourceDataById,
1958
+ configDelegate: createConfigDelegate({ contentSourceDataById: this.contentSourceDataById, logger: this.userLogger })
1959
+ });
1960
+ return filteredAssets;
1816
1961
  }
1817
1962
 
1818
- getLocalizedApiObjects({ locale, objectIds }: { locale?: string; objectIds?: string[] }): ContentStoreTypes.APIObject[] {
1963
+ getLocalizedApiObjects({
1964
+ locale,
1965
+ objectIds,
1966
+ user
1967
+ }: {
1968
+ locale?: string;
1969
+ objectIds?: string[];
1970
+ user?: ContentStoreTypes.User;
1971
+ }): ContentStoreTypes.APIObject[] {
1819
1972
  const hasExplicitLocale = !_.isEmpty(locale);
1820
1973
  return _.reduce(
1821
1974
  this.contentSourceDataById,
@@ -1824,10 +1977,36 @@ export class ContentStore {
1824
1977
  ? contentSourceData.documents.filter((document) => objectIds.includes(document.srcObjectId))
1825
1978
  : contentSourceData.documents;
1826
1979
  documents = hasExplicitLocale ? documents.filter((document) => !document.locale || document.locale === locale) : documents;
1980
+
1827
1981
  let assets = objectIds ? contentSourceData.assets.filter((asset) => objectIds.includes(asset.srcObjectId)) : contentSourceData.assets;
1828
1982
  assets = hasExplicitLocale ? assets.filter((asset) => !asset.locale || asset.locale === locale) : assets;
1983
+
1829
1984
  const currentLocale = locale ?? contentSourceData.defaultLocaleCode;
1830
- const filteredDocuments = documents.filter((document) => document.srcModelName !== StackbitPresetModelName);
1985
+ let filteredDocuments = documents.filter((document) => document.srcModelName !== STACKBIT_PRESET_MODEL_NAME);
1986
+
1987
+ filteredDocuments = getFilteredDocumentsForUser({
1988
+ user,
1989
+ documents: filteredDocuments,
1990
+ filterModel: this.stackbitConfig?.filterModel,
1991
+ filterDocument: this.stackbitConfig?.filterDocument,
1992
+ contentSourceDataById: this.contentSourceDataById,
1993
+ assetSources: this.stackbitConfig?.assetSources ?? [],
1994
+ createConfigDelegate: getCreateConfigDelegateThunk({
1995
+ getContentSourceDataById: () => this.contentSourceDataById,
1996
+ logger: this.userLogger
1997
+ }),
1998
+ logger: this.userLogger,
1999
+ customActionRunStateMap: this.customActionRunStateMap
2000
+ });
2001
+
2002
+ assets = getFilteredAssetsForUser({
2003
+ user,
2004
+ assets,
2005
+ filterAsset: this.stackbitConfig?.filterAsset,
2006
+ contentSourceDataById: this.contentSourceDataById,
2007
+ configDelegate: createConfigDelegate({ contentSourceDataById: this.contentSourceDataById, logger: this.userLogger })
2008
+ });
2009
+
1831
2010
  const documentObjects = mapDocumentsToLocalizedApiObjects({
1832
2011
  documents: filteredDocuments,
1833
2012
  locale: currentLocale,
@@ -1845,8 +2024,9 @@ export class ContentStore {
1845
2024
  srcProjectId,
1846
2025
  pageSize = 20,
1847
2026
  pageNum = 1,
1848
- searchQuery
1849
- }: { srcType?: string; srcProjectId?: string; pageSize?: number; pageNum?: number; searchQuery?: string } = {}): {
2027
+ searchQuery,
2028
+ user
2029
+ }: { srcType?: string; srcProjectId?: string; pageSize?: number; pageNum?: number; searchQuery?: string; user?: ContentStoreTypes.User } = {}): {
1850
2030
  assets: ContentStoreTypes.APIAsset[];
1851
2031
  pageSize: number;
1852
2032
  pageNum: number;
@@ -1856,12 +2036,26 @@ export class ContentStore {
1856
2036
  if (srcProjectId && srcType) {
1857
2037
  const contentSourceId = getContentSourceId(srcType, srcProjectId);
1858
2038
  const contentSourceData = this.getContentSourceDataByIdOrThrow(contentSourceId);
1859
- assets = mapStoreAssetsToAPIAssets(contentSourceData.assets, this.staticAssetsPublicPath, contentSourceData.defaultLocaleCode);
2039
+ const filteredAssets = getFilteredAssetsForUser({
2040
+ user,
2041
+ assets: contentSourceData.assets,
2042
+ filterAsset: this.stackbitConfig?.filterAsset,
2043
+ contentSourceDataById: this.contentSourceDataById,
2044
+ configDelegate: createConfigDelegate({ contentSourceDataById: this.contentSourceDataById, logger: this.userLogger })
2045
+ });
2046
+ assets = mapStoreAssetsToAPIAssets(filteredAssets, this.staticAssetsPublicPath, contentSourceData.defaultLocaleCode);
1860
2047
  } else {
1861
2048
  assets = _.reduce(
1862
2049
  this.contentSourceDataById,
1863
2050
  (result: ContentStoreTypes.APIAsset[], contentSourceData) => {
1864
- const assets = mapStoreAssetsToAPIAssets(contentSourceData.assets, this.staticAssetsPublicPath, contentSourceData.defaultLocaleCode);
2051
+ const filteredAssets = getFilteredAssetsForUser({
2052
+ user,
2053
+ assets: contentSourceData.assets,
2054
+ filterAsset: this.stackbitConfig?.filterAsset,
2055
+ contentSourceDataById: this.contentSourceDataById,
2056
+ configDelegate: createConfigDelegate({ contentSourceDataById: this.contentSourceDataById, logger: this.userLogger })
2057
+ });
2058
+ const assets = mapStoreAssetsToAPIAssets(filteredAssets, this.staticAssetsPublicPath, contentSourceData.defaultLocaleCode);
1865
2059
  return result.concat(assets);
1866
2060
  },
1867
2061
  []
@@ -2051,9 +2245,9 @@ export class ContentStore {
2051
2245
  const document = await this.createDocument({
2052
2246
  srcType: this.presetsContentSource.getContentSourceType(),
2053
2247
  srcProjectId: this.presetsContentSource.getProjectId(),
2054
- modelName: StackbitPresetModelName,
2248
+ modelName: STACKBIT_PRESET_MODEL_NAME,
2055
2249
  object: {
2056
- ...getDocumentObjectFromPreset(preset, contentSourceData.modelMap[StackbitPresetModelName]),
2250
+ ...getDocumentObjectFromPreset(preset, contentSourceData.modelMap[STACKBIT_PRESET_MODEL_NAME]),
2057
2251
  thumbnail
2058
2252
  },
2059
2253
  user
@@ -2720,11 +2914,13 @@ export class ContentStore {
2720
2914
  modelName: string;
2721
2915
  }>;
2722
2916
  locale?: string;
2917
+ user?: ContentStoreTypes.User;
2723
2918
  }): Promise<{
2724
2919
  total: number;
2725
2920
  items: ContentStoreTypes.Document[];
2726
2921
  }> {
2727
2922
  this.logger.debug('searchDocuments');
2923
+
2728
2924
  const locale = data.locale;
2729
2925
  const objectsBySourceId = _.groupBy(data.models, (object) => getContentSourceId(object.srcType, object.srcProjectId));
2730
2926
  const contentSourceIds = Object.keys(objectsBySourceId);
@@ -2742,9 +2938,28 @@ export class ContentStore {
2742
2938
  ? contentSourceData.documents
2743
2939
  : contentSourceData.documents.filter((document) => !document.locale || document.locale === locale);
2744
2940
  const contentSourceScheduledActions = contentSourceData.scheduledActions;
2745
- const filteredDocuments = contentSourceDocuments.filter((document) => document.srcModelName !== StackbitPresetModelName);
2746
- documents.push(...filteredDocuments);
2941
+ const filteredDocuments = contentSourceDocuments.filter((document) => document.srcModelName !== STACKBIT_PRESET_MODEL_NAME);
2942
+ const userDocuments = getFilteredDocumentsForUser({
2943
+ user: data.user,
2944
+ documents: filteredDocuments,
2945
+ filterModel: this.stackbitConfig?.filterModel,
2946
+ filterDocument: this.stackbitConfig?.filterDocument,
2947
+ contentSourceDataById: this.contentSourceDataById,
2948
+ assetSources: this.stackbitConfig?.assetSources ?? [],
2949
+ createConfigDelegate: getCreateConfigDelegateThunk({
2950
+ getContentSourceDataById: () => this.contentSourceDataById,
2951
+ logger: this.userLogger
2952
+ }),
2953
+ logger: this.userLogger,
2954
+ customActionRunStateMap: this.customActionRunStateMap
2955
+ });
2956
+
2957
+ // filter out hidden documents from the search
2958
+ const visibleDocuments = userDocuments.filter((document) => !document.hidden);
2959
+
2960
+ documents.push(...visibleDocuments);
2747
2961
  scheduledActions.push(...contentSourceScheduledActions);
2962
+
2748
2963
  if (contentSourceData.defaultLocaleCode) {
2749
2964
  defaultLocales[contentSourceId] = contentSourceData.defaultLocaleCode;
2750
2965
  }
@@ -2764,12 +2979,14 @@ export class ContentStore {
2764
2979
  srcType,
2765
2980
  srcProjectId,
2766
2981
  documentId,
2767
- locale
2982
+ locale,
2983
+ user
2768
2984
  }: {
2769
2985
  srcType: string;
2770
2986
  srcProjectId: string;
2771
2987
  documentId: string;
2772
2988
  locale?: string;
2989
+ user?: ContentStoreTypes.User;
2773
2990
  }): Promise<{ versions: ContentStoreTypes.APIDocumentVersion[] }> {
2774
2991
  const contentSourceId = getContentSourceId(srcType, srcProjectId);
2775
2992
  const contentSourceData = this.getContentSourceDataByIdOrThrow(contentSourceId);
@@ -2787,6 +3004,15 @@ export class ContentStore {
2787
3004
  }
2788
3005
 
2789
3006
  const { versions } = await contentSourceData.instance.getDocumentVersions({ documentId });
3007
+ const models = getContentSourceFilteredModelsForUser({
3008
+ user,
3009
+ contentSourceData,
3010
+ configDelegate: createConfigDelegate({
3011
+ contentSourceDataById: this.contentSourceDataById,
3012
+ logger: this.userLogger
3013
+ }),
3014
+ filterModel: this.stackbitConfig?.filterModel
3015
+ });
2790
3016
  const apiVersions = mapDocumentVersionsToApiDocumentVersions({
2791
3017
  assetSources: this.stackbitConfig?.assetSources ?? [],
2792
3018
  customActionRunStateMap: this.customActionRunStateMap,
@@ -2797,7 +3023,8 @@ export class ContentStore {
2797
3023
  versions,
2798
3024
  contentSourceData,
2799
3025
  contentSourceDataById: this.contentSourceDataById,
2800
- locale
3026
+ locale,
3027
+ modelMap: getModelMap({ models })
2801
3028
  });
2802
3029
 
2803
3030
  return { versions: apiVersions };
@@ -2808,13 +3035,15 @@ export class ContentStore {
2808
3035
  srcProjectId,
2809
3036
  documentId,
2810
3037
  versionId,
2811
- locale
3038
+ locale,
3039
+ user
2812
3040
  }: {
2813
3041
  srcType: string;
2814
3042
  srcProjectId: string;
2815
3043
  documentId: string;
2816
3044
  versionId: string;
2817
3045
  locale?: string;
3046
+ user?: ContentStoreTypes.User;
2818
3047
  }): Promise<{ version: ContentStoreTypes.APIDocumentVersionWithDocument }> {
2819
3048
  const contentSourceId = getContentSourceId(srcType, srcProjectId);
2820
3049
  const contentSourceData = this.getContentSourceDataByIdOrThrow(contentSourceId);
@@ -2832,6 +3061,15 @@ export class ContentStore {
2832
3061
  }
2833
3062
 
2834
3063
  const { version } = await contentSourceData.instance.getDocumentForVersion({ documentId, versionId });
3064
+ const models = getContentSourceFilteredModelsForUser({
3065
+ user,
3066
+ contentSourceData,
3067
+ configDelegate: createConfigDelegate({
3068
+ contentSourceDataById: this.contentSourceDataById,
3069
+ logger: this.userLogger
3070
+ }),
3071
+ filterModel: this.stackbitConfig?.filterModel
3072
+ });
2835
3073
  const [apiVersion] = mapDocumentVersionsToApiDocumentVersions({
2836
3074
  versions: [version],
2837
3075
  assetSources: this.stackbitConfig?.assetSources ?? [],
@@ -2842,7 +3080,8 @@ export class ContentStore {
2842
3080
  }),
2843
3081
  contentSourceData,
2844
3082
  locale,
2845
- contentSourceDataById: this.contentSourceDataById
3083
+ contentSourceDataById: this.contentSourceDataById,
3084
+ modelMap: getModelMap({ models })
2846
3085
  });
2847
3086
 
2848
3087
  if (!apiVersion || !apiVersion.object || !apiVersion.document || !apiVersion.apiDocument) {
@@ -17,6 +17,7 @@ export interface Document {
17
17
  srcModelName: string;
18
18
  srcModelLabel: string;
19
19
  isChanged: boolean;
20
+ hidden?: boolean;
20
21
  status: DocumentStatus;
21
22
  createdAt: string;
22
23
  createdBy?: string;
@@ -44,6 +45,7 @@ export interface Asset {
44
45
  srcModelName: string;
45
46
  srcModelLabel: string;
46
47
  isChanged: boolean;
48
+ hidden?: boolean;
47
49
  status: DocumentStatus;
48
50
  createdAt: string;
49
51
  createdBy?: string;
@@ -1,5 +1,5 @@
1
1
  import * as StackbitTypes from '@stackbit/types';
2
- import { Model } from '@stackbit/sdk';
2
+ import { ImageModel, Model } from '@stackbit/sdk';
3
3
  import { Asset, Document } from './content-store-documents';
4
4
  import { BackCompatContentSourceInterface } from '../utils/backward-compatibility';
5
5
 
@@ -121,3 +121,5 @@ export interface ContentStoreLocale {
121
121
  locales: string[];
122
122
  defaultLocale: string | undefined;
123
123
  }
124
+
125
+ export type RecordSchema = Record<string, Record<string, Record<string, Model | ImageModel>>>;
@@ -62,10 +62,14 @@ export type SearchFilterItem =
62
62
  | BetweenFilterItem
63
63
  | EmptyFilterItem;
64
64
 
65
- export type SearchFilter = LogicalOperator;
65
+ export type SearchFilterItemGrouped = SearchFilterItem | (LogicalAndOperator | LogicalOrOperator);
66
66
 
67
- type LogicalOperator = LogicalAndOperator;
67
+ export type SearchFilter = LogicalAndOperator | LogicalOrOperator;
68
68
 
69
69
  type LogicalAndOperator = {
70
- and: SearchFilterItem[];
70
+ and: SearchFilterItemGrouped[];
71
+ };
72
+
73
+ type LogicalOrOperator = {
74
+ or: SearchFilterItemGrouped[];
71
75
  };
@@ -12,6 +12,7 @@ type MapDocumentToApiProps = {
12
12
  customActionRunStateMap: ContentStoreTypes.CustomActionRunStateMap;
13
13
  createConfigDelegate: () => StackbitTypes.ConfigDelegate;
14
14
  contentSourceDataById: Record<string, ContentSourceData>;
15
+ modelMap: Record<string, StackbitTypes.Model>;
15
16
  };
16
17
 
17
18
  export function mapDocumentVersionsToApiDocumentVersions({
@@ -21,7 +22,8 @@ export function mapDocumentVersionsToApiDocumentVersions({
21
22
  assetSources,
22
23
  customActionRunStateMap,
23
24
  createConfigDelegate,
24
- contentSourceDataById
25
+ contentSourceDataById,
26
+ modelMap
25
27
  }: MapDocumentToApiProps & { versions: StackbitTypes.DocumentVersion[] }): ContentStoreTypes.APIDocumentVersion[] {
26
28
  return versions.map((version) => {
27
29
  if (!version.document) {
@@ -32,8 +34,8 @@ export function mapDocumentVersionsToApiDocumentVersions({
32
34
  const contentStoreDocuments = mapCSIDocumentsToStoreDocuments({
33
35
  csiDocuments: [version.document],
34
36
  contentSourceInstance: contentSourceData.instance,
35
- modelMap: contentSourceData.modelMap,
36
37
  defaultLocaleCode: contentSourceData.defaultLocaleCode,
38
+ modelMap,
37
39
  createConfigDelegate,
38
40
  customActionRunStateMap,
39
41
  assetSources,