diff --git a/src/main/java/org/olat/core/commons/controllers/filechooser/LinkFileCombiCalloutController.java b/src/main/java/org/olat/core/commons/controllers/filechooser/LinkFileCombiCalloutController.java index 87e0fe2bfe2214bab6135d3b3a661c9da9970fce..d4dd633dc6e52d20e8b3803a1632b1c85ba99886 100644 --- a/src/main/java/org/olat/core/commons/controllers/filechooser/LinkFileCombiCalloutController.java +++ b/src/main/java/org/olat/core/commons/controllers/filechooser/LinkFileCombiCalloutController.java @@ -46,8 +46,9 @@ import org.olat.core.util.vfs.VFSContainer; import org.olat.core.util.vfs.VFSItem; import org.olat.core.util.vfs.VFSLeaf; import org.olat.core.util.vfs.VFSManager; -import org.olat.core.util.vfs.filters.VFSSystemItemFilter; import org.olat.core.util.vfs.filters.VFSItemFilter; +import org.olat.core.util.vfs.filters.VFSSystemItemFilter; +import org.olat.course.run.tools.CourseToolLinkTreeModel; import org.olat.modules.edusharing.VFSEdusharingProvider; @@ -67,6 +68,7 @@ public class LinkFileCombiCalloutController extends BasicController { private Link calloutTriggerLink; private CloseableCalloutWindowController calloutCtr; private CustomLinkTreeModel customLinkTreeModel; + private CustomLinkTreeModel courseToolLinkTreeModel; private Link editLink, removeLink; @@ -109,18 +111,22 @@ public class LinkFileCombiCalloutController extends BasicController { * @param customLinkTreeModel * The custom link tree model or NULL if no link tree model used * in HTML editor + * @param courseToolLinkTreeModel + * @param courseToolLinkTreeModel * @param edusharingProviderm * Enable content from edu-sharing with this provider */ public LinkFileCombiCalloutController(UserRequest ureq, WindowControl wControl, VFSContainer baseContainer, String relFilePath, boolean relFilPathIsProposal, boolean allowEditorRelativeLinks, boolean allowRemove, - CustomLinkTreeModel customLinkTreeModel, VFSEdusharingProvider edusharingProvider) { + CustomLinkTreeModel customLinkTreeModel, CourseToolLinkTreeModel courseToolLinkTreeModel, + VFSEdusharingProvider edusharingProvider) { super(ureq, wControl); this.baseContainer = baseContainer; this.relFilPathIsProposal = relFilPathIsProposal; this.allowEditorRelativeLinks = allowEditorRelativeLinks; this.customLinkTreeModel = customLinkTreeModel; + this.courseToolLinkTreeModel = courseToolLinkTreeModel; this.edusharingProvider = edusharingProvider; // Main container for everything @@ -272,7 +278,8 @@ public class LinkFileCombiCalloutController extends BasicController { } // Open HTML editor in dialog HTMLEditorController wysiwygCtr = WysiwygFactory.createWysiwygControllerWithInternalLink(ureq, - getWindowControl(), editorBaseContainer, editorRelPath, true, customLinkTreeModel, edusharingProvider); + getWindowControl(), editorBaseContainer, editorRelPath, true, customLinkTreeModel, + courseToolLinkTreeModel, edusharingProvider); displayModal(wysiwygCtr); } diff --git a/src/main/java/org/olat/core/commons/controllers/linkchooser/LinkChooserController.java b/src/main/java/org/olat/core/commons/controllers/linkchooser/LinkChooserController.java index 2582b7a7097d3c1c77326e2f9eea8ee82585dd75..fa83aabebf4e5e777692f88f49109036c00c8902 100644 --- a/src/main/java/org/olat/core/commons/controllers/linkchooser/LinkChooserController.java +++ b/src/main/java/org/olat/core/commons/controllers/linkchooser/LinkChooserController.java @@ -37,7 +37,6 @@ import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.controller.BasicController; import org.olat.core.util.vfs.VFSContainer; -import org.olat.course.run.tools.CourseToolLinkTreeModel; /** * Offer tabbed pane with certain type of link chooser. @@ -67,14 +66,15 @@ public class LinkChooserController extends BasicController { * @param suffixes Supported file suffixes for file-chooser. * @param uriValidation Set to true if the filename need to be a valid URI * @param fileName Base file-path for file-chooser. - * @param userActivityLogger + * @param customLinkTreeModel + * @param toolLinkTreeModel * @param internalLinkTreeModel Model with internal links e.g. course-node * tree model. The internal-link chooser tab won't be shown when the * internalLinkTreeModel is null. */ public LinkChooserController(UserRequest ureq, WindowControl wControl, VFSContainer rootDir, String uploadRelPath, String absolutPath, String[] suffixes, boolean uriValidation, String fileName, - CustomLinkTreeModel customLinkTreeModel, boolean allowCustomMediaChooserFactory) { + CustomLinkTreeModel customLinkTreeModel, CustomLinkTreeModel toolLinkTreeModel, boolean allowCustomMediaChooserFactory) { super(ureq, wControl); tabbedPaneViewVC = createVelocityContainer("linkchooser"); @@ -90,11 +90,9 @@ public class LinkChooserController extends BasicController { courseLinkChooserController = new CustomLinkChooserController(ureq, wControl, customLinkTreeModel); listenTo(courseLinkChooserController); linkChooserTabbedPane.addTab(translate("linkchooser.tabbedpane.label.internallinkchooser"), courseLinkChooserController.getInitialComponent()); - - // customLinkTreeModel is always the course node tree model. Instead of transfer a second model through all - // controller and factories, we just create the tool link model here, because if a goto course node can be chosen - // a goto course tool can be chosen as well. - courseToolLinkChooserController = new CustomLinkChooserController(ureq, wControl, new CourseToolLinkTreeModel(getLocale())); + } + if (toolLinkTreeModel != null && toolLinkTreeModel.getRootNode().getChildCount() > 0) { + courseToolLinkChooserController = new CustomLinkChooserController(ureq, wControl, toolLinkTreeModel); listenTo(courseToolLinkChooserController); linkChooserTabbedPane.addTab(translate("linkchooser.tabbedpane.label.internaltoolchooser"), courseToolLinkChooserController.getInitialComponent()); } diff --git a/src/main/java/org/olat/core/commons/controllers/linkchooser/MediaChooserController.java b/src/main/java/org/olat/core/commons/controllers/linkchooser/MediaChooserController.java index 120db398b526bea23434f6a5f5f032c8020775d7..d3cbcb333b3d5a082a2db8df31a4c2cac2885d9a 100644 --- a/src/main/java/org/olat/core/commons/controllers/linkchooser/MediaChooserController.java +++ b/src/main/java/org/olat/core/commons/controllers/linkchooser/MediaChooserController.java @@ -64,7 +64,7 @@ public class MediaChooserController extends LinkChooserController { */ public MediaChooserController(UserRequest ureq, WindowControl wControl, VFSContainer rootDir, String uploadRelPath, String[] suffixes, String fileName, CustomLinkTreeModel customLinkTreeModel, boolean allowCustomMediaFactory) { - super(ureq, wControl, rootDir, uploadRelPath, null, suffixes, false, fileName, customLinkTreeModel, allowCustomMediaFactory); + super(ureq, wControl, rootDir, uploadRelPath, null, suffixes, false, fileName, customLinkTreeModel, null, allowCustomMediaFactory); } /** diff --git a/src/main/java/org/olat/core/commons/editor/fileeditor/FileEditorController.java b/src/main/java/org/olat/core/commons/editor/fileeditor/FileEditorController.java index 8aca5a73eda9c930e195df8cf3bd353726a7d06a..725b118f11c7bd04dc9a21ea93c46405b1741e39 100644 --- a/src/main/java/org/olat/core/commons/editor/fileeditor/FileEditorController.java +++ b/src/main/java/org/olat/core/commons/editor/fileeditor/FileEditorController.java @@ -26,16 +26,17 @@ package org.olat.core.commons.editor.fileeditor; +import org.apache.logging.log4j.Logger; import org.olat.core.commons.controllers.linkchooser.CustomLinkTreeModel; import org.olat.core.commons.editor.htmleditor.HTMLEditorConfig; import org.olat.core.commons.editor.htmleditor.HTMLEditorController; import org.olat.core.commons.editor.htmleditor.HTMLReadOnlyController; import org.olat.core.commons.editor.htmleditor.WysiwygFactory; import org.olat.core.commons.editor.plaintexteditor.TextEditorController; -import org.olat.core.commons.services.doceditor.DocEditorConfigs; -import org.olat.core.commons.services.doceditor.DocEditorSecurityCallback; import org.olat.core.commons.services.doceditor.DocEditor.Mode; +import org.olat.core.commons.services.doceditor.DocEditorConfigs; import org.olat.core.commons.services.doceditor.DocEditorConfigs.Config; +import org.olat.core.commons.services.doceditor.DocEditorSecurityCallback; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.velocity.VelocityContainer; @@ -44,7 +45,6 @@ import org.olat.core.gui.control.Event; import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.controller.BasicController; import org.olat.core.gui.control.controller.BlankController; -import org.apache.logging.log4j.Logger; import org.olat.core.logging.Tracing; import org.olat.core.util.vfs.VFSLeaf; import org.olat.core.util.vfs.VFSLockApplicationType; @@ -97,7 +97,7 @@ public class FileEditorController extends BasicController { if (customLinkTreeModel != null) { htmlCtrl = WysiwygFactory.createWysiwygControllerWithInternalLink(ureq, getWindowControl(), config.getVfsContainer(), config.getFilePath(), true, secCallback.isVersionControlled(), customLinkTreeModel, - config.getEdusharingProvider()); + null, config.getEdusharingProvider()); } else { htmlCtrl = WysiwygFactory.createWysiwygController(ureq, getWindowControl(), config.getVfsContainer(), config.getFilePath(), config.getMediaPath(), true, secCallback.isVersionControlled(), config.getEdusharingProvider()); diff --git a/src/main/java/org/olat/core/commons/editor/htmleditor/HTMLEditorController.java b/src/main/java/org/olat/core/commons/editor/htmleditor/HTMLEditorController.java index 37317126a03ebdb5bd2fd1c623b54c89c658560d..27d6a28c6a55fb5d22d3c0bde990e044a46a59f1 100644 --- a/src/main/java/org/olat/core/commons/editor/htmleditor/HTMLEditorController.java +++ b/src/main/java/org/olat/core/commons/editor/htmleditor/HTMLEditorController.java @@ -112,6 +112,7 @@ public class HTMLEditorController extends FormBasicController { private FormCancel cancel; private FormLink save, saveClose; private CustomLinkTreeModel customLinkTreeModel; + private CustomLinkTreeModel toolLinkTreeModel; private VelocityContainer metadataVC; private boolean editable = true; @@ -151,10 +152,10 @@ public class HTMLEditorController extends FormBasicController { * @return Controller with internal-link selector */ public HTMLEditorController(UserRequest ureq, WindowControl wControl, VFSContainer baseContainer, - String relFilePath, CustomLinkTreeModel customLinkTreeModel, String mediaPath, boolean editorCheckEnabled, - boolean versions, VFSEdusharingProvider edusharingProvider) { + String relFilePath, CustomLinkTreeModel customLinkTreeModel, CustomLinkTreeModel toolLinkTreeModel, + String mediaPath, boolean editorCheckEnabled, boolean versions, VFSEdusharingProvider edusharingProvider) { super(ureq, wControl, "htmleditor"); - initEditorForm(baseContainer, relFilePath, customLinkTreeModel, mediaPath, editorCheckEnabled, versions, true, edusharingProvider); + initEditorForm(baseContainer, relFilePath, customLinkTreeModel, toolLinkTreeModel, mediaPath, editorCheckEnabled, versions, true, edusharingProvider); initForm(ureq); } @@ -164,12 +165,12 @@ public class HTMLEditorController extends FormBasicController { boolean versions, boolean withButtons, VFSEdusharingProvider edusharingProvider, Form rootForm) { super(ureq, wControl, LAYOUT_CUSTOM, "htmleditor", rootForm); // set some basic variables - initEditorForm(baseContainer, relFilePath, customLinkTreeModel, mediaPath, editorCheckEnabled, versions, withButtons, edusharingProvider); + initEditorForm(baseContainer, relFilePath, customLinkTreeModel, null, mediaPath, editorCheckEnabled, versions, withButtons, edusharingProvider); initForm(ureq); } private void initEditorForm(VFSContainer bContainer, String relFilePath, CustomLinkTreeModel linkTreeModel, - String mPath, boolean editorCheck, boolean versions, boolean withButtons, + CustomLinkTreeModel toolLinkTreeModel, String mPath, boolean editorCheck, boolean versions, boolean withButtons, VFSEdusharingProvider edusharingProvider) { this.baseContainer = bContainer; @@ -178,6 +179,7 @@ public class HTMLEditorController extends FormBasicController { this.versionsEnabled = versions; this.buttonsEnabled = withButtons; this.customLinkTreeModel = linkTreeModel; + this.toolLinkTreeModel = toolLinkTreeModel; this.editorCheckEnabled = editorCheck; // make sure the filename doesn't start with a slash this.fileName = ((relFilePath.charAt(0) == '/') ? relFilePath.substring(1) : relFilePath); @@ -297,7 +299,9 @@ public class HTMLEditorController extends FormBasicController { VelocityContainer vc = (VelocityContainer) formLayout.getComponent(); vc.contextPut("fileToLargeError", fileToLargeError); } else { - htmlElement = uifactory.addRichTextElementForFileData("rtfElement", null, body, -1, -1, baseContainer, fileName, customLinkTreeModel, formLayout, ureq.getUserSession(), getWindowControl()); + htmlElement = uifactory.addRichTextElementForFileData("rtfElement", null, body, -1, -1, baseContainer, + fileName, customLinkTreeModel, toolLinkTreeModel, formLayout, ureq.getUserSession(), + getWindowControl()); // // Add resize handler RichTextConfiguration editorConfiguration = htmlElement.getEditorConfiguration(); diff --git a/src/main/java/org/olat/core/commons/editor/htmleditor/WysiwygFactory.java b/src/main/java/org/olat/core/commons/editor/htmleditor/WysiwygFactory.java index bbe50594acfa3a3d69d45149e527007c2dda9e0f..31840c1498fd15482e03ddf69ae2178046142f39 100644 --- a/src/main/java/org/olat/core/commons/editor/htmleditor/WysiwygFactory.java +++ b/src/main/java/org/olat/core/commons/editor/htmleditor/WysiwygFactory.java @@ -80,7 +80,7 @@ public class WysiwygFactory { public static HTMLEditorController createWysiwygController(UserRequest ureq, WindowControl wControl, VFSContainer rootDir, String filePath, String mediaPath, boolean editorCheckEnabled, boolean versions, VFSEdusharingProvider edusharingProvider) { - return new HTMLEditorController(ureq, wControl, rootDir, filePath, null, mediaPath, editorCheckEnabled, + return new HTMLEditorController(ureq, wControl, rootDir, filePath, null, null, mediaPath, editorCheckEnabled, versions, edusharingProvider); } @@ -113,23 +113,23 @@ public class WysiwygFactory { String relFilePath, boolean editorCheckEnabled, CustomLinkTreeModel customLinkTreeModel) { return createWysiwygControllerWithInternalLink(ureq, wControl, baseContainer, relFilePath, editorCheckEnabled, - customLinkTreeModel, null); + customLinkTreeModel, null, null); } - public static HTMLEditorController createWysiwygControllerWithInternalLink( - UserRequest ureq, WindowControl wControl, VFSContainer baseContainer, - String relFilePath, boolean editorCheckEnabled, - CustomLinkTreeModel customLinkTreeModel, VFSEdusharingProvider edusharingProvider) { + public static HTMLEditorController createWysiwygControllerWithInternalLink(UserRequest ureq, WindowControl wControl, + VFSContainer baseContainer, String relFilePath, boolean editorCheckEnabled, + CustomLinkTreeModel customLinkTreeModel, CustomLinkTreeModel toolLinkTreeModel, + VFSEdusharingProvider edusharingProvider) { return createWysiwygControllerWithInternalLink(ureq, wControl, baseContainer, relFilePath, editorCheckEnabled, - true, customLinkTreeModel, edusharingProvider); + true, customLinkTreeModel, toolLinkTreeModel, edusharingProvider); } - public static HTMLEditorController createWysiwygControllerWithInternalLink( - UserRequest ureq, WindowControl wControl, VFSContainer baseContainer, - String relFilePath, boolean editorCheckEnabled, boolean version, - CustomLinkTreeModel customLinkTreeModel, VFSEdusharingProvider edusharingProvider) { + public static HTMLEditorController createWysiwygControllerWithInternalLink(UserRequest ureq, WindowControl wControl, + VFSContainer baseContainer, String relFilePath, boolean editorCheckEnabled, boolean version, + CustomLinkTreeModel customLinkTreeModel, CustomLinkTreeModel toolLinkTreeModel, + VFSEdusharingProvider edusharingProvider) { return new HTMLEditorController(ureq, wControl, baseContainer, relFilePath, - customLinkTreeModel, null, editorCheckEnabled, version, edusharingProvider); + customLinkTreeModel, toolLinkTreeModel, null, editorCheckEnabled, version, edusharingProvider); } /** diff --git a/src/main/java/org/olat/core/commons/modules/bc/FileCopyController.java b/src/main/java/org/olat/core/commons/modules/bc/FileCopyController.java index 510ccd891aee6c465bd76886868c6a89a822e393..449e576763a041273baa6b009cb6635631cec2c4 100644 --- a/src/main/java/org/olat/core/commons/modules/bc/FileCopyController.java +++ b/src/main/java/org/olat/core/commons/modules/bc/FileCopyController.java @@ -91,7 +91,7 @@ public class FileCopyController extends LinkChooserController { public FileCopyController(UserRequest ureq, WindowControl wControl, VFSContainer rootDir, FolderComponent folderComponent) { - super(ureq, wControl, rootDir, null, null, null, false, "", null, true); + super(ureq, wControl, rootDir, null, null, null, false, "", null, null, true); this.folderComponent = folderComponent; } diff --git a/src/main/java/org/olat/core/commons/modules/singlepage/SinglePageController.java b/src/main/java/org/olat/core/commons/modules/singlepage/SinglePageController.java index e64a76615c44a1935fb1c6fc1fba25049ae52ab8..106486d695d0dd8033620987d84863660a029dd5 100644 --- a/src/main/java/org/olat/core/commons/modules/singlepage/SinglePageController.java +++ b/src/main/java/org/olat/core/commons/modules/singlepage/SinglePageController.java @@ -28,6 +28,7 @@ package org.olat.core.commons.modules.singlepage; import java.util.List; +import org.apache.logging.log4j.Logger; import org.olat.core.commons.controllers.linkchooser.CustomLinkTreeModel; import org.olat.core.commons.editor.htmleditor.WysiwygFactory; import org.olat.core.gui.UserRequest; @@ -51,7 +52,6 @@ import org.olat.core.id.OLATResourceable; import org.olat.core.id.context.BusinessControl; import org.olat.core.id.context.ContextEntry; import org.olat.core.id.context.StateEntry; -import org.apache.logging.log4j.Logger; import org.olat.core.logging.Tracing; import org.olat.core.logging.activity.CoreLoggingResourceable; import org.olat.core.logging.activity.CourseLoggingAction; @@ -86,6 +86,7 @@ public class SinglePageController extends BasicController implements CloneableCo private final IFrameDisplayController idc; private final VelocityContainer myContent; private CustomLinkTreeModel customLinkTreeModel; + private CustomLinkTreeModel toolLinkTreeModel; private final String frameId; private final boolean randomizeMapper; @@ -263,7 +264,7 @@ public class SinglePageController extends BasicController implements CloneableCo htmlEditorController = WysiwygFactory.createWysiwygController(ureq, getWindowControl(), g_new_rootContainer, g_curURI, true, true, edusharingProvider); } else { htmlEditorController = WysiwygFactory.createWysiwygControllerWithInternalLink(ureq, getWindowControl(), g_new_rootContainer, - g_curURI, true, customLinkTreeModel, edusharingProvider ); + g_curURI, true, customLinkTreeModel, toolLinkTreeModel, edusharingProvider); } listenTo(htmlEditorController); mainPanel.setContent(htmlEditorController.getInitialComponent()); @@ -291,6 +292,10 @@ public class SinglePageController extends BasicController implements CloneableCo public void setInternalLinkTreeModel(CustomLinkTreeModel customLinkTreeModel) { this.customLinkTreeModel = customLinkTreeModel; } + + public void setToolLinkTreeModel(CustomLinkTreeModel toolLinkTreeModel) { + this.toolLinkTreeModel = toolLinkTreeModel; + } @Override public Controller cloneController(UserRequest ureq, WindowControl control) { 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 c654ce7e746aaaedd4307f8d3553f5fa113bb6f9..f52b24d394b4dadc02e7f807922a38cb79397455 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 @@ -906,6 +906,7 @@ public class FormUIFactory { * The path to the file relative to the baseContainer * @param customLinkTreeModel * A custom link tree model or NULL not not use a custom model + * @param toolLinkTreeModel * @param formLayout * The form item container where to add the rich text element * @param usess @@ -914,19 +915,17 @@ public class FormUIFactory { * the current window controller * @return The richt text element instance */ - public RichTextElement addRichTextElementForFileData(String name, - final String i18nLabel, String initialValue, final int rows, int cols, - VFSContainer baseContainer, String relFilePath, - CustomLinkTreeModel customLinkTreeModel, - FormItemContainer formLayout, UserSession usess, - WindowControl wControl) { + public RichTextElement addRichTextElementForFileData(String name, final String i18nLabel, String initialValue, + final int rows, int cols, VFSContainer baseContainer, String relFilePath, + CustomLinkTreeModel customLinkTreeModel, CustomLinkTreeModel toolLinkTreeModel, + FormItemContainer formLayout, UserSession usess, WindowControl wControl) { // Create richt text element with bare bone configuration RichTextElement rte = new RichTextElementImpl(name, initialValue, rows, cols, formLayout.getRootForm(), formLayout.getTranslator().getLocale()); setLabelIfNotNull(i18nLabel, rte); // Now configure editor rte.getEditorConfiguration().setConfigProfileFileEditor(usess, wControl.getWindowBackOffice().getWindow().getGuiTheme(), - baseContainer, relFilePath, customLinkTreeModel); + baseContainer, relFilePath, customLinkTreeModel, toolLinkTreeModel); // Add to form and finish formLayout.add(rte); return rte; 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 aaf1e0ca22f785557cfe78c4a70c5c233b22aa54..71f9e4582736f0f16b70a2d7411012d5221364c3 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 @@ -29,6 +29,7 @@ import java.util.Locale; import java.util.Map; import java.util.Map.Entry; +import org.apache.logging.log4j.Logger; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.controllers.linkchooser.CustomLinkTreeModel; import org.olat.core.dispatcher.impl.StaticMediaDispatcher; @@ -43,7 +44,6 @@ import org.olat.core.gui.themes.Theme; import org.olat.core.gui.translator.Translator; import org.olat.core.helpers.Settings; import org.olat.core.id.Identity; -import org.apache.logging.log4j.Logger; import org.olat.core.logging.Tracing; import org.olat.core.util.CodeHelper; import org.olat.core.util.Formatter; @@ -75,7 +75,7 @@ import org.olat.modules.edusharing.EdusharingProvider; * @author gnaegi */ public class RichTextConfiguration implements Disposable { - private static final Logger log = Tracing.createLoggerFor(RichTextConfiguration.class); + private static final Logger log = Tracing.createLoggerFor(RichTextConfiguration.class); private static final String MODE = "mode"; private static final String MODE_VALUE_EXACT = "exact"; private static final String ELEMENTS = "elements"; @@ -87,7 +87,7 @@ public class RichTextConfiguration implements Disposable { private static final String CONVERT_URLS = "convert_urls"; private static final String IMPORTCSS_APPEND = "importcss_append"; private static final String IMPORT_SELECTOR_CONVERTER = "importcss_selector_converter"; - private static final String IMPORT_SELECTOR_CONVERTER_VALUE_REMOVE_EMOTICONS ="function(selector) { if (selector.indexOf('img.b_emoticons') != -1 || selector.indexOf('img.o_emoticons') != -1) {return false;} else { return this.convertSelectorToFormat(selector); }}"; + private static final String IMPORT_SELECTOR_CONVERTER_VALUE_REMOVE_EMOTICONS = "function(selector) { if (selector.indexOf('img.b_emoticons') != -1 || selector.indexOf('img.o_emoticons') != -1) {return false;} else { return this.convertSelectorToFormat(selector); }}"; private static final String IMPORTCSS_SELECTOR_FILTER = "importcss_selector_filter"; private static final String IMPORTCSS_GROUPS = "importcss_groups"; private static final String IMPORTCSS_GROUPS_VALUE_MENU = "[{title: 'Paragraph', filter: /^(p)\\./},{title: 'Div', filter: /^(div|p)\\./},{title: 'Table', filter: /^(table|th|td|tr)\\./},{title: 'Url', filter: /^(a)\\./},{title: 'Style'}]"; @@ -122,7 +122,8 @@ public class RichTextConfiguration implements Disposable { private static final String AUTORESIZE_BOTTOM_MARGIN = "autoresize_bottom_margin"; private static final String AUTORESIZE_MAX_HEIGHT = "autoresize_max_height"; private static final String AUTORESIZE_MIN_HEIGHT = "autoresize_min_height"; - //private static final String AUTORESIZE_OVERFLOW_PADDING = "autoresize_overflow_padding"; + // private static final String AUTORESIZE_OVERFLOW_PADDING = + // "autoresize_overflow_padding"; // // Generic boolean true / false values @@ -144,9 +145,9 @@ public class RichTextConfiguration implements Disposable { // Supported image and media suffixes private static final String[] IMAGE_SUFFIXES_VALUES = { "jpg", "gif", "jpeg", "png" }; - private static final String[] MEDIA_SUFFIXES_VALUES = { "swf", "dcr", "mov", "qt", "mpg", "mp3", "mp4", "mpeg", "avi", "wmv", "wm", "asf", - "asx", "wmx", "wvx", "rm", "ra", "ram" }; - private static final String[] FLASH_PLAYER_SUFFIXES_VALUES = {"flv","f4v","mp3","mp4","aac","m4v","m4a"}; + private static final String[] MEDIA_SUFFIXES_VALUES = { "swf", "dcr", "mov", "qt", "mpg", "mp3", "mp4", "mpeg", + "avi", "wmv", "wm", "asf", "asx", "wmx", "wvx", "rm", "ra", "ram" }; + private static final String[] FLASH_PLAYER_SUFFIXES_VALUES = { "flv", "f4v", "mp3", "mp4", "aac", "m4v", "m4a" }; private String[] linkBrowserImageSuffixes; private String[] linkBrowserMediaSuffixes; @@ -163,32 +164,34 @@ public class RichTextConfiguration implements Disposable { private boolean sendOnBlur; private boolean readOnly; private boolean filenameUriValidation = false; - private CustomLinkTreeModel linkBrowserCustomTreeModel; + private CustomLinkTreeModel linkBrowserCustomTreeModel; + private CustomLinkTreeModel toolLinkTreeModel; // DOM ID of the flexi form element private String domID; - + private String mapperUri; private MapperKey contentMapperKey; - + private final Locale locale; private TinyConfig tinyConfig; - + private List<TextMode> textModes = Collections.singletonList(TextMode.formatted); private RichTextConfigurationDelegate additionalConfiguration; - + private Collection<Filter> valueFilters = new ArrayList<>(1); - + public RichTextConfiguration(Locale locale) { this.locale = locale; - tinyConfig = TinyConfig.minimalisticConfig; + tinyConfig = TinyConfig.minimalisticConfig; } /** * Constructor, only used by RichText element itself. Use * richtTextElement.getEditorConfiguration() to acess this object * - * @param domID The ID of the flexi element in the browser DOM - * @param rootFormDispatchId The dispatch ID of the root form that deals with the submit button + * @param domID The ID of the flexi element in the browser DOM + * @param rootFormDispatchId The dispatch ID of the root form that deals with + * the submit button */ public RichTextConfiguration(String domID, String rootFormDispatchId, Locale locale) { this.domID = domID; @@ -203,15 +206,16 @@ public class RichTextConfiguration implements Disposable { setNonQuotedConfigValue("allow_script_urls", "true"); // use modal windows, all OLAT workflows are implemented to work this way setModalWindowsEnabled(true); - // Start observing of diry richt text element and trigger calling of setFlexiFormDirty() method + // Start observing of diry richt text element and trigger calling of + // setFlexiFormDirty() method // This check is initialized after the editor has fully loaded - addOnInitCallbackFunction(ONINIT_CALLBACK_VALUE_START_DIRTY_OBSERVER + "('" + rootFormDispatchId + "','" + domID + "')"); + addOnInitCallbackFunction( + ONINIT_CALLBACK_VALUE_START_DIRTY_OBSERVER + "('" + rootFormDispatchId + "','" + domID + "')"); addOnInitCallbackFunction("tinyMCE.get('" + domID + "').focus()"); } /** - * Method to add the standard configuration for the form based minimal - * editor + * Method to add the standard configuration for the form based minimal editor * * @param usess * @param externalToolbar @@ -227,10 +231,10 @@ public class RichTextConfiguration implements Disposable { } // Don't allow javascript or iframes setQuotedConfigValue(INVALID_ELEMENTS, INVALID_ELEMENTS_FORM_MINIMALISTIC_VALUE_UNSAVE); - + tinyConfig = TinyConfig.minimalisticConfig; } - + public void setConfigProfileFormParagraphEditor(Theme guiTheme) { setConfigBasics(guiTheme); // Add additional plugins @@ -241,10 +245,10 @@ public class RichTextConfiguration implements Disposable { } // Don't allow javascript or iframes setQuotedConfigValue(INVALID_ELEMENTS, INVALID_ELEMENTS_FORM_MINIMALISTIC_VALUE_UNSAVE); - + tinyConfig = TinyConfig.paragraphEditorConfig; } - + public void setConfigProfileFormCompactEditor(UserSession usess, Theme guiTheme, VFSContainer baseContainer) { setConfigBasics(guiTheme); // Add additional plugins @@ -253,21 +257,24 @@ public class RichTextConfiguration implements Disposable { for (TinyMCECustomPlugin tinyMCECustomPlugin : enabledCustomPlugins) { setCustomPluginEnabled(tinyMCECustomPlugin); } - - // 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)); + + // 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.editorCompactConfig; setPathInStatusBar(false); - + // Setup file and link browser if (baseContainer != null) { tinyConfig = tinyConfig.enableImageAndMedia(); - setFileBrowserCallback(baseContainer, null, IMAGE_SUFFIXES_VALUES, MEDIA_SUFFIXES_VALUES, FLASH_PLAYER_SUFFIXES_VALUES); + setFileBrowserCallback(baseContainer, null, null, IMAGE_SUFFIXES_VALUES, MEDIA_SUFFIXES_VALUES, + FLASH_PLAYER_SUFFIXES_VALUES); // since in form editor mode and not in file mode we use null as relFilePath - setDocumentMediaBase(baseContainer, null, usess); + setDocumentMediaBase(baseContainer, null, usess); } } - + /** * Contains only the image upload and the math plugin. * @@ -275,7 +282,8 @@ public class RichTextConfiguration implements Disposable { * @param guiTheme * @param baseContainer */ - public void setConfigProfileFormVeryMinimalisticConfigEditor(UserSession usess, Theme guiTheme, VFSContainer baseContainer, boolean withLinks) { + public void setConfigProfileFormVeryMinimalisticConfigEditor(UserSession usess, Theme guiTheme, + VFSContainer baseContainer, boolean withLinks) { setConfigBasics(guiTheme); // Add additional plugins TinyMCECustomPluginFactory customPluginFactory = CoreSpringFactory.getImpl(TinyMCECustomPluginFactory.class); @@ -283,32 +291,33 @@ public class RichTextConfiguration implements Disposable { for (TinyMCECustomPlugin tinyMCECustomPlugin : enabledCustomPlugins) { setCustomPluginEnabled(tinyMCECustomPlugin); } - - // 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)); - if(withLinks) { + + // 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)); + if (withLinks) { tinyConfig = TinyConfig.veryMinimalisticWithLinksConfig; } else { tinyConfig = TinyConfig.veryMinimalisticConfig; } setPathInStatusBar(false); - + // Setup file and link browser if (baseContainer != null) { tinyConfig = tinyConfig.enableImageAndMedia(); - setFileBrowserCallback(baseContainer, null, IMAGE_SUFFIXES_VALUES, MEDIA_SUFFIXES_VALUES, FLASH_PLAYER_SUFFIXES_VALUES); + setFileBrowserCallback(baseContainer, null, null, IMAGE_SUFFIXES_VALUES, MEDIA_SUFFIXES_VALUES, + FLASH_PLAYER_SUFFIXES_VALUES); // since in form editor mode and not in file mode we use null as relFilePath - setDocumentMediaBase(baseContainer, null, usess); + setDocumentMediaBase(baseContainer, null, usess); } } - /** - * Method to add the standard configuration for the form based simple and - * full editor + * Method to add the standard configuration for the form based simple and full + * editor * - * @param fullProfile - * true: use full profile; false: use simple profile + * @param fullProfile true: use full profile; false: use simple profile * @param usess * @param externalToolbar * @param guiTheme @@ -323,24 +332,27 @@ public class RichTextConfiguration implements Disposable { List<TinyMCECustomPlugin> enabledCustomPlugins = customPluginFactory.getCustomPlugionsForProfile(); for (TinyMCECustomPlugin tinyMCECustomPlugin : enabledCustomPlugins) { setCustomPluginEnabled(tinyMCECustomPlugin); - } - - if (fullProfile) { - // Don't allow javascript or iframes + } + + if (fullProfile) { + // Don't allow javascript or iframes setQuotedConfigValue(INVALID_ELEMENTS, INVALID_ELEMENTS_FORM_FULL_VALUE_UNSAVE); tinyConfig = TinyConfig.editorFullConfig; } else { - // 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)); + // 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.editorConfig; } // Setup file and link browser if (baseContainer != null) { tinyConfig = tinyConfig.enableImageAndMedia(); - setFileBrowserCallback(baseContainer, customLinkTreeModel, IMAGE_SUFFIXES_VALUES, MEDIA_SUFFIXES_VALUES, FLASH_PLAYER_SUFFIXES_VALUES); + setFileBrowserCallback(baseContainer, customLinkTreeModel, null, IMAGE_SUFFIXES_VALUES, MEDIA_SUFFIXES_VALUES, + FLASH_PLAYER_SUFFIXES_VALUES); // since in form editor mode and not in file mode we use null as relFilePath - setDocumentMediaBase(baseContainer, relFilePath, usess); + setDocumentMediaBase(baseContainer, relFilePath, usess); } } @@ -353,37 +365,40 @@ public class RichTextConfiguration implements Disposable { * @param baseContainer * @param relFilePath * @param customLinkTreeModel + * @param toolLinkTreeModel */ - public void setConfigProfileFileEditor(UserSession usess, Theme guiTheme, VFSContainer baseContainer, String relFilePath, CustomLinkTreeModel customLinkTreeModel) { + public void setConfigProfileFileEditor(UserSession usess, Theme guiTheme, VFSContainer baseContainer, + String relFilePath, CustomLinkTreeModel customLinkTreeModel, CustomLinkTreeModel toolLinkTreeModel) { setConfigBasics(guiTheme); // Line 1 setFullscreenEnabled(true, false); setInsertDateTimeEnabled(true, usess.getLocale()); // Plugins without buttons - setNoneditableContentEnabled(true, null); + setNoneditableContentEnabled(true, null); TinyMCECustomPluginFactory customPluginFactory = CoreSpringFactory.getImpl(TinyMCECustomPluginFactory.class); List<TinyMCECustomPlugin> enabledCustomPlugins = customPluginFactory.getCustomPlugionsForProfile(); for (TinyMCECustomPlugin tinyMCECustomPlugin : enabledCustomPlugins) { setCustomPluginEnabled(tinyMCECustomPlugin); } - + // Allow editing of all kind of HTML elements and attributes setQuotedConfigValue(EXTENDED_VALID_ELEMENTS, EXTENDED_VALID_ELEMENTS_VALUE_FULL + "," + MATHML_VALID_ELEMENTS); setQuotedConfigValue(INVALID_ELEMENTS, INVALID_ELEMENTS_FILE_FULL_VALUE_UNSAVE); - + setNonQuotedConfigValue(PASTE_DATA_IMAGES, "true"); // Setup file and link browser if (baseContainer != null) { - setFileBrowserCallback(baseContainer, customLinkTreeModel, IMAGE_SUFFIXES_VALUES, MEDIA_SUFFIXES_VALUES, FLASH_PLAYER_SUFFIXES_VALUES); - setDocumentMediaBase(baseContainer, relFilePath, usess); + setFileBrowserCallback(baseContainer, customLinkTreeModel, toolLinkTreeModel, IMAGE_SUFFIXES_VALUES, + MEDIA_SUFFIXES_VALUES, FLASH_PLAYER_SUFFIXES_VALUES); + setDocumentMediaBase(baseContainer, relFilePath, usess); } - + tinyConfig = TinyConfig.fileEditorConfig; } /** - * Internal helper to generate the common configurations which are used by - * each profile + * Internal helper to generate the common configurations which are used by each + * profile * * @param usess * @param externalToolbar @@ -403,7 +418,7 @@ public class RichTextConfiguration implements Disposable { public boolean isRelativeUrls() { return relativeUrls; } - + /** * If this option is set to true, all URLs returned from the MCFileManager and * linkConverter will be relative from the specified document_base_url. If it's @@ -423,8 +438,8 @@ public class RichTextConfiguration implements Disposable { /** * If this option is enabled the protocol and host part of the URLs returned - * from the MCFileManager and linkConverter will be removed. This option is - * only used if the relative_urls option is set to false. + * from the MCFileManager and linkConverter will be removed. This option is only + * used if the relative_urls option is set to false. * * @see https://www.tinymce.com/docs/configure/url-handling/#remove_script_host * @@ -448,6 +463,7 @@ public class RichTextConfiguration implements Disposable { /** * Send the content of the rich text element on blur event. + * * @param sendOnBlur */ public void setSendOnBlur(boolean sendOnBlur) { @@ -461,7 +477,6 @@ public class RichTextConfiguration implements Disposable { public void setPathInStatusBar(boolean pathInStatusBar) { this.pathInStatusBar = pathInStatusBar; } - public boolean isReadOnly() { return readOnly; @@ -476,9 +491,9 @@ public class RichTextConfiguration implements Disposable { } public void setSimplestTextModeAllowed(TextMode textMode) { - if(textMode != null) { + if (textMode != null) { List<TextMode> newModes = new ArrayList<>(3); - for(int i=textMode.ordinal(); i<=TextMode.formatted.ordinal(); i++) { + for (int i = textMode.ordinal(); i <= TextMode.formatted.ordinal(); i++) { newModes.add(TextMode.values()[i]); } textModes = newModes; @@ -498,17 +513,17 @@ public class RichTextConfiguration implements Disposable { /** * Add a function name that has to be executed after initialization. <br> * E.g: myFunctionName, (alert('loading successfull')) <br> - * Don't add something like this: function() {alert('loading successfull')}, - * use the following notation instead: (alert('loading successfull')) + * Don't add something like this: function() {alert('loading successfull')}, use + * the following notation instead: (alert('loading successfull')) * * @param functionName */ public void addOnInitCallbackFunction(String functionName) { - if(functionName != null) { + if (functionName != null) { oninit.add(functionName); } } - + protected List<String> getOnInit() { return oninit; } @@ -517,24 +532,24 @@ public class RichTextConfiguration implements Disposable { * Enable the tabfocus plugin * * if enabled its possible to enter/leave the tinyMCE-editor with TAB-key. - * drawback is, that you cannot enter tabs in the editor itself or navigate over buttons! - * see http://bugs.olat.org/jira/browse/OLAT-6242 + * drawback is, that you cannot enter tabs in the editor itself or navigate over + * buttons! see http://bugs.olat.org/jira/browse/OLAT-6242 + * * @param tabFocusEnabled */ - private void setTabFocusEnabled(boolean tabFocusEnabled){ - if (tabFocusEnabled){ + private void setTabFocusEnabled(boolean tabFocusEnabled) { + if (tabFocusEnabled) { setQuotedConfigValue(TABFOCUS_SETTINGS, TABFOCUS_SETTINGS_PREV_NEXT); } } - + /** * Configure the tinymce windowing system * - * @param modalWindowsEnabled - * true: use modal windows; false: use non-modal windows - * @param inlinePopupsEnabled - * true: use inline popups; false: use browser window popup - * windows + * @param modalWindowsEnabled true: use modal windows; false: use non-modal + * windows + * @param inlinePopupsEnabled true: use inline popups; false: use browser window + * popup windows */ private void setModalWindowsEnabled(boolean modalWindowsEnabled) { // in both cases opt in, default values are set to non-inline windows that @@ -545,8 +560,8 @@ public class RichTextConfiguration implements Disposable { } /** - * Set the language for editor interface. If no translation can be found, - * the system fallbacks to EN + * Set the language for editor interface. If no translation can be found, the + * system fallbacks to EN * * @param loc */ @@ -555,47 +570,48 @@ public class RichTextConfiguration implements Disposable { String langKey = loc.getLanguage(); String path = "/static/js/tinymce4/tinymce/langs/" + langKey + ".js"; String realPath = WebappHelper.getContextRealPath(path); - if(realPath == null || !(new File(realPath).exists())) { + if (realPath == null || !(new File(realPath).exists())) { langKey = "en"; } setQuotedConfigValue(LANGUAGE, langKey); } /** - * Enable or disable areas in the editor content that can't be modified at - * all. The areas are identified with the nonEditableCSSClass. + * Enable or disable areas in the editor content that can't be modified at all. + * The areas are identified with the nonEditableCSSClass. * - * @param noneditableContentEnabled - * true: use non-editable areas; false: all areas are editable - * @param nonEditableCSSClass - * the class that identifies the non-editable fields or NULL to - * use the default value 'mceNonEditable' + * @param noneditableContentEnabled true: use non-editable areas; false: all + * areas are editable + * @param nonEditableCSSClass the class that identifies the non-editable + * fields or NULL to use the default value + * 'mceNonEditable' */ private void setNoneditableContentEnabled(boolean noneditableContentEnabled, String nonEditableCSSClass) { if (noneditableContentEnabled) { - if (nonEditableCSSClass != null && !nonEditableCSSClass.equals(NONEDITABLE_NONEDITABLE_CLASS_VALUE_MCENONEDITABLE)) { + if (nonEditableCSSClass != null + && !nonEditableCSSClass.equals(NONEDITABLE_NONEDITABLE_CLASS_VALUE_MCENONEDITABLE)) { // Add non editable class but only when it differs from the default name setQuotedConfigValue(NONEDITABLE_NONEDITABLE_CLASS, nonEditableCSSClass); } } } - + public void disableMedia() { tinyConfig = tinyConfig.disableMedia(); } - + public void disableTinyMedia() { tinyConfig = tinyConfig.disableTinyMedia(); } - + public void disableMathEditor() { tinyConfig = tinyConfig.disableMathEditor(); } - + public void disableImageAndMovie() { tinyConfig = tinyConfig.disableImageAndMedia(); } - + public void disableMenuAndMenuBar() { tinyConfig = tinyConfig.disableMenuAndMenuBar(); } @@ -603,31 +619,31 @@ public class RichTextConfiguration implements Disposable { /** * Enable / disable the full-screen plugin * - * @param fullScreenEnabled - * true: plugin enabled; false: plugin disabled - * @param inNewWindowEnabled - * true: fullscreen opens in new window; false: fullscreen opens - * in same window. - * @param row - * The row where to place the plugin buttons - */ + * @param fullScreenEnabled true: plugin enabled; false: plugin disabled + * @param inNewWindowEnabled true: fullscreen opens in new window; false: + * fullscreen opens in same window. + * @param row The row where to place the plugin buttons + */ private void setFullscreenEnabled(boolean fullScreenEnabled, boolean inNewWindowEnabled) { if (fullScreenEnabled) { // enabled if needed, disabled by default - if (inNewWindowEnabled) setNonQuotedConfigValue(FULLSCREEN_NEW_WINDOW, VALUE_FALSE); + if (inNewWindowEnabled) + setNonQuotedConfigValue(FULLSCREEN_NEW_WINDOW, VALUE_FALSE); } } - /** - * Enable / disable the auto-resizing of the text input field. When enabled, - * the editor will expand the input filed until the maxHeight is reached (if - * set) + * Enable / disable the auto-resizing of the text input field. When enabled, the + * editor will expand the input filed until the maxHeight is reached (if set) * - * @param autoResizeEnabled true: enable auto-resize; false: no auto-resize (default) - * @param maxHeight value of max height in pixels or -1 to indicate infinite height - * @param minHeight value of min height in pixels or -1 to indicate no minimum height - * @param bottomMargin value of bottom margin below or -1 to use html editor default + * @param autoResizeEnabled true: enable auto-resize; false: no auto-resize + * (default) + * @param maxHeight value of max height in pixels or -1 to indicate + * infinite height + * @param minHeight value of min height in pixels or -1 to indicate no + * minimum height + * @param bottomMargin value of bottom margin below or -1 to use html + * editor default */ public void setAutoResizeEnabled(boolean autoResizeEnabled, int maxHeight, int minHeight, int bottomMargin) { if (autoResizeEnabled) { @@ -636,28 +652,23 @@ public class RichTextConfiguration implements Disposable { setNonQuotedConfigValue(AUTORESIZE_MAX_HEIGHT, Integer.toString(maxHeight)); } if (minHeight > -1) { - setNonQuotedConfigValue(AUTORESIZE_MIN_HEIGHT, Integer.toString(minHeight)); + setNonQuotedConfigValue(AUTORESIZE_MIN_HEIGHT, Integer.toString(minHeight)); } if (bottomMargin > -1) { - setNonQuotedConfigValue(AUTORESIZE_BOTTOM_MARGIN, Integer.toString(bottomMargin)); + setNonQuotedConfigValue(AUTORESIZE_BOTTOM_MARGIN, Integer.toString(bottomMargin)); } } else { this.tinyConfig = this.tinyConfig.disableAutoResize(); - + } } - - /** * Enable / disable the date and time insert plugin * - * @param insertDateTimeEnabled - * true: plugin enabled; false: plugin disabled - * @param locale - * the locale used to format the date and time - * @param row - * The row where to place the plugin buttons + * @param insertDateTimeEnabled true: plugin enabled; false: plugin disabled + * @param locale the locale used to format the date and time + * @param row The row where to place the plugin buttons */ private void setInsertDateTimeEnabled(boolean insertDateTimeEnabled, Locale locale) { if (insertDateTimeEnabled) { @@ -673,13 +684,12 @@ public class RichTextConfiguration implements Disposable { * Enable / disable a TinyMCECustomPlugin plugin * * @param customPluginEnabled true: plugin enabled; false: plugin disabled - * @param customPlugin the plugin - * @param profil - * The profile in which context the plugin is used + * @param customPlugin the plugin + * @param profil The profile in which context the plugin is used */ private void setCustomPluginEnabled(TinyMCECustomPlugin customPlugin) { // Add plugin specific parameters - Map<String,String> params = customPlugin.getPluginParameters(locale); + Map<String, String> params = customPlugin.getPluginParameters(locale); if (params != null) { for (Entry<String, String> param : params.entrySet()) { // don't use pluginName var, don't add the '-' char for params @@ -693,22 +703,21 @@ public class RichTextConfiguration implements Disposable { /** * Set the path to content css files used to format the content. * - * @param cssPath - * path to CSS separated by comma or NULL to not use any specific - * CSS files + * @param cssPath path to CSS separated by comma or NULL to not use any specific + * CSS files */ private void setContentCSS(String cssPath) { if (cssPath != null) { quotedConfigValues.put(CONTENT_CSS, cssPath); } else { - if (quotedConfigValues.containsKey(CONTENT_CSS)) quotedConfigValues.remove(CONTENT_CSS); + if (quotedConfigValues.containsKey(CONTENT_CSS)) + quotedConfigValues.remove(CONTENT_CSS); } } /** - * Set the content CSS form the given theme. This will add the content.css - * from the default themen and override it with the current theme - * content.css + * Set the content CSS form the given theme. This will add the content.css from + * the default themen and override it with the current theme content.css * * @param theme */ @@ -726,14 +735,14 @@ public class RichTextConfiguration implements Disposable { } /** - * Set the forced root element that is entered into the edit area when the - * area is empty. By default this is a <p> element + * Set the forced root element that is entered into the edit area when the area + * is empty. By default this is a <p> element * * @param rootElement - - public void setForcedRootElement(String rootElement) { - setQuotedConfigValue(FORCED_ROOT_BLOCK, rootElement); - }*/ + * + * public void setForcedRootElement(String rootElement) { + * setQuotedConfigValue(FORCED_ROOT_BLOCK, rootElement); } + */ /** * Disable the standard root element if no such wrapper element should be @@ -743,51 +752,52 @@ public class RichTextConfiguration implements Disposable { setQuotedConfigValue(FORCED_ROOT_BLOCK, FORCED_ROOT_BLOCK_VALUE_NOROOT); } - /** - * Set the file browser callback for the given vfs container and link tree - * model + * Set the file browser callback for the given vfs container and link tree model * - * @param vfsContainer - * The vfs container from which the files can be choosen - * @param customLinkTreeModel - * an optional custom link tree model - * @param supportedImageSuffixes - * Array of allowed image suffixes (jpg, png etc.) - * @param supportedMediaSuffixes - * Array of allowed media suffixes (mov, wav etc.) + * @param vfsContainer The vfs container from which the files can be + * choosen + * @param customLinkTreeModel an optional custom link tree model + * @param toolLinkTreeModel + * @param supportedImageSuffixes Array of allowed image suffixes (jpg, png etc.) + * @param supportedMediaSuffixes Array of allowed media suffixes (mov, wav etc.) */ - private void setFileBrowserCallback(VFSContainer vfsContainer, CustomLinkTreeModel customLinkTreeModel, String[] supportedImageSuffixes, String[] supportedMediaSuffixes, String[] supportedFlashPlayerSuffixes) { + private void setFileBrowserCallback(VFSContainer vfsContainer, CustomLinkTreeModel customLinkTreeModel, + CustomLinkTreeModel toolLinkTreeModel, String[] supportedImageSuffixes, String[] supportedMediaSuffixes, + String[] supportedFlashPlayerSuffixes) { // Add dom ID variable using prototype curry method - setNonQuotedConfigValue(FILE_BROWSER_CALLBACK, FILE_BROWSER_CALLBACK_VALUE_LINK_BROWSER + ".curry('" + domID + "')"); + setNonQuotedConfigValue(FILE_BROWSER_CALLBACK, + FILE_BROWSER_CALLBACK_VALUE_LINK_BROWSER + ".curry('" + domID + "')"); linkBrowserImageSuffixes = supportedImageSuffixes; linkBrowserMediaSuffixes = supportedMediaSuffixes; linkBrowserFlashPlayerSuffixes = supportedFlashPlayerSuffixes; linkBrowserBaseContainer = vfsContainer; linkBrowserCustomTreeModel = customLinkTreeModel; + this.toolLinkTreeModel = toolLinkTreeModel; } - + public void disableFileBrowserCallback() { linkBrowserImageSuffixes = null; linkBrowserMediaSuffixes = null; linkBrowserFlashPlayerSuffixes = null; linkBrowserBaseContainer = null; linkBrowserCustomTreeModel = null; + toolLinkTreeModel = null; nonQuotedConfigValues.remove(FILE_BROWSER_CALLBACK); } /** * Set an optional path relative to the vfs container of the file browser - * callback that is used as the upload destination when a user uploads a - * file and you don't whant the file to be uploaded into the vfs container - * itself but rather in another directory, e.g. a special media directory. + * callback that is used as the upload destination when a user uploads a file + * and you don't whant the file to be uploaded into the vfs container itself but + * rather in another directory, e.g. a special media directory. * * @param linkBrowserUploadRelPath */ public void setFileBrowserUploadRelPath(String linkBrowserUploadRelPath) { this.linkBrowserUploadRelPath = linkBrowserUploadRelPath; } - + /** * @return The URI to the mapper or null if there isn't any mapper. */ @@ -796,27 +806,25 @@ public class RichTextConfiguration implements Disposable { } /** - * Set the documents media base that is used to deliver media files - * referenced by the content. + * Set the documents media base that is used to deliver media files referenced + * by the content. * - * @param documentBaseContainer - * the vfs container that contains the media files - * @param relFilePath - * The file path of the HTML file relative to the - * documentBaseContainer - * @param usess - * The user session + * @param documentBaseContainer the vfs container that contains the media files + * @param relFilePath The file path of the HTML file relative to the + * documentBaseContainer + * @param usess The user session */ private void setDocumentMediaBase(final VFSContainer documentBaseContainer, String relFilePath, UserSession usess) { linkBrowserRelativeFilePath = relFilePath; - // get a usersession-local mapper for the file storage (and tinymce's references to images and such) + // get a usersession-local mapper for the file storage (and tinymce's references + // to images and such) Mapper contentMapper; - if(StringHelper.containsNonWhitespace(relFilePath)) { + if (StringHelper.containsNonWhitespace(relFilePath)) { contentMapper = new RichTextContainerMapper(documentBaseContainer, relFilePath); } else { contentMapper = new VFSContainerMapper(documentBaseContainer); } - + // Register mapper for this user. This mapper is cleaned up in the // dispose method (RichTextElementImpl will clean it up) // Register mapper as cacheable @@ -828,9 +836,9 @@ public class RichTextConfiguration implements Disposable { // Add classname to the file path to remove conflicts with other // usages of the same file path mapperID = this.getClass().getSimpleName() + ":" + mapperID + ":" + CodeHelper.getRAMUniqueID(); - contentMapperKey = CoreSpringFactory.getImpl(MapperService.class).register(usess, mapperID, contentMapper); + contentMapperKey = CoreSpringFactory.getImpl(MapperService.class).register(usess, mapperID, contentMapper); } - + if (relFilePath != null) { // remove filename, path must end with slash int lastSlash = relFilePath.lastIndexOf('/'); @@ -855,14 +863,12 @@ public class RichTextConfiguration implements Disposable { mapperUri = contentMapperKey.getUrl() + "/" + relFilePath; setQuotedConfigValue(DOCUMENT_BASE_URL, mapperUri); } - + /** * Set a tiny configuration value that must be quoted with double quotes * - * @param key - * The configuration key - * @param value - * The configuration value + * @param key The configuration key + * @param value The configuration value */ private void setQuotedConfigValue(String key, String value) { // remove non-quoted config values with same key @@ -872,36 +878,37 @@ public class RichTextConfiguration implements Disposable { // add or overwrite new value quotedConfigValues.put(key, value); } - + public void setInvalidElements(String elements) { setQuotedConfigValue(RichTextConfiguration.INVALID_ELEMENTS, elements); } - + public void setExtendedValidElements(String elements) { setQuotedConfigValue(RichTextConfiguration.EXTENDED_VALID_ELEMENTS, elements); } - + public boolean isMathEnabled() { return tinyConfig.isMathEnabled(); } - + public void enableCode() { tinyConfig = tinyConfig.enableCode(); } - + public void enableCharCount() { tinyConfig = tinyConfig.enableCharcount(); } - + public void enableQTITools(boolean textEntry, boolean numericalInput, boolean hottext) { tinyConfig = tinyConfig.enableQTITools(textEntry, numericalInput, hottext); setQuotedConfigValue("custom_elements", "~textentryinteraction,~hottext"); setQuotedConfigValue(EXTENDED_VALID_ELEMENTS, "script[src|type|defer],textentryinteraction[*],hottext[*]"); } - + public void enableEdusharing(Identity identity, EdusharingProvider provider) { - if (identity == null || provider == null) return; - + if (identity == null || provider == null) + return; + EdusharingModule edusharingModule = CoreSpringFactory.getImpl(EdusharingModule.class); if (edusharingModule.isEnabled()) { tinyConfig = tinyConfig.enableEdusharing(); @@ -909,7 +916,7 @@ public class RichTextConfiguration implements Disposable { addValueFilter(filter); } } - + public EdusharingFilter getEdusharingFilter() { for (Filter filter : valueFilters) { if (filter instanceof EdusharingFilter) { @@ -918,35 +925,34 @@ public class RichTextConfiguration implements Disposable { } return null; } - + public void addValueFilter(Filter filter) { valueFilters.add(filter); } - + Collection<Filter> getValueFilters() { return valueFilters; } /** - * Set a tiny configuration value that must not be quoted with quotes, e.g. - * JS function references or boolean values + * Set a tiny configuration value that must not be quoted with quotes, e.g. JS + * function references or boolean values * - * @param key - * The configuration key - * @param value - * The configuration value + * @param key The configuration key + * @param value The configuration value */ private void setNonQuotedConfigValue(String key, String value) { // remove quoted config values with same key - if (quotedConfigValues.containsKey(key)) quotedConfigValues.remove(key); + if (quotedConfigValues.containsKey(key)) + quotedConfigValues.remove(key); // add or overwrite new value nonQuotedConfigValues.put(key, value); } - + public void enableEditorHeight() { setNonQuotedConfigValue(RichTextConfiguration.HEIGHT, "b_initialEditorHeight()"); } - + /** * @return True if the fig caption for image is enabled. */ @@ -956,6 +962,7 @@ public class RichTextConfiguration implements Disposable { /** * Enable or disable fig caption for image. + * * @param figCaption */ public void setFigCaption(boolean figCaption) { @@ -992,9 +999,10 @@ public class RichTextConfiguration implements Disposable { public String[] getLinkBrowserMediaSuffixes() { return linkBrowserMediaSuffixes; } - + /** * Get the formats supported by the flash player + * * @return */ public String[] getLinkBrowserFlashPlayerSuffixes() { @@ -1012,6 +1020,7 @@ public class RichTextConfiguration implements Disposable { /** * Get the upload dir relative to the file browser + * * @return */ public String getLinkBrowserUploadRelPath() { @@ -1019,17 +1028,16 @@ public class RichTextConfiguration implements Disposable { } /** - * Get the relative file path in relation to the browser base container or - * an empty string when on same level as base container (e.g. in form and - * not file mode) or NULL when the link browser and base container are not - * set at all + * Get the relative file path in relation to the browser base container or an + * empty string when on same level as base container (e.g. in form and not file + * mode) or NULL when the link browser and base container are not set at all * * @return */ public String getLinkBrowserRelativeFilePath() { return linkBrowserRelativeFilePath; } - + public String getLinkBrowserAbsolutFilePath() { return linkBrowserAbsolutFilePath; } @@ -1040,25 +1048,30 @@ public class RichTextConfiguration implements Disposable { /** * Get the optional custom link browser tree model + * * @return the model or NULL if not defined */ public CustomLinkTreeModel getLinkBrowserCustomLinkTreeModel() { return linkBrowserCustomTreeModel; } + public CustomLinkTreeModel getToolLinkTreeModel() { + return toolLinkTreeModel; + } + protected void appendConfigToTinyJSArray_4(StringOutput out, Translator translator) { // Now add the quoted values - Map<String,String> copyValues = new HashMap<>(quotedConfigValues); + Map<String, String> copyValues = new HashMap<>(quotedConfigValues); // Now add the non-quoted values (e.g. true, false or functions) - Map<String,String> copyNonValues = new HashMap<>(nonQuotedConfigValues); + Map<String, String> copyNonValues = new HashMap<>(nonQuotedConfigValues); String converter = copyNonValues.get(URLCONVERTER_CALLBACK); - if(converter != null) { + if (converter != null) { copyNonValues.put(CONVERT_URLS, "true"); } - + String contentCss = copyValues.remove(CONTENT_CSS); - if(contentCss != null) { + if (contentCss != null) { // add styles from content css and add them to format menu copyNonValues.put(IMPORTCSS_APPEND, "true"); copyValues.put("content_css", contentCss); @@ -1071,102 +1084,90 @@ public class RichTextConfiguration implements Disposable { if (selectorFilter != null) { if (selectorFilter.startsWith("/") && selectorFilter.endsWith("/")) { // a (multi) prefix filter witten as JS regexp pattern - copyNonValues.put(IMPORTCSS_SELECTOR_FILTER, selectorFilter); + copyNonValues.put(IMPORTCSS_SELECTOR_FILTER, selectorFilter); } else { // a simple prefix filter without JS regexp syntax - copyValues.put(IMPORTCSS_SELECTOR_FILTER, selectorFilter); - } + copyValues.put(IMPORTCSS_SELECTOR_FILTER, selectorFilter); + } } } - //new with menu - StringOutput tinyMenuSb = new StringOutput(); - tinyMenuSb.append("plugins: '").append(tinyConfig.getPlugins()).append("',\n") - .append("image_advtab:true,\n") - .append("image_caption:").append(figCaption).append(",\n") - .append("image_title:true,\n") - .append("relative_urls:").append(isRelativeUrls()).append(",\n") - .append("remove_script_host:").append(isRemoveScriptHost()).append(",\n") - .append("statusbar:").append(true).append(",\n") - .append("resize:").append(true).append(",\n") - .append("menubar:").append(tinyConfig.hasMenu()).append(",\n") - .append("font_formats:").append("'Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva',\n") - ; - if(isReadOnly()) { - tinyMenuSb.append("readonly: 1,\n"); - } - - String leftAndClear = "Left and clear"; - String rightAndClear = "Right and clear"; - String leftAndClearNomargin = "Left with caption"; - if(translator != null) { - translator = Util.createPackageTranslator(RichTextConfiguration.class, translator.getLocale(), translator); - leftAndClear = translator.translate("left.clear"); - rightAndClear = translator.translate("right.clear"); - leftAndClearNomargin = translator.translate("left.clear.nomargin"); - } - - tinyMenuSb.append("image_class_list: [\n") - .append(" {title: 'Left', value: 'b_float_left'},\n") - .append(" {title: '").append(leftAndClear).append("', value: 'b_float_left_clear'},\n") - .append(" {title: '").append(leftAndClearNomargin).append("', value: 'b_float_left_clear_nomargin'},\n") - .append(" {title: 'Center', value: 'b_centered'},\n") - .append(" {title: 'Right', value: 'b_float_right'},\n") - .append(" {title: '").append(rightAndClear).append("', value: 'b_float_right_clear'},\n") - .append(" {title: 'Circle', value: 'b_circle'},\n") - .append(" {title: 'Border', value: 'b_with_border'}\n") - .append("],\n"); - tinyMenuSb.append("link_class_list: [\n") - .append(" {title: '', value: ''},\n") - .append(" {title: 'Extern', value: 'b_link_extern'},\n") - .append(" {title: 'Mail', value: 'b_link_mailto'},\n") - .append(" {title: 'Forward', value: 'b_link_forward'}\n") - .append("],\n"); - // predefined table styles selectable in a menu - tinyMenuSb.append("table_class_list: [\n") - .append(" {title: 'No style', value: ''},\n") - .append(" {title: 'Default', value: 'b_default'},\n") - .append(" {title: 'Borderless', value: 'b_borderless'},\n") - .append(" {title: 'Grid', value: 'b_grid'},\n") - .append(" {title: 'Border', value: 'b_border'},\n") - .append(" {title: 'Full', value: 'b_full'},\n") - .append(" {title: 'Middle', value: 'b_middle'},\n") - .append(" {title: 'Gray', value: 'b_gray'},\n") - .append(" {title: 'Red', value: 'b_red'},\n") - .append(" {title: 'Green', value: 'b_green'},\n") - .append(" {title: 'Blue', value: 'b_blue'},\n") - .append(" {title: 'Yellow', value: 'b_yellow'}\n") - .append("],\n"); - - // default table style - tinyMenuSb.append("table_default_attributes: { class: 'b_default' },\n"); - + // new with menu + StringOutput tinyMenuSb = new StringOutput(); + tinyMenuSb.append("plugins: '").append(tinyConfig.getPlugins()).append("',\n").append("image_advtab:true,\n") + .append("image_caption:").append(figCaption).append(",\n").append("image_title:true,\n") + .append("relative_urls:").append(isRelativeUrls()).append(",\n").append("remove_script_host:") + .append(isRemoveScriptHost()).append(",\n").append("statusbar:").append(true).append(",\n") + .append("resize:").append(true).append(",\n").append("menubar:").append(tinyConfig.hasMenu()) + .append(",\n").append("font_formats:") + .append("'Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva',\n"); + if (isReadOnly()) { + tinyMenuSb.append("readonly: 1,\n"); + } + + String leftAndClear = "Left and clear"; + String rightAndClear = "Right and clear"; + String leftAndClearNomargin = "Left with caption"; + if (translator != null) { + translator = Util.createPackageTranslator(RichTextConfiguration.class, translator.getLocale(), translator); + leftAndClear = translator.translate("left.clear"); + rightAndClear = translator.translate("right.clear"); + leftAndClearNomargin = translator.translate("left.clear.nomargin"); + } + + tinyMenuSb.append("image_class_list: [\n").append(" {title: 'Left', value: 'b_float_left'},\n") + .append(" {title: '").append(leftAndClear).append("', value: 'b_float_left_clear'},\n") + .append(" {title: '").append(leftAndClearNomargin) + .append("', value: 'b_float_left_clear_nomargin'},\n") + .append(" {title: 'Center', value: 'b_centered'},\n") + .append(" {title: 'Right', value: 'b_float_right'},\n").append(" {title: '").append(rightAndClear) + .append("', value: 'b_float_right_clear'},\n").append(" {title: 'Circle', value: 'b_circle'},\n") + .append(" {title: 'Border', value: 'b_with_border'}\n").append("],\n"); + tinyMenuSb.append("link_class_list: [\n").append(" {title: '', value: ''},\n") + .append(" {title: 'Extern', value: 'b_link_extern'},\n") + .append(" {title: 'Mail', value: 'b_link_mailto'},\n") + .append(" {title: 'Forward', value: 'b_link_forward'}\n").append("],\n"); + // predefined table styles selectable in a menu + tinyMenuSb.append("table_class_list: [\n").append(" {title: 'No style', value: ''},\n") + .append(" {title: 'Default', value: 'b_default'},\n") + .append(" {title: 'Borderless', value: 'b_borderless'},\n") + .append(" {title: 'Grid', value: 'b_grid'},\n").append(" {title: 'Border', value: 'b_border'},\n") + .append(" {title: 'Full', value: 'b_full'},\n").append(" {title: 'Middle', value: 'b_middle'},\n") + .append(" {title: 'Gray', value: 'b_gray'},\n").append(" {title: 'Red', value: 'b_red'},\n") + .append(" {title: 'Green', value: 'b_green'},\n").append(" {title: 'Blue', value: 'b_blue'},\n") + .append(" {title: 'Yellow', value: 'b_yellow'}\n").append("],\n"); + + // default table style + tinyMenuSb.append("table_default_attributes: { class: 'b_default' },\n"); + if (tinyConfig.getTool1() != null) { tinyMenuSb.append("toolbar1: '").append(tinyConfig.getTool1()).append("',\n"); } else { tinyMenuSb.append("toolbar:false,\n"); } - - if(tinyConfig.hasMenu()) { + + if (tinyConfig.hasMenu()) { tinyMenuSb.append("menu:{\n"); boolean first = true; - for (String menuItem: tinyConfig.getMenu()) { - if(!first) tinyMenuSb.append("\n,"); - if(first) first = false; + for (String menuItem : tinyConfig.getMenu()) { + if (!first) + tinyMenuSb.append("\n,"); + if (first) + first = false; tinyMenuSb.append(menuItem); } tinyMenuSb.append("\n},\n"); } else { tinyMenuSb.append("menu:{},\n"); } - + for (Map.Entry<String, String> entry : copyValues.entrySet()) { tinyMenuSb.append(entry.getKey()).append(": \"").append(entry.getValue()).append("\",\n"); } - for (Map.Entry<String, String> entry : copyNonValues.entrySet()) { - tinyMenuSb.append(entry.getKey()).append(": ").append(entry.getValue()).append(",\n"); + for (Map.Entry<String, String> entry : copyNonValues.entrySet()) { + tinyMenuSb.append(entry.getKey()).append(": ").append(entry.getValue()).append(",\n"); } - out.append(tinyMenuSb); + out.append(tinyMenuSb); } /** @@ -1177,6 +1178,6 @@ public class RichTextConfiguration implements Disposable { if (contentMapperKey != null) { CoreSpringFactory.getImpl(MapperService.class).cleanUp(Collections.singletonList(contentMapperKey)); contentMapperKey = null; - } + } } } \ No newline at end of file diff --git a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/richText/RichTextElementComponent.java b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/richText/RichTextElementComponent.java index f99713e078f87184f6dd61103632cc19e907df7b..91b91a031011af925f4cda46eede1ab1c2a0e406 100644 --- a/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/richText/RichTextElementComponent.java +++ b/src/main/java/org/olat/core/gui/components/form/flexible/impl/elements/richText/RichTextElementComponent.java @@ -178,12 +178,13 @@ class RichTextElementComponent extends FormBaseComponentImpl { String uploadRelPath = config.getLinkBrowserUploadRelPath(); String absolutePath = config.getLinkBrowserAbsolutFilePath(); CustomLinkTreeModel linkBrowserCustomTreeModel = config.getLinkBrowserCustomLinkTreeModel(); + CustomLinkTreeModel toolLinkTreeModel = config.getToolLinkTreeModel(); if (type.equals(CMD_FILEBROWSER)) { // when in file mode we include the internal links to the selection - myLinkChooserController = new LinkChooserController(lureq, lwControl, baseContainer, uploadRelPath, absolutePath, suffixes, uriValidation, fileName, linkBrowserCustomTreeModel, allowCustomMediaFactory); + myLinkChooserController = new LinkChooserController(lureq, lwControl, baseContainer, uploadRelPath, absolutePath, suffixes, uriValidation, fileName, linkBrowserCustomTreeModel, toolLinkTreeModel, allowCustomMediaFactory); } else { // in media or image mode, internal links make no sense here - myLinkChooserController = new LinkChooserController(lureq, lwControl, baseContainer, uploadRelPath, absolutePath, suffixes, uriValidation, fileName, null, allowCustomMediaFactory); + myLinkChooserController = new LinkChooserController(lureq, lwControl, baseContainer, uploadRelPath, absolutePath, suffixes, uriValidation, fileName, null, null, allowCustomMediaFactory); } return new LayoutMain3ColsController(lureq, lwControl, myLinkChooserController); } diff --git a/src/main/java/org/olat/course/nodes/STCourseNode.java b/src/main/java/org/olat/course/nodes/STCourseNode.java index 9c058862edeb6f35be632dd4ab77739b67c69eb9..4baff4da0b3665b19d799f397ef7d864f31337d0 100644 --- a/src/main/java/org/olat/course/nodes/STCourseNode.java +++ b/src/main/java/org/olat/course/nodes/STCourseNode.java @@ -74,6 +74,7 @@ import org.olat.course.run.navigation.NodeRunConstructionResult; import org.olat.course.run.scoring.FailedEvaluationType; import org.olat.course.run.scoring.ScoreCalculator; import org.olat.course.run.scoring.ScoreEvaluation; +import org.olat.course.run.tools.CourseToolLinkTreeModel; import org.olat.course.run.userview.CourseNodeSecurityCallback; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.course.tree.CourseEditorTreeNode; @@ -192,6 +193,7 @@ public class STCourseNode extends AbstractAccessableCourseNode { // set the link tree model to internal for the HTML editor CustomLinkTreeModel linkTreeModel = new CourseInternalLinkTreeModel(userCourseEnv.getCourseEnvironment().getRunStructure().getRootNode()); spCtr.setInternalLinkTreeModel(linkTreeModel); + spCtr.setToolLinkTreeModel(new CourseToolLinkTreeModel(userCourseEnv.getCourseEnvironment().getCourseConfig(), ureq.getLocale())); } spCtr.addLoggingResourceable(LoggingResourceable.wrap(this)); // create clone wrapper layout, allow popping into second window diff --git a/src/main/java/org/olat/course/nodes/dialog/ui/DialogCourseNodeRunController.java b/src/main/java/org/olat/course/nodes/dialog/ui/DialogCourseNodeRunController.java index 7340be602fc7066343a241039e84af81fef882a4..c8849c996b39640517fe57e73dac99af708bfaa3 100644 --- a/src/main/java/org/olat/course/nodes/dialog/ui/DialogCourseNodeRunController.java +++ b/src/main/java/org/olat/course/nodes/dialog/ui/DialogCourseNodeRunController.java @@ -346,7 +346,7 @@ public class DialogCourseNodeRunController extends BasicController implements Ac private class MyLinkChooserController extends LinkChooserController { public MyLinkChooserController(UserRequest ureq, WindowControl wControl, VFSContainer rootDir, String uploadRelPath) { - super(ureq, wControl, rootDir, uploadRelPath, null, null, false, "", null, true); + super(ureq, wControl, rootDir, uploadRelPath, null, null, false, "", null, null, true); } @Override diff --git a/src/main/java/org/olat/course/nodes/iq/IQLayoutConfigurationController.java b/src/main/java/org/olat/course/nodes/iq/IQLayoutConfigurationController.java index 78e4845a518f91e73230836369093a87bf639393..b2a33e3118afe8dd939c35439fec4965e8cc8d57 100644 --- a/src/main/java/org/olat/course/nodes/iq/IQLayoutConfigurationController.java +++ b/src/main/java/org/olat/course/nodes/iq/IQLayoutConfigurationController.java @@ -104,7 +104,7 @@ public class IQLayoutConfigurationController extends BasicController { } combiLinkCtr = new LinkFileCombiCalloutController(ureq, wControl, courseFolderBaseContainer, disclaimer, relFilPathIsProposal, allowRelativeLinks, true, - new CourseInternalLinkTreeModel(course.getEditorTreeModel()), null); + new CourseInternalLinkTreeModel(course.getEditorTreeModel()), null, null); listenTo(combiLinkCtr); myContent.put("combiCtr", combiLinkCtr.getInitialComponent()); myContent.contextPut("editorEnabled", combiLinkCtr.isEditorEnabled()); diff --git a/src/main/java/org/olat/course/nodes/sp/SPEditController.java b/src/main/java/org/olat/course/nodes/sp/SPEditController.java index fc006b6f9b573cc2b99f448220aec2600e4703a5..f00a1e9b000ffd543e87ddecba8522fb52668965 100644 --- a/src/main/java/org/olat/course/nodes/sp/SPEditController.java +++ b/src/main/java/org/olat/course/nodes/sp/SPEditController.java @@ -46,6 +46,7 @@ import org.olat.course.editor.NodeEditController; import org.olat.course.folder.CourseContainerOptions; import org.olat.course.nodes.SPCourseNode; import org.olat.course.run.environment.CourseEnvironment; +import org.olat.course.run.tools.CourseToolLinkTreeModel; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.course.tree.CourseInternalLinkTreeModel; import org.olat.modules.ModuleConfiguration; @@ -123,9 +124,10 @@ public class SPEditController extends ActivateableTabbableDefaultController impl // File create/select controller Long repoKey = course.getCourseEnvironment().getCourseGroupManager().getCourseEntry().getKey(); VFSEdusharingProvider edusharingProvider = new LazyRepositoryEdusharingProvider(repoKey); - combiLinkCtr = new LinkFileCombiCalloutController(ureq, wControl, courseFolderBaseContainer, - relFilePath, relFilPathIsProposal, allowRelativeLinks, false, - new CourseInternalLinkTreeModel(course.getEditorTreeModel()), edusharingProvider); + combiLinkCtr = new LinkFileCombiCalloutController(ureq, wControl, courseFolderBaseContainer, relFilePath, + relFilPathIsProposal, allowRelativeLinks, false, + new CourseInternalLinkTreeModel(course.getEditorTreeModel()), + new CourseToolLinkTreeModel(course.getCourseConfig(), getLocale()), edusharingProvider); combiLinkCtr.setEditable(hasEditRights(relFilePath)); listenTo(combiLinkCtr); myContent.put("combiCtr", combiLinkCtr.getInitialComponent()); diff --git a/src/main/java/org/olat/course/nodes/sp/SPRunController.java b/src/main/java/org/olat/course/nodes/sp/SPRunController.java index 7bb3d75e308fe7a29438840dc2b62e30a06af069..92bcac4663c81156ccdd16cbdd73141171b6181d 100644 --- a/src/main/java/org/olat/course/nodes/sp/SPRunController.java +++ b/src/main/java/org/olat/course/nodes/sp/SPRunController.java @@ -59,6 +59,7 @@ import org.olat.course.groupsandrights.CourseGroupManager; import org.olat.course.groupsandrights.CourseRights; import org.olat.course.nodes.SPCourseNode; import org.olat.course.nodes.TitledWrapperHelper; +import org.olat.course.run.tools.CourseToolLinkTreeModel; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.course.tree.CourseInternalLinkTreeModel; import org.olat.modules.ModuleConfiguration; @@ -88,6 +89,7 @@ public class SPRunController extends BasicController implements Activateable2 { private final boolean hasEditRights; private CustomLinkTreeModel linkTreeModel; + private CustomLinkTreeModel toolLinkTreeModel; private Long repoKey; private final UserCourseEnvironment userCourseEnv; @@ -121,6 +123,7 @@ public class SPRunController extends BasicController implements Activateable2 { if (hasEditRights) { linkTreeModel = new CourseInternalLinkTreeModel(userCourseEnv.getCourseEnvironment().getRunStructure().getRootNode()); + toolLinkTreeModel = new CourseToolLinkTreeModel(userCourseEnv.getCourseEnvironment().getCourseConfig(), getLocale()); } // init main panel and do start page or direct launch @@ -203,6 +206,9 @@ public class SPRunController extends BasicController implements Activateable2 { if (linkTreeModel != null) { spCtr.setInternalLinkTreeModel(linkTreeModel); } + if (toolLinkTreeModel != null) { + spCtr.setToolLinkTreeModel(toolLinkTreeModel); + } } // create clone wrapper layout diff --git a/src/main/java/org/olat/course/nodes/st/STCourseNodeEditController.java b/src/main/java/org/olat/course/nodes/st/STCourseNodeEditController.java index fb3b89e6a58c7f69ba8329cbb183448ff155d0e6..4fb8534fac7b2867f654b4e8c20043df0be50381 100644 --- a/src/main/java/org/olat/course/nodes/st/STCourseNodeEditController.java +++ b/src/main/java/org/olat/course/nodes/st/STCourseNodeEditController.java @@ -52,6 +52,7 @@ import org.olat.course.assessment.CourseAssessmentService; import org.olat.course.assessment.handler.AssessmentConfig; import org.olat.course.assessment.handler.AssessmentConfig.Mode; import org.olat.course.condition.Condition; +import org.olat.course.config.CourseConfig; import org.olat.course.editor.CourseEditorHelper; import org.olat.course.editor.NodeEditController; import org.olat.course.highscore.ui.HighScoreEditController; @@ -61,6 +62,7 @@ import org.olat.course.nodes.CourseNode; import org.olat.course.nodes.STCourseNode; import org.olat.course.nodes.sp.SecuritySettingsForm; import org.olat.course.run.scoring.ScoreCalculator; +import org.olat.course.run.tools.CourseToolLinkTreeModel; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.course.tree.CourseEditorTreeModel; import org.olat.course.tree.CourseInternalLinkTreeModel; @@ -122,6 +124,7 @@ public class STCourseNodeEditController extends ActivateableTabbableDefaultContr private Link activateEasyModeButton; private Link activateExpertModeButton; + private final CourseConfig courseConfig; private VFSContainer courseFolderContainer; private String chosenFile; private boolean allowRelativeLinks; @@ -148,6 +151,7 @@ public class STCourseNodeEditController extends ActivateableTabbableDefaultContr super(ureq, wControl); this.stNode = stNode; this.courseFolderContainer = course.getCourseFolderContainer(); + this.courseConfig = course.getCourseConfig(); this.euce = euce; this.editorModel = course.getEditorTreeModel(); this.repoKey = RepositoryManager.getInstance().lookupRepositoryEntryKey(course, true); @@ -385,9 +389,9 @@ public class STCourseNodeEditController extends ActivateableTabbableDefaultContr } // File create/select controller VFSEdusharingProvider edusharingProvider = new LazyRepositoryEdusharingProvider(repoKey); - combiLinkCtr = new LinkFileCombiCalloutController(ureq, getWindowControl(), courseFolderContainer, - relFilePath, relFilPathIsProposal, allowRelativeLinks, false, - new CourseInternalLinkTreeModel(editorModel), edusharingProvider); + combiLinkCtr = new LinkFileCombiCalloutController(ureq, getWindowControl(), courseFolderContainer, relFilePath, + relFilPathIsProposal, allowRelativeLinks, false, new CourseInternalLinkTreeModel(editorModel), + new CourseToolLinkTreeModel(courseConfig, getLocale()), edusharingProvider); listenTo(combiLinkCtr); configvc.put("combiCtr", combiLinkCtr.getInitialComponent()); configvc.contextPut("editorEnabled", combiLinkCtr.isEditorEnabled()); diff --git a/src/main/java/org/olat/course/run/tools/CourseToolLinkTreeModel.java b/src/main/java/org/olat/course/run/tools/CourseToolLinkTreeModel.java index 0ea3f18a7c9d7b1473ad7c40474314aebcf1fde9..682b7e8ffa0cd13645286af127b05856d5f82568 100644 --- a/src/main/java/org/olat/course/run/tools/CourseToolLinkTreeModel.java +++ b/src/main/java/org/olat/course/run/tools/CourseToolLinkTreeModel.java @@ -28,6 +28,8 @@ import org.olat.core.gui.components.tree.GenericTreeNode; import org.olat.core.gui.components.tree.TreeNode; import org.olat.core.gui.translator.Translator; import org.olat.core.util.Util; +import org.olat.course.config.CourseConfig; +import org.olat.course.learningpath.manager.LearningPathNodeAccessProvider; import org.olat.course.run.CourseRuntimeController; /** @@ -45,20 +47,44 @@ public class CourseToolLinkTreeModel extends CustomLinkTreeModel { private final Translator translator; private TreeNode rootNode; - public CourseToolLinkTreeModel(Locale locale) { + public CourseToolLinkTreeModel(CourseConfig courseConfig, Locale locale) { super("toollinktreenode"); translator = Util.createPackageTranslator(CourseRuntimeController.class, locale); - buildTree(); + buildTree(courseConfig); } - private void buildTree() { + private void buildTree(CourseConfig courseConfig) { rootNode = createTreeNode(ROOT, translator.translate("tool.link.root")); List<TreeNode> toolNodes = new ArrayList<>(CourseTool.values().length); - for (CourseTool courseTool : CourseTool.values()) { - //TODO uh Leistungsnachweis verlinken - TreeNode node = createTreeNode(courseTool); - toolNodes.add(node); + + if (courseConfig.isBlogEnabled()) { + toolNodes.add(createTreeNode(CourseTool.blog)); } + if (courseConfig.isDocumentsEnabled()) { + toolNodes.add(createTreeNode(CourseTool.documents)); + } + if (courseConfig.isEfficencyStatementEnabled() || courseConfig.isCertificateEnabled()) { + toolNodes.add(createTreeNode(CourseTool.efficiencystatement)); + } + if (courseConfig.isEmailEnabled()) { + toolNodes.add(createTreeNode(CourseTool.email)); + } + if (courseConfig.isForumEnabled()) { + toolNodes.add(createTreeNode(CourseTool.forum)); + } + if (courseConfig.isParticipantListEnabled()) { + toolNodes.add(createTreeNode(CourseTool.participantlist)); + } + if (courseConfig.isParticipantInfoEnabled()) { + toolNodes.add(createTreeNode(CourseTool.participantinfos)); + } + if (LearningPathNodeAccessProvider.TYPE.equals(courseConfig.getNodeAccessType().getType())) { + toolNodes.add(createTreeNode(CourseTool.learningpath)); + } + if (courseConfig.isWikiEnabled()) { + toolNodes.add(createTreeNode(CourseTool.wiki)); + } + toolNodes.sort((n1, n2) -> n1.getTitle().compareToIgnoreCase(n2.getTitle())); for (TreeNode node: toolNodes) { rootNode.addChild(node); @@ -76,7 +102,7 @@ public class CourseToolLinkTreeModel extends CustomLinkTreeModel { treeNode.setTitle(title); return treeNode; } - + @Override public TreeNode getRootNode() { return rootNode;