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();
}

Wednesday, July 18, 2007

Customizing the out of the box Collaboration Portal

Creating an entire Collaboration Portal definition, based on the out of the box definition, inside of SharePoint 2007 can be a very stressful thing to do. Once you get your head wrapped around what is really going on its quite simple.

By following the steps below, you will be able to replicate the out of the box portal defintion and then you will be able to customize from there.

Copy and rename the following directories listed below:

  • C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\SiteTemplates\SPS – rename "MYCOMPANYCOLLECTIONSITEDEFINITION"
  • C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\SiteTemplates\SPSNHOME – rename "myCompanyNews"
  • C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\SiteTemplates\SPSSITES – rename "myCompanySites"
  • C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\SiteTemplates\BDR – rename "myCompanyDocCenter"
  • C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\SiteTemplates\SRCHCEN – rename "myCompanySearchCenter"

Create a new WebTemp*.xml file

The new webTemp*.xml file should be located under here C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\1033\XML and name it WebTempMyCompany.xml

You can insert the following code:

<?xml version="1.0" encoding="utf-8"?>
<Templates xmlns:ows="Microsoft SharePoint">
<!--
MYCOMPANY Collections Site Template -->
<
Template Name="MYCOMPANYCOLLECTIONSITEDEF" ID="10500">
<
Configuration ID="0" Title="MYCOMPANY Collection Site" Hidden="FALSE"
ImageUrl="/_layouts/1033/images/template_corp_intranet.png"
Description="This is a collection site for MyCompany"
ProvisionAssembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
ProvisionClass="Microsoft.SharePoint.Publishing.PortalProvisioningProvider"
ProvisionData="xml\\MYCOMPANYCollectionWebManifest.xml"
RootWebOnly="TRUE"
DisplayCategory="MYCOMPANY"
Type="0"
VisibilityFeatureDependency="DF4041D0-9959-43fb-A408-8797632B51E7">
</
Configuration>
</
Template>
<!--
MYCOMPANY Collection Site Definition -->
<
Template Name="MYCOMPANYCOLLECTIONSITEDEFINITION" ID="10501" >
<
Configuration ID="0" Title="MYCOMPANY SharePoint Portal Server Site" Type="0" Description="This template is obsolete, but must be here!" Hidden="TRUE" ImageUrl="/_layouts/images/stsprev.png" DisplayCategory="MYCOMPANY" />
</
Template>
<!--
MYCOMPANY News Site -->
<
Template Name="MYCOMPANYNews" ID="10502" >
<
Configuration ID="0" Title="MYCOMPANY SharePoint Portal Server News Site" Type="0" Description="This template is obsolete, but must be here!" Hidden="TRUE" ImageUrl="/_layouts/images/stsprev.png" DisplayCategory="MYCOMPANY" />
</
Template>
<!--
MYCOMPANY Document Center Site -->
<
Template Name="MYCOMPANYDocumentCenter" ID="10503" >
<
Configuration ID="0" Title="MYCOMPANY SharePoint Portal Server Document Center Site" Type="0" Description="This template is obsolete, but must be here!" Hidden="TRUE" ImageUrl="/_layouts/images/stsprev.png" DisplayCategory="MYCOMPANY" />
</
Template>
<!--
MYCOMPANY Site Directory -->
<
Template Name="MYCOMPANYSiteDirectory" ID="10504" >
<
Configuration ID="0" Title="MYCOMPANY SharePoint Portal Server Site Directory" Type="0" Description="This template is obsolete, but must be here!" Hidden="TRUE" ImageUrl="/_layouts/images/stsprev.png" DisplayCategory="MYCOMPANY" />
</
Template>
<!--
MYCOMPANY Search Center -->
<
Template Name="MYCOMPANYSearchCenter" ID="10505" >
<
Configuration ID="0" Title="MYCOMPANY SharePoint Portal Server Search Center" Type="0" Description="This template is obsolete, but must be here!" Hidden="TRUE" ImageUrl="/_layouts/images/stsprev.png" DisplayCategory="MYCOMPANY" />
</
Template>
</
Templates>

As you will notice there are two different configuration element structures. The first one is the configuration that is going to build your Collaboration Portal.

  <Template Name="MYCOMPANYCOLLECTIONSITEDEF" ID="10500">
<
Configuration ID="0" Title="MYCOMPANY Collection Site" Hidden="FALSE"
ImageUrl="/_layouts/1033/images/template_corp_intranet.png"
Description="This is a collection site for MyCompany"
ProvisionAssembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
ProvisionClass="Microsoft.SharePoint.Publishing.PortalProvisioningProvider"
ProvisionData="xml\\MYCOMPANYCollectionWebManifest.xml"
RootWebOnly="TRUE"
DisplayCategory="MYCOMPANY"
Type="0"
VisibilityFeatureDependency="97A2485F-EF4B-401f-9167-FA4FE177C6F6">
</
Configuration>
</
Template>

Since the Collaboration Portal is using publishing, we must call the publishing assembly and provide provisioning data. The provisioning data file is where we will tell the portalProvisioningProvider what webs to create for our portal. More on that file later. The other three key items to take note of are the DisplayCategory, RootWebOnly and VisibilityFeatureDependency attributes.


  • DisplayCategory attribute is the name of the tab you want your definition to be located under on the _layouts/scsignup.aspx page.
  • RootWebOnly attribute is pretty much self explanatory, but set to TRUE and your definition will only be shown on the _layouts/scsignup.aspx page if a root site can be created.
  • VisibilityFeatureDependency attribute, only if the feature referenced there is turned on can the site be created. If the feature is not turned on your definition will not show up on the _layouts/scsignup.aspx page.

You will notice that there are several template elements where the Hidden attribute is set to TRUE, I have done this because every site definition you create needs a unique ID (above 10000) and I am only letting my users use those site definitions when they create a Collaboration Portal. If you set Hidden to False, your users will be able to create sites based on that single site definition alone.

ProvisioningData file:

This is the file that you will create your portal structure from. This file contains all the webs to be created. Create a new xml document under C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\XML and name it MYCOMPANYCollectionWebManifest.xml and insert the following code:

<?xml version="1.0" encoding="utf-8"?>
<portal xmlns="PortalTemplate.xsd">
<
web name="Home" siteDefinition="MYCOMPANYCOLLECTIONSITEDEFINITION#0" displayName="Home" description="MYCOMPANY Portal Home Site">
<
webs>
<
web name="News" siteDefinition="MYCOMPANYNews#0" displayName="$Resources:spscore,PortalManifest_News_DisplayName;" description="$Resources:spscore,PortalManifest_News_Description;" />
<
web name="SiteDirectory" siteDefinition="MYCOMPANYSiteDirectory#0" displayName="$Resources:spscore,PortalManifest_SiteDirectory_DisplayName;" description="$Resources:spscore,PortalManifest_SiteDirectory_Description;" />
<
web name="SearchCenter" siteDefinition="MYCOMPANYSearchCenter#0" displayName="$Resources:spscore,PortalManifest_SearchCenter_DisplayName;" description="$Resources:spscore,PortalManifest_SearchCenter_Description;" />
<
web name="Docs" siteDefinition="MYCOMPANYDocumentCenter#0" displayName="$Resources:spscore,PortalManifest_DocumentCenter_DisplayName;" description="$Resources:spscore,PortalManifest_DocumentCenter_Description;" />
</
webs>
</
web>
</
portal>

You will notice above in the structure you have a web element followed by a webs element followed by more web elements. You got it; it is telling the PortalProvisioningProvider to create a web with several sub webs. The SiteDefintion attribute contains the name of the site definition you want to use to create the web and the "#0" is the configuration you want to use in the onet.xml of that site definition. As a good practice, always use the "#n" even if you only have one configuration in the onet file.


The second configuration element structure does not contain the PortalProvisoningProvider therefore it will only create that single site.


Now you might be thinking, we’re done, let’s create a site... Nope, it will go boom, if you do so. So we still have several files to create and or modify. First we are going to look back at the WebTempMYCOMPANY.xml file and at the VisibilityFeatureDependency attribute.  This is calling the BaseSiteStapling feaure, this feature will staple several features to site definitions listed in that feature. For example it will staple the BaseSite feature to the SPS#0 site definition.  To get this to work with your new site definition you have two choices: You can either add reference to your definitions in C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\BaseSiteStapling\basesitestapling.xml or you can create a custom stapled feature, so you do not have to modify out of the box code, just incase a service pack overwrites your modifications.

To create your own custom BaseSiteStapling feature copy C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\BaseSiteStapling and rename it to MYCOMPANYBaseSiteStapling.  Modify the Feature.xml file by giving it a new GUID for it's ID.  You could also rename the xml file for the location attribute to mycompanybasesitestapling.xml, if you do this make sure you rename the actual file too.  Now edit the mycompanybasesitestapling.xml as follows:

<?xml version="1.0" encoding="utf-8" ?>
<
Elements xmlns="http://schemas.microsoft.com/sharepoint/">

<
FeatureSiteTemplateAssociation Id="B21B090C-C796-4b0f-AC0F-7EF1659C20AE" TemplateName="MYCOMPANYDocumentCenter#0" />
<
FeatureSiteTemplateAssociation Id="B21B090C-C796-4b0f-AC0F-7EF1659C20AE" TemplateName="MYCOMPANYCOLLECTIONSITEDEFINITION#0" />
<
FeatureSiteTemplateAssociation Id="B21B090C-C796-4b0f-AC0F-7EF1659C20AE" TemplateName="MYCOMPANYCOLLECTIONSITEDEF#0" />
<
FeatureSiteTemplateAssociation Id="B21B090C-C796-4b0f-AC0F-7EF1659C20AE" TemplateName="MYCOMPANYNews#0" />
<
FeatureSiteTemplateAssociation Id="B21B090C-C796-4b0f-AC0F-7EF1659C20AE" TemplateName="MYCOMPANYSiteDirectory#0" />
<
FeatureSiteTemplateAssociation Id="B21B090C-C796-4b0f-AC0F-7EF1659C20AE" TemplateName="MYCOMPANYSearchCenter#0" />

<
FeatureSiteTemplateAssociation Id="99FE402E-89A0-45aa-9163-85342E865DC8" TemplateName="MYCOMPANYDocumentCenter#0" />
<
FeatureSiteTemplateAssociation Id="99FE402E-89A0-45aa-9163-85342E865DC8" TemplateName="MYCOMPANYCOLLECTIONSITEDEFINITION#0" />
<
FeatureSiteTemplateAssociation Id="99FE402E-89A0-45aa-9163-85342E865DC8" TemplateName="MYCOMPANYNews#0" />
<
FeatureSiteTemplateAssociation Id="99FE402E-89A0-45aa-9163-85342E865DC8" TemplateName="MYCOMPANYSiteDirectory#0" />
<
FeatureSiteTemplateAssociation Id="99FE402E-89A0-45aa-9163-85342E865DC8" TemplateName="MYCOMPANYCOLLECTIONSITEDEF#0" />
<
FeatureSiteTemplateAssociation Id="99FE402E-89A0-45aa-9163-85342E865DC8" TemplateName="MYCOMPANYSearchCenter#0" />

</
Elements>

There are two other stapled features you have to modify in the same way. 



  • PublishingStapling
  • PremiumSiteStapling

Follow the same steps above and keep the following in mind:


  • SPS#0 = MYCOMPANYCOLLECTIONSITEDEFINITION#0
  • SPSNHOME#0 = myCompanyNews#0
  • SPSSITES#0 = myCompanySites#0
  • BDR#0 = myCompanyDocCenter#0
  • SRCHCEN#0 = myCompanySearchCenter#0
  • SPSPORTAL#0 = MYCOMPANYCOLLECIONSITEDEF#0

    At this point the Collaboration Site Definition you created will work, it's up to you to modify the onet.xml files for each site definition and create and/or modify the out of the box master pages, page layouts, content types, site columns...

  •