/* =============================================================================== * * Part of the InfoGlue Content Management Platform (www.infoglue.org) * * =============================================================================== * * Copyright (C) * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 2, as published by the * Free Software Foundation. See the file LICENSE.html for more information. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY, including the ied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc. / 59 Temple * Place, Suite 330 / Boston, MA 02111-1307 / USA. * * =============================================================================== */ package org.infoglue.cms.interceptors; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import org.apache.log4j.Logger; import org.exolab.castor.jdo.Database; import org.infoglue.cms.applications.common.VisualFormatter; import org.infoglue.cms.controllers.kernel.impl.simple.BaseController; import org.infoglue.cms.controllers.kernel.impl.simple.CastorDatabaseService; import org.infoglue.cms.controllers.kernel.impl.simple.ContentController; import org.infoglue.cms.controllers.kernel.impl.simple.ContentControllerProxy; import org.infoglue.cms.controllers.kernel.impl.simple.ContentVersionController; import org.infoglue.cms.controllers.kernel.impl.simple.ContentVersionControllerProxy; import org.infoglue.cms.controllers.kernel.impl.simple.InterceptionPointController; import org.infoglue.cms.controllers.kernel.impl.simple.LanguageController; import org.infoglue.cms.controllers.kernel.impl.simple.SiteNodeController; import org.infoglue.cms.controllers.kernel.impl.simple.SiteNodeVersionController; import org.infoglue.cms.controllers.kernel.impl.simple.SubscriptionController; import org.infoglue.cms.controllers.kernel.impl.simple.UserControllerProxy; import org.infoglue.cms.entities.content.Content; import org.infoglue.cms.entities.content.ContentVO; import org.infoglue.cms.entities.content.ContentVersion; import org.infoglue.cms.entities.content.ContentVersionVO; import org.infoglue.cms.entities.kernel.BaseEntityVO; import org.infoglue.cms.entities.management.InterceptionPointVO; import org.infoglue.cms.entities.management.InterceptorVO; import org.infoglue.cms.entities.management.LanguageVO; import org.infoglue.cms.entities.management.SubscriptionVO; import org.infoglue.cms.entities.management.TransactionHistoryVO; import org.infoglue.cms.entities.structure.SiteNode; import org.infoglue.cms.entities.structure.SiteNodeVO; import org.infoglue.cms.entities.structure.SiteNodeVersion; import org.infoglue.cms.entities.structure.SiteNodeVersionVO; import org.infoglue.cms.exception.ConstraintException; import org.infoglue.cms.exception.SystemException; import org.infoglue.cms.security.InfoGluePrincipal; import org.infoglue.cms.security.interceptors.InfoGlueInterceptor; import org.infoglue.cms.util.CmsPropertyHandler; import org.infoglue.cms.util.mail.MailServiceFactory; /** * @author Mattias Bogeblad * * This interceptor is used to handle that subscriptions get's delivered. */ public class SubscriptionsInterceptor extends BaseController implements InfoGlueInterceptor { public class TransactionQueueVO extends TransactionHistoryVO { private String subject; private String description; private InterceptionPointVO interceptionPointVO = null; public InterceptionPointVO getInterceptionPointVO() { return interceptionPointVO; } public void setInterceptionPointVO(InterceptionPointVO interceptionPointVO) { this.interceptionPointVO = interceptionPointVO; } public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } } private final static Logger logger = Logger.getLogger(SubscriptionsInterceptor.class.getName()); private final static VisualFormatter vf = new VisualFormatter(); private static List<TransactionQueueVO> transactionQueue = Collections.synchronizedList(new ArrayList<TransactionQueueVO>()); public void addTransactionHistory(InterceptionPointVO interceptionPointVO, String name, String userName, Integer typeId, String objectName, String objectId, String subject, String description) { TransactionQueueVO transVO = new TransactionQueueVO(); transVO.setInterceptionPointVO(interceptionPointVO); transVO.setName(name); transVO.setSystemUserName(userName); transVO.setTransactionDateTime(java.util.Calendar.getInstance().getTime()); transVO.setTransactionTypeId(typeId); transVO.setTransactionObjectId(objectId); transVO.setTransactionObjectName(objectName); transVO.setSubject(subject); transVO.setDescription(description); synchronized (transactionQueue) { logger.info("Adding transactionQueue:" + interceptionPointVO.getName() + " - " + objectName); transactionQueue.add(transVO); } } public void intercept(InfoGluePrincipal infoGluePrincipal, InterceptionPointVO interceptionPointVO, Map extradata) throws ConstraintException, SystemException, Exception { intercept(infoGluePrincipal, interceptionPointVO, extradata, true); } /** * This method will be called when a interceptionPoint is reached. * * @param interceptionPoint * @param extradata * @throws ConstraintException * @throws SystemException */ public void intercept(InfoGluePrincipal infoGluePrincipal, InterceptionPointVO interceptionPointVO, Map extradata, boolean allowCreatorAccess) throws ConstraintException, SystemException, Exception { Database db = CastorDatabaseService.getDatabase(); try { beginTransaction(db); intercept(infoGluePrincipal, interceptionPointVO, extradata, allowCreatorAccess, db); commitTransaction(db); } catch(Exception e) { logger.error("An error occurred so we should not complete the transaction:" + e, e); rollbackTransaction(db); } } /** * This method will be called when a interceptionPoint is reached and it handle it within a transaction. * * @param interceptionPoint * @param extradata * @throws ConstraintException * @throws SystemException */ public void intercept(InfoGluePrincipal infoGluePrincipal, InterceptionPointVO interceptionPointVO, Map extradata, Database db) throws ConstraintException, SystemException, Exception { intercept(infoGluePrincipal, interceptionPointVO, extradata, true, db); } /** * This method will be called when a interceptionPoint is reached and it handle it within a transaction. * * @param interceptionPoint * @param extradata * @throws ConstraintException * @throws SystemException */ public void intercept(InfoGluePrincipal infoGluePrincipal, InterceptionPointVO interceptionPointVO, Map extradata, boolean allowCreatorAccess, Database db) throws ConstraintException, SystemException, Exception { Locale userLocale = getUserPrefferedLocale(infoGluePrincipal.getName()); String generalFooter = getLocalizedString(userLocale, "tool.common.subscriptionMail.generalFooter"); String entityName = null; String entityId = null; /* if(interceptionPointVO.getName().equalsIgnoreCase("Content.Read")) { Integer contentId = (Integer)extradata.get("contentId"); entityName = Content.class.getName(); entityId = contentId.toString(); } else */if(interceptionPointVO.getName().equalsIgnoreCase("Content.Write")) { Integer contentId = (Integer)extradata.get("contentId"); entityName = Content.class.getName(); entityId = contentId.toString(); } else if(interceptionPointVO.getName().equalsIgnoreCase("Content.Create")) { Integer contentId = (Integer)extradata.get("contentId"); entityName = Content.class.getName(); entityId = contentId.toString(); } else if(interceptionPointVO.getName().equalsIgnoreCase("Content.Delete")) { Integer contentId = (Integer)extradata.get("contentId"); entityName = Content.class.getName(); entityId = contentId.toString(); } else if(interceptionPointVO.getName().equalsIgnoreCase("Content.Move")) { Integer contentId = (Integer)extradata.get("contentId"); entityName = Content.class.getName(); entityId = contentId.toString(); } else if(interceptionPointVO.getName().equalsIgnoreCase("Content.CreateVersion")) { Integer contentId = (Integer)extradata.get("contentId"); entityName = Content.class.getName(); entityId = contentId.toString(); } else if(interceptionPointVO.getName().equalsIgnoreCase("Content.SubmitToPublish")) { Integer contentId = (Integer)extradata.get("contentId"); entityName = Content.class.getName(); entityId = contentId.toString(); } else if(interceptionPointVO.getName().equalsIgnoreCase("Content.ChangeAccessRights")) { Integer contentId = (Integer)extradata.get("contentId"); entityName = Content.class.getName(); entityId = contentId.toString(); } /* else if(interceptionPointVO.getName().equalsIgnoreCase("ContentVersion.Read")) { Integer contentVersionId = (Integer)extradata.get("contentVersionId"); entityName = ContentVersion.class.getName(); entityId = contentVersionId.toString(); } */ else if(interceptionPointVO.getName().equalsIgnoreCase("ContentVersion.Write")) { Integer contentVersionId = (Integer)extradata.get("contentVersionId"); entityName = ContentVersion.class.getName(); entityId = contentVersionId.toString(); ContentVersionVO contentVersionVO = ContentVersionControllerProxy.getController().getContentVersionVOWithId(contentVersionId, db); ContentVO contentVO = ContentControllerProxy.getController().getContentVOWithId(contentVersionVO.getContentId(), db); InterceptionPointVO contentInterceptionPointVO = InterceptionPointController.getController().getInterceptionPointVOWithName("Content.Write", db); String generalSubject = getLocalizedString(userLocale, "tool.common.subscriptionMail.generalSubject"); String generalMessage = getLocalizedString(userLocale, "tool.common.subscriptionMail.generalMessage", entityName, entityId) + generalFooter; String subject = getLocalizedString(userLocale, "tool.common.subscriptionMail.contentUpdatedSubject"); String message = getLocalizedString(userLocale, "tool.common.subscriptionMail.contentUpdatedMessage", contentVO.getName()) + generalFooter; addTransactionHistory(contentInterceptionPointVO, "SubscriptionsEvents", infoGluePrincipal.getName(), new Integer(999), Content.class.getName(), contentVersionVO.getContentId().toString(), subject, message); addTransactionHistory(interceptionPointVO, "SubscriptionsEvents", infoGluePrincipal.getName(), new Integer(999), entityName, entityId, generalSubject, generalMessage); } else if(interceptionPointVO.getName().equalsIgnoreCase("ContentVersion.Delete")) { Integer contentVersionId = (Integer)extradata.get("contentVersionId"); entityName = ContentVersion.class.getName(); entityId = contentVersionId.toString(); ContentVersionVO contentVersionVO = ContentVersionControllerProxy.getController().getContentVersionVOWithId(contentVersionId, db); ContentVO contentVO = ContentControllerProxy.getController().getContentVOWithId(contentVersionVO.getContentId(), db); InterceptionPointVO contentInterceptionPointVO = InterceptionPointController.getController().getInterceptionPointVOWithName("Content.Write", db); //TODO - is Content.Write correct addTransactionHistory(contentInterceptionPointVO, "SubscriptionsEvents", infoGluePrincipal.getName(), new Integer(999), Content.class.getName(), contentVersionVO.getContentId().toString(), "Subscription: Content deleted", "A user has changed the content '" + contentVO.getName() + "'. You subscribe to this event why this message is sent."); addTransactionHistory(interceptionPointVO, "SubscriptionsEvents", infoGluePrincipal.getName(), new Integer(999), entityName, entityId, "Subscription notification", "The entity " + entityName + " was deleted. You subscribe to this event why this message is sent."); } else if(interceptionPointVO.getName().equalsIgnoreCase("Content.ExpireDateComingUp")) { ContentVO contentVO = (ContentVO)extradata.get("contentVO"); entityName = ContentVersion.class.getName(); entityId = contentVO.getId().toString(); String fullPath = ContentController.getContentController().getContentPath(new Integer(entityId), true, true); addTransactionHistory(interceptionPointVO, "SubscriptionsEvents", infoGluePrincipal.getName(), new Integer(999), entityName, entityId, "Subscription notification", "The content \"" + fullPath + "\" will expire on " + vf.formatDate(contentVO.getExpireDateTime(), "yyyy-MM-dd HH:ss") + ". <hr/>You either subscribe to this event, created the content or was the last person to modify the content why this message is sent."); } /* else if(interceptionPointVO.getName().equalsIgnoreCase("SiteNodeVersion.Read")) { Integer siteNodeVersionId = (Integer)extradata.get("siteNodeVersionId"); entityName = SiteNodeVersion.class.getName(); entityId = siteNodeVersionId.toString(); } */ else if(interceptionPointVO.getName().equalsIgnoreCase("SiteNode.ExpireDateComingUp")) { SiteNodeVO siteNodeVO = (SiteNodeVO)extradata.get("siteNodeVO"); entityName = ContentVersion.class.getName(); entityId = siteNodeVO.getId().toString(); String fullPath = SiteNodeController.getController().getSiteNodePath(new Integer(entityId), db); addTransactionHistory(interceptionPointVO, "SubscriptionsEvents", infoGluePrincipal.getName(), new Integer(999), entityName, entityId, "Subscription notification", "The page \"" + fullPath + "\" will expire on " + vf.formatDate(siteNodeVO.getExpireDateTime(), "yyyy-MM-dd HH:ss") + ". <hr/>You either subscribe to this event, created the page or was the last person to modify the page why this message is sent."); } else if(interceptionPointVO.getName().equalsIgnoreCase("SiteNodeVersion.Write")) { Integer siteNodeVersionId = (Integer)extradata.get("siteNodeVersionId"); entityName = SiteNodeVersion.class.getName(); entityId = siteNodeVersionId.toString(); } else if(interceptionPointVO.getName().equalsIgnoreCase("SiteNodeVersion.CreateSiteNode")) { Integer parentSiteNodeId = (Integer)extradata.get("siteNodeId"); entityName = SiteNode.class.getName(); entityId = parentSiteNodeId.toString(); } else if(interceptionPointVO.getName().equalsIgnoreCase("SiteNodeVersion.DeleteSiteNode")) { Integer siteNodeId = (Integer)extradata.get("siteNodeId"); entityName = SiteNode.class.getName(); entityId = siteNodeId.toString(); } else if(interceptionPointVO.getName().equalsIgnoreCase("SiteNodeVersion.MoveSiteNode")) { Integer siteNodeId = (Integer)extradata.get("siteNodeId"); entityName = SiteNode.class.getName(); entityId = siteNodeId.toString(); } else if(interceptionPointVO.getName().equalsIgnoreCase("SiteNodeVersion.SubmitToPublish")) { Integer siteNodeVersionId = (Integer)extradata.get("siteNodeVersionId"); entityName = SiteNodeVersion.class.getName(); entityId = siteNodeVersionId.toString(); } else if(interceptionPointVO.getName().equalsIgnoreCase("SiteNodeVersion.ChangeAccessRights")) { Integer siteNodeVersionId = (Integer)extradata.get("siteNodeVersionId"); entityName = SiteNodeVersion.class.getName(); entityId = siteNodeVersionId.toString(); } } public List processTransactionQueue() throws Exception { List<TransactionQueueVO> completeTransactions = new ArrayList<TransactionQueueVO>(); List<TransactionQueueVO> localTransactionQueue = new ArrayList<TransactionQueueVO>(); synchronized (transactionQueue) { logger.info("Moving the transactions to this threads local queue..."); localTransactionQueue.addAll(transactionQueue); transactionQueue.clear(); } Iterator<TransactionQueueVO> localTransactionQueueIterator = localTransactionQueue.iterator(); while(localTransactionQueueIterator.hasNext()) { TransactionQueueVO transactionQueueVO = localTransactionQueueIterator.next(); Database db = CastorDatabaseService.getDatabase(); try { beginTransaction(db); logger.info("InterceptionPointVO:" + transactionQueueVO.getInterceptionPointVO().getName()); logger.info(" " + transactionQueueVO.getTransactionObjectName() + "=" + transactionQueueVO.getTransactionObjectId()); List<SubscriptionVO> subscriptionVOList = SubscriptionController.getController().getSubscriptionVOList(transactionQueueVO.getInterceptionPointVO().getId(), null, false, transactionQueueVO.getTransactionObjectName(), transactionQueueVO.getTransactionObjectId(), null, null, db, true); logger.info("subscriptionVOList:" + subscriptionVOList.size()); if(transactionQueueVO.getInterceptionPointVO().getName().equalsIgnoreCase("SiteNode.ExpireDateComingUp")) { logger.info("It's a expiredate coming up event... let's find add a fake subscription on the last modifier."); SiteNodeVO siteNodeVO = SiteNodeController.getController().getSiteNodeVOWithId(new Integer(transactionQueueVO.getTransactionObjectId()), db); if(siteNodeVO != null) { SiteNodeVersionVO version = SiteNodeVersionController.getController().getLatestSiteNodeVersionVO(db, siteNodeVO.getId()); SubscriptionVO creatorSubscriptionVO = new SubscriptionVO(); creatorSubscriptionVO.setEntityId(transactionQueueVO.getTransactionObjectId()); creatorSubscriptionVO.setEntityName(transactionQueueVO.getTransactionObjectName()); creatorSubscriptionVO.setInterceptionPointId(transactionQueueVO.getInterceptionPointVO().getId()); creatorSubscriptionVO.setName("Standard subscription"); creatorSubscriptionVO.setUserName(siteNodeVO.getCreatorName()); subscriptionVOList.add(creatorSubscriptionVO); if(version != null) { if(!version.getVersionModifier().equals(siteNodeVO.getCreatorName())) { SubscriptionVO modifyerSubscriptionVO = new SubscriptionVO(); modifyerSubscriptionVO.setEntityId(transactionQueueVO.getTransactionObjectId()); modifyerSubscriptionVO.setEntityName(transactionQueueVO.getTransactionObjectName()); modifyerSubscriptionVO.setInterceptionPointId(transactionQueueVO.getInterceptionPointVO().getId()); modifyerSubscriptionVO.setName("Standard subscription"); modifyerSubscriptionVO.setUserName(version.getVersionModifier()); subscriptionVOList.add(modifyerSubscriptionVO); } } } } else if(transactionQueueVO.getInterceptionPointVO().getName().equalsIgnoreCase("Content.ExpireDateComingUp")) { logger.info("It's a expiredate coming up event... let's find add a fake subscription on the last modifier."); ContentVO contentVO = ContentController.getContentController().getContentVOWithId(new Integer(transactionQueueVO.getTransactionObjectId()), db); if(contentVO != null) { SubscriptionVO creatorSubscriptionVO = new SubscriptionVO(); creatorSubscriptionVO.setEntityId(transactionQueueVO.getTransactionObjectId()); creatorSubscriptionVO.setEntityName(transactionQueueVO.getTransactionObjectName()); creatorSubscriptionVO.setInterceptionPointId(transactionQueueVO.getInterceptionPointVO().getId()); creatorSubscriptionVO.setName("Standard subscription"); creatorSubscriptionVO.setUserName(contentVO.getCreatorName()); subscriptionVOList.add(creatorSubscriptionVO); List languages = LanguageController.getController().getLanguageVOList(contentVO.getRepositoryId(), db); Iterator languagesIterator = languages.iterator(); while(languagesIterator.hasNext()) { LanguageVO languageVO = (LanguageVO)languagesIterator.next(); ContentVersionVO version = ContentVersionController.getContentVersionController().getLatestContentVersionVO(contentVO.getId(), languageVO.getId(), db); if(version != null) { if(!version.getVersionModifier().equals(contentVO.getCreatorName())) { SubscriptionVO modifyerSubscriptionVO = new SubscriptionVO(); modifyerSubscriptionVO.setEntityId(transactionQueueVO.getTransactionObjectId()); modifyerSubscriptionVO.setEntityName(transactionQueueVO.getTransactionObjectName()); modifyerSubscriptionVO.setInterceptionPointId(transactionQueueVO.getInterceptionPointVO().getId()); modifyerSubscriptionVO.setName("Standard subscription"); modifyerSubscriptionVO.setUserName(version.getVersionModifier()); subscriptionVOList.add(modifyerSubscriptionVO); } } } } } Iterator<SubscriptionVO> subscriptionVOListIterator = subscriptionVOList.iterator(); while(subscriptionVOListIterator.hasNext()) { SubscriptionVO subscriptionVO = subscriptionVOListIterator.next(); boolean subscriptionHandled = handleSubscription(subscriptionVO, transactionQueueVO, db); if(subscriptionHandled) completeTransactions.add(transactionQueueVO); } commitTransaction(db); } catch(Exception e) { logger.error("An error occurred so we should not complete the transaction:" + e, e); rollbackTransaction(db); } } logger.info("localTransactionQueue:" + localTransactionQueue.size()); return completeTransactions; } private boolean handleSubscription(SubscriptionVO subscriptionVO, TransactionQueueVO transactionQueueVO, Database db) throws Exception { boolean handledSubscription = true; logger.info("subscriptionVO:" + subscriptionVO); if(subscriptionVO.getSubscriptionFilterVOList() == null || subscriptionVO.getSubscriptionFilterVOList().size() == 0) { //This first part handles simple subscriptions - that is subscriptions on an interception point with id or without filters String email = subscriptionVO.getUserEmail(); if(email == null) { InfoGluePrincipal principal = UserControllerProxy.getController(db).getUser(subscriptionVO.getUserName()); if(principal != null) { logger.info("principal:" + principal.getEmail()); email = principal.getEmail(); } } logger.info("Was a simple subscription without filters:" + email); if(email != null) MailServiceFactory.getService().sendEmail("text/html", CmsPropertyHandler.getSystemEmailSender(), email, null, null, null, null, transactionQueueVO.getSubject(), transactionQueueVO.getDescription(), "utf-8"); } else { //This part handles more complex subscriptions. } return handledSubscription; } public BaseEntityVO getNewVO() { // TODO Auto-generated method stub return null; } public void setInterceptorVO(InterceptorVO vo) { //Dont need it for now } }