From bb1a0bb478a116a97ac4239808731cea7a6690e4 Mon Sep 17 00:00:00 2001 From: srosse <none@none> Date: Mon, 12 Dec 2011 09:18:55 +0100 Subject: [PATCH] FXOLAT-358: merge members BB (from VCRP) --- .../olat/course/nodes/MembersCourseNode.java | 102 ++++ .../MembersCourseNodeConfiguration.java | 72 +++ .../MembersCourseNodeEditController.java | 77 +++ .../MembersCourseNodeRunController.java | 447 ++++++++++++++++++ .../nodes/members/_content/memberList.html | 13 + .../nodes/members/_content/members.html | 16 + .../members/_i18n/LocalStrings_de.properties | 12 + .../members/_i18n/LocalStrings_en.properties | 12 + .../members/_spring/membersCourseContext.xml | 13 + .../resources/serviceconfig/olat.properties | 3 +- .../webapp/static/themes/default/all/olat.css | 9 + 11 files changed, 775 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/olat/course/nodes/MembersCourseNode.java create mode 100644 src/main/java/org/olat/course/nodes/members/MembersCourseNodeConfiguration.java create mode 100644 src/main/java/org/olat/course/nodes/members/MembersCourseNodeEditController.java create mode 100644 src/main/java/org/olat/course/nodes/members/MembersCourseNodeRunController.java create mode 100644 src/main/java/org/olat/course/nodes/members/_content/memberList.html create mode 100644 src/main/java/org/olat/course/nodes/members/_content/members.html create mode 100644 src/main/java/org/olat/course/nodes/members/_i18n/LocalStrings_de.properties create mode 100644 src/main/java/org/olat/course/nodes/members/_i18n/LocalStrings_en.properties create mode 100644 src/main/java/org/olat/course/nodes/members/_spring/membersCourseContext.xml diff --git a/src/main/java/org/olat/course/nodes/MembersCourseNode.java b/src/main/java/org/olat/course/nodes/MembersCourseNode.java new file mode 100644 index 00000000000..a007f0c0aff --- /dev/null +++ b/src/main/java/org/olat/course/nodes/MembersCourseNode.java @@ -0,0 +1,102 @@ +/** + * <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.course.nodes; + +import java.util.List; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.control.Controller; +import org.olat.core.gui.control.WindowControl; +import org.olat.core.gui.control.generic.tabbable.TabbableController; +import org.olat.core.util.Util; +import org.olat.course.ICourse; +import org.olat.course.editor.CourseEditorEnv; +import org.olat.course.editor.NodeEditController; +import org.olat.course.editor.StatusDescription; +import org.olat.course.nodes.info.InfoCourseNodeEditController; +import org.olat.course.nodes.members.MembersCourseNodeEditController; +import org.olat.course.nodes.members.MembersCourseNodeRunController; +import org.olat.course.run.navigation.NodeRunConstructionResult; +import org.olat.course.run.userview.NodeEvaluation; +import org.olat.course.run.userview.UserCourseEnvironment; +import org.olat.repository.RepositoryEntry; + + +/** + * + * Description:<br> + * The course node show all members of the course + * + * <P> + * Initial Date: 11 mars 2011 <br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + */ +public class MembersCourseNode extends AbstractAccessableCourseNode { + + private static final long serialVersionUID = -8404722446386415061L; + + public static final String TYPE = "cmembers"; + + public MembersCourseNode() { + super(TYPE); + updateModuleConfigDefaults(true); + } + + @Override + public RepositoryEntry getReferencedRepositoryEntry() { + return null; + } + + @Override + public boolean needsReferenceToARepositoryEntry() { + return false; + } + + @Override + public StatusDescription isConfigValid() { + return StatusDescription.NOERROR; + } + + @Override + public StatusDescription[] isConfigValid(CourseEditorEnv cev) { + oneClickStatusCache = null; + String translatorStr = Util.getPackageName(InfoCourseNodeEditController.class); + List<StatusDescription> statusDescs =isConfigValidWithTranslator(cev, translatorStr, getConditionExpressions()); + oneClickStatusCache = StatusDescriptionHelper.sort(statusDescs); + return oneClickStatusCache; + } + + @Override + public TabbableController createEditController(UserRequest ureq, WindowControl wControl, ICourse course, UserCourseEnvironment euce) { + MembersCourseNodeEditController childTabCntrllr = new MembersCourseNodeEditController(ureq, wControl, this, course, euce); + CourseNode chosenNode = course.getEditorTreeModel().getCourseNode(euce.getCourseEditorEnv().getCurrentCourseNodeId()); + return new NodeEditController(ureq, wControl, course.getEditorTreeModel(), course, chosenNode, course.getCourseEnvironment() + .getCourseGroupManager(), euce, childTabCntrllr); + } + + @Override + public NodeRunConstructionResult createNodeRunConstructionResult(UserRequest ureq, WindowControl wControl, + UserCourseEnvironment userCourseEnv, NodeEvaluation ne, String nodecmd) { + + MembersCourseNodeRunController infoCtrl = new MembersCourseNodeRunController(ureq, wControl, this, userCourseEnv); + Controller titledCtrl = TitledWrapperHelper.getWrapper(ureq, wControl, infoCtrl, this, "o_cmembers_icon"); + return new NodeRunConstructionResult(titledCtrl); + } +} \ No newline at end of file diff --git a/src/main/java/org/olat/course/nodes/members/MembersCourseNodeConfiguration.java b/src/main/java/org/olat/course/nodes/members/MembersCourseNodeConfiguration.java new file mode 100644 index 00000000000..fc714f815e7 --- /dev/null +++ b/src/main/java/org/olat/course/nodes/members/MembersCourseNodeConfiguration.java @@ -0,0 +1,72 @@ +/** + * <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.course.nodes.members; + +import java.util.Locale; + +import org.olat.core.gui.translator.Translator; +import org.olat.core.util.Util; +import org.olat.course.nodes.AbstractCourseNodeConfiguration; +import org.olat.course.nodes.CourseNode; +import org.olat.course.nodes.CourseNodeConfiguration; +import org.olat.course.nodes.MembersCourseNode; + +/** + * + * Description:<br> + * Node configuration for the course building block. Nothing to do. + * + * <P> + * Initial Date: 11 mars 2011 <br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + */ +public class MembersCourseNodeConfiguration extends AbstractCourseNodeConfiguration implements CourseNodeConfiguration { + + private MembersCourseNodeConfiguration() { + super(); + } + + @Override + public String getAlias() { + return "cmembers"; + } + + @Override + public CourseNode getInstance() { + return new MembersCourseNode(); + } + + @Override + public String getLinkText(Locale locale) { + Translator fallback = Util.createPackageTranslator(CourseNodeConfiguration.class, locale); + Translator translator = Util.createPackageTranslator(MembersCourseNodeConfiguration.class, locale, fallback); + return translator.translate("title_info"); + } + + @Override + public String getIconCSSClass() { + return "o_cmembers_icon"; + } + + @Override + public String getLinkCSSClass() { + return null; + } +} diff --git a/src/main/java/org/olat/course/nodes/members/MembersCourseNodeEditController.java b/src/main/java/org/olat/course/nodes/members/MembersCourseNodeEditController.java new file mode 100644 index 00000000000..1bfab3af79f --- /dev/null +++ b/src/main/java/org/olat/course/nodes/members/MembersCourseNodeEditController.java @@ -0,0 +1,77 @@ +/** + * <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.course.nodes.members; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.Component; +import org.olat.core.gui.components.tabbedpane.TabbedPane; +import org.olat.core.gui.control.ControllerEventListener; +import org.olat.core.gui.control.Event; +import org.olat.core.gui.control.WindowControl; +import org.olat.core.gui.control.generic.tabbable.ActivateableTabbableDefaultController; +import org.olat.course.ICourse; +import org.olat.course.nodes.MembersCourseNode; +import org.olat.course.run.userview.UserCourseEnvironment; + +/** + * + * Description:<br> + * Edit panel for the members course building block. Nothing to do. + * + * <P> + * Initial Date: 11 mars 2011 <br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + */ +public class MembersCourseNodeEditController extends ActivateableTabbableDefaultController implements ControllerEventListener { + private static final String[] paneKeys = {}; + + private TabbedPane myTabbedPane; + + public MembersCourseNodeEditController(UserRequest ureq, WindowControl wControl, MembersCourseNode courseNode, ICourse course, + UserCourseEnvironment euce) { + super(ureq,wControl); + + } + + @Override + protected void doDispose() { + // + } + + @Override + public String[] getPaneKeys() { + return paneKeys; + } + + @Override + public TabbedPane getTabbedPane() { + return myTabbedPane; + } + + @Override + public void addTabs(TabbedPane tabbedPane) { + myTabbedPane = tabbedPane; + } + + @Override + protected void event(UserRequest ureq, Component source, Event event) { + // + } +} diff --git a/src/main/java/org/olat/course/nodes/members/MembersCourseNodeRunController.java b/src/main/java/org/olat/course/nodes/members/MembersCourseNodeRunController.java new file mode 100644 index 00000000000..3e0e58dc4ce --- /dev/null +++ b/src/main/java/org/olat/course/nodes/members/MembersCourseNodeRunController.java @@ -0,0 +1,447 @@ +/** + * <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.course.nodes.members; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; + +import javax.servlet.http.HttpServletRequest; + +import org.olat.NewControllerFactory; +import org.olat.basesecurity.BaseSecurity; +import org.olat.basesecurity.BaseSecurityManager; +import org.olat.core.dispatcher.mapper.Mapper; +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.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.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.media.MediaResource; +import org.olat.core.id.Identity; +import org.olat.core.id.User; +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.course.CourseFactory; +import org.olat.course.ICourse; +import org.olat.course.groupsandrights.CourseGroupManager; +import org.olat.course.nodes.CourseNode; +import org.olat.course.run.userview.UserCourseEnvironment; +import org.olat.modules.co.ContactFormController; +import org.olat.repository.RepositoryEntry; +import org.olat.repository.RepositoryManager; +import org.olat.user.DisplayPortraitManager; + +/** + * + * 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 + */ +public class MembersCourseNodeRunController extends FormBasicController { + + private final RepositoryManager rm ; + private final BaseSecurity securityManager; + private final UserCourseEnvironment userCourseEnv; + private final DisplayPortraitManager portraitManager; + private final String avatarBaseURL; + + private FormLink ownersEmailLink; + private FormLink coachesEmailLink; + private FormLink participantsEmailLink; + private List<FormLink> memberLinks = new ArrayList<FormLink>(); + private List<FormLink> emailLinks = new ArrayList<FormLink>(); + + private List<FormLink> ownerLinks; + private List<FormLink> coachesLinks; + private List<FormLink> participantsLinks; + + private ContactFormController emailController; + private CloseableModalController cmc; + + public MembersCourseNodeRunController(UserRequest ureq, WindowControl wControl, CourseNode courseNode, UserCourseEnvironment userCourseEnv) { + super(ureq, wControl, "members"); + + avatarBaseURL = registerCacheableMapper("avatars-members", new AvatarMapper()); + + rm = RepositoryManager.getInstance(); + securityManager = BaseSecurityManager.getInstance(); + this.userCourseEnv = userCourseEnv; + portraitManager = DisplayPortraitManager.getInstance(); + + initForm(ureq); + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + + CourseGroupManager cgm = userCourseEnv.getCourseEnvironment().getCourseGroupManager(); + Long courseResId = userCourseEnv.getCourseEnvironment().getCourseResourceableId(); + ICourse course = CourseFactory.loadCourse(courseResId); + RepositoryEntry courseRepositoryEntry = rm.lookupRepositoryEntry(course, true); + List<Identity> owners = securityManager.getIdentitiesOfSecurityGroup(courseRepositoryEntry.getOwnerGroup()); + List<Identity> coaches = cgm.getCoachesFromLearningGroup(null); + //fxdiff VCRP-1,2: access control of resources + coaches.addAll(cgm.getCoaches()); + List<Identity> participants = cgm.getParticipantsFromLearningGroup(null); + participants.addAll(cgm.getParticipants()); + Comparator<Identity> idComparator = new IdentityComparator(); + Collections.sort(owners, idComparator); + Collections.sort(coaches, idComparator); + Collections.sort(participants, idComparator); + + boolean canEmail = canEmail(owners, coaches); + if(canEmail) { + ownersEmailLink = uifactory.addFormLink("owners-email", "members.email", null, formLayout, Link.LINK); + ownersEmailLink.setCustomEnabledLinkCSS("b_small_icon o_cmembers_mail"); + coachesEmailLink = uifactory.addFormLink("coaches-email", "members.email", null, formLayout, Link.LINK); + coachesEmailLink.setCustomEnabledLinkCSS("b_small_icon o_cmembers_mail"); + participantsEmailLink = uifactory.addFormLink("participants-email", "members.email", null, formLayout, Link.LINK); + participantsEmailLink.setCustomEnabledLinkCSS("b_small_icon o_cmembers_mail"); + + formLayout.add("owners-email", ownersEmailLink); + formLayout.add("coaches-email", coachesEmailLink); + formLayout.add("participants-email", participantsEmailLink); + } + + Set<Long> duplicateCatcher = new HashSet<Long>(); + ownerLinks = initFormMemberList("owners", owners, duplicateCatcher, formLayout, canEmail); + coachesLinks = initFormMemberList("coaches", coaches, duplicateCatcher, formLayout, canEmail); + participantsLinks = initFormMemberList("participants", participants, duplicateCatcher, formLayout, canEmail); + + if(formLayout instanceof FormLayoutContainer) { + FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout; + layoutCont.contextPut("hasOwners", new Boolean(!ownerLinks.isEmpty())); + layoutCont.contextPut("hasCoaches", new Boolean(!coachesLinks.isEmpty())); + layoutCont.contextPut("hasParticipants", new Boolean(!participantsLinks.isEmpty())); + } + } + + private boolean canEmail(List<Identity> owners, List<Identity> coaches) { + for(Identity owner:owners) { + if(owner.equalsByPersistableKey(getIdentity())) { + return true; + } + } + + for(Identity coach:coaches) { + if(coach.equalsByPersistableKey(getIdentity())) { + return true; + } + } + return false; + } + + private List<FormLink> 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<FormLink> links = createMemberLinks(ids, duplicateCatcher, container, withEmail); + container.contextPut("memberLinks", links); + container.contextPut("avatarBaseURL", avatarBaseURL); + return links; + } + + protected List<FormLink> createMemberLinks(List<Identity> identities, Set<Long> duplicateCatcher, FormLayoutContainer formLayout, boolean withEmail) { + List<FormLink> idLinks = new ArrayList<FormLink>(); + for(Identity identity:identities) { + if(duplicateCatcher.contains(identity.getKey())) continue; + + Member member = createMember(identity); + FormLink idLink = uifactory.addFormLink("id_" + identity.getKey(), member.getFullName(), null, formLayout, Link.NONTRANSLATED); + idLink.setUserObject(member); + idLinks.add(idLink); + formLayout.add(idLink.getComponent().getComponentName(), idLink); + memberLinks.add(idLink); + + if(withEmail) { + FormLink emailLink = uifactory.addFormLink("mail_" + identity.getKey(), member.getFullName(), null, formLayout, Link.NONTRANSLATED); + emailLink.setUserObject(member); + emailLink.setCustomEnabledLinkCSS("b_small_icon o_cmembers_mail"); + formLayout.add(emailLink.getComponent().getComponentName(), emailLink); + emailLinks.add(emailLink); + member.setEmailLink(emailLink); + } + duplicateCatcher.add(identity.getKey()); + } + return idLinks; + } + + protected Member createMember(Identity identity) { + User user = identity.getUser(); + String firstname = user.getProperty(UserConstants.FIRSTNAME, null); + String lastname = user.getProperty(UserConstants.LASTNAME, null); + MediaResource rsrc = portraitManager.getPortrait(identity, DisplayPortraitManager.PORTRAIT_SMALL_FILENAME); + + String portraitCssClass = null; + String gender = identity.getUser().getProperty(UserConstants.GENDER, Locale.ENGLISH); + if (gender.equalsIgnoreCase("male")) { + portraitCssClass = DisplayPortraitManager.DUMMY_MALE_SMALL_CSS_CLASS; + } else if (gender.equalsIgnoreCase("female")) { + portraitCssClass = DisplayPortraitManager.DUMMY_FEMALE_SMALL_CSS_CLASS; + } else { + portraitCssClass = DisplayPortraitManager.DUMMY_SMALL_CSS_CLASS; + } + + Member member = new Member(identity.getKey(), identity, firstname, lastname, rsrc != null, portraitCssClass); + return member; + } + + @Override + protected void doDispose() { + // + } + + @Override + protected void formOK(UserRequest ureq) { + // + } + + @Override + protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { + if(memberLinks.contains(source)) { + FormLink memberLink = (FormLink)source; + Member member = (Member)memberLink.getUserObject(); + openHomePage(member.getIdentity(), ureq); + } else if (emailLinks.contains(source)) { + FormLink emailLink = (FormLink)source; + Member member = (Member)emailLink.getUserObject(); + ContactList memberList = new ContactList(translate("members.to", new String[]{member.getFullName(), this.userCourseEnv.getCourseEnvironment().getCourseTitle()})); + memberList.add(member.getIdentity()); + sendEmailToMember(memberList, ureq); + } else if (source == coachesEmailLink) { + ContactList coachList = new ContactList(translate("coaches.to", new String[]{this.userCourseEnv.getCourseEnvironment().getCourseTitle()})); + for(FormLink coachLink:coachesLinks) { + Member member = (Member)coachLink.getUserObject(); + coachList.add(member.getIdentity()); + } + sendEmailToMember(coachList, ureq); + } else if (source == ownersEmailLink) { + ContactList ownerList = new ContactList(translate("owners.to", new String[]{this.userCourseEnv.getCourseEnvironment().getCourseTitle()})); + for(FormLink ownerLink:ownerLinks) { + Member member = (Member)ownerLink.getUserObject(); + ownerList.add(member.getIdentity()); + } + sendEmailToMember(ownerList, ureq); + } else if (source == participantsEmailLink) { + ContactList participantList = new ContactList(translate("participants.to", new String[]{this.userCourseEnv.getCourseEnvironment().getCourseTitle()})); + for(FormLink participantLink:participantsLinks) { + Member member = (Member)participantLink.getUserObject(); + participantList.add(member.getIdentity()); + } + sendEmailToMember(participantList, ureq); + } + } + + @Override + protected void event(UserRequest ureq, Controller source, Event event) { + if(source == cmc) { + removeAsListenerAndDispose(emailController); + removeAsListenerAndDispose(cmc); + emailController = null; + cmc = null; + } else if (source == emailController) { + cmc.deactivate(); + removeAsListenerAndDispose(emailController); + removeAsListenerAndDispose(cmc); + emailController = null; + cmc = null; + } + super.event(ureq, source, event); + } + + protected void sendEmailToMember(ContactList contactList, UserRequest ureq) { + if (contactList.getEmailsAsStrings().size() > 0) { + removeAsListenerAndDispose(emailController); + + ContactMessage cmsg = new ContactMessage(ureq.getIdentity()); + cmsg.addEmailTo(contactList); + + emailController = new ContactFormController(ureq, getWindowControl(), false, true, false, false, cmsg); + listenTo(emailController); + + removeAsListenerAndDispose(cmc); + String title = translate("members.email.title"); + cmc = new CloseableModalController(getWindowControl(), translate("close"), emailController.getInitialComponent(), true, title); + listenTo(cmc); + + cmc.activate(); + } + } + + protected void openHomePage(Identity member, UserRequest ureq) { + String url = "[Identity:" + member.getKey() + "]"; + BusinessControl bc = BusinessControlFactory.getInstance().createFromString(url); + WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(bc, getWindowControl()); + NewControllerFactory.getInstance().launch(ureq, bwControl); + } + + public class AvatarMapper implements Mapper { + + @Override + public MediaResource handle(String relPath, HttpServletRequest request) { + if(relPath != null && relPath.endsWith("/portrait_small.jpg")) { + if(relPath.startsWith("/")) { + relPath = relPath.substring(1, relPath.length()); + } + + int endKeyIndex = relPath.indexOf('/'); + if(endKeyIndex > 0) { + String idKey = relPath.substring(0, endKeyIndex); + Long key = Long.parseLong(idKey); + for(FormLink memberLink:memberLinks) { + Member m = (Member)memberLink.getUserObject(); + if(m.getIdentity().getKey().equals(key)) { + return portraitManager.getPortrait(m.getIdentity(), DisplayPortraitManager.PORTRAIT_SMALL_FILENAME); + } + } + } + } + return null; + } + } + + public class Member { + private final String firstName; + private final String lastName; + private final Long key; + private Identity identity; + private boolean portrait; + private String portraitCssClass; + private FormLink emailLink; + + public Member(Long key, Identity identity, String firstName, String lastName, boolean portrait, String portraitCssClass) { + this.firstName = firstName; + this.lastName = lastName; + this.identity = identity; + this.key = key; + this.portrait = portrait; + this.portraitCssClass = portraitCssClass; + } + + public String getFirstName() { + return firstName; + } + + public String getLastName() { + return lastName; + } + + public String getPortraitCssClass() { + return portraitCssClass; + } + + public Identity getIdentity() { + return identity; + } + + public boolean isPortraitAvailable() { + return portrait; + } + + public FormLink getEmailLink() { + return emailLink; + } + + public void setEmailLink(FormLink emailLink) { + this.emailLink = emailLink; + } + + public String getFullName() { + StringBuilder sb = new StringBuilder(); + if(StringHelper.containsNonWhitespace(lastName)) { + sb.append(lastName); + } + if(StringHelper.containsNonWhitespace(firstName)) { + if(sb.length() > 0) { + sb.append(' '); + } + sb.append(firstName); + } + return sb.toString(); + } + + public Long getKey() { + return key; + } + + @Override + public int hashCode() { + return key.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if(this == obj) { + return true; + } + if(obj instanceof Member) { + Member member = (Member)obj; + return key != null && key.equals(member.key); + } + return false; + } + } + + public 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/course/nodes/members/_content/memberList.html b/src/main/java/org/olat/course/nodes/members/_content/memberList.html new file mode 100644 index 00000000000..dcccc800190 --- /dev/null +++ b/src/main/java/org/olat/course/nodes/members/_content/memberList.html @@ -0,0 +1,13 @@ +#foreach($memberLink in $memberLinks) + <div class="o_cmember"> + #if($memberLink.getUserObject().isPortraitAvailable()) + <img class="o_cmember_portrait" src="$avatarBaseURL/$memberLink.getUserObject().getKey()/portrait_small.jpg" width="50"/> + #else + <img class="o_cmember_portrait $memberLink.getUserObject().getPortraitCssClass()" src="$r.staticLink("images/transparent.gif")" alt="user portrait" width="50" height="50" /> + #end + $r.render($memberLink.getComponent().getComponentName()) + #if($r.available($memberLink.getUserObject().getEmailLink().getComponent().getComponentName())) + $r.render($memberLink.getUserObject().getEmailLink().getComponent().getComponentName()) + #end + </div> +#end diff --git a/src/main/java/org/olat/course/nodes/members/_content/members.html b/src/main/java/org/olat/course/nodes/members/_content/members.html new file mode 100644 index 00000000000..0e2225c205f --- /dev/null +++ b/src/main/java/org/olat/course/nodes/members/_content/members.html @@ -0,0 +1,16 @@ +<div class="o_cmembers"> + #if($hasOwners) + <h4>$r.translate("members.owners") #if($r.available("owners-email")) $r.render("owners-email") #end</h4> + $r.render("owners") + #end + #if($hasCoaches) + <h4>$r.translate("members.coaches") #if($hasCoaches) $r.render("coaches-email") #end</h4> + $r.render("coaches") + #end + <h4>$r.translate("members.participants") #if($hasParticipants && $r.available("participants-email")) $r.render("participants-email") #end</h4> + #if($hasParticipants) + $r.render("participants") + #else + $r.translate("members.noParticipants.message") + #end +</div> 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 new file mode 100644 index 00000000000..1e48ced3616 --- /dev/null +++ b/src/main/java/org/olat/course/nodes/members/_i18n/LocalStrings_de.properties @@ -0,0 +1,12 @@ +#Mon Mar 02 09:54:04 CET 2009 +title_info=Teilnehmerliste +pane.tab.accessibility=Zugang +members.owners=Kursadministrator +members.coaches=Betreuer +members.participants=Teilnehmer +members.noParticipants.message=Diesem Kurs sind keine Lerngruppen mit Teilnehmern zugeordnet. Erstellen Sie eine Lerngruppe im Gruppenwerkzeug und fügen Sie dort Teilnehmer hinzu. +members.email.title=E-Mail versenden +members.to=Teilnehmer "{0}" von Kurs "{1}" +owners.to=Aministratoren von Kurs "{0}" +coaches.to=Betreuer von Kurs "{0}" +participants.to=Teilnehmer von Kurs "{0}" \ 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 new file mode 100644 index 00000000000..49fa915987d --- /dev/null +++ b/src/main/java/org/olat/course/nodes/members/_i18n/LocalStrings_en.properties @@ -0,0 +1,12 @@ +#Mon May 16 17:20:06 CEST 2011 +members.coaches=Coach +members.email.title=Send e-mail +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.to="{0}" of course "{1}" +owners.to=administrators of course "{0}" +coaches.to=coaches of course "{0}" +participants.to=participants of course "{0}" +pane.tab.accessibility=Access +title_info=Participant list diff --git a/src/main/java/org/olat/course/nodes/members/_spring/membersCourseContext.xml b/src/main/java/org/olat/course/nodes/members/_spring/membersCourseContext.xml new file mode 100644 index 00000000000..496ec57edba --- /dev/null +++ b/src/main/java/org/olat/course/nodes/members/_spring/membersCourseContext.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation=" + http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> + + <bean id="membersCourseNodeConf" class="org.olat.course.nodes.members.MembersCourseNodeConfiguration" scope="prototype"> + <property name="enabled" value="${course.node.members.enabled}" /> + <property name="order" value="190" /> + </bean> + +</beans> \ No newline at end of file diff --git a/src/main/resources/serviceconfig/olat.properties b/src/main/resources/serviceconfig/olat.properties index b88ad72003f..ef42f087ed2 100644 --- a/src/main/resources/serviceconfig/olat.properties +++ b/src/main/resources/serviceconfig/olat.properties @@ -634,12 +634,13 @@ method.free.enabled=true # Course building blocks, every course building block can be disabled by adding a property here and reference it in # appropriate spring config file (by default are course bb are enabled) ######################################## -course.node.linklist.enabled=false +course.node.linklist.enabled=true course.node.checklist.enabled=false course.node.dateenrollment.enabled=false course.node.basiclti.enabled=true course.node.portfolio.enabled=true course.node.infomessage.enabled=true +course.node.members.enabled=true course.node.vc.enabled=false course.node.vitero.enabled=false diff --git a/src/main/webapp/static/themes/default/all/olat.css b/src/main/webapp/static/themes/default/all/olat.css index 9befeffcaba..fe2b6375ec1 100644 --- a/src/main/webapp/static/themes/default/all/olat.css +++ b/src/main/webapp/static/themes/default/all/olat.css @@ -614,6 +614,15 @@ s .o_infomsg_icon { background-image: url(../images/olat/infomessage.png); } .o_infomsg_create_button { position:absolute; top:0; right:250px; } + /* MEMBERS BB */ + div.o_cmembers {} + div.o_cmembers * { vertical-align:middle; } + div.o_cmembers div.o_cmember { float:left; width:30%; margin: 5px 5px 5px 0; padding:5px; background-color:#EBEBEB; border:1px solid #ddd; border-radius:5px; -moz-border-radius:5px; -webkit-border-radius:5px; -webkit-box-shadow: 0 1px 2px #d3d3d3; -moz-box-shadow: 0 1px 2px #d3d3d3; -o-box-shadow: 0 1px 2px #d3d3d3; box-shadow: 0 1px 2px #d3d3d3; } + div.o_cmembers div.o_cmember img.o_cmember_portrait { margin-right:5px; border:1px solid #ddd; background-color:white; background-position:50% 50%; background-repeat:no-repeat; } + div.o_cmembers a.o_cmembers_mail { float:none; margin-left:5px; padding-left:20px; background-image:url(../images/olat/email.png); } + div.o_cmembers a.o_cmembers_mail span { display:none; } + div.o_cmembers h4 { padding: 7px 0 0 0; clear:both;} + /* LINK LIST */ div.o_ll_container ul li { list-style: none; margin: 1em; } div.o_ll_container ul li div { font-style: italic;} -- GitLab