Posts Tagged 'Sharepoint'

Sharepoint Error: Save Conflict Your changes conflict with those made concurrently by another user

If you develop Sharepoint components that modify list items, you might know this error:

Save Conflict

Your changes conflict with those made concurrently by another user. If you want your changes to be applied, click Back in your Web browser, refresh the page, and resubmit your changes.

This happens whenever you try to save a SPListItem which has been modified since you opened it. Review your code to ensure that you don’t open the same list item multiple times, work with a single SPListItem object instead.

Sometimes however it is necessary to open the same list item in parallel, as example if you want to perform an operation with elevated privileges on the item. If you take this approach you need to ensure that if you save one of the SPListItem objects, you need to reload all other SPListItems that point to the same actual item. Otherwise you’ll get the error above whenever you perform a change on the outdated items.

You can use the following utility method to ease the reloading of items:

        /// <summary>
        /// Reload a list item from scratch
        /// </summary>
        /// <param name="item">The list item</param>
        /// <remarks>
        /// Useful to avoid save conflicts
        /// </remarks>
        /// <returns></returns>
        public static SPListItem ReloadListItem(SPListItem item)
        {
            if (item == null)
                return null;

            return item.ParentList.GetItemByUniqueId(item.UniqueId);
        }

Custom redirect after creating a new Sharepoint Item

When creating a new Item in a Sharepoint list, the redirection works according to these rules:

  • If the URL contains a valid “Source” parameter, the user user will be redirected to this URL
  • Otherwise the user will be redirected to the default view of the list, such as AllItems.aspx

There are different approaches to change this behavior:

Via URL Parameter

One appraoch is to override the redirection by changing the Source parameter of the URL. This means the incoming link to the NewForm.aspx contains the redirection URL already. This is pretty static, sometimes you want to set the redirection on the fly. Also the appraoch enforces the same redirection for both Safe and Close buttons on the form.

Via Event Receiver

It is possible to redirect within an event receiver. This means placing a redirect command within a synchrounous event which aborts the normal event flow in a way, that asynchrounous events and other attached event receivers won’t work anymore.

Via Custom Safe Buttons

It is possible to replace the default buttons on the NewForm.aspx page by custom controls. So basically you develop a custom button that inherits from the Sharepoint “SaveButton” class. In there you can apply custom logic for redirection.

Via JavaScript

This is a novel approach I developed to get more flexibility and stability for the redirection behavior. It allows to set the redirection on the clientside.

$(document).ready(function() {

    var button = $("input[id$=SaveItem]");
    // change redirection behavior
        button.removeAttr("onclick");
        button.click(function() {
            var elementName = $(this).attr("name");
            var aspForm = $("form[name=aspnetForm]");
            var oldPostbackUrl = aspForm.get(0).action;
            var currentSourceValue = GetUrlKeyValue("Source", true, oldPostbackUrl);
            var newPostbackUrl = oldPostbackUrl.replace(currentSourceValue, "MyRedirectionDestination.aspx");

            if (!PreSaveItem()) return false;
            WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions(elementName, "", true, "", newPostbackUrl, false, true));
        });
    
});

So basically we modify the click behavior of the create button on the NewForm.aspx to pass in a new “Source” parameter value just before the form is submitted.

The “Column Element” in Sharepoint Render pattern is empty sometimes

According to the Sharepoint SDK:

Column Element (View)
Returns the raw data value for a particular field, as opposed to the formatted version provided by the Field element. In the case of a Lookup field, Column returns the local value (an ID number in the list that references another list), while the LookupColumn element returns the data from the foreign table.

They failed to mention that the output will be empty if the specified column is not included in the containing view already! Seems like a bug / undocumented issue to me.

The “show quoted messages” link breaks other controls

In Sharepoint discussion boards, there is the “show quoted messages” button within each post. It allows to collapse/expand the quoted message.

However this button causes a serious problem:
If you place additional postback sensitive controls on the discussion page, they might break whenever the “show quoted messages” is clicked. This happens, because this button does not perform a valid postback.

Here is a JS based fix:



// workaround for broken show-quoted-text button in discussion board
// see core.js for the original function

function ShowQuotedText(guid, anchor) {
    var frm = document.forms[MSOWebPartPageFormName];
    frm.CAML_ShowOriginalEmailBody.value = frm.CAML_ShowOriginalEmailBody.value.concat(guid);
    if (frm.action.indexOf("#") > 0) {
        frm.action = frm.action.substr(0, frm.action.indexOf("#"));
    }
    frm.action = frm.action.concat("#" + anchor);
    
    // a raw form submit will cause the people editor to break
    //frm.submit();
    // perform a proper postback instead
    __doPostBack('__Page', ''); 
    
    return false;
}

Simply include this snipped in the header of the affected page, or in an external JS file. Just make sure that it is being loaded after core.js.

Sharepoint SPGridView, expand specific group using Javascript

Sometimes you want to expand a specific group within a SPGridView using Javascript.

The following function achieves just that:

function spGridViewExpandViewGroup(gridviewId, groupIndex) {

    var gridview = $("[id$='" + gridviewId + "']");
    var rows = gridview.find("tr[isexp]");

    if (rows.length >= groupIndex) {
        var row = rows.get(groupIndex);
        
        if ($(row).attr('isexp') == 'false') {
            var link = $(row).find("a[title='Expand/Collapse']");
            link.click();
        }
    }
}

Basically you pass in the (serverside) ID of the SPGridView control and the index of the group that should be expanded.

As example, the function call being contructed on the asp.net side:

string javaScript = string.Format("spGridViewExpandViewGroup('{0}',{1});", gridView.ID, 0);
Page.ClientScript.RegisterStartupScript(typeof(string), "expandviewgroup", javaScript, true);

Note: The script requires the jQuery library to be included.

Sharepoint SPGridView control is expanded by default

The Sharepoint SPGridView control seems to be rendered as expanded sometimes, although it should be collapsed.

Here is a javascript function to fix this issue:

function spGridViewFixExpandedGroups(gridviewId) {
    var gridview = $("[id$='" + gridviewId + "']");
    var links = gridview.find("tr[isexp='true'] a[title='Expand/Collapse']");
    links.click();
}

Simply call the script upon page load. Note: The script requires the jQuery library to be included.

Disable Sharepoint item events firing during item update

In Sharepoint, the attached item events are fired whenever you perform an item.Update()
Ryan posted a solution to this issue, using a custom event handler that disables the events upon request.

What I dislike about the solution, is the risk of leaving the events disabled. Assume an exception occurs, the code will never reach the line where the event firing is enabled (restored) again.

I suggest a using-pattern, much like the TransactionScope class used for db transactions in the entity framework.

    class DisabledItemEventsScope : SPItemEventReceiver, IDisposable
    {
        public DisabledItemEventsScope()
        {
            base.DisableEventFiring();
        }

        #region IDisposable Members

        public void Dispose()
        {
            base.EnableEventFiring();
        }

        #endregion
    }

Now we can use this class together with the using syntax to create a scope within all item event firing is disabled.

                using (DisabledItemEventsScope scope = new DisabledItemEventsScope())
                {
                    item.SystemUpdate(); // will NOT fire events
                }
                item.SystemUpdate(); // will fire events again

This way, the event firing is enabled again as soon as the code leaves the defined scope, be it through normal operation or by exceptions.

Update 1

For SharePoint 2010, use this snippet instead:

    /// <summary>
    /// Disabled item events scope
    /// </summary>
    /// <see cref="https://adrianhenke.wordpress.com/2010/01/29/disable-item-events-firing-during-item-update/"/>
    class DisabledItemEventsScope : SPItemEventReceiver, IDisposable
    {
        bool oldValue;

        public DisabledItemEventsScope()
        {
            this.oldValue = base.EventFiringEnabled;
            base.EventFiringEnabled = false;
        }

        #region IDisposable Members

        public void Dispose()
        {
            base.EventFiringEnabled = oldValue;
        }

        #endregion
    }