From 40a802e74de433b7fc372ee81a804b89c7e7e820 Mon Sep 17 00:00:00 2001 From: fkiefer <none@none> Date: Tue, 21 Mar 2017 11:14:35 +0100 Subject: [PATCH] OO-2621 Notification messages in groups, refactor group information tool --- .../collaboration/CollaborationTools.java | 45 +++ .../CollaborationToolsSettingsController.java | 8 +- .../collaboration/NewsFormController.java | 70 ++-- .../_i18n/LocalStrings_de.properties | 1 + .../_i18n/LocalStrings_en.properties | 1 + .../manager/InfoMessageFrontendManager.java | 9 +- .../InfoMessageFrontendManagerImpl.java | 38 +- .../info/manager/InfoMessageManager.java | 4 + .../info/manager/InfoMessageManagerImpl.java | 49 ++- .../commons/info/model/InfoMessageImpl.java | 85 ++++- .../InfoMessageNotificationHandler.java | 26 +- .../notification/InfoSubscriptionManager.java | 3 + .../InfoSubscriptionManagerImpl.java | 5 + .../_i18n/LocalStrings_de.properties | 1 + .../_i18n/LocalStrings_en.properties | 1 + .../info/ui/InfoDisplayController.java | 19 + .../info/ui/SendInfoMailFormatter.java} | 13 +- .../course/nodes/info/InfoRunController.java | 3 +- .../manager/BusinessGroupServiceImpl.java | 11 +- .../run/BusinessGroupMainRunController.java | 2 +- .../group/ui/run/InfoGroupRunController.java | 214 +++++++++++ .../ui/run/SendGroupMembersMailOption.java | 64 ++++ .../org/olat/group/ui/run/_content/run.html | 6 + .../ui/run/_i18n/LocalStrings_de.properties | 4 + .../ui/run/_i18n/LocalStrings_en.properties | 4 + .../org/olat/search/_spring/searchContext.xml | 4 + .../indexer/group/GroupInfoIndexer.java | 99 +++++ .../org/olat/upgrade/OLATUpgrade_11_4_0.java | 161 ++++++++ .../olat/upgrade/_spring/upgradeContext.xml | 1 + src/main/resources/META-INF/persistence.xml | 2 +- .../olat/commons/info/InfoManagerTest.java | 87 +++++ .../info/InfoMessageFrontendManagerTest.java | 344 ++++++++++++++++++ 32 files changed, 1306 insertions(+), 78 deletions(-) rename src/main/java/org/olat/{course/nodes/info/SendMailFormatterForCourse.java => commons/info/ui/SendInfoMailFormatter.java} (90%) create mode 100644 src/main/java/org/olat/group/ui/run/InfoGroupRunController.java create mode 100644 src/main/java/org/olat/group/ui/run/SendGroupMembersMailOption.java create mode 100644 src/main/java/org/olat/group/ui/run/_content/run.html create mode 100644 src/main/java/org/olat/search/service/indexer/group/GroupInfoIndexer.java create mode 100644 src/main/java/org/olat/upgrade/OLATUpgrade_11_4_0.java create mode 100644 src/test/java/org/olat/commons/info/InfoMessageFrontendManagerTest.java diff --git a/src/main/java/org/olat/collaboration/CollaborationTools.java b/src/main/java/org/olat/collaboration/CollaborationTools.java index 373ca49f731..be000968b50 100644 --- a/src/main/java/org/olat/collaboration/CollaborationTools.java +++ b/src/main/java/org/olat/collaboration/CollaborationTools.java @@ -82,6 +82,7 @@ import org.olat.course.ICourse; import org.olat.course.run.calendar.CourseLinkProviderController; import org.olat.group.BusinessGroup; import org.olat.group.BusinessGroupService; +import org.olat.group.ui.run.InfoGroupRunController; import org.olat.instantMessaging.InstantMessagingModule; import org.olat.instantMessaging.ui.ChatToolController; import org.olat.modules.co.ContactFormController; @@ -221,6 +222,7 @@ public class CollaborationTools implements Serializable { * cache for Boolean Objects representing the State */ private final static String KEY_NEWS = "news"; + private final static String KEY_NEWS_ACCESS = "newsAccess"; public final static String KEY_CALENDAR_ACCESS = "cal"; public final static String KEY_FOLDER_ACCESS = "folder"; @@ -250,6 +252,12 @@ public class CollaborationTools implements Serializable { String news = lookupNews(); return new SimpleNewsController(ureq, wControl, news); } + + public Controller createInfoMessageController(UserRequest ureq, WindowControl wControl) { + String canAdmin = getNewsAccessProperty(); + boolean canAccess = "all".equals(canAdmin); + return new InfoGroupRunController(ureq, wControl, ores, canAccess); + } /** * TODO: rename to getForumController and save instance? @@ -787,6 +795,35 @@ public class CollaborationTools implements Serializable { return new CollaborationToolsSettingsController(ureq, wControl, ores); } + /** + * @return Gets the news access property + */ + public String getNewsAccessProperty() { + NarrowedPropertyManager npm = NarrowedPropertyManager.getInstance(ores); + Property property = npm.findProperty(null, null, PROP_CAT_BG_COLLABTOOLS, KEY_NEWS_ACCESS); + if (property == null) { // no entry + return null; + } + // read the text value of the existing property + String text = property.getStringValue(); + return text; + } + + /** + * @param Save news access property. + */ + public void saveNewsAccessProperty(String access) { + NarrowedPropertyManager npm = NarrowedPropertyManager.getInstance(ores); + Property property = npm.findProperty(null, null, PROP_CAT_BG_COLLABTOOLS, KEY_NEWS_ACCESS); + if (property == null) { // create a new one + Property nP = npm.createPropertyInstance(null, null, PROP_CAT_BG_COLLABTOOLS, KEY_NEWS_ACCESS, null, null, access, null); + npm.saveProperty(nP); + } else { // modify the existing one + property.setStringValue(access); + npm.updateProperty(property); + } + } + /** * @return the news; if there is no news yet: return null; */ @@ -800,6 +837,14 @@ public class CollaborationTools implements Serializable { String text = property.getTextValue(); return text; } + + public Property lookupNewsDBEntry() { + NarrowedPropertyManager npm = NarrowedPropertyManager.getInstance(ores); + Property property = npm.findProperty(null, null, PROP_CAT_BG_COLLABTOOLS, KEY_NEWS); + if (property == null) { // no entry + return null; + } else return property; + } /** * @param news diff --git a/src/main/java/org/olat/collaboration/CollaborationToolsSettingsController.java b/src/main/java/org/olat/collaboration/CollaborationToolsSettingsController.java index bfb17ccd5b2..16785b2389a 100644 --- a/src/main/java/org/olat/collaboration/CollaborationToolsSettingsController.java +++ b/src/main/java/org/olat/collaboration/CollaborationToolsSettingsController.java @@ -157,12 +157,12 @@ public class CollaborationToolsSettingsController extends BasicController { private void addNewsTool(UserRequest ureq) { CollaborationTools collabTools = CollaborationToolsFactory.getInstance().getOrCreateCollaborationTools(businessGroup); - String newsValue = collabTools.lookupNews(); + String access = collabTools.getNewsAccessProperty(); if (newsController != null) { removeAsListenerAndDispose(newsController); } - newsController = new NewsFormController(ureq, getWindowControl(), (newsValue == null ? "" : newsValue)); + newsController = new NewsFormController(ureq, getWindowControl(), (access == null ? "" : access)); newsController.setEnabled(!managed); listenTo(newsController); @@ -244,8 +244,8 @@ public class CollaborationToolsSettingsController extends BasicController { } } else if (source == newsController) { if (event.equals(Event.DONE_EVENT)) { - String news = newsController.getNewsValue(); - collabTools.saveNews(news); + String access = newsController.getAccessPropertyValue(); + collabTools.saveNewsAccessProperty(access); } } else if (source == calendarForm) { diff --git a/src/main/java/org/olat/collaboration/NewsFormController.java b/src/main/java/org/olat/collaboration/NewsFormController.java index 19ea2f48457..c0bb6f254bb 100644 --- a/src/main/java/org/olat/collaboration/NewsFormController.java +++ b/src/main/java/org/olat/collaboration/NewsFormController.java @@ -22,13 +22,12 @@ package org.olat.collaboration; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.form.flexible.FormItemContainer; -import org.olat.core.gui.components.form.flexible.elements.RichTextElement; +import org.olat.core.gui.components.form.flexible.elements.SingleSelection; import org.olat.core.gui.components.form.flexible.impl.FormBasicController; import org.olat.core.gui.components.form.flexible.impl.elements.FormSubmit; 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.util.StringHelper; /** * Provides a controller for entering an information text for group members @@ -40,26 +39,19 @@ import org.olat.core.util.StringHelper; public class NewsFormController extends FormBasicController { private FormSubmit submit; - /** - * The rich text element for the information text. - */ - private RichTextElement newsInputElement; - - /** - * The information text. - */ - private String news; + private SingleSelection newsAccessEl; + private String access; /** * Initializes this controller. * * @param ureq The user request. * @param wControl The window control. - * @param news The information text. + * @param access The information text. */ - public NewsFormController(UserRequest ureq, WindowControl wControl, String news) { + public NewsFormController(UserRequest ureq, WindowControl wControl, String access) { super(ureq, wControl); - this.news = news; + this.access = access; initForm(ureq); } @@ -95,9 +87,26 @@ public class NewsFormController extends FormBasicController { protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { setFormTitle("news.content"); formLayout.setElementCssClass("o_sel_collaboration_news"); - newsInputElement = uifactory.addRichTextElementForStringData("news.content", "news.content", this.news, 10, -1, false, null, - null, formLayout, ureq.getUserSession(), getWindowControl()); - newsInputElement.setMandatory(true); + + + String[] keys = new String[] { "owner", "all" }; + String values[] = new String[] { + // can use folder's translation keys + translate("folder.access.owners"), + translate("folder.access.all") + }; + newsAccessEl = uifactory.addRadiosVertical("news.access", "news.access", formLayout, keys, values); + + switch (access) { + case "all": + newsAccessEl.select(access, true); + break; + case "owner": + newsAccessEl.select(access, true); + break; + default: + newsAccessEl.select("owner", true); + } // Create submit button submit = uifactory.addFormSubmitButton("submit", formLayout); @@ -105,18 +114,19 @@ public class NewsFormController extends FormBasicController { } public void setEnabled(boolean enabled) { - newsInputElement.setEnabled(enabled); submit.setVisible(enabled); } + /** - * Returns the information text. - * - * @return The information text. + * Returns the access property value. + * + * @return the access property value */ - public String getNewsValue() { - return newsInputElement.getValue(); + public String getAccessPropertyValue() { + return newsAccessEl.getSelectedKey(); } + /** * @see org.olat.core.gui.components.form.flexible.impl.FormBasicController#validateFormLogic(org.olat.core.gui.UserRequest) @@ -124,19 +134,9 @@ public class NewsFormController extends FormBasicController { @Override protected boolean validateFormLogic(UserRequest ureq) { boolean newsOk = true; - // This field is mandatory, so check whether there's something in it. - String newsText = newsInputElement.getValue(); - if (!StringHelper.containsNonWhitespace(newsText) || newsText.length() > 4000) { - newsOk = false; - if(newsText.length() > 4000) { - newsInputElement.setErrorKey("input.toolong", new String[] {"4000"}); - } else { - newsInputElement.setErrorKey("form.legende.mandatory", new String[] {}); - } - } else { - newsInputElement.clearError(); + if (!newsAccessEl.isOneSelected()) { + newsOk &= false; } - return newsOk && super.validateFormLogic(ureq); } } diff --git a/src/main/java/org/olat/collaboration/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/collaboration/_i18n/LocalStrings_de.properties index da7b8ba75af..6e5aa5d73c2 100644 --- a/src/main/java/org/olat/collaboration/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/collaboration/_i18n/LocalStrings_de.properties @@ -17,5 +17,6 @@ folder.access=Ordner Schreibberechtigung folder.access.all=Alle Mitglieder folder.access.owners=Besitzer bzw. Betreuer folder.access.title=Ordner Schreibberechtigung konfigurieren +news.access=Informationen Schreibberechtigung news.content=Information an Mitglieder selection=Auswahl diff --git a/src/main/java/org/olat/collaboration/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/collaboration/_i18n/LocalStrings_en.properties index 2adaa265c3c..f824cb235b4 100644 --- a/src/main/java/org/olat/collaboration/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/collaboration/_i18n/LocalStrings_en.properties @@ -17,5 +17,6 @@ folder.access=Folder write permission folder.access.all=All members folder.access.owners=Coaches folder.access.title=Configure folder write permission +news.access=News write permission news.content=Information for members selection=Selection diff --git a/src/main/java/org/olat/commons/info/manager/InfoMessageFrontendManager.java b/src/main/java/org/olat/commons/info/manager/InfoMessageFrontendManager.java index 125f4383b58..99fda9dbdff 100644 --- a/src/main/java/org/olat/commons/info/manager/InfoMessageFrontendManager.java +++ b/src/main/java/org/olat/commons/info/manager/InfoMessageFrontendManager.java @@ -28,6 +28,7 @@ import org.olat.commons.info.model.InfoMessage; import org.olat.core.id.Identity; import org.olat.core.id.OLATResourceable; import org.olat.core.util.resource.OresHelper; +import org.olat.group.BusinessGroup; /** * @@ -41,7 +42,7 @@ public interface InfoMessageFrontendManager { public static final OLATResourceable oresFrontend = OresHelper.lookupType(InfoMessageFrontendManager.class); - + public static final String businessGroupResSubPath = "";// may not be null public InfoMessage loadInfoMessage(Long key); @@ -57,8 +58,14 @@ public interface InfoMessageFrontendManager { */ public boolean sendInfoMessage(InfoMessage msgs, MailFormatter mailFormatter, Locale locale, Identity from, List<Identity> tos); + public void saveInfoMessage(InfoMessage msg); + public void deleteInfoMessage(InfoMessage infoMessage); + public void deleteInfoMessagesOfIdentity(BusinessGroup businessGroup, Identity identity); + + public void removeInfoMessagesAndSubscriptionContext(BusinessGroup group); + public List<InfoMessage> loadInfoMessageByResource(OLATResourceable ores, String subPath, String businessPath, Date after, Date before, int firstResult, int maxReturn); diff --git a/src/main/java/org/olat/commons/info/manager/InfoMessageFrontendManagerImpl.java b/src/main/java/org/olat/commons/info/manager/InfoMessageFrontendManagerImpl.java index d8b89a66614..57aab19ea57 100644 --- a/src/main/java/org/olat/commons/info/manager/InfoMessageFrontendManagerImpl.java +++ b/src/main/java/org/olat/commons/info/manager/InfoMessageFrontendManagerImpl.java @@ -28,9 +28,11 @@ import java.util.Set; import org.olat.commons.info.model.InfoMessage; import org.olat.commons.info.notification.InfoSubscriptionManager; +import org.olat.core.commons.services.notifications.SubscriptionContext; import org.olat.core.id.Identity; import org.olat.core.id.OLATResourceable; -import org.olat.core.manager.BasicManager; +import org.olat.core.logging.OLog; +import org.olat.core.logging.Tracing; import org.olat.core.util.StringHelper; import org.olat.core.util.coordinate.CoordinatorManager; import org.olat.core.util.event.MultiUserEvent; @@ -40,6 +42,7 @@ import org.olat.core.util.mail.MailContext; import org.olat.core.util.mail.MailContextImpl; import org.olat.core.util.mail.MailManager; import org.olat.core.util.mail.MailerResult; +import org.olat.group.BusinessGroup; /** * @@ -49,8 +52,10 @@ import org.olat.core.util.mail.MailerResult; * Initial Date: 28 juil. 2010 <br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com */ -public class InfoMessageFrontendManagerImpl extends BasicManager implements InfoMessageFrontendManager { +public class InfoMessageFrontendManagerImpl implements InfoMessageFrontendManager { + private static final OLog log = Tracing.createLoggerFor(InfoMessageFrontendManagerImpl.class); + private MailManager mailManager; private CoordinatorManager coordinatorManager; private InfoMessageManager infoMessageManager; @@ -97,6 +102,11 @@ public class InfoMessageFrontendManagerImpl extends BasicManager implements Info public InfoMessage createInfoMessage(OLATResourceable ores, String subPath, String businessPath, Identity author) { return infoMessageManager.createInfoMessage(ores, subPath, businessPath, author); } + + @Override + public void saveInfoMessage(InfoMessage infoMessage) { + infoMessageManager.saveInfoMessage(infoMessage); + } @Override public boolean sendInfoMessage(InfoMessage infoMessage, MailFormatter mailFormatter, Locale locale, Identity from, List<Identity> tos) { @@ -136,7 +146,7 @@ public class InfoMessageFrontendManagerImpl extends BasicManager implements Info MailerResult result = mailManager.sendMessage(bundle); send = result.isSuccessful(); } catch (Exception e) { - logError("Cannot send info messages", e); + log.error("Cannot send info messages", e); } } @@ -151,6 +161,28 @@ public class InfoMessageFrontendManagerImpl extends BasicManager implements Info infoMessageManager.deleteInfoMessage(infoMessage); infoSubscriptionManager.markPublisherNews(infoMessage.getOLATResourceable(), infoMessage.getResSubPath()); } + + @Override + public void deleteInfoMessagesOfIdentity(BusinessGroup businessGroup, Identity identity) { + List<InfoMessage> infoMessages = infoMessageManager.loadInfoMessagesOfIdentity(businessGroup, identity); + for (InfoMessage infoMessage : infoMessages) { + infoMessageManager.deleteInfoMessage(infoMessage); + infoSubscriptionManager.markPublisherNews(infoMessage.getOLATResourceable(), infoMessage.getResSubPath()); + } + } + + @Override + public void removeInfoMessagesAndSubscriptionContext(BusinessGroup group) { + List<InfoMessage> messages = infoMessageManager.loadInfoMessageByResource(group, + InfoMessageFrontendManager.businessGroupResSubPath, null, null, null, 0, 0); + for (InfoMessage im : messages) { + infoMessageManager.deleteInfoMessage(im); + } + String resName = group.getResourceableTypeName(); + Long resId = group.getResourceableId(); + SubscriptionContext subscriptionContext = new SubscriptionContext(resName, resId, ""); + infoSubscriptionManager.deleteSubscriptionContext(subscriptionContext); + } @Override public List<InfoMessage> loadInfoMessageByResource(OLATResourceable ores, String subPath, String businessPath, diff --git a/src/main/java/org/olat/commons/info/manager/InfoMessageManager.java b/src/main/java/org/olat/commons/info/manager/InfoMessageManager.java index 6d8f1d47d6c..24f4b65916e 100644 --- a/src/main/java/org/olat/commons/info/manager/InfoMessageManager.java +++ b/src/main/java/org/olat/commons/info/manager/InfoMessageManager.java @@ -23,9 +23,11 @@ package org.olat.commons.info.manager; import java.util.Date; import java.util.List; +import org.olat.basesecurity.IdentityRef; import org.olat.commons.info.model.InfoMessage; import org.olat.core.id.Identity; import org.olat.core.id.OLATResourceable; +import org.olat.group.BusinessGroupRef; public abstract class InfoMessageManager { @@ -43,6 +45,8 @@ public abstract class InfoMessageManager { public abstract void deleteInfoMessage(InfoMessage infoMessage); + public abstract List<InfoMessage> loadInfoMessagesOfIdentity(BusinessGroupRef businessGroup, IdentityRef identity); + public abstract InfoMessage loadInfoMessageByKey(Long key); public abstract List<InfoMessage> loadInfoMessageByResource(OLATResourceable ores, String subPath, String businessPath, diff --git a/src/main/java/org/olat/commons/info/manager/InfoMessageManagerImpl.java b/src/main/java/org/olat/commons/info/manager/InfoMessageManagerImpl.java index 1aeda700178..eeccc4f8cfe 100644 --- a/src/main/java/org/olat/commons/info/manager/InfoMessageManagerImpl.java +++ b/src/main/java/org/olat/commons/info/manager/InfoMessageManagerImpl.java @@ -25,6 +25,7 @@ import java.util.Date; import java.util.List; import java.util.StringTokenizer; +import org.olat.basesecurity.IdentityRef; import org.olat.commons.info.model.InfoMessage; import org.olat.commons.info.model.InfoMessageImpl; import org.olat.core.commons.persistence.DB; @@ -32,6 +33,7 @@ import org.olat.core.commons.persistence.DBQuery; import org.olat.core.id.Identity; import org.olat.core.id.OLATResourceable; import org.olat.core.util.StringHelper; +import org.olat.group.BusinessGroupRef; /** * @@ -95,19 +97,43 @@ public class InfoMessageManagerImpl extends InfoMessageManager { } } } + + + @Override + public List<InfoMessage> loadInfoMessagesOfIdentity(BusinessGroupRef businessGroup, IdentityRef identity) { + StringBuilder sb = new StringBuilder(); + sb.append("select msg from ").append(InfoMessageImpl.class.getName()).append(" msg") + .append(" inner join fetch msg.author author") + .append(" inner join fetch author.user") + .append(" left join fetch msg.modifier modifier") + .append(" left join fetch modifier.user") + .append(" where author.key=:authorKey") + .append(" and msg.resId=:groupKey"); + return dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), InfoMessage.class) + .setParameter("authorKey",identity.getKey()) + .setParameter("groupKey", businessGroup.getKey()) + .getResultList(); + } @Override public InfoMessage loadInfoMessageByKey(Long key) { StringBuilder sb = new StringBuilder(); - sb.append("select msg from ").append(InfoMessageImpl.class.getName()) - .append(" msg where msg.key=:key"); + sb.append("select msg from ").append(InfoMessageImpl.class.getName()).append(" msg") + .append(" inner join fetch msg.author author") + .append(" inner join fetch author.user") + .append(" left join fetch msg.modifier modifier") + .append(" left join fetch modifier.user") + .append(" where msg.key=:key"); - DBQuery query = dbInstance.createQuery(sb.toString()); - query.setLong("key", key); - @SuppressWarnings("unchecked") - List<InfoMessage> msgs = query.list(); - if(msgs.isEmpty()) return null; - return msgs.get(0); + List<InfoMessage> infoMessages = dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), InfoMessage.class) + .setParameter("key", key) + .getResultList(); + if (infoMessages.size() == 0) { + return null; + } + return infoMessages.get(0); } @Override @@ -148,6 +174,13 @@ public class InfoMessageManagerImpl extends InfoMessageManager { sb.append(" from ").append(InfoMessageImpl.class.getName()).append(" msg"); + if (!count) { + sb.append(" inner join fetch msg.author author") + .append(" inner join fetch author.user") + .append(" left join fetch msg.modifier modifier") + .append(" left join fetch modifier.user"); + } + if(ores != null) { appendAnd(sb, "msg.resId=:resId and msg.resName=:resName "); } diff --git a/src/main/java/org/olat/commons/info/model/InfoMessageImpl.java b/src/main/java/org/olat/commons/info/model/InfoMessageImpl.java index b626c565452..5e976126033 100644 --- a/src/main/java/org/olat/commons/info/model/InfoMessageImpl.java +++ b/src/main/java/org/olat/commons/info/model/InfoMessageImpl.java @@ -22,31 +22,82 @@ package org.olat.commons.info.model; import java.util.Date; -import org.olat.core.commons.persistence.PersistentObject; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.Parameter; +import org.olat.basesecurity.IdentityImpl; +import org.olat.core.id.CreateInfo; import org.olat.core.id.Identity; import org.olat.core.id.OLATResourceable; +import org.olat.core.id.Persistable; -public class InfoMessageImpl extends PersistentObject implements InfoMessage { +/** + * Initial Date: 17.03.2017 + * @author fkiefer, fabian.kiefer@frentix.com, www.frentix.com + */ +@Entity(name="infomessage") +@Table(name="o_info_message") +public class InfoMessageImpl implements InfoMessage, CreateInfo, Persistable { private static final long serialVersionUID = 6373476657660866469L; - + + @Id + @GeneratedValue(generator = "system-uuid") + @GenericGenerator(name = "system-uuid", strategy = "enhanced-sequence", parameters={ + @Parameter(name="sequence_name", value="hibernate_unique_key"), + @Parameter(name="force_table_use", value="true"), + @Parameter(name="optimizer", value="legacy-hilo"), + @Parameter(name="value_column", value="next_hi"), + @Parameter(name="increment_size", value="32767"), + @Parameter(name="initial_value", value="32767") + }) + @Column(name="info_id", nullable=false, unique=true, insertable=true, updatable=false) + private Long key; + @Column(name="version", nullable=false, insertable=true, updatable=false) + private int version = 0; + + @Temporal(TemporalType.TIMESTAMP) + @Column(name="creationdate", nullable=false, insertable=true, updatable=false) + private Date creationDate; + @Temporal(TemporalType.TIMESTAMP) + @Column(name="modificationdate", nullable=true, insertable=true, updatable=false) private Date modificationDate; + @Column(name="title", nullable=true, insertable=true, updatable=true) private String title; + @Column(name="message", nullable=true, insertable=true, updatable=true) private String message; + @Column(name="resid", nullable=false, insertable=true, updatable=false) private Long resId; + @Column(name="resname", nullable=false, insertable=true, updatable=false) private String resName; - private String subPath; + @Column(name="ressubpath", nullable=true, insertable=true, updatable=false) + private String resSubPath; + @Column(name="businesspath", nullable=true, insertable=true, updatable=false) private String businessPath; + @ManyToOne(targetEntity=IdentityImpl.class, fetch=FetchType.LAZY, optional=false) + @JoinColumn(name="fk_author_id", nullable=false, insertable=true, updatable=false) private Identity author; + @ManyToOne(targetEntity=IdentityImpl.class, fetch=FetchType.LAZY, optional=true) + @JoinColumn(name="fk_modifier_id", nullable=true, insertable=true, updatable=true) private Identity modifier; public InfoMessageImpl() { // } - + public Date getModificationDate() { return modificationDate; } @@ -54,6 +105,10 @@ public class InfoMessageImpl extends PersistentObject implements InfoMessage { public void setModificationDate(Date modificationDate) { this.modificationDate = modificationDate; } + + public void setCreationDate(Date creationDate) { + this.creationDate= creationDate; + } public String getTitle() { return title; @@ -88,11 +143,11 @@ public class InfoMessageImpl extends PersistentObject implements InfoMessage { } public String getResSubPath() { - return subPath; + return resSubPath; } public void setResSubPath(String subPath) { - this.subPath = subPath; + this.resSubPath = subPath; } public String getBusinessPath() { @@ -150,4 +205,20 @@ public class InfoMessageImpl extends PersistentObject implements InfoMessage { } return false; } + + @Override + public boolean equalsByPersistableKey(Persistable persistable) { + return equals(persistable); + } + + @Override + public Long getKey() { + return key; + } + + + @Override + public Date getCreationDate() { + return creationDate; + } } diff --git a/src/main/java/org/olat/commons/info/notification/InfoMessageNotificationHandler.java b/src/main/java/org/olat/commons/info/notification/InfoMessageNotificationHandler.java index 64073fce04c..2c6c8baca41 100644 --- a/src/main/java/org/olat/commons/info/notification/InfoMessageNotificationHandler.java +++ b/src/main/java/org/olat/commons/info/notification/InfoMessageNotificationHandler.java @@ -26,6 +26,7 @@ import java.util.Locale; import org.olat.commons.info.manager.InfoMessageManager; import org.olat.commons.info.model.InfoMessage; +import org.olat.core.CoreSpringFactory; import org.olat.core.commons.services.notifications.NotificationHelper; import org.olat.core.commons.services.notifications.NotificationsHandler; import org.olat.core.commons.services.notifications.NotificationsManager; @@ -41,6 +42,8 @@ import org.olat.core.id.context.BusinessControlFactory; import org.olat.core.logging.LogDelegator; import org.olat.core.util.Util; import org.olat.core.util.resource.OresHelper; +import org.olat.group.BusinessGroup; +import org.olat.group.BusinessGroupService; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryManager; @@ -72,15 +75,24 @@ public class InfoMessageNotificationHandler extends LogDelegator implements Noti final Long resId = subscriber.getPublisher().getResId(); final String resName = subscriber.getPublisher().getResName(); String resSubPath = subscriber.getPublisher().getSubidentifier(); - - RepositoryEntry re = RepositoryManager.getInstance().lookupRepositoryEntry(OresHelper.createOLATResourceableInstance(resName, resId), false); - if(re.getRepositoryEntryStatus().isClosed() || re.getRepositoryEntryStatus().isUnpublished()) { - return NotificationsManager.getInstance().getNoSubscriptionInfo(); - } - String displayName = re.getDisplayname(); + String displayName, notificationtitle; + if ("BusinessGroup".equals(resName)) { + BusinessGroupService groupService = CoreSpringFactory.getImpl(BusinessGroupService.class); + BusinessGroup group = groupService.loadBusinessGroup(resId); + displayName = group.getName(); + notificationtitle = "notification.title.group"; + } else { + RepositoryEntry re = RepositoryManager.getInstance().lookupRepositoryEntry(OresHelper.createOLATResourceableInstance(resName, resId), false); + if(re.getRepositoryEntryStatus().isClosed() || re.getRepositoryEntryStatus().isUnpublished()) { + return NotificationsManager.getInstance().getNoSubscriptionInfo(); + } + displayName = re.getDisplayname(); + notificationtitle = "notification.title"; + } + Translator translator = Util.createPackageTranslator(this.getClass(), locale); - String title = translator.translate("notification.title", new String[]{ displayName }); + String title = translator.translate(notificationtitle, new String[]{ displayName }); si = new SubscriptionInfo(subscriber.getKey(), p.getType(), new TitleItem(title, CSS_CLASS_ICON), null); OLATResourceable ores = OresHelper.createOLATResourceableInstance(resName, resId); diff --git a/src/main/java/org/olat/commons/info/notification/InfoSubscriptionManager.java b/src/main/java/org/olat/commons/info/notification/InfoSubscriptionManager.java index 770f44056d2..210b10e8362 100644 --- a/src/main/java/org/olat/commons/info/notification/InfoSubscriptionManager.java +++ b/src/main/java/org/olat/commons/info/notification/InfoSubscriptionManager.java @@ -61,4 +61,7 @@ public abstract class InfoSubscriptionManager { public abstract void unsubscribe(OLATResourceable resource, String subPath, Identity identity); public abstract void markPublisherNews(OLATResourceable resource, String subPath); + + public abstract void deleteSubscriptionContext(SubscriptionContext context); + } diff --git a/src/main/java/org/olat/commons/info/notification/InfoSubscriptionManagerImpl.java b/src/main/java/org/olat/commons/info/notification/InfoSubscriptionManagerImpl.java index 3c2e636739d..369d27aeb3b 100644 --- a/src/main/java/org/olat/commons/info/notification/InfoSubscriptionManagerImpl.java +++ b/src/main/java/org/olat/commons/info/notification/InfoSubscriptionManagerImpl.java @@ -122,4 +122,9 @@ public class InfoSubscriptionManagerImpl extends InfoSubscriptionManager { SubscriptionContext context = getInfoSubscriptionContext(resource, subPath); notificationsManager.markPublisherNews(context, null, true); } + + @Override + public void deleteSubscriptionContext(SubscriptionContext context) { + notificationsManager.delete(context); + } } diff --git a/src/main/java/org/olat/commons/info/notification/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/commons/info/notification/_i18n/LocalStrings_de.properties index 3f14671a2da..f6e338c0fa3 100644 --- a/src/main/java/org/olat/commons/info/notification/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/commons/info/notification/_i18n/LocalStrings_de.properties @@ -1,3 +1,4 @@ #Mon Mar 02 09:54:04 CET 2009 notification.title=Mitteilungen im Kurs "{0}" +notification.title.group=Mitteilungen in der Gruppe "{0}" notifications.entry=Mitteilung "{0}" eingestellt von {1} \ No newline at end of file diff --git a/src/main/java/org/olat/commons/info/notification/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/commons/info/notification/_i18n/LocalStrings_en.properties index 362d53e0e4f..185e58e7dfa 100644 --- a/src/main/java/org/olat/commons/info/notification/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/commons/info/notification/_i18n/LocalStrings_en.properties @@ -1,3 +1,4 @@ #Fri Sep 30 15:26:44 CEST 2016 notification.title=Notifications in course "{0}" +notification.title.group=Notifications in group "{0}" notifications.entry=Message "{0}" added by {1} diff --git a/src/main/java/org/olat/commons/info/ui/InfoDisplayController.java b/src/main/java/org/olat/commons/info/ui/InfoDisplayController.java index 4de458c208d..16d73dc4c2c 100644 --- a/src/main/java/org/olat/commons/info/ui/InfoDisplayController.java +++ b/src/main/java/org/olat/commons/info/ui/InfoDisplayController.java @@ -63,6 +63,7 @@ import org.olat.core.util.coordinate.CoordinatorManager; import org.olat.core.util.coordinate.LockResult; import org.olat.core.util.resource.OresHelper; import org.olat.course.nodes.info.InfoCourseNodeConfiguration; +import org.olat.group.BusinessGroup; import org.olat.modules.ModuleConfiguration; import org.olat.user.UserManager; import org.olat.util.logging.activity.LoggingResourceable; @@ -108,6 +109,24 @@ public class InfoDisplayController extends FormBasicController { private MailFormatter sendMailFormatter; private List<SendMailOption> sendMailOptions = new ArrayList<SendMailOption>(); + public InfoDisplayController(UserRequest ureq, WindowControl wControl, InfoSecurityCallback secCallback, + BusinessGroup businessGroup, String resSubPath, String businessPath) { + super(ureq, wControl, "display"); + userManager = CoreSpringFactory.getImpl(UserManager.class); + infoMessageManager = CoreSpringFactory.getImpl(InfoMessageFrontendManager.class); + this.secCallback = secCallback; + this.ores = businessGroup.getResource(); + this.resSubPath = resSubPath; + this.businessPath = businessPath; + // default show 10 messages for groups + maxResults = maxResultsConfig = 10; + + initForm(ureq); + + // now load with configuration + loadMessages(); + } + public InfoDisplayController(UserRequest ureq, WindowControl wControl, ModuleConfiguration config, InfoSecurityCallback secCallback, OLATResourceable ores, String resSubPath, String businessPath) { super(ureq, wControl, "display"); diff --git a/src/main/java/org/olat/course/nodes/info/SendMailFormatterForCourse.java b/src/main/java/org/olat/commons/info/ui/SendInfoMailFormatter.java similarity index 90% rename from src/main/java/org/olat/course/nodes/info/SendMailFormatterForCourse.java rename to src/main/java/org/olat/commons/info/ui/SendInfoMailFormatter.java index 41d9b966f1a..edff133c6db 100644 --- a/src/main/java/org/olat/course/nodes/info/SendMailFormatterForCourse.java +++ b/src/main/java/org/olat/commons/info/ui/SendInfoMailFormatter.java @@ -19,7 +19,7 @@ */ -package org.olat.course.nodes.info; +package org.olat.commons.info.ui; import java.text.DateFormat; import java.util.List; @@ -41,20 +41,19 @@ import org.olat.core.id.context.ContextEntry; * Initial Date: 24 aug. 2010 <br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com */ -public class SendMailFormatterForCourse implements MailFormatter { +public class SendInfoMailFormatter implements MailFormatter { - private final String courseTitle; + private final String title; private final String businessPath; private final Translator translator; - public SendMailFormatterForCourse(String courseTitle, String businessPath, Translator translator) { - this.courseTitle = courseTitle; + public SendInfoMailFormatter(String title, String businessPath, Translator translator) { + this.title = title; this.translator = translator; this.businessPath = businessPath; } @Override - //fxdiff VCRP-16: intern mail system public String getBusinessPath() { return businessPath; } @@ -76,7 +75,7 @@ public class SendMailFormatterForCourse implements MailFormatter { StringBuilder sb = new StringBuilder(); sb.append("<div style='background: #FAFAFA; border: 1px solid #eee; border-radius: 5px; padding: 0 0.5em 0.5em 0.5em; margin: 1em 0 1em 0;' class='o_m_h'>"); - sb.append("<h3>").append(translator.translate("mail.body.title", new String[]{courseTitle})).append("</h3>"); + sb.append("<h3>").append(translator.translate("mail.body.title", new String[]{title})).append("</h3>"); sb.append("<div style='font-size: 90%; color: #888' class='o_m_a'>").append(translator.translate("mail.body.from", new String[]{author, date})).append("</div>"); sb.append("</div>"); diff --git a/src/main/java/org/olat/course/nodes/info/InfoRunController.java b/src/main/java/org/olat/course/nodes/info/InfoRunController.java index e4e21ca0afe..6245d6f1dbe 100644 --- a/src/main/java/org/olat/course/nodes/info/InfoRunController.java +++ b/src/main/java/org/olat/course/nodes/info/InfoRunController.java @@ -30,6 +30,7 @@ import org.olat.commons.info.notification.InfoSubscription; import org.olat.commons.info.notification.InfoSubscriptionManager; import org.olat.commons.info.ui.InfoDisplayController; import org.olat.commons.info.ui.InfoSecurityCallback; +import org.olat.commons.info.ui.SendInfoMailFormatter; import org.olat.commons.info.ui.SendSubscriberMailOption; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.services.notifications.PublisherData; @@ -136,7 +137,7 @@ public class InfoRunController extends BasicController { infoDisplayController.addSendMailOptions(new SendSubscriberMailOption(infoResourceable, resSubPath, CoreSpringFactory.getImpl(InfoMessageFrontendManager.class))); infoDisplayController.addSendMailOptions(new SendMembersMailOption(course.getCourseEnvironment().getCourseGroupManager().getCourseResource(), RepositoryManager.getInstance(), repositoryService, CoreSpringFactory.getImpl(BusinessGroupService.class))); - MailFormatter mailFormatter = new SendMailFormatterForCourse(course.getCourseTitle(), businessPath, getTranslator()); + MailFormatter mailFormatter = new SendInfoMailFormatter(course.getCourseTitle(), businessPath, getTranslator()); infoDisplayController.setSendMailFormatter(mailFormatter); listenTo(infoDisplayController); diff --git a/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java b/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java index fd798171b2e..e177c724e0f 100644 --- a/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java +++ b/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java @@ -44,6 +44,7 @@ import org.olat.basesecurity.IdentityRef; import org.olat.basesecurity.SecurityGroup; import org.olat.collaboration.CollaborationTools; import org.olat.collaboration.CollaborationToolsFactory; +import org.olat.commons.info.manager.InfoMessageFrontendManager; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.persistence.DB; import org.olat.core.commons.services.notifications.NotificationsManager; @@ -161,6 +162,8 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD @Autowired private NotificationsManager notificationsManager; @Autowired + private InfoMessageFrontendManager infoMessageManager; + @Autowired private MailManager mailManager; @Autowired private ACReservationDAO reservationDao; @@ -772,9 +775,11 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD // 5) delete the publisher attached to this group (e.g. the forum and folder // publisher) notificationsManager.deletePublishersOf(group); - // 6) the group + // 6) delete info messages and subscription context associated with this group + infoMessageManager.removeInfoMessagesAndSubscriptionContext(group); + // 7) the group businessGroupDAO.delete(group); - // 7) delete the associated security groups + // 8) delete the associated security groups //TODO group dbInstance.commit(); @@ -1030,7 +1035,7 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD private void removeParticipant(Identity ureqIdentity, Identity identity, BusinessGroup group, MailPackage mailing, List<BusinessGroupModifiedEvent.Deferred> events) { - + infoMessageManager.deleteInfoMessagesOfIdentity(group, identity); boolean removed = businessGroupRelationDAO.removeRole(identity, group, GroupRoles.participant.name()); if(removed) { // notify currently active users of this business group 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 56cb92b4e61..b9dc197c116 100644 --- a/src/main/java/org/olat/group/ui/run/BusinessGroupMainRunController.java +++ b/src/main/java/org/olat/group/ui/run/BusinessGroupMainRunController.java @@ -745,7 +745,7 @@ public class BusinessGroupMainRunController extends MainLayoutBasicController im addToHistory(ureq, bwControl); CollaborationTools collabTools = CollaborationToolsFactory.getInstance().getOrCreateCollaborationTools(businessGroup); - collabToolCtr = collabTools.createNewsController(ureq, bwControl); + collabToolCtr = collabTools.createInfoMessageController(ureq, bwControl); listenTo(collabToolCtr); mainPanel.setContent(collabToolCtr.getInitialComponent()); } diff --git a/src/main/java/org/olat/group/ui/run/InfoGroupRunController.java b/src/main/java/org/olat/group/ui/run/InfoGroupRunController.java new file mode 100644 index 00000000000..aba5469da6e --- /dev/null +++ b/src/main/java/org/olat/group/ui/run/InfoGroupRunController.java @@ -0,0 +1,214 @@ +/** + * <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.List; +import java.util.StringTokenizer; + +import org.olat.commons.info.manager.InfoMessageFrontendManager; +import org.olat.commons.info.manager.MailFormatter; +import org.olat.commons.info.notification.InfoSubscription; +import org.olat.commons.info.notification.InfoSubscriptionManager; +import org.olat.commons.info.ui.InfoDisplayController; +import org.olat.commons.info.ui.InfoSecurityCallback; +import org.olat.commons.info.ui.SendInfoMailFormatter; +import org.olat.commons.info.ui.SendMailOption; +import org.olat.commons.info.ui.SendSubscriberMailOption; +import org.olat.core.commons.services.notifications.PublisherData; +import org.olat.core.commons.services.notifications.SubscriptionContext; +import org.olat.core.commons.services.notifications.ui.ContextualSubscriptionController; +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.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.OLATResourceable; +import org.olat.core.id.Roles; +import org.olat.core.util.UserSession; +import org.olat.core.util.resource.OresHelper; +import org.olat.group.BusinessGroup; +import org.olat.group.BusinessGroupService; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Initial Date: 15.03.2017 + * @author fkiefer, fabian.kiefer@frentix.com, www.frentix.com + */ +public class InfoGroupRunController extends BasicController { + + public static final String resSubPath = InfoMessageFrontendManager.businessGroupResSubPath; + + private final VelocityContainer runVC; + private final InfoDisplayController infoDisplayController; + private ContextualSubscriptionController subscriptionController; + + private final String businessPath; + private InfoSubscriptionManager subscriptionManager; + + @Autowired + private BusinessGroupService groupService; + @Autowired + private InfoMessageFrontendManager messageManager; + + + public InfoGroupRunController(UserRequest ureq, WindowControl wControl, BusinessGroup businessGroup, boolean canAccess) { + super(ureq, wControl); + + long groupId = businessGroup.getKey(); + OLATResourceable infoResourceable = new InfoOLATGroupResourceable(groupId); + businessPath = normalizeBusinessPath(wControl.getBusinessControl().getAsString()); + + UserSession usess = ureq.getUserSession(); + if(!usess.getRoles().isGuestOnly()) { + subscriptionManager = InfoSubscriptionManager.getInstance(); + SubscriptionContext subContext = subscriptionManager.getInfoSubscriptionContext(infoResourceable, resSubPath); + PublisherData pdata = subscriptionManager.getInfoPublisherData(infoResourceable, businessPath); + subscriptionController = new ContextualSubscriptionController(ureq, getWindowControl(), subContext, pdata); + listenTo(subscriptionController); + } + + Roles roles = usess.getRoles(); + boolean canAdmin = roles.isOLATAdmin() || roles.isGroupManager(); + boolean canAdd = canAdmin || canAccess; + + InfoSecurityCallback secCallback = new InfoGroupSecurityCallback(canAdd, canAdmin); + infoDisplayController = new InfoDisplayController(ureq, wControl, secCallback, businessGroup, resSubPath, businessPath); + SendMailOption subscribers = new SendSubscriberMailOption(infoResourceable, resSubPath, messageManager); + infoDisplayController.addSendMailOptions(subscribers); + SendMailOption groupMembers = new SendGroupMembersMailOption(groupService, businessGroup); + infoDisplayController.addSendMailOptions(groupMembers); + MailFormatter mailFormatter = new SendInfoMailFormatter(businessGroup.getName(), businessPath, getTranslator()); + infoDisplayController.setSendMailFormatter(mailFormatter); + listenTo(infoDisplayController); + + runVC = createVelocityContainer("run"); + if(subscriptionController != null) { + runVC.put("infoSubscription", subscriptionController.getInitialComponent()); + } + runVC.put("displayInfos", infoDisplayController.getInitialComponent()); + + putInitialPanel(runVC); + } + + /** + * Remove ROOT, remove identity context entry or duplicate, + * @param url + * @return + */ + private String normalizeBusinessPath(String url) { + if (url == null) return null; + if (url.startsWith("ROOT")) { + url = url.substring(4, url.length()); + } + List<String> tokens = new ArrayList<String>(); + for(StringTokenizer tokenizer = new StringTokenizer(url, "[]"); tokenizer.hasMoreTokens(); ) { + String token = tokenizer.nextToken(); + if(token.startsWith("Identity")) { + //The portlet "My courses" add an Identity context entry to the business path + //ignore it + continue; + } + if(!tokens.contains(token)) { + tokens.add(token); + } + } + + StringBuilder sb = new StringBuilder(); + for(String token:tokens) { + sb.append('[').append(token).append(']'); + } + return sb.toString(); + } + + @Override + protected void doDispose() { + // + } + + @Override + protected void event(UserRequest ureq, Component source, Event event) { + // + } + + @Override + protected void event(UserRequest ureq, Controller source, Event event) { + if(source == subscriptionController) { + InfoSubscription infoSubscription = subscriptionManager.getInfoSubscription(ureq.getUserSession().getGuiPreferences()); + if(subscriptionController.isSubscribed()) { + infoSubscription.subscribed(businessPath, true); + } else { + infoSubscription.unsubscribed(businessPath); + } + } + super.event(ureq, source, event); + } + + private class InfoGroupSecurityCallback implements InfoSecurityCallback { + private final boolean canAdd; + private final boolean canAdmin; + + public InfoGroupSecurityCallback(boolean canAdd, boolean canAdmin) { + this.canAdd = canAdd; + this.canAdmin = canAdmin; + } + + @Override + public boolean canRead() { + return true; + } + + @Override + public boolean canAdd() { + return canAdd; + } + + @Override + public boolean canEdit() { + return canAdd; + } + + @Override + public boolean canDelete() { + return canAdmin; + } + } + + private class InfoOLATGroupResourceable implements OLATResourceable { + private final Long resId; + + public InfoOLATGroupResourceable(Long groupId) { + this.resId = groupId; + } + + @Override + public String getResourceableTypeName() { + return OresHelper.calculateTypeName(BusinessGroup.class); + } + + @Override + public Long getResourceableId() { + return resId; + } + } + +} diff --git a/src/main/java/org/olat/group/ui/run/SendGroupMembersMailOption.java b/src/main/java/org/olat/group/ui/run/SendGroupMembersMailOption.java new file mode 100644 index 00000000000..aad0902a4d7 --- /dev/null +++ b/src/main/java/org/olat/group/ui/run/SendGroupMembersMailOption.java @@ -0,0 +1,64 @@ +/** + * <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.List; +import java.util.Locale; + +import org.olat.commons.info.ui.SendMailOption; +import org.olat.core.gui.translator.Translator; +import org.olat.core.id.Identity; +import org.olat.core.util.Util; +import org.olat.group.BusinessGroup; +import org.olat.group.BusinessGroupService; + +/** + * Initial Date: 15.03.2017 + * @author fkiefer, fabian.kiefer@frentix.com, www.frentix.com + */ +public class SendGroupMembersMailOption implements SendMailOption { + + private BusinessGroupService groupService; + private BusinessGroup businessGroup; + + + public SendGroupMembersMailOption(BusinessGroupService groupService, BusinessGroup businessGroup) { + this.groupService = groupService; + this.businessGroup = businessGroup; + } + + @Override + public String getOptionKey() { + return "send-mail-group-members"; + } + + @Override + public String getOptionTranslatedName(Locale locale) { + Translator translator = Util.createPackageTranslator(SendGroupMembersMailOption.class, locale); + return translator.translate("wizard.step1.send_option.member"); + } + + @Override + public List<Identity> getSelectedIdentities() { + List<Identity> groupMembers = groupService.getMembers(businessGroup); + return groupMembers; + } + +} diff --git a/src/main/java/org/olat/group/ui/run/_content/run.html b/src/main/java/org/olat/group/ui/run/_content/run.html new file mode 100644 index 00000000000..8138c3af743 --- /dev/null +++ b/src/main/java/org/olat/group/ui/run/_content/run.html @@ -0,0 +1,6 @@ +#if($r.available("infoSubscription")) + <div class="clearfix"> + $r.render("infoSubscription") + </div> +#end +$r.render("displayInfos") 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 dbc2974356e..7331919d4f6 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 @@ -11,6 +11,9 @@ grouprun.details.name=Name der Gruppe grouprun.details.title=Informationen grouprun.disabled=Zurzeit sind alle kollaborativen Werkzeuge in OLAT f\u00FCr Sie gesperrt (z.B. da Sie einen Test bearbeiten). Sie m\u00FCssen den Test zuerst abbrechen / beenden, um kollaborative Werkzeuge benutzen zu k\u00F6nnen. grouprun.removedfromgroup=Die Konfiguration dieser Gruppe wurde ver\u00E4ndert (Gruppe gel\u00F6scht, Gruppenmitgliedern ge\u00E4ndert). Schliessen Sie den Tab. +mail.body.title=Mitteilung aus Kurs {0} +mail.body.from=Verfasst von {0} am {1} +mail.body.more=Weitere Mitteilungen menutree.administration=Administration menutree.administration.alt=Administration menutree.calendar=Kalender @@ -72,3 +75,4 @@ userlist.show.no.waitinglist.title=Warteliste 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 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 4560447f50f..bb38a432319 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 @@ -11,6 +11,9 @@ grouprun.details.name=Group name grouprun.details.title=Information grouprun.disabled=Currently all collaborative elements in OLAT are locked (e.g. because you are editing a test). Cancel or complete first in order to be able to use collaborative tools. grouprun.removedfromgroup=This group's configuration has been modified (group deleted, members changed). Please close the tab. +mail.body.title=Notification regarding course {0} +mail.body.from=Written by {0} on {1} +mail.body.more=Further information menutree.ac=Bookings menutree.ac.alt=Bookings menutree.administration=Administration @@ -72,3 +75,4 @@ 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 diff --git a/src/main/java/org/olat/search/_spring/searchContext.xml b/src/main/java/org/olat/search/_spring/searchContext.xml index 14736634f9f..dd7492657b5 100644 --- a/src/main/java/org/olat/search/_spring/searchContext.xml +++ b/src/main/java/org/olat/search/_spring/searchContext.xml @@ -206,6 +206,7 @@ <ref bean="groupFolderIndexer" /> <ref bean="groupWikiIndexer" /> <ref bean="groupPortfolioIndexer" /> + <ref bean="groupInfoIndexer" /> </list> </property> </bean> @@ -215,6 +216,9 @@ <property name="collaborationManager" ref="collaborationManager"/> </bean> <bean id="groupWikiIndexer" class="org.olat.search.service.indexer.group.GroupWikiIndexer" /> + <bean id="groupInfoIndexer" class="org.olat.search.service.indexer.group.GroupInfoIndexer"> + <property name="infoMessageManager" ref="infoMessageManager"/> + </bean> <!-- Portfolio indexers --> <bean id="epDefaultMapIndexer" class="org.olat.search.service.indexer.PortfolioMapIndexer"> diff --git a/src/main/java/org/olat/search/service/indexer/group/GroupInfoIndexer.java b/src/main/java/org/olat/search/service/indexer/group/GroupInfoIndexer.java new file mode 100644 index 00000000000..85383e3020e --- /dev/null +++ b/src/main/java/org/olat/search/service/indexer/group/GroupInfoIndexer.java @@ -0,0 +1,99 @@ +/** + * <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.search.service.indexer.group; + +import java.io.IOException; +import java.util.List; + +import org.apache.lucene.document.Document; +import org.olat.commons.info.manager.InfoMessageFrontendManager; +import org.olat.commons.info.manager.InfoMessageManager; +import org.olat.commons.info.model.InfoMessage; +import org.olat.core.id.OLATResourceable; +import org.olat.core.logging.AssertException; +import org.olat.core.logging.OLog; +import org.olat.core.logging.Tracing; +import org.olat.core.util.resource.OresHelper; +import org.olat.group.BusinessGroup; +import org.olat.group.ui.run.BusinessGroupMainRunController; +import org.olat.search.service.SearchResourceContext; +import org.olat.search.service.document.InfoMessageDocument; +import org.olat.search.service.indexer.AbstractHierarchicalIndexer; +import org.olat.search.service.indexer.OlatFullIndexer; + +/** + * Initial Date: 20.03.2017 + * @author fkiefer, fabian.kiefer@frentix.com, www.frentix.com + */ +public class GroupInfoIndexer extends AbstractHierarchicalIndexer{ + + private static final OLog log = Tracing.createLoggerFor(GroupInfoIndexer.class); + + public static final String TYPE = "type.group.info.message"; + + private final static String SUPPORTED_TYPE_NAME = "org.olat.group.BusinessGroup"; + + private InfoMessageManager infoMessageManager; + + /** + * [used by Spring] + * @param infoMessageManager + */ + public void setInfoMessageManager(InfoMessageManager infoMessageManager) { + this.infoMessageManager = infoMessageManager; + } + + @Override + public String getSupportedTypeName() { + return SUPPORTED_TYPE_NAME; + } + + + @Override + public void doIndex(SearchResourceContext searchResourceContext, Object businessObject, OlatFullIndexer indexerWriter) + throws IOException, InterruptedException { + if (!(businessObject instanceof BusinessGroup)) throw new AssertException("businessObject must be BusinessGroup"); + BusinessGroup businessGroup = (BusinessGroup) businessObject; + try { + SearchResourceContext messagesGroupResourceContext = new SearchResourceContext(searchResourceContext); + messagesGroupResourceContext.setBusinessControlFor(BusinessGroupMainRunController.ORES_TOOLMSG); + messagesGroupResourceContext.setDocumentType(TYPE); + doIndexInfos(messagesGroupResourceContext, businessGroup, indexerWriter); + } catch (Exception ex) { + log.error("Exception indexing businessGroup=" + businessGroup, ex); + } catch (Error err) { + log.error("Error indexing businessGroup=" + businessGroup, err); + } + } + + private void doIndexInfos(SearchResourceContext parentResourceContext, BusinessGroup businessGroup, OlatFullIndexer indexWriter) + throws IOException, InterruptedException { + List<InfoMessage> messages = infoMessageManager.loadInfoMessageByResource(businessGroup, + InfoMessageFrontendManager.businessGroupResSubPath, null, null, null, 0, -1); + for(InfoMessage message : messages) { + SearchResourceContext searchResourceContext = new SearchResourceContext(parentResourceContext); + OLATResourceable ores = OresHelper.createOLATResourceableInstance(InfoMessage.class, message.getKey()); + searchResourceContext.setBusinessControlFor(ores); + Document document = InfoMessageDocument.createDocument(searchResourceContext, message); + indexWriter.addDocument(document); + } + } + +} diff --git a/src/main/java/org/olat/upgrade/OLATUpgrade_11_4_0.java b/src/main/java/org/olat/upgrade/OLATUpgrade_11_4_0.java new file mode 100644 index 00000000000..b53359fe722 --- /dev/null +++ b/src/main/java/org/olat/upgrade/OLATUpgrade_11_4_0.java @@ -0,0 +1,161 @@ +/** + * <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.upgrade; + +import java.util.List; + +import org.olat.basesecurity.GroupRoles; +import org.olat.collaboration.CollaborationTools; +import org.olat.collaboration.CollaborationToolsFactory; +import org.olat.commons.info.manager.InfoMessageFrontendManager; +import org.olat.commons.info.model.InfoMessageImpl; +import org.olat.core.commons.persistence.DB; +import org.olat.core.gui.translator.Translator; +import org.olat.core.id.Identity; +import org.olat.core.util.Util; +import org.olat.core.util.i18n.I18nModule; +import org.olat.group.BusinessGroup; +import org.olat.group.BusinessGroupService; +import org.olat.properties.Property; +import org.olat.repository.RepositoryDeletionModule; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Initial Date: 16.03.2017 + * @author fkiefer, fabian.kiefer@frentix.com, www.frentix.com + */ +public class OLATUpgrade_11_4_0 extends OLATUpgrade { + + private static final String VERSION = "OLAT_11.4.0"; + private static final String GROUP_INFO_MSG = "GROUP INFO MESSAGE"; + + @Autowired + private DB dbInstance; + @Autowired + private BusinessGroupService groupService; + @Autowired + private CollaborationToolsFactory toolsF; + @Autowired + private InfoMessageFrontendManager infoMessageManager; + @Autowired + private RepositoryDeletionModule deletionManager; + + + public OLATUpgrade_11_4_0() { + super(); + } + + @Override + public String getVersion() { + return VERSION; + } + + @Override + public boolean doPreSystemInitUpgrade(UpgradeManager upgradeManager) { + return false; + } + + @Override + public boolean doPostSystemInitUpgrade(UpgradeManager upgradeManager) { + UpgradeHistoryData uhd = upgradeManager.getUpgradesHistory(VERSION); + if (uhd == null) { + // has never been called, initialize + uhd = new UpgradeHistoryData(); + } else if (uhd.isInstallationComplete()) { + return false; + } + + boolean allOk = true; + allOk &= upgradeGroupInfoMessage(upgradeManager, uhd); + + uhd.setInstallationComplete(allOk); + upgradeManager.setUpgradesHistory(uhd, VERSION); + if(allOk) { + log.audit("Finished OLATUpgrade_11_4_0 successfully!"); + } else { + log.audit("OLATUpgrade_11_4_0 not finished, try to restart OpenOLAT!"); + } + return allOk; + } + + private boolean upgradeGroupInfoMessage(UpgradeManager upgradeManager, UpgradeHistoryData uhd) { + boolean allOk = true; + if (!uhd.getBooleanDataValue(GROUP_INFO_MSG)) { + + List<BusinessGroup> allBusinessGroups = groupService.loadAllBusinessGroups(); + for (BusinessGroup businessGroup : allBusinessGroups) { + if(businessGroup == null) continue; + + allOk &= processInfoMessage(businessGroup); + dbInstance.commitAndCloseSession(); + } + + uhd.setBooleanDataValue(GROUP_INFO_MSG, allOk); + upgradeManager.setUpgradesHistory(uhd, VERSION); + } + return allOk; + } + + /** + * @param business group + * @return true if upgrade went well + */ + private boolean processInfoMessage(BusinessGroup businessGroup) { + // iterate all groups and translate their singular info message to the new standard + try { + String businessPath = "[BusinessGroup:" + businessGroup.getKey() + "][toolmsg:0]"; + int messageCount = infoMessageManager.countInfoMessageByResource(businessGroup.getResource(), + InfoMessageFrontendManager.businessGroupResSubPath, businessPath, null, null); + // only upgrade if business group has not any info messages of the new kind yet + if (1 > messageCount) { + CollaborationTools collabTools = toolsF.getOrCreateCollaborationTools(businessGroup); + Property property = collabTools.lookupNewsDBEntry(); + if (property != null) { + String oldNews = property.getTextValue();//collabTools.lookupNews(); + Identity author; + List<Identity> members = groupService.getMembers(businessGroup, GroupRoles.owner.name(), GroupRoles.coach.name()); + if (members == null || (members != null && members.isEmpty())) { + author = deletionManager.getAdminUserIdentity(); + } else { + author = members.get(0); + } + InfoMessageImpl infoMessage = (InfoMessageImpl)infoMessageManager.createInfoMessage(businessGroup.getResource(), + InfoMessageFrontendManager.businessGroupResSubPath, businessPath, author); + Translator trans = Util.createPackageTranslator(CollaborationTools.class, I18nModule.getDefaultLocale()); + String title = trans.translate("news.content"); + infoMessage.setTitle(title); + infoMessage.setMessage(oldNews); + infoMessage.setCreationDate(property.getCreationDate()); + infoMessageManager.saveInfoMessage(infoMessage); + } else { + log.warn("The group " + businessGroup.getName() + " does not have an info message"); + } + } + return true; + } catch (Exception e) { + log.warn("Update InfoMessage for " + businessGroup.getName() + " failed", e); + return false; + } + } + + + + +} diff --git a/src/main/java/org/olat/upgrade/_spring/upgradeContext.xml b/src/main/java/org/olat/upgrade/_spring/upgradeContext.xml index 54ba2c9a97d..64aa7c1335a 100644 --- a/src/main/java/org/olat/upgrade/_spring/upgradeContext.xml +++ b/src/main/java/org/olat/upgrade/_spring/upgradeContext.xml @@ -50,6 +50,7 @@ <bean id="upgrade_11_0_6" class="org.olat.upgrade.OLATUpgrade_11_0_6"/> <bean id="upgrade_11_2_1" class="org.olat.upgrade.OLATUpgrade_11_2_1"/> <bean id="upgrade_11_3_0" class="org.olat.upgrade.OLATUpgrade_11_3_0"/> + <bean id="upgrade_11_4_0" class="org.olat.upgrade.OLATUpgrade_11_4_0"/> </list> </property> </bean> diff --git a/src/main/resources/META-INF/persistence.xml b/src/main/resources/META-INF/persistence.xml index a8d4cc3605a..af690253fee 100644 --- a/src/main/resources/META-INF/persistence.xml +++ b/src/main/resources/META-INF/persistence.xml @@ -15,7 +15,6 @@ <mapping-file>org/olat/note/NoteImpl.hbm.xml</mapping-file> <mapping-file>org/olat/commons/lifecycle/LifeCycleEntry.hbm.xml</mapping-file> <mapping-file>org/olat/commons/coordinate/cluster/lock/LockImpl.hbm.xml</mapping-file> - <mapping-file>org/olat/commons/info/model/InfoMessageImpl.hbm.xml</mapping-file> <mapping-file>org/olat/group/area/BGAreaImpl.hbm.xml</mapping-file> <mapping-file>org/olat/group/area/BGtoAreaRelationImpl.hbm.xml</mapping-file> <mapping-file>org/olat/resource/OLATResourceImpl.hbm.xml</mapping-file> @@ -84,6 +83,7 @@ <class>org.olat.commons.calendar.model.ImportedCalendar</class> <class>org.olat.commons.calendar.model.ImportedToCalendar</class> <class>org.olat.commons.calendar.model.CalendarUserConfiguration</class> + <class>org.olat.commons.info.model.InfoMessageImpl</class> <class>org.olat.core.commons.services.lock.pessimistic.PLockImpl</class> <class>org.olat.core.commons.services.notifications.model.SubscriberImpl</class> <class>org.olat.core.commons.services.notifications.model.PublisherImpl</class> diff --git a/src/test/java/org/olat/commons/info/InfoManagerTest.java b/src/test/java/org/olat/commons/info/InfoManagerTest.java index 19027ca7a92..d57d2cb0115 100644 --- a/src/test/java/org/olat/commons/info/InfoManagerTest.java +++ b/src/test/java/org/olat/commons/info/InfoManagerTest.java @@ -30,6 +30,7 @@ import java.util.Date; import java.util.List; import java.util.UUID; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.olat.commons.info.manager.InfoMessageManager; @@ -37,6 +38,9 @@ import org.olat.commons.info.model.InfoMessage; import org.olat.core.commons.persistence.DB; import org.olat.core.id.Identity; import org.olat.core.id.OLATResourceable; +import org.olat.group.BusinessGroup; +import org.olat.group.BusinessGroupService; +import org.olat.repository.RepositoryEntry; import org.olat.test.JunitTestHelper; import org.olat.test.OlatTestCase; import org.springframework.beans.factory.annotation.Autowired; @@ -61,6 +65,8 @@ public class InfoManagerTest extends OlatTestCase { @Autowired private InfoMessageManager infoMessageManager; + @Autowired + private BusinessGroupService groupService; /** * Set up a course with learn group and group area @@ -121,6 +127,87 @@ public class InfoManagerTest extends OlatTestCase { assertEquals(msg.getKey(), retrievedMsg.get(0).getKey()); } + @Test + public void loadInfoMessagesOfIdentity() { + Identity id5 = JunitTestHelper.createAndPersistIdentityAsRndUser("info-1"); + Identity id2 = JunitTestHelper.createAndPersistIdentityAsRndUser("info-2"); + Identity id3 = JunitTestHelper.createAndPersistIdentityAsRndUser("info-3"); + Identity id4 = JunitTestHelper.createAndPersistIdentityAsRndUser("info-4"); + RepositoryEntry resource1 = JunitTestHelper.createAndPersistRepositoryEntry(); + BusinessGroup group1 = groupService.createBusinessGroup(null, "gdao1", "gdao1-desc", -1, -1, false, false, resource1); + final OLATResourceable ores1 = new OLATResourceable() { + @Override + public String getResourceableTypeName() { + return group1.getResourceableTypeName(); + } + @Override + public Long getResourceableId() { + return group1.getResourceableId(); + } + }; + RepositoryEntry resource2 = JunitTestHelper.createAndPersistRepositoryEntry(); + BusinessGroup group2 = groupService.createBusinessGroup(null, "gdao2", "gdao2-desc", -1, -1, false, false, resource2); + final OLATResourceable ores2 = new OLATResourceable() { + @Override + public String getResourceableTypeName() { + return group2.getResourceableTypeName(); + } + @Override + public Long getResourceableId() { + return group2.getResourceableId(); + } + }; + + InfoMessage msg1 = infoMessageManager.createInfoMessage(ores2, null, null, id5); + msg1.setTitle("title-1"); + msg1.setMessage("message-1"); + assertNotNull(msg1); + infoMessageManager.saveInfoMessage(msg1); + + InfoMessage msg2 = infoMessageManager.createInfoMessage(ores2, null, null, id2); + msg2.setTitle("title-1"); + msg2.setMessage("message-1"); + assertNotNull(msg2); + infoMessageManager.saveInfoMessage(msg2); + + InfoMessage msg3 = infoMessageManager.createInfoMessage(ores1, null, null, id3); + msg3.setTitle("title-1"); + msg3.setMessage("message-1"); + assertNotNull(msg3); + infoMessageManager.saveInfoMessage(msg3); + + InfoMessage msg4 = infoMessageManager.createInfoMessage(ores1, null, null, id5); + msg4.setTitle("title-1"); + msg4.setMessage("message-1"); + assertNotNull(msg4); + infoMessageManager.saveInfoMessage(msg4); + + InfoMessage msg5 = infoMessageManager.createInfoMessage(ores2, null, null, id5); + msg5.setTitle("title-1"); + msg5.setMessage("message-1"); + assertNotNull(msg5); + infoMessageManager.saveInfoMessage(msg5); + + InfoMessage msg6 = infoMessageManager.createInfoMessage(ores2, null, null, id4); + msg6.setTitle("title-1"); + msg6.setMessage("message-1"); + assertNotNull(msg6); + infoMessageManager.saveInfoMessage(msg6); + + List<InfoMessage> infoMessages = infoMessageManager.loadInfoMessagesOfIdentity(group2, id5); + Assert.assertNotNull(infoMessages); + dbInstance.commitAndCloseSession(); + + Assert.assertEquals(2, infoMessages.size()); + Assert.assertTrue(infoMessages.contains(msg1)); + Assert.assertFalse(infoMessages.contains(msg2)); + Assert.assertFalse(infoMessages.contains(msg3)); + Assert.assertFalse(infoMessages.contains(msg4)); + Assert.assertTrue(infoMessages.contains(msg5)); + Assert.assertFalse(infoMessages.contains(msg6)); + } + + @Test public void testLoadByResource2() { final String resName = UUID.randomUUID().toString(); diff --git a/src/test/java/org/olat/commons/info/InfoMessageFrontendManagerTest.java b/src/test/java/org/olat/commons/info/InfoMessageFrontendManagerTest.java new file mode 100644 index 00000000000..7c68049f898 --- /dev/null +++ b/src/test/java/org/olat/commons/info/InfoMessageFrontendManagerTest.java @@ -0,0 +1,344 @@ +/** + * <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.info; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.util.List; +import java.util.Random; +import java.util.UUID; + +import org.junit.Assert; +import org.junit.Test; +import org.olat.commons.info.manager.InfoMessageFrontendManager; +import org.olat.commons.info.model.InfoMessage; +import org.olat.core.commons.persistence.DB; +import org.olat.core.commons.services.notifications.NotificationsManager; +import org.olat.core.commons.services.notifications.Publisher; +import org.olat.core.commons.services.notifications.PublisherData; +import org.olat.core.commons.services.notifications.SubscriptionContext; +import org.olat.core.id.Identity; +import org.olat.core.id.OLATResourceable; +import org.olat.group.BusinessGroup; +import org.olat.group.BusinessGroupService; +import org.olat.repository.RepositoryEntry; +import org.olat.test.JunitTestHelper; +import org.olat.test.OlatTestCase; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Initial Date: 20.03.2017 + * + * @author fkiefer, fabian.kiefer@frentix.com, www.frentix.com + */ +public class InfoMessageFrontendManagerTest extends OlatTestCase { + + @Autowired + private NotificationsManager notificationManager; + @Autowired + private InfoMessageFrontendManager infoManager; + @Autowired + private BusinessGroupService groupService; + @Autowired + private DB dbInstance; + + @Test + public void createSaveLoadAndCountInfoMessage() { + // same methods as already tested @InfoManagerTest + Identity id1 = JunitTestHelper.createAndPersistIdentityAsRndUser("info-1"); + Identity id2 = JunitTestHelper.createAndPersistIdentityAsRndUser("info-2"); + Identity id3 = JunitTestHelper.createAndPersistIdentityAsRndUser("info-3"); + final String resName = UUID.randomUUID().toString(); + Random random = new Random(); + final InfoOLATResourceable ores = new InfoOLATResourceable(random.nextLong(), resName); + // create, save + InfoMessage msg1 = infoManager.createInfoMessage(ores, InfoMessageFrontendManager.businessGroupResSubPath, null, + id2); + msg1.setTitle("title-1"); + msg1.setMessage("message-1"); + assertNotNull(msg1); + infoManager.saveInfoMessage(msg1); + // create, save + InfoMessage msg2 = infoManager.createInfoMessage(ores, InfoMessageFrontendManager.businessGroupResSubPath, null, + id3); + msg2.setTitle("title-2"); + msg2.setMessage("message-2"); + assertNotNull(msg2); + infoManager.saveInfoMessage(msg2); + // create, not save + InfoMessage msg3 = infoManager.createInfoMessage(ores, InfoMessageFrontendManager.businessGroupResSubPath, null, + id1); + msg3.setTitle("title-3"); + msg3.setMessage("message-3"); + assertNotNull(msg3); + infoManager.saveInfoMessage(msg3); + + dbInstance.commitAndCloseSession(); + + // load by key + InfoMessage loadedMsg1 = infoManager.loadInfoMessage(msg1.getKey()); + assertNotNull(loadedMsg1); + InfoMessage loadedMsg2 = infoManager.loadInfoMessage(msg2.getKey()); + assertNotNull(loadedMsg2); + InfoMessage loadedMsg3 = infoManager.loadInfoMessage(msg3.getKey()); + assertNotNull(loadedMsg3); + + // load by resource + List<InfoMessage> loadedMessages = infoManager.loadInfoMessageByResource(ores, + InfoMessageFrontendManager.businessGroupResSubPath, null, null, null, 0, 0); + assertNotNull(loadedMessages); + Assert.assertEquals(3, loadedMessages.size()); + Assert.assertTrue(loadedMessages.contains(msg1)); + Assert.assertTrue(loadedMessages.contains(msg2)); + Assert.assertTrue(loadedMessages.contains(msg3)); + + // count info messages + int count = infoManager.countInfoMessageByResource(ores, null, null, null, null); + Assert.assertEquals(3, count); + } + + @Test + public void deleteInfoMessage() { + Identity id1 = JunitTestHelper.createAndPersistIdentityAsRndUser("info-1"); + Identity id2 = JunitTestHelper.createAndPersistIdentityAsRndUser("info-2"); + final String resName = UUID.randomUUID().toString(); + final InfoOLATResourceable ores = new InfoOLATResourceable(5l, resName); + // create, save + InfoMessage msg1 = infoManager.createInfoMessage(ores, InfoMessageFrontendManager.businessGroupResSubPath, null, + id2); + msg1.setTitle("title-1"); + msg1.setMessage("message-1"); + assertNotNull(msg1); + infoManager.saveInfoMessage(msg1); + // create, save + InfoMessage msg2 = infoManager.createInfoMessage(ores, InfoMessageFrontendManager.businessGroupResSubPath, null, + id1); + msg2.setTitle("title-2"); + msg2.setMessage("message-2"); + assertNotNull(msg2); + infoManager.saveInfoMessage(msg2); + + dbInstance.commitAndCloseSession(); + + infoManager.deleteInfoMessage(msg1); + dbInstance.commitAndCloseSession(); + + InfoMessage loadedMsg1 = infoManager.loadInfoMessage(msg1.getKey()); + Assert.assertNull(loadedMsg1); + InfoMessage loadedMsg2 = infoManager.loadInfoMessage(msg2.getKey()); + assertNotNull(loadedMsg2); + } + + @Test + public void deleteInfoMessagesOfIdentity() { + Identity id1 = JunitTestHelper.createAndPersistIdentityAsRndUser("info-1"); + Identity id2 = JunitTestHelper.createAndPersistIdentityAsRndUser("info-2"); + RepositoryEntry resource = JunitTestHelper.createAndPersistRepositoryEntry(); + BusinessGroup businessGroup = groupService.createBusinessGroup(null, "gdao1", "gdao1-desc", -1, -1, false, + false, resource); + final OLATResourceable ores = new OLATResourceable() { + @Override + public String getResourceableTypeName() { + return businessGroup.getResourceableTypeName(); + } + + @Override + public Long getResourceableId() { + return businessGroup.getResourceableId(); + } + }; + + // create, save + InfoMessage msg1 = infoManager.createInfoMessage(ores, InfoMessageFrontendManager.businessGroupResSubPath, null, + id2); + msg1.setTitle("title-1"); + msg1.setMessage("message-1"); + assertNotNull(msg1); + infoManager.saveInfoMessage(msg1); + // create, save + InfoMessage msg2 = infoManager.createInfoMessage(ores, InfoMessageFrontendManager.businessGroupResSubPath, null, + id1); + msg2.setTitle("title-2"); + msg2.setMessage("message-2"); + assertNotNull(msg2); + infoManager.saveInfoMessage(msg2); + // create, save + InfoMessage msg3 = infoManager.createInfoMessage(ores, InfoMessageFrontendManager.businessGroupResSubPath, null, + id1); + msg3.setTitle("title-3"); + msg3.setMessage("message-3"); + assertNotNull(msg3); + infoManager.saveInfoMessage(msg3); + + dbInstance.commitAndCloseSession(); + + infoManager.deleteInfoMessagesOfIdentity(businessGroup, id1); + dbInstance.commitAndCloseSession(); + + // load messages after deletion + List<InfoMessage> loadedMessages = infoManager.loadInfoMessageByResource(ores, + InfoMessageFrontendManager.businessGroupResSubPath, null, null, null, 0, 0); + Assert.assertEquals(1, loadedMessages.size()); + Assert.assertTrue(loadedMessages.contains(msg1)); + Assert.assertFalse(loadedMessages.contains(msg2)); + Assert.assertFalse(loadedMessages.contains(msg3)); + } + + @Test + public void removeInfoMessagesAndSubscriptionContext() { + Identity id1 = JunitTestHelper.createAndPersistIdentityAsRndUser("info-1"); + Identity id2 = JunitTestHelper.createAndPersistIdentityAsRndUser("info-2"); + RepositoryEntry resource = JunitTestHelper.createAndPersistRepositoryEntry(); + BusinessGroup businessGroup = groupService.createBusinessGroup(null, "gdao1", "gdao1-desc", -1, -1, false, + false, resource); + final OLATResourceable ores = new OLATResourceable() { + @Override + public String getResourceableTypeName() { + return businessGroup.getResourceableTypeName(); + } + + @Override + public Long getResourceableId() { + return businessGroup.getResourceableId(); + } + }; + // create, save + InfoMessage msg1 = infoManager.createInfoMessage(ores, InfoMessageFrontendManager.businessGroupResSubPath, null, + id2); + msg1.setTitle("title-1"); + msg1.setMessage("message-1"); + assertNotNull(msg1); + infoManager.saveInfoMessage(msg1); + // create, save + InfoMessage msg2 = infoManager.createInfoMessage(ores, InfoMessageFrontendManager.businessGroupResSubPath, null, + id1); + msg2.setTitle("title-2"); + msg2.setMessage("message-2"); + assertNotNull(msg2); + infoManager.saveInfoMessage(msg2); + // create, save + InfoMessage msg3 = infoManager.createInfoMessage(ores, InfoMessageFrontendManager.businessGroupResSubPath, null, + id1); + msg3.setTitle("title-3"); + msg3.setMessage("message-3"); + assertNotNull(msg3); + infoManager.saveInfoMessage(msg3); + + dbInstance.commitAndCloseSession(); + + SubscriptionContext sc = new SubscriptionContext(businessGroup.getResourceableTypeName(), + businessGroup.getResourceableId(), InfoMessageFrontendManager.businessGroupResSubPath); + PublisherData pd = new PublisherData("InfoMessage", "e.g. infoMessage=anyMessage", null); + // subscribe + notificationManager.subscribe(id1, sc, pd); + notificationManager.subscribe(id2, sc, pd); + dbInstance.closeSession(); + + // check if publisher was created + Publisher p = notificationManager.getPublisher(sc); + assertNotNull(p); + + // check before message deletion + List<InfoMessage> loadedMessages1 = infoManager.loadInfoMessageByResource(ores, + InfoMessageFrontendManager.businessGroupResSubPath, null, null, null, 0, 0); + Assert.assertEquals(3, loadedMessages1.size()); + Assert.assertTrue(loadedMessages1.contains(msg1)); + Assert.assertTrue(loadedMessages1.contains(msg2)); + Assert.assertTrue(loadedMessages1.contains(msg3)); + // delete + infoManager.removeInfoMessagesAndSubscriptionContext(businessGroup); + dbInstance.commitAndCloseSession(); + // check if messages are deleted + List<InfoMessage> loadedMessages2 = infoManager.loadInfoMessageByResource(ores, + InfoMessageFrontendManager.businessGroupResSubPath, null, null, null, 0, 0); + Assert.assertEquals(0, loadedMessages2.size()); + Assert.assertFalse(loadedMessages2.contains(msg1)); + Assert.assertFalse(loadedMessages2.contains(msg2)); + Assert.assertFalse(loadedMessages2.contains(msg3)); + // check if pubisher is deleted + Publisher p2 = notificationManager.getPublisher(sc); + assertNull("publisher marked deleted should not be found", p2); + + } + + @Test + public void sendInfoMessage() { + // TODO + } + + @Test + public void getInfoSubscribers() { + Identity id1 = JunitTestHelper.createAndPersistIdentityAsRndUser("info-1"); + Identity id2 = JunitTestHelper.createAndPersistIdentityAsRndUser("info-2"); + RepositoryEntry resource = JunitTestHelper.createAndPersistRepositoryEntry(); + BusinessGroup businessGroup = groupService.createBusinessGroup(null, "gdao", "gdao-desc", -1, -1, false, false, + resource); + final OLATResourceable ores = new OLATResourceable() { + @Override + public String getResourceableTypeName() { + return businessGroup.getResourceableTypeName(); + } + + @Override + public Long getResourceableId() { + return businessGroup.getResourceableId(); + } + }; + // create publisher data + String identifier = InfoMessageFrontendManager.businessGroupResSubPath; + SubscriptionContext context = new SubscriptionContext(businessGroup.getResourceableTypeName(), + businessGroup.getResourceableId(), identifier); + PublisherData publisherData = new PublisherData("testGetSubscriberIdentities", "e.g. data=infomessage", null); + dbInstance.commitAndCloseSession(); + + // add subscribers + notificationManager.subscribe(id1, context, publisherData); + notificationManager.subscribe(id2, context, publisherData); + dbInstance.commitAndCloseSession(); + + // get identities + List<Identity> identities = infoManager.getInfoSubscribers(ores, identifier); + Assert.assertNotNull(identities); + Assert.assertEquals(2, identities.size()); + Assert.assertTrue(identities.contains(id1)); + Assert.assertTrue(identities.contains(id2)); + } + + private class InfoOLATResourceable implements OLATResourceable { + private final Long resId; + private final String resName; + + public InfoOLATResourceable(Long resId, String resName) { + this.resId = resId; + this.resName = resName; + } + + @Override + public String getResourceableTypeName() { + return resName; + } + + @Override + public Long getResourceableId() { + return resId; + } + } +} -- GitLab