Tuesday, July 31, 2012

CAML Query on datetime columns

I always forget (and end up looking it up again), so I might as well write a blog post so I can search my own blog instead of the entire internet.

The issue is when you are doing a CAML query to get items from a sharepoint list, and want to specify a filter based on a datetime column in that list - to be greater than the current date for example.

There are two issues when querying datetime fields - the first, is comparing datet time values in the correct format, and the second is relevant if you want the query to include the time, and not just the date.
To handle the first, the query requires you to convert the date value you are comparing to, to a format recognised by sharepoint's caml. To do so, you should use
the SPUtility.CreateISO8601DateTimeFromSystemDateTime function on the date value you want to compare to.
Here is an example of testing that the "Expires" column is greater or equal to the current date:

<geq>
             <fieldref name="Expires">
             <value type="DateTime">" + SPUtility.CreateISO8601DateTimeFromSystemDateTime(DateTime.Now) + @"</value>
          </fieldref></geq>

The second issue is when we want to make the same comparison, but include the time in the query. we have the "Expires" field set to accept both date and a time, the query above will only compare the date - so even items that should have expired an hour ago, would still show - until tomorrow.
To fix that, we need to add IncludeTimeValue='TRUE' to the value node:
<value includetimevalue="TRUE" type="DateTime">" + SPUtility.CreateISO8601DateTimeFromSystemDateTime(DateTime.Now) + @"</value>

Thursday, July 26, 2012

Securing custom application pages

When you create a custom application page in sharepoint (a page deployed to the layouts folder) as part of a visual studio project, often you will want to make sure it is used by the right people. Especially if the actions on the page happen with elevated privilages.

The only security settings you get out of the box is setting rules on when the links to those pages are shown through custom actions. In a custom action you can specify required permissions on the site, and so the link isnt shown to people who are not supposed to use the page.

The problem is - any user can open the page if they just type the URL in the browser. To avoid this, I always add a bit of script that checks if the user is allowed to use the page. Where? In the Page_Load function!

Here is an example of how I secured a page to only show to web administrators, and I also added a check that a required feature has to be activated in the site or the page will not display. For this purpose, I made sure all the controls in the web page are in a panel called pnlControls - so I can easily hide them when the user shouldnt be using the page. I also created a lable called lblErrors outside the panel - so I can so an error to the user if they are not supposed to use the page. An alternative would be to just redirect the user to the site's home page.


Here is the sample:

protected void Page_Load(object sender, EventArgs e)
{
            lblErrors.Visible=false;
            if (!IsFeatureActivated(SPContext.Current.Web, new Guid(Constants.C_SiteActionsFeatureID)))
            {
                lblErrors.Visible=true;
                lblErrors.Text = "The required feature is not activated in this site. This page is not available when the feature is not activated.";
                pnlControls.Visible = false;
                return;
            }
            if (!SPContext.Current.Web.UserIsWebAdmin)
            {
                lblErrors.Visible = true;
                lblErrors.Text = "You don't have permissions to view this page. Only web administrators are allowed to use the functionality on this page.";
                pnlControls.Visible = false;
                return;
            }
}

public static bool IsFeatureActivated(SPWeb web, Guid featureId)
        {
            return web.Features[featureId] != null;
        }

Friday, July 20, 2012

Troubleshooting SharePoint 2010 issues

Friend and colleague in Extelligent Design Jeremy Taylor is doing a webcast on Troubleshooting SharePoint 2010 issues (with some 2013 coverage). Head over to his blog to register!
http://www.jeremytaylor.net/webcast/

Wednesday, July 11, 2012

Reporting code errors when running code in SPLongOperation

As we all know, SPLongOperation is a great device to run a bit of code that takes a long time, while showing the user a turning cogwheel, and then redirecting when the operation is done.
However, when I checked, I saw people are on the search for a way to properly report on errors that happen during the long operation.
Some solutions involve redirecting to a new page, and passing the errors' details in the query string, or in some other way. Another way is to use the "EndScript" method to show a javascript "alert" (a messagebox, with the details.
I'd like to propose a different way. The page that is showing the cogwheel can be modified using the same javascript ("EndScript" method) by injecting html with the error details into the 's4-simple-card-content' div. This is critical if the page that is doing the long operation was opened as a dialog - in which case redirecting can be confusing, and we want to show the error if there was one, or close the dialog if there wasnt.
Example:

SPLongOperation longOp = new SPLongOperation(this.Page);
StringBuilder sbErrors = new StringBuilder();
longOp.Begin();try
{
throw new Exception("Sample");
}
catch(Exception ex)
{
  sbErrors.Append("An error occurred: " + ex.MEssage);
}

if
(sbErrors.Length > 0)
{
longOp.EndScript(
"document.getElementById('s4-simple-card-content').innerHTML = \"Errors have occurred during the submission. Details: " + sbErrors.ToString() + " \";");
}
//close the dialog if there were no errors

longOp.EndScript("window.frameElement.commitPopup();");

Thursday, May 17, 2012

SharePoint Upgrade or Migrate

Hello again everyone.
In March I have published an article in IDM magazine (australia) about upgrading sharepoint and what is the difference between that and migration, and the pros and cons of each one. I even gave tips to successfull upgrade\migration projects. If you want to read it, I have now republished it in my company's site:
http://www.extelligentdesign.com/Articles/UpgradingSharePoint.aspx
Feel free to drop me comments on the article here (and please feel free to tell people about it).

Monday, May 14, 2012

Useful Event handlers for SharePoint

In a few of my recent posts, I wrote about event handlers to set titles and permissions on items automatically as items are created. Since then, I have received a lot of questions from none developers who wanted to use the code samples, but didnt want to compile themselves, and didnt want to go through making the code more generic and robust. So I have done the work for everyone out there who wants to have the code samples as a downloadable product.
The product I am now selling as part of my Company's offerings is called "Extelligent Design's SharePoint List Events Package". The idea is that I will keep adding event handlers to that package, so you pay once, and get a whole lot of functionality. The event handlers are as flexible as possible - allowing you to configure what permissions are assigned and to whome, or what format the title of the list items or documents should be, and even hide the title field from users in the editing forms.
If you purchase, not only do you get the code compiled, you also benefit from 1 year support (patches and added features).
To read more, and to purchase, go to the product's page in our web site.

Monday, February 06, 2012

Enabling Managed Metadata in Office365 (includes download)

If you have office365 or other hosted sharepoint solutions, you may find that the managed metadata column is not available to use - it shows an error that "the required feature is not enabled for this column type".
There are two possible reasons. The first - you don't have a hosted site that supports managed metadata. To check if that is the case, check with your hosting company, or, if you have Office365, you should have a "manage term store" link in your sharepoint online administration centre. If you dont have that link, then you cannot have managed metadata - sorry.
The other possibility is because a feature that is needed on the site is not enabled by default, and because it is a hidden feature you will not be able to activate it yourself.
If you search for this, you will find a lot of articles about how to solve it on a regular sharepoint farm using either command line or powershell. However, what happens if you don't have access to the server, such as in the case of SharePoint online (Office365) or a hosted environment?
In such a case, you need to use a sandboxed solution to enable the feature. To simplify this, I have created a sandbox solution that has a single feature called "Enable Managed Metadata":
This feature simply activates and deactivates the managed metadata feature.
To download this solution, visit my company's web site's code sample library or click this direct download link.
After you have downloaded the solution, you need to upload it to your site. Go to the site's settings page, and click the "Solutions" link to go to the solution gallery. Here, click on "Solutions" in the ribbon and then "upload solution" button. Browse to the file you downloaded, and upload the file. Once uploaded you will have the option to Activate the solution. This should also automatically activate the feature, so from that moment on you can start creating managed metadata columns!

While you'r there, would you mind clicking the "like" button for our web site?

Sunday, January 15, 2012

The importance of synchronous event handlers

In SharePoint, we can create list item event handlers (or receivers). I have written about this in the past, and even posted some code for setting the security automatically on a list item as it is created.
Now, there is one thing I wanted to write about this topic. Event handlers can be defined as either Synchronous or Asynchronous - regardless of the event type. By default, events were always asynchronous if they had the "ed" suffix (ItemAdded, ItemUpdated), and synchronous if they had the "ing" suffix (ItemAdding, ItemUpdating). However, sometimes we want to run our code in an "ed" event, but have it synchronous (notice that the opposite is impossible have the "ing" properties to run asynchronously)- for example, when we want to modify item properties when its added, but before the user has been redirected to a new page.
A classic example is the upload of a document to a document library. The user gets prompted to browse for a file and clicks upload. The next page would be the properties page for the file that was uploaded. If we want to set some of the properties to be pre-filled for the user, we have to do that after the file has been uploaded - but we cannot do that in ItemAdding, as at that point we don't have access to the item - it wasn't created yet. In ItemAdded we can set values to the item - but by default this event is asynchronous - and sharepoint will show the user the properties dialog while the event is still happening in the background. The result is disastrous : the user sees the properties screen, clicks "Save" and gets an error telling him or her that the item has been modified by another process or user.

To resolve this we use the "Synchronization" XML tag in the receiver's registration XML with the value set to "Synchronous" or, if the receiver is registered using server object model code, we set the "Synchronization" property of the receiver class to SPEventReceiverSynchronization.Synchronous. To quote MSDN: "Synchronous processing provides the ability to run the event in the same thread before sending the Web response back to the browser."

As an example, lets create an event receiver that sets the title of a file to the file name, as the file is being uploaded. The code is simple:

public override void ItemAdded(SPItemEventProperties properties)
       {
           base.ItemAdded(properties);
           if (properties.ListItem["Title"] == null)
           {
               int startingPoint = properties.AfterUrl.LastIndexOf("/");
               properties.ListItem["Title"] = properties.AfterUrl.Substring(startingPoint + 1);
               properties.ListItem.Update();
           }
       }

Now, the XML that registers the receiver is :
<Receivers ListTemplateId="101">
      <Receiver>
        <Name>SetTitleFromFileNameItemAdding</Name>
        <Type>ItemAdded</Type>
        <Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
        <Class>DocumentTitleSetter.SetTitleFromFileName.SetTitleFromFileName</Class>
        <SequenceNumber>10000</SequenceNumber>
        <Synchronization>Synchronous</Synchronization>
      </Receiver>

  </Receivers>
Now, when uploading a file, the users are first asked for the file:
And when they upload, they see the dialog allowing them to change the file's name, and by magic the title is already filled for them!

Where can this be useful? I can think of a very real world example: assigning a number to a document as it is being uploaded. The title setting event can be critical in some scenarios where we have lists with lookup columns to a document library - since the lookup column cannot show the file name column we need to show the title, which means we cannot have an empty title (in which scenario you will also want to set the title to unique, and add extra code to make sure the title you are setting is not unique - dont assume that because it is a copy of the file name there wont be duplicates - remember that users may edit both file names and titles!).

Just in case someone finds this useful, I am uploading the code, and the compiled solution package to my company's web site as a free, open source solution for anyone to use on their own risk. I have tested this to work with Office365 (SharePoint online) and as a farm solution - so enjoy!. You can find all my company's free code samples and solutions in our document library on our web site. The specific files are: [Source Code] and [Sandbox Ready Package].

Wednesday, November 16, 2011

Event handler to archive items when deleted

A person just asked this on a mailing list I am on, so I thought I'd share this code. The requirement - copy a list item that is deleted to an archive list. The solution - event receiver that copies the item. The code below does the job, but again is only a sample - you still need to implement error handling, and there are hard coded variables there you will want to change.
Note: this code is specifically for list items, not for documents in document libraries. You can easily change the code to support documents as well (see the CopyAttachments function for an example on how to copy files).

public override void ItemDeleting(SPItemEventProperties properties)
       {
           //note: may require permission elevation.
           //TODO: add error handling

           //get the item being deleted
           SPListItem item = properties.ListItem;
           //get the target list
           SPList targetList = properties.Web.Lists["Announcements Archive"];
           //create the new item
           SPListItem newItem = targetList.Items.Add();
           //copy the list item to the target
           foreach (SPField f in item.Fields)
           {
                if (!f.ReadOnlyField && newItem.Fields.ContainsField(f.InternalName))
                    newItem[newItem.Fields.GetFieldByInternalName(f.InternalName).Id] = item[f.Id];   
           }
           //copy "special" read only fields that can be written to
           newItem["Created By"] = item["Created By"];
           newItem["Modified By"] = item["Modified By"];
           newItem["Modified"] = item["Modified"];
           newItem["Created"] = item["Created"];
           newItem.SystemUpdate(false);
           CopyAttachments(item, newItem);
           base.ItemDeleting(properties);
       }

Note - for the "CopyAttachments" I used code that I published in the past: http://www.sharepoint-tips.com/2008/11/how-to-copy-attachments-from-one-list.html

Tuesday, November 15, 2011

Setting item permissions for new items

A common question that I recently answered in a sharepoint conference is how to make a sharepoint list only show items to the users who wrote the items, and users specified in the item's properties.
A good example of this is a task list where only the original creator of the task and the assignee can see and change the task. This is impossible to do in sharepoint out of the box, and that is where we, as developers, come in.
The way to do it is by developing a simple event handler and attaching it to the list. I prefer to attach to ItemAdded and ItemUpdated - put the exact same code in both events (so that people can reassign the task). The code sample below is a good starting point for you to figure out how to do this. The code sample assumes you know how to use visual studio to create a new event handler, and is meant only as a sample - it doesn't have error handling and it does have some hard coded values for a task list that you may want to change.

public override void ItemAdded(SPItemEventProperties properties)
        {
            //get the list item that was created
            SPListItem item = properties.ListItem;
            //get the author user who created the task
            SPFieldUserValue valAuthor = new SPFieldUserValue(properties.Web, item["Created By"].ToString());
            //get the "assigned to" user. Note - this will throw an error if the task is not assigned to anyone - implement error handling!
            SPFieldUserValue valAssignedTo = new SPFieldUserValue(properties.Web, item["Assigned To"].ToString());
            //disconnect the security from the list, and delete all permissions
            item.BreakRoleInheritance(false);
            //create the object that will hold the roles for the author user
            SPRoleAssignment authorRole = new SPRoleAssignment(valAuthor.User);
            //create the object that will hold the roles for the assigned to user
            SPRoleAssignment assignedToRoles = new SPRoleAssignment(valAssignedTo.User);

            //get the contribute role from the web. Alternatively use code to create a new role definition with custom permissions.
            SPWeb oWebsite = properties.Web;
            SPRoleDefinitionCollection collRoles = oWebsite.RoleDefinitions;
            SPRoleDefinition oRoleDefinition = collRoles["Contribute"];

            //assign permissions to task author
            authorRole.RoleDefinitionBindings.Add(oRoleDefinition);
            item.RoleAssignments.Add(authorRole);
            //assign permissions to task assignee
            assignedToRoles.RoleDefinitionBindings.Add(oRoleDefinition);
            item.RoleAssignments.Add(assignedToRoles);
            //update the item
            item.Update();
            base.ItemAdded(properties);
        }

You can download the entire code sample as a visual studio project from my company's site's code samples document library at http://www.extelligentdesign.com/Code%20Sample%20Downloads/Forms/AllItems.aspx. You will want to change the project's properties to point to your local development site before debugging.