From fd4c1a205db2fb455fbcdd61ba29b211253a3c77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Janis=20Da=CC=88hne?= <janis.daehne@informatik.uni-halle.de> Date: Tue, 1 Nov 2022 17:01:00 +0100 Subject: [PATCH] - added support for language specific exercise description blocks - releases list now shows programming language --- package.json | 1 + .../markdownRenderer.tsx | 36 ++++++++++++++++++- .../renderContentComponents/renderWrapper.tsx | 6 ++++ .../sites/doExerciseSite/doExerciseSite.tsx | 2 ++ .../editCustomProjectSite.tsx | 1 + .../exerciseEditorSite/exerciseEditorSite.tsx | 1 + .../exerciseSyntaxHelpPanelView.tsx | 2 +- .../previewPanel/previewPanelView.tsx | 7 ++++ .../releasesSite/listView.tsx | 31 +++++++++++++++- .../sites/tutorViewSite/tutorViewSite.tsx | 2 ++ src/constants.ts | 5 ++- src/helpers/markdownHelper.ts | 19 ++++++++++ .../exerciseQuestionSyntaxGuide.md | 22 ++++++++++++ src/styles/common.styl | 3 ++ yarn.lock | 5 +++ 15 files changed, 139 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 7c31e6f5..ed6398c0 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "lodash": "4.17.10", "lodash-es": "4.17.10", "markdown-it": "8.4.2", + "markdown-it-container": "3.0.0", "markdown-it-mathjax": "2.0.0", "mathjax": "2.7.x", "moment": "2.22.2", diff --git a/src/components/renderContentComponents/markdownRenderer.tsx b/src/components/renderContentComponents/markdownRenderer.tsx index b116c768..bf875c58 100644 --- a/src/components/renderContentComponents/markdownRenderer.tsx +++ b/src/components/renderContentComponents/markdownRenderer.tsx @@ -14,6 +14,7 @@ import Logger from "../../helpers/logger"; //because we need mathjax support support in markdown we changed the parser a bit... import markdown from '../../helpers/markdownHelper' +import { markdownPLangBlockDataLangAttribute, markdownPLangBlocksClass } from "../../constants"; //const css = require('./styles.styl'); @@ -23,7 +24,8 @@ const mapStateToProps = (rootState: RootState , props: MyProps) => { return { //test0: rootState... //test: props.test - ...props + ...props, + pLangs: rootState.pLangsState.pLangs, } } @@ -54,6 +56,7 @@ class markdownRenderer extends React.Component<Props, any> { //this can happen if the user opens a dialog --> this will be unmounted //but dummy won't change anymore (only set to false once when everything is loaded & assets created) this.refreshMath(); + this.removeBlocksForWrongPLang(); } } @@ -62,6 +65,7 @@ class markdownRenderer extends React.Component<Props, any> { //see componentDidMount for explanation if (this.props.content && this.props.dummy === false) { this.refreshMath() + this.removeBlocksForWrongPLang(); } } @@ -74,6 +78,36 @@ class markdownRenderer extends React.Component<Props, any> { } } + removeBlocksForWrongPLang(): void { + + const plang = this.props.pLangs.find(p => p.id === this.props.plangId) + + const allPLangBlocks = Array.from(document.querySelectorAll(`.${markdownPLangBlocksClass}[${markdownPLangBlockDataLangAttribute}]`)) + + for (let i = 0; i < allPLangBlocks.length; i++) { + const divBlock = allPLangBlocks[i]; + + const langAttribute = divBlock.getAttribute(`${markdownPLangBlockDataLangAttribute}`) + + if (!langAttribute) { + divBlock.remove() + + } else { + //we have an attribute, check if the block is for the specified lang + + //no filter or correct lang + if (!plang || plang.displayName.trim() === langAttribute.trim()) { + //ok + continue + } + + //else remove + divBlock.remove() + } + } + + } + render(): JSX.Element { diff --git a/src/components/renderContentComponents/renderWrapper.tsx b/src/components/renderContentComponents/renderWrapper.tsx index b8b5a31e..61b4e3ab 100644 --- a/src/components/renderContentComponents/renderWrapper.tsx +++ b/src/components/renderContentComponents/renderWrapper.tsx @@ -33,6 +33,12 @@ export interface MyProps { * id used for printing */ readonly printId: string + + /** + * some (markdown) blocks might be only for specific languages, + * use null to show them all + */ + readonly plangId: number | null } const mapStateToProps = (rootState: RootState, props: MyProps) => { diff --git a/src/components/sites/doExerciseSite/doExerciseSite.tsx b/src/components/sites/doExerciseSite/doExerciseSite.tsx index fa54260b..c0d4afaa 100644 --- a/src/components/sites/doExerciseSite/doExerciseSite.tsx +++ b/src/components/sites/doExerciseSite/doExerciseSite.tsx @@ -1185,6 +1185,7 @@ class doExerciseSite extends React.Component<Props & RouteComponentProps<Matched assets={this.props.doExercise.exerciseDescription.assets} dummy={this.props.dummy} fontSizeInPx={this.props.taskDescriptionFontSizeInPx} + plangId={this.props.solutionPLangId} /> </TabContentHost> @@ -1207,6 +1208,7 @@ class doExerciseSite extends React.Component<Props & RouteComponentProps<Matched contentType={QuestionType.markdown} dummy={this.props.dummy} fontSizeInPx={this.props.taskDescriptionFontSizeInPx} + plangId={null} /> </TabContentHost> } diff --git a/src/components/sites/editCustomProjectSite/editCustomProjectSite.tsx b/src/components/sites/editCustomProjectSite/editCustomProjectSite.tsx index 6e803761..20bbaeb6 100644 --- a/src/components/sites/editCustomProjectSite/editCustomProjectSite.tsx +++ b/src/components/sites/editCustomProjectSite/editCustomProjectSite.tsx @@ -534,6 +534,7 @@ class editCustomProjectSite extends React.Component<Props & RouteComponentProps< assets={this.props.customProjectDescription.assets} dummy={false /*not needed because we have tabs that will re render if tab index changed*/} fontSizeInPx={this.props.taskDescriptionFontSizeInPx} + plangId={null} /> </TabContentHost> diff --git a/src/components/sites/exerciseEditorSite/exerciseEditorSite.tsx b/src/components/sites/exerciseEditorSite/exerciseEditorSite.tsx index 377627e4..b8215d48 100644 --- a/src/components/sites/exerciseEditorSite/exerciseEditorSite.tsx +++ b/src/components/sites/exerciseEditorSite/exerciseEditorSite.tsx @@ -495,6 +495,7 @@ class exerciseEditorSite extends React.Component<Props & RouteComponentProps<Mat assets={this.props.editorExercise.exerciseDescription.assets} dummy={false /*not needed because we have tabs that will re render if tab index changed*/} fontSizeInPx={this.props.taskDescriptionFontSizeInPx} + plangId={null} /> </TabContentHost> diff --git a/src/components/sites/exerciseEditorSite/exerciseSyntaxHelpPanel/exerciseSyntaxHelpPanelView.tsx b/src/components/sites/exerciseEditorSite/exerciseSyntaxHelpPanel/exerciseSyntaxHelpPanelView.tsx index 58ebafe5..7385d91d 100644 --- a/src/components/sites/exerciseEditorSite/exerciseSyntaxHelpPanel/exerciseSyntaxHelpPanelView.tsx +++ b/src/components/sites/exerciseEditorSite/exerciseSyntaxHelpPanel/exerciseSyntaxHelpPanelView.tsx @@ -43,7 +43,7 @@ class exerciseSyntaxHelpPanelView extends React.Component<Props, any> { } return ( - <div className="fh"> + <div className="fh markdown-guide"> <div dangerouslySetInnerHTML={content}> </div> diff --git a/src/components/sites/exerciseEditorSite/previewPanel/previewPanelView.tsx b/src/components/sites/exerciseEditorSite/previewPanel/previewPanelView.tsx index 5cd85a4f..56a785cd 100644 --- a/src/components/sites/exerciseEditorSite/previewPanel/previewPanelView.tsx +++ b/src/components/sites/exerciseEditorSite/previewPanel/previewPanelView.tsx @@ -29,6 +29,12 @@ export interface MyProps { readonly fontSizeInPx: number readonly printId: string + + /** + * some (markdown) blocks might be only for specific languages, + * use null to show them all + */ + readonly plangId: number | null } const mapStateToProps = (rootState: RootState, props: MyProps) => { @@ -87,6 +93,7 @@ class PreviewPanelView extends React.Component<Props, any> { assets={this.props.assets} dummy={this.props.dummy} fontSizeInPx={this.props.fontSizeInPx} + plangId={this.props.plangId} /> } diff --git a/src/components/sites/manageOwnOrGroupExerciseComponents/releasesSite/listView.tsx b/src/components/sites/manageOwnOrGroupExerciseComponents/releasesSite/listView.tsx index b6cf376f..b8dc7c2b 100644 --- a/src/components/sites/manageOwnOrGroupExerciseComponents/releasesSite/listView.tsx +++ b/src/components/sites/manageOwnOrGroupExerciseComponents/releasesSite/listView.tsx @@ -29,6 +29,7 @@ import {MyPopup} from "../../../helpers/myPopup"; import {getI18n} from "../../../../../i18n/i18nRoot"; import PaginationTableFooter from '../../../helpers/paginationTableFooter' import {ReleaseDurationType} from '../../../../types/ReleaseDurationType' +import {ExercisePreviewFromBackend} from '../../../../types/exercisePreview' //const css = require('./styles.styl'); @@ -50,7 +51,8 @@ const mapStateToProps = (rootState: RootState /*, props: MyProps*/) => { pagination: rootState.releaseSiteState.pagination, groupRole: rootState.releaseSiteState.groupRole, - langId: rootState.i18nState.langId + langId: rootState.i18nState.langId, + pLangs: rootState.pLangsState.pLangs, } } @@ -217,7 +219,24 @@ class ListView extends React.Component<Props, any> { </Table.HeaderCell> <Table.HeaderCell> + <span className="clickable" + onClick={() => { + this.props.setSortByKey('pLangId', + rotateSorting<ExerciseReleaseFromBackend>(this.props.sortDirection, + this.props.sortByKey, + 'pLangId', + ) + ) + }} + > + { + <HelpPopup icon="puzzle piece" + defaultText={getI18n(this.props.langId,'Programming language')}/> + } + </span> + </Table.HeaderCell> + <Table.HeaderCell> <span> { getI18n(this.props.langId, 'Miscellaneous') @@ -291,6 +310,7 @@ class ListView extends React.Component<Props, any> { { list.map((exerciseRelease, index) => { + let programmingLanguageForRelease = this.props.pLangs.find(p => p.id === exerciseRelease.pLangId) // tslint:disable-next-line let editNode = null @@ -445,6 +465,15 @@ class ListView extends React.Component<Props, any> { </Table.Cell> + <Table.Cell> + { + programmingLanguageForRelease && + <span> + { programmingLanguageForRelease.displayName } + </span> + } + </Table.Cell> + <Table.Cell> <div> diff --git a/src/components/sites/tutorViewSite/tutorViewSite.tsx b/src/components/sites/tutorViewSite/tutorViewSite.tsx index 741556a4..8cb9c000 100644 --- a/src/components/sites/tutorViewSite/tutorViewSite.tsx +++ b/src/components/sites/tutorViewSite/tutorViewSite.tsx @@ -451,6 +451,7 @@ class tutorViewSite extends React.Component<Props & RouteComponentProps<MatchedP assets={this.props.exercise.exerciseDescription.assets} dummy={this.props.dummy} fontSizeInPx={this.props.taskDescriptionFontSizeInPx} + plangId={null} /> </TabContentHost> @@ -497,6 +498,7 @@ class tutorViewSite extends React.Component<Props & RouteComponentProps<MatchedP contentType={QuestionType.markdown} dummy={this.props.dummy} fontSizeInPx={this.props.taskDescriptionFontSizeInPx} + plangId={null} /> </div> } diff --git a/src/constants.ts b/src/constants.ts index 30d52343..fa0a7369 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -13,7 +13,7 @@ import Logger from './helpers/logger' * y - breaking changes / new features * z - fixes, small changes */ -export const versionString = '2.18.0' +export const versionString = '2.18.1' export const supportMail = 'yapex@informatik.uni-halle.de' @@ -404,3 +404,6 @@ export const dashboardRefreshIntervalInS = 5 //used for all tests export const compileTimeoutDefaultsInMs = 3000 + +export const markdownPLangBlocksClass = `plang-part` +export const markdownPLangBlockDataLangAttribute = `data-programming-lang` \ No newline at end of file diff --git a/src/helpers/markdownHelper.ts b/src/helpers/markdownHelper.ts index bbb50f1d..395ac272 100644 --- a/src/helpers/markdownHelper.ts +++ b/src/helpers/markdownHelper.ts @@ -2,6 +2,7 @@ import * as markdownIt from 'markdown-it' import {insertLineNumbers} from './stringHelper' import Logger from './logger' import {copyToClipboard} from './clipboard' +import { markdownPLangBlockDataLangAttribute, markdownPLangBlocksClass } from '../constants' const hljs = require('highlight.js/lib/highlight') @@ -257,6 +258,24 @@ const mdRenderer = markdownIt({ } }) .use(require('markdown-it-mathjax')()) + .use(require('markdown-it-container'), 'exercise-lang-block', { //by default div class="exercise-lang-block" is rendered + validate: (params: string) => { + return params.trim().match(/lang\s+[\w ]+/) + }, + render: (tokens: ReadonlyArray<any>, idx: number) => { + let m = tokens[idx].info.trim().substr(`lang`.length).trim().match(/^[\w ]+$/); + + if (tokens[idx].nesting === 1) { + // opening tag + + //div class="..." with data attribute to identify lang + return `<div class="${markdownPLangBlocksClass}" ${markdownPLangBlockDataLangAttribute}="${mdRenderer.utils.escapeHtml(m[0])}">\n`; + } else { + // closing tag + return '</div>\n'; + } + } + }) //https://github.com/markdown-it/markdown-it/blob/master/docs/architecture.md const oldLinkRule = function (tokens: any, idx: any, options: any, env: any, self: any): any { diff --git a/src/questionSystem/exerciseQuestionSyntaxGuide.md b/src/questionSystem/exerciseQuestionSyntaxGuide.md index 99e3109a..50824f4d 100644 --- a/src/questionSystem/exerciseQuestionSyntaxGuide.md +++ b/src/questionSystem/exerciseQuestionSyntaxGuide.md @@ -66,6 +66,28 @@ class Main { Für eine List mit Sprachen siehe Abschnitt `Syntax highlighting unterstützte Sprachen` (ganz unten) +### Programmiersprachenspezifische Anmerkungen + +Für Anmerkungen/Markdown, welche nur für eine spezielle Programmiersprache angezeigt werden sollen, kann man folgendermaßen vorgehen: + +``` +::: lang [Programmiersprache, wie sie in der Oberfläche angezeigt wird (nicht interner Name)] +::: +``` + +also z.b. + +``` +::: lang Java +Dieser Hinweis wird **nur** für `Java` angezeigt. +::: +``` + +in diesen Blöcken kann wieder Markdown verwendet werden. + +Die Lister der Programmiersprachen hängt von der Konfiguration ab. +Diese Liste ist z.B. beim freigeben einer Aufgabe zu finden oder beim Erstellen von eigenen Projekten. + * ### Pdf (pdf) Es kann **eine** Url zu einem Pdf angegeben werden. Die Url muss dabei eine Asset-Url sein (beginnt mit asset://). diff --git a/src/styles/common.styl b/src/styles/common.styl index 42185cf2..794cfc9e 100644 --- a/src/styles/common.styl +++ b/src/styles/common.styl @@ -1612,3 +1612,6 @@ $sonarEase = linear } +.markdown-guide pre { + overflow: auto; +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 7d263cd6..cc4e299b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3722,6 +3722,11 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +markdown-it-container@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/markdown-it-container/-/markdown-it-container-3.0.0.tgz#1d19b06040a020f9a827577bb7dbf67aa5de9a5b" + integrity sha512-y6oKTq4BB9OQuY/KLfk/O3ysFhB3IMYoIWhGJEidXt1NQFocFK2sA2t0NYZAMyMShAGL6x5OPIbrmXPIqaN9rw== + markdown-it-mathjax@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/markdown-it-mathjax/-/markdown-it-mathjax-2.0.0.tgz#ae2b4f4c5c719a03f9e475c664f7b2685231d9e9" -- GitLab