From cea98c3f3d2c6712082eca1304837e83bcad1e58 Mon Sep 17 00:00:00 2001 From: fkiefer <none@none> Date: Wed, 5 Apr 2017 11:21:13 +0200 Subject: [PATCH] OO-2635 implement generic switchable avatar and table memberlist for course and group --- .../java/org/olat/_spring/mainContext.xml | 2 +- .../manager/MembersExportManager.java | 168 ++++++ .../memberlist/manager/XlsMembersExport.java | 95 ++++ .../ui/MembersAvatarDisplayRunController.java | 527 ++++++++++++++++++ .../ui/MembersDisplayRunController.java | 184 ++++++ .../ui/MembersListDisplayRunController.java | 363 ++++++++++++ .../memberlist/ui}/MembersMailController.java | 103 +++- .../ui}/MembersPrintController.java | 24 +- .../memberlist/ui/MembersTableController.java | 360 ++++++++++++ .../ui}/SelectMembersController.java | 27 +- .../ui}/_content/individual_members.html | 0 .../memberlist/ui}/_content/memberList.html | 0 .../memberlist/ui}/_content/members.html | 9 + .../memberlist/ui/_content/membersTable.html | 37 ++ .../memberlist/ui/_content/membersToggle.html | 30 + .../memberlist/ui}/_content/print.html | 3 + .../memberlist/ui}/_content/printList.html | 0 .../commons/memberlist/ui/_content/table.html | 5 + .../ui/_i18n/LocalStrings_de.properties | 5 + .../ui/_i18n/LocalStrings_en.properties | 5 + .../ui/_i18n/LocalStrings_fr.properties | 5 + .../ui/_i18n/LocalStrings_it.properties | 5 + .../ui/_i18n/LocalStrings_pl.properties | 5 + .../ui/_i18n/LocalStrings_pt_BR.properties | 5 + .../ui/_i18n/LocalStrings_zh_CN.properties | 5 + src/main/java/org/olat/core/util/Util.java | 19 + .../MembersCourseNodeRunController.java | 413 +------------- .../course/nodes/members/MembersHelpers.java | 24 +- .../members/_i18n/LocalStrings_de.properties | 2 + .../members/_i18n/LocalStrings_en.properties | 2 + .../GroupMembersDisplayController.java | 65 ++- .../_content/groupmembersdisplay.html | 19 - .../group/ui/main/MemberListTableModel.java | 17 +- .../run/BusinessGroupMainRunController.java | 48 +- .../ui/run/GroupMembersRunController.java | 98 ++++ .../ui/run/_i18n/LocalStrings_de.properties | 31 ++ .../ui/run/_i18n/LocalStrings_en.properties | 33 +- 37 files changed, 2208 insertions(+), 535 deletions(-) create mode 100644 src/main/java/org/olat/commons/memberlist/manager/MembersExportManager.java create mode 100644 src/main/java/org/olat/commons/memberlist/manager/XlsMembersExport.java create mode 100644 src/main/java/org/olat/commons/memberlist/ui/MembersAvatarDisplayRunController.java create mode 100644 src/main/java/org/olat/commons/memberlist/ui/MembersDisplayRunController.java create mode 100644 src/main/java/org/olat/commons/memberlist/ui/MembersListDisplayRunController.java rename src/main/java/org/olat/{course/nodes/members => commons/memberlist/ui}/MembersMailController.java (85%) rename src/main/java/org/olat/{course/nodes/members => commons/memberlist/ui}/MembersPrintController.java (83%) create mode 100644 src/main/java/org/olat/commons/memberlist/ui/MembersTableController.java rename src/main/java/org/olat/{course/nodes/members => commons/memberlist/ui}/SelectMembersController.java (83%) rename src/main/java/org/olat/{course/nodes/members => commons/memberlist/ui}/_content/individual_members.html (100%) rename src/main/java/org/olat/{course/nodes/members => commons/memberlist/ui}/_content/memberList.html (100%) rename src/main/java/org/olat/{course/nodes/members => commons/memberlist/ui}/_content/members.html (77%) create mode 100644 src/main/java/org/olat/commons/memberlist/ui/_content/membersTable.html create mode 100644 src/main/java/org/olat/commons/memberlist/ui/_content/membersToggle.html rename src/main/java/org/olat/{course/nodes/members => commons/memberlist/ui}/_content/print.html (96%) rename src/main/java/org/olat/{course/nodes/members => commons/memberlist/ui}/_content/printList.html (100%) create mode 100644 src/main/java/org/olat/commons/memberlist/ui/_content/table.html create mode 100644 src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_de.properties create mode 100644 src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_en.properties create mode 100644 src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_fr.properties create mode 100644 src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_it.properties create mode 100644 src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_pl.properties create mode 100644 src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_pt_BR.properties create mode 100644 src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_zh_CN.properties delete mode 100644 src/main/java/org/olat/group/ui/homepage/_content/groupmembersdisplay.html create mode 100644 src/main/java/org/olat/group/ui/run/GroupMembersRunController.java diff --git a/src/main/java/org/olat/_spring/mainContext.xml b/src/main/java/org/olat/_spring/mainContext.xml index 006d5322469..9f281a42f91 100644 --- a/src/main/java/org/olat/_spring/mainContext.xml +++ b/src/main/java/org/olat/_spring/mainContext.xml @@ -8,7 +8,7 @@ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> - <context:component-scan base-package="org.olat.note,org.olat.social" /> + <context:component-scan base-package="org.olat.note,org.olat.social,org.olat.commons.memberlist" /> <import resource="classpath:/org/olat/core/util/threadlog/_spring/threadlogCorecontext.xml"/> <import resource="classpath:/org/olat/core/_spring/mainCorecontext.xml"/> diff --git a/src/main/java/org/olat/commons/memberlist/manager/MembersExportManager.java b/src/main/java/org/olat/commons/memberlist/manager/MembersExportManager.java new file mode 100644 index 00000000000..d33b004e60f --- /dev/null +++ b/src/main/java/org/olat/commons/memberlist/manager/MembersExportManager.java @@ -0,0 +1,168 @@ +/** + * <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.commons.memberlist.manager; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.olat.basesecurity.BaseSecurity; +import org.olat.core.gui.media.MediaResource; +import org.olat.core.gui.translator.Translator; +import org.olat.core.id.Identity; +import org.olat.core.util.Util; +import org.olat.course.nodes.members.Member; +import org.olat.course.nodes.members.MembersCourseNodeRunController; +import org.olat.group.BusinessGroup; +import org.olat.group.BusinessGroupMembership; +import org.olat.group.BusinessGroupService; +import org.olat.group.ui.run.GroupMembersRunController; +import org.olat.repository.RepositoryEntry; +import org.olat.repository.RepositoryManager; +import org.olat.repository.model.RepositoryEntryMembership; +import org.olat.user.UserManager; +import org.olat.user.propertyhandlers.UserPropertyHandler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + + +/** + * Initial Date: 28.03.2017 + * @author fkiefer, fabian.kiefer@frentix.com, www.frentix.com + */ +@Service +public class MembersExportManager { + + public static final String USER_PROPS_ID = MembersCourseNodeRunController.class.getName(); + + public static final int USER_PROPS_OFFSET = 500; + + @Autowired + private RepositoryManager repositoryManager; + @Autowired + protected BaseSecurity securityManager; + @Autowired + private BusinessGroupService businessGroupService; + @Autowired + private UserManager userManager; + + public Map<Long,RepositoryEntryMembership> getRepoMembershipMap(RepositoryEntry repoEntry) { + List<RepositoryEntryMembership> repoMemberships = repositoryManager.getRepositoryEntryMembership(repoEntry); + Map<Long,RepositoryEntryMembership> map = new HashMap<>(); + for (RepositoryEntryMembership membership : repoMemberships) { + map.put(membership.getIdentityKey(), membership); + } + return map; + } + + public Map<Long,BusinessGroupMembership> getGroupMembershipMap(List<BusinessGroup> groups) { + List<BusinessGroupMembership> groupMemberships = businessGroupService.getBusinessGroupsMembership(groups); + Map<Long,BusinessGroupMembership> map = new HashMap<>(); + for (BusinessGroupMembership membership : groupMemberships) { + map.put(membership.getIdentityKey(), membership); + } + return map; + } + + private void putRoleToMember(List<Identity> rows, Map<Identity, StringBuilder> membersMap, Identity member, String role, Translator roleTranslator) { + if (membersMap.containsKey(member)) { + String roleString = roleTranslator.translate("members." + role); + StringBuilder roleBuilder = membersMap.get(member); + if (!roleBuilder.toString().contains(roleString)) { + roleBuilder.append(", ").append(roleString); + } + } else { + membersMap.put(member, new StringBuilder(roleTranslator.translate("members." + role))); + rows.add(member); + } + } + + public MediaResource getXlsMediaResource(boolean showOwners, boolean showCoaches, boolean showParticipants, boolean showWaiting, + List<Identity> owners, List<Identity> coaches, List<Identity> participants, List<Identity> waiting, + Translator translator, List<UserPropertyHandler> userPropertyHandlers, RepositoryEntry repoEntry, BusinessGroup businessGroup) {//TODO + Map<Long,BusinessGroupMembership> groupmemberships; + Map<Long,RepositoryEntryMembership> repomemberships; + if (repoEntry == null) { + List<BusinessGroup> groups = new ArrayList<>(); + groups.add(businessGroup); + groupmemberships = getGroupMembershipMap(groups); + repomemberships = new HashMap<>(); + } else { + repomemberships = getRepoMembershipMap(repoEntry); + List<BusinessGroup> groups = businessGroupService.findBusinessGroups(null, repoEntry, 0, -1); + groupmemberships = getGroupMembershipMap(groups); + } + + List<List<Identity>> roleMembers = new ArrayList<>(); + if (showOwners) { + roleMembers.add(owners); + } + if (showCoaches) { + roleMembers.add(coaches); + } + if (showParticipants) { + roleMembers.add(participants); + } + if (showWaiting) { + roleMembers.add(waiting); + } + Translator repoTranslator = Util.createPackageTranslator(Member.class, translator.getLocale()); + Translator groupTranslator = Util.createPackageTranslator(GroupMembersRunController.class, translator.getLocale()); + Map<Identity, StringBuilder> membersMap = new HashMap<>(); + List<Identity> rows = new ArrayList<>(); + for (List<Identity> membersList : roleMembers) { + for (Identity member : membersList) { + Long memberKey = member.getKey(); + if (repomemberships != null && !repomemberships.isEmpty() && repomemberships.containsKey(memberKey)) { + RepositoryEntryMembership repomembership = repomemberships.get(memberKey); + if (repomembership.isOwner()) { + putRoleToMember(rows, membersMap, member, "owners", repoTranslator); + } + if (repomembership.isCoach()) { + putRoleToMember(rows, membersMap, member, "coaches", repoTranslator); + } + if (repomembership.isParticipant()) { + putRoleToMember(rows, membersMap, member, "participants", repoTranslator); + } + } + if (groupmemberships != null && !groupmemberships.isEmpty() && groupmemberships.containsKey(memberKey)) { + BusinessGroupMembership groupmembership = groupmemberships.get(memberKey); + if (groupmembership.isOwner()) { + putRoleToMember(rows, membersMap, member, "coaches", groupTranslator); + } + if (groupmembership.isParticipant()) { + putRoleToMember(rows, membersMap, member, "participants", groupTranslator); + } + if (groupmembership.isWaiting()) { + putRoleToMember(rows, membersMap, member, "waiting", groupTranslator); + } + } + } + } + + Translator handlerTranslator = userManager.getPropertyHandlerTranslator(translator); + XlsMembersExport exporter = new XlsMembersExport(); + MediaResource resource = exporter.export(rows, membersMap, handlerTranslator, userPropertyHandlers); + + return resource; + } + +} diff --git a/src/main/java/org/olat/commons/memberlist/manager/XlsMembersExport.java b/src/main/java/org/olat/commons/memberlist/manager/XlsMembersExport.java new file mode 100644 index 00000000000..54a992b018f --- /dev/null +++ b/src/main/java/org/olat/commons/memberlist/manager/XlsMembersExport.java @@ -0,0 +1,95 @@ +/** + * <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.commons.memberlist.manager; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import org.olat.admin.landingpages.ui.RulesDataModel; +import org.olat.core.gui.media.MediaResource; +import org.olat.core.gui.translator.Translator; +import org.olat.core.id.Identity; +import org.olat.core.logging.OLog; +import org.olat.core.logging.Tracing; +import org.olat.core.util.Formatter; +import org.olat.core.util.Util; +import org.olat.core.util.openxml.OpenXMLWorkbook; +import org.olat.core.util.openxml.OpenXMLWorkbookResource; +import org.olat.core.util.openxml.OpenXMLWorksheet; +import org.olat.core.util.openxml.OpenXMLWorksheet.Row; +import org.olat.user.propertyhandlers.UserPropertyHandler; + +/** + * Initial Date: 23.03.2017 + * @author fkiefer, fabian.kiefer@frentix.com, www.frentix.com + */ +public class XlsMembersExport { + + private static final OLog log = Tracing.createLoggerFor(XlsMembersExport.class); + + + public MediaResource export(List<Identity> rows, Map<Identity, StringBuilder> members, Translator translator, List<UserPropertyHandler> userPropertyHandlers) { + + String label = "TableExport_" + + Formatter.formatDatetimeFilesystemSave(new Date(System.currentTimeMillis())) + + ".xlsx"; + + return new OpenXMLWorkbookResource(label) { + @Override + protected void generate(OutputStream out) { + try (OpenXMLWorkbook workbook = new OpenXMLWorkbook(out, 1)) { + OpenXMLWorksheet sheet = workbook.nextWorksheet(); + createHeader(userPropertyHandlers, translator, sheet, workbook); + createData(members, rows, userPropertyHandlers, sheet); + } catch (IOException e) { + log.error("Unable to export xlsx", e); + } + } + }; + } + + private void createHeader(List<UserPropertyHandler> userPropertyHandlers, Translator translator, + OpenXMLWorksheet sheet, OpenXMLWorkbook workbook) { + Row headerRow = sheet.newRow(); + for (int c = 0; c < userPropertyHandlers.size(); c++) { + UserPropertyHandler handler = userPropertyHandlers.get(c); + String header = translator.translate("form.name." + handler.getName()); + headerRow.addCell(c, header, workbook.getStyles().getHeaderStyle()); + } + Translator roleTranslator = Util.createPackageTranslator(RulesDataModel.class, translator.getLocale()); + headerRow.addCell(userPropertyHandlers.size(), roleTranslator.translate("rules.role")); + } + + private void createData(Map<Identity, StringBuilder> members, List<Identity> rows, List<UserPropertyHandler> userPropertyHandlers, OpenXMLWorksheet sheet) { + sheet.setHeaderRows(1); + for (int r = 0; r < rows.size(); r++) { + Row dataRow = sheet.newRow(); + for (int c = 0; c < userPropertyHandlers.size(); c++) { + String value = userPropertyHandlers.get(c).getUserProperty(rows.get(r).getUser(), null); + dataRow.addCell(c, value, null); + } + dataRow.addCell(userPropertyHandlers.size(), members.get(rows.get(r)).toString()); + } + } + +} diff --git a/src/main/java/org/olat/commons/memberlist/ui/MembersAvatarDisplayRunController.java b/src/main/java/org/olat/commons/memberlist/ui/MembersAvatarDisplayRunController.java new file mode 100644 index 00000000000..a885f67e64e --- /dev/null +++ b/src/main/java/org/olat/commons/memberlist/ui/MembersAvatarDisplayRunController.java @@ -0,0 +1,527 @@ +/** + * <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.commons.memberlist.ui; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import org.olat.NewControllerFactory; +import org.olat.basesecurity.BaseSecurity; +import org.olat.commons.memberlist.manager.MembersExportManager; +import org.olat.core.commons.fullWebApp.popup.BaseFullWebappPopupLayoutFactory; +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.Component; +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; +import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +import org.olat.core.gui.components.form.flexible.impl.FormEvent; +import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; +import org.olat.core.gui.components.link.Link; +import org.olat.core.gui.components.link.LinkFactory; +import org.olat.core.gui.components.link.LinkPopupSettings; +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.creator.ControllerCreator; +import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController; +import org.olat.core.gui.media.MediaResource; +import org.olat.core.gui.translator.Translator; +import org.olat.core.helpers.Settings; +import org.olat.core.id.Identity; +import org.olat.core.id.IdentityEnvironment; +import org.olat.core.id.Roles; +import org.olat.core.id.UserConstants; +import org.olat.core.id.context.BusinessControl; +import org.olat.core.id.context.BusinessControlFactory; +import org.olat.core.util.StringHelper; +import org.olat.core.util.mail.ContactList; +import org.olat.core.util.mail.ContactMessage; +import org.olat.core.util.session.UserSessionManager; +import org.olat.course.nodes.members.Member; +import org.olat.course.nodes.members.MembersCourseNodeRunController; +import org.olat.course.run.environment.CourseEnvironment; +import org.olat.group.BusinessGroup; +import org.olat.instantMessaging.InstantMessagingModule; +import org.olat.instantMessaging.InstantMessagingService; +import org.olat.instantMessaging.OpenInstantMessageEvent; +import org.olat.instantMessaging.model.Buddy; +import org.olat.instantMessaging.model.Presence; +import org.olat.modules.co.ContactFormController; +import org.olat.repository.RepositoryEntry; +import org.olat.user.DisplayPortraitManager; +import org.olat.user.UserAvatarMapper; +import org.olat.user.UserManager; +import org.olat.user.propertyhandlers.UserPropertyHandler; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * Description:<br> + * The run controller show the list of members of the course + * + * <P> + * Initial Date: 11 mars 2011 <br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * @author fkiefer + */ +public class MembersAvatarDisplayRunController extends FormBasicController { + + private final List<UserPropertyHandler> userPropertyHandlers; + public static final String USER_PROPS_ID = MembersCourseNodeRunController.class.getName(); + + private final CourseEnvironment courseEnv; + private final DisplayPortraitManager portraitManager; + private final String avatarBaseURL; + + private Link printLink; + private FormLink allEmailLink; + private FormLink downloadLink; + + private List<Member> ownerList; + private List<Member> coachList; + private List<Member> participantList; + private List<Member> waitingtList; + + private List<Identity> owners; + private List<Identity> coaches; + private List<Identity> participants; + private List<Identity> waiting; + + private final boolean canEmail; + private final boolean showOwners; + private final boolean showCoaches; + private final boolean showParticipants; + private final boolean showWaiting; + private final boolean chatEnabled; + private final boolean editable; + + + private FormBasicController mailCtrl; + private ContactFormController emailController; + private CloseableModalController cmc; + + private int count = 0; + private final boolean deduplicateList; + + @Autowired + private UserManager userManager; + @Autowired + private BaseSecurity securityManager; + @Autowired + private InstantMessagingModule imModule; + @Autowired + private InstantMessagingService imService; + @Autowired + private UserSessionManager sessionManager; + @Autowired + private MembersExportManager exportManager; + + private BusinessGroup businessGroup; + private RepositoryEntry repoEntry; + + + public MembersAvatarDisplayRunController(UserRequest ureq, WindowControl wControl, Translator translator, CourseEnvironment courseEnv, BusinessGroup businessGroup, + List<Identity> owners, List<Identity> coaches, List<Identity> participants, List<Identity> waiting, boolean canEmail, boolean deduplicateList, + boolean showOwners, boolean showCoaches, boolean showParticipants, boolean showWaiting, boolean editable) { + super(ureq, wControl, "members", translator); + setTranslator(translator); + + this.courseEnv = courseEnv; + this.businessGroup = businessGroup; + this.repoEntry = courseEnv != null ? courseEnv.getCourseGroupManager().getCourseEntry() : null; + + userPropertyHandlers = userManager.getUserPropertyHandlersFor(USER_PROPS_ID, false); + avatarBaseURL = registerCacheableMapper(ureq, "avatars-members", new UserAvatarMapper(true)); + portraitManager = DisplayPortraitManager.getInstance(); + chatEnabled = imModule.isEnabled() && imModule.isPrivateEnabled(); + // lists + this.owners = owners; + this.coaches = coaches; + this.participants = participants; + this.waiting = waiting; + // flags + this.canEmail = canEmail; + this.showOwners = showOwners; + this.showCoaches = showCoaches; + this.showParticipants = showParticipants; + this.showWaiting = showWaiting; + this.deduplicateList = deduplicateList; + this.editable = editable; + + initForm(ureq); + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + Comparator<Identity> idComparator = new IdentityComparator(); + Collections.sort(owners, idComparator); + Collections.sort(coaches, idComparator); + Collections.sort(participants, idComparator); + Collections.sort(waiting, idComparator); + + if(canEmail) { + allEmailLink = uifactory.addFormLink("email", "members.email.title", null, formLayout, Link.BUTTON); + allEmailLink.setIconLeftCSS("o_icon o_icon_mail"); + } + + IdentityEnvironment idEnv = ureq.getUserSession().getIdentityEnvironment(); + Identity ownId = idEnv.getIdentity(); + Roles roles = idEnv.getRoles(); + boolean memberXlsEnabled = businessGroup != null ? businessGroup.isDownloadMembersLists() && !waiting.contains(ownId) : false; + if (editable && (roles.isOLATAdmin() || roles.isGroupManager() || owners.contains(ownId) || coaches.contains(ownId) || memberXlsEnabled)) { + downloadLink = uifactory.addFormLink("download", "members.download", null, formLayout, Link.BUTTON); + downloadLink.setIconLeftCSS("o_icon o_icon_download"); + if(formLayout instanceof FormLayoutContainer) { + printLink = LinkFactory.createButton("print", ((FormLayoutContainer)formLayout).getFormItemComponent(), this); + printLink.setIconLeftCSS("o_icon o_icon_print o_icon-lg"); + printLink.setPopup(new LinkPopupSettings(700, 500, "print-members")); + ((FormLayoutContainer)formLayout).getFormItemComponent().put("print", printLink); + } + } + + Set<Long> duplicateCatcher = deduplicateList ? new HashSet<Long>() : null; + ownerList = initFormMemberList("owners", owners, duplicateCatcher, formLayout, canEmail); + coachList = initFormMemberList("coaches", coaches, duplicateCatcher, formLayout, canEmail); + participantList = initFormMemberList("participants", participants, duplicateCatcher, formLayout, canEmail); + waitingtList = initFormMemberList("waiting", waiting, duplicateCatcher, formLayout, canEmail); + + if(formLayout instanceof FormLayoutContainer) { + FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout; + layoutCont.contextPut("showOwners", showOwners); + layoutCont.contextPut("hasOwners", new Boolean(!ownerList.isEmpty())); + layoutCont.contextPut("showCoaches", showCoaches); + layoutCont.contextPut("hasCoaches", new Boolean(!coachList.isEmpty())); + layoutCont.contextPut("showParticipants", showParticipants); + layoutCont.contextPut("hasParticipants", new Boolean(!participantList.isEmpty())); + layoutCont.contextPut("showWaiting", showWaiting); + layoutCont.contextPut("hasWaiting", new Boolean(!waitingtList.isEmpty())); + } + } + + private List<Member> initFormMemberList(String name, List<Identity> ids, Set<Long> duplicateCatcher, FormItemContainer formLayout, boolean withEmail) { + String page = velocity_root + "/memberList.html"; + + FormLayoutContainer container = FormLayoutContainer.createCustomFormLayout(name, getTranslator(), page); + formLayout.add(name, container); + container.setRootForm(mainForm); + + List<Member> members = createMemberLinks(ids, duplicateCatcher, container, withEmail); + container.contextPut("members", members); + container.contextPut("avatarBaseURL", avatarBaseURL); + return members; + } + + protected List<Member> createMemberLinks(List<Identity> identities, Set<Long> duplicateCatcher, FormLayoutContainer formLayout, boolean withEmail) { + List<Member> members = new ArrayList<>(); + for(Identity identity:identities) { + if(duplicateCatcher != null && duplicateCatcher.contains(identity.getKey())) continue; + + Member member = createMember(identity); + members.add(member); + + String guiId = Integer.toString(++count); + String fullname = StringHelper.escapeHtml(member.getFullName()); + + FormLink idLink = uifactory.addFormLink("id_".concat(guiId), "id", fullname, null, formLayout, Link.NONTRANSLATED); + + idLink.setUserObject(member); + formLayout.add(idLink.getComponent().getComponentName(), idLink); + member.setIdLink(idLink); + + if(withEmail) { + FormLink emailLink = uifactory.addFormLink("mail_".concat(guiId), "mail", "", null, formLayout, Link.NONTRANSLATED); + emailLink.setUserObject(member); + emailLink.setIconLeftCSS("o_icon o_icon_mail o_icon-lg"); + emailLink.setElementCssClass("o_mail"); + formLayout.add(emailLink.getComponent().getComponentName(), emailLink); + member.setEmailLink(emailLink); + } + if(chatEnabled && editable) { + FormLink chatLink = uifactory.addFormLink("chat_".concat(guiId), "chat", "", null, formLayout, Link.NONTRANSLATED); + chatLink.setUserObject(member); + chatLink.setElementCssClass("o_chat"); + formLayout.add(chatLink.getComponent().getComponentName(), chatLink); + member.setChatLink(chatLink); + } + + if(duplicateCatcher != null) { + duplicateCatcher.add(identity.getKey()); + } + } + + if(chatEnabled && editable) { + Long me = getIdentity().getKey(); + if(imModule.isOnlineStatusEnabled()) { + Map<Long,Member> loadStatus = new HashMap<>(); + + for(Member member:members) { + if(member.getKey().equals(me)) { + member.getChatLink().setVisible(false); + } else if(sessionManager.isOnline(member.getKey())) { + loadStatus.put(member.getKey(), member); + } else { + member.getChatLink().setIconLeftCSS("o_icon o_icon_status_unavailable"); + } + } + + if(loadStatus.size() > 0) { + List<Long> statusToLoadList = new ArrayList<>(loadStatus.keySet()); + Map<Long,String> statusMap = imService.getBuddyStatus(statusToLoadList); + for(Long toLoad:statusToLoadList) { + String status = statusMap.get(toLoad); + Member member = loadStatus.get(toLoad); + if(status == null || Presence.available.name().equals(status)) { + member.getChatLink().setIconLeftCSS("o_icon o_icon_status_available"); + } else if(Presence.dnd.name().equals(status)) { + member.getChatLink().setIconLeftCSS("o_icon o_icon_status_dnd"); + } else { + member.getChatLink().setIconLeftCSS("o_icon o_icon_status_unavailable"); + } + } + } + } else { + for(Member member:members) { + if(member.getKey().equals(me)) { + member.getChatLink().setVisible(false); + } else { + member.getChatLink().setIconLeftCSS("o_icon o_icon_status_chat"); + } + } + } + } + + return members; + } + + private Member createMember(Identity identity) { + boolean hasPortrait = portraitManager.hasPortrait(identity.getName()); + + String portraitCssClass; + String gender = identity.getUser().getProperty(UserConstants.GENDER, Locale.ENGLISH); + if ("male".equalsIgnoreCase(gender)) { + portraitCssClass = DisplayPortraitManager.DUMMY_MALE_BIG_CSS_CLASS; + } else if ("female".equalsIgnoreCase(gender)) { + portraitCssClass = DisplayPortraitManager.DUMMY_FEMALE_BIG_CSS_CLASS; + } else { + portraitCssClass = DisplayPortraitManager.DUMMY_BIG_CSS_CLASS; + } + String fullname = userManager.getUserDisplayName(identity); + return new Member(identity, fullname, userPropertyHandlers, getLocale(), hasPortrait, portraitCssClass); + } + + @Override + protected void doDispose() { + // + } + + @Override + protected void formOK(UserRequest ureq) { + // + } + + + + @Override + public void event(UserRequest ureq, Component source, Event event) { + if(source == printLink) { + doPrint(ureq); + } + super.event(ureq, source, event); + } + + @Override + protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { + if(source == allEmailLink) { + doEmail(ureq); + } else if (source == downloadLink) { + doExport(ureq); + } else if (source instanceof FormLink) { + FormLink link = (FormLink)source; + Object uobject = link.getUserObject(); + if(uobject instanceof Member) { + Member member = (Member)uobject; + String cmd = link.getCmd(); + if("id".equals(cmd)) { + doOpenHomePage(member, ureq); + } else if("mail".equals(cmd)) { + doSendEmailToMember(member, ureq); + } else if("chat".equals(cmd)) { + doOpenChat(member, ureq); + } + } + } + super.formInnerEvent(ureq, source, event); + } + + @Override + protected void event(UserRequest ureq, Controller source, Event event) { + if(source == cmc) { + cleanUp(); + } else if (source == emailController) { + cmc.deactivate(); + cleanUp(); + } else if(source == mailCtrl) { + cmc.deactivate(); + cleanUp(); + } + super.event(ureq, source, event); + } + + private void cleanUp() { + removeAsListenerAndDispose(emailController); + removeAsListenerAndDispose(mailCtrl); + removeAsListenerAndDispose(cmc); + emailController = null; + mailCtrl = null; + cmc = null; + } + + private void doEmail(UserRequest ureq) { + if(mailCtrl != null || cmc != null) return; + removeAsListenerAndDispose(cmc); + removeAsListenerAndDispose(mailCtrl); + mailCtrl = new MembersMailController(ureq, getWindowControl(), getTranslator(), courseEnv, + ownerList, coachList, participantList, waitingtList, createBodyTemplate()); + listenTo(mailCtrl); + + String title = translate("members.email.title"); + cmc = new CloseableModalController(getWindowControl(), translate("close"), mailCtrl.getInitialComponent(), true, title); + listenTo(cmc); + + cmc.activate(); + } + + private void doExport(UserRequest ureq) { + MediaResource resource = exportManager.getXlsMediaResource(showOwners, showCoaches, showParticipants, showWaiting, + owners, coaches, participants, waiting, getTranslator(), userPropertyHandlers, repoEntry, businessGroup); + + ureq.getDispatchResult().setResultingMediaResource(resource); + } + + private void doOpenChat(Member member, UserRequest ureq) { + Buddy buddy = imService.getBuddyById(member.getKey()); + OpenInstantMessageEvent e = new OpenInstantMessageEvent(ureq, buddy); + ureq.getUserSession().getSingleUserEventCenter().fireEventToListenersOf(e, InstantMessagingService.TOWER_EVENT_ORES); + } + + private void doSendEmailToMember(Member member, UserRequest ureq) { + if (!editable) return; + ContactList memberList; + if (courseEnv == null) { + memberList = new ContactList(translate("members.to", new String[]{ member.getFullName(), businessGroup.getName() })); + } else { + memberList = new ContactList(translate("members.to", new String[]{ member.getFullName(), courseEnv.getCourseTitle() })); + } + Identity identity = securityManager.loadIdentityByKey(member.getKey()); + memberList.add(identity); + doSendEmailToMember(memberList, ureq); + } + + private void doSendEmailToMember(ContactList contactList, UserRequest ureq) { + if (contactList.getEmailsAsStrings().size() > 0) { + removeAsListenerAndDispose(cmc); + removeAsListenerAndDispose(emailController); + + ContactMessage cmsg = new ContactMessage(ureq.getIdentity()); + cmsg.addEmailTo(contactList); + // preset body template from i18n + cmsg.setBodyText(createBodyTemplate()); + emailController = new ContactFormController(ureq, getWindowControl(), true, false, false, cmsg); + listenTo(emailController); + + String title = translate("members.email.title"); + cmc = new CloseableModalController(getWindowControl(), translate("close"), emailController.getInitialComponent(), true, title); + listenTo(cmc); + cmc.activate(); + } + } + + private String createBodyTemplate() { + if (courseEnv == null) { + String courseName = businessGroup.getName(); + // Build REST URL to business group, use hack via group manager to access + StringBuilder courseLink = new StringBuilder(); + courseLink.append(Settings.getServerContextPathURI()) + .append("/auth/BusinessGroup/").append(businessGroup.getKey()); + return translate("email.body.template", new String[]{courseName, courseLink.toString()}); + } else { + String courseName = courseEnv.getCourseTitle(); + // Build REST URL to course element, use hack via group manager to access repo entry + StringBuilder courseLink = new StringBuilder(); + RepositoryEntry entry = courseEnv.getCourseGroupManager().getCourseEntry(); + courseLink.append(Settings.getServerContextPathURI()) + .append("/url/RepositoryEntry/").append(entry.getKey()); + return translate("email.body.template", new String[]{courseName, courseLink.toString()}); + } + } + + private void doOpenHomePage(Member member, UserRequest ureq) { + String url = "[HomePage:" + member.getKey() + "]"; + BusinessControl bc = BusinessControlFactory.getInstance().createFromString(url); + WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(bc, getWindowControl()); + NewControllerFactory.getInstance().launch(ureq, bwControl); + } + + private void doPrint(UserRequest ureq) { + ControllerCreator printControllerCreator = new ControllerCreator() { + @Override + public Controller createController(UserRequest lureq, WindowControl lwControl) { + lwControl.getWindowBackOffice().getChiefController().addBodyCssClass("o_cmembers_print"); + return new MembersPrintController(lureq, lwControl, userPropertyHandlers, getTranslator(), ownerList, coachList, + participantList, waitingtList, showOwners, showCoaches, showParticipants, showWaiting, + courseEnv != null ? courseEnv.getCourseTitle() : businessGroup.getName()); + } + }; + ControllerCreator layoutCtrlr = BaseFullWebappPopupLayoutFactory.createPrintPopupLayout(printControllerCreator); + openInNewBrowserWindow(ureq, layoutCtrlr); + } + + public static class IdentityComparator implements Comparator<Identity> { + + @Override + public int compare(Identity id1, Identity id2) { + if(id1 == null) return -1; + if(id2 == null) return 1; + + String l1 = id1.getUser().getProperty(UserConstants.LASTNAME, null); + String l2 = id2.getUser().getProperty(UserConstants.LASTNAME, null); + if(l1 == null) return -1; + if(l2 == null) return 1; + + int result = l1.compareToIgnoreCase(l2); + if(result == 0) { + String f1 = id1.getUser().getProperty(UserConstants.FIRSTNAME, null); + String f2 = id2.getUser().getProperty(UserConstants.FIRSTNAME, null); + if(f1 == null) return -1; + if(f2 == null) return 1; + result = f1.compareToIgnoreCase(f2); + } + return result; + } + } +} diff --git a/src/main/java/org/olat/commons/memberlist/ui/MembersDisplayRunController.java b/src/main/java/org/olat/commons/memberlist/ui/MembersDisplayRunController.java new file mode 100644 index 00000000000..81847dd9960 --- /dev/null +++ b/src/main/java/org/olat/commons/memberlist/ui/MembersDisplayRunController.java @@ -0,0 +1,184 @@ +/** + * <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.commons.memberlist.ui; + +import java.util.List; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.Component; +import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +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.translator.Translator; +import org.olat.core.id.Identity; +import org.olat.core.util.prefs.Preferences; +import org.olat.course.run.environment.CourseEnvironment; +import org.olat.group.BusinessGroup; + +/** + * Initial Date: 28.03.2017 + * @author fkiefer, fabian.kiefer@frentix.com, www.frentix.com + */ +public class MembersDisplayRunController extends BasicController { + + private static final String GUIPREF_KEY_GROUPMEMBER = "groupmemberdisplay"; + private static final String GUIPREF_KEY_COURSEMEMBER = "coursememberdisplay"; + + private VelocityContainer mainVC; + private FormBasicController membersAvatarController; + + private MembersListDisplayRunController membersListController; + + private Link tableCustomLink, tableLink; + + private BusinessGroup businessGroup; + private CourseEnvironment courseEnv; + private String courseOrGroupIdentifier; + + private List<Identity> owners; + private List<Identity> coaches; + private List<Identity> participants; + private List<Identity> waiting; + + private final boolean canEmail; + private final boolean showOwners; + private final boolean showCoaches; + private final boolean showParticipants; + private final boolean showWaiting; + private final boolean editable; + private final boolean deduplicateList; + + + public MembersDisplayRunController(UserRequest ureq, WindowControl wControl, Translator translator, CourseEnvironment courseEnv, BusinessGroup businessGroup, + List<Identity> owners, List<Identity> coaches, List<Identity> participants, List<Identity> waiting, boolean canEmail, boolean deduplicateList, + boolean showOwners, boolean showCoaches, boolean showParticipants, boolean showWaiting, boolean editable) { + super(ureq, wControl); + setTranslator(translator); + this.courseOrGroupIdentifier = courseEnv == null ? GUIPREF_KEY_GROUPMEMBER + businessGroup.getKey() + : GUIPREF_KEY_COURSEMEMBER + courseEnv.getCourseGroupManager().getCourseEntry().getKey(); + this.businessGroup = businessGroup; + this.courseEnv = courseEnv; + // lists + this.owners = owners; + this.coaches = coaches; + this.participants = participants; + this.waiting = waiting; + // flags + this.canEmail = canEmail; + this.showOwners = showOwners; + this.showCoaches = showCoaches; + this.showParticipants = showParticipants; + this.showWaiting = showWaiting; + this.deduplicateList = deduplicateList; + this.editable = editable; + + mainVC = createVelocityContainer("membersToggle"); + + mainVC.contextPut("headline", businessGroup != null); + + tableCustomLink = LinkFactory.createLink(null, "tableCustomLink", "select.custom", "blank", getTranslator(), mainVC, this, Link.BUTTON); + tableCustomLink.setIconLeftCSS( "o_icon o_icon_table_custom o_icon-lg"); + + tableLink = LinkFactory.createLink(null, "tableLink", "select.table", "blank", getTranslator(), mainVC, this, Link.BUTTON); + tableLink.setIconLeftCSS( "o_icon o_icon_table o_icon-lg"); + + if (doLoadMemberListConfig(ureq)) { + doOpenPortraitView(ureq); + } else { + doOpenListView(ureq, true); + } + putInitialPanel(mainVC); + } + + + @Override + protected void event(UserRequest ureq, Controller source, Event event) { + super.event(ureq, source, event); + } + + @Override + protected void event(UserRequest ureq, Component source, Event event) { + if (source == tableCustomLink) { + doOpenPortraitView(ureq); + } else if (source == tableLink) { + doOpenListView(ureq, true); + } + } + + @Override + protected void doDispose() { + // + } + + private void doOpenPortraitView(UserRequest ureq) { + if (membersAvatarController == null) { + membersAvatarController = new MembersAvatarDisplayRunController(ureq, getWindowControl(), getTranslator(), + courseEnv, businessGroup, owners, coaches, participants, waiting, canEmail, deduplicateList, + showOwners, showCoaches, showParticipants, showWaiting, editable); + listenTo(membersAvatarController); + } + mainVC.contextPut("portrait", Boolean.TRUE); + mainVC.put("portraitView", membersAvatarController.getInitialComponent()); + tableCustomLink.setActive(true); + tableLink.setActive(false); + doUpdateMemberListConfig(ureq, true); + } + + private void doOpenListView(UserRequest ureq, boolean onClick) { + if (membersListController == null) { + membersListController = new MembersListDisplayRunController(ureq, getWindowControl(), getTranslator(), + courseEnv, businessGroup, owners, coaches, participants, waiting, canEmail, deduplicateList, + showOwners, showCoaches, showParticipants, showWaiting, editable); + listenTo(membersListController); + } + mainVC.put("listView", membersListController.getInitialComponent()); + mainVC.contextPut("portrait", Boolean.FALSE); + if (onClick) { + tableCustomLink.setActive(false); + tableLink.setActive(true); + } + doUpdateMemberListConfig(ureq, false); + } + + private boolean doLoadMemberListConfig(UserRequest ureq) { + Boolean showPortraitConfig = Boolean.TRUE; + if (ureq != null) { + Preferences guiPrefs = ureq.getUserSession().getGuiPreferences(); + showPortraitConfig = (Boolean) guiPrefs.get(MembersDisplayRunController.class, courseOrGroupIdentifier); + if (showPortraitConfig == null) { + showPortraitConfig = Boolean.TRUE; + } + } + return showPortraitConfig; + } + + private void doUpdateMemberListConfig(UserRequest ureq, boolean newValue) { + // save new config in GUI prefs + Preferences guiPrefs = ureq.getUserSession().getGuiPreferences(); + if (guiPrefs != null) { + guiPrefs.putAndSave(MembersDisplayRunController.class, courseOrGroupIdentifier, Boolean.valueOf(newValue)); + } + } +} diff --git a/src/main/java/org/olat/commons/memberlist/ui/MembersListDisplayRunController.java b/src/main/java/org/olat/commons/memberlist/ui/MembersListDisplayRunController.java new file mode 100644 index 00000000000..78c9887e565 --- /dev/null +++ b/src/main/java/org/olat/commons/memberlist/ui/MembersListDisplayRunController.java @@ -0,0 +1,363 @@ +/** + * <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.commons.memberlist.ui; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import org.olat.basesecurity.BaseSecurityModule; +import org.olat.commons.memberlist.manager.MembersExportManager; +import org.olat.commons.memberlist.ui.MembersAvatarDisplayRunController.IdentityComparator; +import org.olat.core.commons.fullWebApp.popup.BaseFullWebappPopupLayoutFactory; +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.Component; +import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement; +import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +import org.olat.core.gui.components.link.Link; +import org.olat.core.gui.components.link.LinkFactory; +import org.olat.core.gui.components.link.LinkPopupSettings; +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.control.creator.ControllerCreator; +import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController; +import org.olat.core.gui.media.MediaResource; +import org.olat.core.gui.translator.Translator; +import org.olat.core.helpers.Settings; +import org.olat.core.id.Identity; +import org.olat.core.id.IdentityEnvironment; +import org.olat.core.id.Roles; +import org.olat.core.id.UserConstants; +import org.olat.core.util.Util; +import org.olat.course.assessment.manager.UserCourseInformationsManager; +import org.olat.course.nodes.members.Member; +import org.olat.course.nodes.members.MembersCourseNodeRunController; +import org.olat.course.run.environment.CourseEnvironment; +import org.olat.group.BusinessGroup; +import org.olat.group.BusinessGroupMembership; +import org.olat.group.ui.main.MemberListTableModel; +import org.olat.group.ui.main.MemberView; +import org.olat.modules.co.ContactFormController; +import org.olat.repository.RepositoryEntry; +import org.olat.user.DisplayPortraitManager; +import org.olat.user.UserManager; +import org.olat.user.propertyhandlers.UserPropertyHandler; +import org.springframework.beans.factory.annotation.Autowired; + + + +/** + * Initial Date: 29.03.2017 + * @author fkiefer, fabian.kiefer@frentix.com, www.frentix.com + */ +public class MembersListDisplayRunController extends BasicController { + + public static final String USER_PROPS_ID = MembersCourseNodeRunController.class.getName(); + public static final int USER_PROPS_OFFSET = 500; + + private Link printLink; + private Link allEmailLink; + private Link downloadLink; + + private FormBasicController mailCtrl; + private ContactFormController emailController; + private CloseableModalController cmc; + + private List<Member> ownerList; + private List<Member> coachList; + private List<Member> participantList; + private List<Member> waitingtList; + + private List<Identity> owners; + private List<Identity> coaches; + private List<Identity> participants; + private List<Identity> waiting; + + private final boolean showOwners; + private final boolean showCoaches; + private final boolean showParticipants; + private final boolean showWaiting; + + private final List<UserPropertyHandler> userPropertyHandlers; + private final CourseEnvironment courseEnv; + private final BusinessGroup businessGroup; + private final RepositoryEntry repoEntry; + + protected FlexiTableElement ownersTable, coachesTable, participantsTable, waitingTable; + protected MemberListTableModel ownersModel, coachesModel, participantsModel, waitingModel; + + private MembersTableController ownersTableCtrl, coachesTableCtrl, participantsTableCtrl, waitingTableCtrl; + + private Map<Long,BusinessGroupMembership> groupmemberships; + private Map<Long,Date> recentLaunches, initialLaunches; + + + private VelocityContainer mainVC; + + @Autowired + private UserManager userManager; + @Autowired + private MembersExportManager exportManager; + @Autowired + private BaseSecurityModule securityModule; + @Autowired + private UserCourseInformationsManager userInfosMgr; + + + public MembersListDisplayRunController(UserRequest ureq, WindowControl wControl, Translator translator, CourseEnvironment courseEnv, BusinessGroup businessGroup, + List<Identity> owners, List<Identity> coaches, List<Identity> participants, List<Identity> waiting, boolean canEmail, boolean deduplicateList, + boolean showOwners, boolean showCoaches, boolean showParticipants, boolean showWaiting, boolean editable) { + super(ureq, wControl); + Translator fallback = userManager.getPropertyHandlerTranslator(getTranslator()); + setTranslator(Util.createPackageTranslator(translator, fallback, getLocale())); + + mainVC = createVelocityContainer("membersTable"); + + this.courseEnv = courseEnv; + this.businessGroup = businessGroup; + this.repoEntry = courseEnv != null ? courseEnv.getCourseGroupManager().getCourseEntry() : null; + + userPropertyHandlers = userManager.getUserPropertyHandlersFor(USER_PROPS_ID, false); + // lists + this.owners = owners; + this.coaches = coaches; + this.participants = participants; + this.waiting = waiting; + // flags + this.showOwners = showOwners; + this.showCoaches = showCoaches; + this.showParticipants = showParticipants; + this.showWaiting = showWaiting; + + if(canEmail) { + allEmailLink = LinkFactory.createLink(null, "email", "email.all", "members.email.title", getTranslator(), mainVC, this, Link.BUTTON); + allEmailLink.setIconLeftCSS("o_icon o_icon_mail"); + } + + IdentityEnvironment idEnv = ureq.getUserSession().getIdentityEnvironment(); + Identity ownId = idEnv.getIdentity(); + Roles roles = idEnv.getRoles(); + boolean memberXlsEnabled = businessGroup != null ? businessGroup.isDownloadMembersLists() && !waiting.contains(ownId) : false; + if (editable && (roles.isOLATAdmin() || roles.isGroupManager() || owners.contains(ownId) || coaches.contains(ownId) || memberXlsEnabled)) { + downloadLink = LinkFactory.createLink(null, "download", "download", "members.download", getTranslator(), mainVC, this, Link.BUTTON); + downloadLink.setIconLeftCSS("o_icon o_icon_download"); + printLink = LinkFactory.createButton("print", mainVC, this); + printLink.setIconLeftCSS("o_icon o_icon_print o_icon-lg"); + printLink.setPopup(new LinkPopupSettings(700, 500, "print-members")); + } + + Comparator<Identity> idComparator = new IdentityComparator(); + Collections.sort(owners, idComparator); + ownerList = convertIdentitiesToMembers(owners); + Collections.sort(coaches, idComparator); + coachList = convertIdentitiesToMembers(coaches); + Collections.sort(participants, idComparator); + participantList = convertIdentitiesToMembers(participants); + Collections.sort(waiting, idComparator); + waitingtList = convertIdentitiesToMembers(waiting); + + Set<MemberView> duplicateCatcher = new HashSet<>(); + boolean userLastTimeVisible = cacheGroupMemberships(ureq); + + if (showOwners && !owners.isEmpty()) { + ownersTableCtrl = new MembersTableController(ureq, wControl, owners, duplicateCatcher, recentLaunches, initialLaunches, + userPropertyHandlers, groupmemberships, repoEntry, businessGroup, courseEnv, deduplicateList, getTranslator(), + editable, canEmail, userLastTimeVisible); + listenTo(ownersTableCtrl); + mainVC.put("ownerList", ownersTableCtrl.getInitialComponent()); + } + if (showCoaches && !coaches.isEmpty()) { + coachesTableCtrl = new MembersTableController(ureq, wControl, coaches, duplicateCatcher, recentLaunches, initialLaunches, + userPropertyHandlers, groupmemberships, repoEntry, businessGroup, courseEnv, deduplicateList, getTranslator(), editable, + canEmail, userLastTimeVisible); + listenTo(coachesTableCtrl); + mainVC.put("coachList", coachesTableCtrl.getInitialComponent()); + } + if (showParticipants && !participants.isEmpty()) { + participantsTableCtrl = new MembersTableController(ureq, wControl, participants, duplicateCatcher, recentLaunches, initialLaunches, + userPropertyHandlers, groupmemberships, repoEntry, businessGroup, courseEnv, deduplicateList, getTranslator(), editable, + canEmail, userLastTimeVisible); + listenTo(participantsTableCtrl); + mainVC.put("participantList", participantsTableCtrl.getInitialComponent()); + } + if (showWaiting && !waiting.isEmpty()) { + waitingTableCtrl = new MembersTableController(ureq, wControl, waiting, duplicateCatcher, recentLaunches, initialLaunches, + userPropertyHandlers, groupmemberships, repoEntry, businessGroup, courseEnv, deduplicateList, getTranslator(), editable, + canEmail, userLastTimeVisible); + listenTo(waitingTableCtrl); + mainVC.put("waitingList", waitingTableCtrl.getInitialComponent()); + } + + putInitialPanel(mainVC); + } + + + + @Override + public void event(UserRequest ureq, Component source, Event event) { + if (source == printLink) { + doPrint(ureq); + } else if(source == allEmailLink) { + doEmail(ureq); + } else if (source == downloadLink) { + doExport(ureq); + } + } + + @Override + protected void event(UserRequest ureq, Controller source, Event event) { + if(source == cmc) { + cleanUp(); + } else if (source == emailController) { + cmc.deactivate(); + cleanUp(); + } else if(source == mailCtrl) { + cmc.deactivate(); + cleanUp(); + } + super.event(ureq, source, event); + } + + private void cleanUp() { + removeAsListenerAndDispose(emailController); + removeAsListenerAndDispose(mailCtrl); + removeAsListenerAndDispose(cmc); + emailController = null; + mailCtrl = null; + cmc = null; + } + + private boolean cacheGroupMemberships (UserRequest ureq) { + boolean isUserLastVisitVisible = securityModule.isUserLastVisitVisible(ureq.getUserSession().getRoles()); + if (isUserLastVisitVisible) { + if (businessGroup != null) { + List<BusinessGroup> groups = new ArrayList<>(); + groups.add(businessGroup); + groupmemberships = exportManager.getGroupMembershipMap(groups); + } + if (repoEntry != null) { + initialLaunches = userInfosMgr.getInitialLaunchDates(repoEntry.getOlatResource().getResourceableId()); + recentLaunches = userInfosMgr.getRecentLaunchDates(repoEntry.getOlatResource()); + } + } + return isUserLastVisitVisible; + } + + + + + @Override + protected void doDispose() { + // + } + + + + private void doEmail(UserRequest ureq) { + if(mailCtrl != null || cmc != null) return; + removeAsListenerAndDispose(cmc); + removeAsListenerAndDispose(mailCtrl); + mailCtrl = new MembersMailController(ureq, getWindowControl(), getTranslator(), courseEnv, + ownerList, coachList, participantList, waitingtList, createBodyTemplate()); + listenTo(mailCtrl); + + String title = translate("members.email.title"); + cmc = new CloseableModalController(getWindowControl(), translate("close"), mailCtrl.getInitialComponent(), true, title); + listenTo(cmc); + + cmc.activate(); + } + + private String createBodyTemplate() { + if (courseEnv == null) { + String courseName = businessGroup.getName(); + // Build REST URL to business group, use hack via group manager to access + StringBuilder courseLink = new StringBuilder(); + courseLink.append(Settings.getServerContextPathURI()) + .append("/auth/BusinessGroup/").append(businessGroup.getKey()); + return translate("email.body.template", new String[]{courseName, courseLink.toString()}); + } else { + String courseName = courseEnv.getCourseTitle(); + // Build REST URL to course element, use hack via group manager to access repo entry + StringBuilder courseLink = new StringBuilder(); + RepositoryEntry entry = courseEnv.getCourseGroupManager().getCourseEntry(); + courseLink.append(Settings.getServerContextPathURI()) + .append("/url/RepositoryEntry/").append(entry.getKey()); + return translate("email.body.template", new String[]{courseName, courseLink.toString()}); + } + } + + + private void doExport(UserRequest ureq) { + MediaResource resource = exportManager.getXlsMediaResource(showOwners, showCoaches, showParticipants, showWaiting, + owners, coaches, participants, waiting, getTranslator(), userPropertyHandlers, repoEntry, businessGroup); + + ureq.getDispatchResult().setResultingMediaResource(resource); + } + + private void doPrint(UserRequest ureq) { + ControllerCreator printControllerCreator = new ControllerCreator() { + @Override + public Controller createController(UserRequest lureq, WindowControl lwControl) { + lwControl.getWindowBackOffice().getChiefController().addBodyCssClass("o_cmembers_print"); + return new MembersPrintController(lureq, lwControl, userPropertyHandlers, getTranslator(), ownerList, coachList, + participantList, waitingtList, showOwners, showCoaches, showParticipants, showWaiting, + courseEnv != null ? courseEnv.getCourseTitle() : businessGroup.getName()); + } + }; + ControllerCreator layoutCtrlr = BaseFullWebappPopupLayoutFactory.createPrintPopupLayout(printControllerCreator); + openInNewBrowserWindow(ureq, layoutCtrlr); + } + + + private List<Member> convertIdentitiesToMembers(List<Identity> identities) { + List<Member> memberList = new ArrayList<>(); + for (Identity identity : identities) { + Member member = createMember(identity); + memberList.add(member); + } + return memberList; + } + + private Member createMember(Identity identity) { + boolean hasPortrait = DisplayPortraitManager.getInstance().hasPortrait(identity.getName()); + + String portraitCssClass; + String gender = identity.getUser().getProperty(UserConstants.GENDER, Locale.ENGLISH); + if ("male".equalsIgnoreCase(gender)) { + portraitCssClass = DisplayPortraitManager.DUMMY_MALE_BIG_CSS_CLASS; + } else if ("female".equalsIgnoreCase(gender)) { + portraitCssClass = DisplayPortraitManager.DUMMY_FEMALE_BIG_CSS_CLASS; + } else { + portraitCssClass = DisplayPortraitManager.DUMMY_BIG_CSS_CLASS; + } + String fullname = userManager.getUserDisplayName(identity); + return new Member(identity, fullname, userPropertyHandlers, getLocale(), hasPortrait, portraitCssClass); + } +} diff --git a/src/main/java/org/olat/course/nodes/members/MembersMailController.java b/src/main/java/org/olat/commons/memberlist/ui/MembersMailController.java similarity index 85% rename from src/main/java/org/olat/course/nodes/members/MembersMailController.java rename to src/main/java/org/olat/commons/memberlist/ui/MembersMailController.java index 11a810071e7..0a5b6146794 100644 --- a/src/main/java/org/olat/course/nodes/members/MembersMailController.java +++ b/src/main/java/org/olat/commons/memberlist/ui/MembersMailController.java @@ -17,7 +17,7 @@ * frentix GmbH, http://www.frentix.com * <p> */ -package org.olat.course.nodes.members; +package org.olat.commons.memberlist.ui; import java.io.File; import java.util.ArrayList; @@ -46,6 +46,7 @@ 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.generic.closablewrapper.CloseableModalController; +import org.olat.core.gui.translator.Translator; import org.olat.core.gui.util.CSSHelper; import org.olat.core.id.Identity; import org.olat.core.logging.activity.ThreadLocalUserActivityLogger; @@ -63,6 +64,7 @@ import org.olat.core.util.mail.MailModule; import org.olat.core.util.mail.MailerResult; import org.olat.core.util.mail.ui.EMailIdentity; import org.olat.course.groupsandrights.CourseGroupManager; +import org.olat.course.nodes.members.Member; import org.olat.course.run.environment.CourseEnvironment; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryService; @@ -83,7 +85,7 @@ public class MembersMailController extends FormBasicController { private FileElement attachmentEl; private FormLink addMemberButton; private TextElement subjectEl, externalAddressesEl; - private MultipleSelectionElement ownerEl, coachEl, participantEl, individualEl, externalEl, copyFromEl; + private MultipleSelectionElement ownerEl, coachEl, participantEl, waitingEl, individualEl, externalEl, copyFromEl; private FormLayoutContainer uploadCont, individualMemberCont; private CloseableModalController cmc; @@ -96,7 +98,7 @@ public class MembersMailController extends FormBasicController { private final int contactAttachmentMaxSizeInMb; private final List<Member> selectedMembers = new ArrayList<>(); private final List<Attachment> attachments = new ArrayList<>(); - private final List<Member> ownerList, coachList, participantList; + private final List<Member> ownerList, coachList, participantList, waitingList; @Autowired private UserManager userManager; @@ -109,14 +111,17 @@ public class MembersMailController extends FormBasicController { @Autowired private RepositoryService repositoryService; - public MembersMailController(UserRequest ureq, WindowControl wControl, CourseEnvironment courseEnv, - List<Member> ownerList, List<Member> coachList, List<Member> participantList, String bodyTemplate) { + public MembersMailController(UserRequest ureq, WindowControl wControl, Translator translator, CourseEnvironment courseEnv, + List<Member> ownerList, List<Member> coachList, List<Member> participantList, List<Member> waitingList, String bodyTemplate) { super(ureq, wControl, Util.createPackageTranslator(MailHelper.class, ureq.getLocale())); + setTranslator(Util.createPackageTranslator(translator, MailHelper.class, ureq.getLocale())); +// setTranslator(translator); this.courseEnv = courseEnv; this.ownerList = ownerList; this.coachList = coachList; this.participantList = participantList; + this.waitingList = waitingList; this.contactAttachmentMaxSizeInMb = mailModule.getMaxSizeForAttachement(); initForm(ureq); @@ -154,10 +159,16 @@ public class MembersMailController extends FormBasicController { participantEl = uifactory.addCheckboxesHorizontal("contact.all.participants", to, formLayout, keys, values); to = null; } + if(waitingList != null && waitingList.size() > 0) { + String[] values = new String[] { translate("contact.all.waiting") }; + waitingEl = uifactory.addCheckboxesHorizontal("contact.all.waiting", to, formLayout, keys, values); + to = null; + } if((ownerList != null && ownerList.size() > 0) || (coachList != null && coachList.size() > 0) - || (participantList != null && participantList.size() > 0)) { + || (participantList != null && participantList.size() > 0) + || (waitingList != null && waitingList.size() > 0)) { String[] values = new String[] { translate("contact.individual") }; individualEl = uifactory.addCheckboxesHorizontal("contact.individual", to, formLayout, keys, values); individualEl.addActionListener(FormEvent.ONCHANGE); @@ -350,6 +361,7 @@ public class MembersMailController extends FormBasicController { List<Member> owners = ownerList; List<Member> coaches = coachList; List<Member> participants = participantList; + List<Member> waiting = waitingList; if(ownerEl != null && ownerEl.isAtLeastSelected(1)) { owners = null; } @@ -359,11 +371,15 @@ public class MembersMailController extends FormBasicController { if(participantEl != null && participantEl.isAtLeastSelected(1)) { participants = null; } + if(waitingEl != null && waitingEl.isAtLeastSelected(1)) { + waiting = null; + } if(owners == null && coaches == null && participants == null) { showWarning("already.all.selected"); } else { - selectMemberCtrl = new SelectMembersController(ureq, getWindowControl(), selectedMembers, owners, coaches, participants); + selectMemberCtrl = new SelectMembersController(ureq, getWindowControl(), getTranslator(), + selectedMembers, owners, coaches, participants, waiting); listenTo(selectMemberCtrl); String title = translate("select.members"); @@ -414,30 +430,59 @@ public class MembersMailController extends FormBasicController { private void doSend(UserRequest ureq) { ContactList contactList = new ContactList(""); - if(ownerEl != null && ownerEl.isAtLeastSelected(1)) { - RepositoryEntry courseRepositoryEntry = courseEnv.getCourseGroupManager().getCourseEntry(); - List<Identity> owners = repositoryService.getMembers(courseRepositoryEntry, GroupRoles.owner.name()); - contactList.addAllIdentites(owners); - } - - if(coachEl != null && coachEl.isAtLeastSelected(1)) { - Set<Long> sendToWhatYouSee = new HashSet<>(); - for(Member coach:coachList) { - sendToWhatYouSee.add(coach.getKey()); + if (courseEnv == null) { + if(coachEl != null && coachEl.isAtLeastSelected(1)) { + List<Long> identityKeys = new ArrayList<>(coachList.size()); + for(Member coach:coachList) { + identityKeys.add(coach.getKey()); + } + List<Identity> coaches = securityManager.loadIdentityByKeys(identityKeys); + contactList.addAllIdentites(coaches); } - CourseGroupManager cgm = courseEnv.getCourseGroupManager(); - avoidInvisibleMember(cgm.getCoachesFromBusinessGroups(), contactList, sendToWhatYouSee); - avoidInvisibleMember(cgm.getCoaches(), contactList, sendToWhatYouSee); - } - - if(participantEl != null && participantEl.isAtLeastSelected(1)) { - Set<Long> sendToWhatYouSee = new HashSet<>(); - for(Member participant:participantList) { - sendToWhatYouSee.add(participant.getKey()); + + if(participantEl != null && participantEl.isAtLeastSelected(1)) { + List<Long> identityKeys = new ArrayList<>(participantList.size()); + for(Member participant:participantList) { + identityKeys.add(participant.getKey()); + } + List<Identity> participants = securityManager.loadIdentityByKeys(identityKeys); + contactList.addAllIdentites(participants); + } + + if(waitingEl != null && waitingEl.isAtLeastSelected(1)) { + List<Long> identityKeys = new ArrayList<>(waitingList.size()); + for(Member waiter:waitingList) { + identityKeys.add(waiter.getKey()); + } + List<Identity> waiters = securityManager.loadIdentityByKeys(identityKeys); + contactList.addAllIdentites(waiters); + } + } else { + if(ownerEl != null && ownerEl.isAtLeastSelected(1)) { + RepositoryEntry courseRepositoryEntry = courseEnv.getCourseGroupManager().getCourseEntry(); + List<Identity> owners = repositoryService.getMembers(courseRepositoryEntry, GroupRoles.owner.name()); + contactList.addAllIdentites(owners); + } + + if(coachEl != null && coachEl.isAtLeastSelected(1)) { + Set<Long> sendToWhatYouSee = new HashSet<>(); + for(Member coach:coachList) { + sendToWhatYouSee.add(coach.getKey()); + } + CourseGroupManager cgm = courseEnv.getCourseGroupManager(); + avoidInvisibleMember(cgm.getCoachesFromBusinessGroups(), contactList, sendToWhatYouSee); + avoidInvisibleMember(cgm.getCoaches(), contactList, sendToWhatYouSee); + } + + if(participantEl != null && participantEl.isAtLeastSelected(1)) { + Set<Long> sendToWhatYouSee = new HashSet<>(); + for(Member participant:participantList) { + sendToWhatYouSee.add(participant.getKey()); + } + CourseGroupManager cgm = courseEnv.getCourseGroupManager(); + avoidInvisibleMember(cgm.getParticipantsFromBusinessGroups(), contactList, sendToWhatYouSee); + avoidInvisibleMember(cgm.getParticipants(), contactList, sendToWhatYouSee); } - CourseGroupManager cgm = courseEnv.getCourseGroupManager(); - avoidInvisibleMember(cgm.getParticipantsFromBusinessGroups(), contactList, sendToWhatYouSee); - avoidInvisibleMember(cgm.getParticipants(), contactList, sendToWhatYouSee); } if(individualEl != null && individualEl.isAtLeastSelected(1) diff --git a/src/main/java/org/olat/course/nodes/members/MembersPrintController.java b/src/main/java/org/olat/commons/memberlist/ui/MembersPrintController.java similarity index 83% rename from src/main/java/org/olat/course/nodes/members/MembersPrintController.java rename to src/main/java/org/olat/commons/memberlist/ui/MembersPrintController.java index 5379f30bfb8..6571af4de64 100644 --- a/src/main/java/org/olat/course/nodes/members/MembersPrintController.java +++ b/src/main/java/org/olat/commons/memberlist/ui/MembersPrintController.java @@ -17,7 +17,7 @@ * frentix GmbH, http://www.frentix.com * <p> */ -package org.olat.course.nodes.members; +package org.olat.commons.memberlist.ui; import java.util.List; @@ -35,9 +35,11 @@ import org.olat.core.gui.control.WindowControl; import org.olat.core.gui.control.controller.BasicController; import org.olat.core.gui.media.MediaResource; import org.olat.core.gui.media.NotFoundMediaResource; +import org.olat.core.gui.translator.Translator; +import org.olat.core.util.Util; import org.olat.core.util.vfs.VFSLeaf; import org.olat.core.util.vfs.VFSMediaResource; -import org.olat.course.run.environment.CourseEnvironment; +import org.olat.course.nodes.members.Member; import org.olat.user.DisplayPortraitManager; import org.olat.user.UserManager; import org.olat.user.propertyhandlers.UserPropertyHandler; @@ -61,28 +63,32 @@ public class MembersPrintController extends BasicController { @Autowired private DisplayPortraitManager portraitManager; - public MembersPrintController(UserRequest ureq, WindowControl wControl, - CourseEnvironment courseEnv, List<UserPropertyHandler> userPropertyHandlers, - List<Member> owners, List<Member> coaches, List<Member> participants) { + public MembersPrintController(UserRequest ureq, WindowControl wControl, List<UserPropertyHandler> userPropertyHandlers, + Translator translator, List<Member> owners, List<Member> coaches, List<Member> participants, List<Member> waiting, + boolean showOwners, boolean showCoaches, boolean showParticipants, boolean showWaiting, String title) { super(ureq, wControl); + setTranslator(Util.createPackageTranslator(translator, getTranslator(), getLocale())); avatarBaseURL = registerCacheableMapper(ureq, "avatars-members-high-quality", new UserAvatarHQMapper()); this.userPropertyHandlers = userPropertyHandlers; mainVC = createVelocityContainer("print"); - mainVC.contextPut("courseTitle", courseEnv.getCourseTitle()); + mainVC.contextPut("courseTitle", title); mainVC.contextPut("avatarBaseURL", avatarBaseURL); mainVC.contextPut("userPropertyHandlers", userPropertyHandlers); - if(owners != null && owners.size() > 0) { + if(showOwners && owners != null && owners.size() > 0) { initFormMemberList("owners", translate("members.owners"), owners); } - if(coaches != null && coaches.size() > 0) { + if(showCoaches && coaches != null && coaches.size() > 0) { initFormMemberList("coaches", translate("members.coaches"), coaches); } - if(participants != null && participants.size() > 0) { + if(showParticipants && participants != null && participants.size() > 0) { initFormMemberList("participants", translate("members.participants"), participants); } + if(showWaiting && waiting != null && waiting.size() > 0) { + initFormMemberList("waiting", translate("members.waiting"), waiting); + } MainPanel mainPanel = new MainPanel("membersPrintPanel"); mainPanel.setContent(mainVC); diff --git a/src/main/java/org/olat/commons/memberlist/ui/MembersTableController.java b/src/main/java/org/olat/commons/memberlist/ui/MembersTableController.java new file mode 100644 index 00000000000..69d1aa322c9 --- /dev/null +++ b/src/main/java/org/olat/commons/memberlist/ui/MembersTableController.java @@ -0,0 +1,360 @@ +/** + * <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.commons.memberlist.ui; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; + +import org.olat.NewControllerFactory; +import org.olat.basesecurity.BaseSecurity; +import org.olat.core.commons.persistence.SortKey; +import org.olat.core.gui.UserRequest; +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; +import org.olat.core.gui.components.form.flexible.elements.FlexiTableSortOptions; +import org.olat.core.gui.components.form.flexible.elements.FormLink; +import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +import org.olat.core.gui.components.form.flexible.impl.FormEvent; +import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel; +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiColumnModel; +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel; +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModelFactory; +import org.olat.core.gui.components.form.flexible.impl.elements.table.SelectionEvent; +import org.olat.core.gui.components.form.flexible.impl.elements.table.StaticFlexiCellRenderer; +import org.olat.core.gui.components.form.flexible.impl.elements.table.TextFlexiCellRenderer; +import org.olat.core.gui.components.link.Link; +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.generic.closablewrapper.CloseableModalController; +import org.olat.core.gui.translator.Translator; +import org.olat.core.helpers.Settings; +import org.olat.core.id.Identity; +import org.olat.core.id.UserConstants; +import org.olat.core.id.context.BusinessControl; +import org.olat.core.id.context.BusinessControlFactory; +import org.olat.core.util.mail.ContactList; +import org.olat.core.util.mail.ContactMessage; +import org.olat.course.nodes.members.MembersCourseNodeRunController; +import org.olat.course.run.environment.CourseEnvironment; +import org.olat.group.BusinessGroup; +import org.olat.group.BusinessGroupMembership; +import org.olat.group.ui.main.MemberListTableModel; +import org.olat.group.ui.main.MemberListTableModel.Cols; +import org.olat.group.ui.main.MemberView; +import org.olat.instantMessaging.InstantMessagingModule; +import org.olat.instantMessaging.InstantMessagingService; +import org.olat.instantMessaging.OpenInstantMessageEvent; +import org.olat.instantMessaging.model.Buddy; +import org.olat.modules.co.ContactFormController; +import org.olat.repository.RepositoryEntry; +import org.olat.user.UserManager; +import org.olat.user.propertyhandlers.UserPropertyHandler; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Initial Date: 30.03.2017 + * @author fkiefer, fabian.kiefer@frentix.com, www.frentix.com + */ +public class MembersTableController extends FormBasicController { + + public static final String USER_PROPS_ID = MembersCourseNodeRunController.class.getName(); + public static final int USER_PROPS_OFFSET = 500; + + protected FlexiTableElement membersTable; + protected MemberListTableModel membersModel; + + + private ContactFormController emailController; + private CloseableModalController cmc; + + private final AtomicInteger counter = new AtomicInteger(); + + private final boolean chatEnabled, canEmail, deduplicateList, editable, userLastTimeVisible; + + private final List<UserPropertyHandler> userPropertyHandlers; + private final List<MemberView> membersList; + private final RepositoryEntry repoEntry; + private Set<MemberView> duplicateCatcher; + + @Autowired + private UserManager userManager; + @Autowired + private InstantMessagingModule imModule; + @Autowired + private InstantMessagingService imService; + @Autowired + private BaseSecurity securityManager; + + private BusinessGroup businessGroup; + private CourseEnvironment courseEnv; + + private int pageSize = 20; + + public MembersTableController(UserRequest ureq, WindowControl wControl, List<Identity> members, Set<MemberView> duplicateCatcher, Map<Long,Date> recentLaunches, Map<Long,Date> initialLaunches, + List<UserPropertyHandler> userPropertyHandlers, Map<Long,BusinessGroupMembership> groupmemberships, RepositoryEntry repoEntry, BusinessGroup businessGroup, + CourseEnvironment courseEnv, boolean deduplicateList, Translator translator, boolean editable, boolean canEmail, boolean userLastTimeVisible) { + super(ureq, wControl, "table"); + setTranslator(translator); + + chatEnabled = imModule.isEnabled() && imModule.isPrivateEnabled(); + + this.userPropertyHandlers = userPropertyHandlers; + this.duplicateCatcher = duplicateCatcher; + this.repoEntry = repoEntry; + this.deduplicateList = deduplicateList; + this.editable = editable; + this.canEmail = canEmail; + this.userLastTimeVisible = userLastTimeVisible; + + this.businessGroup = businessGroup; + this.courseEnv = courseEnv; + + this.membersList = getMembersFromIdentity(ureq, members, groupmemberships, recentLaunches, initialLaunches); + + initForm(ureq); + } + + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + FlexiTableColumnModel columnsModel = FlexiTableDataModelFactory.createFlexiTableColumnModel(); + SortKey defaultSortKey = initColumns(columnsModel); + membersModel = new MemberListTableModel(columnsModel, imModule.isOnlineStatusEnabled()); + membersModel.setObjects(membersList); + membersTable = uifactory.addTableElement(getWindowControl(), "table", membersModel, pageSize, false, getTranslator(), formLayout); + membersTable.setEmtpyTableMessageKey("nomembers"); + membersTable.setAndLoadPersistedPreferences(ureq, this.getClass().getSimpleName()); + membersTable.setExportEnabled(false); + membersTable.setElementCssClass("o_sel_member_list"); + + if(defaultSortKey != null) { + FlexiTableSortOptions options = new FlexiTableSortOptions(); + options.setDefaultOrderBy(defaultSortKey); + membersTable.setSortSettings(options); + } + } + + @Override + protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { + if (source == membersTable) { + if(event instanceof SelectionEvent) { + SelectionEvent se = (SelectionEvent)event; + String cmd = se.getCommand(); + if ("vcard".equals(cmd)) { + MemberView row = membersModel.getObject(se.getIndex()); + doOpenHomePage(row, ureq); + } else if ("email".equals(cmd)) { + MemberView row = membersModel.getObject(se.getIndex()); + doSendEmailToMember(row, ureq); + } + } + } else if (source instanceof FormLink) { + FormLink link = (FormLink)source; + String cmd = link.getCmd(); + MemberView row = (MemberView)link.getUserObject(); + if ("im".equals(cmd)) { + doOpenChat(row, ureq); + } + } + super.formInnerEvent(ureq, source, event); + } + + @Override + protected void event(UserRequest ureq, Controller source, Event event) { + if(source == cmc) { + cleanUp(); + } else if (source == emailController) { + cmc.deactivate(); + cleanUp(); + } + super.event(ureq, source, event); + } + + private void cleanUp() { + removeAsListenerAndDispose(emailController); + removeAsListenerAndDispose(cmc); + emailController = null; + cmc = null; + } + + @Override + protected void formOK(UserRequest ureq) { + // + } + + @Override + protected void doDispose() { + // + } + + private List<MemberView> getMembersFromIdentity(UserRequest ureq, List<Identity> identities, + Map<Long,BusinessGroupMembership> groupmemberships, Map<Long,Date> recentLaunches, Map<Long,Date> initialLaunches) { + if (!deduplicateList) { + duplicateCatcher = new HashSet<>(); + } + List<MemberView> memberList = new ArrayList<>(); + for (Identity identity : identities) { + MemberView member = new MemberView(identity, userPropertyHandlers, getLocale()); + if (userLastTimeVisible) { + if (repoEntry == null) { + BusinessGroupMembership groupmembership = groupmemberships.get(identity.getKey()); + member.setFirstTime(groupmembership.getCreationDate()); + member.setLastTime(groupmembership.getLastModified()); + } else { + member.setFirstTime(initialLaunches.get(identity.getKey())); + member.setLastTime(recentLaunches.get(identity.getKey())); + } + } + if (!duplicateCatcher.contains(member)) { + memberList.add(member); + if (!identity.equals(ureq.getIdentity())){ + forgeChatLink(member); + } + } + duplicateCatcher.add(member); + } + return memberList; + } + + protected void forgeChatLink(MemberView row) { + FormLink chatLink = uifactory.addFormLink("tools_" + counter.incrementAndGet(), "im", "", null, null, Link.NONTRANSLATED); + chatLink.setIconLeftCSS("o_icon o_icon_status_unavailable"); + chatLink.setUserObject(row); + row.setChatLink(chatLink); + } + + private SortKey initColumns(FlexiTableColumnModel columnsModel) { + SortKey defaultSortKey = null; + String editAction = "vcard"; + + if (chatEnabled && editable) { + DefaultFlexiColumnModel chatCol = new DefaultFlexiColumnModel(Cols.online.i18n(), Cols.online.ordinal()); + chatCol.setExportable(false); + columnsModel.addFlexiColumnModel(chatCol); + } + + int colPos = USER_PROPS_OFFSET; + for (UserPropertyHandler userPropertyHandler : userPropertyHandlers) { + if (userPropertyHandler == null) continue; + + String propName = userPropertyHandler.getName(); + boolean visible = userManager.isMandatoryUserProperty(USER_PROPS_ID , userPropertyHandler); + String myEditAction = editAction; + FlexiColumnModel col; + if(UserConstants.FIRSTNAME.equals(propName) || UserConstants.LASTNAME.equals(propName) || UserConstants.EMAIL.equals(propName)) { + if (UserConstants.EMAIL.equals(propName)) { + myEditAction = "email"; + if (!canEmail) { + continue; + } + } + col = new DefaultFlexiColumnModel(userPropertyHandler.i18nColumnDescriptorLabelKey(), + colPos, myEditAction, true, propName, + new StaticFlexiCellRenderer(myEditAction, new TextFlexiCellRenderer())); + } else { + col = new DefaultFlexiColumnModel(visible, userPropertyHandler.i18nColumnDescriptorLabelKey(), colPos, true, propName); + } + columnsModel.addFlexiColumnModel(col); + colPos++; + + if(defaultSortKey == null) { + defaultSortKey = new SortKey(propName, true); + } + } + if (userLastTimeVisible) { + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.firstTime.i18n(), Cols.firstTime.ordinal(), true, Cols.firstTime.name())); + columnsModel.addFlexiColumnModel(new DefaultFlexiColumnModel(Cols.lastTime.i18n(), Cols.lastTime.ordinal(), true, Cols.lastTime.name())); + } + return defaultSortKey; + } + + private void doSendEmailToMember(MemberView member, UserRequest ureq) { + if (!editable) return; + ContactList memberList; + Identity identity = securityManager.loadIdentityByKey(member.getIdentityKey()); + String fullName = userManager.getUserDisplayName(identity); + if (courseEnv == null) { + memberList = new ContactList(translate("members.to", new String[]{ fullName, businessGroup.getName() })); + } else { + memberList = new ContactList(translate("members.to", new String[]{ fullName, courseEnv.getCourseTitle() })); + } + memberList.add(identity); + doSendEmailToMember(memberList, ureq); + } + + private void doSendEmailToMember(ContactList contactList, UserRequest ureq) { + if (contactList.getEmailsAsStrings().size() > 0) { + removeAsListenerAndDispose(cmc); + removeAsListenerAndDispose(emailController); + + ContactMessage cmsg = new ContactMessage(ureq.getIdentity()); + cmsg.addEmailTo(contactList); + // preset body template from i18n + cmsg.setBodyText(createBodyTemplate()); + emailController = new ContactFormController(ureq, getWindowControl(), true, false, false, cmsg); + listenTo(emailController); + + String title = translate("members.email.title"); + cmc = new CloseableModalController(getWindowControl(), translate("close"), emailController.getInitialComponent(), true, title); + listenTo(cmc); + cmc.activate(); + } + } + + private String createBodyTemplate() { + if (courseEnv == null) { + String courseName = businessGroup.getName(); + // Build REST URL to business group, use hack via group manager to access + StringBuilder courseLink = new StringBuilder(); + courseLink.append(Settings.getServerContextPathURI()) + .append("/auth/BusinessGroup/").append(businessGroup.getKey()); + return translate("email.body.template", new String[]{courseName, courseLink.toString()}); + } else { + String courseName = courseEnv.getCourseTitle(); + // Build REST URL to course element, use hack via group manager to access repo entry + StringBuilder courseLink = new StringBuilder(); + RepositoryEntry entry = courseEnv.getCourseGroupManager().getCourseEntry(); + courseLink.append(Settings.getServerContextPathURI()) + .append("/url/RepositoryEntry/").append(entry.getKey()); + return translate("email.body.template", new String[]{courseName, courseLink.toString()}); + } + } + + private void doOpenHomePage(MemberView member, UserRequest ureq) { + String url = "[HomePage:" + member.getIdentityKey() + "]"; + BusinessControl bc = BusinessControlFactory.getInstance().createFromString(url); + WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(bc, getWindowControl()); + NewControllerFactory.getInstance().launch(ureq, bwControl); + } + + private void doOpenChat(MemberView member, UserRequest ureq) { + Buddy buddy = imService.getBuddyById(member.getIdentityKey()); + OpenInstantMessageEvent e = new OpenInstantMessageEvent(ureq, buddy); + ureq.getUserSession().getSingleUserEventCenter().fireEventToListenersOf(e, InstantMessagingService.TOWER_EVENT_ORES); + } + +} diff --git a/src/main/java/org/olat/course/nodes/members/SelectMembersController.java b/src/main/java/org/olat/commons/memberlist/ui/SelectMembersController.java similarity index 83% rename from src/main/java/org/olat/course/nodes/members/SelectMembersController.java rename to src/main/java/org/olat/commons/memberlist/ui/SelectMembersController.java index 055d59dfe77..79a7f920911 100644 --- a/src/main/java/org/olat/course/nodes/members/SelectMembersController.java +++ b/src/main/java/org/olat/commons/memberlist/ui/SelectMembersController.java @@ -17,7 +17,7 @@ * frentix GmbH, http://www.frentix.com * <p> */ -package org.olat.course.nodes.members; +package org.olat.commons.memberlist.ui; import java.util.ArrayList; import java.util.Collection; @@ -31,26 +31,30 @@ import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; 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.translator.Translator; +import org.olat.course.nodes.members.Member; /** * * Initial date: 22.12.2015<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com - * + * @author fkiefer */ public class SelectMembersController extends FormBasicController { - private MultipleSelectionElement ownerEl, coachEl, participantEl; - private final List<Member> ownerList, coachList, participantList, preSelectedMembers; + private MultipleSelectionElement ownerEl, coachEl, participantEl, waitingEl; + private final List<Member> ownerList, coachList, participantList, waitingList, preSelectedMembers; private List<Member> selectedMembers = new ArrayList<>(); - public SelectMembersController(UserRequest ureq, WindowControl wControl, List<Member> preSelectedMembers, - List<Member> ownerList, List<Member> coachList, List<Member> participantList) { + public SelectMembersController(UserRequest ureq, WindowControl wControl, Translator translator, + List<Member> preSelectedMembers, List<Member> ownerList, List<Member> coachList, List<Member> participantList, List<Member> waitingList) { super(ureq, wControl, LAYOUT_VERTICAL); + setTranslator(translator); this.ownerList = ownerList; this.coachList = coachList; this.participantList = participantList; + this.waitingList = waitingList; this.preSelectedMembers = preSelectedMembers; initForm(ureq); } @@ -62,15 +66,19 @@ public class SelectMembersController extends FormBasicController { @Override protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { if(ownerList != null && ownerList.size() > 0) { - ownerEl = makeSelection("owners", ownerList, formLayout); + ownerEl = makeSelection("members.owners", ownerList, formLayout); } if(coachList != null && coachList.size() > 0) { - coachEl = makeSelection("coaches", coachList, formLayout); + coachEl = makeSelection("members.coaches", coachList, formLayout); } if(participantList != null && participantList.size() > 0) { - participantEl = makeSelection("participants", participantList, formLayout); + participantEl = makeSelection("members.participants", participantList, formLayout); + } + + if(waitingList != null && waitingList.size() > 0) { + waitingEl = makeSelection("members.waiting", waitingList, formLayout); } FormLayoutContainer buttonGroupLayout = FormLayoutContainer.createButtonLayout("buttonGroupLayout", getTranslator()); @@ -111,6 +119,7 @@ public class SelectMembersController extends FormBasicController { selectMembers(ownerEl, ownerList); selectMembers(coachEl, coachList); selectMembers(participantEl, participantList); + selectMembers(waitingEl, waitingList); fireEvent(ureq, Event.DONE_EVENT); } diff --git a/src/main/java/org/olat/course/nodes/members/_content/individual_members.html b/src/main/java/org/olat/commons/memberlist/ui/_content/individual_members.html similarity index 100% rename from src/main/java/org/olat/course/nodes/members/_content/individual_members.html rename to src/main/java/org/olat/commons/memberlist/ui/_content/individual_members.html diff --git a/src/main/java/org/olat/course/nodes/members/_content/memberList.html b/src/main/java/org/olat/commons/memberlist/ui/_content/memberList.html similarity index 100% rename from src/main/java/org/olat/course/nodes/members/_content/memberList.html rename to src/main/java/org/olat/commons/memberlist/ui/_content/memberList.html diff --git a/src/main/java/org/olat/course/nodes/members/_content/members.html b/src/main/java/org/olat/commons/memberlist/ui/_content/members.html similarity index 77% rename from src/main/java/org/olat/course/nodes/members/_content/members.html rename to src/main/java/org/olat/commons/memberlist/ui/_content/members.html index 554b50f1cc4..bd5e933e8a1 100644 --- a/src/main/java/org/olat/course/nodes/members/_content/members.html +++ b/src/main/java/org/olat/commons/memberlist/ui/_content/members.html @@ -2,6 +2,9 @@ #if($r.available("email")) $r.render("email") #end +#if($r.available("download")) + $r.render("download") +#end #if($r.available("print")) $r.render("print") #end @@ -30,4 +33,10 @@ #end </div> #end + #if($showWaiting && $hasWaiting) + <div class="clearfix o_block o_participants"> + <h4>$r.translate("members.waiting")</h4> + $r.render("waiting") + </div> + #end </div> diff --git a/src/main/java/org/olat/commons/memberlist/ui/_content/membersTable.html b/src/main/java/org/olat/commons/memberlist/ui/_content/membersTable.html new file mode 100644 index 00000000000..e0a6e9c2b81 --- /dev/null +++ b/src/main/java/org/olat/commons/memberlist/ui/_content/membersTable.html @@ -0,0 +1,37 @@ +<div class="pull-right"> +#if($r.available("email")) + $r.render("email") +#end +#if($r.available("download")) + $r.render("download") +#end +#if($r.available("print")) + $r.render("print") +#end +</div> +<div class="clearfix"> + #if ($r.available("ownerList")) + <h4>$r.translate("members.owners")</h4> + <div> + $r.render("ownerList") + </div> + #end + #if ($r.available("coachList")) + <h4>$r.translate("members.coaches")</h4> + <div> + $r.render("coachList") + </div> + #end + #if ($r.available("participantList")) + <h4>$r.translate("members.participants")</h4> + <div> + $r.render("participantList") + </div> + #end + #if ($r.available("waitingList")) + <h4>$r.translate("members.waiting")</h4> + <div> + $r.render("waitingList") + </div> + #end +</div> \ No newline at end of file diff --git a/src/main/java/org/olat/commons/memberlist/ui/_content/membersToggle.html b/src/main/java/org/olat/commons/memberlist/ui/_content/membersToggle.html new file mode 100644 index 00000000000..00369b16f7a --- /dev/null +++ b/src/main/java/org/olat/commons/memberlist/ui/_content/membersToggle.html @@ -0,0 +1,30 @@ +<div class="clearfix"> + <div class="o_header_with_buttons"> + #if ($headline) + <h2><i class="o_icon o_icon_group"> </i> $r.translate("userlist.title")</h2> + #end + <div class="o_button_group"> + <div class="btn-group"> + #if($r.available("tableCustomLink")) + $r.render("tableCustomLink") + #end + #if($r.available("tableLink")) + $r.render("tableLink") + #end + </div> + </div> + </div> + #if ($portrait) + #if ($r.available("portraitView")) + <div> + $r.render("portraitView") + </div> + #end + #else + #if ($r.available("listView")) + <div> + $r.render("listView") + </div> + #end + #end +</div> \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/members/_content/print.html b/src/main/java/org/olat/commons/memberlist/ui/_content/print.html similarity index 96% rename from src/main/java/org/olat/course/nodes/members/_content/print.html rename to src/main/java/org/olat/commons/memberlist/ui/_content/print.html index bfb27657aca..01691912801 100644 --- a/src/main/java/org/olat/course/nodes/members/_content/print.html +++ b/src/main/java/org/olat/commons/memberlist/ui/_content/print.html @@ -12,6 +12,9 @@ #if($r.available("participants")) $r.render("participants") #end +#if($r.available("waiting")) + $r.render("waiting") +#end <script type='text/javascript'> /* <![CDATA[ */ jQuery(function() { diff --git a/src/main/java/org/olat/course/nodes/members/_content/printList.html b/src/main/java/org/olat/commons/memberlist/ui/_content/printList.html similarity index 100% rename from src/main/java/org/olat/course/nodes/members/_content/printList.html rename to src/main/java/org/olat/commons/memberlist/ui/_content/printList.html diff --git a/src/main/java/org/olat/commons/memberlist/ui/_content/table.html b/src/main/java/org/olat/commons/memberlist/ui/_content/table.html new file mode 100644 index 00000000000..399d41789c3 --- /dev/null +++ b/src/main/java/org/olat/commons/memberlist/ui/_content/table.html @@ -0,0 +1,5 @@ +#if ($r.available("table")) + <div> + $r.render("table") + </div> +#end \ No newline at end of file diff --git a/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_de.properties new file mode 100644 index 00000000000..c1d6e89cbe9 --- /dev/null +++ b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_de.properties @@ -0,0 +1,5 @@ +#Tue Nov 13 17:14:15 CET 2012 +table.header.firstTime=Beitritt +table.header.lastTime=Zuletzt besucht +table.header.online=$org.olat.group.ui.main\:table.header.online +nomembers=Keine Mitglieder diff --git a/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_en.properties new file mode 100644 index 00000000000..3ffdcd55cdd --- /dev/null +++ b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_en.properties @@ -0,0 +1,5 @@ +#Tue Feb 09 11:43:56 CET 2016 +table.header.firstTime=Registration +table.header.lastTime=Last visit +table.header.online=$org.olat.group.ui.main\:table.header.online +nomembers=No members \ No newline at end of file diff --git a/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_fr.properties new file mode 100644 index 00000000000..3cc2cfc9907 --- /dev/null +++ b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_fr.properties @@ -0,0 +1,5 @@ +#Wed Jan 11 22:07:40 CET 2017 +table.header.firstTime=Admission +table.header.lastTime=Derni\u00E8re visite +table.header.online=$org.olat.group.ui.main\:table.header.online +nomembers=No members diff --git a/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_it.properties b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_it.properties new file mode 100644 index 00000000000..88b33f0a441 --- /dev/null +++ b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_it.properties @@ -0,0 +1,5 @@ +#Wed Feb 01 13:37:33 CET 2017 +table.header.firstTime=Ammissione +table.header.lastTime=Ultima visita +table.header.online=$org.olat.group.ui.main\:table.header.online +nomembers=No members diff --git a/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_pl.properties b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_pl.properties new file mode 100644 index 00000000000..1943f12213f --- /dev/null +++ b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_pl.properties @@ -0,0 +1,5 @@ +#Wed Jan 15 13:44:33 CET 2014 +table.header.firstTime=Rejestracja +table.header.lastTime=Ostatnia wizyta +table.header.online=$org.olat.group.ui.main\:table.header.online +nomembers=No members diff --git a/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_pt_BR.properties b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_pt_BR.properties new file mode 100644 index 00000000000..4fe088176c7 --- /dev/null +++ b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_pt_BR.properties @@ -0,0 +1,5 @@ +#Mon Jan 16 21:03:20 CET 2017 +table.header.firstTime=Entrando +table.header.lastTime=\u00DAltima visita +table.header.online=$org.olat.group.ui.main\:table.header.online +nomembers=No members \ No newline at end of file diff --git a/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_zh_CN.properties b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_zh_CN.properties new file mode 100644 index 00000000000..09613aad63e --- /dev/null +++ b/src/main/java/org/olat/commons/memberlist/ui/_i18n/LocalStrings_zh_CN.properties @@ -0,0 +1,5 @@ +#Thu Oct 16 09:10:54 CEST 2014 +table.header.firstTime=\u52A0\u5165 +table.header.lastTime=\u4E0A\u6B21\u8BBF\u95EE +table.header.online=$org.olat.group.ui.main\:table.header.online +nomembers=No members \ No newline at end of file diff --git a/src/main/java/org/olat/core/util/Util.java b/src/main/java/org/olat/core/util/Util.java index 1cdb6217c04..83a67bd18f0 100644 --- a/src/main/java/org/olat/core/util/Util.java +++ b/src/main/java/org/olat/core/util/Util.java @@ -82,6 +82,25 @@ public class Util { return createPackageTranslator(baseClass, locale, null); } + public static Translator createPackageTranslator(Class<?> baseClass, Class<?> fallbackClass, Locale locale) { + String fallbackpackage = Util.getPackageName(fallbackClass); + Translator fallback = new PackageTranslator(fallbackpackage, locale); + String transpackage = Util.getPackageName(baseClass); + Translator translator = new PackageTranslator(transpackage, locale, fallback); + return translator; + } + + public static Translator createPackageTranslator(Translator baseClass, Class<?> fallbackClass, Locale locale) { + String fallbackpackage = Util.getPackageName(fallbackClass); + Translator fallback = new PackageTranslator(fallbackpackage, locale); + Translator translator = new PackageTranslator(((PackageTranslator)baseClass).getPackageName(), locale, fallback); + return translator; + } + + public static Translator createPackageTranslator(Translator translator, Translator fallback, Locale locale) { + return new PackageTranslator(((PackageTranslator)translator).getPackageName(), locale, fallback); + } + /** * * returns a Translator for the given baseclass and locale diff --git a/src/main/java/org/olat/course/nodes/members/MembersCourseNodeRunController.java b/src/main/java/org/olat/course/nodes/members/MembersCourseNodeRunController.java index 09389eebb08..2e9e07277f4 100644 --- a/src/main/java/org/olat/course/nodes/members/MembersCourseNodeRunController.java +++ b/src/main/java/org/olat/course/nodes/members/MembersCourseNodeRunController.java @@ -21,62 +21,26 @@ package org.olat.course.nodes.members; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import org.olat.NewControllerFactory; import org.olat.basesecurity.BaseSecurity; -import org.olat.core.commons.fullWebApp.popup.BaseFullWebappPopupLayoutFactory; +import org.olat.commons.memberlist.ui.MembersDisplayRunController; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; -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; -import org.olat.core.gui.components.form.flexible.impl.FormBasicController; -import org.olat.core.gui.components.form.flexible.impl.FormEvent; -import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer; -import org.olat.core.gui.components.link.Link; -import org.olat.core.gui.components.link.LinkFactory; -import org.olat.core.gui.components.link.LinkPopupSettings; -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.creator.ControllerCreator; -import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController; -import org.olat.core.helpers.Settings; +import org.olat.core.gui.control.controller.BasicController; import org.olat.core.id.Identity; -import org.olat.core.id.UserConstants; -import org.olat.core.id.context.BusinessControl; -import org.olat.core.id.context.BusinessControlFactory; -import org.olat.core.util.StringHelper; -import org.olat.core.util.mail.ContactList; -import org.olat.core.util.mail.ContactMessage; -import org.olat.core.util.session.UserSessionManager; import org.olat.course.groupsandrights.CourseGroupManager; import org.olat.course.nodes.CourseNodeFactory; import org.olat.course.nodes.MembersCourseNode; import org.olat.course.run.environment.CourseEnvironment; import org.olat.course.run.userview.UserCourseEnvironment; import org.olat.group.BusinessGroupService; -import org.olat.instantMessaging.InstantMessagingModule; -import org.olat.instantMessaging.InstantMessagingService; -import org.olat.instantMessaging.OpenInstantMessageEvent; -import org.olat.instantMessaging.model.Buddy; -import org.olat.instantMessaging.model.Presence; import org.olat.modules.IModuleConfiguration; import org.olat.modules.ModuleConfiguration; -import org.olat.modules.co.ContactFormController; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryService; -import org.olat.user.DisplayPortraitManager; -import org.olat.user.UserAvatarMapper; -import org.olat.user.UserManager; -import org.olat.user.propertyhandlers.UserPropertyHandler; import org.springframework.beans.factory.annotation.Autowired; /** @@ -87,63 +51,34 @@ import org.springframework.beans.factory.annotation.Autowired; * <P> * Initial Date: 11 mars 2011 <br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * @author fkiefer */ -public class MembersCourseNodeRunController extends FormBasicController { +public class MembersCourseNodeRunController extends BasicController { - private final List<UserPropertyHandler> userPropertyHandlers; public static final String USER_PROPS_ID = MembersCourseNodeRunController.class.getName(); - - private final CourseEnvironment courseEnv; - private final DisplayPortraitManager portraitManager; - private final String avatarBaseURL; - - private Link printLink; - private FormLink allEmailLink; - private List<Member> ownerList; - private List<Member> coachList; - private List<Member> participantList; - - private final boolean canEmail; - private final boolean showOwners; - private final boolean chatEnabled; + private CourseEnvironment courseEnv; + private boolean canEmail, deduplicateList, showOwners, showCoaches = false, showParticipants = false; + private List<Identity> owners, coaches, participants; - private MembersMailController mailCtrl; - private ContactFormController emailController; - private CloseableModalController cmc; + private MembersDisplayRunController membersDisplayRunController; - private int count = 0; - private final boolean deduplicateList; - - @Autowired - private UserManager userManager; - @Autowired - private BaseSecurity securityManager; @Autowired private RepositoryService repositoryService; @Autowired - private InstantMessagingModule imModule; - @Autowired - private InstantMessagingService imService; - @Autowired - private UserSessionManager sessionManager; - @Autowired private BusinessGroupService businessGroupService; - - private final ModuleConfiguration config; + @Autowired + protected BaseSecurity securityManager; public MembersCourseNodeRunController(UserRequest ureq, WindowControl wControl, UserCourseEnvironment userCourseEnv, ModuleConfiguration config) { - super(ureq, wControl, "members"); - - this.config = config; - userPropertyHandlers = userManager.getUserPropertyHandlersFor(USER_PROPS_ID, false); - + super(ureq, wControl); + courseEnv = userCourseEnv.getCourseEnvironment(); - avatarBaseURL = registerCacheableMapper(ureq, "avatars-members", new UserAvatarMapper(true)); - portraitManager = DisplayPortraitManager.getInstance(); + + this.coaches = new ArrayList<>(); + this.participants = new ArrayList<>(); showOwners = config.getBooleanSafe(MembersCourseNode.CONFIG_KEY_SHOWOWNER); - chatEnabled = imModule.isEnabled() && imModule.isPrivateEnabled(); MembersCourseNodeConfiguration nodeConfig = (MembersCourseNodeConfiguration)CourseNodeFactory.getInstance().getCourseNodeConfiguration("cmembers"); deduplicateList = nodeConfig.isDeduplicateList(); @@ -151,32 +86,15 @@ public class MembersCourseNodeRunController extends FormBasicController { String emailFct = config.getStringValue(MembersCourseNode.CONFIG_KEY_EMAIL_FUNCTION, MembersCourseNode.EMAIL_FUNCTION_COACH_ADMIN); canEmail = MembersCourseNode.EMAIL_FUNCTION_ALL.equals(emailFct) || userCourseEnv.isAdmin() || userCourseEnv.isCoach(); - initForm(ureq); - } - - @Override - protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { - if(formLayout instanceof FormLayoutContainer) { - printLink = LinkFactory.createButton("print", ((FormLayoutContainer)formLayout).getFormItemComponent(), this); - printLink.setIconLeftCSS("o_icon o_icon_print o_icon-lg"); - printLink.setPopup(new LinkPopupSettings(700, 500, "print-members")); - ((FormLayoutContainer)formLayout).getFormItemComponent().put("print", printLink); - } - IModuleConfiguration membersFrag = IModuleConfiguration.fragment("members", config); - List<Identity> owners; if(showOwners) { RepositoryEntry courseRepositoryEntry = courseEnv.getCourseGroupManager().getCourseEntry(); owners = MembersHelpers.getOwners(repositoryService, courseRepositoryEntry); } else { owners = Collections.emptyList(); } - - boolean showCoaches = false; - boolean showParticipants = false; - List<Identity> coaches = new ArrayList<>(); if(membersFrag.anyTrue(MembersCourseNode.CONFIG_KEY_COACHES_ALL, MembersCourseNode.CONFIG_KEY_COACHES_COURSE) || membersFrag.hasAnyOf(MembersCourseNode.CONFIG_KEY_COACHES_GROUP, MembersCourseNode.CONFIG_KEY_COACHES_AREA)) { @@ -186,7 +104,6 @@ public class MembersCourseNodeRunController extends FormBasicController { showCoaches = true; } - List<Identity> participants = new ArrayList<>(); if(membersFrag.anyTrue(MembersCourseNode.CONFIG_KEY_PARTICIPANTS_ALL, MembersCourseNode.CONFIG_KEY_PARTICIPANTS_COURSE) || membersFrag.hasAnyOf(MembersCourseNode.CONFIG_KEY_PARTICIPANTS_GROUP, MembersCourseNode.CONFIG_KEY_PARTICIPANTS_AREA)) { @@ -195,307 +112,27 @@ public class MembersCourseNodeRunController extends FormBasicController { showParticipants = true; } - - Comparator<Identity> idComparator = new IdentityComparator(); - Collections.sort(owners, idComparator); - Collections.sort(coaches, idComparator); - Collections.sort(participants, idComparator); - if(canEmail) { - allEmailLink = uifactory.addFormLink("email", "members.email.title", null, formLayout, Link.BUTTON); - allEmailLink.setIconLeftCSS("o_icon o_icon_mail"); - } - - Set<Long> duplicateCatcher = deduplicateList ? new HashSet<Long>() : null; - ownerList = initFormMemberList("owners", owners, duplicateCatcher, formLayout, canEmail); - coachList = initFormMemberList("coaches", coaches, duplicateCatcher, formLayout, canEmail); - participantList = initFormMemberList("participants", participants, duplicateCatcher, formLayout, canEmail); - - if(formLayout instanceof FormLayoutContainer) { - FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout; - layoutCont.contextPut("showOwners", showOwners); - layoutCont.contextPut("hasOwners", new Boolean(!ownerList.isEmpty())); - layoutCont.contextPut("showCoaches", showCoaches); - layoutCont.contextPut("hasCoaches", new Boolean(!coachList.isEmpty())); - layoutCont.contextPut("showParticipants", showParticipants); - layoutCont.contextPut("hasParticipants", new Boolean(!participantList.isEmpty())); - } - } - - private List<Member> initFormMemberList(String name, List<Identity> ids, Set<Long> duplicateCatcher, FormItemContainer formLayout, boolean withEmail) { - String page = velocity_root + "/memberList.html"; - - FormLayoutContainer container = FormLayoutContainer.createCustomFormLayout(name, getTranslator(), page); - formLayout.add(name, container); - container.setRootForm(mainForm); - - List<Member> members = createMemberLinks(ids, duplicateCatcher, container, withEmail); - container.contextPut("members", members); - container.contextPut("avatarBaseURL", avatarBaseURL); - return members; - } - - protected List<Member> createMemberLinks(List<Identity> identities, Set<Long> duplicateCatcher, FormLayoutContainer formLayout, boolean withEmail) { - List<Member> members = new ArrayList<>(); - for(Identity identity:identities) { - if(duplicateCatcher != null && duplicateCatcher.contains(identity.getKey())) continue; - - Member member = createMember(identity); - members.add(member); - - String guiId = Integer.toString(++count); - String fullname = StringHelper.escapeHtml(member.getFullName()); - - FormLink idLink = uifactory.addFormLink("id_".concat(guiId), "id", fullname, null, formLayout, Link.NONTRANSLATED); - - idLink.setUserObject(member); - formLayout.add(idLink.getComponent().getComponentName(), idLink); - member.setIdLink(idLink); - - if(withEmail) { - FormLink emailLink = uifactory.addFormLink("mail_".concat(guiId), "mail", "", null, formLayout, Link.NONTRANSLATED); - emailLink.setUserObject(member); - emailLink.setIconLeftCSS("o_icon o_icon_mail o_icon-lg"); - emailLink.setElementCssClass("o_mail"); - formLayout.add(emailLink.getComponent().getComponentName(), emailLink); - member.setEmailLink(emailLink); - } - if(chatEnabled) { - FormLink chatLink = uifactory.addFormLink("chat_".concat(guiId), "chat", "", null, formLayout, Link.NONTRANSLATED); - chatLink.setUserObject(member); - chatLink.setElementCssClass("o_chat"); - formLayout.add(chatLink.getComponent().getComponentName(), chatLink); - member.setChatLink(chatLink); - } - - if(duplicateCatcher != null) { - duplicateCatcher.add(identity.getKey()); - } - } + membersDisplayRunController = new MembersDisplayRunController(ureq, wControl, getTranslator(), courseEnv, null, + owners, coaches, participants, new ArrayList<>(), canEmail, deduplicateList, showOwners, showCoaches, + showParticipants, false, true); + listenTo(membersDisplayRunController); - if(chatEnabled) { - Long me = getIdentity().getKey(); - if(imModule.isOnlineStatusEnabled()) { - Map<Long,Member> loadStatus = new HashMap<>(); - - for(Member member:members) { - if(member.getKey().equals(me)) { - member.getChatLink().setVisible(false); - } else if(sessionManager.isOnline(member.getKey())) { - loadStatus.put(member.getKey(), member); - } else { - member.getChatLink().setIconLeftCSS("o_icon o_icon_status_unavailable"); - } - } - - if(loadStatus.size() > 0) { - List<Long> statusToLoadList = new ArrayList<>(loadStatus.keySet()); - Map<Long,String> statusMap = imService.getBuddyStatus(statusToLoadList); - for(Long toLoad:statusToLoadList) { - String status = statusMap.get(toLoad); - Member member = loadStatus.get(toLoad); - if(status == null || Presence.available.name().equals(status)) { - member.getChatLink().setIconLeftCSS("o_icon o_icon_status_available"); - } else if(Presence.dnd.name().equals(status)) { - member.getChatLink().setIconLeftCSS("o_icon o_icon_status_dnd"); - } else { - member.getChatLink().setIconLeftCSS("o_icon o_icon_status_unavailable"); - } - } - } - } else { - for(Member member:members) { - if(member.getKey().equals(me)) { - member.getChatLink().setVisible(false); - } else { - member.getChatLink().setIconLeftCSS("o_icon o_icon_status_chat"); - } - } - } - } - - return members; + putInitialPanel(membersDisplayRunController.getInitialComponent()); } - private Member createMember(Identity identity) { - boolean hasPortrait = portraitManager.hasPortrait(identity.getName()); - String portraitCssClass; - String gender = identity.getUser().getProperty(UserConstants.GENDER, Locale.ENGLISH); - if ("male".equalsIgnoreCase(gender)) { - portraitCssClass = DisplayPortraitManager.DUMMY_MALE_BIG_CSS_CLASS; - } else if ("female".equalsIgnoreCase(gender)) { - portraitCssClass = DisplayPortraitManager.DUMMY_FEMALE_BIG_CSS_CLASS; - } else { - portraitCssClass = DisplayPortraitManager.DUMMY_BIG_CSS_CLASS; - } - String fullname = userManager.getUserDisplayName(identity); - return new Member(identity, fullname, userPropertyHandlers, getLocale(), hasPortrait, portraitCssClass); - } - @Override - protected void doDispose() { - // - } - - @Override - protected void formOK(UserRequest ureq) { + protected void event(UserRequest ureq, Component source, Event event) { // } - - @Override - public void event(UserRequest ureq, Component source, Event event) { - if(source == printLink) { - doPrint(ureq); - } - super.event(ureq, source, event); - } @Override - protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { - if(source == allEmailLink) { - doEmail(ureq); - } else if(source instanceof FormLink) { - FormLink link = (FormLink)source; - Object uobject = link.getUserObject(); - if(uobject instanceof Member) { - Member member = (Member)uobject; - String cmd = link.getCmd(); - if("id".equals(cmd)) { - doOpenHomePage(member, ureq); - } else if("mail".equals(cmd)) { - doSendEmailToMember(member, ureq); - } else if("chat".equals(cmd)) { - doOpenChat(member, ureq); - } - } - } - super.formInnerEvent(ureq, source, event); - } - - @Override - protected void event(UserRequest ureq, Controller source, Event event) { - if(source == cmc) { - cleanUp(); - } else if (source == emailController) { - cmc.deactivate(); - cleanUp(); - } else if(source == mailCtrl) { - cmc.deactivate(); - cleanUp(); - } - super.event(ureq, source, event); - } - - private void cleanUp() { - removeAsListenerAndDispose(emailController); - removeAsListenerAndDispose(mailCtrl); - removeAsListenerAndDispose(cmc); - emailController = null; - mailCtrl = null; - cmc = null; - } - - private void doEmail(UserRequest ureq) { - if(mailCtrl != null || cmc != null) return; - removeAsListenerAndDispose(cmc); - removeAsListenerAndDispose(mailCtrl); - - mailCtrl = new MembersMailController(ureq, getWindowControl(), courseEnv, ownerList, coachList, participantList, createBodyTemplate()); - listenTo(mailCtrl); - - String title = translate("members.email.title"); - cmc = new CloseableModalController(getWindowControl(), translate("close"), mailCtrl.getInitialComponent(), true, title); - listenTo(cmc); - - cmc.activate(); - } - - private void doOpenChat(Member member, UserRequest ureq) { - Buddy buddy = imService.getBuddyById(member.getKey()); - OpenInstantMessageEvent e = new OpenInstantMessageEvent(ureq, buddy); - ureq.getUserSession().getSingleUserEventCenter().fireEventToListenersOf(e, InstantMessagingService.TOWER_EVENT_ORES); - } - - private void doSendEmailToMember(Member member, UserRequest ureq) { - ContactList memberList = new ContactList(translate("members.to", new String[]{ member.getFullName(), courseEnv.getCourseTitle() })); - Identity identity = securityManager.loadIdentityByKey(member.getKey()); - memberList.add(identity); - doSendEmailToMember(memberList, ureq); - } - - private void doSendEmailToMember(ContactList contactList, UserRequest ureq) { - if (contactList.getEmailsAsStrings().size() > 0) { - removeAsListenerAndDispose(cmc); - removeAsListenerAndDispose(emailController); - - ContactMessage cmsg = new ContactMessage(ureq.getIdentity()); - cmsg.addEmailTo(contactList); - // preset body template from i18n - cmsg.setBodyText(createBodyTemplate()); - emailController = new ContactFormController(ureq, getWindowControl(), true, false, false, cmsg); - listenTo(emailController); - - String title = translate("members.email.title"); - cmc = new CloseableModalController(getWindowControl(), translate("close"), emailController.getInitialComponent(), true, title); - listenTo(cmc); - cmc.activate(); - } - } - - private String createBodyTemplate() { - String courseName = courseEnv.getCourseTitle(); - // Build REST URL to course element, use hack via group manager to access repo entry - StringBuilder courseLink = new StringBuilder(); - RepositoryEntry entry = courseEnv.getCourseGroupManager().getCourseEntry(); - courseLink.append(Settings.getServerContextPathURI()) - .append("/url/RepositoryEntry/").append(entry.getKey()); - return translate("email.body.template", new String[]{courseName, courseLink.toString()}); - } - - private void doOpenHomePage(Member member, UserRequest ureq) { - String url = "[HomePage:" + member.getKey() + "]"; - BusinessControl bc = BusinessControlFactory.getInstance().createFromString(url); - WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(bc, getWindowControl()); - NewControllerFactory.getInstance().launch(ureq, bwControl); - } - - private void doPrint(UserRequest ureq) { - ControllerCreator printControllerCreator = new ControllerCreator() { - @Override - public Controller createController(UserRequest lureq, WindowControl lwControl) { - lwControl.getWindowBackOffice().getChiefController().addBodyCssClass("o_cmembers_print"); - return new MembersPrintController(lureq, lwControl, courseEnv, userPropertyHandlers, - ownerList, coachList, participantList); - } - }; - ControllerCreator layoutCtrlr = BaseFullWebappPopupLayoutFactory.createPrintPopupLayout(printControllerCreator); - openInNewBrowserWindow(ureq, layoutCtrlr); - } - - public static class IdentityComparator implements Comparator<Identity> { - - @Override - public int compare(Identity id1, Identity id2) { - if(id1 == null) return -1; - if(id2 == null) return 1; - - String l1 = id1.getUser().getProperty(UserConstants.LASTNAME, null); - String l2 = id2.getUser().getProperty(UserConstants.LASTNAME, null); - if(l1 == null) return -1; - if(l2 == null) return 1; - - int result = l1.compareToIgnoreCase(l2); - if(result == 0) { - String f1 = id1.getUser().getProperty(UserConstants.FIRSTNAME, null); - String f2 = id2.getUser().getProperty(UserConstants.FIRSTNAME, null); - if(f1 == null) return -1; - if(f2 == null) return 1; - result = f1.compareToIgnoreCase(f2); - } - return result; - } + protected void doDispose() { + // nothing to dispose } } + + \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/members/MembersHelpers.java b/src/main/java/org/olat/course/nodes/members/MembersHelpers.java index 5beb12d2ac1..decdbe1f021 100644 --- a/src/main/java/org/olat/course/nodes/members/MembersHelpers.java +++ b/src/main/java/org/olat/course/nodes/members/MembersHelpers.java @@ -39,20 +39,20 @@ import org.olat.repository.RepositoryService; * <p>Initial date: May 20, 2016 * @author lmihalkovic, http://www.frentix.com */ -/*public*/ class MembersHelpers { +public class MembersHelpers { private MembersHelpers() { // CANNOT CREATE } // ----------------------------------------------------- - static List<Identity> getOwners(RepositoryService repositoryService, RepositoryEntry courseRepositoryEntry) { + public static List<Identity> getOwners(RepositoryService repositoryService, RepositoryEntry courseRepositoryEntry) { return repositoryService.getMembers(courseRepositoryEntry, GroupRoles.owner.name()); } // ----------------------------------------------------- - static void addCoaches(IModuleConfiguration moduleConfiguration, CourseGroupManager cgm, BusinessGroupService bgs, List<Identity> list) { + public static void addCoaches(IModuleConfiguration moduleConfiguration, CourseGroupManager cgm, BusinessGroupService bgs, List<Identity> list) { if(moduleConfiguration.has(MembersCourseNode.CONFIG_KEY_COACHES_GROUP)) { String coachGroupNames = moduleConfiguration.val(MembersCourseNode.CONFIG_KEY_COACHES_GROUP); @@ -81,24 +81,24 @@ import org.olat.repository.RepositoryService; } } - static List<Identity> retrieveCoachesFromAreas(List<Long> areaKeys, CourseGroupManager cgm) { + public static List<Identity> retrieveCoachesFromAreas(List<Long> areaKeys, CourseGroupManager cgm) { List<Identity> coaches = cgm.getCoachesFromAreas(areaKeys); Set<Identity> coachesWithoutDuplicates = new HashSet<Identity>(coaches); coaches = new ArrayList<Identity>(coachesWithoutDuplicates); return coaches; } - static List<Identity> retrieveCoachesFromGroups(List<Long> groupKeys, CourseGroupManager cgm) { + public static List<Identity> retrieveCoachesFromGroups(List<Long> groupKeys, CourseGroupManager cgm) { List<Identity> coaches = new ArrayList<Identity>(new HashSet<Identity>(cgm.getCoachesFromBusinessGroups(groupKeys))); return coaches; } - static List<Identity> retrieveCoachesFromCourse(CourseGroupManager cgm) { + public static List<Identity> retrieveCoachesFromCourse(CourseGroupManager cgm) { List<Identity> coaches = cgm.getCoaches(); return coaches; } - static List<Identity> retrieveCoachesFromCourseGroups(CourseGroupManager cgm) { + public static List<Identity> retrieveCoachesFromCourseGroups(CourseGroupManager cgm) { Set<Identity> uniq = new HashSet<Identity>(); { List<Identity> coaches = cgm.getCoachesFromAreas(); @@ -113,7 +113,7 @@ import org.olat.repository.RepositoryService; // ----------------------------------------------------- - static void addParticipants(IModuleConfiguration moduleConfiguration, CourseGroupManager cgm, BusinessGroupService bgs, List<Identity> list) { + public static void addParticipants(IModuleConfiguration moduleConfiguration, CourseGroupManager cgm, BusinessGroupService bgs, List<Identity> list) { if(moduleConfiguration.has(MembersCourseNode.CONFIG_KEY_PARTICIPANTS_GROUP)) { String participantGroupNames = moduleConfiguration.val(MembersCourseNode.CONFIG_KEY_PARTICIPANTS_GROUP); @@ -142,22 +142,22 @@ import org.olat.repository.RepositoryService; } } - static List<Identity> retrieveParticipantsFromAreas(List<Long> areaKeys, CourseGroupManager cgm) { + public static List<Identity> retrieveParticipantsFromAreas(List<Long> areaKeys, CourseGroupManager cgm) { List<Identity> participiants = cgm.getParticipantsFromAreas(areaKeys); return participiants; } - static List<Identity> retrieveParticipantsFromGroups(List<Long> groupKeys, CourseGroupManager cgm) { + public static List<Identity> retrieveParticipantsFromGroups(List<Long> groupKeys, CourseGroupManager cgm) { List<Identity> participiants = cgm.getParticipantsFromBusinessGroups(groupKeys); return participiants; } - static List<Identity> retrieveParticipantsFromCourse(CourseGroupManager cgm) { + public static List<Identity> retrieveParticipantsFromCourse(CourseGroupManager cgm) { List<Identity> participiants = cgm.getParticipants(); return participiants; } - static List<Identity> retrieveParticipantsFromCourseGroups(CourseGroupManager cgm) { + public static List<Identity> retrieveParticipantsFromCourseGroups(CourseGroupManager cgm) { Set<Identity> uniq = new HashSet<Identity>(); { List<Identity> participiants = cgm.getParticipantsFromAreas(); diff --git a/src/main/java/org/olat/course/nodes/members/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/nodes/members/_i18n/LocalStrings_de.properties index 28e6465e986..3bcdfa30079 100644 --- a/src/main/java/org/olat/course/nodes/members/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/course/nodes/members/_i18n/LocalStrings_de.properties @@ -30,6 +30,7 @@ members.info=W\u00E4hlen Sie welche Benutzergruppen in der Teilnehmerliste angez members.noParticipants.message=Diesem Kurs sind keine Lerngruppen mit Teilnehmern zugeordnet. Erstellen Sie eine Lerngruppe im Gruppenwerkzeug und f\u00FCgen Sie dort Teilnehmer hinzu. members.owners=Kursadministrator members.participants=Teilnehmer +members.download=Herunterladen members.to=Teilnehmer "{0}" von Kurs "{1}" owners.to=Aministratoren von Kurs "{0}" pane.tab.accessibility=Zugang @@ -40,3 +41,4 @@ select.members=Benutzer ausw\u00E4hlen title_info=Teilnehmerliste message.want.coaches=Betreuer message.want.participants=Teilnehmer +blank= \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/members/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/nodes/members/_i18n/LocalStrings_en.properties index 7646a7ab4f7..8a4f3a68ac4 100644 --- a/src/main/java/org/olat/course/nodes/members/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/course/nodes/members/_i18n/LocalStrings_en.properties @@ -25,6 +25,7 @@ members.info=Select which user group should be shown in the participant list. members.noParticipants.message=This course has no learning groups with participants. Create a learning group in the group management tool and add a member there. members.owners=Course administrator members.participants=Participant +members.download=Download members.to="{0}" of course "{1}" msg.send.ok=$org.olat.modules.co\:msg.send.ok error.msg.send.partially.nok=$org.olat.modules.co\:error.msg.send.partially.nok @@ -41,3 +42,4 @@ title_info=Participant list message.want.coaches=Coaches message.want.participants=Participants +blank= \ No newline at end of file diff --git a/src/main/java/org/olat/group/ui/homepage/GroupMembersDisplayController.java b/src/main/java/org/olat/group/ui/homepage/GroupMembersDisplayController.java index c97f69a074a..78dd158e5b9 100644 --- a/src/main/java/org/olat/group/ui/homepage/GroupMembersDisplayController.java +++ b/src/main/java/org/olat/group/ui/homepage/GroupMembersDisplayController.java @@ -19,53 +19,70 @@ */ package org.olat.group.ui.homepage; -import org.olat.basesecurity.Group; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import org.olat.basesecurity.GroupRoles; -import org.olat.basesecurity.ui.GroupController; -import org.olat.core.CoreSpringFactory; +import org.olat.commons.memberlist.ui.MembersDisplayRunController; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; -import org.olat.core.gui.components.velocity.VelocityContainer; 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.id.Identity; +import org.olat.core.util.Util; import org.olat.group.BusinessGroup; import org.olat.group.BusinessGroupService; +import org.olat.group.ui.run.GroupMembersRunController; +import org.springframework.beans.factory.annotation.Autowired; /** * * Initial Date: Aug 19, 2009 <br> * @author twuersch, www.frentix.com * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * @author fkiefer */ public class GroupMembersDisplayController extends BasicController { - private final VelocityContainer content; + private MembersDisplayRunController membersDisplayRunController; + + @Autowired + private BusinessGroupService businessGroupService; + public GroupMembersDisplayController(UserRequest ureq, WindowControl wControl, BusinessGroup businessGroup) { super(ureq, wControl); + setTranslator(Util.createPackageTranslator(GroupMembersRunController.class, getLocale())); // display owners and participants - content = createVelocityContainer("groupmembersdisplay"); - BusinessGroupService bgs = CoreSpringFactory.getImpl(BusinessGroupService.class); - Group group = bgs.getGroup(businessGroup); - - if(businessGroup.isOwnersVisiblePublic()) { - GroupController groupOwnersController = new GroupController(ureq, wControl, false, true, false, false, false, false, group, GroupRoles.coach.name()); - content.put("owners", groupOwnersController.getInitialComponent()); - listenTo(groupOwnersController); - } - if(businessGroup.isParticipantsVisiblePublic()) { - GroupController groupParticipantsController = new GroupController(ureq, wControl, false, true, false, false, false, false, group, GroupRoles.participant.name()); - content.put("participants", groupParticipantsController.getInitialComponent()); - listenTo(groupParticipantsController); + + List<Identity> coaches, participants, waiting; + boolean showCoaches = businessGroup.isOwnersVisiblePublic(); + if (showCoaches) { + coaches = businessGroupService.getMembers(businessGroup, GroupRoles.coach.name()); + } else { + coaches = Collections.emptyList(); } - if(businessGroup.isWaitingListVisiblePublic()) { - GroupController groupWaitingListController = new GroupController(ureq, wControl, false, true, false, false, false, false, group, GroupRoles.waiting.name()); - content.put("waitingList", groupWaitingListController.getInitialComponent()); - listenTo(groupWaitingListController); + boolean showParticipants = businessGroup.isParticipantsVisiblePublic(); + if (showParticipants) { + participants = businessGroupService.getMembers(businessGroup, GroupRoles.participant.name()); + } else { + participants = Collections.emptyList(); } - putInitialPanel(content); + boolean showWaiting = businessGroup.isWaitingListVisiblePublic(); + if (showWaiting) { + waiting = businessGroupService.getMembers(businessGroup, GroupRoles.waiting.name()); + } else { + waiting = Collections.emptyList(); + } + + membersDisplayRunController = new MembersDisplayRunController(ureq, wControl, getTranslator(), null, businessGroup, + new ArrayList<>(), coaches, participants, waiting, false, true, false, showCoaches, showParticipants, showWaiting, false); + listenTo(membersDisplayRunController); + + putInitialPanel(membersDisplayRunController.getInitialComponent()); } @Override @@ -75,6 +92,6 @@ public class GroupMembersDisplayController extends BasicController { @Override protected void event(UserRequest ureq, Component source, Event event) { - // Do nothing. + // events handled in child controller } } diff --git a/src/main/java/org/olat/group/ui/homepage/_content/groupmembersdisplay.html b/src/main/java/org/olat/group/ui/homepage/_content/groupmembersdisplay.html deleted file mode 100644 index 26672b710fd..00000000000 --- a/src/main/java/org/olat/group/ui/homepage/_content/groupmembersdisplay.html +++ /dev/null @@ -1,19 +0,0 @@ -<h2><i class="o_icon o_icon_group"> </i> $r.translate("userlist.title")</h2> -#if($r.available("owners")) - <h3>$r.translate("userlist.owners.title")</h3> - <div class="o_sel_group_coaches"> - $r.render("owners") - </div> -#end -#if($r.available("participants")) - <h3>$r.translate("userlist.participants.title")</h3> - <div class="o_sel_group_participants"> - $r.render("participants") - </div> -#end -#if($r.available("waitingList")) - <h3>$r.translate("userlist.waitingList.title")</h3> - <div class="o_sel_group_waiting_list"> - $r.render("waitingList") - </div> -#end \ No newline at end of file diff --git a/src/main/java/org/olat/group/ui/main/MemberListTableModel.java b/src/main/java/org/olat/group/ui/main/MemberListTableModel.java index 8471a9cc7a8..9384373eefb 100644 --- a/src/main/java/org/olat/group/ui/main/MemberListTableModel.java +++ b/src/main/java/org/olat/group/ui/main/MemberListTableModel.java @@ -24,6 +24,7 @@ import java.util.List; import org.olat.core.commons.persistence.SortKey; import org.olat.core.gui.components.form.flexible.elements.FormLink; import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiTableDataModel; +import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiSortableColumnDef; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel; import org.olat.core.gui.components.form.flexible.impl.elements.table.SortableFlexiTableDataModel; import org.olat.instantMessaging.model.Presence; @@ -102,7 +103,7 @@ public class MemberListTableModel extends DefaultFlexiTableDataModel<MemberView> return new MemberListTableModel(getTableColumnModel(), onlineStatusEnabled); } - public enum Cols { + public enum Cols implements FlexiSortableColumnDef{ username("table.header.login"), firstTime("table.header.firstTime"), lastTime("table.header.lastTime"), @@ -120,5 +121,19 @@ public class MemberListTableModel extends DefaultFlexiTableDataModel<MemberView> public String i18n() { return i18n; } + + @Override + public String i18nHeaderKey() { + return i18n; + } + + @Override + public boolean sortable() { + return true; + } + @Override + public String sortKey() { + return i18n; + } } } diff --git a/src/main/java/org/olat/group/ui/run/BusinessGroupMainRunController.java b/src/main/java/org/olat/group/ui/run/BusinessGroupMainRunController.java index f7eddc2be21..f52a68026d3 100644 --- a/src/main/java/org/olat/group/ui/run/BusinessGroupMainRunController.java +++ b/src/main/java/org/olat/group/ui/run/BusinessGroupMainRunController.java @@ -31,9 +31,7 @@ import java.util.List; import org.olat.NewControllerFactory; import org.olat.basesecurity.BaseSecurityModule; -import org.olat.basesecurity.Group; import org.olat.basesecurity.GroupRoles; -import org.olat.basesecurity.ui.GroupController; import org.olat.collaboration.CollaborationTools; import org.olat.collaboration.CollaborationToolsFactory; import org.olat.commons.calendar.CalendarModule; @@ -189,12 +187,9 @@ public class BusinessGroupMainRunController extends MainLayoutBasicController im private BusinessGroupEditController bgEditCntrllr; private Controller bgACHistoryCtrl; private TableController resourcesCtr; + private GroupMembersRunController groupMembersToggleViewController; private BusinessGroupSendToChooserForm sendToChooserForm; - - private GroupController gownersC; - private GroupController gparticipantsC; - private GroupController waitingListController; /** * Business group administrator @@ -852,42 +847,11 @@ public class BusinessGroupMainRunController extends MainLayoutBasicController im } private void doShowMembers(UserRequest ureq) { - VelocityContainer membersVc = createVelocityContainer("ownersandmembers"); - // 1. show owners if configured with Owners - boolean downloadAllowed = businessGroup.isDownloadMembersLists(); - Group group = businessGroupService.getGroup(businessGroup); - if (businessGroup.isOwnersVisibleIntern()) { - removeAsListenerAndDispose(gownersC); - gownersC = new GroupController(ureq, getWindowControl(), false, true, true, false, downloadAllowed, false, group, GroupRoles.coach.name()); - listenTo(gownersC); - membersVc.put("owners", gownersC.getInitialComponent()); - membersVc.contextPut("showOwnerGroups", Boolean.TRUE); - } else { - membersVc.contextPut("showOwnerGroups", Boolean.FALSE); - } - // 2. show participants if configured with Participants - if (businessGroup.isParticipantsVisibleIntern()) { - removeAsListenerAndDispose(gparticipantsC); - gparticipantsC = new GroupController(ureq, getWindowControl(), false, true, true, false, downloadAllowed, false, group, GroupRoles.participant.name()); - listenTo(gparticipantsC); - - membersVc.put("participants", gparticipantsC.getInitialComponent()); - membersVc.contextPut("showPartipsGroups", Boolean.TRUE); - } else { - membersVc.contextPut("showPartipsGroups", Boolean.FALSE); - } - // 3. show waiting-list if configured - membersVc.contextPut("hasWaitingList", new Boolean(businessGroup.getWaitingListEnabled()) ); - if (businessGroup.isWaitingListVisibleIntern()) { - removeAsListenerAndDispose(waitingListController); - waitingListController = new GroupController(ureq, getWindowControl(), false, true, true, false, downloadAllowed, false, group, GroupRoles.waiting.name()); - listenTo(waitingListController); - membersVc.put("waitingList", waitingListController.getInitialComponent()); - membersVc.contextPut("showWaitingList", Boolean.TRUE); - } else { - membersVc.contextPut("showWaitingList", Boolean.FALSE); - } - mainPanel.setContent(membersVc); + CollaborationTools collabTools = CollaborationToolsFactory.getInstance().getOrCreateCollaborationTools(this.businessGroup); + boolean canEmail = collabTools.isToolEnabled(CollaborationTools.TOOL_CONTACT); + groupMembersToggleViewController = new GroupMembersRunController(ureq, getWindowControl(), businessGroup, canEmail); + listenTo(groupMembersToggleViewController); + mainPanel.setContent(groupMembersToggleViewController.getInitialComponent()); collabToolCtr = null; addToHistory(ureq, ORES_TOOLMEMBERS, null); } diff --git a/src/main/java/org/olat/group/ui/run/GroupMembersRunController.java b/src/main/java/org/olat/group/ui/run/GroupMembersRunController.java new file mode 100644 index 00000000000..33f35ee9e3f --- /dev/null +++ b/src/main/java/org/olat/group/ui/run/GroupMembersRunController.java @@ -0,0 +1,98 @@ +/** + * <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.group.ui.run; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.olat.basesecurity.GroupRoles; +import org.olat.commons.memberlist.ui.MembersDisplayRunController; +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.Component; +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.id.Identity; +import org.olat.group.BusinessGroup; +import org.olat.group.BusinessGroupService; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Initial Date: 22.03.2017 + * @author fkiefer, fabian.kiefer@frentix.com, www.frentix.com + */ +public class GroupMembersRunController extends BasicController { + + private MembersDisplayRunController membersDisplayRunController; + + @Autowired + private BusinessGroupService businessGroupService; + + public GroupMembersRunController(UserRequest ureq, WindowControl wControl, BusinessGroup businessGroup, boolean canEmail) { + super(ureq, wControl); + + List<Identity> coaches, participants, waiting; + boolean showCoaches = businessGroup.isOwnersVisibleIntern(); + if (showCoaches) { + coaches = businessGroupService.getMembers(businessGroup, GroupRoles.coach.name()); + } else { + coaches = Collections.emptyList(); + } + boolean showParticipants = businessGroup.isParticipantsVisibleIntern(); + if (showParticipants) { + participants = businessGroupService.getMembers(businessGroup, GroupRoles.participant.name()); + } else { + participants = Collections.emptyList(); + } + boolean showWaiting = businessGroup.isWaitingListVisibleIntern(); + if (showWaiting) { + waiting = businessGroupService.getMembers(businessGroup, GroupRoles.waiting.name()); + } else { + waiting = Collections.emptyList(); + } + membersDisplayRunController = new MembersDisplayRunController(ureq, wControl, getTranslator(), null, businessGroup, + new ArrayList<>(), coaches, participants, waiting, canEmail, true, false, showCoaches, showParticipants, showWaiting, true); + listenTo(membersDisplayRunController); + + putInitialPanel(membersDisplayRunController.getInitialComponent()); + } + + + + + @Override + protected void event(UserRequest ureq, Controller source, Event event) { + super.event(ureq, source, event); + } + + @Override + protected void event(UserRequest ureq, Component source, Event event) { + // + } + + @Override + protected void doDispose() { + // + } + + +} diff --git a/src/main/java/org/olat/group/ui/run/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/group/ui/run/_i18n/LocalStrings_de.properties index 7331919d4f6..b0d9f668220 100644 --- a/src/main/java/org/olat/group/ui/run/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/group/ui/run/_i18n/LocalStrings_de.properties @@ -76,3 +76,34 @@ userlist.show.no.owners.text=nicht sichtbar userlist.show.no.participants.text=nicht sichtbar userlist.show.no.waitinglist.text=nicht sichtbar wizard.step1.send_option.member=Gruppenmitglieder und Gruppenbetreuer +contact.all.coaches=Alle Gruppenbetreuer +contact.all.participants=Alle Gruppenteilnehmer +contact.all.waiting=Alle auf Warteliste +contact.attachment=$org.olat.modules.co\:contact.attachment +contact.attachment.maxsize=$org.olat.modules.co\:contact.attachment.maxsize +contact.individual=Einzelne Teilnehmer +contact.external=Externe E-Mail Adresse +email.body.template=<p /><p /><p>Link zur Gruppe <strong>{0}</strong>:<br /><a href\='{1}'>{1}</a></p> +email.from=Von +email.send=Schicken +email.function=Emailfunktion +email.function.all=Emailfunktion f\u00FCr alle Benutzer freischalten +email.function.coachAndAdmin=Emailfunktion nur f\u00FCr Betreuer und Administratoren freischalten +yemail.title=E-Mail senden +members.to=Teilnehmer "{0}" aus Gruppe "{1}" +msg.send.ok=$org.olat.modules.co\:msg.send.ok +error.msg.send.partially.nok=$org.olat.modules.co\:error.msg.send.partially.nok +error.msg.send.invalid.rcps=$org.olat.modules.co\:error.msg.send.invalid.rcps +error.msg.send.nok=$org.olat.modules.co\:error.msg.send.nok +error.msg.send.553=$org.olat.modules.co\:error.msg.send.553 +blank= +members.email.title=E-Mail versenden +members.owners=Gruppenbesitzer +members.coaches=Gruppenbetreuer +members.participants=Gruppenteilnehmer +members.waiting=Warteliste +members.toggle=Portrait +members.download=Herunterladen +select.members=Benutzer ausw\u00E4hlen +contact.external.list.example=xyz@olat.com, abc@frentix.com<br/>user@yourcompany.ch +print.title=Fotobuch diff --git a/src/main/java/org/olat/group/ui/run/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/group/ui/run/_i18n/LocalStrings_en.properties index bb38a432319..ffc4544b716 100644 --- a/src/main/java/org/olat/group/ui/run/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/group/ui/run/_i18n/LocalStrings_en.properties @@ -75,4 +75,35 @@ userlist.show.no.participants.title=Participant userlist.show.no.waitinglist.text=not visible userlist.show.no.waitinglist.title=Waiting list userlist.title=Group members -wizard.step1.send_option.member=Group members and group coaches \ No newline at end of file +wizard.step1.send_option.member=Group members and group coaches +contact.all.coaches=All group coaches +contact.all.participants=All group participants +contact.all.waiting=All on waiting list +contact.attachment=$org.olat.modules.co\:contact.attachment +contact.attachment.maxsize=$org.olat.modules.co\:contact.attachment.maxsize +contact.individual=Selected participants +contact.external=External E-Mail address +email.body.template=<p /><p /><p>Link to group <strong>{0}</strong>:<br /><a href\='{1}'>{1}</a></p> +email.from=From +email.send=Send +email.function=Email function +email.function.all=Enable email for all users +email.function.coachAndAdmin=Enable email for all users for coaches and admins +email.title=Send email +members.to=Members "{0}" of group "{1}" +msg.send.ok=$org.olat.modules.co\:msg.send.ok +error.msg.send.partially.nok=$org.olat.modules.co\:error.msg.send.partially.nok +error.msg.send.invalid.rcps=$org.olat.modules.co\:error.msg.send.invalid.rcps +error.msg.send.nok=$org.olat.modules.co\:error.msg.send.nok +error.msg.send.553=$org.olat.modules.co\:error.msg.send.553 +blank= +members.email.title=Send e-mail +members.owners=Group owners +members.coaches=Group coaches +members.participants=Group participants +members.waiting=Waiting list +members.toggle=Portrait +members.download=Download +select.members=Select members +contact.external.list.example=xyz@olat.com, abc@frentix.com<br/>user@yourcompany.ch +print.title=Face book \ No newline at end of file -- GitLab