passing settings into markdown rendering

This commit is contained in:
2024-11-19 14:14:08 -07:00
parent e2eb63660d
commit cf200dab7f
19 changed files with 104 additions and 35 deletions

View File

@@ -3,3 +3,7 @@
- [ ] check out trpc - [ ] check out trpc
- <https://brockherion.dev/blog/posts/how-to-use-trpc-with-nextjs-app-router/> - <https://brockherion.dev/blog/posts/how-to-use-trpc-with-nextjs-app-router/>
- <https://trpc.io/docs/client/react/server-components> - <https://trpc.io/docs/client/react/server-components>
lecture link prefectch false

View File

@@ -3,6 +3,7 @@ import { CanvasAssignment } from "@/models/canvas/assignments/canvasAssignment";
import { CanvasPage } from "@/models/canvas/pages/canvasPageModel"; import { CanvasPage } from "@/models/canvas/pages/canvasPageModel";
import { CanvasQuiz } from "@/models/canvas/quizzes/canvasQuizModel"; import { CanvasQuiz } from "@/models/canvas/quizzes/canvasQuizModel";
import { LocalAssignment } from "@/models/local/assignment/localAssignment"; import { LocalAssignment } from "@/models/local/assignment/localAssignment";
import { LocalCourseSettings } from "@/models/local/localCourseSettings";
import { LocalCoursePage } from "@/models/local/page/localCoursePage"; import { LocalCoursePage } from "@/models/local/page/localCoursePage";
import { LocalQuiz } from "@/models/local/quiz/localQuiz"; import { LocalQuiz } from "@/models/local/quiz/localQuiz";
import { import {
@@ -17,10 +18,12 @@ export const getStatus = ({
item, item,
canvasItem, canvasItem,
type, type,
settings,
}: { }: {
item: LocalQuiz | LocalAssignment | LocalCoursePage; item: LocalQuiz | LocalAssignment | LocalCoursePage;
canvasItem?: CanvasQuiz | CanvasAssignment | CanvasPage; canvasItem?: CanvasQuiz | CanvasAssignment | CanvasPage;
type: "assignment" | "page" | "quiz"; type: "assignment" | "page" | "quiz";
settings: LocalCourseSettings;
}): { }): {
status: "localOnly" | "incomplete" | "published"; status: "localOnly" | "incomplete" | "published";
message: ReactNode; message: ReactNode;
@@ -102,7 +105,7 @@ export const getStatus = ({
}; };
const htmlIsSame = htmlIsCloseEnough( const htmlIsSame = htmlIsCloseEnough(
markdownToHTMLSafe(assignment.description), markdownToHTMLSafe(assignment.description, settings),
canvasAssignment.description canvasAssignment.description
); );
if (!htmlIsSame) if (!htmlIsSame)

View File

@@ -12,8 +12,10 @@ import {
import { ReactNode } from "react"; import { ReactNode } from "react";
import { useCalendarItemsContext } from "../../context/calendarItemsContext"; import { useCalendarItemsContext } from "../../context/calendarItemsContext";
import { getStatus } from "./getStatus"; import { getStatus } from "./getStatus";
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
export function useTodaysItems(day: string) { export function useTodaysItems(day: string) {
const [settings] = useLocalCourseSettingsQuery();
const dayAsDate = getDateFromStringOrThrow( const dayAsDate = getDateFromStringOrThrow(
day, day,
"calculating same month in day items" "calculating same month in day items"
@@ -43,6 +45,7 @@ export function useTodaysItems(day: string) {
item: assignment, item: assignment,
canvasItem: canvasAssignment, canvasItem: canvasAssignment,
type: "assignment", type: "assignment",
settings,
}), }),
}; };
}) })
@@ -65,6 +68,7 @@ export function useTodaysItems(day: string) {
item: quiz, item: quiz,
canvasItem: canvasQuiz, canvasItem: canvasQuiz,
type: "quiz", type: "quiz",
settings,
}), }),
}; };
}) })
@@ -87,6 +91,7 @@ export function useTodaysItems(day: string) {
item: page, item: page,
canvasItem: canvasPage, canvasItem: canvasPage,
type: "page", type: "page",
settings,
}), }),
}; };
}) })

View File

@@ -1,7 +1,9 @@
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
import { Lecture } from "@/models/local/lecture"; import { Lecture } from "@/models/local/lecture";
import { markdownToHTMLSafe } from "@/services/htmlMarkdownUtils"; import { markdownToHTMLSafe } from "@/services/htmlMarkdownUtils";
export default function LecturePreview({ lecture }: { lecture: Lecture }) { export default function LecturePreview({ lecture }: { lecture: Lecture }) {
const [settings] = useLocalCourseSettingsQuery();
return ( return (
<> <>
<section className="border-b-slate-700 border-b-4"> <section className="border-b-slate-700 border-b-4">
@@ -12,7 +14,7 @@ export default function LecturePreview({ lecture }: { lecture: Lecture }) {
<div <div
className="markdownPreview text-xl" className="markdownPreview text-xl"
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: markdownToHTMLSafe(lecture.content), __html: markdownToHTMLSafe(lecture.content, settings),
}} }}
></div> ></div>
</section> </section>

View File

@@ -16,7 +16,6 @@ export default function LecturePreviewPage({
const lecture = weeks const lecture = weeks
.flatMap(({ lectures }) => lectures.map((lecture) => lecture)) .flatMap(({ lectures }) => lectures.map((lecture) => lecture))
.find((l) => l.date === lectureDay); .find((l) => l.date === lectureDay);
console.log(lecture);
if (!lecture) { if (!lecture) {
return <div>lecture not found for day</div>; return <div>lecture not found for day</div>;

View File

@@ -1,3 +1,4 @@
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
import { LocalAssignment } from "@/models/local/assignment/localAssignment"; import { LocalAssignment } from "@/models/local/assignment/localAssignment";
import { rubricItemIsExtraCredit } from "@/models/local/assignment/rubricItem"; import { rubricItemIsExtraCredit } from "@/models/local/assignment/rubricItem";
import { markdownToHTMLSafe } from "@/services/htmlMarkdownUtils"; import { markdownToHTMLSafe } from "@/services/htmlMarkdownUtils";
@@ -8,6 +9,7 @@ export default function AssignmentPreview({
}: { }: {
assignment: LocalAssignment; assignment: LocalAssignment;
}) { }) {
const [settings] = useLocalCourseSettingsQuery();
const totalPoints = assignment.rubric.reduce( const totalPoints = assignment.rubric.reduce(
(sum, cur) => (rubricItemIsExtraCredit(cur) ? sum : sum + cur.points), (sum, cur) => (rubricItemIsExtraCredit(cur) ? sum : sum + cur.points),
0 0
@@ -59,7 +61,7 @@ export default function AssignmentPreview({
<div <div
className="markdownPreview" className="markdownPreview"
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: markdownToHTMLSafe(assignment.description), __html: markdownToHTMLSafe(assignment.description, settings),
}} }}
></div> ></div>
</section> </section>

View File

@@ -1,13 +1,15 @@
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
import { LocalCoursePage } from "@/models/local/page/localCoursePage"; import { LocalCoursePage } from "@/models/local/page/localCoursePage";
import { markdownToHTMLSafe } from "@/services/htmlMarkdownUtils"; import { markdownToHTMLSafe } from "@/services/htmlMarkdownUtils";
import React from "react"; import React from "react";
export default function PagePreview({ page }: { page: LocalCoursePage }) { export default function PagePreview({ page }: { page: LocalCoursePage }) {
const [settings] = useLocalCourseSettingsQuery();
return ( return (
<div <div
className="markdownPreview" className="markdownPreview"
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: markdownToHTMLSafe(page.text), __html: markdownToHTMLSafe(page.text, settings),
}} }}
></div> ></div>
); );

View File

@@ -1,4 +1,5 @@
import CheckIcon from "@/components/icons/CheckIcon"; import CheckIcon from "@/components/icons/CheckIcon";
import { useLocalCourseSettingsQuery } from "@/hooks/localCourse/localCoursesHooks";
import { useQuizQuery } from "@/hooks/localCourse/quizHooks"; import { useQuizQuery } from "@/hooks/localCourse/quizHooks";
import { import {
LocalQuizQuestion, LocalQuizQuestion,
@@ -14,6 +15,7 @@ export default function QuizPreview({
moduleName: string; moduleName: string;
}) { }) {
const [quiz] = useQuizQuery(moduleName, quizName); const [quiz] = useQuizQuery(moduleName, quizName);
const [settings] = useLocalCourseSettingsQuery();
return ( return (
<div style={{ overflow: "scroll", height: "100%" }}> <div style={{ overflow: "scroll", height: "100%" }}>
<div className="columns-2"> <div className="columns-2">
@@ -53,7 +55,7 @@ export default function QuizPreview({
<div <div
className="p-3 markdownPreview" className="p-3 markdownPreview"
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: markdownToHTMLSafe(quiz.description), __html: markdownToHTMLSafe(quiz.description, settings),
}} }}
></div> ></div>
<div className="p-3 rounded-md bg-slate-950 m-5 flex flex-col gap-3"> <div className="p-3 rounded-md bg-slate-950 m-5 flex flex-col gap-3">
@@ -75,6 +77,7 @@ export default function QuizPreview({
} }
function QuizQuestionPreview({ question }: { question: LocalQuizQuestion }) { function QuizQuestionPreview({ question }: { question: LocalQuizQuestion }) {
const [settings] = useLocalCourseSettingsQuery();
return ( return (
<div className="rounded bg-slate-900 px-2"> <div className="rounded bg-slate-900 px-2">
<div className="flex flex-row justify-between text-slate-400"> <div className="flex flex-row justify-between text-slate-400">
@@ -86,7 +89,7 @@ function QuizQuestionPreview({ question }: { question: LocalQuizQuestion }) {
<div <div
className="ms-4 mb-2 markdownPreview" className="ms-4 mb-2 markdownPreview"
dangerouslySetInnerHTML={{ __html: markdownToHTMLSafe(question.text) }} dangerouslySetInnerHTML={{ __html: markdownToHTMLSafe(question.text, settings) }}
></div> ></div>
{question.questionType === QuestionType.MATCHING && ( {question.questionType === QuestionType.MATCHING && (
<div> <div>
@@ -128,7 +131,7 @@ function QuizQuestionPreview({ question }: { question: LocalQuizQuestion }) {
<div <div
className="markdownQuizAnswerPreview" className="markdownQuizAnswerPreview"
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: markdownToHTMLSafe(answer.text), __html: markdownToHTMLSafe(answer.text, settings),
}} }}
/> />
</div> </div>

View File

@@ -104,6 +104,7 @@ export default function NewCourseForm() {
return { ...groupWithoutCanvas, canvasId: undefined }; return { ...groupWithoutCanvas, canvasId: undefined };
} }
), ),
assets: [],
} }
: { : {
name: selectedDirectory, name: selectedDirectory,
@@ -120,6 +121,7 @@ export default function NewCourseForm() {
defaultFileUploadTypes: ["pdf", "png", "jpg", "jpeg"], defaultFileUploadTypes: ["pdf", "png", "jpg", "jpeg"],
defaultLockHoursOffset: 0, defaultLockHoursOffset: 0,
holidays: [], holidays: [],
assets: [],
}; };
await createCourse.mutateAsync({ await createCourse.mutateAsync({
settings: newSettings, settings: newSettings,

View File

@@ -22,6 +22,7 @@ export const useCanvasAssignmentsQuery = () => {
}); });
}; };
export const useAddAssignmentToCanvasMutation = () => { export const useAddAssignmentToCanvasMutation = () => {
const [settings] = useLocalCourseSettingsQuery(); const [settings] = useLocalCourseSettingsQuery();
const { data: canvasModules } = useCanvasModulesQuery(); const { data: canvasModules } = useCanvasModulesQuery();
@@ -44,9 +45,11 @@ export const useAddAssignmentToCanvasMutation = () => {
const assignmentGroup = settings.assignmentGroups.find( const assignmentGroup = settings.assignmentGroups.find(
(g) => g.name === assignment.localAssignmentGroupName (g) => g.name === assignment.localAssignmentGroupName
); );
const canvasAssignmentId = await canvasAssignmentService.create( const canvasAssignmentId = await canvasAssignmentService.create(
settings.canvasId, settings.canvasId,
assignment, assignment,
settings,
assignmentGroup?.canvasId assignmentGroup?.canvasId
); );
const canvasModule = canvasModules.find((c) => c.name === moduleName); const canvasModule = canvasModules.find((c) => c.name === moduleName);
@@ -89,6 +92,7 @@ export const useUpdateAssignmentInCanvasMutation = () => {
settings.canvasId, settings.canvasId,
canvasAssignmentId, canvasAssignmentId,
assignment, assignment,
settings,
assignmentGroup?.canvasId assignmentGroup?.canvasId
); );
}, },

View File

@@ -44,7 +44,7 @@ export const useCreateCanvasPageMutation = () => {
} }
const canvasPage = await canvasPageService.create( const canvasPage = await canvasPageService.create(
settings.canvasId, settings.canvasId,
page page,settings
); );
const canvasModule = canvasModules.find((c) => c.name === moduleName); const canvasModule = canvasModules.find((c) => c.name === moduleName);
@@ -78,7 +78,7 @@ export const useUpdateCanvasPageMutation = () => {
}: { }: {
page: LocalCoursePage; page: LocalCoursePage;
canvasPageId: number; canvasPageId: number;
}) => canvasPageService.update(settings.canvasId, canvasPageId, page), }) => canvasPageService.update(settings.canvasId, canvasPageId, page, settings),
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: canvasPageKeys.pagesInCourse(settings.canvasId), queryKey: canvasPageKeys.pagesInCourse(settings.canvasId),

View File

@@ -50,6 +50,7 @@ export const useAddQuizToCanvasMutation = () => {
const canvasQuizId = await canvasQuizService.create( const canvasQuizId = await canvasQuizService.create(
settings.canvasId, settings.canvasId,
quiz, quiz,
settings,
assignmentGroup?.canvasId assignmentGroup?.canvasId
); );

View File

@@ -7,7 +7,6 @@ import {
LocalAssignmentGroup, LocalAssignmentGroup,
zodLocalAssignmentGroup, zodLocalAssignmentGroup,
} from "./assignment/localAssignmentGroup"; } from "./assignment/localAssignmentGroup";
import { LocalModule } from "./localModules";
import { parse, stringify } from "yaml"; import { parse, stringify } from "yaml";
// export interface LocalCourse { // export interface LocalCourse {
@@ -59,6 +58,10 @@ export interface LocalCourseSettings {
name: string; name: string;
days: string[]; days: string[];
}[]; }[];
assets: {
sourceUrl: string;
canvasUrl: string;
}[];
} }
export const zodLocalCourseSettings = z.object({ export const zodLocalCourseSettings = z.object({
@@ -78,6 +81,12 @@ export const zodLocalCourseSettings = z.object({
days: z.string().array(), days: z.string().array(),
}) })
.array(), .array(),
assets: z
.object({
sourceUrl: z.string(),
canvasUrl: z.string(),
})
.array(),
}); });
export function getDayOfWeek(date: Date): DayOfWeek { export function getDayOfWeek(date: Date): DayOfWeek {

View File

@@ -7,6 +7,7 @@ import { CanvasRubricCreationResponse } from "@/models/canvas/assignments/canvas
import { assignmentPoints } from "@/models/local/assignment/utils/assignmentPointsUtils"; import { assignmentPoints } from "@/models/local/assignment/utils/assignmentPointsUtils";
import { getDateFromString } from "@/models/local/utils/timeUtils"; import { getDateFromString } from "@/models/local/utils/timeUtils";
import { getRubricCriterion } from "./canvasRubricUtils"; import { getRubricCriterion } from "./canvasRubricUtils";
import { LocalCourseSettings } from "@/models/local/localCourseSettings";
export const canvasAssignmentService = { export const canvasAssignmentService = {
async getAll(courseId: number): Promise<CanvasAssignment[]> { async getAll(courseId: number): Promise<CanvasAssignment[]> {
@@ -23,6 +24,7 @@ export const canvasAssignmentService = {
async create( async create(
canvasCourseId: number, canvasCourseId: number,
localAssignment: LocalAssignment, localAssignment: LocalAssignment,
settings: LocalCourseSettings,
canvasAssignmentGroupId?: number canvasAssignmentGroupId?: number
) { ) {
console.log(`Creating assignment: ${localAssignment.name}`); console.log(`Creating assignment: ${localAssignment.name}`);
@@ -36,7 +38,7 @@ export const canvasAssignmentService = {
allowed_extensions: localAssignment.allowedFileUploadExtensions.map( allowed_extensions: localAssignment.allowedFileUploadExtensions.map(
(e) => e.toString() (e) => e.toString()
), ),
description: markdownToHTMLSafe(localAssignment.description), description: markdownToHTMLSafe(localAssignment.description, settings),
due_at: getDateFromString(localAssignment.dueAt)?.toISOString(), due_at: getDateFromString(localAssignment.dueAt)?.toISOString(),
lock_at: lock_at:
localAssignment.lockAt && localAssignment.lockAt &&
@@ -58,6 +60,7 @@ export const canvasAssignmentService = {
courseId: number, courseId: number,
canvasAssignmentId: number, canvasAssignmentId: number,
localAssignment: LocalAssignment, localAssignment: LocalAssignment,
settings: LocalCourseSettings,
canvasAssignmentGroupId?: number canvasAssignmentGroupId?: number
) { ) {
console.log(`Updating assignment: ${localAssignment.name}`); console.log(`Updating assignment: ${localAssignment.name}`);
@@ -71,7 +74,7 @@ export const canvasAssignmentService = {
allowed_extensions: localAssignment.allowedFileUploadExtensions.map( allowed_extensions: localAssignment.allowedFileUploadExtensions.map(
(e) => e.toString() (e) => e.toString()
), ),
description: markdownToHTMLSafe(localAssignment.description), description: markdownToHTMLSafe(localAssignment.description, settings),
due_at: getDateFromString(localAssignment.dueAt)?.toISOString(), due_at: getDateFromString(localAssignment.dueAt)?.toISOString(),
lock_at: lock_at:
localAssignment.lockAt && localAssignment.lockAt &&

View File

@@ -4,6 +4,7 @@ import { canvasApi, paginatedRequest } from "./canvasServiceUtils";
import { markdownToHTMLSafe } from "../htmlMarkdownUtils"; import { markdownToHTMLSafe } from "../htmlMarkdownUtils";
import { axiosClient } from "../axiosUtils"; import { axiosClient } from "../axiosUtils";
import { rateLimitAwareDelete } from "./canvasWebRequestor"; import { rateLimitAwareDelete } from "./canvasWebRequestor";
import { LocalCourseSettings } from "@/models/local/localCourseSettings";
export const canvasPageService = { export const canvasPageService = {
async getAll(courseId: number): Promise<CanvasPage[]> { async getAll(courseId: number): Promise<CanvasPage[]> {
@@ -17,14 +18,15 @@ export const canvasPageService = {
async create( async create(
canvasCourseId: number, canvasCourseId: number,
page: LocalCoursePage page: LocalCoursePage,
settings: LocalCourseSettings
): Promise<CanvasPage> { ): Promise<CanvasPage> {
console.log(`Creating course page: ${page.name}`); console.log(`Creating course page: ${page.name}`);
const url = `${canvasApi}/courses/${canvasCourseId}/pages`; const url = `${canvasApi}/courses/${canvasCourseId}/pages`;
const body = { const body = {
wiki_page: { wiki_page: {
title: page.name, title: page.name,
body: markdownToHTMLSafe(page.text), body: markdownToHTMLSafe(page.text, settings),
}, },
}; };
@@ -38,14 +40,15 @@ export const canvasPageService = {
async update( async update(
courseId: number, courseId: number,
canvasPageId: number, canvasPageId: number,
page: LocalCoursePage page: LocalCoursePage,
settings: LocalCourseSettings
): Promise<void> { ): Promise<void> {
console.log(`Updating course page: ${page.name}`); console.log(`Updating course page: ${page.name}`);
const url = `${canvasApi}/courses/${courseId}/pages/${canvasPageId}`; const url = `${canvasApi}/courses/${courseId}/pages/${canvasPageId}`;
const body = { const body = {
wiki_page: { wiki_page: {
title: page.name, title: page.name,
body: markdownToHTMLSafe(page.text), body: markdownToHTMLSafe(page.text, settings),
}, },
}; };
await axiosClient.put(url, body); await axiosClient.put(url, body);

View File

@@ -10,8 +10,12 @@ import {
QuestionType, QuestionType,
} from "@/models/local/quiz/localQuizQuestion"; } from "@/models/local/quiz/localQuizQuestion";
import { CanvasQuizQuestion } from "@/models/canvas/quizzes/canvasQuizQuestionModel"; 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) if (question.questionType === QuestionType.MATCHING)
return question.answers.map((a) => ({ return question.answers.map((a) => ({
answer_match_left: a.text, answer_match_left: a.text,
@@ -19,7 +23,7 @@ const getAnswers = (question: LocalQuizQuestion) => {
})); }));
return question.answers.map((answer) => ({ return question.answers.map((answer) => ({
answer_html: markdownToHTMLSafe(answer.text), answer_html: markdownToHTMLSafe(answer.text, settings),
answer_weight: answer.correct ? 100 : 0, answer_weight: answer.correct ? 100 : 0,
})); }));
}; };
@@ -28,18 +32,19 @@ const createQuestionOnly = async (
canvasCourseId: number, canvasCourseId: number,
canvasQuizId: number, canvasQuizId: number,
question: LocalQuizQuestion, question: LocalQuizQuestion,
position: number position: number,
settings: LocalCourseSettings
) => { ) => {
console.log("Creating individual question"); //, question); console.log("Creating individual question"); //, question);
const url = `${canvasApi}/courses/${canvasCourseId}/quizzes/${canvasQuizId}/questions`; const url = `${canvasApi}/courses/${canvasCourseId}/quizzes/${canvasQuizId}/questions`;
const body = { const body = {
question: { question: {
question_text: markdownToHTMLSafe(question.text), question_text: markdownToHTMLSafe(question.text, settings),
question_type: `${question.questionType}_question`, question_type: `${question.questionType}_question`,
points_possible: question.points, points_possible: question.points,
position, position,
answers: getAnswers(question), answers: getAnswers(question, settings),
}, },
}; };
@@ -93,13 +98,20 @@ const hackFixRedundantAssignments = async (canvasCourseId: number) => {
const createQuizQuestions = async ( const createQuizQuestions = async (
canvasCourseId: number, canvasCourseId: number,
canvasQuizId: number, canvasQuizId: number,
localQuiz: LocalQuiz localQuiz: LocalQuiz,
settings: LocalCourseSettings
) => { ) => {
console.log("Creating quiz questions"); //, localQuiz); console.log("Creating quiz questions"); //, localQuiz);
const tasks = localQuiz.questions.map( const tasks = localQuiz.questions.map(
async (question, index) => async (question, index) =>
await createQuestionOnly(canvasCourseId, canvasQuizId, question, index) await createQuestionOnly(
canvasCourseId,
canvasQuizId,
question,
index,
settings
)
); );
const questionAndPositions = await Promise.all(tasks); const questionAndPositions = await Promise.all(tasks);
await hackFixQuestionOrdering( await hackFixQuestionOrdering(
@@ -126,15 +138,17 @@ export const canvasQuizService = {
async create( async create(
canvasCourseId: number, canvasCourseId: number,
localQuiz: LocalQuiz, localQuiz: LocalQuiz,
settings: LocalCourseSettings,
canvasAssignmentGroupId?: number canvasAssignmentGroupId?: number
) { ) {
console.log("Creating quiz", localQuiz); console.log("Creating quiz", localQuiz);
const url = `${canvasApi}/courses/${canvasCourseId}/quizzes`; const url = `${canvasApi}/courses/${canvasCourseId}/quizzes`;
const body = { const body = {
quiz: { quiz: {
title: localQuiz.name, title: localQuiz.name,
description: markdownToHTMLSafe(localQuiz.description), description: markdownToHTMLSafe(localQuiz.description, settings),
shuffle_answers: localQuiz.shuffleAnswers, shuffle_answers: localQuiz.shuffleAnswers,
access_code: localQuiz.password, access_code: localQuiz.password,
show_correct_answers: localQuiz.showCorrectAnswers, show_correct_answers: localQuiz.showCorrectAnswers,
@@ -158,7 +172,7 @@ export const canvasQuizService = {
}; };
const { data: canvasQuiz } = await axiosClient.post<CanvasQuiz>(url, body); const { data: canvasQuiz } = await axiosClient.post<CanvasQuiz>(url, body);
await createQuizQuestions(canvasCourseId, canvasQuiz.id, localQuiz); await createQuizQuestions(canvasCourseId, canvasQuiz.id, localQuiz, settings);
return canvasQuiz.id; return canvasQuiz.id;
}, },
async delete(canvasCourseId: number, canvasQuizId: number) { async delete(canvasCourseId: number, canvasQuizId: number) {

View File

@@ -50,6 +50,9 @@ const populateDefaultValues = (settingsFromFile: LocalCourseSettings) => {
holidays: Array.isArray(settingsFromFile.holidays) holidays: Array.isArray(settingsFromFile.holidays)
? settingsFromFile.holidays ? settingsFromFile.holidays
: [], : [],
assets: Array.isArray(settingsFromFile.assets)
? settingsFromFile.assets
: [],
}; };
return settings; return settings;
}; };

View File

@@ -1,18 +1,27 @@
"use client"; "use client";
import { marked } from "marked"; import { marked } from "marked";
// import markedKatex from "marked-katex-extension";
import * as DOMPurify from "isomorphic-dompurify"; import * as DOMPurify from "isomorphic-dompurify";
import { LocalCourseSettings } from "@/models/local/localCourseSettings";
export function markdownToHTMLSafe(markdownString: string) { function extractImageSources(html: string) {
// const options = { const parser = new DOMParser();
// throwOnError: false, const doc = parser.parseFromString(html, "text/html");
// nonStandard: true const imgElements = doc.querySelectorAll("img");
// }; const srcUrls = Array.from(imgElements).map((img) => img.src);
return srcUrls;
// marked.use(markedKatex(options)); }
function handleImages(html: string, settings: LocalCourseSettings) {
const imageSources = extractImageSources(html);
console.log(imageSources);
}
export function markdownToHTMLSafe(
markdownString: string,
settings: LocalCourseSettings
) {
const clean = DOMPurify.sanitize( const clean = DOMPurify.sanitize(
marked.parse(markdownString, { async: false, pedantic: false, gfm: true }) marked.parse(markdownString, { async: false, pedantic: false, gfm: true })
); );
handleImages(clean, settings);
return clean; return clean;
} }

View File

@@ -31,6 +31,7 @@ describe("FileStorageTests", () => {
defaultAssignmentSubmissionTypes: [], defaultAssignmentSubmissionTypes: [],
defaultFileUploadTypes: [], defaultFileUploadTypes: [],
holidays: [], holidays: [],
assets: []
}; };
await fileStorageService.settings.updateCourseSettings(name, settings); await fileStorageService.settings.updateCourseSettings(name, settings);