Posts tagged ‘SharePoint’

SharePoint Application Development the Free and Easy Way

These resources accompany a presentation at the Western New York Dot Net Users Group (WNYDNUG):

So what does the title of this presentation really mean?

By SharePoint, I mean Windows SharePoint Services 3.0 (WSS 3.0) not to be confused with MOSS2007 which adds features on top of WSS 3.0 and requires licenses.

By Application Development, I don’t mean canned features that work right out of the box, but additional web parts, web pages, and functionality that require the skill of an intermediate ASP.Net web developer.

By Free I mean that in addition to the free WSS 3.0 platform, all the development tools used in the presentation are also completely without cost.

And by Easy I mean ‘easier’ than the way I used to develop SharePoint solutions years ago. Unfortunately, developing SharePoint using the standard, documented methods and templates is really, really, really, really, really, really (so glad I got that thesaurus), really, really, hard. And when I began to settle on a set of tools and techniques that relieved my pain, I got kind of excited and decided to do a presentation. Enjoy.

Resources:

Windows SharePoint Services 3.0 SP1 Developer Evaluation VPC Image

…Windows Server 2003, WSS 3.0, Visual Studio 2008 Pro with extensions for SharePoint, Silverlight, and Ajax, SharePoint Designer 2007, Office 2007, and… (I’m seriously impressed they did this) a shortcut to Notepad already added to windows’ right-click ‘Send To’ menu!!!!!

SmartPart

This turns the nightmare of Web Part development into a dream. 

ASP.Net apps in the _layouts folder

You’re an ASP.Net developer. Now act like one! Using this technique, you can just deploy your standard asp.net web apps right to SharePoint.

MVC apps in SharePoint

Similar approach to asp.net in _layouts, but with the magic of MVC.

U2U CAML Query Builder

When CAML makes you cry, CAML Query Builder will dry your tears.

OfficeToolbox.SharePoint.Lists

Sometimes all you really want is for SharePoint lists to have a teensy bit more functionality (hide columns, move column descriptions around). Here ‘ya go.

SharePoint Features

Added features (list printing, user management, turn stuff off on List Toolbars, and much more) from a community on codeplex. Woah! Scot Hillier put these together; that’s a name you can trust in SharePoint development.

Tool Basket

Another Codeplex community project. The easy Form Designer alone is worth the expense (what! it’s free??)

 Roger Lamb - Common Coding Issues When Using the SharePoint Object Model

As soon as you’ve written enough code with SharePoint Object Model to be dangerous, you owe it to yourself to review it against this set of guidelines

Jan Tielens

Jan was the first one to nudge me in the right direction with SmarPart. He continues to provide lots of fresh ideas; JQuery techniques as of late.

SmartTools

In addition to SmartPart, Jan also made these numerous additions to SharePoint features available on CodePlex (check out the slick charts!)

Manage large SharePoint lists for better performance

Eventually you may end up with a lot of data (items in lists). It’s possible to have a list with millions of items, but it’s best not to exceed 2,000 items per ‘container’ (root or folder). It’s also advised not to select more than 2,000 items at a time. If you’re handling 5,000+ items at a time, you’re really asking for trouble, as MS SQLServer may lock the whole table. That’s THE TABLE; the one and only table that holds every item for every list in every site of your SharePoint collection. Youch!

SharePoint development the “right” way

Finally, if you’re producing apps commercially, or deploying to environments that enforce stricter standards, you may be forced to develop SharePoint the “Right” way. You’ll need to learn the intricacies of web part plumbing and architecture, you won’t have drag-n-drop controls, and to properly deploy all this, you’ll need to build WSP Solution Packages which contain numerous files which must be configured properly and end up in all the right nooks and crannies of the web server (12 hive, GAC, bin, etc.). Deployment typically forces a restart of your site, too.

To get a full picture of what’s involved in building and deploying a WSP Package WITHOUT the ease of the short-cuts and tools provided above see this example project: http://www.codeproject.com/KB/sharepoint/WebParticles.aspx.  I like this example because it perfectly illustrates the complexity involved, but also provides a guide to building your own UserControl wrapper (like SmartPart).

The following tools from Microsoft and Codeplex will help with developing and deploying applications as WSP Solution Packages.

VSeWSS 1.3 CTP

…hey, how’s that for a bunch of letters and numbers thrown together (the lower case ‘e’ is a nice touch, too)! These are Microsoft’s extensions for Visual Studio 2008 to help with developing and deploying SharePoint Web Parts and such. And it only took me like 3 hours to install and configure correctly *sigh*.

These screencast demos might help too (they make it look sooooo easy)

http://channel9.msdn.com/posts/kirke/

…but over the years, developer communities weren’t waiting around for Microsoft, and these two tools have become the de-facto standard for SharePoint development and deployment.

WSPBuilder

STSDEV

s

Technical Notes: 

  • Smart Part User Controls are kept in:
    C:\Inetpub\wwwroot\wss\VirtualDirectories\80\UserControls
  • Custom ASP.Net web applications are kept in:
    C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS
  • If you have supporting libraries in your \layouts\ web projects, you should set their build output to go to:
    C:\Inetpub\wwwroot\wss\VirtualDirectories\80\bin
  • Another gotcha I experienced. Once, when I deployed SmartPart, some of the samples didn’t run for a couple reasons. 1. if you’re using ajax enabled controls, you have to make sure you’ve got all that ajax.net stuff configured and that there’s a script manager somewhere in the page or master page. 2. The windows permissions on the sample ascx files, themselves got messed up when I was copying the samples, and I got Access Denied errors. ‘Touch’ the permission on those files, or maybe copy and paste the contents into new file and those errors should clear up.
  • If you plan on inserting updating deleting list items, you’ll probably run into security errors. The file to adjust is:
    \12\CONFIG\wss_mediumtrust.configFor details on this and MANY OTHER ‘tips and tricks’ see:
    http://msdn.microsoft.com/en-us/library/dd583162.aspx?ppud=4
  • In the custom web applications you add to the \layouts folder, the app will barf if your web.config has this line:
    <authentication mode=”Windows” />, so comment that out.
  • Also, regarding web.config, keep in mind that to take advantage of LINQ and new c# 3.5 language features, the default web.config may not up to snuff, so you’ll probably have to add a few settings to get that working. Here are settings I added recently to a ‘new’ setup of a VPC to get my code examples running….<compilation><assemblies>
     <add assembly=”System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089″/>
     <add assembly=”System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/>
     <add assembly=”System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089″/>
     <add assembly=”System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089″/>
     <add assembly=”Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71E9BCE111E9429C”/>
    </assemblies><compilation>…<system.codedom>
      <compilers>
       <compiler language=”c#;cs;csharp” extension=”.cs” warningLevel=”4″ type=”Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″>
        <providerOption name=”CompilerVersion” value=”v3.5″/>
        <providerOption name=”WarnAsError” value=”false”/>
       </compiler>
       <compiler language=”vb;vbs;visualbasic;vbscript” extension=”.vb” warningLevel=”4″ type=”Microsoft.VisualBasic.VBCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″>
        <providerOption name=”CompilerVersion” value=”v3.5″/>
        <providerOption name=”OptionInfer” value=”true”/>
        <providerOption name=”WarnAsError” value=”false”/>
       </compiler>
      </compilers>
     </system.codedom>…<runtime>
      <assemblyBinding xmlns=”urn:schemas-microsoft-com:asm.v1″>
       <dependentAssembly>
        <assemblyIdentity name=”System.Web.Extensions” publicKeyToken=”31bf3856ad364e35″/>
        <bindingRedirect oldVersion=”1.0.0.0-1.1.0.0″ newVersion=”3.5.0.0″/>
       </dependentAssembly>
       <dependentAssembly>
        <assemblyIdentity name=”System.Web.Extensions.Design” publicKeyToken=”31bf3856ad364e35″/>
        <bindingRedirect oldVersion=”1.0.0.0-1.1.0.0″ newVersion=”3.5.0.0″/>
       </dependentAssembly>
      </assemblyBinding>
     </runtime>…

 

 

  

Hope this all helps.

Cheers!

-Denise

Blueprint CSS for SharePoint

I like using Blueprint CSS, available at http://www.blueprintcss.org/, but it clashes a bit with some of SharePoint’s css settings, especially since SharePoint uses a lot of tables for layout and Blueprint’s table settings are generously padded and 100% wide (makes your SharePoint page look like it blew apart!).

But If you create another stylesheet with some overrides, and reference it after Blueprint’s, you can fix most of these problems. As I discover more of these fixes, I’ll add them here.

/*override Blueprint css table settings for SharePoint */
table {margin-bottom:0;}
th, td, caption {padding:0px 0px 0px 0px;}
table.ms-MenuUI,
table.ms-MenuUIItemTable,
.ms-consoleminiframe table,
.ms-globalbreadcrumb table,
.ms-bannerContainer table,
#MSOWebPart_Header table
{width:auto;}

Display PDF document in SharePoint

Rather than merely link to a pdf file, why not display the pdf right within a SharePoint page?

Add a Page Viewer web part and configure it like this.

SharePoint Page Viewer Web Part - Pdf configuration

To make the pdf display without the adobe tool bar and scroll bars, make sure to add parameters to the PDF’s URL.

The full URL to the pdf file should look something like this:
/Shared Documents/doc.pdf#toolbar=0&navpanes=0&scrollbar=0

For more parameters see this documentation:
http://partners.adobe.com/public/developer/en/acrobat/PDFOpenParameters.pdf

setting SharePoint buttons to ignore jquery validation

I’m using JQuery with SharePoint. When using the Validation plug-in I had some trouble with the buttons on the Tool Part causing validation errors and preventing me from saving changes to the web part configuration. Thankfully, each of the buttons had the class “UserButton” assigned. So this bit of code fixed the problem.

$(".UserButton").addClass("cancel");

The validation plug-in will now ignore the click events on these buttons because they’ve been tagged as “cancel”.

Update SharePoint User Information with enterprise data

SharePoint stores ‘user’ data in its “User Information List”. You can check it out here: http://YourSharePointServer/_layouts/people.aspx. It’s a lot like any other SharePoint list. You can add or remove fields to suit the needs of your organization.

When users first visit/authenticate to the site, or someone first adds a user via the people picker control, data from Active Directory is automatically copied into the “User Information List”. In my experience, this data is pretty minimal. Account (login), Name, and E-mail for example. SharePoint apps can really fall short when you need to provide more detailed, accurate, and up-to-date user information.

There’s probably plenty of excellent user data sitting in some other database, usually maintained by Human Resources, but leveraging it from within SharePoint can be a challenge. To help with this I wrote a console app that copies an organization’s “HR” data to SharePoint. Here’s what the main routine looks like:

private static void UpdateSharePointUsers() {

    //get disposable reference to SharePoint site
    using (SPSite site = new SPSite(Settings.Default.SharePointSiteUrl))
    {
        Console.WriteLine("Updating Users for {0}", site.Url);

        Console.WriteLine("Getting HR Data...");

        HrDataContext hrDb = new HrDataContext();
        foreach (Employee hrEmployee in hrDb.Employees)
        {
            Console.Write("Attempting to get or create SPUser for {0}:", hrEmployee.Login);

            SPUser user = site.GetOrCreateSPUser( hrEmployee );

            if (null == user)
            {
                Console.WriteLine(" Failed");
            }
            else
            {
                Console.WriteLine(" Got SPUser #{0}", user.ID);

                try
                {
                    Console.Write("Attempting to update User Information for SPUser #{0}:", user.ID);
                    user.UpdateUserInformation( hrEmployee );
                    Console.WriteLine(" Success");

                }
                catch (Exception ex) {
                    Console.WriteLine(" Failed.\r\nError: {0}",
                        user.ID, ex.Message);
                }
            }
        }
    }
}

So the SPSite class has a GetOrCreateSPUser(SPSite), and the SPUser class has a handy UpdateUserInformation(Employee) method. What? Your SharePoint object model doesn’t have these methods? I’m afraid this article wasn’t too much help then.

Happy Coding! Cheers!!

Just kidding. My SharePoint object model doesn’t have those methods either. The magic of Extension Methods has allowed my console application to ‘velcro’ these methods onto SPSite and SPUser.

Here they are:

public static SPUser GetSPUser(this SPSite site, Employee hrEmployee) {
    try
    {
        return site.RootWeb.SiteUsers[hrEmployee.Login];
    }
    catch (Exception)
    {
        return null;
    }
}

public static SPUser CreateSPUser(this SPSite site, Employee hrEmployee)
{
    SPUser user = null;
    try
    {
        site.RootWeb.SiteUsers.Add(
            hrEmployee.Login,
            hrEmployee.Email,
            hrEmployee.FullnameLast,
            "User added via automated process.");

        user = site.RootWeb.SiteUsers[hrEmployee.Login];

    }
    catch (Exception) { 

    }
    return user;
}

public static SPUser GetOrCreateSPUser(this SPSite site, Employee hrEmployee)
{
    SPUser user = site.GetSPUser(hrEmployee);
    if (null == user)
    {
        user = site.CreateSPUser(hrEmployee);
    }
    return user;
}

public static SPListItem GetUserInformation(this SPUser user) {
    return user.ParentWeb.SiteUserInfoList.GetItemById(user.ID);
}

public static void UpdateUserInformation( this SPUser user, Employee hrEmployee)
{
    var userInfo = user.GetUserInformation();

    userInfo["Title"] = hrEmployee.FullNameLast;
    userInfo["E-Mail"] = hrEmployee.Email;
    userInfo["Employee ID"] = hrEmployee.EmployeeNumber;
    userInfo["Department Number"] = hrEmployee.DepartmentNumber;
    userInfo["Supervisor Name"] = hrEmployee.Supervisor.FullnameLast;
    userInfo["Phone Extension"] = hrEmployee.Extension;
    userInfo["Cell"] = hrEmployee.CellPhone;

    userInfo.Update();    //write changes to SharePoint
}

NOTE: I used “Title” in place of the “Name” field in SharePoint’s User Information List, because even though SharePoint displays this field as “Name”, the “internal field name” is actually “Title”. Stuff like this tends to bite you a lot in SharePoint. There are ways to get around ‘internal’ vs. ‘display’ field names by taking extra steps. You can also refer to fields via GUID or index. Field name is easiest to read though, I think.

So, another thing that would be great… Doesn’t a SharePoint list support User fields? So rather than have a “Supervisor Name” Text field, why not instead have a “Supervisor” Person field? Here’s an example of the UpdateUserInformation() method with that bit added.

public static void UpdateUserInformation( this SPUser user, Employee hrEmployee)
{
    var userInfo = user.GetUserInformation();

    userInfo["Title"] = hrEmployee.FullNameLast;
    userInfo["E-Mail"] = hrEmployee.Email;
    userInfo["Employee ID"] = hrEmployee.EmployeeNumber;
    userInfo["Department Number"] = hrEmployee.DepartmentNumber;
    userInfo["Phone Extension"] = hrEmployee.Extension;
    userInfo["Cell"] = hrEmployee.CellPhone;

    //Instead of just a name,
    // add a 'user' as this person's supervisor
    //userInfo["Supervisor Name"] = hrEmployee.Supervisor.FullnameLast;
    var supervisorSPUser = user.ParentWeb.Site.GetSPUser(hrEmployee.Supervisor);
    userInfo["Supervisor"] = supervisorSPUser.ID;

    userInfo.Update();    //write changes to SharePoint
}
...

The UpdateUserInformation() method in the example above has a lot of hard-coded field mappings in it. Wouldn’t it be nice to add mappings or make other changes without having to re-compile and re-deploy the app?

Here’s an example that uses a collection of mapping objects that define how Employee properties map to SharePoint User Information fields. SharePoint field type is also indicated so that “User” fields are handled differently than “Text” fields.

public static void UpdateUserInformation(
    this SPUser user,
    Employee hrEmployee,
    List<HrSharePointMapping> mappings)
{
    var userInfo = user.GetUserInformation();

    foreach (var mapping in mappings)
    {
        //handle user fields differently from text fields
        if (mapping.SPFieldType == SPFieldType.User)
        {
            try
            {
                //since the target is a SPFieldUser type,
                //we'll assume that the HrField is an Employee object
                var anotherHrEmployee = hrEmployee.GetPropertyValue(mapping.HrField) as Employee;
                var anotherUser = user.ParentWeb.Site.GetSPUser(anotherHrEmployee);
                //assign user id to this field
                userInfo[mapping.SPField] = anotherUser.ID;
            }
            catch (Exception)
            {
                //couldn't assign user field. Not a show stopper though
            }
        }
        else   //probably just a text field. TODO: add other type conditions if needed
        {
            userInfo[mapping.SPField] = hrEmployee.GetPropertyValue(mapping.HrField);
        }
    }
    userInfo.Update();    //write changes to SharePoint
}

Here’s the HrSharePointMapping class:

public class HrSharePointMapping
{
    public string HrField { get; set; }
    public string SPField { get; set; }
    public SPFieldType SPFieldType { get; set; }
}

And this code builds the List from an XML file:

public List<HrSharePointMapping> GetHrSharePointMappings() {
    XDocument mapXml = XDocument.Load(Settings.Default.HrSharePointMappingXmlFile);
    var mappings = from m in mapXml.Descendants("Mapping")
                   select new HrSharePointMapping
                   {
                       HrField = m.Element("HrField").Value,
                       SPField = m.Element("SPField").Value,
                       SPFieldType = (SPFieldType)Enum.Parse(
                           typeof(SPFieldType),
                           m.Element("SPFieldType").Value,
                           true )
                   };
    return mappings.ToList();
}

Example mappings XML file, which allows mappings to be configured without recompiling and re-deploying the app:

<?xml version="1.0" encoding="utf-8" ?>
<Mappings>
	<Mapping>
		<HrField>Email</HrField>
		<SPField>E-Mail</SPField>
		<SPFieldType>Text</SPFieldType>
	</Mapping>
	<Mapping>
		<HrField>EmployeeNumber</HrField>
		<SPField>Employee Number</SPField>
		<SPFieldType>Text</SPFieldType>
	</Mapping>
	<Mapping>
		<HrField>FullnameLast</HrField>
		<SPField>Title</SPField>
		<SPFieldType>Text</SPFieldType>
	</Mapping>
	<Mapping>
		<HrField>Department</HrField>
		<SPField>Department Number</SPField>
		<SPFieldType>Text</SPFieldType>
	</Mapping>
	<Mapping>
		<HrField>Extension</HrField>
		<SPField>Phone Extension</SPField>
		<SPFieldType>Text</SPFieldType>
	</Mapping>
	<Mapping>
		<HrField>CellPhone</HrField>
		<SPField>Cell</SPField>
		<SPFieldType>Text</SPFieldType>
	</Mapping>
	<Mapping>
		<HrField>Supervisor</HrField>
		<SPField>Supervisor</SPField>
		<SPFieldType>User</SPFieldType>
	</Mapping>
</Mappings>

Perhaps ADO would have lent itself to this configurable mapping approach better than our Employee object. The DataRow object, for example, allows you to access its field values by column name string. But since we’re using an Employee object, I had to employ a bit of reflection to enable configuration. Here’s the GetPropertyValue() Extension Method that makes the Employee object a bit more ‘dynamic’. This could come in handy for any object, by the way.

public static object GetPropertyValue(this object obj, string propertyName)
{
    Type classType = obj.GetType();
    PropertyInfo info = classType.GetProperty(propertyName);
    return info.GetValue(obj, null);
}