The code is configurable, so additional child lists can be easily added by updating an item in a SharePoint configuration list.
For this to work, each of the child lists should begin with the same content.
I created an ItemEventReceiver. It will fire on the following events:
ItemAdded
ItemUpdated
ItemDeleting
I created a list called Configuration in the main source site. In that list I created 3 columns: Title, Value and Description
As shown above, I created 3 list items:
SynchSourceList
SynchDestLists
SynchDestSites
Each of the destination lists have the same name, so I didn't need to list 3 separate destination list names. These three items will be read by the Item Event Receiver.
This is the code for the ItemAdded Event:
public override void ItemAdded(SPItemEventProperties properties)
{
// copy the list item to the other lists
try
{
this.DisableEventFiring();
SPListItem SourceItem = properties.ListItem;
SPSecurity.RunWithElevatedPrivileges(delegate()
{
string DestURL = getConfig.getConfigValue(properties.WebUrl, "SynchDestSites");
string DestList = getConfig.getConfigValue(properties.WebUrl, "SynchDestLists");
string[] sites = DestURL.Split(';');
// foreach site in list
foreach (string siteName in sites)
{
using (SPSite site = new SPSite(siteName))
{
using (SPWeb destWeb = site.OpenWeb())
{
SPList SPDestList = destWeb.GetList(destWeb.ServerRelativeUrl.TrimEnd('/') + "/Lists/" + DestList);
SPListItem spListItem = SourceItem;
SPListItem newDestItem = SPDestList.Items.Add();
foreach (SPField field in spListItem.Fields)
{
if (!field.ReadOnlyField &&
field.Type != SPFieldType.Invalid &&
field.Type != SPFieldType.WorkflowStatus &&
field.Type != SPFieldType.File &&
field.Type != SPFieldType.Attachments &&
field.Type != SPFieldType.Computed &&
field.Type != SPFieldType.Lookup)
{
newDestItem[field.InternalName] = spListItem[field.InternalName];
}
}
newDestItem.Update();
}
}
}
});
base.ItemAdded(properties);
}
catch(Exception ex)
{
}
finally
{
this.EnableEventFiring();
}
}
{
// copy the list item to the other lists
try
{
this.DisableEventFiring();
SPListItem SourceItem = properties.ListItem;
SPSecurity.RunWithElevatedPrivileges(delegate()
{
string DestURL = getConfig.getConfigValue(properties.WebUrl, "SynchDestSites");
string DestList = getConfig.getConfigValue(properties.WebUrl, "SynchDestLists");
string[] sites = DestURL.Split(';');
// foreach site in list
foreach (string siteName in sites)
{
using (SPSite site = new SPSite(siteName))
{
using (SPWeb destWeb = site.OpenWeb())
{
SPList SPDestList = destWeb.GetList(destWeb.ServerRelativeUrl.TrimEnd('/') + "/Lists/" + DestList);
SPListItem spListItem = SourceItem;
SPListItem newDestItem = SPDestList.Items.Add();
foreach (SPField field in spListItem.Fields)
{
if (!field.ReadOnlyField &&
field.Type != SPFieldType.Invalid &&
field.Type != SPFieldType.WorkflowStatus &&
field.Type != SPFieldType.File &&
field.Type != SPFieldType.Attachments &&
field.Type != SPFieldType.Computed &&
field.Type != SPFieldType.Lookup)
{
newDestItem[field.InternalName] = spListItem[field.InternalName];
}
}
newDestItem.Update();
}
}
}
});
base.ItemAdded(properties);
}
catch(Exception ex)
{
}
finally
{
this.EnableEventFiring();
}
}
The ItemAdded Event calls GetConfigValue which is listed here:
class getConfig
{
public static string getConfigValue(string webURL, string listItem)
{
// get the destURL and destList from the Configuration List
string retValue;
try
{
SPSite thisSite = new SPSite(webURL);
SPWeb thisWeb = thisSite.OpenWeb();
SPList configList = thisWeb.GetList(thisWeb.ServerRelativeUrl.TrimEnd('/') + "/Lists/Configuration");
SPQuery query = new SPQuery();
query.ViewFields =
@"";
query.Query = @"" + listItem +
@" ";
SPListItemCollection items = configList.GetItems(query);
// just get the first item
SPItem firstItem = items[0];
retValue = firstItem["Value"].ToString();
if (thisWeb != null)
thisWeb.Dispose();
if (thisSite != null)
thisSite.Dispose();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("RegistrationListEventReceiver.ItemAdded() exception in getConfigValue" + ex.ToString());
retValue = "Error";
}
return retValue;
}
}
{
public static string getConfigValue(string webURL, string listItem)
{
// get the destURL and destList from the Configuration List
string retValue;
try
{
SPSite thisSite = new SPSite(webURL);
SPWeb thisWeb = thisSite.OpenWeb();
SPList configList = thisWeb.GetList(thisWeb.ServerRelativeUrl.TrimEnd('/') + "/Lists/Configuration");
SPQuery query = new SPQuery();
query.ViewFields =
@"
query.Query = @"
@"
SPListItemCollection items = configList.GetItems(query);
// just get the first item
SPItem firstItem = items[0];
retValue = firstItem["Value"].ToString();
if (thisWeb != null)
thisWeb.Dispose();
if (thisSite != null)
thisSite.Dispose();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("RegistrationListEventReceiver.ItemAdded() exception in getConfigValue" + ex.ToString());
retValue = "Error";
}
return retValue;
}
}
I am also capturing the ItemDeleting Event:
public override void ItemDeleting(SPItemEventProperties properties)
{
// remove the list item from the other lists
// find the item in the other list with the same ID and delete it
string key;
try
{
this.DisableEventFiring();
SPListItem SourceItem = properties.ListItem;
SPSecurity.RunWithElevatedPrivileges(delegate()
{
key = SourceItem["RecordID"].ToString();
// do a query on the destination list and get the item with the same key
string DestURL = getConfig.getConfigValue(properties.WebUrl, "SynchDestSites");
string DestList = getConfig.getConfigValue(properties.WebUrl, "SynchDestLists");
string[] sites = DestURL.Split(';');
// for each site in list
foreach (string siteName in sites)
{
using (SPSite site = new SPSite(siteName))
{
using (SPWeb destWeb = site.OpenWeb())
{
SPQuery qry = new SPQuery();
qry.ViewFields = @"";
qry.Query = @"" + key + " ";
SPList SPDestList = destWeb.GetList(destWeb.ServerRelativeUrl.TrimEnd('/') + "/Lists/" + DestList);
SPListItemCollection items = SPDestList.GetItems(qry);
if (items.Count > 0)
{
for (int i = items.Count - 1; i >= 0; i--)
{
items.Delete(i);
}
}
}
}
}
}
);
base.ItemDeleting(properties);
}
catch (Exception ex)
{
}
finally
{
this.EnableEventFiring();
}
}
{
// remove the list item from the other lists
// find the item in the other list with the same ID and delete it
string key;
try
{
this.DisableEventFiring();
SPListItem SourceItem = properties.ListItem;
SPSecurity.RunWithElevatedPrivileges(delegate()
{
key = SourceItem["RecordID"].ToString();
// do a query on the destination list and get the item with the same key
string DestURL = getConfig.getConfigValue(properties.WebUrl, "SynchDestSites");
string DestList = getConfig.getConfigValue(properties.WebUrl, "SynchDestLists");
string[] sites = DestURL.Split(';');
// for each site in list
foreach (string siteName in sites)
{
using (SPSite site = new SPSite(siteName))
{
using (SPWeb destWeb = site.OpenWeb())
{
SPQuery qry = new SPQuery();
qry.ViewFields = @"
qry.Query = @"
SPList SPDestList = destWeb.GetList(destWeb.ServerRelativeUrl.TrimEnd('/') + "/Lists/" + DestList);
SPListItemCollection items = SPDestList.GetItems(qry);
if (items.Count > 0)
{
for (int i = items.Count - 1; i >= 0; i--)
{
items.Delete(i);
}
}
}
}
}
}
);
base.ItemDeleting(properties);
}
catch (Exception ex)
{
}
finally
{
this.EnableEventFiring();
}
}
and the ItemUpdated Event:
public override void ItemUpdated(SPItemEventProperties properties)
{
//update the list item in the other list(s)
string key;
try
{
this.DisableEventFiring();
SPListItem SourceItem = properties.ListItem;
SPSecurity.RunWithElevatedPrivileges(delegate()
{
key = SourceItem["RecordID"].ToString();
// do a query on the destination list and get the item with the same key
string DestURL = getConfig.getConfigValue(properties.WebUrl, "SynchDestSites");
string DestList = getConfig.getConfigValue(properties.WebUrl, "SynchDestLists");
string[] sites = DestURL.Split(';');
// for each site in list
foreach (string siteName in sites)
{
using (SPSite site = new SPSite(siteName))
{
using (SPWeb destWeb = site.OpenWeb())
{
SPQuery qry = new SPQuery();
qry.ViewFields = @"";
qry.Query = @"" + key + " ";
SPList SPDestList = destWeb.GetList(destWeb.ServerRelativeUrl.TrimEnd('/') + "/Lists/" + DestList);
SPListItemCollection items = SPDestList.GetItems(qry);
foreach (SPListItem item in items)
{
//update the fields from the source item
foreach (SPField field in SourceItem.Fields)
{
if (!field.ReadOnlyField &&
field.Type != SPFieldType.Invalid &&
field.Type != SPFieldType.WorkflowStatus &&
field.Type != SPFieldType.File &&
field.Type != SPFieldType.Attachments &&
field.Type != SPFieldType.Computed &&
field.Type != SPFieldType.Lookup)
{
item[field.InternalName] = SourceItem[field.InternalName];
}
}
item.Update();
}
}
}
}
}
);
base.ItemUpdated(properties);
}
catch (Exception ex)
{
}
finally
{
this.EnableEventFiring();
}
}
{
//update the list item in the other list(s)
string key;
try
{
this.DisableEventFiring();
SPListItem SourceItem = properties.ListItem;
SPSecurity.RunWithElevatedPrivileges(delegate()
{
key = SourceItem["RecordID"].ToString();
// do a query on the destination list and get the item with the same key
string DestURL = getConfig.getConfigValue(properties.WebUrl, "SynchDestSites");
string DestList = getConfig.getConfigValue(properties.WebUrl, "SynchDestLists");
string[] sites = DestURL.Split(';');
// for each site in list
foreach (string siteName in sites)
{
using (SPSite site = new SPSite(siteName))
{
using (SPWeb destWeb = site.OpenWeb())
{
SPQuery qry = new SPQuery();
qry.ViewFields = @"
qry.Query = @"
SPList SPDestList = destWeb.GetList(destWeb.ServerRelativeUrl.TrimEnd('/') + "/Lists/" + DestList);
SPListItemCollection items = SPDestList.GetItems(qry);
foreach (SPListItem item in items)
{
//update the fields from the source item
foreach (SPField field in SourceItem.Fields)
{
if (!field.ReadOnlyField &&
field.Type != SPFieldType.Invalid &&
field.Type != SPFieldType.WorkflowStatus &&
field.Type != SPFieldType.File &&
field.Type != SPFieldType.Attachments &&
field.Type != SPFieldType.Computed &&
field.Type != SPFieldType.Lookup)
{
item[field.InternalName] = SourceItem[field.InternalName];
}
}
item.Update();
}
}
}
}
}
);
base.ItemUpdated(properties);
}
catch (Exception ex)
{
}
finally
{
this.EnableEventFiring();
}
}
I also created a Feacture Event Receiver so that when the Feature is Activated, it would associate the Item EventReceivers with the specified sourceList. You can see the code here:
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
try
{
using (SPSite destSite = new SPSite("http://yoursite.org"))
{
using (SPWeb destWeb = destSite.OpenWeb())
{
//make this configurable
string listName;
listName = SynchronizeLists.getConfig.getConfigValue("http://yoursite.org", "SynchSourceList");
destWeb.Lists[listName].EventReceivers.Add(
SPEventReceiverType.ItemAdded,
"SynchronizeLists, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3900cea7f3c6a92a",
"SynchronizeLists.SynchronizeListsItemEventReceiver");
destWeb.Lists[listName].EventReceivers.Add(
SPEventReceiverType.ItemDeleting,
"SynchronizeLists, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3900cea7f3c6a92a",
"SynchronizeLists.SynchronizeListsItemEventReceiver");
destWeb.Lists[listName].EventReceivers.Add(
SPEventReceiverType.ItemUpdated,
"SynchronizeLists, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3900cea7f3c6a92a",
"SynchronizeLists.SynchronizeListsItemEventReceiver");
}
}
}
catch (Exception ex)
{
}
}
{
try
{
using (SPSite destSite = new SPSite("http://yoursite.org"))
{
using (SPWeb destWeb = destSite.OpenWeb())
{
//make this configurable
string listName;
listName = SynchronizeLists.getConfig.getConfigValue("http://yoursite.org", "SynchSourceList");
destWeb.Lists[listName].EventReceivers.Add(
SPEventReceiverType.ItemAdded,
"SynchronizeLists, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3900cea7f3c6a92a",
"SynchronizeLists.SynchronizeListsItemEventReceiver");
destWeb.Lists[listName].EventReceivers.Add(
SPEventReceiverType.ItemDeleting,
"SynchronizeLists, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3900cea7f3c6a92a",
"SynchronizeLists.SynchronizeListsItemEventReceiver");
destWeb.Lists[listName].EventReceivers.Add(
SPEventReceiverType.ItemUpdated,
"SynchronizeLists, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3900cea7f3c6a92a",
"SynchronizeLists.SynchronizeListsItemEventReceiver");
}
}
}
catch (Exception ex)
{
}
}
There is a FeatureDeactivating event receiver as well:
public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
try
{
using (SPWeb web = properties.Feature.Parent as SPWeb)
{
string listName;
listName = getConfig.getConfigValue("http://yoursite.org", "SourceList");
SPList TheList = web.Lists[listName];
//Declare the full assembly name
String AssemblyName = "SynchronizeLists, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3900cea7f3c6a92a";
int i;
for (i = 0; i < TheList.EventReceivers.Count; i++) { if (TheList.EventReceivers[i].Assembly.Equals(AssemblyName)) TheList.EventReceivers[i].Delete(); } //Update the list TheList.Update(); web.Update(); } } catch (Exception ex) { } }
{
try
{
using (SPWeb web = properties.Feature.Parent as SPWeb)
{
string listName;
listName = getConfig.getConfigValue("http://yoursite.org", "SourceList");
SPList TheList = web.Lists[listName];
//Declare the full assembly name
String AssemblyName = "SynchronizeLists, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3900cea7f3c6a92a";
int i;
for (i = 0; i < TheList.EventReceivers.Count; i++) { if (TheList.EventReceivers[i].Assembly.Equals(AssemblyName)) TheList.EventReceivers[i].Delete(); } //Update the list TheList.Update(); web.Update(); } } catch (Exception ex) { } }