Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
13 changes: 13 additions & 0 deletions apps/api/src/query/copy_move.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,19 @@ export async function copyContent({
throw new InvalidRequestError("Content not found or not visible");
}

// Prevent copying assignment roots
const assignmentRoots = await prisma.content.findMany({
where: {
id: { in: contentIds },
isAssignmentRoot: true,
},
select: { id: true },
});

if (assignmentRoots.length > 0) {
throw new InvalidRequestError("Cannot copy an assignment");
}

let desiredParentIsPublic = false;
let desiredParentLicenseCode: LicenseCode = "CCDUAL";
let desiredParentShares: Uint8Array[] = [];
Expand Down
44 changes: 44 additions & 0 deletions apps/api/src/test/copy_move.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -899,6 +899,50 @@ test("cannot copy into assigned problem set", async () => {
).rejects.toThrow("Cannot copy content into an assigned activity");
});

test("cannot copy an assignment", async () => {
const { userId: ownerId } = await createTestUser();
const [problemSetId] = await setupTestContent(ownerId, {
"problem set 1": pset({
"doc 1": doc(""),
}),
});
const { assignmentId } = await createAssignment({
contentId: problemSetId,
loggedInUserId: ownerId,
destinationParentId: null,
closedOn: DateTime.now(),
});

await expect(
copyContent({
contentIds: [assignmentId],
loggedInUserId: ownerId,
parentId: null,
}),
).rejects.toThrow("Cannot copy an assignment");
});

test("cannot copy an assignment (singleDoc)", async () => {
const { userId: ownerId } = await createTestUser();
const [docId] = await setupTestContent(ownerId, {
"doc 1": doc(""),
});
const { assignmentId } = await createAssignment({
contentId: docId,
loggedInUserId: ownerId,
destinationParentId: null,
closedOn: DateTime.now(),
});

await expect(
copyContent({
contentIds: [assignmentId],
loggedInUserId: ownerId,
parentId: null,
}),
).rejects.toThrow("Cannot copy an assignment");
});

test("MoveCopyContent does not allow singleDoc` as parent type", async () => {
const { userId: loggedInUserId } = await createTestUser();

Expand Down
5 changes: 5 additions & 0 deletions apps/app/src/paths/Activities.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -452,10 +452,15 @@ export function Activities() {
actionBarContext = config.context;
actions = config.actions;
} else {
const anyAssignmentSelected = selectedContentDescriptions.some(
(c) => c.assignmentInfo !== undefined,
);

const config = configOrganizeContentList({
cardSelections,
cardMovement,
forceDisableMoveUpAndDown: haveQuery,
anyAssignmentSelected,
FIX_ME_miscellaneous_buttons,
onMoveTo: () => {
const selectedContent = content.find(
Expand Down
4 changes: 3 additions & 1 deletion apps/app/src/utils/actionBarConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ export function configOrganizeContentList({
cardSelections,
cardMovement,
forceDisableMoveUpAndDown = false,
anyAssignmentSelected = false,
onMoveTo,
onCopy,
onDelete,
Expand All @@ -111,6 +112,7 @@ export function configOrganizeContentList({
cardSelections: CardSelections;
cardMovement: CardMovement;
forceDisableMoveUpAndDown?: boolean;
anyAssignmentSelected?: boolean;
onMoveTo: () => void;
onCopy: () => void;
onDelete: () => void;
Expand Down Expand Up @@ -156,7 +158,7 @@ export function configOrganizeContentList({
{
label: "Make a copy",
onClick: onCopy,
isDisabled: cardSelections.count === 0,
isDisabled: cardSelections.count === 0 || anyAssignmentSelected,
},
];
return { context, actions };
Expand Down
Loading