Wednesday, 21 March 2012

Extending SharePoint 2010 social features

In terms of social, it seems that many SharePoint 2010 customers wanted and/or continue to want a little bit more than “out-of-the-box”. This is especially the case for clients who use the activity feed – one of the features which changed SharePoint to be more of a social platform. “How do I reply/comment on one of these things in my activity feed?” clients usually say. “Where’s the ‘like’ button?!”. And so, a whole host of 3rd party products sprang up – some dedicated to SharePoint (Newsgator, Beezy, SnapworkSocial etc.), some more generic and sometimes cloud-based but with SharePoint integration (Yammer etc.). I’m not an expert in those products, though I have looked at some. What I do know is that most are still evolving, and if you started this journey a while ago, frankly you might not have liked any of them. Maybe you have reasons for still not liking any of them, such as privacy concerns. Or perhaps the fact that many want to be a parallel universe alongside SharePoint, where content is “sync’d” or “published” to. Something I notice about several products is that they completely replace the SharePoint activity feed, and so many of the good things about what Microsoft built are unfortunately lost - notifications around tags I’m interested in, job title changes, someone specifying they have the same interest as me, and so on. These are all valuable features which I’ve seen work well in the enterprise – they help connect people, content and company projects/workstreams which are relevant to each other.

Here I want to show you what we did for one client, perhaps taking a more SharePoint-centric route. I’m not here to sell it (it’s not a shrink-wrapped product, and I don’t own it), but I thought it would be fun to talk about some of the pinch points in SharePoint, and what you might do about them. I won’t be publishing source code. This was part of a project I worked on for Content and Code, a partner in the UK. I worked as part of a small but talented team, consisting of myself, Wes Hackett and Rich Browne (Rich is an extremely capable ‘under the radar’ kinda guy). Some of the client’s offshore team (a great group) also worked on other functionality around this set.

The activity feed – from “one way” to “two way”

So let’s start with the activity feed. Let’s go from this:

OutOfBoxActivityFeed800_thumb

..to this (click to see larger image):

ActivityFeed8001

Notice that the content is pretty much the same in both images. The look and feel is clearly stolen from influenced by Facebook, but I’d like to think there’s more to it than that. Obviously we have the big ‘tick-list’ items such as liking and commenting (more on these later), but we also enhanced some other aspects of the SharePoint 2010 activity feed too:

  • Activity feed items related to SharePoint content – did it ever strike you as weird that SharePoint is an ECM system, but only status updates, job title changes and other ‘social’ things appear in the activity feed? Our client thought so, and we changed that. Our feed has the following content:
    • Announcements, discussions/discussion replies, wiki pages/enterprise wiki pages, blog posts and blog comments. Notice that documents are missing – obviously most SharePoint environments have huge volumes of documents, so this content type requires some thought. Later I’ll talk about our ‘subscriptions’ framework which gives a user the option of seeing documents for certain locations.
    • [Side note: the ECM content types appear in the first image above too, but only because our ‘activity publish’ code is working with the OOTB ‘presentation’ code – they wouldn’t show in a standard SP2010 installation.]
    • [Side note: my buddy Wes has the best reference on the internet on how to do this, including some code samples – see Extending the activity feed with enterprise content]
  • Richer activities:
    • The blog article in the images above is a great example – in the OOTB activity feed, users can only see the article title. In our version, you see the title and the first bit of the content - providing more context to help you decide whether the link is worth following.
    • Also check out the ‘Rich Browne added a new colleague item’ item. If he’s added a new colleague, it might be nice to show a picture of him.
  • Iconography – this makes it easier to quickly identify what an entry is about.
  • ‘Likes’ in the feed - under certain circumstances, ‘likes’ are published to the feed as an activity. See the first item in the second screenshot). More on this later.
  • ‘My activity’ view – this helps me find things I’ve published. For example, if I uploaded a PowerPoint presentation recently and know someone made an interesting comment, this view will help me find it again.
  • Status update bar – no longer any need to navigate off to my SharePoint profile page just to update my status.
  • Look and feel – subjective, but arguably prettier :)

Commenting/note board

Dialogue and conversation is the backbone of most social solutions, and no doubt you’re familiar with how it works on Facebook et al. But SharePoint 2010 already has the note board which allows comments/notes to be added on every document, page and list item – so isn’t that enough? Well, possibly not. For one thing, consider that we want to be able to comment on ‘social’ activities such as status updates, job title changes and so on. On a different tangent, the SharePoint 2010 note board architecture is effectively incompatible with a Facebook-style display of activities and comments hanging off them (as shown in our enhanced activity feed). This is because to fetch the set of comments for a feed containing 20 items, I have to make 20 individual calls to SocialCommentManager.GetComments() – which is clearly not going to perform or scale. So, we need a different approach.

In our case, we took the big decision of scrapping the standard note board. Some big considerations here include the migration path to vNext, but we concluded that to write some migration scripts (and migrate the volume of data we would have) would be completely in-line with the expectations of an upgrade project. So without the shackles of the OOTB design, it’s possible to go back to first principles and use whatever data schema fits best. The key is simply being able to retrieve all your likes/comments (and anything else) in one operation, for whatever page/web part designs you come up with. Nothing more to it. When implemented this way, it means that a user can add a comment to a SharePoint item either from the activity feed OR from the item’s note board, and such comments would show wherever I looked at the item. Notably, the way most ( maybe all?) 3rd party products deal with this is to side-step the issue completely – comments in the activity feed only show in the activity feed, and notes on the note board purely live on the note board – and never the twain shall meet. Our client wasn’t comfortable with this due to the huge overlap between the two concepts - the feeling was this disjointed behaviour could be confusing to users. We debated that our path could mean that some “noise” comments would be stored against items (“Dude, nice kitten photo LOL!”), but our client preferred this to the alternative.

So whilst we’re having to do work around the note board, we may as well give it some UI love. Rich, my partner in crime, decided that the native “show 5 items and do paging” approach was pretty poor in this enlightened age of Facebook infinite scrolling. Even weirder, did you notice that any user can delete another user’s note? Governance, who needs it?! Here’s a reminder of the out-of-the-box note board:

OOTB_noteboard_thumb1

Here’s what we ended up with:

TagsAndNotesWindow_thumb7

If I’d been logged in as one of the users above, then of course ‘Edit’ and ‘Delete’ links would show next to my notes as appropriate (i.e. we fixed the lack of security around ‘Delete’). Notice also that our dates are a bit more user-friendly than SharePoint’s ‘raw’ display of a date/time.

Note board web part

Consider also that in addition to the ‘tags and notes’ window, the SharePoint note board is also a web part which can be dropped on a page. This can be a great idea for a team site home page for example, so we ensured our note board had the same capability:

NoteBoardWebPart_thumb5De

Profile note board

In SharePoint 2010, the note board also appears on a user’s profile page and can be used to leave a note for that person. In that context, it looks like this:

OutOfBoxProfileNoteBoard800_2_thumb

This is definitely a useful piece in the social feature set - people are certainly getting used to sending messages using something other than e-mail. However, the lack of ‘reply’/threaded discussions frequently becomes an issue – you end up with the ridiculously disjointed situation of writing on each other’s profile in order to have a conversation. We thought it should be something more like this:

ProfileNoteBoard800-_2_thumb

If you ever want to do something similar, don’t forget the e-mail notifications which alert a user that a note has been left on their note board. Of course if you are doing something custom, you’ll have the opportunity to enhance the e-mail content and behaviour – for instance, not only sending a notification when someone posts on your note board, but also when someone replies to a conversation you’re involved in.

Entry points

So it’s great that we have this super-duper note board, but it’s also worth mentioning the experience to get to it. Since we already had much of the back-end code to support it, we thought we may as well display the number of likes/comments for the current page (or highlighted list item, or whatever). This is great for instantly seeing “interaction” around the current item, and probably encourages people to perform these social actions:

BadgeCounts_Liked2_thumb

These are basically direct replacements for the social controls added to the ribbon for ‘I like it’ and opening the tags and notes window.

Speaking of the ‘I like it’ functionality in SharePoint, let’s talk about how we deal with ‘likes’.

Likes – more important than you might think

SharePoint 2010’s ‘I like it’ is implemented as a social tag – this enables Microsoft to re-use the plumbing for that feature, and in some ways is a nice way of doing things. However, we had a different vision. For us/our client, the main point of liking was to “generate footfall” – in other words, to put content under the eyes of people who would not otherwise see it. Facebook does this. So if a colleague of mine “likes” some content which I haven’t seen in my activity feed because it was created by a non-colleague or in a site/list/library which I don’t have a ‘subscription’ to (N.B. ‘subscriptions’ are another custom piece, which I’ll talk about later), then I’ll see it in my feed:

LikeActivity_thumb3

So in the image above, Tristan Watkins is my colleague but David Bowman is not. We tossed around several ideas for ensuring a good signal-to-noise ratio here. You could definitely get quite sophisticated in terms of “keeping score” of which users have received likes for which content, and such conversations were a good reminder of the complex algorithms behind Facebook. However, simply ensuring I don’t see likes for content I’m already probably aware of was a good starting point. Remember that if the original activity (e.g. status update/document/whatever) appeared in my feed, then I’ll be able to see likes/comments “hanging off” it right there – but it’s the likes “broadcast” activities we need to be careful with. In the end, our implementation of comments also used the same “broadcast” logic – so I could see comments my colleagues were making on items I might not have seen.

The end result is pretty powerful. Our client had some examples across the business where liking brought people together (e.g. they went on to work together on some initiative which was a success for the company) – if you’re cynical about social, seeing this happen might change your mind.

In user-experience terms, of course we also need to be able to see who liked any particular item. We put that as a new tab in the existing ‘tags and notes’ window. Although not shown in this image, I would see ‘Add as colleague’ buttons (and presence icons) next to any people who I wasn’t already colleagues with, since we want to encourage connections across the business:

LikesDialog_thumb9

Subscriptions

If you’re familiar with the social aspects of SharePoint, you’ll know it’s all about colleague relationships. My activity feed gives me a view of what my colleagues are doing and helps me stay in touch. However, in the environment we were working in, this was considered a little “one-dimensional” - colleague relationships aren’t always ideal. What happens, for example, if I discover a great team site where the content is extremely interesting to my work. Let’s say it’s another part of the business working on SharePoint, and let’s say these guys are producing some great architecture documents. I probably don’t want to add all of the contributors to that site as colleagues – for one thing, I may not particularly want their status updates, birthdays, manager changes and so on. I probably do care if they tag documents with ‘SharePoint’ or related terms, but remember that in the out-of-the-box activity feed I can be notified of that simply by ‘following’‘ those tags. So what I really want is just to be notified if new content is created in the site. I guess I could use alerts, but frankly my inbox is busy enough and anyway, surely this is exactly the kind of stuff I occasionally dip into my activity feed for?

So, we created a subscriptions framework. If a user sees some content in SharePoint, they can use it to be notified in their activity feed about new items, without requiring colleague relationships. Users can subscribe at the site (web) level:

SubscribeSiteActionsButton_thumb2

..or they can subscribe to a particular list or library:

SubscribeRibbonButton_thumb2

SubscriptionConfirmation_Small_thumb

It’s also possible to subscribe to a site but exclude a “noisy” list or library. Once I have a subscription, I’ll see new content appear in my activity feed, and this includes documents. As I said earlier, we didn’t implement notifications for documents from colleagues because we felt that would result in noise, simply because documents are so prevalent. With subscriptions, it felt like the user would have more control. The image below shows a document in my activity feed – consider also that we now have multiple ways of things coming into my feed, so we added a context menu so that a user can understand why they were seeing a particular item:

ActivityFeed_ContextMenu_800

Group activity feed

In addition to the main enhancements to the activity feed, we also developed a team/group activity feed. We found this was a great web part to have on a team site home page – visitors to the site could then instantly see new content and other recent activity related to the site. This is a slightly tricky concept to implement with what SharePoint provides, but we were happy with our final design. In our model, this view shows recent activity by any of the owners/members of the site which is relevant to the site, and also any new content, including documents (i.e. as though you had a subscription to the site).

Summary

With SharePoint 2010, the direction of the product moved firmly towards the social arena – of course, the flexibility is there for organisations to opt-in to using this capability or not. However, amongst adopters it’s fairly common to want more than is provided out-of-the-box, and I’ve hopefully given some food for thought on what an enhanced version might look like.

Tuesday, 13 March 2012

SP2010 Continuous Integration–pt 6:Running UI tests as part of a build

The 6th article in our CI with SharePoint 2010 and TFS series is now live on the official SharePoint Developer blog (as mentioned on Twitter last week) – see Running Tests as Part of a Build. It took a while to write, and it took a while to publish (you’d think Mike Morton and his colleagues had actual products to ship or something) but it’s there now.

The article shows how to integrate tests into a build, particularly user interface tests via Visual Studio 2010’s Coded UI Tests. After all, if you’re going to continually check that what you’re putting together works, usually you’ll want to go beyond simply checking that it builds and deploys. You might choose to run unit tests, integration tests or UI tests (or most likely a combination), but UI tests seemed like a good place to start since they can slot into a build very easily.

As a side-note, running integration tests (unit tests which call the SharePoint API) within a build is not so straightforward (but pure unit tests can be done). I might discuss this in a future post.

Links:

TestSubscribeRibbon_RecordSteps

 

CodedUI_RunTest_Results

Thursday, 19 January 2012

WebPartAdderExtension – a better way of deploying web parts

Recently, that nice young man Wictor WilĂ©n blogged about WebPartAdderExtension and web part gallery sources – this is a little-known (and currently undocumented) capability in SharePoint which allows you to deploy custom web parts in a different way, with huge benefits. In summary, SharePoint 2010 provides a means for you to specify web parts to appear in the web part ‘picker’, without requiring a .webpart file in the local web part gallery (which is the usual route). Code is required, but SharePoint 2010 provides a ‘hook’ for you to add this in the form of the WebPartGallerySourceBase class. To help visualize this, the web part highlighted in the image below is deployed using WebPartAdderExtension – it appears just like a custom web part which uses a .webpart file and the user is blissfully unaware of any differences:

WebPartAdderExtension_WebPartSelection

I read Wictor’s two excellent articles, and on a recent project I decided to extend his sample into a model which I think works well for many scenarios. Now, I don’t want to be too dramatic, but I think WebPartAdderExtension in general is A Big Deal – I just can’t imagine going back to the old way of doing web parts at all. In this article we’ll first have a recap of what the problem is and why this new way is better, and I’ll then talk about my implementation and where you can download the files.

The problem

In a nutshell, web parts and the web part ‘gallery’ model in SharePoint are great, until you have to make a change to the definition of a web part that’s already out there. This usually takes the form of:

Client: “Hey can we change the title of ‘foo’ web part? Actually that name we came up with isn’t great and users aren’t finding it.”
Client: “Hey can we move ‘foo’ web part into a different category? That’s a quick change right?”

As a developer you say “Sure, I’ll get right on it!”. But then you realise that it’s deployed to everyone’s My Sites. And you have thousands of them. Or maybe it’s in team sites, but come to think of it you have quite a few of those too. This matters, because the web part is in each site collection’s web part gallery (in the .webpart file), and you’ll have to perform some operation to iterate every site and do some sort of processing on each one. Depending on how many sites you have to deal with, this could take could take significant time to run, and will place a certain amount of load on the infrastructure whilst it does. On my last project, it usually took around 7-8 hours to iterate the 25,000 My Sites we had, and we could only run it during a long maintenance window due to the load on both the server doing the processing and the database servers. Of course, 25,000 sites isn’t that big. In larger enterprises, I’ve even heard of folks writing custom multi-threaded applications to deal with such “iterate the sites” scenarios. I’m not saying you won’t need to occasionally perform this iteration for other reasons, but if we could take one common use case (i.e. changes to web part definitions) out of the equation, that’s got to be good in my book.

The solution

Consider that with WebPartAdderExtension, rolling out such a change to (say) 25,000 sites has the following characteristics:

  • No iteration required, therefore no load on servers
  • Takes immediate effect across all sites
  • Depending on your implementation, may not even require an app pool recycle!

How it works: WebPartAdderExtension is a Feature element (XML), and this can be defined at farm/web app/site collection scope (which is interesting in itself!). This ‘points’ to a class which derives from WebPartGallerySourceBase and in here you can do what you like – all you have to do is return a collection. Each item in the collection will then appear as a web part in the web part gallery. Your custom code is fired when a user browses for a web part (in page edit mode). In Wictor’s sample, he fetched items from a SharePoint list, meaning the pseudo web parts were actually list items rather than each having a traditional .webpart file.

 My implementation

Wictor’s sample conveyed the principle to me perfectly, but I ended up with a different model. Here’s what I came up with to meet our requirements:

  • A series of XML files, which define web parts by web application (e.g. an XML file for ‘teams’ web parts, an XML file for ‘My Site’ web parts, and so on).
    • N.B. It could be the nature of our web parts, but by web app was definitely the most appropriate factoring for us.
  • Code which reads the XML file for the ‘current’ web application
  • A web application-scoped Feature, which uses WebPartAdderExtension
    • In fact, several variations of this Feature are created, one per web app. The ‘teams’ web parts Feature uses WebPartAdderExtension to point to the class which knows about ‘teams’ web parts – this in turn reads from the ‘teams’ web parts XML file.

So rather than Wictor’s list-based sample, I’m happy to use XML files on the filesystem – when we specify the web parts which can be picked, we must specify the assembly/type name etc., and since this is in developer territory I’m comfortable with the fact that a WSP deployment is required to update this XML (the files live under ‘_layouts’). Somewhat surprisingly, SharePoint doesn’t cache this data so even in my file-based implementation, an app pool recycle is not required – just update the WSP (and ensure ResetWebServer = false) and the changes take effect immediately. Even if it DID require a recycle, remember this is still several million times better than a long-running process to iterate site collections.

Here’s my XML (custom schema) for defining the set of ‘teams’ web parts (using dummy web parts here, in my COB namespace): 

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <WebParts>
   3:   <WebPart
   4:     Title="My web part for team sites - deployed via WebPartAdderExtension"
   5:     Category="COB"
   6:     Description="A dummy web part for team sites"
   7:     ID="TeamsWebPart1"
   8:     Assembly="COB.SharePoint.WebPartAdderExtension, Version=1.0.0.0, Culture=neutral, PublicKeyToken=23afbf06fd91fa64"
   9:     Type="COB.SharePoint.WebPartAdderExtension.TeamsWebPart1.TeamsWebPart1"
  10:     IconUrl="_layouts/images/webpart.gif"
  11:     RibbonCommand=""
  12:     OnClientAdd=""
  13:     />
  14:   <WebPart
  15:   Title="My second web part for team sites - deployed via WebPartAdderExtension"
  16:   Category="COB"
  17:   Description="Another dummy web part for team sites"
  18:   ID="TeamsWebPart2"
  19:   Assembly="COB.SharePoint.WebPartAdderExtension, Version=1.0.0.0, Culture=neutral, PublicKeyToken=23afbf06fd91fa64"
  20:   Type="COB.SharePoint.WebPartAdderExtension.TeamsWebPart2.TeamsWebPart2"
  21:   IconUrl="_layouts/images/webpart.gif"
  22:   RibbonCommand=""
  23:   OnClientAdd=""
  24:     />
  25: </WebParts>

We then need some code to consume this. As discussed earlier, this must be a class derived from WebPartGallerySourceBase – since my implementation is ‘XML file per web application’, I created a base class which understands my XML schema and then a derived class per web app. It’s this latter class which knows where to find the XML file:

0: [Guid("98FF0ED8-6697-4E13-8BCC-161FEE3E4B0C")]
1: public class GlobalWebPartGallerySource_Teams : GlobalWebPartGalleryXmlSource

   2: {
   3:     public GlobalWebPartGallerySource_Teams(Page page)
   4:         : base(page)
   5:     {
   6:     }
   7:  
   8:     public override string XmlPath
   9:     {
  10:         get
  11:         {
  12:             string path = SPUtility.GetGenericSetupPath(@"template\layouts\COB.SharePoint.WebPartAdderExtension\GlobalWebParts\GlobalWebParts_Teams.xml");
  13:             return path;
  14:         }
  15:     }
  16: }

..and the base class simply reads from the chosen XML file and returns the collection (of GlobalWebPartGalleryItem instances in my case – the important thing is that it derives from WebPartGalleryItemBase):

   1: public class GlobalWebPartGalleryXmlSource : WebPartGallerySourceBase
   2: {
   3: public GlobalWebPartGalleryXmlSource(Page page)
   4:     : base(page)
   5: {
   6: }
   7:  
   8: public virtual string XmlPath
   9: {
  10:     get
  11:     {
  12:         return string.Empty;
  13:     }
  14: }
  15:  
  16: protected override WebPartGalleryItemBase[] GetItemsCore()
  17: {
  18:     List<WebPartGalleryItemBase> items = new List<WebPartGalleryItemBase>();
  19:     string path = XmlPath;
  20:  
  21:     if (File.Exists(path))
  22:     {
  23:         XElement root = XElement.Load(path);
  24:         var webParts = from wpDef in root.Elements("WebPart")
  25:                         select wpDef;
  26:  
  27:         foreach(XElement wp in webParts)
  28:         {
  29:             string title = wp.Attribute("Title").Value;
  30:             string category = wp.Attribute("Category").Value;
  31:             string description = wp.Attribute("Description").Value;
  32:             string iconUrl = wp.Attribute("IconUrl").Value;
  33:             string id = wp.Attribute("ID").Value;
  34:             string ribbonCommand = wp.Attribute("RibbonCommand").Value;
  35:             string onClientAdd = wp.Attribute("OnClientAdd").Value;
  36:             string assembly = wp.Attribute("Assembly").Value;
  37:             string typeName = wp.Attribute("Type").Value;
  38:  
  39:             items.Add(new GlobalWebPartGalleryItem(this, base.Page, title, category, description, iconUrl, id, assembly, typeName, onClientAdd, ribbonCommand));
  40:         }
  41:     }
  42:  
  43:     return items.ToArray();
  44: }

The key bit that links my GlobalWebPartGallerySource_Teams class above with my ‘teams’ web app, is a web app-scoped Feature, having the following elements:

   1: <Elements xmlns="http://schemas.microsoft.com/sharepoint/"> 
   2:   <WebPartAdderExtension 
   3:       Assembly="$SharePoint.Project.AssemblyFullName$" 
   4:       Class="$SharePoint.Type.98ff0ed8-6697-4e13-8bcc-161fee3e4b0c.FullName$"/> 
   5: </Elements>

Note that I’m using the tokenization support to refer to my class using the GUID attribute it’s decorated with. NOTE – when using such tokens the GUID *must* be lowercase (I kid you not). This is just one of a couple of places in Feature/VS schema that I’m aware of which have this requirement.

Also, I’ll let you into a teensy secret, and say that my implementation has a couple of lines of reflection code in GlobalWebPartGalleryItem – this was necessary to carry the title through to the web part instance which was added to the page. Entirely optional, but since this is such an infrequent action in the grand scheme of web server traffic, it’s completely worth it IMHO for a consistent user experience.

In the end, the act of adding the web part to the page is just how the user expects. If you download my sample (link at the end of this article), here’s what it looks like here:

WebPartAdderExtension_TeamSitesWebPart

XML which is NOT needed (and must be deleted manually from the VS project)

It’s worth pointing here that you have to explicitly DELETE items from your project to use this approach. Remember that Visual Studio/CKS:Dev just know about the ‘regular’ way of developing web parts, and when you select ‘Add New Item> Visual Web Part’, items are added to your project which you no longer need – specifically this is the .webpart file and the elements.xml file which provisions this into the web part gallery. I’ve not tested, but I’d assume you get an error or duplicate if you forget to remove these. For clarity, it’s these:

Elements.xml (automatically added by Visual Studio) when web part added to project:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <!-- DO NOT USE - THIS XML IS NO LONGER NEEDED WHEN USING WEBPARTADDEREXTENSION -->
   3: <Elements xmlns="http://schemas.microsoft.com/sharepoint/" >
   4:   <Module Name="MySitesWebPart1" List="113" Url="_catalogs/wp">
   5:     <File Path="MySitesWebPart1\MySitesWebPart1.webpart" Url="MySitesWebPart1.webpart" Type="GhostableInLibrary" >
   6:       <Property Name="Group" Value="Custom" />
   7:     </File>
   8:   </Module>
   9: </Elements>

MySitesWebPart1.webpart:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <!-- DO NOT USE - THIS XML IS NO LONGER NEEDED WHEN USING WEBPARTADDEREXTENSION -->
   3: <webParts>
   4:   <webPart xmlns="http://schemas.microsoft.com/WebPart/v3">
   5:     <metaData>
   6:       <type name="COB.SharePoint.WebPartAdderExtension.WebParts.MySitesWebPart1.MySitesWebPart1, $SharePoint.Project.AssemblyFullName$" />
   7:       <importErrorMessage>$Resources:core,ImportErrorMessage;</importErrorMessage>
   8:     </metaData>
   9:     <data>
  10:       <properties>
  11:         <property name="Title" type="string">My web part title</property>
  12:         <property name="Description" type="string">My web part description</property>
  13:       </properties>
  14:     </data>
  15:   </webPart>
  16: </webParts>

And that’s it.

Summary

Although the old way of making custom web parts available has worked for years, that doesn’t mean it’s worked well! It turns out there’s an alternative which, in my opinion, has better characteristics – no more having to iterate each site collection if you ever need to make a change to the definition of a web part which is already out there. When I mentioned to our client that I’d used this new approach for our latest web part, the response was “Wow. I think we should go back and switch ALL our custom web parts over to this model.” I think Wictor gets the kudos for bringing it to folks attention though – I’m just providing some icing on the cake :)

Download link

Download my WebPartAdderExtension sample project