SharePoint Gotcha: Debugging Code Blocks and Event Handlers (or the lack thereof) in Site Pages

Here’s a problem one of the other developers here ran into–if you create your own site pages in SharePoint, you’re bound to see an error like this pop up:

An error occurred during the processing of /mypage.aspx. Code blocks are not allowed in this file.

… wait, what?

Yup, that’s what it says. You can’t use code blocks in ASPX files that are stored in SharePoint. No <% Response.Write("Hello World!") %>. No <%# Eval("Name") %>. As an added bonus, <asp:Button runat="server" ID="MyButton" OnClick="MyButton_Click" /> doesn’t even work because it declares an event handler.

That’s not really the gotcha here. This is well-documented in Inside Microsoft Windows SharePoint Services 3.0: check page 81, if you’re following along in your books at home. SharePoint has a very good reason for this security setting to be there: users can create and modify ASPX files that are stored within the SharePoint database. So, if you could run arbitrary code within an ASPX file, a user with no access to the server could potentially run malicious code.

So where does that code go? Well, it goes in the *.cs or *.vb code-behind file. That means to declare an event handler, you’re going to have to do something like this:

protected override void CreateChildControls()
{
    base.CreateChildControls();
    SaveButton.Click += SaveButton_Click;
    CancelButton.Click += CancelButton_Click;
}

And rather than displaying data in a GridView or Repeater using Eval() or Bind(), you have to give it an OnRowDataBound or OnItemDataBound handler. So that means, rather than doing this:

<asp:Repeater runat="server" ID="MyRepeater">
    <ItemTemplate>
        <asp:Literal runat="server" Text='<%# Eval("Name") %>' />
    </ItemTemplate>
</asp:Repeater>

You do this:

protected void MyRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
    // Make sure we have a data item in this row
    if (e.Item.DataItem == null) { return; }

    // Convert the data item to its original type
    DataRowView dr = (DataRowView)e.Item.DataItem;

    // Find our literal control in the current repeater item
    Literal NameLiteral = e.Item.FindControl("NameLiteral") as Literal;

    // If it was found, set its value
    if (NameLiteral != null) { NameLiteral.Text = dr["Name"].ToString(); }
}

I can imagine you’re griping already. It’s easy to see the security reason for doing this–you don’t want users to be able to run arbitrary code on your server. But it’s so freaking complicated. (Well, it is until you get used to the idea.) But as I said, that’s not the real gotcha here. So what is the point of this post?

The real gotcha here is that this doesn’t seem to be a default security setting, at least in the development environments we’re using. (Andy’s using a VirtualPC image he set up himself; I’m using Microsoft’s WSS3 VirtualPC image.) We only ran into this issue after he tried to install a project on the client’s server.

I don’t think we’ve figured out exactly what turns on this security setting in SharePoint configuration, but I have found out how you can force your development environment to throw these sort of errors. All you have to do is dig into your development site’s web.config file and add the following node:

<configuration>
    <SharePoint>
        <SafeMode>
            <PageParserPaths>
                <PageParserPath VirtualPath="/*" CompilationMode="Always" AllowServerSideScript="false" IncludeSubFolders="true" />
            </PageParserPaths>
        </SafeMode>
    </SharePoint>
</configuration>

Once that’s done, you can now catch yourself using stuff that’s not allowed in ASPX pages before it gets to your client’s server.

  • Dylan Wolf

    For what it’s worth, Web User Controls (.ascx files) don’t seem to have this limitation. They make things much easier.