/* * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.curriki.plugin.activitystream.impl; import java.util.ArrayList; import java.util.List; import org.apache.velocity.VelocityContext; import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.api.Document; import com.xpn.xwiki.api.XWiki; import com.xpn.xwiki.doc.XWikiDocument; import com.xpn.xwiki.notify.XWikiDocChangeNotificationInterface; import com.xpn.xwiki.notify.XWikiNotificationRule; import com.xpn.xwiki.objects.BaseObject; import com.xpn.xwiki.plugin.activitystream.api.ActivityEvent; import com.xpn.xwiki.plugin.activitystream.api.ActivityEventPriority; import com.xpn.xwiki.plugin.activitystream.api.ActivityEventType; import com.xpn.xwiki.plugin.activitystream.api.ActivityStreamException; import com.xpn.xwiki.plugin.activitystream.impl.ActivityEventImpl; import com.xpn.xwiki.plugin.activitystream.impl.ActivityStreamImpl; import com.xpn.xwiki.plugin.mailsender.Mail; import com.xpn.xwiki.plugin.mailsender.MailSenderPlugin; import com.xpn.xwiki.plugin.spacemanager.api.Space; import com.xpn.xwiki.plugin.spacemanager.api.SpaceManager; import com.xpn.xwiki.plugin.spacemanager.api.SpaceManagers; import com.xpn.xwiki.plugin.spacemanager.api.SpaceUserProfile; import com.xpn.xwiki.render.XWikiVelocityRenderer; public class CurrikiActivityStream extends ActivityStreamImpl { private static final String CURRIKI_SPACE_TYPE = "currikispace"; private static final String SPACE_CLASS_NAME = "XWiki.SpaceClass"; public static final String DOCUMENTATION_FILE = "documentation-file"; public static final String DOCUMENTATION_WIKI = "documentation-wiki"; public CurrikiActivityStream() { super(); } /** * {@inheritDoc} */ public void notify(XWikiNotificationRule rule, XWikiDocument newdoc, XWikiDocument olddoc, int event, XWikiContext context) { try { String spaceName = newdoc.getSpace(); if (spaceName == null) { return; } if (spaceName.startsWith("Messages_Group_")) { handleMessageEvent(newdoc, olddoc, event, context); } else if (spaceName.startsWith("Documentation_Group_")) { handleDocumentationEvent(newdoc, olddoc, event, context); } else if (spaceName.startsWith("Coll_Group_")) { handleResourceEvent(newdoc, olddoc, event, context); } else if (spaceName.startsWith("UserProfiles_Group_")) { handleMemberEvent(newdoc, olddoc, event, context); } // TODO handle events from MemberGroup, AdminGroup and Role_<roleName>Group } catch (Throwable t) { // Error in activity stream notify should be ignored but logged in the log file t.printStackTrace(); } } protected void handleMessageEvent(XWikiDocument newdoc, XWikiDocument olddoc, int event, XWikiContext context) { String streamName = getStreamName(newdoc.getSpace(), context); if (streamName == null) { return; } BaseObject article = newdoc.getObject("XWiki.ArticleClass"); if (article == null) { if (olddoc == null) { return; } article = olddoc.getObject("XWiki.ArticleClass"); if (article == null) { return; } event = XWikiDocChangeNotificationInterface.EVENT_DELETE; } else { double version = Double.parseDouble(newdoc.getVersion()); double initialVersion = 4.1; if ((olddoc != null && olddoc.getObject("XWiki.ArticleClass") == null) || (olddoc == null && version == initialVersion)) { event = XWikiDocChangeNotificationInterface.EVENT_NEW; } else if (version < initialVersion) { return; } } boolean notify = false; String level = "message"; if ("commentadd".equals(context.getAction())) { event = XWikiDocChangeNotificationInterface.EVENT_NEW; level = "comment"; notify = true; } List params = new ArrayList(); params.add(article.getStringValue("title")); params.add(getUserName(context.getUser(), context)); params.add(level); try { switch (event) { case XWikiDocChangeNotificationInterface.EVENT_NEW: addDocumentActivityEvent(streamName, newdoc, ActivityEventType.CREATE, ActivityEventPriority.NOTIFICATION, "", params, context); break; case XWikiDocChangeNotificationInterface.EVENT_CHANGE: addDocumentActivityEvent(streamName, newdoc, ActivityEventType.UPDATE, ActivityEventPriority.NOTIFICATION, "", params, context); notify = true; break; case XWikiDocChangeNotificationInterface.EVENT_DELETE: addDocumentActivityEvent(streamName, newdoc, ActivityEventType.DELETE, ActivityEventPriority.NOTIFICATION, "", params, context); break; } if (notify) { sendUpdateNotification(newdoc.getSpace().substring("Messages_".length()), newdoc, context); } } catch (Throwable e) { // Error in activity stream notify should be ignored but logged in the log file e.printStackTrace(); } } protected void handleDocumentationEvent(XWikiDocument newdoc, XWikiDocument olddoc, int event, XWikiContext context) { String streamName = getStreamName(newdoc.getSpace(), context); if (streamName == null) { return; } String docTitle = newdoc.getTitle(); BaseObject tag = newdoc.getObject("XWiki.TagClass"); if (tag == null) { if (olddoc == null) { return; } tag = olddoc.getObject("XWiki.TagClass"); if (tag == null) { return; } docTitle = olddoc.getTitle(); event = XWikiDocChangeNotificationInterface.EVENT_DELETE; } else { double version = Double.parseDouble(newdoc.getVersion()); double initialVersion = 4.1; if (tag.getStringValue("tags").contains(DOCUMENTATION_WIKI)) { initialVersion = 3.1; } if ((olddoc != null && olddoc.getObject("XWiki.TagClass") == null) || (olddoc == null && version == initialVersion)) { event = XWikiDocChangeNotificationInterface.EVENT_NEW; } else if (version < initialVersion) { return; } } String docType = tag.getStringValue("tags"); if (docType.contains(DOCUMENTATION_FILE)) { docType = DOCUMENTATION_FILE; } else if (docType.contains(DOCUMENTATION_WIKI)) { docType = DOCUMENTATION_WIKI; } else { return; } List params = new ArrayList(); params.add(docTitle); params.add(getUserName(context.getUser(), context)); params.add(docType); try { switch (event) { case XWikiDocChangeNotificationInterface.EVENT_NEW: addDocumentActivityEvent(streamName, newdoc, ActivityEventType.CREATE, ActivityEventPriority.NOTIFICATION, "", params, context); break; case XWikiDocChangeNotificationInterface.EVENT_CHANGE: addDocumentActivityEvent(streamName, newdoc, ActivityEventType.UPDATE, ActivityEventPriority.NOTIFICATION, "", params, context); sendUpdateNotification( newdoc.getSpace().substring("Documentation_".length()), newdoc, context); break; case XWikiDocChangeNotificationInterface.EVENT_DELETE: addDocumentActivityEvent(streamName, newdoc, ActivityEventType.DELETE, ActivityEventPriority.NOTIFICATION, "", params, context); break; } } catch (Throwable e) { // Error in activity stream notify should be ignored but logged in the log file e.printStackTrace(); } } protected void handleResourceEvent(XWikiDocument newdoc, XWikiDocument olddoc, int event, XWikiContext context) { String streamName = getStreamName(newdoc.getSpace(), context); if (streamName == null) { return; } BaseObject asset = newdoc.getObject("CurrikiCode.AssetClass"); if (asset == null) { if (olddoc == null) { return; } asset = olddoc.getObject("CurrikiCode.AssetClass"); if (asset == null) { return; } event = XWikiDocChangeNotificationInterface.EVENT_DELETE; } else { double version = Double.parseDouble(newdoc.getVersion()); if (version==2.1) { event = XWikiDocChangeNotificationInterface.EVENT_NEW; } else if (!newdoc.getVersion().endsWith(".1")||version==1.1) { // we ignore minor version edits and the first version return; } } List params = new ArrayList(); params.add(newdoc.getTitle()); params.add(getUserName(context.getUser(), context)); try { switch (event) { case XWikiDocChangeNotificationInterface.EVENT_NEW: addDocumentActivityEvent(streamName, newdoc, ActivityEventType.CREATE, ActivityEventPriority.NOTIFICATION, "", params, context); break; case XWikiDocChangeNotificationInterface.EVENT_CHANGE: addDocumentActivityEvent(streamName, newdoc, ActivityEventType.UPDATE, ActivityEventPriority.NOTIFICATION, "", params, context); sendUpdateNotification(newdoc.getSpace().substring("Coll_".length()), newdoc, context); break; case XWikiDocChangeNotificationInterface.EVENT_DELETE: addDocumentActivityEvent(streamName, newdoc, ActivityEventType.DELETE, ActivityEventPriority.NOTIFICATION, "", params, context); break; } } catch (Throwable e) { // Error in activity stream notify should be ignored but logged in the log file e.printStackTrace(); } } protected void handleMemberEvent(XWikiDocument newdoc, XWikiDocument olddoc, int event, XWikiContext context) { String streamName = getStreamName(newdoc.getSpace(), context); if (streamName == null) { return; } BaseObject profile = newdoc.getObject("XWiki.SpaceUserProfileClass"); if (profile == null) { if (olddoc == null) { return; } profile = olddoc.getObject("XWiki.SpaceUserProfileClass"); if (profile == null) { return; } event = XWikiDocChangeNotificationInterface.EVENT_DELETE; } else { double version = Double.parseDouble(newdoc.getVersion()); double initialVersion = 2.1; if ((olddoc != null && olddoc.getObject("XWiki.SpaceUserProfileClass") == null) || (olddoc == null && version == initialVersion)) { event = XWikiDocChangeNotificationInterface.EVENT_NEW; } else if (version < initialVersion) { return; } } try { String profileName = profile.getName(); String user = "XWiki." + profileName.substring(profileName.indexOf(".") + 1); XWikiDocument userDoc = context.getWiki().getDocument(user, context); List params = new ArrayList(); params.add(user); params.add(getUserName(user, context)); ActivityEvent activityEvent = new ActivityEventImpl(); activityEvent.setStream(streamName); activityEvent.setPriority(ActivityEventPriority.NOTIFICATION); activityEvent.setUrl(userDoc.getExternalURL("view", context)); activityEvent.setParams(params); activityEvent.setDate(newdoc.getDate()); switch (event) { case XWikiDocChangeNotificationInterface.EVENT_NEW: activityEvent.setType(ActivityEventType.CREATE); addActivityEvent(activityEvent, newdoc, context); break; case XWikiDocChangeNotificationInterface.EVENT_CHANGE: activityEvent.setType(ActivityEventType.UPDATE); addActivityEvent(activityEvent, newdoc, context); break; case XWikiDocChangeNotificationInterface.EVENT_DELETE: // ignore break; } } catch (Throwable e) { // Error in activity stream notify should be ignored but logged in the log file e.printStackTrace(); } } /** * {@inheritDoc} */ public String getStreamName(String space, XWikiContext context) { XWikiDocument doc; try { doc = context.getWiki().getDocument(space, "WebPreferences", context); String type = doc.getStringValue(SPACE_CLASS_NAME, "type"); if (CURRIKI_SPACE_TYPE.equals(type)) return space; String parentSpace = space.substring(space.indexOf("_") + 1); doc = context.getWiki().getDocument(parentSpace, "WebPreferences", context); type = doc.getStringValue(SPACE_CLASS_NAME, "type"); if (CURRIKI_SPACE_TYPE.equals(type)) return parentSpace; // could not find a Curriki space return null; } catch (Exception e) { return null; } } private String getUserName(String user, XWikiContext context) { String userName = null; try { XWikiDocument userDoc = context.getWiki().getDocument(user, context); if (!userDoc.isNew()) { userName = (userDoc.getStringValue("XWiki.XWikiUsers", "first_name") + " " + userDoc .getStringValue("XWiki.XWikiUsers", "last_name")).trim(); } } catch (XWikiException e) { } if (userName == null) { userName = user.substring(user.indexOf(".") + 1); } return userName; } /** * Sends a notification email to the creator of the given document if it has been updated by * another user and he opted in his space profile for this kind of notifications. * * @param spaceName the space the changed document is associated with. Also, the document * creator's profile in this space can block this notification * @param doc the changed document * @param context the XWiki context * @throws Exception */ private void sendUpdateNotification(String spaceName, XWikiDocument doc, XWikiContext context) throws Exception { if (doc.getCreator().equals(context.getUser())) { return; } SpaceManager spaceManager = SpaceManagers.findSpaceManagerForSpace(spaceName, context); SpaceUserProfile profile = spaceManager.getSpaceUserProfile(spaceName, doc.getCreator(), context); if (!profile.getAllowNotificationsFromSelf()) { return; } Space space = spaceManager.getSpace(spaceName, context); String templateDocFullName = "Groups.MailTemplateUpdateNotification"; XWikiDocument mailDoc = context.getWiki().getDocument(templateDocFullName, context); XWikiDocument translatedMailDoc = mailDoc.getTranslatedDocument(context); VelocityContext vContext = new VelocityContext(); vContext.put("space", space); vContext.put("udoc", new Document(doc, context)); vContext.put("xwiki", new XWiki(context.getWiki(), context)); vContext.put("context", new com.xpn.xwiki.api.Context(context)); String mailFrom = context.getWiki().getXWikiPreference("admin_email", context); String mailTo = context.getWiki().getDocument(doc.getCreator(), context).getStringValue( "XWiki.XWikiUsers", "email"); String mailSubject = XWikiVelocityRenderer.evaluate(translatedMailDoc.getTitle(), templateDocFullName, vContext, context); String mailContent = XWikiVelocityRenderer.evaluate(translatedMailDoc.getContent(), templateDocFullName, vContext, context); MailSenderPlugin mailSender = (MailSenderPlugin) context.getWiki().getPlugin("mailsender", context); mailSender.prepareVelocityContext(mailFrom, mailTo, "", "", vContext, context); Mail mail = new Mail(mailFrom, mailTo, null, null, mailSubject, mailContent, null); mailSender.sendMail(mail, context); } /* Override searchEvents to change group by behavior for filter (give create items intead of update items) */ public List<ActivityEvent> searchEvents(String hql, boolean filter, int nb, int start, XWikiContext context) throws ActivityStreamException { String searchHql; if (filter) { searchHql = "select act from ActivityEventImpl as act, ActivityEventImpl as act2 where act.eventId=act2.eventId and " + hql + " group by act.requestId having (act.priority)=max(act2.priority) and (act.type)=min(act.type) order by act.date desc"; } else { searchHql = "select act from ActivityEventImpl as act where " + hql + " order by act.date desc"; } try { return context.getWiki().search(searchHql, nb, start, context); } catch (XWikiException e) { throw new ActivityStreamException(e); } } }