[APPROVED] Duplicate a record

Hi - it would be useful if there were a way to easily duplicate a record.

One use case is, if you make a “template” record somewhere, with text you want to re-use frequently, you might want to easily copy-paste that.

Is a button the way to go for this?

4 Likes

Hopefully, since multi-select is a feature, this is not far behind.

1 Like

Would love to see some templating-type functionality alongside this as well!

1 Like

+1 ! We would love to implement “processes made of checklists” in Fibery, to link them to actual tasks & projects, but without the “duplicate” feature this does not really make sense. Any visibility on when this would be implemented ? Thanks !

Hi!
So, do you need to duplicate a record or more smth like a recurring process? Lik Projects with repetitive Tasks and etc.

1 Like

Hi, in our case cloning records will be enough, we will put checklists directly into records, with 1 record = 1 process

Then I can suggest you add a Duplicate a record button :slight_smile:
Here is the code you can use

const fibery = context.getService('fibery');
const schema = await fibery.getSchema();

await Promise.all(args.currentEntities.map(async (template) => {
    const type = template.type;

    const newEntityTemplate = schema.typeObjectsByName[type].fieldObjects
        .reduce((result, fieldObject) => {
            if (fieldObject.type === "fibery/Button" || fieldObject.type === "Collaboration~Documents/Document" || fieldObject.isReadOnly) {
                return result;
            } else if (fieldObject.typeObject.isPrimitive) {
                return { ...result, [fieldObject.title]: template[fieldObject.title] };
            } else if (fieldObject.typeObject.isEnum && fieldObject.cardinality === ":cardinality/many-to-one") {
                // Workaround for issue with setting null to enums
                const enumSetter = template[fieldObject.title].Name ? { [fieldObject.title]: template[fieldObject.title].Name } : {};
                return { ...result, ...enumSetter };
            } else if (fieldObject.typeObject.isPrimitive === false && fieldObject.cardinality === ":cardinality/many-to-one") {
                return { ...result, [fieldObject.title]: template[fieldObject.title].Id };
            } else {
                return result;
            }
        }, {});

    console.log(newEntityTemplate);
    // create new entity with some name
    // see https://api.fibery.io/#script
    const [{ result: newEntity }] = await fibery.createEntity(
        type,
        {
            ...newEntityTemplate,
            Rank: template.Rank + 10000,
            Name: `${template.Name} Copy`
        });

    // Copy multiselect enums
    const multiSelectEnumFields = schema.typeObjectsByName[type].fieldObjects
        .filter((fieldObject) => fieldObject.typeObject.isEnum && fieldObject.cardinality === ":cardinality/many-to-many" && fieldObject.isReadOnly === false);
    for (const multiSelectEnumField of multiSelectEnumFields) {
        if (template[multiSelectEnumField.title]) {
            await Promise.all(template[multiSelectEnumField.title].map((val) => fibery.addCollectionItem(type, newEntity["fibery/id"], multiSelectEnumField.name, val.Id)));
        }
    }

    // copy assignees
    const assigneesField = schema.typeObjectsByName[type].fieldObjects
        .find((fieldObject) => fieldObject.name === "assignments/assignees");
    if (assigneesField && template[assigneesField.title]) {
        await Promise.all(template[assigneesField.title].map((val) => fibery.addCollectionItem(type, newEntity["fibery/id"], assigneesField.name, val.Id)));
    }

    //At the moment we cannot set rich text fields using 'createEntity' api, so we set it using 'setDocumentContent' method
    const richTextFields = schema.typeObjectsByName[type].fieldObjects.filter(f => f.type === "Collaboration~Documents/Document");
    for (const richTextField of richTextFields) {
        const content = await fibery.getDocumentContent(template[richTextField.title].Secret, "json");
        if (!content) {
            continue;
        }
        // take deep details of newly created entity (mainly interested in description Secret)
        // see https://api.fibery.io/#get-entities
        // NOTE: unfortunately #script api doesn't support such deep query at the moment
        //       getEntity(id, ["id", ["Description", "Secret"]]) would make things much simplier
        const newEntityDetails = await fibery.executeSingleCommand(
            {
                "command": "fibery.entity/query",
                "args": {
                    "query": {
                        "q/from": type,
                        "q/select": {
                            "Id": "fibery/id",
                            "RichTextField": {
                                "Id": [richTextField.name, "fibery/id"],
                                "Secret": [richTextField.name, "Collaboration~Documents/secret"]
                            },
                        },
                        "q/where": ["=", ["fibery/id"], "$id"],
                        "q/limit": 1
                    },
                    "params": { "$id": newEntity["fibery/id"] }
                }
            });

        // fail if details object not found
        if (!newEntityDetails) {
            throw new Error(`Request for new entity details is failed`);
        }

        const newSecret = newEntityDetails[0].RichTextField.Secret;
        const result = await fibery.setDocumentContent(newSecret, content, 'json');
    }
}));