diff --git a/src/main/java/org/olat/core/commons/services/vfs/manager/VFSRepositoryServiceImpl.java b/src/main/java/org/olat/core/commons/services/vfs/manager/VFSRepositoryServiceImpl.java index 92aaf039825bc868708ead68e254703567f97773..807e8155d34e0ac1e84c0438459cc6f89d9571c0 100644 --- a/src/main/java/org/olat/core/commons/services/vfs/manager/VFSRepositoryServiceImpl.java +++ b/src/main/java/org/olat/core/commons/services/vfs/manager/VFSRepositoryServiceImpl.java @@ -1248,6 +1248,8 @@ public class VFSRepositoryServiceImpl implements VFSRepositoryService, GenericEv @Override public boolean hasEditor(String suffix, Mode mode) { + if (mode == null) return false; + return vfsLeafEditors.stream() .filter(VFSLeafEditor::isEnable) .filter(editor -> editor.isSupportingFormat(suffix, mode)) diff --git a/src/main/java/org/olat/modules/portfolio/ui/MediaCenterController.java b/src/main/java/org/olat/modules/portfolio/ui/MediaCenterController.java index 64db16f3d647adfeba6ac13665418aae46201ce8..705b5e5fe83f365163b0d70aa88015ae57994a37 100644 --- a/src/main/java/org/olat/modules/portfolio/ui/MediaCenterController.java +++ b/src/main/java/org/olat/modules/portfolio/ui/MediaCenterController.java @@ -19,6 +19,8 @@ */ package org.olat.modules.portfolio.ui; +import static org.olat.core.commons.services.vfs.VFSLeafEditor.Mode.EDIT; + import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -29,10 +31,15 @@ import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import org.olat.core.commons.persistence.SortKey; +import org.olat.core.commons.services.filetemplate.FileType; +import org.olat.core.commons.services.filetemplate.FileTypes; +import org.olat.core.commons.services.filetemplate.FileTypes.Builder; import org.olat.core.commons.services.image.Size; +import org.olat.core.commons.services.vfs.VFSRepositoryService; import org.olat.core.dispatcher.mapper.Mapper; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; +import org.olat.core.gui.components.dropdown.Dropdown; 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.FlexiTableElement; @@ -83,6 +90,7 @@ import org.olat.modules.portfolio.ui.event.MediaEvent; import org.olat.modules.portfolio.ui.event.MediaSelectionEvent; import org.olat.modules.portfolio.ui.media.CollectCitationMediaController; import org.olat.modules.portfolio.ui.media.CollectTextMediaController; +import org.olat.modules.portfolio.ui.media.CreateFileMediaController; import org.olat.modules.portfolio.ui.model.MediaRow; import org.olat.modules.portfolio.ui.renderer.MediaTypeCellRenderer; import org.olat.portfolio.PortfolioModule; @@ -109,16 +117,18 @@ public class MediaCenterController extends FormBasicController private FormLink newMediaCallout; private FlexiTableElement tableEl; private String mapperThumbnailUrl; - private Link addFileLink, addMediaLink, addTextLink, addCitationLink, importArtefactV1Link; + private Link addFileLink, createFileLink, addMediaLink, addTextLink, addCitationLink, importArtefactV1Link; private int counter = 0; private final boolean select; + private final List<FileType> editableFileTypes; private List<FormLink> tagLinks; private final TooledStackedPanel stackPanel; private CloseableModalController cmc; private MediaDetailsController detailsCtrl; private MediaUploadController mediaUploadCtrl; + private CreateFileMediaController createFileCtrl; private CollectTextMediaController textUploadCtrl; private EPArtefactPoolRunController importArtefactv1Ctrl; private CollectCitationMediaController citationUploadCtrl; @@ -133,11 +143,15 @@ public class MediaCenterController extends FormBasicController private EPFrontendManager legacyEPFontentManager; @Autowired private PortfolioModule legacyPortfolioModule; + @Autowired + private VFSRepositoryService vfsService; + public MediaCenterController(UserRequest ureq, WindowControl wControl) { super(ureq, wControl, "medias"); this.stackPanel = null; this.select = true; + this.editableFileTypes = getEditableFileTypes(); initForm(ureq); loadModel(); @@ -147,16 +161,43 @@ public class MediaCenterController extends FormBasicController super(ureq, wControl, "medias"); this.stackPanel = stackPanel; this.select = false; + this.editableFileTypes = getEditableFileTypes(); initForm(ureq); loadModel(); } + + private List<FileType> getEditableFileTypes() { + Builder builder = FileTypes.builder(getLocale()); + if (vfsService.hasEditor("docx", EDIT)) { + builder.addDocx(); + } + if (vfsService.hasEditor("xlsx", EDIT)) { + builder.addXlsx(); + } + return builder.build().getFileTypes(); + } @Override public void initTools() { - addFileLink = LinkFactory.createToolLink("add.file", translate("add.file"), this); - addFileLink.setIconLeftCSS("o_icon o_icon-lg o_icon_files"); - stackPanel.addTool(addFileLink, Align.left); + if (editableFileTypes.isEmpty()) { + addFileLink = LinkFactory.createToolLink("add.file", translate("add.file"), this); + addFileLink.setIconLeftCSS("o_icon o_icon-lg o_icon_files"); + stackPanel.addTool(addFileLink, Align.left); + } else { + Dropdown addDropdown = new Dropdown("add.file", "add.file", false, getTranslator()); + addDropdown.setIconCSS("o_icon o_icon-lg o_icon_files"); + + createFileLink = LinkFactory.createToolLink("create.file", translate("create.file"), this); + createFileLink.setIconLeftCSS("o_icon o_icon-fw o_icon_add"); + addDropdown.addComponent(createFileLink); + + addFileLink = LinkFactory.createToolLink("upload.file", translate("upload.file"), this); + addFileLink.setIconLeftCSS("o_icon o_icon-fw o_icon_upload"); + addDropdown.addComponent(addFileLink); + + stackPanel.addTool(addDropdown, Align.left); + } addMediaLink = LinkFactory.createToolLink("add.media", translate("add.media"), this); addMediaLink.setIconLeftCSS("o_icon o_icon-lg o_icon_media"); @@ -372,7 +413,8 @@ public class MediaCenterController extends FormBasicController @Override public void event(UserRequest ureq, Controller source, Event event) { - if(mediaUploadCtrl == source || textUploadCtrl == source || citationUploadCtrl == source) { + if (createFileCtrl == source || mediaUploadCtrl == source || textUploadCtrl == source + || citationUploadCtrl == source) { if(event == Event.DONE_EVENT) { loadModel(); tableEl.reloadData(); @@ -381,7 +423,9 @@ public class MediaCenterController extends FormBasicController cleanUp(); if(select || event == Event.DONE_EVENT) { - if(mediaUploadCtrl == source) { + if(createFileCtrl == source) { + doSelect(ureq, createFileCtrl.getMediaReference().getKey()); + } else if(mediaUploadCtrl == source) { doSelect(ureq, mediaUploadCtrl.getMediaReference().getKey()); } else if(textUploadCtrl == source) { doSelect(ureq, textUploadCtrl.getMediaReference().getKey()); @@ -445,18 +489,22 @@ public class MediaCenterController extends FormBasicController removeAsListenerAndDispose(importArtefactv1Ctrl); removeAsListenerAndDispose(citationUploadCtrl); removeAsListenerAndDispose(mediaUploadCtrl); + removeAsListenerAndDispose(createFileCtrl); removeAsListenerAndDispose(textUploadCtrl); removeAsListenerAndDispose(cmc); importArtefactv1Ctrl = null; citationUploadCtrl = null; mediaUploadCtrl = null; + createFileCtrl = null; textUploadCtrl = null; cmc = null; } @Override public void event(UserRequest ureq, Component source, Event event) { - if(addFileLink == source) { + if(createFileLink == source) { + doCreateFile(ureq); + } else if(addFileLink == source) { doAddMedia(ureq, "add.file"); } else if(addMediaLink == source) { doAddMedia(ureq, "add.media"); @@ -495,6 +543,18 @@ public class MediaCenterController extends FormBasicController protected void formOK(UserRequest ureq) { // } + + private void doCreateFile(UserRequest ureq) { + if(createFileCtrl != null) return; + + createFileCtrl = new CreateFileMediaController(ureq, getWindowControl(), editableFileTypes); + listenTo(createFileCtrl); + + String title = translate("create.file.title"); + cmc = new CloseableModalController(getWindowControl(), null, createFileCtrl.getInitialComponent(), true, title, true); + listenTo(cmc); + cmc.activate(); + } private void doAddMedia(UserRequest ureq, String titleKey) { if(mediaUploadCtrl != null) return; diff --git a/src/main/java/org/olat/modules/portfolio/ui/MediaDetailsController.java b/src/main/java/org/olat/modules/portfolio/ui/MediaDetailsController.java index eb58a36a38bc71234822d175df35321a1f8edc9f..212c0aadcf42fe7af6275dfff0909fcfcff73fd6 100644 --- a/src/main/java/org/olat/modules/portfolio/ui/MediaDetailsController.java +++ b/src/main/java/org/olat/modules/portfolio/ui/MediaDetailsController.java @@ -61,6 +61,7 @@ import org.olat.modules.portfolio.manager.MetadataXStream; import org.olat.modules.portfolio.model.BinderPageUsage; import org.olat.modules.portfolio.model.StandardMediaRenderingHints; import org.olat.modules.portfolio.ui.event.MediaEvent; +import org.olat.modules.portfolio.ui.media.FileMediaController; import org.olat.user.UserManager; import org.springframework.beans.factory.annotation.Autowired; @@ -131,6 +132,10 @@ public class MediaDetailsController extends FormBasicController implements Activ mediaCtrl = handler.getMediaController(ureq, getWindowControl(), media, new StandardMediaRenderingHints()); if(mediaCtrl != null) { + // Move this to the MediaHandler if even more Media types are editable inline. + if (mediaCtrl instanceof FileMediaController && editable) { + ((FileMediaController)mediaCtrl).setEditMode(editable); + } listenTo(mediaCtrl); layoutCont.put("media", mediaCtrl.getInitialComponent()); } diff --git a/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_de.properties index edf891978da89353feb31a06a02228880e5d9c52..6d05d98a23cebf06fc50e8cbf14c6fe253e85281 100644 --- a/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_de.properties @@ -14,8 +14,8 @@ access.rights.reviewer.long=als Gutachter (lesen / kommentieren) action=Action add.citation=Zitat hinzuf\u00FCgen add.container=Container hinzuf\u00FCgen -add.course.coach=Kursbetreuer ausw\u00e4hlen -add.course.participant=Kursmitglieder ausw\u00e4hlen +add.course.coach=Kursbetreuer ausw\u00E4hlen +add.course.participant=Kursmitglieder ausw\u00E4hlen add.file=Dokument hinzuf\u00FCgen add.html=HTML hinzuf\u00FCgen add.image=Bild hinzuf\u00FCgen @@ -23,19 +23,19 @@ add.invitation=Einladung hinzuf\u00FCgen add.media=Mediendatei hinzuf\u00FCgen add.member=Mitglied hinzuf\u00FCgen add.template.form=Fragebogen hinzuf\u00FCgen -add.template.document=Dokumente hinzufügen +add.template.document=Dokumente hinzuf\uFFFDgen add.text=Text hinzuf\u00FCgen add.video=Video hinzuf\u00FCgen all.binder={0} weitere Mappe vorhanden all.binders={0} weitere Mappen vorhanden all.page={0} weitere Beitrag vorhanden -all.pages={0} weitere Beitr\u00e4ge vorhanden -allow.delete.binder=Benutzer d\u00FCrfen Mappe l\u00f6schen -allow.delete.binder.warning=Jetzt d\u00FCrfen Benutzer Ihre Mappe l\u00f6schen. -allow.new.entries=Benutzer d\u00FCrfen neue Eintr\u00e4ge erstellen +all.pages={0} weitere Beitr\u00E4ge vorhanden +allow.delete.binder=Benutzer d\u00FCrfen Mappe l\u00F6schen +allow.delete.binder.warning=Jetzt d\u00FCrfen Benutzer Ihre Mappe l\u00F6schen. +allow.new.entries=Benutzer d\u00FCrfen neue Eintr\u00E4ge erstellen allow.templates.folder=Vorlagenordner hinzuf\u00FCgen -allow.templates.mandatory=Vorlage erforderlich für neue Eintr\u00e4ge -anonym.evaluator=Fremdeinsch\u00e4tzung {0} +allow.templates.mandatory=Vorlage erforderlich f\uFFFDr neue Eintr\u00E4ge +anonym.evaluator=Fremdeinsch\u00E4tzung {0} artefact.EfficiencyStatement=Leistungnachweis artefact.FileResource.BLOG=Blog Eintrag artefact.FileResource.WIKI=Wikiseite @@ -55,12 +55,12 @@ assignment=Aufgabe assignment.content=Aufgabe assignment.document.upload=Dokument assignment.documents=Dokumente zur Aufgabe -assignment.evaluation.form=Einsch\u00e4tzung -assignment.evaluation.form.anonymous.extern=Anonyme Fremdeinsch\u00e4tzung -assignment.evaluation.form.auto=Nur Selbsteinsch\u00e4tzung -assignment.evaluation.form.auto.extern=Selbsteinsch\u00e4tzung und Fremdeinsch\u00e4tzung +assignment.evaluation.form=Einsch\u00E4tzung +assignment.evaluation.form.anonymous.extern=Anonyme Fremdeinsch\u00E4tzung +assignment.evaluation.form.auto=Nur Selbsteinsch\u00E4tzung +assignment.evaluation.form.auto.extern=Selbsteinsch\u00E4tzung und Fremdeinsch\u00E4tzung assignment.evaluation.form.entry=Fragebogen -assignment.evaluation.form.reviewer.see.auto=Selbsteinsch\u00e4tzung an eingeladenen Benutzer anzeigen +assignment.evaluation.form.reviewer.see.auto=Selbsteinsch\u00E4tzung an eingeladenen Benutzer anzeigen assignment.file=Datei assignment.hide=Aufgabenstellung verbergen assignment.in.use=Die Aufgabe wird bereits verwendet. Die Bearbeitung ist begrenzt. @@ -76,16 +76,16 @@ assignments.templates=Aus Vorlage ausw\u00E4hlen assignments.templates.without=Ohne Vorlage attachments=Dokumenten attachments.error.file.exists=Diese Datei existiert bereits und kann nicht erneut hochgeladen werden. -attachments.upload.successful=Die Datei {0} wurde erfolgreich hochgeladen. Bei Bedarf k\u00f6nnen noch weitere Dateien angeh\u00e4ngt werden. +attachments.upload.successful=Die Datei {0} wurde erfolgreich hochgeladen. Bei Bedarf k\u00F6nnen noch weitere Dateien angeh\u00E4ngt werden. author=Autor begin.date=Beginn -binder.atleastone=Bitte w\u00e4hlen Sie mindestens eine Mappe. +binder.atleastone=Bitte w\u00E4hlen Sie mindestens eine Mappe. binder.by=von {0} binder.entry.name=Zum Kurs -binder.last.update=Letzte \u00c4nderung +binder.last.update=Letzte \u00C4nderung binder.none=Floating Eintrag binder.num.comments={0} Kommentare seit Erstelldatum -binder.num.sections.pages={0} Bereich(e) mit insgesamt {1} Eintr\u00e4gen +binder.num.sections.pages={0} Bereich(e) mit insgesamt {1} Eintr\u00E4gen binder.status=Status binder.title=Portfolio Mappe {0} binders=Mappe @@ -93,7 +93,7 @@ bookmark=Bookmark categories=Kategorien categories.add=Kategorien hinzuf\u00FCgen categories.hint=Geben Sie den gew\u00FCnschten Text ein und dr\u00FCcken Sie die Eingabetaste, um eine Kategorie ihrer Wahl zu erstellen. -changes.since=\u00c4nderungen seit +changes.since=\u00C4nderungen seit citation=Zitat close=Eintrag schliessen close.confirm.descr=Wollen Sie diesen Eintrag "{0}" schliessen? <p>Das bedeutet, der Eintrag wird f\u00FCr den Benutzer als abgeschlossen angezeigt und kann nicht mehr bearbeitet werden.</p> @@ -101,7 +101,7 @@ close.confirm.title=Eintrag schliessen close.page=Eintrag schliessen close.page.title=Der Eintrag wird f\u00FCr den Benutzer als geschlossen angezeigt und kann nicht mehr bearbeitet werden. close.section=Bereich schliessen -close.section.confirm.descr=Wollen Sie diesen Bereich "{0}" mit alle Eintr\u00e4ge schliessen? +close.section.confirm.descr=Wollen Sie diesen Bereich "{0}" mit alle Eintr\u00E4ge schliessen? close.section.confirm.title=Bereich schliessen command.openassessment=Bewertungswerkzeug comment.one=1 Kommentar @@ -109,7 +109,7 @@ comment.several={0} Kommentare comment.title=Kommentare comment.zero=Noch keine Kommentare compare.evaluations=Auswertung -confirmation=Best\u00e4tigung +confirmation=Best\u00E4tigung confirm.close.page=Wollen Sie diesen Eintrag abschliessen? Der Eintrag wird f\u00FCr den Lernenden anschliessend als abgeschlossen angezeigt. confirm.close.page.other.coaches=Die folgende Personen haben auch Zugriff auf diesen Eintrag: content.title=Inhalt @@ -117,44 +117,49 @@ create.binder=Mappe erstellen create.empty.binder=Leere Mappe erstellen create.empty.binder.from.course=Mappe f\u00FCr Portfolioaufgabe aus Kurs erstellen create.empty.binder.from.template=Mappe basierend auf Vorlage erstellen +create.file=Erstellen +create.file.name=$org.olat.core.commons.services.filetemplate.ui\:create.file.name +create.file.name.notvalid=$org.olat.core.commons.services.filetemplate.ui\:create.file.name.notvalid +create.file.title=Dokument erstellen +create.file.type=$org.olat.core.commons.services.filetemplate.ui\:create.file.type create.new.assignment=Neue Aufgabe create.new.assignment.descr=Neue Aufgabe erlaubt ... create.new.assignment.title=Neue Aufgabe erstellen create.new.binder=Neue Mappe erstellen -create.new.binder.descr=Erstellen Sie eine Portfolio Mappe, um Ihre Eintr\u00e4ge zu strukturieren und zu organisieren. Sie k\u00f6nnen eine Mappe zum Kommentieren und Bewerten f\u00FCr andere Benutzer freigeben. +create.new.binder.descr=Erstellen Sie eine Portfolio Mappe, um Ihre Eintr\u00E4ge zu strukturieren und zu organisieren. Sie k\u00F6nnen eine Mappe zum Kommentieren und Bewerten f\u00FCr andere Benutzer freigeben. create.new.binder.title=Neue Mappe erstellen create.new.page=Neuen Eintrag erstellen -create.new.page.descr=Erstellen Sie einen neuen Portfolioeintrag, um z.B. ihren Lernprozess zu reflektieren oder anderen Benutzern Ihre Arbeit oder ein bestimmtes Thema zu pr\u00e4sentieren. Portfolioeintr\u00e4ge sind privat und werden erst dann sichtbar, wenn sie f\u00FCr ihren Betreuer oder andere Benutzer sichtbar gemacht werden. +create.new.page.descr=Erstellen Sie einen neuen Portfolioeintrag, um z.B. ihren Lernprozess zu reflektieren oder anderen Benutzern Ihre Arbeit oder ein bestimmtes Thema zu pr\u00E4sentieren. Portfolioeintr\u00E4ge sind privat und werden erst dann sichtbar, wenn sie f\u00FCr ihren Betreuer oder andere Benutzer sichtbar gemacht werden. create.new.page.title=Neuen Eintrag erstellen create.new.section=Neuen Bereich erstellen -create.new.section.text=Erstellen Sie einen neuen Bereich in der Portfolio Mappe um Ihre Eintr\u00e4ge zu organisieren. +create.new.section.text=Erstellen Sie einen neuen Bereich in der Portfolio Mappe um Ihre Eintr\u00E4ge zu organisieren. create.new.section.title=$\:create.new.section create.page=Neuer Eintrag erstellen create.section=Bereich hinzuf\u00FCgen create.start.assignment=Aufgabe bearbeiten creators=Ersteller -delete.assignment.confirm.descr=Wollen Sie diese Aufgabe "{0}" wirklich l\u00f6schen? -delete.assignment.confirm.title=Aufgabe l\u00f6schen +delete.assignment.confirm.descr=Wollen Sie diese Aufgabe "{0}" wirklich l\u00F6schen? +delete.assignment.confirm.title=Aufgabe l\u00F6schen delete.assignment.in.use.confirm.descr=$\:delete.assignment.confirm.descr <div class\='o_warning'>Es gibt bereits Benutzer, die mit der Bearbeitung begonnen haben.</div> -delete.assignment.template.confirm.descr=Wollen Sie diese Vorlage "{0}" wirklich l\u00f6schen? -delete.assignment.template.confirm.title=Vorlage l\u00f6schen -delete.binder=Mappe l\u00f6schen -delete.binder.acknowledge=Ich verstehe, dass die Mappe und alle ihre Eintr\u00e4ge werden definitive gel\u00f6scht. +delete.assignment.template.confirm.descr=Wollen Sie diese Vorlage "{0}" wirklich l\u00F6schen? +delete.assignment.template.confirm.title=Vorlage l\u00F6schen +delete.binder=Mappe l\u00F6schen +delete.binder.acknowledge=Ich verstehe, dass die Mappe und alle ihre Eintr\u00E4ge werden definitive gel\u00F6scht. delete.binder.acknowledge.2=Ich verstehe dass ich die Mappe nicht mehr wiedererstellen kann. -delete.binder.success=Die Mappe wurde erfolgreich gel\u00f6scht. -delete.binder.warning=Wollen Sie wirklich die Mappe "{0}" l\u00f6schen? Sie enth\u00e4lt <strong>{1}</strong> Bereiche, <strong>{2}</strong> Eintr\u00e4ge und <strong>{3}</strong> Kommentar. -delete.def.page=Eintrag l\u00f6schen -delete.def.page.confirm.descr=Wollen Sie wirklich den folgenden Eintrag "{0}" l\u00f6schen? -delete.def.page.confirm.title=Eintrag l\u00f6schen -delete.def.pages.confirm.descr=Wollen Sie wirklich den folgenden Eintr\u00e4ge "{0}" l\u00f6schen? -delete.def.pages.confirm.title={0} Eintr\u00e4ge l\u00f6schen -delete.media.confirm.descr=Wollen Sie wirklich diesem Media "{0}" l\u00f6schen? -delete.media.confirm.title=Media l\u00f6schen +delete.binder.success=Die Mappe wurde erfolgreich gel\u00F6scht. +delete.binder.warning=Wollen Sie wirklich die Mappe "{0}" l\u00F6schen? Sie enth\u00E4lt <strong>{1}</strong> Bereiche, <strong>{2}</strong> Eintr\u00E4ge und <strong>{3}</strong> Kommentar. +delete.def.page=Eintrag l\u00F6schen +delete.def.page.confirm.descr=Wollen Sie wirklich den folgenden Eintrag "{0}" l\u00F6schen? +delete.def.page.confirm.title=Eintrag l\u00F6schen +delete.def.pages.confirm.descr=Wollen Sie wirklich den folgenden Eintr\u00E4ge "{0}" l\u00F6schen? +delete.def.pages.confirm.title={0} Eintr\u00E4ge l\u00F6schen +delete.media.confirm.descr=Wollen Sie wirklich diesem Media "{0}" l\u00F6schen? +delete.media.confirm.title=Media l\u00F6schen delete.page=Eintrag in Papierkorb legen delete.page.confirm.descr=Wollen Sie wirklich den folgenden Eintrag "{0}" entfernen? delete.page.confirm.title=Eintrag entfernen -delete.section.confirm.descr=Wollen Sie wirklich diesem Bereich "{0}" l\u00f6schen? -delete.section.confirm.title=Bereich l\u00f6schen +delete.section.confirm.descr=Wollen Sie wirklich diesem Bereich "{0}" l\u00F6schen? +delete.section.confirm.title=Bereich l\u00F6schen deleted.entries=Papierkorb deleted.pages.breadcrump=Papierkorb document.by=von {0} @@ -164,16 +169,16 @@ down=<i class\='o_icon o_icon o_icon-lg o_icon_move_down'> </i> edit.access.rights=Zugangsrechte bearbeiten edit.assignment=Aufgabe bearbeiten edit.binder.metadata=Metadaten bearbeiten -edit.last.binder=Letzte benutzte Sammelmappe \u00f6ffnen +edit.last.binder=Letzte benutzte Sammelmappe \u00F6ffnen edit.last.entry=Letzten Eintrag bearbeiten -edit.page=Editor \u00f6ffnen +edit.page=Editor \u00F6ffnen edit.page.close=Editor schliessen edit.page.meta=Eintrag editieren edit.page.metadata=Metadaten bearbeiten edition=Edition end.date=Enddatum error.begin.after.end=Das Beginn-Datum muss vor dem Ende-Datum liegen. -error.existing.invitee.selection=Bitte, wählen Sie einen bestehenden Benutzer aus. +error.existing.invitee.selection=Bitte, w\uFFFDhlen Sie einen bestehenden Benutzer aus. error.invalid.type=Dieser Dateityp ist nicht unters\u00FCtzt. error.invitation.mail.used=Diese E-Mailadresse wird bereits von einem OpenOLAT-Benutzer verwendet. error.mail.invalid=Bitte geben Sie eine g\u00FCltige E-Mailadresse an. @@ -191,10 +196,10 @@ fileupload=Titelbild filter.sections.open=Offene Sektionen filter.show.all=Alle anzeigen firstName=Vorname -go.to.trash=Papierkorb \u00f6ffnen -goto.media.center=Mediathek \u00f6ffnen +go.to.trash=Papierkorb \u00F6ffnen +goto.media.center=Mediathek \u00F6ffnen goto.my.binders=Zu meinen Mappen -goto.my.pages=Zeige meine Eintr\u00e4ge +goto.my.pages=Zeige meine Eintr\u00E4ge goto.my.shared.items=Zu meinen freigegebenen Mappen goto.original=Original anzeigen goto.shared.with.me=Zeige an mich freigegebene Mappen @@ -210,7 +215,7 @@ invitation.extern.mail.body=Sie wurden von {2} {3} eingeladen, eine Sammelmappe invitation.extern.mail.subject=Einladung zu einer freigegebenen Sammelmappe invitation.link=Link invitation.mail.body=Sie wurden von {1} eingeladen, eine Sammelmappe in OpenOLAT zu betrachten. Sie finden diese unter folgender Adresse\: {0} -invitation.mail.failure=Fehler beim Versenden der E-Mail. Die eingeladenen Personen konnten nicht per E-Mail benachrichtigt werden. Versuchen Sie es sp\u00e4ter noch einmal oder kontaktieren Sie den Support. +invitation.mail.failure=Fehler beim Versenden der E-Mail. Die eingeladenen Personen konnten nicht per E-Mail benachrichtigt werden. Versuchen Sie es sp\u00E4ter noch einmal oder kontaktieren Sie den Support. invitation.mail.subject=Einladung zu einer freigegebenen Sammelmappe invitation.mail.success=Die eingeladenen Personen wurden erfolgreich per E-Mail benachrichtigt. issue=Ausgabe @@ -218,7 +223,7 @@ language=Language last.used=Zuletzt benutzt lastName=Name leave=Entfernen -leave.explain=Wollen Sie diese Mappe wirklich entfernen? Sie haben anschliessend keinen Zugriff mehr. Damit Sie die Mappe wieder sehen k\u00f6nnen, muss Ihnen der Besitzer der Mappe erneut eine Freigabe erteilen. +leave.explain=Wollen Sie diese Mappe wirklich entfernen? Sie haben anschliessend keinen Zugriff mehr. Damit Sie die Mappe wieder sehen k\u00F6nnen, muss Ihnen der Besitzer der Mappe erneut eine Freigabe erteilen. mail=Mail mail.body=Nachricht mail.subject=Betreffzeile @@ -235,13 +240,13 @@ map.returnDate=$org.olat.course.nodes.portfolio\:map.returnDate map.share.with.mail.error.olatUser=Diese E-Mailadresse wird bereits von einem OpenOLAT-Benutzer verwendet. map.template=Portfoliovorlage media.center=Mediathek -media.center.text=Hier k\u00f6nnen Sie Mediendateien, Dokumente oder andere Artefakte hinzuf\u00FCgen. Verwenden Sie die Medien, um Ihre Eintr\u00e4ge zu illustrieren und zu erg\u00e4nzen. +media.center.text=Hier k\u00F6nnen Sie Mediendateien, Dokumente oder andere Artefakte hinzuf\u00FCgen. Verwenden Sie die Medien, um Ihre Eintr\u00E4ge zu illustrieren und zu erg\u00E4nzen. message.imported.successfully={0} Medien erfolgreich importiert. meta.assignment=Aufgabe meta.binder.section.titles=In Mappe "{0}", Bereich "{1}" meta.categories=Kategorien meta.last.modified=zuletzt bearbeitet am {0} -meta.last.publication=ver\u00f6ffentlich am {0} +meta.last.publication=ver\u00F6ffentlich am {0} meta.page.assignment=Hier ist eine Aufgabe. meta.page.assignment.type=Typ meta.section.assignments=<strong>Aufgabe</strong> in diesem Bereich @@ -255,7 +260,7 @@ mf.isbn=ISBN mf.issue=Ausgabe mf.lastVisitDate=Datum des letzten Zugriffs mf.pages=Seitenangabe -mf.publicationDate=Datum der Ver\u00f6ffentlichung +mf.publicationDate=Datum der Ver\u00F6ffentlichung mf.publicationTitle=Zeitschriftentitel mf.series=Serien mf.sourceType=Typ @@ -267,15 +272,15 @@ mf.sourceType.webpage=Webauftritt mf.volume=Volume move=Schieben move.assignment=Aufgabe schieben -my.entries=Meine Eintr\u00e4ge -my.entries.text=Liste aller Eintr\u00e4ge in chronologischer Reihenfolge unabh\u00e4ngig von ihrem Kontext. Dies ist das Herzst\u00FCck Ihrer Portfolioarbeit. +my.entries=Meine Eintr\u00E4ge +my.entries.text=Liste aller Eintr\u00E4ge in chronologischer Reihenfolge unabh\u00E4ngig von ihrem Kontext. Dies ist das Herzst\u00FCck Ihrer Portfolioarbeit. my.portfolio=Mein Portfolio my.portfolio.binders=Meine Portfoliomappen my.portfolio.binders.breadcrump=Meine Mappen -my.portfolio.binders.text=Arbeiten Sie mit einer spezifischen Mappe. Eine Mappe stellt Ihre Eintr\u00e4ge und Medien in einen Kontext und vereinfacht die Zugangskonfiguration. -my.portfolio.pages.breadcrump=Meine Eintr\u00e4ge +my.portfolio.binders.text=Arbeiten Sie mit einer spezifischen Mappe. Eine Mappe stellt Ihre Eintr\u00E4ge und Medien in einen Kontext und vereinfacht die Zugangskonfiguration. +my.portfolio.pages.breadcrump=Meine Eintr\u00E4ge my.shared.items=Von mir freigegeben -my.shared.items.text=Die Liste aller Eintr\u00e4ge, die Sie an andere Benutzer freigegeben haben. +my.shared.items.text=Die Liste aller Eintr\u00E4ge, die Sie an andere Benutzer freigegeben haben. new.entry=Neuen Eintrag erstellen new.medias=Neues Objekt zur Mediathek hinzuf\u00FCgen new.section.desc=Beschreibung des Bereiches @@ -283,24 +288,24 @@ new.section.title=Bereich no.binders.template.available=Es steht kein Kursvorlage zu Verf\u00FCgung. no.map=$org.olat.course.nodes.portfolio\:no.map not.implemented=Nicht implementiert -notifications.modified.evaluation=Einsch\u00e4tzung '{0}' ge\u00e4ndert -notifications.modified.page=Eintrag '{0}' ge\u00e4ndert -notifications.modified.section=Bereich '{0}' ge\u00e4ndert +notifications.modified.evaluation=Einsch\u00E4tzung '{0}' ge\u00E4ndert +notifications.modified.page=Eintrag '{0}' ge\u00E4ndert +notifications.modified.section=Bereich '{0}' ge\u00E4ndert notifications.new.comment=Neuer Kommentar zu '{0}' von {1} -notifications.new.evaluation=Neue Einsch\u00e4tzung '{0}' +notifications.new.evaluation=Neue Einsch\u00E4tzung '{0}' notifications.new.page=Neuer Eintrag '{0}' hinzugef\u00FCgt notifications.new.section=Neuer Bereich '{0}' hinzugef\u00FCgt -open=\u00d6ffnen -open.assignment.template=\u00d6ffnen +open=\u00D6ffnen +open.assignment.template=\u00D6ffnen open.full.page=Den ganzen Eintrag lesen open.map=$org.olat.course.nodes.portfolio\:open.map open.part=\u00D6ffentlicher Bereich -override.dates.section=Verl\u00e4ngern -page.atleastone=Bitte w\u00e4hlen Sie mindestens ein Eintrag aus. +override.dates.section=Verl\u00E4ngern +page.atleastone=Bitte w\u00E4hlen Sie mindestens ein Eintrag aus. page.binders=Mappe page.paging.previous=Zum vorherigen Eintrag -page.paging.next=Zum n\u00e4chsten Eintrag -page.paging.all=Alle Eintr\u00e4ge +page.paging.next=Zum n\u00E4chsten Eintrag +page.paging.all=Alle Eintr\u00E4ge page.sections=Bereich page.status=Status page.summary=$\:summary @@ -311,9 +316,9 @@ passed.true=$org.olat.course.assessment\:passed.true passed.yourpassed=$org.olat.course.assessment\:passed.yourpassed place=Ort portfolio.assessment=Bewertung -portfolio.entries=Eintr\u00e4ge -portfolio.history=\u00c4nderungsprotokoll -portfolio.overview=\u00dcberblick +portfolio.entries=Eintr\u00E4ge +portfolio.history=\u00C4nderungsprotokoll +portfolio.overview=\u00DCberblick portfolio.personal.menu.title=Portfolio 2.0 portfolio.personal.menu.title.alt=$\:portfolio.personal.menu.title portfolio.publish=Freigabe @@ -324,7 +329,7 @@ portfolio.template.options=Einstellungen portfolio.template.options.title=Einstellungen zur Portfoliovorlage portfoliotask=Portfolioaufgabe portfoliotask.none=Keine -publication.title=Ver\u00f6ffentlichungstitel +publication.title=Ver\u00F6ffentlichungstitel publish=Eintrag publizieren publish.confirm.descr=Wollen Sie diesen Eintrag "{0}" publizieren? <p>Dies bedeutet, der Eintrag bzw. die Aufgabe ist abgeschlossen und bereit f\u00FCr die Freigabe. Sobald der Eintrag publiziert ist, kann er nicht mehr bearbeitet werden\! </p> publish.confirm.title=Publizieren @@ -332,30 +337,30 @@ publish.status.title=Freigabestatus der Mappe "{0}" publisher=Herausgeber quick.links=Schnellzugriff remove=Entfernen -reopen=Neu \u00f6ffnen +reopen=Neu \u00F6ffnen reopen.binder=$org.olat.modules.assessment.ui\:reopen -reopen.confirm.descr=Wollen Sie diesen Eintrag "{0}" neu er\u00f6ffnen? Dadurch wird auch der Bereich neu er\u00f6ffnet. -reopen.confirm.title=Er\u00f6ffnen -reopen.page=Neu er\u00f6ffnen -reopen.section=Neu er\u00f6ffnen -reopen.section.confirm.descr=Wollen Sie diesen Bereich "{0}" neu er\u00f6ffnen? -reopen.section.confirm.title=Bereich er\u00f6ffnen +reopen.confirm.descr=Wollen Sie diesen Eintrag "{0}" neu er\u00F6ffnen? Dadurch wird auch der Bereich neu er\u00F6ffnet. +reopen.confirm.title=Er\u00F6ffnen +reopen.page=Neu er\u00F6ffnen +reopen.section=Neu er\u00F6ffnen +reopen.section.confirm.descr=Wollen Sie diesen Bereich "{0}" neu er\u00F6ffnen? +reopen.section.confirm.title=Bereich er\u00F6ffnen restore.binder=Wiedererstellen restore.binder.confirm.descr=Wollen sie die Mappe "{0}" wiedererstellen? Sie wird wieder in "Meine Portfoliomappen" erscheinen. restore.binder.confirm.title=Mappe wiedererstellen restore.binder.success=Die Mappe wurde erfolgreich wiedererstellt. restore.page=Wiedererstellen revision.confirm.descr=Wollen Sie diesen Eintrag "{0}" neu \u00FCberarbeiten lassen? -revision.confirm.title=\u00dcberarbeitung anfordern -revision.page=\u00dcberarbeitung anfordern -revision.page.title=Der Eintrag wird zur \u00dcberarbeitung an den Benutzer zur\u00FCckgegeben. +revision.confirm.title=\u00DCberarbeitung anfordern +revision.page=\u00DCberarbeitung anfordern +revision.page.title=Der Eintrag wird zur \u00DCberarbeitung an den Benutzer zur\u00FCckgegeben. save.done=$org.olat.modules.assessment.ui\:save.done -section.delete=Bereich l\u00f6schen +section.delete=Bereich l\u00F6schen section.down=Unten section.edit=Bereich bearbeiten -section.ended.explain=Das Enddatum dieses Bereichs wurde erreicht. Es ist weiterhin m\u00f6glich, Aufgaben zu bearbeiten und freizugeben.. -section.paging.all=Alle Eintr\u00e4ge zeigen -section.paging.next=N\u00e4chster Bereich +section.ended.explain=Das Enddatum dieses Bereichs wurde erreicht. Es ist weiterhin m\u00F6glich, Aufgaben zu bearbeiten und freizugeben.. +section.paging.all=Alle Eintr\u00E4ge zeigen +section.paging.next=N\u00E4chster Bereich section.paging.one=Ein Bereich section.paging.previous=Vorheriger Bereich section.paging.with.title=Zu "{0}" @@ -363,8 +368,8 @@ section.score=Punkte section.status=Status section.title=Bereich {0} section.up=Oben -select.document=Dokument ausw\u00e4hlen -select.form=Fragebogen ausw\u00e4hlen +select.document=Dokument ausw\u00E4hlen +select.form=Fragebogen ausw\u00E4hlen select.mymap=$org.olat.course.nodes.portfolio\:select.mymap series=Folge shared.binders=Mappen @@ -374,13 +379,13 @@ shared.with.me=An mich freigegeben shared.with.me.text=Liste aller Mappen, die von anderen Benutzern an Sie freigegeben wurden. show.help.binder=Hilfe source=Quelle -start.assignment.hint=W\u00e4hlen Sie eine Aufgabe zur Bearbeitung +start.assignment.hint=W\u00E4hlen Sie eine Aufgabe zur Bearbeitung status.assignment=Aufgabe status.closed=abgeschlossen -status.deleted=gel\u00f6scht +status.deleted=gel\u00F6scht status.draft=Entwurf status.in.progress=in Bearbeitung -status.in.revision=In \u00dcberarbeitung +status.in.revision=In \u00DCberarbeitung status.inRevision=$\:status.in.revision status.not.started=nicht gestartet status.published=publiziert @@ -390,7 +395,7 @@ status.user.inProcess=In Bearbeitung status.user.done=Erledigt summary=Zusammenfassung summary.close=Zusammenfassung ausblenden -summary.open=Zusammenfassung \u00f6ffnen +summary.open=Zusammenfassung \u00F6ffnen summary.placeholder=Kurze Zusammenfassung \u00FCber den Inhalt table.grading.failed.points=<span class\="o_state o_failed"><i class\="o_icon o_icon_failed"> </i> {0} Punkt(e)</span> table.grading.no=noch keine Bewertung @@ -412,7 +417,7 @@ table.header.inRevision=$\:status.in.revision table.header.key=ID table.header.lastUpdate=Letzte Aktualisierung table.header.new=Neu -table.header.numpages=\# Eintr\u00e4ge +table.header.numpages=\# Eintr\u00E4ge table.header.open=Start table.header.open.sections=Offen table.header.passed=Bestanden @@ -439,16 +444,17 @@ template.none=Keines timeline.switch.off=Zeitstrahl timeline.switch.on=Zeitstrahl title=Titel -trash.binder.acknowledge=Die Mappe wird gel\u00f6scht. +trash.binder.acknowledge=Die Mappe wird gel\u00F6scht. up=<i class\='o_icon o_icon o_icon-lg o_icon_move_up'> </i> +upload.file=Hochladen url=URL used.in=Verwendet in validate.email=E-Mailadresse validieren volume=Buchband warning.binder.synched=Die Portfolioaufgabe wurde mit ihrer Vorlage synchronisiert. -warning.evaluation.not.visible.text=Zur Zeit ist die Einsch\u00e4tzung nicht sichtbar. -warning.evaluation.not.visible.title=Einsch\u00e4tzung nicht sichtbar -warning.portfolio.not.found=Die Portfolio Mappe konnte nicht gefunden werden. Sie wurde wahrscheinlich gel\u00f6scht. -warning.page.locked=Dieser Eintrag Bewertung wird im Moment vom Benutzer {0} ({1}) ver\u00e4ndert und ist daher gesperrt. Bitte versuchen Sie es sp\u00e4ter noch einmal. -warning.page.not.found=Der Eintrag konnte nicht gefunden werden. Sie wurde wahrscheinlich gel\u00f6scht. -warning.template.in.use=Die Vorlage konnte nicht gel\u00f6scht werden, weil sie von einigen Benutzern in Verwendung ist. +warning.evaluation.not.visible.text=Zur Zeit ist die Einsch\u00E4tzung nicht sichtbar. +warning.evaluation.not.visible.title=Einsch\u00E4tzung nicht sichtbar +warning.portfolio.not.found=Die Portfolio Mappe konnte nicht gefunden werden. Sie wurde wahrscheinlich gel\u00F6scht. +warning.page.locked=Dieser Eintrag Bewertung wird im Moment vom Benutzer {0} ({1}) ver\u00E4ndert und ist daher gesperrt. Bitte versuchen Sie es sp\u00E4ter noch einmal. +warning.page.not.found=Der Eintrag konnte nicht gefunden werden. Sie wurde wahrscheinlich gel\u00F6scht. +warning.template.in.use=Die Vorlage konnte nicht gel\u00F6scht werden, weil sie von einigen Benutzern in Verwendung ist. diff --git a/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_en.properties index 4d6981e6636d020f8e572761a86c23b929da3099..27f73e5358d974dfad3e18e1da755ef1ae3372c0 100644 --- a/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/modules/portfolio/ui/_i18n/LocalStrings_en.properties @@ -117,6 +117,11 @@ create.binder=Create binder create.empty.binder=New empty binder create.empty.binder.from.course=New binder from course portfolio task create.empty.binder.from.template=New binder from template +create.file=Create +create.file.name=$org.olat.core.commons.services.filetemplate.ui\:create.file.name +create.file.name.notvalid=$org.olat.core.commons.services.filetemplate.ui\:create.file.name.notvalid +create.file.title=Create document +create.file.type=$org.olat.core.commons.services.filetemplate.ui\:create.file.type create.new.assignment=New assignment create.new.assignment.descr=Create a new assignment to assign something to someone create.new.assignment.title=Create a new assignment @@ -441,6 +446,7 @@ timeline.switch.on=Timeline title=Title trash.binder.acknowledge=The binder will be moved to the trash. up=<i class\='o_icon o_icon o_icon-lg o_icon_move_up'> </i> +upload.file=Upload url=URL used.in=Used in validate.email=Validate e-mail diff --git a/src/main/java/org/olat/modules/portfolio/ui/media/CreateFileMediaController.java b/src/main/java/org/olat/modules/portfolio/ui/media/CreateFileMediaController.java new file mode 100644 index 0000000000000000000000000000000000000000..16d9cbadb69b4abc5dc95142a89b4fe4eea88b91 --- /dev/null +++ b/src/main/java/org/olat/modules/portfolio/ui/media/CreateFileMediaController.java @@ -0,0 +1,247 @@ +/** + * <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.media; + +import static org.olat.core.gui.components.util.KeyValues.entry; + +import java.io.File; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.olat.core.commons.services.filetemplate.FileType; +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItemContainer; +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.TextBoxListElement; +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.FormLayoutContainer; +import org.olat.core.gui.components.util.KeyValues; +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.id.context.BusinessControlFactory; +import org.olat.core.util.FileUtils; +import org.olat.core.util.Formatter; +import org.olat.core.util.StringHelper; +import org.olat.core.util.Util; +import org.olat.core.util.WebappHelper; +import org.olat.core.util.vfs.LocalFileImpl; +import org.olat.core.util.vfs.VFSLeaf; +import org.olat.core.util.vfs.VFSManager; +import org.olat.modules.ceditor.PageElement; +import org.olat.modules.ceditor.PageElementAddController; +import org.olat.modules.ceditor.ui.AddElementInfos; +import org.olat.modules.portfolio.Media; +import org.olat.modules.portfolio.PortfolioService; +import org.olat.modules.portfolio.handler.FileHandler; +import org.olat.modules.portfolio.model.MediaPart; +import org.olat.modules.portfolio.ui.PortfolioHomeController; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * Initial date: 03.04.2019<br> + * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com + * + */ +public class CreateFileMediaController extends FormBasicController implements PageElementAddController { + + private TextElement titleEl; + private RichTextElement descriptionEl; + private TextBoxListElement categoriesEl; + private SingleSelection fileTypeEl; + private TextElement fileNameEl; + + private Media mediaReference; + private Map<String,String> categories = new HashMap<>(); + + private final List<FileType> fileTypes; + private final String businessPath; + private AddElementInfos userObject; + + @Autowired + private FileHandler fileHandler; + @Autowired + private PortfolioService portfolioService; + + public CreateFileMediaController(UserRequest ureq, WindowControl wControl, List<FileType> fileTypes) { + super(ureq, wControl); + this.fileTypes = fileTypes; + setTranslator(Util.createPackageTranslator(PortfolioHomeController.class, getLocale(), getTranslator())); + businessPath = "[HomeSite:" + getIdentity().getKey() + "][PortfolioV2:0][MediaCenter:0]"; + initForm(ureq); + } + + public Media getMediaReference() { + return mediaReference; + } + + @Override + public AddElementInfos getUserObject() { + return userObject; + } + + @Override + public void setUserObject(AddElementInfos userObject) { + this.userObject = userObject; + } + + @Override + public PageElement getPageElement() { + MediaPart part = new MediaPart(); + part.setMedia(mediaReference); + return part; + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + formLayout.setElementCssClass("o_sel_pf_create_document_form"); + + titleEl = uifactory.addTextElement("artefact.title", "artefact.title", 255, "", formLayout); + titleEl.setElementCssClass("o_sel_pf_collect_title"); + titleEl.setMandatory(true); + + String desc = mediaReference == null ? null : mediaReference.getTitle(); + descriptionEl = uifactory.addRichTextElementForStringDataMinimalistic("artefact.descr", "artefact.descr", desc, 8, 60, formLayout, getWindowControl()); + descriptionEl.getEditorConfiguration().setPathInStatusBar(false); + + KeyValues fileTypeKV = new KeyValues(); + for (int i = 0; i < fileTypes.size(); i++) { + FileType fileType = fileTypes.get(i); + String name = fileType.getName() + " (." + fileType.getSuffix() + ")"; + fileTypeKV.add(entry(String.valueOf(i), name)); + } + fileTypeEl = uifactory.addDropdownSingleselect("create.file.type", formLayout, fileTypeKV.keys(), fileTypeKV.values()); + fileTypeEl.setMandatory(true); + + fileNameEl = uifactory.addTextElement("create.file.name", -1, "", formLayout); + fileNameEl.setDisplaySize(100); + fileNameEl.setMandatory(true); + + categoriesEl = uifactory.addTextBoxListElement("categories", "categories", "categories.hint", categories, formLayout, getTranslator()); + categoriesEl.setHelpText(translate("categories.hint")); + categoriesEl.setElementCssClass("o_sel_ep_tagsinput"); + categoriesEl.setAllowDuplicates(false); + + Date collectDate = mediaReference == null ? new Date() : mediaReference.getCollectionDate(); + String date = Formatter.getInstance(getLocale()).formatDate(collectDate); + uifactory.addStaticTextElement("artefact.collect.date", "artefact.collect.date", date, formLayout); + + if(StringHelper.containsNonWhitespace(businessPath)) { + String link = BusinessControlFactory.getInstance().getURLFromBusinessPathString(businessPath); + uifactory.addStaticTextElement("artefact.collect.link", "artefact.collect.link", link, formLayout); + } + + FormLayoutContainer buttonsCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator()); + formLayout.add(buttonsCont); + uifactory.addFormSubmitButton("save", "save", buttonsCont); + uifactory.addFormCancelButton("cancel", buttonsCont, ureq, getWindowControl()); + } + + @Override + protected void doDispose() { + // + } + + @Override + protected boolean validateFormLogic(UserRequest ureq) { + boolean allOk = super.validateFormLogic(ureq); + + titleEl.clearError(); + if (titleEl.isEmpty()) { + titleEl.setErrorKey("form.legende.mandatory", null); + allOk &= false; + } + + String fileName = fileNameEl.getValue(); + fileNameEl.clearError(); + if (!StringHelper.containsNonWhitespace(fileName)) { + fileNameEl.setErrorKey("form.mandatory.hover", null); + allOk = false; + } else { + // update in GUI so user sees how we optimized + fileNameEl.setValue(fileName); + if (invalidFilenName(fileName)) { + fileNameEl.setErrorKey("create.file.name.notvalid", null); + allOk = false; + } + } + + return allOk; + } + + private boolean invalidFilenName(String fileName) { + return !FileUtils.validateFilename(fileName); + } + + private String getFileName() { + String fileName = fileNameEl.getValue(); + FileType fileType = getSelectedFileType(); + String suffix = fileType != null? fileType.getSuffix(): ""; + return fileName.endsWith("." + suffix) + ? fileName + : fileName + "." + suffix; + } + + private FileType getSelectedFileType() { + int index = fileTypeEl.getSelected(); + return index > -1? fileTypes.get(index): null; + } + + @Override + protected void formOK(UserRequest ureq) { + String fileName = getFileName(); + File tempDir = new File(WebappHelper.getTmpDir(), "pf" + UUID.randomUUID()); + tempDir.mkdirs(); + File tempFile = new File(tempDir, fileName); + createContent(tempFile); + + String title = titleEl.getValue(); + String description = descriptionEl.getValue(); + String mimeType = WebappHelper.getMimeType(fileName); + UploadMedia mObject = new UploadMedia(tempFile, fileName, mimeType); + mediaReference = fileHandler.createMedia(title, description, mObject, businessPath, getIdentity()); + FileUtils.deleteFile(tempFile); + FileUtils.deleteFile(tempDir); + + List<String> updatedCategories = categoriesEl.getValueList(); + portfolioService.updateCategories(mediaReference, updatedCategories); + + fireEvent(ureq, Event.DONE_EVENT); + } + + private void createContent(File file) { + VFSLeaf vfsLeaf = new LocalFileImpl(file); + FileType fileType = getSelectedFileType(); + if (fileType != null) { + VFSManager.copyContent(fileType.getContentProvider().getContent(), vfsLeaf); + } + } + + @Override + protected void formCancelled(UserRequest ureq) { + fireEvent(ureq, Event.CANCELLED_EVENT); + } +} diff --git a/src/main/java/org/olat/modules/portfolio/ui/media/FileMediaController.java b/src/main/java/org/olat/modules/portfolio/ui/media/FileMediaController.java index 23775cb2f539c75684fc760b42b0e19eab88e606..bdd6c004b8af371c3be061d6fcaae02d8ab2998d 100644 --- a/src/main/java/org/olat/modules/portfolio/ui/media/FileMediaController.java +++ b/src/main/java/org/olat/modules/portfolio/ui/media/FileMediaController.java @@ -19,13 +19,26 @@ */ package org.olat.modules.portfolio.ui.media; +import java.util.Arrays; +import java.util.List; + +import org.olat.core.commons.services.vfs.VFSLeafEditor.Mode; +import org.olat.core.commons.services.vfs.VFSLeafEditorConfigs; +import org.olat.core.commons.services.vfs.VFSLeafEditorSecurityCallback; +import org.olat.core.commons.services.vfs.VFSLeafEditorSecurityCallbackBuilder; +import org.olat.core.commons.services.vfs.VFSRepositoryService; +import org.olat.core.commons.services.vfs.ui.editor.VFSLeafEditorFullscreenController; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; +import org.olat.core.gui.components.link.Link; +import org.olat.core.gui.components.link.LinkFactory; import org.olat.core.gui.components.velocity.VelocityContainer; +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.controller.BasicController; import org.olat.core.gui.util.CSSHelper; +import org.olat.core.util.FileUtils; import org.olat.core.util.Formatter; import org.olat.core.util.StringHelper; import org.olat.core.util.Util; @@ -33,6 +46,7 @@ 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.VFSMediaMapper; +import org.olat.modules.ceditor.PageElementEditorController; import org.olat.modules.portfolio.Media; import org.olat.modules.portfolio.MediaRenderingHints; import org.olat.modules.portfolio.manager.PortfolioFileStorage; @@ -44,22 +58,39 @@ import org.springframework.beans.factory.annotation.Autowired; /** * * Initial date: 20.06.2016<br> + * * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ -public class FileMediaController extends BasicController { +public class FileMediaController extends BasicController implements PageElementEditorController { + + // Editing is excluded because we do not wand to use the internal editor at that place. + private static final List<String> EDIT_EXCLUDED_SUFFIX = Arrays.asList("html", "htm", "txt"); + + private VelocityContainer mainVC; + private Link editLink; + + private VFSLeafEditorFullscreenController vfsLeafEditorCtrl; + + private final Media media; + private final MediaRenderingHints hints; + private VFSLeaf vfsLeaf; + private boolean editMode = false; @Autowired private PortfolioFileStorage fileStorage; - + @Autowired private UserManager userManager; - + @Autowired + private VFSRepositoryService vfsService; public FileMediaController(UserRequest ureq, WindowControl wControl, Media media, MediaRenderingHints hints) { super(ureq, wControl); + this.media = media; + this.hints = hints; setTranslator(Util.createPackageTranslator(PortfolioHomeController.class, getLocale(), getTranslator())); - - VelocityContainer mainVC = createVelocityContainer("media_file"); + + mainVC = createVelocityContainer("media_file"); mainVC.contextPut("filename", media.getContent()); String desc = media.getDescription(); mainVC.contextPut("description", StringHelper.containsNonWhitespace(desc) ? desc : null); @@ -71,40 +102,122 @@ public class FileMediaController extends BasicController { VFSContainer container = fileStorage.getMediaContainer(media); VFSItem item = container.resolve(media.getRootFilename()); - if(item instanceof VFSLeaf) { - VFSLeaf leaf = (VFSLeaf)item; - String mapperUri = registerCacheableMapper(ureq, "File-Media-" + media.getKey() + "-" + leaf.getLastModified(), new VFSMediaMapper(leaf)); + if (item instanceof VFSLeaf) { + vfsLeaf = (VFSLeaf) item; + String mapperUri = registerCacheableMapper(ureq, + "File-Media-" + media.getKey() + "-" + vfsLeaf.getLastModified(), new VFSMediaMapper(vfsLeaf)); mainVC.contextPut("mapperUri", mapperUri); - String iconCss = CSSHelper.createFiletypeIconCssClassFor(leaf.getName()); + String iconCss = CSSHelper.createFiletypeIconCssClassFor(vfsLeaf.getName()); mainVC.contextPut("fileIconCss", iconCss); - mainVC.contextPut("filename", leaf.getName()); + mainVC.contextPut("filename", vfsLeaf.getName()); mainVC.contextPut("size", Formatter.formatBytes(((VFSLeaf) item).getSize())); - + String cssClass = CSSHelper.createFiletypeIconCssClassFor(item.getName()); - if(cssClass == null) { + if (cssClass == null) { cssClass = "o_filetype_file"; } mainVC.contextPut("cssClass", cssClass); + + updateUI(); } - - if(hints.isExtendedMetadata()) { + + if (hints.isExtendedMetadata()) { MediaMetadataController metaCtrl = new MediaMetadataController(ureq, wControl, media); listenTo(metaCtrl); mainVC.put("meta", metaCtrl.getInitialComponent()); } - + mainVC.setDomReplacementWrapperRequired(false); putInitialPanel(mainVC); } + private void updateUI() { + updateOpenLink(); + } + + private void updateOpenLink() { + if (editLink != null) mainVC.remove(editLink); + + if (vfsLeaf != null && !hints.isToPdf()) { + Mode mode = getOpenMode(); + if (vfsService.hasEditor(vfsLeaf, mode)) { + editLink = LinkFactory.createCustomLink("edit", "edit", "", Link.NONTRANSLATED | Link.LINK, mainVC, + this); + String editIcon = Mode.EDIT.equals(mode)? "o_icon_edit": "o_icon_preview"; + editLink.setIconLeftCSS("o_icon " + editIcon); + editLink.setUserObject(mode); + } + } + } + + @Override + public boolean isEditMode() { + return editMode; + } + + @Override + public void setEditMode(boolean editMode) { + this.editMode = editMode; + updateUI(); + } + + private Mode getOpenMode() { + if (isEditingExcluded()) { + return null; + } else if (editMode && vfsService.hasEditor(vfsLeaf, Mode.EDIT)) { + return Mode.EDIT; + } else if (vfsService.hasEditor(vfsLeaf, Mode.VIEW)) { + return Mode.VIEW; + } + return null; + } + + private boolean isEditingExcluded() { + String suffix = FileUtils.getFileSuffix(vfsLeaf.getName()); + return EDIT_EXCLUDED_SUFFIX.contains(suffix); + } + @Override protected void event(UserRequest ureq, Component source, Event event) { - // + if (source == editLink) { + Mode mode = (Mode)editLink.getUserObject(); + doOpen(ureq, mode); + } + } + + @Override + protected void event(UserRequest ureq, Controller source, Event event) { + if (source == vfsLeafEditorCtrl) { + if(event == Event.DONE_EVENT) { + cleanUp(); + } + } + super.event(ureq, source, event); + } + + private void cleanUp() { + removeAsListenerAndDispose(vfsLeafEditorCtrl); + vfsLeafEditorCtrl = null; + } + + private void doOpen(UserRequest ureq, Mode mode) { + VFSContainer container = fileStorage.getMediaContainer(media); + VFSItem vfsItem = container.resolve(media.getRootFilename()); + if(vfsItem == null || !(vfsItem instanceof VFSLeaf)) { + showError("error.missing.file"); + } else { + VFSLeafEditorSecurityCallback secCallback = VFSLeafEditorSecurityCallbackBuilder.builder() + .withMode(mode) + .build(); + VFSLeafEditorConfigs configs = VFSLeafEditorConfigs.builder().build(); + vfsLeafEditorCtrl = new VFSLeafEditorFullscreenController(ureq, getWindowControl(), (VFSLeaf)vfsItem, secCallback, configs); + listenTo(vfsLeafEditorCtrl); + } } @Override protected void doDispose() { // } - + } diff --git a/src/main/java/org/olat/modules/portfolio/ui/media/_content/media_file.html b/src/main/java/org/olat/modules/portfolio/ui/media/_content/media_file.html index 3555fdec3f64e6ee0ba98f75f92d6343b4679d57..c1660d77d73913aecc248ac0a537adc928fd51c0 100644 --- a/src/main/java/org/olat/modules/portfolio/ui/media/_content/media_file.html +++ b/src/main/java/org/olat/modules/portfolio/ui/media/_content/media_file.html @@ -17,6 +17,9 @@ <div class="o_download"> <a id="o_c8000010939" href="$mapperUri/$filename" target="_blank"><i class="o_icon o_icon-fw $cssClass"></i> $filename</a> <span class="o_size">($size)</span> + #if($r.available("edit")) + $r.render("edit") + #end </div> #if($r.available("meta")) <div class="panel panel-default o_artefact_metadata">