mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-26 23:58:31 -06:00
passing settings into markdown rendering
This commit is contained in:
@@ -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
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 &&
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user