From b7792e81590e8f2c0024669c70ccccfffa2925f1 Mon Sep 17 00:00:00 2001 From: srosse <stephane.rosse@frentix.com> Date: Tue, 8 Sep 2020 08:36:10 +0200 Subject: [PATCH] OO-4771: option to delete automatically resources in trash --- .../RepositoryEntryLifeCycleValue.java | 3 +- .../org/olat/repository/RepositoryModule.java | 27 ++- .../_i18n/LocalStrings_de.properties | 3 + .../_i18n/LocalStrings_en.properties | 1 + .../manager/AutomaticLifecycleService.java | 77 ++++++--- .../model/RepositoryEntryLifecycle.java | 9 +- .../RepositoryLifecycleAdminController.java | 105 ++++++++---- .../repository/ui/_content/date_rule.html | 2 +- .../AutomaticLifecycleServiceTest.java | 158 +++++++++++++++++- 9 files changed, 310 insertions(+), 75 deletions(-) diff --git a/src/main/java/org/olat/repository/RepositoryEntryLifeCycleValue.java b/src/main/java/org/olat/repository/RepositoryEntryLifeCycleValue.java index 63ea4c2d2be..48a3602b885 100644 --- a/src/main/java/org/olat/repository/RepositoryEntryLifeCycleValue.java +++ b/src/main/java/org/olat/repository/RepositoryEntryLifeCycleValue.java @@ -107,13 +107,14 @@ public class RepositoryEntryLifeCycleValue implements Comparable<RepositoryEntry public static RepositoryEntryLifeCycleValue parse(String string) { RepositoryEntryLifeCycleValue val = null; - if(StringHelper.containsNonWhitespace(string)) { + if(StringHelper.containsNonWhitespace(string) && !"-".equals(string)) { char lastCh = string.charAt(string.length() - 1); switch(lastCh) { case 'y': val = parse(string, RepositoryEntryLifeCycleUnit.day); break;//day case 'k': val = parse(string, RepositoryEntryLifeCycleUnit.week); break;//week case 'h': val = parse(string, RepositoryEntryLifeCycleUnit.month); break;//month case 'r': val = parse(string, RepositoryEntryLifeCycleUnit.year); break;//year + default: val = null; } } return val; diff --git a/src/main/java/org/olat/repository/RepositoryModule.java b/src/main/java/org/olat/repository/RepositoryModule.java index 763e833e1b9..d483938dc1e 100644 --- a/src/main/java/org/olat/repository/RepositoryModule.java +++ b/src/main/java/org/olat/repository/RepositoryModule.java @@ -68,6 +68,7 @@ public class RepositoryModule extends AbstractSpringModule { private static final String LIFECYCLE_AUTO_CLOSE = "repo.lifecycle.auto.close"; private static final String LIFECYCLE_AUTO_DELETE = "repo.lifecycle.auto.delete"; + private static final String LIFECYCLE_AUTO_DEFINITIVELY_DELETE = "repo.lifecycle.auto.definitively.delete"; private static final String LIFECYCLE_NOTIFICATION_CLOSE_DELETE = "rrepo.lifecylce.notification.close.delete"; private static final String TAXONOMY_TREE_KEY = "taxonomy.tree.key"; @@ -100,6 +101,8 @@ public class RepositoryModule extends AbstractSpringModule { private String lifecycleAutoClose; @Value("${repo.lifecycle.auto.delete:}") private String lifecycleAutoDelete; + @Value("${repo.lifecycle.auto.definitively.delete:6month}") + private String lifecycleAutoDefinitivelyDelete; @Value("${repo.lifecylce.notification.close.delete:}") private String lifecycleNotificationByCloseDelete; @@ -213,6 +216,8 @@ public class RepositoryModule extends AbstractSpringModule { lifecycleAutoDelete = autoDelete; } + lifecycleAutoDefinitivelyDelete = getStringPropertyValue(LIFECYCLE_AUTO_DEFINITIVELY_DELETE, lifecycleAutoDefinitivelyDelete); + String notificationCloseDelete = getStringPropertyValue(LIFECYCLE_NOTIFICATION_CLOSE_DELETE, true); if(StringHelper.containsNonWhitespace(notificationCloseDelete)) { lifecycleNotificationByCloseDelete = notificationCloseDelete; @@ -376,8 +381,8 @@ public class RepositoryModule extends AbstractSpringModule { } public void setLifecycleAutoClose(String lifecycleAutoClose) { - this.lifecycleAutoClose = lifecycleAutoClose; - setStringProperty(LIFECYCLE_AUTO_CLOSE, lifecycleAutoClose, true); + this.lifecycleAutoClose = StringHelper.containsNonWhitespace(lifecycleAutoClose) ? lifecycleAutoClose : "-"; + setStringProperty(LIFECYCLE_AUTO_CLOSE, this.lifecycleAutoClose, true); } public String getLifecycleAutoDelete() { @@ -389,9 +394,23 @@ public class RepositoryModule extends AbstractSpringModule { } public void setLifecycleAutoDelete(String lifecycleAutoDelete) { - this.lifecycleAutoDelete = lifecycleAutoDelete; - setStringProperty(LIFECYCLE_AUTO_DELETE, lifecycleAutoDelete, true); + this.lifecycleAutoDelete = StringHelper.containsNonWhitespace(lifecycleAutoDelete) ? lifecycleAutoDelete : "-"; + setStringProperty(LIFECYCLE_AUTO_DELETE, this.lifecycleAutoDelete, true); + } + + public String getLifecycleAutoDefinitivelyDelete() { + return lifecycleAutoDefinitivelyDelete; + } + + public RepositoryEntryLifeCycleValue getLifecycleAutoDefinitivelyDeleteValue() { + return RepositoryEntryLifeCycleValue.parse(lifecycleAutoDefinitivelyDelete); + } + + public void setLifecycleAutoDefinitivelyDelete(String lifecycleAutoDefinitivelyDelete) { + this.lifecycleAutoDefinitivelyDelete = StringHelper.containsNonWhitespace(lifecycleAutoDefinitivelyDelete) ? lifecycleAutoDefinitivelyDelete: "-"; + setStringProperty(LIFECYCLE_AUTO_DEFINITIVELY_DELETE, this.lifecycleAutoDefinitivelyDelete, true); } + public boolean isLifecycleNotificationByCloseDeleteEnabled() { return "enabled".equals(lifecycleNotificationByCloseDelete); diff --git a/src/main/java/org/olat/repository/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/repository/_i18n/LocalStrings_de.properties index 119379e72c2..79243ab96e8 100644 --- a/src/main/java/org/olat/repository/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/repository/_i18n/LocalStrings_de.properties @@ -73,6 +73,7 @@ addinfo.owner=Zusatzinformationen admin.lifecycles.intro=Erstellen und verwalten Sie Semesterdaten um bei Kurslaufzeiten auf eine Semesterbezeichnung anstelle eines Datums verweisen zu k\u00f6nnen. Die hier erstellten Semesterdaten k\u00f6nnen bei der Konfiguration des Durchf\u00fchrungszeitraumes eines Kurses in der Lernressourcenverwaltung verwendet werden. Als Bezeichnung k\u00f6nnten z.B. "SS15" und als Name "Sommersemester 2015" verwendet werden. admin.lifecycles.title=Verwalten von Semesterdaten f\u00fcr Kurse admin.menu.lifecycle.title=Semesterdaten +after.course.deletion=nach der Kurs gel\u00F6scht wurde after.course.end=nach Kursende allcourses=Alle meine Kurse assessment=$org.olat.group.ui.main\:assessment @@ -86,6 +87,8 @@ change.to.close=Beendet change.to.close.text=wechselt zu beenden (Benutzer behalten den Zugriff, Lesemodus) change.to.delete=L\u00f6schen change.to.delete.text=wechselt zu gel\u00f6scht (Bezitzer wird entfernt, Kurs befindet sich im Papierkorb) +change.to.delete.definitively=Endg\u00FCltig l\u00F6schen +change.to.delete.definitively.text=endg\u00FCltig l\u00F6schen (alle Daten werden endg\u00FCltig gel\u00F6scht und k\u00F6nnen nicht mehr erstellt werden) chkbx.calendar.onoff=Kalender verwenden chkbx.chat.onoff=Kurs-Chat verwenden chkbx.efficency.onoff=Leistungsnachweis verwenden diff --git a/src/main/java/org/olat/repository/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/repository/_i18n/LocalStrings_en.properties index 01c54723ef8..de679fe8a25 100644 --- a/src/main/java/org/olat/repository/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/repository/_i18n/LocalStrings_en.properties @@ -73,6 +73,7 @@ addinfo.owner=Additional information admin.lifecycles.intro=Create and edit semester terms to define semester course execution periods instead of defining the course execution period individually per course. Semester terms you created here can be used when configuring the course execution period. As an identifier you could use something like "SS15" and as name "Summer semester 2015" admin.lifecycles.title=Management of semester terms for courses admin.menu.lifecycle.title=Semester terms +after.course.deletion=after the course has been deleted after.course.end=after the course ends allcourses=All courses assessment=$org.olat.group.ui.main\:assessment diff --git a/src/main/java/org/olat/repository/manager/AutomaticLifecycleService.java b/src/main/java/org/olat/repository/manager/AutomaticLifecycleService.java index 3d27535cde1..932db11883d 100644 --- a/src/main/java/org/olat/repository/manager/AutomaticLifecycleService.java +++ b/src/main/java/org/olat/repository/manager/AutomaticLifecycleService.java @@ -59,6 +59,7 @@ public class AutomaticLifecycleService { public void manage() { close(); delete(); + definitivelyDelete(); } private void close() { @@ -66,12 +67,12 @@ public class AutomaticLifecycleService { if(StringHelper.containsNonWhitespace(autoClose)) { RepositoryEntryLifeCycleValue autoCloseVal = RepositoryEntryLifeCycleValue.parse(autoClose); Date markerDate = autoCloseVal.limitDate(new Date()); - List<RepositoryEntry> entriesToClose = getRepositoryEntriesToClose(markerDate); + List<RepositoryEntry> entriesToClose = getRepositoryEntries(markerDate, RepositoryEntryStatusEnum.preparationToPublished()); for(RepositoryEntry entry:entriesToClose) { try { boolean closeManaged = RepositoryEntryManagedFlag.isManaged(entry, RepositoryEntryManagedFlag.close); if(!closeManaged) { - log.info(Tracing.M_AUDIT, "Automatic closing course: " + entry.getDisplayname() + " [" + entry.getKey() + "]"); + log.info(Tracing.M_AUDIT, "Automatic closing course: {} [{}]", entry.getDisplayname(), entry.getKey()); repositoryService.closeRepositoryEntry(entry, null, false); dbInstance.commit(); } @@ -83,36 +84,17 @@ public class AutomaticLifecycleService { } } - public List<RepositoryEntry> getRepositoryEntriesToClose(Date date) { - QueryBuilder sb = new QueryBuilder(512); - sb.append("select v from repositoryentry as v ") - .append(" inner join fetch v.olatResource as ores") - .append(" inner join fetch v.statistics as statistics") - .append(" inner join fetch v.lifecycle as lifecycle") - .append(" where lifecycle.validTo<:now and v.status ").in(RepositoryEntryStatusEnum.preparationToPublished()); - - Calendar cal = Calendar.getInstance(); - cal.setTime(date); - CalendarUtils.getEndOfDay(cal); - Date endOfDay = cal.getTime(); - - return dbInstance.getCurrentEntityManager() - .createQuery(sb.toString(), RepositoryEntry.class) - .setParameter("now", endOfDay) - .getResultList(); - } - private void delete() { String autoDelete = repositoryModule.getLifecycleAutoDelete(); if(StringHelper.containsNonWhitespace(autoDelete)) { RepositoryEntryLifeCycleValue autoDeleteVal = RepositoryEntryLifeCycleValue.parse(autoDelete); Date markerDate = autoDeleteVal.limitDate(new Date()); - List<RepositoryEntry> entriesToDelete = getRepositoryEntriesToDelete(markerDate); + List<RepositoryEntry> entriesToDelete = getRepositoryEntries(markerDate, RepositoryEntryStatusEnum.preparationToClosed()); for(RepositoryEntry entry:entriesToDelete) { try { boolean deleteManaged = RepositoryEntryManagedFlag.isManaged(entry, RepositoryEntryManagedFlag.delete); if(!deleteManaged) { - log.info(Tracing.M_AUDIT, "Automatic deleting (soft) course: " + entry.getDisplayname() + " [" + entry.getKey() + "]"); + log.info(Tracing.M_AUDIT, "Automatic deleting (soft) course: {} [{}]", entry.getDisplayname(), entry.getKey() ); repositoryService.deleteSoftly(entry, null, true, false); dbInstance.commit(); } @@ -124,13 +106,54 @@ public class AutomaticLifecycleService { } } - public List<RepositoryEntry> getRepositoryEntriesToDelete(Date date) { + private void definitivelyDelete() { + String autoDefinitivelyDelete = repositoryModule.getLifecycleAutoDefinitivelyDelete(); + if(StringHelper.containsNonWhitespace(autoDefinitivelyDelete)) { + RepositoryEntryLifeCycleValue autoDefinitivelyDeleteVal = RepositoryEntryLifeCycleValue.parse(autoDefinitivelyDelete); + Date markerDate = autoDefinitivelyDeleteVal.limitDate(new Date()); + List<RepositoryEntry> entriesToDelete = getRepositoryEntriesInTrash(markerDate); + for(RepositoryEntry entry:entriesToDelete) { + try { + boolean deleteManaged = RepositoryEntryManagedFlag.isManaged(entry, RepositoryEntryManagedFlag.delete); + if(!deleteManaged) { + log.info(Tracing.M_AUDIT, "Automatic deleting (definitively) course: {} [{}]", entry.getDisplayname(), entry.getKey()); + repositoryService.deletePermanently(entry, null, null, null); + dbInstance.commit(); + } + } catch (Exception e) { + log.error("", e); + dbInstance.commitAndCloseSession(); + } + } + } + } + + protected List<RepositoryEntry> getRepositoryEntries(Date date, RepositoryEntryStatusEnum[] states) { + QueryBuilder sb = new QueryBuilder(512); + sb.append("select v from repositoryentry as v ") + .append(" inner join fetch v.olatResource as ores") + .append(" left join fetch v.statistics as statistics") + .append(" left join fetch v.lifecycle as lifecycle") + .append(" where lifecycle.validTo<:now and v.status ").in(states); + + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + CalendarUtils.getEndOfDay(cal); + Date endOfDay = cal.getTime(); + + return dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), RepositoryEntry.class) + .setParameter("now", endOfDay) + .getResultList(); + } + + protected List<RepositoryEntry> getRepositoryEntriesInTrash(Date date) { QueryBuilder sb = new QueryBuilder(512); sb.append("select v from repositoryentry as v ") .append(" inner join fetch v.olatResource as ores") - .append(" inner join fetch v.statistics as statistics") - .append(" inner join fetch v.lifecycle as lifecycle") - .append(" where lifecycle.validTo<:now and v.status ").in(RepositoryEntryStatusEnum.preparationToClosed()); + .append(" left join fetch v.statistics as statistics") + .append(" left join fetch v.lifecycle as lifecycle") + .append(" where v.deletionDate<:now and v.status ").in(RepositoryEntryStatusEnum.trash); Calendar cal = Calendar.getInstance(); cal.setTime(date); diff --git a/src/main/java/org/olat/repository/model/RepositoryEntryLifecycle.java b/src/main/java/org/olat/repository/model/RepositoryEntryLifecycle.java index 9d980a22712..ca0612ba8b9 100644 --- a/src/main/java/org/olat/repository/model/RepositoryEntryLifecycle.java +++ b/src/main/java/org/olat/repository/model/RepositoryEntryLifecycle.java @@ -26,7 +26,6 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; -import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.Table; import javax.persistence.Temporal; @@ -47,11 +46,9 @@ import org.olat.core.id.Persistable; @Cacheable(false) @Entity(name="repositoryentrylifecycle") @Table(name="o_repositoryentry_cycle") -@NamedQueries({ - @NamedQuery(name="loadReLifeCycle", query="select relifecycle from repositoryentrylifecycle relifecycle where relifecycle.key=:key order by relifecycle.validFrom desc"), - @NamedQuery(name="loadPublicReLifeCycle", query="select relifecycle from repositoryentrylifecycle relifecycle where relifecycle.privateCycle=false order by relifecycle.validFrom desc"), - @NamedQuery(name="countPublicReLifeCycle", query="select count(relifecycle) from repositoryentrylifecycle relifecycle where relifecycle.privateCycle=false") -}) +@NamedQuery(name="loadReLifeCycle", query="select relifecycle from repositoryentrylifecycle relifecycle where relifecycle.key=:key order by relifecycle.validFrom desc") +@NamedQuery(name="loadPublicReLifeCycle", query="select relifecycle from repositoryentrylifecycle relifecycle where relifecycle.privateCycle=false order by relifecycle.validFrom desc") +@NamedQuery(name="countPublicReLifeCycle", query="select count(relifecycle) from repositoryentrylifecycle relifecycle where relifecycle.privateCycle=false") public class RepositoryEntryLifecycle implements Persistable, CreateInfo, ModifiedInfo { private static final long serialVersionUID = -8484159601386853047L; diff --git a/src/main/java/org/olat/repository/ui/RepositoryLifecycleAdminController.java b/src/main/java/org/olat/repository/ui/RepositoryLifecycleAdminController.java index b5b8ec15296..4d0c6711b03 100644 --- a/src/main/java/org/olat/repository/ui/RepositoryLifecycleAdminController.java +++ b/src/main/java/org/olat/repository/ui/RepositoryLifecycleAdminController.java @@ -55,10 +55,18 @@ public class RepositoryLifecycleAdminController extends FormBasicController { }; private MultipleSelectionElement notificationEl; - private MultipleSelectionElement toCloseEl, toDeleteEl; - private TextElement closeValueEl, deleteValueEl; - private SingleSelection closeUnitEl, deleteUnitEl; - private FormLayoutContainer closeRuleCont, deleteRuleCont; + private MultipleSelectionElement toCloseEl; + private MultipleSelectionElement toDeleteEl; + private MultipleSelectionElement toDefinitivelyDeleteEl; + private TextElement closeValueEl; + private TextElement deleteValueEl; + private TextElement definitivelyDeleteValueEl; + private SingleSelection closeUnitEl; + private SingleSelection deleteUnitEl; + private SingleSelection definitivelyDeleteUnitEl; + private FormLayoutContainer closeRuleCont; + private FormLayoutContainer deleteRuleCont; + private FormLayoutContainer definitivelyDeleteRuleCont; @Autowired private RepositoryModule repositoryModule; @@ -83,6 +91,30 @@ public class RepositoryLifecycleAdminController extends FormBasicController { translate(RepositoryEntryLifeCycleUnit.month.name()), translate(RepositoryEntryLifeCycleUnit.year.name()) }; + initCloseForm(id, page, unitValues, lifecycleCont); + initDeleteForm(id, page, unitValues, lifecycleCont); + initDefinitivelyDeleteForm(id, page, unitValues, lifecycleCont); + + FormLayoutContainer notificationsCont = FormLayoutContainer.createDefaultFormLayout("notis", getTranslator()); + notificationsCont.setFormTitle(translate("repository.admin.lifecycle.notifications.title")); + formLayout.add(notificationsCont); + notificationsCont.setRootForm(mainForm); + + boolean notification = repositoryModule.isLifecycleNotificationByCloseDeleteEnabled(); + String[] notificationValues = new String[] { translate("repository.admin.lifecycle.notifications.enabled") }; + notificationEl = uifactory.addCheckboxesHorizontal("repository.admin.lifecycle.notifications", notificationsCont, onKeys, notificationValues); + notificationEl.addActionListener(FormEvent.ONCHANGE); + if(notification) { + notificationEl.select(onKeys[0], true); + } + + FormLayoutContainer buttonsCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator()); + buttonsCont.setRootForm(mainForm); + notificationsCont.add(buttonsCont); + uifactory.addFormSubmitButton("save", buttonsCont); + } + + private void initCloseForm(String id, String page, String[] unitValues, FormLayoutContainer lifecycleCont) { RepositoryEntryLifeCycleValue autoCloseValue = repositoryModule.getLifecycleAutoCloseValue(); String[] toCloseValues = new String[] { translate("change.to.close.text") }; toCloseEl = uifactory.addCheckboxesHorizontal("change.to.close", lifecycleCont, onKeys, toCloseValues); @@ -92,6 +124,7 @@ public class RepositoryLifecycleAdminController extends FormBasicController { } closeRuleCont = FormLayoutContainer.createCustomFormLayout("close.".concat(id), lifecycleCont.getTranslator(), page); + closeRuleCont.contextPut("ruleMsg", translate("after.course.end")); closeRuleCont.setLabel(null, null); closeRuleCont.setVisible(toCloseEl.isAtLeastSelected(1)); closeRuleCont.contextPut("prefix", "clo"); @@ -106,7 +139,9 @@ public class RepositoryLifecycleAdminController extends FormBasicController { closeUnitEl = uifactory.addDropdownSingleselect("clo-unit", null, closeRuleCont, unitKeys, unitValues, null); closeUnitEl.setDomReplacementWrapperRequired(false); selectUnitEl(closeUnitEl, autoCloseValue); - + } + + private void initDeleteForm(String id, String page, String[] unitValues, FormLayoutContainer lifecycleCont) { RepositoryEntryLifeCycleValue autoDeleteValue = repositoryModule.getLifecycleAutoDeleteValue(); String[] toDeleteValues = new String[] { translate("change.to.delete.text") }; toDeleteEl = uifactory.addCheckboxesHorizontal("change.to.delete", lifecycleCont, onKeys, toDeleteValues); @@ -116,6 +151,7 @@ public class RepositoryLifecycleAdminController extends FormBasicController { } deleteRuleCont = FormLayoutContainer.createCustomFormLayout("delete.".concat(id), lifecycleCont.getTranslator(), page); + deleteRuleCont.contextPut("ruleMsg", translate("after.course.end")); deleteRuleCont.setLabel(null, null); deleteRuleCont.setVisible(toDeleteEl.isAtLeastSelected(1)); deleteRuleCont.contextPut("prefix", "del"); @@ -130,24 +166,33 @@ public class RepositoryLifecycleAdminController extends FormBasicController { deleteUnitEl = uifactory.addDropdownSingleselect("del-unit", null, deleteRuleCont, unitKeys, unitValues, null); deleteUnitEl.setDomReplacementWrapperRequired(false); selectUnitEl(deleteUnitEl, autoDeleteValue); - - FormLayoutContainer notificationsCont = FormLayoutContainer.createDefaultFormLayout("notis", getTranslator()); - notificationsCont.setFormTitle(translate("repository.admin.lifecycle.notifications.title")); - formLayout.add(notificationsCont); - notificationsCont.setRootForm(mainForm); - - boolean notification = repositoryModule.isLifecycleNotificationByCloseDeleteEnabled(); - String[] notificationValues = new String[] { translate("repository.admin.lifecycle.notifications.enabled") }; - notificationEl = uifactory.addCheckboxesHorizontal("repository.admin.lifecycle.notifications", notificationsCont, onKeys, notificationValues); - notificationEl.addActionListener(FormEvent.ONCHANGE); - if(notification) { - notificationEl.select(onKeys[0], true); + } + + private void initDefinitivelyDeleteForm(String id, String page, String[] unitValues, FormLayoutContainer lifecycleCont) { + RepositoryEntryLifeCycleValue autoDefinitivelyDeleteValue = repositoryModule.getLifecycleAutoDefinitivelyDeleteValue(); + String[] toDeleteValues = new String[] { translate("change.to.delete.definitively.text") }; + toDefinitivelyDeleteEl = uifactory.addCheckboxesHorizontal("change.to.delete.definitively", lifecycleCont, onKeys, toDeleteValues); + toDefinitivelyDeleteEl.addActionListener(FormEvent.ONCHANGE); + if(autoDefinitivelyDeleteValue != null) { + toDefinitivelyDeleteEl.select(onKeys[0], true); } - FormLayoutContainer buttonsCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator()); - buttonsCont.setRootForm(mainForm); - notificationsCont.add(buttonsCont); - uifactory.addFormSubmitButton("save", buttonsCont); + definitivelyDeleteRuleCont = FormLayoutContainer.createCustomFormLayout("def-delete.".concat(id), lifecycleCont.getTranslator(), page); + definitivelyDeleteRuleCont.contextPut("ruleMsg", translate("after.course.deletion")); + definitivelyDeleteRuleCont.setLabel(null, null); + definitivelyDeleteRuleCont.setVisible(toDefinitivelyDeleteEl.isAtLeastSelected(1)); + definitivelyDeleteRuleCont.contextPut("prefix", "def-del"); + lifecycleCont.add(definitivelyDeleteRuleCont); + definitivelyDeleteRuleCont.setRootForm(mainForm); + + String currentDeleteValue = autoDefinitivelyDeleteValue == null ? null : Integer.toString(autoDefinitivelyDeleteValue.getValue()); + definitivelyDeleteValueEl = uifactory.addTextElement("def-del-value", null, 128, currentDeleteValue, definitivelyDeleteRuleCont); + definitivelyDeleteValueEl.setDomReplacementWrapperRequired(false); + definitivelyDeleteValueEl.setDisplaySize(3); + + definitivelyDeleteUnitEl = uifactory.addDropdownSingleselect("def-del-unit", null, definitivelyDeleteRuleCont, unitKeys, unitValues, null); + definitivelyDeleteUnitEl.setDomReplacementWrapperRequired(false); + selectUnitEl(definitivelyDeleteUnitEl, autoDefinitivelyDeleteValue); } private void selectUnitEl(SingleSelection unitEl, RepositoryEntryLifeCycleValue currentUnit) { @@ -177,19 +222,15 @@ public class RepositoryLifecycleAdminController extends FormBasicController { allOk &= validateFormLogic(toCloseEl, closeValueEl, closeUnitEl); allOk &= validateFormLogic(toDeleteEl, deleteValueEl, deleteUnitEl); + allOk &= validateFormLogic(toDefinitivelyDeleteEl, definitivelyDeleteValueEl, definitivelyDeleteUnitEl); RepositoryEntryLifeCycleValue autoClose = getValue(toCloseEl, closeValueEl, closeUnitEl); RepositoryEntryLifeCycleValue autoDelete = getValue(toDeleteEl, deleteValueEl, deleteUnitEl); - - if(autoDelete != null) { - if(autoClose != null) { - if(autoDelete.compareTo(autoClose) <= 0) { - deleteValueEl.setErrorKey("error.lifecycle.after", null); - allOk &= false; - } - } + if(autoDelete != null && autoClose != null && autoDelete.compareTo(autoClose) <= 0) { + deleteValueEl.setErrorKey("error.lifecycle.after", null); + allOk &= false; } - + return allOk; } @@ -228,6 +269,8 @@ public class RepositoryLifecycleAdminController extends FormBasicController { closeRuleCont.setVisible(toCloseEl.isAtLeastSelected(1)); } else if(toDeleteEl == source) { deleteRuleCont.setVisible(toDeleteEl.isAtLeastSelected(1)); + } else if(toDefinitivelyDeleteEl == source) { + definitivelyDeleteRuleCont.setVisible(toDefinitivelyDeleteEl.isAtLeastSelected(1)); } } @@ -237,6 +280,8 @@ public class RepositoryLifecycleAdminController extends FormBasicController { repositoryModule.setLifecycleAutoClose(autoClose); String autoDelete = getStringValue(toDeleteEl, deleteValueEl, deleteUnitEl); repositoryModule.setLifecycleAutoDelete(autoDelete); + String autoDefinitivelyDelete = getStringValue(toDefinitivelyDeleteEl, definitivelyDeleteValueEl, definitivelyDeleteUnitEl); + repositoryModule.setLifecycleAutoDefinitivelyDelete(autoDefinitivelyDelete); boolean notification = notificationEl.isAtLeastSelected(1); repositoryModule.setLifecycleNotificationByCloseDeleteEnabled(notification); } diff --git a/src/main/java/org/olat/repository/ui/_content/date_rule.html b/src/main/java/org/olat/repository/ui/_content/date_rule.html index 17a82500f0f..1b558c1892b 100644 --- a/src/main/java/org/olat/repository/ui/_content/date_rule.html +++ b/src/main/java/org/olat/repository/ui/_content/date_rule.html @@ -1,5 +1,5 @@ <div class='form-inline'> - $r.render("${prefix}-value") $r.render("${prefix}-unit") <span class="form-control-static">$r.translate("after.course.end")</span> + $r.render("${prefix}-value") $r.render("${prefix}-unit") <span class="form-control-static">$ruleMsg</span> #if($f.hasError("${prefix}-value")) <br/>$r.render("${prefix}-value_ERROR") #end diff --git a/src/test/java/org/olat/repository/manager/AutomaticLifecycleServiceTest.java b/src/test/java/org/olat/repository/manager/AutomaticLifecycleServiceTest.java index 27151f532b4..d6bd8049c7b 100644 --- a/src/test/java/org/olat/repository/manager/AutomaticLifecycleServiceTest.java +++ b/src/test/java/org/olat/repository/manager/AutomaticLifecycleServiceTest.java @@ -24,7 +24,14 @@ import java.util.List; import org.junit.Assert; import org.junit.Test; +import org.olat.core.commons.persistence.DB; +import org.olat.core.util.DateUtils; import org.olat.repository.RepositoryEntry; +import org.olat.repository.RepositoryEntryStatusEnum; +import org.olat.repository.RepositoryManager; +import org.olat.repository.RepositoryModule; +import org.olat.repository.model.RepositoryEntryLifecycle; +import org.olat.test.JunitTestHelper; import org.olat.test.OlatTestCase; import org.springframework.beans.factory.annotation.Autowired; @@ -37,19 +44,158 @@ import org.springframework.beans.factory.annotation.Autowired; */ public class AutomaticLifecycleServiceTest extends OlatTestCase { + @Autowired + private DB dbInstance; + @Autowired + private RepositoryModule repositoryModule; + @Autowired + private RepositoryManager repositoryManager; @Autowired private AutomaticLifecycleService automaticLifecycleService; + @Autowired + private RepositoryEntryLifecycleDAO repositoryEntryLifecycleDao; + @Test - public void getRepositoryEntriesToDelete() { - List<RepositoryEntry> entriesToDelete = automaticLifecycleService.getRepositoryEntriesToDelete(new Date()); - Assert.assertNotNull(entriesToDelete); + public void getRepositoryEntries() { + RepositoryEntry entry = createRepositoryEntry("To close 1", RepositoryEntryStatusEnum.published, -120, -60); + + List<RepositoryEntry> entriesToClose2 = automaticLifecycleService + .getRepositoryEntries(DateUtils.addDays(new Date(), -200), RepositoryEntryStatusEnum.preparationToPublished()); + Assert.assertNotNull(entriesToClose2); + Assert.assertFalse(entriesToClose2.contains(entry)); + + List<RepositoryEntry> entriesToClose50 = automaticLifecycleService + .getRepositoryEntries(DateUtils.addDays(new Date(), -50), RepositoryEntryStatusEnum.preparationToPublished()); + Assert.assertNotNull(entriesToClose50); + Assert.assertTrue(entriesToClose50.contains(entry)); } @Test - public void getRepositoryEntriesToClose() { - List<RepositoryEntry> entriesToClose = automaticLifecycleService.getRepositoryEntriesToClose(new Date()); - Assert.assertNotNull(entriesToClose); + public void getDeletedRepositoryEntries() { + RepositoryEntry entry = createRepositoryEntry("To close 1", RepositoryEntryStatusEnum.trash, -120, -60); + entry.setDeletionDate(DateUtils.addDays(new Date(), -80)); + entry = dbInstance.getCurrentEntityManager().merge(entry); + dbInstance.commitAndCloseSession(); + + List<RepositoryEntry> deletedEntries = automaticLifecycleService + .getRepositoryEntriesInTrash(DateUtils.addDays(new Date(), -200)); + Assert.assertNotNull(deletedEntries); + Assert.assertFalse(deletedEntries.contains(entry)); + + List<RepositoryEntry> deletedEntries70 = automaticLifecycleService + .getRepositoryEntriesInTrash(DateUtils.addDays(new Date(), -70)); + Assert.assertNotNull(deletedEntries70); + Assert.assertTrue(deletedEntries70.contains(entry)); } + + @Test + public void getDeletedRepositoryEntriesnegativeTest() { + RepositoryEntry entry = createTrashedRepositoryEntry("To close 1", RepositoryEntryStatusEnum.published, -120, -60, -80); + List<RepositoryEntry> deletedEntries = automaticLifecycleService + .getRepositoryEntriesInTrash(DateUtils.addDays(new Date(), -20)); + Assert.assertNotNull(deletedEntries); + Assert.assertFalse(deletedEntries.contains(entry)); + } + + @Test + public void manageAutoClose() { + repositoryModule.setLifecycleAutoClose("40day"); + repositoryModule.setLifecycleAutoDelete(null); + repositoryModule.setLifecycleAutoDefinitivelyDelete(null); + + RepositoryEntry entryToClose = createRepositoryEntry("To close 3", RepositoryEntryStatusEnum.published, -120, -60); + RepositoryEntry entryInUse = createRepositoryEntry("In use 1", RepositoryEntryStatusEnum.published, -20, 60); + dbInstance.commitAndCloseSession(); + + automaticLifecycleService.manage(); + + // check the entry to close + RepositoryEntry reloadEntryToClose = repositoryManager.lookupRepositoryEntry(entryToClose.getKey()); + Assert.assertEquals(entryToClose, reloadEntryToClose); + Assert.assertEquals(RepositoryEntryStatusEnum.closed, reloadEntryToClose.getEntryStatus()); + + // check the used entry is not closed + RepositoryEntry reloadEntryInUse = repositoryManager.lookupRepositoryEntry(entryInUse.getKey()); + Assert.assertEquals(entryInUse, reloadEntryInUse); + Assert.assertEquals(RepositoryEntryStatusEnum.published, reloadEntryInUse.getEntryStatus()); + } + + @Test + public void manageAutoDelete() { + repositoryModule.setLifecycleAutoClose(null); + repositoryModule.setLifecycleAutoDelete("120day"); + repositoryModule.setLifecycleAutoDefinitivelyDelete(null); + + RepositoryEntry entryToClose1 = createRepositoryEntry("To trash 4", RepositoryEntryStatusEnum.published, -180, -130); + RepositoryEntry entryToClose2 = createRepositoryEntry("To trash 5 ", RepositoryEntryStatusEnum.closed, -210, -180); + RepositoryEntry entryInUse = createRepositoryEntry("In use 2", RepositoryEntryStatusEnum.published, -30, 60); + dbInstance.commitAndCloseSession(); + + automaticLifecycleService.manage(); + + // check the entries to delete + RepositoryEntry reloadEntryToClose1 = repositoryManager.lookupRepositoryEntry(entryToClose1.getKey()); + Assert.assertEquals(entryToClose1, reloadEntryToClose1); + Assert.assertEquals(RepositoryEntryStatusEnum.trash, reloadEntryToClose1.getEntryStatus()); + + RepositoryEntry reloadEntryToClose2 = repositoryManager.lookupRepositoryEntry(entryToClose2.getKey()); + Assert.assertEquals(entryToClose2, reloadEntryToClose2); + Assert.assertEquals(RepositoryEntryStatusEnum.trash, reloadEntryToClose2.getEntryStatus()); + + // check the used entry is not closed + RepositoryEntry reloadEntryInUse = repositoryManager.lookupRepositoryEntry(entryInUse.getKey()); + Assert.assertEquals(entryInUse, reloadEntryInUse); + Assert.assertEquals(RepositoryEntryStatusEnum.published, reloadEntryInUse.getEntryStatus()); + } + + @Test + public void manageAutoDefinitivelyDelete() { + repositoryModule.setLifecycleAutoClose(null); + repositoryModule.setLifecycleAutoDelete(null); + repositoryModule.setLifecycleAutoDefinitivelyDelete("210day"); + + RepositoryEntry entryToDelete= createTrashedRepositoryEntry("To def. delete 6", RepositoryEntryStatusEnum.trash, -120, -70, -240); + RepositoryEntry entryClosed = createTrashedRepositoryEntry("To def. delete 7", RepositoryEntryStatusEnum.closed, -210, -90, -300); + RepositoryEntry entryInUse = createRepositoryEntry("In use 2", RepositoryEntryStatusEnum.published, -30, 60); + dbInstance.commitAndCloseSession(); + + automaticLifecycleService.manage(); + + // check the entries to delete + RepositoryEntry reloadEntryToDelete1 = repositoryManager.lookupRepositoryEntry(entryToDelete.getKey()); + Assert.assertNull(reloadEntryToDelete1); + + RepositoryEntry reloadEntryClosed = repositoryManager.lookupRepositoryEntry(entryClosed.getKey()); + Assert.assertEquals(entryClosed, reloadEntryClosed); + Assert.assertEquals(RepositoryEntryStatusEnum.closed, reloadEntryClosed.getEntryStatus()); + + // check the used entry is not closed + RepositoryEntry reloadEntryInUse = repositoryManager.lookupRepositoryEntry(entryInUse.getKey()); + Assert.assertEquals(entryInUse, reloadEntryInUse); + Assert.assertEquals(RepositoryEntryStatusEnum.published, reloadEntryInUse.getEntryStatus()); + } + + + private RepositoryEntry createTrashedRepositoryEntry(String displayName, RepositoryEntryStatusEnum status, int startDays, int endDays, int trashedDays) { + RepositoryEntry entry = createRepositoryEntry(displayName, status, startDays, endDays); + + entry.setDeletionDate(DateUtils.addDays(new Date(), trashedDays)); + entry = dbInstance.getCurrentEntityManager().merge(entry); + dbInstance.commitAndCloseSession(); + + return entry; + } + + private RepositoryEntry createRepositoryEntry(String displayName, RepositoryEntryStatusEnum status, int startDays, int endDays) { + RepositoryEntry entry = JunitTestHelper.createAndPersistRepositoryEntry(); + entry = repositoryManager.setAccess(entry, status, false, false); + Date start = DateUtils.addDays(new Date(), startDays); + Date end = DateUtils.addDays(new Date(), endDays); + RepositoryEntryLifecycle cycle = repositoryEntryLifecycleDao.create("Sem.", null, true, start, end); + entry = repositoryManager.setDescriptionAndName(entry, displayName, "Fake course to close", null, null, null, null, null, cycle); + dbInstance.commitAndCloseSession(); + return entry; + } } -- GitLab