diff --git a/src/main/java/org/olat/core/util/vfs/VFSManager.java b/src/main/java/org/olat/core/util/vfs/VFSManager.java index 0049c747f67e10244588c50b52f50e01be6e78a5..bf4db7bd14fa4f6c69d2cafe92b0a0078811074a 100644 --- a/src/main/java/org/olat/core/util/vfs/VFSManager.java +++ b/src/main/java/org/olat/core/util/vfs/VFSManager.java @@ -29,6 +29,7 @@ package org.olat.core.util.vfs; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -758,6 +759,21 @@ public class VFSManager { return false; } + /** + * Copy the content of the file in the target leaf. + * @param source A file + * @param target The target leaf + * @return + */ + public static boolean copyContent(File source, VFSLeaf target) { + try(InputStream inStream = new FileInputStream(source)) { + return copyContent(inStream, target, true); + } catch(IOException ex) { + log.error("", ex); + return false; + } + } + /** * Copies the stream to the target leaf. * diff --git a/src/main/java/org/olat/course/certificate/ui/CertificatesOptionsController.java b/src/main/java/org/olat/course/certificate/ui/CertificatesOptionsController.java index 8a726cef6928347d41643e21ad596d7f4fccb4d3..d95b9455aaa2e87ee80346840bd936c191e97616 100644 --- a/src/main/java/org/olat/course/certificate/ui/CertificatesOptionsController.java +++ b/src/main/java/org/olat/course/certificate/ui/CertificatesOptionsController.java @@ -61,6 +61,7 @@ import org.olat.core.logging.activity.ThreadLocalUserActivityLogger; import org.olat.core.util.FileUtils; import org.olat.core.util.Util; import org.olat.core.util.coordinate.CoordinatorManager; +import org.olat.core.util.coordinate.LockResult; import org.olat.core.util.event.EventBus; import org.olat.core.util.vfs.VFSLeaf; import org.olat.core.util.vfs.VFSMediaResource; @@ -75,9 +76,11 @@ import org.olat.course.config.CourseConfig; import org.olat.course.config.CourseConfigEvent; import org.olat.course.config.CourseConfigEvent.CourseConfigType; import org.olat.course.config.ui.CourseOptionsController; +import org.olat.course.run.RunMainController; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryEntryManagedFlag; import org.olat.repository.RepositoryManager; +import org.olat.user.UserManager; import org.springframework.beans.factory.annotation.Autowired; /** @@ -119,7 +122,10 @@ public class CertificatesOptionsController extends FormBasicController { }; private final String mapperUrl; + private LockResult lockEntry; + @Autowired + private UserManager userManager; @Autowired private CertificatesManager certificatesManager; @Autowired @@ -133,13 +139,32 @@ public class CertificatesOptionsController extends FormBasicController { RepositoryEntry entry, CourseConfig courseConfig, boolean editable) { super(ureq, wControl); setTranslator(Util.createPackageTranslator(CourseOptionsController.class, getLocale(), getTranslator())); + setTranslator(Util.createPackageTranslator(RunMainController.class, getLocale(), getTranslator())); this.courseConfig = courseConfig; this.entry = entry; - this.editable = editable; mapperUrl = registerMapper(ureq, new TemplateMapper()); - + lockEntry = CoordinatorManager.getInstance().getCoordinator().getLocker() + .acquireLock(entry.getOlatResource(), getIdentity(), CourseFactory.COURSE_EDITOR_LOCK); + this.editable = (lockEntry != null && lockEntry.isSuccess()) && editable; + initForm (ureq); + + if(lockEntry != null && !lockEntry.isSuccess()) { + String lockerName = "???"; + if(lockEntry.getOwner() != null) { + lockerName = userManager.getUserDisplayName(lockEntry.getOwner()); + } + showWarning("error.editoralreadylocked", new String[] { lockerName }); + } + } + + @Override + protected void doDispose() { + if (lockEntry != null && lockEntry.isSuccess()) { + CoordinatorManager.getInstance().getCoordinator().getLocker().releaseLock(lockEntry); + lockEntry = null; + } } @Override @@ -176,6 +201,7 @@ public class CertificatesOptionsController extends FormBasicController { templateCont.setLabel("pdf.certificates.template", null); selectTemplateLink = uifactory.addFormLink("select", templateCont, Link.BUTTON); + selectTemplateLink.setEnabled(editable); Long templateId = courseConfig.getCertificateTemplate(); boolean hasTemplate = templateId != null && templateId.longValue() > 0; if(hasTemplate) { @@ -195,6 +221,7 @@ public class CertificatesOptionsController extends FormBasicController { boolean reCertificationEnabled = courseConfig.isRecertificationEnabled(); reCertificationEl = uifactory.addCheckboxesHorizontal("recertification", formLayout, new String[]{ "xx" }, new String[]{ "" }); reCertificationEl.addActionListener(FormEvent.ONCHANGE); + reCertificationEl.setEnabled(editable); if(reCertificationEnabled) { reCertificationEl.select("xx", true); } @@ -208,6 +235,7 @@ public class CertificatesOptionsController extends FormBasicController { reCertificationTimelapseEl = uifactory.addIntegerElement("timelapse", null, timelapse, recertificationCont); reCertificationTimelapseEl.setDomReplacementWrapperRequired(false); reCertificationTimelapseEl.setDisplaySize(4); + reCertificationTimelapseEl.setEnabled(editable); String[] timelapseUnitValues = new String[] { translate("recertification.day"), translate("recertification.week"), @@ -216,6 +244,7 @@ public class CertificatesOptionsController extends FormBasicController { RecertificationTimeUnit timelapseUnit = courseConfig.getRecertificationTimelapseUnit(); reCertificationTimelapseUnitEl = uifactory.addDropdownSingleselect("timelapse.unit", null, recertificationCont, timelapseUnitKeys, timelapseUnitValues, null); reCertificationTimelapseUnitEl.setDomReplacementWrapperRequired(false); + reCertificationTimelapseUnitEl.setEnabled(editable); if(timelapseUnit != null) { reCertificationTimelapseUnitEl.select(timelapseUnit.name(), true); } else { @@ -236,7 +265,7 @@ public class CertificatesOptionsController extends FormBasicController { boolean none = !pdfCertificatesEl.isAtLeastSelected(1); templateCont.setVisible(!none); - selectTemplateLink.setEnabled(!none); + selectTemplateLink.setEnabled(!none && editable); if(none || selectedTemplate == null) { templateCont.contextPut("templateName", translate("default.template")); previewTemplateLink.setEnabled(false); @@ -249,13 +278,8 @@ public class CertificatesOptionsController extends FormBasicController { boolean enableRecertification = !none && reCertificationEl.isAtLeastSelected(1); recertificationCont.setVisible(enableRecertification); - reCertificationTimelapseEl.setEnabled(enableRecertification); - reCertificationTimelapseUnitEl.setEnabled(enableRecertification); - } - - @Override - protected void doDispose() { - // + reCertificationTimelapseEl.setEnabled(enableRecertification && editable); + reCertificationTimelapseUnitEl.setEnabled(enableRecertification && editable); } @Override @@ -354,6 +378,11 @@ public class CertificatesOptionsController extends FormBasicController { private void doChangeConfig(UserRequest ureq) { OLATResourceable courseOres = entry.getOlatResource(); + if(CourseFactory.isCourseEditSessionOpen(courseOres.getResourceableId())) { + showWarning("error.editoralreadylocked", new String[] { "???" }); + return; + } + ICourse course = CourseFactory.openCourseEditSession(courseOres.getResourceableId()); courseConfig = course.getCourseEnvironment().getCourseConfig(); diff --git a/src/main/java/org/olat/course/config/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/config/ui/_i18n/LocalStrings_en.properties index a0000291ec1ae698dd7ad34aa60e0efd4117c2c7..c1a756daec7c374e848b106e0f702e6733fa4383 100644 --- a/src/main/java/org/olat/course/config/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/course/config/ui/_i18n/LocalStrings_en.properties @@ -23,7 +23,7 @@ lock.failed=These course settings are edited since {1} by user {0}. selectfile=Choose file sf.changesfresource=Replace sf.notconfigured=<i>No resource folder selected</i> -sf.resourcetitle=Selected recource folder +sf.resourcetitle=Selected resource folder sf.resourcetitle.helptext=The files stored there can be found in the storage folder of your course when selecting the sub-folder "_sharedfolder". sf.resource.readonly=Read only sf.selectsfresource=Select diff --git a/src/main/java/org/olat/course/nodes/gta/ui/GTACoachedGroupListController.java b/src/main/java/org/olat/course/nodes/gta/ui/GTACoachedGroupListController.java index 4440d35f43dabc6bcd3f7a0f60a45e59b6a1fd68..8981c164c65a99a45f95e364e9b6d970b32f9787 100644 --- a/src/main/java/org/olat/course/nodes/gta/ui/GTACoachedGroupListController.java +++ b/src/main/java/org/olat/course/nodes/gta/ui/GTACoachedGroupListController.java @@ -92,7 +92,9 @@ public class GTACoachedGroupListController extends GTACoachedListController { columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("select", translate("select"), "select")); tableModel = new CoachGroupsTableModel(columnsModel); - tableEl = uifactory.addTableElement(getWindowControl(), "entries", tableModel, getTranslator(), formLayout); + tableEl = uifactory.addTableElement(getWindowControl(), "entries", tableModel, 10, false, getTranslator(), formLayout); + tableEl.setShowAllRowsEnabled(true); + tableEl.setAndLoadPersistedPreferences(ureq, "gta-coached-groups"); } protected void updateModel() { diff --git a/src/main/java/org/olat/course/nodes/gta/ui/GTACoachedParticipantListController.java b/src/main/java/org/olat/course/nodes/gta/ui/GTACoachedParticipantListController.java index 69c431a54f91c00ca7cf707da53a81dfe0280c8d..2db1d81c7e1e3484db3a2c3c84f1e03a0641b686 100644 --- a/src/main/java/org/olat/course/nodes/gta/ui/GTACoachedParticipantListController.java +++ b/src/main/java/org/olat/course/nodes/gta/ui/GTACoachedParticipantListController.java @@ -172,7 +172,9 @@ public class GTACoachedParticipantListController extends GTACoachedListControlle columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel("select", translate("select"), "select")); tableModel = new CoachParticipantsTableModel(userPropertyHandlers, getLocale(), columnsModel); - tableEl = uifactory.addTableElement(getWindowControl(), "entries", tableModel, getTranslator(), formLayout); + tableEl = uifactory.addTableElement(getWindowControl(), "entries", tableModel, 10, false, getTranslator(), formLayout); + tableEl.setShowAllRowsEnabled(true); + tableEl.setAndLoadPersistedPreferences(ureq, "gta-coached-participants"); } protected void updateModel() { diff --git a/src/main/java/org/olat/repository/RepositoryManager.java b/src/main/java/org/olat/repository/RepositoryManager.java index a0ae97f2a63067cb4ee8372197e870f08eb2f213..9cf61d0057f18cf5687b1b56ed11d307dd7dca63 100644 --- a/src/main/java/org/olat/repository/RepositoryManager.java +++ b/src/main/java/org/olat/repository/RepositoryManager.java @@ -206,6 +206,10 @@ public class RepositoryManager { currentImage.delete(); } + if(newImageFile == null || !newImageFile.exists() || newImageFile.getSize() <= 0) { + return false; + } + String targetExtension = ".png"; String extension = FileUtils.getFileSuffix(newImageFile.getName()); if("jpg".equalsIgnoreCase(extension) || "jpeg".equalsIgnoreCase(extension)) { diff --git a/src/test/java/org/olat/selenium/AssessmentTest.java b/src/test/java/org/olat/selenium/AssessmentTest.java index c2ef243da053bb4fdcd58d2ad68490f76e173114..a36fb1a4c7077c5f6a7bf5f8b1228f7b5ccda0fb 100644 --- a/src/test/java/org/olat/selenium/AssessmentTest.java +++ b/src/test/java/org/olat/selenium/AssessmentTest.java @@ -148,7 +148,7 @@ public class AssessmentTest { .selectWithTitle(testNodeTitle); //check that the title of the start page of test is correct - WebElement testH2 = browser.findElement(By.cssSelector("div.o_titled_wrapper.o_course_run h2")); + WebElement testH2 = browser.findElement(By.cssSelector("div.o_course_run h2")); Assert.assertEquals(testNodeTitle, testH2.getText().trim()); //start the test @@ -245,7 +245,7 @@ public class AssessmentTest { .selectWithTitle(testNodeTitle); //check that the title of the start page of test is correct - WebElement testH2 = browser.findElement(By.cssSelector("div.o_titled_wrapper.o_course_run h2")); + WebElement testH2 = browser.findElement(By.cssSelector("div.o_course_run h2")); Assert.assertEquals(testNodeTitle, testH2.getText().trim()); //add Ryomou as a course member @@ -407,7 +407,7 @@ public class AssessmentTest { .clickTree() .selectWithTitle(scormNodeTitle); - By scormH2By = By.cssSelector("div.o_titled_wrapper.o_course_run h2"); + By scormH2By = By.cssSelector("div.o_course_run h2"); WebElement scormH2 = ryomouBrowser.findElement(scormH2By); Assert.assertEquals(scormNodeTitle, scormH2.getText().trim()); @@ -509,7 +509,7 @@ public class AssessmentTest { OOGraphene.closeBlueMessageWindow(browser); //check that the title of the start page of test is correct - WebElement testH2 = browser.findElement(By.cssSelector("div.o_titled_wrapper.o_course_run h2")); + WebElement testH2 = browser.findElement(By.cssSelector("div.o_course_run h2")); Assert.assertEquals(testNodeTitle, testH2.getText().trim()); //add Ryomou and Kanu as a course member diff --git a/src/test/java/org/olat/selenium/CourseTest.java b/src/test/java/org/olat/selenium/CourseTest.java index d5e0a418a4a63b1caa9fa0c7e3c8acd32358ffba..86eee946ee0c29ed8888bac516583ad74ad5672b 100644 --- a/src/test/java/org/olat/selenium/CourseTest.java +++ b/src/test/java/org/olat/selenium/CourseTest.java @@ -748,7 +748,7 @@ public class CourseTest { .selectWithTitle(testNodeTitle); //check that the title of the start page of test is correct - WebElement testH2 = browser.findElement(By.cssSelector("div.o_titled_wrapper.o_course_run h2")); + WebElement testH2 = browser.findElement(By.cssSelector("div.o_course_run h2")); Assert.assertEquals(testNodeTitle, testH2.getText().trim()); } @@ -1047,7 +1047,7 @@ public class CourseTest { .select(courseTitle)//go to the details page .start(); - By courseTitleBy = By.cssSelector(".o_course_run h2"); + By courseTitleBy = By.cssSelector("div.o_course_run h2"); WebElement courseTitleEl = userBrowser.findElement(courseTitleBy); Assert.assertTrue(courseTitleEl.getText().contains(courseTitle)); }