style: fix linting issues met Prettier

This commit is contained in:
Lint Action 2025-03-11 03:09:12 +00:00
parent aa1a85e64e
commit 2a2881ec30
84 changed files with 846 additions and 1013 deletions

View file

@ -1,21 +1,23 @@
import {getAttachmentRepository} from "../../data/repositories";
import {Attachment} from "../../entities/content/attachment.entity";
import {LearningObjectIdentifier} from "../../interfaces/learning-content";
import { getAttachmentRepository } from '../../data/repositories';
import { Attachment } from '../../entities/content/attachment.entity';
import { LearningObjectIdentifier } from '../../interfaces/learning-content';
const attachmentService = {
getAttachment(learningObjectId: LearningObjectIdentifier, attachmentName: string): Promise<Attachment | null> {
const attachmentRepo = getAttachmentRepository();
if (learningObjectId.version) {
return attachmentRepo.findByLearningObjectIdAndName({
hruid: learningObjectId.hruid,
language: learningObjectId.language,
version: learningObjectId.version,
}, attachmentName);
}
return attachmentRepo.findByMostRecentVersionOfLearningObjectAndName(learningObjectId.hruid, learningObjectId.language, attachmentName);
}
}
return attachmentRepo.findByLearningObjectIdAndName(
{
hruid: learningObjectId.hruid,
language: learningObjectId.language,
version: learningObjectId.version,
},
attachmentName
);
}
return attachmentRepo.findByMostRecentVersionOfLearningObjectAndName(learningObjectId.hruid, learningObjectId.language, attachmentName);
},
};
export default attachmentService;

View file

@ -1,16 +1,11 @@
import {LearningObjectProvider} from "./learning-object-provider";
import {
FilteredLearningObject,
LearningObjectIdentifier,
LearningPathIdentifier
} from "../../interfaces/learning-content";
import {getLearningObjectRepository, getLearningPathRepository} from "../../data/repositories";
import {Language} from "../../entities/content/language";
import {LearningObject} from "../../entities/content/learning-object.entity";
import {getUrlStringForLearningObject} from "../../util/links";
import processingService from "./processing/processing-service";
import {NotFoundError} from "@mikro-orm/core";
import { LearningObjectProvider } from './learning-object-provider';
import { FilteredLearningObject, LearningObjectIdentifier, LearningPathIdentifier } from '../../interfaces/learning-content';
import { getLearningObjectRepository, getLearningPathRepository } from '../../data/repositories';
import { Language } from '../../entities/content/language';
import { LearningObject } from '../../entities/content/learning-object.entity';
import { getUrlStringForLearningObject } from '../../util/links';
import processingService from './processing/processing-service';
import { NotFoundError } from '@mikro-orm/core';
function convertLearningObject(learningObject: LearningObject | null): FilteredLearningObject | null {
if (!learningObject) {
@ -34,20 +29,18 @@ function convertLearningObject(learningObject: LearningObject | null): FilteredL
educationalGoals: learningObject.educationalGoals,
returnValue: {
callback_url: learningObject.returnValue.callbackUrl,
callback_schema: JSON.parse(learningObject.returnValue.callbackSchema)
callback_schema: JSON.parse(learningObject.returnValue.callbackSchema),
},
skosConcepts: learningObject.skosConcepts,
targetAges: learningObject.targetAges || [],
teacherExclusive: learningObject.teacherExclusive
}
teacherExclusive: learningObject.teacherExclusive,
};
}
function findLearningObjectEntityById(id: LearningObjectIdentifier): Promise<LearningObject | null> {
const learningObjectRepo = getLearningObjectRepository();
return learningObjectRepo.findLatestByHruidAndLanguage(
id.hruid, id.language as Language
);
return learningObjectRepo.findLatestByHruidAndLanguage(id.hruid, id.language as Language);
}
/**
@ -68,16 +61,11 @@ const databaseLearningObjectProvider: LearningObjectProvider = {
async getLearningObjectHTML(id: LearningObjectIdentifier): Promise<string | null> {
const learningObjectRepo = getLearningObjectRepository();
const learningObject = await learningObjectRepo.findLatestByHruidAndLanguage(
id.hruid, id.language as Language
);
const learningObject = await learningObjectRepo.findLatestByHruidAndLanguage(id.hruid, id.language as Language);
if (!learningObject) {
return null;
}
return await processingService.render(
learningObject,
(id) => findLearningObjectEntityById(id)
);
return await processingService.render(learningObject, (id) => findLearningObjectEntityById(id));
},
/**
@ -88,9 +76,9 @@ const databaseLearningObjectProvider: LearningObjectProvider = {
const learningPath = await learningPathRepo.findByHruidAndLanguage(id.hruid, id.language);
if (!learningPath) {
throw new NotFoundError("The learning path with the given ID could not be found.");
throw new NotFoundError('The learning path with the given ID could not be found.');
}
return learningPath.nodes.map(it => it.learningObjectHruid); // TODO: Determine this based on the submissions of the user.
return learningPath.nodes.map((it) => it.learningObjectHruid); // TODO: Determine this based on the submissions of the user.
},
/**
@ -101,23 +89,23 @@ const databaseLearningObjectProvider: LearningObjectProvider = {
const learningPath = await learningPathRepo.findByHruidAndLanguage(id.hruid, id.language);
if (!learningPath) {
throw new NotFoundError("The learning path with the given ID could not be found.");
throw new NotFoundError('The learning path with the given ID could not be found.');
}
const learningObjects = await Promise.all(
learningPath.nodes.map(it => {
learningPath.nodes.map((it) => {
const learningObject = this.getLearningObjectById({
hruid: it.learningObjectHruid,
language: it.language,
version: it.version
})
version: it.version,
});
if (learningObject === null) {
console.log(`WARN: Learning object corresponding with node ${it} not found!`);
}
return learningObject;
})
);
return learningObjects.filter(it => it !== null); // TODO: Determine this based on the submissions of the user.
}
}
return learningObjects.filter((it) => it !== null); // TODO: Determine this based on the submissions of the user.
},
};
export default databaseLearningObjectProvider;

View file

@ -1,22 +1,22 @@
import { DWENGO_API_BASE } from '../../config.js';
import { fetchWithLogging } from '../../util/apiHelper.js';
import {
FilteredLearningObject, LearningObjectIdentifier,
FilteredLearningObject,
LearningObjectIdentifier,
LearningObjectMetadata,
LearningObjectNode, LearningPathIdentifier,
LearningObjectNode,
LearningPathIdentifier,
LearningPathResponse,
} from '../../interfaces/learning-content.js';
import dwengoApiLearningPathProvider from '../learning-paths/dwengo-api-learning-path-provider.js';
import {LearningObjectProvider} from "./learning-object-provider";
import { LearningObjectProvider } from './learning-object-provider';
/**
* Helper function to convert the learning object metadata retrieved from the API to a FilteredLearningObject which
* our API should return.
* @param data
*/
function filterData(
data: LearningObjectMetadata
): FilteredLearningObject {
function filterData(data: LearningObjectMetadata): FilteredLearningObject {
return {
key: data.hruid, // Hruid learningObject (not path)
_id: data._id,
@ -43,25 +43,16 @@ function filterData(
/**
* Generic helper function to fetch all learning objects from a given path (full data or just HRUIDs)
*/
async function fetchLearningObjects(
learningPathId: LearningPathIdentifier,
full: boolean
): Promise<FilteredLearningObject[] | string[]> {
async function fetchLearningObjects(learningPathId: LearningPathIdentifier, full: boolean): Promise<FilteredLearningObject[] | string[]> {
try {
const learningPathResponse: LearningPathResponse =
await dwengoApiLearningPathProvider.fetchLearningPaths(
[learningPathId.hruid],
learningPathId.language,
`Learning path for HRUID "${learningPathId.hruid}"`
);
const learningPathResponse: LearningPathResponse = await dwengoApiLearningPathProvider.fetchLearningPaths(
[learningPathId.hruid],
learningPathId.language,
`Learning path for HRUID "${learningPathId.hruid}"`
);
if (
!learningPathResponse.success ||
!learningPathResponse.data?.length
) {
console.error(
`⚠️ WARNING: Learning path "${learningPathId.hruid}" exists but contains no learning objects.`
);
if (!learningPathResponse.success || !learningPathResponse.data?.length) {
console.error(`⚠️ WARNING: Learning path "${learningPathId.hruid}" exists but contains no learning objects.`);
return [];
}
@ -72,10 +63,12 @@ async function fetchLearningObjects(
}
return await Promise.all(
nodes.map(async (node) => dwengoApiLearningObjectProvider.getLearningObjectById({
nodes.map(async (node) =>
dwengoApiLearningObjectProvider.getLearningObjectById({
hruid: node.learningobject_hruid,
language: learningPathId.language
}))
language: learningPathId.language,
})
)
).then((objects) => objects.filter((obj): obj is FilteredLearningObject => obj !== null));
} catch (error) {
console.error('❌ Error fetching learning objects:', error);
@ -87,19 +80,17 @@ const dwengoApiLearningObjectProvider: LearningObjectProvider = {
/**
* Fetches a single learning object by its HRUID
*/
async getLearningObjectById(
id: LearningObjectIdentifier
): Promise<FilteredLearningObject | null> {
async getLearningObjectById(id: LearningObjectIdentifier): Promise<FilteredLearningObject | null> {
const metadataUrl = `${DWENGO_API_BASE}/learningObject/getMetadata`;
const metadata = await fetchWithLogging<LearningObjectMetadata>(
metadataUrl,
`Metadata for Learning Object HRUID "${id.hruid}" (language ${id.language})`,
{
params: id
params: id,
}
);
if (!metadata || typeof metadata !== "object") {
if (!metadata || typeof metadata !== 'object') {
console.error(`⚠️ WARNING: Learning object "${id.hruid}" not found.`);
return null;
}
@ -111,10 +102,7 @@ const dwengoApiLearningObjectProvider: LearningObjectProvider = {
* Fetch full learning object data (metadata)
*/
async getLearningObjectsFromPath(id: LearningPathIdentifier): Promise<FilteredLearningObject[]> {
return (await fetchLearningObjects(
id,
true,
)) as FilteredLearningObject[];
return (await fetchLearningObjects(id, true)) as FilteredLearningObject[];
},
/**
@ -130,13 +118,9 @@ const dwengoApiLearningObjectProvider: LearningObjectProvider = {
*/
async getLearningObjectHTML(id: LearningObjectIdentifier): Promise<string | null> {
const htmlUrl = `${DWENGO_API_BASE}/learningObject/getRaw`;
const html = await fetchWithLogging<string>(
htmlUrl,
`Metadata for Learning Object HRUID "${id.hruid}" (language ${id.language})`,
{
params: id
}
);
const html = await fetchWithLogging<string>(htmlUrl, `Metadata for Learning Object HRUID "${id.hruid}" (language ${id.language})`, {
params: id,
});
if (!html) {
console.error(`⚠️ WARNING: Learning object "${id.hruid}" not found.`);
@ -144,7 +128,7 @@ const dwengoApiLearningObjectProvider: LearningObjectProvider = {
}
return html;
}
},
};
export default dwengoApiLearningObjectProvider;

View file

@ -1,8 +1,4 @@
import {
FilteredLearningObject,
LearningObjectIdentifier,
LearningPathIdentifier
} from "../../interfaces/learning-content";
import { FilteredLearningObject, LearningObjectIdentifier, LearningPathIdentifier } from '../../interfaces/learning-content';
export interface LearningObjectProvider {
/**

View file

@ -1,19 +1,14 @@
import {
FilteredLearningObject,
LearningObjectIdentifier,
LearningPathIdentifier
} from "../../interfaces/learning-content";
import dwengoApiLearningObjectProvider from "./dwengo-api-learning-object-provider";
import {LearningObjectProvider} from "./learning-object-provider";
import {EnvVars, getEnvVar} from "../../util/envvars";
import databaseLearningObjectProvider from "./database-learning-object-provider";
import { FilteredLearningObject, LearningObjectIdentifier, LearningPathIdentifier } from '../../interfaces/learning-content';
import dwengoApiLearningObjectProvider from './dwengo-api-learning-object-provider';
import { LearningObjectProvider } from './learning-object-provider';
import { EnvVars, getEnvVar } from '../../util/envvars';
import databaseLearningObjectProvider from './database-learning-object-provider';
function getProvider(id: LearningObjectIdentifier): LearningObjectProvider {
if (id.hruid.startsWith(getEnvVar(EnvVars.UserContentPrefix))) {
return databaseLearningObjectProvider;
}
return dwengoApiLearningObjectProvider;
}
return dwengoApiLearningObjectProvider;
}
/**
@ -46,7 +41,7 @@ const learningObjectService = {
*/
getLearningObjectHTML(id: LearningObjectIdentifier): Promise<string | null> {
return getProvider(id).getLearningObjectHTML(id);
}
},
};
export default learningObjectService;

View file

@ -5,12 +5,11 @@
*/
import DOMPurify from 'isomorphic-dompurify';
import {type} from "node:os";
import {DwengoContentType} from "../content-type";
import {StringProcessor} from "../string-processor";
import { type } from 'node:os';
import { DwengoContentType } from '../content-type';
import { StringProcessor } from '../string-processor';
class AudioProcessor extends StringProcessor {
constructor() {
super(DwengoContentType.AUDIO_MPEG);
}

View file

@ -3,16 +3,16 @@
*/
enum DwengoContentType {
TEXT_PLAIN = "text/plain",
TEXT_MARKDOWN = "text/markdown",
IMAGE_BLOCK = "image/image-block",
IMAGE_INLINE = "image/image",
AUDIO_MPEG = "audio/mpeg",
APPLICATION_PDF = "application/pdf",
EXTERN = "extern",
BLOCKLY = "blockly",
GIFT = "text/gift",
CT_SCHEMA = "text/ct-schema"
TEXT_PLAIN = 'text/plain',
TEXT_MARKDOWN = 'text/markdown',
IMAGE_BLOCK = 'image/image-block',
IMAGE_INLINE = 'image/image',
AUDIO_MPEG = 'audio/mpeg',
APPLICATION_PDF = 'application/pdf',
EXTERN = 'extern',
BLOCKLY = 'blockly',
GIFT = 'text/gift',
CT_SCHEMA = 'text/ct-schema',
}
export { DwengoContentType }
export { DwengoContentType };

View file

@ -5,10 +5,10 @@
*/
import DOMPurify from 'isomorphic-dompurify';
import {ProcessingError} from "../processing-error";
import {isValidHttpUrl} from "../../../../util/links";
import {DwengoContentType} from "../content-type";
import {StringProcessor} from "../string-processor";
import { ProcessingError } from '../processing-error';
import { isValidHttpUrl } from '../../../../util/links';
import { DwengoContentType } from '../content-type';
import { StringProcessor } from '../string-processor';
class ExternProcessor extends StringProcessor {
constructor() {
@ -17,23 +17,23 @@ class ExternProcessor extends StringProcessor {
override renderFn(externURL: string) {
if (!isValidHttpUrl(externURL)) {
throw new ProcessingError("The url is not valid: " + externURL);
throw new ProcessingError('The url is not valid: ' + externURL);
}
// If a seperate youtube-processor would be added, this code would need to move to that processor
// Converts youtube urls to youtube-embed urls
const match = /(.*youtube.com\/)watch\?v=(.*)/.exec(externURL)
const match = /(.*youtube.com\/)watch\?v=(.*)/.exec(externURL);
if (match) {
externURL = match[1] + "embed/" + match[2];
externURL = match[1] + 'embed/' + match[2];
}
return DOMPurify.sanitize(`
return DOMPurify.sanitize(
`
<div class="iframe-container">
<iframe src="${externURL}" allowfullscreen></iframe>
</div>`,
{ ADD_TAGS: ["iframe"], ADD_ATTR: ['allow', 'allowfullscreen', 'frameborder', 'scrolling']}
{ ADD_TAGS: ['iframe'], ADD_ATTR: ['allow', 'allowfullscreen', 'frameborder', 'scrolling'] }
);
}
}

View file

@ -3,21 +3,20 @@
*/
import DOMPurify from 'isomorphic-dompurify';
import {GIFTQuestion, parse} from "gift-pegjs"
import {DwengoContentType} from "../content-type";
import {GIFTQuestionRenderer} from "./question-renderers/gift-question-renderer";
import {MultipleChoiceQuestionRenderer} from "./question-renderers/multiple-choice-question-renderer";
import {CategoryQuestionRenderer} from "./question-renderers/category-question-renderer";
import {DescriptionQuestionRenderer} from "./question-renderers/description-question-renderer";
import {EssayQuestionRenderer} from "./question-renderers/essay-question-renderer";
import {MatchingQuestionRenderer} from "./question-renderers/matching-question-renderer";
import {NumericalQuestionRenderer} from "./question-renderers/numerical-question-renderer";
import {ShortQuestionRenderer} from "./question-renderers/short-question-renderer";
import {TrueFalseQuestionRenderer} from "./question-renderers/true-false-question-renderer";
import {StringProcessor} from "../string-processor";
import { GIFTQuestion, parse } from 'gift-pegjs';
import { DwengoContentType } from '../content-type';
import { GIFTQuestionRenderer } from './question-renderers/gift-question-renderer';
import { MultipleChoiceQuestionRenderer } from './question-renderers/multiple-choice-question-renderer';
import { CategoryQuestionRenderer } from './question-renderers/category-question-renderer';
import { DescriptionQuestionRenderer } from './question-renderers/description-question-renderer';
import { EssayQuestionRenderer } from './question-renderers/essay-question-renderer';
import { MatchingQuestionRenderer } from './question-renderers/matching-question-renderer';
import { NumericalQuestionRenderer } from './question-renderers/numerical-question-renderer';
import { ShortQuestionRenderer } from './question-renderers/short-question-renderer';
import { TrueFalseQuestionRenderer } from './question-renderers/true-false-question-renderer';
import { StringProcessor } from '../string-processor';
class GiftProcessor extends StringProcessor {
private renderers: RendererMap = {
Category: new CategoryQuestionRenderer(),
Description: new DescriptionQuestionRenderer(),
@ -26,8 +25,8 @@ class GiftProcessor extends StringProcessor {
Numerical: new NumericalQuestionRenderer(),
Short: new ShortQuestionRenderer(),
TF: new TrueFalseQuestionRenderer(),
MC: new MultipleChoiceQuestionRenderer()
}
MC: new MultipleChoiceQuestionRenderer(),
};
constructor() {
super(DwengoContentType.GIFT);
@ -40,11 +39,11 @@ class GiftProcessor extends StringProcessor {
let i = 1;
for (const question of quizQuestions) {
html += ` <div class='gift-question' id='gift-q${i}'>\n`;
html += " " + this.renderQuestion(question, i).replaceAll(/\n(.+)/g, "\n $1"); // Replace for indentation.
html += ' ' + this.renderQuestion(question, i).replaceAll(/\n(.+)/g, '\n $1'); // Replace for indentation.
html += ` </div>\n`;
i++;
}
html += "</div>\n"
html += '</div>\n';
return DOMPurify.sanitize(html);
}
@ -56,7 +55,7 @@ class GiftProcessor extends StringProcessor {
}
type RendererMap = {
[K in GIFTQuestion["type"]]: GIFTQuestionRenderer<Extract<GIFTQuestion, { type: K }>>
[K in GIFTQuestion['type']]: GIFTQuestionRenderer<Extract<GIFTQuestion, { type: K }>>;
};
export default GiftProcessor;

View file

@ -1,6 +1,6 @@
import {GIFTQuestionRenderer} from "./gift-question-renderer";
import {Category} from "gift-pegjs";
import {ProcessingError} from "../../processing-error";
import { GIFTQuestionRenderer } from './gift-question-renderer';
import { Category } from 'gift-pegjs';
import { ProcessingError } from '../../processing-error';
export class CategoryQuestionRenderer extends GIFTQuestionRenderer<Category> {
render(question: Category, questionNumber: number): string {

View file

@ -1,6 +1,6 @@
import {GIFTQuestionRenderer} from "./gift-question-renderer";
import {Description} from "gift-pegjs";
import {ProcessingError} from "../../processing-error";
import { GIFTQuestionRenderer } from './gift-question-renderer';
import { Description } from 'gift-pegjs';
import { ProcessingError } from '../../processing-error';
export class DescriptionQuestionRenderer extends GIFTQuestionRenderer<Description> {
render(question: Description, questionNumber: number): string {

View file

@ -1,9 +1,9 @@
import {GIFTQuestionRenderer} from "./gift-question-renderer";
import {Essay} from "gift-pegjs";
import { GIFTQuestionRenderer } from './gift-question-renderer';
import { Essay } from 'gift-pegjs';
export class EssayQuestionRenderer extends GIFTQuestionRenderer<Essay> {
render(question: Essay, questionNumber: number): string {
let renderedHtml = "";
let renderedHtml = '';
if (question.title) {
renderedHtml += `<h2 class='gift-title' id='gift-q${questionNumber}-title'>${question.title}</h2>\n`;
}

View file

@ -1,4 +1,4 @@
import {GIFTQuestion} from "gift-pegjs";
import { GIFTQuestion } from 'gift-pegjs';
/**
* Subclasses of this class are renderers which can render a specific type of GIFT questions to HTML.

View file

@ -1,6 +1,6 @@
import {GIFTQuestionRenderer} from "./gift-question-renderer";
import {Matching} from "gift-pegjs";
import {ProcessingError} from "../../processing-error";
import { GIFTQuestionRenderer } from './gift-question-renderer';
import { Matching } from 'gift-pegjs';
import { ProcessingError } from '../../processing-error';
export class MatchingQuestionRenderer extends GIFTQuestionRenderer<Matching> {
render(question: Matching, questionNumber: number): string {

View file

@ -1,9 +1,9 @@
import {GIFTQuestionRenderer} from "./gift-question-renderer";
import {MultipleChoice} from "gift-pegjs";
import { GIFTQuestionRenderer } from './gift-question-renderer';
import { MultipleChoice } from 'gift-pegjs';
export class MultipleChoiceQuestionRenderer extends GIFTQuestionRenderer<MultipleChoice> {
render(question: MultipleChoice, questionNumber: number): string {
let renderedHtml = "";
let renderedHtml = '';
if (question.title) {
renderedHtml += `<h2 class='gift-title' id='gift-q${questionNumber}-title'>${question.title}</h2>\n`;
}

View file

@ -1,6 +1,6 @@
import {GIFTQuestionRenderer} from "./gift-question-renderer";
import {Numerical} from "gift-pegjs";
import {ProcessingError} from "../../processing-error";
import { GIFTQuestionRenderer } from './gift-question-renderer';
import { Numerical } from 'gift-pegjs';
import { ProcessingError } from '../../processing-error';
export class NumericalQuestionRenderer extends GIFTQuestionRenderer<Numerical> {
render(question: Numerical, questionNumber: number): string {

View file

@ -1,6 +1,6 @@
import {GIFTQuestionRenderer} from "./gift-question-renderer";
import {ShortAnswer} from "gift-pegjs";
import {ProcessingError} from "../../processing-error";
import { GIFTQuestionRenderer } from './gift-question-renderer';
import { ShortAnswer } from 'gift-pegjs';
import { ProcessingError } from '../../processing-error';
export class ShortQuestionRenderer extends GIFTQuestionRenderer<ShortAnswer> {
render(question: ShortAnswer, questionNumber: number): string {

View file

@ -1,6 +1,6 @@
import {GIFTQuestionRenderer} from "./gift-question-renderer";
import {TrueFalse} from "gift-pegjs";
import {ProcessingError} from "../../processing-error";
import { GIFTQuestionRenderer } from './gift-question-renderer';
import { TrueFalse } from 'gift-pegjs';
import { ProcessingError } from '../../processing-error';
export class TrueFalseQuestionRenderer extends GIFTQuestionRenderer<TrueFalse> {
render(question: TrueFalse, questionNumber: number): string {

View file

@ -2,15 +2,15 @@
* Based on https://github.com/dwengovzw/Learning-Object-Repository/blob/main/app/processors/image/block_image_processor.js
*/
import InlineImageProcessor from "./inline-image-processor.js"
import InlineImageProcessor from './inline-image-processor.js';
import DOMPurify from 'isomorphic-dompurify';
class BlockImageProcessor extends InlineImageProcessor {
constructor(){
constructor() {
super();
}
override renderFn(imageUrl: string){
override renderFn(imageUrl: string) {
const inlineHtml = super.render(imageUrl);
return DOMPurify.sanitize(`<div>${inlineHtml}</div>`);
}

View file

@ -3,10 +3,10 @@
*/
import DOMPurify from 'isomorphic-dompurify';
import {DwengoContentType} from "../content-type.js";
import {ProcessingError} from "../processing-error.js";
import {isValidHttpUrl} from "../../../../util/links";
import {StringProcessor} from "../string-processor";
import { DwengoContentType } from '../content-type.js';
import { ProcessingError } from '../processing-error.js';
import { isValidHttpUrl } from '../../../../util/links';
import { StringProcessor } from '../string-processor';
class InlineImageProcessor extends StringProcessor {
constructor(contentType: DwengoContentType = DwengoContentType.IMAGE_INLINE) {

View file

@ -1,15 +1,15 @@
/**
* Based on https://github.com/dwengovzw/Learning-Object-Repository/blob/main/app/processors/markdown/learing_object_markdown_renderer.js [sic!]
*/
import PdfProcessor from "../pdf/pdf-processor.js";
import AudioProcessor from "../audio/audio-processor.js";
import ExternProcessor from "../extern/extern-processor.js";
import InlineImageProcessor from "../image/inline-image-processor.js";
import * as marked from "marked";
import {getUrlStringForLearningObjectHTML, isValidHttpUrl} from "../../../../util/links";
import {ProcessingError} from "../processing-error";
import {LearningObjectIdentifier} from "../../../../interfaces/learning-content";
import {Language} from "../../../../entities/content/language";
import PdfProcessor from '../pdf/pdf-processor.js';
import AudioProcessor from '../audio/audio-processor.js';
import ExternProcessor from '../extern/extern-processor.js';
import InlineImageProcessor from '../image/inline-image-processor.js';
import * as marked from 'marked';
import { getUrlStringForLearningObjectHTML, isValidHttpUrl } from '../../../../util/links';
import { ProcessingError } from '../processing-error';
import { LearningObjectIdentifier } from '../../../../interfaces/learning-content';
import { Language } from '../../../../entities/content/language';
import Image = marked.Tokens.Image;
import Heading = marked.Tokens.Heading;
@ -27,11 +27,11 @@ const prefixes = {
};
function extractLearningObjectIdFromHref(href: string): LearningObjectIdentifier {
const [hruid, language, version] = href.split(/\/(.+)/, 2)[1].split("/");
const [hruid, language, version] = href.split(/\/(.+)/, 2)[1].split('/');
return {
hruid,
language: language as Language,
version: parseInt(version)
version: parseInt(version),
};
}
@ -41,39 +41,40 @@ function extractLearningObjectIdFromHref(href: string): LearningObjectIdentifier
* - links to other learning objects,
* - embeddings of other learning objects.
*/
const dwengoMarkedRenderer: RendererObject = {
const dwengoMarkedRenderer: RendererObject = {
heading(heading: Heading): string {
const text = heading.text;
const level = heading.depth;
const escapedText = text.toLowerCase().replace(/[^\w]+/g, '-');
return `<h${level}>\n` +
` <a name="${escapedText}" class="anchor" href="#${escapedText}">\n` +
` <span class="header-link"></span>\n` +
` </a>\n` +
` ${text}\n` +
`</h${level}>\n`
return (
`<h${level}>\n` +
` <a name="${escapedText}" class="anchor" href="#${escapedText}">\n` +
` <span class="header-link"></span>\n` +
` </a>\n` +
` ${text}\n` +
`</h${level}>\n`
);
},
// When the syntax for a link is used => [text](href "title")
// Render a custom link when the prefix for a learning object is used.
link(link: Link): string {
const href = link.href;
const title = link.title || "";
const title = link.title || '';
const text = marked.parseInline(link.text); // There could for example be an image in the link.
if (href.startsWith(prefixes.learningObject)) {
// Link to learning-object
const learningObjectId = extractLearningObjectIdFromHref(href);
return `<a href="${getUrlStringForLearningObjectHTML(learningObjectId)}" target="_blank" title="${title}">${text}</a>`;
}
// Any other link
if (!isValidHttpUrl(href)) {
throw new ProcessingError("Link is not a valid HTTP URL!");
}
//<a href="https://kiks.ilabt.imec.be/hub/tmplogin?id=0101" title="Notebooks Werking"><img src="Knop.png" alt="" title="Knop"></a>
return `<a href="${href}" target="_blank" title="${title}">${text}</a>`;
}
// Any other link
if (!isValidHttpUrl(href)) {
throw new ProcessingError('Link is not a valid HTTP URL!');
}
//<a href="https://kiks.ilabt.imec.be/hub/tmplogin?id=0101" title="Notebooks Werking"><img src="Knop.png" alt="" title="Knop"></a>
return `<a href="${href}" target="_blank" title="${title}">${text}</a>`;
},
// When the syntax for an image is used => ![text](href "title")
@ -98,12 +99,11 @@ function extractLearningObjectIdFromHref(href: string): LearningObjectIdentifier
// Embedded youtube video or notebook (or other extern content)
const proc = new ExternProcessor();
return proc.render(href.split(/\/(.+)/, 2)[1]);
}
// Embedded image
const proc = new InlineImageProcessor();
return proc.render(href)
}
// Embedded image
const proc = new InlineImageProcessor();
return proc.render(href);
},
}
};
export default dwengoMarkedRenderer;

View file

@ -2,12 +2,12 @@
* Based on https://github.com/dwengovzw/Learning-Object-Repository/blob/main/app/processors/markdown/markdown_processor.js
*/
import {marked} from 'marked';
import { marked } from 'marked';
import InlineImageProcessor from '../image/inline-image-processor.js';
import {DwengoContentType} from "../content-type";
import dwengoMarkedRenderer from "./dwengo-marked-renderer";
import {StringProcessor} from "../string-processor";
import {ProcessingError} from "../processing-error";
import { DwengoContentType } from '../content-type';
import dwengoMarkedRenderer from './dwengo-marked-renderer';
import { StringProcessor } from '../string-processor';
import { ProcessingError } from '../processing-error';
class MarkdownProcessor extends StringProcessor {
constructor() {
@ -15,10 +15,10 @@ class MarkdownProcessor extends StringProcessor {
}
override renderFn(mdText: string) {
let html = "";
let html = '';
try {
marked.use({renderer: dwengoMarkedRenderer});
html = marked(mdText, {async: false});
marked.use({ renderer: dwengoMarkedRenderer });
html = marked(mdText, { async: false });
html = this.replaceLinks(html); // Replace html image links path
} catch (e: any) {
throw new ProcessingError(e.message);
@ -28,14 +28,10 @@ class MarkdownProcessor extends StringProcessor {
replaceLinks(html: string) {
const proc = new InlineImageProcessor();
html = html.replace(/<img.*?src="(.*?)".*?(alt="(.*?)")?.*?(title="(.*?)")?.*?>/g, (
match: string,
src: string,
alt: string,
altText: string,
title: string,
titleText: string
) => proc.render(src));
html = html.replace(
/<img.*?src="(.*?)".*?(alt="(.*?)")?.*?(title="(.*?)")?.*?>/g,
(match: string, src: string, alt: string, altText: string, title: string, titleText: string) => proc.render(src)
);
return html;
}
}

View file

@ -5,10 +5,10 @@
*/
import DOMPurify from 'isomorphic-dompurify';
import {DwengoContentType} from "../content-type.js";
import {isValidHttpUrl} from "../../../../util/links.js";
import {ProcessingError} from "../processing-error.js";
import {StringProcessor} from "../string-processor";
import { DwengoContentType } from '../content-type.js';
import { isValidHttpUrl } from '../../../../util/links.js';
import { ProcessingError } from '../processing-error.js';
import { StringProcessor } from '../string-processor';
class PdfProcessor extends StringProcessor {
constructor() {
@ -20,9 +20,11 @@ class PdfProcessor extends StringProcessor {
throw new ProcessingError(`PDF URL is invalid: ${pdfUrl}`);
}
return DOMPurify.sanitize(`
return DOMPurify.sanitize(
`
<embed src="${pdfUrl}" type="application/pdf" width="100%" height="800px"/>
`, { ADD_TAGS: ["embed"] }
`,
{ ADD_TAGS: ['embed'] }
);
}
}

View file

@ -2,20 +2,20 @@
* Based on https://github.com/dwengovzw/Learning-Object-Repository/blob/main/app/processors/processing_proxy.js
*/
import BlockImageProcessor from "./image/block-image-processor.js";
import InlineImageProcessor from "./image/inline-image-processor.js";
import { MarkdownProcessor } from "./markdown/markdown-processor.js";
import TextProcessor from "./text/text-processor.js";
import AudioProcessor from "./audio/audio-processor.js";
import PdfProcessor from "./pdf/pdf-processor.js";
import ExternProcessor from "./extern/extern-processor.js";
import GiftProcessor from "./gift/gift-processor.js";
import {LearningObject} from "../../../entities/content/learning-object.entity";
import Processor from "./processor";
import {DwengoContentType} from "./content-type";
import {LearningObjectIdentifier} from "../../../interfaces/learning-content";
import {Language} from "../../../entities/content/language";
import {replaceAsync} from "../../../util/async";
import BlockImageProcessor from './image/block-image-processor.js';
import InlineImageProcessor from './image/inline-image-processor.js';
import { MarkdownProcessor } from './markdown/markdown-processor.js';
import TextProcessor from './text/text-processor.js';
import AudioProcessor from './audio/audio-processor.js';
import PdfProcessor from './pdf/pdf-processor.js';
import ExternProcessor from './extern/extern-processor.js';
import GiftProcessor from './gift/gift-processor.js';
import { LearningObject } from '../../../entities/content/learning-object.entity';
import Processor from './processor';
import { DwengoContentType } from './content-type';
import { LearningObjectIdentifier } from '../../../interfaces/learning-content';
import { Language } from '../../../entities/content/language';
import { replaceAsync } from '../../../util/async';
const EMBEDDED_LEARNING_OBJECT_PLACEHOLDER = /<learning-object hruid="([^"]+)" language="([^"]+)" version="([^"]+)"\/>/g;
const LEARNING_OBJECT_DOES_NOT_EXIST = "<div class='non-existing-learning-object' />";
@ -32,12 +32,10 @@ class ProcessingService {
new AudioProcessor(),
new PdfProcessor(),
new ExternProcessor(),
new GiftProcessor()
new GiftProcessor(),
];
this.processors = new Map(
processors.map(processor => [processor.contentType, processor])
)
this.processors = new Map(processors.map((processor) => [processor.contentType, processor]));
}
/**
@ -65,7 +63,7 @@ class ProcessingService {
const learningObject = await fetchEmbeddedLearningObjects({
hruid,
language: language as Language,
version: parseInt(version)
version: parseInt(version),
});
// If it does not exist, replace it by a placeholder.

View file

@ -1,6 +1,6 @@
import {LearningObject} from "../../../entities/content/learning-object.entity";
import {ProcessingError} from "./processing-error";
import {DwengoContentType} from "./content-type";
import { LearningObject } from '../../../entities/content/learning-object.entity';
import { ProcessingError } from './processing-error';
import { DwengoContentType } from './content-type';
/**
* Abstract base class for all processors.

View file

@ -1,5 +1,5 @@
import Processor from "./processor";
import {LearningObject} from "../../../entities/content/learning-object.entity";
import Processor from './processor';
import { LearningObject } from '../../../entities/content/learning-object.entity';
export abstract class StringProcessor extends Processor<string> {
/**
@ -14,6 +14,6 @@ export abstract class StringProcessor extends Processor<string> {
* @protected
*/
protected renderLearningObjectFn(toRender: LearningObject): string {
return this.render(toRender.content.toString("ascii"));
return this.render(toRender.content.toString('ascii'));
}
}

View file

@ -3,8 +3,8 @@
*/
import DOMPurify from 'isomorphic-dompurify';
import {DwengoContentType} from "../content-type.js";
import {StringProcessor} from "../string-processor";
import { DwengoContentType } from '../content-type.js';
import { StringProcessor } from '../string-processor';
class TextProcessor extends StringProcessor {
constructor() {