February 15, 2012

Welcome

Hi, welcome to my blog!

I’m a female Computer Science graduate working at a government regulatory body as part of their SharePoint Development team.

I specialise in Sharepoint 2010 development, administration and configuration, C# .Net development, JavaScript & jQuery, SQL Server Administration & Configuration, database design and implementation and Java (and Android) development.

I hope to post useful snippets of code as and when I come across them in my day to day work.

Available for consultancy and one-off project work.

Advertisements
September 3, 2014

Reassociate a migrated list workflow with its list

If you’ve ever migrated a list by saving it as a List Template, you may find that any workflows are no longer associated with it.

In my case, I could see my workflows using SharePoint Designer in the ‘Workflows’ section of the Site Objects navigation on the left, but when I viewed the list it was supposed to be running on, it did not appear in its Workflows section.

A quick Google lead me to this article on Gavin McKay’s blog.

The steps involve creating a dummy workflow on the list and noting down the various list GUIDs in this dummy workflow’s .xoml.wfconfig.xml file. After noting down the list IDs, I went to copy them over to the same file within the Workflow that had somehow disassociated itself with the list…. only to find the IDs matched perfectly.

When I looked again at the Workflows that were not attached to the list, I noticed Designer did actually show the name of the correct list. So I thought, if it’s already broken, this won’t hurt it, and clicked the Publish button.

Et, voila! Workflow reattached itself to the list.

June 6, 2014

Get current page URL without Query String using ECMAScript / JavaScript

I keep needing to do this, and I keep forgetting how. Thanks to this useful post over at Praneeth Moka’s blog, which lists a number of useful functions, we can get the server-relative URL for the current page as follows:

function getCurrentPageUrlWithoutQueryString() {
   JSRequest.EnsureSetup();
   alert(JSRequest.PathName); //returns "/siteCollection/site/myPage.aspx"
}

I can’t seem to find a similar function that works in 2010 for getting the first bit of the URL, the web application URL, i.e. http://sharepoint. Leave a comment if you know how!

May 13, 2014

How To: Copy a file from one site to another

I have two features, one scoped to the Site Collection and one scoped to Web. The Site Collection feature has a module which provisions a number of files to a document library in the root web. The Web feature, when activated, is required to take a copy of some of the files provisioned by the Site Collection feature and add them to a document library within the site that the feature was activated on.

I found this trickier than I thought it would be, mostly due to the fact that the SPFile.CopyTo(string strNewUrl) method does not state that you can’t use it to move a file between SPWebs. It took multiple attempts providing the URLs in different ways for me to realise that.

Here’s my code in the overriden FeatureActivated method to copy a file from the root web to a sub web, when the feature is activated on said sub-web.


public override void FeatureActivated(SPFeatureReceiverProperties properties) {
using (SPWeb web = (SPWeb)properties.Feature.Parent)
{
SPList destination = web.Lists.TryGetList("MyLibrary");
if (destination == null)
{
web.Lists.Add("MyLibrary", "Created by Feature Event Receiver", SPListTemplateType.DocumentLibrary);
}
SPFolder destinationFolder = web.GetFolder(web.Url + "/MyLibrary/MyFolder");
if (!destinationFolder.Exists)
{
destinationFolder = web.Folders.Add(web.Url + "/MyLibrary/MyFolder");
}
SPFile fileToCopy = web.Site.RootWeb.GetFile(web.Site.RootWeb.Url + "/MyLibrary/MyFolder/MyDoc.doc");
if (!fileToCopy.Exists)
{
//Error: source file does not exist
}
else
{
destinationFolder.Files.Add("MyDoc.doc", fileToCopy.OpenBinary());
}
}
}

Just a warning – my sample code doesn’t show my error handling for when the source file does not exist. In this situation, I normally write an error to the event log using the following method:


private void WriteToEventLog(string eventText, EventLogEntryType eventLogEntryType)
{
try
{
const string source = "MySolutionName";
const string log = "Application";
if (!EventLog.SourceExists(source))
EventLog.CreateEventSource(source, log);
EventLog.WriteEntry(source, eventText, eventLogEntryType);
}
catch (Exception ex)
{
//application does not have access to the Event Log
}
}

December 19, 2013

Custom Web Part with SharePoint Attachment Control

I recently had to design a form for a list from scratch. There’s lots of custom functionality, so it’s a web part written in C#.

One thing the form had to be able to do was give the user the ability to upload an attachment to the list item the form was creating, as well as display any existing attachments.

Like most things SharePoint, I figured there would be a C# control I could use whilst creating my web part, and there is.

My entire form is creating using FormField controls. I use the following method to create my FormField and add it to my table:

private FormField getFormFieldControl(string fieldname, Guid listID, int itemID, SPControlMode controlMode)
{
    FormField ff = new FormField();
    ff.ListID = listID;
    ff.FieldName = fieldname;
    ff.ID = string.Format("Field_{0}", fieldname);
    ff.ControlMode = controlMode;
    ff.ItemID = itemID;
    return ff;
}

This has worked perfectly for all the other fields in the list (except perhaps any DateTime columns – see here), and it worked perfectly for the list item attachments too: it displayed my existing attachments as expected. When the ControlMode is set to SPControlMode.Edit, the list of attachments shows the ‘Delete’ link next to them, and when using SPControlMode.Display, the attachments are just listed on their own.

The next step was figuring out how to add a new attachment. It’s not as easy, unfortunately, as setting the control mode to SPControlMode.New – you actually have to add a couple of extra controls to the page.

So, to get it all working, you must add the controls in the correct order. You must also use a little jiggery-pokery to wrap one of the controls in a <span> tag, otherwise you’ll get a little JavaScript alert pop-up with an error message saying: “The form was customized and attachments will not work correctly because the HTML ‘span’ element does not contain an ‘id’ attribute named ‘part1′”.

So, the first thing I added to the page was the FormField control (with FieldName property set to ‘Attachments’. After that, you’ll want to add an AttachmentUpload control (setting values for ListId, ItemId and ControlMode as appropriate), and after that, an AttachmentButton (again, setting ListId, ItemId and ControlMode. However, when adding the AttachmentButton, you must wrap that control in a <span> with id of ‘part1’:

<<span id='part1'></span>

. Because I’m adding all my controls programmatically, I’ve just added these tags as LiteralControls before and after the button.

That should be everything! Now you just need to style to your heart’s content. The default width of the upload control is extraordinarily wide!

Oh, if you’re wondering about how to save the attachment – I’ve actually not done anything special to handle this. The ‘Save’ button on my page is a SharePoint SaveButton control: it should handle all of that for you.

December 18, 2013

How To: Set the value of a Date/Time FormField control

If you’ve ever written a custom form, or used SharePoint FormFields within a custom Web Part, you may have found that setting the value of a Date/Time column is pretty tricky.

My first issue was that despite the column having it’s default value set to [Today], the time was never set correctly. It would round to the nearest hour, instead of the nearest 5-minute interval, which would have been adequate (although still not ideal).

I tried a number of different options. My first was JavaScript. I got the current minute and constructed a new <option> tag and appended it to the minute drop-down. However, this caused the page to error when submitting, I assume because SharePoint recognised that the control had been edited.

I tried simply setting the value of the FormField control. I tried setting myDateTimeFormField.Value, myDateTimeFormField.ItemFieldValue, myDateTimeFormField.ListItemFieldValue… I tried setting it to a straight DateTime object, to an ISO8601 string (using SPUtility.CreateISO8601DateTimeFromSystemDateTime()). I tried reading the value of an existing item and printing it to the screen and then constructing my own string based on the formatting of what was displayed — NOTHING. I tried casting the FormField to a DateTimeControl (myFormField.Controls[0].Controls[0].Controls[1] eventually gives you an object of type DateTimeControl) and setting its SelectedDate value – nada.

In the end, the only successful way I found was:

myDateTimeFormField.ListItemFieldValue = DateTime.Now.ToString(“yyyy-MM-ddTHH:mm:00Z”);

Hope this helps someone. I just wasted 30 minutes of my life trying to get that to work!

December 10, 2013

How To: Create a Timer Job in SP2010

I recently had to import a super-old and on-its-last-legs Access database in to a SharePoint list. This wouldn’t be a problem normally, but the (only) table had 90,000 rows, so I knew it would take a while. After the import, I browsed to the list that had been created and recoiled in horror: none of the fields had been set as the Title column – you know, the type of column that you can click on to view the entire List Item. I don’t know why this happened. As far as I understood it, SharePoint would pick the first field that looked like a ‘Single Line of Text’ column. I had one of those. It was called Description and it was the first column in the table after the ID.

After a bit of Googling, I was none the wiser as to why this had happened, and, as I’d done the import to my Virtual Machine whose drives were rapidly running out of space, I couldn’t do much more testing.

Instead, I followed this really good article to create a Timer Job that took the value of the Description column and inserted it in to the (hidden and empty) Title column of the newly created list. The only other thing I had to take in to account was the fact that I had already added ItemUpdated Event Receivers to the list, so I followed the instructions in this post to ensure my Event Receiver didn’t fire whilst doing the updates.
My final TimerJob class looked like this (I’ve not bothered to include the HandleEventFiring class – it’s exactly the same as in the post I’ve linked to):


using System;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint;

namespace MyProject
{
   public class UpdateTitleFieldTimerJob: SPJobDefinition
   {
      public const string JobName = "UpdateTitleFieldTimerJob";

      public UpdateTitleFieldTimerJob() : base() { }

      public UpdateTitleFieldTimerJob(SPWebApplication webApp) 
        : base(JobName, webApp, null, SPJobLockType.Job)
      {
         Title = "UpdateTitleFieldTimerJob";
      }

      public override void Execute(Guid targetInstanceId)
      {
         HandleEventFiring hef = new HandleEventFiring();
         hef.CustomDisableEventFiring();
         SPWebApplication webApp = this.Parent as SPWebApplication;
         using (SPWeb web = webApp.Sites[0].OpenWeb("MyWeb"))
         {
            SPList myList = web.Lists["MyList"];
            foreach (SPListItem li in myList.GetItems())
            {
               if (li["Description"] != null)
               {
                  li["Title"] = li["Description"];
                  li.Update();
               }
            }
         }
         hef.CustomEnableEventFiring();
      }
   }
}
November 28, 2013

Add an Event Receiver to a specific list instance

When you use Visual Studio 2010 to create an Event Receiver for a list, it prompts you to select the type of Event Receiver you want to create (List Item Events) and which item should be the event source. Selecting any of the options in this second drop-down might make you think twice – “I don’t want this firing for all Custom Lists in my site!”

There are two routes you can take to get your event receiver to only fire for a specific list.

1. Modify the Elements.xml file

This is by far the easiest way to do things. Open up your Event Receiver Elements.xml file and find the Receivers tag. It should read something like <Receivers ListTemplateId="100">, where the ListTemplateId depends on the type of list you selected from the Wizard (100 is the ListTemplateId for Custom Lists).

Delete the ListTemplateId attribute and replace it with a ListUrl attribute: <Receivers ListUrl="Lists/MyListName">

The Feature that contains this Event Receiver must be scoped to a Web level. However, if the list you’re targeting is in a subsite (i.e. not in a top-level site) you might find that using VS2010 to deploy your solution won’t work. This is normally because you have set your Features to activate automatically on deployment, and during deployment you’ll get an error during the activating features step: "Error occurred in deployment step 'Activate Features': <nativehr>0x80070002</nativehr><nativestack></nativestack>"

This is easy to fix – you just need to alter the Site URL for your project. To do this, single-click on your project name in the Solution Explorer (not the main Solution node, but the one under it). In the Properties pane, find the Site URL property, which you’ll see is set to your top-level site URL. Simply add the remainder of the URL to point to your subsite (i.e. original Site URL property was http://sharepoint, change to http://sharepoint/mySubSite).

Once you’ve done this, you should be able to successfully deploy your solution through VS.

To avoid the error altogether, you can set the ‘Activate on Default’ property of the Feature that contains your Event Receiver to False. To do this, expand the Features node in the Solution Explorer pane and find the Feature that contains your Event Receiver. Double-click the .feature file and view the Properties pane. The top option, ‘Activate On Default’, should be changed to ‘False’. You’ll then have to navigate to your sub-site and activate the Feature manually.

2. Modify your Event Receiver code

The alternative, which I don’t like, is to add code to your Event Receiver to check that it is running on the correct list. You’ll need to access the List property and check that it’s the one you want it to run on. Depending on the type of Event you’re listening for, this will be slightly different, but for ItemAdded and ItemUpdated (and other events that fire after the change has been committed), you can do this by accessing the SPItemEventProperties.ListItem.ParentList field. In any of the overridden events, you’ll need to check that properties.ListItem.ParentList.ID (or properties.ListItem.ParentList.Title (although that’s risky – what if you have two separate sub-webs, each with their own List called MyCustomList?) equals the List you’re looking for.

The reason I don’t like this method is that the Event Receiver is attached to all Lists of the type you selected regardless of whether it’s the one you want, it’s just the code doesn’t run when the Events you’ve overridden occur. That can be extremely confusing for anyone looking at Event Receivers attached to lists.

April 24, 2013

SPQuery: Lookups and Joins (and why you might as well just use Linq)

What a nightmare I’ve just had trying to do something I thought would be relatively simple.

I have two lists: ActivityOptions and ActivityChoices. The former has a list of activities in it that pupils can choose from. The choices list stores these choices, by having one column set as a lookup to the ActivityOptions. Each ActivityOption has a teacher assigned to it.

What we wanted to do was allow the teacher that “owns” each activity to review the choices that had been made for their activities. So I went to write a CAML query. And there I discovered CAML query Joins.

I won’t go in to this in too much depth. Suffice to say, the complexities were…. complex. Some Googling later and I read that you can use Linq to retrieve your items without any joins, by using a wonderful Visual Studio plug in called CKS Dev (there are downloads for both Sharepoint Server and Sharepoint Foundation).

What this does is use a nifty tool called SPMetal to generate classes based on your Sharepoint site data. You simply navigate to your site in the Server Explorer section of VS2010, find the site that has the lists you want to manipulate, right-click and choose “Generate entity classes”. You can then use these classes, and their related methods, together with a Linq query to do all sorts of useful things with your lists.

So, how do I get all of the activities chosen by any pupil for a specific teacher? Check out the code below:

using (MySPSiteDataContext dc = new MySPSiteDataContext(http://myserver))
{
   string CurrentUser = SPContext.Current.Web.CurrentUser.LoginName;
   var pupilChoices = from choices in dc.ActivityChoices.ToList()
   where choices.Activity.TeacherName.ToLower() == CurrentUser
   select new PupilChoice
   {
      PupilName = choices.FirstName + " " + choices.Surname,
      ActivityChoice = choices.Activity.Title, //title field of the lookup to ActivityOptions list
      ActivityTeam = choices.Activity.TeamOptions,
      ActivityTeacher = choices.Activity.TeacherName //this is cool - see note below
   };
   foreach (var choice in pupilChoices)
   {
      //Do what you like
   }
}

public class PupilChoice
{
   public string PupilName { get; set; }
   public string ActivityChoice { get; set; }
   public string ActivityTeam { get; set; }
   public string ActivityTeacher { get; set; }
}

So, a few points to note here. This query gives me a nice List of PupilChoice items I can iterate through and process. This is, in some ways, nicer than an SPListItemCollection you would get back from an SPQuery, because the Linq creates all the objects for you.

My favourite bit though, by far, is how Linq and the SPMetal generated classes handle SPUser fields. No messing around here – the classes generated allow me to directly access the log in SPUser.LoginName field by creating a field named by appending ‘Name’ on to the end of your PeoplePicker field. You can also directly access the SPUser.ID field in the same manner.

So, there you go. No joins, no complex CAML – just get your head around the backwards syntax of Linq and you’re all set to query on lists with lookups.

March 18, 2013

How To Hide the Meeting Workspace Checkbox on a New Calendar Entry

Continuing on my school’s Houses theme, I was asked to create a calendar system.

There are loads of things to consider when creating a calendar system in Sharepoint, but I’ll just focus on one annoying section of OOTB Sharepoint Calendars – the Meeting Workspace.

Don’t get me wrong, Meeting Workspaces are great, when used correctly, but for our needs, we wanted to hide this option completely. As the calendar will be added to by staff and pupils alike, I had nightmare visions of Meeting Workspace sites popping up all over the place.

The best way that I’ve found to turn off this feature in a calendar is to create a new Event Content Type that doesn’t include this option. Easy! Then, once you’ve created a new calendar, you can navigate to the list settings, remove the default Event content type and add your custom one.

To create a new Content Type, navigate to the Site Settings page and under the Galleries heading, find the ‘Site content types’ option.

Calendars are basically just lists, so you’ll find the default Calendar ‘Event’ content type under the ‘List Content Types’ heading. But we don’t want to edit the default option, so let’s create a new one. At the top of the Site Content Types page, you’ll see a Create button – use it.

Give your Content Type a practical name. In my case, I chose ‘House Event’. I think it’s important to leave the default Content Type name in your new Content Type’s name, just because you’re not going to work at your business forever, and consistent naming conventions will help out a lot once you’re gone. Give the Content Type a description: ‘A standard Sharepoint calendar Event content type, with no ability to create a Meeting Workspace’.

Now we let Sharepoint do the hard work for us. Choose ‘List Content Types’ as the container to choose the Parent Content Type from, and then choose the Event type in the Parent Content Type drop-down box. To help you find it later, you can either categorise it under one of the existing headings we saw on the list of content types page, or you can create your own category. I’ve filed this one under a new group called ‘House Content Types’ – just because this is the first one I make for our Houses doesn’t mean it will be the last!

When you’re happy, click ‘OK’ and you’ll be presented with a list of columns that are present on this content type. And you’ll see our problem – because we’ve inherited from the ‘Event’ content type, we have all the Event columns, including the one we want to get rid of, and lo and behold, the one we want to get rid of can’t be edited. Thanks, Sharepoint.

Don’t worry though, it’s easy to fix! Open your site in Sharepoint Designer, and use the menu on the left to select ‘Content Types’. You’ll see a view very similar to the Site Settings page that lists all Site Content Types.

Find your new Content Type and click it’s name. You should see a section called ‘Customization’ – click the link to ‘Edit content type columns’. Find the ‘Workspace’ column and click it once to select it. Try double-clicking the row. Nothing. Sharepoint really doesn’t want you to hide this!

Here’s the trick: TRIPLE-CLICK the Workspace row, specifically where it says ‘Optional’. Wait a second, and the ‘Optional’ text should change in to a drop-down box. Change to ‘Hidden’, save, and run away cackling gleefully.

No, no, don’t run away. Go back to your calendar, go to the list settings and assign this as your new default content type for this particular list. Now when users add a new event, no longer will they see the Workspace checkbox.

March 18, 2013

Sharepoint Modal Dialogs – A Quick ‘How To’

One thing to remember when developing for Sharepoint is that you don’t want your development to look like it’s custom. It should blend in with the out-of-the-box Sharepoint well. Using Sharepoint’s Modal Dialogs to add and edit new items is one way of achieving this – there’s no point in reinventing the wheel, after all!

Project

Our school has a quite complex ‘duty’ system. We operate a 6-week Duty Week carousel, meaning that each of our 6 Houses gets a Duty Week every 6 weeks. Most of our Housemasters delegate the responsibility of drawing up the duty rotas to a House Prefect.

At present, we have 6 duty ‘types’ – from monitoring the Tuck Shop to keeping an eye on the Lunch Queue. Each duty type needs between one and three pupils assigned to it each day; that makes a total of around 65 boys getting a duty each week.

They needed a system to take over from their Excel spreadsheets that would allow designated pupils (let’s call them ‘Duty Administrators’) to configure the Duty Weeks and then display on the page of any boy that had been assigned a duty a reminder.

Solution

So, the first step is to set up a couple of custom lists: one to hold the list of possible duties (this is important; just because there are 6 duty types now, doesn’t mean there will be forever! We need this system to be able to grow and adapt as years go on), and one to hold the actual duty assignment.

In our web part that we’re developing, you can then use code to generate a nice table that has the duties in rows, and the days of the week along the top, similar to a calendar.

The question is – what’s the best way to now assign the duties? My initial reaction was the Sharepoint PeoplePicker control. WRONG. Why would you put those controls directly on the page? With 6 duty types, that makes at least 30 PeoplePickers on the page, and then you have to find a way to submit that data to a list on a button click, and… well. Effort.

The answer? The Sharepoint Modal Dialog! This is the small window that pops up and greys out the rest of the screen when you click on a list item, for example, to edit it, or when you click to add a new list item. it focusses the user’s attention on the task at hand, and saves you reinventing the wheel. So instead of having controls embedded in to my table, instead I’m going to get my code to generate a series of ‘Add new item’ links in each cell of the table that will call the NewForm.aspx page (the default Add New Item form that is generated when you create a Sharepoint list).

The Code

So, how do you get a Modal Dialog to appear? Actually, it’s really easy. You’ll need to put some JavaScript in the page you’re working with. I was actually working with an application page, so this was easy – just a couple of script tags in the .aspx file. Alternatively, you can add the script tag programatically using C# (see this MSDN article), or even cheat and add your script to a .txt file, upload the .txt file to a Sharepoint document library, and link to it from a Content Editor part.

The Sharepoint Modal Dialog is opened using the Sharepoint Client Object Model – i.e. the script version of all the code you’ve been writing in Visual Studio. The COM method that you’ll need takes one parameter, an array of options.

Each of my links looks like this:

<a href="javascript:openNewDutyForm();">Add new item</a>

And here’s the openNewDutyForm code:


function openNewDutyForm() {
var options = {
url: "/MyWeb/Lists/MyList/NewForm.aspx",
title: "Add New Duty",
allowMaximize: true,
showClose: true,
width: 625,
height: 525,
dialogReturnValeCallback: ShowStatus
};
SP.UI.ModalDialog.showModalDialog(options);
}

function ShowStatus(dialogResult, returnValue) {
if (dialogResult == SP.UI.DialogResult.OK) {
SP.UI.ModalDialog.RefreshPage(SP.UI.DialogResult.OK);
}
}