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();");