Tag Archive for 'sharepoint'

27
Jun

Error when provisioning a Project Workspace

Project Server splits project data across two areas: Project Server holds the plan data (Tasks, Milestones) & resourcing data (Resources, Timesheets), and then SharePoint (WSS or MOSS) holds the Risks, Issues, Documents and other custom data.

When a project is initially published in Project Server, PWA (Project Web Access) will attempt to provision a new SharePoint subweb. This means Project Server creates a Project Workspace in SharePoint.

The problem I’m witnessing? That even manually creating the workspace results in a single dialog box in which the only text is “Error”.

Continue reading ‘Error when provisioning a Project Workspace’

17
Jun

Workflow in SharePoint Project Server templates

Apparently this isn’t as simple as it should be.

The environment:

Project Server 2007 with SharePoint v3. Each Project has a SharePoint subweb as a “Project Workspace”. The Project workspaces are provisioned automatically by Project Server. Project Server applies a template to the new workspaces. The template can be customised but must be derived from the one Project Server originally shipped.

The scenario:

Put workflow into the template so that each provisioned Project workspace starts with a number of pre-defined processes

Shouldn’t be hard right? Well, it was.

Continue reading ‘Workflow in SharePoint Project Server templates’

10
Jun

Visual Studio 2008 Extension for SharePoint v3 (WSS and MOSS)

Finally this has been released.

And it passed me by totally.

So much so, that I emailed Alex Malek over at Microsoft to badger him to let me have a copy of the unfinished product so that I could get something done for a rather important client, only to have him respond with the public download URL… doh!

So this blog post is really just to ping the search engines out there in the hope that those who are looking can find. Karma-whore… for sure.

Continue reading ‘Visual Studio 2008 Extension for SharePoint v3 (WSS and MOSS)’

26
Feb

SharePoint SPListItem quirks

There are some funny things going on inside SharePoint, and this is one of the funniest…

We have a list, with two fields:

<Field
  Type="Text"
  DisplayName="Category"
  MaxLength="255"
  Name="Category0"
  ColName="nvarchar17"
/>
<Field
  Name="Category"
  FromBaseType="TRUE"
  Type="Choice"
  DisplayName="Category (deprecated)"
  Format="Dropdown"
  FillInChoice="FALSE"
  ColName="nvarchar2"
><!-- CHOICES --></Field>

Note that one has the DisplayName of ‘Category’ and the other has the Name of ‘Category’.

The one with the Name ‘Category’ is deprecated, and we actually want to populate the field with the DisplayName ‘Category’.

You’d expect this would be fine… so consider that we have an empty list and that we create a new item and try and populate the item:

SPListItem newListItem = spList.Items.Add();
newListItem["Title"] = "The title";
newListItem["Category"] = "Category 1";
newListItem.Update();

That should work right? I mean MSDN very plainly states that on a SPListItem that you access a field using the DisplayName of the SPField that you want to set the value against.

It tries to populate the wrong field.

Why? Because it seems that if a SPField exists that has an InternalName the same as a DisplayName, then that SPField takes priority. As the Name property of a SPField *is* the InternalName, we’ve actually just attempted to assign the string “Category 1″ to the Choice Field that is “Category (deprecated)”.

A very subtle but pain in the arse thing that.

So how to get around it? Well, instead of passing in the DisplayName you can use the overloaded version of spListItem[] to pass in the int32 index of the SPField in the spList.Fields collection.

So if you loop the spList.Fields collection, work out where the real “Category” field is that we want to update, and grab the index… you’d expect to be able to update the list item right?

Right, but… not if the item you are creating is the first item to ever go into the list. It will work for every subsequent item, but never the first item.

For some reason the index based accessors only work after an item exists, so if we go back to that original piece of code and update it with the Field index:

SPListItem newListItem = spList.Items.Add();
newListItem["Title"] = "The title";
newListItem[2] = "Category 1";
newListItem.Update();

That won’t work.

Instead you have to save the item, and then grab a fresh instance of the item and save that:

SPListItem newListItem = spList.Items.Add();
newListItem["Title"] = "The title";
newListItem.Update();

SPListItem existingListItem = spList.GetItemByID(newListItem.ID);
existingListItem[2] = "Category 1";
existingListItem.Update();

And that piece of code works.

The rules here:

  • You can’t set a value to a field using the DisplayName if another field exists with a matching Name / InternalName.
  • You can’t use the index based accessors until an item exists within the SharePoint list.

This probably won’t matter to the majority of SharePoint developers, but to those who encounter either of the above, it’s really worth noting them down… close to three days we lost on this.

Credit to Louise B for discovering through much pain the first part of this two-part bug.

06
Oct

Impersonating a user in C#

A random tech post, I just know I’m going to need this again sometime and this is my web notepad.

This is some code, to impersonate another user using calls to unmanaged code, and then to loop some Sharepoint lists as the impersonated user and then go back to the user of the process.

using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Text;

using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;

namespace ConsoleApplication1
{
  class Class1
  {
    [STAThread]
    static void Main(string[] args)
    {
      Console.WriteLine(WindowsIdentity.GetCurrent().Name);

      string siteUrl = "http://ukserver/sites/projectserver_101/";

      string user = "username";
      string userDomain = "domain";
      string password = "pass@word1";

      bool impersonate = true;
      IntPtr userHandle = new IntPtr(0);
      WindowsImpersonationContext impersonatedUser = null;

      if (impersonate)
      {
        bool returnValue = LogonUser(
          user,
          userDomain,
          password,
          LOGON32_LOGON_INTERACTIVE,
          LOGON32_PROVIDER_DEFAULT,
          ref userHandle
          );

        if (!returnValue)
        {
          throw new Exception("Invalid Username");
        }
        WindowsIdentity newId = new WindowsIdentity(userHandle);
        impersonatedUser = newId.Impersonate();
        Console.WriteLine(WindowsIdentity.GetCurrent().Name);
      }

      StringBuilder viewField = new StringBuilder();

      SPSite siteCollection = new SPSite(siteUrl);

      SPWeb spWeb = siteCollection.OpenWeb();

      foreach (SPList currentList in spWeb.Lists)
      {

        if (!currentList.BaseType.Equals(SPBaseType.DocumentLibrary))
        {
          viewField.AppendFormat(
            "<list Name='{0}' Type='{1}' />",
            SPEncode.HtmlEncode(currentList.Title),
            SPEncode.HtmlEncode(currentList.GetType().ToString())
            );
        }
      }
      Console.WriteLine(viewField.ToString());

      if (impersonate)
      {
        //
        // Clean up the impersonated user, returning to our process owner.
        //
        impersonatedUser.Undo();
        CloseHandle(userHandle);
      }

      Console.WriteLine(WindowsIdentity.GetCurrent().Name);
    }

    //
    // This stuff required for impersonation
    //
    public const int LOGON32_LOGON_INTERACTIVE = 2;
    public const int LOGON32_LOGON_SERVICE = 3;
    public const int LOGON32_PROVIDER_DEFAULT = 0;

    [DllImport("advapi32.dll", CharSet=CharSet.Auto)]
    public static extern bool LogonUser(
      String lpszUserName,
      String lpszDomain,
      String lpszPassword,
      int dwLogonType,
      int dwLogonProvider,
      ref IntPtr phToken
    );

    [DllImport("kernel32.dll", CharSet=CharSet.Auto)]
    public extern static bool CloseHandle(IntPtr handle);

  }
}

You can use that impersonation stuff anywhere… just take out the Sharepoint stuff :)

07
Jan

When Web Parts break Web Part Pages

One of the most frustrating things with WSS is the feeling that you’re not in control.

You work hard to develop a web part, have it deployed, and feel a surge of pride when it’s being used… but the day it breaks is the day you’re called on at the 11th, against the clock, to fix it. This is just when you don’t want to be staring at a Web Part Page that has an error, but doesn’t tell you where.

So the thing to do is to tell WSS to give us the error message.

To do this, simply edit the web.config file that applies to the site that the Web Part Page is on.

Apply the following XPATH logic to web.config to see the full Sharepoint error message:
//configuration/Sharepoint/SafeMode[attribute::CallStack='true']

Apply this if you haven’t yet configured the web.config to allow you to see error messages:
//configuration/system.web/customErrors[attribute::mode='Off']

If you also wish to see the form posted and environmental values of the HTTP request, then you can also modify your machine.config file as such:
//configuration/system.web/trace[attribute::enabled='true']

And for those of you who want the easy way out, you can access the Web Part Maintenance page by appending the following to the querystring of the Web Part Page:
Contents=1