diff --git a/package-lock.json b/package-lock.json index c9700417c..4b05c059f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15209,7 +15209,7 @@ "name": "cms-starter", "version": "0.0.0", "dependencies": { - "framer-plugin": "^3.3.0", + "framer-plugin": "^3.3.1", "react": "^18.3.1", "react-dom": "^18.3.1" }, @@ -15424,9 +15424,9 @@ } }, "starters/cms/node_modules/framer-plugin": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/framer-plugin/-/framer-plugin-3.3.0.tgz", - "integrity": "sha512-8zrW63Oml0tUFNHoKjA55OJg2YyXCyeL9D5Y8lSZ334A45kOJOwdRJK731iGdtW/7PjctLRYa5IP/XQ3iGHEYw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/framer-plugin/-/framer-plugin-3.3.1.tgz", + "integrity": "sha512-uIPLoGS/0wQqhl2dnnsNLs/b4h/vLI1Tvu8FeUt3VBhNwUSu1FO8SsSsQyncq9L2EsbiBP3Mnz7lK9jB/b2DYg==", "peerDependencies": { "react": "^18.2.0", "react-dom": "^18.2.0" diff --git a/starters/cms/package.json b/starters/cms/package.json index a2ce10845..b20e26c78 100644 --- a/starters/cms/package.json +++ b/starters/cms/package.json @@ -14,7 +14,7 @@ "typecheck": "tsc" }, "dependencies": { - "framer-plugin": "^3.3.0", + "framer-plugin": "^3.3.1", "react": "^18.3.1", "react-dom": "^18.3.1" }, diff --git a/starters/cms/src/FieldMapping.tsx b/starters/cms/src/FieldMapping.tsx index cc5c49f45..51eabf713 100644 --- a/starters/cms/src/FieldMapping.tsx +++ b/starters/cms/src/FieldMapping.tsx @@ -1,6 +1,6 @@ -import { type ManagedCollectionFieldInput, framer, type ManagedCollection } from "framer-plugin" +import { type ManagedCollectionFieldInput, framer, type ManagedCollection, useIsAllowedTo } from "framer-plugin" import { useEffect, useState } from "react" -import { type DataSource, dataSourceOptions, mergeFieldsWithExistingFields, syncCollection } from "./data" +import { type DataSource, dataSourceOptions, mergeFieldsWithExistingFields, syncCollection, syncMethods } from "./data" interface FieldMappingRowProps { field: ManagedCollectionFieldInput @@ -8,6 +8,7 @@ interface FieldMappingRowProps { isIgnored: boolean onToggleDisabled: (fieldId: string) => void onNameChange: (fieldId: string, name: string) => void + disabled: boolean } function FieldMappingRow({ @@ -16,6 +17,7 @@ function FieldMappingRow({ isIgnored, onToggleDisabled, onNameChange, + disabled, }: FieldMappingRowProps) { return ( <> @@ -23,6 +25,7 @@ function FieldMappingRow({ type="button" className={`source-field ${isIgnored ? "ignored" : ""}`} onClick={() => onToggleDisabled(field.id)} + disabled={disabled} > {originalFieldName ?? field.id} @@ -40,7 +43,7 @@ function FieldMappingRow({ onNameChange(field.id, event.target.value)} @@ -136,6 +139,8 @@ export function FieldMapping({ collection, dataSource, initialSlugFieldId }: Fie }) } + const isAllowedToManage = useIsAllowedTo("ManagedCollection.setFields", ...syncMethods) + const handleSubmit = async (event: React.FormEvent) => { event.preventDefault() @@ -150,8 +155,11 @@ export function FieldMapping({ collection, dataSource, initialSlugFieldId }: Fie try { setStatus("syncing-collection") - const fieldsToSync = fields.filter(field => !ignoredFieldIds.has(field.id)) + const fieldsToSync = fields + .filter(field => !ignoredFieldIds.has(field.id)) + .map(field => ({ ...field, name: field.name.trim() || field.id })) + await collection.setFields(fieldsToSync) await syncCollection(collection, dataSource, fieldsToSync, selectedSlugField) await framer.closePlugin("Synchronization successful", { variant: "success" }) } catch (error) { @@ -189,6 +197,7 @@ export function FieldMapping({ collection, dataSource, initialSlugFieldId }: Fie if (!selectedField) return setSelectedSlugField(selectedField) }} + disabled={!isAllowedToManage} > {possibleSlugFields.map(possibleSlugField => { return ( @@ -211,13 +220,18 @@ export function FieldMapping({ collection, dataSource, initialSlugFieldId }: Fie isIgnored={ignoredFieldIds.has(field.id)} onToggleDisabled={toggleFieldDisabledState} onNameChange={changeFieldName} + disabled={!isAllowedToManage} /> ))} diff --git a/starters/cms/src/data.ts b/starters/cms/src/data.ts index 24daa29e2..8d8069314 100644 --- a/starters/cms/src/data.ts +++ b/starters/cms/src/data.ts @@ -4,6 +4,7 @@ import { framer, type ManagedCollection, type ManagedCollectionItemInput, + type ProtectedMethod, } from "framer-plugin" export const PLUGIN_KEYS = { @@ -101,11 +102,6 @@ export async function syncCollection( fields: readonly ManagedCollectionFieldInput[], slugField: ManagedCollectionFieldInput ) { - const sanitizedFields = fields.map(field => ({ - ...field, - name: field.name.trim() || field.id, - })) - const items: ManagedCollectionItemInput[] = [] const unsyncedItems = new Set(await collection.getItemIds()) @@ -123,7 +119,7 @@ export async function syncCollection( const fieldData: FieldDataInput = {} for (const [fieldName, value] of Object.entries(item)) { - const field = sanitizedFields.find(field => field.id === fieldName) + const field = fields.find(field => field.id === fieldName) // Field is in the data but skipped based on selected fields. if (!field) continue @@ -141,7 +137,6 @@ export async function syncCollection( }) } - await collection.setFields(sanitizedFields) await collection.removeItems(Array.from(unsyncedItems)) await collection.addItems(items) @@ -149,6 +144,12 @@ export async function syncCollection( await collection.setPluginData(PLUGIN_KEYS.SLUG_FIELD_ID, slugField.id) } +export const syncMethods = [ + "ManagedCollection.removeItems", + "ManagedCollection.addItems", + "ManagedCollection.setPluginData", +] as const satisfies ProtectedMethod[] + export async function syncExistingCollection( collection: ManagedCollection, previousDataSourceId: string | null, @@ -162,6 +163,10 @@ export async function syncExistingCollection( return { didSync: false } } + if (!framer.isAllowedTo(...syncMethods)) { + return { didSync: false } + } + try { const dataSource = await getDataSource(previousDataSourceId) const existingFields = await collection.getFields()