/** * OLAT - Online Learning and Training<br> * http://www.olat.org * <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 * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <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> * Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> * University of Zurich, Switzerland. * <hr> * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * This file has been modified by the OpenOLAT community. Changes are licensed * under the Apache 2.0 license as the original file. */ package org.olat.repository.ui.author; import java.io.File; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.UUID; import org.olat.NewControllerFactory; 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.DateChooser; import org.olat.core.gui.components.form.flexible.elements.FileElement; 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.form.flexible.impl.elements.FileElementEvent; import org.olat.core.gui.components.form.flexible.impl.elements.FormSubmit; 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.util.FileUtils; import org.olat.core.util.StringHelper; import org.olat.core.util.WebappHelper; import org.olat.core.util.vfs.LocalFileImpl; import org.olat.core.util.vfs.LocalFolderImpl; import org.olat.core.util.vfs.VFSContainer; import org.olat.core.util.vfs.VFSLeaf; import org.olat.course.CourseModule; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryEntryManagedFlag; import org.olat.repository.RepositoryManager; import org.olat.repository.RepositoryService; import org.olat.repository.handlers.RepositoryHandler; import org.olat.repository.handlers.RepositoryHandlerFactory; import org.olat.repository.manager.RepositoryEntryLifecycleDAO; import org.olat.repository.model.RepositoryEntryLifecycle; import org.olat.resource.OLATResource; import org.olat.user.UserManager; import org.springframework.beans.factory.annotation.Autowired; /** * Description:<br> * * @author Ingmar Kroll * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ public class RepositoryEditDescriptionController extends FormBasicController { private static final Set<String> imageMimeTypes = new HashSet<String>(); static { imageMimeTypes.add("image/gif"); imageMimeTypes.add("image/jpg"); imageMimeTypes.add("image/jpeg"); imageMimeTypes.add("image/png"); } private VFSContainer mediaContainer; private RepositoryEntry repositoryEntry; private final String repoEntryType; private static final int picUploadlimitKB = 5120; private static final int movieUploadlimitKB = 102400; private FileElement fileUpload, movieUpload; private TextElement externalRef, displayName, authors, expenditureOfWork, language, location; private RichTextElement description, objectives, requirements, credits; private SingleSelection dateTypesEl, publicDatesEl; private DateChooser startDateEl, endDateEl; private FormSubmit submit; private FormLayoutContainer privateDatesCont; private static final String[] dateKeys = new String[]{ "none", "private", "public"}; @Autowired private UserManager userManager; @Autowired private RepositoryService repositoryService; @Autowired private RepositoryManager repositoryManager; @Autowired private RepositoryEntryLifecycleDAO lifecycleDao; @Autowired private RepositoryHandlerFactory repositoryHandlerFactory; /** * Create a repository add controller that adds the given resourceable. * * @param ureq * @param wControl * @param sourceEntry */ public RepositoryEditDescriptionController(UserRequest ureq, WindowControl wControl, RepositoryEntry entry) { super(ureq, wControl); setBasePackage(RepositoryService.class); this.repositoryEntry = entry; repoEntryType = repositoryEntry.getOlatResource().getResourceableTypeName(); initForm(ureq); } public RepositoryEntry getEntry() { return repositoryEntry; } @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { setFormContextHelp("Info Page: Add Meta Data"); formLayout.setElementCssClass("o_sel_edit_repositoryentry"); String id = repositoryEntry.getResourceableId() == null ? "-" : repositoryEntry.getResourceableId().toString(); uifactory.addStaticTextElement("cif.id", id, formLayout); String externalId = repositoryEntry.getExternalId(); if(StringHelper.containsNonWhitespace(externalId)) { uifactory.addStaticTextElement("cif.externalid", externalId, formLayout); } String extRef = repositoryEntry.getExternalRef(); if(StringHelper.containsNonWhitespace(repositoryEntry.getManagedFlagsString())) { if(StringHelper.containsNonWhitespace(extRef)) { uifactory.addStaticTextElement("cif.externalref", extRef, formLayout); } } else { externalRef = uifactory.addTextElement("cif.externalref", "cif.externalref", 100, extRef, formLayout); externalRef.setHelpText(translate("cif.externalref.hover")); externalRef.setHelpUrlForManualPage("Info Page#_identification"); externalRef.setDisplaySize(30); } String initalAuthor = repositoryEntry.getInitialAuthor() == null ? "-" : repositoryEntry.getInitialAuthor(); if(repositoryEntry.getInitialAuthor() != null) { initalAuthor = userManager.getUserDisplayName(initalAuthor); } initalAuthor = StringHelper.escapeHtml(initalAuthor); uifactory.addStaticTextElement("cif.initialAuthor", initalAuthor, formLayout); // Add resource type String typeName = null; OLATResource res = repositoryEntry.getOlatResource(); if (res != null) { typeName = res.getResourceableTypeName(); } String typeDisplay ; if (typeName != null) { // add image and typename code typeDisplay = NewControllerFactory.translateResourceableTypeName(typeName, getLocale()); } else { typeDisplay = translate("cif.type.na"); } uifactory.addStaticTextElement("cif.type", typeDisplay, formLayout); uifactory.addSpacerElement("spacer1", formLayout, false); displayName = uifactory.addTextElement("cif.displayname", "cif.displayname", 100, repositoryEntry.getDisplayname(), formLayout); displayName.setDisplaySize(30); displayName.setMandatory(true); displayName.setEnabled(!RepositoryEntryManagedFlag.isManaged(repositoryEntry, RepositoryEntryManagedFlag.title)); authors = uifactory.addTextElement("cif.authors", "cif.authors", 255, repositoryEntry.getAuthors(), formLayout); authors.setDisplaySize(60); language = uifactory.addTextElement("cif.mainLanguage", "cif.mainLanguage", 16, repositoryEntry.getMainLanguage(), formLayout); location = uifactory.addTextElement("cif.location", "cif.location", 255, repositoryEntry.getLocation(), formLayout); location.setEnabled(!RepositoryEntryManagedFlag.isManaged(repositoryEntry, RepositoryEntryManagedFlag.location)); RepositoryHandler handler = repositoryHandlerFactory.getRepositoryHandler(repositoryEntry); mediaContainer = handler.getMediaContainer(repositoryEntry); if(mediaContainer != null && mediaContainer.getName().equals("media")) { mediaContainer = mediaContainer.getParentContainer(); mediaContainer.setDefaultItemFilter(new MediaContainerFilter(mediaContainer)); } String desc = (repositoryEntry.getDescription() != null ? repositoryEntry.getDescription() : " "); description = uifactory.addRichTextElementForStringData("cif.description", "cif.description", desc, 10, -1, false, mediaContainer, null, formLayout, ureq.getUserSession(), getWindowControl()); description.setEnabled(!RepositoryEntryManagedFlag.isManaged(repositoryEntry, RepositoryEntryManagedFlag.description)); description.getEditorConfiguration().setFileBrowserUploadRelPath("media"); uifactory.addSpacerElement("spacer2", formLayout, false); if(CourseModule.getCourseTypeName().equals(repoEntryType)) { String[] dateValues = new String[] { translate("cif.dates.none"), translate("cif.dates.private"), translate("cif.dates.public") }; dateTypesEl = uifactory.addRadiosVertical("cif.dates", formLayout, dateKeys, dateValues); dateTypesEl.setElementCssClass("o_sel_repo_lifecycle_type"); if(repositoryEntry.getLifecycle() == null) { dateTypesEl.select("none", true); } else if(repositoryEntry.getLifecycle().isPrivateCycle()) { dateTypesEl.select("private", true); } else { dateTypesEl.select("public", true); } dateTypesEl.addActionListener(FormEvent.ONCHANGE); List<RepositoryEntryLifecycle> cycles = lifecycleDao.loadPublicLifecycle(); List<RepositoryEntryLifecycle> filteredCycles = new ArrayList<>(); //just make the upcomming and acutual running cycles or the pre-selected visible in the UI LocalDateTime now = LocalDateTime.now(); for(RepositoryEntryLifecycle cycle:cycles) { if(cycle.getValidTo() == null || now.isBefore(LocalDateTime.ofInstant(cycle.getValidTo().toInstant(), ZoneId.systemDefault())) || (repositoryEntry.getLifecycle() != null && repositoryEntry.getLifecycle().equals(cycle))) { filteredCycles.add(cycle); } } String[] publicKeys = new String[filteredCycles.size()]; String[] publicValues = new String[filteredCycles.size()]; int count = 0; for(RepositoryEntryLifecycle cycle:filteredCycles) { publicKeys[count] = cycle.getKey().toString(); StringBuilder sb = new StringBuilder(32); boolean labelAvailable = StringHelper.containsNonWhitespace(cycle.getLabel()); if(labelAvailable) { sb.append(cycle.getLabel()); } if(StringHelper.containsNonWhitespace(cycle.getSoftKey())) { if(labelAvailable) sb.append(" - "); sb.append(cycle.getSoftKey()); } publicValues[count++] = sb.toString(); } publicDatesEl = uifactory.addDropdownSingleselect("cif.public.dates", formLayout, publicKeys, publicValues, null); String privateDatePage = velocity_root + "/cycle_dates.html"; privateDatesCont = FormLayoutContainer.createCustomFormLayout("private.date", getTranslator(), privateDatePage); privateDatesCont.setRootForm(mainForm); privateDatesCont.setLabel("cif.private.dates", null); formLayout.add("private.date", privateDatesCont); startDateEl = uifactory.addDateChooser("date.start", "cif.date.start", null, privateDatesCont); startDateEl.setElementCssClass("o_sel_repo_lifecycle_validfrom"); endDateEl = uifactory.addDateChooser("date.end", "cif.date.end", null, privateDatesCont); endDateEl.setElementCssClass("o_sel_repo_lifecycle_validto"); if(repositoryEntry.getLifecycle() != null) { RepositoryEntryLifecycle lifecycle = repositoryEntry.getLifecycle(); if(lifecycle.isPrivateCycle()) { startDateEl.setDate(lifecycle.getValidFrom()); endDateEl.setDate(lifecycle.getValidTo()); } else { String key = lifecycle.getKey().toString(); for(String publicKey:publicKeys) { if(key.equals(publicKey)) { publicDatesEl.select(key, true); break; } } } } updateDatesVisibility(); uifactory.addSpacerElement("spacer3", formLayout, false); expenditureOfWork = uifactory.addTextElement("cif.expenditureOfWork", "cif.expenditureOfWork", 100, repositoryEntry.getExpenditureOfWork(), formLayout); expenditureOfWork.setExampleKey("details.expenditureOfWork.example", null); String obj = (repositoryEntry.getObjectives() != null ? repositoryEntry.getObjectives() : " "); objectives = uifactory.addRichTextElementForStringData("cif.objectives", "cif.objectives", obj, 10, -1, false, mediaContainer, null, formLayout, ureq.getUserSession(), getWindowControl()); objectives.setEnabled(!RepositoryEntryManagedFlag.isManaged(repositoryEntry, RepositoryEntryManagedFlag.objectives)); objectives.getEditorConfiguration().setFileBrowserUploadRelPath("media"); String req = (repositoryEntry.getRequirements() != null ? repositoryEntry.getRequirements() : " "); requirements = uifactory.addRichTextElementForStringData("cif.requirements", "cif.requirements", req, 10, -1, false, mediaContainer, null, formLayout, ureq.getUserSession(), getWindowControl()); requirements.setEnabled(!RepositoryEntryManagedFlag.isManaged(repositoryEntry, RepositoryEntryManagedFlag.requirements)); requirements.getEditorConfiguration().setFileBrowserUploadRelPath("media"); requirements.setMaxLength(2000); String cred = (repositoryEntry.getCredits() != null ? repositoryEntry.getCredits() : " "); credits = uifactory.addRichTextElementForStringData("cif.credits", "cif.credits", cred, 10, -1, false, mediaContainer, null, formLayout, ureq.getUserSession(), getWindowControl()); credits.setEnabled(!RepositoryEntryManagedFlag.isManaged(repositoryEntry, RepositoryEntryManagedFlag.credits)); credits.getEditorConfiguration().setFileBrowserUploadRelPath("media"); credits.setMaxLength(2000); uifactory.addSpacerElement("spacer4", formLayout, false); } boolean managed = RepositoryEntryManagedFlag.isManaged(repositoryEntry, RepositoryEntryManagedFlag.details); VFSLeaf img = repositoryManager.getImage(repositoryEntry); fileUpload = uifactory.addFileElement(getWindowControl(), "rentry.pic", "rentry.pic", formLayout); fileUpload.setExampleKey("rentry.pic.example", new String[] {RepositoryManager.PICTURE_WIDTH + "x" + (RepositoryManager.PICTURE_HEIGHT)}); fileUpload.setMaxUploadSizeKB(picUploadlimitKB, null, null); fileUpload.setPreview(ureq.getUserSession(), true); fileUpload.addActionListener(FormEvent.ONCHANGE); fileUpload.setDeleteEnabled(!managed); if(img instanceof LocalFileImpl) { fileUpload.setPreview(ureq.getUserSession(), true); fileUpload.setInitialFile(((LocalFileImpl)img).getBasefile()); } fileUpload.setVisible(!managed); fileUpload.limitToMimeType(imageMimeTypes, "cif.error.mimetype", new String[]{ imageMimeTypes.toString()} ); VFSLeaf movie = repositoryService.getIntroductionMovie(repositoryEntry); movieUpload = uifactory.addFileElement(getWindowControl(), "rentry.movie", "rentry.movie", formLayout); movieUpload.setExampleKey("rentry.movie.example", new String[] {"3:2"}); movieUpload.setMaxUploadSizeKB(movieUploadlimitKB, null, null); movieUpload.setPreview(ureq.getUserSession(), true); movieUpload.addActionListener(FormEvent.ONCHANGE); movieUpload.setDeleteEnabled(!managed); if(movie instanceof LocalFileImpl) { movieUpload.setPreview(ureq.getUserSession(), true); movieUpload.setInitialFile(((LocalFileImpl)movie).getBasefile()); } movieUpload.setVisible(!managed); FormLayoutContainer buttonContainer = FormLayoutContainer.createButtonLayout("buttonContainer", getTranslator()); formLayout.add("buttonContainer", buttonContainer); buttonContainer.setElementCssClass("o_sel_repo_save_details"); submit = uifactory.addFormSubmitButton("submit", buttonContainer); submit.setVisible(!managed); uifactory.addFormCancelButton("cancel", buttonContainer, ureq, getWindowControl()); } private void updateDatesVisibility() { if(dateTypesEl.isOneSelected()) { String type = dateTypesEl.getSelectedKey(); if("none".equals(type)) { publicDatesEl.setVisible(false); privateDatesCont.setVisible(false); } else if("public".equals(type)) { publicDatesEl.setVisible(true); privateDatesCont.setVisible(false); } else if("private".equals(type)) { publicDatesEl.setVisible(false); privateDatesCont.setVisible(true); } } } @Override protected void doDispose() { // Controllers autodisposed by basic controller } @Override protected boolean validateFormLogic(UserRequest ureq) { // Check for empty display name boolean allOk = true; if (!StringHelper.containsNonWhitespace(displayName.getValue())) { displayName.setErrorKey("cif.error.displayname.empty", new String[] {}); allOk = false; } else if (displayName.hasError()) { allOk = false; } else { displayName.clearError(); } allOk &= validateTextElement(language, 255); allOk &= validateTextElement(location, 255); allOk &= validateTextElement(objectives, 2000); allOk &= validateTextElement(requirements, 2000); allOk &= validateTextElement(credits, 2000); allOk &= validateTextElement(externalRef, 58); allOk &= validateTextElement(expenditureOfWork, 225); allOk &= validateTextElement(authors, 2000); if (publicDatesEl != null) { publicDatesEl.clearError(); if(publicDatesEl.isEnabled() && publicDatesEl.isVisible()) { if(!publicDatesEl.isOneSelected()) { publicDatesEl.setErrorKey("form.legende.mandatory", null); allOk &= false; } } } return allOk & super.validateFormLogic(ureq); } private boolean validateTextElement(TextElement el, int maxLength) { boolean ok; if(el == null) { ok = true; } else { String val = el.getValue(); el.clearError(); if(val != null && val.length() > maxLength) { el.setErrorKey("input.toolong", new String[]{ Integer.toString(maxLength) }); ok = false; } else { ok = true; } } return ok; } @Override protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { if (source == dateTypesEl) { updateDatesVisibility(); } else if (source == fileUpload) { if(FileElementEvent.DELETE.equals(event.getCommand())) { fileUpload.clearError(); VFSLeaf img = repositoryManager.getImage(repositoryEntry); if(fileUpload.getUploadFile() != null && fileUpload.getUploadFile() != fileUpload.getInitialFile()) { fileUpload.reset(); if(img != null) { fileUpload.setInitialFile(((LocalFileImpl)img).getBasefile()); } } else if(img != null) { repositoryManager.deleteImage(repositoryEntry); fileUpload.setInitialFile(null); } flc.setDirty(true); } } else if (source == movieUpload) { if(FileElementEvent.DELETE.equals(event.getCommand())) { movieUpload.clearError(); VFSLeaf movie = repositoryService.getIntroductionMovie(repositoryEntry); if(movieUpload.getUploadFile() != null && movieUpload.getUploadFile() != movieUpload.getInitialFile()) { movieUpload.reset(); if(movie != null) { movieUpload.setInitialFile(((LocalFileImpl)movie).getBasefile()); } } else if(movie != null) { movie.delete(); movieUpload.setInitialFile(null); } flc.setDirty(true); } } super.formInnerEvent(ureq, source, event); } @Override protected void formOK(UserRequest ureq) { File uploadedImage = fileUpload.getUploadFile(); if(uploadedImage != null && uploadedImage.exists()) { VFSContainer tmpHome = new LocalFolderImpl(new File(WebappHelper.getTmpDir())); VFSContainer container = tmpHome.createChildContainer(UUID.randomUUID().toString()); VFSLeaf newFile = fileUpload.moveUploadFileTo(container);//give it it's real name and extension boolean ok = repositoryManager.setImage(newFile, repositoryEntry); if (!ok) { showWarning("cif.error.image"); } else { VFSLeaf image = repositoryManager.getImage(repositoryEntry); if(image instanceof LocalFileImpl) { fileUpload.setInitialFile(((LocalFileImpl)image).getBasefile()); } } container.delete(); } File uploadedMovie = movieUpload.getUploadFile(); if(uploadedMovie != null && uploadedMovie.exists()) { VFSContainer m = (VFSContainer)mediaContainer.resolve("media"); VFSLeaf newFile = movieUpload.moveUploadFileTo(m); if (newFile == null) { showWarning("cif.error.movie"); } else { String filename = movieUpload.getUploadFileName(); String extension = FileUtils.getFileSuffix(filename); newFile.rename(repositoryEntry.getKey() + "." + extension); } } String displayname = displayName.getValue().trim(); repositoryEntry.setDisplayname(displayname); String mainLanguage = language.getValue(); if(StringHelper.containsNonWhitespace(mainLanguage)) { repositoryEntry.setMainLanguage(mainLanguage); } else { repositoryEntry.setMainLanguage(null); } if(dateTypesEl != null) { String type = "none"; if(dateTypesEl.isOneSelected()) { type = dateTypesEl.getSelectedKey(); } if("none".equals(type)) { repositoryEntry.setLifecycle(null); } else if("public".equals(type)) { String key = publicDatesEl.getSelectedKey(); if(StringHelper.isLong(key)) { Long cycleKey = Long.parseLong(key); RepositoryEntryLifecycle cycle = lifecycleDao.loadById(cycleKey); repositoryEntry.setLifecycle(cycle); } } else if("private".equals(type)) { Date start = startDateEl.getDate(); Date end = endDateEl.getDate(); RepositoryEntryLifecycle cycle = repositoryEntry.getLifecycle(); if(cycle == null || !cycle.isPrivateCycle()) { String softKey = "lf_" + repositoryEntry.getSoftkey(); cycle = lifecycleDao.create(displayname, softKey, true, start, end); } else { cycle.setValidFrom(start); cycle.setValidTo(end); cycle = lifecycleDao.updateLifecycle(cycle); } repositoryEntry.setLifecycle(cycle); } } if(externalRef != null && externalRef.isEnabled()) { String ref = externalRef.getValue().trim(); repositoryEntry.setExternalRef(ref); } String desc = description.getValue().trim(); repositoryEntry.setDescription(desc); if(authors != null) { String auth = authors.getValue().trim(); repositoryEntry.setAuthors(auth); } if(objectives != null) { String obj = objectives.getValue().trim(); repositoryEntry.setObjectives(obj); } if(requirements != null) { String req = requirements.getValue().trim(); repositoryEntry.setRequirements(req); } if(credits != null) { String cred = credits.getValue().trim(); repositoryEntry.setCredits(cred); } if(expenditureOfWork != null) { String exp = expenditureOfWork.getValue().trim(); repositoryEntry.setExpenditureOfWork(exp); } if(location != null) { String loc = location.getValue().trim(); repositoryEntry.setLocation(loc); } repositoryEntry = repositoryManager.setDescriptionAndName(repositoryEntry, repositoryEntry.getDisplayname(), repositoryEntry.getExternalRef(), repositoryEntry.getAuthors(), repositoryEntry.getDescription(), repositoryEntry.getObjectives(), repositoryEntry.getRequirements(), repositoryEntry.getCredits(), repositoryEntry.getMainLanguage(), repositoryEntry.getLocation(), repositoryEntry.getExpenditureOfWork(), repositoryEntry.getLifecycle()); if(repositoryEntry == null) { showWarning("repositoryentry.not.existing"); fireEvent(ureq, Event.CLOSE_EVENT); } else { fireEvent(ureq, Event.CHANGED_EVENT); } } @Override protected void formCancelled(UserRequest ureq) { fireEvent(ureq, Event.CANCELLED_EVENT); } /** * @return Returns the repositoryEntry. */ public RepositoryEntry getRepositoryEntry() { return repositoryEntry; } }