diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/FormUIFactory.java b/src/main/java/org/olat/core/gui/components/form/flexible/FormUIFactory.java index 2aa7fc91e42a3b8c3c41c72a582165b6982e2568..2b16115c08ccc50ed47d9e7b22dba85333504975 100644 --- a/src/main/java/org/olat/core/gui/components/form/flexible/FormUIFactory.java +++ b/src/main/java/org/olat/core/gui/components/form/flexible/FormUIFactory.java @@ -854,14 +854,14 @@ public class FormUIFactory { return rte; } - public RichTextElement addRichTextElementForQTI21Match(String name, String i18nLabel, String initialHTMLValue, int rows, - int cols, VFSContainer baseContainer, FormItemContainer formLayout, UserSession usess, WindowControl wControl) { + public RichTextElement addRichTextElementVeryMinimalistic(String name, String i18nLabel, String initialHTMLValue, int rows, + int cols, boolean withLinks, VFSContainer baseContainer, FormItemContainer formLayout, UserSession usess, WindowControl wControl) { // Create rich text element with bare bone configuration RichTextElement rte = new RichTextElementImpl(name, initialHTMLValue, rows, cols, formLayout.getRootForm(), formLayout.getTranslator().getLocale()); setLabelIfNotNull(i18nLabel, rte); // Now configure editor Theme theme = wControl.getWindowBackOffice().getWindow().getGuiTheme(); - rte.getEditorConfiguration().setConfigProfileFormVeryMinimalisticConfigEditor(usess, theme, baseContainer); + rte.getEditorConfiguration().setConfigProfileFormVeryMinimalisticConfigEditor(usess, theme, baseContainer, withLinks); rte.getEditorConfiguration().setInvalidElements(RichTextConfiguration.INVALID_ELEMENTS_FORM_FULL_VALUE_UNSAVE_WITH_SCRIPT); rte.getEditorConfiguration().setExtendedValidElements("script[src|type|defer]"); rte.getEditorConfiguration().disableTinyMedia(); diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/richText/RichTextConfiguration.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/richText/RichTextConfiguration.java index d01ca545e986b896b0f9d3efa98d4c6763ab927d..1e0704d5b66563c74cf2210d810cb82042ffc1de 100644 --- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/richText/RichTextConfiguration.java +++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/richText/RichTextConfiguration.java @@ -274,7 +274,7 @@ public class RichTextConfiguration implements Disposable { * @param guiTheme * @param baseContainer */ - public void setConfigProfileFormVeryMinimalisticConfigEditor(UserSession usess, Theme guiTheme, VFSContainer baseContainer) { + public void setConfigProfileFormVeryMinimalisticConfigEditor(UserSession usess, Theme guiTheme, VFSContainer baseContainer, boolean withLinks) { setConfigBasics(guiTheme); // Add additional plugins TinyMCECustomPluginFactory customPluginFactory = CoreSpringFactory.getImpl(TinyMCECustomPluginFactory.class); @@ -285,7 +285,11 @@ public class RichTextConfiguration implements Disposable { // Don't allow javascript or iframes, if the file browser is there allow also media elements (the full values) setQuotedConfigValue(INVALID_ELEMENTS, (baseContainer == null ? INVALID_ELEMENTS_FORM_SIMPLE_VALUE_UNSAVE : INVALID_ELEMENTS_FORM_FULL_VALUE_UNSAVE)); - tinyConfig = TinyConfig.veryMinimalisticConfig; + if(withLinks) { + tinyConfig = TinyConfig.veryMinimalisticWithLinksConfig; + } else { + tinyConfig = TinyConfig.veryMinimalisticConfig; + } setPathInStatusBar(false); // Setup file and link browser diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/richText/TinyConfig.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/richText/TinyConfig.java index 0b13271d5a86d69ba7e6c3c0800192755f558afd..b0937013b11c9298f80083a528b1a7adb3fceb13 100644 --- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/richText/TinyConfig.java +++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/richText/TinyConfig.java @@ -32,6 +32,7 @@ public class TinyConfig { protected static final TinyConfig editorCompactConfig; protected static final TinyConfig fileEditorConfig; protected static final TinyConfig minimalisticConfig; + protected static final TinyConfig veryMinimalisticWithLinksConfig; protected static final TinyConfig veryMinimalisticConfig; protected static final TinyConfig paragraphEditorConfig; @@ -72,6 +73,12 @@ public class TinyConfig { String tools1 = "bold italic underline | image olatmatheditor"; veryMinimalisticConfig = new TinyConfig(plugins, null, tools1); } + static { + String plugins = "contextmenu,colorpicker,textcolor,hr,charmap,image,insertdatetime,table,visualchars,visualblocks,noneditable,olatsmileys,paste,link,quotespliter,olatmatheditor,olatmovieviewer,tabfocus,visualchars,visualblocks,noneditable,media"; + String tools1 = "bold italic underline | image olatmatheditor | link unlink"; + veryMinimalisticWithLinksConfig = new TinyConfig(plugins, null, tools1); + } + //paragraph editor for content editor static { String plugins = "lists,contextmenu,textcolor,hr,noneditable,paste,link,olatmatheditor,tabfocus,noneditable"; diff --git a/src/main/java/org/olat/core/gui/components/link/Link.java b/src/main/java/org/olat/core/gui/components/link/Link.java index 43beaa3137847a8b8d827b4f177ba390fd2cafb0..830bb29db61a29cf5d72d33cc98afbc588c17c94 100644 --- a/src/main/java/org/olat/core/gui/components/link/Link.java +++ b/src/main/java/org/olat/core/gui/components/link/Link.java @@ -91,6 +91,7 @@ public class Link extends AbstractComponent { private String customDisabledLinkCSS; private String iconLeftCSS; private String iconRightCSS; + private String labelCSS; private String target; private FormLink flexiLink; private Object userObject; @@ -632,6 +633,20 @@ public class Link extends AbstractComponent { return iconRightCSS; } + /** + * @return The label (span tag) CSS classes or NULL + */ + public String getLabelCSS() { + return labelCSS; + } + + /** + * @param labelCSS The CSS classes used for the label (span tag) + */ + public void setLabelCSS(String labelCSS) { + this.labelCSS = labelCSS; + } + /** * Compare also with isEnabled(); * @return true if the link is active (only a rendering issue); false if link not active diff --git a/src/main/java/org/olat/core/gui/components/link/LinkRenderer.java b/src/main/java/org/olat/core/gui/components/link/LinkRenderer.java index 86fc40b65bd51aad1d903578844df05d9cbbee11..5d96cb0135a900e42d4ce554e4b85330024737fd 100644 --- a/src/main/java/org/olat/core/gui/components/link/LinkRenderer.java +++ b/src/main/java/org/olat/core/gui/components/link/LinkRenderer.java @@ -243,7 +243,11 @@ public class LinkRenderer extends DefaultComponentRenderer { sb.append("<i class='o_icon o_icon_back'> </i> "); // one space needed } - sb.append("<span>"); // inner wrapper for layouting + sb.append("<span"); // inner wrapper for layouting + if(StringHelper.containsNonWhitespace(link.getLabelCSS())) { + sb.append(" class='").append(link.getLabelCSS()).append("'"); + } + sb.append(">"); if (customDisplayText != null) { //link is not translated but has custom text sb.append(customDisplayText); diff --git a/src/main/java/org/olat/core/gui/render/velocity/VelocityRenderDecorator.java b/src/main/java/org/olat/core/gui/render/velocity/VelocityRenderDecorator.java index 9184aa5331c9058755b784cbd6e35059f275ed48..05ff7f8da5b6dd89bfb2b51b9b7add2f1fd1a14c 100644 --- a/src/main/java/org/olat/core/gui/render/velocity/VelocityRenderDecorator.java +++ b/src/main/java/org/olat/core/gui/render/velocity/VelocityRenderDecorator.java @@ -843,6 +843,10 @@ public class VelocityRenderDecorator implements Closeable { } } + public String replace(String text, String target, String replacement) { + return text.replace(target, replacement); + } + /** * @param componentName * @return true if the component with name componentName is a child of the current container. Used to "if" the render diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseController.java index 46cfe08a34a8816dd88707c2372d551be9dd75d9..084029854b4b15381151f2f823e111197399cb93 100644 --- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseController.java +++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentIdentityCourseController.java @@ -71,8 +71,10 @@ public class AssessmentIdentityCourseController extends BasicController private final TooledStackedPanel stackPanel; private final VelocityContainer identityAssessmentVC; - private Link nextLink, previousLink, courseNodeSelectionLink; private Link pdfLink; + private Link nextLink; + private Link previousLink; + private Link courseNodeSelectionLink; private IdentityCertificatesController certificateCtrl; private AssessedIdentityLargeInfosController infosController; @@ -288,7 +290,7 @@ public class AssessmentIdentityCourseController extends BasicController stackPanel.pushController(courseNode.getShortTitle(), currentNodeCtrl); previousLink = LinkFactory.createToolLink("previouselement", translate("previous"), this, "o_icon_previous"); - previousLink.setTitle(translate("command.previous")); + previousLink.setTitle(translate("command.previous.node")); previousLink.setEnabled(hasPrevious(courseNode)); stackPanel.addTool(previousLink, Align.rightEdge, false); @@ -298,10 +300,11 @@ public class AssessmentIdentityCourseController extends BasicController courseNodeSelectionLink.setElementCssClass("dropdown-toggle "); courseNodeSelectionLink.setIconLeftCSS("o_icon " + courseNodeCssClass); courseNodeSelectionLink.setIconRightCSS("o_icon o_icon_caret"); + courseNodeSelectionLink.setLabelCSS("o_label"); stackPanel.addTool(courseNodeSelectionLink, Align.rightEdge, false, "o_tool_dropdown dropdown"); nextLink = LinkFactory.createToolLink("nextelement", translate("next"), this, "o_icon_next"); - nextLink.setTitle(translate("command.next")); + nextLink.setTitle(translate("command.next.node")); CourseNode nextNode = treeOverviewCtrl.getNextNode(courseNode); boolean hasNext = (nextNode != null && nextNode.getParent() != null); nextLink.setEnabled(hasNext); diff --git a/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_de.properties index 2e18293ad3bbd0db6eed472c8949738c7a45a96c..1435b98c36acd170458b7af38a06d165059f2bb7 100644 --- a/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_de.properties @@ -23,7 +23,9 @@ certificates.wizard.title=$org.olat.course.certificate.ui\:certificates.wizard.t change.visibility=Sichtbarkeit \u00E4ndern change.visibility.title=Sichtbarkeit vom Ergebnis \u00E4ndern command.next=Weiter zu n\u00E4chste Benutzer +command.next.node=Weiter zum n\u00E4chsten Kursbaustein command.previous=Zur\u00FCck zum letzten Benutzer +command.previous.node=Zur\u00FCck zum letzten Kursbaustein confirm.certificate.deleted=$org.olat.course.certificate.ui\:confirm.certificate.deleted confirm.delete.certificate.text=$org.olat.course.certificate.ui\:confirm.delete.certificate.text confirm.delete.certificate.title=$org.olat.course.certificate.ui\:confirm.delete.certificate.title diff --git a/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_en.properties index 7e61cacb0afc99c41118c9980c78f4f7c666979f..99a16b9f07f037418712a1e6334a6ac2fcfc07be 100644 --- a/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_en.properties @@ -23,7 +23,9 @@ certificates.wizard.title=$org.olat.course.certificate.ui\:certificates.wizard.t change.visibility=Change visibility change.visibility.title=Change the visibility of results command.next=To the next user +command.next.node=To the next course element command.previous=Back to previous user +command.previous.node=Back to previous course element confirm.certificate.deleted=$org.olat.course.certificate.ui\:confirm.certificate.deleted confirm.delete.certificate.text=$org.olat.course.certificate.ui\:confirm.delete.certificate.text confirm.delete.certificate.title=$org.olat.course.certificate.ui\:confirm.delete.certificate.title diff --git a/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_fr.properties index f95f4577ebc22c88875d343dfadd1cb942a78dac..345ec32b6a0f3337b516feb3e7261e714640b293 100644 --- a/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_fr.properties +++ b/src/main/java/org/olat/course/assessment/ui/tool/_i18n/LocalStrings_fr.properties @@ -24,7 +24,9 @@ certificates.wizard.title=$org.olat.course.certificate.ui\:certificates.wizard.t change.visibility=Modifier la visibilit\u00E9 change.visibility.title=Changer la visibilit\u00E9 des r\u00E9sultats command.next=Utilisateur suivant +command.next.node=El\u00E9ment de cours suivant command.previous=Utilisateur pr\u00E9c\u00E9dent +command.previous.node=El\u00E9ment de cours pr\u00E9c\u00E9dent confirm.certificate.deleted=$org.olat.course.certificate.ui\:confirm.certificate.deleted confirm.delete.certificate.text=$org.olat.course.certificate.ui\:confirm.delete.certificate.text confirm.delete.certificate.title=$org.olat.course.certificate.ui\:confirm.delete.certificate.title diff --git a/src/main/java/org/olat/course/highscore/manager/HighScoreManager.java b/src/main/java/org/olat/course/highscore/manager/HighScoreManager.java index 36a5bcbfadb90daf29c8705e9fb88215937b4e0d..a72273eb874699c36ce7d3c0a3bb0e8d7ab6dc56 100644 --- a/src/main/java/org/olat/course/highscore/manager/HighScoreManager.java +++ b/src/main/java/org/olat/course/highscore/manager/HighScoreManager.java @@ -75,13 +75,11 @@ public class HighScoreManager { float buffer = -1; int index = 0; -// int rank = 1; double[] allScores = new double[allMembers.size()]; for (int j = 0; j < allMembers.size(); j++) { HighScoreTableEntry member = allMembers.get(j); if (member.getScore() < buffer){ index++; -// rank = j + 1; } //first three position are put in separate lists, exclude zero scorers if (index < 3 && member.getScore() > 0) { @@ -103,7 +101,7 @@ public class HighScoreManager { .filter(a -> a.getIdentity().equals(ownIdentity)) .collect(Collectors.toList())); - if (ownIdMembers.size() > 0) { + if (!ownIdMembers.isEmpty()) { log.info(Tracing.M_AUDIT, "2nd Highscore Table established"); } @@ -122,7 +120,7 @@ public class HighScoreManager { double min = Math.floor(Arrays.stream(scores).min().getAsDouble()); double range = max - min; // use original scores if range is too small else convert results to fit histogram - if (range <= 20 && range <0) { + if (range <= 20) { classwidth = 1; return new HighScoreRankingResults(scores, classwidth, min); } else { @@ -168,7 +166,7 @@ public class HighScoreManager { // allow one extension if no borders are defined primeRange = upperBorder - lowerBorder > 0; } - // steps can only be natural numbers + // steps can only be natural numbers classwidth = Math.round(range / numberofclasses); // modified scores are calculated and saved double[] allScores = new double[scores.length]; @@ -189,7 +187,7 @@ public class HighScoreManager { /** - * Calculate histogram cutvalue using results from the method (processHistogramData(double[])) + * Calculate histogram cut value using results from the method (processHistogramData(double[])) * * @param score the score * @return the double @@ -199,11 +197,9 @@ public class HighScoreManager { // determine n-th class to fit the current score result double n = Math.ceil((score - min) / classwidth); // calculate higher score to fit the class width - double cutvalue = min + (n * classwidth); - return cutvalue; - } else { - return score; - } + return min + (n * classwidth); + } + return score; } diff --git a/src/main/java/org/olat/course/nodes/ms/MSCourseNodeRunController.java b/src/main/java/org/olat/course/nodes/ms/MSCourseNodeRunController.java index 7554fd2a80ea69d1e85f9ced7c9368785deb3602..40fb325bf9d684625a7c8afd091ff1faa58b5b20 100644 --- a/src/main/java/org/olat/course/nodes/ms/MSCourseNodeRunController.java +++ b/src/main/java/org/olat/course/nodes/ms/MSCourseNodeRunController.java @@ -114,8 +114,7 @@ public class MSCourseNodeRunController extends BasicController implements Activa this.overrideUserResultsVisiblity = overrideUserResultsVisiblity; myContent = createVelocityContainer("run"); - ModuleConfiguration config = courseNode.getModuleConfiguration(); - if (config.getBooleanSafe(MSCourseNode.CONFIG_KEY_HAS_SCORE_FIELD,false)) { + if (courseNode.hasScoreConfigured()) { HighScoreRunController highScoreCtr = new HighScoreRunController(ureq, wControl, userCourseEnv, courseNode); if (highScoreCtr.isViewHighscore()) { Component highScoreComponent = highScoreCtr.getInitialComponent(); diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/MatchEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/MatchEditorController.java index 5c0f56bb12ac83d7477e72cece430ef4c78ad5b4..a1425dda370a3deb3cf457765765bbaab6a0b660 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/MatchEditorController.java +++ b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/MatchEditorController.java @@ -233,7 +233,7 @@ public class MatchEditorController extends FormBasicController { List<FlowStatic> choiceFlow = choice.getFlowStatics(); String choiceContent = itemBuilder.getHtmlHelper().flowStaticString(choiceFlow); String choiceId = "answer" + count++; - RichTextElement choiceEl = uifactory.addRichTextElementForQTI21Match(choiceId, "form.imd.answer", choiceContent, 4, -1, itemContainer, + RichTextElement choiceEl = uifactory.addRichTextElementVeryMinimalistic(choiceId, "form.imd.answer", choiceContent, 4, -1, false, itemContainer, answersCont, ureq.getUserSession(), getWindowControl()); choiceEl.setUserObject(choice); choiceEl.setEnabled(!readOnly); diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/TrueFalseEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/TrueFalseEditorController.java index 3c414afd1cfe28b6f50fdcbcc046678c39de9088..a42d65b1966696f2ee6e3c99dcc307e9a1ddbb71 100644 --- a/src/main/java/org/olat/ims/qti21/ui/editor/interactions/TrueFalseEditorController.java +++ b/src/main/java/org/olat/ims/qti21/ui/editor/interactions/TrueFalseEditorController.java @@ -175,7 +175,7 @@ public class TrueFalseEditorController extends FormBasicController { List<FlowStatic> choiceFlow = choice.getFlowStatics(); String choiceContent = itemBuilder.getHtmlHelper().flowStaticString(choiceFlow); String choiceId = "answer" + count++; - RichTextElement choiceEl = uifactory.addRichTextElementForQTI21Match(choiceId, "form.imd.answer", choiceContent, 4, -1, itemContainer, + RichTextElement choiceEl = uifactory.addRichTextElementVeryMinimalistic(choiceId, "form.imd.answer", choiceContent, 4, -1, false, itemContainer, answersCont, ureq.getUserSession(), getWindowControl()); choiceEl.setUserObject(choice); choiceEl.setEnabled(!readOnly); diff --git a/src/main/java/org/olat/modules/lecture/ui/coach/AbsenceNoticesListController.java b/src/main/java/org/olat/modules/lecture/ui/coach/AbsenceNoticesListController.java index c1882aea0fdbe9e3bb37f58de5ffb9b48b50b5f7..c0019d8d82cc555367ba3e09e6a2137c29e43a14 100644 --- a/src/main/java/org/olat/modules/lecture/ui/coach/AbsenceNoticesListController.java +++ b/src/main/java/org/olat/modules/lecture/ui/coach/AbsenceNoticesListController.java @@ -110,6 +110,7 @@ public class AbsenceNoticesListController extends FormBasicController { * Show per default the start / end date as whole date */ private final boolean wholeDateDefault; + private final boolean withUserProperties; private final boolean isAdministrativeUser; private final List<UserPropertyHandler> userPropertyHandlers; private final LecturesSecurityCallback secCallback; @@ -134,7 +135,8 @@ public class AbsenceNoticesListController extends FormBasicController { private BaseSecurityModule securityModule; public AbsenceNoticesListController(UserRequest ureq, WindowControl wControl, Date currentDate, - boolean authorizedEnabled, LecturesSecurityCallback secCallback, String tableId) { + boolean authorizedEnabled, LecturesSecurityCallback secCallback, boolean withUserProperties, + String tableId) { super(ureq, wControl, "absences_list", Util.createPackageTranslator(LectureRepositoryAdminController.class, ureq.getLocale())); setTranslator(userManager.getPropertyHandlerTranslator(getTranslator())); this.tableId = tableId; @@ -142,6 +144,7 @@ public class AbsenceNoticesListController extends FormBasicController { this.currentDate = currentDate; wholeDateDefault = (currentDate == null); this.authorizedEnabled = authorizedEnabled; + this.withUserProperties = withUserProperties; isAdministrativeUser = securityModule.isUserAllowedAdminProps(ureq.getUserSession().getRoles()); userPropertyHandlers = userManager.getUserPropertyHandlersFor(USER_USAGE_IDENTIFIER, isAdministrativeUser); @@ -167,7 +170,9 @@ public class AbsenceNoticesListController extends FormBasicController { FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(false, NoticeCols.id)); - initUserColumns(columnsModel); + if(withUserProperties) { + initUserColumns(columnsModel); + } initColumns(columnsModel); tableModel = new AbsenceNoticesListTableModel(columnsModel, userManager, userPropertyHandlers, getLocale()); diff --git a/src/main/java/org/olat/modules/lecture/ui/coach/AbsencesController.java b/src/main/java/org/olat/modules/lecture/ui/coach/AbsencesController.java index bed5c19d5f7cd57c22d62e77dc3500ccac6d45b3..b3c4f5a57ae97d953ccec58334dc97efde225042 100644 --- a/src/main/java/org/olat/modules/lecture/ui/coach/AbsencesController.java +++ b/src/main/java/org/olat/modules/lecture/ui/coach/AbsencesController.java @@ -85,7 +85,7 @@ public class AbsencesController extends BasicController { searchCtrl = new AbsenceNoticeSearchController(ureq, getWindowControl(), currentDate); listenTo(searchCtrl); noticesListCtlr = new AbsenceNoticesListController(ureq, getWindowControl(), - null, true, secCallback, "absences"); + null, true, secCallback, true, "absences"); listenTo(noticesListCtlr); mainVC = createVelocityContainer("absences"); diff --git a/src/main/java/org/olat/modules/lecture/ui/coach/DailyAbsencesController.java b/src/main/java/org/olat/modules/lecture/ui/coach/DailyAbsencesController.java index 4c8a4c8d79079244f3dcc1eb4875920eabe3c348..ef9f343ae3012dce61d4b1ecc1af16ac1783e845 100644 --- a/src/main/java/org/olat/modules/lecture/ui/coach/DailyAbsencesController.java +++ b/src/main/java/org/olat/modules/lecture/ui/coach/DailyAbsencesController.java @@ -67,7 +67,7 @@ public class DailyAbsencesController extends BasicController { searchParams.setParticipant(profiledIdentity); } noticesListCtlr = new AbsenceNoticesListController(ureq, getWindowControl(), - currentDate, true, secCallback, "daily"); + currentDate, true, secCallback, true, "daily"); listenTo(noticesListCtlr); mainVC = createVelocityContainer("daily_absences"); diff --git a/src/main/java/org/olat/modules/lecture/ui/coach/DispensationsController.java b/src/main/java/org/olat/modules/lecture/ui/coach/DispensationsController.java index bece847b09feb9e03de5f5b8e7a5833401179369..7276fa8e5fa1266f5bb53c48e133b71761e511cd 100644 --- a/src/main/java/org/olat/modules/lecture/ui/coach/DispensationsController.java +++ b/src/main/java/org/olat/modules/lecture/ui/coach/DispensationsController.java @@ -33,6 +33,7 @@ import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.controller.BasicController; import org.olat.core.gui.control.generic.wizard.StepRunnerCallback; import org.olat.core.gui.control.generic.wizard.StepsMainRunController; +import org.olat.core.id.Identity; import org.olat.core.util.Util; import org.olat.modules.lecture.AbsenceNoticeSearchParameters; import org.olat.modules.lecture.AbsenceNoticeType; @@ -64,8 +65,34 @@ public class DispensationsController extends BasicController { private AbsenceNoticeSearchController searchCtrl; private AbsenceNoticesListController noticesListCtlr; + /** + * Show a list of dispense (type notified or dispensation). + * + * @param ureq The user request + * @param wControl The window control + * @param currentDate The date to restrict the list + * @param secCallback The security callback + * @param withSearch With search + * @param withAddAbsence With button the add absence (if allowed by security callback) + */ public DispensationsController(UserRequest ureq, WindowControl wControl, Date currentDate, LecturesSecurityCallback secCallback, boolean withSearch, boolean withAddAbsence) { + this(ureq, wControl, currentDate, secCallback, null, withSearch, withAddAbsence); + } + + /** + * Show a list of dispense (type notified or dispensation). + * + * @param ureq The user request + * @param wControl The window control + * @param currentDate The date to restrict the list + * @param secCallback The security callback + * @param profiledIdentity Limit to a single identity (can be null) + * @param withSearch With search + * @param withAddAbsence With button the add absence (if allowed by security callback) + */ + public DispensationsController(UserRequest ureq, WindowControl wControl, Date currentDate, + LecturesSecurityCallback secCallback, Identity profiledIdentity, boolean withSearch, boolean withAddAbsence) { super(ureq, wControl, Util.createPackageTranslator(LectureRepositoryAdminController.class, ureq.getLocale())); this.secCallback = secCallback; @@ -76,11 +103,13 @@ public class DispensationsController extends BasicController { searchParams.setLinkedToRollCall(false); searchParams.setStartDate(CalendarUtils.startOfDay(currentDate)); searchParams.setEndDate(CalendarUtils.endOfDay(currentDate)); + searchParams.setParticipant(profiledIdentity); searchCtrl = new AbsenceNoticeSearchController(ureq, getWindowControl(), currentDate); listenTo(searchCtrl); + boolean showUserProperties = profiledIdentity == null; noticesListCtlr = new AbsenceNoticesListController(ureq, getWindowControl(), - null, false, secCallback, "notices"); + null, false, secCallback, showUserProperties, "notices"); listenTo(noticesListCtlr); mainVC = createVelocityContainer("dispensations"); diff --git a/src/main/java/org/olat/modules/lecture/ui/profile/IdentityProfileController.java b/src/main/java/org/olat/modules/lecture/ui/profile/IdentityProfileController.java index 4cfa8cc66b0788d896846081a4e9cc979267a7b7..e3e1ea960c5608a317483779c279fc38977b4836 100644 --- a/src/main/java/org/olat/modules/lecture/ui/profile/IdentityProfileController.java +++ b/src/main/java/org/olat/modules/lecture/ui/profile/IdentityProfileController.java @@ -159,7 +159,7 @@ public class IdentityProfileController extends BasicController implements Activa // dispensation if(lectureModule.isAbsenceNoticeEnabled()) { dispensationsTab = tabPane.addTab(translate("user.overview.dispensation"), uureq -> { - dispensationsCtrl = new DispensationsController(uureq, getWindowControl(), null, secCallback, false, false); + dispensationsCtrl = new DispensationsController(uureq, getWindowControl(), null, secCallback, profiledIdentity, false, false); listenTo(dispensationsCtrl); return dispensationsCtrl.getInitialComponent(); }); diff --git a/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyLevelRow.java b/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyLevelRow.java index 196546218e5b66c1bc4ecd88905d71d1aed66a74..6bf00983c420d90016acc7898d7479e3f06989b6 100644 --- a/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyLevelRow.java +++ b/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyLevelRow.java @@ -84,6 +84,10 @@ public class TaxonomyLevelRow implements TaxonomyLevelRef, FlexiTreeTableNode { return taxonomyLevel.getIdentifier(); } + public String getExternalId() { + return taxonomyLevel.getExternalId(); + } + public Long getTypeKey() { return type == null ? null : type.getKey(); } diff --git a/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyTreeTableController.java b/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyTreeTableController.java index d5785c12e53545bff897f45cbbd34ca3de26a93a..2c32b5a2e53bdf359f349403b11672fcd4ff86c4 100644 --- a/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyTreeTableController.java +++ b/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyTreeTableController.java @@ -132,6 +132,7 @@ public class TaxonomyTreeTableController extends FormBasicController implements treeNodeRenderer.setFlatBySearchAndFilter(true); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TaxonomyLevelCols.displayName, treeNodeRenderer)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TaxonomyLevelCols.identifier, "select")); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TaxonomyLevelCols.externalId, "select")); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TaxonomyLevelCols.typeIdentifier)); columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(TaxonomyLevelCols.numOfChildren)); DefaultFlexiColumnModel selectColumn = new DefaultFlexiColumnModel("zoom", translate("zoom"), "tt-focus"); diff --git a/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyTreeTableModel.java b/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyTreeTableModel.java index b39310bc86a8c32c38e1c11db80496b166024f63..6dc95b42767d1f5c5be8966391aee74b42a1adc3 100644 --- a/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyTreeTableModel.java +++ b/src/main/java/org/olat/modules/taxonomy/ui/TaxonomyTreeTableModel.java @@ -116,6 +116,7 @@ implements FilterableFlexiTableModel { case key: return level.getKey(); case displayName: return level.getDisplayName(); case identifier: return level.getIdentifier(); + case externalId: return level.getExternalId(); case typeIdentifier: return level.getTypeIdentifier(); case numOfChildren: return level.getNumberOfChildren(); case tools: return level.getToolsLink(); @@ -127,6 +128,7 @@ implements FilterableFlexiTableModel { key("table.header.key"), displayName("table.header.taxonomy.level.displayName"), identifier("table.header.taxonomy.level.identifier"), + externalId("table.header.taxonomy.level.externalId"), typeIdentifier("table.header.taxonomy.level.type.identifier"), numOfChildren("table.header.taxonomy.level.num.children"), tools("table.header.actions"); diff --git a/src/main/java/org/olat/modules/taxonomy/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/taxonomy/ui/_i18n/LocalStrings_de.properties index d1e27a8903dd34fa7645e695ee51622b9fccdae8..e6c2fcf1c124cd55ff63f4e371ee874a847f32dd 100644 --- a/src/main/java/org/olat/modules/taxonomy/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/modules/taxonomy/ui/_i18n/LocalStrings_de.properties @@ -93,7 +93,7 @@ table.header.taxonomy.displayName=Taxonomie table.header.taxonomy.externalId=Taxanomie Ext. Ref. table.header.taxonomy.identifier=Taxonomie ID table.header.taxonomy.level.displayName=Anzeigename -table.header.taxonomy.level.externalId=Level Ext. Ref. +table.header.taxonomy.level.externalId=Ebene Ext. Ref. table.header.taxonomy.level.identifier=Kennzeichen table.header.taxonomy.level.num.children=Anzahl table.header.taxonomy.level.type=Ebenentyp diff --git a/src/main/java/org/olat/modules/video/ui/VideoDisplayController.java b/src/main/java/org/olat/modules/video/ui/VideoDisplayController.java index d166f6cf904da62cdb6dbd2be5ae7eabece8ebf6..942d58daa265d95ca32c34a3e264870cf130b06f 100644 --- a/src/main/java/org/olat/modules/video/ui/VideoDisplayController.java +++ b/src/main/java/org/olat/modules/video/ui/VideoDisplayController.java @@ -146,6 +146,7 @@ public class VideoDisplayController extends BasicController { putInitialPanel(mainVC); mainVC.put("markers", markerPanel); markerVC = createVelocityContainer("video_markers"); + markerVC.getContext().put("videoElementId", getVideoElementId()); questionCtrl = new VideoAssessmentItemController(ureq, getWindowControl(), videoEntry, entry, courseNode, getVideoElementId(), displayOptions.isAuthorMode()); @@ -606,24 +607,30 @@ public class VideoDisplayController extends BasicController { private void doMarkerMoved(UserRequest ureq) { String markerId = ureq.getParameter("marker_id"); MarkerMovedEvent event = new MarkerMovedEvent(markerId); - event.setTop(parseDouble(ureq, "top", 0.0d)); - event.setLeft(parseDouble(ureq, "left", 0.0d)); + event.setTop(parseDouble(ureq, "top", 0.0d, 0.98d)); + event.setLeft(parseDouble(ureq, "left", 0.0d, 0.98d)); fireEvent(ureq, event); } private void doMarkerResized(UserRequest ureq) { String markerId = ureq.getParameter("marker_id"); MarkerResizedEvent event = new MarkerResizedEvent(markerId); - event.setTop(parseDouble(ureq, "top", 0.0d)); - event.setLeft(parseDouble(ureq, "left", 0.0d)); - event.setWidth(parseDouble(ureq, "width", 10.0d)); - event.setHeight(parseDouble(ureq, "height", 10.0d)); + event.setTop(parseDouble(ureq, "top", 0.0d, 0.98d)); + event.setLeft(parseDouble(ureq, "left", 0.0d, 0.98d)); + event.setWidth(parseDouble(ureq, "width", 10.0d, 0.98d)); + event.setHeight(parseDouble(ureq, "height", 10.0d, 0.98d)); fireEvent(ureq, event); } - private double parseDouble(UserRequest ureq, String name, double def) { + private double parseDouble(UserRequest ureq, String name, double def, double max) { try { - return Double.parseDouble(ureq.getParameter(name)); + double val = Double.parseDouble(ureq.getParameter(name)); + if(val < 0.0d) { + val = 0.0d; + } else if(val > max) { + val = max; + } + return val; } catch (NumberFormatException e) { return def; } diff --git a/src/main/java/org/olat/modules/video/ui/_content/video_markers.html b/src/main/java/org/olat/modules/video/ui/_content/video_markers.html index ec24531026192b6e69ce66eafa5a06fb62061b01..5a44ad4112b2e352b23034ca8362eccb42c9b416 100644 --- a/src/main/java/org/olat/modules/video/ui/_content/video_markers.html +++ b/src/main/java/org/olat/modules/video/ui/_content/video_markers.html @@ -1,55 +1,111 @@ #foreach($marker in $markers) -<div id="marker_${marker.id}" class="mejs__overlay o_video_marker" style="" #if($r.isTrue($dragMarkers)) draggable="true" #end>$r.xssScan($marker.text)</div> +<div id="marker_${marker.id}" class="mejs__overlay o_video_marker" style="" #if($r.isTrue($dragMarkers)) draggable="true" #end>$r.xssScan($marker.text) ${videoElementId}</div> +#set($markedFuncId = $r.replace(${marker.id}, "-", "")) <script> jQuery(function() { - var containerEl = jQuery(".mejs__container"); - var top = ${marker.top} * containerEl.height(); - var left = ${marker.left} * containerEl.width(); - var markerEl =jQuery("#marker_${marker.id}") - .css('top', top + 'px') - .css('left', left + 'px'); - var color = '${marker.style}'; - if(color.indexOf('#') == 0 || color.indexOf('rgb') == 0 || color.indexOf('rgba') == 0) { - markerEl.css('border-color', color); - } else { - markerEl.addClass(color); + + function _size${markedFuncId}Marker() { + var containerEl = jQuery(".mejs__container"); + var cHeight = containerEl.height(); + var cWidth = containerEl.width(); + var cLeftOffset = 0; + var cTopOffset = 0; + var player = jQuery('#o_so_vid${videoElementId}').data('player'); + if(player !== "undefined" && player.isFullScreen && player.normalWidth > 0 && player.normalHeight > 0) { + var nScale = player.normalWidth / player.normalHeight; + var cScale = cWidth / cHeight; + if(cScale > nScale) { + // reduce width + var scale = cHeight / player.normalHeight; + var sWidth = player.normalWidth * scale; + cLeftOffset = (cWidth - sWidth) / 2.0; + cWidth = sWidth; + } else { + var scale = cWidth / player.normalWidth; + var sHeight = player.normalHeight * scale; + cTopOffset = (cHeight - sHeight) / 2.0; + cHeight = sHeight; + } + } + + var topPercent = ${marker.top}; + var leftPercent = ${marker.left}; + var top = (topPercent * cHeight) + cTopOffset; + var left = (leftPercent * cWidth) + cLeftOffset; + var markerEl = jQuery("#marker_${marker.id}") + .css('top', top + 'px') + .css('left', left + 'px'); + var color = '${marker.style}'; + if(color.indexOf('#') == 0 || color.indexOf('rgb') == 0 || color.indexOf('rgba') == 0) { + markerEl.css('border-color', color); + } else { + markerEl.addClass(color); + } + + #if($r.isNotNull($marker.width) && ${marker.width} > 0.01) + var widthPercent = ${marker.width}; + if(widthPercent + leftPercent > 1.0) { + widthPercent = 1.0 - leftPercent; + } + markerEl.width((widthPercent * cWidth) + 'px') + #end + #if($r.isNotNull($marker.height) && ${marker.height} > 0.01) + var heightPercent = ${marker.height}; + if(heightPercent + topPercent > 1.0) { + heightPercent = 1.0 - topPercent; + } + markerEl.height((heightPercent * cHeight) + 'px') + #end } - #if($r.isNotNull($marker.width) && ${marker.width} > 0.01) - markerEl.width((${marker.width} * containerEl.width()) + 'px') - #end - #if($r.isNotNull($marker.height) && ${marker.height} > 0.01) - markerEl.height((${marker.height} * containerEl.height()) + 'px') - #end #if($r.isTrue($dragMarkers)) - markerEl.draggable({ + function boxPercent(val) { + val = (val < 0.0) ? 0.0 : val; + val = (val > 0.98) ? 0.98 : val; + return val; + } + + jQuery("#marker_${marker.id}").draggable({ containment: ".mejs__container", scroll: false, stop: function(event, ui) { - var top = ui.position.top / containerEl.height(); - var left = ui.position.left / containerEl.width(); + var containerEl = jQuery(".mejs__container"); + var top = boxPercent(ui.position.top / containerEl.height()); + var left = boxPercent(ui.position.left / containerEl.width()); $r.openJavaScriptCommand('marker_moved'), 'marker_id', '${marker.id}', 'top', top, 'left', left); - jQuery("div.o_sel_video_marker_top input").val(Math.round(top * 100)); - jQuery("div.o_sel_video_marker_left input").val(Math.round(left * 100)); + jQuery("div.o_sel_video_marker_top input").val(Math.round(top * 100.0)); + jQuery("div.o_sel_video_marker_left input").val(Math.round(left * 100.0)); } }).resizable({ handles: 'all', containment: '.mejs__container', grid: [o_info.emPxFactor, 10000], stop: function(event, ui) { - var width = ui.size.height / containerEl.height(); - var height = ui.size.width / containerEl.width(); - var top = ui.position.top / containerEl.height(); - var left = ui.position.left / containerEl.width(); + var containerEl = jQuery(".mejs__container"); + var height = boxPercent(ui.size.height / containerEl.height()); + var width = boxPercent(ui.size.width / containerEl.width()); + var top = boxPercent(ui.position.top / containerEl.height()); + var left = boxPercent(ui.position.left / containerEl.width()); $r.openJavaScriptCommand('marker_resized'), 'marker_id', '${marker.id}', 'top', top, 'left', left, 'width', width, 'height', height); - jQuery("div.o_sel_video_marker_width input").val(Math.round(width * 100)); - jQuery("div.o_sel_video_marker_height input").val(Math.round(width * 100)); - jQuery("div.o_sel_video_marker_top input").val(Math.round(top * 100)); - jQuery("div.o_sel_video_marker_left input").val(Math.round(left * 100)); + jQuery("div.o_sel_video_marker_width input").val(Math.round(width * 100.0)); + jQuery("div.o_sel_video_marker_height input").val(Math.round(height * 100.0)); + jQuery("div.o_sel_video_marker_top input").val(Math.round(top * 100.0)); + jQuery("div.o_sel_video_marker_left input").val(Math.round(left * 100.0)); } }); + #else + var screenChangeEvents = "resize webkitfullscreenchange mozfullscreenchange fullscreenchange MSFullscreenChange"; + jQuery(window).on(screenChangeEvents, function _resize${markedFuncId}Marker(e) { + if(jQuery("#marker_${marker.id}").length > 0) { + _size${markedFuncId}Marker(); + } else { + jQuery(window).off(screenChangeEvents, _resize${markedFuncId}Marker); + } + }); #end + _size${markedFuncId}Marker(); + setTimeout(function() { BFormatter.formatLatexFormulas('marker_${marker.id}'); }, 100); }); </script> diff --git a/src/main/java/org/olat/modules/video/ui/marker/MarkerEditController.java b/src/main/java/org/olat/modules/video/ui/marker/MarkerEditController.java index 307315fa238c457f7582fafc18af52cd4a02ceb1..f3a7a42ef1ed62fc5a9e93207457ca58b0cd1e45 100644 --- a/src/main/java/org/olat/modules/video/ui/marker/MarkerEditController.java +++ b/src/main/java/org/olat/modules/video/ui/marker/MarkerEditController.java @@ -118,7 +118,7 @@ public class MarkerEditController extends FormBasicController { @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { - markerTextEl = uifactory.addRichTextElementForQTI21Match("text", "video.marker.text", "", 4, -1, null, + markerTextEl = uifactory.addRichTextElementVeryMinimalistic("text", "video.marker.text", "", 4, -1, true, null, formLayout, ureq.getUserSession(), getWindowControl()); markerTextEl.getEditorConfiguration().disableImageAndMovie(); markerTextEl.getEditorConfiguration().setSimplestTextModeAllowed(TextMode.oneLine); diff --git a/src/test/java/org/olat/course/highscore/HighScoreManagerTest.java b/src/test/java/org/olat/course/highscore/HighScoreManagerTest.java index 7c82bb9d5176e32062a486c06a853582dc9de8af..90dd9be22436ae79223348a7e564b7bd404b648c 100644 --- a/src/test/java/org/olat/course/highscore/HighScoreManagerTest.java +++ b/src/test/java/org/olat/course/highscore/HighScoreManagerTest.java @@ -24,6 +24,7 @@ * <p> */ package org.olat.course.highscore; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -39,6 +40,7 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; +import org.junit.Assert; import org.junit.Test; import org.olat.core.commons.persistence.DB; import org.olat.core.id.Identity; @@ -101,4 +103,46 @@ public class HighScoreManagerTest extends OlatTestCase { long classwidth = highScoreManager.processHistogramData(allScores, 0F, 30F).getClasswidth(); assertEquals(2L, classwidth); } + + @Test + public void highscoreTest_sameResults() { + List<AssessmentEntry> assessEntries = new ArrayList<>(); + + //Create entries, add to List + for (int i = 0; i < 10; i++) { + Identity assessedIdentity = JunitTestHelper.createAndPersistIdentityAsRndUser("as-node-2"); + RepositoryEntry entry = JunitTestHelper.createAndPersistRepositoryEntry(); + String subIdent = UUID.randomUUID().toString(); + AssessmentEntry nodeAssessment = courseNodeAssessmentDao + .createAssessmentEntry(assessedIdentity, subIdent, entry, subIdent, entry); + nodeAssessment.setScore(new BigDecimal(8.0)); + dbInstance.commitAndCloseSession(); + AssessmentEntry reloadedAssessment = courseNodeAssessmentDao.loadAssessmentEntryById(nodeAssessment.getKey()); + assessEntries.add(reloadedAssessment); + } + + List<Integer> ownIdIndices = new ArrayList<>(); + List<HighScoreTableEntry> allMembers = new ArrayList<>(); + List<HighScoreTableEntry> ownIdMembers = new ArrayList<>(); + List<List<HighScoreTableEntry>> allPodium = new ArrayList<>(); + allPodium.add(new ArrayList<>()); + allPodium.add(new ArrayList<>()); + allPodium.add(new ArrayList<>()); + + double[] allScores = highScoreManager.sortRankByScore(assessEntries, allMembers, ownIdMembers, allPodium, + ownIdIndices, 5, JunitTestHelper.createAndPersistIdentityAsRndUser("as-node-2")) + .getScores(); + for(int i=allScores.length; i-->0; ) { + Assert.assertEquals(8.0d, allScores[i], 0.00001d); + } + + double[] histogramData = highScoreManager.processHistogramData(allScores, 0F, 30F).getModifiedScores(); + assertNotNull(histogramData); + for(int i=allScores.length; i-->0; ) { + Assert.assertEquals(8.0d, allScores[i], 0.00001d); + } + + long classwidth = highScoreManager.processHistogramData(allScores, 0F, 30F).getClasswidth(); + assertEquals(1l, classwidth); + } } \ No newline at end of file