Navigation

Monday, 12 July 2010

RCWP Part 3 - Edit Web Part using a Ribbon modal dialog

Also check out the other part of the series:
This follows on from Part 1 (where we created a “Related Content Web Part”) and Part 2 (where we added a contextual tab to the Ribbon).

This post summarised the final part of this Web Part (which we completed in the final session of the day of SPRetreat last Saturday).
We wanted to provide a pop-up window, accessed through our new Contextual Tab in the Ribbon, which allowed us to easily modify some web part properties.
The basis of this was quite straight-forward, and it certainly starts off quite easy.
We created a new Application Page (RCWP_SetFieldValue.aspx) which would contain the code to update our Web Part Properties.

In this file we added a simple ASP.Net Label, Drop Down List and button.
<asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server">
<p>
    This allows you to set the field value for the <strong>Related Content Web Part</strong>
</p>
<asp:Label runat="server" ID="lblChoice" Text="Select Field:" AssociatedControlID="ddlFields" /><br />
<asp:DropDownList runat="server" ID="ddlFields" /><br />
<asp:Button runat="server" ID="btnNike" Text="Just Do It!" />
</asp:Content>
Back in Part 2 we created a JavaScript file which was use for the “Command” events for our Buttons (yes .. I told you we’d be looking at that again!).

Here we are going to modify one of the Buttons so that it throws up a SharePoint Modal Dialog with our Application Page in it.

The code below is modified from the original MS Blog Article I referenced in Part 2 (called “How to create a Web Part with a Contextual Tab”).
if (commandId === 'CustomContextualTab.GoodbyeWorldCommand') {
            //alert('Good-bye, world!');
            var options = {
                url: '/_layouts/SPR3/RCWP_SetFieldValue.aspx,
                title: 'Set Field',
                allowMaximize: false,
                showClose: true,
                width: 800,
                height: 600
            };
            SP.UI.ModalDialog.showModalDialog(options);
        }
I have basically changed the JavaScript for the “GoodbyeWorldCommand” button so that it does something different.

I am using the new SP.UI.ModalDialog namespace in the SharePoint ECMAScript to pop up a modal dialog window.

(Note - I also changed the display text to “Set Field” .. and deleted the other button to clean up the ribbon a bit)

But don’t forget that our Application Page is running from _layouts … it’s in a completely different place to our Web Part so this really isn’t enough for our page to work. In order to do anything else our Layouts page would need the following information:
  • The Page that the web part is on (URL)
  • Which Web Part to update on that page (Web Part ID)
The URL of the current page is easy enough using JavaScript (location.href) but the Web Part ID … this represented a new challenge.
How do you get the server-side Web Part ID through JavaScript?
This problem took the entire final hour of the day (Session 5) and took quite a bit of research and web searching. Eventually (after a few suggestions) we hit upon the answer:

Back in Part 2 we created a JavaScript file which was used to register our Contextual Tab. The JavaScript file that registers the Contextual Tab contains a reference to a “PageComponentId”.
getId: function ContextualTabWebPart_CustomPageComponent$getId() {
    return this._webPartPageComponentId;
}
Thes pecific instance of our Web Part had a “PageComponentID” of “WebPartWPQ2” and after some digging we found it in the Source of the page!
<div WebPartID="866ef42d-6626-45e0-af9c-a00467ed2666" WebPartID2="1ad9529a-5e86-4e7c-9d4d-022a1fa6e6c0" HasPers="false" id="WebPartWPQ2" width="100%" class="ms-WPBody noindex ms-wpContentDivSpace" allowRemove="false" allowDelete="false" style="" >
The attribute that REALLY stands out though is the WebPartID:
WebPartID="866ef42d-6626-45e0-af9c-a00467ed2666"
This is clearly a GUID value, referring to the server-side Web Part ID for that instance of the Web Part.
So .. how do we get this to our dialog.. well, good old trusty document.GetElementById() (we could have used JQuery, but I didn’t want to have to install the framework .. and don’t forget .. I only had 1 hour to get this working at SPRetreat!!)

Using this information, I could modify my JavaScript to retrieve these values, and pass them through to my Modal Dialog.
// get the Web Part DIV element
            var element = document.getElementById(this._webPartPageComponentId);
            // extract the Web Part ID (as a GUID object)
            var wpID = element.attributes["WebPartId"];
            // pass through the URL and Web Part ID
            var options = {
                url: '/_layouts/SPR3/RCWP_SetFieldValue.aspx?wpID=' + wpID.nodeValue + '&url=' + location.href,
                title: 'Set Field',
                allowMaximize: false,
                showClose: true,
                width: 800,
                height: 600
            };
            SP.UI.ModalDialog.showModalDialog(options);
Note – as the Web Part ID is of type HTML Attribute, we need to use the “NodeValue” property instead of toString();

So .. first off, in our Application Page we can use the URL to retrieve the fields from the page’s Content Type and populate our Drop Down List.
protected void Page_Load(object sender, EventArgs e)
        {
            TargetUrl = Request.QueryString["url"];
            // remove any query strings
            if (TargetUrl.IndexOf("?") != -1)
            {
                TargetUrl = TargetUrl.Substring(0, TargetUrl.IndexOf("?"));
            }
            if (!Page.IsPostBack)
            {
                ddlFields.Items.Clear();
                SPFile file = this.Web.GetFile(TargetUrl);
                foreach (SPField field in file.Item.Fields)
                {
                    if (!field.Hidden)
                    {
                        ListItem item = new ListItem(field.Title, field.StaticName);
                        ddlFields.Items.Add(item);
                    }
                }
            }
            btnNike.Click += new EventHandler(btnNike_Click);
        }
I did a bit of string manipulation on the URL to make sure we trim out any URL query strings, and then use that to retrieve an SPFile object.

We then just iterate through the SPListItem.Fields collection, adding any fields that are not hidden.

Note – we are using an ASP.Net ListItem object in the Drop Down List, so that we can use the Display Name in the drop-down, but store the Static Name as the value .. it’s the Static Name we need to save to our Web Part!

The next bit is under our Click event. We can now use the URL to get the SPLimitedWebPartManager for the page, and pass through the Web Part ID property, and it would retrieve the instance of my Web Part (allowing me to set the field value).
protected void btnNike_Click(object sender, EventArgs e)
       {
           // get Web Part ID
           wpID = Request.QueryString["wpID"];
           // retrieve the Web Part Panager for the URL
           SPFile file = this.Web.GetFile(TargetUrl);
           SPLimitedWebPartManager wpm = file.GetLimitedWebPartManager(PersonalizationScope.Shared);
           // get the safely-casted web part object
           RelatedContentWebPart.RelatedContentWebPart wp =
               wpm.WebParts[new Guid(wpID)] as RelatedContentWebPart.RelatedContentWebPart;
           if (wp != null)
           {
               // set the web part property, and save settings
               wp.FieldName = ddlFields.SelectedValue;
               wpm.SaveChanges(wp);
           }
           // close the modal dialog
           this.Context.Response.Write("<script type='text/javascript'>window.frameElement.commitPopup();</script>");
           this.Context.Response.End();
       }
So .. we should be done…

Build / Deploy / Test




So .. a long journey but worth it, five different 1 hour sessions and a great day at #SPRetreat .. but definitely worthwhile, and a new “Related Content Web Part” to boot!

A massive thanks to Andrew Woodward (21Apps) and Ben Robb (CScape) for organising the event, the venue and the food! (great food!!!)

Source Code

Sorry it took so long for me to get it all online, I was very busy then went on holiday. You can find all of the source code downloadable from my SkyDrive here:

http://cid-60f12a60288e5607.office.live.com/self.aspx/SPRetreat/SPR3.zip

8 comments:

  1. Martin,

    Wondering when the source code may be available online....thanks

    regards

    Julian

    ReplyDelete
  2. Yeh sorry about that.

    Took ages (slipped my mind, then lots of client work followed by summer holiday).

    Up now though! cheers

    ReplyDelete
  3. Hello,

    This is really interesting take on the concept. I never thought of it that way. I came across this site recently which I think it will be a great use of new ideas and informations.

    Extract Web

    ReplyDelete
  4. Hi,
    I have succesfully deployed your wsp package and its working for me. But when i add it as a webpart, I can see the results page but refinement panel is not working with it. it works fine when i do the regular Search core result webpart but not with Related Content webpart. I even tried connections but gets message saying "No Schema". please let me know if you have any ideas. Thanks

    ReplyDelete
    Replies
    1. Anon,

      This is a custom web part for a specific purpose, showing a simple set of results which are related to the current page.

      I haven't checked but it was never the intention to get this working with other search features such as Refinement Panels.

      The source code is available for download, so by all means look through it and use it / customise it for your needs

      Delete
  5. Hi,

    I was able to try contextual tab for the webpart, but when i added another instance of the same webpart into same page, contextual tab button works only for the first instance. For the second instance tab is available but the button in the ribbon tab is not enabled.

    ReplyDelete
  6. Hi Martin

    The download link doesn't seem to be working??

    Thanks
    Martin

    ReplyDelete
  7. Martin,

    Worked for me .. what error are you getting?

    ReplyDelete

This blog has been moved to www.martinhatch.com

Note: only a member of this blog may post a comment.