Wednesday, October 29, 2008

Read data stored in the custom section of a SharePoint web.config file using XPath

Introduction
In order to store Data shared by all sites, easily usable by server code programming and easy to deploy, some SharePoint Application needs custom entries in the Web Application web.config file. One simple solution is to add a line with a key and a value in the appSettings section.
However, you may need to customize your web.config file with a more complex and structured section. They are many post on how to add a custom section in a web.config for a SharePoint application using programmatically.
then, there is also code sample that shows how to read datas stored in the custom section in order to use them while programming.
Maybe the more complet sample is this one for Asp .Net but usable in SharePoint:

How To Create a Custom ASP.NET Configuration Section Handler in Visual C# .NET

I did practice this solution, but think it is a bit heavy. Then I wondered if it would not be more simple to use xpath to read the data sotred in the Custom ASP.NET Configuration Section of the SharePoint Application Web.config file.
1 - Exposing the business case
To make a clear demonstration, asume we are working for a company that owns several stores, and that the employees want to access to the company SharePoint intranet with Form Based Authentication Mode. We want to manage FBA profiles in order to map FBA roles with the SharePoint Portal application functionnalities like:
  • Access to a knowledge base
  • Make in line ordering
  • Edit order tracking reports
  • etc.

The FBA roles are the followings;
  • employee,
  • manager
  • owner
Thus, we want to store these FBA roles and Features in our SharePoint Portal web.config file with more technical data as aspnet FBA database connection string name, and we are going first to modify the web.config file
2 - Add a custom section to the SharePoint Portal web.config file
We open the web.config file (after having made a back-up) and add a section group to the configSection section.
    <sectionGroup name="System.Workflow.ComponentModel.WorkflowCompiler" type="System.Workflow.ComponentModel.Compiler.WorkflowCompilerConfigurationSectionGroup, System.Workflow.ComponentModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
      <section name="authorizedTypes" type="System.Workflow.ComponentModel.Compiler.AuthorizedTypesSectionHandler, System.Workflow.ComponentModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    </sectionGroup>
<!--start adding -->
    <sectionGroup name="myPortal" >
      <section name="profileManagement" type="System.Configuration.SingleTagSectionHandler, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
    </sectionGroup>
<!-- end adding -->
  </configSections>
Then, we add the "myPortal custom section
    <machineKey validationKey="80464397F43642274573BDB3C8C49CA32AD2DD99A86B46EC" decryptionKey="75DF7F2B93BE8584983E065036615D4BB091D5B3C8A668C8" validation="SHA1" />
    <sessionState mode="SQLServer" timeout="60" allowCustomSqlDatabase="true" partitionResolverType="Microsoft.Office.Server.Administration.SqlSessionStateResolver, Microsoft.Office.Server, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
  </system.web>
<!--start adding -->
   <myPortal>
    <profileManagement connectionStringName="AspNetDbFBAConnectionString" applicationName="/" baseStoreFeatures="15" enableSessionState="false">
      <mainRoles>
        <role name="Owner" hasAllFeatures="true" />
        <role name="Manager" hasAllFeatures="true" />
        <role name="Employee" hasAllFeatures="false" />
      </mainRoles>
      <storeFeatures>
        <storeFeature name="PersonalizedHomePage" value="1" />
        <storeFeature name="ContactForm" value="2" />
        <storeFeature name="KnowledgeBase" value="4" default="true" />
        <storeFeature name="MyContacts" value="8" default="true" />
        <storeFeature name="Ordering" value="16" />
        <storeFeature name="Tracking" value="32" />
        <storeFeature name="BusinessReport" value="64" />
        <storeFeature name="FullMask" value="65535" />
      </storeFeatures>
    </profileManagement>
  </myPortal>
<!-- end adding -->
 <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
3 - Reading data stored in the web.config file
In order to read data stored in the web.config file, we are going to use the XMLExplorateur class that I introduced in a previous post:

Simplify XPath using a C# Class

and that I had completed to be more powerfull.
here is the class code:
        class XMLExplorateur
        {
            protected XPathDocument docNav;
            protected XPathNavigator nav;
            protected XPathNodeIterator xit;
            protected bool initpath = true;
            public XMLExplorateur() { }

            public XMLExplorateur(String path)
            {
                try
                {
                    docNav = new XPathDocument(path);
                    nav = docNav.CreateNavigator();
                }
                catch
                {
                    docNav = null;
                    nav = null;
                }
            }
            public bool Init(String path)
            {
                try
                {
                    docNav = new XPathDocument(path);
                    nav = docNav.CreateNavigator();
                }
                catch
                {
                    docNav = null;
                    nav = null;
                    return false;
                }
                return true;
            }

            public List<string> ValuesOf(String Item)
            {
                List<string> myList = new List<string>();
                if (nav == null) return null;
                String tmp = "descendant::" + Item;
                try
                {
                    xit = nav.Select(tmp);
                    while (xit.MoveNext())
                    {
                        myList.Add(xit.Current.Value);
                    }
                }
                catch
                {
                    myList = null;
                }
                return myList;
            }

            public Dictionary<string, uint> DictionaryStringUintItemsOf(String Item, string att1, string att2)
            {
                Dictionary<string, uint> myDictionary = new Dictionary<string, uint>();
                if (nav == null) return null;
                String tmp = "descendant::" + Item;
                try
                {
                    xit = nav.Select(tmp);
                    while (xit.MoveNext())
                    {
                        myDictionary.Add(xit.Current.GetAttribute(att1, ""), Convert.ToUInt32(xit.Current.GetAttribute(att2, "")));
                    }
                }
                catch
                {
                    myDictionary = null;
                }
                return myDictionary;
            }

            public Dictionary<string, bool> DictionaryStringBoolItemsOf(String Item, string att1, string att2)
            {
                Dictionary<string, bool> myDictionary = new Dictionary<string, bool>();
                if (nav == null) return null;
                String tmp = "descendant::" + Item;
                try
                {
                    xit = nav.Select(tmp);
                    while (xit.MoveNext())
                    {
                        myDictionary.Add(xit.Current.GetAttribute(att1, ""), Convert.ToBoolean(xit.Current.GetAttribute(att2, "")));
                    }
                }
                catch
                {
                    myDictionary = null;
                }
                return myDictionary;
            }

            public String ValueOf(String Item)
            {
                if (nav == null) return "Erreur Navigateur null";
                String tmp = "descendant::" + Item;
                try
                {
                    xit = nav.Select(tmp);
                    if (xit.MoveNext()) tmp = xit.Current.Value;
                    else tmp = "null";
                }
                catch
                {
                    tmp = "null";
                }
                return tmp;
            }
        }

Now, this is the way we can program to read all the data stored in the custom section :
        private static string _connectionStringName = null;
        private static string _applicationName = null;
        private static uint _baseStoreFeatures = uint.MinValue;
        private static Dictionary<string, uint> _availableStoreFeatures = null;
        private static Dictionary<string, bool> _mainRoles = null;
        private static bool _enableSessionState = false;


        void Page_Load(object sender, EventArgs e)
        {
            XMLExplorateur xe = new XMLExplorateur();

            Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(
                delegate()
                {
                    xe.Init(HttpContext.Current.Server.MapPath("~/web.config"));
                }
            );

            _connectionStringName = xe.ValueOf("configuration/myPortal/profileManagement/@connectionStringName");

            _applicationName = xe.ValueOf("configuration/myPortal/profileManagement/@applicationName");
            
            _baseStoreFeatures = Convert.ToUInt32(xe.ValueOf("configuration/myPortal/profileManagement/@baseStoreFeatures"));

            _enableSessionState = Convert.ToBoolean(xe.ValueOf("configuration/myPortal/profileManagement/@enableSessionState"));

            _availableStoreFeatures = xe.DictionaryStringUintItemsOf("configuration/myPortal/profileManagement/storeFeatures/storeFeature", "name", "value");

            _mainRoles = xe.DictionaryStringBoolItemsOf("configuration/myPortal/profileManagement/mainRoles/role", "name", "hasAllFeatures");

        }

Here is the complet code of the .aspx page. As it is an application page, place this page in the LAYOUTS directory, and call it from any site of your portal using this url (assume you call the page _readwebconfig.aspx as I did):
http://hostheader/anysite/anysubsite/_layouts/_readwebconfig.aspx

<%@ Page Language="C#" AutoEventWireup="true"  %>

<%@ Register TagPrefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls"  Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"  %> 
<%@ Register TagPrefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"  %>
<%@ Import Namespace="Microsoft.SharePoint"  %>
<%@ Import Namespace="Microsoft.SharePoint.ApplicationPages"  %>
<%@ Import Namespace="System.Collections.Generic"  %>
<%@ Import Namespace="System.Text"  %>
<%@ Import Namespace="System.Xml"  %>
<%@ Import Namespace="System.Xml.XPath"  %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title  ></title>
</head>
<body>

    <script runat="server" id="class_XMLExplorateur">

        class XMLExplorateur
        {
            protected XPathDocument docNav;
            protected XPathNavigator nav;
            protected XPathNodeIterator xit;
            protected bool initpath = true;
            public XMLExplorateur() { }

            public XMLExplorateur(String path)
            {
                try
                {
                    docNav = new XPathDocument(path);
                    nav = docNav.CreateNavigator();
                }
                catch
                {
                    docNav = null;
                    nav = null;
                }
            }
            public bool Init(String path)
            {
                try
                {
                    docNav = new XPathDocument(path);
                    nav = docNav.CreateNavigator();
                }
                catch
                {
                    docNav = null;
                    nav = null;
                    return false;
                }
                return true;
            }

            public List<string> ValuesOf(String Item)
            {
                List<string> myList = new List<string>();
                if (nav == null) return null;
                String tmp = "descendant::" + Item;
                try
                {
                    xit = nav.Select(tmp);
                    while (xit.MoveNext())
                    {
                        myList.Add(xit.Current.Value);
                    }
                }
                catch
                {
                    myList = null;
                }
                return myList;
            }

            public Dictionary<string, uint> DictionaryStringUintItemsOf(String Item, string att1, string att2)
            {
                Dictionary<string, uint> myDictionary = new Dictionary<string, uint>();
                if (nav == null) return null;
                String tmp = "descendant::" + Item;
                try
                {
                    xit = nav.Select(tmp);
                    while (xit.MoveNext())
                    {
                        myDictionary.Add(xit.Current.GetAttribute(att1, ""), Convert.ToUInt32(xit.Current.GetAttribute(att2, "")));
                    }
                }
                catch
                {
                    myDictionary = null;
                }
                return myDictionary;
            }

            public Dictionary<string, bool> DictionaryStringBoolItemsOf(String Item, string att1, string att2)
            {
                Dictionary<string, bool> myDictionary = new Dictionary<string, bool>();
                if (nav == null) return null;
                String tmp = "descendant::" + Item;
                try
                {
                    xit = nav.Select(tmp);
                    while (xit.MoveNext())
                    {
                        myDictionary.Add(xit.Current.GetAttribute(att1, ""), Convert.ToBoolean(xit.Current.GetAttribute(att2, "")));
                    }
                }
                catch
                {
                    myDictionary = null;
                }
                return myDictionary;
            }

            public String ValueOf(String Item)
            {
                if (nav == null) return "Erreur Navigateur null";
                String tmp = "descendant::" + Item;
                try
                {
                    xit = nav.Select(tmp);
                    if (xit.MoveNext()) tmp = xit.Current.Value;
                    else tmp = "null";
                }
                catch
                {
                    tmp = "null";
                }
                return tmp;
            }
        }
    </script>

    <script runat="server" id="method">
        
        private static string _connectionStringName = null;
        private static string _applicationName = null;
        private static uint _baseStoreFeatures = uint.MinValue;
        private static Dictionary<string, uint> _availableStoreFeatures = null;
        private static Dictionary<string, bool> _mainRoles = null;
        private static bool _enableSessionState = false;


        void Page_Load(object sender, EventArgs e)
        {
            //XmlDocument document = new XmlDocument();
            XMLExplorateur xe = new XMLExplorateur();

            Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(

                delegate()
                {
                    xe.Init(HttpContext.Current.Server.MapPath("~/web.config"));
                }

            );

            _connectionStringName = xe.ValueOf("configuration/myPortal/profileManagement/@connectionStringName");
            Response.Write("<br />_connectionStringName: " + _connectionStringName);

            _applicationName = xe.ValueOf("configuration/myPortal/profileManagement/@applicationName");
            Response.Write("<br />_applicationName: " + _applicationName);
            
            
            _baseStoreFeatures = Convert.ToUInt32(xe.ValueOf("configuration/myPortal/profileManagement/@baseStoreFeatures"));
            Response.Write("<br />_baseStoreFeatures:" + " " + _baseStoreFeatures.ToString());

            _enableSessionState = Convert.ToBoolean(xe.ValueOf("configuration/myPortal/profileManagement/@enableSessionState"));
            Response.Write("<br />_enableSessionState:" + " " + _enableSessionState.ToString());

            _availableStoreFeatures = xe.DictionaryStringUintItemsOf("configuration/myPortal/profileManagement/storeFeatures/storeFeature", "name", "value");
            Response.Write("<br /><br />_availableStoreFeatures:");
            foreach (string key in _availableStoreFeatures.Keys)
            {
                Response.Write("<br />Key: " + key + "Value:" + _availableStoreFeatures[key].ToString());
            }

            _mainRoles = xe.DictionaryStringBoolItemsOf("configuration/myPortal/profileManagement/mainRoles/role", "name", "hasAllFeatures");
            Response.Write("<br /><br />_mainRoles:");
            foreach (string key in _mainRoles.Keys)
            {
                Response.Write("<br />Key: " + key + " Value:" + _mainRoles[key].ToString());
            }
        }
        
    </script>
</body>
</html>
You should obtain that result when you call the page.



No comments: