Skip to content

Commit

Permalink
SAK-46434 As a teacher, allow to hide members that already joined a g…
Browse files Browse the repository at this point in the history
…roup from a joinable set (sakaiproject#9945)
  • Loading branch information
josecebe authored Dec 16, 2021
1 parent d4babce commit 63ad0ee
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 21 deletions.
2 changes: 2 additions & 0 deletions kernel/api/src/main/java/org/sakaiproject/site/api/Group.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public interface Group extends Edit, Serializable, AuthzGroup
static final String GROUP_PROP_VIEW_MEMBERS = "group_prop_view_members";
/** The property to indicate whether the joinable group is unjoinable or not*/
static final String GROUP_PROP_JOINABLE_UNJOINABLE = "group_prop_joinable_unjoinable";
/** The property to show all site members when editing/creating a joinable set*/
static final String GROUP_PROP_JOINABLE_SHOW_ALL = "group_prop_joinable_show_all";
/** The property to indicate by which group it was filtered */
static final String GROUP_PROP_FILTERED_BY = "group_prop_filtered_by";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import javax.inject.Inject;
Expand Down Expand Up @@ -63,12 +65,15 @@ public class GroupController {
@Autowired
private SakaiService sakaiService;

private static List<User> selectableMemberList;

@RequestMapping(value = "/group")
public String showGroup(Model model,
@RequestParam(required=false) String groupId,
@RequestParam(required=false) String filterByGroupId,
@RequestParam(required=false) String currentTitle,
@RequestParam(required=false) String currentDescription ) {
@RequestParam(required=false) String currentDescription,
@RequestParam(required=false) Boolean currentShowAllUsers) {
log.debug("showGroup called with groupId {}.", groupId);

Optional<Site> siteOptional = sakaiService.getCurrentSite();
Expand All @@ -86,6 +91,7 @@ public String showGroup(Model model,
groupForm.setJoinableSetNumOfMembers(String.valueOf(1));
groupForm.setGroupAllowPreviewMembership(false);
groupForm.setGroupUnjoinable(false);
groupForm.setGroupJoinableShowAllUsers(false);

// The list of sections assigned to the group, only for existing groups.
List<Member> sectionProvidedUsers = new ArrayList<>();
Expand All @@ -94,9 +100,11 @@ public String showGroup(Model model,
// The list of members assigned to the group, only for existing groups.
List<String> currentGroupMembers = new ArrayList<String>();
// List of joinable sets that can be assigned to the group
List<String> joinableSetList = new ArrayList<String>();
Set<String> groupJoinableSet = new HashSet<>();
// List of user ids that will be able to join this group of a joinable set
Set<String> joinableMemberSet = new HashSet<>();
// Full list of the site members.
List<User> siteMemberList = new ArrayList<User>();
Set<User> siteMemberSet = new HashSet<>();
// Group and Section lists for the group filter.
List<Group> groupList = new ArrayList<Group>();
List<Group> sectionList = new ArrayList<Group>();
Expand Down Expand Up @@ -170,35 +178,53 @@ public String showGroup(Model model,
groupForm.setJoinableSetNumOfMembers(StringUtils.isNotBlank(group.getProperties().getProperty(Group.GROUP_PROP_JOINABLE_SET_MAX)) ? group.getProperties().getProperty(Group.GROUP_PROP_JOINABLE_SET_MAX) : String.valueOf(0));
groupForm.setGroupAllowPreviewMembership(Boolean.valueOf(group.getProperties().getProperty(Group.GROUP_PROP_JOINABLE_SET_PREVIEW)));
groupForm.setGroupUnjoinable(Boolean.valueOf(group.getProperties().getProperty(Group.GROUP_PROP_JOINABLE_UNJOINABLE)));
// If there is no property saved, true by default to respect the original behaviour
Boolean propertyShowAllUsers = (StringUtils.isNotEmpty(group.getProperties().getProperty(Group.GROUP_PROP_JOINABLE_SHOW_ALL)) ? Boolean.valueOf(group.getProperties().getProperty(Group.GROUP_PROP_JOINABLE_SHOW_ALL)) : true);
groupForm.setGroupJoinableShowAllUsers(currentShowAllUsers == null ? propertyShowAllUsers : currentShowAllUsers);
}
}
}

// Get all the joinable sets from all the site groups.
site.getGroups().forEach(group -> {
String joinableSet = group.getProperties().getProperty(Group.GROUP_PROP_JOINABLE_SET);
if (StringUtils.isNotBlank(joinableSet) && !joinableSetList.contains(joinableSet)) {
joinableSetList.add(joinableSet);
}
});
Collections.sort(joinableSetList, new AlphaNumericComparator());

// For every member of the site or the filtered group, add it to the selector except if they were provided by a role.
for (Member member : filterGroup == null ? site.getMembers() : filterGroup.getMembers()) {
if (!roleProviderList.contains(member.getRole().getId()) && sectionProvidedUsers.stream().noneMatch(m -> m.getUserId().equals(member.getUserId()))) {
Optional<User> memberUserOptional = sakaiService.getUser(member.getUserId());
if (memberUserOptional.isPresent()) {
siteMemberList.add(memberUserOptional.get());
siteMemberSet.add(memberUserOptional.get());
}
}
}
//Sort the members of the site by sort name.
List<User> siteMemberList = new ArrayList<>(siteMemberSet);
Collections.sort(siteMemberList, new UserSortNameComparator());
selectableMemberList = new ArrayList<>(siteMemberList);

// Get all the joinable sets from all the site groups.
site.getGroups().forEach(group -> {
String joinableSet = group.getProperties().getProperty(Group.GROUP_PROP_JOINABLE_SET);
if (StringUtils.isNotBlank(joinableSet)) {
groupJoinableSet.add(joinableSet);
// Remove from selectable users the ones that are currently in any group from the joinable set
if (!groupForm.isGroupJoinableShowAllUsers()) {
joinableMemberSet.addAll(group.getUsers());
if (!groupForm.getGroupId().equals(group.getId())) {
selectableMemberList = selectableMemberList.stream().filter(user -> {
// If the user is not in any group from the joinable set, or the user joined the current group, then the participant is a selectable member
return !joinableMemberSet.contains(user.getId()) || groupForm.getGroupMembers().contains(user.getId());
}).collect(Collectors.toList());
}
}
}
});
List<String> joinableSetList = new ArrayList<>(groupJoinableSet);
Collections.sort(joinableSetList, new AlphaNumericComparator());

// Add the attributes to the model
model.addAttribute("groupForm", groupForm);
model.addAttribute("siteRoleList", site.getRoles().stream().filter(role -> !role.getId().startsWith(".")).collect(Collectors.toList()));
model.addAttribute("joinableSetList", joinableSetList);
model.addAttribute("joinableMemberSet", joinableMemberSet);
model.addAttribute("selectableMemberList", selectableMemberList);
model.addAttribute("siteMemberList", siteMemberList);
model.addAttribute("groupList", groupList);
model.addAttribute("sectionList", sectionList);
Expand All @@ -225,6 +251,7 @@ public String saveGroup(@ModelAttribute GroupForm groupForm, Model model) {
String groupDescription = groupForm.getGroupDescription();
String filterByGroupId = groupForm.getFilterByGroupId();
String joinableSetName = groupForm.getJoinableSetName();
boolean joinableShowAllUsers = groupForm.isGroupJoinableShowAllUsers();
// The group can be new or existing.
Group group = null;
List<Member> currentGroupMembers = null;
Expand All @@ -240,13 +267,13 @@ public String saveGroup(@ModelAttribute GroupForm groupForm, Model model) {
//Ensure the group title is provided
if (StringUtils.isBlank(groupTitle)) {
model.addAttribute("errorMessage", messageSource.getMessage("groups.error.providetitle", null, userLocale));
return showGroup(model, groupId, filterByGroupId, groupTitle, groupDescription);
return showGroup(model, groupId, filterByGroupId, groupTitle, groupDescription, joinableShowAllUsers);
}

//Ensure the group title is shorter than the maximum allowed
if (groupTitle.length() > 99) {
model.addAttribute("errorMessage", messageSource.getMessage("groups.error.titlelength", null, userLocale));
return showGroup(model, groupId, filterByGroupId, groupTitle, groupDescription);
return showGroup(model, groupId, filterByGroupId, groupTitle, groupDescription, joinableShowAllUsers);
}

// If a joinable set is selected, validate the maximum number of members.
Expand All @@ -258,14 +285,14 @@ public String saveGroup(@ModelAttribute GroupForm groupForm, Model model) {
}
} catch (NumberFormatException e) {
model.addAttribute("errorMessage", messageSource.getMessage("groups.error.maxmembers", null, userLocale));
return showGroup(model, groupId, filterByGroupId, groupTitle, groupDescription);
return showGroup(model, groupId, filterByGroupId, groupTitle, groupDescription, joinableShowAllUsers);
}
}

//Ensure the group title is not in use by other groups.
if (site.getGroups().stream().anyMatch(g -> !g.getId().equals(groupId) && groupTitle.equalsIgnoreCase(g.getTitle()))) {
model.addAttribute("errorMessage", messageSource.getMessage("groups.error.sametitle", null, userLocale));
return showGroup(model, groupId, filterByGroupId, groupTitle, groupDescription);
return showGroup(model, groupId, filterByGroupId, groupTitle, groupDescription, joinableShowAllUsers);
}

// If the group already exists, get it from the site and delete all the members.
Expand Down Expand Up @@ -303,11 +330,13 @@ public String saveGroup(@ModelAttribute GroupForm groupForm, Model model) {
group.getProperties().addProperty(Group.GROUP_PROP_JOINABLE_SET_MAX, groupForm.getJoinableSetNumOfMembers());
group.getProperties().addProperty(Group.GROUP_PROP_JOINABLE_SET_PREVIEW, String.valueOf(groupForm.isGroupAllowPreviewMembership()));
group.getProperties().addProperty(Group.GROUP_PROP_JOINABLE_UNJOINABLE, String.valueOf(groupForm.isGroupUnjoinable()));
group.getProperties().addProperty(Group.GROUP_PROP_JOINABLE_SHOW_ALL, String.valueOf(groupForm.isGroupJoinableShowAllUsers()));
} else {
group.getProperties().removeProperty(Group.GROUP_PROP_JOINABLE_SET);
group.getProperties().removeProperty(Group.GROUP_PROP_JOINABLE_SET_MAX);
group.getProperties().removeProperty(Group.GROUP_PROP_JOINABLE_SET_PREVIEW);
group.getProperties().removeProperty(Group.GROUP_PROP_JOINABLE_UNJOINABLE);
group.getProperties().removeProperty(Group.GROUP_PROP_JOINABLE_SHOW_ALL);
}

// Assign roles or members to the groups.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ public class GroupForm {
private String joinableSetNumOfMembers;
private boolean groupAllowPreviewMembership;
private boolean groupUnjoinable;
private boolean groupJoinableShowAllUsers;
private String filterByGroupId;
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ groups.joinableset=Joinable Set
groups.joinableset.allowpreviewmembership=Allow user to see group membership before joining
groups.joinableset.allowunjoin=Allow members to unjoin the group after joining
groups.joinableset.maxmembers=Maximum number of members
groups.joinableset.maxmembers.info=A participant cannot join a group if the group is full. A teacher can add participants as many as wanted.
groups.joinableset.show.all=Show all site participants, even if they already joined a group of this joinable set
groups.joinableset.show.all.info=A participant cannot join more than one group from a joinable set. As a teacher you can mark this checkbox and add participants that already joined other groups from a same joinable set.
groups.membership.controls=Membership controls
groups.multiselect.search=Search...
groups.none=None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@
includeWebjarLibrary('select2');

$(document).ready(function() {
// Initialize popovers
$('[data-toggle="popover"]').popover();

//Disable or enable the submit depending on the group title value.
$('#create-group-submit-button').prop('disabled', $('#groupTitle').val() === '');
Expand All @@ -138,12 +140,13 @@
$(':input[type="submit"]').prop('disabled', $(this).val() === '');
});

$('#groupMembershipFilter').on('change', function() {
$('#groupMembershipFilter, #groupJoinableShowAllUsers').on('change', function() {
const groupParams = {
filterByGroupId: this.value,
groupId: $('#groupId').val(),
currentTitle: $('#groupTitle').val(),
currentDescription: $('#groupDescription').val()
currentDescription: $('#groupDescription').val(),
currentShowAllUsers: document.getElementById("groupJoinableShowAllUsers").checked
};
window.location = 'group?'+new URLSearchParams(groupParams);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,22 @@ <h1 th:if="${groupForm.groupId != null}" th:text="#{groups.header.edit}"></h1>
<span th:text="#{groups.joinableset.allowunjoin}"></span>
</label>
</div>
<label for="joinableSetNumOfMembers" class="form-control-label block" th:text="#{groups.joinableset.maxmembers}">Max members:</label>
<div class="block">
<label for="joinableSetNumOfMembers" class="form-control-label" th:text="#{groups.joinableset.maxmembers}">Max members:</label>
<a class="fa fa-info-circle" tabindex="0" data-placement="bottom" role="button" data-toggle="popover" data-trigger="focus" data-html="true" th:attr="data-content=''+#{groups.joinableset.maxmembers.info}+''"></a>
</div>
<input name="joinableSetNumOfMembers" id="joinableSetNumOfMembers" type="number" th:field="*{joinableSetNumOfMembers}" min="1" max="999" class="form-control"/>
</div>
</div>
<div class="col-sm-8">
<div class="checkbox block">
<label for="groupJoinableShowAllUsers" class="form-control-label">
<input id="groupJoinableShowAllUsers" class="form-control" type="checkbox" th:field="*{groupJoinableShowAllUsers}" th:checked="${groupForm.groupJoinableShowAllUsers}"/>
<span th:text="#{groups.joinableset.show.all}">Show all site participants, even if they already joined a group of this joinable set</span>
</label>
<a class="fa fa-info-circle" tabindex="0" data-placement="bottom" role="button" data-toggle="popover" data-trigger="focus" data-html="true" th:attr="data-content=''+#{groups.joinableset.show.all.info}+''"></a>
</div>
</div>
</div>
<div class="form-group row" th:if="${groupFilterEnabled && (!groupList.isEmpty() || !sectionList.isEmpty())}">
<div class="col-sm-8">
Expand All @@ -81,7 +93,7 @@ <h1 th:if="${groupForm.groupId != null}" th:text="#{groups.header.edit}"></h1>
<select name="groupMembers" th:field="*{groupMembers}" id="groupMembers" class="form-control" multiple="multiple">
<option th:selected="${groupForm.groupMembers != null && groupForm.groupMembers.contains(siteRole.id)}" th:each="siteRole : ${siteRoleList}" th:value="${siteRole.id}" th:text="|#{groups.role} ${siteRole.id}|"></option>
<option th:selected="${groupForm.groupMembers != null && groupForm.groupMembers.contains(siteSection.title)}" th:each="siteSection : ${sectionList}" th:value="${siteSection.title}" th:text="|#{groups.section} ${siteSection.title}|"></option>
<option th:selected="${groupForm.groupMembers != null && groupForm.groupMembers.contains(siteMember.id)}" th:each="siteMember : ${siteMemberList}" th:value="${siteMember.id}" th:text="|${siteMember.sortName} (${siteMember.eid})|"></option>
<option th:selected="${groupForm.groupMembers != null && groupForm.groupMembers.contains(siteMember.id)}" th:each="siteMember : ${selectableMemberList}" th:value="${siteMember.id}" th:text="|${siteMember.sortName} (${siteMember.eid})|"></option>
</select>
</div>
</div>
Expand Down

0 comments on commit 63ad0ee

Please sign in to comment.