Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implemented CMS entry copy features #130

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 96 additions & 3 deletions platform/wab/src/wab/client/components/cms/CmsEntryDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
renderMaybeLocalizedInput,
} from "@/wab/client/components/cms/CmsInputs";
import { isCmsTextLike } from "@/wab/client/components/cms/utils";
import Button from "@/wab/client/components/widgets/Button";
import { Modal } from "@/wab/client/components/widgets/Modal";
import { useApi, useAppCtx } from "@/wab/client/contexts/AppContexts";
import {
DefaultCmsEntryDetailsProps,
Expand All @@ -33,7 +35,15 @@ import { spawn } from "@/wab/shared/common";
import { DEVFLAGS } from "@/wab/shared/devflags";
import { substituteUrlParams } from "@/wab/shared/utils/url-utils";
import { HTMLElementRefOf } from "@plasmicapp/react-web";
import { Drawer, Form, Menu, message, notification, Tooltip } from "antd";
import {
Drawer,
Form,
Input,
Menu,
message,
notification,
Tooltip,
} from "antd";
import { useForm } from "antd/lib/form/Form";
import { isEqual, isNil, mapValues, pickBy } from "lodash";
import * as React from "react";
Expand Down Expand Up @@ -175,6 +185,10 @@ function CmsEntryDetailsForm_(
const [hasUnpublishedChanges, setHasUnpublishedChanges] = React.useState(
!!row?.draftData
);
// Hyuna
const [showCopyModal, setShowCopyModal] = React.useState(false);
const [inputCopyIdentifier, setInputCopyIdentifier] = React.useState("");

const [revision, setRevision] = React.useState(row.revision);
const [inConflict, setInConflict] = React.useState(false);
const mutateRow_ = useMutateRow();
Expand Down Expand Up @@ -572,16 +586,30 @@ function CmsEntryDetailsForm_(
content: "Reverted.",
});
}}
disabled={isSaving || isPublishing}
disabled={isSaving}
>
<Tooltip title="Reverts draft data to previously-published data">
<span>Revert to published entry</span>
</Tooltip>
</Menu.Item>
)}
<Menu.Divider />
</>
)}
<Menu.Item
key="copy"
onClick={() => {
if (row.identifier) {
setInputCopyIdentifier(`Copy of ${row.identifier}`);
} else {
setInputCopyIdentifier("");
}
hyunnbunt marked this conversation as resolved.
Show resolved Hide resolved
setShowCopyModal(true);
}}
disabled={isSaving || isPublishing}
>
<span>Copy entry</span>
</Menu.Item>
<Menu.Divider />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove divider

<Menu.Item
key="delete"
onClick={async () => {
Expand Down Expand Up @@ -632,6 +660,71 @@ function CmsEntryDetailsForm_(
}}
/>
</Form>
{showCopyModal && (
<Modal
title="Duplicate CMS entry"
footer={null}
open={true}
onCancel={() => setShowCopyModal(false)}
>
<Form.Item name="copyIdentifier">
<span>
{`Duplicate the CMS entry with identifier "${inputCopyIdentifier}"?`}
<br />
{`This will create an unpublished copy of the entry and all its data.`}
</span>
<Input
defaultValue={inputCopyIdentifier}
onChange={(e) => setInputCopyIdentifier(e.target.value)}
/>
</Form.Item>
<Form.Item>
<Button
type="primary"
onClick={async (e) => {
e.currentTarget.disabled = true;
await message.loading({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the loading notification go before copyCmsRow so that it is shown at the right time?

Also, update the content to be Duplicating CMS entry "${identifier}..." and remove the duration.

key: "copy-message",
content: `Duplicating CMS entry "${
row.identifier ? row.identifier : "Untitled"
}"...`,
});
const copiedRow = await api.copyCmsRow(row.id, {
identifier: inputCopyIdentifier,
});
if (copiedRow) {
await message.success({
key: "copy-message",
content: (
<>
{`A duplicate of CMS entry
"${
copiedRow.identifier
? copiedRow.identifier
: "Untitled"
}"
has been created.`}
<br />
{`You are now viewing the duplicated entry.`}
</>
),
});
history.push(
UU.cmsEntry.fill({
databaseId: database.id,
tableId: table.id,
rowId: copiedRow.id,
})
);
}
}}
>
Copy
</Button>
<Button onClick={() => setShowCopyModal(false)}>Cancel</Button>
</Form.Item>
</Modal>
)}
</>
);
}
Expand Down
3 changes: 3 additions & 0 deletions platform/wab/src/wab/server/AppServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import {
import {
cloneDatabase,
cmsFileUpload,
copyRow,
createDatabase,
createRows,
createTable,
Expand Down Expand Up @@ -826,6 +827,8 @@ export function addCmsEditorRoutes(app: express.Application) {
app.get("/api/v1/cmse/rows/:rowId/revisions", withNext(listRowRevisions));
app.put("/api/v1/cmse/rows/:rowId", withNext(updateRow));
app.delete("/api/v1/cmse/rows/:rowId", withNext(deleteRow));
// Hyuna
app.post("/api/v1/cmse/rows/:rowId/copy", withNext(copyRow));
app.get("/api/v1/cmse/row-revisions/:revId", withNext(getRowRevision));

app.post(
Expand Down
21 changes: 21 additions & 0 deletions platform/wab/src/wab/server/db/DbMgr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ import {
UpdateResult,
} from "typeorm";
import * as uuid from "uuid";
// Hyuna

import { getLowestCommonAncestor } from "@/wab/shared/site-diffs/commit-graph";

Expand Down Expand Up @@ -7342,6 +7343,26 @@ export class DbMgr implements MigrationDbMgr {
await this.entMgr.save(row);
}

// Hyuna
async copyCmsRow(
tableId: CmsTableId,
rowId: CmsRowId,
opts: {
identifier?: string;
data?: Dict<Dict<unknown>>;
draftData?: Dict<Dict<unknown>> | null;
}
) {
await this.checkCmsRowPerms(rowId, "content");
const row = await this.getCmsRowById(rowId);
opts = {
identifier: opts.identifier !== "" ? opts.identifier : undefined,
draftData: row.draftData || row.data,
};
const copiedRow = await this.createCmsRow(tableId, opts);
return await this.entMgr.save(copiedRow);
}

// TODO We are always querying just the default locale.
async queryCmsRows(
tableId: CmsTableId,
Expand Down
21 changes: 21 additions & 0 deletions platform/wab/src/wab/server/routes/cmse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,27 @@ export async function deleteRow(req: Request, res: Response) {
res.json({});
}

// Hyuna
export async function copyRow(req: Request, res: Response) {
const mgr = userDbMgr(req);
const row = await mgr.getCmsRowById(req.params.rowId as CmsRowId);
userAnalytics(req).track({
event: "Copy cms row",
properties: {
rowId: row.id as CmsRowId,
tableId: row.tableId,
tableName: row.table?.name,
databaseId: row.table?.databaseId,
},
});
const copiedRow = await mgr.copyCmsRow(
row.tableId as CmsTableId,
req.params.rowId as CmsRowId,
req.body
);
res.json(copiedRow);
}

export async function updateRow(req: Request, res: Response) {
const mgr = userDbMgr(req);
const row = await mgr.updateCmsRow(req.params.rowId as CmsRowId, req.body);
Expand Down
9 changes: 9 additions & 0 deletions platform/wab/src/wab/shared/SharedApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1724,6 +1724,15 @@ export abstract class SharedApi {
return (await this.put(`/cmse/rows/${rowId}`, opts)) as ApiCmseRow;
}

async copyCmsRow(
rowId: CmsRowId,
opts: {
identifier: string;
}
) {
return (await this.post(`/cmse/rows/${rowId}/copy`, opts)) as ApiCmseRow;
}

async deleteCmsRow(rowId: CmsRowId) {
return await this.delete(`/cmse/rows/${rowId}`);
}
Expand Down
Loading