Wednesday, December 5, 2007

Adding Group and Quick Add Groups to your custom webpart programmatically

I have seen that most people are doing this using the frontend, but if you have hundreds or thousands of sites, you might want to do this programmatically. I will not go in to creating the webpart itself, just listing it in the webpart gallery.

Create a new feature "MyCompanysCoolWebpart".  Paste the following in the the feature.xml file.

<?xml version="1.0" encoding="utf-8" ?>
<Feature Id="{Insert GUID here}"
  Title="MyCompanyCoolWebpart"
  Description="This is what this feature does."
  Hidden="TRUE"
  Scope="Site"
  xmlns="http://schemas.microsoft.com/sharepoint/">
<ElementManifests>
  <ElementManifest Location="elements.xml"/>
  <ElementFile Location="dwp\myCompanyCoolWebpart.webpart" />
</ElementManifests>
</Feature>

Nothing out of the ordinary here. We are just creating a feature that will be at the site level. Next create an elements.xml file and add the following:

<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Module Name="WebPartPopulation" Url="_catalogs/wp" Path="dwp" RootWebOnly="TRUE">
    <File Url="myCompanyCoolWebpart.webpart" Type="GhostableInLibrary" >
      <Property Name="Group" Value="MyCompany" />
      <Property Name="QuickAddGroups" Value=";#Middle Left;#" />
    </File>
  </Module>
</Elements>

Now, lets see what we did in the elements.xml file.  First we pointed the module URL attribute to "_catalogs/wp" since that is the location of the webpart gallery and we set RootWebOnly to TRUE since we only need this feature at the Root Web level. We then set two properties "Group" and "QuickAddGroups". 

Group is going to be the name of the group your webpart will be categorized under in the "Add Web Parts" dialog. 

 grouping

QuickAddGroups will display your webpart at the top of the "Add Web Parts" dialog as "Suggested Web Parts for yourZone". The value(s) of the QuickAddGroups attribute must be separated by ";#" (semicolon hash, without the quotes, of course). If your webpart's Quick Add Group is Top and you click "Add a Web Part" in the Top zone (like follows)

topzone

You will then see your webpart is suggested for that zone.

Suggested

One thing to keep in mind is that the QuickAddGroups is not the name of the webpart zone, but the value of the QuickAdd-GroupName attribute of the WebPartZone of your page layout.

<webpartpages:webpartzone runat="server" allowpersonalization="false" frametype="TitleBarOnly"
        id="TopZone" title=""
  <% $Resources:sps,LayoutPageZone_TopZone %>
    " orientation="Vertical"
    quickadd-groupnames="Top" quickadd-showlistsandlibraries="false"><ZoneTemplate></ZoneTemplate>
</webpartpages:webpartzone>

To deploy this feature you can put it together with your webpart in a solution, install this feature using stsadm commands, call the feature from within a custom site definition or staple this to a site definition.

Add a new Site Column to a Custom Site Content Type for existing sites

After modifying and redeploying your custom content type feature, run the following code to update your current site with the change.

SPWeb web = new SPSite("http://myCompany/sites/CompanyStuff/").OpenWeb();
//Content type to update
SPContentTypeId cTypeID = new SPContentTypeId("0x010100054553FEE9554f4182A551FC55544EEC");
//set the new field
SPField field = web.Fields["myCompanyNewField"];
//create a fieldLink to the new field
SPFieldLink fieldLink = new SPFieldLink(field);
//set the content type to update
SPContentType cType = web.ContentTypes[cTypeID];
//create a var to hold sealed value
bool bIsSealed = false;
//create a field collection for the currnet ctype
SPFieldCollection fieldCollection = cType.Fields;

//if the new field does not exist in this ctype, add it
if (!fieldCollection.ContainsField(field.Title))
{
//test for sealed cType, if so un-seal before making changes to the ctype
if (cType.Sealed)
{
bIsSealed = true;
cType.Sealed = false;
}

cType.FieldLinks.Add(fieldLink);

//if the ctype was sealed, we need to re-seal it
if (bIsSealed)
{
cType.Sealed = true;
}

cType.Update(true);

web.Close();
web.Dispose();



In the example, I know the content type exists in my site, if I was going to loop through many sites, I would test to see if the content type exists before proceeding.  Another thing to keep in mind is that, the new field being added must be added as a Site Column first.  To update existing sites with the new site column, see this



If the content type you are updating is Sealed or ReadOnly you must set Sealed and/or ReadOnly to false before trying to make any changes to the content type, just make sure to set them back to their original state before calling cType.Update().  In the above code I have only tested for Sealed.

Updating Custom Site Columns Feature for an existing Site

After updating and deploying your custom site column feature, simply deactivate and activate the custom feature on each existing site in order to get the new site column(s) to show up in the existing site. 

If you have a lot of sites or your site column feature is hidden you most probably want to do this programmatically.  The following code example will update an existing site with the new site column(s).

                //open site
SPSite site = new SPSite("http://myCompany/sites/CompanyStuff/");

try
{
//set the guid of the feature to be toggled
Guid g = new Guid("4055557A-9AB2-43b4-83EB-3B55555555EA");

//fetch and toggle
SPFeatureCollection featureCollect = site.Features;
//reomve the custom site column feature
featureCollect.Remove(g);
//add the custom site feature back and activate it
featureCollect.Add(g);
}
catch (Exception ex)
{
throw new Exception("Something went wrong: " + ex.Message);
}
finally
{
site.Close();
site.Dispose();
}