Archives

Archives / 2011 / July
  • Deploying Web Parts Farm-wide using the WebPartAdderExtension element in SharePoint 2010

    Tags: Web Parts, SharePoint 2010

    Here goes another post using the WebPartAdderExtension Element. I previously wrote an introduction to custom Web Part Gallery sources and a second one on how to enhance the end-user experience when adding new Web Parts. Now I'm going to show you another trick that this technique can be used for.

    Introduction

    Deploying Web Parts are normally done by using the Module element in the Elements manifest and using that element uploading/deploying Web Part Controls Description files (.webpart or .dwp files) into the Web Part catalog. These Element manifests for Web Parts must be scoped to the Site Collection level, since it is there where the Web Part catalog lives (~/site/_catalogs/wp). So if you want do deploy a solution containing a set of Web Parts you need to activate that Feature on each and every Site Collection. You can do it in a number of ways such as Feature Stapling, code, scripting etc. Could be quite tedious work if you have a large farm with many site collections and web applications. Also when retracting solutions with Features that uses Modules we manually have to clean up the files provisioned.

    Enter WebPartAdderExtension!

    So once again our new acquaintance the WebPartAdderExtension can help us in the scenario where we want to deploy Web Parts in a more efficient manner. In the two previous posts I built both examples using Site Collection scoped features - that's the way we normally think when programming for Web Parts. But the good news is that the WebPartAdderExtension element that is used to deploy the custom Gallery Source can actually be scoped to the following scopes:

    • Site - a Site Collection
    • WebApplication - a Web Application
    • Farm - the whole Farm

    Did you see that! Using this simple technique you can easily deploy and maintain Web Parts for a specific Web Application or the whole Farm if you like!

    The WebPartAdderExtension element

    Just follow the previous two guides and change the Scope of your Feature and you're ready to go.

    A word of caution

    It's unsafe  - watch out for zombies!There is one caveat with this though. If you set your Feature to Farm and deploy the solution, but only deploy/install it to one ore more Web Applications. The Safe Controls will only be installed on those specific Web Applications - not on all Web Applications in the farm. But the custom Gallery Source is still visible in the whole farm. This results in that you will get Safe Control errors as soon as you add the Web Part to a page in an Web Application where the Solution is not deployed.

    Summary

    This was the third really cool thing about the Web Part Adder and it's friends. It's never been this easy deploying Web Parts in SharePoint 2010. There are still a few more parts to be disclosed about this little nugget!

  • Improve the experience using Modal Dialogs when adding Web Parts in SharePoint 2010

    Tags: Web Parts, SharePoint 2010

    This is a follow-up post on yesterdays introduction to the WebPartAdder (which lately has been one of my favorite features of SharePoint 2010). In that post I mentioned that you could invoke a JavaScript function when a Web Part is added using a custom Web Part Gallery source.

    The Silverlight Web Part

    The Silverlight Web PartIf you have been working with SharePoint as an end-user you've probably seen the nice modal dialog that pops up when you're adding a Silverlight Web Part to a page, see the image to the right. It allows you to very easily configure the Web Part with the appropriate XAP file without editing the Web Part properties. If more Web Parts were like this it would be a whole lot easier working with Web Parts.

    The good news is that you can do this for your Web Parts - using a custom Gallery Source, just as I showed yesterday. And I'll show you how.

    Create a custom Web Part with a default Modal Dialog property form

    In this sample we're basically building out the same structure as I showed yesterday. I'll show you a bit different approach though - which is a bit more compact. And we'll need to throw in some custom JavaScript and an Application Page as well.

    Create a new Visual Studio project

    The SPIFirst of all create a new empty SharePoint project in Visual Studio. Then add an Empty Element SPI, we'll skip adding a Web Part like yesterdays post showed. In this Empty Element SPI add three files; one for the Web Part, one for the Gallery Source and one for the Gallery Item.

    Make sure to change the Scope of the automatically added Feature to Site Collection, instead of the default Web value!

    Implement the Web Part

    We're completely manually creating the Web Part in this case (my favorite way to do it :-). Add a simple string property (in this case called Data), a default constructor, another constructor that takes the property value as parameter and finally make the UI in the CreateChildControls() method. In order to get it to work you need to manually add a reference to System.Web. It should look something like this:

    public class ScriptedAdderWebPart : WebPart {
    
        public ScriptedAdderWebPart() {
        }
    
        public ScriptedAdderWebPart(string data) {
            Data = data;
        }
    
        protected override void CreateChildControls() {
            this.Controls.Add(new LiteralControl(
                String.Format("You entered: {0}", this.Data)
                ));
            base.CreateChildControls();
        }
    
        [WebBrowsable]
        [Personalizable(PersonalizationScope.Shared)]
        public string Data { get; set; }
    }

    You also need to add the SafeControl entry for this Web Part on the SPI. Right-click the SPI and choose Properties. Click the ellipsis button on the Safe Controls row and click Add to add the Safe Control. Click OK when you're done.

    Create the custom Gallery Source

    The custom Gallery Source is very simple, in this case we only want it to return a single custom Gallery Item. So the implementation should look as follows:

    [Guid("f62379dd-8ec0-402e-8034-7a0bcaaf7a8a")]
    public class ScriptedAdderWebPartGallerySource : WebPartGallerySourceBase {
        public ScriptedAdderWebPartGallerySource(Page page)
            : base(page) {
        }
    
        protected override WebPartGalleryItemBase[] GetItemsCore() {
            return new WebPartGalleryItemBase[] {
                new ScriptedAdderWebPartGalleryItem(this, base.Page)
            };
        }
    }

    Create the custom Gallery Item

    It's in the custom Gallery the magic happens, it's where we tell the WebPartAdder to invoke a JavaScript before adding the Web Part to the page. The implementation is very similar to the one in yesterdays post, and I'll only show you the differences. First of all we need to override the OnClientAdd property, it is a string and should contain the name of the JavaScript method that should be invoked.

    public override string OnClientAdd {
        get {
            return "adderWithScriptDlg";
        }
    }

    Next we need to modify the Instantiate() method. We'll create our custom Web Part using the constructor that takes the data as argument and pass in the value of the WebPartContent property. The WebPartContent property is a property of the Gallery Item that is set during the post back after your JavaScript has been invoked.

    public override System.Web.UI.WebControls.WebParts.WebPart Instantiate() {
        return new ScriptedAdderWebPart(this.WebPartContent);
    }

    This property could basically contain anything, as long as it can be represented as a string. You can create JSON structures for instance if you need to pass more advanced data structures to your Web Part if you perhaps like to set several properties.

    Create the custom JavaScript

    Now it's time to implement the JavaScript that is invoked when the end-user clicks the Add button to add a Web Part. Add the "Layouts" mapped folder to your project and add a new JavaScript file. In this file create a new function with the name you entered in the OnClientAdd property. It is important that it takes three arguments; item, zoneNum and zoneIndex. In this case we would like to open a Modal Dialog and retrieve the Web Part property value, so we implement it as follows:

    function adderWithScriptDlg(item, zoneNum, zoneIndex) {
        var options = {
            url: SP.Utilities.Utility.getLayoutsPageUrl('Wictor.AdderWithScript/AddPage.aspx'),
            title: "Enter the data for the Web Part",
            width: 400,
            height: 300,
            dialogReturnValueCallback: 
                Function.createDelegate(null, function (dialogResult, returnValue) {
                if (dialogResult == 1) {
                    var wpVal = WPAdder._getHiddenField('wpVal');
                    if (wpVal) {
                        wpVal.value = returnValue;
                    }
                    WPAdder.addItemToPage(item, zoneNum, zoneIndex);
                }
            })
        }
        SP.UI.ModalDialog.showModalDialog(options);
    }

    First of all we create the options variable that contains all the options for the Modal Dialog and then show the dialog using the showModalDialog() method. The properties for the modal dialog is configured so that It's pointing to a custom Application Page (we'll create that one in a moment). Then we set some other properties and finally the callback function of the dialog. In the callback we do something interesting - we retrieve a hidden field called wpVal and set the value of that field to the value returned from the modal dialog. It is this property that then is posted back to the WebPartAdder and inserted into the WebPartContent property. And then to actually add the Web Part we need to call back into the WebPartAdder JavaScript API (WPAdder) using the addItemToPage() method.

    Create the custom Application Page

    Now it's time to create the custom Application Page. This is a standard procedure, so I'm not showing you all the details. But basically you add a new Application Page to the mapped Layouts folder and then create a UI. In this case a textbox and a button is sufficient. The server side code of the Application Page should handle the click of the button and return (using JavaScript) the value in the textbox to the callback method. This could be implemented as follows:

    protected void btn_Click(object sender, EventArgs e) {
        Response.Clear();
        Response.Write(
            String.Format(
            "<script type='text/javascript'>window.frameElement.commonModalDialogClose(1,'{0}');</script>",
            data.Text
            ));
        Response.End();
    }

    It's just closing the Dialog with an OK value (1) and then passing the value of the textbox as second argument.

    And finally some CAML!

    What would SharePoint development be without some CAML. We need to add our custom Gallery Source as a Web Part Adder extension and we need to make sure the JavaScript is loaded (for the sake of simplicity I add it to all pages). Modify the elements manifest of your SPI as below:

    <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
        <WebPartAdderExtension
            Assembly="$SharePoint.Project.AssemblyFullName$"
            Class="$SharePoint.Type.f62379dd-8ec0-402e-8034-7a0bcaaf7a8a.FullName$"/>
        
        <CustomAction
            Location="ScriptLink"
            ScriptSrc="~site/_layouts/Wictor.AdderWithScript/AdderWithScript.js" />
    
    Elements>
    First I use the WebPartAdderExtension to register the custom Gallery Source and then a CustomAction with Location set to ScriptLink to load the JavaScript.

    Try it out!

    Time to take this baby out for a spin. Deploy the solution and edit a page. When you choose to add a Web Part you should see something that looks like this, depending on what categories etc you set for your custom Gallery Item:

    The Web Part Gallery

    Now when you click Add you should, instead of seeing the Web Part on the page, see a Modal Dialog popping up.

    The custom Modal Dialog

    If you close the dialog without clicking the button your Web Part is not added, but if you write something in the text box and click the button the Web Part should be added to the page with the custom property set as specified in the dialog.

    The awesome result!

    Summary

    Once again the WebPartAdder has proven to be very useful for end-user scenarios where you want to make it easier for users to add and configure Web Parts. Not only can we dynamically populate the Web Part Gallery, we can also force users to specify configuration/information for the Web Parts. Happy devving!

  • Dynamically populate the Web Part Gallery using the WebPartAdder in SharePoint 2010

    Tags: Web Parts, SharePoint 2010

    Writing this post has been on my agenda for some time, initially I intended to put it into my SharePoint 2010 Web Parts in Action book, but there was not enough time, you know how it is! This is an excellent new feature to SharePoint 2010 which allows you to dynamically populate the Web Part Gallery with Categories and Web Parts. So here we go.

    Introduction to the Web Part Adder and the Web Part Gallery

    Think of the List and Libraries category in the Web Part Gallery - it is dynamically populated with the lists and libraries available in the current web. This is all done using the WebPartAdder class which loads all the Web Parts available in the gallery from a number of different sources, see figure below. These source include the List and Libraries category, the local Web Part catalog, files in the wpcatalog, closed and uploaded Web Parts etc.

    WebPartAdder and sources

    The really cool thing with this is that you can actually create custom Gallery Sources! This is a new feature of SharePoint 2010 and is really simple to do. I'll show you how!

    Create a custom Web Part Gallery Source

    I've not yet seen any guide on how to create a custom Web Part Gallery Source, even though the classes and methods in MSDN are documented (the MSFT minimalistic way). To get this all working you need to use a new Feature element available in the Element manifest called WebPartAdderExtension. This element is referenced only once in MSDN, where it only says that the element is not documented!

    In this walkthrough I will use a very simple custom gallery source. It will dynamically populate the gallery with Product Web Parts that displays information about products. The product information is stored within a SharePoint list, in the local site, called Products.

    The product list

    From this list we'll create a new category in the gallery that lists all products, and when a product is added to the page it should add a new Web Part displaying that particular product information.

    Create a new SharePoint project

    imageStart by creating a new Empty SharePoint project. Make it a farm solution. Then add a new Web Part SPI - this will also add a Site Collection scoped feature to your project. In the Web Part SPI remove the .webpart file. We do not want to add this Web Part to the gallery - we only need the Web Part class. You can of course make the Web Part without an SPI if you want to.

    Next step is to build the Web Part user interface - and here you can get fancy if you like. If you feel for a copy and paste excursion you can copy and paste the code below.

    public class ProductWebPart : WebPart {
        public ProductWebPart() {
        }
        public ProductWebPart(SPListItem item) {
            ProductId = item.ID;
        }
        protected override void CreateChildControls() {
            SPList productList = SPContext.Current.Web.Lists.TryGetList("Products");
            if (productList != null) {
                try {                    
                    SPListItem item = productList.GetItemById(ProductId);
                    this.Title = "Product information:" + item.Title;
                    this.Controls.Add(new LiteralControl(
                        String.Format("Product: {0} costs ${1}", item.Title, item["Price"])));
                }
                catch (ArgumentException) {
                    this.Title = "Unknown product";
                    this.Controls.Add(new LiteralControl("Product not found"));
                }
            }
        }
    
        [Personalizable(PersonalizationScope.Shared)]
        [WebBrowsable]
        public int ProductId { get; set; }
    }

    As you can see it's a simple Web Part with one property ProductId that contains the id of the product to be listed. A specific constructor has been added which takes an SPListItem as argument - this one is used later by the custom source to instantiate the Web Part.

    The CreateChildControls is very simple and just writes the name of the product and its price, if the product is found - otherwise it shows an error.

    Create the Custom Web Part Gallery Source

    Next is to create the Custom Web Part Gallery Source. First add a new Empty Element SPI to your project - the SPI will automatically be associated with the feature that was created by the Web Part SPI (unless you installed Waldeks fancy Visual Studio extensions :-).

    In that SPI add two new classes; ProductsWebPartGallerySource and ProductsWebPartGalleryItem. The first one is the actual custom source and the second class represents the items in that source.

    The ProdcutsWebPartGallerySource is responsible for discovering and iterating the items for custom gallery extension. This class must derive from the WebPartGallerySource and implement one constructor as well as one method as follows:

    [Guid("01db1e81-5853-448e-b8c7-4b6564994ae3")]
    public class ProductsWebPartGallerySource : WebPartGallerySourceBase {
        public ProductsWebPartGallerySource(Page page)
            : base(page) {
        }
    
        protected override WebPartGalleryItemBase[] GetItemsCore() {
            List<WebPartGalleryItemBase> items = 
                new List<WebPartGalleryItemBase>();
            SPList list = SPContext.Current.Web.Lists["Products"];
            foreach (SPListItem item in list.Items) {
                items.Add(
                    new ProductsWebPartGalleryItem(this, base.Page, item));
            }
                
            return items.ToArray();
        }
       
    }

    The required constructor required only passes on to its base class. The overridden GetItemsCore() method is the method that is responsible for returning all Web Parts discovered by this source. So in this case the method simply iterates over all available items in the Products list and creates a ProductsWebPartGalleryItem object. Also notice that I added the Guid attribute to the class - we'll need that later.

    Now it's time to implement the gallery items. This is done in the second class file you created. The ProductsWebPartGalleryItem should be derived from the abstract class WebPartGalleryItemBase. It is implemented as follows:

    public class ProductsWebPartGalleryItem : WebPartGalleryItemBase {
    
        private readonly string title;
        private readonly SPListItem item;
    
        public ProductsWebPartGalleryItem(WebPartGallerySource source, Page page, SPListItem item)
            : base(source, page) {
            this.item = item;
            this.title = item.Title;
        }
    
        public override System.Web.UI.WebControls.WebParts.WebPart Instantiate() {
            return new ProductWebPart.ProductWebPart(item);
        }
    
        public override string Category {
            get { return "Products"; }
        }
    
        public override string Description {
            get { return "Show listing for product: " + title; }
        }
    
        public override string IconUrl {
            get { return "/_layouts/Wictor.Adder/product.png"; }
        }
    
        public override string Id {
            get { return item.ID.ToString(); }
        }
    
        public override bool IsRecommended {
            get { return false; }
        }
    
        public override string Title {
            get { return title; }
        }
    }

    This implementation has one constructor, taking the source, the current page and the specific list item as arguments. It stores some values locally in the class and passes the source and page to its base class. Before discussing the Instantiate() method let's take a look at the properties. These are the minimum properties required for the implementation to work, but there are several more that can be used to do some nifty stuff! Such as the ClientAdd method which allows you to specify a JavaScript method to execute when the Web Part is added or the RibbonCommand which allows you to execute a specific Ribbon command. What they do is pretty self explanatory! Interesting thing here is that you do not need to have the same category on all Web Parts in the source - you can populate several categories in the gallery, new or existing.

    The Instantiate() method is responsible for creating the Web Part with the correct properties and is called by the WebPartAdder and the custom source when a Web Part is added to the page. In this case we create the Web Part using the custom constructor and pass in the list item.

    So now all the code are done and it should compile fine. There is just one small thing left - the mysterious WebPartAdderExtension element. In your empty element SPI open the elements manifest file and make it look like the following:

    Elements xmlns="http://schemas.microsoft.com/sharepoint/">
        <WebPartAdderExtension 
            Assembly="$SharePoint.Project.AssemblyFullName$" 
            Class="$SharePoint.Type.01db1e81-5853-448e-b8c7-4b6564994ae3.FullName$"/>
    </Elements>

    As attribute values I use the Visual Studio replaceable tokens for the assembly full name and for the full name of the custom Web Part Gallery source class - here's the usefulness of the Guid attribute, no need to remember the class name and namespace and it allows for easier refactoring.

    As a final icing on the cake I also added a sweet little image to be used in the gallery. Your solution should look like this:

    image

    Ok, we're done!

    Test the custom Web Part Gallery Source!

    Now it is time to take this for a test drive. Deploy your solution, go to a page and edit it. Then click on Insert > Web Part. It should look something like this:

    SNAGHTML11b9c05

    The items in the Product list ends up as Web Parts in the Products category, with the sweet little icon. You can now choose a Web Part and insert into the page and see how it renders the correct information!

    Summary

    You've now seen how you can extend the Web Part Gallery with custom Web Part Gallery Sources. It is fairly simple and it can be very useful if you want to help the users by creating pre-configured Web Parts. I can see really many good use cases of this. Oh, I forgot - this does not work in the Sandbox.

  • The SharePoint 2010 4TB content database limit fine prints - just a warning!

    Tags: SQL Server, SharePoint 2010

    I guess by now we all seen or read about the new SharePoint 2010 guidance on scaling limits announced by the product group today. To sum it up it this is the new guidance on content database sizing:

    • up to 200GB - still the recommendation
    • 200GB to 4TB - yes, it's been done and can be done (with the help of a skilled professional architect :-)
    • 4TB or more - only for near read-only "record centers" with very sparse writing

    This looks good right, and it can be in some cases. But now on to the fine prints, which actually are written in the updated Software Boundaries and Limits article. If you read the announcement and the boundaries article you see that to be supported you need to follow a number of hard rules (such as IOPS per GB) and you must have governance rules (such as backup and restore plans) in place. Ok, if I got the IOPS needed, the best disaster recovery plans ever made and a skilled professional - should I go for the 4TB limit then? I think not, unless you really need the scale and have the hardware requirements.

    RBS: The content database size is the sum of all the data in the database and all other blobs stored on disk using RBS. So RBS does not get you around these limits!

    Size matters!

    First of all take a look at the file sizes of the content databases. Ok, you say, they are still taking the same amount of disk space whether I have a single content database or multiple content databases. Yes, the do occupy the same disk space, but you can't split them on separate physical medias (unless you go for multiple files for the database - which is another thing you should avoid), which might be necessary for performance, SLA and other reasons.

    Also consider moving really large files from your backup media, perhaps over the wire from a remote location, to restore something...

    Now think about your upgrades and patching. Remember that you update on a per content database basis, so an upgrade may take mighty long time. The SharePoint database schema is updated once in a while and if you're using really large content databases for collaboration sites (for instance) your users will be more than furious when the day of the upgrade comes along.

    Ok, I can live with all this you say. Then you need to take a look at all the other limits/thresholds/boundaries in the TechNet article - the Site Collection limits for instance. There is a strong recommendation for the size of a Site Collection to be less than 100GB, and it's there for a reason. Moving sites (using PowerShell) larger than this limit can fail or lock the database. And SharePoint (built-in) backup does only support backup of Site Collections less than 100GB.

    Exceeding the 4TB limit and aim for the sky for Records Centers?  It can be done (MS done it obviously) but also this under very explicit guidance. For instance you must base your sites on the Document or Records Center site definitions. Why? I'm not 100% sure since they are just site definitions, so it has to be some kind of "upgrade promise" from the product group that these site definitions will not have any rough upgrade paths in the future. The reason is to "reinforce the ask that the unlimited content database is for archive scenarios only".

    Summary

    This post is all about raising a finger of warning and tell you that you should not run away and tell your clients that they can now fill their content databases up to the new limits. Consider this really carefully and in most, if not all, cases use the 200GB limit when designing your SharePoint architectures. It's still good that there now is support for larger content databases when scale is needed and that we can pass the 200GB limit.

    Note: updated some parts to clarify my points.

About Wictor...

Wictor Wilén is a Director and SharePoint Architect working at Connecta AB. Wictor has achieved the Microsoft Certified Architect (MCA) - SharePoint 2010, Microsoft Certified Solutions Master (MCSM) - SharePoint  and Microsoft Certified Master (MCM) - SharePoint 2010 certifications. He has also been awarded Microsoft Most Valuable Professional (MVP) for four consecutive years.

And a word from our sponsors...

SharePoint 2010 Web Parts in Action