Skip Ribbon Commands
Skip to main content

Advanced SharePoint Development

:

Quick Launch

Home
June 22
Checking if a User Account is Enabled in Active Directory

On occasion, I would receive an error when using the SPWeb.EnsureUser() method. The error stated: "The specified user {0} could not be found.". What thing I have found is that this error throws when the user account does not exist in Active Directory, or the user account is set to “Disabled”.

SharePoint does not seem to clear out ACL’s that reference these disabled or deleted users, this means permissions can apply to users that do not exist. I can understand why the User Info list is not cleaned out when users become disabled, users may be referenced by “Created By” fields, or other SPFieldUser fields in SharePoint, so you might want their name or email address. But the ACL’s themselves never get removed either, and I can’t think of a good reason to keep them around. I’m not sure why they are not just trimmed by the sync job, at least when the accounts no longer exist in AD altogether. ACL’s and User Information lists will eventually just grow and grow un-checked.
 
None the less, I was attempting to programmatically copy Role Assignments from one list item, to another list item, the targeted list item was in another site collection. What I found is that "The specified user {0} could not be found." popped up when I attempted to run the EnsureUser method. The reason for this was because the user with the ACL in the foreign list referenced a user that was now disabled, and as such could not be referenced by any new ACLs you might want to create.
 
What I needed was a way to check if:
 

1.     The user exists in Active Directory

2.     The user is not disabled
 
So I built some re-usable code to do this.
 
public static bool NTTokenExists(string name)

        {
            if (SpecialNTToken(name)) return true;
            bool NTTokenExists = false;
            try           
            {               
                NTAccount Acct = new NTAccount(name);
                SecurityIdentifier id =
                     (SecurityIdentifier)Acct.Translate(typeof(SecurityIdentifier));
                bool SidExists = id.IsAccountSid();
                if (SidExists)
                {
                    DirectoryEntry user = GetDirectoryEntryForNTAccount(Acct);
                    if (user.Properties.Contains("userAccountControl") &&
                           user.Properties["userAccountControl"].Count > 0)
                    {
                        const int DISABLED = 2;
                        bool isDisabled =
                           (((int)user.Properties["userAccountControl"][0])
& DISABLED) == DISABLED;
                        NTTokenExists = !isDisabled;
                    }
                }
                else
                {
                    NTTokenExists = false;
                }
 
 
            }
            catch (IdentityNotMappedException er)
            {
                Utility.TraceError("NTTokenExists "+name,er);
            }
            return NTTokenExists;
        }
 
This method does the checks required, although it relies on some other methods in my helper class. Because what I needed was a way to generically get a directory entry record from a DOMAIN\user style token regardless of the domain and username entered. This is how it’s done.
Firstly, to resolve the NetBios domain name back to it’s LDAP search root, the following method is used.
public static string FriendlyDomainToLdapDomain(string friendlyDomainName)
        {
            string ldapPath = null;
 
            DirectoryContext objContext = new DirectoryContext(
                DirectoryContextType.Domain, friendlyDomainName);
            Domain objDomain = Domain.GetDomain(objContext);
            ldapPath = objDomain.Name;
 
            return ldapPath;
        }
 
Then, a search against the LDAP root DN is performed with the username as the sAMaccountname property.
public static DirectoryEntry GetObjectDirectoryEntry(objectClass objectCls, string objectName, string LdapDomain)
        {
            string distinguishedName = string.Empty;
            string connectionPrefix = "LDAP://" + LdapDomain;
            DirectoryEntry entry = new DirectoryEntry(connectionPrefix);
            using (DirectorySearcher mySearcher = new DirectorySearcher(entry))
            {
 
                switch (objectCls)
                {
                    case objectClass.user:
                        mySearcher.Filter = "(&(objectClass=user)(|(cn=" +
objectName + ")(sAMAccountName=" + objectName + ")))";
                        break;
                    case objectClass.group:
                        mySearcher.Filter = "(&(objectClass=group)(|(cn=" +
objectName + ")(dn=" + objectName + ")))";
                        break;
                    case objectClass.computer:
                        mySearcher.Filter = "(&(objectClass=computer)(|(cn=" +
                            objectName + ")(dn=" + objectName + ")))";
                        break;
                }
 
                SearchResult result = mySearcher.FindOne();
 
                if (result == null)
                {
                    throw new NullReferenceException
                    ("unable to locate the distinguishedName for the object " +
                    objectName + " in the " + LdapDomain + " domain");
                }
                DirectoryEntry directoryObject = result.GetDirectoryEntry();
                return directoryObject;
            }
        }
 
This code is not 100% original, a lot of it is modified by code I found using Google, Google knows best. I however have no idea who originally authored it, if it looks familiar, thanks for the help.

June 10
Building Modular Code

Having been shown demos of some really impressive reporting technology built using SilverLight, Google maps and BI integration, the client turned around and commented that the development work done so far was too impressive. In that the developers produced too much functionality. Getting the demo done took 2 weeks, which in real terms is not a long period of time for this type of complexity. These types of comments tend to get the development firms turning around and rebuilding large portions of the system, this takes even more time, and produces something less functional. This begs the question, why on gods earth would you spend more time, producing a less functional result?

There are two sides of this story, complexity reduction, and re-use of built components. And each has its strengths and weaknesses.

Firstly, developers, specifically smart developers build their code for re-use. Modular components that can be switched out much the same way an engine in a car could be. They just need to be re-wired and all the pipes connected to have the module up and running in a new context. This has a surprising consequence, in that complex functionality can be set up quickly, quicker in fact than scratch building solutions. And because the modules invariably go through large amounts of refinement, tend to be more stable as well.

Developers spend years perfecting these modules, doing more refinement each time the module is re-used. In fact the more a module is used in various solutions, the more refined it becomes. Scratch built code on the other hand has to be refined from scratch as well as built from scratch. On average, scratch built code will go through more change cycles after deployment than modular code, and the result will be more manila, and plain.

There is one big problem with the modular approach. That is complexity.

The developer or the team of developers building these modules are the only ones who really understand the nuances of the code. They understand the reasons for the multitude of delicate subtleties in the code. Any new developer coming in cannot see these nuances, and may actually introduce bugs when modifying things that address these finer points.

Another thing is the rigidity of the requirement, in my experience, if the client takes an approach that enforces delivery of a very specific result, the development time taken to meet the result becomes extended, and re-use of this intellectual property becomes limited. As the client you need to answer a few questions, as you would in any other decision. For instance, if your mechanic fitted a 3 litre engine to your car, but you asked for a 2 litre engine, would that really be a problem? Did you ask for the the 2 litre engine for its fuel consumption, or just because your car needed an engine? You should keep in mind that your mechanic may be a 3 litre engine specialist, and could fit and service your engine best if it is the engine he knows best.

Some of my thoughts around this:

Complexity reduction

  1. Keep in mind that simple means basic, and basic is limited in its scope, reach and functionality.
  2. What does the phrase “Complexity reduction” mean to you? Do you mean it’s simpler to build and maintain? Or that it is simpler and more intelligent from a usability perspective? These two are mutually exclusive.
  3. Complex functionality does not need to be complex, if modules are built and packaged in a “black box” approach; the complex functionality in a module becomes an atomic unit. A solution built using a collection of these complex modules is therefore less complex.

Information systems are in essence a collection of man-made systems stacked on top of each other. They are massively complex systems when viewed holistically. From the hardware of the servers, all the way up to the interface the user interacts with, the complexity is simply immense. It is only made simpler by taking a modular approach.

Building modular

  1. Make sure your development contractors, or internal development staff, build modular components as a standard. If you need reports built, build them using reusable reporting modules.
  2. Open a long term engagement with the firms responsible for developing your modules, this way modification to the modules is more successful, and retention of lessons learnt is better.
  3. Protect and re-use your intellectual property, your modules were built for you, and if you followed points 1 and 2, they can be reused and modified. Make use of those facts.
  4. In IT, lessons are hard learnt; make sure your organisation remembers them. Much time and money can be wasted when intellectual property is lost.

 

 

 

 

 

April 29
Automating Solution File Deployment

Often you'll have a development environment or UAT testing platform that has a specific set of solution files installed. You may want to export the solution files used by one of your farms so that you can deploy changes to another. If you development was done correctly, you will not need much more than the solution files, and some time to change certain key things manually. 

While busy building the deployment process for the latest project I’m busy on, I built a deployment console application which can export and import solution files, it’s built in c#.
 
The following code exports solution files deployed to the specified webApplication (from the config database)
 
 

 
                    using (SPSite site = new SPSite(webAppURL))
                    {
                        using (SPWeb web = site.OpenWeb())
                        {

                            // Solutions

                            foreach (SPSolution sol in SPFarm.Local.Solutions)
                            {


                                Console.WriteLine(sol.Name);

                                try
                                {
                                    if (sol.ContainsWebApplicationResource)
                                    {
                                        foreach (SPWebApplication webapp in sol.DeployedWebApplications)
                                        {
                                            if (webapp.Id == site.WebApplication.Id)
                                            {
                                                sol.SolutionFile.SaveAs(targetDirectory + "\\SolutionFiles\\" + sol.Name);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        sol.SolutionFile.SaveAs(targetDirectory + "\\SolutionFiles\\" + sol.Name);
                                    }
                                }
                                catch (Exception er)
                                {
                                    sol.SolutionFile.SaveAs(targetDirectory + "\\SolutionFiles\\" + sol.Name);
                                    Console.WriteLine(er.Message);
                                    Console.WriteLine(er.StackTrace);
                                }
                            }

                        }
                    } 
 

The following code waits for all solution deployment timer jobs to complete.
 

 
        public static void WaitForDeploymentJobs()
        {
            bool jobsRunning = true;
            while (jobsRunning)
            {
                jobsRunning = false;
                Console.Clear();
                Console.WriteLine("Waiting for deployment Jobs to finish...");
                foreach (SPService service in SPFarm.Local.Services)
                {

                    foreach (SPJobDefinition jobDefinition in service.JobDefinitions)
                    {
                        //Console.WriteLine(jobDefinition.Name);
                        if (jobDefinition.GetType().FullName == "Microsoft.SharePoint.Administration.SPSolutionDeploymentJobDefinition")
                        {

                            Console.WriteLine(jobDefinition.Name);

                            jobsRunning = true;
                        }
                    }
                }
                Thread.Sleep(1000);
            }
        }
 

The following code deploys or upgrades all solution files found in a specific directory to a web application
 

 
			using (SPSite site = new SPSite(webAppURL))
                        {
                            using (SPWeb web = site.OpenWeb())
                            {


                                foreach (FileInfo file in soldir.GetFiles())
                                {
                                    try
                                    {
                                        SPSolution foundSolution = null;



                                        foreach (SPSolution sol in SPFarm.Local.Solutions)
                                        {
                                            if (sol.Name == file.Name)
                                            {
                                                foundSolution = sol;
                                            }
                                        }

                                        if (foundSolution != null)
                                        {
                                            try
                                            {
                                                Console.WriteLine("    Upgrading " + file.Name);
                                                foundSolution.Upgrade(file.FullName);
                                            }
                                            catch (Exception er)
                                            {
                                                Console.WriteLine("    SOLUTION UPGRADE WARNING: " + er.Message);
                                            }
                                        }
                                        else
                                        {
                                            Console.WriteLine("    Adding " + file.Name);
                                            foundSolution = SPFarm.Local.Solutions.Add(file.FullName);
                                        }

                                        bool deployed = foundSolution.Deployed;

                                        if (deployed)
                                        {
                                            if (foundSolution.ContainsWebApplicationResource)
                                            {
                                                deployed = false;
                                                // check if deployed to web application...
                                                try
                                                {
                                                    foreach (SPWebApplication webapp in foundSolution.DeployedWebApplications)
                                                    {
                                                        if (webapp.Id == site.WebApplication.Id)
                                                        {
                                                            deployed = true;
                                                        }
                                                    }

                                                }
                                                catch (Exception er)
                                                {
                                                    Console.WriteLine(er.Message);
                                                    Console.WriteLine(er.StackTrace);
                                                }
                                            }
                                        }

                                        if (!deployed)
                                        {
                                            try
                                            {
                                                if (!foundSolution.ContainsWebApplicationResource)
                                                {
                                                    Console.WriteLine("    Deploying " + file.Name);
                                                    foundSolution.Deploy(DateTime.Now, true, true);
                                                }
                                                else
                                                {
                                                    Console.WriteLine("    Deploying " + file.Name + " to " + site.Url);
                                                    Collection<SPWebApplication> webapps = new Collection<SPWebApplication>();
                                                    webapps.Add(site.WebApplication);
                                                    foundSolution.Deploy(DateTime.Now, true, webapps, true);
                                                }

                                            }
                                            catch (Exception er)
                                            {
                                                Console.WriteLine(er.Message);
                                                Console.WriteLine(er.StackTrace);
                                            }
                                        }
                                    }
                                    catch (Exception er)
                                    {
                                        Console.WriteLine(er.Message);
                                        Console.WriteLine(er.StackTrace);
                                    }
                                }
                            }

 

April 29
SharePoint 2010 + WCF + AJAX

Ok, so I’ve been working with some of the .NET 3.5 functionality in SharePoint 2010, some of this functionality has been difficult to implement. The idea is to use the AJAX functionality, together with the WCF capabilities for client integration in SharePoint, for now this will include JavaScript client side proxies for services (Service Reference in the AJAX Script Manager). We need a few things to get this to function.

  1. The AJAX ScriptManager must be added to the page.
  2. To be used by the AJAX ScriptManager, the WCF service needs to use the enableWebScript web.config option (or WebScriptEnablingBehavior class added to the endpoint)
JavaScript proxy generation is an extremely useful function of the AJAX toolkit in .NET. Using WCF and a ScriptManager, you can get things rolling without having to worry about generating soap request envelopes yourself, dealing with the XHTTPRequest object in JavaScript, and parsing JSON results.  Everything is done for you.
 
In fact if you use Data Contracts in the responses from your services, the Object will be serialized, transported and de-serialized automatically, you’ll just be able to access the object properties using JavaScript.
To keep things simple you should consider the following:
  1. You don’t want to be making changes to MasterPages in order to get the AJAX ScriptManager added to the page, you should register it on all pages when a feature is activated. This will allow you to disable it if needed.
  2. You don’t want to have to mess around with web.config settings for the WCF service, you should use a factory class to pre-configure the service.
So the first thing we are going to look at is the ScriptManager, we will create a delegate control which adds a ScriptManager  to the page if it doesn’t already exist, the delegate control will also register the WCF Service we create with the script manager as well as any other CSS/JavaScript files you’ll need.

It’ll look something like this (can’t take credit for this code comnpletely, it’s based on something I found some time back and unable to find at the moment), it is attached to the AdditionalPageHead delegate by a feature.
 
public class MyDelegate : WebControl
    {
        public static String ControllerFileName = "Controller.js";
       
 
        protected override void OnInit(EventArgs e)
        {
            Page.Init += delegate(object sender, EventArgs e_Init)
            {
                if (ScriptManager.GetCurrent(Page) == null)
                {
                    ScriptManager sMgr = new ScriptManager();
                    Page.Form.Controls.AddAt(0, sMgr);
                }
 
                if (ScriptManager.GetCurrent(Page) != null)
                {
                    // build reference to Service Script
                    String url = SPContext.Current.Web.Url;
                    if(!url.EndsWith("/")) url += "/";
                    Uri service = new Uri(new Uri(url),"_vti_bin/MyService/MyService.svc");
                    ScriptManager.GetCurrent(Page).Services.Add(new ServiceReference(service.ToString()));
 
                    // register scripts
                    ClientScriptManager csm = this.Page.ClientScript;
 
                   
                    if (!csm.IsClientScriptIncludeRegistered(ControllerFileName))
                    {
                        csm.RegisterClientScriptInclude(ControllerFileName, this.ResolveClientUrl("/_layouts/MyService/" + ControllerFileName));
                        csm.RegisterClientScriptBlock(typeof(CheckInNotifyDelegate), "CSS", "<link rel=\"stylesheet\" type=\"text/css\" href=\"" + this.ResolveClientUrl("/MyService/Styles.css") + "\"/>"); 
                    }
                }
            };
            base.OnInit(e);
        }
 
This causes the ScriptManager to be added the Controls collection on the Page.Init event, this is the correct place in the rendering life cycle of ASP.NET to modify the Controls collection.
The Feature can then contain a delegate control which is added to the AdditionalPageHead delegate.The element manifest will look something like this.
 
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Control Id="AdditionalPageHead" Sequence="50" ControlClass="MyAssembly.MyDelegate" ControlAssembly="$SharePoint.Project.AssemblyFullName$"/>
</Elements>
 
The Control itself basically adds the ScriptManager to the page (if one doesn't exist already) registers the service with the Script Manager, creates a reference to a controller JavaScript file on the page (which would contain any logic needed to process the results of the service invocation).
What I normally do for any style sheets, is ghost them in the content database, it makes customization of the style sheets easier without having to re-deploy the solution file.
I would recommend you try use XSLT where you can, because it can be ghosted in the content database, and then easily modified if format tweaks are needed. You can easily serialize DataContracts into XML for processing against XSLT.
The next thing is the WCF Service. SharePoint has factory classes which provision the service to handle the detail around things like authentication (Claims based authentication for instance). This removes the need for customised web.config entries or custom code to handle the complexities around this. The Service Host is configured automatically. This is a great help. These are the options.
 
Service Type
Service Factory
Description
SOAP service
MultipleBaseAddressBasicHttpBindingServiceHostFactory
Basic HTTP binding must be used, which creates endpoints for a service based on the basic HTTP binding.
REST Service
MultipleBaseAddressWebServiceHostFactory
The service factory creates endpoints with web bindings.
ADO.NET Data Service
MultipleBaseAddressDataServiceHostFactory
A data service host factory can be used.
 
To make use of one of these you .svc file would look something like this.
 
<%@ ServiceHost Language="C#"
    Service="MyAssembly.MyService,
        $SharePoint.Project.AssemblyFullName$"     Factory="Microsoft.SharePoint.Client.Services.MultipleBaseAddressBasicHttpBindingServiceHostFactory,
Microsoft.SharePoint.Client.ServerRuntime, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c
"%>
 
None of these options however support the enableWebScripts behaviour on the end points. The enableWebScripts behaviour (or WebScriptEnablingBehavior class) is used by the AJAX framework, and allows the service to return a service proxy in javascript when “/js” or “/jsdebug” is added to the URL of the service.
The Service Host Factory classes in SharePoint do however set things up like Claims Based Authentication, or whatever authentication method you are using on the site automatically. What we want is a service host that does both, it configures the service host based according to SharePoint, and automatically adds the enableWebScripts behaviour to the endpoints.
 
Luckily we can build our own service hosts and service host factories. All we need is a custom class for the service host and a custom class for the factory class. We extend the REST based SharePoint Service Host, we will need to remove any end point behaviours that are there already, and add the enableWebScript behaviour.
 
That will be done in the custom service host, but first we need to build our own service host factory class, also extending the REST based SharePoint factory class.
 
   public class MyServiceHostFactory : Microsoft.SharePoint.Client.Services.MultipleBaseAddressBasicHttpBindingServiceHostFactory
    {
 
        protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
        {
            return new MyServiceHost (serviceType, baseAddresses);
        }
 
 
    }
 
Then we extend the MultipleBaseAddressWebServiceHost class and modify the behaviours when the service host is opened.
 
public class MyServiceHost  : Microsoft.SharePoint.Client.Services.MultipleBaseAddressWebServiceHost
    {
 
        public MyServiceHost (Type serviceType, params Uri[] baseAddresses)
            : base(serviceType, baseAddresses)
            {
            }
     
        protected override void OnOpening()
        {
            base.OnOpening();
           
            foreach (ServiceEndpoint endpoint in base.Description.Endpoints)
            {
                if (((endpoint.Binding != null) && (endpoint.Binding.CreateBindingElements().Find<WebMessageEncodingBindingElement>() != null)) && (endpoint.Behaviors.Find<WebScriptEnablingBehavior>() == null))
                {
                    // try remove any previous behaviours
                    while (endpoint.Behaviors.Count > 0)
                    {
                        endpoint.Behaviors.RemoveAt(0);
                    }
                    endpoint.Behaviors.Add(new WebScriptEnablingBehavior());
                }
               
            }
 
 
            ServiceDebugBehavior debug = this.Description.Behaviors.Find<ServiceDebugBehavior>(); 
            // if not found - add behavior with setting turned on 
            if (debug == null) {    
                this.Description.Behaviors.Add(         
                    new ServiceDebugBehavior() { IncludeExceptionDetailInFaults = true }); }
            else {      
                // make sure setting is turned ON    
                if (!debug.IncludeExceptionDetailInFaults)    
                {        
                    debug.IncludeExceptionDetailInFaults = true;    
                }
            }
 
            ServiceMetadataBehavior metadata = this.Description.Behaviors.Find<ServiceMetadataBehavior>();
            // if not found - add behavior with setting turned on 
            if (metadata == null)
            {
                this.Description.Behaviors.Add(
                    new ServiceMetadataBehavior() { HttpGetEnabled = true });
            }
            else
            {
                // make sure setting is turned ON    
                if (!metadata.HttpGetEnabled)
                {
                    metadata.HttpGetEnabled = true;
                }
            }
 
        }
 
 
    }
 
This also sets some service behaviours as well to enable debugging and metadata.
So now your SVC file can look like this.
 
<%@ ServiceHost Language="C#"
    Service="MyAssembly.MyService,
        $SharePoint.Project.AssemblyFullName$"
    Factory=" MyAssembly.MyServiceHostFactory, $SharePoint.Project.AssemblyFullName$"%>
 
Now when you load up your service with the “/js” or “/jsdebug” added to the URL you’ll get a JavaScript Service Proxy that the script manager will use.
 

 

One last thing, the Interface for the service should have the correct serialization attributes. These will ensure that your service functions when accessed via the AJAX Service Proxy class. The Service Interface Class would look something like this.

 

 
[ServiceContract(Namespace="", Name="MyService")]
        public interface IMyService
        {
 
            [OperationContract]
            [WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat=WebMessageFormat.Json, ResponseFormat=WebMessageFormat.Json)]
            string GetMyParameter(String Option1, String Option2);
 
 
        }
 Just for completeness I’ll include the implementation of the service as well.
 
    [ServiceBehavior(Namespace="MyService")]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class MyService : IMyService
    {
        public string GetMyParameter(String Option1, String Option2)
        {
              return "This Worked!!!";
        }
    }

 

 

 About this blog

 
 
Welcome to my Advanced SharePoint development Blog. This blog is intended to detail solutions to common but complex issues that may arise while building solutions based on SharePoint technologies.