Archive

Archive for the ‘Share Point’ Category

SharePoint: Updates are currently disallowed on GET requests

March 4th, 2011 No comments

     When I need to set unique permissions to a SPListItem I usually use the code like the following:

using (SPSite spSite = new SPSite("some url"))
{
    using (SPWeb spWeb = spSite.OpenWeb())
    {
        bool oldAllowUnsafeUpdates = spWeb.AllowUnsafeUpdates;
        spWeb.AllowUnsafeUpdates = true;
        spWeb.Update();

        try
        {
            SPList spList = spWeb.Lists["some list"];

            SPListItem spLisItem = spList.GetItemById(someId);
            spLisItem.BreakRoleInheritance(false);

            SPRoleDefinition reader = spWeb.RoleDefinitions.GetByType(SPRoleType.Reader);
            SPGroup someGrp = spWeb.Groups["some group"];

            SPRoleAssignment roleAssignment = new SPRoleAssignment(someGrp);
            roleAssignment.RoleDefinitionBindings.Add(reader);
            spListItem.RoleAssignments.Add(roleAssignment); // (***) exception

        }
        catch (Exception ex)
        {
            // logging
        }

        spWeb.AllowUnsafeUpdates = oldAllowUnsafeUpdates;
    }
}

     It works fine almost everywhere: in feature receivers, in jobs, in console applications and so on. But today I’ve found out that it doesn’t work correctly if it runs from aspx-page’s code-behind when we have GET request (Page.IsPostBack = false). In the line marked (***) I receives a traditional exception – “Updates are currently disallowed on GET requests. To allow updates on a GET, set the ‘AllowUnsafeUpdates’ property on SPWeb”. As you can see I set spWeb.AllowUnsafeUpdates to true and even do spWeb.Update() (though it’s unnecessary in most cases), but nothing helps. Wrapping this code in SPSecurity.RunWithElevatedPrivileges doesn’t help either.

     After debugging for a while I’ve noticed that spWeb.AllowUnsafeUpdates gets false after spLisItem.BreakRoleInheritance:

// here spWeb.AllowUnsafeUpdates = true
spLisItem.BreakRoleInheritance(false);
// here spWeb.AllowUnsafeUpdates = false

     It should be noted that the same happens when we have a POST request (Page.IsPostback = true), but in this case it doesn’t cause exception. Interestingly that we have some kind of special treatment for GET request here šŸ™‚

     The reason of such behavior has been found, as usual, by means of .NET Reflector. Not going into details I say that the calling of BreakRoleInheritance leads to the calling of Invalidate() method of SPWeb. Let’s take a look at this method:

internal void Invalidate()
{
    this.ReleasePinnedResource();
    if (this.m_Request != null)
    {
        if (this.m_RequestOwnedByThisWeb)
        {
            SPRequestManager.Release(this.m_Request);
        }
        this.m_Request = null;
    }
    this.m_bInited = false;
    this.m_bPublicPropertiesInited = false;
    this.m_Url = null;
}

     It looks like Invalidate releases some resources and cleans itself (m_bInited = false), in the same time, doesn’t dispose itself, but just provokes reinitializing during the next address to it. After reinitialization spWeb.AllowUnsafeUpdates turns into false. An evident workaround is to set true to spWeb.AllowUnsafeUpdates again after BreakRoleInheritance.

// some code is skipped
spLisItem.BreakRoleInheritance(false);
spWeb.AllowUnsafeUpdates = true;
spWeb.Update();
// some code is skipped

     I think there are many methods, which can cause SPWeb.Invalidate, for example, ResetRoleInheritance does the same, and, in a few special cases, SPWeb.Update either. That is why be ready to add restoring of AllowUnsafeUpdates to true. I hope this post will save time for somebody.

Related posts:

SharePoint: LookupField bug in SharePoint 2010

February 28th, 2011 No comments

    This post is about the same LookupField bug I faced in SharePoint 2007 and described here
http://dotnetfollower.com/wordpress/2011/02/sharepoint-lookupfield-bug/. Briefly, LookupField doesnā€™t save selected value after an ā€œidle postbackā€. I’ve analyzed the code of LookupField control from SharePoint 2010, the bug still remains. The steps to reproduce are absolutely identical. They have changed a little bit method SetFieldControlValue (method, where the problem was), now it looks like the following:

private void SetFieldControlValue(object value)
{
    if ((this.m_value != value) || !this.m_hasValueSet)
    {
        this.Clear();
        this.m_value = value;
        this.m_hasValueSet = true;
        if (this.DataSource != null)
        {
            // some code is skipped
            if (this.m_tbx != null)
            {
                DataRowView view = null;
                if (this.m_selectedValueIndex >= 0)
                {
                    view = this.m_dataSource[this.m_selectedValueIndex];
                    this.m_tbx.Text = view["TextField"] as string;
                }
                if (this.Page != null)
                {
                    string str = "0";
                    if (this.m_selectedValueIndex >= 0) // (***) here is the problem
                    {
                        view = this.m_dataSource[this.m_selectedValueIndex];
                        str = ((int)view["ValueField"]).ToString(CultureInfo.InvariantCulture);
                    }
                    else if (this.Page.IsPostBack) // get picked value only if option stored in SPListItem is invalid (m_selectedValueIndex < 0)
                    {
                        str = this.Context.Request.Form[this.HiddenFieldName];
                        if (string.IsNullOrEmpty(str))
                        {
                            str = "0";
                        }
                    }
                    this.Page.ClientScript.RegisterHiddenField(this.HiddenFieldName, str);
                }
            }
        }
    }
}

     Despite changes, they still use m_selectedValueIndex to detect whether they should get value from the hidden html-feild or not. But I’m repeating myself, m_selectedValueIndex reflects the option stored in SPListItem, we shouldn’t take m_selectedValueIndex into account here. When SPListItem is being saving, SharePoint uses the Value property to get the option picked by user on the page. It’s more interesting that inside Value property they rightly get the value from hidden html-field and don’t analyze m_selectedValueIndex.

public override object Value
{
    get
    {
        this.EnsureChildControls();
        if (this.m_tbx != null)
        {
            if (this.Page.IsPostBack) // if it's postback, always get the picked value
            {
                string str = this.Context.Request.Form[this.HiddenFieldName];
                return (string.IsNullOrEmpty(str) ? 0 : int.Parse(str, CultureInfo.InstalledUICulture));
            }
            return  ((this.m_selectedValueIndex >= 0) ? this.m_selectedValueIndex : 0);
        }
        // some code is skipped
    }
}

     To eliminate the bug you still can use FixedLookupField from the previous post http://dotnetfollower.com/wordpress/2011/02/sharepoint-lookupfield-bug/

Related posts:

SharePoint: LookupField bug

February 26th, 2011 No comments

    Recently I’ve found an interesting bug in the LookupField (Microsoft.SharePoint.WebControls.LookupField) from SharePoint 2007. LookupField doesn’t save selected value after an “idle postback”. By “idle postback” I mean any postback, which doesn’t lead to an item saving. For example, you have changed a list item and click on Save-button, but some validation fails and the page is just reloaded with an appropriate error message.

Below are depicted some steps to reproduce the bug.

Steps to reproduce LookupField bug

     This bug reveals itself only when the amount of items in the lookup-list is more than 20. It’s related with the fact, that LookupField renders itself as usual DropDownList if items amount <= 20, and as TextBox with the dynamically appeared Html-select when the items amount > 20. We can see this difference in the following piece of CreateChildControls() from Reflector:

protected override void CreateChildControls()
{
    // some code skipped	    
    this.Controls.Clear();
    if (((this.DataSource != null) && 
        (((this.DataSource.Count > 20) && !base.InDesign) && SPUtility.IsIE55Up(this.Page.Request))) && 
        !SPUtility.IsAccessibilityMode(this.Page.Request))
    {
        // rendering as TextBox
        this.m_tbx = new TextBox();
        this.m_tbx.Attributes.Add("choices", this.Choices);
        this.m_tbx.Attributes.Add("match", "");
        this.m_tbx.Attributes.Add("onkeydown", "HandleKey()");
        this.m_tbx.Attributes.Add("onkeypress", "HandleChar()");
        this.m_tbx.Attributes.Add("onfocusout", "HandleLoseFocus()");
        this.m_tbx.Attributes.Add("onchange", "HandleChange()");
        this.m_tbx.Attributes.Add("class", "ms-lookuptypeintextbox");
        this.m_tbx.Attributes.Add("title", field.Title);
        this.m_tbx.TabIndex = this.TabIndex;
        this.m_tbx.Attributes["optHid"] = this.HiddenFieldName;
        Literal child = new Literal();
        child.Text = "<span style=\"vertical-align:middle\">";
        Literal literal2 = new Literal();
        literal2.Text = "</span>";
        this.Controls.Add(child);
        this.Controls.Add(this.m_tbx);
        this.m_tbx.Attributes.Add("opt", "_Select");
        this.m_dropImage = new Image();
        this.m_dropImage.ImageUrl = "/_layouts/images/dropdown.gif";
        this.m_dropImage.Attributes.Add("alt", SPResource.GetString("LookupWordWheelDropdownAlt", new object[0]));
        this.m_dropImage.Attributes.Add("style", "vertical-align:middle;");
        this.Controls.Add(this.m_dropImage);
        this.Controls.Add(literal2);
    }
    else
    {
        // rendering as DropDownList
        this.m_dropList = new DropDownList();
        this.m_dropList.ID = "Lookup";
        this.m_dropList.TabIndex = this.TabIndex;
        this.m_dropList.DataSource = this.DataSource;
        this.m_dropList.DataValueField = "ValueField";
        this.m_dropList.DataTextField = "TextField";
        this.m_dropList.ToolTip = SPHttpUtility.NoEncode(field.Title);
        this.m_dropList.DataBind();
        this.Controls.Add(this.m_dropList);
    }
    // some code skipped
    this.SetFieldControlValue(this.ItemFieldValue);
}

     Another interesting point is a method SetFieldControlValue (in above shown code it’s invoked at the end of CreateChildControls()). SetFieldControlValue registers a hidden html-field that contains the identifier of the option selected by user. When Sharepoint save all changes to SPListItem, it uses the identifier from this hidden field (to be more exact, SharePoint deals with property Value, which, in turn, gets value from the hidden field). That is why it’s very important to have the right value in the hidden field. Let’s take a look at SetFieldControlValue:

private void SetFieldControlValue(object value)
{
    if ((this.m_value != value) || !this.m_hasValueSet)
    {
        this.Clear();
        this.m_value = value;
        this.m_hasValueSet = true;
        if (this.DataSource != null) // here m_selectedValueIndex will be initialized with the index of the option currently stored in SPListItem
        {
            // some code skipped
            if (this.m_tbx != null)
            {
                DataRowView view = null;
                if (this.m_selectedValueIndex >= 0)
                {
                    view = this.m_dataSource[this.m_selectedValueIndex];
                    this.m_tbx.Text = view["TextField"] as string;
                }
                if (this.Page != null)
                {
                    string str = "0";
                    if (this.m_selectedValueIndex < 0)  // (***) here is the problem
                    {
                        if (this.Page.IsPostBack)
                        {
                            // extract the option picked by user
                            str = this.Context.Request.Form[this.HiddenFieldName];
                            if (string.IsNullOrEmpty(str))
                            {
                                str = "0";
                            }
                        }
                    }
                    else
                    {
                        // extract the option stored in SPListItem, because m_selectedValueIndex still contains the old value
                        view = this.m_dataSource[this.m_selectedValueIndex];
                        str = ((int)view["ValueField"]).ToString(CultureInfo.InvariantCulture);
                    }
                    // register the hidden field with, in some cases, wrong value
                    this.Page.ClientScript.RegisterHiddenField(this.HiddenFieldName, str);
                }
            }
        }
    }
}

     Let’s examine this.m_selectedValueIndex. DataSource contains available options to choose. In turn, m_selectedValueIndex contains the index of the option currently stored in SPListItem, the index inside DataSource. Note that m_selectedValueIndex doesn’t by no means reflect the option picked by user on the page, but it reflects the option currently stored in SPListItem.

     I marked with (***) the code line where we face the problem. While postback, SetFieldControlValue doesn’t extract from hidden html-field the option picked by user (Context.Request.Form[this.HiddenFieldName]), if some valid option has been already stored in SPListItem before (i.e. if this.m_selectedValueIndex >= 0). In other words, LookupField ignores the option selected by user and populates the next hidden html-field with old value. As the result, during the next successful postback, the old option will be again stored in SPListItem.

     Now how to fix this bug. I’ve implemented a descendant of LookupField, which allows to avoid above described problem.

public class FixedLookupField : LookupField
{
    protected object _selectedValue = null;

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);

        // preserve picked option (property Value gets the selected option from hidden html-field)
        if (Page.IsPostBack)
            _selectedValue = Value;
    }

    protected override void OnPreRender(EventArgs e)
    {
        base.OnPreRender(e);

        try
        {
            if (Page.IsPostBack && _selectedValue != null && IsTextBox())
            {
                // register a javascript, which overrides value contained in hidden html-field with the right one
                string hiddenFieldName = GetHiddenFieldName();
                string startupScript = string.Format("document.getElementById('{0}').value = {1};", hiddenFieldName, _selectedValue.ToString());
                string startupScriptKey = "FixedLookupField_" + hiddenFieldName;
                if (!Page.ClientScript.IsStartupScriptRegistered(startupScriptKey))
                    Page.ClientScript.RegisterStartupScript(this.GetType(), startupScriptKey, startupScript, true);
            }
        }
        catch (Exception ex)
        {
        }
    }

    // allows to detect what way of rendering we have (DropDownList or TextBox with javascript tricks)
    protected bool IsTextBox()
    {
        Type baseType = this.GetType().BaseType;
        FieldInfo fldInfo = baseType.GetField("m_tbx", BindingFlags.Instance | BindingFlags.NonPublic);
        object tb = fldInfo.GetValue(this);
        return tb != null;
    }

    // returns the ID of hidden html-field how it will be on the page
    protected string GetHiddenFieldName()
    {
        Type baseType = this.GetType().BaseType;
        PropertyInfo propInfo = baseType.GetProperty("HiddenFieldName", BindingFlags.Instance | BindingFlags.NonPublic);
        return (string)propInfo.GetValue(this, null);
    }
}
Related posts:

SharePoint: Find Field Control

February 23rd, 2011 No comments

     In page code-behind I need to get a control related to some field. It’s considered the best practice is using of SPContext.Current.FormContext.FieldControlCollection. It’s an ArrayList that stores the field controls in the form. The code can look like the following:

public static FieldMetadata FindFieldControl(string fieldName)
{
    if (SPContext.Current.FormContext != null)
    {
        foreach (Control control in SPContext.Current.FormContext.FieldControlCollection)
        {
            FieldMetadata formField = control as FieldMetadata;
            if (formField != null && string.Compare(formField.FieldName, fieldName, true) == 0)
                return formField;
        }
    }
    return null;
}

     But the problem is that this collection is still empty or filled short on some early stages of page life cycle. In the same time the required field control can be already in the page tree of controls (Page.Controls).

     In my case to create a new list item we use a custom aspx-page. This aspx is very similar to the original SharePoint new-page for lists, the main difference is in the attribute Inherits in page declaration (<%@ Page … Inherits=”MyPageClass” … %>). Such approach allows us to do something in code-behind without redefinition of the standard html layout and, as the result, allows to have one page for many content types, because a set of field controls for every content type is still rendered by SharePoint infrastructure. But let’s get back on track.

     Let’s see the following example:

public class MyPageClass : WebPartPage
{
    protected override void OnInitComplete(EventArgs e)
    {
        FieldMetadata fldTmp = FindFieldControl("Model");
        // fldTmp = null, because the field control isn't created yet, SPContext.Current.FormContext.FieldControlCollection is empty


        base.OnInitComplete(e);


        fldTmp = FindFieldControl("Model");
        // fld = null, SPContext.Current.FormContext.FieldControlCollection isn't empty, but it doesn't contain the required field control,
        // in the same time the required field control is already in page tree of controls (Page.Controls)
    }


    protected override void OnLoad(EventArgs e)
    {
        FieldMetadata fldTmp = FindFieldControl("Model");
        // fld = null, SPContext.Current.FormContext.FieldControlCollection still doesn't contain the required field control,
        // but the required field control is presented in page tree of controls (Page.Controls)


        base.OnLoad(e);

        fldTmp = FindFieldControl("Model");
        // finally, fldTmp isn't null, SPContext.Current.FormContext.FieldControlCollection contains the required field control
    }
}

     Obviously, to get the field control as earlier as possible, we can use page tree of controls. It can be implemented in this way:

public static FieldMetadata FindFieldControlRecursive(Control root, string fieldName)
{
    FieldMetadata fieldMetadata = root as FieldMetadata;
    if (fieldMetadata != null && string.Compare(fieldMetadata.FieldName, fieldName, true) == 0)
        return fieldMetadata;

    foreach (Control c in root.Controls)
    {
        FieldMetadata t = FindFieldControlRecursive(c, fieldName);
        if (t != null)
            return t;
    }

    return null;
}

     The minus of this method is that it’s a recursive method and requires more time than if we use SPContext.Current.FormContext.FieldControlCollection. But we have to use FindFieldControlRecursive, if we need to get field controls at the early stage of page life cycle.

     The better way is to combine two these approaches into one method. The result looks like the following:

public static FieldMetadata FindFieldControl(Control root, string fieldName)
{
    if (SPContext.Current.FormContext != null)
    {
        foreach (Control control in SPContext.Current.FormContext.FieldControlCollection)
        {
            FieldMetadata formField = control as FieldMetadata;
            if (formField != null && string.Compare(formField.FieldName, fieldName, true) == 0)
                return formField;
        }
    }

    return FindFieldControlRecursive(root, fieldName);
}
public static FieldMetadata FindFieldControlRecursive(Control root, string fieldName)
{
    FieldMetadata fieldMetadata = root as FieldMetadata;
    if (fieldMetadata != null && string.Compare(fieldMetadata.FieldName, fieldName, true) == 0)
        return fieldMetadata;

    foreach (Control c in root.Controls)
    {
        FieldMetadata t = FindFieldControlRecursive(c, fieldName);
        if (t != null)
            return t;
    }

    return null;
}

     After this we will have

public class MyPageClass : WebPartPage
{
    protected override void OnInitComplete(EventArgs e)
    {
        FieldMetadata fldTmp = FindFieldControl(this, "Model");
        // fldTmp = null, because the field control isn't created yet


        base.OnInitComplete(e);


        fldTmp = FindFieldControl(this, "Model");
        // fldTmp isn't null, fldTmp was found in page tree of controls (Page.Controls)
    }


    protected override void OnLoad(EventArgs e)
    {
        FieldMetadata fldTmp = FindFieldControl(this, "Model");
        // fldTmp isn't null, fldTmp was found in page tree of controls (Page.Controls)


        base.OnLoad(e);

        fldTmp = FindFieldControl(this, "Model");
        // fldTmp isn't null, fldTmp was found in SPContext.Current.FormContext.FieldControlCollection
    }
}

     Don’t forget to use Static field name, do not use Display name. Thanks for attention!

Related posts:

SharePoint: Get SPControlMode, which is associated with the current request

December 30th, 2010 No comments

     As I said before, I have custom display/edit/new forms for every usable list. Some of them are derived from a page base class. In some cases, e.g. basing on characteristics of a list item we are working at, I have to limit access to the forms (display/edit/new) for some users. These additional restrictions is a sort of superstructure over the SharePoint groups and permissions. I try to check conditions and deny access to the page as soon as possible in bounds of the page lifecycle. I prefer doing it in the base class inside OnInit-method, so it allows to avoid such actions as creation of controls, redirections and so on, that usually happen inside and between OnInit and OnLoad, and that are already vain if you deny access to the page, of course. In other words, it looks like the following:

public class MyBaseWebPartPage : WebPartPage
{
    protected override void OnInit(EventArgs e)
    {
        if(!DoesUserHaveAccess())
            SPUtility.HandleAccessDenied(new Exception("User doesn't have access!"));
        base.OnInit(e);
    }
}

     Very often to analyze page accessibility I need to know what mode (display/edit/new) corresponds to the current page, i.e. I need to know exactly whether it’s a display-page or an edit-page or a page to create a new item. SPContext.Current contains property FormContext, which, in turn, contains property FormMode. FormMode is a value of SPControlMode enumeration and indicates what mode is active now on page.

public enum SPControlMode
{
    Invalid = 0,
    Display = 1,
    Edit = 2,
    New = 3,
}

     The only problem with FormMode is that, in the most cases, this property returns Invalid value inside OnInit-method. But, there is another way to find out the current mode of page. In schema.xml of any list there is a section <Forms>, which contains urls of aspx-pages that correspond to Display, Edit and New form modes.

<?xml version="1.0" encoding="utf-8"?>
<List Name="SomeList" Title="Some list" Description="" Direction="0" BaseType="0" Url="Lists/SomeList" Type="100" Id="48C2DB5D-C74A-4246-ACB5-1FF536D37C19" xmlns="http://schemas.microsoft.com/sharepoint/" VersioningEnabled="TRUE" DisableAttachments="TRUE">
  <MetaData>
    <Views>...</Views>
    <Fields>...</Fields>
    <ContentTypes>...</ContentTypes>

    <Forms>
      <Form Type="DisplayForm" Url="MyDispForm.aspx" WebPartZoneID="Main" />
      <Form Type="EditForm" Url="MyEditForm.aspx" WebPartZoneID="Main" />
      <Form Type="NewForm" Url="MyNewForm.aspx" WebPartZoneID="Main" />
    </Forms>
  </MetaData>
</List>

     These aspx-pages are used for each item of the list, but they can be overridden by urls defined for content types.

     If you have different content types, you, probably, have a section <FormUrls> inside content type definitions in schema.xml. As I said above, Urls defined in this section override the Urls defined in the section <Forms>, but only for this content type.

<?xml version="1.0" encoding="utf-8"?>
<List Name="SomeList" Title="Some list" Description="" Direction="0" BaseType="0" Url="Lists/SomeList" Type="100" Id="48C2DB5D-C74A-4246-ACB5-1FF536D37C19" xmlns="http://schemas.microsoft.com/sharepoint/" VersioningEnabled="TRUE" DisableAttachments="TRUE">
  <MetaData>
    <Views>...</Views>
    <Fields>...</Fields>
    <ContentTypes>
      <ContentType ID="0x0100204CA6A33177A5488DA2681769D46781" Name="SomeTypeOfItem" Group="List Content Types" Description="Create a new list item.">
        <FieldRefs>...</FieldRefs>
        <XmlDocuments>
          <XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms/url">
            <FormUrls xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms/url">
              <Display>Lists/SomeList/MyView.aspx</Display>
              <Edit>Lists/SomeList/MyEdit.aspx</Edit>
              <New>Lists/SomeList/MyNew.aspx</New>
            </FormUrls>
          </XmlDocument>
        </XmlDocuments>
        <Folder>...</Folder>
      </ContentType>
    </ContentTypes>

    <Forms>...</Forms>
  </MetaData>
</List>

     So, by turns comparing urls, which is defined for a content type and/or a list, with url of the current Page.Request, we can definitely detect the current form mode of the page. The given approach can be implemented as the following method:

protected SPControlMode GetCurrentControlMode()
{
    // if current control mode is already accessible and valid, return it
    if (SPContext.Current.FormContext != null && SPContext.Current.FormContext.FormMode != SPControlMode.Invalid)
        return SPContext.Current.FormContext.FormMode;

    // compare url of the current request with url of every page defined in list and/or in content type

    // get current content type
    SPContentType spContentType = GetCurrentContentType();

    // store information about three possible form modes
    Dictionary<SPControlMode, KeyValuePair<string, PAGETYPE>> controlModeInfos = new Dictionary<SPControlMode, KeyValuePair<string, PAGETYPE>>(3);
    controlModeInfos[SPControlMode.Display] = new KeyValuePair<string, PAGETYPE>(spContentType != null ? spContentType.DisplayFormUrl : null, PAGETYPE.PAGE_DISPLAYFORM);
    controlModeInfos[SPControlMode.Edit]    = new KeyValuePair<string, PAGETYPE>(spContentType != null ? spContentType.EditFormUrl    : null, PAGETYPE.PAGE_EDITFORM);
    controlModeInfos[SPControlMode.New]     = new KeyValuePair<string, PAGETYPE>(spContentType != null ? spContentType.NewFormUrl     : null, PAGETYPE.PAGE_NEWFORM);

    // check each form mode whether it corresponds to the current request
    foreach (SPControlMode spControlMode in controlModeInfos.Keys)
    {
        bool res = false;

        // get a form url, that is defined in content type
        string cntTypeFormUrl = controlModeInfos[spControlMode].Key;
        // check whether the form url corresponds to the current request
        if (!string.IsNullOrEmpty(cntTypeFormUrl))
            res = IsCurrentPage(cntTypeFormUrl);
        if (!res)
        {
            // get a form url, that is defined in list
            SPForm spForm = SPContext.Current.List.Forms[controlModeInfos[spControlMode].Value];
            // check whether the form url corresponds to the current request
            if (spForm != null)
                res = IsCurrentPage(spForm.Url);
        }

        // return control mode if a coincidence is found
        if (res)
            return spControlMode;
    }

    return SPControlMode.Invalid;
}

     This method uses collection SPContext.Current.List.Forms to get access to urls defined in section <Forms> and properties SPContentType.DisplayFormUrl, SPContentType.EditFormUrl and SPContentType.NewFormUrl of the current content type to get access to urls defined in section <FormUrls>. The method GetCurrentContentType() works fine inside OnInit and was described in my previous post (SharePoint: Get content type, which is associated with the current request). The method IsCurrentPage is cited below:

protected bool IsCurrentPage(string pageName)
{
    if (!string.IsNullOrEmpty(pageName))
    {
        pageName = pageName.Trim('/');
        int urlDelimeterIndex = pageName.LastIndexOf('/');
        if (urlDelimeterIndex >= 0)
            pageName = pageName.Substring(urlDelimeterIndex + 1);
        return Page.Request.Url.AbsolutePath.EndsWith(pageName);
    }
    return false;
}
Related posts: