Tag Archive: Object Model


If you’re like me and you do a fair bit of object model programming, you’ve likely seen this error before. More specifically, the error is as follows:

Unhandled Exception: Microsoft.SharePoint.SPException: Invalid data has been used to update the list item. The field you are trying to update may be read only.

There are probably more than one cause for this particular issue, but in my case, it always comes down to an error in the way I’ve assigned a field of type Lookup.

So to set up our scenario, let’s say we have a custom list called Types, and it contains 3 items:

  1. Employee
  2. Contractor
  3. Manager

Now let’s assume we’ve created a custom list called Users and it contains a column called User Type of type Lookup that points to the Types list using the Title column.

If we’re doing some updates to our list programmatically, we’ll need to be able to set the Type column. Your typical scenario is that you’re reading this from a form control and populating this field. In my case, I knew specifically what value I wanted to set. So let’s start with something like this:

1
2
3
SPListItem item = list.Items.Add();
item["Type"] = "Employee";
item.Update();

Unfortunately, the above bit of code will fail with the error outlined at the beginning of this post. The data we’re attempting to assign is not valid. What we need to do is create another object, an SPFieldLookupValue object to be more specific, and use one of the constructors to build the lookup that we’ll then assign to our field.

1
2
3
4
SPListItem item = list.Items.Add();
SPFieldLookupValue lookup = new SPFieldLookupValue(1, "Employee");
item["Type"] = lookup;
item.Update();

I’m not satisfied with the above, because it’s forcing me to hard code the id and the value. I am interested in some feedback from my readers — does anyone have a better approach to this problem? I’ll be awarding a free Black Ninja TSHIRT to the reader who can provide me the most elegant workaround.

Problem

You’ve developed some custom new, edit and display application pages that are stored in the _layouts directory. Let’s assume the filenames are newform.aspx, dispform.aspx and editform.aspx. You now want to change the properties of your custom list so that any new, edit or display requests point to your custom pages.

If you open up your site within SharePoint Designer and expand the Lists folder, from there you can access the properties of that custom list by right clicking on it and selecting Properties. Once the List Properties pane is open, click on the Supporting Files tab.

You’ll see there that you can actually choose what display, edit and new forms you want your list to be using. So if you’ve developed something custom, you would click Browse…, point to your new location and select the file. Here is where it starts to fail, I am only able to browse within the site itself and cannot navigate to my _layouts directory to select the files I mentioned above.

Solution

I suspected this was a limitation of the SharePoint Designer UI and not actually a limitation of the Object Model. I did a bit of fiddling with my SPList object and was not able to find anything that let me change those properties. If you look at the screenshot above, there is a key piece of information that’ll make the light turn on (at least it did for me). The Supporting Files tab has a drop down selector for the Content type specific forms. If you think about that a moment, you’ll remember that ALL lists within SharePoint inherit from a default content type.

So armed with that knowledge, I created an SPContentType object and took a look at it’s properties and methods. Sure enough, there are 3 properties I can set to change these forms: EditFormUrl, NewFormUrl and DisplayFormUrl. Here is some sample code I used to change the forms for my custom list called ‘My List’ that inherits from the Item content type:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SPWeb web = SPContext.Current.Web;
 
web.AllowUnsafeUpdates = true;
 
SPList list = web.Lists["My List"];
 
SPContentType ct = list.ContentTypes["Item"];
 
ct.EditFormUrl = "_layouts/editform.aspx";
ct.NewFormUrl = "_layouts/newform.aspx";
ct.DisplayFormUrl = "_layouts/dispform.aspx";
 
ct.Update();
list.Update();

So in the example code above, I had to determine what default content type my list inherited from. In this case it was the Item content type.

NOTE: If you need to change the content type directly, you can definitely do that, however, I did a bit of testing and found that any lists ALREADY inheriting from that content type did not pickup my changes to the form locations. Any NEW list that I created that was inheriting from that content type did pickup the changes. In order for me to change the form locations for the existing list I had to use the code above. For reference, here is how you would change the content type directly. The code has only a subtle difference.

1
2
3
4
5
6
7
8
9
10
11
SPWeb web = SPContext.Current.Web;
 
web.AllowUnsafeUpdates = true;
 
SPContentType ct = web.ContentTypes["Name Of Your Content Type"];
 
ct.EditFormUrl = "_layouts/editform.aspx";
ct.NewFormUrl = "_layouts/newform.aspx";
ct.DisplayFormUrl = "_layouts/dispform.aspx";
 
ct.Update();

As always, any questions, let me know!

I was recently working on a Black Ninja Software project for a client of ours where performance became an issues for one of the custom application pages we had developed. I plan to write up a more detailed report on how I managed to cut the load times of this particular page in half using the ANTS Profiler, but for now I wanted to highlight a piece of the puzzle that I discovered today.

I won’t cover how to load values into a PeopleEditor control from within a SharePoint list, you can view that in more detail here.

Take a look at the example below:

1
2
3
SPFieldUserValue user = new SPFieldUserValue(web, Convert.ToString(listItem["Employee]));
 
peEmployee.CommaSeparatedAccounts = user.LookupValue;

If we have an SPFieldUserValue object, calling upon the LookupValue property will return the Name of that user. This is not to be confused with the LoginName property. Assigning that to the CommaSeparatedAccounts property may do the trick and will load that user account into the control but not without a performance hit.

A better approach would be:

1
2
3
SPFieldUserValue user = new SPFieldUserValue(web, Convert.ToString(listItem["Employee"]));
 
peEmployee.CommaSeparatedAccounts = user.User.LoginName;

The difference is minor. Instead of using the LookupValue property, we leverage the SPUser object and call upon the LoginName property. In all of my testing, I noticed an improvement in speed when using the LoginName property.

Depending on your environment, the output from either of those properties will differ and that’s the heart of the performance issues. In my environment, LoginName and Name outputted the following:

Name – “Joe User”
LoginName – “domain\juser”

Having the domain specified seems to speed this whole process up. I would be interested to hear from anyone else who’s encountered anything similar.

Validating a PeopleEditor Control on PostBack

Let’s talk about validation and the PeopleEditor control. There doesn’t seem to be a consensus on how this is supposed to be done so I’ll outline my findings and what eventually worked for me.

I’ll start by explaining what I was attempting to do. I have a PeopleEditor control on a custom application page. My custom application page is located in the _layouts directory. On this page, I also have a submit button that saves my data to a SharePoint list upon submit.

Now, I need to ensure before submit, that the PeopleEditor control contains a valid entry. Blank and invalid values are not permitted.

I first attempted to set the AlllowEmpty property to false. However, that didn’t seem to help. If I clicked submit, the page would post back and then the control would display an error once the page reloaded. At this point, it was too late to be informing the user that there was a problem with their entry, the page had already posted back, so this was not useful.

I also tried setting the ValidationEnabled property to true. That didn’t seem to make any difference either. I am confused as to how these two properties are supposed to work.

My next attempt was to add an ASP.NET required field validator to the page as follows:

1
2
3
<wssawc:PeopleEditor AllowEmpty="false" ValidatorEnabled="true" Width="250px" ID="pePR" runat="server" SelectionSet="User" MultiSelect="false" />
 
<asp:RequiredFieldValidator ID="rfvPR" ControlToValidate="pePR" runat="server" ErrorMessage="Project Requestor (Cannot be blank)" Text="Cannot be blank." ValidationGroup="SubmitForm" Display="Dynamic"></asp:RequiredFieldValidator>

This took care of the client side validation that prevented the user from clicking submit without first entering a value into the control.

However, this did not handle the scenario when a user entered an invalid value in the control and hit submit. In this case, the form will attempt to submit and create the item, but will fail to save the value to the list item field because the value was invalid. An example of an invalid value would be bad data in that field.

To fix this, in my submit method, I added an IF statement to check how many resolved entities there were.

1
2
3
4
5
6
if (pePR.ResolvedEntities.Count > 0) {
    // continue with the submit
}
else {
    // output an error that the user did not enter a valid user
}

If anyone has any feedback, or has had success using the AllowEmpty property, please comment on this post or drop me a note. I’m interested in hearing how others were able to get around this issue.

I ran into a problem recently where I was attempting to update the permissions on an SPListItem within code but ran into the following error:

Updates are currently disallowed on GET requests. To allow updates on a GET, set the ‘AllowUnsafeUpdates’ property on SPWeb.

Now I’m quite familiar with this error, and it’s one of the few errors in SharePoint that gives you a hint in terms of what you need to do: set the AllowUnsafeUpdates property to true on your SPWeb object.

Well, I did that, but without success. I was still receiving this error. I did a bit of digging and came across some useful information on Hristo Pavlov’s blog. His article is titled What You Need To Know About AllowUnsafeUpdates (Part 1) and is a must read for anyone working with these objects.

The root of my issue turned out to be that my AllowUnsafeUpdates property was being reset to false, even after I had already set it to true:

When any object that implements ISecurable (those are SPWeb, SPList and SPListItem) breaks or reverts their role definition inheritance. This means every time you call SPRoleDefinitionCollection.BreakInheritance(), BreakRoleInheritance(), ResetRoleInheritance() or set the value of HasUniquePerm the AllowUnsafeUpdates property of the parent web will reset to its default value and you may need to set it back to true in order to do further updates to the same objects.

To resolve my issue, I had to reset the AllowUnsafeUpdates property to true after executing BreakRoleInheritance() in my code. I encourge you to read the rest of Hristo’s blog as it really helped me understand this property a little better.

This article will talk specifically about how to add a Rich Text Editor to your custom application pages. When initially doing some research on this topic, I found a lot of information, but not anything useful that I could actually implement. Here are my steps for adding a control of this type to your pages:

  1. If you would like to leverage the default sharepoint rich text editor control within your own custom pages, there’s one class you need to get familiar with: InputFormTextBox
  2. The actual MSDN reference is not very helpful so to get started with a control of this type, you’ll need to know a couple things. Let’s begin by adding the control to our page as follows: 
    1
    2
    
    <wssawc:InputFormTextBox ID="iftxtDescription" runat="server" TextMode="MultiLine" Rows="20"
    RichTextMode="FullHtml" RichText="true"></wssawc:InputFormTextBox>
  3. The reason I know it’s wssawc is because at the top of the custom application page, Microsoft.SharePoint.WebControls namespace should be registered with a tagprefix of wssawc
    1
    
    <%@ Register TagPrefix="wssawc" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
  4. Some properties to be aware of:
    • ID – all controls need this. You’ll use the ID to work with the control in the code behind or the inline code
    • TextMode – this specifies whether the control is a Password control, a MultiLine control or SingleLine control. If you’re attempting to create a rich text editor, you’ll want to set this to MultiLine.
    • Rows – specifies the rows/height of this control
    • RichTextMode – specifies whether the mode for the rich text. Options are: Compatible, FullHtml, HtmlAsXml.
    • RichText – true or false. Don’t forget to set this to true!
  5. To save data from this type of control:
    calendarListItem["Job Description"] = iftxtDescription.Text;
  6. To read data from a rich text column into this control:
    1
    2
    
    SPFieldMultiLineText multiDescription = (SPFieldMultiLineText)listItem.Fields["Job Description"];
    iftxtDescription.Text = multiDescription.GetFieldValueForEdit(listItem["Job Description"]);

That should do it! I should point out that the multiDescription object that we created above actually has a couple useful methods beyond the one that I used. GetFieldValueForEdit() will grab the contents of the field in rich text and display it in my control quite nicely. I could also have used GetFieldValueAsHTML() for html or GetFieldValueAsText() for plain text.

Note: If you attempt to set the Enabled or ReadOnly properties of this control, they won’t work. I ran into this issue myself and confirmed it with several other users having the same problem. Microsoft has acknowledged this as a bug (37846) but I have yet to find any documentation on this bug. If you do find anything, please let me know!

How to Write an SPQuery to Sort Your List

If you’re working with an SPListItemCollection, you might have the need to sort the data that stored in the collection. The best way I’ve found to do this is to build an SPQuery object and use that to actually query for the information. Using an object of this type makes it possible to send in whatever sort and/or orderby clause we’d like to use.

For example:

1
<OrderBy><FieldRef Name='EventDate' Ascending='FALSE'></FieldRef></OrderBy>

The full query would look something like this:

1
2
3
4
5
SPQuery oQuery = new SPQuery();
 
oQuery.Query = "<Where><Eq><FieldRef Name='AP_x0020__x002f__x0020_O'/>" +
"<Value Type='Text'>" + fruitName + "</Value></Eq></Where>" +
"<OrderBy><FieldRef Name='EventDate' Ascending='FALSE'></FieldRef></OrderBy>";

If you receive an error similar to:

One or more field types are not installed properly. Go to the list settings page to delete these fields.

Then you’ve set the FieldRef Name incorrectly. The trick to resolving this is:

  1. Navigate to your list that the column/field is contained within
  2. Click the New button as you normally would to create a new item in this list
  3. Click on View, Source from the toolbar in your browser window.
  4. Finally, do a find on the phrase fieldinternalname and locate the field you’re trying to query on
  5. Whatever value is stored in fieldinternalname is what you’ll want to use in your query

Any questions, let me know.

UPDATE: I recently discovered another trick to this. If you want to avoid having to seek out what the internal name of a particular field is, when you first name your column, do not include any spaces or special characters. Once the field (column) has been created, go back and rename the field to include the spaces or special characters as desired. SharePoint will still retain the original field name without spaces and you can use that directly in your query without issue.

The PeopleEditor control is a common control that you’ll find implemented throughout SharePoint. If you’re building any custom web parts or application pages, you may want users to enter people specific information. The PeopleEditor is a good choice, however, there isn’t much out there in terms of documenting it’s use. Hopefully the information below will help get you started. If you are having issues, or if there is something I’ve missed in my post, please feel free to leave me a comment.

NOTE: One thing I found was that in order for this control to successfully save a user, that user has to exist within the site collection. If they don’t exist, the web.SiteUsers call will fail with an exception ‘user cannot be found’.

UPDATE: You can use the EnsureUser method to verify that the specified user exists and is a valid user of the web site. The neat thing about this method is that it will add the user to the site if they do not already exist, thus making the SiteUsers call unnecessary. Keep in mind that if you do use this method, you may need to elevate permissions to make that call, as not all users have access to add other users to your site collection.

To get started, create a new Custom List named Demo. Within List Settings, create a column of type Person or Group named Managers.

In this particular example, I’ll be building a custom application page with a single PeopleEditor control and a submit button. Create a new page (or download the files I’ve prepared for this article here), called Demo.aspx and store it in the _layouts directory.

Since the people editor control is located within the Microsoft.SharePoint.WebControls namespace, you’ll have to register the tag prefix at the top of your page:

1
<%@ Register TagPrefix="wssawc" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

Insert the control on the page. If you look at my Demo.aspx page, I’ve put my control within a table tag, but you can format this however you’d like:

1
<wssawc:PeopleEditor AllowEmpty="false" Width="300px" id="peManagers" runat="server" SelectionSet="User" />

AllowEmtpy – let’s you specify if blank values are permitted for this control
SelectionSet – can be User or SecGroup or SPGroup or all three: User, SecGroup, SPGroup
MultiSelect – set to true or false, false if you don’t want the user to be able to select more than one

Insert an asp:Button control onto the page; we’ll use this to submit the people data to the list.

1
<asp:Button ID="btnSubmit" runat="server" OnClick="btnSubmit_Click" ValidationGroup="Main" Text="OK" CssClass="ms-ButtonHeightWidth" />

In order to save values from the control to the column we created, you’ll need the following code in the button submit event:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
protected void btnSubmit_Click(object sender, EventArgs e)
{
    // create a web object with context for the current site
 
    SPWeb web = SPContext.Current.Web;
 
    // get the entries that were entered into the people editor and store them in a string
 
    string managers = peManagers.CommaSeparatedAccounts;
 
    // commaseparatedaccounts returns entries that are comma separated. we want to split those up
 
    char[] splitter = { ',' };
 
    string[] splitPPData = managers.Split(splitter);
 
    // this collection will store the user values from the people editor which we'll eventually use
    // to populate the field in the list
 
    SPFieldUserValueCollection values = new SPFieldUserValueCollection();
 
    // for each item in our array, create a new sp user object given the loginname and add to our collection
 
    for (int i = 0; i < splitPPData.Length; i++)
    {
        string loginName = splitPPData[i];
 
        if (!string.IsNullOrEmpty(loginName))
        {
            SPUser user = web.SiteUsers[loginName];
 
            // you could also use SPUser user = web.EnsureUser(loginName);
 
            SPFieldUserValue fuv = new SPFieldUserValue(web, user.ID, user.LoginName);
 
            values.Add(fuv);
        }
 
    }
 
    // set the Person or Group column
 
    SPListItemCollection listItems = web.Lists["Demo"].Items;
 
    SPListItem manager = listItems.Add();
 
    manager["Managers"] = values;
 
    manager.Update();
}

That should do it. You should be able to save the entries to the list. If you’d like to learn how to read values from a list column into a PeopleEditor control, you can read that post here. Any feedback?

This post was really planned as a Part 2 to the first post I made about saving data from the PeopleEditor control. My aim for this entry is to tackle the reverse scenario: reading data from a field of type Person or Group and loading it into a PeopleEditor control. If you have any troubles with Part 1 or Part 2 of this series or if there’s something I’ve missed, I would love to hear about it. Leave a comment or drop me an email. Thanks!

If you’ve followed Part 1 of this series, you should already have a list named Demo created with a column of type Person or Group named Managers. If not, begin by creating a new Custom List named Demo. Within List Settings, create a column of type Person or Group and name it Managers. Set the Allow multiple selections checkbox to Yes.

Once you’ve created the list and the column, add a few new entries so we have some data to load.

Now let’s create a custom application page with a single PeopleEditor control on it. Create a new page (or download it here) called Load.aspx and store it in the layouts directory.

Since the people editor control is located within the Microsoft.SharePoint.WebControls namespace, you’ll have to register the tag prefix at the top of your page:

1
<%@ Register TagPrefix="wssawc" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

Insert the control on the page. This is the control we’ll be loading the data into:

1
<wssawc:PeopleEditor AllowEmpty="false" Width="300px" id="Managers" runat="server" SelectionSet="User" />

The next step will require that we wire up our Page_Load event to load the necessary data from the list to the control.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
protected void Page_Load(object sender, EventArgs e)
{
 
    try
    {
 
        SPWeb web = SPContext.Current.Web;
 
        // get a handle to the list we’ll be pulling values from
        SPList list = web.Lists["Demo"];
 
        // using the querystring parameter containing the id, get the list item we’ll be dealing with
        SPListItem listItem = list.GetItemById(Convert.ToInt32(Request["id"]));
 
        // the managers column is of type Person or Group, so we can use a spfielduservaluecollection to     store the values from it
        SPFieldUserValueCollection users = (SPFieldUserValueCollection)listItem["Managers"];
 
        // our array will hold the entities we’ll use to eventually assign to the people editor control
        ArrayList entityArrayList = new ArrayList();
 
        // loop through each use in the collection, set the key, add to the array
        for (int i = 0; i < users.Count; i++)
        {
 
            PickerEntity entity = new PickerEntity();
            entity.Key = users[i].User.LoginName;
            entityArrayList.Add(entity);
 
        }
 
        Managers.UpdateEntities(entityArrayList);
 
    }
 
    catch (Exception ex)
    {
 
        Response.Write(ex.ToString());
 
    }
 
}

Now when you navigate to your page, pass the id of a valid item in the list, and the entries in the manager column should load: http://servername/_layouts/Load.aspx?id=1 (replace servername with the name of your MOSS install)

SPWeb.SiteUsers vs SPWeb.Users

Update: I recently ran into this issue again and discovered that there is in fact a way to ensure that the user exists before creating the user object — the EnsureUser method of the SPWeb class. According to MSDN this method “Checks whether the specified login name belongs to a valid user of the Web site, and if the login name does not already exist, adds it to the Web site.” Check it out here. I tried this and it seems to resolve my issue. If the user is not a member of the web site, this method will add it which is effectively what I needed it to do.

Please note that you’ll need to set the AllowUnsafeUpdates property to true in order for the EnsureUser method to do it’s thing.

When trying to use SPWeb.Users to find a particular user in a site, I was receiving a “User cannot be found” error within the site that my method was being called from. I took a look at the site permissions and discovered that the user account I was attempting to add did not actually exist as a member of that site. At first glance, I didn’t actually think that this would be a problem, but apparently there is a key disctinction between the various SPWeb methods.

If I added the user to the site manually, the SPWeb.Users call worked. Any users automatically added to the site upon creation, will not be found with a SPWeb.Users call. This is an important disctinction.

My next attempt was to use SPWeb.SiteUsers which was only successful if the user existed at the site collection level. However, if the user was not a member of any group at the site collection level, my method would still fail with a “User cannot be found” error.

Alternatively, I might be able to leverage the user profile database to get a list of all possible users to choose from but I haven’t done any experimenting with this yet. If anyone has any additional information or feedback on this, please drop me a note.

Windows SharePoint Services 3.0 SDK

SPWeb.AllUsers – Gets the collection of user objects that represents all users who are either members of the site or who have browsed to the site as authenticated members of a domain group in the site.

SPWeb.SiteUsers – Gets the collection of all users that belong to the site collection.

SPWeb.Users – Gets the collection of user objects that are explicitly assigned permissions on the Web site.

Powered by WordPress | Theme: Motion by 85ideas.