SharePoint Events

 ProjectReady Applications for the AEC
 8/23/2016 - Webcast: What's New in Document Management for SharePoint 2016 & Online
 8/24/2016 - Webcast: AEC Project & Program Management Made Easy in SharePoint & Office 365
 8/25/2016 - Webcast: Legal Matters Management in SharePoint & Microsoft Outlook
 9/22/2016 - Webcast: Modernizing the Enterprise with SharePoint
 9/27/2016 - Webcast: Legal Email Management and Routing with Microsoft Outlook
 9/28/2016 - Webcast: Document Control for the AEC with SharePoint – RFI, Transmittal & Submittal Management
 9/29/2016 - Webcast: Project & Program Management with SharePoint
(More Links...)

 SharePoint Videos

 Security & Sharing in SharePoint 2016, Online & Office 365
 Enterprise Records Management in SharePoint & Office 365
 Best New Features of SharePoint 2016
 The Value of the Microsoft Cloud: Understanding the Scalability, Affordable Storage & Security Advantages of Office 365
 Task, Resource & Utilization Management & Reporting for the AEC with SharePoint and Office 365
 Legal Matter Task Management and Reports in SharePoint and Office 365
 What’s Coming in Office 365 & SharePoint 2016
 Automating Contract Management with Workflow in the Cloud or On-Premise
 Manage Email in the Cloud or On-Premise with SharePoint & Office 365
 Governance Essentials for SharePoint 2013 and SharePoint Online
 Easily Move Email, Files and SharePoint to Office 365
 Enterprise Workflow in the cloud with SharePoint & Office 365
 Why SharePoint 2013
 SharePoint 2013 vs SharePoint Online and Office 365 Security, Compliance and eDiscovery
 Expanding the Use, Potential and Value of SharePoint and Office 365 through 3rd Party Applications
 Keys to Successful SharePoint Initiatives and User Adoption
 SharePoint 2013 vs. SharePoint Online: Workflow
 Ensure Auditability and Compliance with Advanced Workflows in SharePoint
 SharePoint 2013 vs. SharePoint Online and Office 365: Enterprise Content Management
 SharePoint 2013 vs. SharePoint Online and Office 365: Business Intelligence
(More Links...)

 Archives ‭[1]‬

Opening SharePoint Links in a new window
Mail Enabled Lists vs. The Missing Windows 2008 POP3/IMAP Server 
7 Tools for SharePoint Developers
Public Facing Masterpage Techniques
How to Quickly Deploy and Activate a Timer Service to Your Site Collection
Custom SharePoint Master Page Feature with WSP Builder
Date Math with InfoPath
Enterprise Search Tricks and Tips Part 1
Populating Word Documents With SharePoint Data. Try The DIP!
Programmatic Deep Dive into Blank SharePoint Lookup Columns
1 - 10Next
Advanced Configuration Lists in SharePoint

By: Robert Christ

I’ve noticed recently that what I believe to be the most important aspect of custom SharePoint development appears to be missing completely from the SharePoint development community.  I’m talking of course, about configuration lists.

By the end of this guide:

1)   Your client will now have the ability to manipulate their SharePoint site without worrying about breaking dependencies of your workflow, event receiver, or timer job.

2)   Your client will now have the ability to manipulate the workflow, event receiver or timer job at run time.

3)   Your client will have an improved view of the quality of your solution, due to the nature of the UI

4)   Your workflow, event receiver or timer job will automatically verify that the configuration list holds correct data, and load said data automatically in an object oriented fashion.

Usually, customers will require the ability to change the number and names of list columns, and list locations, without having to worry about potentially breaking a custom SharePoint solution.  For more complicated solutions, they will likely also require the ability to tweak solution performance at runtime via any one of a number of solution specific parameters.

Because workflows, timer jobs, and event receivers work “behind the scenes” in SharePoint, this requires that we create a user interface of some form to allow the user to easily manipulate these features.  This of course is a configuration list.

The_SharePoint_Blog_Advanced_Configuration_Lists_in_SharePoint

1 - A Typical SharePoint Configuration List          

For developers who have been working within the SharePoint platform for a while, this concept likely appears intuitive and obvious.  Above we see the column and list dependencies for a SharePoint workflow of some form, which the workflow will presumably read through at run time, like a typical configuration file, to learn where it should query for data.  The client is now free to make whatever changes they would like to their site, and provided they also change such information here, we can be guaranteed that the custom solution will work correctly.

The Problem

The problem here is that while a simple configuration list like this might be second nature for a developer, it often is NOT for a client.  Even worse is that a solution like this will become harder and harder to maintain as a project continues to scale in size.

The first, easiest tweak we can make is to organize the entries in the list in an alphabetical and pre-ordered format.  By adding Contacts List – etc to all of the column names in our example, this list becomes much more transparent. 

To add to that effect, we’ve also added a Description column, which will include a “no IT experience needed” explanation of what that parameter of the configuration list does.  While these two tweaks may seem innocuous, I personally guarantee that together, they can turn around your Client’s view of the solution overnight.

The_SharePoint_Blog_Advanced_Configuration_Lists_in_SharePoint

2 - A More Intuitive Configuration List

While it is always important to keep the number of items in this list to a minimum, if the custom solution does need to grow to meet project requirements, it is often a good idea to create a categories column.  This will allow filtering of the list, in a quicker manner.

The_SharePoint_Blog_Advanced_Configuration_Lists_in_SharePoint

3 - A Programmable Configuration List

While I will leave it to the comments section to suggest further possible improvements to this method, I will share one additional developer trick that becomes possible when creating a Configuration List in this form: Once we have organized the configuration items according to type, it becomes extremely easy to query the list for all of the items of a specific type.  This in turn allows us to easily pass these parameters into a configuration object within our code, and populate such an object automatically.

A Developer’s Tweak

For example, judging by this example configuration list, we might create a class called “Column Names,” to store all of the configuration listItem, column name data.

public class ColumnNamesMetadata

{

public string ContactsList_FirstNameColumnName { get; set; }

       public string ContactsList_LastNameColumnName { get; set; }

       public string ContactsList_CompanyNameColumnName { get; set; }

       public string ContactsList_BusinessPhoneColumnName { get; set; }

       public string ContactsList_HomePhoneColumnName { get; set; }

       public string ContactsList_EmailAddressColumnName { get; set; }

       

}

 

While this is a good start, if we were to use the above class, we would have to populate each of the above parameters manually within the code.  But with a little bit of System.Reflection magic, we can even make these parameters auto populate and self-verify.

 

public class ColumnNamesMetadata : MetadataBase

{

[ConfigurationItem("Contacts List - First Name Column Name")]

       public string ContactsList_FirstNameColumnName { get; set; }

       

       [ConfigurationItem("Contacts List - Last Name Column Name")]

       public string ContactsList_LastNameColumnName { get; set; }

       

       [ConfigurationItem("Contacts List - Company Name Column Name")]

       public string ContactsList_CompanyNameColumnName { get; set; }

       

       [ConfigurationItem("Contacts List - Business Phone Column Name")]

       public string ContactsList_BusinessPhoneColumnName { get; set; }

 

       [ConfigurationItem("Contacts List - Home Phone Column Name")]

       public string ContactsList_HomePhoneColumnName { get; set; }

 

       [ConfigurationItem("Contacts List - Email Address Column Name")]

       public string ContactsList_EmailAddressColumnName { get; set; }

 

       public ColumnNamesMetadata(SPWeb rootWeb)

       {

              base.PopulateMetadata<ColumnNamesMetadata>(SPWebOfConfigurationList,

this, Global.CONFIGURATION_LIST_COLUMN_NAME);

base.VerifyMetadata(this);

       }

}

 

public class MetadataBase

{

/// <summary>

       /// Populates the parameters of the class implementing MetadataHoder

       /// </summary>

       protected void PopulateMetadata<T>(SPWeb rootWeb,

object callingClassObject,

string Config_List_Config_Type)

{

            SPList configList = rootWeb.Lists[Global.CONFIGURATION_LIST_NAME];

            SPListItemCollection listNameConfigItems = ListItemHelper.GetListItemsByColumnValue(

configList,

"Configuration Type",

Config_List_Config_Type);

 

            foreach (SPListItem configItem in configItems)

            {

                string value = ListItemHelper.GetColumnValue(configItem, "Value");

 

                if (string.IsNullOrEmpty(value))

                    throw new Exception("The Value column for '" + configItem.Title + "' cannot be empty in the configuration list.");

 

                ReflectionHelper.SetPropertyValue<T>(callingClassObject, configItem.Title, value);

            }

        }

 

        /// <summary>

        /// Verifies the metadata values currently in the class.  For example, ensures that no

 /// necessary values are null or empty.

        /// If there are any empty properties, it throws an error listing them all.

        /// </summary>

        protected void VerifyMetadata(object callingClassObject)

        {

            List<string> emptyProperties = GetEmptyProperties(callingClassObject);

            if (emptyProperties.Count == 0)

                return;

 

string errorMessage = "The following properties were not read correctly from the

   configuration list. ";

 

            foreach (string emptyProperty in emptyProperties)

            {

                errorMessage += emptyProperty + ", ";

            }

 

            throw new ArgumentException(errorMessage);

        }

 

public static List<string> GetEmptyProperties(object o)

       {

            Type type = o.GetType();

            List<string> list = new List<string>();

 

            PropertyInfo[] properties = type.GetProperties();

 

            foreach (PropertyInfo propertyInfo in properties)

            {

                if (IsPropertyEmpty(o, propertyInfo))

                    list.Add(propertyInfo.Name);

            }

 

            return list;

        }

 

public static void SetPropertyValue<T>(object o, string configItemTitle,

string configItemValue)

        {

            PropertyInfo propertyInfo = FindPropertyByAtribute<T>(o, configItemTitle);

 

            if (propertyInfo == null)

                throw new Exception("Invalid property name '" + configItemTitle + "'.");

 

            string propertyInfoType = propertyInfo.PropertyType.ToString();

            switch (propertyInfoType)

            {

                case "System.String":

                    propertyInfo.SetValue(o, configItemValue, null);

                    break;

 

                case "System.Boolean":

                    bool convertedBool = false;

                    if (String.Compare(configItemValue, "True", true, CultureInfo.InvariantCulture) == 0)

                        convertedBool = true;

 

                    propertyInfo.SetValue(o, convertedBool, null);

                    break;

 

                case "System.Int32":

                    int convertedInt = Int32.Parse(configItemValue);

 

                    propertyInfo.SetValue(o, convertedInt, null);

                    break;

               

                default:

                    throw new Exception(string.Format("Cannot handle column '{0}' of type '{1}'",

propertyInfoType, propertyInfoType));

            }

        }

 

    }

 

class ConfigurationItemAttribute : Attribute

{

private string _configItemTitle;

      

       private ConfigurationItemAttribute() { }

 

       public ConfigurationItemAttribute(string configItemTitle)

       {

           _configItemTitle = configItemTitle;

       }

 

        public string ConfigItemTitle { get { return _configItemTitle; } }

}

 

With the above code, one should now simple be able to construct a ColumnNamesMetadataObject(SPWeb rootWeb), and each of the public parameters in ColumnNamesMetadata should hold the value specified in the configuration list.  If the object was unable to read a value in the configuration list, it will throw an error of every configuration list item that presented a problem.

If you’ve followed this guide, the following steps should be completed:

1)   Your client will now have the ability to manipulate their SharePoint site without worrying about breaking dependencies of your workflow, event receiver, or timer job.

2)   Your client will now have the ability to manipulate the workflow, event receiver or timer job at run time.

3)   Your client will have an improved view of the quality of your solution, due to the nature of the UI

4)   Your workflow will automatically verify that the configuration list holds correct data, and load said data automatically in an object oriented fashion.

 

Feel welcome to ask questions in the comments!

 

By: Robert Christ

        

 Subscribe

  GigWerks RSS  Gig Werks Mailing List 

 Contact Us

 Connect

 Resources

 On Demand SharePoint Webcast Recordings
 Upcoming Webinars
 SharePoint Resources
 Business Intelligence Resources
 Gig Werks Website



©2009 Gig Werks. All rights reserved. Privacy Policy