/** * <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.modules.portfolio.ui; import java.io.File; import java.text.Collator; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import javax.servlet.http.HttpServletRequest; import org.olat.core.commons.modules.bc.vfs.OlatRootFolderImpl; import org.olat.core.dispatcher.mapper.Mapper; import org.olat.core.gui.UserRequest; 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.elements.FileElement; import org.olat.core.gui.components.form.flexible.elements.FormLink; import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement; import org.olat.core.gui.components.form.flexible.elements.RichTextElement; import org.olat.core.gui.components.form.flexible.elements.SingleSelection; import org.olat.core.gui.components.form.flexible.elements.TextElement; 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.form.flexible.impl.FormLayoutContainer; import org.olat.core.gui.components.link.Link; import org.olat.core.gui.control.Controller; 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.media.MediaResource; import org.olat.core.util.CodeHelper; import org.olat.core.util.FileUtils; import org.olat.core.util.StringHelper; import org.olat.core.util.Util; 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.VFSMediaResource; import org.olat.core.util.vfs.filters.SystemItemFilter; import org.olat.modules.forms.handler.EvaluationFormResource; import org.olat.modules.portfolio.Assignment; import org.olat.modules.portfolio.AssignmentType; import org.olat.modules.portfolio.Binder; import org.olat.modules.portfolio.PortfolioService; import org.olat.modules.portfolio.Section; import org.olat.modules.portfolio.manager.PortfolioFileStorage; import org.olat.modules.portfolio.model.SectionKeyRef; import org.olat.repository.RepositoryEntry; import org.olat.repository.controllers.ReferencableEntriesSearchController; import org.springframework.beans.factory.annotation.Autowired; /** * * Initial date: 12.07.2016<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ public class AssignmentEditController extends FormBasicController { public static final String[] typeKeys = new String[] { AssignmentType.essay.name(), //AssignmentType.document.name(), AssignmentType.form.name() }; public static final String[] onKeys = new String[] { "on" }; public static final String[] evaKeys = new String[] { "only-autoevaluation", "alien-evaluation"}; private TextElement titleEl; private RichTextElement summaryEl; private SingleSelection typeEl; private SingleSelection sectionsEl; private FileElement documentUploadEl; private FormLayoutContainer filesLayout, selectFormLayout; private RichTextElement contentEl; private FormLink selectFormButton; private SingleSelection evaluationFormEl; private MultipleSelectionElement reviewerSeeAutoEvaEl; private MultipleSelectionElement anonymousExternEvaEl; private CloseableModalController cmc; private ReferencableEntriesSearchController searchFormCtrl; private Binder binder; private Section section; private Assignment assignment; private RepositoryEntry formEntry; private VFSContainer tempUploadFolder; private VFSContainer documentContainer; private String mapperUri; private boolean assignmentInUse; @Autowired private PortfolioFileStorage fileStorage; @Autowired private PortfolioService portfolioService; @Autowired private PortfolioFileStorage portfolioFileStorage; public AssignmentEditController(UserRequest ureq, WindowControl wControl, Binder binder) { super(ureq, wControl); this.binder = binder; assignmentInUse = false; initForm(ureq); } public AssignmentEditController(UserRequest ureq, WindowControl wControl, Section section) { super(ureq, wControl); this.section = section; assignmentInUse = false; initForm(ureq); } public AssignmentEditController(UserRequest ureq, WindowControl wControl, Assignment assignment) { super(ureq, wControl); this.assignment = assignment; assignmentInUse = portfolioService.isAssignmentInUse(assignment); formEntry = assignment.getFormEntry(); initForm(ureq); } @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { formLayout.setElementCssClass("o_sel_pf_edit_assignment_form"); if(assignmentInUse) { setFormWarning("assignment.in.use"); } String title = assignment == null ? null : assignment.getTitle(); titleEl = uifactory.addTextElement("title", "assignment.title", 255, title, formLayout); titleEl.setElementCssClass("o_sel_pf_edit_assignment_title"); titleEl.setMandatory(true); String summary = assignment == null ? null : assignment.getSummary(); summaryEl = uifactory.addRichTextElementForStringDataMinimalistic("summary", "summary", summary, 8, 60, formLayout, getWindowControl()); summaryEl.setElementCssClass("o_sel_pf_edit_assignment_summary"); summaryEl.setPlaceholderKey("summary.placeholder", null); summaryEl.getEditorConfiguration().setPathInStatusBar(false); String content = assignment == null ? null : assignment.getContent(); contentEl = uifactory.addRichTextElementForStringDataCompact("content", "assignment.content", content, 12, 60, null, formLayout, ureq.getUserSession(), getWindowControl()); contentEl.setElementCssClass("o_sel_pf_edit_assignment_content"); contentEl.getEditorConfiguration().disableMedia(); contentEl.getEditorConfiguration().disableImageAndMovie(); if(binder != null) { List<Section> sections = portfolioService.getSections(binder); String selectedKey = null; int numOfSections = sections.size(); String[] theKeys = new String[numOfSections]; String[] theValues = new String[numOfSections]; for (int i = 0; i < numOfSections; i++) { Long sectionKey = sections.get(i).getKey(); theKeys[i] = sectionKey.toString(); theValues[i] = (i + 1) + ". " + sections.get(i).getTitle(); if (section != null && section.getKey().equals(sectionKey)) { selectedKey = theKeys[i]; } } sectionsEl = uifactory.addDropdownSingleselect("sections", "page.sections", formLayout, theKeys, theValues, null); if (selectedKey != null) { sectionsEl.select(selectedKey, true); } else if(theKeys.length > 0) { sectionsEl.select(theKeys[0], true); } } String[] typeValues = new String[ typeKeys.length ]; for(int i=typeValues.length; i-->0; ) { typeValues[i] = translate("assignment.type.".concat(typeKeys[i])); } typeEl = uifactory.addDropdownSingleselect("type", "assignment.type", formLayout, typeKeys, typeValues, null); typeEl.setElementCssClass("o_sel_pf_edit_assignment_type"); typeEl.addActionListener(FormEvent.ONCHANGE); String selectedType = assignment == null ? typeKeys[0] : assignment.getAssignmentType().name(); typeEl.select(selectedType, true); typeEl.setEnabled(!assignmentInUse); createAssignmentEvaluationFormForm(formLayout); createAssignmentDocumentForm(formLayout); FormLayoutContainer buttonsCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator()); buttonsCont.setRootForm(mainForm); formLayout.add(buttonsCont); uifactory.addFormCancelButton("cancel", buttonsCont, ureq, getWindowControl()); uifactory.addFormSubmitButton("save", buttonsCont); updateAssignmentDocumentForm(ureq); updateAssignmentForms(); } protected void createAssignmentEvaluationFormForm(FormItemContainer formLayout) { String editPage = Util.getPackageVelocityRoot(getClass()) + "/assignment_select_form.html"; selectFormLayout = FormLayoutContainer.createCustomFormLayout("selectFormLayout", getTranslator(), editPage); selectFormLayout.setLabel("assignment.evaluation.form.entry", null); selectFormLayout.setMandatory(true); formLayout.add(selectFormLayout); if(formEntry != null) { String displayName = StringHelper.escapeHtml(formEntry.getDisplayname()); selectFormLayout.contextPut("formEntryName", displayName); } selectFormButton = uifactory.addFormLink("select.form", selectFormLayout, Link.BUTTON); selectFormButton.getComponent().setSuppressDirtyFormWarning(true); selectFormButton.setVisible(!assignmentInUse); String[] evaValues = new String[]{ translate("assignment.evaluation.form.auto"), translate("assignment.evaluation.form.auto.extern") }; evaluationFormEl = uifactory.addRadiosVertical("assignment.evaluation.form", formLayout, evaKeys, evaValues); evaluationFormEl.addActionListener(FormEvent.ONCHANGE); if(assignment == null || assignment.isOnlyAutoEvaluation()) { evaluationFormEl.select(evaKeys[0], true); } else { evaluationFormEl.select(evaKeys[1], true); } String[] reviewerValues = new String[] { translate("assignment.evaluation.form.reviewer.see.auto") }; reviewerSeeAutoEvaEl = uifactory.addCheckboxesHorizontal("assignment.evaluation.form.reviewer.see.auto", null, formLayout, onKeys, reviewerValues); if(assignment != null && assignment.isReviewerSeeAutoEvaluation()) { reviewerSeeAutoEvaEl.select(onKeys[0], true); } String[] anonValues = new String[] { translate("assignment.evaluation.form.anonymous.extern") }; anonymousExternEvaEl = uifactory.addCheckboxesHorizontal("assignment.evaluation.form.anonymous.extern", null, formLayout, onKeys, anonValues); if(assignment != null && assignment.isAnonymousExternalEvaluation()) { anonymousExternEvaEl.select(onKeys[0], true); } } protected void createAssignmentDocumentForm(FormItemContainer formLayout) { String editPage = Util.getPackageVelocityRoot(getClass()) + "/assignment_editfiles.html"; filesLayout = FormLayoutContainer.createCustomFormLayout("filesLayout", getTranslator(), editPage); filesLayout.setLabel("assignment.document.upload", null); formLayout.add(filesLayout); documentUploadEl = uifactory.addFileElement(getWindowControl(), "assignment.document.upload", formLayout); documentUploadEl.addActionListener(FormEvent.ONCHANGE); } protected void updateAssignmentForms() { if(AssignmentType.essay.name().equals(typeEl.getSelectedKey())) { selectFormLayout.setVisible(false); evaluationFormEl.setVisible(false); reviewerSeeAutoEvaEl.setVisible(false); anonymousExternEvaEl.setVisible(false); } else if(AssignmentType.form.name().equals(typeEl.getSelectedKey())) { selectFormLayout.setVisible(true); evaluationFormEl.setVisible(true); boolean otherOptions = evaluationFormEl.isOneSelected() && evaluationFormEl.isSelected(1); reviewerSeeAutoEvaEl.setVisible(otherOptions); anonymousExternEvaEl.setVisible(otherOptions); } } protected void updateAssignmentDocumentForm(UserRequest ureq) { if(mapperUri == null) { mapperUri = registerCacheableMapper(ureq, "assigment-" + CodeHelper.getRAMUniqueID(), new DocumentMapper()); filesLayout.contextPut("mapperUri", mapperUri); } List<VFSItem> files = new ArrayList<>(); if(assignment != null && StringHelper.containsNonWhitespace(assignment.getStorage())) { documentContainer = fileStorage.getAssignmentContainer(assignment); files.addAll(documentContainer.getItems(new SystemItemFilter())); } // add files from TempFolder if(tempUploadFolder != null) { files.addAll(tempUploadFolder.getItems(new SystemItemFilter())); } Collections.sort(files, new Comparator<VFSItem>(){ final Collator c = Collator.getInstance(getLocale()); @Override public int compare(final VFSItem o1, final VFSItem o2) { return c.compare((o1).getName(), (o2).getName()); } }); filesLayout.contextPut("files", files); // add delete links for each attachment if user is allowed to see them int count = 0; for (VFSItem file : files) { FormLink deleteLink = uifactory.addFormLink("delete_" + (++count), filesLayout, Link.BUTTON_XSMALL); deleteLink.setUserObject(file); deleteLink.setI18nKey("delete"); } boolean hasFile = files.size() > 0; filesLayout.setVisible(files.size() > 0); filesLayout.showLabel(hasFile); documentUploadEl.showLabel(!hasFile); } @Override protected void doDispose() { deleteTempStorage(); } @Override protected void event(UserRequest ureq, Controller source, Event event) { if(source == searchFormCtrl) { if(event == ReferencableEntriesSearchController.EVENT_REPOSITORY_ENTRY_SELECTED) { doSelectForm(searchFormCtrl.getSelectedEntry()); } cmc.deactivate(); cleanUp(); } else if(source == cmc) { cleanUp(); } super.event(ureq, source, event); } private void cleanUp() { removeAsListenerAndDispose(searchFormCtrl); removeAsListenerAndDispose(cmc); searchFormCtrl = null; cmc = null; } @Override protected boolean validateFormLogic(UserRequest ureq) { boolean allOk = true; if(sectionsEl != null) { sectionsEl.clearError(); if(!sectionsEl.isOneSelected()) { sectionsEl.setErrorKey("form.legende.mandatory", null); allOk &= false; } } typeEl.clearError(); selectFormLayout.clearError(); if(typeEl.isOneSelected()) { AssignmentType type = AssignmentType.valueOf(typeEl.getSelectedKey()); if(type == AssignmentType.form) { if(formEntry == null) { selectFormLayout.setErrorKey("form.legende.mandatory", null); allOk &= false; } } } else { typeEl.setErrorKey("form.legende.mandatory", null); allOk &= false; } return allOk & super.validateFormLogic(ureq); } @Override protected void formOK(UserRequest ureq) { String title = titleEl.getValue(); String summary = summaryEl.getValue(); String content = contentEl.getValue(); AssignmentType type = AssignmentType.valueOf(typeEl.getSelectedKey()); boolean onlyAutoEvaluation = evaluationFormEl.isOneSelected() && evaluationFormEl.isSelected(0); boolean reviewerCanSeeAutoEvaluation = reviewerSeeAutoEvaEl.isAtLeastSelected(1); boolean anonymousExternEvaluation = anonymousExternEvaEl.isAtLeastSelected(1); Section selectedSection = section; if(sectionsEl != null && sectionsEl.isOneSelected()) { String selectedKey = sectionsEl.getSelectedKey(); Long selectedSectionKey = new Long(selectedKey); selectedSection = portfolioService.getSection(new SectionKeyRef(selectedSectionKey)); } if(assignment == null) { assignment = portfolioService.addAssignment(title, summary, content, type, selectedSection, onlyAutoEvaluation, reviewerCanSeeAutoEvaluation, anonymousExternEvaluation, formEntry); } else { assignment = portfolioService.updateAssignment(assignment, title, summary, content, type, onlyAutoEvaluation, reviewerCanSeeAutoEvaluation, anonymousExternEvaluation, formEntry); } persistUploadedFiles(); deleteTempStorage(); fireEvent(ureq, Event.DONE_EVENT); } private void persistUploadedFiles() { if(tempUploadFolder == null) return; VFSContainer container = portfolioFileStorage.getAssignmentContainer(assignment); if (container != null) { List<VFSItem> tmpFList = tempUploadFolder.getItems(new SystemItemFilter()); for (VFSItem file : tmpFList) { try { VFSLeaf leaf = (VFSLeaf) file; VFSLeaf storedFile = container.createChildLeaf(leaf.getName()); FileUtils.bcopy(leaf.getInputStream(), storedFile.getOutputStream(false), ""); } catch (Exception e) { logError("", e); } } } } private void deleteTempStorage() { if(tempUploadFolder != null) { tempUploadFolder.delete(); } } @Override protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { if(source == typeEl || source == evaluationFormEl) { updateAssignmentForms(); } else if (source == documentUploadEl) { if (documentUploadEl.isUploadSuccess()) { doUploadDocument(ureq); } } else if(source == selectFormButton) { doSelectForm(ureq); } else if (source instanceof FormLink) { FormLink activeLink = (FormLink) source; Object uobject = activeLink.getUserObject(); if (uobject instanceof VFSLeaf) { VFSLeaf file = (VFSLeaf)uobject; file.delete(); } updateAssignmentDocumentForm(ureq); } super.formInnerEvent(ureq, source, event); } private void doSelectForm(UserRequest ureq) { if(searchFormCtrl != null) return; searchFormCtrl = new ReferencableEntriesSearchController(getWindowControl(), ureq, EvaluationFormResource.TYPE_NAME, translate("select.form")); listenTo(searchFormCtrl); cmc = new CloseableModalController(getWindowControl(), translate("close"), searchFormCtrl.getInitialComponent(), true, translate("select.form")); cmc.suppressDirtyFormWarningOnClose(); listenTo(cmc); cmc.activate(); } private void doSelectForm(RepositoryEntry entry) { formEntry = entry; String displayName = StringHelper.escapeHtml(entry.getDisplayname()); selectFormLayout.contextPut("formEntryName", displayName); } private void doUploadDocument(UserRequest ureq) { String fileName = documentUploadEl.getUploadFileName(); // checking tmp-folder and msg-container for filename boolean fileExists = false; if (tempUploadFolder != null && tempUploadFolder.resolve(fileName) != null) { fileExists = true; } else if (documentContainer != null && documentContainer.resolve(fileName) != null) { fileExists = true; } if (fileExists) { documentUploadEl.setErrorKey("attachments.error.file.exists", null); documentUploadEl.getUploadFile().delete(); documentUploadEl.showError(true); } else { // files got stored in an extra tempFolder, to use the same // fileUploader multiple times if(tempUploadFolder == null) { tempUploadFolder = new OlatRootFolderImpl(File.separator + "tmp/" + CodeHelper.getGlobalForeverUniqueID() + "/", null); } documentUploadEl.moveUploadFileTo(tempUploadFolder); documentUploadEl.showError(false); documentUploadEl.reset(); updateAssignmentDocumentForm(ureq); showInfo("attachments.upload.successful", fileName); } } @Override protected void formCancelled(UserRequest ureq) { fireEvent(ureq, Event.CANCELLED_EVENT); } public class DocumentMapper implements Mapper { @Override public MediaResource handle(String relPath, HttpServletRequest request) { if(relPath.startsWith("/")) { relPath = relPath.substring(1, relPath.length()); } @SuppressWarnings("unchecked") List<VFSItem> files = (List<VFSItem>)filesLayout.contextGet("files"); if(files != null) { for(VFSItem file:files) { if(relPath.equalsIgnoreCase(file.getName()) && file instanceof VFSLeaf) { return new VFSMediaResource((VFSLeaf)file); } } } return null; } } }