diff --git a/src/main/java/org/olat/group/manager/BusinessGroupDAO.java b/src/main/java/org/olat/group/manager/BusinessGroupDAO.java index 9d5bb508cac65a3966af227fe332bfc878930810..f20dfa8cc3b661b206e7e0dda2666d2efa0d7aab 100644 --- a/src/main/java/org/olat/group/manager/BusinessGroupDAO.java +++ b/src/main/java/org/olat/group/manager/BusinessGroupDAO.java @@ -1040,6 +1040,11 @@ public class BusinessGroupDAO { query.setParameter("atDate", new Date()); } } + + // last usage + if(params.getLastUsageBefore() != null) { + query.setParameter("lastUsageBefore", params.getLastUsageBefore()); + } } private void filterBusinessGroupToSearch(StringBuilder sb, BusinessGroupQueryParams params, boolean includeMemberships) { @@ -1183,6 +1188,12 @@ public class BusinessGroupDAO { .append(" where bGroup.key=headMembership.group.key and headMembership.role in ('").append(GroupRoles.coach.name()).append("','").append(GroupRoles.participant.name()).append("')") .append(" )"); } + + // last usage + if(params.getLastUsageBefore() != null) { + where = PersistenceHelper.appendAnd(sb, where); + sb.append(" bgi.lastUsage <= :lastUsageBefore"); + } } private void loadMemberships(IdentityRef identity, Map<Long, ? extends BusinessGroupRow> keyToGroup) { diff --git a/src/main/java/org/olat/group/model/BusinessGroupQueryParams.java b/src/main/java/org/olat/group/model/BusinessGroupQueryParams.java index 6f060fea4304c55db69e1ad8bc5fb620099e4335..0c59f054a3ea876bb7d80e2cc262db8a6e9ca21d 100644 --- a/src/main/java/org/olat/group/model/BusinessGroupQueryParams.java +++ b/src/main/java/org/olat/group/model/BusinessGroupQueryParams.java @@ -19,6 +19,7 @@ **/ package org.olat.group.model; +import java.util.Date; import java.util.List; import org.olat.repository.RepositoryEntryRef; @@ -48,6 +49,7 @@ public class BusinessGroupQueryParams { private Boolean resources; private boolean headless = false; private boolean authorConnection; + private Date lastUsageBefore; private List<Long> businessGroupKeys; private RepositoryEntryRef repositoryEntry; @@ -216,4 +218,12 @@ public class BusinessGroupQueryParams { public void setHeadless(boolean headless) { this.headless = headless; } + + public Date getLastUsageBefore() { + return lastUsageBefore; + } + + public void setLastUsageBefore(Date lastUsageBefore) { + this.lastUsageBefore = lastUsageBefore; + } } diff --git a/src/main/java/org/olat/group/ui/main/BusinessGroupSearchController.java b/src/main/java/org/olat/group/ui/main/BusinessGroupSearchController.java index 22784925433a0407755bd39b74acf282467afc52..b4d0d82121f2c5d6bfc8649299a5aba8d3811e22 100644 --- a/src/main/java/org/olat/group/ui/main/BusinessGroupSearchController.java +++ b/src/main/java/org/olat/group/ui/main/BusinessGroupSearchController.java @@ -20,11 +20,13 @@ package org.olat.group.ui.main; import java.util.Collections; +import java.util.Date; import java.util.List; import org.olat.basesecurity.GroupRoles; import org.olat.core.CoreSpringFactory; import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.AbstractComponent; 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.FormLink; @@ -43,6 +45,7 @@ import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.generic.dtabs.Activateable2; import org.olat.core.id.context.ContextEntry; import org.olat.core.id.context.StateEntry; +import org.olat.core.util.DateUtils; import org.olat.core.util.StringHelper; import org.olat.group.BusinessGroupModule; @@ -68,6 +71,7 @@ public class BusinessGroupSearchController extends FormBasicController implement private SingleSelection managedEl; private SingleSelection resourceEl; private MultipleSelectionElement headlessEl; + private TextElement lastUsageEl; private final boolean showRoles; private final boolean showAdminTools; @@ -169,6 +173,7 @@ public class BusinessGroupSearchController extends FormBasicController implement } managedEl = uifactory.addRadiosHorizontal("managedBg", "search.managed", rightContainer, managedKeys, managedValues); managedEl.select("all", true); + managedEl.setVisible(managedEnable); //resources String[] resourceValues = new String[resourceKeys.length]; @@ -184,6 +189,17 @@ public class BusinessGroupSearchController extends FormBasicController implement headlessEl = uifactory.addCheckboxesHorizontal("headless.groups", "search.headless", rightContainer, keys, values); headlessEl.setElementCssClass("o_sel_group_search_headless_field"); headlessEl.setVisible(showAdminTools); + + // Last usage + String page = velocity_root + "/last_usage.html"; + FormLayoutContainer lastUsageCont = FormLayoutContainer.createCustomFormLayout("lastUsage", getTranslator(), page); + rightContainer.add("lastUsage", lastUsageCont); + lastUsageCont.setLabel("search.last.usage", null); + lastUsageCont.setRootForm(mainForm); + + lastUsageEl = uifactory.addTextElement("search.last.usage", 10, null, lastUsageCont); + ((AbstractComponent)lastUsageEl.getComponent()).setDomReplacementWrapperRequired(false); + lastUsageEl.setDisplaySize(5); FormLayoutContainer buttonLayout = FormLayoutContainer.createButtonLayout("button_layout", getTranslator()); formLayout.add(buttonLayout); @@ -250,7 +266,26 @@ public class BusinessGroupSearchController extends FormBasicController implement protected boolean validateFormLogic(UserRequest ureq) { if(!enabled) return true; - return super.validateFormLogic(ureq); + boolean allOk = super.validateFormLogic(ureq); + + lastUsageEl.clearError(); + if (lastUsageEl != null && lastUsageEl.isVisible()) { + String lastUsage = lastUsageEl.getValue(); + if (StringHelper.containsNonWhitespace(lastUsage)) { + try { + int lastUsageInt = Integer.parseInt(lastUsage); + if (lastUsageInt < 1) { + allOk &= false; + lastUsageEl.setErrorKey("error.int.positive", null); + } + } catch(Exception e) { + allOk &= false; + lastUsageEl.setErrorKey("error.int.positive", null); + } + } + } + + return allOk; } @Override @@ -275,7 +310,9 @@ public class BusinessGroupSearchController extends FormBasicController implement @Override protected void formInnerEvent (UserRequest ureq, FormItem source, FormEvent event) { if (source == searchButton) { - fireSearchEvent(ureq); + if (validateFormLogic(ureq)) { + fireSearchEvent(ureq); + } } } @@ -384,6 +421,13 @@ public class BusinessGroupSearchController extends FormBasicController implement e.setResources(Boolean.FALSE); } } + + if (StringHelper.containsNonWhitespace(lastUsageEl.getValue())) { + Integer lastUsage = Integer.valueOf(lastUsageEl.getValue()); + Date lastUsageBefore = DateUtils.addDays(new Date(), -lastUsage.intValue()); + e.setLastUsageBefore(lastUsageBefore); + } + fireEvent(ureq, e); } } \ No newline at end of file diff --git a/src/main/java/org/olat/group/ui/main/SearchEvent.java b/src/main/java/org/olat/group/ui/main/SearchEvent.java index a2458d950a88b5456fb596ae3c090a4dd7f7c34d..13f5a2be030a423603b459abfc64f1131af0aee3 100644 --- a/src/main/java/org/olat/group/ui/main/SearchEvent.java +++ b/src/main/java/org/olat/group/ui/main/SearchEvent.java @@ -19,6 +19,8 @@ */ package org.olat.group.ui.main; +import java.util.Date; + import org.olat.core.gui.control.Event; import org.olat.core.id.context.StateEntry; import org.olat.core.util.StringHelper; @@ -44,6 +46,7 @@ public class SearchEvent extends Event implements StateEntry { private Boolean publicGroups; private Boolean managed; private Boolean resources; + private Date lastUsageBefore; public SearchEvent() { super("search"); @@ -145,6 +148,14 @@ public class SearchEvent extends Event implements StateEntry { this.headless = headless; } + public Date getLastUsageBefore() { + return lastUsageBefore; + } + + public void setLastUsageBefore(Date lastUsageBefore) { + this.lastUsageBefore = lastUsageBefore; + } + public BusinessGroupQueryParams convertToBusinessGroupQueriesParams() { BusinessGroupQueryParams params = new BusinessGroupQueryParams(); params.setIdRef(StringHelper.containsNonWhitespace(idRef) ? idRef : null); @@ -159,6 +170,7 @@ public class SearchEvent extends Event implements StateEntry { params.setManaged(getManaged()); params.setResources(getResources()); params.setHeadless(isHeadless()); + params.setLastUsageBefore(getLastUsageBefore()); return params; } @@ -175,7 +187,9 @@ public class SearchEvent extends Event implements StateEntry { clone.waiting = waiting; clone.headless = headless; clone.publicGroups = publicGroups; + clone.publicGroups = publicGroups; clone.resources = resources; + clone.lastUsageBefore = lastUsageBefore; return clone; } } diff --git a/src/main/java/org/olat/group/ui/main/_content/last_usage.html b/src/main/java/org/olat/group/ui/main/_content/last_usage.html new file mode 100644 index 0000000000000000000000000000000000000000..4749f7666598f8c4a082e8bea84732ce4b9a2bc3 --- /dev/null +++ b/src/main/java/org/olat/group/ui/main/_content/last_usage.html @@ -0,0 +1,6 @@ +<div class="form-inline"> +$r.render("search.last.usage") <span class="form-control-static">$r.translate("search.last.usage.days")</span> +#if($f.hasError("search.last.usage")) + <br/>$r.render("search.last.usage_ERROR") +#end +</div> \ No newline at end of file diff --git a/src/main/java/org/olat/group/ui/main/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/group/ui/main/_i18n/LocalStrings_de.properties index 9851639136f00116372f6d6c92496894756db7e8..84367cd57a669d4071af30fe7aad6a643a1d4716 100644 --- a/src/main/java/org/olat/group/ui/main/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/group/ui/main/_i18n/LocalStrings_de.properties @@ -28,6 +28,7 @@ remove.send.mail.label=E-Mail versenden error.atleastone=Es muss mindestens einen Besitzer im Kurs geben. error.msg.send.no.rcps=$org.olat.modules.co\:error.msg.send.no.rcps error.managed.group=Die gew\u00e4hlte Gruppe "{0}" wird von einem externen System verwaltet. Die Operation kann nicht ausgef\u00fchrt werden. +error.int.positive=Positive Ganzzahl erwartet error.select.one=Sie m\u00fcssen mindestens eine Gruppe w\u00e4hlen. error.select.one.user=Sie m\u00fcssen mindestens einen Benutzer w\u00e4hlen. mail.member=E-Mail @@ -82,6 +83,8 @@ search.resources=In Kursen verwendet search.headless=Orphans search.headless.check=Gruppen ohne Mitglieder und Ressourcen search.id.format=Keine g\u00fcltige Gruppen-ID +search.last.usage=Letzter Zugriff +search.last.usage.days=Tage tools=<i class\="o_icon o_icon_actions o_icon-lg"> </i> cif.displayname=Name cif.description=Beschreibung diff --git a/src/main/java/org/olat/group/ui/main/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/group/ui/main/_i18n/LocalStrings_en.properties index 02fd70192349ab641736d42d921dac3b150f2f93..bc48c380d1f508f03f36969261eb537545fd25f4 100644 --- a/src/main/java/org/olat/group/ui/main/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/group/ui/main/_i18n/LocalStrings_en.properties @@ -48,6 +48,7 @@ edit.member.title=Member rights course {0} edit.members=Edit email.group=Send E-mail error.atleastone=At least one owner is required in a course. +error.int.positive=Positive integer expected error.managed.group=The selected group "{0}" is managed by an external system. The operation cannot be executed. error.msg.send.no.rcps=$org.olat.modules.co\:error.msg.send.no.rcps error.select.one=You need to select at least one group. @@ -123,6 +124,8 @@ search.headless=Orphans search.headless.check=Groups without members and resources search.id.format=Not a valid group ID search.intro=Search in group +search.last.usage=Last usage +search.last.usage.days=days search.limit.type=Type search.managed=Externally managed search.no=no diff --git a/src/test/java/org/olat/group/test/BusinessGroupDAOTest.java b/src/test/java/org/olat/group/test/BusinessGroupDAOTest.java index 44f05fbab8d875a89087c5c347c1a80da971fca2..89ebc5a6f4285ec330e12ce9288f1ff84b869d3c 100644 --- a/src/test/java/org/olat/group/test/BusinessGroupDAOTest.java +++ b/src/test/java/org/olat/group/test/BusinessGroupDAOTest.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Date; +import java.util.GregorianCalendar; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -39,6 +40,7 @@ import org.olat.core.commons.persistence.DB; import org.olat.core.commons.persistence.PersistenceHelper; import org.olat.core.commons.services.mark.MarkManager; import org.olat.core.id.Identity; +import org.olat.core.util.DateUtils; import org.olat.group.BusinessGroup; import org.olat.group.BusinessGroupMembership; import org.olat.group.BusinessGroupRef; @@ -755,7 +757,34 @@ public class BusinessGroupDAOTest extends OlatTestCase { Assert.assertFalse(retrievedGroupkey.contains(groupManaged.getKey())); Assert.assertFalse(retrievedGroupkey.contains(groupExternalId.getKey())); Assert.assertTrue(retrievedGroupkey.contains(groupUnmanaged.getKey())); - + } + + @Test + public void findBusinessLastUsageBefore() { + Date lastUsageBefore = new GregorianCalendar(2020, 8, 9).getTime(); + Identity identity = JunitTestHelper.createAndPersistIdentityAsRndUser(random()); + BusinessGroup before = businessGroupDao.createAndPersist(null, random(), random(), random(), + null, 0, 5, true, false, true, false, false); + before.setLastUsage(DateUtils.addDays(lastUsageBefore, -2)); + businessGroupDao.merge(before); + BusinessGroup after = businessGroupDao.createAndPersist(null, random(), random(), null, + null, 0, 5, true, false, true, false, false); + after.setLastUsage(DateUtils.addDays(lastUsageBefore, 3)); + businessGroupDao.merge(after); + dbInstance.commitAndCloseSession(); + + // Check managed + BusinessGroupQueryParams params = new BusinessGroupQueryParams(); + params.setLastUsageBefore(lastUsageBefore); + List<StatisticsBusinessGroupRow> groups = businessGroupDao.searchBusinessGroupsForSelection(params, identity); + Assert.assertNotNull(groups); + + Set<Long> retrievedGroupkey = new HashSet<>(); + for(StatisticsBusinessGroupRow group:groups) { + retrievedGroupkey.add(group.getKey()); + } + Assert.assertTrue(retrievedGroupkey.contains(before.getKey())); + Assert.assertFalse(retrievedGroupkey.contains(after.getKey())); } @Test