/**
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at the
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Initial code contributed and copyrighted by<br>
* frentix GmbH, http://www.frentix.com
* <p>
*/
package org.olat.course.nodes.video;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.form.flexible.FormItem;
import org.olat.core.gui.components.form.flexible.FormItemContainer;
import org.olat.core.gui.components.form.flexible.FormUIFactory;
import org.olat.core.gui.components.form.flexible.elements.RichTextElement;
import org.olat.core.gui.components.form.flexible.elements.SelectionElement;
import org.olat.core.gui.components.form.flexible.elements.SingleSelection;
import org.olat.core.gui.components.form.flexible.elements.StaticTextElement;
import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
import org.olat.core.gui.components.form.flexible.impl.FormEvent;
import org.olat.core.gui.components.link.Link;
import org.olat.core.gui.components.link.LinkFactory;
import org.olat.core.gui.components.panel.Panel;
import org.olat.core.gui.components.tabbedpane.TabbedPane;
import org.olat.core.gui.components.velocity.VelocityContainer;
import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.ControllerEventListener;
import org.olat.core.gui.control.Event;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController;
import org.olat.core.gui.control.generic.tabbable.ActivateableTabbableDefaultController;
import org.olat.core.logging.AssertException;
import org.olat.core.util.Formatter;
import org.olat.core.util.StringHelper;
import org.olat.core.util.filter.FilterFactory;
import org.olat.core.util.vfs.VFSContainer;
import org.olat.core.util.vfs.VFSContainerMapper;
import org.olat.course.ICourse;
import org.olat.course.assessment.AssessmentHelper;
import org.olat.course.condition.Condition;
import org.olat.course.condition.ConditionEditController;
import org.olat.course.editor.NodeEditController;
import org.olat.course.nodes.VideoCourseNode;
import org.olat.course.run.userview.UserCourseEnvironment;
import org.olat.fileresource.types.VideoFileResource;
import org.olat.modules.ModuleConfiguration;
import org.olat.modules.video.VideoManager;
import org.olat.modules.video.ui.VideoDisplayController;
import org.olat.repository.RepositoryEntry;
import org.olat.repository.RepositoryManager;
import org.olat.repository.controllers.ReferencableEntriesSearchController;
import org.olat.repository.handlers.RepositoryHandler;
import org.olat.repository.handlers.RepositoryHandlerFactory;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Editcontroller of videonode
*
* @author dfakae, dirk.furrer@frentix.com, http://www.frentix.com
*
*/
public class VideoEditController extends ActivateableTabbableDefaultController implements ControllerEventListener {
public static final String PANE_TAB_VIDEOCONFIG = "pane.tab.videoconfig";
private static final String PANE_TAB_ACCESSIBILITY = "pane.tab.accessibility";
final static String[] paneKeys = { PANE_TAB_VIDEOCONFIG, PANE_TAB_ACCESSIBILITY };
public static final String NLS_ERROR_VIDEOREPOENTRYMISSING = "error.videorepoentrymissing";
private static final String NLS_CONDITION_ACCESSIBILITY_TITLE = "condition.accessibility.title";
private static final String NLS_COMMAND_CHOOSEVIDEO = "command.choosevideo";
private static final String NLS_COMMAND_CREATEVID = "command.createvideo";
private static final String NLS_COMMAND_CHANGEVID = "command.changevideo";
public static final String CONFIG_KEY_REPOSITORY_SOFTKEY = "reporef";
public static final String CONFIG_KEY_AUTOPLAY = "autoplay";
public static final String CONFIG_KEY_COMMENTS = "comments";
public static final String CONFIG_KEY_RATING = "rating";
public static final String CONFIG_KEY_DESCRIPTION_SELECT = "descriptionSelect";
public static final String CONFIG_KEY_DESCRIPTION_CUSTOMTEXT = "descriptionText";
private static final String VC_CHOSENVIDEO = "chosenvideo";
private static final String NLS_NO_VIDEO_CHOSEN = "no.video.chosen";
protected FormUIFactory uifactory = FormUIFactory.getInstance();
private Panel main;
private VelocityContainer videoConfigurationVc;
private ModuleConfiguration config;
private final VideoCourseNode videoNode;
private RepositoryEntry repositoryEntry;
private VideoOptionsForm videoOptionsCtrl;
private ReferencableEntriesSearchController searchController;
private Link previewLink;
private Link chooseVideoButton;
private Link changeVideoButton;
private ConditionEditController accessibilityCondContr;
private Controller previewCtr;
private TabbedPane myTabbedPane;
private CloseableModalController cmc;
public VideoEditController(VideoCourseNode videoNode, UserRequest ureq, WindowControl wControl, ICourse course, UserCourseEnvironment euce) {
super(ureq, wControl);
this.config = videoNode.getModuleConfiguration();
this.videoNode = videoNode;
main = new Panel("videomain");
videoConfigurationVc = createVelocityContainer("edit");
chooseVideoButton = LinkFactory.createButtonSmall(NLS_COMMAND_CREATEVID, videoConfigurationVc, this);
chooseVideoButton.setElementCssClass("o_sel_cp_choose_repofile");
changeVideoButton = LinkFactory.createButtonSmall(NLS_COMMAND_CHANGEVID, videoConfigurationVc, this);
changeVideoButton.setElementCssClass("o_sel_cp_change_repofile");
if (config.get(CONFIG_KEY_REPOSITORY_SOFTKEY) != null) {
// fetch repository entry to display the repository entry title of
// the chosen cp
repositoryEntry = getVideoReference(config, false);
if (repositoryEntry == null) {
// we cannot display the entries name, because the repository
// entry had been deleted between the time when it was chosen
// here, and now
showError(NLS_ERROR_VIDEOREPOENTRYMISSING);
videoConfigurationVc.contextPut("showPreviewButton", Boolean.FALSE);
videoConfigurationVc.contextPut(VC_CHOSENVIDEO, translate("no.video.chosen"));
} else {
videoConfigurationVc.contextPut("showPreviewButton", Boolean.TRUE);
String displayname = StringHelper.escapeHtml(repositoryEntry.getDisplayname());
previewLink = LinkFactory.createCustomLink("command.preview", "command.preview", displayname, Link.NONTRANSLATED, videoConfigurationVc, this);
previewLink.setTitle(getTranslator().translate("command.preview"));
previewLink.setIconLeftCSS("o_icon o_icon-fw o_icon_preview");
previewLink.setEnabled(true);
}
videoConfigurationVc.contextPut("showOptions", Boolean.TRUE);
VideoOptionsForm videoOptions = new VideoOptionsForm(ureq, getWindowControl(), repositoryEntry, config);
videoConfigurationVc.put("videoOptions", videoOptions.getInitialComponent());
listenTo(videoOptions);
} else {
// no valid config yet
videoConfigurationVc.contextPut("showPreviewButton", Boolean.FALSE);
videoConfigurationVc.contextPut("showOptions", Boolean.FALSE);
videoConfigurationVc.contextPut(VC_CHOSENVIDEO, translate(NLS_NO_VIDEO_CHOSEN));
}
// Accessibility precondition
Condition accessCondition = videoNode.getPreConditionAccess();
accessibilityCondContr = new ConditionEditController(ureq, getWindowControl(), euce,
accessCondition, AssessmentHelper.getAssessableNodes(course.getEditorTreeModel(), videoNode));
listenTo(accessibilityCondContr);
main.setContent(videoConfigurationVc);
}
@Override
public void addTabs(TabbedPane tabbedPane) {
myTabbedPane = tabbedPane;
tabbedPane.addTab(translate(PANE_TAB_ACCESSIBILITY), accessibilityCondContr.getWrappedDefaultAccessConditionVC(translate(NLS_CONDITION_ACCESSIBILITY_TITLE)));
tabbedPane.addTab(translate(PANE_TAB_VIDEOCONFIG), main);
}
@Override
public String[] getPaneKeys() {
return paneKeys;
}
@Override
public TabbedPane getTabbedPane() {
return myTabbedPane;
}
@Override
protected void event(UserRequest ureq, Component source, Event event) {
if (source == chooseVideoButton || source == changeVideoButton) {
removeAsListenerAndDispose(searchController);
searchController = new ReferencableEntriesSearchController(getWindowControl(), ureq, new String[] {VideoFileResource.TYPE_NAME}, translate(NLS_COMMAND_CHOOSEVIDEO), true, false, false, false);
listenTo(searchController);
removeAsListenerAndDispose(cmc);
cmc = new CloseableModalController(
getWindowControl(), translate("close"), searchController.getInitialComponent(), true, translate(NLS_COMMAND_CHOOSEVIDEO)
);
listenTo(cmc);
cmc.activate();
} else if(source == previewLink){
VideoDisplayController previewController = null;
switch(config.getStringValue(VideoEditController.CONFIG_KEY_DESCRIPTION_SELECT)){
case "resourceDescription":
previewController = new VideoDisplayController(ureq, getWindowControl(), repositoryEntry, config.getBooleanSafe(VideoEditController.CONFIG_KEY_AUTOPLAY), config.getBooleanSafe(VideoEditController.CONFIG_KEY_COMMENTS), config.getBooleanSafe(VideoEditController.CONFIG_KEY_RATING), true,
"", false,false, "", true);
break;
case "customDescription":
previewController = new VideoDisplayController(ureq, getWindowControl(), repositoryEntry, config.getBooleanSafe(VideoEditController.CONFIG_KEY_AUTOPLAY), config.getBooleanSafe(VideoEditController.CONFIG_KEY_COMMENTS), config.getBooleanSafe(VideoEditController.CONFIG_KEY_RATING), true,
"", true,false, config.getStringValue(VideoEditController.CONFIG_KEY_DESCRIPTION_CUSTOMTEXT), true);
break;
case "none":
previewController = new VideoDisplayController(ureq, getWindowControl(), repositoryEntry, config.getBooleanSafe(VideoEditController.CONFIG_KEY_AUTOPLAY), config.getBooleanSafe(VideoEditController.CONFIG_KEY_COMMENTS), config.getBooleanSafe(VideoEditController.CONFIG_KEY_RATING), true,
"", true,false, "", true);
break;
}
cmc = new CloseableModalController(
getWindowControl(), translate("close"), previewController.getInitialComponent(), true, translate(NLS_COMMAND_CHOOSEVIDEO)
);
listenTo(cmc);
cmc.activate();
}
}
/**
* @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest,
* org.olat.core.gui.control.Controller, org.olat.core.gui.control.Event)
*/
@Override
public void event(UserRequest urequest, Controller source, Event event) {
if (source == searchController) {
if (event == ReferencableEntriesSearchController.EVENT_REPOSITORY_ENTRY_SELECTED) {
// -> close closeable modal controller
cmc.deactivate();
repositoryEntry = searchController.getSelectedEntry();
if (repositoryEntry != null) {
setVideoReference(repositoryEntry, config);
videoConfigurationVc.contextPut("showPreviewButton", Boolean.TRUE);
String displayname = StringHelper.escapeHtml(repositoryEntry.getDisplayname());
previewLink = LinkFactory.createCustomLink("command.preview", "command.preview", displayname, Link.NONTRANSLATED, videoConfigurationVc, this);
previewLink.setIconLeftCSS("o_icon o_icon-fw o_icon_preview");
previewLink.setTitle(getTranslator().translate("command.preview"));
// fire event so the updated config is saved by the editormaincontroller
fireEvent(urequest, NodeEditController.NODECONFIG_CHANGED_EVENT);
videoConfigurationVc.contextPut("showOptions", Boolean.TRUE);
removeAsListenerAndDispose(videoOptionsCtrl);
videoOptionsCtrl = new VideoOptionsForm(urequest, getWindowControl(), repositoryEntry, config);
videoConfigurationVc.put("videoOptions", videoOptionsCtrl.getInitialComponent());
listenTo(videoOptionsCtrl);
}
}
} else if (source == accessibilityCondContr) {
if (event == Event.CHANGED_EVENT) {
Condition cond = accessibilityCondContr.getCondition();
videoNode.setPreConditionAccess(cond);
fireEvent(urequest, NodeEditController.NODECONFIG_CHANGED_EVENT);
}
}
if (event == NodeEditController.NODECONFIG_CHANGED_EVENT){
fireEvent(urequest, NodeEditController.NODECONFIG_CHANGED_EVENT);
}
}
@Override
protected void doDispose() {
//child controllers registered with listenTo() get disposed in BasicController
if (previewCtr != null) {
previewCtr.dispose();
previewCtr = null;
}
}
public static RepositoryEntry getVideoReference(ModuleConfiguration config, boolean strict) {
if (config == null) {
if (strict) throw new AssertException("missing config in Video");
else return null;
}
String repoSoftkey = (String) config.get(VideoEditController.CONFIG_KEY_REPOSITORY_SOFTKEY);
if (repoSoftkey == null) {
if (strict) throw new AssertException("invalid config when being asked for references");
else return null;
}
RepositoryManager rm = RepositoryManager.getInstance();
RepositoryEntry entry = rm.lookupRepositoryEntryBySoftkey(repoSoftkey, strict);
// entry can be null only if !strict
return entry;
}
/**
* @param moduleConfiguration
* @return boolean
*/
public static boolean isModuleConfigValid(ModuleConfiguration moduleConfiguration) {
boolean isValid = moduleConfiguration.get(CONFIG_KEY_REPOSITORY_SOFTKEY) != null;
if (isValid) {
Object repoEntry = getVideoReference(moduleConfiguration, false);
if (repoEntry == null) {
isValid = false;
removeVideoReference(moduleConfiguration);
}
}
return isValid;
}
/**
* Set the referenced repository entry.
*
* @param re
* @param moduleConfiguration
*/
public static void setVideoReference(RepositoryEntry re, ModuleConfiguration moduleConfiguration) {
moduleConfiguration.set(CONFIG_KEY_REPOSITORY_SOFTKEY, re.getSoftkey());
}
public static void removeVideoReference(ModuleConfiguration moduleConfiguration){
moduleConfiguration.remove(CONFIG_KEY_REPOSITORY_SOFTKEY);
}
}
class VideoOptionsForm extends FormBasicController{
/**
* Simple form for the Videooptions
*
* @author Dirk Furrer
*/
@Autowired
protected VideoManager videoManager;
private SelectionElement videoComments;
private SelectionElement videoRating;
private SelectionElement videoAutoplay;
private SingleSelection description;
private RichTextElement descriptionField;
private StaticTextElement descriptionRepoField;
private boolean commentsEnabled;
private boolean ratingEnabled;
private boolean autoplay;
private String mediaRepoBaseUrl;
private final RepositoryEntry repoEntry;
private final ModuleConfiguration config;
VideoOptionsForm(UserRequest ureq, WindowControl wControl, RepositoryEntry repoEntry, ModuleConfiguration moduleConfiguration) {
super(ureq, wControl);
this.config = moduleConfiguration;
this.repoEntry = repoEntry;
commentsEnabled = config.getBooleanSafe(VideoEditController.CONFIG_KEY_COMMENTS);
ratingEnabled = config.getBooleanSafe(VideoEditController.CONFIG_KEY_RATING);
autoplay = config.getBooleanSafe(VideoEditController.CONFIG_KEY_AUTOPLAY);
RepositoryHandler handler = RepositoryHandlerFactory.getInstance().getRepositoryHandler(repoEntry);
VFSContainer mediaContainer = handler.getMediaContainer(repoEntry);
if(mediaContainer != null) {
mediaRepoBaseUrl = registerMapper(ureq, new VFSContainerMapper(mediaContainer.getParentContainer()));
}
initForm(ureq);
}
public boolean isVideoComments() {
return videoComments.isSelected(0);
}
@Override
protected void formOK(UserRequest ureq) {
config.setBooleanEntry(VideoEditController.CONFIG_KEY_COMMENTS, videoComments.isSelected(0));
config.setBooleanEntry(VideoEditController.CONFIG_KEY_RATING, videoRating.isSelected(0));
config.setBooleanEntry(VideoEditController.CONFIG_KEY_AUTOPLAY, videoAutoplay.isSelected(0));
config.setStringValue(VideoEditController.CONFIG_KEY_DESCRIPTION_SELECT, description.getSelectedKey());
if(description.getSelectedKey() == "customDescription"){
config.setStringValue(VideoEditController.CONFIG_KEY_DESCRIPTION_CUSTOMTEXT, descriptionField.getValue());
}
fireEvent(ureq, NodeEditController.NODECONFIG_CHANGED_EVENT);
}
@Override
protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
//add checkboxes for displayoptions
videoComments = uifactory.addCheckboxesHorizontal("videoComments", "video.config.comments", formLayout, new String[]{"xx"}, new String[]{null});
videoComments.select("xx",commentsEnabled);
videoRating = uifactory.addCheckboxesHorizontal("videoRating", "video.config.rating", formLayout, new String[]{"xx"}, new String[]{null});
videoRating.select("xx",ratingEnabled);
videoAutoplay = uifactory.addCheckboxesHorizontal("videoAutoplay", "video.config.autoplay", formLayout, new String[]{"xx"}, new String[]{null});
videoAutoplay.select("xx",autoplay);
String[] descriptionkeys = new String[]{ "none", "resourceDescription", "customDescription" };
String[] descriptionValues = new String[]{ translate("description.none"), translate("description.resource"), translate("description.custom") };
//add textfield for custom description
description = uifactory.addDropdownSingleselect("video.config.description", formLayout, descriptionkeys, descriptionValues, null);
description.addActionListener(FormEvent.ONCHANGE);
description.select(config.getStringValue(VideoEditController.CONFIG_KEY_DESCRIPTION_SELECT,"none"), true);
String desc = repoEntry.getDescription();
descriptionField = uifactory.addRichTextElementForStringDataMinimalistic("description", "", desc, -1, -1, formLayout, getWindowControl());
descriptionRepoField = uifactory.addStaticTextElement("description.repo", "", "", formLayout);
updateDescriptionField();
uifactory.addFormSubmitButton("submit", formLayout);
//init options-config
config.setBooleanEntry(VideoEditController.CONFIG_KEY_COMMENTS, videoComments.isSelected(0));
config.setBooleanEntry(VideoEditController.CONFIG_KEY_RATING, videoRating.isSelected(0));
config.setBooleanEntry(VideoEditController.CONFIG_KEY_AUTOPLAY, videoAutoplay.isSelected(0));
config.setStringValue(VideoEditController.CONFIG_KEY_DESCRIPTION_SELECT, description.getSelectedKey());
if(description.getSelectedKey() == "customDescription"){
config.setStringValue(VideoEditController.CONFIG_KEY_DESCRIPTION_CUSTOMTEXT, descriptionField.getValue());
}
}
/**
* Update visibility of the textfield for entering custom description
*/
private void updateDescriptionField() {
String selectDescOption = description.getSelectedKey();
if("none".equals(selectDescOption)) {
descriptionField.setVisible(false);
descriptionRepoField.setVisible(false);
} else if("resourceDescription".equals(selectDescOption)) {
descriptionField.setVisible(false);
descriptionField.setEnabled(false);
String text = repoEntry.getDescription();
if(StringHelper.containsNonWhitespace(text)) {
text = StringHelper.xssScan(text);
if(mediaRepoBaseUrl != null) {
text = FilterFactory.getBaseURLToMediaRelativeURLFilter(mediaRepoBaseUrl).filter(text);
}
text = Formatter.formatLatexFormulas(text);
}
descriptionRepoField.setValue(text);
descriptionRepoField.setVisible(true);
} else if("customDescription".equals(selectDescOption)) {
descriptionField.setVisible(true);
descriptionField.setEnabled(true);
descriptionField.setValue(config.getStringValue(VideoEditController.CONFIG_KEY_DESCRIPTION_CUSTOMTEXT, ""));
descriptionRepoField.setVisible(false);
}
}
@Override
protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
if(source == description){
updateDescriptionField();
}
}
@Override
protected void doDispose() {
//
}
}