Total Pageviews

18 Jan 2014

Check if a Field was Changed Inside ItemUpdated Handler of a List EventReceiver

I think, I found the easiest way to check if a particular field was changed inside ItemUpdated handler. Here is what you should do:
  1. Add a new hidden field to the list.
  2. Register a new EventReceiver for ItemUpdating. It will save previous field values.
  3. In ItemUpdating save a field value from before properties of the field to afterProperties of the hidden field.
  4. In ItemUpdated compare afterProperties of the wanted field with the after properties of the hiddenField
2. Registering event receivers inside a feature receiver with the web scope:


 private static void AddEventReceivers(SPFeatureReceiverProperties properties)
        {
            SPWeb web = (SPWeb) properties.Feature.Parent;
            string assembly = Assembly.GetExecutingAssembly().FullName;

            web.AllowUnsafeUpdates = true;

            SPList list = GetTheListSomehow();
            SPEventReceiverDefinition ERDefinitionUpdated = list.EventReceivers.Add();
            ERDefinitionUpdated.Assembly = assembly;
            ERDefinitionUpdated.Class = typeof(Event_Receiver_Class).FullName;
            ERDefinitionUpdated.Type = SPEventReceiverType.ItemUpdated;
            ERDefinitionUpdated.Name = "ClipsEventReceiverItemUpdated";
            ERDefinitionUpdated.Update();

            SPEventReceiverDefinition ERDefinitionUpdating = list.EventReceivers.Add();
            ERDefinitionUpdating.Assembly = assembly;
            ERDefinitionUpdating.Class = typeof(Event_Receiver_Class).FullName;
            ERDefinitionUpdating.Type = SPEventReceiverType.ItemUpdating;
            ERDefinitionUpdating.Name = "ClipsEventReceiverItemUpdating";
            ERDefinitionUpdating.Update();
}


3. ItemUpdating EventReceiver:

public override void ItemUpdating(SPItemEventProperties properties)
{
    //warning: you should check if the field is not null
    properties.AfterProperties["hiddenFieldName"] = properties.ListItem["fieldName"];
}
warning: do not try to save values straight the hidden field. You should use afterProperties instead like is shown above.

4. ItemUpdated EventReceiver:

public override void ItemUpdated(SPItemEventProperties properties)
{
   string oldfieldValue = properties.ListItem["hiddenField"];

    //simplified example. you should check for nulls and maybe use a better way of comparing field values
   if (String.Equals(oldfieldValue, properties.AfterProperties["fieldName"]))
   {
       return;
   }
   //some code...
}



15 Jan 2014

Send Email with HTML Link to the Current User with SPUtility.SendEmail


Code:
        private void SendEmailWithReportLink(SPWeb web)
        {
            string bodyText = "To see the report <a href='" + "/_layouts/report.aspx" + "'>click here</a>. <br>";
            Boolean emailSent = true;
            SPSecurity.RunWithElevatedPrivileges(delegate() { 
                emailSent = SPUtility.SendEmail(web, true,false, SPContext.Current.Web.CurrentUser.Email,
                    "Report «Quality Control Summary» is ready", bodyText, true);
            
            });

            if (emailSent)
            {
               //TODO: log message
            }
            else
            {
                //TODO: log error
            }
        }

10 Jan 2014

SharePoint 2013 Feature Comparison All in One (credits to Peter Ekerot)

Here is a link to the Excel file with the comprehensive comparison across different versions of SharePoint 2013


Increase the Width of the Lookup Control

If you need to increase the width of the lookup field on the edit form - add this piece of CSS to your master page:


.ms-formtable table.ms-long {
    width:650px !important;
}

.ms-formtable table.ms-long .ms-input>select {
    width:300px !important;
}

The result should look like so:

4 Jan 2014

Using AppFabric Programmatically on SharePoint 2013 Farm

First of all, if you are going to use standard App Fabric cluster that goes with the prerequisites for SharePoint 2013 you'll end up being unsupported by Microsoft:

If you are using custom applications in SharePoint Server 2013 which use the AppFabric client APIs, or are creating custom caches, you should create a separate AppFabric cache cluster to support your custom applications. Do not use the AppFabric cache cluster supporting your SharePoint Server 2013 farm. Run your separate AppFabric cache cluster for your custom applications on separate servers from the servers dedicated to your SharePoint Server 2013 farm.

If you still want to use the OOB AF cluster, here is a sample code for it:

 static void Main(string[] args)
    {
        List<DataCacheServerEndpoint> servers = new List<DataCacheServerEndpoint>();
        servers.Add(new DataCacheServerEndpoint("server.domain", 22233));
        DataCacheFactoryConfiguration configuration = new DataCacheFactoryConfiguration();
        configuration.ChannelOpenTimeout = TimeSpan.FromSeconds(120);
        configuration.IsCompressionEnabled = false;
        configuration.MaxConnectionsToServer = 10;
        configuration.RequestTimeout = TimeSpan.FromSeconds(120);
        configuration.Servers = servers;
        configuration.TransportProperties.MaxBufferSize = 1000000;
        DataCacheFactory dataCacheFactory = new DataCacheFactory(configuration);
        DataCache cache = dataCacheFactory.GetCache("DistributedDefaultCache_" + SPFarm.Local.Id);

        if (cache.Get("MyKEY") == null)
        {
            cache.Add("MyKEY", DateTime.Now);
        }
        DateTime dt = (DateTime)cache.Get("MyKEY");
        Console.WriteLine(dt);
    }

Note that you need to get the list of all servers in your cluster. In order to do it dynamically, you can use this piece of code:
string cacheClusterName = "SPDistributedCacheCluster_" + SPFarm.Local.Id;
var cacheClusterManager = SPDistributedCacheClusterInfoManager.Local;
var cacheClusterInfo =     cacheClusterManager.GetSPDistributedCacheClusterInfo(cacheClusterName);

foreach (var cacheHost in cacheClusterInfo.CacheHostsInfoCollection)
{
  servers.Add(new DataCacheServerEndpoint(cacheHost.HostName, cacheHost.CachePort));
}
I've also found a very decent helper class for working with OOB App Fabric cache (credits to Bernado Nguyen-Hoan).

2 Jan 2014

Generating Lots of List Items and Docuiments for Testing Purposes

I've uploaded a first version of List Manager for SharePoint utility on CodePlex.

It saves me a lot of time on generating list items for testing puroposes. It can also fill most of the field types including Multichoice, Lookups and Taxonomy metadata fields. It can generate text and word documents in case you need it.



This utility does not use bulk operations yet, so it might be a bit slow when generating thouthands of items, but it does its job in the end.

Here is a short demo:

SPRemoteAPIExplorer by Steve Curran MVP


A Visual Studio server explorer extension to provide the ability to explore and discover SharePoint 2013 remote API capabilities in REST and CSOM(Javascript, .Net and Silverlight). This extension is very useful when working with the SharePoint 2013 REST api. It lists all the types, methods, parameters and properties available to be called remotely. Metadata for types, methods and properties lists whether it can be used by CSOM, REST, JSOM and .Net remote api. Other information available in the property grid includes, OData type, whether to call the method using a querystring, path or body.  Requires Microsoft SharePoint 2013 installed locally and the Office Developer Tools for Visual Studio 2012.