@@ -86,7 +89,7 @@ function QuizQuestionPreview({ question }: { question: LocalQuizQuestion }) {
{question.questionType === QuestionType.MATCHING && (
@@ -128,7 +131,7 @@ function QuizQuestionPreview({ question }: { question: LocalQuizQuestion }) {
diff --git a/nextjs/src/app/newCourse/NewCourseForm.tsx b/nextjs/src/app/newCourse/NewCourseForm.tsx
index afcc51a..ebe7305 100644
--- a/nextjs/src/app/newCourse/NewCourseForm.tsx
+++ b/nextjs/src/app/newCourse/NewCourseForm.tsx
@@ -104,6 +104,7 @@ export default function NewCourseForm() {
return { ...groupWithoutCanvas, canvasId: undefined };
}
),
+ assets: [],
}
: {
name: selectedDirectory,
@@ -120,6 +121,7 @@ export default function NewCourseForm() {
defaultFileUploadTypes: ["pdf", "png", "jpg", "jpeg"],
defaultLockHoursOffset: 0,
holidays: [],
+ assets: [],
};
await createCourse.mutateAsync({
settings: newSettings,
diff --git a/nextjs/src/hooks/canvas/canvasAssignmentHooks.ts b/nextjs/src/hooks/canvas/canvasAssignmentHooks.ts
index 464ab07..2be8a90 100644
--- a/nextjs/src/hooks/canvas/canvasAssignmentHooks.ts
+++ b/nextjs/src/hooks/canvas/canvasAssignmentHooks.ts
@@ -22,6 +22,7 @@ export const useCanvasAssignmentsQuery = () => {
});
};
+
export const useAddAssignmentToCanvasMutation = () => {
const [settings] = useLocalCourseSettingsQuery();
const { data: canvasModules } = useCanvasModulesQuery();
@@ -44,9 +45,11 @@ export const useAddAssignmentToCanvasMutation = () => {
const assignmentGroup = settings.assignmentGroups.find(
(g) => g.name === assignment.localAssignmentGroupName
);
+
const canvasAssignmentId = await canvasAssignmentService.create(
settings.canvasId,
assignment,
+ settings,
assignmentGroup?.canvasId
);
const canvasModule = canvasModules.find((c) => c.name === moduleName);
@@ -89,6 +92,7 @@ export const useUpdateAssignmentInCanvasMutation = () => {
settings.canvasId,
canvasAssignmentId,
assignment,
+ settings,
assignmentGroup?.canvasId
);
},
diff --git a/nextjs/src/hooks/canvas/canvasPageHooks.ts b/nextjs/src/hooks/canvas/canvasPageHooks.ts
index d181679..86e653d 100644
--- a/nextjs/src/hooks/canvas/canvasPageHooks.ts
+++ b/nextjs/src/hooks/canvas/canvasPageHooks.ts
@@ -44,7 +44,7 @@ export const useCreateCanvasPageMutation = () => {
}
const canvasPage = await canvasPageService.create(
settings.canvasId,
- page
+ page,settings
);
const canvasModule = canvasModules.find((c) => c.name === moduleName);
@@ -78,7 +78,7 @@ export const useUpdateCanvasPageMutation = () => {
}: {
page: LocalCoursePage;
canvasPageId: number;
- }) => canvasPageService.update(settings.canvasId, canvasPageId, page),
+ }) => canvasPageService.update(settings.canvasId, canvasPageId, page, settings),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: canvasPageKeys.pagesInCourse(settings.canvasId),
diff --git a/nextjs/src/hooks/canvas/canvasQuizHooks.ts b/nextjs/src/hooks/canvas/canvasQuizHooks.ts
index cdfb68c..f13bc99 100644
--- a/nextjs/src/hooks/canvas/canvasQuizHooks.ts
+++ b/nextjs/src/hooks/canvas/canvasQuizHooks.ts
@@ -50,6 +50,7 @@ export const useAddQuizToCanvasMutation = () => {
const canvasQuizId = await canvasQuizService.create(
settings.canvasId,
quiz,
+ settings,
assignmentGroup?.canvasId
);
diff --git a/nextjs/src/models/local/localCourseSettings.ts b/nextjs/src/models/local/localCourseSettings.ts
index 1daccfe..4d7ea53 100644
--- a/nextjs/src/models/local/localCourseSettings.ts
+++ b/nextjs/src/models/local/localCourseSettings.ts
@@ -7,7 +7,6 @@ import {
LocalAssignmentGroup,
zodLocalAssignmentGroup,
} from "./assignment/localAssignmentGroup";
-import { LocalModule } from "./localModules";
import { parse, stringify } from "yaml";
// export interface LocalCourse {
@@ -59,6 +58,10 @@ export interface LocalCourseSettings {
name: string;
days: string[];
}[];
+ assets: {
+ sourceUrl: string;
+ canvasUrl: string;
+ }[];
}
export const zodLocalCourseSettings = z.object({
@@ -78,6 +81,12 @@ export const zodLocalCourseSettings = z.object({
days: z.string().array(),
})
.array(),
+ assets: z
+ .object({
+ sourceUrl: z.string(),
+ canvasUrl: z.string(),
+ })
+ .array(),
});
export function getDayOfWeek(date: Date): DayOfWeek {
diff --git a/nextjs/src/services/canvas/canvasAssignmentService.ts b/nextjs/src/services/canvas/canvasAssignmentService.ts
index 1d26e25..5b420f3 100644
--- a/nextjs/src/services/canvas/canvasAssignmentService.ts
+++ b/nextjs/src/services/canvas/canvasAssignmentService.ts
@@ -7,6 +7,7 @@ import { CanvasRubricCreationResponse } from "@/models/canvas/assignments/canvas
import { assignmentPoints } from "@/models/local/assignment/utils/assignmentPointsUtils";
import { getDateFromString } from "@/models/local/utils/timeUtils";
import { getRubricCriterion } from "./canvasRubricUtils";
+import { LocalCourseSettings } from "@/models/local/localCourseSettings";
export const canvasAssignmentService = {
async getAll(courseId: number): Promise
{
@@ -23,6 +24,7 @@ export const canvasAssignmentService = {
async create(
canvasCourseId: number,
localAssignment: LocalAssignment,
+ settings: LocalCourseSettings,
canvasAssignmentGroupId?: number
) {
console.log(`Creating assignment: ${localAssignment.name}`);
@@ -36,7 +38,7 @@ export const canvasAssignmentService = {
allowed_extensions: localAssignment.allowedFileUploadExtensions.map(
(e) => e.toString()
),
- description: markdownToHTMLSafe(localAssignment.description),
+ description: markdownToHTMLSafe(localAssignment.description, settings),
due_at: getDateFromString(localAssignment.dueAt)?.toISOString(),
lock_at:
localAssignment.lockAt &&
@@ -58,6 +60,7 @@ export const canvasAssignmentService = {
courseId: number,
canvasAssignmentId: number,
localAssignment: LocalAssignment,
+ settings: LocalCourseSettings,
canvasAssignmentGroupId?: number
) {
console.log(`Updating assignment: ${localAssignment.name}`);
@@ -71,7 +74,7 @@ export const canvasAssignmentService = {
allowed_extensions: localAssignment.allowedFileUploadExtensions.map(
(e) => e.toString()
),
- description: markdownToHTMLSafe(localAssignment.description),
+ description: markdownToHTMLSafe(localAssignment.description, settings),
due_at: getDateFromString(localAssignment.dueAt)?.toISOString(),
lock_at:
localAssignment.lockAt &&
diff --git a/nextjs/src/services/canvas/canvasPageService.ts b/nextjs/src/services/canvas/canvasPageService.ts
index 6e912f4..e72a9b4 100644
--- a/nextjs/src/services/canvas/canvasPageService.ts
+++ b/nextjs/src/services/canvas/canvasPageService.ts
@@ -4,6 +4,7 @@ import { canvasApi, paginatedRequest } from "./canvasServiceUtils";
import { markdownToHTMLSafe } from "../htmlMarkdownUtils";
import { axiosClient } from "../axiosUtils";
import { rateLimitAwareDelete } from "./canvasWebRequestor";
+import { LocalCourseSettings } from "@/models/local/localCourseSettings";
export const canvasPageService = {
async getAll(courseId: number): Promise {
@@ -17,14 +18,15 @@ export const canvasPageService = {
async create(
canvasCourseId: number,
- page: LocalCoursePage
+ page: LocalCoursePage,
+ settings: LocalCourseSettings
): Promise {
console.log(`Creating course page: ${page.name}`);
const url = `${canvasApi}/courses/${canvasCourseId}/pages`;
const body = {
wiki_page: {
title: page.name,
- body: markdownToHTMLSafe(page.text),
+ body: markdownToHTMLSafe(page.text, settings),
},
};
@@ -38,14 +40,15 @@ export const canvasPageService = {
async update(
courseId: number,
canvasPageId: number,
- page: LocalCoursePage
+ page: LocalCoursePage,
+ settings: LocalCourseSettings
): Promise {
console.log(`Updating course page: ${page.name}`);
const url = `${canvasApi}/courses/${courseId}/pages/${canvasPageId}`;
const body = {
wiki_page: {
title: page.name,
- body: markdownToHTMLSafe(page.text),
+ body: markdownToHTMLSafe(page.text, settings),
},
};
await axiosClient.put(url, body);
diff --git a/nextjs/src/services/canvas/canvasQuizService.ts b/nextjs/src/services/canvas/canvasQuizService.ts
index 942b22d..599dc5c 100644
--- a/nextjs/src/services/canvas/canvasQuizService.ts
+++ b/nextjs/src/services/canvas/canvasQuizService.ts
@@ -10,8 +10,12 @@ import {
QuestionType,
} from "@/models/local/quiz/localQuizQuestion";
import { CanvasQuizQuestion } from "@/models/canvas/quizzes/canvasQuizQuestionModel";
+import { LocalCourseSettings } from "@/models/local/localCourseSettings";
-const getAnswers = (question: LocalQuizQuestion) => {
+const getAnswers = (
+ question: LocalQuizQuestion,
+ settings: LocalCourseSettings
+) => {
if (question.questionType === QuestionType.MATCHING)
return question.answers.map((a) => ({
answer_match_left: a.text,
@@ -19,7 +23,7 @@ const getAnswers = (question: LocalQuizQuestion) => {
}));
return question.answers.map((answer) => ({
- answer_html: markdownToHTMLSafe(answer.text),
+ answer_html: markdownToHTMLSafe(answer.text, settings),
answer_weight: answer.correct ? 100 : 0,
}));
};
@@ -28,18 +32,19 @@ const createQuestionOnly = async (
canvasCourseId: number,
canvasQuizId: number,
question: LocalQuizQuestion,
- position: number
+ position: number,
+ settings: LocalCourseSettings
) => {
console.log("Creating individual question"); //, question);
const url = `${canvasApi}/courses/${canvasCourseId}/quizzes/${canvasQuizId}/questions`;
const body = {
question: {
- question_text: markdownToHTMLSafe(question.text),
+ question_text: markdownToHTMLSafe(question.text, settings),
question_type: `${question.questionType}_question`,
points_possible: question.points,
position,
- answers: getAnswers(question),
+ answers: getAnswers(question, settings),
},
};
@@ -93,13 +98,20 @@ const hackFixRedundantAssignments = async (canvasCourseId: number) => {
const createQuizQuestions = async (
canvasCourseId: number,
canvasQuizId: number,
- localQuiz: LocalQuiz
+ localQuiz: LocalQuiz,
+ settings: LocalCourseSettings
) => {
console.log("Creating quiz questions"); //, localQuiz);
const tasks = localQuiz.questions.map(
async (question, index) =>
- await createQuestionOnly(canvasCourseId, canvasQuizId, question, index)
+ await createQuestionOnly(
+ canvasCourseId,
+ canvasQuizId,
+ question,
+ index,
+ settings
+ )
);
const questionAndPositions = await Promise.all(tasks);
await hackFixQuestionOrdering(
@@ -126,15 +138,17 @@ export const canvasQuizService = {
async create(
canvasCourseId: number,
localQuiz: LocalQuiz,
+ settings: LocalCourseSettings,
canvasAssignmentGroupId?: number
) {
console.log("Creating quiz", localQuiz);
const url = `${canvasApi}/courses/${canvasCourseId}/quizzes`;
+
const body = {
quiz: {
title: localQuiz.name,
- description: markdownToHTMLSafe(localQuiz.description),
+ description: markdownToHTMLSafe(localQuiz.description, settings),
shuffle_answers: localQuiz.shuffleAnswers,
access_code: localQuiz.password,
show_correct_answers: localQuiz.showCorrectAnswers,
@@ -158,7 +172,7 @@ export const canvasQuizService = {
};
const { data: canvasQuiz } = await axiosClient.post(url, body);
- await createQuizQuestions(canvasCourseId, canvasQuiz.id, localQuiz);
+ await createQuizQuestions(canvasCourseId, canvasQuiz.id, localQuiz, settings);
return canvasQuiz.id;
},
async delete(canvasCourseId: number, canvasQuizId: number) {
diff --git a/nextjs/src/services/fileStorage/settingsFileStorageService.ts b/nextjs/src/services/fileStorage/settingsFileStorageService.ts
index d0ed69a..3aa9812 100644
--- a/nextjs/src/services/fileStorage/settingsFileStorageService.ts
+++ b/nextjs/src/services/fileStorage/settingsFileStorageService.ts
@@ -50,6 +50,9 @@ const populateDefaultValues = (settingsFromFile: LocalCourseSettings) => {
holidays: Array.isArray(settingsFromFile.holidays)
? settingsFromFile.holidays
: [],
+ assets: Array.isArray(settingsFromFile.assets)
+ ? settingsFromFile.assets
+ : [],
};
return settings;
};
diff --git a/nextjs/src/services/htmlMarkdownUtils.ts b/nextjs/src/services/htmlMarkdownUtils.ts
index 1e295d3..631d89d 100644
--- a/nextjs/src/services/htmlMarkdownUtils.ts
+++ b/nextjs/src/services/htmlMarkdownUtils.ts
@@ -1,18 +1,27 @@
"use client";
import { marked } from "marked";
-// import markedKatex from "marked-katex-extension";
import * as DOMPurify from "isomorphic-dompurify";
+import { LocalCourseSettings } from "@/models/local/localCourseSettings";
-export function markdownToHTMLSafe(markdownString: string) {
- // const options = {
- // throwOnError: false,
- // nonStandard: true
- // };
-
- // marked.use(markedKatex(options));
+function extractImageSources(html: string) {
+ const parser = new DOMParser();
+ const doc = parser.parseFromString(html, "text/html");
+ const imgElements = doc.querySelectorAll("img");
+ const srcUrls = Array.from(imgElements).map((img) => img.src);
+ return srcUrls;
+}
+function handleImages(html: string, settings: LocalCourseSettings) {
+ const imageSources = extractImageSources(html);
+ console.log(imageSources);
+}
+export function markdownToHTMLSafe(
+ markdownString: string,
+ settings: LocalCourseSettings
+) {
const clean = DOMPurify.sanitize(
marked.parse(markdownString, { async: false, pedantic: false, gfm: true })
);
+ handleImages(clean, settings);
return clean;
}
diff --git a/nextjs/src/services/tests/fileStorage.test.ts b/nextjs/src/services/tests/fileStorage.test.ts
index b007e3f..9d7f11b 100644
--- a/nextjs/src/services/tests/fileStorage.test.ts
+++ b/nextjs/src/services/tests/fileStorage.test.ts
@@ -31,6 +31,7 @@ describe("FileStorageTests", () => {
defaultAssignmentSubmissionTypes: [],
defaultFileUploadTypes: [],
holidays: [],
+ assets: []
};
await fileStorageService.settings.updateCourseSettings(name, settings);