I'm having some problems when trying to make a number of classes and triggers to run on a dispatcher class. The components for this architecture are the following:
- One -TriggerDispatcher class.
- One trigger - MainAttachmentEntry
- One Apex class - AttachmentVisitReport
The first point for this architectural to work is the MainAttachmentEntry Code bellow:
trigger MainAttachmentEntry on Attachment (after delete, after insert, after update, before delete, before insert, before update)
{
TriggerDispatcher.entry(new TriggerDispatcher.TriggerParameters(Trigger.isBefore, Trigger.isAfter, Trigger.isDelete, Trigger.isInsert, Trigger.isUpdate, Trigger.isExecuting, Trigger.old, Trigger.new, Trigger.oldMap, Trigger.newMap));
}
This is packet that contains all trigger context variables, in this case except after undelete. This design is based on Adam Purkins dispatcher.
The MainAttachmentEntry trigger calls the TriggerDispatcher class and the entry() method.
Here is the TriggerDispatcher class:
public with sharing class TriggerDispatcher
{
private static final String SACCOUNT = 'Account';
private static final String SCONTACT = 'Contact';
private static final String SLEAD = 'Lead';
private static final String SOPPORTUNITY = 'Opportunity';
private static final String SUSER = 'User';
private static final String SEVENT = 'Event';
private static final String SATTACHMENT = 'Attachment';
private static final String SFEEDITEM = 'FeedItem';
public static ITriggerEntry activeInstance = null;
public static Map<Id, SObject> sObjectsToUpdate = new Map<Id, SObject>();
public interface ITriggerEntry
{
void mainEntry(TriggerParameters tp);
void inProgressEntry(TriggerParameters tp);
}
public class TriggerParameters
{
public String triggerObject {get; private set;}
public Boolean isBefore {get; private set;}
public Boolean isAfter {get; private set;}
public Boolean isDelete {get; private set;}
public Boolean isInsert {get; private set;}
public Boolean isUpdate {get; private set;}
public Boolean isExecuting {get; private set;}
public List<SObject> oldList {get; private set;}
public List<SObject> newList {get; set;}
public Map<Id, SObject> oldMap {get; private set;}
public Map<Id, SObject> newMap {get; set;}
public TriggerParameters(Boolean ib, Boolean ia, Boolean id, Boolean ii, Boolean iu, Boolean ie,
List<SObject> ol, List<SObject> nl, Map<Id, SObject> om, Map<Id, SObject> nm )
{
this.isBefore = ib;
this.isAfter = ia;
this.isDelete = id;
this.isInsert = ii;
this.isUpdate = iu;
this.isExecuting = ie;
this.oldList = ol;
this.newList = nl;
this.oldMap = om;
this.newMap = nm;
this.triggerObject = getSObjType((this.oldList != null && this.oldList.size() > 0) ?
this.oldList[0] : this.newList[0]);
}
private String getSObjType(SObject so)
{
String retVal;
if(so instanceof Account) retVal = SACCOUNT;
else if( so instanceof Contact) retVal = SCONTACT;
else if( so instanceof Lead) retVal = SLEAD;
else if( so instanceof Opportunity) retVal = SOPPORTUNITY;
else if( so instanceof User) retVal =SUSER;
else if( so instanceof Event) retVal = SEVENT;
else if( so instanceof Attachment) retVal = SATTACHMENT;
else if( so instanceof FeedItem) retVal = SFEEDITEM;
return retVal;
}
}
//Central dispatch entry
public static void entry(TriggerParameters tp)
{
if(activeInstance == null)
processWork(tp);
else
activeInstance.inProgressEntry(tp);
}
//Order of execution is controlled
private static void processWork(TriggerParameters tp)
{
if(tp.triggerObject == SATTACHMENT && tp.isAfter && tp.isInsert)
{
execute(new AttachmentVisitReport(), tp);
}
if(sObjectsToUpdate.size() >0)
update sObjectsToUpdate.values();
}
private static void execute(ITriggerEntry ite, TriggerParameters tp)
{
activeInstance = ite;
activeInstance.mainEntry(tp);
}
}
The entry() actually will pass the TriggerParametes tp and also make sure that this is the first time we are running those values within the TriggerParameters. If that is the case it will jump to another method, processWork(). There I can add the criteria for the using and if statement. In this case we are making sure that if the object passed on the TriggerParametes is of type Attachment, to execute a class AttachmentVisitReport.
Here is the code for AttachmentVisitReport. This class implements ITriggerEntry interface which it is inside the TriggerDispatcher class:
public with sharing class AttachmentVisitReport implements TriggerDispatcher.ITriggerEntry
{
public void inProgressEntry(TriggerDispatcher.TriggerParameters tp)
{
//Logic .....
}
public void mainEntry(TriggerDispatcher.TriggerParameters tp)
{
Map<Id, Attachment> newMap = (Map<Id, Attachment>) tp.newMap;
Map<Id, Attachment> attMap = new Map<Id, Attachment>([SELECT ParentId FROM Attachment WHERE Parent.Type = 'Event' AND Id IN :newMap.keySet()]);
Set<Id> eveIds = new Set<Id>(); //Event Ids
Set<Id> accIds = new Set<Id>(); // Account Ids for Events
Set<Id> ownIds = new Set<Id>(); // Event OwnerIds
for(Attachment a: attMap.values())
if(!attMap.isEmpty())
eveIds.add(a.ParentId);
Map<Id, Event> eveMap = new Map<Id, Event>([SELECT OwnerId, WhatID FROM Event WHERE Id IN : eveIds]);
for(Event e : eveMap.values())
{
if(!eveMap.isEmpty())
accIds.add(e.WhatId);
ownIds.add(e.OwnerId);
List<EntitySubscription> entityListAcct = [SELECT id, ParentId, SubscriberId FROM EntitySubscription WHERE ParentId IN :accIds];
List<EntitySubscription> entityListOwn = [SELECT id, ParentId, SubscriberId FROM EntitySubscription WHERE ParentId IN :ownIds];
for( EntitySubscription en :entityListAcct)
{
if(!entityListAcct.isEmpty())
{
FeedItem feedItemAcct = new FeedItem(
Body='Update done to Visit Report. Attachment inserted or updated',
ParentId = en.ParentId);
insert feedItemAcct;
TriggerDispatcher.sObjectsToUpdate.put(feedItemAcct.id , feedItemAcct);
}
}
}
}
}
The intention of this code is to add a Feed for those following the Owner of events if those contain an attachment. It also tries to do the same for Accounts followers if there is an Event related to an Account. Actually he logic is:
If an Event has WhatId = Account and an Attachment is inserted in the Event, a new feed should be send to the Event owner followers and to the Account followers.
I tried different things but is not inserting the Feed. On this code I'm just trying to do this which new attachments and only for Account followers. This section:
for( EntitySubscription en :entityListAcct)
{
if(!entityListAcct.isEmpty())
{
FeedItem feedItemAcct = new FeedItem(
Body='Update done to Visit Report. Attachment inserted or updated',
ParentId = en.ParentId);
insert feedItemAcct;
TriggerDispatcher.sObjectsToUpdate.put(feedItemAcct.id , feedItemAcct);
I tried to change the SObjectToUpdate to do a insert rather than update ... but is not inserting the Feed. So, can anyone give some directions on how to make this work? Thanks.