Using a hyperlink in the group description of a SharePoint group

January 22nd, 2007 by tonstegeman

In one of the comments on this blogpost on SharePoint sitegroups, I was asked if it is possible to set a hyperlink in the group description, just like SharePoint does out of the box. If you create a new site called “Team”, that site has a sitegroup called “Team Members”. The desciption for that site is: “Use this group to give people contribute permissions to the SharePoint site: Team”. The last part is a link to the site, as you can see in the screenshot below.

Sitegroupdescription1

The question how to programmatically set this link in the description. The problem with this is that the Description property is a normal string property, that does not have the HTML value that is displayed above. The trick here is not to use the SPGroup object, but set the property directly on the listitem in a list called “User Information List. This is the list you are looking at when you are viewing People and Groups. Just read the comment in the screenshot above: “There are no items to show in this view of the "User Information List" list.”.

All site groups (and users) are stored in this list, that is available in the rootweb of your site collection. I have extended the code of the previous example, so that it now also sets the description of the SPGroup to have a nice description with a hyperlink to the site it is used for.

 

    // Initialize
    string customerName = "Contoso";
    string accountManager = @"office2007jsmith";
 
    // Get the site and the account manager for the new customer site
    SPSite site = new SPSite(string.Format("http://office2007:13145/customers/{0}", customerName));
    SPWeb web = site.OpenWeb();
    SPUser owner = web.AllUsers[accountManager];
 
    // Get the site group
    string groupName = string.Empty;
    SPGroup group = null;
    string groupAssociations = string.Empty;
    groupName = string.Format("{0} Customer Team", customerName);
    group = GetSiteGroup(web, groupName);
 
    // Add the group if it does not yet exist
    if (group == null)
    {
        web.SiteGroups.Add(
            groupName, owner, null,
            groupName);
        group = GetSiteGroup(web, groupName);
    }
    // If the group was created, set the Visitor reference in the site property bag
    // and set the description for the site group.
    if (group != null)
    {
        web.Properties["vti_associatevisitorgroup"] = group.ID.ToString();
        groupAssociations = string.Format("{0};", group.ID.ToString());
 
        // Get the userlist from the rootweb
        SPWeb root = site.RootWeb;
        SPList userInformation = root.Lists["User Information List"];
 
        // Construct and submit the query to find the qroup
        SPQuery query = new SPQuery();
        query.Query = string.Format("<Where><Eq><FieldRef Name='ID'/>
            <Value Type='Text'>{0}</Value></Eq></Where>", group.ID.ToString());
        SPListItemCollection items = userInformation.GetItems(query);
 
        // Update the item if it was found. 
        if ((items != null) && (items.Count == 1))
        {
            SPListItem item = items[0];
            item["About me"] = string.Format("Use this group to add people to the customer
                support team for site: <a href="{0}">{1}</a>", web.ServerRelativeUrl, customerName);
            item.Update();
        }
    }

In the last bit of this code example, a reference to the RootWeb of the site collection is set. This SPWeb has a SPList object called “User Information List”. We can find the correct item in this list using the ID of the SPGroup object we just created. We create a SPQuery object and run that against the list. This returns 1 SPListItem object. The description for the site group is stored in a field called “About me” (The internal name of this field is “Notes”). After updating the item and running the code, we now have set the description as we wanted:

Sitegroupdescription2

Copy / Migrate a SharePoint site to another farm by using a SQL Server backup

January 17th, 2007 by tonstegeman

I came across this TechNet article when I was searching for ways to copy one SharePoint 2007 web application with all site collections to another farm. In our case we used the steps described under “Migrating using SQL Server Tools” to move databases across farms. In our customer environments (we have a mix of beta2, beta2TR and RTM), the normal backup/restore options did not work. Exporting the site using STSADM also failed. But by using this technique it was very easy to do it.

And it turned out that it is also a pretty nice and clean way to migrate sites gradually to the RTM version. Here are the steps (described very briefly):

  • Create a SQL backup of the content database(s) of the web application you want to copy/migrate
  • On the destination server, restore the backup in a new database(s)
  • Create a new web application on the destination server (use the default database name proposed by sharepoint)
  • Detach the database
    • Go to Application Management – Content databases
    • Select the correct web application
    • Set the database status to offline
      Backuprestore1
    • Click Add a content database
    • Enter the correct details about your restored database and click OK
    • That’s all

When I was copying a content database from a Beta2–TR server farm to a server farm running the release version, I got this message when I attached the restored database:
Attaching this database requires upgrade, which could time out the browser session.  You must use the STSADM command 'addcontentdb' to attach this database.

Instead of using the user interface I now added the new content database by using STSADM (replace my sample parameters between [ and ]):

stsadm -o addcontentdb -url [http://office2007:85] -databasename [WSS_Content_85] -databaseserver [OFFICE2007OfficeServers]

After running the command I got the message “Operation completed successfully” and my site was up and running in RTM. Please note that you can only use supported upgrades in this scenario; you can go from beta-2 to beta-2 technical refresh and from beta-2 TR to RTM.

SharePoint 2007 and cross site groups???

January 17th, 2007 by tonstegeman

When I was preparing for beta exam 70–541 I studied all items in the preparation guide. The last item confused me a bit. You should know how to “Add a cross-site group to a site group on different site”. I am confused, because I thought cross site groups no longer existed (or all SharePoint groups are considered to be cross site). The beta exam however, had a few questions about this subject. This made me a bit curious how this works in SharePoint 2007, so I started to look into it. But now I am even more confused…..

If you look at the object model, a SPWeb object has 2 properties:

  • Groups – this returns all cross site groups for the site (according to the SDK)
  • SiteGroups – this returns all cross site groups for the site collection.

I created a testprogram that saves the Xml of these groups to a file to compare the differences between the 2.

  • Both properties return the same groups in the document center in a new portal
  • When I create a new group (using the UI) in the document center, it shows up in SiteGroups.
  • When I use “Set Up Groups” in the document center People and Groups to create a new group, it also shows up in SiteGroups.
  • When I setup the document center to have unique permissions and create a new group, it shows up both in Groups and in SiteGroups.

I is not very clear to me what exactly the difference is between cross site groups for a site and cross site groups for a site collection. In the SharePoint UI there appears to be no difference. Anyone who knows how this works?

And if you are also preparing for the exam, this blog by Ishai Sagi contains more usefull information.

Creating a custom search page for searching a specific search scope in SharePoint 2007

January 9th, 2007 by tonstegeman

The search options in MOSS 2007 now have scopes. By using a search scope users of your search pages can easily just search in specific parts of the content. To create a scope, you define rules. These rules have a ‘source rule type’, which can have 3 values:

  • url (‘scope’ the search results based on url)
  • propery query (‘scope’ the search results based on the value of a property (a property restriction)
  • content source (‘scope’ the search results based on the content source they are part of)

In our intranet we have added a field called ‘InformationType’ to all content that we upload. All our sites that are ‘Knowledge’ related, automatically classify the content in it as Knowledge. By using a scope we can make it much easier for our users to just search in knowledge related content. After that we created a custom search page, specific for knowledge that just searches in ‘Knowledge’.

Here’s a quick how-to (especially the last step took some time for me to find out….) The trick is that the Search Core Results webpart has a property called “Scope”, which is hidden in the Miscellaneous section, which is not where I expected it…. Here you can manually enter the name of the scope you want to use.

Here’s a brief How-To:

  1. Create a sitecolumn called InformationType and add at least 2 different values
  2. Attach the sitecolumn to some libraries or lists
  3. Upload/Add some content and select at least 2 different values
  4. Do an incremental crawl of your search index
  5. Create a managed property:
  • Go to the “Metadata property mappings” page (see search options in the SSP)
  • Click “New Managed Property”
  • Enter “InformationType” as the name
  • Click “Add Mapping” and search for your InformationType property (this is why you need to do the crawl in step 4, otherwise it won’t show up).
  • Check “Allow this property to be searched in scopes”
    Search1
  • Run a full crawl on your index
  • Go to “View Scope” in the search settings on your SSP and create a new scope
  • In the Scope overview click “Add Rule” in the “Updates Status” column
  • Set the Scope Rule Type to “Property Query” and add the appropriate property restriction (InformationType equals “Knowledge”)
    Search2
  • Go back to the search settings and click “Start updates now”.  The scopes have to be compiled before they work.
  • Add the InformationType to your scope dropdown (this is an easy one to forget):
    • Go to the top level site settings
    • Click “Search scopes” in the site collection administration setting
    • Click on the link “Search Dropdown” (name of a display group)
    • Check the checkbox for your new “Knowledge” scope
      Search3
    • Get a coffee (it actually takes some time before the scope appears in the dropdown….)
    • Test if the scope works
  • Add a new page (use a page layout that can have webparts) to your site
  • Or alternatively add a new search tab to your search center
  • Add the “Search Box” and the “Search Core Results” webparts to your page
  • In the Miscellaneous section of the Search Box, set the value for “Target search results page url” to your new page (which in my demo case is the homepage of and ordinary WSS teamsite).
    Search4
  • Set the “Scopes dropdown” mode to “Do not show scopes dropdown”
  • And the last one (which took me some time to figure out….): Set the Scope value in the Miscellaneous section to “Knowledge”.
    Search6
  • Test your page:
    Search7

    Users now never have to think about in what content they are searching, they just search “Knowledge” Their search results are just documents from the knowledge collection, instead of all being mixed with all other content that is out there.

     

  • SharePoint 2007 SiteGroups – part 3 – Setting permissions in code

    November 7th, 2006 by tonstegeman

    This is the last part in a small series on MOSS sitegroups.

    Part 1: The basics

    Part 2: Creating SiteGroups in code

    This last part is about setting the permissions for our custom created sitegroups from code.

    The first thing to do is to break the permission inheritance for our site.If our site (SPWeb) does not yet have unique permission, we’ll break it:

        if (!web.HasUniqueRoleAssignments)
        {
            web.BreakRoleInheritance(false);
        }

    Next thing to check is if our custom Permission Level called “Account Manager” is already available. This permission level will be used to give our account managers a bit more permissions than the Contributor level, but not Full Control. Please note that these permission levels are called “Roles” in the object model. This snippet checks if our permission level is available.

        SPRoleDefinitionCollection roles = web.RoleDefinitions;
     
        // Try to find our custom Account Manager role.
        SPRoleDefinition role = null;
        for (int i = 0; i < roles.Count; i++)
        {
            if (roles[ i ].Name == "Account Manager")
            {
                role = roles[ i ];
                break;
            }
        }

    If the “role” object remains unassigned, we’ll need to create our role. The first thing to do now is break the inheritance for the permission levels. Just as permission inheritance, the permission levels are inherited from the parentweb. The best way would be to create the permission level at the top level and not break the inheritance, but for demo purposes, I’ll show how to do this. Please not that your web needs to have unique permission to be able to break the inheritance for the roledefinitions.

        if (role == null)
        {
            if (!web.HasUniqueRoleDefinitions)
            {
                // Break the inheritance of role definitions
                web.RoleDefinitions.BreakInheritance(true, false);
            }
            role = new SPRoleDefinition();
            role.Name = "Account Manager";
            roles.Add(role);
        }

    After creating the roledefinition, we’ll set the permissions for our custom permission level. This sample just adds 3 permissions (to keep the sample short):

        role.BasePermissions = SPBasePermissions.ManageWeb | SPBasePermissions.ManageSubwebs | SPBasePermissions.ManageLists;
        role.Update();

    After setting up the permission levels, we will grant our account managersgroup the correct permissions. First we’ll find out which SharePoint group is our account managers group by using the property bag of the site (see part 2). Using this group, the code sample create a new SPRoleAssignment and adds the SPRoleDefinition we just created to that. The role assignment is added to the RoleAssignments for the SPWeb:

        // Set permissions for Account Managers
        groupID = int.Parse(web.Properties["vti_associateownergroup"]);
        group = web.SiteGroups.GetByID(groupID);
        if (group != null)
        {
            SPRoleAssignment assignment = new SPRoleAssignment(group);
            SPRoleDefinition role = web.RoleDefinitions["Account Manager"];
            assignment.RoleDefinitionBindings.Add(role);
            web.RoleAssignments.Add(assignment);
        }

    After running our workflow (see part 1 for a description on the process), we have create a new site, created custom sitegroups for that site and set the permissions for that site.

    Sitegroups9

    By using some simple pieces of code, we have made our lives much easier. Creating and configuring sites for our customer teams now is much easier, quicker and always using the same model.

     

    Adding webparts as Suggested Webparts to your pages in SharePoint 2007

    November 7th, 2006 by tonstegeman

    In SharePoint 2007 you can select which webparts you want to appear in the “Add Web Parts” dialog as the suggested webparts. If you set the search center page in edit mode and click the “Add a Web Part” link, you will see the “Suggested Web Parts for bottom zone” being populated with just search webparts:

    Webparts1

    This is configured in the web part gallery. If you edit the properties of a webpart registration, you can enter one or more Quick Add Groups:

    You can select one or more of the standard groups, or set up your own group(s).

    Webparts2

    The last step is to let the page layout know which group to show as the suggested webpart group. Open your page layout in SharePoint Designer and search for the WebPartZone. This control has an attribute called “QuickAdd-GroupNames”. In case of the search example this is set to “Search”.

    SharePoint 2007 SiteGroups – part 2 – Creating SiteGroups in code

    October 18th, 2006 by tonstegeman

    This is the second article in a series (of 3) about SharePoint sitegroups. In part 1 I discussed how you can manage these site groups by using the MOSS interface. I also introduced the scenario that we will build on in this item. After creating a special teamsite for each of our customers, we configured the site groups for this customer site. This was quite a few mouse clicks. Takes a lot of time to configure this manually. Also we want to standardize the naming of these groups across all sites that have the same site definition. That is hard to do manually. And we prefer to give our account managers a self-service system to create and maintain their customer sites. Explaining them how to configure sitegroups and permission levels is not going to work.

    We created a workflow that does all this work for us. This workflow is triggered by editting or creating an item in a list with customer data. This workflow is part of what we call our “SiteBuilder”. In this post I will show how you can create the same sitegroups and configure the QuickLaunch in the site to show them.

    First we’ll get a reference to the site and we will try to find the account manager:

                string customerName = "Contoso";
                string accountManager = @"office2007jsmith";
     
                SPSite site = new SPSite(string.Format("http://office2007:90/customers/{0}", customerName));
                SPWeb web = site.OpenWeb();
                SPUser owner = web.AllUsers[accountManager];

    Then we generate the name for the Customer Team group. In this case this will be “Contoso Customer Team”. We will check if the group already exists. If it doesn’t, we’ll add it. The Account Manager will be the owner of the group.

    Update 21-01-2007: Please read this item if you want to set a hyperlink in the group description, just like SharePoint does out of the box.

                string groupName = string.Empty;
                SPGroup group = null;
                string groupAssociations = string.Empty;
     
                groupName = string.Format("{0} Customer Team", customerName);
                group = GetSiteGroup(web, groupName);
                if (group == null)
                {
                    web.SiteGroups.Add(
                        groupName, owner, null,
                        string.Format("{0} customer team", customerName));
                    group = GetSiteGroup(web, groupName);
                }

    The owner is the second parameter of the Add. This is a SPMember object, so it can be both a user and a site group. Being the owner of the groups, the Account Manager can manage the membership of these group. He can grant people access to his site, by adding them to the groups, depending on their role. They therefore do not have the “Manage Permissions” permission on their sites, but they can manage the membership themselves. And we keep the nice clean, clear, standardized security setup for all our customer sites.

    The function GetSiteGroup that you saw above is just a helper function to test if a group exists.

            private SPGroup GetSiteGroup(SPWeb web, string name)
            {
                foreach (SPGroup group in web.SiteGroups)
                    if (group.Name.ToLower() == name.ToLower())
                        return group;
                return null;
            }

    If you get it straight from the collection, you get an error when it doesn’t exists.

    The next step is to setup the SiteGroup we just created as the “Visitors” sitegroup for our site. By doing this you will see this group appearing as “Visitors to this Site” when you go to “Set Up Groups”. It took somedigging around before I found where this is stored. It turned out to be the property bag of the SPWeb. If you are interested in the property bag, please check this weblog by Serge van den Oever. The property bag has 3 properties for these groups:

    • vti_associatevisitorgroup
    • vti_associatemembergroup
    • vti_associateownergroup

    To set one of the values, use this line of code:

            web.Properties["vti_associatevisitorgroup"] = group.ID.ToString();

    For the other 2 groups the process is exactly the same. For the Account Managers group we will make 1 difference. We will already add our account manager to the group, which gives him/her direct access to the site after it is created. This can be done when creating the group by using the defaultUser parameter (an SPUser object):

            web.SiteGroups.Add(
                  groupName, owner, owner,
                  string.Format("e-office account managers team for {0}", customerName));

    The last step is to configure the site to show our 3 custom groups in the quick launch when users navigate to “People and Groups”. Once again this is stored in the propertybag. This has a property called “vti_associategroups”. This contains a string value with the ID’s of the groups that you want to show, separate by “;”.

            web.Properties["vti_associategroups"] = "23;24;25";
            web.Properties.Update();

    Please don’t forget to call the Update of the property bag after you updated any of these properties, otherwise they will not be persisted.

    If all is well you end up with 3 new sitegroups, that are attached to the site, accessible from the People and Groups menu and can be managed by the account manager:

    Sitegroups8

    I have only shown the most important pieces of the code here. This should be enough to get you started. Next time I will show how to set the permissions for these groups.

     

    SharePoint 2007 SiteGroups – part 1 – the basics

    October 14th, 2006 by tonstegeman

    This post is the first in a small series. This will be about SharePoint sitegroups in MOSS 2007. It is based on the usage of sitegroups in the “Collaboration Portal” site template. This is the site template that you typically use as a starting point for an intranet. If will first describe how these groups and their permissions are set up. After that I will describe a scenario where we have customer team sites, and their security setup.

    When you create a new site collection, the top level site automatically gets a number of new sitegroups. You can see these groups, if you go to your top level site, select Site Actions and then click People and Groups.

    This screen shows all available sitegroups and their members. The most important ones for our site are:

    • <site_name> Visitors
    • <site_name> Members
    • <site_name> Owners

    These 3 groups are actually tied to the site itself. This connection can be seen when you are in People and Groups and you click the Set Up Groups menu item from the Settings menu.

    As stated in the comments, the Visitors group has “Read” permissions. This means that this SiteGroup has been assigned the “Read” permission level. Members has “Contribute” and Owners has the “Full Control” permission level. These Permission Levels are new in MOSS 2007. Basically they are a set of permissions grouped together. “Read” for example is a set of 11 permissions (like View Items, Open Items, Create alerts, etc.). If you want to know more about permissions and permission levels, you should check this post by my coworker Henk Hooiveld (yes, we still work on the same project).

    The fact that you see these groups here does not actually mean that they have the permissions that are described in the comments. This is configured in the site’s permissions. Click on “Site Permissions” in the Groups Quick Launch, or click Site Actions – Site Settings – Modify All Site Settings – Advanced permissions.

    Sitegroups3

    Here you see our 3 site groups and the permission levels that they are assigned. These permissions are inherited to all subsites. On any of these subsites you can setup the 3 groups and connect them to the site.

    In my scenario we have a site called “Customers”. This site is a parent site for all our customer sites. Each customer site is a teamsite. We setup our permissions using the same model. We also have 3 usergroups that we want to add:

    • <customer name> Customer Team (these are our customers who have read access to the site)
    • <customer name> Customer Support Team (our internal support team for this customer; contribute access to the site)
    • <customer name> Account Managers (they have full control over their site) 

    In our customer site, go to People and Groups and click Set Up Groups from the Settings menu. Here you can directly create 3 new sitegroups by clicking the “Create a new group” radiobuttons. In the same screen you can directly add members to these groups. You don’t need to add members, except for the Owners group. This group has to have at least 1 member. If you select Create a new group, the group will by default be named “<sitename> Visitors”.

    Sitegroups4

    If you read the warning at the top of the screenshot above, you will see what we just have seen. The fact that we now have configured the 3 groups for our site, does not mean that we have set the permissions. We should do this by adding these groups to the parent site (as is suggested) or by breaking the permission inheritance and create unique permissions for our site. The last option is what we want. If we don’t do anything, our groups are created, but adding people does not help, because permissions are not set.We do this by clicking Site Permissions. In the Actions menu we select Edit Permission. SharePoint asks us if we are sure and we end up with unique permissions for our site. By doing so the parent permissions are copied to our site. We delete all our existing permissions and add our custom groups that we have just created. Deleting can be done by selecting all items and selecting “Remove User Permissions” from the Actions menu. By clicking Add Users in the New menu, we can add our groups. (it is called Add users, but you can also add groups). Select the group and select “Give users permission directly”. Select the appropriate permission level and click OK.

    Sitegroups5

    After configuring our 3 sitegroups we end up having this permission setup:

    Sitegroups6

    This is a little off topic, but because we now have unique permissions on our sub site, we can also setup unique permission levels in our site. In the screenshot above you see we now have a Settings menu in the Site Permissions screen. If you select the option Permission Levels, you can create unique permission levels for your site. Nice option, but think about it carefully if you really want this. Things can get very complicated by using unique permission levels.

    The last bit that I wanted to show is how you can configure you site to display just the 3 custom groups in the quick launch when you go to People and Groups in the customer site. This makes it much easier for our account managers to manage the permissions by adding people to the groups. Otherwise they would have to scroll through all site groups to find their specific groups. Although we created the site groups directly from our site, they are created in the top level site. In People and Groups in the customer site, open the Setting smenu and select “Edit Group Quick Launch”. This gives you a box where you can select our 3 custom groups. The rest will be available through the “More” link. After selecting the correct groups, this looks like this: (instead of showing all groups, with the 3 most important for this site somewhere in the list)

    Sitegroups7

    As we have seen in this post, SharePoint offers a nice way to setup a security model (using permission levels and sitegroups) and make it easy to manage the membership of these groups easily from the site that uses them. Configuring this however is a lot of work, especially when you want to do this for a lot of sites in the same way. In the next post I will describe how you can create these sitegroups from code.

    Using a SharePoint 2007 content type in a List definition

    September 29th, 2006 by tonstegeman

    In previous posts I showed you how you can create site columns and content types by using SharePoint features. In this post I will describe how you can use this new contenttype in a custom list definition. We will create a new site column called "Region" and add that to the existing out of the box "Task" content type. After that we will create a new list template for our custom Task definition and add that to a new site definition. In our new sites, we will end up having a taks list with a "Region" field.

    I will not describe how you create, deploy and active features, please see my previous posts for that.

    These samples were built using beta2-TR. I tried re-using my beta2 samples, but I found some resource strings that were change going to TR, so I ended up recreating everything.

    Step 1 – Create a site column for the Region field.

    Create a new folder in the SharePoint FEATURES folder called "MyFields". Add the XML that defines the site column to a new xml file called MyFields.xml. Add a feature.xml file and reference Myfields.xml from the feature.xml. Activate the feature using STSADM.

    The XML for the site column that we are using:

    
    
    <?
    
    xml version="1.0" encoding="utf-8"?>
    
    <
    
    Elements xmlns="http://schemas.microsoft.com/sharepoint/">
    
    <
    
    Field
    
    ID="{9efcfb6d-138c-4e53-a5b1-8c05a59f0256}" Name="Region" Group="My Metadata Model" Type="Choice" DisplayName="Region" SourceID="http://schemas.microsoft.com/sharepoint/v3/fields" StaticName="Region" Description="" Sealed="TRUE" Required="TRUE" AllowDeletion="FALSE" FillInChoice="FALSE"> < CHOICES>
    <
    CHOICE>ASPAC</CHOICE> < CHOICE>Americas</CHOICE> < CHOICE>EMA</CHOICE> </ CHOICES> < Default>EMA</Default> </ Field> </ Elements>
     

    Please see this post for detailed instructions.

    Step 2 – Create a content type for our custom Task

    In the SharePoint FEATURES folder, create a new subfolder called "MyContentTypes". Add a feature.xml file and create a file for the ElementManifest called "MyContentTypes.xml". Add the xml below to this file to create the content type. Activate the feature using STSADM.

    <?xml version="1.0" encoding="utf-8"?>
    
    <
    
    Elements xmlns="http://schemas.microsoft.com/sharepoint/">
    
    <
    ContentType
    ID="0x010810" Name="My Task" Group="My Content Types" Description="My Content Types for testing purposes" Version="0" V2ListTemplateName="tasks"> < FieldRefs>
    <
    FieldRef
    ID="{9efcfb6d-138c-4e53-a5b1-8c05a59f0256}" Name="Region" Required="TRUE" /> </ FieldRefs> </ ContentType> </ Elements>
     
    
    The ID of our new content type is "0x010810". This means that our content type inherits from contenttype "0x0108", which is the out of the box "Task". In the FieldRefs element you will find a reference to the Region site column we just created. Make sure the ID's (guid) are equal.
    
    This post has all the detailed instructions.
    
    Step 3 - Create a feature that creates and registers the list template
    
    In this step we create a new schema file for our custom list and we will register a new ListTemplate for our custom list. In SharePoint 2003 you used to do this in the onet.xml file for each site definition that needed the custom list. The SharePoint 2007 solution is much cleaner. You just create the list template and it's registration in a feature and activate this feature in the site definitions that need it. The result is that we have central management of our list template, instead of in a number LISTS folders across the file system.
    
    First create a new folder in the FEATURES folder called "MyTasksList". The easiest way to do this, is copy the out of the box "TasksList" folder. Edit the "Tasks.xml" file in the "ListTemplates" folder. This xml will look familiar for those of you who created custom list templates in SharePoint V2. My xml looks like this:
    
    
    
    <?
    
    xml version="1.0" encoding="utf-8"?>
    
    <
    
    Elements xmlns="http://schemas.microsoft.com/sharepoint/">
    
    <
    
    ListTemplate 
    
    Name="tasks" Type="5500" BaseType="0" OnQuickLaunch="TRUE" SecurityBits="11" Sequence="360" DisplayName="My Task" Description="My Task for testing purposes" Image="/_layouts/images/ittask.gif" /> </ Elements> 
     

    The most important change is the new "Type" value. The value is important, because you will need it later.

    The subfolder "Tasks" contains the schema file for our custom list. Edit this file and search for the "<ContentTypes>" element. You will find a ContentTypeRef element for the default Task content type. Change this to our custom content type:

    
    
    <
    
    ContentTypes>
    
    <
    ContentTypeRef ID="0x010810">
    <
    Folder TargetName="Task" /> </ ContentTypeRef> < ContentTypeRef ID="0x0120" /> </ ContentTypes> 
     

    The next thing that you will have to do is not very logical in my opinion. You probably end up thinking 'why am I doing this????', but it is the only way to get it working. What you will need to do is register a new <Field> element in the <Fields> element, that is an exact copy of the <Field> element in the site definition of Step 1. This seemed a bit strange to me the first time I needed to do this. After all, we registered our custom content type, that knows which fields we have. I guess that we need this because the views are not part of a content type, and therefore the list schema needs to have the field definitions that can be registered in a view directly from the schema. If you don't do this step, you end up with a custom list that has the custom content type attached to the list, but does not have your custom fields. If you manually attach the content type to the list, it will work correctly. To get around it you have to add your fields to the schema:

    
    
    <
    
    Field 
    
    ID="{9efcfb6d-138c-4e53-a5b1-8c05a59f0256}" Name="Region" Type="Choice" DisplayName="Region" SourceID="http://schemas.microsoft.com/sharepoint/v3/fields" StaticName="Region" Description="" Sealed="TRUE" Required="FALSE" AllowDeletion="FALSE" FillInChoice="FALSE"> < CHOICES>
    <
    CHOICE>ASPAC</CHOICE> < CHOICE>Americas</CHOICE> < CHOICE>EMA</CHOICE> </ CHOICES> < Default>EMA</Default> </ Field> 
     

    If you keep the ID the same as the site column definition, your column will be based on the site column, so you will have all the benefits of using site columns. The WSS SDK contains a special note on this issue. The current version can be found here. Search for "How to: Add a Content Type to a List" and read the note carefully.

    Step 4 - Create a new Site definition and add our custom list

    This excellent blogpost by Todd Baginski explains how to create and register your custom site definition. Mine is called "MyTestSite". You need to change the onet.xml of your site definition. In the SiteFeatures you will need to activate the site column and content type features, created in step 1 and 2

    
    
    <
    
    SiteFeatures>
    
    <!--
    MY TEST FEATURES --> < Feature ID="f95f506b-13cf-41ff-8ca9-7b2a19a34e83" /> <!-- Site Column --> < Feature ID="95db9cfb-dd0b-4b18-8933-ff623a09adea" /> <!-- Content Type --> </ SiteFeatures> 
     

    Make sure that you activate the sitecolumn before the contenttype!

    In the <Configuration> element of your onet.xml file you will also find the <Lists> element. In this element you can instantiate your list template to a list.

    
    
    <
    
    Lists>
    
    <
    List Title="My Test Tasks" Description="My Test Tasks" Url="Lists/Tasks" Type="5500" FeatureId="406dc0e2-cad1-4063-bc5b-ce3e598bd24a" /> </ Lists> 
     

    In the "Type" attribute, you need to put the same value as in the type attribute of the list template in step 3. The "FeatureID" attribute is equal to the ID of the feature "MyTasksList". After registering the site definition and an IISRESET, you are ready to test your new site definition!

     Step 5 - Test

    Create a new site. In you did not specify a tab, your template will show up in the "Custom" tab:

       

    If you go to "View All Site Content" you will see a new list called "My Test Tasks":

    Now click the new button, and you will see the new dropdown for the Region field:

    I have attached a zip file with all the files that I used to create this blog. Enjoy!

    Creating a site administration page that looks like a real SharePoint administration page

    August 12th, 2006 by tonstegeman

    In one of the previous articles I described how you can create a SharePoint Feature that stores values in the property bag of the site. These values were set in the site definition (onet.xml). In this article I will describe how you can easily create an administration page to edit these properties. This page will have to run in the context of the site. Serge van den Oever (we work on the same project) had some issues with that in one of his blogposts. This post will handle some of his questions, but not all. I’ll try to find to other answers later.

    In the previous example we have set 2 properties from the site definition: “NavigationParent” and “NavigationExpand”.  At the end of this article our administration page will look like this:

     

       

    When a user submits the page, the page will also validate if the url that was entered is a valid SharePoint site.

     

    Step 1 Add a link to the Site Settings menu.

    The first step is to create a link to the Site Settings menu. We will lock this down, so that it is only available for administrators. The easiest way to do this is by using a SharePoint Feature. How to install and activate can be found in the previous article. The feature xml for this feature looks like this:

     

        <Feature  Id="06d77b33-76b2-4a14-bef4-3e4b4203d88e"
                  Title="Custom Navigation Site Menu items by Ton"
                  Description="My custom Site Menuitems for setting up navigation."
                  Version="1.0.0.0"
                  Scope="Site"
                  xmlns="http://schemas.microsoft.com/sharepoint/">
            <ElementManifests>
                <ElementManifest Location="tstsitemenus.xml"/>
            </ElementManifests>
        </Feature>

     

    This is the XML for installing the feature. The file that is referenced in the ElementManifest node actually adds the menu item:

     

        <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
        <CustomAction 
            Id="TSTSiteMenus.SiteSettings"
            Title="Setup Custom Navigation">
            Location="Microsoft.SharePoint.SiteSettings"

            GroupId="Customization"

            Sequence="106"
            RequireSiteAdministrator="TRUE" 
            <UrlAction Url="~site/_layouts/CustomNavigation.aspx"/>
          </CustomAction>
        </Elements>

     

     

    A number of attributes further explained:

    ·         Title – the title of the link

    ·         Location – The location of the new action. This can be the Site Actions menu, edit menus, toolbars etc. I have picked “Microsoft.SharePoint.SiteSettings” to make it appear in the site settings page.

    ·         GroupId – The group in which the action will appear. I have picked “Customization”, because this makes our action appear in the “Look and Feel” section of the site settings. You can add these sections yourself using a “CustomActionGroup”. See the WSS SDK for more details.

    ·         RequireSiteAdministrator – This makes the option only appear for Site Administrators.

    ·         UrlAction – The url to navigate to when the user clicks the link. By using the “~site” in the url, we make our administration page run in the context of the site.

     

    After installing and activating the feature, our site administration now has a new item:

       

     

    Step 2 – Create the administration page

    In the second step we will create the CustomNavigation.aspx page that is referenced from the menuoption in step 1. The first step in making your administration page look like real SharePoint administration pages, is inheriting it from “WebAdminPageBase”, which is available in the “Microsoft.SharePoint.ApplicationPages” namespace. I created a new class library project and added a new class called “CustomNavigation”.

    Our aspx page should use this code behind, and also register the namespaces and controls that we will use to build the page. I will not copy all code, you can find that in the attached ZIP file.

     

            <%@ Assembly Name="Microsoft.SharePoint.ApplicationPages, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%>

            <%@ Page Language="C#" Inherits="Eog.OfficeServer.ApplicationPages.CustomNavigation, Eog.OfficeServer.ApplicationPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4765c72bbf274873" MasterPageFile="~/_layouts/application.master" %>

     

    Step 3 – Add controls to the placeholders

    Because our page inherits from WebAdminPageBase, we now have several placeholders that we need to populate. We don’t need to build the whole page, the masterpage does that for us, we just populate a few placeholder. The first placeholders are the title placeholders:

     

            <asp:Content

                ContentPlaceHolderId="PlaceHolderPageTitle"

                runat="server">

                <SharePoint:EncodedLiteral

                    runat="server"

                    text="Custom Navigation"

                    EncodeMethod='HtmlEncode'/>

            </asp:Content>

            <asp:Content

                ContentPlaceHolderId="PlaceHolderPageTitleInTitleArea"

                runat="server">

            Setup Custom Navigation

            </asp:Content>

     

    The main content are of the page (called PlaceHolderMain) will re-use some standard control templates that are available in the ControlTemplates folder.

    These are registered at the top of the page as well:

     

            <%@ Register TagPrefix="wssuc" TagName="InputFormSection" src="~/_controltemplates/InputFormSection.ascx" %>

     

    We start off creating a new section on the page for our “Local Navigation” settings. This can be done by using the InputFormSection control. The InputFormSection can have one or multiple controls. These are added within a “<Template_InputFormControls>“.

    This template can have multiple controls. These controls are InputFormControl controls, that are a combination of the label and the control. They look like this:

     

            <wssuc:InputFormControl

                LabelText="Navigation Expand Depth"

                LabelAssociatedControlID="navigationExpandTextBox"

                runat="server">

                <Template_Control>

                    <wssawc:InputFormTextBox

                        Title="Navigation Expand Depth"

                        class="ms-input"

                        ID="navigationExpandTextBox"

                        Runat="server"

                        MaxLength="255" />

                </Template_Control>

            </wssuc:InputFormControl>

     

    Step 4 – Add OK and Cancel button

    After adding the control to edit the values, we add the OK and Cancel button to the page. These are added in a special ButtonSection, that will be added just below the InputFormSection. The code for the OK button (see the zip for the rest):

     

            <Template_Buttons>

                <asp:Button

                    UseSubmitBehavior="false"

                    runat="server"

                    class="ms-ButtonHeightWidth"

                    OnClick="HandleOKButtonClick"

                    Text="<%$Resources:wss,multipages_okbutton_text%>"

                    id="buttonOK"

                    accesskey="<%$Resources:wss,okbutton_accesskey%>"/>

            </Template_Buttons>

     

    Step 5 – Add validation

    The last part of the aspx page at this stage is the validation of the page. Most out of the box administration pages add a FormDigest control to the page. This is a SharePoint control that adds security validation to the page. This is specific for the user requesting the page and the time it is requested. After some time it expires, making it impossible to post the page. I have not added this yet to my page, will write a post on it after I sorted this out.

    The validation that we will add is a validator for checking if the url entered in the Navigation Parent field is a valid SharePoint site. Just after the InputFormTextbox, we add a CustomValidator:

     

            <asp:CustomValidator

                ID="validateParent"

                ControlToValidate="navigationParentTextBox"

                Display = "Dynamic"

                ErrorMessage = "Please enter a url for a SharePoint site."

                OnServerValidate="ValidateNavigationParent"

                runat="server"/>

     

     

    Step 6 – Implement code-behind

    The code to actually make the page work is implemented in the CustomNavigation.cs. First we need to ensure that users who are not administrator cannot access the page by using the url directly. That is easy J; just add these lines :

     

            protected override bool RequireSiteAdministrator

            {

                get

                {

                    return true;

                }

            }

     

    You can find all code to load and save the siteproperties in the attached zip file. Saving a property to the property bag can be done like this:

            // Store NavigationExpand in the property bag.

            if (web.Properties.ContainsKey("navigationexpand"))

            {

                if (web.Properties["navigationexpand"].ToLower() != navigationExpandTextBox.Text.ToLower())

                {

                    web.Properties["navigationexpand"] = navigationExpandTextBox.Text;

                    modified = true;

                }

            }

            else

            {

                web.Properties.Add("navigationexpand", navigationExpandTextBox.Text);

                modified = true;

            }

     

    And don’t forget to call Update:

            web.Properties.Update();

     

    After saving the properties to the property bag, we will redirect the user back to the site settings page. The code to do that:

     

            SPUtility.Redirect(

                "settings.aspx",

                SPRedirectFlags.UseSource |

                SPRedirectFlags.RelativeToLayoutsPage,

                this.Context);

                }

            }

     

    Step 7 – Deploy the administration page

    The proper way to deploy our administration page in SharePoint 2007 is through a Solution Deployment package. I haven’t had the time to sort that out, but is a very nice way of deploying packages like this. Hope to find some time to sort this out for a next blogpost. You can find more information on the WSS FAQ site of Mike Walsh.

    I have just added the assembly to the GAC and copied the aspx file to the LAYOUTS folder manually (by default “C:Program FilesCommon FilesMicrosoft Sharedweb server extensions12TEMPLATELAYOUTS”).