diff --git a/portal/portal-api/api/pom.xml b/portal/portal-api/api/pom.xml
index 1bbf45aabb..20a2e6fb81 100644
--- a/portal/portal-api/api/pom.xml
+++ b/portal/portal-api/api/pom.xml
@@ -27,10 +27,30 @@
org.sakaiproject.kernel
sakai-component-manager
+
+ org.sakaiproject.message
+ sakai-message-api
+
+
+ org.sakaiproject.announcement
+ sakai-announcement-api
+
+
+ org.sakaiproject.assignment
+ sakai-assignment-api
+
+
+ org.sakaiproject.commons
+ commons-api
+
javax.portlet
portlet-api
+
+ org.sakaiproject.profile2
+ profile2-api
+
javax.servlet
javax.servlet-api
@@ -39,6 +59,14 @@
org.hibernate
hibernate-core
+
+ org.apache.commons
+ commons-lang3
+
+
+ org.springframework
+ spring-context
+
diff --git a/portal/portal-api/api/src/java/org/sakaiproject/portal/api/BullhornData.java b/portal/portal-api/api/src/java/org/sakaiproject/portal/api/BullhornData.java
new file mode 100644
index 0000000000..0c35ff90eb
--- /dev/null
+++ b/portal/portal-api/api/src/java/org/sakaiproject/portal/api/BullhornData.java
@@ -0,0 +1,15 @@
+package org.sakaiproject.portal.api;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@AllArgsConstructor @Getter
+public class BullhornData {
+
+ private String from;
+ private String to;
+ private String siteId;
+ private String title;
+ private String url;
+ private boolean social;
+}
diff --git a/portal/portal-api/api/src/java/org/sakaiproject/portal/api/BullhornHandler.java b/portal/portal-api/api/src/java/org/sakaiproject/portal/api/BullhornHandler.java
new file mode 100644
index 0000000000..46ffee36f6
--- /dev/null
+++ b/portal/portal-api/api/src/java/org/sakaiproject/portal/api/BullhornHandler.java
@@ -0,0 +1,41 @@
+/**********************************************************************************
+ * $URL$
+ * $Id$
+ ***********************************************************************************
+ *
+ * Copyright (c) 2005, 2006, 2007, 2008 The Sakai Foundation
+ *
+ * Licensed under the Educational Community License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.opensource.org/licenses/ECL-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ **********************************************************************************/
+
+package org.sakaiproject.portal.api;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import org.sakaiproject.event.api.Event;
+import org.sakaiproject.memory.api.Cache;
+
+/**
+ * A handler of events for the bullhorns service. Produces BullhornData
objects for
+ * the service to add to the bullhorn alerts table
+ *
+ * @author Adrian Fish
+ */
+public interface BullhornHandler {
+
+ public String getHandledEvent();
+ public Optional> handleEvent(Event e, Cache countCache);
+}
diff --git a/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/AbstractBullhornHandler.java b/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/AbstractBullhornHandler.java
new file mode 100644
index 0000000000..5e75c98756
--- /dev/null
+++ b/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/AbstractBullhornHandler.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright (c) 2003-2017 The Apereo Foundation
+ *
+ * Licensed under the Educational Community License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://opensource.org/licenses/ecl2
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sakaiproject.portal.beans.bullhornhandlers;
+
+import java.util.Arrays;
+
+import javax.inject.Inject;
+
+import org.sakaiproject.authz.api.SecurityAdvisor;
+import org.sakaiproject.authz.api.SecurityService;
+import org.sakaiproject.portal.api.BullhornHandler;
+
+abstract class AbstractBullhornHandler implements BullhornHandler {
+
+ @Inject
+ protected SecurityService securityService;
+
+ protected SecurityAdvisor unlock(final String[] functions) {
+
+ SecurityAdvisor securityAdvisor = (String userId, String function, String reference) -> {
+
+ if (functions != null) {
+ if (Arrays.asList(functions).contains(function)) {
+ return SecurityAdvisor.SecurityAdvice.ALLOWED;
+ } else {
+ return SecurityAdvisor.SecurityAdvice.NOT_ALLOWED;
+ }
+ } else {
+ return SecurityAdvisor.SecurityAdvice.ALLOWED;
+ }
+ };
+
+ securityService.pushAdvisor(securityAdvisor);
+ return securityAdvisor;
+ }
+
+ protected void lock(SecurityAdvisor sa) {
+ securityService.popAdvisor(sa);
+ }
+}
diff --git a/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/AddAssignmentBullhornHandler.java b/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/AddAssignmentBullhornHandler.java
new file mode 100644
index 0000000000..269c508c1e
--- /dev/null
+++ b/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/AddAssignmentBullhornHandler.java
@@ -0,0 +1,107 @@
+/**
+ * Copyright (c) 2003-2017 The Apereo Foundation
+ *
+ * Licensed under the Educational Community License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://opensource.org/licenses/ecl2
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sakaiproject.portal.beans.bullhornhandlers;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import java.time.Instant;
+
+import javax.inject.Inject;
+
+import org.sakaiproject.assignment.api.AssignmentConstants;
+import org.sakaiproject.assignment.api.AssignmentService;
+import org.sakaiproject.assignment.api.AssignmentServiceConstants;
+import org.sakaiproject.assignment.api.model.Assignment;
+import org.sakaiproject.authz.api.AuthzGroupService;
+import org.sakaiproject.authz.api.SecurityAdvisor;
+import org.sakaiproject.event.api.Event;
+import org.sakaiproject.memory.api.Cache;
+import org.sakaiproject.portal.api.BullhornData;
+import org.sakaiproject.site.api.Site;
+import org.sakaiproject.site.api.SiteService;
+
+import org.springframework.stereotype.Component;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Component
+public class AddAssignmentBullhornHandler extends AbstractBullhornHandler {
+
+ @Inject
+ private AssignmentService assignmentService;
+
+ @Inject
+ private AuthzGroupService authzGroupService;
+
+ @Inject
+ private SiteService siteService;
+
+ @Override
+ public String getHandledEvent() {
+ return AssignmentConstants.EVENT_ADD_ASSIGNMENT;
+ }
+
+ @Override
+ public Optional> handleEvent(Event e, Cache countCache) {
+
+ String from = e.getUserId();
+
+ String ref = e.getResource();
+ String[] pathParts = ref.split("/");
+
+ String siteId = pathParts[3];
+ String assignmentId = pathParts[pathParts.length - 1];
+ SecurityAdvisor sa = unlock(new String[] {AssignmentServiceConstants.SECURE_ACCESS_ASSIGNMENT, AssignmentServiceConstants.SECURE_ADD_ASSIGNMENT_SUBMISSION});
+ try {
+ Assignment assignment = assignmentService.getAssignment(assignmentId);
+ Instant openTime = assignment.getOpenDate();
+ if (openTime == null || openTime.isBefore(Instant.now())) {
+ Site site = siteService.getSite(siteId);
+ String title = assignment.getTitle();
+ String url = assignmentService.getDeepLink(siteId, assignmentId);
+ Set groupIds = assignment.getGroups();
+ Collection groupsUsers = authzGroupService.getAuthzUsersInGroups(groupIds);
+
+ List bhEvents = new ArrayList<>();
+
+ // Get all the members of the site with read ability
+ for (String to : site.getUsersIsAllowed(AssignmentServiceConstants.SECURE_ACCESS_ASSIGNMENT)) {
+ // If this is a grouped assignment, is 'to' in one of the groups?
+ if (groupIds.size() == 0 || groupsUsers.contains(to)) {
+ if (!from.equals(to) && !securityService.isSuperUser(to)) {
+ bhEvents.add(new BullhornData(from, to, siteId, title, url, false));
+ countCache.remove(to);
+ }
+ }
+ }
+
+ return Optional.of(bhEvents);
+ }
+ } catch (Exception ex) {
+ log.error("Failed to find either the assignment or the site", ex);
+ } finally {
+ lock(sa);
+ }
+
+ return Optional.empty();
+ }
+}
diff --git a/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/AnnouncementsBullhornHandler.java b/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/AnnouncementsBullhornHandler.java
new file mode 100644
index 0000000000..0bebb70349
--- /dev/null
+++ b/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/AnnouncementsBullhornHandler.java
@@ -0,0 +1,107 @@
+/**
+ * Copyright (c) 2003-2017 The Apereo Foundation
+ *
+ * Licensed under the Educational Community License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://opensource.org/licenses/ecl2
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sakaiproject.portal.beans.bullhornhandlers;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+import org.sakaiproject.announcement.api.AnnouncementMessage;
+import org.sakaiproject.announcement.api.AnnouncementMessageHeader;
+import org.sakaiproject.announcement.api.AnnouncementService;
+import org.sakaiproject.authz.api.SecurityAdvisor;
+import org.sakaiproject.component.api.ServerConfigurationService;
+import org.sakaiproject.entity.api.EntityManager;
+import org.sakaiproject.event.api.Event;
+import org.sakaiproject.memory.api.Cache;
+import org.sakaiproject.portal.api.BullhornData;
+import org.sakaiproject.site.api.Site;
+import org.sakaiproject.site.api.SiteService;
+
+import org.springframework.stereotype.Component;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Component
+public class AnnouncementsBullhornHandler extends AbstractBullhornHandler {
+
+ @Inject
+ private AnnouncementService announcementService;
+
+ @Inject
+ private EntityManager entityManager;
+
+ @Inject
+ private ServerConfigurationService serverConfigurationService;
+
+ @Inject
+ private SiteService siteService;
+
+ @Override
+ public String getHandledEvent() {
+ return AnnouncementService.SECURE_ANNC_ADD;
+ }
+
+ @Override
+ public Optional> handleEvent(Event e, Cache countCache) {
+
+ String from = e.getUserId();
+
+ String ref = e.getResource();
+ String[] pathParts = ref.split("/");
+
+ String siteId = pathParts[3];
+
+ SecurityAdvisor sa = unlock(new String[] {AnnouncementService.SECURE_ANNC_READ});
+ try {
+ AnnouncementMessage message
+ = (AnnouncementMessage) announcementService.getMessage(
+ entityManager.newReference(ref));
+
+ if (announcementService.isMessageViewable(message)) {
+ Site site = siteService.getSite(siteId);
+ String toolId = site.getToolForCommonId("sakai.announcements").getId();
+ String url = serverConfigurationService.getPortalUrl() + "/directtool/" + toolId
+ + "?itemReference=" + ref + "&sakai_action=doShowmetadata";
+
+ // In this case title = announcement subject
+ String title
+ = ((AnnouncementMessageHeader) message.getHeader()).getSubject();
+
+ List bhEvents = new ArrayList<>();
+
+ // Get all the members of the site with read ability
+ for (String to : site.getUsersIsAllowed(AnnouncementService.SECURE_ANNC_READ)) {
+ if (!from.equals(to) && !securityService.isSuperUser(to)) {
+ bhEvents.add(new BullhornData(from, to, siteId, title, url, false));
+ countCache.remove(to);
+ }
+ }
+ return Optional.of(bhEvents);
+ }
+ } catch (Exception ex) {
+ log.error("No site with id '" + siteId + "'", ex);
+ } finally {
+ lock(sa);
+ }
+
+ return Optional.empty();
+ }
+}
diff --git a/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/CommonsCommentBullhornHandler.java b/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/CommonsCommentBullhornHandler.java
new file mode 100644
index 0000000000..a74f7b6890
--- /dev/null
+++ b/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/CommonsCommentBullhornHandler.java
@@ -0,0 +1,124 @@
+/**
+ * Copyright (c) 2003-2017 The Apereo Foundation
+ *
+ * Licensed under the Educational Community License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://opensource.org/licenses/ecl2
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sakaiproject.portal.beans.bullhornhandlers;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+import org.sakaiproject.commons.api.CommonsEvents;
+import org.sakaiproject.commons.api.CommonsManager;
+import org.sakaiproject.commons.api.datamodel.Comment;
+import org.sakaiproject.commons.api.datamodel.Post;
+import org.sakaiproject.component.api.ServerConfigurationService;
+import org.sakaiproject.event.api.Event;
+import org.sakaiproject.exception.IdUnusedException;
+import org.sakaiproject.memory.api.Cache;
+import org.sakaiproject.portal.api.BullhornData;
+import org.sakaiproject.site.api.Site;
+import org.sakaiproject.site.api.SiteService;
+
+import org.springframework.stereotype.Component;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Component
+public class CommonsCommentBullhornHandler extends AbstractBullhornHandler {
+
+ @Inject
+ private CommonsManager commonsManager;
+
+ @Inject
+ private ServerConfigurationService serverConfigurationService;
+
+ @Inject
+ private SiteService siteService;
+
+ @Override
+ public String getHandledEvent() {
+ return CommonsEvents.COMMENT_CREATED;
+ }
+
+ @Override
+ public Optional> handleEvent(Event e, Cache countCache) {
+
+ String commentCreator = e.getUserId();
+
+ String ref = e.getResource();
+ final String siteId = e.getContext();
+ String[] pathParts = ref.split("/");
+
+ String postId = pathParts[4];
+
+ // To is always going to be the author of the original post
+ Post post = commonsManager.getPost(postId, true);
+ if (post != null) {
+ if ("SOCIAL".equals(siteId)) {
+ return Optional.empty();
+ }
+
+ String postCreator = post.getCreatorId();
+
+ String url = null;
+ String siteTitle = null;
+ try {
+ Site site = siteService.getSite(siteId);
+ siteTitle = site.getTitle();
+ //ToolConfiguration toolConfig = site.getToolForCommonId("sakai.commons");
+ String toolId = site.getToolForCommonId("sakai.commons").getId();
+ url = serverConfigurationService.getPortalUrl() + "/directtool/"
+ + toolId + "/posts/" + postId;
+ } catch (IdUnusedException ex) {
+ log.error("Couldn't find site " + siteId, ex);
+ return Optional.empty();
+ }
+
+ List bhEvents = new ArrayList<>();
+
+ // First, send an alert to the post author
+ if (!commentCreator.equals(postCreator)) {
+ bhEvents.add(new BullhornData(commentCreator, postCreator, siteId, siteTitle, url, false));
+ countCache.remove(postCreator);
+ }
+
+ List sentAlready = new ArrayList<>();
+
+ // Now, send an alert to anybody else who has commented on this post.
+ for (Comment comment : post.getComments()) {
+
+ String to = comment.getCreatorId();
+
+ // If we're commenting on our own post, no alert needed
+ if (to.equals(post.getCreatorId()) || to.equals(commentCreator)) {
+ continue;
+ }
+
+ if (!sentAlready.contains(to)) {
+ bhEvents.add(new BullhornData(commentCreator, to, siteId, siteTitle, url, false));
+ countCache.remove(to);
+ sentAlready.add(to);
+ }
+ }
+ return Optional.of(bhEvents);
+ }
+
+ return Optional.empty();
+ }
+}
diff --git a/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/FriendConfirmBullhornHandler.java b/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/FriendConfirmBullhornHandler.java
new file mode 100644
index 0000000000..aafa0cfcee
--- /dev/null
+++ b/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/FriendConfirmBullhornHandler.java
@@ -0,0 +1,86 @@
+/**
+ * Copyright (c) 2003-2017 The Apereo Foundation
+ *
+ * Licensed under the Educational Community License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://opensource.org/licenses/ecl2
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sakaiproject.portal.beans.bullhornhandlers;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.sakaiproject.event.api.Event;
+import org.sakaiproject.memory.api.Cache;
+import org.sakaiproject.portal.api.BullhornData;
+import org.sakaiproject.profile2.logic.ProfileLinkLogic;
+import org.sakaiproject.profile2.util.ProfileConstants;
+
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+
+import org.springframework.stereotype.Component;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Component
+public class FriendConfirmBullhornHandler extends AbstractBullhornHandler {
+
+ @Inject
+ private ProfileLinkLogic profileLinkLogic;
+
+ @Inject @Named("org.sakaiproject.springframework.orm.hibernate.GlobalSessionFactory")
+ private SessionFactory sessionFactory;
+
+ @Override
+ public String getHandledEvent() {
+ return ProfileConstants.EVENT_FRIEND_CONFIRM;
+ }
+
+ public boolean isAcademic() {
+ return false;
+ }
+
+ @Override
+ public Optional> handleEvent(Event e, Cache countCache) {
+
+ String from = e.getUserId();
+
+ String ref = e.getResource();
+ String[] pathParts = ref.split("/");
+
+ String to = pathParts[2];
+ Session session = sessionFactory.openSession();
+ Transaction tx = session.beginTransaction();
+ try {
+ session.createQuery("delete BullhornAlert where event = :event and fromUser = :fromUser")
+ .setString("event", ProfileConstants.EVENT_FRIEND_REQUEST)
+ .setString("fromUser", to).executeUpdate();
+ tx.commit();
+ } catch (Exception e1) {
+ log.error("Failed to delete bullhorn request event", e1);
+ tx.rollback();
+ } finally {
+ session.close();
+ }
+ String url = profileLinkLogic.getInternalDirectUrlToUserConnections(to);
+ countCache.remove(to);
+ countCache.remove(from);
+ return Optional.of(Collections.singletonList(new BullhornData(from, to, "", "", url, true)));
+ }
+}
diff --git a/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/FriendIgnoreBullhornHandler.java b/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/FriendIgnoreBullhornHandler.java
new file mode 100644
index 0000000000..60a7fbb359
--- /dev/null
+++ b/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/FriendIgnoreBullhornHandler.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2003-2017 The Apereo Foundation
+ *
+ * Licensed under the Educational Community License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://opensource.org/licenses/ecl2
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sakaiproject.portal.beans.bullhornhandlers;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.sakaiproject.event.api.Event;
+import org.sakaiproject.memory.api.Cache;
+import org.sakaiproject.portal.api.BullhornData;
+import org.sakaiproject.profile2.util.ProfileConstants;
+
+import org.hibernate.Session;
+import org.hibernate.SessionFactory;
+import org.hibernate.Transaction;
+
+import org.springframework.stereotype.Component;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Component
+public class FriendIgnoreBullhornHandler extends AbstractBullhornHandler {
+
+ @Inject @Named("org.sakaiproject.springframework.orm.hibernate.GlobalSessionFactory")
+ private SessionFactory sessionFactory;
+
+ @Override
+ public String getHandledEvent() {
+ return ProfileConstants.EVENT_FRIEND_IGNORE;
+ }
+
+ @Override
+ public Optional> handleEvent(Event e, Cache countCache) {
+
+ String from = e.getUserId();
+
+ String ref = e.getResource();
+ String[] pathParts = ref.split("/");
+
+ String to = pathParts[2];
+ Session session = sessionFactory.openSession();
+ Transaction tx = session.beginTransaction();
+ try {
+ session.createQuery("delete BullhornAlert where event = :event and fromUser = :fromUser")
+ .setString("event", ProfileConstants.EVENT_FRIEND_REQUEST)
+ .setString("fromUser", to).executeUpdate();
+ tx.commit();
+ } catch (Exception e1) {
+ log.error("Failed to delete bullhorn request event", e1);
+ tx.rollback();
+ } finally {
+ session.close();
+ }
+ countCache.remove(from);
+ return Optional.empty();
+ }
+}
diff --git a/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/FriendMessageBullhornHandler.java b/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/FriendMessageBullhornHandler.java
new file mode 100644
index 0000000000..fc6c955571
--- /dev/null
+++ b/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/FriendMessageBullhornHandler.java
@@ -0,0 +1,77 @@
+/**
+ * Copyright (c) 2003-2017 The Apereo Foundation
+ *
+ * Licensed under the Educational Community License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://opensource.org/licenses/ecl2
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sakaiproject.portal.beans.bullhornhandlers;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+import org.sakaiproject.component.api.ServerConfigurationService;
+import org.sakaiproject.event.api.Event;
+import org.sakaiproject.exception.IdUnusedException;
+import org.sakaiproject.memory.api.Cache;
+import org.sakaiproject.portal.api.BullhornData;
+import org.sakaiproject.profile2.util.ProfileConstants;
+import org.sakaiproject.site.api.Site;
+import org.sakaiproject.site.api.SiteService;
+
+import org.springframework.stereotype.Component;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Component
+public class FriendMessageBullhornHandler extends AbstractBullhornHandler {
+
+ @Inject
+ private ServerConfigurationService serverConfigurationService;
+
+ @Inject
+ private SiteService siteService;
+
+ @Override
+ public String getHandledEvent() {
+ return ProfileConstants.EVENT_MESSAGE_SENT;
+ }
+
+ @Override
+ public Optional> handleEvent(Event e, Cache countCache) {
+
+ String from = e.getUserId();
+
+ String ref = e.getResource();
+ String[] pathParts = ref.split("/");
+
+ String to = pathParts[2];
+ String siteId = "~" + to;
+
+ try {
+ Site site = siteService.getSite(siteId);
+ String toolId = site.getToolForCommonId("sakai.profile2").getId();
+ String url = serverConfigurationService.getPortalUrl() + "/site/" + siteId
+ + "/tool/" + toolId + "/messages";
+ countCache.remove(to);
+ return Optional.of(Collections.singletonList(new BullhornData(from, to, siteId, "", url, true)));
+ } catch (IdUnusedException idue) {
+ log.error("No site for id: " + siteId);
+ }
+
+ return Optional.empty();
+ }
+}
diff --git a/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/FriendRequestBullhornHandler.java b/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/FriendRequestBullhornHandler.java
new file mode 100644
index 0000000000..a49e735e0e
--- /dev/null
+++ b/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/FriendRequestBullhornHandler.java
@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) 2003-2017 The Apereo Foundation
+ *
+ * Licensed under the Educational Community License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://opensource.org/licenses/ecl2
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sakaiproject.portal.beans.bullhornhandlers;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+import org.sakaiproject.component.api.ServerConfigurationService;
+import org.sakaiproject.event.api.Event;
+import org.sakaiproject.exception.IdUnusedException;
+import org.sakaiproject.memory.api.Cache;
+import org.sakaiproject.portal.api.BullhornData;
+import org.sakaiproject.profile2.util.ProfileConstants;
+import org.sakaiproject.site.api.Site;
+import org.sakaiproject.site.api.SiteService;
+
+import org.springframework.stereotype.Component;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Component
+public class FriendRequestBullhornHandler extends AbstractBullhornHandler {
+
+ @Inject
+ private ServerConfigurationService serverConfigurationService;
+
+ @Inject
+ private SiteService siteService;
+
+ @Override
+ public String getHandledEvent() {
+ return ProfileConstants.EVENT_FRIEND_REQUEST;
+ }
+
+ public boolean isAcademic() {
+ return false;
+ }
+
+ @Override
+ public Optional> handleEvent(Event e, Cache countCache) {
+
+ String from = e.getUserId();
+
+ String ref = e.getResource();
+ String[] pathParts = ref.split("/");
+
+ String to = pathParts[2];
+ String siteId = "~" + to;
+ try {
+ Site site = siteService.getSite(siteId);
+ String toolId = site.getToolForCommonId("sakai.profile2").getId();
+ String url = serverConfigurationService.getPortalUrl() + "/site/" + siteId
+ + "/tool/" + toolId + "/connections";
+ countCache.remove(to);
+ return Optional.of(Collections.singletonList(new BullhornData(from, to, siteId, "", url, true)));
+ } catch (IdUnusedException idue) {
+ log.error("No site for id: " + siteId);
+ }
+
+ return Optional.empty();
+ }
+}
diff --git a/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/FriendStatusBullhornHandler.java b/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/FriendStatusBullhornHandler.java
new file mode 100644
index 0000000000..4f9d0ef72e
--- /dev/null
+++ b/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/FriendStatusBullhornHandler.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright (c) 2003-2017 The Apereo Foundation
+ *
+ * Licensed under the Educational Community License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://opensource.org/licenses/ecl2
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sakaiproject.portal.beans.bullhornhandlers;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+import org.sakaiproject.event.api.Event;
+import org.sakaiproject.memory.api.Cache;
+import org.sakaiproject.portal.api.BullhornData;
+import org.sakaiproject.profile2.logic.ProfileConnectionsLogic;
+import org.sakaiproject.profile2.logic.ProfileLinkLogic;
+import org.sakaiproject.profile2.util.ProfileConstants;
+import org.sakaiproject.user.api.User;
+
+import org.springframework.stereotype.Component;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Component
+public class FriendStatusBullhornHandler extends AbstractBullhornHandler {
+
+ @Inject
+ private ProfileConnectionsLogic profileConnectionsLogic;
+
+ @Inject
+ private ProfileLinkLogic profileLinkLogic;
+
+ @Override
+ public String getHandledEvent() {
+ return ProfileConstants.EVENT_STATUS_UPDATE;
+ }
+
+ @Override
+ public Optional> handleEvent(Event e, Cache countCache) {
+
+ String from = e.getUserId();
+
+ String ref = e.getResource();
+ String[] pathParts = ref.split("/");
+
+ List bhEvents = new ArrayList<>();
+
+ // Get all the posters friends
+ List connections = profileConnectionsLogic.getConnectedUsersForUserInsecurely(from);
+ for (User connection : connections) {
+ String to = connection.getId();
+ String url = profileLinkLogic.getInternalDirectUrlToUserProfile(to, from);
+ bhEvents.add(new BullhornData(from, to, "", "", url, true));
+ countCache.remove(to);
+ }
+
+ return Optional.of(bhEvents);
+ }
+}
diff --git a/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/GradeAssignmentBullhornHandler.java b/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/GradeAssignmentBullhornHandler.java
new file mode 100644
index 0000000000..5c67ecf655
--- /dev/null
+++ b/portal/portal-api/api/src/java/org/sakaiproject/portal/beans/bullhornhandlers/GradeAssignmentBullhornHandler.java
@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2003-2017 The Apereo Foundation
+ *
+ * Licensed under the Educational Community License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://opensource.org/licenses/ecl2
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sakaiproject.portal.beans.bullhornhandlers;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+import org.sakaiproject.assignment.api.AssignmentConstants;
+import org.sakaiproject.assignment.api.AssignmentService;
+import org.sakaiproject.assignment.api.AssignmentServiceConstants;
+import org.sakaiproject.assignment.api.model.Assignment;
+import org.sakaiproject.assignment.api.model.AssignmentSubmission;
+import org.sakaiproject.authz.api.SecurityAdvisor;
+import org.sakaiproject.event.api.Event;
+import org.sakaiproject.memory.api.Cache;
+import org.sakaiproject.portal.api.BullhornData;
+
+import org.springframework.stereotype.Component;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Component
+public class GradeAssignmentBullhornHandler extends AbstractBullhornHandler {
+
+ @Inject
+ private AssignmentService assignmentService;
+
+ @Override
+ public String getHandledEvent() {
+ return AssignmentConstants.EVENT_GRADE_ASSIGNMENT_SUBMISSION;
+ }
+
+ @Override
+ public Optional> handleEvent(Event e, Cache countCache) {
+
+ String from = e.getUserId();
+
+ String ref = e.getResource();
+ String[] pathParts = ref.split("/");
+
+ String siteId = pathParts[3];
+ String submissionId = pathParts[pathParts.length - 1];
+ SecurityAdvisor sa = unlock(new String[] {AssignmentServiceConstants.SECURE_ACCESS_ASSIGNMENT_SUBMISSION
+ , AssignmentServiceConstants.SECURE_ACCESS_ASSIGNMENT
+ , AssignmentServiceConstants.SECURE_ADD_ASSIGNMENT_SUBMISSION});
+
+ // Without hacking assignment's permissions model, this is only way to
+ // get a submission, other than switching to the submitting user.
+ try {
+ AssignmentSubmission submission = assignmentService.getSubmission(submissionId);
+ if (submission.getGradeReleased()) {
+ Assignment assignment = submission.getAssignment();
+ String title = assignment.getTitle();
+ String url = assignmentService.getDeepLink(siteId, assignment.getId());
+ List bhEvents = new ArrayList<>();
+ submission.getSubmitters().forEach(to -> {
+ bhEvents.add(new BullhornData(from, to.getSubmitter(), siteId, title, url, false));
+ countCache.remove(to.getSubmitter());
+ });
+
+ return Optional.of(bhEvents);
+ }
+ } catch (Exception ex) {
+ log.error("Failed to find either the submission or the site", ex);
+ } finally {
+ lock(sa);
+ }
+
+ return Optional.empty();
+ }
+}
diff --git a/portal/portal-impl/impl/src/bundle/bullhorns.properties b/portal/portal-impl/impl/src/bundle/bullhorns.properties
index a3e91635f6..ee484800c6 100644
--- a/portal/portal-impl/impl/src/bundle/bullhorns.properties
+++ b/portal/portal-impl/impl/src/bundle/bullhorns.properties
@@ -15,5 +15,5 @@ assignmentCreated = \u0020created a new assignment "{0}" in "{1}"
assignmentSubmissionGraded = \u0020graded your submission for assignment "{0}" in "{1}"
clearAll = Clear All
unrecognisedAlert = Unrecognised alert
-academicCommentCreated = \u0020commented on your post in "{0}"
+academicCommentCreated = \u0020commented on your post, or on a post you commented on, in "{0}"
academicLessonBuilderCommentCreate = \u0020commented on a lessons page in "{0}"
diff --git a/portal/portal-service-impl/impl/pom.xml b/portal/portal-service-impl/impl/pom.xml
index cb226173e9..b6bc126860 100644
--- a/portal/portal-service-impl/impl/pom.xml
+++ b/portal/portal-service-impl/impl/pom.xml
@@ -35,30 +35,10 @@
org.sakaiproject.kernel
sakai-kernel-util
-
- org.sakaiproject.announcement
- sakai-announcement-api
-
-
- org.sakaiproject.assignment
- sakai-assignment-api
-
-
- org.sakaiproject.message
- sakai-message-api
-
-
- org.sakaiproject.profile2
- profile2-api
-
org.sakaiproject.lessonbuilder
lessonbuilder-api
-
- org.sakaiproject.commons
- commons-api
-
${project.groupId}
sakai-portal-api
diff --git a/portal/portal-service-impl/impl/src/java/org/sakaiproject/portal/service/BullhornServiceImpl.java b/portal/portal-service-impl/impl/src/java/org/sakaiproject/portal/service/BullhornServiceImpl.java
index 3fbc91d4b7..99d74e5d2e 100644
--- a/portal/portal-service-impl/impl/src/java/org/sakaiproject/portal/service/BullhornServiceImpl.java
+++ b/portal/portal-service-impl/impl/src/java/org/sakaiproject/portal/service/BullhornServiceImpl.java
@@ -15,37 +15,25 @@
*/
package org.sakaiproject.portal.service;
-import java.time.Instant;
-import java.util.*;
-import java.sql.ResultSet;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.function.Function;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Observable;
+import java.util.Observer;
+import java.util.Optional;
+import java.util.stream.Collectors;
import org.apache.commons.lang.StringUtils;
-import org.hibernate.criterion.Restrictions;
-import org.hibernate.criterion.Projections;
-import org.hibernate.HibernateException;
-import org.hibernate.Session;
import org.hibernate.SessionFactory;
-import org.hibernate.Transaction;
-
-import org.sakaiproject.announcement.api.AnnouncementMessage;
-import org.sakaiproject.announcement.api.AnnouncementMessageHeader;
-import org.sakaiproject.announcement.api.AnnouncementService;
-import org.sakaiproject.assignment.api.AssignmentConstants;
-import org.sakaiproject.assignment.api.AssignmentService;
-import org.sakaiproject.assignment.api.AssignmentServiceConstants;
-import org.sakaiproject.assignment.api.model.Assignment;
-import org.sakaiproject.assignment.api.model.AssignmentSubmission;
-import org.sakaiproject.authz.api.AuthzGroupService;
-import org.sakaiproject.authz.api.SecurityAdvisor;
+import org.hibernate.criterion.Projections;
+import org.hibernate.criterion.Restrictions;
import org.sakaiproject.authz.api.SecurityService;
-import org.sakaiproject.commons.api.CommonsEvents;
-import org.sakaiproject.commons.api.CommonsManager;
-import org.sakaiproject.commons.api.datamodel.Comment;
-import org.sakaiproject.commons.api.datamodel.Post;
-import org.sakaiproject.component.api.ComponentManager;
import org.sakaiproject.component.api.ServerConfigurationService;
-import org.sakaiproject.entity.api.EntityManager;
import org.sakaiproject.event.api.Event;
import org.sakaiproject.event.api.EventTrackingService;
import org.sakaiproject.exception.IdUnusedException;
@@ -56,20 +44,18 @@ import org.sakaiproject.lessonbuildertool.SimplePageComment;
import org.sakaiproject.lessonbuildertool.SimplePageItem;
import org.sakaiproject.memory.api.Cache;
import org.sakaiproject.memory.api.MemoryService;
+import org.sakaiproject.portal.api.BullhornData;
+import org.sakaiproject.portal.api.BullhornHandler;
import org.sakaiproject.portal.api.BullhornService;
import org.sakaiproject.portal.beans.BullhornAlert;
-import org.sakaiproject.profile2.logic.ProfileConnectionsLogic;
-import org.sakaiproject.profile2.logic.ProfileLinkLogic;
-import org.sakaiproject.profile2.logic.ProfileStatusLogic;
-import org.sakaiproject.profile2.util.ProfileConstants;
-import org.sakaiproject.site.api.Site;
-import org.sakaiproject.site.api.SitePage;
import org.sakaiproject.site.api.SiteService;
import org.sakaiproject.time.api.Time;
import org.sakaiproject.user.api.User;
import org.sakaiproject.user.api.UserDirectoryService;
import org.sakaiproject.user.api.UserNotDefinedException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
@@ -77,6 +63,7 @@ import org.springframework.transaction.TransactionStatus;
import javax.inject.Inject;
+
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@@ -85,27 +72,11 @@ public class BullhornServiceImpl implements BullhornService, Observer {
private static final List HANDLED_EVENTS = new ArrayList<>();
- @Setter
- private AnnouncementService announcementService;
- @Setter
- private AssignmentService assignmentService;
@Inject
- private AuthzGroupService authzGroupService;
- @Inject
- private CommonsManager commonsManager;
- @Setter
- private EntityManager entityManager;
- @Setter
private EventTrackingService eventTrackingService;
@Setter
private MemoryService memoryService;
@Setter
- private ProfileConnectionsLogic profileConnectionsLogic;
- @Setter
- private ProfileLinkLogic profileLinkLogic;
- @Setter
- private ProfileStatusLogic profileStatusLogic;
- @Setter
private UserDirectoryService userDirectoryService;
@Setter
private SecurityService securityService;
@@ -122,20 +93,20 @@ public class BullhornServiceImpl implements BullhornService, Observer {
private Cache countCache = null;
+ @Autowired
+ private List handlers;
+
+ private Map handlerMap;
+
public void init() {
if (serverConfigurationService.getBoolean("portal.bullhorns.enabled", true)) {
- HANDLED_EVENTS.add(ProfileConstants.EVENT_STATUS_UPDATE);
- HANDLED_EVENTS.add(ProfileConstants.EVENT_FRIEND_REQUEST);
- HANDLED_EVENTS.add(ProfileConstants.EVENT_FRIEND_CONFIRM);
- HANDLED_EVENTS.add(ProfileConstants.EVENT_FRIEND_IGNORE);
- HANDLED_EVENTS.add(ProfileConstants.EVENT_MESSAGE_SENT);
- HANDLED_EVENTS.add(AnnouncementService.SECURE_ANNC_ADD);
- HANDLED_EVENTS.add(AssignmentConstants.EVENT_ADD_ASSIGNMENT);
- HANDLED_EVENTS.add(AssignmentConstants.EVENT_GRADE_ASSIGNMENT_SUBMISSION);
- HANDLED_EVENTS.add(CommonsEvents.COMMENT_CREATED);
HANDLED_EVENTS.add(LessonBuilderEvents.COMMENT_CREATE);
HANDLED_EVENTS.add(SiteService.EVENT_SITE_PUBLISH);
+
+ HANDLED_EVENTS.addAll(handlers.stream().map(h -> h.getHandledEvent()).collect(Collectors.toList()));
+ handlerMap = handlers.stream().collect(Collectors.toMap(BullhornHandler::getHandledEvent, Function.identity()));
+
eventTrackingService.addLocalObserver(this);
}
@@ -156,156 +127,21 @@ public class BullhornServiceImpl implements BullhornService, Observer {
String from = e.getUserId();
long at = e.getEventTime().getTime();
try {
- if (ProfileConstants.EVENT_STATUS_UPDATE.equals(event)) {
- // Get all the posters friends
- List connections = profileConnectionsLogic.getConnectedUsersForUserInsecurely(from);
- for (User connection : connections) {
- String to = connection.getId();
- String url = profileLinkLogic.getInternalDirectUrlToUserProfile(to, from);
- doSocialInsert(from, to, event, ref, e.getEventTime(), url);
- countCache.remove(to);
- }
- } else if (ProfileConstants.EVENT_FRIEND_REQUEST.equals(event)) {
- String to = pathParts[2];
- String siteId = "~" + to;
- Site site = siteService.getSite(siteId);
- String toolId = site.getToolForCommonId("sakai.profile2").getId();
- String url = serverConfigurationService.getPortalUrl() + "/site/" + siteId
- + "/tool/" + toolId + "/connections";
- doSocialInsert(from, to, event, ref, e.getEventTime(), url);
- countCache.remove(to);
- } else if (ProfileConstants.EVENT_FRIEND_CONFIRM.equals(event)
- || ProfileConstants.EVENT_FRIEND_IGNORE.equals(event)) {
- String to = pathParts[2];
- Session session = sessionFactory.openSession();
- Transaction tx = session.beginTransaction();
- try {
- session.createQuery("delete BullhornAlert where event = :event and fromUser = :fromUser")
- .setString("event", ProfileConstants.EVENT_FRIEND_REQUEST)
- .setString("fromUser", to).executeUpdate();
- tx.commit();
- } catch (Exception e1) {
- log.error("Failed to delete bullhorn request event", e1);
- tx.rollback();
- } finally {
- session.close();
- }
- String url = profileLinkLogic.getInternalDirectUrlToUserConnections(to);
- doSocialInsert(from, to, event, ref, e.getEventTime(), url);
- countCache.remove(from);
- countCache.remove(to);
- } else if (ProfileConstants.EVENT_MESSAGE_SENT.equals(event)) {
- String to = pathParts[2];
- String siteId = "~" + to;
- Site site = siteService.getSite(siteId);
- String toolId = site.getToolForCommonId("sakai.profile2").getId();
- String url = serverConfigurationService.getPortalUrl() + "/site/" + siteId
- + "/tool/" + toolId + "/messages";
- doSocialInsert(from, to, event, ref, e.getEventTime(), url);
- countCache.remove(to);
- } else if (CommonsEvents.COMMENT_CREATED.equals(event)) {
- String type = pathParts[2];
- String postId = pathParts[4];
- // To is always going to be the author of the original post
- Post post = commonsManager.getPost(postId, true);
- if (post != null) {
- Set tos = new HashSet<>();
- String siteId = post.getSiteId();
- String to = post.getCreatorId();
- tos.add(to);
- for (Comment comment : post.getComments()) {
- to = comment.getCreatorId();
- tos.add(to);
- }
- doCommonsCommentInserts(from, event, ref, e, siteId, postId, tos);
- }
- } else if (AnnouncementService.SECURE_ANNC_ADD.equals(event)) {
- String siteId = pathParts[3];
- String announcementId = pathParts[pathParts.length - 1];
-
- SecurityAdvisor sa = unlock(new String[] {AnnouncementService.SECURE_ANNC_READ});
- try {
- AnnouncementMessage message
- = (AnnouncementMessage) announcementService.getMessage(
- entityManager.newReference(ref));
-
- if (announcementService.isMessageViewable(message)) {
- Site site = siteService.getSite(siteId);
- String toolId = site.getToolForCommonId("sakai.announcements").getId();
- String url = serverConfigurationService.getPortalUrl() + "/directtool/" + toolId
- + "?itemReference=" + ref + "&sakai_action=doShowmetadata";
-
- // In this case title = announcement subject
- String title
- = ((AnnouncementMessageHeader) message.getHeader()).getSubject();
-
- // Get all the members of the site with read ability
- for (String to : site.getUsersIsAllowed(AnnouncementService.SECURE_ANNC_READ)) {
- if (!from.equals(to) && !securityService.isSuperUser(to)) {
- doAcademicInsert(from, to, event, ref, title, siteId, e.getEventTime(), url);
- countCache.remove(to);
- }
- }
- }
- } catch (IdUnusedException idue) {
- log.error("No site with id '" + siteId + "'", idue);
- } finally {
- lock(sa);
- }
- } else if (AssignmentConstants.EVENT_ADD_ASSIGNMENT.equals(event)) {
- String siteId = pathParts[3];
- String assignmentId = pathParts[pathParts.length - 1];
- SecurityAdvisor sa = unlock(new String[] {AssignmentServiceConstants.SECURE_ACCESS_ASSIGNMENT, AssignmentServiceConstants.SECURE_ADD_ASSIGNMENT_SUBMISSION});
- try {
- Assignment assignment = assignmentService.getAssignment(assignmentId);
- Instant openTime = assignment.getOpenDate();
- if (openTime == null || openTime.isBefore(Instant.now())) {
- Site site = siteService.getSite(siteId);
- String title = assignment.getTitle();
- String url = assignmentService.getDeepLink(siteId, assignmentId);
- Set groupIds = assignment.getGroups();
- Collection groupsUsers = authzGroupService.getAuthzUsersInGroups(groupIds);
- // Get all the members of the site with read ability
- for (String to : site.getUsersIsAllowed(AssignmentServiceConstants.SECURE_ACCESS_ASSIGNMENT)) {
- // If this is a grouped assignment, is 'to' in one of the groups?
- if (groupIds.size() == 0 || groupsUsers.contains(to)) {
- if (!from.equals(to) && !securityService.isSuperUser(to)) {
- doAcademicInsert(from, to, event, ref, title, siteId, e.getEventTime(), url);
- countCache.remove(to);
- }
- }
+ BullhornHandler handler = handlerMap.get(event);
+
+ if (handler != null ) {
+ Optional> result = handler.handleEvent(e, countCache);
+ if (result.isPresent()) {
+ result.get().forEach(bd -> System.out.println(bd.getTo() + " : " + bd.getTitle()));
+ result.get().forEach(bd -> {
+
+ if (bd.isSocial()) {
+ doSocialInsert(bd.getFrom(), bd.getTo(), event, ref, e.getEventTime(), bd.getUrl());
+ } else {
+ doAcademicInsert(from, bd.getTo(), event, ref, bd.getTitle(),
+ bd.getSiteId(), e.getEventTime(), bd.getUrl());
}
- }
- } catch (IdUnusedException idue) {
- log.error("Failed to find either the assignment or the site", idue);
- } finally {
- lock(sa);
- }
- } else if (AssignmentConstants.EVENT_GRADE_ASSIGNMENT_SUBMISSION.equals(event)) {
- String siteId = pathParts[3];
- String submissionId = pathParts[pathParts.length - 1];
- SecurityAdvisor sa = unlock(new String[] {AssignmentServiceConstants.SECURE_ACCESS_ASSIGNMENT_SUBMISSION
- , AssignmentServiceConstants.SECURE_ACCESS_ASSIGNMENT
- , AssignmentServiceConstants.SECURE_ADD_ASSIGNMENT_SUBMISSION});
-
- // Without hacking assignment's permissions model, this is only way to
- // get a submission, other than switching to the submitting user.
- try {
- AssignmentSubmission submission = assignmentService.getSubmission(submissionId);
- if (submission.getGradeReleased()) {
- Site site = siteService.getSite(siteId);
- Assignment assignment = submission.getAssignment();
- String title = assignment.getTitle();
- String url = assignmentService.getDeepLink(siteId, assignment.getId());
- submission.getSubmitters().forEach(to -> {
- doAcademicInsert(from, to.getSubmitter(), event, ref, title, siteId, e.getEventTime(), url);
- countCache.remove(to.getSubmitter());
- });
- }
- } catch (IdUnusedException idue) {
- log.error("Failed to find either the submission or the site", idue);
- } finally {
- lock(sa);
+ });
}
} else if (LessonBuilderEvents.COMMENT_CREATE.equals(event)) {
try {
@@ -377,38 +213,9 @@ public class BullhornServiceImpl implements BullhornService, Observer {
}
}
- /**
- * Supply null to this and everything will be allowed. Supply
- * a list of functions and only they will be allowed.
- */
- private SecurityAdvisor unlock(final String[] functions) {
-
- SecurityAdvisor securityAdvisor = new SecurityAdvisor() {
- public SecurityAdvice isAllowed(String userId, String function, String reference) {
-
- if (functions != null) {
- if (Arrays.asList(functions).contains(function)) {
- return SecurityAdvice.ALLOWED;
- } else {
- return SecurityAdvice.NOT_ALLOWED;
- }
- } else {
- return SecurityAdvice.ALLOWED;
- }
- }
- };
- securityService.pushAdvisor(securityAdvisor);
- return securityAdvisor;
- }
-
- private void lock(SecurityAdvisor securityAdvisor) {
- securityService.popAdvisor(securityAdvisor);
- }
-
private void doAcademicInsert(String from, String to, String event, String ref
, String title, String siteId, Date eventDate, String url) {
-
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
@@ -460,39 +267,6 @@ public class BullhornServiceImpl implements BullhornService, Observer {
});
}
- private void doCommonsCommentInserts(String from, String event, String ref, Event e, String siteId, String postId, Set tos) {
-
- log.debug("Inserting Commons comment alerts: from is {}, tos is {}, siteId is {}", from, tos, siteId);
- boolean isSocial = siteId.equals("SOCIAL");
- for (String to : tos) {
- // If we're commenting on our own post, no alert needed
- if (!from.equals(to)) {
- String mySiteId = siteId;
- if (isSocial) {
- mySiteId = "~" + to;
- }
- Site site = null;
- try {
- site = siteService.getSite(mySiteId);
- } catch (IdUnusedException ex) {
- log.error("Couldn't find site " + mySiteId, ex);
- }
- if (site != null) {
- String toolId = site.getToolForCommonId("sakai.commons").getId();
- String url = serverConfigurationService.getPortalUrl() + "/directtool/"
- + toolId + "/posts/" + postId;
- if (isSocial) {
- doSocialInsert(from, to, event, ref, e.getEventTime(), url);
- } else {
- String title = "";
- doAcademicInsert(from, to, event, ref, title, siteId, e.getEventTime(), url);
- }
- countCache.remove(to);
- }
- }
- }
- }
-
@Transactional
public List getSocialAlerts(String userId) {
diff --git a/portal/portal-service-impl/impl/src/webapp/WEB-INF/components.xml b/portal/portal-service-impl/impl/src/webapp/WEB-INF/components.xml
index 0e156bb8dd..08c0cfc6f2 100644
--- a/portal/portal-service-impl/impl/src/webapp/WEB-INF/components.xml
+++ b/portal/portal-service-impl/impl/src/webapp/WEB-INF/components.xml
@@ -7,7 +7,7 @@
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
-
+
-
-
-
-
-
-
-